diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fad8c7fd5..2eaf75e32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,12 +20,24 @@ jobs: matrix: java_version: ['17', '21', '24', '25'] include: - - java_version: '17' + # Release/deploy runs on 25 so the blackbird module (profile-gated + # to JDK 25+) is part of the deployed reactor. + - java_version: '25' release_build: 'R' env: JAVA_OPTS: "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + # Blackbird builds only on JDK 25+, but its jar must run on 17-24 too + # (module discovery skips it there); blackbird-tests starts those JDKs. + - name: Set up older JDKs for Blackbird runtime tests + if: ${{ matrix.java_version == '25' }} + uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + with: + distribution: 'temurin' + java-version: | + 17 + 21 - name: Set up JDK uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: @@ -39,7 +51,12 @@ jobs: # gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import # gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase - name: Build - run: ./mvnw -B -ff -ntp clean verify + run: | + OLD_JDKS="" + if [ -n "${JAVA_HOME_17_X64:-}" ] && [ -n "${JAVA_HOME_21_X64:-}" ]; then + OLD_JDKS="-Dblackbird.test.oldJdks=${JAVA_HOME_17_X64},${JAVA_HOME_21_X64}" + fi + ./mvnw -B -ff -ntp clean verify ${OLD_JDKS} - name: Extract project Maven version id: projectVersion run: echo "version=$(./mvnw org.apache.maven.plugins:maven-help-plugin:3.5.1:evaluate -DforceStdout -Dexpression=project.version -q)" >> $GITHUB_OUTPUT diff --git a/CLAUDE.md b/CLAUDE.md index 3a44da006..5715ce89e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,12 +44,12 @@ Every module follows the same pattern: - A `*Module` class (e.g., `AfterburnerModule`, `BlackbirdModule`) extends Jackson's `tools.jackson.databind.JacksonModule`, registered on a mapper builder (e.g., `JsonMapper.builder().addModule(...)`) - Package: `tools.jackson.module.` (hyphens dropped or turned into sub-packages: `androidrecord`, `noctordeser`, `spisubtypes`, `jakarta.xmlbind`) - `PackageVersion.java` is generated from `PackageVersion.java.in` by the replacer Maven plugin — do not edit manually -- JPMS module descriptors are real sources: `/src/main/java/module-info.java` (and `src/test/java/module-info.java` for tests). Moditect is no longer used; stray `src/moditect/` dirs in blackbird and android-record are leftovers +- JPMS module descriptors are real sources: `/src/main/java/module-info.java` (and `src/test/java/module-info.java` for tests). Moditect is no longer used; the stray `src/moditect/` dir in android-record is a leftover - SPI registration: `src/main/resources/META-INF/services/tools.jackson.databind.JacksonModule` (all modules except guice, guice7, osgi) ### Key module categories -- **Performance (bytecode generation):** afterburner (Byte Buddy-based), blackbird (LambdaMetafactory-based, modern replacement) — both disable themselves automatically in GraalVM native images via `NativeImageUtil.isInNativeImage()` check in `setupModule` +- **Performance (bytecode generation):** afterburner (Byte Buddy-based), blackbird (generated per-bean codecs through the JDK ClassFile API; a multi-release jar that builds and runs on JDK 25+, see `blackbird/README.md`) — both disable themselves automatically in GraalVM native images via `NativeImageUtil.isInNativeImage()` check in `setupModule` - **DI integration:** guice (javax.inject), guice7 (jakarta.inject) — note: guice's `ObjectMapperModule` implements Guice's `com.google.inject.Module`, not Jackson's `JacksonModule` - **Type materialization:** mrbean (generates implementations for interfaces/abstract classes, Byte Buddy-based) - **Annotation support:** jaxb (javax.xml.bind), jakarta-xmlbind (jakarta.xml.bind) diff --git a/blackbird-jpms-tests/pom.xml b/blackbird-jpms-tests/pom.xml new file mode 100644 index 000000000..8259286d2 --- /dev/null +++ b/blackbird-jpms-tests/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + tools.jackson.module + jackson-modules-base + 3.3.0-SNAPSHOT + + jackson-module-blackbird-jpms-tests + Jackson module: Blackbird JPMS integration tests + jar + Module-path integration tests: a foreign named module that +requires blackbird, exercising codec generation for its own non-public +classes. Not published. + + + 25 + 25 + 25 + + true + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + true + + + + + + + + + tools.jackson.module + jackson-module-blackbird + ${project.version} + + + org.junit.jupiter + junit-jupiter + test + + + diff --git a/blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/Beans.java b/blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/Beans.java new file mode 100644 index 000000000..d83f90e4c --- /dev/null +++ b/blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/Beans.java @@ -0,0 +1,16 @@ +package blackbird.jpms.test.beans; + +import java.lang.invoke.MethodHandles; + +/** Hands tests this module's lookup and its non-public bean type. */ +public final class Beans { + private Beans() {} + + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + + public static Class pkgBeanClass() { + return PkgBean.class; + } +} diff --git a/blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/PkgBean.java b/blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/PkgBean.java new file mode 100644 index 000000000..73d5b2090 --- /dev/null +++ b/blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/PkgBean.java @@ -0,0 +1,14 @@ +package blackbird.jpms.test.beans; + +// Deliberately package-private: the codec for this bean must be defined in +// this module's package context, which only works when the generated class +// can resolve its supertype from here. +class PkgBean { + private int count; + private String name; + + public int getCount() { return count; } + public void setCount(int v) { count = v; } + public String getName() { return name; } + public void setName(String v) { name = v; } +} diff --git a/blackbird-jpms-tests/src/main/java/module-info.java b/blackbird-jpms-tests/src/main/java/module-info.java new file mode 100644 index 000000000..9e9471324 --- /dev/null +++ b/blackbird-jpms-tests/src/main/java/module-info.java @@ -0,0 +1,15 @@ +// A foreign named module from Blackbird's point of view: it requires the +// blackbird module and holds non-public beans plus a lookup of its own, the +// exact arrangement a modular application has. +module blackbird.jpms.test +{ + requires tools.jackson.databind; + requires tools.jackson.module.blackbird; + + exports blackbird.jpms.test.beans; + // What a modular application must do for stock databind to construct + // non-public beans reflectively - and the ONLY requirement blackbird + // adds: none. Deliberately NOT opened to blackbird: the accelerated path + // rides databind's fixAccess through unreflected constant handles. + opens blackbird.jpms.test.beans to tools.jackson.databind; +} diff --git a/blackbird-jpms-tests/src/test/java/blackbird/jpms/test/beans/JpmsPackagePrivateTest.java b/blackbird-jpms-tests/src/test/java/blackbird/jpms/test/beans/JpmsPackagePrivateTest.java new file mode 100644 index 000000000..c17ebce53 --- /dev/null +++ b/blackbird-jpms-tests/src/test/java/blackbird/jpms/test/beans/JpmsPackagePrivateTest.java @@ -0,0 +1,93 @@ +package blackbird.jpms.test.beans; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.invoke.MethodHandles; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.module.blackbird.BlackbirdModule; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Runs on the module path as the named module blackbird.jpms.test, which + * requires blackbird: the arrangement a modular application has. A + * package-private bean of this module accelerates with no user lookup at all: + * the only JPMS requirement is the {@code opens ... to tools.jackson.databind} + * that stock databind already needs. Databind's fixAccess marks the members + * accessible, the codec reaches them through unreflected constant handles, + * and the generated class defines in blackbird's own module context. The + * lookup-supplying constructor remains a supported (now redundant) API. + * + * Engagement is asserted through the blackbird.debug.codegen diagnostic + * stream: byte-identical output makes generated and stock otherwise + * indistinguishable from out here. + */ +public class JpmsPackagePrivateTest +{ + private static final String DOC = "{\"count\":7,\"name\":\"x\"}"; + + // The lookup API is a released contract and must keep working even though + // acceleration no longer needs it. + @Test + public void packagePrivateBeanAcceleratesWithModuleLookup() throws Exception { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new BlackbirdModule() { + private static final long serialVersionUID = 1L; + @Override + protected Supplier findLookupSupplier() { + return Beans::lookup; + } + }) + .build(); + String err = captureErr(() -> { + PkgBean bean = mapper.readValue(DOC, PkgBean.class); + assertEquals(7, bean.getCount()); + assertEquals("x", bean.getName()); + }); + assertTrue(err.contains("BBReader_PkgBean"), + "expected a generated codec for PkgBean; diagnostics:\n" + err); + assertFalse(err.contains("null (gated)"), + "PkgBean was gated instead of accelerated; diagnostics:\n" + err); + } + + @Test + public void acceleratesWithoutUserLookup() throws Exception { + // Widened from a demotion pin: no lookup is required any more. The + // opens-to-databind that stock needs is the whole contract. + ObjectMapper mapper = JsonMapper.builder() + .addModule(new BlackbirdModule()) + .build(); + String err = captureErr(() -> { + PkgBean bean = mapper.readValue(DOC, PkgBean.class); + assertEquals(7, bean.getCount()); + assertEquals("x", bean.getName()); + }); + assertTrue(err.contains("BBReader_PkgBean"), + "expected a generated codec for PkgBean with no user lookup; diagnostics:\n" + + err); + assertFalse(err.contains("null (gated)"), + "PkgBean was gated instead of accelerated; diagnostics:\n" + err); + } + + private interface Body { + void run() throws Exception; + } + + private static synchronized String captureErr(Body body) throws Exception { + PrintStream original = System.err; + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + System.setErr(new PrintStream(buffer, true, StandardCharsets.UTF_8)); + try { + body.run(); + } finally { + System.setErr(original); + } + return buffer.toString(StandardCharsets.UTF_8); + } +} diff --git a/blackbird-tests/README.md b/blackbird-tests/README.md index 545450afb..a24fbd4e7 100644 --- a/blackbird-tests/README.md +++ b/blackbird-tests/README.md @@ -5,50 +5,128 @@ test-only and is never published.** ## Why this module exists -This module is the Blackbird counterpart to -[`afterburner-tests`](../afterburner-tests/README.md). The coverage gap it -addresses is narrower than Afterburner's, because Blackbird's optimizer -mechanism is structurally different — and better aligned with JPMS — than -Afterburner's. - -**Afterburner:** uses ByteBuddy + `ClassLoader.defineClass` + reflective -access to `java.lang.ClassLoader` methods. On Java 9+, reflective access to -`ClassLoader` is gated by JPMS, and in the JPMS-named test module every test -POJO ends up in a sealed package. That combination silently disables the -optimizer in the in-tree afterburner test suite. See the `afterburner-tests` -README for the full story. - -**Blackbird:** uses `MethodHandles.Lookup.defineClass` (a JDK 9+ supported -API) + `MethodHandles.privateLookupIn`. Neither needs reflective access to -`ClassLoader` methods, neither cares whether the target package is "sealed by -module", and `privateLookupIn` works across the JPMS module boundary whenever -the target module opens its package to the caller (or lives in the unnamed -module). As a result, Blackbird's in-tree tests **do** exercise the optimizer -for setter-based POJOs. - -What's NOT exercised by Blackbird's in-tree tests, and is covered here: - -- **Classpath / unnamed-module POJOs.** A real-world POJO loaded by the - system class loader rather than as part of the Blackbird JPMS module. - This module's test POJOs live in the unnamed module (no `module-info.java` - anywhere), so the optimizer has to cross the module boundary. -- **The documented limitations of Blackbird's optimizer** — specifically - that direct public-field access is **not** optimized on either the - deserializer or serializer side. Blackbird's - `BBDeserializerModifier.nextProperty` and - `BBSerializerModifier.createProperty` both skip non-method members. -- **The `CrossLoaderAccess` fast-path behavior.** Blackbird's - `CrossLoaderAccess` contains a slow-path that defines a - `$$JacksonBlackbirdAccess` companion class via `Lookup.defineClass(byte[])` - to upgrade a partial-privilege lookup. `CrossLoaderAccessTest` pins the - current behavior that on JDK 9+ with an unnamed-module bean, the fast - path always wins and the companion class is never defined. +Blackbird's in-tree tests run as Blackbird's own named JPMS module, so their +test beans live in that module. This module has no `module-info.java`, and its +tests run on the classpath: the test beans live in the unnamed module, as in +most applications. It also holds the tests that need the packaged jar or other +JDKs. + +## What is covered + +- `inject`: generated codecs engage for setter-based and field-backed POJOs + in the unnamed module, on the reader and the writer side. +- `diff`: differential tests. Each scenario runs through a stock mapper and a + mapper with Blackbird, built from the same configuration, and the two must + give the same value or the same exception (class and message, with the + reference path). Each scenario then asserts which path Blackbird took: the + generated codec, a stock property inside it, or the stock (de)serializer for + the whole bean. The checks look at the (de)serializer that the mapper uses, + not at the placeholder that a modifier sees. The scenarios cover polymorphic + values, non-standard creation, property wrappers, ignoral sets, absent + record components, construction and setter failures, builder setters that + return null, null serializers, self-references, failures with their + reference paths, and wide beans. +- `jdk`: the packaged jar. `JdkCompatibilityTest` runs a small program + against the jar in child JVMs, on the build JDK and on each Java 17-24 home + listed in `-Dblackbird.test.oldJdks=[,]`: module discovery on the + class path and module path, with a second module provider on the path, and + explicit registration with discovery before it, after it and without it. + Without that property, the old-JDK cases are skipped. `PackagingTest` + checks the module descriptor, the OSGi exports and the release guard of the + root POM. `IsolatedLoaderTest` loads Jackson and Blackbird in a class loader + that the system class loader cannot see, as an application server or OSGi + does. + +Some checks need the packaged jar (`mvn verify`); a build that stops before the +package phase reads `blackbird/target/classes`, which is not a multi-release +jar, and those checks are skipped. + +## Databind test corpus (manual) + +The `corpus` directory runs the jackson-databind test suite with +`BlackbirdModule` registered in every mapper that the tests build. Each +failure is a difference between Blackbird and stock databind, or one of the 7 +expected failures that the harness itself causes. The build does not run the +corpus. Run it manually before you submit a change to Blackbird. + +The corpus is not part of the build for two reasons. Databind publishes no +test jar, so the run needs a databind source tree. Also, the hook is a patch +to databind main code, which a databind change can break. Thus a databind +change that breaks Blackbird shows only at the next manual run. + +### Files + +- `databind-register-blackbird.patch`: the only source change to databind. +- `corpus.sh`: the runner. It also compares the failures with the expected + list. +- `stripmod.py`: copies the Blackbird jar without `module-info.class`. +- `failures.py`: lists the failed tests and the tests with errors from the + surefire reports. +- `expected-failures.txt`: the 7 expected failures and their cause. + +### How to run it + +1. Get jackson-databind at commit `f75dc9039fe00f84cf3098c005bbfbb1c5c0bc2d` + (3.x, 2026-10-14), for example from + `https://github.com/FasterXML/jackson-databind/archive/f75dc9039fe00f84cf3098c005bbfbb1c5c0bc2d.tar.gz`. + A later databind commit can also work, but its result can be different. +2. In the databind tree, apply the patch: + `git apply /blackbird-tests/corpus/databind-register-blackbird.patch`. +3. In jackson-modules-base, build the Blackbird jar on JDK 25: + `./mvnw -B -pl blackbird -am package -DskipTests`. +4. Run the corpus on JDK 25 or later: + + ```sh + JAVA_HOME= blackbird-tests/corpus/corpus.sh + ``` + +The runner uses the jar in `blackbird/target` of the same checkout. To use a +different jar, give its path as a third argument. To get the baseline +without Blackbird, give `none` as the third argument. + +### Expected result + +The runner prints `Result: only the expected failures.` and exits with 0. +With databind `f75dc9039fe0`, the suite has 7476 tests: 7468 pass, 1 is +skipped (also without Blackbird), and the 7 expected failures fail. These 7 +tests assert the exact list or number of registered modules, and the harness +adds one module. The baseline without Blackbird has 0 failures. + +The runner exits with 1 when the failures are not the expected ones, and it +lists each difference. Each unexpected failure is a gap in Blackbird: fix the +codec, or add a gate that keeps the bean on the stock path. The full report +of each failure is in `/failures.txt` and `/reports`. + +### What the harness changes in the databind build + +The patch adds 10 lines at the start of the `ObjectMapper(MapperBuilder)` +constructor. When the system property `blackbird.corpus` is `true`, the +constructor creates `tools.jackson.module.blackbird.BlackbirdModule` through +reflection and adds it to the builder. Every mapper goes through this +constructor: the public constructors and `build()` (also after `rebuild()`) +call it. Without the property, databind is unchanged. + +The runner changes no POM. It gives these settings to Maven: + +- `-Dmaven.test.additionalClasspath` adds the Blackbird jar. The runner + removes `module-info.class` from the jar first, so the jar is the + automatic module `blackbird.corpus` in the test run. +- `-DargLine` keeps the `--add-opens` of the databind POM, and adds + `--add-exports` for `deser.impl`, `ser.impl` and `ext.javatime.ser.key`. + The test descriptor of databind does not have these exports of the main + descriptor. +- `-Dblackbird.corpus=true` turns on the registration. +- `-Dtools.jackson.module.blackbird.failOnCodegenError=true` makes a failure + of code generation fail the test, instead of a silent fallback to stock. +- `-Dmaven.test.failure.ignore=true` lets the run continue to the report. + +To see the generation decisions, set +`CORPUS_EXTRA_ARGS=-Dblackbird.debug.codegen=true`. ## Do not add a `module-info.java` here -Same reason as `afterburner-tests`: adding a module descriptor would put the -test POJOs back into a named module and change how `privateLookupIn` -resolves them, silently undermining the classpath-coverage purpose. +Adding a module descriptor would put the test beans back into a named module +and remove the classpath coverage. ## Do not publish this module @@ -57,41 +135,3 @@ that `mvn install` on this module produces no jar, no SBOM, no Gradle module metadata, and no OSGi bundle. See `afterburner-tests/pom.xml` for the same pattern with extended commentary; this module's `pom.xml` shares the same shape. - -## What is covered - -- `BBDeserializerModifier` — setter specializations (int / long / boolean / - String / Object) in `SetterOptimizationTest`. -- `BBSerializerModifier` — getter-based writer specializations in - `SerializerInjectionTest`. -- Field-access negative cases (known design limitation) on both sides, in - `FieldAccessNotOptimizedTest` and `SerializerInjectionTest`. -- `CrossLoaderAccess` fast-path short-circuit for unnamed-module beans, in - `CrossLoaderAccessTest`. - -## What is *not* covered - -- Private-class guard (`Modifier.isPrivate(beanClass)` in both modifiers). -- The slow-path `CrossLoaderAccess.accessClassIn` companion-class definition - — that code is effectively dead for classpath POJOs on JDK 9+; see - `CrossLoaderAccessTest` javadoc for the explanation. -- `CreatorOptimizer` — Blackbird has one; a follow-up could mirror - `afterburner-tests/CreatorOptimizerTest`. -- Concurrent deserializer construction. -- Bean classes in a JPMS module other than Blackbird's own. Testing that - would require a third submodule with its own `module-info.java` that - `opens` its package to Blackbird, which adds complexity for unclear - additional value. -- Lambda-classloader growth as a function of mapper count. Blackbird's - fundamental design creates a new anonymous lambda class per - `LambdaMetafactory.metafactory` invocation; this isn't a bug and isn't - caching-related, so there's nothing to assert. - -## References - -- PR that introduced this module — see git history -- [`afterburner-tests/README.md`](../afterburner-tests/README.md) for the - sibling module and the broader rationale -- Issue #348 — analogous (but different) afterburner classloader caching - issue. Does not apply to Blackbird: Blackbird uses `Lookup.defineClass`, - not reflective `ClassLoader.defineClass`, so it is structurally immune. diff --git a/blackbird-tests/corpus/corpus.sh b/blackbird-tests/corpus/corpus.sh new file mode 100755 index 000000000..e400f86ee --- /dev/null +++ b/blackbird-tests/corpus/corpus.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# Runs the jackson-databind test suite with Blackbird registered in every +# mapper, and compares the failures with expected-failures.txt. See +# ../README.md ("Databind test corpus"). +# +# Usage: corpus.sh [ | none] +# databind-tree a jackson-databind checkout with databind-register-blackbird.patch +# out-dir receives build.log, reports/ and failures.txt (emptied first) +# blackbird-jar default: the jar in blackbird/target of this checkout +# none runs the suite without Blackbird (the baseline) +# JAVA_HOME must be a JDK 25 or later. CORPUS_EXTRA_ARGS adds JVM arguments to +# the forked test JVM (for example -Dblackbird.debug.codegen=true). +# Exit status: 0 when the failures are exactly the expected ones, 1 when they +# differ, 2 or more when the run could not complete. +set -u +if [ $# -lt 2 ] || [ $# -gt 3 ]; then + sed -n '6,10p' "$0" >&2 + exit 2 +fi +HERE=$(cd "$(dirname "$0")" && pwd) +REPO=$(cd "$HERE/../.." && pwd) +TREE=$(cd "$1" && pwd) || exit 2 +OUT=$2 +JAR=${3:-} +if [ -z "$JAR" ]; then + for f in "$REPO"/blackbird/target/jackson-module-blackbird-*.jar; do + case "$f" in + *-sources.jar|*-javadoc.jar|*-tests.jar) ;; + *) [ -f "$f" ] && JAR=$f ;; + esac + done + if [ -z "$JAR" ]; then + echo "No Blackbird jar in $REPO/blackbird/target: run" >&2 + echo " ./mvnw -B -pl blackbird -am package -DskipTests" >&2 + exit 2 + fi +fi +if ! grep -q 'blackbird.corpus' "$TREE/src/main/java/tools/jackson/databind/ObjectMapper.java"; then + echo "$TREE does not have the corpus hook: in that tree, run" >&2 + echo " git apply $HERE/databind-register-blackbird.patch" >&2 + exit 2 +fi +rm -rf "$OUT" +mkdir -p "$OUT" +OUT=$(cd "$OUT" && pwd) + +# The same opens that databind's own surefire configuration passes. +BASEARG="--add-opens=java.base/java.lang=tools.jackson.databind --add-opens=java.base/java.util=tools.jackson.databind --add-opens=tools.jackson.databind/tools.jackson.databind.jdk21=org.junit.platform.commons" +if [ "$JAR" = none ]; then + EXTRA=("-Dcorpus.none=true") + ARGLINE="$BASEARG" +else + echo "Blackbird jar: $JAR" + # Without module-info, the jar joins the test run as the automatic module + # "blackbird.corpus". Databind's test descriptor leaves out three exports of + # the real descriptor that Blackbird uses, so they are added here. + python3 -I "$HERE/stripmod.py" "$JAR" "$OUT/blackbird-corpus.jar" || exit 3 + EXTRA=("-Dmaven.test.additionalClasspath=$OUT/blackbird-corpus.jar") + EXPORTS="" + for p in tools.jackson.databind.deser.impl tools.jackson.databind.ser.impl tools.jackson.databind.ext.javatime.ser.key; do + EXPORTS="$EXPORTS --add-exports=tools.jackson.databind/$p=blackbird.corpus" + done + ARGLINE="$BASEARG $EXPORTS -Dblackbird.corpus=true -Dtools.jackson.module.blackbird.failOnCodegenError=true ${CORPUS_EXTRA_ARGS:-}" +fi +cd "$TREE" || exit 2 +rm -rf target/surefire-reports +mvn -B -ntp test -Djacoco.skip=true \ + -Dmaven.test.failure.ignore=true \ + "${EXTRA[@]}" "-DargLine=$ARGLINE" > "$OUT/build.log" 2>&1 +STATUS=$? +if [ $STATUS -ne 0 ] || [ ! -d target/surefire-reports ]; then + echo "The databind build failed (exit $STATUS): see $OUT/build.log" >&2 + exit 3 +fi +cp -r target/surefire-reports "$OUT/reports" +grep -E 'Tests run:.*Fail' "$OUT/build.log" | tail -1 +python3 -I "$HERE/failures.py" "$OUT/reports" > "$OUT/failures.txt" || exit 3 +tail -1 "$OUT/failures.txt" + +if [ "$JAR" = none ]; then + EXPECTED=/dev/null +else + EXPECTED="$HERE/expected-failures.txt" +fi +grep -E '^[FE] ' "$OUT/failures.txt" | awk '{print $2}' | sort > "$OUT/failed-names.txt" +grep -v -E '^#|^$' "$EXPECTED" | awk '{print $1}' | sort > "$OUT/expected-names.txt" +UNEXPECTED=$(comm -23 "$OUT/failed-names.txt" "$OUT/expected-names.txt") +MISSING=$(comm -13 "$OUT/failed-names.txt" "$OUT/expected-names.txt") +if [ -z "$UNEXPECTED" ] && [ -z "$MISSING" ]; then + echo "Result: only the expected failures." + exit 0 +fi +if [ -n "$UNEXPECTED" ]; then + echo "Unexpected failures (details in $OUT/failures.txt):" + echo "$UNEXPECTED" +fi +if [ -n "$MISSING" ]; then + echo "Expected failures that did not fail (update expected-failures.txt):" + echo "$MISSING" +fi +exit 1 diff --git a/blackbird-tests/corpus/databind-register-blackbird.patch b/blackbird-tests/corpus/databind-register-blackbird.patch new file mode 100644 index 000000000..e9d113204 --- /dev/null +++ b/blackbird-tests/corpus/databind-register-blackbird.patch @@ -0,0 +1,20 @@ +diff -ru a/src/main/java/tools/jackson/databind/ObjectMapper.java b/src/main/java/tools/jackson/databind/ObjectMapper.java +--- a/src/main/java/tools/jackson/databind/ObjectMapper.java ++++ b/src/main/java/tools/jackson/databind/ObjectMapper.java +@@ -299,6 +299,16 @@ + */ + protected ObjectMapper(MapperBuilder builder) + { ++ // CORPUS HARNESS (not upstream): register Blackbird in every mapper. ++ if (Boolean.getBoolean("blackbird.corpus")) { ++ try { ++ builder.addModule((JacksonModule) Class.forName( ++ "tools.jackson.module.blackbird.BlackbirdModule") ++ .getDeclaredConstructor().newInstance()); ++ } catch (ReflectiveOperationException e) { ++ throw new IllegalStateException(e); ++ } ++ } + // First things first: finalize building process. Saved state + // consists of snapshots and is safe to keep references to; used + // for rebuild()ing mapper instances diff --git a/blackbird-tests/corpus/expected-failures.txt b/blackbird-tests/corpus/expected-failures.txt new file mode 100644 index 000000000..c3c6430d0 --- /dev/null +++ b/blackbird-tests/corpus/expected-failures.txt @@ -0,0 +1,11 @@ +# The 7 failures that the harness itself causes. The harness adds +# BlackbirdModule to every mapper, so a test that asserts the exact list or +# number of registered modules sees one module more. None of them is a +# Blackbird behavior difference. Format: class#method :: assertion. +tools.jackson.databind.ObjectMapperTest#testRegisterDependentModules :: expected: <[dep1, dep2, main]> but was: <[dep1, dep2, main, tools.jackson.module.blackbird.BlackbirdModule]> +tools.jackson.databind.ObjectMapperTest#testRegisterTransitiveModuleDependencies :: expected: <[A, B, C]> but was: <[A, B, C, tools.jackson.module.blackbird.BlackbirdModule]> +tools.jackson.databind.module.BuilderModuleReuse5481Test#testBuilderReuseWithDifferentModules :: expected: <[ModuleA, ModuleB]> but was: <[ModuleA, ModuleB, BlackbirdModule]> +tools.jackson.databind.module.SimpleModuleTest#testGetRegisteredModules :: expected: <2> but was: <3> +tools.jackson.databind.module.SimpleModuleTest#testMultipleSimpleModules :: expected: <2> but was: <3> +tools.jackson.databind.module.SimpleModuleTest#testAutoDiscovery :: expected: <0> but was: <1> +tools.jackson.databind.module.SimpleModuleTest#testDuplicateModules5063 :: expected: <2> but was: <3> diff --git a/blackbird-tests/corpus/failures.py b/blackbird-tests/corpus/failures.py new file mode 100644 index 000000000..9a687b521 --- /dev/null +++ b/blackbird-tests/corpus/failures.py @@ -0,0 +1,28 @@ +"""List failing/erroring test cases from surefire XML reports. + +Usage: failures.py [--detail] +""" +import glob +import os +import sys +import xml.etree.ElementTree as ET + +reports = sys.argv[1] +detail = "--detail" in sys.argv +rows = [] +for path in sorted(glob.glob(os.path.join(reports, "TEST-*.xml"))): + root = ET.parse(path).getroot() + for tc in root.iter("testcase"): + for kind in ("failure", "error"): + el = tc.find(kind) + if el is not None: + msg = (el.get("message") or "").replace("\n", " ")[:300] + typ = el.get("type") or "" + rows.append((tc.get("classname"), tc.get("name"), kind, typ, msg, + (el.text or ""))) +for cls, name, kind, typ, msg, text in rows: + print(f"{kind[0].upper()} {cls}#{name} :: {typ} :: {msg}") + if detail: + lines = [l for l in text.splitlines() if l.strip()][:25] + print(" " + "\n ".join(lines)) +print(f"total {len(rows)}") diff --git a/blackbird-tests/corpus/stripmod.py b/blackbird-tests/corpus/stripmod.py new file mode 100644 index 000000000..87d20c3a9 --- /dev/null +++ b/blackbird-tests/corpus/stripmod.py @@ -0,0 +1,10 @@ +"""Copy a jar without any module-info.class entries (base or versioned).""" +import sys +import zipfile + +src, dst = sys.argv[1], sys.argv[2] +with zipfile.ZipFile(src) as zin, zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED) as zout: + for item in zin.infolist(): + if item.filename.endswith("module-info.class"): + continue + zout.writestr(item, zin.read(item.filename)) diff --git a/blackbird-tests/pom.xml b/blackbird-tests/pom.xml index 51c382b19..18c31701a 100644 --- a/blackbird-tests/pom.xml +++ b/blackbird-tests/pom.xml @@ -10,16 +10,18 @@ Jackson module: Blackbird integration tests jar - Classpath-mode integration tests for jackson-module-blackbird. Unlike -jackson-module-afterburner-tests, Blackbird's in-tree tests already exercise most of -the optimizer because Blackbird uses the JDK 9+ supported MethodHandles.Lookup API -rather than reflective ClassLoader access. What this module adds is coverage of the -classpath/unnamed-module path: when a POJO lives outside Blackbird's own JPMS module, -Blackbird's CrossLoaderAccess has to define a $$JacksonBlackbirdAccess companion class -via Lookup.defineClass to upgrade the caller lookup to one with full privilege access -over the target package. That path is not exercised by in-tree tests (caller and bean -share a module, so privateLookupIn already has full privilege). This module is test-only -and is never published. + Classpath-mode integration tests for jackson-module-blackbird: test beans in +the unnamed module, differential tests against stock databind, and runs of the packaged +jar on older JDKs. This module is test-only and is never published. + + + + 25 + 25 + 25 + + + @@ -150,14 +152,17 @@ and is never published. + so tests must run on the classpath (unnamed module): Blackbird's + in-tree tests run as Blackbird's own named module instead. --> org.apache.maven.plugins maven-surefire-plugin false + + + ${blackbird.test.oldJdks} + diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/DifferentialTestBase.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/DifferentialTestBase.java new file mode 100644 index 000000000..32ca437fa --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/DifferentialTestBase.java @@ -0,0 +1,177 @@ +package tools.jackson.module.blackbird.diff; + +import java.util.function.UnaryOperator; + +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.internal.GeneratedBuilderBase; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Differential tests: every scenario runs through two mappers built from the + * same configuration, one of them with Blackbird, and must give the same + * outcome - the same value (class and stock serialization), or the same + * exception (class and message, which includes the reference path). + * Scenarios then assert which path Blackbird took: the generated codec, a + * stock arm inside it for one property, or the stock (de)serializer for the + * whole bean. The assertions look at the (de)serializer the mapper actually + * uses, not at what a modifier saw. + */ +public abstract class DifferentialTestBase +{ + static { + // Strict mode: a codec-generation failure fails the test instead of + // demoting to the stock path (see CodegenFallbacks). + System.setProperty("tools.jackson.module.blackbird.failOnCodegenError", "true"); + } + + // Describes values; never has Blackbird. + private static final ObjectMapper DESCRIBER = JsonMapper.builder().build(); + + /** Two mappers with one configuration; only the second has Blackbird. */ + protected record Mappers(JsonMapper stock, JsonMapper blackbird) {} + + protected static Mappers mappers() { + return mappers(b -> b); + } + + protected static Mappers mappers(UnaryOperator config) { + return new Mappers(config.apply(JsonMapper.builder()).build(), + config.apply(JsonMapper.builder()).addModule(new BlackbirdModule()).build()); + } + + /** The result of one operation: a value, or the exception it threw. */ + protected record Outcome(Object value, Throwable error) { + String describe() { + if (error != null) { + return "threw " + error.getClass().getName() + ": " + error.getMessage(); + } + if (value == null) { + return "null"; + } + if (value instanceof String s) { + return "string " + s; + } + try { + return value.getClass().getName() + " " + DESCRIBER.writeValueAsString(value); + } catch (RuntimeException e) { + return value.getClass().getName() + " " + value; + } + } + + @SuppressWarnings("unchecked") + T get() { + assertNull(error, () -> "expected a value, got " + describe()); + return (T) value; + } + + T error(Class type) { + assertTrue(type.isInstance(error), () -> "expected " + type.getName() + ", got " + + describe()); + return type.cast(error); + } + } + + protected static Outcome read(ObjectMapper mapper, String json, Class type) { + try { + return new Outcome(mapper.readValue(json, type), null); + } catch (RuntimeException | StackOverflowError e) { + return new Outcome(null, e); + } + } + + protected static Outcome write(ObjectMapper mapper, Object value) { + try { + return new Outcome(mapper.writeValueAsString(value), null); + } catch (RuntimeException | StackOverflowError e) { + return new Outcome(null, e); + } + } + + /** Reads with both mappers; returns the Blackbird outcome once it matches stock. */ + protected static Outcome assertSameRead(Mappers m, String json, Class type) { + Outcome stock = read(m.stock(), json, type); + Outcome blackbird = read(m.blackbird(), json, type); + assertEquals(stock.describe(), blackbird.describe(), + "Blackbird and stock differ reading " + type.getSimpleName() + " from " + json); + return blackbird; + } + + /** Writes with both mappers; returns the Blackbird outcome once it matches stock. */ + protected static Outcome assertSameWrite(Mappers m, Object value) { + Outcome stock = write(m.stock(), value); + Outcome blackbird = write(m.blackbird(), value); + assertEquals(stock.describe(), blackbird.describe(), + "Blackbird and stock differ writing " + value.getClass().getSimpleName()); + return blackbird; + } + + /* + /********************************************************************** + /* Which path Blackbird took + /********************************************************************** + */ + + @SuppressWarnings("unchecked") + protected static ValueDeserializer rootReader(ObjectMapper mapper, Class type) { + DeserializationContext ctxt = mapper._deserializationContext(); + // Contextual but not type-wrapped: under default typing a root value + // is read through a TypeWrappedDeserializer around this one. + return (ValueDeserializer) ctxt.findContextualValueDeserializer( + ctxt.constructType(type), null); + } + + protected static ValueSerializer rootWriter(ObjectMapper mapper, Class type) { + SerializationContext ctxt = mapper._serializationContext(); + return ctxt.handleRootContextualization(ctxt.findValueSerializer(type)); + } + + /** Asserts that a generated reader handles the type; returns its "name=KIND" layout. */ + protected static String assertGeneratedReader(ObjectMapper mapper, Class type) { + ValueDeserializer d = rootReader(mapper, type); + if (d instanceof GeneratedReaderBase r) { + return r.codecLayout(); + } + if (d instanceof GeneratedBuilderBase b) { + return b.codecLayout(); + } + return fail(type.getSimpleName() + " should use a generated reader, uses " + d); + } + + /** Asserts that the stock deserializer handles the whole type. */ + protected static void assertStockReader(ObjectMapper mapper, Class type) { + ValueDeserializer d = rootReader(mapper, type); + assertFalse(d instanceof GeneratedReaderBase || d instanceof GeneratedBuilderBase, + type.getSimpleName() + " should keep the stock deserializer, uses " + d); + } + + /** Asserts that a generated writer handles the type; returns its "name=KIND" layout. */ + protected static String assertGeneratedWriter(ObjectMapper mapper, Class type) { + ValueSerializer s = rootWriter(mapper, type); + if (s instanceof GeneratedWriterBase w) { + return w.codecLayout(); + } + return fail(type.getSimpleName() + " should use a generated writer, uses " + s); + } + + /** Asserts that the stock serializer handles the whole type. */ + protected static void assertStockWriter(ObjectMapper mapper, Class type) { + ValueSerializer s = rootWriter(mapper, type); + assertFalse(s instanceof GeneratedWriterBase, + type.getSimpleName() + " should keep the stock serializer, uses " + s); + } + + /** Asserts that the layout has the arm, e.g. "pet=STOCK". */ + protected static void assertArm(String layout, String arm) { + assertTrue(("," + layout + ",").contains("," + arm + ","), + "expected arm " + arm + " in layout " + layout); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderErrorsDiffTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderErrorsDiffTest.java new file mode 100644 index 000000000..26f3793a2 --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderErrorsDiffTest.java @@ -0,0 +1,294 @@ +package tools.jackson.module.blackbird.diff; + +import java.lang.reflect.Field; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.DatabindException; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.annotation.JsonPOJOBuilder; +import tools.jackson.databind.deser.DeserializationProblemHandler; +import tools.jackson.databind.exc.ValueInstantiationException; +import tools.jackson.module.blackbird.deser.BBDeserializerModifier; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Failures inside generated POJO and builder codecs: constructors, build + * methods and setters fail with the exception class, message and reference + * path of stock, with and without WRAP_EXCEPTIONS. A builder setter that + * returns null keeps the current builder, as in stock. Every scenario's bean + * keeps the generated codec. + */ +public class ReaderErrorsDiffTest extends DifferentialTestBase +{ + /* + /********************************************************************** + /* Constructor and build() failures + /********************************************************************** + */ + + public static class ThrowingCtor { + static final ThreadLocal FAIL = ThreadLocal.withInitial(() -> true); + + public String a; + + public ThrowingCtor() { + if (FAIL.get()) { + throw new IllegalStateException("no default instance"); + } + } + + static ThrowingCtor created() { + FAIL.set(false); + try { + return new ThrowingCtor(); + } finally { + FAIL.remove(); + } + } + } + + @Test + public void constructorFailureIsValueInstantiationException() { + Mappers m = mappers(); + ValueInstantiationException e = assertSameRead(m, "{\"a\":\"x\"}", ThrowingCtor.class) + .error(ValueInstantiationException.class); + assertTrue(e.getMessage().contains("no default instance"), e.getMessage()); + assertSameRead(mappers(b -> b.disable(DeserializationFeature.WRAP_EXCEPTIONS)), + "{\"a\":\"x\"}", ThrowingCtor.class); + assertGeneratedReader(m.blackbird(), ThrowingCtor.class); + } + + static final class CtorHandler extends DeserializationProblemHandler { + @Override + public Object handleInstantiationProblem(DeserializationContext ctxt, Class instClass, + Object argument, Throwable t) { + return (instClass == ThrowingCtor.class) ? ThrowingCtor.created() : NOT_HANDLED; + } + } + + @Test + public void problemHandlerReplacesFailedConstruction() { + Mappers m = mappers(b -> b.addHandler(new CtorHandler())); + ThrowingCtor t = assertSameRead(m, "{\"a\":\"x\"}", ThrowingCtor.class).get(); + assertEquals("x", t.a); + assertGeneratedReader(m.blackbird(), ThrowingCtor.class); + } + + @JsonDeserialize(builder = Immutable.Builder.class) + public static class Immutable { + public final String name; + public final int count; + + Immutable(String name, int count) { + this.name = name; + this.count = count; + } + + @JsonPOJOBuilder(withPrefix = "") + public static class Builder { + private String name; + private int count; + + public Builder name(String n) { name = n; return this; } + public Builder count(int c) { count = c; return this; } + + public Immutable build() { + if (name == null) { + throw new IllegalStateException( + "Cannot build Immutable, some of required attributes are not set [name]"); + } + return new Immutable(name, count); + } + } + } + + @Test + public void buildFailureIsValueInstantiationException() { + Mappers m = mappers(); + assertEquals("a", assertSameRead(m, "{\"name\":\"a\",\"count\":1}", Immutable.class) + .get().name); + ValueInstantiationException e = assertSameRead(m, "{\"count\":1}", Immutable.class) + .error(ValueInstantiationException.class); + assertTrue(e.getMessage().contains("required attributes"), e.getMessage()); + assertSameRead(mappers(b -> b.disable(DeserializationFeature.WRAP_EXCEPTIONS)), + "{\"count\":1}", Immutable.class); + assertGeneratedReader(m.blackbird(), Immutable.class); + } + + /* + /********************************************************************** + /* Setter failures + /********************************************************************** + */ + + public static class Child { + public String name; + } + + public static class Guarded { + private int age; + private String code; + private Child child; + public String other; + + public int getAge() { return age; } + + public void setAge(int age) { + if (age < 0) { + throw new IllegalArgumentException("age must be >= 0"); + } + this.age = age; + } + + public String getCode() { return code; } + + public void setCode(String code) { + if (code.isEmpty()) { + throw new IllegalStateException("empty code"); + } + this.code = code; + } + + public Child getChild() { return child; } + + public void setChild(Child child) { + if (child.name == null) { + throw new IllegalArgumentException(null, null); + } + this.child = child; + } + } + + @Test + public void setterIllegalArgument() { + Mappers m = mappers(); + DatabindException e = assertSameRead(m, "{\"other\":\"o\",\"age\":-1}", Guarded.class) + .error(DatabindException.class); + assertTrue(e.getMessage().startsWith("Problem deserializing property 'age'"), + e.getMessage()); + // Stock turns the IllegalArgumentException into a DatabindException + // even without wrapping. + assertSameRead(mappers(b -> b.disable(DeserializationFeature.WRAP_EXCEPTIONS)), + "{\"age\":-1}", Guarded.class).error(DatabindException.class); + // A child value: the message names its class; no message from the setter. + assertSameRead(m, "{\"child\":{}}", Guarded.class).error(DatabindException.class); + String layout = assertGeneratedReader(m.blackbird(), Guarded.class); + assertArm(layout, "age=INT"); + assertArm(layout, "child=CHILD"); + } + + @Test + public void setterOtherRuntimeException() { + Mappers m = mappers(); + assertSameRead(m, "{\"code\":\"\"}", Guarded.class).error(DatabindException.class); + assertSameRead(mappers(b -> b.disable(DeserializationFeature.WRAP_EXCEPTIONS)), + "{\"code\":\"\"}", Guarded.class).error(IllegalStateException.class); + assertArm(assertGeneratedReader(m.blackbird(), Guarded.class), "code=STRING"); + } + + @JsonDeserialize(builder = GuardedBuilt.Builder.class) + public static class GuardedBuilt { + public final int age; + + GuardedBuilt(int age) { + this.age = age; + } + + @JsonPOJOBuilder(withPrefix = "with") + public static class Builder { + private int age; + + public Builder withAge(int a) { + if (a < 0) { + throw new IllegalArgumentException("age must be >= 0"); + } + age = a; + return this; + } + + public GuardedBuilt build() { return new GuardedBuilt(age); } + } + } + + @Test + public void builderSetterIllegalArgument() { + Mappers m = mappers(); + assertSameRead(m, "{\"age\":-1}", GuardedBuilt.class).error(DatabindException.class); + assertArm(assertGeneratedReader(m.blackbird(), GuardedBuilt.class), "age=INT"); + } + + /* + /********************************************************************** + /* Builder setters that return null + /********************************************************************** + */ + + // A setter declared to return the builder may return null; stock + // deserializeSetAndReturn then keeps the current builder. + @JsonDeserialize(builder = NullFluent.Builder.class) + public static class NullFluent { + public final String a; + public final int n; + public final Child child; + public final String b; + + NullFluent(String a, int n, Child child, String b) { + this.a = a; + this.n = n; + this.child = child; + this.b = b; + } + + @JsonPOJOBuilder(withPrefix = "") + public static class Builder { + private String a; + private int n; + private Child child; + private String b; + + public Builder a(String v) { a = v; return null; } + public Builder n(int v) { n = v; return null; } + public Builder child(Child v) { child = v; return null; } + public Builder b(String v) { b = v; return this; } + + public NullFluent build() { return new NullFluent(a, n, child, b); } + } + } + + @Test + public void builderSetterReturningNullKeepsBuilder() { + Mappers m = mappers(); + NullFluent v = assertSameRead(m, + "{\"a\":\"x\",\"n\":3,\"child\":{\"name\":\"c\"},\"b\":\"y\"}", NullFluent.class) + .get(); + assertEquals("x", v.a); + assertEquals(3, v.n); + assertEquals("c", v.child.name); + assertEquals("y", v.b); + String layout = assertGeneratedReader(m.blackbird(), NullFluent.class); + assertArm(layout, "a=STRING"); + assertArm(layout, "n=INT"); + assertArm(layout, "child=CHILD"); + assertArm(layout, "b=STRING"); + } + + /* + /********************************************************************** + /* No thread-local hand-off of the build method + /********************************************************************** + */ + + @Test + public void modifierKeepsNoThreadLocalState() { + for (Field f : BBDeserializerModifier.class.getDeclaredFields()) { + assertFalse(ThreadLocal.class.isAssignableFrom(f.getType()), f.toString()); + } + Mappers m = mappers(); + assertSameRead(m, "{\"name\":\"a\",\"count\":1}", Immutable.class); + assertGeneratedReader(m.blackbird(), Immutable.class); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderGatesDiffTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderGatesDiffTest.java new file mode 100644 index 000000000..3ffaecc46 --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderGatesDiffTest.java @@ -0,0 +1,348 @@ +package tools.jackson.module.blackbird.diff; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIgnoreType; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DefaultTyping; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.deser.BeanDeserializerBuilder; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.UnrecognizedPropertyException; +import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import tools.jackson.databind.module.SimpleModule; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Reader features that the generated loop must leave to stock: polymorphic + * property values, non-standard creation (external type ids, delegating + * creators), property wrappers, and the ignoral sets stock computes. Each + * scenario has a plain control bean that must keep the generated codec. + */ +public class ReaderGatesDiffTest extends DifferentialTestBase +{ + public static class Plain { + public String name; + public int count; + } + + /* + /********************************************************************** + /* Polymorphic property values + /********************************************************************** + */ + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@t") + @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog") }) + public static class Animal { + public String name; + } + + public static class Dog extends Animal { + public boolean bark; + } + + public static class Owner { + public String name; + public Animal pet; + } + + @Test + public void polymorphicPropertyKeepsSubtype() { + Mappers m = mappers(); + Owner owner = assertSameRead(m, + "{\"name\":\"o\",\"pet\":{\"@t\":\"dog\",\"name\":\"rex\",\"bark\":true}}", + Owner.class).get(); + assertEquals(Dog.class, owner.pet.getClass()); + assertTrue(((Dog) owner.pet).bark); + String layout = assertGeneratedReader(m.blackbird(), Owner.class); + assertArm(layout, "pet=STOCK"); + assertArm(layout, "name=STRING"); + } + + public static class Pet { + public String name; + } + + public static class Cat extends Pet { + public int lives; + } + + public static class PetHolder { + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) + @JsonSubTypes({ @JsonSubTypes.Type(value = Cat.class, name = "cat") }) + public Pet pet; + public String tag; + } + + @Test + public void propertyLevelTypeInfoKeepsSubtype() { + Mappers m = mappers(); + PetHolder h = assertSameRead(m, + "{\"pet\":{\"@type\":\"cat\",\"name\":\"c\",\"lives\":9},\"tag\":\"t\"}", + PetHolder.class).get(); + assertEquals(Cat.class, h.pet.getClass()); + String layout = assertGeneratedReader(m.blackbird(), PetHolder.class); + assertArm(layout, "pet=STOCK"); + assertArm(layout, "tag=STRING"); + } + + public static class DefaultTyped { + public Pet pet; + public String tag; + } + + @Test + public void defaultTypingKeepsSubtype() { + // As a property, the type id sits inside the value object. + Mappers m = mappers(b -> b.activateDefaultTypingAsProperty( + BasicPolymorphicTypeValidator.builder().allowIfBaseType(Object.class).build(), + DefaultTyping.NON_FINAL, "@class")); + DefaultTyped value = new DefaultTyped(); + Cat cat = new Cat(); + cat.name = "c"; + cat.lives = 7; + value.pet = cat; + value.tag = "t"; + String json = m.stock().writeValueAsString(value); + DefaultTyped read = assertSameRead(m, json, DefaultTyped.class).get(); + assertEquals(Cat.class, read.pet.getClass()); + // Default typing types the property; the bean itself is non-final, + // so it is read through the typed path, which stays stock. + assertArm(assertGeneratedReader(m.blackbird(), DefaultTyped.class), "pet=STOCK"); + } + + /* + /********************************************************************** + /* Non-standard creation + /********************************************************************** + */ + + public static class ExtBase { + public String v; + } + + public static class ExtImpl extends ExtBase { + public int n; + } + + public static class ExtHolder { + public String note; + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "kind") + @JsonSubTypes({ @JsonSubTypes.Type(value = ExtImpl.class, name = "impl") }) + public ExtBase payload; + } + + @Test + public void externalTypeIdKeepsStockBean() { + Mappers m = mappers(); + ExtHolder h = assertSameRead(m, + "{\"note\":\"x\",\"kind\":\"impl\",\"payload\":{\"v\":\"a\",\"n\":3}}", + ExtHolder.class).get(); + assertEquals(ExtImpl.class, h.payload.getClass()); + assertEquals(3, ((ExtImpl) h.payload).n); + assertStockReader(m.blackbird(), ExtHolder.class); + assertGeneratedReader(m.blackbird(), Plain.class); + } + + public static class Delegating { + public String v; + + public Delegating() { } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static Delegating of(Map m) { + Delegating d = new Delegating(); + d.v = "delegated:" + m.get("v"); + return d; + } + } + + @Test + public void delegatingCreatorBesideDefaultConstructorKeepsStockBean() { + Mappers m = mappers(); + Delegating d = assertSameRead(m, "{\"v\":\"x\",\"w\":1}", Delegating.class).get(); + assertEquals("delegated:x", d.v); + assertStockReader(m.blackbird(), Delegating.class); + assertGeneratedReader(m.blackbird(), Plain.class); + } + + /* + /********************************************************************** + /* Property wrappers + /********************************************************************** + */ + + public static class Parent { + public String name; + @JsonManagedReference + public Kid kid; + } + + public static class Kid { + public String name; + @JsonBackReference + public Parent parent; + } + + @Test + public void managedReferenceSetsBackReference() { + Mappers m = mappers(); + Parent p = assertSameRead(m, "{\"name\":\"p\",\"kid\":{\"name\":\"k\"}}", + Parent.class).get(); + assertSame(p, p.kid.parent); + Parent stock = m.stock().readValue("{\"name\":\"p\",\"kid\":{\"name\":\"k\"}}", + Parent.class); + assertSame(stock, stock.kid.parent); + String layout = assertGeneratedReader(m.blackbird(), Parent.class); + assertArm(layout, "kid=STOCK"); + assertArm(layout, "name=STRING"); + } + + // A user wrapper: upper-cases every String property value. + static final class UpperCaseProperty extends SettableBeanProperty.Delegating { + private static final long serialVersionUID = 1L; + + UpperCaseProperty(SettableBeanProperty d) { + super(d); + } + + @Override + protected SettableBeanProperty withDelegate(SettableBeanProperty d) { + return new UpperCaseProperty(d); + } + + @Override + public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, + Object instance) { + delegate.set(ctxt, instance, p.getString().toUpperCase()); + } + + @Override + public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, + Object instance) { + return delegate.setAndReturn(ctxt, instance, p.getString().toUpperCase()); + } + } + + public static class Wrapped { + public String text; + public int count; + } + + private static SimpleModule upperCaseModule() { + return new SimpleModule("upper").setDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + + @Override + public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, + BeanDescription.Supplier beanDescRef, BeanDeserializerBuilder builder) { + if (beanDescRef.getBeanClass() == Wrapped.class) { + SettableBeanProperty text = builder.findProperty( + tools.jackson.databind.PropertyName.construct("text")); + builder.addOrReplaceProperty(new UpperCaseProperty(text), true); + } + return builder; + } + }); + } + + @Test + public void userPropertyWrapperRuns() { + Mappers m = mappers(b -> b.addModule(upperCaseModule())); + Wrapped w = assertSameRead(m, "{\"text\":\"xy\",\"count\":2}", Wrapped.class).get(); + assertEquals("XY", w.text); + String layout = assertGeneratedReader(m.blackbird(), Wrapped.class); + assertArm(layout, "text=STOCK"); + assertArm(layout, "count=INT"); + } + + /* + /********************************************************************** + /* Ignoral sets as stock computes them + /********************************************************************** + */ + + @JsonIgnoreType + public static class Secret { + public String s; + } + + public static class HasSecret { + public String name; + public Secret secret; + } + + @Test + public void ignoredTypePropertyIsIgnoredNotUnknown() { + Mappers m = mappers(b -> b.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + HasSecret h = assertSameRead(m, "{\"name\":\"n\",\"secret\":{\"s\":\"x\"}}", + HasSecret.class).get(); + assertEquals("n", h.name); + assertNull(h.secret); + assertGeneratedReader(m.blackbird(), HasSecret.class); + } + + @JsonIgnoreProperties(value = "x", allowSetters = true) + public static class AllowSetters { + public String name; + public String x; + } + + @Test + public void allowSettersStillReadsProperty() { + Mappers m = mappers(); + AllowSetters a = assertSameRead(m, "{\"name\":\"n\",\"x\":\"set\"}", + AllowSetters.class).get(); + assertEquals("set", a.x); + assertGeneratedReader(m.blackbird(), AllowSetters.class); + } + + @JsonIgnoreProperties(value = "y", allowSetters = true) + public static class AllowSettersUnknown { + public String name; + } + + @Test + public void allowSettersNameWithoutSetterIsUnknown() { + Mappers m = mappers(b -> b.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + UnrecognizedPropertyException e = assertSameRead(m, "{\"name\":\"n\",\"y\":1}", + AllowSettersUnknown.class).error(UnrecognizedPropertyException.class); + assertEquals("y", e.getPropertyName()); + assertGeneratedReader(m.blackbird(), AllowSettersUnknown.class); + } + + public record IgnoredComponent(int id, @JsonIgnore String name) {} + + @Test + public void ignoredRecordComponentKeepsAbsentValue() { + Mappers m = mappers(); + IgnoredComponent r = assertSameRead(m, "{\"id\":123,\"name\":\"Bob\"}", + IgnoredComponent.class).get(); + assertEquals(new IgnoredComponent(123, null), r); + assertGeneratedReader(m.blackbird(), IgnoredComponent.class); + } + + @Test + public void ignoredRecordComponentFailsWhenIgnoredPropertiesFail() { + Mappers m = mappers(b -> b.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)); + assertSameRead(m, "{\"id\":123,\"name\":\"Bob\"}", IgnoredComponent.class) + .error(tools.jackson.databind.exc.IgnoredPropertyException.class); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/RecordsDiffTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/RecordsDiffTest.java new file mode 100644 index 000000000..fdb35ce19 --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/RecordsDiffTest.java @@ -0,0 +1,300 @@ +package tools.jackson.module.blackbird.diff; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.deser.DeserializationProblemHandler; +import tools.jackson.databind.exc.InvalidNullException; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.exc.ValueInstantiationException; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Record codecs: absent components get the stock absent value, the + * canonical constructor's failures are reported as stock reports them, and + * unknown names are reported against the record once it exists. Every + * scenario's record keeps the generated codec. + */ +public class RecordsDiffTest extends DifferentialTestBase +{ + /* + /********************************************************************** + /* Absent components + /********************************************************************** + */ + + public record OptRec(String a, Optional b) {} + + @Test + public void absentOptionalIsEmpty() { + Mappers m = mappers(); + OptRec r = assertSameRead(m, "{\"a\":\"1\"}", OptRec.class).get(); + assertEquals(new OptRec("1", Optional.empty()), r); + assertGeneratedReader(m.blackbird(), OptRec.class); + } + + @Test + public void absentOptionalPassesFailOnNullCreatorProperties() { + Mappers m = mappers(b -> b.enable(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES)); + OptRec r = assertSameRead(m, "{\"a\":\"1\"}", OptRec.class).get(); + assertEquals(new OptRec("1", Optional.empty()), r); + // An absent String has no non-null absent value: the null fails. + assertSameRead(m, "{\"b\":\"x\"}", OptRec.class).error(MismatchedInputException.class); + assertGeneratedReader(m.blackbird(), OptRec.class); + } + + public record AsEmptyRec(String a, @JsonSetter(nulls = Nulls.AS_EMPTY) String b) {} + + @Test + public void absentAsEmptyComponentIsEmpty() { + Mappers m = mappers(); + AsEmptyRec r = assertSameRead(m, "{\"a\":\"1\"}", AsEmptyRec.class).get(); + assertEquals(new AsEmptyRec("1", ""), r); + assertGeneratedReader(m.blackbird(), AsEmptyRec.class); + } + + public record FailNullsRec(String a, @JsonSetter(nulls = Nulls.FAIL) List b) {} + + @Test + public void absentFailNullsComponentFails() { + Mappers m = mappers(); + assertSameRead(m, "{\"a\":\"1\"}", FailNullsRec.class).error(InvalidNullException.class); + assertGeneratedReader(m.blackbird(), FailNullsRec.class); + } + + public record RequiredRec(@JsonProperty(required = true) String a, int b) {} + + @Test + public void missingRequiredAndFailOnMissing() { + Mappers m = mappers(); + assertSameRead(m, "{\"b\":1}", RequiredRec.class).error(MismatchedInputException.class); + Mappers strict = mappers(b -> + b.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES)); + assertSameRead(strict, "{\"a\":\"x\"}", RequiredRec.class) + .error(MismatchedInputException.class); + assertGeneratedReader(m.blackbird(), RequiredRec.class); + } + + public record Prims(int i, long l, boolean z, double d, String s) {} + + @Test + public void absentPrimitivesAreDefaults() { + Mappers m = mappers(); + assertEquals(new Prims(0, 0L, false, 0.0, null), + assertSameRead(m, "{}", Prims.class).get()); + Mappers strict = mappers(b -> + b.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)); + assertSameRead(strict, "{\"s\":\"x\"}", Prims.class); + assertSameRead(strict, "{\"i\":null}", Prims.class); + assertGeneratedReader(m.blackbird(), Prims.class); + } + + public static class NullInt extends ValueDeserializer { + @Override + public Integer deserialize(JsonParser p, DeserializationContext ctxt) { + p.skipChildren(); + return null; + } + } + + public record NullPrimitiveRec(@JsonDeserialize(using = NullInt.class) int n, String s) {} + + @Test + public void nullFromCustomDeserializerForPrimitiveComponent() { + Mappers m = mappers(); + assertSameRead(m, "{\"n\":5,\"s\":\"x\"}", NullPrimitiveRec.class); + assertSameRead(m, "{\"n\":5,\"n\":6,\"s\":\"x\"}", NullPrimitiveRec.class); + assertGeneratedReader(m.blackbird(), NullPrimitiveRec.class); + } + + /* + /********************************************************************** + /* Constructor failures + /********************************************************************** + */ + + public record Validated(int n, String s) { + public Validated { + if (n < 0) { + throw new IllegalArgumentException("n must be >= 0"); + } + } + } + + @Test + public void constructorFailureIsValueInstantiationException() { + Mappers m = mappers(); + ValueInstantiationException e = assertSameRead(m, "{\"n\":-1,\"s\":\"x\"}", + Validated.class).error(ValueInstantiationException.class); + assertTrue(e.getMessage().contains("n must be >= 0"), e.getMessage()); + // Also when a component is absent (the stock creator constructs). + assertSameRead(m, "{\"n\":-1}", Validated.class) + .error(ValueInstantiationException.class); + assertGeneratedReader(m.blackbird(), Validated.class); + } + + @Test + public void constructorFailureWithoutWrapping() { + Mappers m = mappers(b -> b.disable(DeserializationFeature.WRAP_EXCEPTIONS)); + assertSameRead(m, "{\"n\":-1,\"s\":\"x\"}", Validated.class); + assertSameRead(m, "{\"n\":-1}", Validated.class); + } + + static final class ReplacingHandler extends DeserializationProblemHandler { + @Override + public Object handleInstantiationProblem(DeserializationContext ctxt, Class instClass, + Object argument, Throwable t) { + return (instClass == Validated.class) ? new Validated(0, "replaced") : NOT_HANDLED; + } + } + + @Test + public void problemHandlerReplacesFailedConstruction() { + Mappers m = mappers(b -> b.addHandler(new ReplacingHandler())); + Validated v = assertSameRead(m, "{\"n\":-1,\"s\":\"x\"}", Validated.class).get(); + assertEquals(new Validated(0, "replaced"), v); + assertGeneratedReader(m.blackbird(), Validated.class); + } + + /* + /********************************************************************** + /* JSON names that are not JVM names + /********************************************************************** + */ + + public record Dotted(@JsonProperty("user.name") String userName, + @JsonProperty("a;b/c[d]") int n) {} + + @Test + public void namesThatAreNotJvmNames() { + Mappers m = mappers(); + Dotted d = assertSameRead(m, "{\"user.name\":\"a\",\"a;b/c[d]\":1}", Dotted.class) + .get(); + assertEquals(new Dotted("a", 1), d); + assertGeneratedReader(m.blackbird(), Dotted.class); + } + + /* + /********************************************************************** + /* Unknown names are reported against the record built + /********************************************************************** + */ + + @Test + public void unknownNameReportedAfterConstruction() { + Mappers m = mappers(b -> b.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + // The constructor fails first, as in stock. + assertSameRead(m, "{\"zzz\":1,\"n\":-1,\"s\":\"x\"}", Validated.class) + .error(ValueInstantiationException.class); + // A valid record: the unknown name fails, against the instance. + assertSameRead(m, "{\"zzz\":1,\"n\":1,\"s\":\"x\"}", Validated.class) + .error(tools.jackson.databind.exc.UnrecognizedPropertyException.class); + assertGeneratedReader(m.blackbird(), Validated.class); + } + + static final class RecordingHandler extends DeserializationProblemHandler { + final List seen = new ArrayList<>(); + + @Override + public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, + ValueDeserializer deserializer, Object beanOrClass, String propertyName) { + seen.add(beanOrClass); + p.skipChildren(); + return true; + } + } + + @Test + public void problemHandlerSeesRecordInstance() { + RecordingHandler stock = new RecordingHandler(); + RecordingHandler blackbird = new RecordingHandler(); + Mappers m = new Mappers( + tools.jackson.databind.json.JsonMapper.builder().addHandler(stock).build(), + tools.jackson.databind.json.JsonMapper.builder().addHandler(blackbird) + .addModule(new tools.jackson.module.blackbird.BlackbirdModule()).build()); + Validated v = assertSameRead(m, "{\"zzz\":1,\"n\":1,\"s\":\"x\"}", Validated.class).get(); + assertEquals(List.of(new Validated(1, "x")), stock.seen); + assertEquals(stock.seen, blackbird.seen); + assertEquals(new Validated(1, "x"), v); + assertGeneratedReader(m.blackbird(), Validated.class); + } + + /* + /********************************************************************** + /* Nulls.AS_EMPTY for a record-valued property + /********************************************************************** + */ + + public record Inner(String a, int b) {} + + public record Names(String a, String b) {} + + public static class AsEmptyHolder { + @JsonSetter(nulls = Nulls.AS_EMPTY) + public Names names; + @JsonSetter(nulls = Nulls.AS_EMPTY) + public Inner inner; + public String name; + } + + // The empty value of a record comes from its creator with null + // arguments, which a primitive component cannot take: stock fails then. + @Test + public void asEmptyRecordProperty() { + Mappers m = mappers(); + AsEmptyHolder h = assertSameRead(m, "{\"names\":null,\"name\":\"n\"}", + AsEmptyHolder.class).get(); + assertEquals(new Names(null, null), h.names); + assertSameRead(m, "{\"inner\":null}", AsEmptyHolder.class) + .error(ValueInstantiationException.class); + assertGeneratedReader(m.blackbird(), AsEmptyHolder.class); + assertGeneratedReader(m.blackbird(), Names.class); + assertGeneratedReader(m.blackbird(), Inner.class); + } + + /* + /********************************************************************** + /* Names as stock reads them through the creator + /********************************************************************** + */ + + public record CiRec(@JsonAlias("nm") String name, int count) {} + + @Test + public void caseInsensitiveNamesAndAliases() { + Mappers m = mappers(b -> b.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); + assertEquals(new CiRec("a", 1), + assertSameRead(m, "{\"NAME\":\"a\",\"Count\":1}", CiRec.class).get()); + assertEquals(new CiRec("b", 2), + assertSameRead(m, "{\"NM\":\"b\",\"count\":2}", CiRec.class).get()); + assertGeneratedReader(m.blackbird(), CiRec.class); + } + + public record AnyCreatorRec(String name, + @com.fasterxml.jackson.annotation.JsonAnySetter java.util.Map rest) {} + + @Test + public void anySetterCreatorParameterKeepsStockBean() { + Mappers m = mappers(); + AnyCreatorRec r = assertSameRead(m, "{\"name\":\"n\",\"x\":1}", AnyCreatorRec.class) + .get(); + assertEquals(java.util.Map.of("x", 1), r.rest()); + assertStockReader(m.blackbird(), AnyCreatorRec.class); + assertGeneratedReader(m.blackbird(), Inner.class); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WideBeanTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WideBeanTest.java new file mode 100644 index 000000000..ebf2f357d --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WideBeanTest.java @@ -0,0 +1,134 @@ +package tools.jackson.module.blackbird.diff; + +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.MethodModel; +import java.lang.classfile.attribute.CodeAttribute; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.invoke.MethodHandles; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.module.blackbird.codegen.ClassFileEmitters; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * HotSpot does not JIT-compile a method over 8000 bytes, so a bean whose + * generated method would be larger keeps the stock (de)serializer. The test + * builds beans of growing width, dumps every generated class, and checks + * that each generated method stays within + * {@link ClassFileEmitters#MAX_METHOD_BYTES}, that wide beans do generate up + * to near that limit, and that wider ones are demoted. + */ +public class WideBeanTest extends DifferentialTestBase +{ + @TempDir + static Path dumpDir; + + private static int beans; + + // A public class with n public String fields f0..f(n-1), defined next to + // this test (databind reads and writes public fields). + private static Class wideBean(int n) throws IllegalAccessException { + String name = WideBeanTest.class.getPackageName() + ".Wide" + n + "_" + (beans++); + byte[] bytes = ClassFile.of().build(ClassDesc.of(name), clb -> { + clb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER); + for (int i = 0; i < n; i++) { + clb.withField("f" + i, ConstantDescs.CD_String, ClassFile.ACC_PUBLIC); + } + clb.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, ClassFile.ACC_PUBLIC, + mb -> mb.withCode(cob -> cob.aload(0) + .invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME, + ConstantDescs.MTD_void) + .return_())); + }); + return MethodHandles.lookup().defineClass(bytes); + } + + private static String document(int n) { + StringBuilder sb = new StringBuilder("{"); + for (int i = 0; i < n; i++) { + sb.append(i == 0 ? "" : ",").append("\"f").append(i).append("\":\"v").append(i) + .append('"'); + } + return sb.append('}').toString(); + } + + private static int largestMethod(Path classFile) throws Exception { + int largest = 0; + for (MethodModel m : ClassFile.of().parse(Files.readAllBytes(classFile)).methods()) { + CodeAttribute code = m.findAttribute(Attributes.code()).orElse(null); + if (code != null) { + largest = Math.max(largest, code.codeLength()); + } + } + return largest; + } + + @Test + public void generatedMethodsStayWithinTheLimit() throws Exception { + System.setProperty("blackbird.debug.dumpDir", dumpDir.toString()); + List readers = new ArrayList<>(); + int widestReader = 0; + int largestReader = 0; + int largestWriter = 0; + try { + for (int n = 20; n <= 260; n += 20) { + Class type = wideBean(n); + Mappers m = mappers(); + String json = document(n); + Object value = assertSameRead(m, json, type).get(); + assertSameWrite(m, value); + + ValueDeserializer reader = rootReader(m.blackbird(), type); + Path readerDump = dumpDir.resolve(type.getName() + "-reader.class"); + if (reader instanceof GeneratedReaderBase) { + int size = largestMethod(readerDump); + assertTrue(size <= ClassFileEmitters.MAX_METHOD_BYTES, n + ": " + size); + widestReader = n; + largestReader = Math.max(largestReader, size); + readers.add(n + "=" + size); + } else { + assertFalse(Files.exists(readerDump), "a demoted reader is not dumped"); + } + ValueSerializer writer = rootWriter(m.blackbird(), type); + if (writer instanceof GeneratedWriterBase) { + int size = largestMethod(dumpDir.resolve(type.getName() + "-writer.class")); + assertTrue(size <= ClassFileEmitters.MAX_METHOD_BYTES, n + ": " + size); + largestWriter = Math.max(largestWriter, size); + } + } + } finally { + System.clearProperty("blackbird.debug.dumpDir"); + } + // Wide beans do generate, up to near the limit, and the widest ones + // keep the stock reader. + assertTrue(widestReader >= 60, "readers: " + readers); + assertTrue(widestReader < 260, "readers: " + readers); + assertTrue(largestReader > ClassFileEmitters.MAX_METHOD_BYTES - 1000, + "readers: " + readers); + assertTrue(largestWriter > 0); + } + + @Test + public void wideBeanKeepsStockReader() throws Exception { + Class type = wideBean(260); + Mappers m = mappers(); + assertSameRead(m, document(260), type); + assertStockReader(m.blackbird(), type); + Class narrow = wideBean(20); + assertSameRead(m, document(20), narrow); + assertGeneratedReader(m.blackbird(), narrow); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WriterDiffTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WriterDiffTest.java new file mode 100644 index 000000000..cd88c5c0f --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WriterDiffTest.java @@ -0,0 +1,310 @@ +package tools.jackson.module.blackbird.diff; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonApplyView; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonView; + +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.StreamWriteConstraints; +import tools.jackson.core.exc.StreamConstraintsException; +import tools.jackson.core.json.JsonFactory; +import tools.jackson.databind.DatabindException; +import tools.jackson.databind.DefaultTyping; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.SerializationFeature; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.annotation.JsonSerialize; +import tools.jackson.databind.exc.InvalidDefinitionException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import tools.jackson.databind.module.SimpleModule; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Generated writers: values with type serializers and non-default null + * serializers stay with the stock property writer, a value that is the bean + * itself gets stock self-reference handling, applied views apply, and + * failures carry the stock exception, message and reference path. + */ +public class WriterDiffTest extends DifferentialTestBase +{ + public static class Plain { + public String name = "p"; + public int count = 1; + } + + /* + /********************************************************************** + /* Type serializers, null serializers, applied views + /********************************************************************** + */ + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@t") + @JsonSubTypes({ @JsonSubTypes.Type(value = TypedRec.class, name = "rec") }) + public record TypedRec(String v) {} + + public static class HoldsTypedRec { + public String name = "h"; + public TypedRec rec = new TypedRec("x"); + } + + @Test + public void childWithTypeInfoWritesTypeId() { + Mappers m = mappers(); + String json = assertSameWrite(m, new HoldsTypedRec()).get(); + assertTrue(json.contains("\"@t\":\"rec\""), json); + String layout = assertGeneratedWriter(m.blackbird(), HoldsTypedRec.class); + assertArm(layout, "rec=STOCK"); + assertArm(layout, "name=STRING"); + } + + public record PlainRec(String v) {} + + public static class HoldsRecord { + public PlainRec rec = new PlainRec("x"); + public String name = "h"; + } + + @Test + public void defaultTypingWritesTypeIds() { + Mappers m = mappers(b -> b.activateDefaultTypingAsProperty( + BasicPolymorphicTypeValidator.builder().allowIfBaseType(Object.class).build(), + DefaultTyping.NON_FINAL_AND_RECORDS, "@class")); + String json = assertSameWrite(m, new HoldsRecord()).get(); + assertTrue(json.contains("\"@class\""), json); + assertArm(assertGeneratedWriter(m.blackbird(), HoldsRecord.class), "rec=STOCK"); + } + + public static class EmptyForNull extends ValueSerializer { + @Override + public void serialize(Object value, JsonGenerator g, SerializationContext ctxt) { + g.writeString(""); + } + } + + public static class NullsUsing { + @JsonSerialize(nullsUsing = EmptyForNull.class) + public String a; + public String b; + } + + @Test + public void propertyNullSerializer() { + Mappers m = mappers(); + assertEquals("{\"a\":\"\",\"b\":null}", assertSameWrite(m, new NullsUsing()).get()); + String layout = assertGeneratedWriter(m.blackbird(), NullsUsing.class); + assertArm(layout, "a=STOCK"); + assertArm(layout, "b=STRING"); + } + + // Final value types get statically assigned serializers, so without the + // null-serializer gate a generated child arm would write them. + public static final class FinalKid { + public String name = "k"; + } + + public static class HoldsChild { + public FinalKid kid; + public String s; + public int n = 3; + } + + @Test + public void defaultNullSerializer() { + Mappers m = mappers(b -> b.addModule(new SimpleModule("nulls") + .setDefaultNullValueSerializer(new EmptyForNull()))); + assertEquals("{\"kid\":\"\",\"n\":3,\"s\":\"\"}", + assertSameWrite(m, new HoldsChild()).get()); + String layout = assertGeneratedWriter(m.blackbird(), HoldsChild.class); + assertArm(layout, "kid=STOCK"); + assertArm(layout, "s=STOCK"); + // Primitives are never null: they keep the generated write. + assertArm(layout, "n=INT"); + } + + public static class ViewA { } + public static class ViewB { } + + public static final class Viewed { + @JsonView(ViewA.class) + public String a = "1"; + @JsonView(ViewB.class) + public String b = "2"; + } + + public static class AppliesView { + @JsonView(ViewA.class) + @JsonApplyView(ViewB.class) + public Viewed inner = new Viewed(); + @JsonView(ViewA.class) + public Viewed plain = new Viewed(); + } + + @Test + public void appliedViewApplies() { + Mappers m = mappers(); + Outcome stock = new Outcome(m.stock().writerWithView(ViewA.class) + .writeValueAsString(new AppliesView()), null); + Outcome blackbird = new Outcome(m.blackbird().writerWithView(ViewA.class) + .writeValueAsString(new AppliesView()), null); + assertEquals(stock.describe(), blackbird.describe()); + assertEquals("{\"inner\":{\"b\":\"2\"},\"plain\":{\"a\":\"1\"}}", blackbird.get()); + String layout = assertGeneratedWriter(m.blackbird(), AppliesView.class); + assertArm(layout, "inner=STOCK"); + assertArm(layout, "plain=CHILD"); + } + + /* + /********************************************************************** + /* Failures and the reference path + /********************************************************************** + */ + + public static final class ThrowingGetter { + public String a = "x"; + + public String getB() { + throw new IllegalStateException("getter boom"); + } + } + + public static final class Inner { + public ThrowingGetter tg = new ThrowingGetter(); + } + + public static class Outer { + public Inner inner = new Inner(); + } + + @Test + public void getterFailureCarriesReferencePath() { + Mappers m = mappers(); + DatabindException e = assertSameWrite(m, new ThrowingGetter()) + .error(DatabindException.class); + assertTrue(e.getMessage().contains("getter boom"), e.getMessage()); + assertTrue(e.getMessage().contains("ThrowingGetter[\"b\"]"), e.getMessage()); + DatabindException nested = assertSameWrite(m, new Outer()) + .error(DatabindException.class); + assertTrue(nested.getMessage().contains("Outer[\"inner\"]->"), nested.getMessage()); + assertGeneratedWriter(m.blackbird(), ThrowingGetter.class); + assertArm(assertGeneratedWriter(m.blackbird(), Outer.class), "inner=CHILD"); + } + + @Test + public void getterFailureWithoutWrapping() { + Mappers m = mappers(b -> b.disable(SerializationFeature.WRAP_EXCEPTIONS)); + assertSameWrite(m, new ThrowingGetter()).error(IllegalStateException.class); + assertSameWrite(m, new Outer()).error(IllegalStateException.class); + } + + public static class CycA { + public String name = "a"; + public CycB b; + } + + public static class CycB { + public String name = "b"; + public CycA a; + } + + private static CycA cycle() { + CycA a = new CycA(); + CycB b = new CycB(); + a.b = b; + b.a = a; + return a; + } + + @Test + public void cycleHitsNestingLimitWithStockPath() { + Mappers m = mappers(); + StreamConstraintsException e = assertSameWrite(m, cycle()) + .error(StreamConstraintsException.class); + assertTrue(e.getMessage().contains("CycA[\"b\"]->"), e.getMessage()); + assertTrue(e.getMessage().contains("CycB[\"a\"]->"), e.getMessage()); + assertArm(assertGeneratedWriter(m.blackbird(), CycA.class), "name=STRING"); + } + + @Test + public void infiniteRecursion() { + // A class first initialized near the overflow would fail to + // initialize for good, so the exception paths run once up front. + assertSameWrite(mappers(), new ThrowingGetter()); + assertSameWrite(mappers(), cycle()); + // No nesting limit: the stack overflows first. + JsonFactory unlimited = JsonFactory.builder() + .streamWriteConstraints(StreamWriteConstraints.builder() + .maxNestingDepth(Integer.MAX_VALUE).build()) + .build(); + Mappers m = new Mappers(JsonMapper.builder(unlimited).build(), + JsonMapper.builder(unlimited) + .addModule(new tools.jackson.module.blackbird.BlackbirdModule()).build()); + Outcome stock = write(m.stock(), cycle()); + Outcome blackbird = write(m.blackbird(), cycle()); + DatabindException se = stock.error(DatabindException.class); + DatabindException be = blackbird.error(DatabindException.class); + assertTrue(se.getMessage().startsWith("Infinite recursion (StackOverflowError)"), + se.getMessage()); + // The overflow depth differs between the paths, and databind keeps a + // bounded number of path entries, so the chains are cut at different + // points of the same alternation. + assertEquals(head(se.getMessage()), head(be.getMessage())); + assertEquals(cycleEntries(), Set.copyOf(chain(se.getMessage()))); + assertEquals(cycleEntries(), Set.copyOf(chain(be.getMessage()))); + } + + private static final String CHAIN_START = "(through reference chain: "; + + private static String head(String message) { + return message.substring(0, message.indexOf(CHAIN_START)); + } + + private static List chain(String message) { + String chain = message.substring(message.indexOf(CHAIN_START) + CHAIN_START.length(), + message.lastIndexOf(')')); + return Arrays.stream(chain.split("->")).filter(e -> !e.equals("...")).toList(); + } + + private static Set cycleEntries() { + return Set.of(CycA.class.getName() + "[\"b\"]", CycB.class.getName() + "[\"a\"]"); + } + + /* + /********************************************************************** + /* Self-references + /********************************************************************** + */ + + public static class Selfie { + public String name = "s"; + + public Selfie getSelf() { + return this; + } + } + + @Test + public void directSelfReferenceFails() { + Mappers m = mappers(); + InvalidDefinitionException e = assertSameWrite(m, new Selfie()) + .error(InvalidDefinitionException.class); + assertTrue(e.getMessage().startsWith("Direct self-reference leading to cycle"), + e.getMessage()); + assertGeneratedWriter(m.blackbird(), Selfie.class); + } + + @Test + public void directSelfReferenceAsNull() { + Mappers m = mappers(b -> b.disable(SerializationFeature.FAIL_ON_SELF_REFERENCES) + .enable(SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL)); + assertEquals("{\"name\":\"s\",\"self\":null}", assertSameWrite(m, new Selfie()).get()); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/BlackbirdInjectionTestBase.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/BlackbirdInjectionTestBase.java index 1f3575043..77072cdd9 100644 --- a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/BlackbirdInjectionTestBase.java +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/BlackbirdInjectionTestBase.java @@ -1,180 +1,55 @@ package tools.jackson.module.blackbird.inject; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import tools.jackson.databind.BeanDescription; -import tools.jackson.databind.DeserializationConfig; -import tools.jackson.databind.SerializationConfig; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.SerializationContext; import tools.jackson.databind.ValueDeserializer; import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.deser.SettableBeanProperty; -import tools.jackson.databind.deser.ValueDeserializerModifier; -import tools.jackson.databind.deser.bean.BeanDeserializer; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.module.SimpleModule; -import tools.jackson.databind.ser.BeanPropertyWriter; -import tools.jackson.databind.ser.ValueSerializerModifier; import tools.jackson.module.blackbird.BlackbirdModule; - -import static org.junit.jupiter.api.Assertions.assertNotNull; - -// Test utilities for verifying that Blackbird's lambda-based injection pipeline -// actually ran on a given POJO. Because Blackbird's optimized property and writer -// classes (SettableIntProperty, etc.; IntPropertyWriter, etc.) are package-private -// inside the blackbird module, these checks rely on reflection and simple-name -// matching rather than compile-time type references. Mirrors the afterburner-tests -// harness; see that module's README for the broader rationale. +import tools.jackson.module.blackbird.internal.GeneratedBuilderBase; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; + +// Test utilities for verifying that Blackbird's codec generation engaged for a +// given POJO loaded from the unnamed module (classpath). The checks look at +// the (de)serializer the mapper uses for the type: a generated codec, or the +// stock one when a gate demoted the bean. (A modifier would only see the +// placeholder that stands in for either until resolution.) abstract class BlackbirdInjectionTestBase { + static { + // Strict mode: a codec-generation failure fails the test instead of + // demoting to the stock path (see CodegenFallbacks). + System.setProperty("tools.jackson.module.blackbird.failOnCodegenError", "true"); + } + protected static Harness newHarness() { return new Harness(); } protected static final class Harness { - private final ConcurrentMap, ValueDeserializer> desers = new ConcurrentHashMap<>(); - private final ConcurrentMap, ValueSerializer> sers = new ConcurrentHashMap<>(); - final JsonMapper mapper; - - Harness() { - SimpleModule capture = new SimpleModule("capture") { - private static final long serialVersionUID = 1L; - @Override - public void setupModule(SetupContext ctxt) { - super.setupModule(ctxt); - ctxt.addDeserializerModifier(new ValueDeserializerModifier() { - private static final long serialVersionUID = 1L; - @Override - public ValueDeserializer modifyDeserializer( - DeserializationConfig cfg, BeanDescription.Supplier ref, - ValueDeserializer d) { - desers.put(ref.getBeanClass(), d); - return d; - } - }); - ctxt.addSerializerModifier(new ValueSerializerModifier() { - private static final long serialVersionUID = 1L; - @Override - public ValueSerializer modifySerializer( - SerializationConfig cfg, BeanDescription.Supplier ref, - ValueSerializer s) { - sers.put(ref.getBeanClass(), s); - return s; - } - }); - } - }; - this.mapper = JsonMapper.builder() - .addModule(new BlackbirdModule()) - .addModule(capture) - .build(); - } + final JsonMapper mapper = JsonMapper.builder() + .addModule(new BlackbirdModule()) + .build(); ValueDeserializer deserFor(Class cls) { - ValueDeserializer d = desers.get(cls); - assertNotNull(d, "no deserializer captured for " + cls.getName()); - return d; + DeserializationContext ctxt = mapper._deserializationContext(); + return ctxt.findContextualValueDeserializer(ctxt.constructType(cls), null); } ValueSerializer serFor(Class cls) { - ValueSerializer s = sers.get(cls); - assertNotNull(s, "no serializer captured for " + cls.getName()); - return s; - } - } - - /** Returns the `_propsByIndex` array from a bean deserializer. */ - protected static SettableBeanProperty[] propsOf(ValueDeserializer deser) { - if (!(deser instanceof BeanDeserializer)) { - throw new AssertionError("not a BeanDeserializer: " + deser.getClass().getName()); + SerializationContext ctxt = mapper._serializationContext(); + return ctxt.handleRootContextualization(ctxt.findValueSerializer(cls)); } - return (SettableBeanProperty[]) reflectField(deser, "_propsByIndex"); } - /** Returns the BeanPropertyWriter[] from a bean serializer, as a list. */ - protected static List writersOf(ValueSerializer ser) { - BeanPropertyWriter[] arr = (BeanPropertyWriter[]) reflectField(ser, "_props"); - List out = new ArrayList<>(arr.length); - for (BeanPropertyWriter w : arr) { - out.add(w); - } - return out; + /** True if the deserializer is a generated Blackbird codec. */ + protected static boolean isBlackbirdReader(ValueDeserializer deser) { + return (deser instanceof GeneratedReaderBase) || (deser instanceof GeneratedBuilderBase); } - /** Walks the class chain of {@code instance} looking for a declared field - * named {@code fieldName}. Picks the error message hypothesis based on the - * class's package — databind/blackbird fields usually mean a rename, - * anything else means the caller passed the wrong receiver. */ - protected static Object reflectField(Object instance, String fieldName) { - Class origClass = instance.getClass(); - Class c = origClass; - while (c != null) { - try { - Field f = c.getDeclaredField(fieldName); - f.setAccessible(true); - return f.get(instance); - } catch (NoSuchFieldException ignore) { - c = c.getSuperclass(); - } catch (IllegalAccessException e) { - throw new AssertionError("cannot read field '" + fieldName + "' on " - + origClass.getName(), e); - } - } - String pkg = origClass.getPackageName(); - String hint; - if (pkg.startsWith("tools.jackson.databind") - || pkg.startsWith("tools.jackson.module.blackbird")) { - hint = "databind or blackbird may have renamed or removed it;" - + " update " + BlackbirdInjectionTestBase.class.getSimpleName() - + " to match."; - } else { - hint = "this looks like the wrong receiver type — '" + fieldName - + "' is an internal Jackson field and the caller passed an" - + " instance of " + origClass.getName() + "."; - } - throw new AssertionError("field '" + fieldName + "' not found on " - + origClass.getName() + " (walked up full class chain) — " + hint); - } - - /** True if `prop`'s class chain contains Blackbird's OptimizedSettableBeanProperty. */ - protected static boolean isOptimizedProperty(SettableBeanProperty prop) { - return blackbirdClassChainIncludes(prop.getClass(), "OptimizedSettableBeanProperty"); - } - - /** True if `writer`'s class chain contains Blackbird's OptimizedBeanPropertyWriter. */ - protected static boolean isOptimizedWriter(BeanPropertyWriter writer) { - return blackbirdClassChainIncludes(writer.getClass(), "OptimizedBeanPropertyWriter"); - } - - /** Walks the superclass chain of {@code cls} looking for a class whose simple - * name is {@code simpleName}. Used to recognize Blackbird's package-private - * optimized types without importing them. */ - protected static boolean classChainIncludes(Class cls, String simpleName) { - Class c = cls; - while (c != null) { - if (simpleName.equals(c.getSimpleName())) { - return true; - } - c = c.getSuperclass(); - } - return false; - } - - /** Like {@link #classChainIncludes} but additionally requires the matched - * class to live inside a blackbird package. Guards against false positives - * from unrelated classes that happen to share a simple name. */ - protected static boolean blackbirdClassChainIncludes(Class cls, String simpleName) { - Class c = cls; - while (c != null) { - if (simpleName.equals(c.getSimpleName()) - && c.getPackageName().startsWith("tools.jackson.module.blackbird")) { - return true; - } - c = c.getSuperclass(); - } - return false; + /** True if the serializer is a generated Blackbird writer. */ + protected static boolean isBlackbirdWriter(ValueSerializer ser) { + return ser instanceof GeneratedWriterBase; } } diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/CrossLoaderAccessTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/CrossLoaderAccessTest.java deleted file mode 100644 index b775e7599..000000000 --- a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/CrossLoaderAccessTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package tools.jackson.module.blackbird.inject; - -import org.junit.jupiter.api.Test; - -import tools.jackson.databind.deser.SettableBeanProperty; - -import static org.junit.jupiter.api.Assertions.*; - -// Documents a finding about Blackbird's CrossLoaderAccess code path: -// on JDK 9+ with a bean in the unnamed module (classpath), the companion -// `$$JacksonBlackbirdAccess` class is **never** defined. -// -// Why: BBDeserializerModifier.updateBuilder obtains a lookup via -// MethodHandles.lookup() inside BlackbirdModule (full privilege on blackbird's -// own class), then calls ReflectionHack.privateLookupIn(beanClass, lookup). -// For an unnamed-module bean, privateLookupIn always succeeds and returns a -// lookup with hasFullPrivilegeAccess() == true over the bean's package. Then -// CrossLoaderAccess.grantAccess short-circuits on `hasFullAccess(lookup)` and -// returns the lookup unchanged, skipping the slow path that would define a -// $$JacksonBlackbirdAccess companion class via Lookup.defineClass(byte[]). -// -// That slow path exists for historical / edge-case scenarios (JDK 8 when -// DEFINE_CLASS is null, or a future JDK change that lowers privileges on -// privateLookupIn). It is not exercised by either Blackbird's in-tree test -// suite (same-module case is also full-privilege) nor by this classpath -// test module. -// -// This test pins the current behavior: the fast path must win, and the -// companion class must not be introduced as a side effect of deserialization. -// If Blackbird's CrossLoaderAccess logic changes such that the slow path -// starts firing for classpath POJOs, this test will catch it — at which -// point the maintainer should decide whether that's intended (and update the -// assertion) or accidental (and revert). -public class CrossLoaderAccessTest extends BlackbirdInjectionTestBase -{ - public static class XLoaderBean { - private int a; - private String b; - public int getA() { return a; } - public void setA(int a) { this.a = a; } - public String getB() { return b; } - public void setB(String b) { this.b = b; } - } - - private static final String COMPANION_CLASS_NAME = - XLoaderBean.class.getPackage().getName() + ".$$JacksonBlackbirdAccess"; - - @Test - public void testFastPathWins_NoCompanionClassDefined() throws Exception - { - Harness h = newHarness(); - - // Trigger Blackbird's modifier chain for XLoaderBean. - XLoaderBean bean = h.mapper.readValue("{\"a\":1,\"b\":\"hi\"}", XLoaderBean.class); - assertEquals(1, bean.getA()); - assertEquals("hi", bean.getB()); - - // Optimization still ran — setters are Blackbird-optimized. - SettableBeanProperty[] props = propsOf(h.deserFor(XLoaderBean.class)); - assertEquals(2, props.length); - for (SettableBeanProperty p : props) { - assertTrue(isOptimizedProperty(p), - "XLoaderBean." + p.getName() + " not optimized: " - + p.getClass().getName()); - } - - // But the $$JacksonBlackbirdAccess companion class must NOT have been - // defined — CrossLoaderAccess.grantAccess short-circuits for a lookup - // with hasFullPrivilegeAccess() == true, which is what privateLookupIn - // returns for an unnamed-module target. - assertFalse(companionClassExists(), - "CrossLoaderAccess unexpectedly defined " + COMPANION_CLASS_NAME - + " for an unnamed-module bean — the grantAccess fast path" - + " should have short-circuited. If Blackbird has started" - + " taking the slow path (e.g. because privateLookupIn" - + " semantics changed), update this test."); - } - - private static boolean companionClassExists() { - try { - Class.forName(COMPANION_CLASS_NAME, false, - XLoaderBean.class.getClassLoader()); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } -} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessNotOptimizedTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessNotOptimizedTest.java deleted file mode 100644 index f36ee1cf9..000000000 --- a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessNotOptimizedTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package tools.jackson.module.blackbird.inject; - -import org.junit.jupiter.api.Test; - -import tools.jackson.databind.deser.SettableBeanProperty; - -import static org.junit.jupiter.api.Assertions.*; - -// Documents a known design limitation of Blackbird: direct public-field access -// is NOT optimized. BBDeserializerModifier.nextProperty only handles properties -// whose backing JDK member is a Method (setter); if it's a Field, the method -// returns early and the property is left as a plain FieldProperty. Afterburner -// optimizes both setter and field access; Blackbird deliberately doesn't. -// -// The point of this test is to pin that contract. If Blackbird ever grows -// field-access support, this test will start failing — which is the correct -// signal to update it. -public class FieldAccessNotOptimizedTest extends BlackbirdInjectionTestBase -{ - public static class FieldOnlyBean { - public int intField; - public long longField; - public boolean boolField; - public String stringField; - } - - private final Harness h = newHarness(); - - @Test - public void testFieldPropsAreNotReplacedWithOptimizedVersions() throws Exception - { - // End-to-end deserialization must still work — Blackbird just delegates - // to databind's plain FieldProperty for these. - FieldOnlyBean bean = h.mapper.readValue( - "{\"intField\":1,\"longField\":2,\"boolField\":true,\"stringField\":\"x\"}", - FieldOnlyBean.class); - assertEquals(1, bean.intField); - assertEquals(2L, bean.longField); - assertTrue(bean.boolField); - assertEquals("x", bean.stringField); - - // None of the properties should be Blackbird-optimized. - SettableBeanProperty[] props = propsOf(h.deserFor(FieldOnlyBean.class)); - assertEquals(4, props.length); - for (SettableBeanProperty p : props) { - assertFalse(isOptimizedProperty(p), - "Blackbird unexpectedly optimized field property '" + p.getName() - + "' (is " + p.getClass().getName() + "). If Blackbird" - + " has grown field-access support, update this test to" - + " assert the positive case instead."); - } - } -} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessTest.java new file mode 100644 index 000000000..91365b579 --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessTest.java @@ -0,0 +1,37 @@ +package tools.jackson.module.blackbird.inject; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +// Public-field access is generated: a bean whose properties are all public +// fields engages a Blackbird codec that stores through putfield (read side) and +// getfield (write side), the same as a setter/getter POJO. Values must match +// stock databind. +public class FieldAccessTest extends BlackbirdInjectionTestBase +{ + public static class FieldOnlyBean { + public int intField; + public long longField; + public boolean boolField; + public String stringField; + } + + private final Harness h = newHarness(); + + @Test + public void testFieldBackedBeanEngagesCodec() throws Exception + { + FieldOnlyBean bean = h.mapper.readValue( + "{\"intField\":1,\"longField\":2,\"boolField\":true,\"stringField\":\"x\"}", + FieldOnlyBean.class); + assertEquals(1, bean.intField); + assertEquals(2L, bean.longField); + assertTrue(bean.boolField); + assertEquals("x", bean.stringField); + + assertTrue(isBlackbirdReader(h.deserFor(FieldOnlyBean.class)), + "FieldOnlyBean did not engage a Blackbird codec: " + + h.deserFor(FieldOnlyBean.class).getClass().getName()); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SerializerInjectionTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SerializerInjectionTest.java index 41f6ada69..431480fab 100644 --- a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SerializerInjectionTest.java +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SerializerInjectionTest.java @@ -1,19 +1,15 @@ package tools.jackson.module.blackbird.inject; -import java.util.List; - import org.junit.jupiter.api.Test; -import tools.jackson.databind.ser.BeanPropertyWriter; +import tools.jackson.databind.json.JsonMapper; import static org.junit.jupiter.api.Assertions.*; -// Verifies Blackbird's serializer-side optimization runs end-to-end for all -// accessor-method specializations, and documents the parallel limitation with -// the deserializer side: direct public-field writers are not optimized either. -// BBSerializerModifier.createProperty skips properties whose backing JDK -// member isn't an AnnotatedMethod (see BBSerializerModifier.java lines -// 103-109), so public fields stay as plain BeanPropertyWriter instances. +// Verifies Blackbird's serializer codec generation engages end-to-end on the +// classpath and produces output identical to stock databind, for a getter POJO +// and for a public-field POJO (whose values are read through a generated +// getfield). public class SerializerInjectionTest extends BlackbirdInjectionTestBase { public static class GetterSerPojo { @@ -44,44 +40,32 @@ public FieldSerPojo() { } } @Test - public void testAllGetterBasedWritersOptimized() throws Exception + public void testGetterPojoEngagesGeneratedWriter() throws Exception { Harness h = newHarness(); + GetterSerPojo pojo = new GetterSerPojo(1, 2L, true, "x", java.util.Arrays.asList("a", "b")); - String json = h.mapper.writeValueAsString( - new GetterSerPojo(1, 2L, true, "x", java.util.Arrays.asList("a", "b"))); - assertTrue(json.contains("\"intProp\":1"), json); - assertTrue(json.contains("\"longProp\":2"), json); - assertTrue(json.contains("\"boolProp\":true"), json); - assertTrue(json.contains("\"stringProp\":\"x\""), json); - assertTrue(json.contains("\"objectProp\":[\"a\",\"b\"]"), json); + String json = h.mapper.writeValueAsString(pojo); + assertEquals(new JsonMapper().writeValueAsString(pojo), json, + "generated writer output differs from stock databind"); - List writers = writersOf(h.serFor(GetterSerPojo.class)); - assertEquals(5, writers.size(), "expected 5 writers, got " + writers); - for (BeanPropertyWriter w : writers) { - assertTrue(isOptimizedWriter(w), - "ser writer '" + w.getName() + "' not optimized (is " - + w.getClass().getName() + "); BBSerializerModifier" - + " did not replace it with a Blackbird-generated writer"); - } + assertTrue(isBlackbirdWriter(h.serFor(GetterSerPojo.class)), + "GetterSerPojo did not engage a Blackbird writer codec: " + + h.serFor(GetterSerPojo.class).getClass().getName()); } @Test - public void testFieldBackedWriterNotOptimized() throws Exception + public void testFieldBackedWriterEngagesCodec() throws Exception { Harness h = newHarness(); + FieldSerPojo pojo = new FieldSerPojo(42); - // Serialization still works end-to-end, just not via a Blackbird-generated accessor. - String json = h.mapper.writeValueAsString(new FieldSerPojo(42)); - assertTrue(json.contains("\"value\":42"), json); + String json = h.mapper.writeValueAsString(pojo); + assertEquals(new JsonMapper().writeValueAsString(pojo), json, + "field-backed output differs from stock databind"); - List writers = writersOf(h.serFor(FieldSerPojo.class)); - assertEquals(1, writers.size()); - BeanPropertyWriter w = writers.get(0); - assertFalse(isOptimizedWriter(w), - "Blackbird unexpectedly optimized a field-backed writer '" + w.getName() - + "' (is " + w.getClass().getName() + "). If BBSerializerModifier" - + " has grown field-access support, update this test to assert" - + " the positive case instead."); + assertTrue(isBlackbirdWriter(h.serFor(FieldSerPojo.class)), + "FieldSerPojo did not engage a Blackbird writer codec: " + + h.serFor(FieldSerPojo.class).getClass().getName()); } } diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SetterOptimizationTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SetterOptimizationTest.java index 93db3d552..05fa1f8d1 100644 --- a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SetterOptimizationTest.java +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/SetterOptimizationTest.java @@ -2,17 +2,12 @@ import org.junit.jupiter.api.Test; -import tools.jackson.databind.deser.SettableBeanProperty; - import static org.junit.jupiter.api.Assertions.*; -// End-to-end verification that Blackbird's setter optimization runs on POJOs -// loaded from the unnamed module (classpath). The specializations tested here -// correspond to BBDeserializerModifier's four primitive branches (int, long, -// boolean, object/String) plus the object/non-String branch. -// -// Note: Blackbird does NOT optimize direct public-field access — only setter -// methods. See FieldAccessNotOptimizedTest for the documented negative case. +// End-to-end verification that Blackbird's codec generation engages for setter +// POJOs loaded from the unnamed module (classpath). The beans cover the +// generator's scalar kinds (int, long, boolean, String) plus a non-scalar +// setter that rides the codec's stock-property arm. public class SetterOptimizationTest extends BlackbirdInjectionTestBase { public static class IntSetterBean { @@ -47,28 +42,28 @@ public static class ObjectSetterBean { public void testIntSetter() throws Exception { IntSetterBean b = h.mapper.readValue("{\"value\":42}", IntSetterBean.class); assertEquals(42, b.getValue()); - assertAllPropsOptimized(IntSetterBean.class); + assertCodecEngaged(IntSetterBean.class); } @Test public void testLongSetter() throws Exception { LongSetterBean b = h.mapper.readValue("{\"value\":9999999999}", LongSetterBean.class); assertEquals(9999999999L, b.getValue()); - assertAllPropsOptimized(LongSetterBean.class); + assertCodecEngaged(LongSetterBean.class); } @Test public void testBooleanSetter() throws Exception { BooleanSetterBean b = h.mapper.readValue("{\"value\":true}", BooleanSetterBean.class); assertTrue(b.isValue()); - assertAllPropsOptimized(BooleanSetterBean.class); + assertCodecEngaged(BooleanSetterBean.class); } @Test public void testStringSetter() throws Exception { StringSetterBean b = h.mapper.readValue("{\"value\":\"hi\"}", StringSetterBean.class); assertEquals("hi", b.getValue()); - assertAllPropsOptimized(StringSetterBean.class); + assertCodecEngaged(StringSetterBean.class); } @Test @@ -76,17 +71,12 @@ public void testObjectSetter() throws Exception { ObjectSetterBean b = h.mapper.readValue("{\"value\":[\"a\",\"b\"]}", ObjectSetterBean.class); assertEquals(2, b.getValue().size()); assertEquals("a", b.getValue().get(0)); - assertAllPropsOptimized(ObjectSetterBean.class); + assertCodecEngaged(ObjectSetterBean.class); } - private void assertAllPropsOptimized(Class cls) { - SettableBeanProperty[] props = propsOf(h.deserFor(cls)); - assertTrue(props.length > 0, "no properties for " + cls.getSimpleName()); - for (SettableBeanProperty p : props) { - assertTrue(isOptimizedProperty(p), - cls.getSimpleName() + "." + p.getName() + " not optimized: " - + p.getClass().getName() - + " — BBDeserializerModifier did not install a SettableXProperty"); - } + private void assertCodecEngaged(Class cls) { + assertTrue(isBlackbirdReader(h.deserFor(cls)), + cls.getSimpleName() + " did not engage a Blackbird codec: " + + h.deserFor(cls).getClass().getName()); } } diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/IsolatedLoaderTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/IsolatedLoaderTest.java new file mode 100644 index 000000000..eebc7e69e --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/IsolatedLoaderTest.java @@ -0,0 +1,182 @@ +package tools.jackson.module.blackbird.jdk; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Label; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.Field; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.codegen.CodegenFallbacks; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The ClassFile API computes stack map frames with a class hierarchy + * resolver. Its default resolver reads class files through the system class + * loader, which does not see Jackson when an application server, a plugin + * host or OSGi loads Jackson and Blackbird in a loader of their own. This + * test runs a child JVM whose class path has only the test classes, and + * loads Jackson and Blackbird there in a loader whose parent is the platform + * loader. + */ +public class IsolatedLoaderTest +{ + public static class Child { + public String name; + public int rank; + } + + public static class Parent { + public String id; + public Child child; + public List children; + public long count; + public boolean flag; + } + + public record Rec(String name, int count, Child child) {} + + @TempDir + Path dir; + + /** Runs inside the isolated loader. */ + public static class Probe { + public static List run() throws Exception { + System.setProperty(CodegenFallbacks.FAIL_ON_ERROR_PROPERTY, "true"); + List out = new ArrayList<>(); + out.add("isolated " + (JsonMapper.class.getClassLoader() == Probe.class.getClassLoader())); + Field field = Class.forName("tools.jackson.module.blackbird.codegen.BeanReaderGenerator") + .getDeclaredField("CLASS_FILE"); + field.setAccessible(true); + out.add("default resolver " + mergeFrame(ClassFile.of()) + + ", generators " + mergeFrame((ClassFile) field.get(null))); + + ObjectMapper mapper = JsonMapper.builder().addModule(new BlackbirdModule()).build(); + Parent p = mapper.readValue("{\"id\":\"p\",\"child\":{\"name\":\"c\",\"rank\":2}," + + "\"children\":[{\"name\":\"d\",\"rank\":3}],\"count\":4,\"flag\":true}", + Parent.class); + out.add(mapper.writeValueAsString(p)); + Rec r = mapper.readValue("{\"name\":\"r\",\"count\":5,\"child\":{\"name\":\"e\"}}", + Rec.class); + out.add(mapper.writeValueAsString(r)); + DeserializationContext dc = mapper._deserializationContext(); + SerializationContext sc = mapper._serializationContext(); + for (Class type : List.of(Parent.class, Child.class, Rec.class)) { + boolean reader = dc.findRootValueDeserializer(dc.constructType(type)) + instanceof GeneratedReaderBase; + boolean writer = sc.handleRootContextualization(sc.findValueSerializer(type)) + instanceof GeneratedWriterBase; + out.add(type.getSimpleName() + " reader=" + reader + " writer=" + writer); + } + return out; + } + + // Two branches leave a GeneratedReaderBase and a BeanDeserializer on + // the stack. The frame where they join needs their common superclass, + // so the resolver must find both classes. + private static String mergeFrame(ClassFile classFile) { + ClassDesc reader = ClassDesc.of(GeneratedReaderBase.class.getName()); + ClassDesc stock = ClassDesc.of(BeanDeserializer.class.getName()); + try { + classFile.build(ClassDesc.of("Merge"), clb -> clb.withMethod("pick", + MethodTypeDesc.of(ConstantDescs.CD_Object, ConstantDescs.CD_boolean, + reader, stock), + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(cob -> { + Label second = cob.newLabel(); + Label join = cob.newLabel(); + cob.iload(0).ifeq(second) + .aload(1).goto_(join) + .labelBinding(second).aload(2) + .labelBinding(join).areturn(); + }))); + return "resolves"; + } catch (IllegalArgumentException e) { + return "fails"; + } + } + } + + /** The main class of the child JVM: it sees only the test classes. */ + public static class Launcher { + public static void main(String[] args) throws Exception { + URL[] urls = new URL[args.length]; + for (int i = 0; i < args.length; i++) { + urls[i] = Path.of(args[i]).toUri().toURL(); + } + try (URLClassLoader loader = new URLClassLoader("isolated", urls, + ClassLoader.getPlatformClassLoader())) { + Class probe = Class.forName(Launcher.class.getName() + .replace("$Launcher", "$Probe"), true, loader); + for (Object line : (List) probe.getMethod("run").invoke(null)) { + System.out.println(line); + } + } + } + } + + private static String location(Class type) throws URISyntaxException { + return Path.of(type.getProtectionDomain().getCodeSource().getLocation().toURI()) + .toString(); + } + + @Test + public void codecsGenerateWhenTheSystemLoaderCannotSeeJackson() throws Exception { + String testClasses = location(IsolatedLoaderTest.class); + List cmd = List.of( + Path.of(System.getProperty("java.home"), "bin", "java").toString(), + "--class-path", testClasses, + Launcher.class.getName(), + location(BlackbirdModule.class), + location(ObjectMapper.class), + location(JsonParser.class), + location(JsonProperty.class), + testClasses); + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.environment().remove("JAVA_TOOL_OPTIONS"); + pb.environment().remove("JDK_JAVA_OPTIONS"); + pb.redirectOutput(dir.resolve("out.txt").toFile()); + pb.redirectError(dir.resolve("err.txt").toFile()); + Process process = pb.start(); + if (!process.waitFor(120, TimeUnit.SECONDS)) { + process.destroyForcibly(); + fail("child JVM timed out: " + cmd); + } + String err = Files.readString(dir.resolve("err.txt")); + assertEquals(0, process.exitValue(), err); + assertEquals(List.of( + "isolated true", + "default resolver fails, generators resolves", + "{\"child\":{\"name\":\"c\",\"rank\":2}," + + "\"children\":[{\"name\":\"d\",\"rank\":3}],\"count\":4,\"flag\":true," + + "\"id\":\"p\"}", + "{\"name\":\"r\",\"count\":5,\"child\":{\"name\":\"e\",\"rank\":0}}", + "Parent reader=true writer=true", + "Child reader=true writer=true", + "Rec reader=true writer=true"), + Files.readAllLines(dir.resolve("out.txt")), err); + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/JdkCompatibilityTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/JdkCompatibilityTest.java new file mode 100644 index 000000000..f6376c73c --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/JdkCompatibilityTest.java @@ -0,0 +1,401 @@ +package tools.jackson.module.blackbird.jdk; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.tools.ToolProvider; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.module.blackbird.BlackbirdModule; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Runs a small program against the packaged Blackbird jar in child JVMs: on + * the JDK that runs the build (25 or later), and on each Java 17-24 home + * listed (comma-separated) in the {@value #OLD_JDKS_PROPERTY} system property. + * The jar is multi-release: its base classes are class file 61 and only the + * two code generators are release 25 classes under META-INF/versions/25. On + * Java 17-24, module discovery must skip Blackbird with one warning and still + * register every other module, and an explicit registration must fail with a + * clear message, also when discovery runs before or after it; on 25+, both + * must engage the generated codecs. A second module provider, compiled at + * release 17, is on the path of every run. The program is a single source + * file that the child's launcher compiles (JEP 330), so it runs on every JDK + * no matter what release this module compiles at. + */ +public class JdkCompatibilityTest +{ + static final String OLD_JDKS_PROPERTY = "blackbird.test.oldJdks"; + + private static final String INACTIVE_WARNING = + "found through module discovery, is inactive"; + + private static final String PROBE = """ + import java.util.ArrayList; + import java.util.Collections; + import java.util.List; + + import tools.jackson.databind.JacksonModule; + import tools.jackson.databind.ObjectMapper; + import tools.jackson.databind.json.JsonMapper; + + // Names Blackbird only reflectively, like an application that + // gets it through module discovery. + public class BlackbirdProbe { + public static class Bean { + public int x; + public String s; + } + + public static void main(String[] args) throws Exception { + System.out.println("runtime " + Runtime.version().feature() + + " " + Runtime.version()); + if (args[0].equals("discover")) { + for (int i = 0; i < 2; i++) { + ObjectMapper mapper = JsonMapper.builder().findAndAddModules().build(); + boolean found = false; + for (JacksonModule m : mapper.registeredModules()) { + found |= m.getClass().getName() + .startsWith("tools.jackson.module.blackbird."); + } + Bean bean = mapper.readValue("{\\"x\\":1,\\"s\\":\\"a\\"}", Bean.class); + System.out.println("discovered " + found + " modules " + names(mapper) + + " roundtrip " + mapper.writeValueAsString(bean)); + } + return; + } + JacksonModule module = (JacksonModule) Class.forName( + "tools.jackson.module.blackbird.BlackbirdModule") + .getDeclaredConstructor().newInstance(); + JsonMapper.Builder builder = JsonMapper.builder(); + switch (args[0]) { + case "explicit" -> builder.addModule(module); + case "explicit-discover" -> builder.addModule(module).findAndAddModules(); + case "discover-explicit" -> builder.findAndAddModules().addModule(module); + default -> throw new IllegalArgumentException(args[0]); + } + try { + System.out.println("registered modules " + names(builder.build())); + } catch (IllegalStateException e) { + System.out.println("rejected " + e.getMessage()); + } + } + + private static List names(ObjectMapper mapper) { + List names = new ArrayList<>(); + for (JacksonModule m : mapper.registeredModules()) { + names.add(m.getModuleName()); + } + Collections.sort(names); + return names; + } + } + """; + + // The module provider of another library, for the class path + // (META-INF/services) and the module path (module-info). + private static final String OTHER_MODULE_NAME = "OtherProbeModule"; + + private static final String OTHER_MODULE_INFO = """ + module blackbird.probe.other { + requires tools.jackson.databind; + provides tools.jackson.databind.JacksonModule with probe.other.OtherProbeModule; + } + """; + + private static final String OTHER_MODULE = """ + package probe.other; + + import tools.jackson.core.Version; + import tools.jackson.databind.JacksonModule; + + public final class OtherProbeModule extends JacksonModule { + @Override + public String getModuleName() { + return "OtherProbeModule"; + } + + @Override + public Version version() { + return Version.unknownVersion(); + } + + @Override + public void setupModule(SetupContext context) { + } + } + """; + + @TempDir + static Path probeDir; + + static Stream javaHomes() { + List homes = new ArrayList<>(); + homes.add(Path.of(System.getProperty("java.home"))); + homes.addAll(oldJdkHomes()); + return homes.stream(); + } + + private static List oldJdkHomes() { + String prop = System.getProperty(OLD_JDKS_PROPERTY, "").trim(); + if (prop.isEmpty()) { + return List.of(); + } + return Arrays.stream(prop.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .map(Path::of) + .collect(Collectors.toList()); + } + + @Test + public void oldJdksAreConfigured() { + assumeTrue(!oldJdkHomes().isEmpty(), "No Java 17-24 home configured: set -D" + + OLD_JDKS_PROPERTY + "=[,] to run the old-runtime cases"); + for (Path home : oldJdkHomes()) { + assertTrue(Files.isExecutable(home.resolve("bin/java")), "not a JDK home: " + home); + } + } + + @ParameterizedTest + @MethodSource("javaHomes") + public void discoveryOnClassPath(Path javaHome) throws Exception { + assertDiscovery(run(javaHome, false, "discover")); + } + + @ParameterizedTest + @MethodSource("javaHomes") + public void discoveryOnModulePath(Path javaHome) throws Exception { + assertDiscovery(run(javaHome, true, "discover")); + } + + @ParameterizedTest + @MethodSource("javaHomes") + public void explicitRegistration(Path javaHome) throws Exception { + assertExplicit(run(javaHome, false, "explicit"), List.of("BlackbirdModule")); + } + + // Databind keeps the module added last for each registration id, so the + // discovery provider must not replace an explicit registration. + @ParameterizedTest + @MethodSource("javaHomes") + public void explicitRegistrationThenDiscovery(Path javaHome) throws Exception { + assertExplicit(run(javaHome, false, "explicit-discover"), + List.of("BlackbirdModule", OTHER_MODULE_NAME)); + } + + @ParameterizedTest + @MethodSource("javaHomes") + public void discoveryThenExplicitRegistration(Path javaHome) throws Exception { + assertExplicit(run(javaHome, false, "discover-explicit"), + List.of("BlackbirdModule", OTHER_MODULE_NAME)); + } + + // The packaged jar is what users get; target/classes (used by a reactor + // build that stops before the package phase) is not multi-release. + @Test + public void jarIsMultiReleaseWithJava17Base() throws Exception { + Path jar = blackbirdJar(); + try (JarFile jf = new JarFile(jar.toFile())) { + Attributes main = jf.getManifest().getMainAttributes(); + assertEquals("true", main.getValue("Multi-Release")); + assertEquals("osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=17))\"", + main.getValue("Require-Capability")); + List versioned = new ArrayList<>(); + for (JarEntry e : jf.stream().toList()) { + if (!e.getName().endsWith(".class")) { + continue; + } + int major = classMajor(jf, e); + if (e.getName().startsWith("META-INF/versions/25/")) { + assertEquals(69, major, e.getName()); + versioned.add(e.getName()); + } else { + assertEquals(61, major, e.getName()); + } + } + assertTrue(versioned.contains("META-INF/versions/25/tools/jackson/module/blackbird/codegen/BeanReaderGenerator.class"), + "versioned entries: " + versioned); + assertTrue(versioned.contains("META-INF/versions/25/tools/jackson/module/blackbird/codegen/BeanWriterGenerator.class"), + "versioned entries: " + versioned); + } + } + + private static void assertDiscovery(Result r) { + assertEquals(0, r.exitCode, r.toString()); + String discovered = "discovered true modules [BlackbirdModule, " + OTHER_MODULE_NAME + + "] roundtrip {\"s\":\"a\",\"x\":1}"; + assertEquals(List.of(r.stdout.get(0), discovered, discovered), r.stdout, r.toString()); + long warnings = r.stderr.stream().filter(l -> l.contains(INACTIVE_WARNING)).count(); + boolean engaged = r.stderr.stream().anyMatch(l -> l.contains("BBReader_BlackbirdProbe")) + && r.stderr.stream().anyMatch(l -> l.contains("BBWriter_BlackbirdProbe")); + if (r.feature >= 25) { + assertEquals(0, warnings, r.toString()); + assertTrue(engaged, "expected generated codecs: " + r); + } else { + assertEquals(1, warnings, "expected exactly one warning: " + r); + assertFalse(engaged, r.toString()); + } + } + + private static void assertExplicit(Result r, List modules) { + assertEquals(0, r.exitCode, r.toString()); + assertEquals(2, r.stdout.size(), r.toString()); + if (r.feature >= 25) { + assertEquals("registered modules " + modules, r.stdout.get(1), r.toString()); + } else { + assertEquals("rejected Cannot register BlackbirdModule: it requires Java 25 or" + + " later, and this runtime is Java " + r.version, r.stdout.get(1) + .replaceFirst("\\. Remove the registration.*$", ""), + r.toString()); + } + } + + private static Result run(Path javaHome, boolean modulePath, String mode) + throws IOException, InterruptedException, URISyntaxException { + Path probe = probeDir.resolve("BlackbirdProbe.java"); + if (!Files.exists(probe)) { + Files.writeString(probe, PROBE); + } + String path = Stream.of(blackbirdJar(), otherModuleJar(), codeSource(ObjectMapper.class), + codeSource(JsonParser.class), codeSource(JsonProperty.class)) + .map(Path::toString) + .collect(Collectors.joining(java.io.File.pathSeparator)); + List cmd = new ArrayList<>(); + cmd.add(javaHome.resolve("bin/java").toString()); + cmd.add("-Dblackbird.debug.codegen=true"); + if (modulePath) { + cmd.addAll(List.of("--module-path", path, "--add-modules", "ALL-MODULE-PATH")); + } else { + cmd.addAll(List.of("--class-path", path)); + } + cmd.add(probe.toString()); + cmd.add(mode); + ProcessBuilder pb = new ProcessBuilder(cmd); + Map env = pb.environment(); + env.remove("JAVA_TOOL_OPTIONS"); + env.remove("JDK_JAVA_OPTIONS"); + Path out = probeDir.resolve(mode + "-" + modulePath + "-out.txt"); + Path err = probeDir.resolve(mode + "-" + modulePath + "-err.txt"); + pb.redirectOutput(out.toFile()); + pb.redirectError(err.toFile()); + Process process = pb.start(); + if (!process.waitFor(120, TimeUnit.SECONDS)) { + process.destroyForcibly(); + fail("probe timed out: " + cmd); + } + Result r = new Result(cmd, process.exitValue(), + Files.readAllLines(out, StandardCharsets.UTF_8), + Files.readAllLines(err, StandardCharsets.UTF_8)); + assertFalse(r.stdout.isEmpty(), r.toString()); + return r; + } + + private static Path otherModuleJar() throws IOException, URISyntaxException { + Path jar = probeDir.resolve("other-probe-module.jar"); + if (Files.exists(jar)) { + return jar; + } + Path src = probeDir.resolve("other-src"); + Path classes = probeDir.resolve("other-classes"); + Files.createDirectories(src.resolve("probe/other")); + Files.writeString(src.resolve("module-info.java"), OTHER_MODULE_INFO); + Files.writeString(src.resolve("probe/other/OtherProbeModule.java"), OTHER_MODULE); + String modulePath = Stream.of(codeSource(ObjectMapper.class), + codeSource(JsonParser.class), codeSource(JsonProperty.class)) + .map(Path::toString) + .collect(Collectors.joining(java.io.File.pathSeparator)); + ByteArrayOutputStream diagnostics = new ByteArrayOutputStream(); + int rc = ToolProvider.getSystemJavaCompiler().run(null, diagnostics, diagnostics, + "--release", "17", "--module-path", modulePath, "-d", classes.toString(), + src.resolve("module-info.java").toString(), + src.resolve("probe/other/OtherProbeModule.java").toString()); + assertEquals(0, rc, diagnostics.toString(StandardCharsets.UTF_8)); + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jar), manifest)) { + for (String name : List.of("module-info.class", "probe/other/OtherProbeModule.class")) { + out.putNextEntry(new JarEntry(name)); + out.write(Files.readAllBytes(classes.resolve(name))); + out.closeEntry(); + } + out.putNextEntry(new JarEntry("META-INF/services/" + JacksonModule.class.getName())); + out.write("probe.other.OtherProbeModule\n".getBytes(StandardCharsets.UTF_8)); + out.closeEntry(); + } + return jar; + } + + private static Path blackbirdJar() throws URISyntaxException { + Path location = codeSource(BlackbirdModule.class); + assumeTrue(Files.isRegularFile(location), "Needs the packaged Blackbird jar (mvn verify);" + + " this run reads " + location + ", which is not multi-release"); + return location; + } + + private static Path codeSource(Class cls) throws URISyntaxException { + return Path.of(cls.getProtectionDomain().getCodeSource().getLocation().toURI()); + } + + private static int classMajor(JarFile jf, JarEntry e) throws IOException { + try (InputStream in = jf.getInputStream(e)) { + byte[] head = in.readNBytes(8); + return ((head[6] & 0xFF) << 8) | (head[7] & 0xFF); + } + } + + private static final class Result { + final List cmd; + final int exitCode; + final List stdout; + final List stderr; + final int feature; + final String version; + + Result(List cmd, int exitCode, List stdout, List stderr) { + this.cmd = cmd; + this.exitCode = exitCode; + this.stdout = stdout; + this.stderr = stderr; + String first = stdout.isEmpty() ? "" : stdout.get(0); + // "runtime " + String[] parts = first.split(" "); + this.feature = (parts.length == 3) ? Integer.parseInt(parts[1]) : -1; + this.version = (parts.length == 3) ? parts[2] : "?"; + } + + @Override + public String toString() { + return "command " + cmd + "\nexit " + exitCode + "\nstdout:\n " + + String.join("\n ", stdout) + "\nstderr:\n " + String.join("\n ", stderr); + } + } +} diff --git a/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/PackagingTest.java b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/PackagingTest.java new file mode 100644 index 000000000..33ea9a3c9 --- /dev/null +++ b/blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/PackagingTest.java @@ -0,0 +1,155 @@ +package tools.jackson.module.blackbird.jdk; + +import java.io.InputStream; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.constantpool.MethodRefEntry; +import java.lang.module.ModuleDescriptor; +import java.lang.reflect.Method; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.codegen.MemberHandles; +import tools.jackson.module.blackbird.internal.DiscoveredBlackbirdModule; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Structural checks of the published module: what it exports (JPMS and + * OSGi) and requires, what its helpers can reach, what it needs from + * jackson-core, and the release guard of the build. + */ +public class PackagingTest +{ + private static Path blackbirdLocation() throws URISyntaxException { + return Path.of(BlackbirdModule.class.getProtectionDomain().getCodeSource() + .getLocation().toURI()); + } + + private static ModuleDescriptor descriptor() throws Exception { + Path location = blackbirdLocation(); + if (Files.isDirectory(location)) { + try (InputStream in = Files.newInputStream(location.resolve("module-info.class"))) { + return ModuleDescriptor.read(in); + } + } + try (JarFile jar = new JarFile(location.toFile()); + InputStream in = jar.getInputStream(jar.getEntry("module-info.class"))) { + return ModuleDescriptor.read(in); + } + } + + // The internal package is not exported: generated codecs define in + // Blackbird's own module, so no other module links to it. + @Test + public void moduleExportsOnlyTheModulePackage() throws Exception { + ModuleDescriptor d = descriptor(); + assertEquals(Set.of("tools.jackson.module.blackbird"), d.exports().stream() + .map(ModuleDescriptor.Exports::source).collect(Collectors.toSet())); + assertTrue(d.exports().stream().noneMatch(ModuleDescriptor.Exports::isQualified)); + assertTrue(d.opens().isEmpty()); + } + + // Logging goes through System.Logger; no java.logging dependency. + @Test + public void moduleRequiresNoJavaLogging() throws Exception { + Set requires = descriptor().requires().stream() + .map(ModuleDescriptor.Requires::name).collect(Collectors.toSet()); + assertFalse(requires.contains("java.logging"), requires.toString()); + assertEquals(Set.of("java.base", "tools.jackson.core", "tools.jackson.databind"), + requires); + } + + @Test + public void moduleProvidesTheDiscoveryProvider() throws Exception { + ModuleDescriptor.Provides provides = descriptor().provides().stream() + .filter(p -> p.service().equals(JacksonModule.class.getName())) + .findFirst().orElseThrow(); + assertEquals(java.util.List.of(DiscoveredBlackbirdModule.class.getName()), + provides.providers()); + } + + // OSGi exports match the module descriptor. + @Test + public void bundleExportsOnlyTheModulePackage() throws Exception { + Path location = blackbirdLocation(); + assumeTrue(Files.isRegularFile(location), "Needs the packaged Blackbird jar (mvn verify);" + + " this run reads " + location); + try (JarFile jar = new JarFile(location.toFile())) { + String exports = jar.getManifest().getMainAttributes().getValue("Export-Package"); + assertTrue(exports.startsWith("tools.jackson.module.blackbird;"), exports); + assertFalse(exports.contains("tools.jackson.module.blackbird."), exports); + } + } + + // MemberHandles grants no access of its own: a member nobody made + // accessible unreflects only when public. Blackbird's own full-privilege + // lookup would also reach Blackbird's package-private members. + @Test + public void memberHandlesGrantNoPrivilege() throws Exception { + // A package-private static method of a package-private class in the + // package of MemberHandles. + Class descs = Class.forName("tools.jackson.module.blackbird.codegen.Descs"); + Method internal = descs.getDeclaredMethod("of", Class.class); + assertThrows(IllegalAccessException.class, () -> MemberHandles.unary(internal)); + Method open = descs.getDeclaredMethod("of", Class.class); + open.setAccessible(true); + assertNotNull(MemberHandles.unary(open)); + } + + // The generated readers call JsonParser.nextNameMatchAndToken (new + // in jackson-core 3.3), and so does stock BeanDeserializer: Blackbird + // needs no newer jackson-core than the databind it runs with. + @Test + public void stockDatabindAlreadyNeedsFusedNameMatch() throws Exception { + assertNotNull(JsonParser.class.getMethod("nextNameMatchAndToken", + tools.jackson.core.sym.PropertyNameMatcher.class)); + ClassModel stock; + try (InputStream in = BeanDeserializer.class.getResourceAsStream( + "BeanDeserializer.class")) { + stock = ClassFile.of().parse(in.readAllBytes()); + } + boolean calls = false; + for (var entry : stock.constantPool()) { + if (entry instanceof MethodRefEntry ref + && ref.name().equalsString("nextNameMatchAndToken")) { + calls = true; + } + } + assertTrue(calls, "stock BeanDeserializer no longer calls nextNameMatchAndToken"); + } + + // A release built on JDK < 25 would leave Blackbird out. The release profile (maven-release-plugin passes -Prelease) + // requires JDK 25; a manual check confirmed that + // "mvn -N -Prelease validate" fails on JDK 21 and passes on JDK 25. + @Test + public void releaseProfileRequiresJdk25() throws Exception { + Path rootPom = Path.of(System.getProperty("user.dir")).resolveSibling("pom.xml"); + assumeTrue(Files.isRegularFile(rootPom), "root pom.xml not found from " + + System.getProperty("user.dir")); + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder() + .parse(rootPom.toFile()); + String version = (String) XPathFactory.newInstance().newXPath().evaluate( + "/*[local-name()='project']/*[local-name()='profiles']" + + "/*[local-name()='profile'][*[local-name()='id']='release']" + + "//*[local-name()='requireJavaVersion']/*[local-name()='version']", + doc, XPathConstants.STRING); + assertEquals("[25,)", version.trim()); + } +} diff --git a/blackbird/GenBlackbirdAccess.java b/blackbird/GenBlackbirdAccess.java deleted file mode 100644 index 802cf38e7..000000000 --- a/blackbird/GenBlackbirdAccess.java +++ /dev/null @@ -1,276 +0,0 @@ -import java.util.ArrayList; -import java.util.Collections; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.tools.Diagnostic; -import javax.tools.DiagnosticListener; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import javax.tools.FileObject; -import javax.tools.JavaFileManager; -import javax.tools.JavaFileObject; -import javax.tools.JavaFileObject.Kind; -import javax.tools.SimpleJavaFileObject; -import javax.tools.StandardJavaFileManager; -import javax.tools.StandardLocation; - -/** - * The following code produces the class file stored in the data arrays in CrossLoaderAccess. - * Loading the Java compiler is extremely expensive so we inline the byte data instead. - * - * This code is not compiled or shipped with Blackbird, just here for maintainer reference. - */ -class GrantAccess { - private static MethodHandles.Lookup grantAccessSlow(final MethodHandles.Lookup lookup, final Package pkg) throws IOException, ReflectiveOperationException { - final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - final BlackbirdDiagnosticListener diagnostics = new BlackbirdDiagnosticListener<>(); - final StandardJavaFileManager delegateFileManager = compiler.getStandardFileManager(diagnostics, Locale.ROOT, StandardCharsets.UTF_8); - final String myPackage = pkg.getName(); - final BlackbirdFileManager fileManager = new BlackbirdFileManager(delegateFileManager, myPackage); - final JavaFileObject file = new BlackbirdFileManager.BlackbirdInputJavaFileObject("BlackbirdAccess", "package " + myPackage + "; public class BlackbirdAccess { public static final java.lang.invoke.MethodHandles.Lookup LOOKUP = java.lang.invoke.MethodHandles.lookup(); }"); - compiler - .getTask(null, fileManager, diagnostics, Arrays.asList("--release", "8"), null, Arrays.asList(file)) - .call(); - diagnostics.assertSuccess(); - final FileObject accessClassFile = fileManager.getFileForInput(StandardLocation.CLASS_PATH, myPackage, "BlackbirdAccess"); - final byte[] accessClassBytes = accessClassFile.openInputStream().readAllBytes(); - final Class accessClass = lookup.defineClass(accessClassBytes); - return (MethodHandles.Lookup) accessClass.getField("LOOKUP").get(null); - } -} - -class BlackbirdDiagnosticListener implements DiagnosticListener { - private static final Set FAILURES; - - static { - FAILURES = Collections.unmodifiableSet( - EnumSet.of(Diagnostic.Kind.ERROR, Diagnostic.Kind.WARNING, Diagnostic.Kind.MANDATORY_WARNING)); - } - - private final List> failures = new ArrayList<>(); - - @Override - public void report(final Diagnostic diagnostic) { - if (FAILURES.contains(diagnostic.getKind())) { - failures.add(diagnostic); - } - } - - public void assertSuccess() { - if (!failures.isEmpty()) { - throw new RuntimeException("Compilation failed:\n" - + failures.stream() - .map(Object::toString) - .collect(Collectors.joining("\n"))); - } - } -} - - -class BlackbirdFileManager implements JavaFileManager { - private final StandardJavaFileManager delegate; - private final Map createdFiles = new HashMap<>(); - private final String myPackage; - - public BlackbirdFileManager(final StandardJavaFileManager delegate, final String myPackage) { - this.delegate = delegate; - this.myPackage = myPackage; - } - - @Override - public ClassLoader getClassLoader(final Location location) { - return delegate.getClassLoader(location); - } - - @Override - public Iterable list(final Location location, final String packageName, final Set kinds, final boolean recurse) throws IOException { - final Iterable stdList = delegate.list(location, packageName, kinds, recurse); - if (StandardLocation.CLASS_PATH.equals(location)) { - return () -> new Iterator() { - boolean stdDone; - Iterator iter; - - @Override - public boolean hasNext() { - if (iter == null) { - iter = stdList.iterator(); - } - if (iter.hasNext()) { - return true; - } - if (stdDone) { - return false; - } - stdDone = true; - iter = createdFiles.values().iterator(); - return iter.hasNext(); - } - - @Override - public JavaFileObject next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return iter.next(); - } - }; - } - return stdList; - } - - @Override - public String inferBinaryName(final Location location, final JavaFileObject file) { - if (file instanceof BlackbirdOutputJavaFileObject) { - return file.getName(); - } - return delegate.inferBinaryName(location, file); - } - - @Override - public boolean handleOption(final String current, final Iterator remaining) { - return delegate.handleOption(current, remaining); - } - - @Override - public boolean hasLocation(final Location location) { - return delegate.hasLocation(location); - } - - @Override - public JavaFileObject getJavaFileForInput(final Location location, final String className, final Kind kind) throws IOException { - return delegate.getJavaFileForInput(location, className, kind); - } - - @Override - public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) throws IOException { - final BlackbirdOutputJavaFileObject out = new BlackbirdOutputJavaFileObject(className, kind); - createdFiles.put(className, out); - return out; - } - - @Override - public FileObject getFileForInput(final Location location, final String packageName, final String relativeName) throws IOException { - if (StandardLocation.CLASS_PATH.equals(location) && myPackage.equals(packageName)) { - final BlackbirdOutputJavaFileObject file = createdFiles.get(packageName + "." + relativeName); - if (file != null) { - return file; - } - } - return delegate.getFileForInput(location, packageName, relativeName); - } - - @Override - public FileObject getFileForOutput(final Location location, final String packageName, final String relativeName, final FileObject sibling) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public void flush() throws IOException {} - - @Override - public void close() throws IOException {} - - @Override - public int isSupportedOption(final String option) { - return delegate.isSupportedOption(option); - } - - @Override - public boolean isSameFile(final FileObject a, final FileObject b) { - return delegate.isSameFile(a, b); - } - - @Override - public Location getLocationForModule(final Location location, final String moduleName) throws IOException { - return delegate.getLocationForModule(location, moduleName); - } - - @Override - public Location getLocationForModule(final Location location, final JavaFileObject fo) throws IOException { - return delegate.getLocationForModule(location, fo); - } - - @Override - public String inferModuleName(final Location location) throws IOException { - return delegate.inferModuleName(location); - } - - @Override - public Iterable> listLocationsForModules(final Location location) throws IOException { - return delegate.listLocationsForModules(location); - } - - @Override - public boolean contains(final Location location, final FileObject file) throws IOException { - return delegate.contains(location, file); - } - - public static class BlackbirdJavaFileObject extends SimpleJavaFileObject { - - protected BlackbirdJavaFileObject(final String name, final Kind kind) { - super(URI.create("string:///" + name.replace('.', '/') - + kind.extension), kind); - } - } - - public static class BlackbirdInputJavaFileObject extends BlackbirdJavaFileObject { - private final String contents; - - protected BlackbirdInputJavaFileObject(final String name, final String contents) { - super(name, Kind.SOURCE); - this.contents = contents; - } - - @Override - public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws IOException { - return contents; - } - } - - public static class BlackbirdOutputJavaFileObject extends BlackbirdJavaFileObject { - private final String name; - private ByteArrayOutputStream baos = new ByteArrayOutputStream(); - private byte[] bytes; - - protected BlackbirdOutputJavaFileObject(final String name, final Kind kind) { - super(name, kind); - this.name = name; - } - - @Override - public String getName() { - return name; - } - - public byte[] getBytes() { - if (bytes == null) { - bytes = baos.toByteArray(); - baos = null; - } - return bytes; - } - - @Override - public OutputStream openOutputStream() throws IOException { - return baos; - } - - @Override - public InputStream openInputStream() throws IOException { - return new ByteArrayInputStream(getBytes()); - } - } -} - diff --git a/blackbird/README.md b/blackbird/README.md index 1d1c26ddc..5825385ce 100644 --- a/blackbird/README.md +++ b/blackbird/README.md @@ -1,38 +1,46 @@ # Jackson Blackbird -_Upgrade your Afterburner for your Java 11+ Environment 🚀_ - -The [Afterburner](https://github.com/FasterXML/jackson-modules-base/tree/master/afterburner) -has long been your engine of choice for maximum Jackson performance. -But in the brave new Java 11 world, the trusty Afterburner is showing its age. -It uses horrifying bytecode manipulation and cracks `Unsafe.defineClass` which will -[stop working soon](https://github.com/FasterXML/jackson-modules-base/issues/37). - -Blackbird is Afterburner Mk II - powered by 100% renewable Lambda based fuel. - -The [LambdaMetafactory](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/invoke/LambdaMetafactory.html) -introduces a standard Java API for dynamically instantiating function objects. -The current OpenJDK implementation generates anonymous classes in a somewhat similar fashion -to the classic Afterburner. While the metafactory cannot generate comparably specialized -implementations, we can write needed adapters as simple Java code and use the metafactory -to create distinct call sites for every needed access path. This should allow each accessor to -have a monomorphic call profile and easily inline for maximum performance. - -> **Warning** -> The Java implementation of lambdas associates each generated lambda object strongly with the -> ClassLoader of the target class. This means that each Blackbird instance will add dynamic accessors -> to your classes that cannot be unloaded until the entire ClassLoader is garbage collected. -> Therefore, Blackbird is not appropriate if you instantiate many ObjectMappers, since -> you will eventually run out of class space and OOM. +Blackbird accelerates Jackson databind with generated per-bean codecs. For +each bean type it defines one hidden class with the JDK ClassFile API +(JEP 484) and installs it as that bean's deserializer. The generated codec +matches property names with the parser's `PropertyNameMatcher`, dispatches on +the match index with a `tableswitch`, and reads each property with direct, +monomorphic code. The JIT sees one inlinable region per bean type instead of +the stock dispatch chain. + +This is the second Blackbird engine. The first (Jackson 2.x, and 3.x before +this version) built accessor lambdas with `LambdaMetafactory` inside the stock +deserializer loop. The current engine replaces the loop itself. Blackbird +2.x continues to serve Jackson 2.x users. + +## Requirements + +- Jackson 3.x, with `jackson-core` and `jackson-databind` of the same version. +- Java 25 or later at run time. The code generators use the ClassFile API + (final in Java 24) and compile at release 25, the current LTS release. The + jar is a multi-release jar: the generators are under + `META-INF/versions/25`, and all other classes are release 17. On Java 17 + to 24, module discovery (`MapperBuilder.findAndAddModules()`) skips + Blackbird with one logged warning, and the mapper works without it. An + explicit `addModule(new BlackbirdModule())` throws + `IllegalStateException`. +- If you repackage Blackbird into an uber jar (for example with the Maven + Shade plugin), keep `Multi-Release: true` in the manifest of that jar. + Without it, the JVM does not see the release-25 classes, and Blackbird + behaves as on Java 24. +- Building the module needs JDK 25 or later. On an older JDK the reactor + leaves the Blackbird modules out, and a release build (`-Prelease`) fails. ## Status [![Maven Central](https://img.shields.io/maven-central/v/tools.jackson.module/jackson-module-blackbird.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/tools.jackson.module/jackson-module-blackbird) -Blackbird is new and not as mature as Afterburner, but has been tested and runs well. -The code is written to fail-fast and explode on the tarmac rather than later at runtime. +The module passes the full Blackbird test suite. -Blackbird passes all the original Afterburner tests (except a couple that didn't make sense anymore). +Generated codecs unload with the mapper: the hidden classes are anchored only +by the codec instances in the mapper's caches. The old engine's lambdas stayed +alive with the target ClassLoader, so applications that created many mappers +ran out of metaspace. That limitation is gone. ## Usage @@ -47,9 +55,9 @@ Blackbird is available on Maven Central: ``` -### Registering module +### Registering the module -To use the Module in Jackson, simply register it with the ObjectMapper instance: +Register the module with the mapper: ```java ObjectMapper mapper = JsonMapper.builder() @@ -57,46 +65,140 @@ ObjectMapper mapper = JsonMapper.builder() .build(); ``` -after which you just do data-binding as usual: +then do data binding as usual: ```java Value val = mapper.readValue(jsonSource, Value.class); mapper.writeValue(new File("result.json"), val); ``` -If you're really brave and running with a modulepath, you may need to grant Blackbird access to your classes. -This is done by calling the caller sensitive `MethodHandles.lookup()`. The module constructor allows you to customize -which lookup instances are used for what classes. - -### What is optimized? - -Following things are optimized: - -* For serialization (POJOs to JSON): - * Getter methods are inlined using generated lambdas instead of reflection - * Serializers for small number of 'primitive' types (`int`, `long`, `boolean`, `String`) are replaced with lambda-specialized generators, instead of getting delegated to `JsonSerializer`s -* For deserialization (JSON to POJOs): - * Calls to default (no-argument) constructors are lambda-fied instead of using reflection - * Calls to @JsonCreate factory methods and constructors with arguments get a slightly less efficient lambda based implementation - - This is new in Blackbird; Afterburner never did this - * Setter methods are called using lambdas instead of reflection - * Deserializers for small number of 'primitive' types (`int`, `long`, `boolean`, `String`) are replaced with lambda-specialized parsers, instead of getting delegated to `JsonDeserializer`s - -### ... and what is not? - -* Any sort of direct field access. This may be possible to support someday but requires additional JDK support. Use methods instead. -* Streaming parser/generator access -* Tree Model: there isn't much that can be done at this level +No access setup is required beyond what stock databind already needs. On the +module path, that is `opens your.package to tools.jackson.databind` for +non-public classes and members; Blackbird adds no requirement of its own. +Generated codecs reach members through `MethodHandle` constants unreflected +after databind's own access checks, so everything stock databind can read or +write - private members, non-public classes, records, foreign classloaders - +accelerates. A member databind cannot open falls back to the stock path, +which fails (or succeeds) exactly as it would without Blackbird. + +The `BlackbirdModule` constructors and overrides that supply a +`MethodHandles.Lookup` (`findLookup()`, `findLookupSupplier()`) remain for +compatibility, but Blackbird no longer uses the lookup: no acceleration +needs it. The module exports only `tools.jackson.module.blackbird`. +Blackbird defines the generated codecs in its own module. + +## What is optimized? + +Blackbird generates a codec for a bean only where the generated code does +what stock databind does. The codec is a copy of the stock deserializer or +serializer whose main read or write method is generated. Inputs and features +that the generated code does not handle go to the stock code. Other modifiers +and databind's own checks therefore see the stock `BeanDeserializer`, +`BuilderBasedDeserializer` or `BeanSerializer` types. + +Deserialization (JSON to POJOs): + +- POJOs that stock builds with a no-arg constructor: construction and + stores through constant `MethodHandle`s, which the JIT inlines like direct + calls. Non-public setters and constructors accelerate the same way public + ones do. Non-final fields (any visibility) store through constant field + handles. Final fields keep stock behavior. +- Records with their canonical constructor: typed locals in + canonical-constructor order and a direct constructor call. When the + document leaves out a component, the stock creator completes the + arguments (absent values such as `Optional.empty()`, required and + `FAIL_ON_MISSING_CREATOR_PROPERTIES` checks). +- Builder-based beans (for example Immutables types): fluent setter calls + through constant handles, and the build method called through a constant + handle. +- `String`, `int`, `long` and `boolean` values are read with direct parser + calls, and nested beans that have a generated codec are read by that codec. + All other values, and any value with an unexpected token (such as `null` or + a string for a number), go through the stock property. + +Serialization (POJOs to JSON): straight-line writers with pre-encoded name +constants and direct getter or field reads. Property writes go through one +generated class of write helpers that all writers share. The helpers are +sized to compile as standalone units, which avoids a C2 code-quality penalty +for inlined copies of hot jackson-core methods inside looping writer bodies, +and one shared copy keeps the compiled code small. + +A property keeps the stock property implementation inside the generated +codec when it has polymorphic type handling (`@JsonTypeInfo` or default +typing), a custom deserializer or serializer, a property wrapper (managed and +back references, or wrappers from a `ValueDeserializerModifier`), a +non-default null serializer, an applied view, or value suppression. A bean +keeps the stock deserializer or serializer when it has Object Ids, a +delegating or property-based creator (other than the canonical constructor +of a record), an external type id, unwrapped or merging properties, or, for +records, an any-setter or injectable values. A bean also keeps the stock code +when one of its generated methods would be larger than 7000 bytes of +bytecode: HotSpot does not compile methods over 8000 bytes. + +## What is not? + +- Streaming parser and generator access. +- The tree model. + +## Debugging + +With `-Dblackbird.debug.codegen=true`, the module prints each generation +decision (engaged, or which gate demoted the bean) to standard error. With +`-Dblackbird.debug.dumpDir=`, the module writes each generated class's +bytes to that directory as `-reader.class` or +`-writer.class`, for inspection with javap or a decompiler. + +When code generation fails for a bean, the bean keeps the stock path, and +the module logs the failure at `DEBUG` (`FINE` in java.util.logging) through +the `System.Logger` named `tools.jackson.module.blackbird`. ## Performance -Blackbird has been lightly performance tested using `jmh` on openjdk 11 and 12. -For reading and writing a moderately complex bean, Blackbird performance seems -to be almost exactly on par with Afterburner - up to 20% better than vanilla Jackson in some cases. -At that point the method invocation overhead disappears behind the heat generated -from the long parser and UTF8 decoder. - -I used [jitwatch](https://github.com/AdoptOpenJDK/jitwatch) with `hsdis` to verify the generated code at a -superficial level. The generated call sites do seem to be good targets for inlining, although unfortunately -[jitwatch currently can't analyze lambdas very well](https://github.com/AdoptOpenJDK/jitwatch/issues/282) so -more advanced analysis will have to come later. +The table gives throughput relative to stock databind. Each ratio is from +one JMH run that measured both engines: the classic column from a run of +the classic jar, and the new column from a run of this version. "Classic" +is Blackbird 3.2.2, the last release of the LambdaMetafactory engine, with +the same databind. + +| Benchmark | Classic | This version | +|---------------------------------------------|--------:|-------------:| +| Record read (nested record, list of 3) | 1.40x | 2.32x | +| Record write | 1.08x | 1.22x | +| Builder bean read (8 properties) | 1.01x | 1.26x | +| Builder bean write | 1.21x | 1.44x | +| Setter POJO read (media item) | 0.97x | 1.22x | +| Setter POJO write (media item) | 1.01x | 1.19x | +| 1000 beans, 8 `int` properties, read | 1.07x | 1.34x | +| 1000 beans, 8 `int` properties, write | 1.14x | 1.59x | +| 1000 beans, 5 properties, read | 1.06x | 1.20x | +| 1000 beans, 5 properties, write | 1.01x | 1.22x | +| Same beans through a `@JsonCreator`, read | 1.26x | 1.02x | +| 40-property bean, read | 1.06x | 1.36x | +| 40-property bean, write | 1.25x | 1.72x | +| 200-property bean, read | 1.04x | 0.99x | +| 200-property bean, write | 1.14x | 2.07x | + +Two rows are slower than classic Blackbird. In both, this version keeps the +stock deserializer: + +- A POJO with a property-based `@JsonCreator` stays on the stock + deserializer. Classic Blackbird accelerates that creator. +- The generated reader of the 200-property bean would be larger than 7000 + bytes of bytecode, so the bean keeps the stock deserializer. HotSpot does + not compile methods over 8000 bytes (`DontCompileHugeMethods`), and an + interpreted reader was 3.7 times slower than stock. + +Cold start costs more than stock. The first read and write of 500 distinct +types (250 records and 250 setter beans) in a fresh JVM take 0.83 ms per +type with stock databind, 1.85 ms with this version and 1.82 ms with +classic Blackbird. Thus each bean type costs approximately 1 ms more on +first use, for introspection, code generation and class definition. + +Environment: JMH 1.37, JDK 25.0.4, Linux on x86_64 (AMD Ryzen 7 5800X3D, +8 cores and 16 threads), 3 forks of 5 iterations of 1 s after 5 warmup +iterations, and 20 forks for cold start. Other processes used 1 to 2 CPUs +during the runs, and a run during which they used more than 3 CPUs was +repeated. For the read rows, whose code did not change, an earlier run on +the same host with less than 0.4 CPU of other load gave ratios within +0.07x of these. To run the benchmarks, see +[benchmarks/README.md](benchmarks/README.md). diff --git a/blackbird/benchmarks/README.md b/blackbird/benchmarks/README.md index 757804bc1..a4a523bc9 100644 --- a/blackbird/benchmarks/README.md +++ b/blackbird/benchmarks/README.md @@ -1,137 +1,96 @@ -# Blackbird Benchmarks - -Blackbird needs to show compelling performance to be a credible alternative to Afterburner. -I've run some simple benchmarks built with JMH. - -``` -JMH version: 1.25 -VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -Linux 5.8.4-200.fc32.x86_64 -Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz -``` - -### Deserialize Bean Array - -Deserialize an array of 1000 like beans with 5 properties - -``` -Benchmark Mode Cnt Score Error Units -Vanilla.beanArrayDeser thrpt 50 1.184 ± 0.005 ops/ms -Afterburner.beanArrayDeser thrpt 50 1.299 ± 0.009 ops/ms -Blackbird.beanArrayDeser thrpt 50 1.301 ± 0.005 ops/ms - -VanillaGraal.beanArrayDeser thrpt 50 1.117 ± 0.009 ops/ms -AfterburnerGraal.beanArrayDeser thrpt 50 1.314 ± 0.015 ops/ms -BlackbirdGraal.beanArrayDeser thrpt 50 1.333 ± 0.014 ops/ms -``` - -### Serialize Bean Array - -Serialize an array of 1000 like beans with 5 properties - -``` -Vanilla.beanArraySer thrpt 50 2.041 ± 0.014 ops/ms -Afterburner.beanArraySer thrpt 50 2.193 ± 0.010 ops/ms -Blackbird.beanArraySer thrpt 50 2.115 ± 0.009 ops/ms - -VanillaGraal.beanArraySer thrpt 50 2.091 ± 0.013 ops/ms -AfterburnerGraal.beanArraySer thrpt 50 2.293 ± 0.018 ops/ms -BlackbirdGraal.beanArraySer thrpt 50 2.256 ± 0.015 ops/ms -``` - -### Constructor Array Deserialize - -Deserialize the same 5 property bean with a `@JsonConstructor` rather than setters - -``` -Vanilla.constructorArrayDeser thrpt 50 1.114 ± 0.003 ops/ms -Afterburner.constructorArrayDeser thrpt 50 1.124 ± 0.004 ops/ms -Blackbird.constructorArrayDeser thrpt 50 1.122 ± 0.004 ops/ms - -VanillaGraal.constructorArrayDeser thrpt 50 1.063 ± 0.007 ops/ms -AfterburnerGraal.constructorArrayDeser thrpt 50 1.087 ± 0.004 ops/ms -BlackbirdGraal.constructorArrayDeser thrpt 50 1.092 ± 0.003 ops/ms -``` - -### 8-int Bean Deserialize - -Deserialize a list of 1000 beans, each with 8 integer properties - -``` -Vanilla.classicBeanItemDeser thrpt 50 1.070 ± 0.008 ops/ms -Afterburner.classicBeanItemDeser thrpt 50 1.398 ± 0.024 ops/ms -Blackbird.classicBeanItemDeser thrpt 50 1.250 ± 0.017 ops/ms - -VanillaGraal.classicBeanItemDeser thrpt 50 1.082 ± 0.006 ops/ms -AfterburnerGraal.classicBeanItemDeser thrpt 50 1.515 ± 0.014 ops/ms -BlackbirdGraal.classicBeanItemDeser thrpt 50 1.480 ± 0.020 ops/ms -``` - -### 8-int Bean Serialize - -Serialize a list of 1000 beans, each with 8 integer properties - -``` -Vanilla.classicBeanItemSer thrpt 50 1.949 ± 0.020 ops/ms -Afterburner.classicBeanItemSer thrpt 50 2.325 ± 0.024 ops/ms -Blackbird.classicBeanItemSer thrpt 50 2.140 ± 0.017 ops/ms - -VanillaGraal.classicBeanItemSer thrpt 50 2.017 ± 0.019 ops/ms -AfterburnerGraal.classicBeanItemSer thrpt 50 2.485 ± 0.043 ops/ms -BlackbirdGraal.classicBeanItemSer thrpt 50 2.202 ± 0.053 ops/ms -``` - -### Photo Metadata Deserialize - -Deserialize a slightly more complicated bean with photo metadata - -``` -Vanilla.classicMediaItemDeser thrpt 50 324.917 ± 3.942 ops/ms -Afterburner.classicMediaItemDeser thrpt 50 403.335 ± 1.337 ops/ms -Blackbird.classicMediaItemDeser thrpt 50 387.160 ± 1.507 ops/ms - -VanillaGraal.classicMediaItemDeser thrpt 50 332.992 ± 4.667 ops/ms -AfterburnerGraal.classicMediaItemDeser thrpt 50 429.227 ± 2.092 ops/ms -BlackbirdGraal.classicMediaItemDeser thrpt 50 430.031 ± 1.192 ops/ms -``` - -### Photo Metadata Serialize - -Serialize a slightly more complicated bean with photo metadata - -``` -Vanilla.classicMediaItemSer thrpt 50 564.082 ± 6.896 ops/ms -Afterburner.classicMediaItemSer thrpt 50 610.446 ± 1.996 ops/ms -Blackbird.classicMediaItemSer thrpt 50 575.804 ± 4.887 ops/ms - -VanillaGraal.classicMediaItemSer thrpt 50 611.400 ± 3.187 ops/ms -AfterburnerGraal.classicMediaItemSer thrpt 50 676.551 ± 5.980 ops/ms -BlackbirdGraal.classicMediaItemSer thrpt 50 659.393 ± 2.591 ops/ms -``` - -### Startup time - -_(lower is better)_ - -``` -StartupTimeHotspot.vanilla ss 10 256.619 ± 14.163 ms/op -StartupTimeHotpost.afterburner ss 10 296.559 ± 27.770 ms/op -StartupTimeHotspot.blackbird ss 10 292.001 ± 26.581 ms/op - -StartupTimeGraal .vanilla ss 10 298.862 ± 37.968 ms/op -StartupTimeGraal .afterburner ss 10 369.617 ± 59.404 ms/op -StartupTimeGraal .blackbird ss 10 351.144 ± 53.208 ms/op -``` - -Both Afterburner and Blackbird incur a slight startup penalty. -Graal imposes another startup penalty. - -These JMH benchmarks should demonstrate that on modern JVMs (11+), -Blackbird is roughly comparable to Afterburner, and comfortably faster than vanilla Jackson. -If you can afford the startup time, Graal seems to generate faster code in almost all circumstances. -Right now you have to build the executable jar yourself with Maven. -Please report any problems or performance issues so we can improve Blackbird! - -TODO: pretty graphs - -TODO: merge this into [jackson-benchmarks](https://github.com/FasterXML/jackson-benchmarks) as appropriate +# Blackbird benchmarks + +JMH benchmarks that compare stock databind, the classic Blackbird engine +(LambdaMetafactory, release 3.2.2) and the generated-codec engine of this +build. + +## Benchmarks + +Each benchmark class has a `mode` parameter: `stock` (no module), +`blackbird` or `afterburner`. The default run measures `stock` and +`blackbird`. + +- `PojoBenchmark`: setter POJOs. Arrays of 1000 small beans, the same beans + through a property-based `@JsonCreator`, the media item bean, and arrays + of 1000 beans with 8 `int` properties. +- `RecordBenchmark`: a record with a nested record and a list of 3 records. +- `BuilderBenchmark`: a bean with 8 properties and a fluent builder + (`@JsonPOJOBuilder`). +- `WideBeanBenchmark`: setter beans with 40 and 200 properties. +- `ColdStartBenchmark`: the first read and write of 500 distinct types + (250 records, 250 setter beans) in a fresh JVM, with 20 forks. The score + is the time for all 500 types. + +## How to run + +Blackbird needs JDK 25 or later. The benchmark module is not part of the +reactor, so install the modules first: + +``` +./mvnw -pl blackbird,afterburner -am install -DskipTests +cd blackbird/benchmarks +../../mvnw package +../../mvnw package -Pclassic-blackbird +``` + +The first `package` writes `target/benchmarks.jar` (this build). The +`classic-blackbird` profile writes `target/classic/benchmarks-classic.jar`, +which has Blackbird 3.2.2 and the same databind. Run the two jars with the +same JMH options: + +``` +java -jar target/benchmarks.jar -f 3 -wi 5 -w 1s -i 5 -r 1s \ + 'Pojo|Record|Builder|WideBean' -rf json -rff new.json +java -jar target/classic/benchmarks-classic.jar -f 3 -wi 5 -w 1s -i 5 -r 1s \ + 'Pojo|Record|Builder|WideBean' -rf json -rff classic.json +java -jar target/benchmarks.jar ColdStart -rf json -rff cold-new.json +java -jar target/classic/benchmarks-classic.jar ColdStart \ + -rf json -rff cold-classic.json +``` + +Each run measures stock databind too. Divide each Blackbird score by the +stock score of the same run. A ratio across two runs includes any change +in the load of the host between the runs. + +Do not give `-wi` or `-i` to `ColdStartBenchmark`. A warmup iteration +makes the measured iteration warm. + +To see which beans get a generated codec, add +`-jvmArgsAppend -Dblackbird.debug.codegen=true`. + +## Results + +JMH 1.37, JDK 25.0.4, Linux on x86_64 (AMD Ryzen 7 5800X3D, 8 cores and 16 +threads), with the options above and `-Xms512m -Xmx512m` in each fork. +Other processes used 1 to 2 CPUs during the runs, and a run during which +they used more than 3 CPUs was repeated. Scores are ops/ms with the 99.9% +confidence interval. Classic is Blackbird 3.2.2, and new is this build. +Each Blackbird column has the stock score of its own run. + +| Benchmark | Stock (classic run) | Classic | Stock (new run) | New | +|---|---:|---:|---:|---:| +| BuilderBenchmark.builderDeser | 2074 ± 19 | 2093 ± 25 | 2166 ± 18 | 2719 ± 40 | +| BuilderBenchmark.builderSer | 2955 ± 79 | 3576 ± 139 | 3077 ± 51 | 4429 ± 146 | +| PojoBenchmark.beanArrayDeser | 2.65 ± 0.07 | 2.80 ± 0.06 | 2.58 ± 0.08 | 3.09 ± 0.06 | +| PojoBenchmark.beanArraySer | 4.73 ± 0.07 | 4.76 ± 0.12 | 4.53 ± 0.14 | 5.54 ± 0.14 | +| PojoBenchmark.classicBeanDeser | 2.45 ± 0.11 | 2.61 ± 0.08 | 2.48 ± 0.07 | 3.31 ± 0.03 | +| PojoBenchmark.classicBeanSer | 4.09 ± 0.13 | 4.65 ± 0.08 | 4.18 ± 0.08 | 6.65 ± 0.17 | +| PojoBenchmark.constructorArrayDeser | 1.87 ± 0.04 | 2.35 ± 0.05 | 1.87 ± 0.03 | 1.90 ± 0.04 | +| PojoBenchmark.mediaItemDeser | 738 ± 6 | 718 ± 11 | 736 ± 18 | 894 ± 16 | +| PojoBenchmark.mediaItemSer | 1201 ± 29 | 1217 ± 24 | 1176 ± 26 | 1402 ± 51 | +| RecordBenchmark.recordDeser | 598 ± 9 | 837 ± 34 | 600 ± 11 | 1391 ± 15 | +| RecordBenchmark.recordSer | 1859 ± 44 | 2016 ± 42 | 1839 ± 35 | 2252 ± 90 | +| WideBeanBenchmark.wideDeser (40) | 527 ± 33 | 558 ± 10 | 545 ± 12 | 738 ± 16 | +| WideBeanBenchmark.wideDeser (200) | 87.0 ± 2.1 | 90.2 ± 2.4 | 88.4 ± 1.9 | 87.1 ± 1.5 | +| WideBeanBenchmark.wideSer (40) | 813 ± 14 | 1013 ± 26 | 825 ± 21 | 1418 ± 30 | +| WideBeanBenchmark.wideSer (200) | 134 ± 4 | 152 ± 3 | 138 ± 8 | 286 ± 2 | + +`ColdStartBenchmark.firstUse`, ms for 500 types (lower is better): classic +run stock 416 ± 11 and classic 911 ± 8, new run stock 415 ± 6 and new +925 ± 10. + +The new engine does not generate a reader for `BeanWithPropertyConstructor` +(property-based creator), so `constructorArrayDeser` measures the stock +deserializer in the new column. It also does not generate a reader for +`Wide200`: the generated method would be larger than 7000 bytes, which +HotSpot does not compile. diff --git a/blackbird/benchmarks/pom.xml b/blackbird/benchmarks/pom.xml index 059c74656..dad2f38a3 100644 --- a/blackbird/benchmarks/pom.xml +++ b/blackbird/benchmarks/pom.xml @@ -23,11 +23,9 @@ tools.jackson.module jackson-modules-base - 3.1.6 - - + 3.3.0-SNAPSHOT + + ../../pom.xml jackson-module-blackbird-benchmark @@ -35,8 +33,13 @@ Jackson Blackbird JMH benchmarks - 1.25 + 1.37 + 25 + 25 + 25 true + true + ${project.version} benchmarks @@ -50,7 +53,7 @@ tools.jackson.module jackson-module-blackbird - ${project.version} + ${blackbird.version} tools.jackson.module @@ -100,6 +103,10 @@ org.openjdk.jmh.Main + + + true + @@ -122,4 +129,19 @@ + + + + + classic-blackbird + + 3.2.2 + benchmarks-classic + + + ${project.basedir}/target/classic + + + diff --git a/blackbird/benchmarks/results/profile_graal.txt b/blackbird/benchmarks/results/profile_graal.txt deleted file mode 100644 index c6fc93021..000000000 --- a/blackbird/benchmarks/results/profile_graal.txt +++ /dev/null @@ -1,978 +0,0 @@ - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 4 iterations, 10 s each -# Measurement: 6 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser - -# Run progress: 33.33% complete, ETA 00:07:38 -# Fork: 1 of 1 -# Preparing profilers: LinuxPerfAsmProfiler -# Profilers consume stdout and stderr from target VM, use -v EXTRA to copy to console -# Warmup Iteration 1: 97.185 ops/ms -# Warmup Iteration 2: 469.084 ops/ms -# Warmup Iteration 3: 646.051 ops/ms -# Warmup Iteration 4: 645.022 ops/ms -Iteration 1: 646.729 ops/ms -Iteration 2: 644.663 ops/ms -Iteration 3: 653.936 ops/ms -Iteration 4: 644.031 ops/ms -Iteration 5: 650.329 ops/ms -Iteration 6: 654.378 ops/ms -# Processing profiler results: LinuxPerfAsmProfiler - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser": - 649.011 ±(99.9%) 12.772 ops/ms [Average] - (min, avg, max) = (644.031, 649.011, 654.378), stdev = 4.555 - CI (99.9%): [636.239, 661.783] (assumes normal distribution) - -Secondary result "com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser:·asm": -PrintAssembly processed: 595658 total address lines. -Perf output processed (skipped 46.061 seconds): - Column 1: cycles (180765 events) - -Hottest code regions (>10.00% "cycles" events): - -....[Hottest Region 1].............................................................................. -jvmci, level 4, com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString, version 2261 (1016 bytes) - - JVMCI data [0x00007f24916e7810,0x00007f24916e7820] = 16 - [Constant Pool (empty)] - [MachCode] - [Entry Point] - # {method} {0x00007f2440e2ba68} '_finishAndReturnString' '()Ljava/lang/String;' in 'com/fasterxml/jackson/core/json/UTF8StreamJsonParser' - # [sp+0x40] (sp of caller) - 0x00007f24916e64c0: 448b 5608 | 49c1 e203 - 0x00007f24916e64c8: ; {runtime_call ic_miss_stub} - 0x00007f24916e64c8: 493b c20f | 852f c86c | f590 6666 | 0f1f 8400 | 0000 0000 | 6666 6690 - [Verified Entry Point] - 0.03% 0x00007f24916e64e0: 8984 2400 | c0fe ff48 | 83ec 3848 | 896c 2430 - 0.02% 0x00007f24916e64f0: ;*getfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@1 (line 2447) - 0.02% 0x00007f24916e64f0: 448b 5e1c - 0.02% 0x00007f24916e64f4: ;*getfield _inputEnd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@7 (line 2448) - 0.02% 0x00007f24916e64f4: 448b 5640 | 0f1f 8400 | 0000 0000 | 453b d30f | 8ec4 0800 - 0x00007f24916e6508: ;*if_icmplt {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@10 (line 2448) - 0x00007f24916e6508: 0044 8b46 - 0x00007f24916e650c: ;*aload_0 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@0 (line 2447) - 0x00007f24916e650c: 7846 0fbe | 14c5 1c00 | 0000 428b | 3cc5 2c00 - 0x00007f24916e651c: ;*getfield _currentSegment {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@42 (line 712) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e651c: 0000 6690 | 4584 d20f | 85a4 0800 - 0x00007f24916e6528: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@34 (line 709) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e6528: 0042 c704 | c50c 0000 | 00ff ffff - 0x00007f24916e6534: ;*putfield _inputStart {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@2 (line 700) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e6534: ff42 c704 | c518 0000 | 0000 0000 - 0x00007f24916e6540: ;*putfield _currentSize {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@7 (line 701) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e6540: 0042 c704 | c510 0000 | 0000 0000 - 0x00007f24916e654c: ;*putfield _inputLen {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@12 (line 702) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e654c: 0041 807f | 3000 0f85 | d303 0000 | 4c8b ce42 | c704 c524 | 0000 0000 - 0x00007f24916e6564: ;*putfield _inputBuffer {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@17 (line 704) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e6564: 0000 0041 | 807f 3000 | 0f85 f703 | 0000 42c7 | 04c5 3000 | 0000 0000 - 0x00007f24916e657c: ;*putfield _resultString {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@22 (line 705) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e657c: 0000 6690 | 4180 7f30 | 000f 851a | 0400 0042 | c704 c534 | 0000 0000 - 0x00007f24916e6594: ;*putfield _resultArray {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@27 (line 706) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e6594: 0000 0085 | ff0f 84f7 - 0x00007f24916e659c: ;*ifnonnull {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@47 (line 713) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@28 (line 2453) - 0x00007f24916e659c: 0400 0048 - 0x00007f24916e65a0: ;*astore_3 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@31 (line 2453) - 0x00007f24916e65a0: c1e7 038b | 770c 418b - 0x00007f24916e65a8: ;*getfield _inputEnd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@38 (line 2456) - 0x00007f24916e65a8: 5140 418b - 0x00007f24916e65ac: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@44 (line 2456) - 0x00007f24916e65ac: c303 c63b | d00f 8e6d - 0x00007f24916e65b4: ; {oop([I{0x00000005cae53e00})} - 0x00007f24916e65b4: 0300 0048 | ba00 3ee5 | ca05 0000 | 0041 3bc3 | 0f8e 3b08 - 0x00007f24916e65c8: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@59 (line 2458) - 0x00007f24916e65c8: 0000 458b | 91b4 0000 - 0x00007f24916e65d0: ;*istore {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@48 (line 2456) - 0x00007f24916e65d0: 0042 8b0c | d50c 0000 | 000f 1f80 | 0000 0000 | 413b cb0f | 86a1 0700 | 0047 0fbe - 0x00007f24916e65ec: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@65 (line 2459) - 0x00007f24916e65ec: 44d3 1041 - 0x00007f24916e65f0: ;*iaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@76 (line 2460) - 0x00007f24916e65f0: 0fb6 d883 | 7c9a 1000 | 0f85 8604 - 0x00007f24916e65fc: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@77 (line 2460) - 0x00007f24916e65fc: 0000 6690 | 85f6 0f84 | a507 0000 | 450f b6c0 | 6644 8947 - 0x00007f24916e6610: ;*castore {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@114 (line 2468) - 0x00007f24916e6610: 1041 ffc3 | 458b c341 | bb01 0000 | 0066 6690 | e940 0000 - 0x00007f24916e6624: ;*iload_1 {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@56 (line 2458) - 0x00007f24916e6624: 0066 6666 | 0f1f 8400 - 0x00007f24916e662c: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@59 (line 2458) - 0x00007f24916e662c: 0000 0000 | 413b c80f | 86b7 0700 | 0043 0fbe - 0x00007f24916e663c: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@65 (line 2459) - 0x00007f24916e663c: 5cd0 100f - 0x00007f24916e6640: ;*iaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@76 (line 2460) - 0x00007f24916e6640: b6eb 837c | aa10 000f | 8522 0000 - 0x00007f24916e664c: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@77 (line 2460) - 0x00007f24916e664c: 0041 3bf3 | 0f86 6707 | 0000 0fb6 | db66 4289 - 0x00007f24916e665c: ;*castore {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@114 (line 2468) - 0x00007f24916e665c: 5c5f 1041 - 0x00007f24916e6660: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@103 (line 2467) - 0x00007f24916e6660: ffc0 41ff - 0x00007f24916e6664: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@108 (line 2468) - 0x00007f24916e6664: c341 3bc0 | 7fc6 e9b8 - 0x00007f24916e666c: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@59 (line 2458) - 0x00007f24916e666c: 0600 000f - 0x00007f24916e6670: ;*iand {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@69 (line 2459) - 0x00007f24916e6670: b6f3 83fe | 220f 85f1 - 0x00007f24916e6678: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@84 (line 2461) - 0x00007f24916e6678: 0600 0041 - 0x00007f24916e667c: ;*iload {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@80 (line 2461) - 0x00007f24916e667c: 8b59 788b | 14dd 1400 | 0000 448b | 14dd 2c00 - 0x00007f24916e668c: ;*getfield _currentSegment {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@36 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e668c: 0000 83fa | 010f 8dd5 - 0x00007f24916e6694: ;*ifle {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@9 (line 728) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e6694: 0600 0066 | 0f1f 8400 | 0000 0000 | 4585 db0f | 84c3 0600 - 0x00007f24916e66a8: ;*iload {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@80 (line 2461) - 0x00007f24916e66a8: 0042 8b2c | d50c 0000 | 0045 85db | 0f8c b206 - 0x00007f24916e66b8: ;*iflt {reexecute=0 rethrow=0 return_oop=0} - ; - java.lang.String::checkBoundsOffCount@5 (line 3740) - ; - java.lang.String::rangeCheck@4 (line 298) - ; - java.lang.String::<init>@7 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e66b8: 0000 8bd5 - 0.01% 0x00007f24916e66bc: ;*isub {reexecute=0 rethrow=0 return_oop=0} - ; - java.lang.String::checkBoundsOffCount@11 (line 3740) - ; - java.lang.String::rangeCheck@4 (line 298) - ; - java.lang.String::<init>@7 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0.01% 0x00007f24916e66bc: 412b d390 | 85d2 0f8c - 0x00007f24916e66c4: ;*if_icmple {reexecute=0 rethrow=0 return_oop=0} - ; - java.lang.String::checkBoundsOffCount@12 (line 3740) - ; - java.lang.String::rangeCheck@4 (line 298) - ; - java.lang.String::<init>@7 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e66c4: a406 0000 - 0x00007f24916e66c8: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@90 (line 2462) - 0x00007f24916e66c8: 41ff c045 - 0x00007f24916e66cc: ;*putfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@91 (line 2462) - 0x00007f24916e66cc: 8941 1c44 | 891c dd18 - 0x00007f24916e66d4: ;*putfield _currentSize {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@2 (line 726) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e66d4: 0000 004d | 8b87 2001 | 0000 498b | b730 0100 | 0041 8bfb | 488d 7f17 | 4883 e7f8 | 488b d749 - 0x00007f24916e66f4: ;*newarray {reexecute=0 rethrow=0 return_oop=0} - ; - java.lang.StringUTF16::compress@1 (line 161) - ; - java.lang.String::<init>@36 (line 3651) - ; - java.lang.String::<init>@10 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e66f4: 03d0 448b | cb49 c1e1 - 0x00007f24916e66fc: ;*getfield _textBuffer {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@95 (line 2463) - 0x00007f24916e66fc: 0366 6690 | 483b d60f | 8768 0400 | 0049 8997 | 2001 0000 | 420f 1884 | 0700 0100 | 0042 0f18 - 0x00007f24916e671c: 8407 4001 | 0000 420f | 1884 0780 | 0100 0045 | 8958 0c49 | c700 0100 | 0000 41c7 - 0x00007f24916e6738: ; {metadata({type array byte})} - 0x00007f24916e6738: 4008 0c01 | 00f8 488d | 4ff0 4881 | f900 0800 | 000f 8348 | 0400 0048 | c7c6 1000 | 0000 e911 - 0x00007f24916e6758: 0000 000f | 1f44 0000 | 49c7 0430 | 0000 0000 | 488d 7608 | 483b fe77 - 0x00007f24916e6770: ;*newarray {reexecute=0 rethrow=0 return_oop=0} - ; - java.lang.StringUTF16::compress@1 (line 161) - ; - java.lang.String::<init>@36 (line 3651) - ; - java.lang.String::<init>@10 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e6770: ef44 3bdd | 0f8f 3b06 - 0x00007f24916e6778: ;* unwind (locked if synchronized) - ; - java.lang.StringUTF16::compress@-3 - ; - java.lang.StringUTF16::compress@9 (line 162) - ; - java.lang.String::<init>@36 (line 3651) - ; - java.lang.String::<init>@10 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e6778: 0000 49c1 - 0x00007f24916e677c: ;*getfield _currentSegment {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@36 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e677c: e203 498d | 7210 498d | 7810 418b | d352 8bc2 | b900 ff00 | ff83 e2f0 | 83e0 0f85 | d20f 8447 - 0x00007f24916e679c: 0000 00c5 | f96e c1c5 | f970 c000 | c5e1 efdb | 488d 3456 | 488d 3c17 | 48f7 dac5 | fa6f 0c56 - 0.13% 0x00007f24916e67bc: c5e1 ebd9 | c5fa 6f54 | 5610 c5e1 | ebda c4e2 | 7917 d80f | 8573 0000 | 00c5 f167 | cac5 fa7f - 0x00007f24916e67dc: 0c17 6690 | 4883 c210 | 75d1 8bd0 | 83e2 f883 | e007 85d2 | 7428 c5f9 | 6ec1 c5f9 | 70c0 00c5 - 0x00007f24916e67fc: e9ef d2c5 | fa6f 0ec4 | e279 17c8 | 753e c5f1 | 67ca c5f9 | d60f 4883 | c610 4883 | c708 8bd0 - 0.01% 0x00007f24916e681c: 0f1f 4000 | 85d2 7421 | 488d 3456 | 488d 3c17 | 48f7 da0f | b704 56f7 | c000 ff00 | 0075 0d88 - 0x00007f24916e683c: 0417 6690 | 48ff c275 | ea58 eb06 | 33c0 4883 - 0x00007f24916e684c: ;* unwind (locked if synchronized) - ; - java.lang.StringUTF16::compress@-3 - ; - java.lang.StringUTF16::compress@9 (line 162) - ; - java.lang.String::<init>@36 (line 3651) - ; - java.lang.String::<init>@10 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e684c: c408 443b | d80f 85f7 - 0x00007f24916e6854: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - java.lang.StringUTF16::compress@13 (line 162) - ; - java.lang.String::<init>@36 (line 3651) - ; - java.lang.String::<init>@10 (line 294) - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@41 (line 733) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e6854: 0400 0049 | 8bb7 2001 | 0000 488d | 5618 49ba - 0x00007f24916e6864: ; {metadata('java/lang/String')} - 0x00007f24916e6864: e81c 00c0 | 0700 0000 | 493b 9730 | 0100 000f | 87dc 0200 | 0049 8997 | 2001 0000 | 0f18 8618 - 0x00007f24916e6884: 0100 0049 | 8b92 b800 | 0000 4889 | 16c7 4608 - 0x00007f24916e6894: ; {metadata('java/lang/String')} - 0x00007f24916e6894: 9d03 00f8 | c746 0c00 | 0000 0048 | c746 1000 | 0000 004c | 8bde 49c1 | e803 4589 | 430c 4180 - 0x00007f24916e68b4: 7f30 000f | 8522 0100 | 0049 8bc3 | 48c1 e803 | 8904 dd30 - 0x00007f24916e68c8: ;*putfield _resultString {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::setCurrentAndReturn@47 (line 734) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@99 (line 2463) - 0x00007f24916e68c8: 0000 0049 | 8bc1 4933 | c348 f7c0 | 0000 e0ff | 0f84 4101 | 0000 6690 | 4d85 db0f | 844f 0100 - 0x00007f24916e68e8: 0048 be00 | 80f6 8224 | 7f00 0049 | c1e9 0966 | 0f1f 8400 | 0000 0000 | 4180 3c31 | 080f 8546 - 0x00007f24916e6908: 0100 0049 - 0x00007f24916e690c: ;*areturn {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString@102 (line 2463) - 0x00007f24916e690c: 8bc3 488b | 6c24 3048 | 83c4 3849 | 8b8f 1001 - 0x00007f24916e691c: ; {poll_return} - 0x00007f24916e691c: 0000 8501 | c5f8 77c3 | 8bc2 e98c | fcff ff4e | 8d14 c524 | 0000 0045 | 8b12 4585 | d20f 840e - 0x00007f24916e693c: 0200 0049 | 8b6f 3849 | c1e2 0348 | 85ed 0f84 | 6c02 0000 | 498b 5748 | 4c89 5415 | f84c 8d55 - 0x00007f24916e695c: f84d 8957 | 384c 8bce | e9f2 fbff - 0x00007f24916e6968: ;*putfield _inputBuffer {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.util.TextBuffer::emptyAndGetCurrentSegment@17 (line 704) -.................................................................................................... - 22.09% - -....[Hottest Region 2].............................................................................. -jvmci, level 4, com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName, version 2266 (1098 bytes) - - dependencies [0x00007f24917d8030,0x00007f24917d8038] = 8 - nul chk table [0x00007f24917d8038,0x00007f24917d8090] = 88 - JVMCI data [0x00007f24917d8090,0x00007f24917d80a0] = 16 - [Constant Pool (empty)] - [MachCode] - [Entry Point] - # {method} {0x00007f2440e27a18} 'nextFieldName' '(Lcom/fasterxml/jackson/core/SerializableString;)Z' in 'com/fasterxml/jackson/core/json/UTF8StreamJsonParser' - # this: rsi:rsi = 'com/fasterxml/jackson/core/json/UTF8StreamJsonParser' - # parm0: rdx:rdx = 'com/fasterxml/jackson/core/SerializableString' - # [sp+0x40] (sp of caller) - 0.00% 0x00007f24917d5440: 448b 5608 | 49c1 e203 - 0x00007f24917d5448: ; {runtime_call ic_miss_stub} - 0x00007f24917d5448: 493b c20f | 85af d85d | f590 6666 | 0f1f 8400 | 0000 0000 | 6666 6690 - [Verified Entry Point] - 0.00% 0x00007f24917d5460: 8984 2400 | c0fe ff48 | 83ec 3848 | 896c 2430 | 4889 7424 | 1044 8b56 - 0x00007f24917d5478: ;*getfield _currToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@6 (line 909) - 0x00007f24917d5478: 140f 1f80 | 0000 0000 - 0.00% 0x00007f24917d5480: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025ec0})} - 0.00% 0x00007f24917d5480: 4181 fad8 | 4b60 b90f | 84dc 1100 | 0080 bea0 | 0000 0000 | 0f85 a413 - 0x00007f24917d5498: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@26 (line 913) - 0x00007f24917d5498: 0000 8b46 - 0x00007f24917d549c: ;*getfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@1 (line 2992) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d549c: 1c44 8b5e - 0x00007f24917d54a0: ;*getfield _inputEnd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@5 (line 2992) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d54a0: 4044 3bd8 | 0f8e e913 - 0x00007f24917d54a8: ;*if_icmplt {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@8 (line 2992) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d54a8: 0000 448b | 86b4 0000 - 0x00007f24917d54b0: ;*aload_0 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@0 (line 908) - 0x00007f24917d54b0: 0046 8b0c | c50c 0000 | 000f 1f80 | 0000 0000 | 443b c80f | 86fd 1300 - 0x00007f24917d54c8: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@38 (line 2997) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d54c8: 0042 0fb6 - 0x00007f24917d54cc: ;*iand {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@42 (line 2997) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d54cc: 4cc0 1083 | f921 0f82 - 0x00007f24917d54d4: ;*if_icmple {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@47 (line 2998) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d54d4: 0b12 0000 | 83f9 230f | 1f44 0000 | 0f8c 2900 | 0000 83f9 | 230f 8418 | 1300 0083 | f92f 0f84 - 0x00007f24917d54f4: 8f11 0000 | 83f9 5d0f | 1f44 0000 | 0f84 4313 | 0000 83f9 | 7d0f 84c2 - 0x00007f24917d550c: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@53 (line 2999) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d550c: 1300 008b - 0x00007f24917d5510: ;*aload_0 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@0 (line 908) - 0x00007f24917d5510: 5e70 448b | 14dd 0c00 | 0000 8b3c | dd10 0000 - 0x00007f24917d5520: ;*getfield _index {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::expectComma@2 (line 192) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@100 (line 937) - 0x00007f24917d5520: 0041 83fa | 020f 8529 - 0x00007f24917d5528: ;*ifne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@190 (line 951) - 0x00007f24917d5528: 1300 0048 | 85d2 0f84 | ef03 0000 | 448b 5208 | 49c1 e203 - 0.06% 0x00007f24917d553c: ; {metadata('com/fasterxml/jackson/core/io/SerializedString')} - 0.06% 0x00007f24917d553c: 48bd 7862 | 28c0 0700 | 0000 493b | ea0f 8540 - 0x00007f24917d554c: ;*invokeinterface asQuotedUTF8 {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@216 (line 961) - 0x00007f24917d554c: 1100 00c7 | 4654 0000 - 0x00007f24917d5554: ;*putfield _numTypesValid {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@2 (line 908) - 0x00007f24917d5554: 0000 448b - 0x00007f24917d5558: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@34 (line 2997) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0x00007f24917d5558: e841 ffc5 - 0.01% 0x00007f24917d555c: ;*putfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@35 (line 2997) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@34 (line 916) - 0.01% 0x00007f24917d555c: 4489 6e1c | 4180 7f30 | 000f 855c | 0600 004c | 8bf6 41c7 | 8684 0000 | 0000 0000 - 0x00007f24917d5578: ;*putfield _binaryValue {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@55 (line 922) - 0x00007f24917d5578: 00ff c789 | 3cdd 1000 - 0x00007f24917d5580: ;*putfield _index {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::expectComma@8 (line 192) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@100 (line 937) - 0x00007f24917d5580: 0000 83ff | 010f 8c32 - 0x00007f24917d5588: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@103 (line 937) - 0x00007f24917d5588: 0600 0045 | 3bcd 0f86 - 0x00007f24917d5590: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@26 (line 2936) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d5590: 5212 0000 | 430f b674 - 0x00007f24917d5598: ;*iand {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@30 (line 2936) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d5598: c510 660f | 1f44 0000 | 83fe 210f | 82b2 1100 - 0x00007f24917d55a8: ;*if_icmple {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@35 (line 2937) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d55a8: 0083 f92c | 0f85 0b12 - 0x00007f24917d55b0: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@109 (line 938) - 0x00007f24917d55b0: 0000 453b | dd0f 8ec2 - 0x00007f24917d55b8: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@8 (line 2935) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d55b8: 1200 000f | 1f44 0000 | 83fe 2f0f | 84bf 1200 - 0x00007f24917d55c8: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@41 (line 2938) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d55c8: 0083 fe23 | 0f84 e011 - 0x00007f24917d55d0: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@47 (line 2938) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d55d0: 0000 6666 | 0f1f 8400 | 0000 0000 | 6666 6690 | 41f7 460c | 0008 0000 | 0f85 4011 - 0x00007f24917d55ec: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@160 (line 944) - 0x00007f24917d55ec: 0000 448d - 0x00007f24917d55f0: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@22 (line 2936) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d55f0: 5002 4589 - 0x00007f24917d55f4: ;*putfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS@23 (line 2936) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@148 (line 941) - 0x00007f24917d55f4: 561c 83fe | 220f 8508 - 0x00007f24917d55fc: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@212 (line 959) - 0x00007f24917d55fc: 1100 0041 - 0x00007f24917d5600: ;*getfield _currInputRow {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateNameLocation@2 (line 3785) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@206 (line 958) - 0x00007f24917d5600: 8b76 4441 | 89b6 9800 - 0x00007f24917d5608: ;*putfield _nameStartRow {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateNameLocation@5 (line 3785) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@206 (line 958) - 0x00007f24917d5608: 0000 4589 | 9694 0000 - 0x00007f24917d5610: ;*putfield _nameStartOffset {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateNameLocation@15 (line 3787) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@206 (line 958) - 0x00007f24917d5610: 0045 2b56 - 0x00007f24917d5614: ;*isub {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateNameLocation@24 (line 3788) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@206 (line 958) - 0x00007f24917d5614: 4845 8996 - 0x00007f24917d5618: ;*putfield _nameStartCol {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateNameLocation@25 (line 3788) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@206 (line 958) - 0x00007f24917d5618: 9c00 0000 - 0.65% 0x00007f24917d561c: ;*getfield _quotedUTF8Ref {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.io.SerializedString::asQuotedUTF8@1 (line 119) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@216 (line 961) - 0.65% 0x00007f24917d561c: 8b72 1049 | bb00 80f6 | 8224 7f00 - 0x00007f24917d5628: ;*putfield _currentName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::setCurrentName@2 (line 197) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@289 (line 973) - 0x00007f24917d5628: 004c 895c | 2420 85f6 | 0f84 b70e - 0x00007f24917d5634: ;*ifnonnull {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.io.SerializedString::asQuotedUTF8@6 (line 120) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@216 (line 961) - 0x00007f24917d5634: 0000 48c1 - 0x00007f24917d5638: ;*astore_3 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@221 (line 961) - 0x00007f24917d5638: e603 448b | 560c 418b - 0x00007f24917d5640: ;*getfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@227 (line 965) - 0x00007f24917d5640: 6e1c 458b - 0x00007f24917d5644: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@232 (line 965) - 0x00007f24917d5644: c244 03c5 - 0x00007f24917d5648: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@234 (line 965) - 0x00007f24917d5648: 418d 4004 | 413b 4640 | 0f8d 6410 - 0x00007f24917d5654: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@239 (line 965) - 0x00007f24917d5654: 0000 418b | 86b4 0000 - 0x00007f24917d565c: ;*astore_3 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@221 (line 961) - 0x00007f24917d565c: 0044 8b0c | c50c 0000 | 0045 3bc8 | 0f86 6d10 - 0x00007f24917d566c: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@257 (line 968) - 0x00007f24917d566c: 0000 4180 | 7cc0 1022 | 0f85 a311 - 0x00007f24917d5678: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@260 (line 968) - 0x00007f24917d5678: 0000 660f | 1f44 0000 | 4585 d20f | 842d 0500 - 0x00007f24917d5688: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@276 (line 972) - 0x00007f24917d5688: 0044 3bcd | 0f86 d110 - 0x00007f24917d5690: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@318 (line 978) - 0x00007f24917d5690: 0000 0fbe - 0x00007f24917d5694: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@311 (line 978) - 0x00007f24917d5694: 4e10 6666 | 0f1f 8400 | 0000 0000 | 3a4c c510 | 0f85 7c10 - 0x00007f24917d56a8: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@319 (line 978) - 0x00007f24917d56a8: 0000 ffc5 | bb01 0000 | 008b cde9 - 0x00007f24917d56b4: ;*iload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@272 (line 972) - 0x00007f24917d56b4: 4100 0000 | 0f1f 8400 - 0x00007f24917d56bc: ;*invokevirtual setCurrentName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@289 (line 973) - 0x00007f24917d56bc: 0000 0000 | 443b d30f | 86c3 1000 - 0x00007f24917d56c8: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@311 (line 978) - 0x00007f24917d56c8: 0044 3bc9 | 0f86 4011 - 0x00007f24917d56d0: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@318 (line 978) - 0x00007f24917d56d0: 0000 0fbe - 0x00007f24917d56d4: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@311 (line 978) - 0x00007f24917d56d4: 6c1e 1066 | 0f1f 8400 | 0000 0000 | 403a 6cc1 | 100f 853d - 0x00007f24917d56e8: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@319 (line 978) - 0x00007f24917d56e8: 1100 0049 | 8baf 1001 - 0x00007f24917d56f0: ; ImmutableOopMap {rax=NarrowOop rdx=Oop rsi=Oop r14=Oop [16]=Oop } - ;*iload {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@272 (line 972) - ; {poll} - 0x00007f24917d56f0: 0000 8545 - 0x00007f24917d56f4: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@325 (line 981) - 0x00007f24917d56f4: 00ff c3ff - 0x00007f24917d56f8: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@328 (line 982) - 0x00007f24917d56f8: c141 3bc8 - 0.01% 0x00007f24917d56fc: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@276 (line 972) - 0.01% 0x00007f24917d56fc: 75c2 458b - 0x00007f24917d5700: ;*aload_0 {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@279 (line 973) - 0x00007f24917d5700: 4670 428b | 34c5 2000 | 0000 85f6 | 0f85 ea10 - 0x00007f24917d5710: ;*ifnull {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::setCurrentName@9 (line 198) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@289 (line 973) - 0x00007f24917d5710: 0000 8bf1 - 0x00007f24917d5714: ;*iadd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@296 (line 974) - 0x00007f24917d5714: ffc6 443b | ce0f 867f - 0x00007f24917d571c: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@8 (line 1103) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d571c: 1100 0090 | 807c c610 | 3a0f 85c6 - 0x00007f24917d5728: ;*if_icmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@13 (line 1104) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d5728: 1000 008d - 0x00007f24917d572c: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@5 (line 1103) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d572c: 7102 443b | ce0f 86fc | 1000 0044 | 0fbe 4cc6 - 0x00007f24917d573c: ;*baload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@24 (line 1105) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d573c: 1066 6690 | 4183 f921 | 0f8c 9211 - 0x00007f24917d5748: ;*if_icmple {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@29 (line 1106) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d5748: 0000 4183 | f92f 0f84 - 0x00007f24917d5750: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@35 (line 1107) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d5750: f70e 0000 | 4183 f923 | 0f84 f20f - 0x00007f24917d575c: ;*if_icmpeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@41 (line 1107) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d575c: 0000 6690 | 4180 7f30 | 000f 85d4 - 0x00007f24917d5768: ;*putfield _currentName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::setCurrentName@2 (line 197) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@289 (line 973) - 0x00007f24917d5768: 0400 008b - 0x00007f24917d576c: ;*getfield _value {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.io.SerializedString::getValue@1 (line 91) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@284 (line 973) - 0x00007f24917d576c: 720c 4289 | 34c5 2800 | 0000 48c1 - 0x00007f24917d5778: ;*putfield _currentName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::setCurrentName@2 (line 197) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@289 (line 973) - 0x00007f24917d5778: e603 49c1 - 0x00007f24917d577c: ;*getfield _parsingContext {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@280 (line 973) - 0x00007f24917d577c: e003 4d8b | d04c 33d6 | 49f7 c200 | 00e0 ff0f | 841a 0000 | 0048 85f6 | 0f84 1100 | 0000 49c1 - 0x00007f24917d579c: e809 6690 | 4380 3c18 | 080f 8553 - 0x00007f24917d57a8: ;*putfield _currentName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::setCurrentName@2 (line 197) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@289 (line 973) - 0x00007f24917d57a8: 0500 0044 - 0x00007f24917d57ac: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@21 (line 1105) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d57ac: 8d41 0345 - 0x00007f24917d57b0: ;*putfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipColonFast@46 (line 1108) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@297 (line 974) - 0x00007f24917d57b0: 8946 1c41 | 807f 3000 | 0f85 4704 - 0x00007f24917d57bc: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025ec0})} - 0x00007f24917d57bc: 0000 41c7 | 4614 d84b - 0x00007f24917d57c4: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025ec0})} - 0x00007f24917d57c4: 60b9 48be | c05e 02cb | 0500 0000 | 4d8b d64c | 33d6 6666 | 0f1f 8400 | 0000 0000 | 49f7 c200 - 0x00007f24917d57e4: 00e0 ff0f | 841e 0000 | 0048 85f6 | 0f84 1500 | 0000 498b | f648 c1ee | 0966 6690 | 4280 3c1e - 0x00007f24917d5804: 080f 85de - 0x00007f24917d5808: ;*putfield _currToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@4 (line 1151) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d5808: 0400 0041 - 0x00007f24917d580c: ;*getfield _currInputRow {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@2 (line 3776) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d580c: 8b76 4441 - 0x00007f24917d5810: ;*putfield _tokenInputRow {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@5 (line 3776) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d5810: 8976 4c49 - 0x00007f24917d5814: ;*i2l {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@19 (line 3778) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d5814: 63f0 4903 - 0x00007f24917d5818: ;*ladd {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@20 (line 3778) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d5818: 7620 4989 - 0x00007f24917d581c: ;*putfield _tokenInputTotal {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@21 (line 3778) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d581c: 7628 418b | f041 2b76 - 0x00007f24917d5824: ;*isub {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@30 (line 3779) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d5824: 4841 8976 - 0x00007f24917d5828: ;*putfield _tokenInputCol {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_updateLocation@31 (line 3779) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@8 (line 1152) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d5828: 5041 83f9 | 220f 8482 | 0000 0041 | 83f9 310f | 84f2 0000 | 0041 83f9 | 320f 84e8 | 0000 0041 - 0x00007f24917d5848: 83f9 5b0f | 842e 0400 | 0041 83f9 | 330f 84d4 | 0000 0041 | 83f9 3490 | 0f84 c900 | 0000 4183 - 0x00007f24917d5868: f936 0f84 | bf00 0000 | 4183 f937 | 0f84 b500 | 0000 4183 | f97b 6690 | 0f84 f104 | 0000 4183 - 0x00007f24917d5888: f935 0f84 | 9f00 0000 | 4183 f930 | 0f84 9500 | 0000 4183 | f938 6690 | 0f84 8900 | 0000 4183 - 0x00007f24917d58a8: f939 0f84 | 7f00 0000 | e9e2 0d00 - 0x00007f24917d58b4: ;*lookupswitch {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@12 (line 1154) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d58b4: 0041 c686 | a000 0000 - 0x00007f24917d58bc: ;*putfield _tokenIncomplete {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_isNextTokenNameYes@170 (line 1156) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName@303 (line 975) - 0x00007f24917d58bc: 0166 6690 | 4180 7f30 | 000f 855a - 0x00007f24917d58c8: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025e90})} - 0x00007f24917d58c8: 0400 0041 | c746 74d2 -.................................................................................................... - 21.52% - -....[Hottest Region 3].............................................................................. -jvmci, level 4, com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize, version 2396 (780 bytes) - - 0.00% 0x00007f2491954ea0: 48f7 c600 | 00e0 ff0f | 841d 0000 | 004d 85db | 0f84 1400 - 0x00007f2491954eb4: ;*putfield _currentValue {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.JsonReadContext::setCurrentValue@2 (line 96) - ; - com.fasterxml.jackson.core.base.ParserBase::setCurrentValue@5 (line 256) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@112 (line 138) - 0x00007f2491954eb4: 0000 488b | f048 c1ee | 0966 6690 | 803c 3e08 | 0f85 bb11 - 0x00007f2491954ec8: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb026068})} - 0x00007f2491954ec8: 0000 49ba | 6860 02cb | 0500 0000 | 4c89 5424 | 280f 1f80 | 0000 0000 | 4585 c90f | 8493 0b00 - 0x00007f2491954ee8: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@170 (line 148) - 0x00007f2491954ee8: 0044 894c | 245c 8b73 - 0x00007f2491954ef0: ;*return {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.JsonReadContext::setCurrentValue@5 (line 97) - ; - com.fasterxml.jackson.core.base.ParserBase::setCurrentValue@5 (line 256) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@112 (line 138) - 0x00007f2491954ef0: 608b 2cf5 | 0c00 0000 | 468b 04c5 - 0x00007f2491954efc: ;*return {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.core.json.JsonReadContext::setCurrentValue@5 (line 97) - ; - com.fasterxml.jackson.core.base.ParserBase::setCurrentValue@5 (line 256) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@112 (line 138) - 0x00007f2491954efc: 1000 0000 - 0.00% 0x00007f2491954f00: ; implicit exception: deoptimizes - ; ImmutableOopMap {rax=Oop rcx=Oop rdx=Oop rbx=Oop rsi=NarrowOop r8=NarrowOop r11=Oop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - 0.00% 0x00007f2491954f00: 468b 2cc5 | 0800 0000 | 85ed 0f84 | 7c21 0000 | 4489 6c24 | 588b 34f5 | 1000 0000 | 8bc6 48c1 - 0x00007f2491954f20: ;*aaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@189 (line 150) - 0x00007f2491954f20: e003 49c1 - 0x00007f2491954f24: ;*aaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@179 (line 149) - 0x00007f2491954f24: e003 488b - 0x00007f2491954f28: ;*invokevirtual nextFieldName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@190 (line 150) - 0x00007f2491954f28: f248 8bd0 | 4c89 4424 | 2048 b870 | 3131 c007 | 0000 000f | 1f40 0090 | 0f1f 8000 - 0x00007f2491954f44: ; {virtual_call} - 0x00007f2491954f44: 0000 00e8 - 0x00007f2491954f48: ; ImmutableOopMap {[32]=Oop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - ;*invokevirtual nextFieldName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@190 (line 150) - 0x00007f2491954f48: f404 e8ff | 9085 c00f | 84b7 2100 - 0x00007f2491954f54: ;*ifne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@193 (line 150) - 0x00007f2491954f54: 0048 8b54 | 2440 448b - 0x00007f2491954f5c: ;*getfield _parsingContext {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@24 (line 883) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f5c: 5a70 8b72 - 0x00007f2491954f60: ;*getfield _currToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@1 (line 715) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f60: 1445 8bc3 | 49c1 e003 - 0x00007f2491954f68: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025ec0})} - 0x00007f2491954f68: 81fe d84b | 60b9 0f85 - 0x00007f2491954f70: ;*if_acmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@7 (line 715) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f70: 130d 0000 - 0.03% 0x00007f2491954f74: ;*putfield _nameCopied {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@2 (line 875) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0.03% 0x00007f2491954f74: c642 6900 | 0f1f 8400 | 0000 0000 | 4180 7f30 | 000f 85c4 - 0x00007f2491954f88: ;*putfield _nextToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@12 (line 877) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f88: 1100 0044 - 0x00007f2491954f8c: ;*getfield _nextToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@6 (line 876) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f8c: 8b52 74c7 | 4274 0000 - 0x00007f2491954f94: ;*putfield _nextToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@12 (line 877) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f94: 0000 448b - 0x00007f2491954f98: ;*getfield _tokenInputRow {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@28 (line 883) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f98: 4a4c 8b4a - 0x00007f2491954f9c: ;*getfield _tokenInputCol {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@32 (line 883) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954f9c: 5041 8bda | 48c1 e303 - 0x00007f2491954fa4: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025f50})} - 0x00007f2491954fa4: 4181 faea | 4b60 b90f | 8443 1300 - 0x00007f2491954fb0: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025f20})} - 0x00007f2491954fb0: 0041 81fa | e44b 60b9 | 0f84 e714 - 0x00007f2491954fbc: ;*putfield _parsingContext {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@67 (line 885) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954fbc: 0000 4889 | 5c24 1844 | 8954 2414 | 488b 7c24 - 0x00007f2491954fcc: ;*aload_0 {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@70 (line 887) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491954fcc: 6041 807f | 3000 0f85 | ad11 0000 | 448b 5424 | 1444 8952 | 1448 8bf2 | 4833 7424 | 1848 f7c6 - 0x00007f2491954fec: 0000 e0ff | 0f84 c911 | 0000 488b | 5c24 180f | 1f44 0000 | 4885 db0f | 84c0 1100 | 0048 8bf2 - 0.02% 0x00007f249195500c: 48c1 ee09 | 803c 3e08 | 0f85 7513 | 0000 448b - 0x00007f249195501c: ;*putfield _currToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@73 (line 887) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f249195501c: 6c24 584a | 8b1c ed30 | 0300 0048 | 8b43 3848 | 8b74 2420 | 4c8b da48 - 0x00007f2491955034: ;*invokevirtual deserializeAndSet {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@226 (line 159) - 0x00007f2491955034: 8b4c 2438 | 4c8b 4424 - 0x00007f249195503c: ; ImmutableOopMap {[32]=Oop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - ;*invokevirtual deserializeAndSet {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@226 (line 159) - 0x00007f249195503c: 30ff d090 | 41ba 0100 | 0000 e97c - 0x00007f2491955048: ;*iload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@166 (line 148) - 0x00007f2491955048: 0100 000f - 0x00007f249195504c: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@170 (line 148) - 0x00007f249195504c: 1f44 0000 - 0x00007f2491955050: ;*getfield _orderedProperties {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@174 (line 149) - 0x00007f2491955050: 8b5e 6466 | 0f1f 8400 | 0000 0000 - 0.00% 0x00007f249195505c: ;*iload {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@166 (line 148) - 0.00% 0x00007f249195505c: 6666 6690 - 0x00007f2491955060: ; implicit exception: deoptimizes - ; ImmutableOopMap {rdx=Oop rbx=NarrowOop rsi=Oop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - 0x00007f2491955060: 443b 14dd | 0c00 0000 | 0f83 b81e - 0x00007f249195506c: ;*aaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@179 (line 149) - 0x00007f249195506c: 0000 8b46 - 0x00007f2491955070: ;*iload {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@166 (line 148) - 0x00007f2491955070: 6044 3b14 | c50c 0000 | 000f 83a7 - 0x00007f249195507c: ;*aaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@189 (line 150) - 0x00007f249195507c: 1e00 0048 - 0x00007f2491955080: ;*getfield _orderedProperties {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@174 (line 149) - 0x00007f2491955080: c1e3 0342 - 0x00007f2491955084: ;*iload {reexecute=1 rethrow=0 return_oop=0} - ; - (reexecute) com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@166 (line 148) - 0x00007f2491955084: 8b5c 9310 - 0.04% 0x00007f2491955088: ; implicit exception: deoptimizes - ; ImmutableOopMap {rax=NarrowOop rdx=Oop rbx=NarrowOop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - 0.04% 0x00007f2491955088: 8b2c dd08 | 0000 0048 - 0x00007f2491955090: ;*getfield _orderedPropertyNames {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@184 (line 150) - 0x00007f2491955090: c1e0 0342 | 8b44 9010 - 0x00007f2491955098: ;*aaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@189 (line 150) - 0x00007f2491955098: 48c1 e003 - 0x00007f249195509c: ;*aaload {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@179 (line 149) - 0x00007f249195509c: 48c1 e303 | 488b f248 - 0x00007f24919550a4: ;*invokevirtual nextFieldName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@190 (line 150) - 0x00007f24919550a4: 8bd0 4489 | 5424 5848 | 895c 2418 | 896c 2454 | 48b8 7031 | 31c0 0700 | 0000 9090 | 0f1f 8000 - 0x00007f24919550c4: ; {virtual_call} - 0x00007f24919550c4: 0000 00e8 - 0x00007f24919550c8: ; ImmutableOopMap {[24]=Oop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - ;*invokevirtual nextFieldName {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@190 (line 150) - 0x00007f24919550c8: 7403 e8ff | 9085 c00f | 8482 1d00 - 0x00007f24919550d4: ;*ifne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@193 (line 150) - 0x00007f24919550d4: 0048 8b54 | 2440 448b - 0x00007f24919550dc: ;*getfield _parsingContext {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@24 (line 883) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f24919550dc: 5a70 8b72 - 0x00007f24919550e0: ;*getfield _currToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@1 (line 715) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f24919550e0: 1445 8bc3 | 49c1 e003 - 0.02% 0x00007f24919550e8: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025ec0})} - 0.02% 0x00007f24919550e8: 81fe d84b | 60b9 0f85 - 0x00007f24919550f0: ;*if_acmpne {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@7 (line 715) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f24919550f0: f100 0000 - 0.05% 0x00007f24919550f4: ;*putfield _nameCopied {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@2 (line 875) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0.05% 0x00007f24919550f4: c642 6900 | 0f1f 8400 | 0000 0000 | 4180 7f30 | 000f 85da - 0x00007f2491955108: ;*putfield _nextToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@12 (line 877) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491955108: 0200 0044 - 0x00007f249195510c: ;*getfield _nextToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@6 (line 876) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f249195510c: 8b52 74c7 | 4274 0000 - 0x00007f2491955114: ;*putfield _nextToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@12 (line 877) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491955114: 0000 448b - 0x00007f2491955118: ;*getfield _tokenInputRow {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@28 (line 883) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f2491955118: 4a4c 8b4a - 0x00007f249195511c: ;*getfield _tokenInputCol {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@32 (line 883) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f249195511c: 5041 8bda | 48c1 e303 - 0.01% 0x00007f2491955124: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025f50})} - 0.01% 0x00007f2491955124: 4181 faea | 4b60 b90f | 84e2 0300 - 0x00007f2491955130: ; {oop(a 'com/fasterxml/jackson/core/JsonToken'{0x00000005cb025f20})} - 0x00007f2491955130: 0041 81fa | e44b 60b9 | 0f84 9104 - 0x00007f249195513c: ;*putfield _parsingContext {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@67 (line 885) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f249195513c: 0000 4889 | 5c24 0844 | 8954 2414 | 488b 7c24 - 0x00007f249195514c: ;*aload_0 {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@70 (line 887) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f249195514c: 6041 807f | 3000 0f85 | c702 0000 | 448b 5424 | 1444 8952 | 1448 8bf2 | 4833 7424 | 0848 f7c6 - 0x00007f249195516c: 0000 e0ff | 0f84 de02 | 0000 488b | 5c24 080f | 1f44 0000 | 4885 db0f | 84d4 0200 | 0048 8bf2 - 0.13% 0x00007f249195518c: 48c1 ee09 | 803c 3e08 | 0f85 e503 | 0000 8b6c - 0x00007f249195519c: ;*putfield _currToken {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextAfterName@73 (line 887) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@11 (line 716) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f249195519c: 2454 488b | 1ced 3003 | 0000 488b | 4338 488b | 7424 184c | 8bda 488b - 0x00007f24919551b4: ;*invokevirtual deserializeAndSet {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@226 (line 159) - 0x00007f24919551b4: 4c24 384c | 8b44 2430 - 0.10% 0x00007f24919551bc: ; ImmutableOopMap {[24]=Oop [40]=Oop [48]=Oop [56]=Oop [64]=Oop [72]=Oop } - ;*invokevirtual deserializeAndSet {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@226 (line 159) - 0.10% 0x00007f24919551bc: ffd0 9044 | 8b54 2458 - 0.04% 0x00007f24919551c4: ;*iinc {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@247 (line 148) - 0.04% 0x00007f24919551c4: 41ff c244 | 8b4c 245c | 488b 5424 | 4048 8b74 | 2448 453b | ca0f 8f71 | feff ff90 | e997 0800 - 0x00007f24919551e4: ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@170 (line 148) - 0x00007f24919551e4: 0080 baa0 | 0000 0000 | 0f85 6a1f - 0x00007f24919551f0: ;*ifeq {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@24 (line 721) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) - 0x00007f24919551f0: 0000 8b72 - 0x00007f24919551f4: ;*getfield _inputPtr {reexecute=0 rethrow=0 return_oop=0} - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWSOrEnd@1 (line 2992) - ; - com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken@32 (line 724) - ; - com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize@217 (line 157) -.................................................................................................... - 12.60% - -....[Hottest Regions]............................................................................... - 22.09% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString, version 2261 (1016 bytes) - 21.52% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName, version 2266 (1098 bytes) - 12.60% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize, version 2396 (780 bytes) - 9.09% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName, version 2266 (771 bytes) - 2.49% jvmci, level 4 com.fasterxml.jackson.databind.deser.std.EnumDeserializer::deserialize, version 2467 (533 bytes) - 2.22% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableObjectProperty::deserializeAndSet, version 2423 (241 bytes) - 2.17% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableStringProperty::deserializeAndSet, version 2403 (315 bytes) - 1.57% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize, version 2396 (448 bytes) - 1.36% jvmci, level 4 com.fasterxml.jackson.core.JsonFactory::createParser, version 2643 (693 bytes) - 1.27% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken, version 2363 (294 bytes) - 1.14% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableIntProperty::deserializeAndSet, version 2411 (324 bytes) - 1.10% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString, version 2261 (21 bytes) - 1.10% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableIntProperty::deserializeAndSet, version 2411 (188 bytes) - 1.10% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize, version 2396 (199 bytes) - 0.92% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextTokenNotInObject, version 2432 (404 bytes) - 0.73% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableLongProperty::deserializeAndSet, version 2509 (439 bytes) - 0.68% jvmci, level 4 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer::_deserializeFromArray, version 2654 (432 bytes) - 0.56% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize, version 2396 (230 bytes) - 0.56% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextTokenNotInObject, version 2432 (141 bytes) - 0.55% jvmci, level 4 com.fasterxml.jackson.core.json.JsonReadContext::createChildArrayContext, version 2496 (374 bytes) - 15.17% <...other 765 warm regions...> -.................................................................................................... -100.00% - -....[Hottest Methods (after inlining)].............................................................. - 31.19% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextFieldName, version 2266 - 23.39% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_finishAndReturnString, version 2261 - 16.75% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SuperSonicBeanDeserializer::deserialize, version 2396 - 2.95% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableObjectProperty::deserializeAndSet, version 2423 - 2.94% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableStringProperty::deserializeAndSet, version 2403 - 2.88% jvmci, level 4 com.fasterxml.jackson.databind.deser.std.CollectionDeserializer::_deserializeFromArray, version 2654 - 2.63% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableIntProperty::deserializeAndSet, version 2411 - 2.59% jvmci, level 4 com.fasterxml.jackson.databind.deser.std.EnumDeserializer::deserialize, version 2467 - 2.33% jvmci, level 4 com.fasterxml.jackson.core.JsonFactory::createParser, version 2643 - 1.69% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextToken, version 2363 - 1.56% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_nextTokenNotInObject, version 2432 - 1.55% jvmci, level 4 com.fasterxml.jackson.databind.ObjectReader::_bindAndClose, version 2640 - 1.42% jvmci, level 4 com.fasterxml.jackson.databind.deser.std.StringCollectionDeserializer::deserialize, version 2690 - 1.11% jvmci, level 4 com.fasterxml.jackson.module.blackbird.deser.SettableLongProperty::deserializeAndSet, version 2509 - 0.66% jvmci, level 4 com.fasterxml.jackson.core.json.JsonReadContext::createChildArrayContext, version 2496 - 0.48% jvmci, level 4 com.fasterxml.jackson.module.blackbird.jmh_generated.Blackbird_classicMediaItemDeser_jmhTest::classicMediaItemDeser_thrpt_jmhStub, version 19656 - 0.48% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::nextTextValue, version 2470 - 0.42% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_closeArrayScope, version 13575 - 0.39% jvmci, level 4 com.fasterxml.jackson.core.json.UTF8StreamJsonParser::_skipWS, version 2377 - 0.39% jvmci, level 4 com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer::<init>, version 2645 - 2.22% <...other 380 warm methods...> -.................................................................................................... -100.00% - -....[Distribution by Source]........................................................................ - 98.71% jvmci, level 4 - 0.75% libjvm.so - 0.22% runtime stub - 0.13% kernel - 0.12% libc-2.31.so - 0.02% [vdso] - 0.02% ld-2.31.so - 0.02% libpthread-2.31.so - 0.00% interpreter - 0.00% c1, level 1 -.................................................................................................... -100.00% - diff --git a/blackbird/benchmarks/results/results_graal.txt b/blackbird/benchmarks/results/results_graal.txt deleted file mode 100644 index ff992ab0f..000000000 --- a/blackbird/benchmarks/results/results_graal.txt +++ /dev/null @@ -1,3308 +0,0 @@ -WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by org.openjdk.jmh.util.Utils (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to field java.io.PrintStream.charOut -WARNING: Please consider reporting this to the maintainers of org.openjdk.jmh.util.Utils -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.beanArrayDeser - -# Run progress: 0.00% complete, ETA 11:40:00 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.898 ops/ms -# Warmup Iteration 2: 1.333 ops/ms -# Warmup Iteration 3: 1.337 ops/ms -# Warmup Iteration 4: 1.331 ops/ms -# Warmup Iteration 5: 1.337 ops/ms -# Warmup Iteration 6: 1.334 ops/ms -# Warmup Iteration 7: 1.337 ops/ms -# Warmup Iteration 8: 1.335 ops/ms -# Warmup Iteration 9: 1.332 ops/ms -# Warmup Iteration 10: 1.337 ops/ms -Iteration 1: 1.337 ops/ms -Iteration 2: 1.337 ops/ms -Iteration 3: 1.337 ops/ms -Iteration 4: 1.338 ops/ms -Iteration 5: 1.338 ops/ms -Iteration 6: 1.337 ops/ms -Iteration 7: 1.337 ops/ms -Iteration 8: 1.337 ops/ms -Iteration 9: 1.338 ops/ms -Iteration 10: 1.337 ops/ms - -# Run progress: 0.95% complete, ETA 11:36:54 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.845 ops/ms -# Warmup Iteration 2: 1.283 ops/ms -# Warmup Iteration 3: 1.290 ops/ms -# Warmup Iteration 4: 1.289 ops/ms -# Warmup Iteration 5: 1.288 ops/ms -# Warmup Iteration 6: 1.290 ops/ms -# Warmup Iteration 7: 1.290 ops/ms -# Warmup Iteration 8: 1.289 ops/ms -# Warmup Iteration 9: 1.287 ops/ms -# Warmup Iteration 10: 1.290 ops/ms -Iteration 1: 1.290 ops/ms -Iteration 2: 1.288 ops/ms -Iteration 3: 1.290 ops/ms -Iteration 4: 1.289 ops/ms -Iteration 5: 1.290 ops/ms -Iteration 6: 1.290 ops/ms -Iteration 7: 1.290 ops/ms -Iteration 8: 1.289 ops/ms -Iteration 9: 1.290 ops/ms -Iteration 10: 1.290 ops/ms - -# Run progress: 1.90% complete, ETA 11:29:49 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.818 ops/ms -# Warmup Iteration 2: 1.257 ops/ms -# Warmup Iteration 3: 1.265 ops/ms -# Warmup Iteration 4: 1.265 ops/ms -# Warmup Iteration 5: 1.265 ops/ms -# Warmup Iteration 6: 1.262 ops/ms -# Warmup Iteration 7: 1.265 ops/ms -# Warmup Iteration 8: 1.265 ops/ms -# Warmup Iteration 9: 1.264 ops/ms -# Warmup Iteration 10: 1.265 ops/ms -Iteration 1: 1.266 ops/ms -Iteration 2: 1.266 ops/ms -Iteration 3: 1.266 ops/ms -Iteration 4: 1.266 ops/ms -Iteration 5: 1.266 ops/ms -Iteration 6: 1.266 ops/ms -Iteration 7: 1.265 ops/ms -Iteration 8: 1.266 ops/ms -Iteration 9: 1.265 ops/ms -Iteration 10: 1.266 ops/ms - -# Run progress: 2.86% complete, ETA 11:23:26 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.866 ops/ms -# Warmup Iteration 2: 1.328 ops/ms -# Warmup Iteration 3: 1.334 ops/ms -# Warmup Iteration 4: 1.334 ops/ms -# Warmup Iteration 5: 1.334 ops/ms -# Warmup Iteration 6: 1.334 ops/ms -# Warmup Iteration 7: 1.335 ops/ms -# Warmup Iteration 8: 1.330 ops/ms -# Warmup Iteration 9: 1.334 ops/ms -# Warmup Iteration 10: 1.334 ops/ms -Iteration 1: 1.335 ops/ms -Iteration 2: 1.335 ops/ms -Iteration 3: 1.335 ops/ms -Iteration 4: 1.335 ops/ms -Iteration 5: 1.335 ops/ms -Iteration 6: 1.335 ops/ms -Iteration 7: 1.335 ops/ms -Iteration 8: 1.334 ops/ms -Iteration 9: 1.335 ops/ms -Iteration 10: 1.335 ops/ms - -# Run progress: 3.81% complete, ETA 11:16:28 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.846 ops/ms -# Warmup Iteration 2: 1.335 ops/ms -# Warmup Iteration 3: 1.341 ops/ms -# Warmup Iteration 4: 1.342 ops/ms -# Warmup Iteration 5: 1.337 ops/ms -# Warmup Iteration 6: 1.341 ops/ms -# Warmup Iteration 7: 1.342 ops/ms -# Warmup Iteration 8: 1.338 ops/ms -# Warmup Iteration 9: 1.342 ops/ms -# Warmup Iteration 10: 1.342 ops/ms -Iteration 1: 1.343 ops/ms -Iteration 2: 1.341 ops/ms -Iteration 3: 1.342 ops/ms -Iteration 4: 1.343 ops/ms -Iteration 5: 1.342 ops/ms -Iteration 6: 1.343 ops/ms -Iteration 7: 1.343 ops/ms -Iteration 8: 1.343 ops/ms -Iteration 9: 1.342 ops/ms -Iteration 10: 1.342 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.beanArrayDeser": - 1.314 ±(99.9%) 0.015 ops/ms [Average] - (min, avg, max) = (1.265, 1.314, 1.343), stdev = 0.031 - CI (99.9%): [1.299, 1.329] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.beanArraySer - -# Run progress: 4.76% complete, ETA 11:09:41 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.693 ops/ms -# Warmup Iteration 2: 2.343 ops/ms -# Warmup Iteration 3: 2.347 ops/ms -# Warmup Iteration 4: 2.348 ops/ms -# Warmup Iteration 5: 2.344 ops/ms -# Warmup Iteration 6: 2.346 ops/ms -# Warmup Iteration 7: 2.348 ops/ms -# Warmup Iteration 8: 2.349 ops/ms -# Warmup Iteration 9: 2.349 ops/ms -# Warmup Iteration 10: 2.348 ops/ms -Iteration 1: 2.340 ops/ms -Iteration 2: 2.347 ops/ms -Iteration 3: 2.347 ops/ms -Iteration 4: 2.348 ops/ms -Iteration 5: 2.348 ops/ms -Iteration 6: 2.347 ops/ms -Iteration 7: 2.346 ops/ms -Iteration 8: 2.347 ops/ms -Iteration 9: 2.348 ops/ms -Iteration 10: 2.347 ops/ms - -# Run progress: 5.71% complete, ETA 11:03:15 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.547 ops/ms -# Warmup Iteration 2: 2.290 ops/ms -# Warmup Iteration 3: 2.302 ops/ms -# Warmup Iteration 4: 2.300 ops/ms -# Warmup Iteration 5: 2.299 ops/ms -# Warmup Iteration 6: 2.299 ops/ms -# Warmup Iteration 7: 2.300 ops/ms -# Warmup Iteration 8: 2.300 ops/ms -# Warmup Iteration 9: 2.300 ops/ms -# Warmup Iteration 10: 2.300 ops/ms -Iteration 1: 2.299 ops/ms -Iteration 2: 2.299 ops/ms -Iteration 3: 2.300 ops/ms -Iteration 4: 2.299 ops/ms -Iteration 5: 2.300 ops/ms -Iteration 6: 2.300 ops/ms -Iteration 7: 2.303 ops/ms -Iteration 8: 2.303 ops/ms -Iteration 9: 2.302 ops/ms -Iteration 10: 2.303 ops/ms - -# Run progress: 6.67% complete, ETA 10:56:24 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.825 ops/ms -# Warmup Iteration 2: 2.309 ops/ms -# Warmup Iteration 3: 2.311 ops/ms -# Warmup Iteration 4: 2.312 ops/ms -# Warmup Iteration 5: 2.308 ops/ms -# Warmup Iteration 6: 2.310 ops/ms -# Warmup Iteration 7: 2.311 ops/ms -# Warmup Iteration 8: 2.311 ops/ms -# Warmup Iteration 9: 2.311 ops/ms -# Warmup Iteration 10: 2.311 ops/ms -Iteration 1: 2.311 ops/ms -Iteration 2: 2.310 ops/ms -Iteration 3: 2.311 ops/ms -Iteration 4: 2.310 ops/ms -Iteration 5: 2.312 ops/ms -Iteration 6: 2.310 ops/ms -Iteration 7: 2.311 ops/ms -Iteration 8: 2.310 ops/ms -Iteration 9: 2.311 ops/ms -Iteration 10: 2.311 ops/ms - -# Run progress: 7.62% complete, ETA 10:49:55 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.484 ops/ms -# Warmup Iteration 2: 2.248 ops/ms -# Warmup Iteration 3: 2.258 ops/ms -# Warmup Iteration 4: 2.257 ops/ms -# Warmup Iteration 5: 2.253 ops/ms -# Warmup Iteration 6: 2.252 ops/ms -# Warmup Iteration 7: 2.253 ops/ms -# Warmup Iteration 8: 2.251 ops/ms -# Warmup Iteration 9: 2.251 ops/ms -# Warmup Iteration 10: 2.251 ops/ms -Iteration 1: 2.252 ops/ms -Iteration 2: 2.252 ops/ms -Iteration 3: 2.253 ops/ms -Iteration 4: 2.252 ops/ms -Iteration 5: 2.252 ops/ms -Iteration 6: 2.252 ops/ms -Iteration 7: 2.262 ops/ms -Iteration 8: 2.262 ops/ms -Iteration 9: 2.261 ops/ms -Iteration 10: 2.262 ops/ms - -# Run progress: 8.57% complete, ETA 10:43:08 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.482 ops/ms -# Warmup Iteration 2: 2.243 ops/ms -# Warmup Iteration 3: 2.251 ops/ms -# Warmup Iteration 4: 2.253 ops/ms -# Warmup Iteration 5: 2.253 ops/ms -# Warmup Iteration 6: 2.249 ops/ms -# Warmup Iteration 7: 2.250 ops/ms -# Warmup Iteration 8: 2.250 ops/ms -# Warmup Iteration 9: 2.250 ops/ms -# Warmup Iteration 10: 2.248 ops/ms -Iteration 1: 2.249 ops/ms -Iteration 2: 2.249 ops/ms -Iteration 3: 2.249 ops/ms -Iteration 4: 2.250 ops/ms -Iteration 5: 2.249 ops/ms -Iteration 6: 2.248 ops/ms -Iteration 7: 2.252 ops/ms -Iteration 8: 2.252 ops/ms -Iteration 9: 2.251 ops/ms -Iteration 10: 2.253 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.beanArraySer": - 2.293 ±(99.9%) 0.018 ops/ms [Average] - (min, avg, max) = (2.248, 2.293, 2.348), stdev = 0.036 - CI (99.9%): [2.275, 2.311] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemDeser - -# Run progress: 9.52% complete, ETA 10:36:21 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.024 ops/ms -# Warmup Iteration 2: 1.507 ops/ms -# Warmup Iteration 3: 1.528 ops/ms -# Warmup Iteration 4: 1.527 ops/ms -# Warmup Iteration 5: 1.526 ops/ms -# Warmup Iteration 6: 1.526 ops/ms -# Warmup Iteration 7: 1.525 ops/ms -# Warmup Iteration 8: 1.520 ops/ms -# Warmup Iteration 9: 1.528 ops/ms -# Warmup Iteration 10: 1.528 ops/ms -Iteration 1: 1.528 ops/ms -Iteration 2: 1.528 ops/ms -Iteration 3: 1.528 ops/ms -Iteration 4: 1.528 ops/ms -Iteration 5: 1.528 ops/ms -Iteration 6: 1.528 ops/ms -Iteration 7: 1.527 ops/ms -Iteration 8: 1.528 ops/ms -Iteration 9: 1.527 ops/ms -Iteration 10: 1.528 ops/ms - -# Run progress: 10.48% complete, ETA 10:29:35 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.030 ops/ms -# Warmup Iteration 2: 1.488 ops/ms -# Warmup Iteration 3: 1.501 ops/ms -# Warmup Iteration 4: 1.500 ops/ms -# Warmup Iteration 5: 1.497 ops/ms -# Warmup Iteration 6: 1.502 ops/ms -# Warmup Iteration 7: 1.502 ops/ms -# Warmup Iteration 8: 1.495 ops/ms -# Warmup Iteration 9: 1.501 ops/ms -# Warmup Iteration 10: 1.503 ops/ms -Iteration 1: 1.503 ops/ms -Iteration 2: 1.503 ops/ms -Iteration 3: 1.503 ops/ms -Iteration 4: 1.504 ops/ms -Iteration 5: 1.503 ops/ms -Iteration 6: 1.503 ops/ms -Iteration 7: 1.502 ops/ms -Iteration 8: 1.502 ops/ms -Iteration 9: 1.502 ops/ms -Iteration 10: 1.502 ops/ms - -# Run progress: 11.43% complete, ETA 10:22:51 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.046 ops/ms -# Warmup Iteration 2: 1.506 ops/ms -# Warmup Iteration 3: 1.516 ops/ms -# Warmup Iteration 4: 1.517 ops/ms -# Warmup Iteration 5: 1.516 ops/ms -# Warmup Iteration 6: 1.517 ops/ms -# Warmup Iteration 7: 1.517 ops/ms -# Warmup Iteration 8: 1.512 ops/ms -# Warmup Iteration 9: 1.517 ops/ms -# Warmup Iteration 10: 1.517 ops/ms -Iteration 1: 1.517 ops/ms -Iteration 2: 1.515 ops/ms -Iteration 3: 1.517 ops/ms -Iteration 4: 1.517 ops/ms -Iteration 5: 1.517 ops/ms -Iteration 6: 1.517 ops/ms -Iteration 7: 1.517 ops/ms -Iteration 8: 1.517 ops/ms -Iteration 9: 1.516 ops/ms -Iteration 10: 1.518 ops/ms - -# Run progress: 12.38% complete, ETA 10:16:07 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.066 ops/ms -# Warmup Iteration 2: 1.505 ops/ms -# Warmup Iteration 3: 1.520 ops/ms -# Warmup Iteration 4: 1.521 ops/ms -# Warmup Iteration 5: 1.519 ops/ms -# Warmup Iteration 6: 1.520 ops/ms -# Warmup Iteration 7: 1.518 ops/ms -# Warmup Iteration 8: 1.520 ops/ms -# Warmup Iteration 9: 1.521 ops/ms -# Warmup Iteration 10: 1.521 ops/ms -Iteration 1: 1.522 ops/ms -Iteration 2: 1.521 ops/ms -Iteration 3: 1.521 ops/ms -Iteration 4: 1.521 ops/ms -Iteration 5: 1.521 ops/ms -Iteration 6: 1.521 ops/ms -Iteration 7: 1.521 ops/ms -Iteration 8: 1.521 ops/ms -Iteration 9: 1.521 ops/ms -Iteration 10: 1.521 ops/ms - -# Run progress: 13.33% complete, ETA 10:09:24 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.014 ops/ms -# Warmup Iteration 2: 1.515 ops/ms -# Warmup Iteration 3: 1.528 ops/ms -# Warmup Iteration 4: 1.528 ops/ms -# Warmup Iteration 5: 1.527 ops/ms -# Warmup Iteration 6: 1.529 ops/ms -# Warmup Iteration 7: 1.523 ops/ms -# Warmup Iteration 8: 1.524 ops/ms -# Warmup Iteration 9: 1.527 ops/ms -# Warmup Iteration 10: 1.528 ops/ms -Iteration 1: 1.326 ops/ms -Iteration 2: 1.529 ops/ms -Iteration 3: 1.529 ops/ms -Iteration 4: 1.529 ops/ms -Iteration 5: 1.528 ops/ms -Iteration 6: 1.528 ops/ms -Iteration 7: 1.527 ops/ms -Iteration 8: 1.526 ops/ms -Iteration 9: 1.527 ops/ms -Iteration 10: 1.527 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemDeser": - 1.515 ±(99.9%) 0.014 ops/ms [Average] - (min, avg, max) = (1.326, 1.515, 1.529), stdev = 0.029 - CI (99.9%): [1.501, 1.530] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemSer - -# Run progress: 14.29% complete, ETA 10:02:39 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.948 ops/ms -# Warmup Iteration 2: 2.563 ops/ms -# Warmup Iteration 3: 2.584 ops/ms -# Warmup Iteration 4: 2.583 ops/ms -# Warmup Iteration 5: 2.568 ops/ms -# Warmup Iteration 6: 2.586 ops/ms -# Warmup Iteration 7: 2.586 ops/ms -# Warmup Iteration 8: 2.583 ops/ms -# Warmup Iteration 9: 2.582 ops/ms -# Warmup Iteration 10: 2.580 ops/ms -Iteration 1: 2.582 ops/ms -Iteration 2: 2.579 ops/ms -Iteration 3: 2.580 ops/ms -Iteration 4: 2.582 ops/ms -Iteration 5: 2.582 ops/ms -Iteration 6: 2.587 ops/ms -Iteration 7: 2.584 ops/ms -Iteration 8: 2.585 ops/ms -Iteration 9: 2.584 ops/ms -Iteration 10: 2.582 ops/ms - -# Run progress: 15.24% complete, ETA 09:55:58 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.742 ops/ms -# Warmup Iteration 2: 2.390 ops/ms -# Warmup Iteration 3: 2.397 ops/ms -# Warmup Iteration 4: 2.401 ops/ms -# Warmup Iteration 5: 2.396 ops/ms -# Warmup Iteration 6: 2.399 ops/ms -# Warmup Iteration 7: 2.400 ops/ms -# Warmup Iteration 8: 2.399 ops/ms -# Warmup Iteration 9: 2.396 ops/ms -# Warmup Iteration 10: 2.397 ops/ms -Iteration 1: 2.397 ops/ms -Iteration 2: 2.397 ops/ms -Iteration 3: 2.397 ops/ms -Iteration 4: 2.397 ops/ms -Iteration 5: 2.395 ops/ms -Iteration 6: 2.396 ops/ms -Iteration 7: 2.388 ops/ms -Iteration 8: 2.387 ops/ms -Iteration 9: 2.392 ops/ms -Iteration 10: 2.396 ops/ms - -# Run progress: 16.19% complete, ETA 09:49:15 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.928 ops/ms -# Warmup Iteration 2: 2.576 ops/ms -# Warmup Iteration 3: 2.599 ops/ms -# Warmup Iteration 4: 2.597 ops/ms -# Warmup Iteration 5: 2.596 ops/ms -# Warmup Iteration 6: 2.596 ops/ms -# Warmup Iteration 7: 2.594 ops/ms -# Warmup Iteration 8: 2.599 ops/ms -# Warmup Iteration 9: 2.594 ops/ms -# Warmup Iteration 10: 2.596 ops/ms -Iteration 1: 2.596 ops/ms -Iteration 2: 2.593 ops/ms -Iteration 3: 2.590 ops/ms -Iteration 4: 2.593 ops/ms -Iteration 5: 2.592 ops/ms -Iteration 6: 2.597 ops/ms -Iteration 7: 2.593 ops/ms -Iteration 8: 2.598 ops/ms -Iteration 9: 2.598 ops/ms -Iteration 10: 2.594 ops/ms - -# Run progress: 17.14% complete, ETA 09:42:32 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.750 ops/ms -# Warmup Iteration 2: 2.413 ops/ms -# Warmup Iteration 3: 2.422 ops/ms -# Warmup Iteration 4: 2.425 ops/ms -# Warmup Iteration 5: 2.416 ops/ms -# Warmup Iteration 6: 2.425 ops/ms -# Warmup Iteration 7: 2.424 ops/ms -# Warmup Iteration 8: 2.426 ops/ms -# Warmup Iteration 9: 2.426 ops/ms -# Warmup Iteration 10: 2.422 ops/ms -Iteration 1: 2.426 ops/ms -Iteration 2: 2.426 ops/ms -Iteration 3: 2.424 ops/ms -Iteration 4: 2.428 ops/ms -Iteration 5: 2.427 ops/ms -Iteration 6: 2.427 ops/ms -Iteration 7: 2.425 ops/ms -Iteration 8: 2.426 ops/ms -Iteration 9: 2.426 ops/ms -Iteration 10: 2.427 ops/ms - -# Run progress: 18.10% complete, ETA 09:35:49 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.717 ops/ms -# Warmup Iteration 2: 2.430 ops/ms -# Warmup Iteration 3: 2.431 ops/ms -# Warmup Iteration 4: 2.433 ops/ms -# Warmup Iteration 5: 2.427 ops/ms -# Warmup Iteration 6: 2.432 ops/ms -# Warmup Iteration 7: 2.433 ops/ms -# Warmup Iteration 8: 2.433 ops/ms -# Warmup Iteration 9: 2.434 ops/ms -# Warmup Iteration 10: 2.433 ops/ms -Iteration 1: 2.433 ops/ms -Iteration 2: 2.432 ops/ms -Iteration 3: 2.432 ops/ms -Iteration 4: 2.430 ops/ms -Iteration 5: 2.432 ops/ms -Iteration 6: 2.433 ops/ms -Iteration 7: 2.424 ops/ms -Iteration 8: 2.428 ops/ms -Iteration 9: 2.428 ops/ms -Iteration 10: 2.427 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemSer": - 2.485 ±(99.9%) 0.043 ops/ms [Average] - (min, avg, max) = (2.387, 2.485, 2.598), stdev = 0.086 - CI (99.9%): [2.443, 2.528] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemDeser - -# Run progress: 19.05% complete, ETA 09:29:05 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -243.988 ops/ms -# Warmup Iteration 2: 425.598 ops/ms -# Warmup Iteration 3: 431.461 ops/ms -# Warmup Iteration 4: 431.309 ops/ms -# Warmup Iteration 5: 431.172 ops/ms -# Warmup Iteration 6: 431.330 ops/ms -# Warmup Iteration 7: 431.398 ops/ms -# Warmup Iteration 8: 431.233 ops/ms -# Warmup Iteration 9: 431.477 ops/ms -# Warmup Iteration 10: 431.766 ops/ms -Iteration 1: 431.264 ops/ms -Iteration 2: 431.423 ops/ms -Iteration 3: 431.279 ops/ms -Iteration 4: 431.473 ops/ms -Iteration 5: 431.238 ops/ms -Iteration 6: 431.180 ops/ms -Iteration 7: 431.410 ops/ms -Iteration 8: 431.291 ops/ms -Iteration 9: 430.958 ops/ms -Iteration 10: 430.953 ops/ms - -# Run progress: 20.00% complete, ETA 09:22:22 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -285.108 ops/ms -# Warmup Iteration 2: 414.745 ops/ms -# Warmup Iteration 3: 422.055 ops/ms -# Warmup Iteration 4: 421.981 ops/ms -# Warmup Iteration 5: 421.820 ops/ms -# Warmup Iteration 6: 422.187 ops/ms -# Warmup Iteration 7: 422.160 ops/ms -# Warmup Iteration 8: 422.253 ops/ms -# Warmup Iteration 9: 422.043 ops/ms -# Warmup Iteration 10: 422.028 ops/ms -Iteration 1: 421.926 ops/ms -Iteration 2: 421.957 ops/ms -Iteration 3: 421.952 ops/ms -Iteration 4: 422.074 ops/ms -Iteration 5: 421.821 ops/ms -Iteration 6: 422.013 ops/ms -Iteration 7: 421.901 ops/ms -Iteration 8: 421.687 ops/ms -Iteration 9: 421.548 ops/ms -Iteration 10: 421.588 ops/ms - -# Run progress: 20.95% complete, ETA 09:15:39 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -232.332 ops/ms -# Warmup Iteration 2: 424.228 ops/ms -# Warmup Iteration 3: 426.715 ops/ms -# Warmup Iteration 4: 427.992 ops/ms -# Warmup Iteration 5: 428.399 ops/ms -# Warmup Iteration 6: 428.119 ops/ms -# Warmup Iteration 7: 428.262 ops/ms -# Warmup Iteration 8: 428.053 ops/ms -# Warmup Iteration 9: 428.412 ops/ms -# Warmup Iteration 10: 427.957 ops/ms -Iteration 1: 428.409 ops/ms -Iteration 2: 428.470 ops/ms -Iteration 3: 428.278 ops/ms -Iteration 4: 428.410 ops/ms -Iteration 5: 428.358 ops/ms -Iteration 6: 428.342 ops/ms -Iteration 7: 428.422 ops/ms -Iteration 8: 428.226 ops/ms -Iteration 9: 428.326 ops/ms -Iteration 10: 428.516 ops/ms - -# Run progress: 21.90% complete, ETA 09:08:57 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -276.721 ops/ms -# Warmup Iteration 2: 422.702 ops/ms -# Warmup Iteration 3: 430.548 ops/ms -# Warmup Iteration 4: 429.974 ops/ms -# Warmup Iteration 5: 430.582 ops/ms -# Warmup Iteration 6: 430.402 ops/ms -# Warmup Iteration 7: 430.700 ops/ms -# Warmup Iteration 8: 430.641 ops/ms -# Warmup Iteration 9: 430.973 ops/ms -# Warmup Iteration 10: 430.679 ops/ms -Iteration 1: 428.337 ops/ms -Iteration 2: 430.469 ops/ms -Iteration 3: 430.271 ops/ms -Iteration 4: 430.735 ops/ms -Iteration 5: 430.868 ops/ms -Iteration 6: 430.421 ops/ms -Iteration 7: 430.670 ops/ms -Iteration 8: 430.222 ops/ms -Iteration 9: 430.747 ops/ms -Iteration 10: 430.572 ops/ms - -# Run progress: 22.86% complete, ETA 09:02:14 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -288.681 ops/ms -# Warmup Iteration 2: 432.696 ops/ms -# Warmup Iteration 3: 434.543 ops/ms -# Warmup Iteration 4: 435.271 ops/ms -# Warmup Iteration 5: 434.991 ops/ms -# Warmup Iteration 6: 434.372 ops/ms -# Warmup Iteration 7: 435.642 ops/ms -# Warmup Iteration 8: 435.423 ops/ms -# Warmup Iteration 9: 435.111 ops/ms -# Warmup Iteration 10: 434.738 ops/ms -Iteration 1: 434.750 ops/ms -Iteration 2: 434.756 ops/ms -Iteration 3: 435.156 ops/ms -Iteration 4: 434.538 ops/ms -Iteration 5: 434.810 ops/ms -Iteration 6: 434.950 ops/ms -Iteration 7: 433.254 ops/ms -Iteration 8: 433.854 ops/ms -Iteration 9: 433.501 ops/ms -Iteration 10: 433.763 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemDeser": - 429.227 ±(99.9%) 2.092 ops/ms [Average] - (min, avg, max) = (421.548, 429.227, 435.156), stdev = 4.226 - CI (99.9%): [427.134, 431.319] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemSer - -# Run progress: 23.81% complete, ETA 08:55:31 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -441.643 ops/ms -# Warmup Iteration 2: 656.208 ops/ms -# Warmup Iteration 3: 660.027 ops/ms -# Warmup Iteration 4: 660.429 ops/ms -# Warmup Iteration 5: 660.094 ops/ms -# Warmup Iteration 6: 659.792 ops/ms -# Warmup Iteration 7: 660.502 ops/ms -# Warmup Iteration 8: 660.249 ops/ms -# Warmup Iteration 9: 660.627 ops/ms -# Warmup Iteration 10: 660.605 ops/ms -Iteration 1: 660.225 ops/ms -Iteration 2: 660.413 ops/ms -Iteration 3: 660.384 ops/ms -Iteration 4: 660.604 ops/ms -Iteration 5: 660.579 ops/ms -Iteration 6: 660.588 ops/ms -Iteration 7: 651.040 ops/ms -Iteration 8: 651.045 ops/ms -Iteration 9: 650.728 ops/ms -Iteration 10: 650.967 ops/ms - -# Run progress: 24.76% complete, ETA 08:48:49 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -454.195 ops/ms -# Warmup Iteration 2: 685.598 ops/ms -# Warmup Iteration 3: 688.144 ops/ms -# Warmup Iteration 4: 689.180 ops/ms -# Warmup Iteration 5: 689.536 ops/ms -# Warmup Iteration 6: 689.036 ops/ms -# Warmup Iteration 7: 689.528 ops/ms -# Warmup Iteration 8: 689.565 ops/ms -# Warmup Iteration 9: 688.792 ops/ms -# Warmup Iteration 10: 689.504 ops/ms -Iteration 1: 689.420 ops/ms -Iteration 2: 687.971 ops/ms -Iteration 3: 689.540 ops/ms -Iteration 4: 689.600 ops/ms -Iteration 5: 689.467 ops/ms -Iteration 6: 689.273 ops/ms -Iteration 7: 689.308 ops/ms -Iteration 8: 689.042 ops/ms -Iteration 9: 689.338 ops/ms -Iteration 10: 689.461 ops/ms - -# Run progress: 25.71% complete, ETA 08:42:06 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -471.130 ops/ms -# Warmup Iteration 2: 677.693 ops/ms -# Warmup Iteration 3: 683.694 ops/ms -# Warmup Iteration 4: 683.661 ops/ms -# Warmup Iteration 5: 683.107 ops/ms -# Warmup Iteration 6: 683.316 ops/ms -# Warmup Iteration 7: 683.160 ops/ms -# Warmup Iteration 8: 683.413 ops/ms -# Warmup Iteration 9: 683.657 ops/ms -# Warmup Iteration 10: 683.670 ops/ms -Iteration 1: 683.621 ops/ms -Iteration 2: 683.175 ops/ms -Iteration 3: 683.492 ops/ms -Iteration 4: 683.431 ops/ms -Iteration 5: 683.424 ops/ms -Iteration 6: 683.431 ops/ms -Iteration 7: 682.752 ops/ms -Iteration 8: 682.754 ops/ms -Iteration 9: 682.773 ops/ms -Iteration 10: 682.380 ops/ms - -# Run progress: 26.67% complete, ETA 08:35:24 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -455.205 ops/ms -# Warmup Iteration 2: 678.644 ops/ms -# Warmup Iteration 3: 683.124 ops/ms -# Warmup Iteration 4: 683.336 ops/ms -# Warmup Iteration 5: 683.406 ops/ms -# Warmup Iteration 6: 683.107 ops/ms -# Warmup Iteration 7: 683.635 ops/ms -# Warmup Iteration 8: 683.815 ops/ms -# Warmup Iteration 9: 683.470 ops/ms -# Warmup Iteration 10: 683.511 ops/ms -Iteration 1: 683.634 ops/ms -Iteration 2: 683.777 ops/ms -Iteration 3: 683.520 ops/ms -Iteration 4: 683.208 ops/ms -Iteration 5: 683.335 ops/ms -Iteration 6: 683.651 ops/ms -Iteration 7: 683.795 ops/ms -Iteration 8: 683.932 ops/ms -Iteration 9: 683.790 ops/ms -Iteration 10: 684.023 ops/ms - -# Run progress: 27.62% complete, ETA 08:28:42 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -432.710 ops/ms -# Warmup Iteration 2: 658.589 ops/ms -# Warmup Iteration 3: 669.610 ops/ms -# Warmup Iteration 4: 669.984 ops/ms -# Warmup Iteration 5: 669.228 ops/ms -# Warmup Iteration 6: 670.203 ops/ms -# Warmup Iteration 7: 670.141 ops/ms -# Warmup Iteration 8: 669.994 ops/ms -# Warmup Iteration 9: 669.855 ops/ms -# Warmup Iteration 10: 670.004 ops/ms -Iteration 1: 669.964 ops/ms -Iteration 2: 669.956 ops/ms -Iteration 3: 670.119 ops/ms -Iteration 4: 669.724 ops/ms -Iteration 5: 670.031 ops/ms -Iteration 6: 670.052 ops/ms -Iteration 7: 670.419 ops/ms -Iteration 8: 669.794 ops/ms -Iteration 9: 670.345 ops/ms -Iteration 10: 670.246 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemSer": - 676.551 ±(99.9%) 5.980 ops/ms [Average] - (min, avg, max) = (650.728, 676.551, 689.600), stdev = 12.079 - CI (99.9%): [670.571, 682.531] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.constructorArrayDeser - -# Run progress: 28.57% complete, ETA 08:21:59 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.701 ops/ms -# Warmup Iteration 2: 1.079 ops/ms -# Warmup Iteration 3: 1.097 ops/ms -# Warmup Iteration 4: 1.097 ops/ms -# Warmup Iteration 5: 1.098 ops/ms -# Warmup Iteration 6: 1.097 ops/ms -# Warmup Iteration 7: 1.098 ops/ms -# Warmup Iteration 8: 1.099 ops/ms -# Warmup Iteration 9: 1.098 ops/ms -# Warmup Iteration 10: 1.096 ops/ms -Iteration 1: 1.098 ops/ms -Iteration 2: 1.098 ops/ms -Iteration 3: 1.098 ops/ms -Iteration 4: 1.098 ops/ms -Iteration 5: 1.098 ops/ms -Iteration 6: 1.098 ops/ms -Iteration 7: 1.099 ops/ms -Iteration 8: 1.099 ops/ms -Iteration 9: 1.098 ops/ms -Iteration 10: 1.099 ops/ms - -# Run progress: 29.52% complete, ETA 08:15:17 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.653 ops/ms -# Warmup Iteration 2: 1.059 ops/ms -# Warmup Iteration 3: 1.081 ops/ms -# Warmup Iteration 4: 1.079 ops/ms -# Warmup Iteration 5: 1.080 ops/ms -# Warmup Iteration 6: 1.080 ops/ms -# Warmup Iteration 7: 1.079 ops/ms -# Warmup Iteration 8: 1.081 ops/ms -# Warmup Iteration 9: 1.080 ops/ms -# Warmup Iteration 10: 1.076 ops/ms -Iteration 1: 1.080 ops/ms -Iteration 2: 1.080 ops/ms -Iteration 3: 1.080 ops/ms -Iteration 4: 1.080 ops/ms -Iteration 5: 1.080 ops/ms -Iteration 6: 1.080 ops/ms -Iteration 7: 1.079 ops/ms -Iteration 8: 1.080 ops/ms -Iteration 9: 1.080 ops/ms -Iteration 10: 1.080 ops/ms - -# Run progress: 30.48% complete, ETA 08:08:35 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.711 ops/ms -# Warmup Iteration 2: 1.072 ops/ms -# Warmup Iteration 3: 1.092 ops/ms -# Warmup Iteration 4: 1.093 ops/ms -# Warmup Iteration 5: 1.092 ops/ms -# Warmup Iteration 6: 1.092 ops/ms -# Warmup Iteration 7: 1.092 ops/ms -# Warmup Iteration 8: 1.092 ops/ms -# Warmup Iteration 9: 1.092 ops/ms -# Warmup Iteration 10: 1.091 ops/ms -Iteration 1: 1.088 ops/ms -Iteration 2: 1.093 ops/ms -Iteration 3: 1.092 ops/ms -Iteration 4: 1.093 ops/ms -Iteration 5: 1.093 ops/ms -Iteration 6: 1.092 ops/ms -Iteration 7: 1.093 ops/ms -Iteration 8: 1.092 ops/ms -Iteration 9: 1.093 ops/ms -Iteration 10: 1.093 ops/ms - -# Run progress: 31.43% complete, ETA 08:01:53 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.688 ops/ms -# Warmup Iteration 2: 1.067 ops/ms -# Warmup Iteration 3: 1.088 ops/ms -# Warmup Iteration 4: 1.087 ops/ms -# Warmup Iteration 5: 1.088 ops/ms -# Warmup Iteration 6: 1.088 ops/ms -# Warmup Iteration 7: 1.088 ops/ms -# Warmup Iteration 8: 1.088 ops/ms -# Warmup Iteration 9: 1.088 ops/ms -# Warmup Iteration 10: 1.086 ops/ms -Iteration 1: 1.088 ops/ms -Iteration 2: 1.088 ops/ms -Iteration 3: 1.088 ops/ms -Iteration 4: 1.088 ops/ms -Iteration 5: 1.088 ops/ms -Iteration 6: 1.088 ops/ms -Iteration 7: 1.088 ops/ms -Iteration 8: 1.088 ops/ms -Iteration 9: 1.088 ops/ms -Iteration 10: 1.087 ops/ms - -# Run progress: 32.38% complete, ETA 07:55:11 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -0.657 ops/ms -# Warmup Iteration 2: 1.055 ops/ms -# Warmup Iteration 3: 1.078 ops/ms -# Warmup Iteration 4: 1.078 ops/ms -# Warmup Iteration 5: 1.078 ops/ms -# Warmup Iteration 6: 1.078 ops/ms -# Warmup Iteration 7: 1.078 ops/ms -# Warmup Iteration 8: 1.079 ops/ms -# Warmup Iteration 9: 1.078 ops/ms -# Warmup Iteration 10: 1.073 ops/ms -Iteration 1: 1.078 ops/ms -Iteration 2: 1.078 ops/ms -Iteration 3: 1.078 ops/ms -Iteration 4: 1.079 ops/ms -Iteration 5: 1.079 ops/ms -Iteration 6: 1.079 ops/ms -Iteration 7: 1.078 ops/ms -Iteration 8: 1.078 ops/ms -Iteration 9: 1.078 ops/ms -Iteration 10: 1.078 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.constructorArrayDeser": - 1.087 ±(99.9%) 0.004 ops/ms [Average] - (min, avg, max) = (1.078, 1.087, 1.099), stdev = 0.008 - CI (99.9%): [1.084, 1.091] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.beanArrayDeser - -# Run progress: 33.33% complete, ETA 07:48:30 -# Fork: 1 of 5 -# Warmup Iteration 1: 0.865 ops/ms -# Warmup Iteration 2: 1.327 ops/ms -# Warmup Iteration 3: 1.336 ops/ms -# Warmup Iteration 4: 1.336 ops/ms -# Warmup Iteration 5: 1.336 ops/ms -# Warmup Iteration 6: 1.337 ops/ms -# Warmup Iteration 7: 1.337 ops/ms -# Warmup Iteration 8: 1.335 ops/ms -# Warmup Iteration 9: 1.336 ops/ms -# Warmup Iteration 10: 1.337 ops/ms -Iteration 1: 1.338 ops/ms -Iteration 2: 1.338 ops/ms -Iteration 3: 1.337 ops/ms -Iteration 4: 1.338 ops/ms -Iteration 5: 1.338 ops/ms -Iteration 6: 1.337 ops/ms -Iteration 7: 1.337 ops/ms -Iteration 8: 1.337 ops/ms -Iteration 9: 1.338 ops/ms -Iteration 10: 1.338 ops/ms - -# Run progress: 34.29% complete, ETA 07:41:48 -# Fork: 2 of 5 -# Warmup Iteration 1: 0.870 ops/ms -# Warmup Iteration 2: 1.332 ops/ms -# Warmup Iteration 3: 1.340 ops/ms -# Warmup Iteration 4: 1.338 ops/ms -# Warmup Iteration 5: 1.339 ops/ms -# Warmup Iteration 6: 1.337 ops/ms -# Warmup Iteration 7: 1.338 ops/ms -# Warmup Iteration 8: 1.339 ops/ms -# Warmup Iteration 9: 1.338 ops/ms -# Warmup Iteration 10: 1.339 ops/ms -Iteration 1: 1.338 ops/ms -Iteration 2: 1.338 ops/ms -Iteration 3: 1.339 ops/ms -Iteration 4: 1.340 ops/ms -Iteration 5: 1.339 ops/ms -Iteration 6: 1.339 ops/ms -Iteration 7: 1.339 ops/ms -Iteration 8: 1.339 ops/ms -Iteration 9: 1.339 ops/ms -Iteration 10: 1.339 ops/ms - -# Run progress: 35.24% complete, ETA 07:35:06 -# Fork: 3 of 5 -# Warmup Iteration 1: 0.900 ops/ms -# Warmup Iteration 2: 1.361 ops/ms -# Warmup Iteration 3: 1.365 ops/ms -# Warmup Iteration 4: 1.364 ops/ms -# Warmup Iteration 5: 1.365 ops/ms -# Warmup Iteration 6: 1.365 ops/ms -# Warmup Iteration 7: 1.365 ops/ms -# Warmup Iteration 8: 1.360 ops/ms -# Warmup Iteration 9: 1.366 ops/ms -# Warmup Iteration 10: 1.365 ops/ms -Iteration 1: 1.366 ops/ms -Iteration 2: 1.364 ops/ms -Iteration 3: 1.365 ops/ms -Iteration 4: 1.366 ops/ms -Iteration 5: 1.366 ops/ms -Iteration 6: 1.366 ops/ms -Iteration 7: 1.366 ops/ms -Iteration 8: 1.367 ops/ms -Iteration 9: 1.366 ops/ms -Iteration 10: 1.367 ops/ms - -# Run progress: 36.19% complete, ETA 07:28:24 -# Fork: 4 of 5 -# Warmup Iteration 1: 0.850 ops/ms -# Warmup Iteration 2: 1.279 ops/ms -# Warmup Iteration 3: 1.281 ops/ms -# Warmup Iteration 4: 1.281 ops/ms -# Warmup Iteration 5: 1.281 ops/ms -# Warmup Iteration 6: 1.279 ops/ms -# Warmup Iteration 7: 1.282 ops/ms -# Warmup Iteration 8: 1.281 ops/ms -# Warmup Iteration 9: 1.274 ops/ms -# Warmup Iteration 10: 1.281 ops/ms -Iteration 1: 1.280 ops/ms -Iteration 2: 1.279 ops/ms -Iteration 3: 1.282 ops/ms -Iteration 4: 1.281 ops/ms -Iteration 5: 1.281 ops/ms -Iteration 6: 1.281 ops/ms -Iteration 7: 1.282 ops/ms -Iteration 8: 1.282 ops/ms -Iteration 9: 1.281 ops/ms -Iteration 10: 1.281 ops/ms - -# Run progress: 37.14% complete, ETA 07:21:43 -# Fork: 5 of 5 -# Warmup Iteration 1: 0.883 ops/ms -# Warmup Iteration 2: 1.337 ops/ms -# Warmup Iteration 3: 1.343 ops/ms -# Warmup Iteration 4: 1.343 ops/ms -# Warmup Iteration 5: 1.343 ops/ms -# Warmup Iteration 6: 1.343 ops/ms -# Warmup Iteration 7: 1.343 ops/ms -# Warmup Iteration 8: 1.339 ops/ms -# Warmup Iteration 9: 1.343 ops/ms -# Warmup Iteration 10: 1.344 ops/ms -Iteration 1: 1.344 ops/ms -Iteration 2: 1.344 ops/ms -Iteration 3: 1.344 ops/ms -Iteration 4: 1.344 ops/ms -Iteration 5: 1.344 ops/ms -Iteration 6: 1.344 ops/ms -Iteration 7: 1.344 ops/ms -Iteration 8: 1.344 ops/ms -Iteration 9: 1.343 ops/ms -Iteration 10: 1.344 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.beanArrayDeser": - 1.333 ±(99.9%) 0.014 ops/ms [Average] - (min, avg, max) = (1.279, 1.333, 1.367), stdev = 0.028 - CI (99.9%): [1.319, 1.348] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.beanArraySer - -# Run progress: 38.10% complete, ETA 07:15:01 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.741 ops/ms -# Warmup Iteration 2: 2.300 ops/ms -# Warmup Iteration 3: 2.305 ops/ms -# Warmup Iteration 4: 2.306 ops/ms -# Warmup Iteration 5: 2.304 ops/ms -# Warmup Iteration 6: 2.305 ops/ms -# Warmup Iteration 7: 2.306 ops/ms -# Warmup Iteration 8: 2.306 ops/ms -# Warmup Iteration 9: 2.307 ops/ms -# Warmup Iteration 10: 2.304 ops/ms -Iteration 1: 2.307 ops/ms -Iteration 2: 2.306 ops/ms -Iteration 3: 2.307 ops/ms -Iteration 4: 2.307 ops/ms -Iteration 5: 2.308 ops/ms -Iteration 6: 2.307 ops/ms -Iteration 7: 2.309 ops/ms -Iteration 8: 2.310 ops/ms -Iteration 9: 2.309 ops/ms -Iteration 10: 2.309 ops/ms - -# Run progress: 39.05% complete, ETA 07:08:21 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.533 ops/ms -# Warmup Iteration 2: 2.249 ops/ms -# Warmup Iteration 3: 2.254 ops/ms -# Warmup Iteration 4: 2.256 ops/ms -# Warmup Iteration 5: 2.254 ops/ms -# Warmup Iteration 6: 2.255 ops/ms -# Warmup Iteration 7: 2.253 ops/ms -# Warmup Iteration 8: 2.254 ops/ms -# Warmup Iteration 9: 2.255 ops/ms -# Warmup Iteration 10: 2.146 ops/ms -Iteration 1: 2.232 ops/ms -Iteration 2: 2.255 ops/ms -Iteration 3: 2.255 ops/ms -Iteration 4: 2.255 ops/ms -Iteration 5: 2.256 ops/ms -Iteration 6: 2.255 ops/ms -Iteration 7: 2.257 ops/ms -Iteration 8: 2.257 ops/ms -Iteration 9: 2.257 ops/ms -Iteration 10: 2.257 ops/ms - -# Run progress: 40.00% complete, ETA 07:01:39 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.473 ops/ms -# Warmup Iteration 2: 2.248 ops/ms -# Warmup Iteration 3: 2.252 ops/ms -# Warmup Iteration 4: 2.253 ops/ms -# Warmup Iteration 5: 2.251 ops/ms -# Warmup Iteration 6: 2.253 ops/ms -# Warmup Iteration 7: 2.256 ops/ms -# Warmup Iteration 8: 2.254 ops/ms -# Warmup Iteration 9: 2.255 ops/ms -# Warmup Iteration 10: 2.255 ops/ms -Iteration 1: 2.255 ops/ms -Iteration 2: 2.254 ops/ms -Iteration 3: 2.254 ops/ms -Iteration 4: 2.255 ops/ms -Iteration 5: 2.255 ops/ms -Iteration 6: 2.255 ops/ms -Iteration 7: 2.253 ops/ms -Iteration 8: 2.253 ops/ms -Iteration 9: 2.253 ops/ms -Iteration 10: 2.253 ops/ms - -# Run progress: 40.95% complete, ETA 06:54:57 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.494 ops/ms -# Warmup Iteration 2: 2.229 ops/ms -# Warmup Iteration 3: 2.244 ops/ms -# Warmup Iteration 4: 2.243 ops/ms -# Warmup Iteration 5: 2.242 ops/ms -# Warmup Iteration 6: 2.244 ops/ms -# Warmup Iteration 7: 2.244 ops/ms -# Warmup Iteration 8: 2.244 ops/ms -# Warmup Iteration 9: 2.244 ops/ms -# Warmup Iteration 10: 2.244 ops/ms -Iteration 1: 2.245 ops/ms -Iteration 2: 2.245 ops/ms -Iteration 3: 2.244 ops/ms -Iteration 4: 2.245 ops/ms -Iteration 5: 2.244 ops/ms -Iteration 6: 2.244 ops/ms -Iteration 7: 2.253 ops/ms -Iteration 8: 2.252 ops/ms -Iteration 9: 2.252 ops/ms -Iteration 10: 2.252 ops/ms - -# Run progress: 41.90% complete, ETA 06:48:15 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.486 ops/ms -# Warmup Iteration 2: 2.210 ops/ms -# Warmup Iteration 3: 2.216 ops/ms -# Warmup Iteration 4: 2.219 ops/ms -# Warmup Iteration 5: 2.216 ops/ms -# Warmup Iteration 6: 2.213 ops/ms -# Warmup Iteration 7: 2.214 ops/ms -# Warmup Iteration 8: 2.215 ops/ms -# Warmup Iteration 9: 2.213 ops/ms -# Warmup Iteration 10: 2.214 ops/ms -Iteration 1: 2.215 ops/ms -Iteration 2: 2.215 ops/ms -Iteration 3: 2.213 ops/ms -Iteration 4: 2.212 ops/ms -Iteration 5: 2.213 ops/ms -Iteration 6: 2.215 ops/ms -Iteration 7: 2.225 ops/ms -Iteration 8: 2.226 ops/ms -Iteration 9: 2.225 ops/ms -Iteration 10: 2.224 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.beanArraySer": - 2.256 ±(99.9%) 0.015 ops/ms [Average] - (min, avg, max) = (2.212, 2.256, 2.310), stdev = 0.030 - CI (99.9%): [2.242, 2.271] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemDeser - -# Run progress: 42.86% complete, ETA 06:41:33 -# Fork: 1 of 5 -# Warmup Iteration 1: 0.996 ops/ms -# Warmup Iteration 2: 1.517 ops/ms -# Warmup Iteration 3: 1.531 ops/ms -# Warmup Iteration 4: 1.531 ops/ms -# Warmup Iteration 5: 1.530 ops/ms -# Warmup Iteration 6: 1.532 ops/ms -# Warmup Iteration 7: 1.527 ops/ms -# Warmup Iteration 8: 1.531 ops/ms -# Warmup Iteration 9: 1.533 ops/ms -# Warmup Iteration 10: 1.533 ops/ms -Iteration 1: 1.533 ops/ms -Iteration 2: 1.533 ops/ms -Iteration 3: 1.534 ops/ms -Iteration 4: 1.535 ops/ms -Iteration 5: 1.534 ops/ms -Iteration 6: 1.533 ops/ms -Iteration 7: 1.533 ops/ms -Iteration 8: 1.533 ops/ms -Iteration 9: 1.532 ops/ms -Iteration 10: 1.532 ops/ms - -# Run progress: 43.81% complete, ETA 06:34:51 -# Fork: 2 of 5 -# Warmup Iteration 1: 0.965 ops/ms -# Warmup Iteration 2: 1.406 ops/ms -# Warmup Iteration 3: 1.417 ops/ms -# Warmup Iteration 4: 1.417 ops/ms -# Warmup Iteration 5: 1.415 ops/ms -# Warmup Iteration 6: 1.416 ops/ms -# Warmup Iteration 7: 1.417 ops/ms -# Warmup Iteration 8: 1.410 ops/ms -# Warmup Iteration 9: 1.417 ops/ms -# Warmup Iteration 10: 1.417 ops/ms -Iteration 1: 1.417 ops/ms -Iteration 2: 1.417 ops/ms -Iteration 3: 1.417 ops/ms -Iteration 4: 1.418 ops/ms -Iteration 5: 1.417 ops/ms -Iteration 6: 1.417 ops/ms -Iteration 7: 1.417 ops/ms -Iteration 8: 1.417 ops/ms -Iteration 9: 1.417 ops/ms -Iteration 10: 1.417 ops/ms - -# Run progress: 44.76% complete, ETA 06:28:09 -# Fork: 3 of 5 -# Warmup Iteration 1: 0.997 ops/ms -# Warmup Iteration 2: 1.467 ops/ms -# Warmup Iteration 3: 1.478 ops/ms -# Warmup Iteration 4: 1.479 ops/ms -# Warmup Iteration 5: 1.477 ops/ms -# Warmup Iteration 6: 1.480 ops/ms -# Warmup Iteration 7: 1.481 ops/ms -# Warmup Iteration 8: 1.471 ops/ms -# Warmup Iteration 9: 1.481 ops/ms -# Warmup Iteration 10: 1.481 ops/ms -Iteration 1: 1.481 ops/ms -Iteration 2: 1.480 ops/ms -Iteration 3: 1.481 ops/ms -Iteration 4: 1.481 ops/ms -Iteration 5: 1.481 ops/ms -Iteration 6: 1.480 ops/ms -Iteration 7: 1.481 ops/ms -Iteration 8: 1.481 ops/ms -Iteration 9: 1.481 ops/ms -Iteration 10: 1.481 ops/ms - -# Run progress: 45.71% complete, ETA 06:21:28 -# Fork: 4 of 5 -# Warmup Iteration 1: 0.984 ops/ms -# Warmup Iteration 2: 1.447 ops/ms -# Warmup Iteration 3: 1.462 ops/ms -# Warmup Iteration 4: 1.462 ops/ms -# Warmup Iteration 5: 1.458 ops/ms -# Warmup Iteration 6: 1.462 ops/ms -# Warmup Iteration 7: 1.463 ops/ms -# Warmup Iteration 8: 1.458 ops/ms -# Warmup Iteration 9: 1.461 ops/ms -# Warmup Iteration 10: 1.461 ops/ms -Iteration 1: 1.461 ops/ms -Iteration 2: 1.461 ops/ms -Iteration 3: 1.461 ops/ms -Iteration 4: 1.461 ops/ms -Iteration 5: 1.462 ops/ms -Iteration 6: 1.462 ops/ms -Iteration 7: 1.462 ops/ms -Iteration 8: 1.462 ops/ms -Iteration 9: 1.462 ops/ms -Iteration 10: 1.462 ops/ms - -# Run progress: 46.67% complete, ETA 06:14:46 -# Fork: 5 of 5 -# Warmup Iteration 1: 0.969 ops/ms -# Warmup Iteration 2: 1.493 ops/ms -# Warmup Iteration 3: 1.503 ops/ms -# Warmup Iteration 4: 1.503 ops/ms -# Warmup Iteration 5: 1.502 ops/ms -# Warmup Iteration 6: 1.505 ops/ms -# Warmup Iteration 7: 1.505 ops/ms -# Warmup Iteration 8: 1.490 ops/ms -# Warmup Iteration 9: 1.503 ops/ms -# Warmup Iteration 10: 1.506 ops/ms -Iteration 1: 1.506 ops/ms -Iteration 2: 1.505 ops/ms -Iteration 3: 1.506 ops/ms -Iteration 4: 1.505 ops/ms -Iteration 5: 1.506 ops/ms -Iteration 6: 1.505 ops/ms -Iteration 7: 1.506 ops/ms -Iteration 8: 1.506 ops/ms -Iteration 9: 1.505 ops/ms -Iteration 10: 1.505 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemDeser": - 1.480 ±(99.9%) 0.020 ops/ms [Average] - (min, avg, max) = (1.417, 1.480, 1.535), stdev = 0.040 - CI (99.9%): [1.460, 1.499] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemSer - -# Run progress: 47.62% complete, ETA 06:08:04 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.505 ops/ms -# Warmup Iteration 2: 2.139 ops/ms -# Warmup Iteration 3: 2.149 ops/ms -# Warmup Iteration 4: 2.151 ops/ms -# Warmup Iteration 5: 2.152 ops/ms -# Warmup Iteration 6: 2.146 ops/ms -# Warmup Iteration 7: 2.150 ops/ms -# Warmup Iteration 8: 2.148 ops/ms -# Warmup Iteration 9: 2.150 ops/ms -# Warmup Iteration 10: 2.123 ops/ms -Iteration 1: 2.146 ops/ms -Iteration 2: 2.150 ops/ms -Iteration 3: 2.150 ops/ms -Iteration 4: 2.150 ops/ms -Iteration 5: 2.150 ops/ms -Iteration 6: 2.149 ops/ms -Iteration 7: 2.145 ops/ms -Iteration 8: 2.145 ops/ms -Iteration 9: 2.146 ops/ms -Iteration 10: 2.146 ops/ms - -# Run progress: 48.57% complete, ETA 06:01:22 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.571 ops/ms -# Warmup Iteration 2: 2.202 ops/ms -# Warmup Iteration 3: 2.237 ops/ms -# Warmup Iteration 4: 2.225 ops/ms -# Warmup Iteration 5: 2.227 ops/ms -# Warmup Iteration 6: 2.238 ops/ms -# Warmup Iteration 7: 2.240 ops/ms -# Warmup Iteration 8: 2.240 ops/ms -# Warmup Iteration 9: 2.239 ops/ms -# Warmup Iteration 10: 2.234 ops/ms -Iteration 1: 2.238 ops/ms -Iteration 2: 2.238 ops/ms -Iteration 3: 2.237 ops/ms -Iteration 4: 2.239 ops/ms -Iteration 5: 2.238 ops/ms -Iteration 6: 2.238 ops/ms -Iteration 7: 2.234 ops/ms -Iteration 8: 2.235 ops/ms -Iteration 9: 2.234 ops/ms -Iteration 10: 2.234 ops/ms - -# Run progress: 49.52% complete, ETA 05:54:40 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.502 ops/ms -# Warmup Iteration 2: 2.100 ops/ms -# Warmup Iteration 3: 2.116 ops/ms -# Warmup Iteration 4: 2.118 ops/ms -# Warmup Iteration 5: 2.119 ops/ms -# Warmup Iteration 6: 2.108 ops/ms -# Warmup Iteration 7: 2.119 ops/ms -# Warmup Iteration 8: 2.119 ops/ms -# Warmup Iteration 9: 2.119 ops/ms -# Warmup Iteration 10: 2.118 ops/ms -Iteration 1: 2.119 ops/ms -Iteration 2: 2.120 ops/ms -Iteration 3: 2.119 ops/ms -Iteration 4: 2.119 ops/ms -Iteration 5: 2.120 ops/ms -Iteration 6: 2.119 ops/ms -Iteration 7: 2.118 ops/ms -Iteration 8: 2.119 ops/ms -Iteration 9: 2.120 ops/ms -Iteration 10: 2.120 ops/ms - -# Run progress: 50.48% complete, ETA 05:47:59 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.689 ops/ms -# Warmup Iteration 2: 2.380 ops/ms -# Warmup Iteration 3: 2.399 ops/ms -# Warmup Iteration 4: 2.400 ops/ms -# Warmup Iteration 5: 2.383 ops/ms -# Warmup Iteration 6: 2.396 ops/ms -# Warmup Iteration 7: 2.396 ops/ms -# Warmup Iteration 8: 2.396 ops/ms -# Warmup Iteration 9: 2.396 ops/ms -# Warmup Iteration 10: 2.396 ops/ms -Iteration 1: 2.396 ops/ms -Iteration 2: 2.396 ops/ms -Iteration 3: 2.397 ops/ms -Iteration 4: 2.396 ops/ms -Iteration 5: 2.396 ops/ms -Iteration 6: 2.395 ops/ms -Iteration 7: 2.396 ops/ms -Iteration 8: 2.396 ops/ms -Iteration 9: 2.395 ops/ms -Iteration 10: 2.395 ops/ms - -# Run progress: 51.43% complete, ETA 05:41:17 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.445 ops/ms -# Warmup Iteration 2: 2.083 ops/ms -# Warmup Iteration 3: 2.108 ops/ms -# Warmup Iteration 4: 2.116 ops/ms -# Warmup Iteration 5: 2.111 ops/ms -# Warmup Iteration 6: 2.106 ops/ms -# Warmup Iteration 7: 2.116 ops/ms -# Warmup Iteration 8: 2.113 ops/ms -# Warmup Iteration 9: 2.113 ops/ms -# Warmup Iteration 10: 2.113 ops/ms -Iteration 1: 2.114 ops/ms -Iteration 2: 2.114 ops/ms -Iteration 3: 2.114 ops/ms -Iteration 4: 2.115 ops/ms -Iteration 5: 2.114 ops/ms -Iteration 6: 2.115 ops/ms -Iteration 7: 2.105 ops/ms -Iteration 8: 2.105 ops/ms -Iteration 9: 2.105 ops/ms -Iteration 10: 2.105 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemSer": - 2.202 ±(99.9%) 0.053 ops/ms [Average] - (min, avg, max) = (2.105, 2.202, 2.397), stdev = 0.108 - CI (99.9%): [2.149, 2.255] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser - -# Run progress: 52.38% complete, ETA 05:34:35 -# Fork: 1 of 5 -# Warmup Iteration 1: 269.842 ops/ms -# Warmup Iteration 2: 426.965 ops/ms -# Warmup Iteration 3: 427.886 ops/ms -# Warmup Iteration 4: 427.760 ops/ms -# Warmup Iteration 5: 427.672 ops/ms -# Warmup Iteration 6: 427.550 ops/ms -# Warmup Iteration 7: 427.680 ops/ms -# Warmup Iteration 8: 427.674 ops/ms -# Warmup Iteration 9: 427.671 ops/ms -# Warmup Iteration 10: 427.791 ops/ms -Iteration 1: 427.871 ops/ms -Iteration 2: 427.645 ops/ms -Iteration 3: 427.736 ops/ms -Iteration 4: 427.744 ops/ms -Iteration 5: 427.634 ops/ms -Iteration 6: 427.466 ops/ms -Iteration 7: 427.705 ops/ms -Iteration 8: 427.661 ops/ms -Iteration 9: 427.602 ops/ms -Iteration 10: 427.812 ops/ms - -# Run progress: 53.33% complete, ETA 05:27:54 -# Fork: 2 of 5 -# Warmup Iteration 1: 264.994 ops/ms -# Warmup Iteration 2: 432.247 ops/ms -# Warmup Iteration 3: 434.637 ops/ms -# Warmup Iteration 4: 434.852 ops/ms -# Warmup Iteration 5: 434.296 ops/ms -# Warmup Iteration 6: 434.533 ops/ms -# Warmup Iteration 7: 434.305 ops/ms -# Warmup Iteration 8: 434.574 ops/ms -# Warmup Iteration 9: 434.759 ops/ms -# Warmup Iteration 10: 434.833 ops/ms -Iteration 1: 434.794 ops/ms -Iteration 2: 433.783 ops/ms -Iteration 3: 434.736 ops/ms -Iteration 4: 434.715 ops/ms -Iteration 5: 434.723 ops/ms -Iteration 6: 434.631 ops/ms -Iteration 7: 434.835 ops/ms -Iteration 8: 434.625 ops/ms -Iteration 9: 434.351 ops/ms -Iteration 10: 434.534 ops/ms - -# Run progress: 54.29% complete, ETA 05:21:12 -# Fork: 3 of 5 -# Warmup Iteration 1: 271.124 ops/ms -# Warmup Iteration 2: 428.101 ops/ms -# Warmup Iteration 3: 429.294 ops/ms -# Warmup Iteration 4: 429.562 ops/ms -# Warmup Iteration 5: 429.510 ops/ms -# Warmup Iteration 6: 429.475 ops/ms -# Warmup Iteration 7: 429.138 ops/ms -# Warmup Iteration 8: 429.367 ops/ms -# Warmup Iteration 9: 429.293 ops/ms -# Warmup Iteration 10: 429.242 ops/ms -Iteration 1: 429.146 ops/ms -Iteration 2: 428.835 ops/ms -Iteration 3: 429.279 ops/ms -Iteration 4: 429.285 ops/ms -Iteration 5: 429.218 ops/ms -Iteration 6: 429.479 ops/ms -Iteration 7: 429.519 ops/ms -Iteration 8: 429.586 ops/ms -Iteration 9: 429.405 ops/ms -Iteration 10: 429.479 ops/ms - -# Run progress: 55.24% complete, ETA 05:14:30 -# Fork: 4 of 5 -# Warmup Iteration 1: 278.919 ops/ms -# Warmup Iteration 2: 425.624 ops/ms -# Warmup Iteration 3: 429.124 ops/ms -# Warmup Iteration 4: 429.193 ops/ms -# Warmup Iteration 5: 429.275 ops/ms -# Warmup Iteration 6: 428.729 ops/ms -# Warmup Iteration 7: 428.849 ops/ms -# Warmup Iteration 8: 428.840 ops/ms -# Warmup Iteration 9: 428.624 ops/ms -# Warmup Iteration 10: 429.092 ops/ms -Iteration 1: 429.160 ops/ms -Iteration 2: 428.441 ops/ms -Iteration 3: 428.926 ops/ms -Iteration 4: 428.795 ops/ms -Iteration 5: 429.211 ops/ms -Iteration 6: 428.798 ops/ms -Iteration 7: 428.962 ops/ms -Iteration 8: 428.797 ops/ms -Iteration 9: 428.819 ops/ms -Iteration 10: 428.828 ops/ms - -# Run progress: 56.19% complete, ETA 05:07:49 -# Fork: 5 of 5 -# Warmup Iteration 1: 270.693 ops/ms -# Warmup Iteration 2: 428.425 ops/ms -# Warmup Iteration 3: 430.090 ops/ms -# Warmup Iteration 4: 430.014 ops/ms -# Warmup Iteration 5: 429.679 ops/ms -# Warmup Iteration 6: 430.022 ops/ms -# Warmup Iteration 7: 429.834 ops/ms -# Warmup Iteration 8: 430.877 ops/ms -# Warmup Iteration 9: 429.457 ops/ms -# Warmup Iteration 10: 428.485 ops/ms -Iteration 1: 429.235 ops/ms -Iteration 2: 430.117 ops/ms -Iteration 3: 429.521 ops/ms -Iteration 4: 430.274 ops/ms -Iteration 5: 430.086 ops/ms -Iteration 6: 430.021 ops/ms -Iteration 7: 429.557 ops/ms -Iteration 8: 429.530 ops/ms -Iteration 9: 429.257 ops/ms -Iteration 10: 429.360 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser": - 430.031 ±(99.9%) 1.192 ops/ms [Average] - (min, avg, max) = (427.466, 430.031, 434.835), stdev = 2.407 - CI (99.9%): [428.839, 431.222] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemSer - -# Run progress: 57.14% complete, ETA 05:01:07 -# Fork: 1 of 5 -# Warmup Iteration 1: 419.815 ops/ms -# Warmup Iteration 2: 653.959 ops/ms -# Warmup Iteration 3: 660.267 ops/ms -# Warmup Iteration 4: 660.270 ops/ms -# Warmup Iteration 5: 659.690 ops/ms -# Warmup Iteration 6: 660.104 ops/ms -# Warmup Iteration 7: 659.442 ops/ms -# Warmup Iteration 8: 660.428 ops/ms -# Warmup Iteration 9: 659.630 ops/ms -# Warmup Iteration 10: 660.550 ops/ms -Iteration 1: 660.554 ops/ms -Iteration 2: 660.062 ops/ms -Iteration 3: 660.594 ops/ms -Iteration 4: 659.338 ops/ms -Iteration 5: 659.314 ops/ms -Iteration 6: 658.849 ops/ms -Iteration 7: 662.076 ops/ms -Iteration 8: 662.076 ops/ms -Iteration 9: 662.230 ops/ms -Iteration 10: 662.442 ops/ms - -# Run progress: 58.10% complete, ETA 04:54:25 -# Fork: 2 of 5 -# Warmup Iteration 1: 431.011 ops/ms -# Warmup Iteration 2: 653.791 ops/ms -# Warmup Iteration 3: 657.293 ops/ms -# Warmup Iteration 4: 656.996 ops/ms -# Warmup Iteration 5: 656.962 ops/ms -# Warmup Iteration 6: 656.695 ops/ms -# Warmup Iteration 7: 657.525 ops/ms -# Warmup Iteration 8: 657.216 ops/ms -# Warmup Iteration 9: 656.581 ops/ms -# Warmup Iteration 10: 657.250 ops/ms -Iteration 1: 657.329 ops/ms -Iteration 2: 657.331 ops/ms -Iteration 3: 657.506 ops/ms -Iteration 4: 657.544 ops/ms -Iteration 5: 657.322 ops/ms -Iteration 6: 657.273 ops/ms -Iteration 7: 657.881 ops/ms -Iteration 8: 657.667 ops/ms -Iteration 9: 657.905 ops/ms -Iteration 10: 658.438 ops/ms - -# Run progress: 59.05% complete, ETA 04:47:43 -# Fork: 3 of 5 -# Warmup Iteration 1: 431.742 ops/ms -# Warmup Iteration 2: 646.056 ops/ms -# Warmup Iteration 3: 651.938 ops/ms -# Warmup Iteration 4: 651.688 ops/ms -# Warmup Iteration 5: 651.590 ops/ms -# Warmup Iteration 6: 651.669 ops/ms -# Warmup Iteration 7: 651.384 ops/ms -# Warmup Iteration 8: 651.560 ops/ms -# Warmup Iteration 9: 651.615 ops/ms -# Warmup Iteration 10: 650.625 ops/ms -Iteration 1: 651.499 ops/ms -Iteration 2: 651.333 ops/ms -Iteration 3: 651.278 ops/ms -Iteration 4: 650.907 ops/ms -Iteration 5: 651.245 ops/ms -Iteration 6: 651.398 ops/ms -Iteration 7: 650.099 ops/ms -Iteration 8: 650.504 ops/ms -Iteration 9: 649.807 ops/ms -Iteration 10: 650.461 ops/ms - -# Run progress: 60.00% complete, ETA 04:41:02 -# Fork: 4 of 5 -# Warmup Iteration 1: 418.774 ops/ms -# Warmup Iteration 2: 663.891 ops/ms -# Warmup Iteration 3: 667.084 ops/ms -# Warmup Iteration 4: 666.423 ops/ms -# Warmup Iteration 5: 666.864 ops/ms -# Warmup Iteration 6: 666.846 ops/ms -# Warmup Iteration 7: 666.455 ops/ms -# Warmup Iteration 8: 666.951 ops/ms -# Warmup Iteration 9: 666.543 ops/ms -# Warmup Iteration 10: 666.752 ops/ms -Iteration 1: 666.794 ops/ms -Iteration 2: 667.143 ops/ms -Iteration 3: 666.799 ops/ms -Iteration 4: 666.316 ops/ms -Iteration 5: 666.235 ops/ms -Iteration 6: 666.516 ops/ms -Iteration 7: 665.412 ops/ms -Iteration 8: 665.276 ops/ms -Iteration 9: 665.358 ops/ms -Iteration 10: 665.454 ops/ms - -# Run progress: 60.95% complete, ETA 04:34:20 -# Fork: 5 of 5 -# Warmup Iteration 1: 426.510 ops/ms -# Warmup Iteration 2: 660.379 ops/ms -# Warmup Iteration 3: 662.777 ops/ms -# Warmup Iteration 4: 663.068 ops/ms -# Warmup Iteration 5: 663.108 ops/ms -# Warmup Iteration 6: 662.787 ops/ms -# Warmup Iteration 7: 662.482 ops/ms -# Warmup Iteration 8: 663.283 ops/ms -# Warmup Iteration 9: 663.045 ops/ms -# Warmup Iteration 10: 663.005 ops/ms -Iteration 1: 662.232 ops/ms -Iteration 2: 662.971 ops/ms -Iteration 3: 663.310 ops/ms -Iteration 4: 663.342 ops/ms -Iteration 5: 663.310 ops/ms -Iteration 6: 663.327 ops/ms -Iteration 7: 659.804 ops/ms -Iteration 8: 659.166 ops/ms -Iteration 9: 658.826 ops/ms -Iteration 10: 659.791 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemSer": - 659.393 ±(99.9%) 2.591 ops/ms [Average] - (min, avg, max) = (649.807, 659.393, 667.143), stdev = 5.234 - CI (99.9%): [656.802, 661.984] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.constructorArrayDeser - -# Run progress: 61.90% complete, ETA 04:27:39 -# Fork: 1 of 5 -# Warmup Iteration 1: 0.704 ops/ms -# Warmup Iteration 2: 1.088 ops/ms -# Warmup Iteration 3: 1.100 ops/ms -# Warmup Iteration 4: 1.099 ops/ms -# Warmup Iteration 5: 1.099 ops/ms -# Warmup Iteration 6: 1.099 ops/ms -# Warmup Iteration 7: 1.100 ops/ms -# Warmup Iteration 8: 1.098 ops/ms -# Warmup Iteration 9: 1.098 ops/ms -# Warmup Iteration 10: 1.091 ops/ms -Iteration 1: 1.100 ops/ms -Iteration 2: 1.098 ops/ms -Iteration 3: 1.099 ops/ms -Iteration 4: 1.100 ops/ms -Iteration 5: 1.100 ops/ms -Iteration 6: 1.091 ops/ms -Iteration 7: 1.091 ops/ms -Iteration 8: 1.092 ops/ms -Iteration 9: 1.091 ops/ms -Iteration 10: 1.092 ops/ms - -# Run progress: 62.86% complete, ETA 04:20:57 -# Fork: 2 of 5 -# Warmup Iteration 1: 0.694 ops/ms -# Warmup Iteration 2: 1.072 ops/ms -# Warmup Iteration 3: 1.092 ops/ms -# Warmup Iteration 4: 1.091 ops/ms -# Warmup Iteration 5: 1.092 ops/ms -# Warmup Iteration 6: 1.092 ops/ms -# Warmup Iteration 7: 1.093 ops/ms -# Warmup Iteration 8: 1.092 ops/ms -# Warmup Iteration 9: 1.092 ops/ms -# Warmup Iteration 10: 1.077 ops/ms -Iteration 1: 1.083 ops/ms -Iteration 2: 1.084 ops/ms -Iteration 3: 1.084 ops/ms -Iteration 4: 1.084 ops/ms -Iteration 5: 1.083 ops/ms -Iteration 6: 1.084 ops/ms -Iteration 7: 1.085 ops/ms -Iteration 8: 1.084 ops/ms -Iteration 9: 1.084 ops/ms -Iteration 10: 1.084 ops/ms - -# Run progress: 63.81% complete, ETA 04:14:15 -# Fork: 3 of 5 -# Warmup Iteration 1: 0.691 ops/ms -# Warmup Iteration 2: 1.081 ops/ms -# Warmup Iteration 3: 1.093 ops/ms -# Warmup Iteration 4: 1.093 ops/ms -# Warmup Iteration 5: 1.093 ops/ms -# Warmup Iteration 6: 1.092 ops/ms -# Warmup Iteration 7: 1.089 ops/ms -# Warmup Iteration 8: 1.093 ops/ms -# Warmup Iteration 9: 1.093 ops/ms -# Warmup Iteration 10: 1.090 ops/ms -Iteration 1: 1.093 ops/ms -Iteration 2: 1.093 ops/ms -Iteration 3: 1.093 ops/ms -Iteration 4: 1.093 ops/ms -Iteration 5: 1.093 ops/ms -Iteration 6: 1.093 ops/ms -Iteration 7: 1.093 ops/ms -Iteration 8: 1.093 ops/ms -Iteration 9: 1.093 ops/ms -Iteration 10: 1.093 ops/ms - -# Run progress: 64.76% complete, ETA 04:07:34 -# Fork: 4 of 5 -# Warmup Iteration 1: 0.668 ops/ms -# Warmup Iteration 2: 1.071 ops/ms -# Warmup Iteration 3: 1.089 ops/ms -# Warmup Iteration 4: 1.089 ops/ms -# Warmup Iteration 5: 1.091 ops/ms -# Warmup Iteration 6: 1.090 ops/ms -# Warmup Iteration 7: 1.088 ops/ms -# Warmup Iteration 8: 1.090 ops/ms -# Warmup Iteration 9: 1.066 ops/ms -# Warmup Iteration 10: 1.018 ops/ms -Iteration 1: 1.091 ops/ms -Iteration 2: 1.088 ops/ms -Iteration 3: 1.086 ops/ms -Iteration 4: 1.087 ops/ms -Iteration 5: 1.089 ops/ms -Iteration 6: 1.089 ops/ms -Iteration 7: 1.089 ops/ms -Iteration 8: 1.089 ops/ms -Iteration 9: 1.088 ops/ms -Iteration 10: 1.091 ops/ms - -# Run progress: 65.71% complete, ETA 04:00:52 -# Fork: 5 of 5 -# Warmup Iteration 1: 0.700 ops/ms -# Warmup Iteration 2: 1.083 ops/ms -# Warmup Iteration 3: 1.106 ops/ms -# Warmup Iteration 4: 1.107 ops/ms -# Warmup Iteration 5: 1.106 ops/ms -# Warmup Iteration 6: 1.101 ops/ms -# Warmup Iteration 7: 1.093 ops/ms -# Warmup Iteration 8: 1.097 ops/ms -# Warmup Iteration 9: 1.101 ops/ms -# Warmup Iteration 10: 1.092 ops/ms -Iteration 1: 1.098 ops/ms -Iteration 2: 1.096 ops/ms -Iteration 3: 1.099 ops/ms -Iteration 4: 1.098 ops/ms -Iteration 5: 1.099 ops/ms -Iteration 6: 1.097 ops/ms -Iteration 7: 1.099 ops/ms -Iteration 8: 1.098 ops/ms -Iteration 9: 1.098 ops/ms -Iteration 10: 1.098 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.constructorArrayDeser": - 1.092 ±(99.9%) 0.003 ops/ms [Average] - (min, avg, max) = (1.083, 1.092, 1.100), stdev = 0.006 - CI (99.9%): [1.089, 1.095] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.beanArrayDeser - -# Run progress: 66.67% complete, ETA 03:54:11 -# Fork: 1 of 5 -# Warmup Iteration 1: 0.725 ops/ms -# Warmup Iteration 2: 1.116 ops/ms -# Warmup Iteration 3: 1.137 ops/ms -# Warmup Iteration 4: 1.136 ops/ms -# Warmup Iteration 5: 1.135 ops/ms -# Warmup Iteration 6: 1.136 ops/ms -# Warmup Iteration 7: 1.136 ops/ms -# Warmup Iteration 8: 1.136 ops/ms -# Warmup Iteration 9: 1.136 ops/ms -# Warmup Iteration 10: 1.130 ops/ms -Iteration 1: 1.137 ops/ms -Iteration 2: 1.137 ops/ms -Iteration 3: 1.137 ops/ms -Iteration 4: 1.137 ops/ms -Iteration 5: 1.136 ops/ms -Iteration 6: 1.137 ops/ms -Iteration 7: 1.137 ops/ms -Iteration 8: 1.138 ops/ms -Iteration 9: 1.137 ops/ms -Iteration 10: 1.138 ops/ms - -# Run progress: 67.62% complete, ETA 03:47:29 -# Fork: 2 of 5 -# Warmup Iteration 1: 0.713 ops/ms -# Warmup Iteration 2: 1.076 ops/ms -# Warmup Iteration 3: 1.096 ops/ms -# Warmup Iteration 4: 1.096 ops/ms -# Warmup Iteration 5: 1.096 ops/ms -# Warmup Iteration 6: 1.096 ops/ms -# Warmup Iteration 7: 1.097 ops/ms -# Warmup Iteration 8: 1.096 ops/ms -# Warmup Iteration 9: 1.096 ops/ms -# Warmup Iteration 10: 1.093 ops/ms -Iteration 1: 1.097 ops/ms -Iteration 2: 1.097 ops/ms -Iteration 3: 1.097 ops/ms -Iteration 4: 1.097 ops/ms -Iteration 5: 1.097 ops/ms -Iteration 6: 1.097 ops/ms -Iteration 7: 1.098 ops/ms -Iteration 8: 1.097 ops/ms -Iteration 9: 1.097 ops/ms -Iteration 10: 1.097 ops/ms - -# Run progress: 68.57% complete, ETA 03:40:48 -# Fork: 3 of 5 -# Warmup Iteration 1: 0.649 ops/ms -# Warmup Iteration 2: 1.079 ops/ms -# Warmup Iteration 3: 1.087 ops/ms -# Warmup Iteration 4: 1.087 ops/ms -# Warmup Iteration 5: 1.087 ops/ms -# Warmup Iteration 6: 1.087 ops/ms -# Warmup Iteration 7: 1.090 ops/ms -# Warmup Iteration 8: 1.092 ops/ms -# Warmup Iteration 9: 1.092 ops/ms -# Warmup Iteration 10: 1.081 ops/ms -Iteration 1: 1.093 ops/ms -Iteration 2: 1.093 ops/ms -Iteration 3: 1.093 ops/ms -Iteration 4: 1.093 ops/ms -Iteration 5: 1.093 ops/ms -Iteration 6: 1.093 ops/ms -Iteration 7: 1.094 ops/ms -Iteration 8: 1.095 ops/ms -Iteration 9: 1.094 ops/ms -Iteration 10: 1.094 ops/ms - -# Run progress: 69.52% complete, ETA 03:34:06 -# Fork: 4 of 5 -# Warmup Iteration 1: 0.714 ops/ms -# Warmup Iteration 2: 1.115 ops/ms -# Warmup Iteration 3: 1.127 ops/ms -# Warmup Iteration 4: 1.127 ops/ms -# Warmup Iteration 5: 1.127 ops/ms -# Warmup Iteration 6: 1.127 ops/ms -# Warmup Iteration 7: 1.127 ops/ms -# Warmup Iteration 8: 1.127 ops/ms -# Warmup Iteration 9: 1.127 ops/ms -# Warmup Iteration 10: 1.125 ops/ms -Iteration 1: 1.128 ops/ms -Iteration 2: 1.126 ops/ms -Iteration 3: 1.128 ops/ms -Iteration 4: 1.128 ops/ms -Iteration 5: 1.127 ops/ms -Iteration 6: 1.127 ops/ms -Iteration 7: 1.129 ops/ms -Iteration 8: 1.129 ops/ms -Iteration 9: 1.128 ops/ms -Iteration 10: 1.129 ops/ms - -# Run progress: 70.48% complete, ETA 03:27:25 -# Fork: 5 of 5 -# Warmup Iteration 1: 0.736 ops/ms -# Warmup Iteration 2: 1.119 ops/ms -# Warmup Iteration 3: 1.131 ops/ms -# Warmup Iteration 4: 1.131 ops/ms -# Warmup Iteration 5: 1.131 ops/ms -# Warmup Iteration 6: 1.131 ops/ms -# Warmup Iteration 7: 1.131 ops/ms -# Warmup Iteration 8: 1.132 ops/ms -# Warmup Iteration 9: 1.128 ops/ms -# Warmup Iteration 10: 1.118 ops/ms -Iteration 1: 1.131 ops/ms -Iteration 2: 1.131 ops/ms -Iteration 3: 1.131 ops/ms -Iteration 4: 1.132 ops/ms -Iteration 5: 1.131 ops/ms -Iteration 6: 1.131 ops/ms -Iteration 7: 1.132 ops/ms -Iteration 8: 1.133 ops/ms -Iteration 9: 1.132 ops/ms -Iteration 10: 1.132 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.beanArrayDeser": - 1.117 ±(99.9%) 0.009 ops/ms [Average] - (min, avg, max) = (1.093, 1.117, 1.138), stdev = 0.018 - CI (99.9%): [1.108, 1.127] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.beanArraySer - -# Run progress: 71.43% complete, ETA 03:20:43 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.314 ops/ms -# Warmup Iteration 2: 2.039 ops/ms -# Warmup Iteration 3: 2.046 ops/ms -# Warmup Iteration 4: 2.047 ops/ms -# Warmup Iteration 5: 2.043 ops/ms -# Warmup Iteration 6: 2.045 ops/ms -# Warmup Iteration 7: 2.047 ops/ms -# Warmup Iteration 8: 2.046 ops/ms -# Warmup Iteration 9: 2.048 ops/ms -# Warmup Iteration 10: 2.047 ops/ms -Iteration 1: 2.048 ops/ms -Iteration 2: 2.047 ops/ms -Iteration 3: 2.047 ops/ms -Iteration 4: 2.047 ops/ms -Iteration 5: 2.047 ops/ms -Iteration 6: 2.047 ops/ms -Iteration 7: 2.047 ops/ms -Iteration 8: 2.047 ops/ms -Iteration 9: 2.047 ops/ms -Iteration 10: 2.048 ops/ms - -# Run progress: 72.38% complete, ETA 03:14:02 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.356 ops/ms -# Warmup Iteration 2: 2.110 ops/ms -# Warmup Iteration 3: 2.119 ops/ms -# Warmup Iteration 4: 2.119 ops/ms -# Warmup Iteration 5: 2.121 ops/ms -# Warmup Iteration 6: 2.111 ops/ms -# Warmup Iteration 7: 2.115 ops/ms -# Warmup Iteration 8: 2.117 ops/ms -# Warmup Iteration 9: 2.116 ops/ms -# Warmup Iteration 10: 2.116 ops/ms -Iteration 1: 2.115 ops/ms -Iteration 2: 2.116 ops/ms -Iteration 3: 2.117 ops/ms -Iteration 4: 2.116 ops/ms -Iteration 5: 2.116 ops/ms -Iteration 6: 2.117 ops/ms -Iteration 7: 2.113 ops/ms -Iteration 8: 2.113 ops/ms -Iteration 9: 2.112 ops/ms -Iteration 10: 2.113 ops/ms - -# Run progress: 73.33% complete, ETA 03:07:20 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.346 ops/ms -# Warmup Iteration 2: 2.061 ops/ms -# Warmup Iteration 3: 2.077 ops/ms -# Warmup Iteration 4: 2.075 ops/ms -# Warmup Iteration 5: 2.076 ops/ms -# Warmup Iteration 6: 2.076 ops/ms -# Warmup Iteration 7: 2.079 ops/ms -# Warmup Iteration 8: 2.078 ops/ms -# Warmup Iteration 9: 2.073 ops/ms -# Warmup Iteration 10: 2.079 ops/ms -Iteration 1: 2.079 ops/ms -Iteration 2: 2.078 ops/ms -Iteration 3: 2.079 ops/ms -Iteration 4: 2.080 ops/ms -Iteration 5: 2.079 ops/ms -Iteration 6: 2.078 ops/ms -Iteration 7: 2.070 ops/ms -Iteration 8: 2.070 ops/ms -Iteration 9: 2.070 ops/ms -Iteration 10: 2.069 ops/ms - -# Run progress: 74.29% complete, ETA 03:00:39 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.354 ops/ms -# Warmup Iteration 2: 2.093 ops/ms -# Warmup Iteration 3: 2.096 ops/ms -# Warmup Iteration 4: 2.100 ops/ms -# Warmup Iteration 5: 2.100 ops/ms -# Warmup Iteration 6: 2.098 ops/ms -# Warmup Iteration 7: 2.098 ops/ms -# Warmup Iteration 8: 2.100 ops/ms -# Warmup Iteration 9: 2.099 ops/ms -# Warmup Iteration 10: 2.098 ops/ms -Iteration 1: 2.100 ops/ms -Iteration 2: 2.095 ops/ms -Iteration 3: 2.100 ops/ms -Iteration 4: 2.099 ops/ms -Iteration 5: 2.099 ops/ms -Iteration 6: 2.100 ops/ms -Iteration 7: 2.099 ops/ms -Iteration 8: 2.099 ops/ms -Iteration 9: 2.099 ops/ms -Iteration 10: 2.099 ops/ms - -# Run progress: 75.24% complete, ETA 02:53:57 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.386 ops/ms -# Warmup Iteration 2: 2.101 ops/ms -# Warmup Iteration 3: 2.113 ops/ms -# Warmup Iteration 4: 2.117 ops/ms -# Warmup Iteration 5: 2.119 ops/ms -# Warmup Iteration 6: 2.107 ops/ms -# Warmup Iteration 7: 2.118 ops/ms -# Warmup Iteration 8: 2.116 ops/ms -# Warmup Iteration 9: 2.117 ops/ms -# Warmup Iteration 10: 2.117 ops/ms -Iteration 1: 2.117 ops/ms -Iteration 2: 2.118 ops/ms -Iteration 3: 2.117 ops/ms -Iteration 4: 2.118 ops/ms -Iteration 5: 2.118 ops/ms -Iteration 6: 2.118 ops/ms -Iteration 7: 2.121 ops/ms -Iteration 8: 2.121 ops/ms -Iteration 9: 2.121 ops/ms -Iteration 10: 2.121 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.beanArraySer": - 2.091 ±(99.9%) 0.013 ops/ms [Average] - (min, avg, max) = (2.047, 2.091, 2.121), stdev = 0.027 - CI (99.9%): [2.078, 2.104] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemDeser - -# Run progress: 76.19% complete, ETA 02:47:16 -# Fork: 1 of 5 -# Warmup Iteration 1: 0.686 ops/ms -# Warmup Iteration 2: 1.065 ops/ms -# Warmup Iteration 3: 1.080 ops/ms -# Warmup Iteration 4: 1.080 ops/ms -# Warmup Iteration 5: 1.080 ops/ms -# Warmup Iteration 6: 1.080 ops/ms -# Warmup Iteration 7: 1.079 ops/ms -# Warmup Iteration 8: 1.081 ops/ms -# Warmup Iteration 9: 1.081 ops/ms -# Warmup Iteration 10: 1.077 ops/ms -Iteration 1: 1.082 ops/ms -Iteration 2: 1.081 ops/ms -Iteration 3: 1.082 ops/ms -Iteration 4: 1.082 ops/ms -Iteration 5: 1.082 ops/ms -Iteration 6: 1.081 ops/ms -Iteration 7: 1.083 ops/ms -Iteration 8: 1.083 ops/ms -Iteration 9: 1.083 ops/ms -Iteration 10: 1.083 ops/ms - -# Run progress: 77.14% complete, ETA 02:40:34 -# Fork: 2 of 5 -# Warmup Iteration 1: 0.703 ops/ms -# Warmup Iteration 2: 1.083 ops/ms -# Warmup Iteration 3: 1.096 ops/ms -# Warmup Iteration 4: 1.095 ops/ms -# Warmup Iteration 5: 1.096 ops/ms -# Warmup Iteration 6: 1.094 ops/ms -# Warmup Iteration 7: 1.090 ops/ms -# Warmup Iteration 8: 1.095 ops/ms -# Warmup Iteration 9: 1.096 ops/ms -# Warmup Iteration 10: 1.090 ops/ms -Iteration 1: 1.096 ops/ms -Iteration 2: 1.096 ops/ms -Iteration 3: 1.097 ops/ms -Iteration 4: 1.096 ops/ms -Iteration 5: 1.096 ops/ms -Iteration 6: 1.096 ops/ms -Iteration 7: 1.095 ops/ms -Iteration 8: 1.096 ops/ms -Iteration 9: 1.095 ops/ms -Iteration 10: 1.095 ops/ms - -# Run progress: 78.10% complete, ETA 02:33:53 -# Fork: 3 of 5 -# Warmup Iteration 1: 0.695 ops/ms -# Warmup Iteration 2: 1.076 ops/ms -# Warmup Iteration 3: 1.089 ops/ms -# Warmup Iteration 4: 1.089 ops/ms -# Warmup Iteration 5: 1.089 ops/ms -# Warmup Iteration 6: 1.088 ops/ms -# Warmup Iteration 7: 1.086 ops/ms -# Warmup Iteration 8: 1.088 ops/ms -# Warmup Iteration 9: 1.089 ops/ms -# Warmup Iteration 10: 1.084 ops/ms -Iteration 1: 1.089 ops/ms -Iteration 2: 1.089 ops/ms -Iteration 3: 1.089 ops/ms -Iteration 4: 1.090 ops/ms -Iteration 5: 1.089 ops/ms -Iteration 6: 1.089 ops/ms -Iteration 7: 1.090 ops/ms -Iteration 8: 1.090 ops/ms -Iteration 9: 1.089 ops/ms -Iteration 10: 1.090 ops/ms - -# Run progress: 79.05% complete, ETA 02:27:11 -# Fork: 4 of 5 -# Warmup Iteration 1: 0.676 ops/ms -# Warmup Iteration 2: 1.035 ops/ms -# Warmup Iteration 3: 1.050 ops/ms -# Warmup Iteration 4: 1.050 ops/ms -# Warmup Iteration 5: 1.050 ops/ms -# Warmup Iteration 6: 1.051 ops/ms -# Warmup Iteration 7: 1.052 ops/ms -# Warmup Iteration 8: 1.056 ops/ms -# Warmup Iteration 9: 1.058 ops/ms -# Warmup Iteration 10: 1.057 ops/ms -Iteration 1: 1.055 ops/ms -Iteration 2: 1.055 ops/ms -Iteration 3: 1.057 ops/ms -Iteration 4: 1.058 ops/ms -Iteration 5: 1.057 ops/ms -Iteration 6: 1.057 ops/ms -Iteration 7: 1.063 ops/ms -Iteration 8: 1.063 ops/ms -Iteration 9: 1.064 ops/ms -Iteration 10: 1.061 ops/ms - -# Run progress: 80.00% complete, ETA 02:20:30 -# Fork: 5 of 5 -# Warmup Iteration 1: 0.706 ops/ms -# Warmup Iteration 2: 1.073 ops/ms -# Warmup Iteration 3: 1.081 ops/ms -# Warmup Iteration 4: 1.082 ops/ms -# Warmup Iteration 5: 1.082 ops/ms -# Warmup Iteration 6: 1.082 ops/ms -# Warmup Iteration 7: 1.076 ops/ms -# Warmup Iteration 8: 1.082 ops/ms -# Warmup Iteration 9: 1.081 ops/ms -# Warmup Iteration 10: 1.076 ops/ms -Iteration 1: 1.083 ops/ms -Iteration 2: 1.082 ops/ms -Iteration 3: 1.083 ops/ms -Iteration 4: 1.083 ops/ms -Iteration 5: 1.083 ops/ms -Iteration 6: 1.083 ops/ms -Iteration 7: 1.082 ops/ms -Iteration 8: 1.082 ops/ms -Iteration 9: 1.082 ops/ms -Iteration 10: 1.082 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemDeser": - 1.082 ±(99.9%) 0.006 ops/ms [Average] - (min, avg, max) = (1.055, 1.082, 1.097), stdev = 0.013 - CI (99.9%): [1.076, 1.088] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemSer - -# Run progress: 80.95% complete, ETA 02:13:48 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.359 ops/ms -# Warmup Iteration 2: 2.022 ops/ms -# Warmup Iteration 3: 2.045 ops/ms -# Warmup Iteration 4: 2.047 ops/ms -# Warmup Iteration 5: 2.048 ops/ms -# Warmup Iteration 6: 2.034 ops/ms -# Warmup Iteration 7: 2.048 ops/ms -# Warmup Iteration 8: 2.050 ops/ms -# Warmup Iteration 9: 2.049 ops/ms -# Warmup Iteration 10: 2.049 ops/ms -Iteration 1: 2.049 ops/ms -Iteration 2: 2.048 ops/ms -Iteration 3: 2.049 ops/ms -Iteration 4: 2.047 ops/ms -Iteration 5: 2.047 ops/ms -Iteration 6: 2.048 ops/ms -Iteration 7: 2.038 ops/ms -Iteration 8: 2.039 ops/ms -Iteration 9: 2.038 ops/ms -Iteration 10: 2.039 ops/ms - -# Run progress: 81.90% complete, ETA 02:07:07 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.362 ops/ms -# Warmup Iteration 2: 1.974 ops/ms -# Warmup Iteration 3: 1.982 ops/ms -# Warmup Iteration 4: 1.979 ops/ms -# Warmup Iteration 5: 1.983 ops/ms -# Warmup Iteration 6: 1.976 ops/ms -# Warmup Iteration 7: 1.982 ops/ms -# Warmup Iteration 8: 1.978 ops/ms -# Warmup Iteration 9: 1.985 ops/ms -# Warmup Iteration 10: 1.984 ops/ms -Iteration 1: 1.984 ops/ms -Iteration 2: 1.984 ops/ms -Iteration 3: 1.984 ops/ms -Iteration 4: 1.985 ops/ms -Iteration 5: 1.986 ops/ms -Iteration 6: 1.983 ops/ms -Iteration 7: 2.003 ops/ms -Iteration 8: 2.004 ops/ms -Iteration 9: 2.003 ops/ms -Iteration 10: 2.003 ops/ms - -# Run progress: 82.86% complete, ETA 02:00:25 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.376 ops/ms -# Warmup Iteration 2: 2.014 ops/ms -# Warmup Iteration 3: 2.035 ops/ms -# Warmup Iteration 4: 2.033 ops/ms -# Warmup Iteration 5: 2.036 ops/ms -# Warmup Iteration 6: 2.034 ops/ms -# Warmup Iteration 7: 2.039 ops/ms -# Warmup Iteration 8: 2.039 ops/ms -# Warmup Iteration 9: 2.038 ops/ms -# Warmup Iteration 10: 2.038 ops/ms -Iteration 1: 2.039 ops/ms -Iteration 2: 2.035 ops/ms -Iteration 3: 2.039 ops/ms -Iteration 4: 2.039 ops/ms -Iteration 5: 2.039 ops/ms -Iteration 6: 2.039 ops/ms -Iteration 7: 2.035 ops/ms -Iteration 8: 2.035 ops/ms -Iteration 9: 2.035 ops/ms -Iteration 10: 2.035 ops/ms - -# Run progress: 83.81% complete, ETA 01:53:44 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.401 ops/ms -# Warmup Iteration 2: 1.948 ops/ms -# Warmup Iteration 3: 1.961 ops/ms -# Warmup Iteration 4: 1.958 ops/ms -# Warmup Iteration 5: 1.961 ops/ms -# Warmup Iteration 6: 1.949 ops/ms -# Warmup Iteration 7: 1.959 ops/ms -# Warmup Iteration 8: 1.960 ops/ms -# Warmup Iteration 9: 1.960 ops/ms -# Warmup Iteration 10: 1.961 ops/ms -Iteration 1: 1.960 ops/ms -Iteration 2: 1.960 ops/ms -Iteration 3: 1.959 ops/ms -Iteration 4: 1.961 ops/ms -Iteration 5: 1.960 ops/ms -Iteration 6: 1.960 ops/ms -Iteration 7: 1.955 ops/ms -Iteration 8: 1.954 ops/ms -Iteration 9: 1.954 ops/ms -Iteration 10: 1.954 ops/ms - -# Run progress: 84.76% complete, ETA 01:47:02 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.404 ops/ms -# Warmup Iteration 2: 2.037 ops/ms -# Warmup Iteration 3: 2.045 ops/ms -# Warmup Iteration 4: 2.049 ops/ms -# Warmup Iteration 5: 2.050 ops/ms -# Warmup Iteration 6: 2.053 ops/ms -# Warmup Iteration 7: 2.056 ops/ms -# Warmup Iteration 8: 2.058 ops/ms -# Warmup Iteration 9: 2.058 ops/ms -# Warmup Iteration 10: 2.058 ops/ms -Iteration 1: 2.058 ops/ms -Iteration 2: 2.058 ops/ms -Iteration 3: 2.058 ops/ms -Iteration 4: 2.059 ops/ms -Iteration 5: 2.058 ops/ms -Iteration 6: 2.058 ops/ms -Iteration 7: 2.053 ops/ms -Iteration 8: 2.054 ops/ms -Iteration 9: 2.053 ops/ms -Iteration 10: 2.054 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemSer": - 2.017 ±(99.9%) 0.019 ops/ms [Average] - (min, avg, max) = (1.954, 2.017, 2.059), stdev = 0.038 - CI (99.9%): [1.999, 2.036] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemDeser - -# Run progress: 85.71% complete, ETA 01:40:21 -# Fork: 1 of 5 -# Warmup Iteration 1: 194.252 ops/ms -# Warmup Iteration 2: 339.623 ops/ms -# Warmup Iteration 3: 341.565 ops/ms -# Warmup Iteration 4: 341.914 ops/ms -# Warmup Iteration 5: 341.110 ops/ms -# Warmup Iteration 6: 341.756 ops/ms -# Warmup Iteration 7: 341.894 ops/ms -# Warmup Iteration 8: 341.881 ops/ms -# Warmup Iteration 9: 341.850 ops/ms -# Warmup Iteration 10: 341.508 ops/ms -Iteration 1: 341.821 ops/ms -Iteration 2: 341.460 ops/ms -Iteration 3: 341.993 ops/ms -Iteration 4: 341.772 ops/ms -Iteration 5: 341.898 ops/ms -Iteration 6: 341.823 ops/ms -Iteration 7: 340.236 ops/ms -Iteration 8: 340.162 ops/ms -Iteration 9: 340.074 ops/ms -Iteration 10: 339.919 ops/ms - -# Run progress: 86.67% complete, ETA 01:33:39 -# Fork: 2 of 5 -# Warmup Iteration 1: 196.710 ops/ms -# Warmup Iteration 2: 324.260 ops/ms -# Warmup Iteration 3: 326.333 ops/ms -# Warmup Iteration 4: 326.736 ops/ms -# Warmup Iteration 5: 326.429 ops/ms -# Warmup Iteration 6: 326.540 ops/ms -# Warmup Iteration 7: 326.369 ops/ms -# Warmup Iteration 8: 326.260 ops/ms -# Warmup Iteration 9: 327.050 ops/ms -# Warmup Iteration 10: 326.552 ops/ms -Iteration 1: 326.366 ops/ms -Iteration 2: 326.611 ops/ms -Iteration 3: 326.563 ops/ms -Iteration 4: 326.580 ops/ms -Iteration 5: 326.353 ops/ms -Iteration 6: 326.367 ops/ms -Iteration 7: 329.875 ops/ms -Iteration 8: 329.460 ops/ms -Iteration 9: 329.515 ops/ms -Iteration 10: 330.032 ops/ms - -# Run progress: 87.62% complete, ETA 01:26:58 -# Fork: 3 of 5 -# Warmup Iteration 1: 208.365 ops/ms -# Warmup Iteration 2: 343.539 ops/ms -# Warmup Iteration 3: 346.540 ops/ms -# Warmup Iteration 4: 347.175 ops/ms -# Warmup Iteration 5: 347.156 ops/ms -# Warmup Iteration 6: 346.966 ops/ms -# Warmup Iteration 7: 346.747 ops/ms -# Warmup Iteration 8: 346.482 ops/ms -# Warmup Iteration 9: 346.561 ops/ms -# Warmup Iteration 10: 347.158 ops/ms -Iteration 1: 347.132 ops/ms -Iteration 2: 346.373 ops/ms -Iteration 3: 346.811 ops/ms -Iteration 4: 347.031 ops/ms -Iteration 5: 347.009 ops/ms -Iteration 6: 347.230 ops/ms -Iteration 7: 346.721 ops/ms -Iteration 8: 346.827 ops/ms -Iteration 9: 346.732 ops/ms -Iteration 10: 346.769 ops/ms - -# Run progress: 88.57% complete, ETA 01:20:17 -# Fork: 4 of 5 -# Warmup Iteration 1: 178.961 ops/ms -# Warmup Iteration 2: 320.222 ops/ms -# Warmup Iteration 3: 323.938 ops/ms -# Warmup Iteration 4: 323.920 ops/ms -# Warmup Iteration 5: 323.606 ops/ms -# Warmup Iteration 6: 324.084 ops/ms -# Warmup Iteration 7: 323.640 ops/ms -# Warmup Iteration 8: 323.563 ops/ms -# Warmup Iteration 9: 323.827 ops/ms -# Warmup Iteration 10: 323.461 ops/ms -Iteration 1: 323.801 ops/ms -Iteration 2: 323.579 ops/ms -Iteration 3: 323.628 ops/ms -Iteration 4: 323.750 ops/ms -Iteration 5: 323.807 ops/ms -Iteration 6: 323.110 ops/ms -Iteration 7: 322.514 ops/ms -Iteration 8: 322.010 ops/ms -Iteration 9: 322.266 ops/ms -Iteration 10: 322.123 ops/ms - -# Run progress: 89.52% complete, ETA 01:13:35 -# Fork: 5 of 5 -# Warmup Iteration 1: 174.766 ops/ms -# Warmup Iteration 2: 317.896 ops/ms -# Warmup Iteration 3: 325.847 ops/ms -# Warmup Iteration 4: 325.905 ops/ms -# Warmup Iteration 5: 326.325 ops/ms -# Warmup Iteration 6: 325.535 ops/ms -# Warmup Iteration 7: 326.018 ops/ms -# Warmup Iteration 8: 325.583 ops/ms -# Warmup Iteration 9: 325.625 ops/ms -# Warmup Iteration 10: 326.015 ops/ms -Iteration 1: 325.725 ops/ms -Iteration 2: 325.704 ops/ms -Iteration 3: 325.441 ops/ms -Iteration 4: 325.553 ops/ms -Iteration 5: 325.609 ops/ms -Iteration 6: 325.516 ops/ms -Iteration 7: 327.403 ops/ms -Iteration 8: 327.071 ops/ms -Iteration 9: 326.468 ops/ms -Iteration 10: 327.007 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemDeser": - 332.992 ±(99.9%) 4.667 ops/ms [Average] - (min, avg, max) = (322.010, 332.992, 347.230), stdev = 9.428 - CI (99.9%): [328.325, 337.659] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemSer - -# Run progress: 90.48% complete, ETA 01:06:54 -# Fork: 1 of 5 -# Warmup Iteration 1: 377.117 ops/ms -# Warmup Iteration 2: 602.697 ops/ms -# Warmup Iteration 3: 608.613 ops/ms -# Warmup Iteration 4: 608.291 ops/ms -# Warmup Iteration 5: 608.434 ops/ms -# Warmup Iteration 6: 608.059 ops/ms -# Warmup Iteration 7: 606.685 ops/ms -# Warmup Iteration 8: 547.789 ops/ms -# Warmup Iteration 9: 608.443 ops/ms -# Warmup Iteration 10: 608.273 ops/ms -Iteration 1: 608.259 ops/ms -Iteration 2: 608.075 ops/ms -Iteration 3: 608.447 ops/ms -Iteration 4: 608.454 ops/ms -Iteration 5: 607.956 ops/ms -Iteration 6: 608.272 ops/ms -Iteration 7: 616.531 ops/ms -Iteration 8: 616.309 ops/ms -Iteration 9: 614.612 ops/ms -Iteration 10: 615.975 ops/ms - -# Run progress: 91.43% complete, ETA 01:00:12 -# Fork: 2 of 5 -# Warmup Iteration 1: 385.510 ops/ms -# Warmup Iteration 2: 617.074 ops/ms -# Warmup Iteration 3: 619.024 ops/ms -# Warmup Iteration 4: 619.390 ops/ms -# Warmup Iteration 5: 619.330 ops/ms -# Warmup Iteration 6: 619.985 ops/ms -# Warmup Iteration 7: 619.377 ops/ms -# Warmup Iteration 8: 619.527 ops/ms -# Warmup Iteration 9: 619.200 ops/ms -# Warmup Iteration 10: 619.515 ops/ms -Iteration 1: 619.146 ops/ms -Iteration 2: 619.551 ops/ms -Iteration 3: 619.547 ops/ms -Iteration 4: 618.810 ops/ms -Iteration 5: 619.908 ops/ms -Iteration 6: 619.170 ops/ms -Iteration 7: 624.808 ops/ms -Iteration 8: 625.062 ops/ms -Iteration 9: 625.455 ops/ms -Iteration 10: 624.809 ops/ms - -# Run progress: 92.38% complete, ETA 00:53:31 -# Fork: 3 of 5 -# Warmup Iteration 1: 381.887 ops/ms -# Warmup Iteration 2: 605.476 ops/ms -# Warmup Iteration 3: 608.069 ops/ms -# Warmup Iteration 4: 607.789 ops/ms -# Warmup Iteration 5: 608.153 ops/ms -# Warmup Iteration 6: 607.799 ops/ms -# Warmup Iteration 7: 608.147 ops/ms -# Warmup Iteration 8: 607.683 ops/ms -# Warmup Iteration 9: 608.201 ops/ms -# Warmup Iteration 10: 607.741 ops/ms -Iteration 1: 608.236 ops/ms -Iteration 2: 608.008 ops/ms -Iteration 3: 608.467 ops/ms -Iteration 4: 607.727 ops/ms -Iteration 5: 608.357 ops/ms -Iteration 6: 608.483 ops/ms -Iteration 7: 603.768 ops/ms -Iteration 8: 603.438 ops/ms -Iteration 9: 603.408 ops/ms -Iteration 10: 603.482 ops/ms - -# Run progress: 93.33% complete, ETA 00:46:49 -# Fork: 4 of 5 -# Warmup Iteration 1: 401.221 ops/ms -# Warmup Iteration 2: 611.520 ops/ms -# Warmup Iteration 3: 614.008 ops/ms -# Warmup Iteration 4: 614.989 ops/ms -# Warmup Iteration 5: 613.836 ops/ms -# Warmup Iteration 6: 613.867 ops/ms -# Warmup Iteration 7: 614.326 ops/ms -# Warmup Iteration 8: 613.515 ops/ms -# Warmup Iteration 9: 613.809 ops/ms -# Warmup Iteration 10: 614.965 ops/ms -Iteration 1: 613.396 ops/ms -Iteration 2: 614.386 ops/ms -Iteration 3: 614.504 ops/ms -Iteration 4: 613.974 ops/ms -Iteration 5: 614.332 ops/ms -Iteration 6: 614.035 ops/ms -Iteration 7: 609.787 ops/ms -Iteration 8: 609.511 ops/ms -Iteration 9: 609.617 ops/ms -Iteration 10: 609.087 ops/ms - -# Run progress: 94.29% complete, ETA 00:40:08 -# Fork: 5 of 5 -# Warmup Iteration 1: 385.131 ops/ms -# Warmup Iteration 2: 600.456 ops/ms -# Warmup Iteration 3: 606.621 ops/ms -# Warmup Iteration 4: 606.344 ops/ms -# Warmup Iteration 5: 606.218 ops/ms -# Warmup Iteration 6: 606.414 ops/ms -# Warmup Iteration 7: 606.653 ops/ms -# Warmup Iteration 8: 606.817 ops/ms -# Warmup Iteration 9: 606.638 ops/ms -# Warmup Iteration 10: 606.544 ops/ms -Iteration 1: 607.273 ops/ms -Iteration 2: 607.348 ops/ms -Iteration 3: 606.763 ops/ms -Iteration 4: 607.447 ops/ms -Iteration 5: 607.361 ops/ms -Iteration 6: 607.134 ops/ms -Iteration 7: 602.459 ops/ms -Iteration 8: 603.052 ops/ms -Iteration 9: 602.958 ops/ms -Iteration 10: 603.021 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemSer": - 611.400 ±(99.9%) 3.187 ops/ms [Average] - (min, avg, max) = (602.459, 611.400, 625.455), stdev = 6.438 - CI (99.9%): [608.212, 614.587] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.constructorArrayDeser - -# Run progress: 95.24% complete, ETA 00:33:27 -# Fork: 1 of 5 -# Warmup Iteration 1: 0.661 ops/ms -# Warmup Iteration 2: 1.054 ops/ms -# Warmup Iteration 3: 1.077 ops/ms -# Warmup Iteration 4: 1.079 ops/ms -# Warmup Iteration 5: 1.078 ops/ms -# Warmup Iteration 6: 1.077 ops/ms -# Warmup Iteration 7: 1.079 ops/ms -# Warmup Iteration 8: 1.077 ops/ms -# Warmup Iteration 9: 1.079 ops/ms -# Warmup Iteration 10: 1.077 ops/ms -Iteration 1: 1.080 ops/ms -Iteration 2: 1.080 ops/ms -Iteration 3: 1.080 ops/ms -Iteration 4: 1.080 ops/ms -Iteration 5: 1.080 ops/ms -Iteration 6: 1.080 ops/ms -Iteration 7: 1.080 ops/ms -Iteration 8: 1.080 ops/ms -Iteration 9: 1.080 ops/ms -Iteration 10: 1.080 ops/ms - -# Run progress: 96.19% complete, ETA 00:26:45 -# Fork: 2 of 5 -# Warmup Iteration 1: 0.650 ops/ms -# Warmup Iteration 2: 1.048 ops/ms -# Warmup Iteration 3: 1.068 ops/ms -# Warmup Iteration 4: 1.067 ops/ms -# Warmup Iteration 5: 1.067 ops/ms -# Warmup Iteration 6: 1.067 ops/ms -# Warmup Iteration 7: 1.067 ops/ms -# Warmup Iteration 8: 1.067 ops/ms -# Warmup Iteration 9: 1.066 ops/ms -# Warmup Iteration 10: 1.065 ops/ms -Iteration 1: 1.067 ops/ms -Iteration 2: 1.067 ops/ms -Iteration 3: 1.067 ops/ms -Iteration 4: 1.067 ops/ms -Iteration 5: 1.067 ops/ms -Iteration 6: 1.067 ops/ms -Iteration 7: 1.066 ops/ms -Iteration 8: 1.067 ops/ms -Iteration 9: 1.066 ops/ms -Iteration 10: 1.067 ops/ms - -# Run progress: 97.14% complete, ETA 00:20:04 -# Fork: 3 of 5 -# Warmup Iteration 1: 0.613 ops/ms -# Warmup Iteration 2: 1.027 ops/ms -# Warmup Iteration 3: 1.056 ops/ms -# Warmup Iteration 4: 1.056 ops/ms -# Warmup Iteration 5: 1.056 ops/ms -# Warmup Iteration 6: 1.056 ops/ms -# Warmup Iteration 7: 1.057 ops/ms -# Warmup Iteration 8: 1.057 ops/ms -# Warmup Iteration 9: 1.056 ops/ms -# Warmup Iteration 10: 1.057 ops/ms -Iteration 1: 1.055 ops/ms -Iteration 2: 1.056 ops/ms -Iteration 3: 1.056 ops/ms -Iteration 4: 1.056 ops/ms -Iteration 5: 1.055 ops/ms -Iteration 6: 1.057 ops/ms -Iteration 7: 1.057 ops/ms -Iteration 8: 1.057 ops/ms -Iteration 9: 1.057 ops/ms -Iteration 10: 1.057 ops/ms - -# Run progress: 98.10% complete, ETA 00:13:22 -# Fork: 4 of 5 -# Warmup Iteration 1: 0.639 ops/ms -# Warmup Iteration 2: 1.022 ops/ms -# Warmup Iteration 3: 1.043 ops/ms -# Warmup Iteration 4: 1.042 ops/ms -# Warmup Iteration 5: 1.043 ops/ms -# Warmup Iteration 6: 1.042 ops/ms -# Warmup Iteration 7: 1.042 ops/ms -# Warmup Iteration 8: 1.042 ops/ms -# Warmup Iteration 9: 1.042 ops/ms -# Warmup Iteration 10: 1.042 ops/ms -Iteration 1: 1.041 ops/ms -Iteration 2: 1.043 ops/ms -Iteration 3: 1.043 ops/ms -Iteration 4: 1.043 ops/ms -Iteration 5: 1.043 ops/ms -Iteration 6: 1.042 ops/ms -Iteration 7: 1.041 ops/ms -Iteration 8: 1.041 ops/ms -Iteration 9: 1.041 ops/ms -Iteration 10: 1.042 ops/ms - -# Run progress: 99.05% complete, ETA 00:06:41 -# Fork: 5 of 5 -# Warmup Iteration 1: 0.672 ops/ms -# Warmup Iteration 2: 1.063 ops/ms -# Warmup Iteration 3: 1.072 ops/ms -# Warmup Iteration 4: 1.072 ops/ms -# Warmup Iteration 5: 1.071 ops/ms -# Warmup Iteration 6: 1.071 ops/ms -# Warmup Iteration 7: 1.072 ops/ms -# Warmup Iteration 8: 1.067 ops/ms -# Warmup Iteration 9: 1.071 ops/ms -# Warmup Iteration 10: 1.067 ops/ms -Iteration 1: 1.072 ops/ms -Iteration 2: 1.069 ops/ms -Iteration 3: 1.071 ops/ms -Iteration 4: 1.071 ops/ms -Iteration 5: 1.072 ops/ms -Iteration 6: 1.071 ops/ms -Iteration 7: 1.073 ops/ms -Iteration 8: 1.073 ops/ms -Iteration 9: 1.072 ops/ms -Iteration 10: 1.072 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.constructorArrayDeser": - 1.063 ±(99.9%) 0.007 ops/ms [Average] - (min, avg, max) = (1.041, 1.063, 1.080), stdev = 0.013 - CI (99.9%): [1.057, 1.070] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: -# Measurement: 1 iterations, single-shot each -# Timeout: 10 min per iteration -# Threads: 1 thread -# Benchmark mode: Single shot invocation time -# Benchmark: com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.afterburner - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 1 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -379.078 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 2 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -371.217 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 3 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -396.962 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 4 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -301.322 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 5 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -426.411 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 6 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -376.001 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 7 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -333.731 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 8 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -402.663 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 9 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -320.619 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 10 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -388.165 ms/op - - -Result "com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.afterburner": - N = 10 - mean = 369.617 ±(99.9%) 59.404 ms/op - - Histogram, ms/op: - [300.000, 312.500) = 1 - [312.500, 325.000) = 1 - [325.000, 337.500) = 1 - [337.500, 350.000) = 0 - [350.000, 362.500) = 0 - [362.500, 375.000) = 1 - [375.000, 387.500) = 2 - [387.500, 400.000) = 2 - [400.000, 412.500) = 1 - [412.500, 425.000) = 0 - [425.000, 437.500) = 1 - [437.500, 450.000) = 0 - [450.000, 462.500) = 0 - [462.500, 475.000) = 0 - [475.000, 487.500) = 0 - - Percentiles, ms/op: - p(0.0000) = 301.322 ms/op - p(50.0000) = 377.540 ms/op - p(90.0000) = 424.036 ms/op - p(95.0000) = 426.411 ms/op - p(99.0000) = 426.411 ms/op - p(99.9000) = 426.411 ms/op - p(99.9900) = 426.411 ms/op - p(99.9990) = 426.411 ms/op - p(99.9999) = 426.411 ms/op - p(100.0000) = 426.411 ms/op - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: -# Measurement: 1 iterations, single-shot each -# Timeout: 10 min per iteration -# Threads: 1 thread -# Benchmark mode: Single shot invocation time -# Benchmark: com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.blackbird - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 1 of 10 -Iteration 1: 332.060 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 2 of 10 -Iteration 1: 316.452 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 3 of 10 -Iteration 1: 422.261 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 4 of 10 -Iteration 1: 381.216 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 5 of 10 -Iteration 1: 302.745 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 6 of 10 -Iteration 1: 338.349 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 7 of 10 -Iteration 1: 339.388 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 8 of 10 -Iteration 1: 365.457 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 9 of 10 -Iteration 1: 375.537 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 10 of 10 -Iteration 1: 337.978 ms/op - - -Result "com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.blackbird": - N = 10 - mean = 351.144 ±(99.9%) 53.208 ms/op - - Histogram, ms/op: - [300.000, 312.500) = 1 - [312.500, 325.000) = 1 - [325.000, 337.500) = 1 - [337.500, 350.000) = 3 - [350.000, 362.500) = 0 - [362.500, 375.000) = 1 - [375.000, 387.500) = 2 - [387.500, 400.000) = 0 - [400.000, 412.500) = 0 - [412.500, 425.000) = 1 - [425.000, 437.500) = 0 - [437.500, 450.000) = 0 - [450.000, 462.500) = 0 - [462.500, 475.000) = 0 - [475.000, 487.500) = 0 - - Percentiles, ms/op: - p(0.0000) = 302.745 ms/op - p(50.0000) = 338.868 ms/op - p(90.0000) = 418.157 ms/op - p(95.0000) = 422.261 ms/op - p(99.0000) = 422.261 ms/op - p(99.9000) = 422.261 ms/op - p(99.9900) = 422.261 ms/op - p(99.9990) = 422.261 ms/op - p(99.9999) = 422.261 ms/op - p(100.0000) = 422.261 ms/op - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=/home/steven/mp --upgrade-module-path=/home/steven/mp/compiler.jar:/home/steven/mp/compiler-management.jar -# Warmup: -# Measurement: 1 iterations, single-shot each -# Timeout: 10 min per iteration -# Threads: 1 thread -# Benchmark mode: Single shot invocation time -# Benchmark: com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.vanilla - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 1 of 10 -Iteration 1: 291.318 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 2 of 10 -Iteration 1: 326.087 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 3 of 10 -Iteration 1: 297.577 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 4 of 10 -Iteration 1: 320.014 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 5 of 10 -Iteration 1: 305.533 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 6 of 10 -Iteration 1: 340.941 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 7 of 10 -Iteration 1: 294.189 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 8 of 10 -Iteration 1: 258.291 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 9 of 10 -Iteration 1: 274.227 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 10 of 10 -Iteration 1: 280.447 ms/op - - -Result "com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.vanilla": - N = 10 - mean = 298.862 ±(99.9%) 37.968 ms/op - - Histogram, ms/op: - [250.000, 260.000) = 1 - [260.000, 270.000) = 0 - [270.000, 280.000) = 1 - [280.000, 290.000) = 1 - [290.000, 300.000) = 3 - [300.000, 310.000) = 1 - [310.000, 320.000) = 0 - [320.000, 330.000) = 2 - [330.000, 340.000) = 0 - - Percentiles, ms/op: - p(0.0000) = 258.291 ms/op - p(50.0000) = 295.883 ms/op - p(90.0000) = 339.456 ms/op - p(95.0000) = 340.941 ms/op - p(99.0000) = 340.941 ms/op - p(99.9000) = 340.941 ms/op - p(99.9900) = 340.941 ms/op - p(99.9990) = 340.941 ms/op - p(99.9999) = 340.941 ms/op - p(100.0000) = 340.941 ms/op - - -# Run complete. Total time: 11:43:06 - -REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on -why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial -experiments, perform baseline and negative tests that provide experimental control, make sure -the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. -Do not assume the numbers tell you what you want them to tell. - -Benchmark Mode Cnt Score Error Units -Afterburner.beanArrayDeser thrpt 50 1.314 ± 0.015 ops/ms -Afterburner.beanArraySer thrpt 50 2.293 ± 0.018 ops/ms -Afterburner.classicBeanItemDeser thrpt 50 1.515 ± 0.014 ops/ms -Afterburner.classicBeanItemSer thrpt 50 2.485 ± 0.043 ops/ms -Afterburner.classicMediaItemDeser thrpt 50 429.227 ± 2.092 ops/ms -Afterburner.classicMediaItemSer thrpt 50 676.551 ± 5.980 ops/ms -Afterburner.constructorArrayDeser thrpt 50 1.087 ± 0.004 ops/ms -Blackbird.beanArrayDeser thrpt 50 1.333 ± 0.014 ops/ms -Blackbird.beanArraySer thrpt 50 2.256 ± 0.015 ops/ms -Blackbird.classicBeanItemDeser thrpt 50 1.480 ± 0.020 ops/ms -Blackbird.classicBeanItemSer thrpt 50 2.202 ± 0.053 ops/ms -Blackbird.classicMediaItemDeser thrpt 50 430.031 ± 1.192 ops/ms -Blackbird.classicMediaItemSer thrpt 50 659.393 ± 2.591 ops/ms -Blackbird.constructorArrayDeser thrpt 50 1.092 ± 0.003 ops/ms -Vanilla.beanArrayDeser thrpt 50 1.117 ± 0.009 ops/ms -Vanilla.beanArraySer thrpt 50 2.091 ± 0.013 ops/ms -Vanilla.classicBeanItemDeser thrpt 50 1.082 ± 0.006 ops/ms -Vanilla.classicBeanItemSer thrpt 50 2.017 ± 0.019 ops/ms -Vanilla.classicMediaItemDeser thrpt 50 332.992 ± 4.667 ops/ms -Vanilla.classicMediaItemSer thrpt 50 611.400 ± 3.187 ops/ms -Vanilla.constructorArrayDeser thrpt 50 1.063 ± 0.007 ops/ms -StartupTimeBenchmark.afterburner ss 10 369.617 ± 59.404 ms/op -StartupTimeBenchmark.blackbird ss 10 351.144 ± 53.208 ms/op -StartupTimeBenchmark.vanilla ss 10 298.862 ± 37.968 ms/op diff --git a/blackbird/benchmarks/results/results_hs.txt b/blackbird/benchmarks/results/results_hs.txt deleted file mode 100644 index a6f5ba743..000000000 --- a/blackbird/benchmarks/results/results_hs.txt +++ /dev/null @@ -1,3310 +0,0 @@ -WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by org.openjdk.jmh.util.Utils (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to field java.io.PrintStream.charOut -WARNING: Please consider reporting this to the maintainers of org.openjdk.jmh.util.Utils -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.beanArrayDeser - -# Run progress: 0.00% complete, ETA 11:40:00 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.216 ops/ms -# Warmup Iteration 2: 1.279 ops/ms -# Warmup Iteration 3: 1.278 ops/ms -# Warmup Iteration 4: 1.279 ops/ms -# Warmup Iteration 5: 1.276 ops/ms -# Warmup Iteration 6: 1.281 ops/ms -# Warmup Iteration 7: 1.281 ops/ms -# Warmup Iteration 8: 1.279 ops/ms -# Warmup Iteration 9: 1.280 ops/ms -# Warmup Iteration 10: 1.280 ops/ms -Iteration 1: 1.280 ops/ms -Iteration 2: 1.280 ops/ms -Iteration 3: 1.280 ops/ms -Iteration 4: 1.280 ops/ms -Iteration 5: 1.280 ops/ms -Iteration 6: 1.280 ops/ms -Iteration 7: 1.280 ops/ms -Iteration 8: 1.280 ops/ms -Iteration 9: 1.280 ops/ms -Iteration 10: 1.280 ops/ms - -# Run progress: 0.95% complete, ETA 11:35:12 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.271 ops/ms -# Warmup Iteration 2: 1.323 ops/ms -# Warmup Iteration 3: 1.323 ops/ms -# Warmup Iteration 4: 1.323 ops/ms -# Warmup Iteration 5: 1.322 ops/ms -# Warmup Iteration 6: 1.324 ops/ms -# Warmup Iteration 7: 1.324 ops/ms -# Warmup Iteration 8: 1.323 ops/ms -# Warmup Iteration 9: 1.324 ops/ms -# Warmup Iteration 10: 1.323 ops/ms -Iteration 1: 1.324 ops/ms -Iteration 2: 1.324 ops/ms -Iteration 3: 1.324 ops/ms -Iteration 4: 1.324 ops/ms -Iteration 5: 1.324 ops/ms -Iteration 6: 1.323 ops/ms -Iteration 7: 1.324 ops/ms -Iteration 8: 1.324 ops/ms -Iteration 9: 1.324 ops/ms -Iteration 10: 1.324 ops/ms - -# Run progress: 1.90% complete, ETA 11:30:05 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.243 ops/ms -# Warmup Iteration 2: 1.309 ops/ms -# Warmup Iteration 3: 1.309 ops/ms -# Warmup Iteration 4: 1.309 ops/ms -# Warmup Iteration 5: 1.307 ops/ms -# Warmup Iteration 6: 1.310 ops/ms -# Warmup Iteration 7: 1.310 ops/ms -# Warmup Iteration 8: 1.309 ops/ms -# Warmup Iteration 9: 1.311 ops/ms -# Warmup Iteration 10: 1.310 ops/ms -Iteration 1: 1.309 ops/ms -Iteration 2: 1.310 ops/ms -Iteration 3: 1.310 ops/ms -Iteration 4: 1.310 ops/ms -Iteration 5: 1.310 ops/ms -Iteration 6: 1.309 ops/ms -Iteration 7: 1.310 ops/ms -Iteration 8: 1.310 ops/ms -Iteration 9: 1.310 ops/ms -Iteration 10: 1.310 ops/ms - -# Run progress: 2.86% complete, ETA 11:22:53 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.226 ops/ms -# Warmup Iteration 2: 1.278 ops/ms -# Warmup Iteration 3: 1.280 ops/ms -# Warmup Iteration 4: 1.279 ops/ms -# Warmup Iteration 5: 1.279 ops/ms -# Warmup Iteration 6: 1.280 ops/ms -# Warmup Iteration 7: 1.280 ops/ms -# Warmup Iteration 8: 1.280 ops/ms -# Warmup Iteration 9: 1.277 ops/ms -# Warmup Iteration 10: 1.279 ops/ms -Iteration 1: 1.280 ops/ms -Iteration 2: 1.279 ops/ms -Iteration 3: 1.279 ops/ms -Iteration 4: 1.279 ops/ms -Iteration 5: 1.279 ops/ms -Iteration 6: 1.280 ops/ms -Iteration 7: 1.280 ops/ms -Iteration 8: 1.280 ops/ms -Iteration 9: 1.279 ops/ms -Iteration 10: 1.280 ops/ms - -# Run progress: 3.81% complete, ETA 11:16:13 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.251 ops/ms -# Warmup Iteration 2: 1.301 ops/ms -# Warmup Iteration 3: 1.302 ops/ms -# Warmup Iteration 4: 1.301 ops/ms -# Warmup Iteration 5: 1.300 ops/ms -# Warmup Iteration 6: 1.301 ops/ms -# Warmup Iteration 7: 1.301 ops/ms -# Warmup Iteration 8: 1.300 ops/ms -# Warmup Iteration 9: 1.302 ops/ms -# Warmup Iteration 10: 1.302 ops/ms -Iteration 1: 1.302 ops/ms -Iteration 2: 1.302 ops/ms -Iteration 3: 1.302 ops/ms -Iteration 4: 1.302 ops/ms -Iteration 5: 1.302 ops/ms -Iteration 6: 1.302 ops/ms -Iteration 7: 1.300 ops/ms -Iteration 8: 1.302 ops/ms -Iteration 9: 1.301 ops/ms -Iteration 10: 1.302 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.beanArrayDeser": - 1.299 ±(99.9%) 0.009 ops/ms [Average] - (min, avg, max) = (1.279, 1.299, 1.324), stdev = 0.017 - CI (99.9%): [1.290, 1.308] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.beanArraySer - -# Run progress: 4.76% complete, ETA 11:09:18 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.089 ops/ms -# Warmup Iteration 2: 2.158 ops/ms -# Warmup Iteration 3: 2.161 ops/ms -# Warmup Iteration 4: 2.160 ops/ms -# Warmup Iteration 5: 2.158 ops/ms -# Warmup Iteration 6: 2.160 ops/ms -# Warmup Iteration 7: 2.159 ops/ms -# Warmup Iteration 8: 2.160 ops/ms -# Warmup Iteration 9: 2.162 ops/ms -# Warmup Iteration 10: 2.160 ops/ms -Iteration 1: 2.160 ops/ms -Iteration 2: 2.160 ops/ms -Iteration 3: 2.160 ops/ms -Iteration 4: 2.159 ops/ms -Iteration 5: 2.161 ops/ms -Iteration 6: 2.161 ops/ms -Iteration 7: 2.160 ops/ms -Iteration 8: 2.162 ops/ms -Iteration 9: 2.161 ops/ms -Iteration 10: 2.162 ops/ms - -# Run progress: 5.71% complete, ETA 11:02:43 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.147 ops/ms -# Warmup Iteration 2: 2.211 ops/ms -# Warmup Iteration 3: 2.213 ops/ms -# Warmup Iteration 4: 2.213 ops/ms -# Warmup Iteration 5: 2.213 ops/ms -# Warmup Iteration 6: 2.212 ops/ms -# Warmup Iteration 7: 2.213 ops/ms -# Warmup Iteration 8: 2.214 ops/ms -# Warmup Iteration 9: 2.212 ops/ms -# Warmup Iteration 10: 2.213 ops/ms -Iteration 1: 2.214 ops/ms -Iteration 2: 2.213 ops/ms -Iteration 3: 2.213 ops/ms -Iteration 4: 2.213 ops/ms -Iteration 5: 2.213 ops/ms -Iteration 6: 2.214 ops/ms -Iteration 7: 2.214 ops/ms -Iteration 8: 2.213 ops/ms -Iteration 9: 2.214 ops/ms -Iteration 10: 2.213 ops/ms - -# Run progress: 6.67% complete, ETA 10:55:56 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.147 ops/ms -# Warmup Iteration 2: 2.207 ops/ms -# Warmup Iteration 3: 2.207 ops/ms -# Warmup Iteration 4: 2.208 ops/ms -# Warmup Iteration 5: 2.207 ops/ms -# Warmup Iteration 6: 2.208 ops/ms -# Warmup Iteration 7: 2.207 ops/ms -# Warmup Iteration 8: 2.206 ops/ms -# Warmup Iteration 9: 2.208 ops/ms -# Warmup Iteration 10: 2.209 ops/ms -Iteration 1: 2.208 ops/ms -Iteration 2: 2.208 ops/ms -Iteration 3: 2.208 ops/ms -Iteration 4: 2.208 ops/ms -Iteration 5: 2.208 ops/ms -Iteration 6: 2.207 ops/ms -Iteration 7: 2.208 ops/ms -Iteration 8: 2.208 ops/ms -Iteration 9: 2.208 ops/ms -Iteration 10: 2.207 ops/ms - -# Run progress: 7.62% complete, ETA 10:49:11 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.111 ops/ms -# Warmup Iteration 2: 2.178 ops/ms -# Warmup Iteration 3: 2.177 ops/ms -# Warmup Iteration 4: 2.177 ops/ms -# Warmup Iteration 5: 2.177 ops/ms -# Warmup Iteration 6: 2.180 ops/ms -# Warmup Iteration 7: 2.177 ops/ms -# Warmup Iteration 8: 2.177 ops/ms -# Warmup Iteration 9: 2.178 ops/ms -# Warmup Iteration 10: 2.179 ops/ms -Iteration 1: 2.178 ops/ms -Iteration 2: 2.178 ops/ms -Iteration 3: 2.178 ops/ms -Iteration 4: 2.178 ops/ms -Iteration 5: 2.178 ops/ms -Iteration 6: 2.178 ops/ms -Iteration 7: 2.179 ops/ms -Iteration 8: 2.178 ops/ms -Iteration 9: 2.179 ops/ms -Iteration 10: 2.178 ops/ms - -# Run progress: 8.57% complete, ETA 10:42:24 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.145 ops/ms -# Warmup Iteration 2: 2.201 ops/ms -# Warmup Iteration 3: 2.204 ops/ms -# Warmup Iteration 4: 2.205 ops/ms -# Warmup Iteration 5: 2.204 ops/ms -# Warmup Iteration 6: 2.204 ops/ms -# Warmup Iteration 7: 2.205 ops/ms -# Warmup Iteration 8: 2.205 ops/ms -# Warmup Iteration 9: 2.204 ops/ms -# Warmup Iteration 10: 2.205 ops/ms -Iteration 1: 2.205 ops/ms -Iteration 2: 2.205 ops/ms -Iteration 3: 2.205 ops/ms -Iteration 4: 2.205 ops/ms -Iteration 5: 2.205 ops/ms -Iteration 6: 2.205 ops/ms -Iteration 7: 2.202 ops/ms -Iteration 8: 2.202 ops/ms -Iteration 9: 2.202 ops/ms -Iteration 10: 2.203 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.beanArraySer": - 2.193 ±(99.9%) 0.010 ops/ms [Average] - (min, avg, max) = (2.159, 2.193, 2.214), stdev = 0.020 - CI (99.9%): [2.183, 2.203] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemDeser - -# Run progress: 9.52% complete, ETA 10:35:36 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.447 ops/ms -# Warmup Iteration 2: 1.485 ops/ms -# Warmup Iteration 3: 1.485 ops/ms -# Warmup Iteration 4: 1.486 ops/ms -# Warmup Iteration 5: 1.483 ops/ms -# Warmup Iteration 6: 1.489 ops/ms -# Warmup Iteration 7: 1.487 ops/ms -# Warmup Iteration 8: 1.486 ops/ms -# Warmup Iteration 9: 1.487 ops/ms -# Warmup Iteration 10: 1.485 ops/ms -Iteration 1: 1.486 ops/ms -Iteration 2: 1.486 ops/ms -Iteration 3: 1.486 ops/ms -Iteration 4: 1.487 ops/ms -Iteration 5: 1.486 ops/ms -Iteration 6: 1.487 ops/ms -Iteration 7: 1.488 ops/ms -Iteration 8: 1.488 ops/ms -Iteration 9: 1.487 ops/ms -Iteration 10: 1.488 ops/ms - -# Run progress: 10.48% complete, ETA 10:28:51 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.338 ops/ms -# Warmup Iteration 2: 1.382 ops/ms -# Warmup Iteration 3: 1.382 ops/ms -# Warmup Iteration 4: 1.383 ops/ms -# Warmup Iteration 5: 1.381 ops/ms -# Warmup Iteration 6: 1.382 ops/ms -# Warmup Iteration 7: 1.383 ops/ms -# Warmup Iteration 8: 1.383 ops/ms -# Warmup Iteration 9: 1.384 ops/ms -# Warmup Iteration 10: 1.384 ops/ms -Iteration 1: 1.383 ops/ms -Iteration 2: 1.383 ops/ms -Iteration 3: 1.383 ops/ms -Iteration 4: 1.384 ops/ms -Iteration 5: 1.384 ops/ms -Iteration 6: 1.383 ops/ms -Iteration 7: 1.384 ops/ms -Iteration 8: 1.384 ops/ms -Iteration 9: 1.384 ops/ms -Iteration 10: 1.384 ops/ms - -# Run progress: 11.43% complete, ETA 10:22:08 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.347 ops/ms -# Warmup Iteration 2: 1.399 ops/ms -# Warmup Iteration 3: 1.399 ops/ms -# Warmup Iteration 4: 1.401 ops/ms -# Warmup Iteration 5: 1.397 ops/ms -# Warmup Iteration 6: 1.399 ops/ms -# Warmup Iteration 7: 1.399 ops/ms -# Warmup Iteration 8: 1.396 ops/ms -# Warmup Iteration 9: 1.401 ops/ms -# Warmup Iteration 10: 1.401 ops/ms -Iteration 1: 1.401 ops/ms -Iteration 2: 1.401 ops/ms -Iteration 3: 1.402 ops/ms -Iteration 4: 1.402 ops/ms -Iteration 5: 1.402 ops/ms -Iteration 6: 1.401 ops/ms -Iteration 7: 1.399 ops/ms -Iteration 8: 1.399 ops/ms -Iteration 9: 1.399 ops/ms -Iteration 10: 1.400 ops/ms - -# Run progress: 12.38% complete, ETA 10:15:24 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.296 ops/ms -# Warmup Iteration 2: 1.344 ops/ms -# Warmup Iteration 3: 1.344 ops/ms -# Warmup Iteration 4: 1.345 ops/ms -# Warmup Iteration 5: 1.342 ops/ms -# Warmup Iteration 6: 1.346 ops/ms -# Warmup Iteration 7: 1.345 ops/ms -# Warmup Iteration 8: 1.344 ops/ms -# Warmup Iteration 9: 1.346 ops/ms -# Warmup Iteration 10: 1.346 ops/ms -Iteration 1: 1.346 ops/ms -Iteration 2: 1.346 ops/ms -Iteration 3: 1.346 ops/ms -Iteration 4: 1.346 ops/ms -Iteration 5: 1.347 ops/ms -Iteration 6: 1.346 ops/ms -Iteration 7: 1.321 ops/ms -Iteration 8: 1.345 ops/ms -Iteration 9: 1.345 ops/ms -Iteration 10: 1.346 ops/ms - -# Run progress: 13.33% complete, ETA 10:08:41 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.334 ops/ms -# Warmup Iteration 2: 1.371 ops/ms -# Warmup Iteration 3: 1.370 ops/ms -# Warmup Iteration 4: 1.372 ops/ms -# Warmup Iteration 5: 1.371 ops/ms -# Warmup Iteration 6: 1.373 ops/ms -# Warmup Iteration 7: 1.373 ops/ms -# Warmup Iteration 8: 1.372 ops/ms -# Warmup Iteration 9: 1.373 ops/ms -# Warmup Iteration 10: 1.372 ops/ms -Iteration 1: 1.373 ops/ms -Iteration 2: 1.373 ops/ms -Iteration 3: 1.373 ops/ms -Iteration 4: 1.373 ops/ms -Iteration 5: 1.373 ops/ms -Iteration 6: 1.373 ops/ms -Iteration 7: 1.373 ops/ms -Iteration 8: 1.373 ops/ms -Iteration 9: 1.373 ops/ms -Iteration 10: 1.373 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemDeser": - 1.398 ±(99.9%) 0.024 ops/ms [Average] - (min, avg, max) = (1.321, 1.398, 1.488), stdev = 0.049 - CI (99.9%): [1.373, 1.422] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemSer - -# Run progress: 14.29% complete, ETA 10:01:57 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.313 ops/ms -# Warmup Iteration 2: 2.351 ops/ms -# Warmup Iteration 3: 2.343 ops/ms -# Warmup Iteration 4: 2.352 ops/ms -# Warmup Iteration 5: 2.348 ops/ms -# Warmup Iteration 6: 2.350 ops/ms -# Warmup Iteration 7: 2.351 ops/ms -# Warmup Iteration 8: 2.353 ops/ms -# Warmup Iteration 9: 2.353 ops/ms -# Warmup Iteration 10: 2.352 ops/ms -Iteration 1: 2.351 ops/ms -Iteration 2: 2.351 ops/ms -Iteration 3: 2.352 ops/ms -Iteration 4: 2.353 ops/ms -Iteration 5: 2.334 ops/ms -Iteration 6: 2.352 ops/ms -Iteration 7: 2.335 ops/ms -Iteration 8: 2.354 ops/ms -Iteration 9: 2.348 ops/ms -Iteration 10: 2.351 ops/ms - -# Run progress: 15.24% complete, ETA 09:55:13 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.345 ops/ms -# Warmup Iteration 2: 2.390 ops/ms -# Warmup Iteration 3: 2.387 ops/ms -# Warmup Iteration 4: 2.387 ops/ms -# Warmup Iteration 5: 2.387 ops/ms -# Warmup Iteration 6: 2.386 ops/ms -# Warmup Iteration 7: 2.387 ops/ms -# Warmup Iteration 8: 2.387 ops/ms -# Warmup Iteration 9: 2.388 ops/ms -# Warmup Iteration 10: 2.387 ops/ms -Iteration 1: 2.385 ops/ms -Iteration 2: 2.390 ops/ms -Iteration 3: 2.386 ops/ms -Iteration 4: 2.388 ops/ms -Iteration 5: 2.385 ops/ms -Iteration 6: 2.387 ops/ms -Iteration 7: 2.386 ops/ms -Iteration 8: 2.387 ops/ms -Iteration 9: 2.380 ops/ms -Iteration 10: 2.383 ops/ms - -# Run progress: 16.19% complete, ETA 09:48:31 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.224 ops/ms -# Warmup Iteration 2: 2.232 ops/ms -# Warmup Iteration 3: 2.229 ops/ms -# Warmup Iteration 4: 2.250 ops/ms -# Warmup Iteration 5: 2.259 ops/ms -# Warmup Iteration 6: 2.252 ops/ms -# Warmup Iteration 7: 2.253 ops/ms -# Warmup Iteration 8: 2.264 ops/ms -# Warmup Iteration 9: 2.250 ops/ms -# Warmup Iteration 10: 2.255 ops/ms -Iteration 1: 2.252 ops/ms -Iteration 2: 2.246 ops/ms -Iteration 3: 2.244 ops/ms -Iteration 4: 2.272 ops/ms -Iteration 5: 2.277 ops/ms -Iteration 6: 2.265 ops/ms -Iteration 7: 2.272 ops/ms -Iteration 8: 2.258 ops/ms -Iteration 9: 2.249 ops/ms -Iteration 10: 2.268 ops/ms - -# Run progress: 17.14% complete, ETA 09:41:49 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.256 ops/ms -# Warmup Iteration 2: 2.273 ops/ms -# Warmup Iteration 3: 2.267 ops/ms -# Warmup Iteration 4: 2.269 ops/ms -# Warmup Iteration 5: 2.269 ops/ms -# Warmup Iteration 6: 2.274 ops/ms -# Warmup Iteration 7: 2.273 ops/ms -# Warmup Iteration 8: 2.272 ops/ms -# Warmup Iteration 9: 2.273 ops/ms -# Warmup Iteration 10: 2.273 ops/ms -Iteration 1: 2.272 ops/ms -Iteration 2: 2.273 ops/ms -Iteration 3: 2.273 ops/ms -Iteration 4: 2.283 ops/ms -Iteration 5: 2.282 ops/ms -Iteration 6: 2.283 ops/ms -Iteration 7: 2.283 ops/ms -Iteration 8: 2.279 ops/ms -Iteration 9: 2.282 ops/ms -Iteration 10: 2.283 ops/ms - -# Run progress: 18.10% complete, ETA 09:35:06 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -2.313 ops/ms -# Warmup Iteration 2: 2.355 ops/ms -# Warmup Iteration 3: 2.353 ops/ms -# Warmup Iteration 4: 2.354 ops/ms -# Warmup Iteration 5: 2.352 ops/ms -# Warmup Iteration 6: 2.355 ops/ms -# Warmup Iteration 7: 2.349 ops/ms -# Warmup Iteration 8: 2.354 ops/ms -# Warmup Iteration 9: 2.355 ops/ms -# Warmup Iteration 10: 2.353 ops/ms -Iteration 1: 2.351 ops/ms -Iteration 2: 2.354 ops/ms -Iteration 3: 2.352 ops/ms -Iteration 4: 2.354 ops/ms -Iteration 5: 2.352 ops/ms -Iteration 6: 2.352 ops/ms -Iteration 7: 2.349 ops/ms -Iteration 8: 2.349 ops/ms -Iteration 9: 2.355 ops/ms -Iteration 10: 2.361 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicBeanItemSer": - 2.325 ±(99.9%) 0.024 ops/ms [Average] - (min, avg, max) = (2.244, 2.325, 2.390), stdev = 0.048 - CI (99.9%): [2.301, 2.349] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemDeser - -# Run progress: 19.05% complete, ETA 09:28:25 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -391.916 ops/ms -# Warmup Iteration 2: 403.219 ops/ms -# Warmup Iteration 3: 403.618 ops/ms -# Warmup Iteration 4: 403.723 ops/ms -# Warmup Iteration 5: 403.727 ops/ms -# Warmup Iteration 6: 403.652 ops/ms -# Warmup Iteration 7: 403.653 ops/ms -# Warmup Iteration 8: 403.807 ops/ms -# Warmup Iteration 9: 403.426 ops/ms -# Warmup Iteration 10: 403.559 ops/ms -Iteration 1: 403.407 ops/ms -Iteration 2: 403.520 ops/ms -Iteration 3: 403.564 ops/ms -Iteration 4: 403.689 ops/ms -Iteration 5: 403.551 ops/ms -Iteration 6: 403.599 ops/ms -Iteration 7: 403.084 ops/ms -Iteration 8: 403.222 ops/ms -Iteration 9: 403.335 ops/ms -Iteration 10: 403.357 ops/ms - -# Run progress: 20.00% complete, ETA 09:21:42 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -385.141 ops/ms -# Warmup Iteration 2: 398.113 ops/ms -# Warmup Iteration 3: 399.119 ops/ms -# Warmup Iteration 4: 399.236 ops/ms -# Warmup Iteration 5: 399.002 ops/ms -# Warmup Iteration 6: 399.276 ops/ms -# Warmup Iteration 7: 399.049 ops/ms -# Warmup Iteration 8: 399.021 ops/ms -# Warmup Iteration 9: 399.012 ops/ms -# Warmup Iteration 10: 399.163 ops/ms -Iteration 1: 399.102 ops/ms -Iteration 2: 399.144 ops/ms -Iteration 3: 398.877 ops/ms -Iteration 4: 399.131 ops/ms -Iteration 5: 399.164 ops/ms -Iteration 6: 399.191 ops/ms -Iteration 7: 399.786 ops/ms -Iteration 8: 399.392 ops/ms -Iteration 9: 399.452 ops/ms -Iteration 10: 399.575 ops/ms - -# Run progress: 20.95% complete, ETA 09:15:00 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -396.069 ops/ms -# Warmup Iteration 2: 406.923 ops/ms -# Warmup Iteration 3: 407.835 ops/ms -# Warmup Iteration 4: 408.035 ops/ms -# Warmup Iteration 5: 408.109 ops/ms -# Warmup Iteration 6: 407.750 ops/ms -# Warmup Iteration 7: 407.912 ops/ms -# Warmup Iteration 8: 407.952 ops/ms -# Warmup Iteration 9: 407.945 ops/ms -# Warmup Iteration 10: 407.820 ops/ms -Iteration 1: 407.961 ops/ms -Iteration 2: 407.933 ops/ms -Iteration 3: 408.117 ops/ms -Iteration 4: 407.922 ops/ms -Iteration 5: 407.751 ops/ms -Iteration 6: 407.876 ops/ms -Iteration 7: 405.013 ops/ms -Iteration 8: 407.765 ops/ms -Iteration 9: 407.917 ops/ms -Iteration 10: 407.651 ops/ms - -# Run progress: 21.90% complete, ETA 09:08:17 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -388.940 ops/ms -# Warmup Iteration 2: 402.232 ops/ms -# Warmup Iteration 3: 403.629 ops/ms -# Warmup Iteration 4: 403.344 ops/ms -# Warmup Iteration 5: 403.886 ops/ms -# Warmup Iteration 6: 403.534 ops/ms -# Warmup Iteration 7: 403.627 ops/ms -# Warmup Iteration 8: 403.403 ops/ms -# Warmup Iteration 9: 403.459 ops/ms -# Warmup Iteration 10: 403.500 ops/ms -Iteration 1: 403.632 ops/ms -Iteration 2: 403.538 ops/ms -Iteration 3: 403.709 ops/ms -Iteration 4: 403.727 ops/ms -Iteration 5: 403.718 ops/ms -Iteration 6: 403.520 ops/ms -Iteration 7: 403.322 ops/ms -Iteration 8: 403.356 ops/ms -Iteration 9: 403.488 ops/ms -Iteration 10: 403.180 ops/ms - -# Run progress: 22.86% complete, ETA 09:01:36 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -389.372 ops/ms -# Warmup Iteration 2: 403.056 ops/ms -# Warmup Iteration 3: 402.717 ops/ms -# Warmup Iteration 4: 403.029 ops/ms -# Warmup Iteration 5: 402.653 ops/ms -# Warmup Iteration 6: 402.835 ops/ms -# Warmup Iteration 7: 402.755 ops/ms -# Warmup Iteration 8: 402.737 ops/ms -# Warmup Iteration 9: 402.862 ops/ms -# Warmup Iteration 10: 402.963 ops/ms -Iteration 1: 402.999 ops/ms -Iteration 2: 402.866 ops/ms -Iteration 3: 402.829 ops/ms -Iteration 4: 403.041 ops/ms -Iteration 5: 402.884 ops/ms -Iteration 6: 403.007 ops/ms -Iteration 7: 402.754 ops/ms -Iteration 8: 402.746 ops/ms -Iteration 9: 402.629 ops/ms -Iteration 10: 402.754 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemDeser": - 403.335 ±(99.9%) 1.337 ops/ms [Average] - (min, avg, max) = (398.877, 403.335, 408.117), stdev = 2.700 - CI (99.9%): [401.998, 404.671] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemSer - -# Run progress: 23.81% complete, ETA 08:54:54 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -589.486 ops/ms -# Warmup Iteration 2: 608.238 ops/ms -# Warmup Iteration 3: 609.837 ops/ms -# Warmup Iteration 4: 609.769 ops/ms -# Warmup Iteration 5: 609.624 ops/ms -# Warmup Iteration 6: 609.581 ops/ms -# Warmup Iteration 7: 609.524 ops/ms -# Warmup Iteration 8: 609.515 ops/ms -# Warmup Iteration 9: 609.170 ops/ms -# Warmup Iteration 10: 609.529 ops/ms -Iteration 1: 609.605 ops/ms -Iteration 2: 609.343 ops/ms -Iteration 3: 609.546 ops/ms -Iteration 4: 609.587 ops/ms -Iteration 5: 609.209 ops/ms -Iteration 6: 609.499 ops/ms -Iteration 7: 609.274 ops/ms -Iteration 8: 609.436 ops/ms -Iteration 9: 609.211 ops/ms -Iteration 10: 609.458 ops/ms - -# Run progress: 24.76% complete, ETA 08:48:11 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -596.972 ops/ms -# Warmup Iteration 2: 615.479 ops/ms -# Warmup Iteration 3: 615.213 ops/ms -# Warmup Iteration 4: 615.278 ops/ms -# Warmup Iteration 5: 615.426 ops/ms -# Warmup Iteration 6: 615.380 ops/ms -# Warmup Iteration 7: 615.478 ops/ms -# Warmup Iteration 8: 614.939 ops/ms -# Warmup Iteration 9: 615.347 ops/ms -# Warmup Iteration 10: 615.585 ops/ms -Iteration 1: 615.279 ops/ms -Iteration 2: 615.486 ops/ms -Iteration 3: 615.452 ops/ms -Iteration 4: 615.248 ops/ms -Iteration 5: 615.270 ops/ms -Iteration 6: 615.228 ops/ms -Iteration 7: 616.897 ops/ms -Iteration 8: 616.630 ops/ms -Iteration 9: 616.727 ops/ms -Iteration 10: 616.847 ops/ms - -# Run progress: 25.71% complete, ETA 08:41:32 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -598.024 ops/ms -# Warmup Iteration 2: 614.556 ops/ms -# Warmup Iteration 3: 614.368 ops/ms -# Warmup Iteration 4: 614.309 ops/ms -# Warmup Iteration 5: 614.719 ops/ms -# Warmup Iteration 6: 614.442 ops/ms -# Warmup Iteration 7: 614.036 ops/ms -# Warmup Iteration 8: 614.632 ops/ms -# Warmup Iteration 9: 614.424 ops/ms -# Warmup Iteration 10: 614.347 ops/ms -Iteration 1: 614.660 ops/ms -Iteration 2: 614.505 ops/ms -Iteration 3: 614.757 ops/ms -Iteration 4: 614.498 ops/ms -Iteration 5: 614.670 ops/ms -Iteration 6: 612.457 ops/ms -Iteration 7: 613.982 ops/ms -Iteration 8: 613.638 ops/ms -Iteration 9: 613.956 ops/ms -Iteration 10: 613.418 ops/ms - -# Run progress: 26.67% complete, ETA 08:34:50 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -590.944 ops/ms -# Warmup Iteration 2: 605.799 ops/ms -# Warmup Iteration 3: 604.719 ops/ms -# Warmup Iteration 4: 605.611 ops/ms -# Warmup Iteration 5: 605.521 ops/ms -# Warmup Iteration 6: 604.296 ops/ms -# Warmup Iteration 7: 604.492 ops/ms -# Warmup Iteration 8: 605.078 ops/ms -# Warmup Iteration 9: 604.788 ops/ms -# Warmup Iteration 10: 605.290 ops/ms -Iteration 1: 605.538 ops/ms -Iteration 2: 605.584 ops/ms -Iteration 3: 605.351 ops/ms -Iteration 4: 605.203 ops/ms -Iteration 5: 605.122 ops/ms -Iteration 6: 605.186 ops/ms -Iteration 7: 604.640 ops/ms -Iteration 8: 605.778 ops/ms -Iteration 9: 605.425 ops/ms -Iteration 10: 605.636 ops/ms - -# Run progress: 27.62% complete, ETA 08:28:08 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -589.857 ops/ms -# Warmup Iteration 2: 606.156 ops/ms -# Warmup Iteration 3: 607.449 ops/ms -# Warmup Iteration 4: 607.734 ops/ms -# Warmup Iteration 5: 607.297 ops/ms -# Warmup Iteration 6: 607.587 ops/ms -# Warmup Iteration 7: 607.857 ops/ms -# Warmup Iteration 8: 607.682 ops/ms -# Warmup Iteration 9: 607.814 ops/ms -# Warmup Iteration 10: 607.810 ops/ms -Iteration 1: 607.303 ops/ms -Iteration 2: 607.555 ops/ms -Iteration 3: 607.649 ops/ms -Iteration 4: 607.563 ops/ms -Iteration 5: 607.461 ops/ms -Iteration 6: 607.451 ops/ms -Iteration 7: 607.548 ops/ms -Iteration 8: 607.250 ops/ms -Iteration 9: 607.533 ops/ms -Iteration 10: 607.774 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.classicMediaItemSer": - 610.446 ±(99.9%) 1.996 ops/ms [Average] - (min, avg, max) = (604.640, 610.446, 616.897), stdev = 4.032 - CI (99.9%): [608.450, 612.442] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Afterburner.constructorArrayDeser - -# Run progress: 28.57% complete, ETA 08:21:27 -# Fork: 1 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.057 ops/ms -# Warmup Iteration 2: 1.121 ops/ms -# Warmup Iteration 3: 1.122 ops/ms -# Warmup Iteration 4: 1.121 ops/ms -# Warmup Iteration 5: 1.121 ops/ms -# Warmup Iteration 6: 1.119 ops/ms -# Warmup Iteration 7: 1.121 ops/ms -# Warmup Iteration 8: 1.121 ops/ms -# Warmup Iteration 9: 1.121 ops/ms -# Warmup Iteration 10: 1.118 ops/ms -Iteration 1: 1.121 ops/ms -Iteration 2: 1.121 ops/ms -Iteration 3: 1.122 ops/ms -Iteration 4: 1.121 ops/ms -Iteration 5: 1.121 ops/ms -Iteration 6: 1.120 ops/ms -Iteration 7: 1.122 ops/ms -Iteration 8: 1.122 ops/ms -Iteration 9: 1.122 ops/ms -Iteration 10: 1.122 ops/ms - -# Run progress: 29.52% complete, ETA 08:14:45 -# Fork: 2 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.065 ops/ms -# Warmup Iteration 2: 1.127 ops/ms -# Warmup Iteration 3: 1.128 ops/ms -# Warmup Iteration 4: 1.128 ops/ms -# Warmup Iteration 5: 1.128 ops/ms -# Warmup Iteration 6: 1.126 ops/ms -# Warmup Iteration 7: 1.128 ops/ms -# Warmup Iteration 8: 1.128 ops/ms -# Warmup Iteration 9: 1.128 ops/ms -# Warmup Iteration 10: 1.127 ops/ms -Iteration 1: 1.128 ops/ms -Iteration 2: 1.127 ops/ms -Iteration 3: 1.128 ops/ms -Iteration 4: 1.128 ops/ms -Iteration 5: 1.127 ops/ms -Iteration 6: 1.124 ops/ms -Iteration 7: 1.129 ops/ms -Iteration 8: 1.129 ops/ms -Iteration 9: 1.129 ops/ms -Iteration 10: 1.129 ops/ms - -# Run progress: 30.48% complete, ETA 08:08:05 -# Fork: 3 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.065 ops/ms -# Warmup Iteration 2: 1.127 ops/ms -# Warmup Iteration 3: 1.127 ops/ms -# Warmup Iteration 4: 1.127 ops/ms -# Warmup Iteration 5: 1.127 ops/ms -# Warmup Iteration 6: 1.125 ops/ms -# Warmup Iteration 7: 1.127 ops/ms -# Warmup Iteration 8: 1.127 ops/ms -# Warmup Iteration 9: 1.127 ops/ms -# Warmup Iteration 10: 1.127 ops/ms -Iteration 1: 1.127 ops/ms -Iteration 2: 1.127 ops/ms -Iteration 3: 1.127 ops/ms -Iteration 4: 1.127 ops/ms -Iteration 5: 1.127 ops/ms -Iteration 6: 1.127 ops/ms -Iteration 7: 1.127 ops/ms -Iteration 8: 1.127 ops/ms -Iteration 9: 1.127 ops/ms -Iteration 10: 1.127 ops/ms - -# Run progress: 31.43% complete, ETA 08:01:23 -# Fork: 4 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.053 ops/ms -# Warmup Iteration 2: 1.108 ops/ms -# Warmup Iteration 3: 1.108 ops/ms -# Warmup Iteration 4: 1.109 ops/ms -# Warmup Iteration 5: 1.109 ops/ms -# Warmup Iteration 6: 1.100 ops/ms -# Warmup Iteration 7: 1.110 ops/ms -# Warmup Iteration 8: 1.108 ops/ms -# Warmup Iteration 9: 1.109 ops/ms -# Warmup Iteration 10: 1.106 ops/ms -Iteration 1: 1.110 ops/ms -Iteration 2: 1.110 ops/ms -Iteration 3: 1.110 ops/ms -Iteration 4: 1.110 ops/ms -Iteration 5: 1.110 ops/ms -Iteration 6: 1.110 ops/ms -Iteration 7: 1.110 ops/ms -Iteration 8: 1.110 ops/ms -Iteration 9: 1.110 ops/ms -Iteration 10: 1.110 ops/ms - -# Run progress: 32.38% complete, ETA 07:54:43 -# Fork: 5 of 5 -# Warmup Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -1.074 ops/ms -# Warmup Iteration 2: 1.132 ops/ms -# Warmup Iteration 3: 1.133 ops/ms -# Warmup Iteration 4: 1.132 ops/ms -# Warmup Iteration 5: 1.132 ops/ms -# Warmup Iteration 6: 1.131 ops/ms -# Warmup Iteration 7: 1.134 ops/ms -# Warmup Iteration 8: 1.133 ops/ms -# Warmup Iteration 9: 1.133 ops/ms -# Warmup Iteration 10: 1.133 ops/ms -Iteration 1: 1.133 ops/ms -Iteration 2: 1.133 ops/ms -Iteration 3: 1.133 ops/ms -Iteration 4: 1.134 ops/ms -Iteration 5: 1.133 ops/ms -Iteration 6: 1.133 ops/ms -Iteration 7: 1.133 ops/ms -Iteration 8: 1.133 ops/ms -Iteration 9: 1.134 ops/ms -Iteration 10: 1.133 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Afterburner.constructorArrayDeser": - 1.124 ±(99.9%) 0.004 ops/ms [Average] - (min, avg, max) = (1.110, 1.124, 1.134), stdev = 0.008 - CI (99.9%): [1.120, 1.128] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.beanArrayDeser - -# Run progress: 33.33% complete, ETA 07:48:04 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.239 ops/ms -# Warmup Iteration 2: 1.307 ops/ms -# Warmup Iteration 3: 1.308 ops/ms -# Warmup Iteration 4: 1.309 ops/ms -# Warmup Iteration 5: 1.308 ops/ms -# Warmup Iteration 6: 1.309 ops/ms -# Warmup Iteration 7: 1.309 ops/ms -# Warmup Iteration 8: 1.307 ops/ms -# Warmup Iteration 9: 1.309 ops/ms -# Warmup Iteration 10: 1.309 ops/ms -Iteration 1: 1.309 ops/ms -Iteration 2: 1.309 ops/ms -Iteration 3: 1.309 ops/ms -Iteration 4: 1.309 ops/ms -Iteration 5: 1.309 ops/ms -Iteration 6: 1.309 ops/ms -Iteration 7: 1.309 ops/ms -Iteration 8: 1.309 ops/ms -Iteration 9: 1.309 ops/ms -Iteration 10: 1.309 ops/ms - -# Run progress: 34.29% complete, ETA 07:41:23 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.243 ops/ms -# Warmup Iteration 2: 1.295 ops/ms -# Warmup Iteration 3: 1.293 ops/ms -# Warmup Iteration 4: 1.295 ops/ms -# Warmup Iteration 5: 1.284 ops/ms -# Warmup Iteration 6: 1.296 ops/ms -# Warmup Iteration 7: 1.296 ops/ms -# Warmup Iteration 8: 1.295 ops/ms -# Warmup Iteration 9: 1.296 ops/ms -# Warmup Iteration 10: 1.296 ops/ms -Iteration 1: 1.295 ops/ms -Iteration 2: 1.296 ops/ms -Iteration 3: 1.295 ops/ms -Iteration 4: 1.296 ops/ms -Iteration 5: 1.296 ops/ms -Iteration 6: 1.296 ops/ms -Iteration 7: 1.296 ops/ms -Iteration 8: 1.295 ops/ms -Iteration 9: 1.295 ops/ms -Iteration 10: 1.295 ops/ms - -# Run progress: 35.24% complete, ETA 07:34:43 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.255 ops/ms -# Warmup Iteration 2: 1.310 ops/ms -# Warmup Iteration 3: 1.311 ops/ms -# Warmup Iteration 4: 1.311 ops/ms -# Warmup Iteration 5: 1.308 ops/ms -# Warmup Iteration 6: 1.312 ops/ms -# Warmup Iteration 7: 1.312 ops/ms -# Warmup Iteration 8: 1.310 ops/ms -# Warmup Iteration 9: 1.312 ops/ms -# Warmup Iteration 10: 1.312 ops/ms -Iteration 1: 1.312 ops/ms -Iteration 2: 1.312 ops/ms -Iteration 3: 1.312 ops/ms -Iteration 4: 1.312 ops/ms -Iteration 5: 1.312 ops/ms -Iteration 6: 1.312 ops/ms -Iteration 7: 1.312 ops/ms -Iteration 8: 1.312 ops/ms -Iteration 9: 1.312 ops/ms -Iteration 10: 1.312 ops/ms - -# Run progress: 36.19% complete, ETA 07:28:05 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.247 ops/ms -# Warmup Iteration 2: 1.303 ops/ms -# Warmup Iteration 3: 1.303 ops/ms -# Warmup Iteration 4: 1.303 ops/ms -# Warmup Iteration 5: 1.296 ops/ms -# Warmup Iteration 6: 1.304 ops/ms -# Warmup Iteration 7: 1.305 ops/ms -# Warmup Iteration 8: 1.302 ops/ms -# Warmup Iteration 9: 1.305 ops/ms -# Warmup Iteration 10: 1.305 ops/ms -Iteration 1: 1.305 ops/ms -Iteration 2: 1.305 ops/ms -Iteration 3: 1.305 ops/ms -Iteration 4: 1.305 ops/ms -Iteration 5: 1.304 ops/ms -Iteration 6: 1.305 ops/ms -Iteration 7: 1.305 ops/ms -Iteration 8: 1.305 ops/ms -Iteration 9: 1.305 ops/ms -Iteration 10: 1.305 ops/ms - -# Run progress: 37.14% complete, ETA 07:21:23 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.231 ops/ms -# Warmup Iteration 2: 1.281 ops/ms -# Warmup Iteration 3: 1.280 ops/ms -# Warmup Iteration 4: 1.281 ops/ms -# Warmup Iteration 5: 1.280 ops/ms -# Warmup Iteration 6: 1.283 ops/ms -# Warmup Iteration 7: 1.283 ops/ms -# Warmup Iteration 8: 1.282 ops/ms -# Warmup Iteration 9: 1.282 ops/ms -# Warmup Iteration 10: 1.284 ops/ms -Iteration 1: 1.282 ops/ms -Iteration 2: 1.282 ops/ms -Iteration 3: 1.283 ops/ms -Iteration 4: 1.283 ops/ms -Iteration 5: 1.282 ops/ms -Iteration 6: 1.282 ops/ms -Iteration 7: 1.282 ops/ms -Iteration 8: 1.282 ops/ms -Iteration 9: 1.283 ops/ms -Iteration 10: 1.282 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.beanArrayDeser": - 1.301 ±(99.9%) 0.005 ops/ms [Average] - (min, avg, max) = (1.282, 1.301, 1.312), stdev = 0.011 - CI (99.9%): [1.295, 1.306] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.beanArraySer - -# Run progress: 38.10% complete, ETA 07:14:41 -# Fork: 1 of 5 -# Warmup Iteration 1: 2.038 ops/ms -# Warmup Iteration 2: 2.112 ops/ms -# Warmup Iteration 3: 2.111 ops/ms -# Warmup Iteration 4: 2.113 ops/ms -# Warmup Iteration 5: 2.112 ops/ms -# Warmup Iteration 6: 2.114 ops/ms -# Warmup Iteration 7: 2.113 ops/ms -# Warmup Iteration 8: 2.115 ops/ms -# Warmup Iteration 9: 2.113 ops/ms -# Warmup Iteration 10: 2.112 ops/ms -Iteration 1: 2.113 ops/ms -Iteration 2: 2.113 ops/ms -Iteration 3: 2.114 ops/ms -Iteration 4: 2.113 ops/ms -Iteration 5: 2.113 ops/ms -Iteration 6: 2.031 ops/ms -Iteration 7: 2.110 ops/ms -Iteration 8: 2.110 ops/ms -Iteration 9: 2.111 ops/ms -Iteration 10: 2.111 ops/ms - -# Run progress: 39.05% complete, ETA 07:07:59 -# Fork: 2 of 5 -# Warmup Iteration 1: 2.067 ops/ms -# Warmup Iteration 2: 2.123 ops/ms -# Warmup Iteration 3: 2.123 ops/ms -# Warmup Iteration 4: 2.124 ops/ms -# Warmup Iteration 5: 2.125 ops/ms -# Warmup Iteration 6: 2.125 ops/ms -# Warmup Iteration 7: 2.124 ops/ms -# Warmup Iteration 8: 2.125 ops/ms -# Warmup Iteration 9: 2.124 ops/ms -# Warmup Iteration 10: 2.124 ops/ms -Iteration 1: 2.124 ops/ms -Iteration 2: 2.125 ops/ms -Iteration 3: 2.124 ops/ms -Iteration 4: 2.124 ops/ms -Iteration 5: 2.125 ops/ms -Iteration 6: 2.124 ops/ms -Iteration 7: 2.125 ops/ms -Iteration 8: 2.125 ops/ms -Iteration 9: 2.125 ops/ms -Iteration 10: 2.125 ops/ms - -# Run progress: 40.00% complete, ETA 07:01:18 -# Fork: 3 of 5 -# Warmup Iteration 1: 2.056 ops/ms -# Warmup Iteration 2: 2.137 ops/ms -# Warmup Iteration 3: 2.136 ops/ms -# Warmup Iteration 4: 2.137 ops/ms -# Warmup Iteration 5: 2.138 ops/ms -# Warmup Iteration 6: 2.139 ops/ms -# Warmup Iteration 7: 2.139 ops/ms -# Warmup Iteration 8: 2.137 ops/ms -# Warmup Iteration 9: 2.136 ops/ms -# Warmup Iteration 10: 2.139 ops/ms -Iteration 1: 2.139 ops/ms -Iteration 2: 2.138 ops/ms -Iteration 3: 2.139 ops/ms -Iteration 4: 2.138 ops/ms -Iteration 5: 2.138 ops/ms -Iteration 6: 2.138 ops/ms -Iteration 7: 2.128 ops/ms -Iteration 8: 2.127 ops/ms -Iteration 9: 2.128 ops/ms -Iteration 10: 2.128 ops/ms - -# Run progress: 40.95% complete, ETA 06:54:36 -# Fork: 4 of 5 -# Warmup Iteration 1: 2.036 ops/ms -# Warmup Iteration 2: 2.093 ops/ms -# Warmup Iteration 3: 2.092 ops/ms -# Warmup Iteration 4: 2.094 ops/ms -# Warmup Iteration 5: 2.095 ops/ms -# Warmup Iteration 6: 2.095 ops/ms -# Warmup Iteration 7: 2.097 ops/ms -# Warmup Iteration 8: 2.096 ops/ms -# Warmup Iteration 9: 2.096 ops/ms -# Warmup Iteration 10: 2.096 ops/ms -Iteration 1: 2.096 ops/ms -Iteration 2: 2.096 ops/ms -Iteration 3: 2.096 ops/ms -Iteration 4: 2.096 ops/ms -Iteration 5: 2.096 ops/ms -Iteration 6: 2.096 ops/ms -Iteration 7: 2.093 ops/ms -Iteration 8: 2.093 ops/ms -Iteration 9: 2.093 ops/ms -Iteration 10: 2.092 ops/ms - -# Run progress: 41.90% complete, ETA 06:47:54 -# Fork: 5 of 5 -# Warmup Iteration 1: 2.061 ops/ms -# Warmup Iteration 2: 2.117 ops/ms -# Warmup Iteration 3: 2.117 ops/ms -# Warmup Iteration 4: 2.116 ops/ms -# Warmup Iteration 5: 2.117 ops/ms -# Warmup Iteration 6: 2.118 ops/ms -# Warmup Iteration 7: 2.117 ops/ms -# Warmup Iteration 8: 2.118 ops/ms -# Warmup Iteration 9: 2.117 ops/ms -# Warmup Iteration 10: 2.117 ops/ms -Iteration 1: 2.117 ops/ms -Iteration 2: 2.117 ops/ms -Iteration 3: 2.117 ops/ms -Iteration 4: 2.116 ops/ms -Iteration 5: 2.117 ops/ms -Iteration 6: 2.117 ops/ms -Iteration 7: 2.116 ops/ms -Iteration 8: 2.117 ops/ms -Iteration 9: 2.117 ops/ms -Iteration 10: 2.116 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.beanArraySer": - 2.115 ±(99.9%) 0.009 ops/ms [Average] - (min, avg, max) = (2.031, 2.115, 2.139), stdev = 0.018 - CI (99.9%): [2.106, 2.124] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemDeser - -# Run progress: 42.86% complete, ETA 06:41:12 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.178 ops/ms -# Warmup Iteration 2: 1.217 ops/ms -# Warmup Iteration 3: 1.217 ops/ms -# Warmup Iteration 4: 1.218 ops/ms -# Warmup Iteration 5: 1.219 ops/ms -# Warmup Iteration 6: 1.213 ops/ms -# Warmup Iteration 7: 1.218 ops/ms -# Warmup Iteration 8: 1.218 ops/ms -# Warmup Iteration 9: 1.218 ops/ms -# Warmup Iteration 10: 1.219 ops/ms -Iteration 1: 1.219 ops/ms -Iteration 2: 1.218 ops/ms -Iteration 3: 1.219 ops/ms -Iteration 4: 1.219 ops/ms -Iteration 5: 1.219 ops/ms -Iteration 6: 1.219 ops/ms -Iteration 7: 1.218 ops/ms -Iteration 8: 1.218 ops/ms -Iteration 9: 1.219 ops/ms -Iteration 10: 1.218 ops/ms - -# Run progress: 43.81% complete, ETA 06:34:31 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.271 ops/ms -# Warmup Iteration 2: 1.314 ops/ms -# Warmup Iteration 3: 1.316 ops/ms -# Warmup Iteration 4: 1.315 ops/ms -# Warmup Iteration 5: 1.310 ops/ms -# Warmup Iteration 6: 1.317 ops/ms -# Warmup Iteration 7: 1.316 ops/ms -# Warmup Iteration 8: 1.316 ops/ms -# Warmup Iteration 9: 1.316 ops/ms -# Warmup Iteration 10: 1.316 ops/ms -Iteration 1: 1.316 ops/ms -Iteration 2: 1.316 ops/ms -Iteration 3: 1.316 ops/ms -Iteration 4: 1.316 ops/ms -Iteration 5: 1.316 ops/ms -Iteration 6: 1.316 ops/ms -Iteration 7: 1.317 ops/ms -Iteration 8: 1.317 ops/ms -Iteration 9: 1.317 ops/ms -Iteration 10: 1.317 ops/ms - -# Run progress: 44.76% complete, ETA 06:27:49 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.205 ops/ms -# Warmup Iteration 2: 1.251 ops/ms -# Warmup Iteration 3: 1.250 ops/ms -# Warmup Iteration 4: 1.250 ops/ms -# Warmup Iteration 5: 1.249 ops/ms -# Warmup Iteration 6: 1.248 ops/ms -# Warmup Iteration 7: 1.252 ops/ms -# Warmup Iteration 8: 1.251 ops/ms -# Warmup Iteration 9: 1.251 ops/ms -# Warmup Iteration 10: 1.251 ops/ms -Iteration 1: 1.251 ops/ms -Iteration 2: 1.251 ops/ms -Iteration 3: 1.251 ops/ms -Iteration 4: 1.252 ops/ms -Iteration 5: 1.251 ops/ms -Iteration 6: 1.252 ops/ms -Iteration 7: 1.252 ops/ms -Iteration 8: 1.252 ops/ms -Iteration 9: 1.252 ops/ms -Iteration 10: 1.252 ops/ms - -# Run progress: 45.71% complete, ETA 06:21:08 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.194 ops/ms -# Warmup Iteration 2: 1.232 ops/ms -# Warmup Iteration 3: 1.234 ops/ms -# Warmup Iteration 4: 1.234 ops/ms -# Warmup Iteration 5: 1.235 ops/ms -# Warmup Iteration 6: 1.228 ops/ms -# Warmup Iteration 7: 1.234 ops/ms -# Warmup Iteration 8: 1.234 ops/ms -# Warmup Iteration 9: 1.235 ops/ms -# Warmup Iteration 10: 1.235 ops/ms -Iteration 1: 1.234 ops/ms -Iteration 2: 1.234 ops/ms -Iteration 3: 1.235 ops/ms -Iteration 4: 1.235 ops/ms -Iteration 5: 1.235 ops/ms -Iteration 6: 1.235 ops/ms -Iteration 7: 1.235 ops/ms -Iteration 8: 1.235 ops/ms -Iteration 9: 1.235 ops/ms -Iteration 10: 1.235 ops/ms - -# Run progress: 46.67% complete, ETA 06:14:26 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.191 ops/ms -# Warmup Iteration 2: 1.228 ops/ms -# Warmup Iteration 3: 1.230 ops/ms -# Warmup Iteration 4: 1.229 ops/ms -# Warmup Iteration 5: 1.230 ops/ms -# Warmup Iteration 6: 1.229 ops/ms -# Warmup Iteration 7: 1.229 ops/ms -# Warmup Iteration 8: 1.230 ops/ms -# Warmup Iteration 9: 1.230 ops/ms -# Warmup Iteration 10: 1.230 ops/ms -Iteration 1: 1.230 ops/ms -Iteration 2: 1.230 ops/ms -Iteration 3: 1.230 ops/ms -Iteration 4: 1.230 ops/ms -Iteration 5: 1.230 ops/ms -Iteration 6: 1.229 ops/ms -Iteration 7: 1.230 ops/ms -Iteration 8: 1.230 ops/ms -Iteration 9: 1.230 ops/ms -Iteration 10: 1.230 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemDeser": - 1.250 ±(99.9%) 0.017 ops/ms [Average] - (min, avg, max) = (1.218, 1.250, 1.317), stdev = 0.035 - CI (99.9%): [1.233, 1.268] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemSer - -# Run progress: 47.62% complete, ETA 06:07:45 -# Fork: 1 of 5 -# Warmup Iteration 1: 2.120 ops/ms -# Warmup Iteration 2: 2.151 ops/ms -# Warmup Iteration 3: 2.160 ops/ms -# Warmup Iteration 4: 2.158 ops/ms -# Warmup Iteration 5: 2.150 ops/ms -# Warmup Iteration 6: 2.145 ops/ms -# Warmup Iteration 7: 2.146 ops/ms -# Warmup Iteration 8: 2.112 ops/ms -# Warmup Iteration 9: 2.146 ops/ms -# Warmup Iteration 10: 2.136 ops/ms -Iteration 1: 2.139 ops/ms -Iteration 2: 2.146 ops/ms -Iteration 3: 2.146 ops/ms -Iteration 4: 2.146 ops/ms -Iteration 5: 2.140 ops/ms -Iteration 6: 2.131 ops/ms -Iteration 7: 2.151 ops/ms -Iteration 8: 2.154 ops/ms -Iteration 9: 2.151 ops/ms -Iteration 10: 2.150 ops/ms - -# Run progress: 48.57% complete, ETA 06:01:03 -# Fork: 2 of 5 -# Warmup Iteration 1: 2.074 ops/ms -# Warmup Iteration 2: 2.105 ops/ms -# Warmup Iteration 3: 2.089 ops/ms -# Warmup Iteration 4: 2.101 ops/ms -# Warmup Iteration 5: 2.101 ops/ms -# Warmup Iteration 6: 2.098 ops/ms -# Warmup Iteration 7: 2.100 ops/ms -# Warmup Iteration 8: 2.101 ops/ms -# Warmup Iteration 9: 2.103 ops/ms -# Warmup Iteration 10: 2.101 ops/ms -Iteration 1: 2.101 ops/ms -Iteration 2: 2.101 ops/ms -Iteration 3: 2.101 ops/ms -Iteration 4: 2.101 ops/ms -Iteration 5: 2.100 ops/ms -Iteration 6: 2.102 ops/ms -Iteration 7: 2.088 ops/ms -Iteration 8: 2.088 ops/ms -Iteration 9: 2.088 ops/ms -Iteration 10: 2.089 ops/ms - -# Run progress: 49.52% complete, ETA 05:54:22 -# Fork: 3 of 5 -# Warmup Iteration 1: 2.073 ops/ms -# Warmup Iteration 2: 2.114 ops/ms -# Warmup Iteration 3: 2.115 ops/ms -# Warmup Iteration 4: 2.113 ops/ms -# Warmup Iteration 5: 2.107 ops/ms -# Warmup Iteration 6: 2.117 ops/ms -# Warmup Iteration 7: 2.117 ops/ms -# Warmup Iteration 8: 2.117 ops/ms -# Warmup Iteration 9: 2.117 ops/ms -# Warmup Iteration 10: 2.116 ops/ms -Iteration 1: 2.117 ops/ms -Iteration 2: 2.115 ops/ms -Iteration 3: 2.118 ops/ms -Iteration 4: 2.117 ops/ms -Iteration 5: 2.117 ops/ms -Iteration 6: 2.117 ops/ms -Iteration 7: 2.103 ops/ms -Iteration 8: 2.103 ops/ms -Iteration 9: 2.104 ops/ms -Iteration 10: 2.102 ops/ms - -# Run progress: 50.48% complete, ETA 05:47:41 -# Fork: 4 of 5 -# Warmup Iteration 1: 2.188 ops/ms -# Warmup Iteration 2: 2.224 ops/ms -# Warmup Iteration 3: 2.223 ops/ms -# Warmup Iteration 4: 2.199 ops/ms -# Warmup Iteration 5: 2.174 ops/ms -# Warmup Iteration 6: 2.198 ops/ms -# Warmup Iteration 7: 2.184 ops/ms -# Warmup Iteration 8: 2.204 ops/ms -# Warmup Iteration 9: 2.173 ops/ms -# Warmup Iteration 10: 2.194 ops/ms -Iteration 1: 2.196 ops/ms -Iteration 2: 2.193 ops/ms -Iteration 3: 2.190 ops/ms -Iteration 4: 2.203 ops/ms -Iteration 5: 2.191 ops/ms -Iteration 6: 2.188 ops/ms -Iteration 7: 2.181 ops/ms -Iteration 8: 2.177 ops/ms -Iteration 9: 2.176 ops/ms -Iteration 10: 2.181 ops/ms - -# Run progress: 51.43% complete, ETA 05:41:00 -# Fork: 5 of 5 -# Warmup Iteration 1: 2.121 ops/ms -# Warmup Iteration 2: 2.179 ops/ms -# Warmup Iteration 3: 2.179 ops/ms -# Warmup Iteration 4: 2.173 ops/ms -# Warmup Iteration 5: 2.166 ops/ms -# Warmup Iteration 6: 2.173 ops/ms -# Warmup Iteration 7: 2.173 ops/ms -# Warmup Iteration 8: 2.173 ops/ms -# Warmup Iteration 9: 2.173 ops/ms -# Warmup Iteration 10: 2.173 ops/ms -Iteration 1: 2.174 ops/ms -Iteration 2: 2.173 ops/ms -Iteration 3: 2.173 ops/ms -Iteration 4: 2.172 ops/ms -Iteration 5: 2.173 ops/ms -Iteration 6: 2.173 ops/ms -Iteration 7: 2.142 ops/ms -Iteration 8: 2.142 ops/ms -Iteration 9: 2.142 ops/ms -Iteration 10: 2.141 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicBeanItemSer": - 2.140 ±(99.9%) 0.017 ops/ms [Average] - (min, avg, max) = (2.088, 2.140, 2.203), stdev = 0.035 - CI (99.9%): [2.123, 2.157] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser - -# Run progress: 52.38% complete, ETA 05:34:18 -# Fork: 1 of 5 -# Warmup Iteration 1: 373.099 ops/ms -# Warmup Iteration 2: 386.706 ops/ms -# Warmup Iteration 3: 386.418 ops/ms -# Warmup Iteration 4: 386.766 ops/ms -# Warmup Iteration 5: 386.708 ops/ms -# Warmup Iteration 6: 386.449 ops/ms -# Warmup Iteration 7: 386.559 ops/ms -# Warmup Iteration 8: 386.889 ops/ms -# Warmup Iteration 9: 386.682 ops/ms -# Warmup Iteration 10: 386.846 ops/ms -Iteration 1: 386.704 ops/ms -Iteration 2: 386.533 ops/ms -Iteration 3: 386.954 ops/ms -Iteration 4: 386.505 ops/ms -Iteration 5: 386.644 ops/ms -Iteration 6: 386.357 ops/ms -Iteration 7: 386.382 ops/ms -Iteration 8: 386.481 ops/ms -Iteration 9: 386.339 ops/ms -Iteration 10: 386.344 ops/ms - -# Run progress: 53.33% complete, ETA 05:27:37 -# Fork: 2 of 5 -# Warmup Iteration 1: 377.475 ops/ms -# Warmup Iteration 2: 390.200 ops/ms -# Warmup Iteration 3: 390.929 ops/ms -# Warmup Iteration 4: 390.733 ops/ms -# Warmup Iteration 5: 390.845 ops/ms -# Warmup Iteration 6: 390.707 ops/ms -# Warmup Iteration 7: 390.821 ops/ms -# Warmup Iteration 8: 390.675 ops/ms -# Warmup Iteration 9: 390.821 ops/ms -# Warmup Iteration 10: 390.695 ops/ms -Iteration 1: 390.970 ops/ms -Iteration 2: 390.646 ops/ms -Iteration 3: 390.650 ops/ms -Iteration 4: 390.814 ops/ms -Iteration 5: 390.476 ops/ms -Iteration 6: 390.735 ops/ms -Iteration 7: 389.087 ops/ms -Iteration 8: 389.450 ops/ms -Iteration 9: 389.219 ops/ms -Iteration 10: 389.325 ops/ms - -# Run progress: 54.29% complete, ETA 05:20:55 -# Fork: 3 of 5 -# Warmup Iteration 1: 372.393 ops/ms -# Warmup Iteration 2: 382.887 ops/ms -# Warmup Iteration 3: 383.158 ops/ms -# Warmup Iteration 4: 382.460 ops/ms -# Warmup Iteration 5: 382.930 ops/ms -# Warmup Iteration 6: 382.892 ops/ms -# Warmup Iteration 7: 382.128 ops/ms -# Warmup Iteration 8: 382.340 ops/ms -# Warmup Iteration 9: 382.534 ops/ms -# Warmup Iteration 10: 382.628 ops/ms -Iteration 1: 382.339 ops/ms -Iteration 2: 382.698 ops/ms -Iteration 3: 382.281 ops/ms -Iteration 4: 382.747 ops/ms -Iteration 5: 382.433 ops/ms -Iteration 6: 382.336 ops/ms -Iteration 7: 381.227 ops/ms -Iteration 8: 381.638 ops/ms -Iteration 9: 381.135 ops/ms -Iteration 10: 381.291 ops/ms - -# Run progress: 55.24% complete, ETA 05:14:15 -# Fork: 4 of 5 -# Warmup Iteration 1: 376.557 ops/ms -# Warmup Iteration 2: 389.824 ops/ms -# Warmup Iteration 3: 387.598 ops/ms -# Warmup Iteration 4: 387.706 ops/ms -# Warmup Iteration 5: 388.504 ops/ms -# Warmup Iteration 6: 387.822 ops/ms -# Warmup Iteration 7: 387.301 ops/ms -# Warmup Iteration 8: 387.832 ops/ms -# Warmup Iteration 9: 387.620 ops/ms -# Warmup Iteration 10: 386.801 ops/ms -Iteration 1: 387.770 ops/ms -Iteration 2: 387.595 ops/ms -Iteration 3: 387.981 ops/ms -Iteration 4: 387.718 ops/ms -Iteration 5: 387.554 ops/ms -Iteration 6: 386.737 ops/ms -Iteration 7: 385.877 ops/ms -Iteration 8: 386.851 ops/ms -Iteration 9: 386.506 ops/ms -Iteration 10: 386.397 ops/ms - -# Run progress: 56.19% complete, ETA 05:07:34 -# Fork: 5 of 5 -# Warmup Iteration 1: 374.670 ops/ms -# Warmup Iteration 2: 389.434 ops/ms -# Warmup Iteration 3: 390.256 ops/ms -# Warmup Iteration 4: 389.986 ops/ms -# Warmup Iteration 5: 390.065 ops/ms -# Warmup Iteration 6: 390.244 ops/ms -# Warmup Iteration 7: 390.221 ops/ms -# Warmup Iteration 8: 390.178 ops/ms -# Warmup Iteration 9: 389.840 ops/ms -# Warmup Iteration 10: 389.953 ops/ms -Iteration 1: 390.079 ops/ms -Iteration 2: 390.052 ops/ms -Iteration 3: 390.121 ops/ms -Iteration 4: 390.149 ops/ms -Iteration 5: 390.340 ops/ms -Iteration 6: 390.116 ops/ms -Iteration 7: 389.697 ops/ms -Iteration 8: 389.921 ops/ms -Iteration 9: 390.039 ops/ms -Iteration 10: 389.751 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemDeser": - 387.160 ±(99.9%) 1.507 ops/ms [Average] - (min, avg, max) = (381.135, 387.160, 390.970), stdev = 3.043 - CI (99.9%): [385.653, 388.666] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemSer - -# Run progress: 57.14% complete, ETA 05:00:54 -# Fork: 1 of 5 -# Warmup Iteration 1: 564.856 ops/ms -# Warmup Iteration 2: 563.840 ops/ms -# Warmup Iteration 3: 566.729 ops/ms -# Warmup Iteration 4: 566.415 ops/ms -# Warmup Iteration 5: 567.093 ops/ms -# Warmup Iteration 6: 566.916 ops/ms -# Warmup Iteration 7: 566.675 ops/ms -# Warmup Iteration 8: 566.657 ops/ms -# Warmup Iteration 9: 566.836 ops/ms -# Warmup Iteration 10: 566.812 ops/ms -Iteration 1: 567.264 ops/ms -Iteration 2: 567.316 ops/ms -Iteration 3: 566.845 ops/ms -Iteration 4: 567.368 ops/ms -Iteration 5: 566.994 ops/ms -Iteration 6: 567.245 ops/ms -Iteration 7: 567.747 ops/ms -Iteration 8: 567.346 ops/ms -Iteration 9: 567.455 ops/ms -Iteration 10: 567.475 ops/ms - -# Run progress: 58.10% complete, ETA 04:54:13 -# Fork: 2 of 5 -# Warmup Iteration 1: 570.368 ops/ms -# Warmup Iteration 2: 584.465 ops/ms -# Warmup Iteration 3: 586.427 ops/ms -# Warmup Iteration 4: 586.408 ops/ms -# Warmup Iteration 5: 585.084 ops/ms -# Warmup Iteration 6: 586.455 ops/ms -# Warmup Iteration 7: 586.442 ops/ms -# Warmup Iteration 8: 585.707 ops/ms -# Warmup Iteration 9: 586.499 ops/ms -# Warmup Iteration 10: 586.847 ops/ms -Iteration 1: 586.585 ops/ms -Iteration 2: 586.723 ops/ms -Iteration 3: 586.578 ops/ms -Iteration 4: 586.622 ops/ms -Iteration 5: 586.922 ops/ms -Iteration 6: 586.739 ops/ms -Iteration 7: 588.170 ops/ms -Iteration 8: 587.766 ops/ms -Iteration 9: 587.750 ops/ms -Iteration 10: 588.250 ops/ms - -# Run progress: 59.05% complete, ETA 04:47:32 -# Fork: 3 of 5 -# Warmup Iteration 1: 551.028 ops/ms -# Warmup Iteration 2: 561.184 ops/ms -# Warmup Iteration 3: 564.790 ops/ms -# Warmup Iteration 4: 563.672 ops/ms -# Warmup Iteration 5: 562.532 ops/ms -# Warmup Iteration 6: 561.632 ops/ms -# Warmup Iteration 7: 564.402 ops/ms -# Warmup Iteration 8: 565.039 ops/ms -# Warmup Iteration 9: 564.591 ops/ms -# Warmup Iteration 10: 565.852 ops/ms -Iteration 1: 565.253 ops/ms -Iteration 2: 565.408 ops/ms -Iteration 3: 561.840 ops/ms -Iteration 4: 561.538 ops/ms -Iteration 5: 565.281 ops/ms -Iteration 6: 565.467 ops/ms -Iteration 7: 561.995 ops/ms -Iteration 8: 562.708 ops/ms -Iteration 9: 562.319 ops/ms -Iteration 10: 562.103 ops/ms - -# Run progress: 60.00% complete, ETA 04:40:50 -# Fork: 4 of 5 -# Warmup Iteration 1: 574.157 ops/ms -# Warmup Iteration 2: 583.391 ops/ms -# Warmup Iteration 3: 587.119 ops/ms -# Warmup Iteration 4: 586.384 ops/ms -# Warmup Iteration 5: 587.410 ops/ms -# Warmup Iteration 6: 587.175 ops/ms -# Warmup Iteration 7: 587.178 ops/ms -# Warmup Iteration 8: 587.126 ops/ms -# Warmup Iteration 9: 587.133 ops/ms -# Warmup Iteration 10: 587.425 ops/ms -Iteration 1: 587.456 ops/ms -Iteration 2: 587.545 ops/ms -Iteration 3: 587.688 ops/ms -Iteration 4: 587.647 ops/ms -Iteration 5: 587.500 ops/ms -Iteration 6: 587.030 ops/ms -Iteration 7: 584.387 ops/ms -Iteration 8: 584.994 ops/ms -Iteration 9: 585.153 ops/ms -Iteration 10: 585.184 ops/ms - -# Run progress: 60.95% complete, ETA 04:34:09 -# Fork: 5 of 5 -# Warmup Iteration 1: 561.155 ops/ms -# Warmup Iteration 2: 575.469 ops/ms -# Warmup Iteration 3: 573.437 ops/ms -# Warmup Iteration 4: 574.134 ops/ms -# Warmup Iteration 5: 574.453 ops/ms -# Warmup Iteration 6: 574.563 ops/ms -# Warmup Iteration 7: 574.035 ops/ms -# Warmup Iteration 8: 575.710 ops/ms -# Warmup Iteration 9: 574.868 ops/ms -# Warmup Iteration 10: 575.173 ops/ms -Iteration 1: 575.730 ops/ms -Iteration 2: 574.667 ops/ms -Iteration 3: 576.177 ops/ms -Iteration 4: 576.256 ops/ms -Iteration 5: 574.222 ops/ms -Iteration 6: 575.649 ops/ms -Iteration 7: 573.770 ops/ms -Iteration 8: 572.502 ops/ms -Iteration 9: 573.370 ops/ms -Iteration 10: 574.174 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.classicMediaItemSer": - 575.804 ±(99.9%) 4.887 ops/ms [Average] - (min, avg, max) = (561.538, 575.804, 588.250), stdev = 9.871 - CI (99.9%): [570.917, 580.690] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Blackbird.constructorArrayDeser - -# Run progress: 61.90% complete, ETA 04:27:28 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.050 ops/ms -# Warmup Iteration 2: 1.119 ops/ms -# Warmup Iteration 3: 1.120 ops/ms -# Warmup Iteration 4: 1.119 ops/ms -# Warmup Iteration 5: 1.120 ops/ms -# Warmup Iteration 6: 1.116 ops/ms -# Warmup Iteration 7: 1.120 ops/ms -# Warmup Iteration 8: 1.120 ops/ms -# Warmup Iteration 9: 1.120 ops/ms -# Warmup Iteration 10: 1.118 ops/ms -Iteration 1: 1.120 ops/ms -Iteration 2: 1.120 ops/ms -Iteration 3: 1.120 ops/ms -Iteration 4: 1.120 ops/ms -Iteration 5: 1.120 ops/ms -Iteration 6: 1.120 ops/ms -Iteration 7: 1.120 ops/ms -Iteration 8: 1.119 ops/ms -Iteration 9: 1.119 ops/ms -Iteration 10: 1.119 ops/ms - -# Run progress: 62.86% complete, ETA 04:20:46 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.061 ops/ms -# Warmup Iteration 2: 1.126 ops/ms -# Warmup Iteration 3: 1.127 ops/ms -# Warmup Iteration 4: 1.127 ops/ms -# Warmup Iteration 5: 1.127 ops/ms -# Warmup Iteration 6: 1.123 ops/ms -# Warmup Iteration 7: 1.127 ops/ms -# Warmup Iteration 8: 1.127 ops/ms -# Warmup Iteration 9: 1.127 ops/ms -# Warmup Iteration 10: 1.126 ops/ms -Iteration 1: 1.127 ops/ms -Iteration 2: 1.127 ops/ms -Iteration 3: 1.128 ops/ms -Iteration 4: 1.127 ops/ms -Iteration 5: 1.127 ops/ms -Iteration 6: 1.127 ops/ms -Iteration 7: 1.128 ops/ms -Iteration 8: 1.128 ops/ms -Iteration 9: 1.127 ops/ms -Iteration 10: 1.128 ops/ms - -# Run progress: 63.81% complete, ETA 04:14:05 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.073 ops/ms -# Warmup Iteration 2: 1.130 ops/ms -# Warmup Iteration 3: 1.131 ops/ms -# Warmup Iteration 4: 1.131 ops/ms -# Warmup Iteration 5: 1.132 ops/ms -# Warmup Iteration 6: 1.128 ops/ms -# Warmup Iteration 7: 1.132 ops/ms -# Warmup Iteration 8: 1.132 ops/ms -# Warmup Iteration 9: 1.132 ops/ms -# Warmup Iteration 10: 1.132 ops/ms -Iteration 1: 1.133 ops/ms -Iteration 2: 1.133 ops/ms -Iteration 3: 1.133 ops/ms -Iteration 4: 1.133 ops/ms -Iteration 5: 1.132 ops/ms -Iteration 6: 1.097 ops/ms -Iteration 7: 1.132 ops/ms -Iteration 8: 1.132 ops/ms -Iteration 9: 1.132 ops/ms -Iteration 10: 1.132 ops/ms - -# Run progress: 64.76% complete, ETA 04:07:24 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.060 ops/ms -# Warmup Iteration 2: 1.125 ops/ms -# Warmup Iteration 3: 1.124 ops/ms -# Warmup Iteration 4: 1.125 ops/ms -# Warmup Iteration 5: 1.126 ops/ms -# Warmup Iteration 6: 1.122 ops/ms -# Warmup Iteration 7: 1.126 ops/ms -# Warmup Iteration 8: 1.126 ops/ms -# Warmup Iteration 9: 1.126 ops/ms -# Warmup Iteration 10: 1.126 ops/ms -Iteration 1: 1.127 ops/ms -Iteration 2: 1.126 ops/ms -Iteration 3: 1.126 ops/ms -Iteration 4: 1.126 ops/ms -Iteration 5: 1.126 ops/ms -Iteration 6: 1.126 ops/ms -Iteration 7: 1.122 ops/ms -Iteration 8: 1.122 ops/ms -Iteration 9: 1.122 ops/ms -Iteration 10: 1.122 ops/ms - -# Run progress: 65.71% complete, ETA 04:00:42 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.052 ops/ms -# Warmup Iteration 2: 1.112 ops/ms -# Warmup Iteration 3: 1.112 ops/ms -# Warmup Iteration 4: 1.112 ops/ms -# Warmup Iteration 5: 1.112 ops/ms -# Warmup Iteration 6: 1.111 ops/ms -# Warmup Iteration 7: 1.113 ops/ms -# Warmup Iteration 8: 1.112 ops/ms -# Warmup Iteration 9: 1.113 ops/ms -# Warmup Iteration 10: 1.109 ops/ms -Iteration 1: 1.113 ops/ms -Iteration 2: 1.113 ops/ms -Iteration 3: 1.112 ops/ms -Iteration 4: 1.113 ops/ms -Iteration 5: 1.112 ops/ms -Iteration 6: 1.112 ops/ms -Iteration 7: 1.105 ops/ms -Iteration 8: 1.105 ops/ms -Iteration 9: 1.105 ops/ms -Iteration 10: 1.105 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Blackbird.constructorArrayDeser": - 1.122 ±(99.9%) 0.004 ops/ms [Average] - (min, avg, max) = (1.097, 1.122, 1.133), stdev = 0.009 - CI (99.9%): [1.118, 1.126] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.beanArrayDeser - -# Run progress: 66.67% complete, ETA 03:54:01 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.125 ops/ms -# Warmup Iteration 2: 1.189 ops/ms -# Warmup Iteration 3: 1.189 ops/ms -# Warmup Iteration 4: 1.189 ops/ms -# Warmup Iteration 5: 1.188 ops/ms -# Warmup Iteration 6: 1.187 ops/ms -# Warmup Iteration 7: 1.188 ops/ms -# Warmup Iteration 8: 1.188 ops/ms -# Warmup Iteration 9: 1.182 ops/ms -# Warmup Iteration 10: 1.189 ops/ms -Iteration 1: 1.189 ops/ms -Iteration 2: 1.188 ops/ms -Iteration 3: 1.189 ops/ms -Iteration 4: 1.189 ops/ms -Iteration 5: 1.188 ops/ms -Iteration 6: 1.189 ops/ms -Iteration 7: 1.189 ops/ms -Iteration 8: 1.189 ops/ms -Iteration 9: 1.188 ops/ms -Iteration 10: 1.189 ops/ms - -# Run progress: 67.62% complete, ETA 03:47:20 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.123 ops/ms -# Warmup Iteration 2: 1.182 ops/ms -# Warmup Iteration 3: 1.183 ops/ms -# Warmup Iteration 4: 1.183 ops/ms -# Warmup Iteration 5: 1.182 ops/ms -# Warmup Iteration 6: 1.179 ops/ms -# Warmup Iteration 7: 1.182 ops/ms -# Warmup Iteration 8: 1.183 ops/ms -# Warmup Iteration 9: 1.180 ops/ms -# Warmup Iteration 10: 1.184 ops/ms -Iteration 1: 1.183 ops/ms -Iteration 2: 1.183 ops/ms -Iteration 3: 1.183 ops/ms -Iteration 4: 1.183 ops/ms -Iteration 5: 1.183 ops/ms -Iteration 6: 1.183 ops/ms -Iteration 7: 1.184 ops/ms -Iteration 8: 1.184 ops/ms -Iteration 9: 1.184 ops/ms -Iteration 10: 1.183 ops/ms - -# Run progress: 68.57% complete, ETA 03:40:38 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.115 ops/ms -# Warmup Iteration 2: 1.165 ops/ms -# Warmup Iteration 3: 1.166 ops/ms -# Warmup Iteration 4: 1.165 ops/ms -# Warmup Iteration 5: 1.166 ops/ms -# Warmup Iteration 6: 1.165 ops/ms -# Warmup Iteration 7: 1.166 ops/ms -# Warmup Iteration 8: 1.166 ops/ms -# Warmup Iteration 9: 1.165 ops/ms -# Warmup Iteration 10: 1.166 ops/ms -Iteration 1: 1.166 ops/ms -Iteration 2: 1.166 ops/ms -Iteration 3: 1.166 ops/ms -Iteration 4: 1.166 ops/ms -Iteration 5: 1.166 ops/ms -Iteration 6: 1.166 ops/ms -Iteration 7: 1.166 ops/ms -Iteration 8: 1.166 ops/ms -Iteration 9: 1.167 ops/ms -Iteration 10: 1.166 ops/ms - -# Run progress: 69.52% complete, ETA 03:33:57 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.143 ops/ms -# Warmup Iteration 2: 1.197 ops/ms -# Warmup Iteration 3: 1.197 ops/ms -# Warmup Iteration 4: 1.197 ops/ms -# Warmup Iteration 5: 1.197 ops/ms -# Warmup Iteration 6: 1.194 ops/ms -# Warmup Iteration 7: 1.197 ops/ms -# Warmup Iteration 8: 1.197 ops/ms -# Warmup Iteration 9: 1.193 ops/ms -# Warmup Iteration 10: 1.198 ops/ms -Iteration 1: 1.197 ops/ms -Iteration 2: 1.198 ops/ms -Iteration 3: 1.198 ops/ms -Iteration 4: 1.198 ops/ms -Iteration 5: 1.198 ops/ms -Iteration 6: 1.198 ops/ms -Iteration 7: 1.199 ops/ms -Iteration 8: 1.198 ops/ms -Iteration 9: 1.198 ops/ms -Iteration 10: 1.199 ops/ms - -# Run progress: 70.48% complete, ETA 03:27:16 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.118 ops/ms -# Warmup Iteration 2: 1.181 ops/ms -# Warmup Iteration 3: 1.181 ops/ms -# Warmup Iteration 4: 1.182 ops/ms -# Warmup Iteration 5: 1.182 ops/ms -# Warmup Iteration 6: 1.170 ops/ms -# Warmup Iteration 7: 1.182 ops/ms -# Warmup Iteration 8: 1.183 ops/ms -# Warmup Iteration 9: 1.180 ops/ms -# Warmup Iteration 10: 1.183 ops/ms -Iteration 1: 1.183 ops/ms -Iteration 2: 1.182 ops/ms -Iteration 3: 1.183 ops/ms -Iteration 4: 1.182 ops/ms -Iteration 5: 1.182 ops/ms -Iteration 6: 1.183 ops/ms -Iteration 7: 1.182 ops/ms -Iteration 8: 1.182 ops/ms -Iteration 9: 1.182 ops/ms -Iteration 10: 1.182 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.beanArrayDeser": - 1.184 ±(99.9%) 0.005 ops/ms [Average] - (min, avg, max) = (1.166, 1.184, 1.199), stdev = 0.010 - CI (99.9%): [1.179, 1.189] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.beanArraySer - -# Run progress: 71.43% complete, ETA 03:20:35 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.990 ops/ms -# Warmup Iteration 2: 2.045 ops/ms -# Warmup Iteration 3: 2.045 ops/ms -# Warmup Iteration 4: 2.046 ops/ms -# Warmup Iteration 5: 2.046 ops/ms -# Warmup Iteration 6: 2.047 ops/ms -# Warmup Iteration 7: 2.047 ops/ms -# Warmup Iteration 8: 2.047 ops/ms -# Warmup Iteration 9: 2.046 ops/ms -# Warmup Iteration 10: 2.047 ops/ms -Iteration 1: 2.047 ops/ms -Iteration 2: 2.047 ops/ms -Iteration 3: 2.047 ops/ms -Iteration 4: 2.047 ops/ms -Iteration 5: 2.047 ops/ms -Iteration 6: 2.047 ops/ms -Iteration 7: 2.037 ops/ms -Iteration 8: 2.037 ops/ms -Iteration 9: 2.038 ops/ms -Iteration 10: 2.037 ops/ms - -# Run progress: 72.38% complete, ETA 03:13:53 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.957 ops/ms -# Warmup Iteration 2: 2.012 ops/ms -# Warmup Iteration 3: 2.012 ops/ms -# Warmup Iteration 4: 2.013 ops/ms -# Warmup Iteration 5: 2.013 ops/ms -# Warmup Iteration 6: 2.014 ops/ms -# Warmup Iteration 7: 2.007 ops/ms -# Warmup Iteration 8: 2.006 ops/ms -# Warmup Iteration 9: 2.006 ops/ms -# Warmup Iteration 10: 2.007 ops/ms -Iteration 1: 2.007 ops/ms -Iteration 2: 2.005 ops/ms -Iteration 3: 2.007 ops/ms -Iteration 4: 2.007 ops/ms -Iteration 5: 2.007 ops/ms -Iteration 6: 2.003 ops/ms -Iteration 7: 2.016 ops/ms -Iteration 8: 2.016 ops/ms -Iteration 9: 2.016 ops/ms -Iteration 10: 2.016 ops/ms - -# Run progress: 73.33% complete, ETA 03:07:12 -# Fork: 3 of 5 -# Warmup Iteration 1: 2.017 ops/ms -# Warmup Iteration 2: 2.076 ops/ms -# Warmup Iteration 3: 2.077 ops/ms -# Warmup Iteration 4: 2.080 ops/ms -# Warmup Iteration 5: 2.081 ops/ms -# Warmup Iteration 6: 2.082 ops/ms -# Warmup Iteration 7: 2.082 ops/ms -# Warmup Iteration 8: 2.082 ops/ms -# Warmup Iteration 9: 2.082 ops/ms -# Warmup Iteration 10: 2.083 ops/ms -Iteration 1: 2.082 ops/ms -Iteration 2: 2.082 ops/ms -Iteration 3: 2.083 ops/ms -Iteration 4: 2.082 ops/ms -Iteration 5: 2.082 ops/ms -Iteration 6: 2.082 ops/ms -Iteration 7: 2.083 ops/ms -Iteration 8: 2.084 ops/ms -Iteration 9: 2.084 ops/ms -Iteration 10: 2.084 ops/ms - -# Run progress: 74.29% complete, ETA 03:00:31 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.948 ops/ms -# Warmup Iteration 2: 2.008 ops/ms -# Warmup Iteration 3: 2.008 ops/ms -# Warmup Iteration 4: 2.005 ops/ms -# Warmup Iteration 5: 2.008 ops/ms -# Warmup Iteration 6: 2.008 ops/ms -# Warmup Iteration 7: 2.011 ops/ms -# Warmup Iteration 8: 2.012 ops/ms -# Warmup Iteration 9: 2.011 ops/ms -# Warmup Iteration 10: 2.010 ops/ms -Iteration 1: 2.011 ops/ms -Iteration 2: 2.011 ops/ms -Iteration 3: 2.011 ops/ms -Iteration 4: 2.011 ops/ms -Iteration 5: 2.011 ops/ms -Iteration 6: 2.011 ops/ms -Iteration 7: 2.007 ops/ms -Iteration 8: 2.006 ops/ms -Iteration 9: 2.006 ops/ms -Iteration 10: 2.006 ops/ms - -# Run progress: 75.24% complete, ETA 02:53:50 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.989 ops/ms -# Warmup Iteration 2: 2.057 ops/ms -# Warmup Iteration 3: 2.057 ops/ms -# Warmup Iteration 4: 2.058 ops/ms -# Warmup Iteration 5: 2.059 ops/ms -# Warmup Iteration 6: 2.058 ops/ms -# Warmup Iteration 7: 2.058 ops/ms -# Warmup Iteration 8: 2.058 ops/ms -# Warmup Iteration 9: 2.058 ops/ms -# Warmup Iteration 10: 2.057 ops/ms -Iteration 1: 2.057 ops/ms -Iteration 2: 2.059 ops/ms -Iteration 3: 2.059 ops/ms -Iteration 4: 2.058 ops/ms -Iteration 5: 2.058 ops/ms -Iteration 6: 2.058 ops/ms -Iteration 7: 2.058 ops/ms -Iteration 8: 2.059 ops/ms -Iteration 9: 2.059 ops/ms -Iteration 10: 2.058 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.beanArraySer": - 2.041 ±(99.9%) 0.014 ops/ms [Average] - (min, avg, max) = (2.003, 2.041, 2.084), stdev = 0.029 - CI (99.9%): [2.026, 2.055] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemDeser - -# Run progress: 76.19% complete, ETA 02:47:09 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.021 ops/ms -# Warmup Iteration 2: 1.060 ops/ms -# Warmup Iteration 3: 1.061 ops/ms -# Warmup Iteration 4: 1.061 ops/ms -# Warmup Iteration 5: 1.061 ops/ms -# Warmup Iteration 6: 1.060 ops/ms -# Warmup Iteration 7: 1.061 ops/ms -# Warmup Iteration 8: 1.062 ops/ms -# Warmup Iteration 9: 1.061 ops/ms -# Warmup Iteration 10: 1.061 ops/ms -Iteration 1: 1.062 ops/ms -Iteration 2: 1.062 ops/ms -Iteration 3: 1.062 ops/ms -Iteration 4: 1.062 ops/ms -Iteration 5: 1.062 ops/ms -Iteration 6: 1.062 ops/ms -Iteration 7: 1.062 ops/ms -Iteration 8: 1.062 ops/ms -Iteration 9: 1.062 ops/ms -Iteration 10: 1.062 ops/ms - -# Run progress: 77.14% complete, ETA 02:40:28 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.007 ops/ms -# Warmup Iteration 2: 1.048 ops/ms -# Warmup Iteration 3: 1.048 ops/ms -# Warmup Iteration 4: 1.047 ops/ms -# Warmup Iteration 5: 1.048 ops/ms -# Warmup Iteration 6: 1.047 ops/ms -# Warmup Iteration 7: 1.048 ops/ms -# Warmup Iteration 8: 1.048 ops/ms -# Warmup Iteration 9: 1.048 ops/ms -# Warmup Iteration 10: 1.047 ops/ms -Iteration 1: 1.048 ops/ms -Iteration 2: 1.048 ops/ms -Iteration 3: 1.048 ops/ms -Iteration 4: 1.048 ops/ms -Iteration 5: 1.048 ops/ms -Iteration 6: 1.048 ops/ms -Iteration 7: 1.048 ops/ms -Iteration 8: 1.048 ops/ms -Iteration 9: 1.048 ops/ms -Iteration 10: 1.048 ops/ms - -# Run progress: 78.10% complete, ETA 02:33:47 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.036 ops/ms -# Warmup Iteration 2: 1.088 ops/ms -# Warmup Iteration 3: 1.088 ops/ms -# Warmup Iteration 4: 1.088 ops/ms -# Warmup Iteration 5: 1.088 ops/ms -# Warmup Iteration 6: 1.082 ops/ms -# Warmup Iteration 7: 1.089 ops/ms -# Warmup Iteration 8: 1.089 ops/ms -# Warmup Iteration 9: 1.089 ops/ms -# Warmup Iteration 10: 1.089 ops/ms -Iteration 1: 1.089 ops/ms -Iteration 2: 1.089 ops/ms -Iteration 3: 1.089 ops/ms -Iteration 4: 1.089 ops/ms -Iteration 5: 1.089 ops/ms -Iteration 6: 1.090 ops/ms -Iteration 7: 1.089 ops/ms -Iteration 8: 1.089 ops/ms -Iteration 9: 1.088 ops/ms -Iteration 10: 1.089 ops/ms - -# Run progress: 79.05% complete, ETA 02:27:05 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.026 ops/ms -# Warmup Iteration 2: 1.065 ops/ms -# Warmup Iteration 3: 1.065 ops/ms -# Warmup Iteration 4: 1.065 ops/ms -# Warmup Iteration 5: 1.065 ops/ms -# Warmup Iteration 6: 1.062 ops/ms -# Warmup Iteration 7: 1.064 ops/ms -# Warmup Iteration 8: 1.064 ops/ms -# Warmup Iteration 9: 1.064 ops/ms -# Warmup Iteration 10: 1.064 ops/ms -Iteration 1: 1.066 ops/ms -Iteration 2: 1.066 ops/ms -Iteration 3: 1.066 ops/ms -Iteration 4: 1.065 ops/ms -Iteration 5: 1.065 ops/ms -Iteration 6: 1.065 ops/ms -Iteration 7: 1.066 ops/ms -Iteration 8: 1.065 ops/ms -Iteration 9: 1.065 ops/ms -Iteration 10: 1.066 ops/ms - -# Run progress: 80.00% complete, ETA 02:20:24 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.044 ops/ms -# Warmup Iteration 2: 1.084 ops/ms -# Warmup Iteration 3: 1.084 ops/ms -# Warmup Iteration 4: 1.083 ops/ms -# Warmup Iteration 5: 1.084 ops/ms -# Warmup Iteration 6: 1.082 ops/ms -# Warmup Iteration 7: 1.084 ops/ms -# Warmup Iteration 8: 1.083 ops/ms -# Warmup Iteration 9: 1.084 ops/ms -# Warmup Iteration 10: 1.084 ops/ms -Iteration 1: 1.085 ops/ms -Iteration 2: 1.085 ops/ms -Iteration 3: 1.085 ops/ms -Iteration 4: 1.085 ops/ms -Iteration 5: 1.085 ops/ms -Iteration 6: 1.084 ops/ms -Iteration 7: 1.089 ops/ms -Iteration 8: 1.089 ops/ms -Iteration 9: 1.089 ops/ms -Iteration 10: 1.089 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemDeser": - 1.070 ±(99.9%) 0.008 ops/ms [Average] - (min, avg, max) = (1.048, 1.070, 1.090), stdev = 0.016 - CI (99.9%): [1.062, 1.078] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemSer - -# Run progress: 80.95% complete, ETA 02:13:43 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.956 ops/ms -# Warmup Iteration 2: 2.010 ops/ms -# Warmup Iteration 3: 2.019 ops/ms -# Warmup Iteration 4: 2.021 ops/ms -# Warmup Iteration 5: 2.023 ops/ms -# Warmup Iteration 6: 2.018 ops/ms -# Warmup Iteration 7: 2.019 ops/ms -# Warmup Iteration 8: 2.018 ops/ms -# Warmup Iteration 9: 2.017 ops/ms -# Warmup Iteration 10: 2.018 ops/ms -Iteration 1: 2.018 ops/ms -Iteration 2: 2.019 ops/ms -Iteration 3: 2.018 ops/ms -Iteration 4: 2.017 ops/ms -Iteration 5: 2.018 ops/ms -Iteration 6: 2.016 ops/ms -Iteration 7: 2.007 ops/ms -Iteration 8: 2.007 ops/ms -Iteration 9: 2.006 ops/ms -Iteration 10: 2.007 ops/ms - -# Run progress: 81.90% complete, ETA 02:07:02 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.915 ops/ms -# Warmup Iteration 2: 1.976 ops/ms -# Warmup Iteration 3: 1.973 ops/ms -# Warmup Iteration 4: 1.965 ops/ms -# Warmup Iteration 5: 1.970 ops/ms -# Warmup Iteration 6: 1.971 ops/ms -# Warmup Iteration 7: 1.972 ops/ms -# Warmup Iteration 8: 1.970 ops/ms -# Warmup Iteration 9: 1.970 ops/ms -# Warmup Iteration 10: 1.972 ops/ms -Iteration 1: 1.973 ops/ms -Iteration 2: 1.972 ops/ms -Iteration 3: 1.972 ops/ms -Iteration 4: 1.972 ops/ms -Iteration 5: 1.972 ops/ms -Iteration 6: 1.973 ops/ms -Iteration 7: 1.982 ops/ms -Iteration 8: 1.982 ops/ms -Iteration 9: 1.980 ops/ms -Iteration 10: 1.982 ops/ms - -# Run progress: 82.86% complete, ETA 02:00:21 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.873 ops/ms -# Warmup Iteration 2: 1.906 ops/ms -# Warmup Iteration 3: 1.924 ops/ms -# Warmup Iteration 4: 1.924 ops/ms -# Warmup Iteration 5: 1.921 ops/ms -# Warmup Iteration 6: 1.925 ops/ms -# Warmup Iteration 7: 1.927 ops/ms -# Warmup Iteration 8: 1.929 ops/ms -# Warmup Iteration 9: 1.928 ops/ms -# Warmup Iteration 10: 1.929 ops/ms -Iteration 1: 1.926 ops/ms -Iteration 2: 1.926 ops/ms -Iteration 3: 1.929 ops/ms -Iteration 4: 1.929 ops/ms -Iteration 5: 1.929 ops/ms -Iteration 6: 1.929 ops/ms -Iteration 7: 1.920 ops/ms -Iteration 8: 1.920 ops/ms -Iteration 9: 1.921 ops/ms -Iteration 10: 1.920 ops/ms - -# Run progress: 83.81% complete, ETA 01:53:40 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.895 ops/ms -# Warmup Iteration 2: 1.920 ops/ms -# Warmup Iteration 3: 1.920 ops/ms -# Warmup Iteration 4: 1.932 ops/ms -# Warmup Iteration 5: 1.937 ops/ms -# Warmup Iteration 6: 1.934 ops/ms -# Warmup Iteration 7: 1.932 ops/ms -# Warmup Iteration 8: 1.930 ops/ms -# Warmup Iteration 9: 1.930 ops/ms -# Warmup Iteration 10: 1.930 ops/ms -Iteration 1: 1.931 ops/ms -Iteration 2: 1.930 ops/ms -Iteration 3: 1.930 ops/ms -Iteration 4: 1.931 ops/ms -Iteration 5: 1.932 ops/ms -Iteration 6: 1.930 ops/ms -Iteration 7: 1.926 ops/ms -Iteration 8: 1.927 ops/ms -Iteration 9: 1.928 ops/ms -Iteration 10: 1.929 ops/ms - -# Run progress: 84.76% complete, ETA 01:46:58 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.859 ops/ms -# Warmup Iteration 2: 1.900 ops/ms -# Warmup Iteration 3: 1.900 ops/ms -# Warmup Iteration 4: 1.894 ops/ms -# Warmup Iteration 5: 1.904 ops/ms -# Warmup Iteration 6: 1.895 ops/ms -# Warmup Iteration 7: 1.898 ops/ms -# Warmup Iteration 8: 1.897 ops/ms -# Warmup Iteration 9: 1.898 ops/ms -# Warmup Iteration 10: 1.899 ops/ms -Iteration 1: 1.898 ops/ms -Iteration 2: 1.897 ops/ms -Iteration 3: 1.898 ops/ms -Iteration 4: 1.899 ops/ms -Iteration 5: 1.898 ops/ms -Iteration 6: 1.898 ops/ms -Iteration 7: 1.906 ops/ms -Iteration 8: 1.907 ops/ms -Iteration 9: 1.907 ops/ms -Iteration 10: 1.907 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicBeanItemSer": - 1.949 ±(99.9%) 0.020 ops/ms [Average] - (min, avg, max) = (1.897, 1.949, 2.019), stdev = 0.041 - CI (99.9%): [1.929, 1.969] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemDeser - -# Run progress: 85.71% complete, ETA 01:40:17 -# Fork: 1 of 5 -# Warmup Iteration 1: 307.076 ops/ms -# Warmup Iteration 2: 320.514 ops/ms -# Warmup Iteration 3: 319.693 ops/ms -# Warmup Iteration 4: 319.831 ops/ms -# Warmup Iteration 5: 319.237 ops/ms -# Warmup Iteration 6: 319.485 ops/ms -# Warmup Iteration 7: 319.655 ops/ms -# Warmup Iteration 8: 319.951 ops/ms -# Warmup Iteration 9: 319.970 ops/ms -# Warmup Iteration 10: 319.948 ops/ms -Iteration 1: 319.879 ops/ms -Iteration 2: 319.929 ops/ms -Iteration 3: 319.821 ops/ms -Iteration 4: 319.920 ops/ms -Iteration 5: 319.646 ops/ms -Iteration 6: 319.848 ops/ms -Iteration 7: 319.331 ops/ms -Iteration 8: 319.545 ops/ms -Iteration 9: 319.567 ops/ms -Iteration 10: 319.362 ops/ms - -# Run progress: 86.67% complete, ETA 01:33:36 -# Fork: 2 of 5 -# Warmup Iteration 1: 309.629 ops/ms -# Warmup Iteration 2: 322.616 ops/ms -# Warmup Iteration 3: 323.571 ops/ms -# Warmup Iteration 4: 323.505 ops/ms -# Warmup Iteration 5: 322.865 ops/ms -# Warmup Iteration 6: 323.349 ops/ms -# Warmup Iteration 7: 323.207 ops/ms -# Warmup Iteration 8: 323.478 ops/ms -# Warmup Iteration 9: 323.068 ops/ms -# Warmup Iteration 10: 323.566 ops/ms -Iteration 1: 323.341 ops/ms -Iteration 2: 323.430 ops/ms -Iteration 3: 323.300 ops/ms -Iteration 4: 323.341 ops/ms -Iteration 5: 323.077 ops/ms -Iteration 6: 323.379 ops/ms -Iteration 7: 321.567 ops/ms -Iteration 8: 321.919 ops/ms -Iteration 9: 321.842 ops/ms -Iteration 10: 321.934 ops/ms - -# Run progress: 87.62% complete, ETA 01:26:55 -# Fork: 3 of 5 -# Warmup Iteration 1: 303.332 ops/ms -# Warmup Iteration 2: 317.430 ops/ms -# Warmup Iteration 3: 317.701 ops/ms -# Warmup Iteration 4: 317.709 ops/ms -# Warmup Iteration 5: 318.044 ops/ms -# Warmup Iteration 6: 317.705 ops/ms -# Warmup Iteration 7: 317.675 ops/ms -# Warmup Iteration 8: 318.006 ops/ms -# Warmup Iteration 9: 317.763 ops/ms -# Warmup Iteration 10: 317.847 ops/ms -Iteration 1: 317.761 ops/ms -Iteration 2: 317.534 ops/ms -Iteration 3: 317.842 ops/ms -Iteration 4: 317.867 ops/ms -Iteration 5: 317.812 ops/ms -Iteration 6: 317.637 ops/ms -Iteration 7: 322.327 ops/ms -Iteration 8: 322.548 ops/ms -Iteration 9: 322.095 ops/ms -Iteration 10: 322.222 ops/ms - -# Run progress: 88.57% complete, ETA 01:20:14 -# Fork: 4 of 5 -# Warmup Iteration 1: 320.327 ops/ms -# Warmup Iteration 2: 337.747 ops/ms -# Warmup Iteration 3: 338.104 ops/ms -# Warmup Iteration 4: 338.374 ops/ms -# Warmup Iteration 5: 338.032 ops/ms -# Warmup Iteration 6: 338.216 ops/ms -# Warmup Iteration 7: 338.101 ops/ms -# Warmup Iteration 8: 338.265 ops/ms -# Warmup Iteration 9: 338.511 ops/ms -# Warmup Iteration 10: 338.356 ops/ms -Iteration 1: 338.195 ops/ms -Iteration 2: 338.283 ops/ms -Iteration 3: 338.456 ops/ms -Iteration 4: 338.282 ops/ms -Iteration 5: 338.314 ops/ms -Iteration 6: 338.311 ops/ms -Iteration 7: 339.568 ops/ms -Iteration 8: 339.515 ops/ms -Iteration 9: 339.438 ops/ms -Iteration 10: 339.442 ops/ms - -# Run progress: 89.52% complete, ETA 01:13:32 -# Fork: 5 of 5 -# Warmup Iteration 1: 312.903 ops/ms -# Warmup Iteration 2: 325.678 ops/ms -# Warmup Iteration 3: 326.211 ops/ms -# Warmup Iteration 4: 326.388 ops/ms -# Warmup Iteration 5: 326.058 ops/ms -# Warmup Iteration 6: 326.327 ops/ms -# Warmup Iteration 7: 326.052 ops/ms -# Warmup Iteration 8: 326.301 ops/ms -# Warmup Iteration 9: 326.313 ops/ms -# Warmup Iteration 10: 326.341 ops/ms -Iteration 1: 326.231 ops/ms -Iteration 2: 326.195 ops/ms -Iteration 3: 326.159 ops/ms -Iteration 4: 326.269 ops/ms -Iteration 5: 326.270 ops/ms -Iteration 6: 302.513 ops/ms -Iteration 7: 326.429 ops/ms -Iteration 8: 325.975 ops/ms -Iteration 9: 326.295 ops/ms -Iteration 10: 326.089 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemDeser": - 324.917 ±(99.9%) 3.942 ops/ms [Average] - (min, avg, max) = (302.513, 324.917, 339.568), stdev = 7.963 - CI (99.9%): [320.975, 328.859] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemSer - -# Run progress: 90.48% complete, ETA 01:06:51 -# Fork: 1 of 5 -# Warmup Iteration 1: 539.407 ops/ms -# Warmup Iteration 2: 542.896 ops/ms -# Warmup Iteration 3: 544.085 ops/ms -# Warmup Iteration 4: 543.990 ops/ms -# Warmup Iteration 5: 543.925 ops/ms -# Warmup Iteration 6: 543.329 ops/ms -# Warmup Iteration 7: 544.025 ops/ms -# Warmup Iteration 8: 543.787 ops/ms -# Warmup Iteration 9: 543.440 ops/ms -# Warmup Iteration 10: 543.892 ops/ms -Iteration 1: 544.268 ops/ms -Iteration 2: 544.181 ops/ms -Iteration 3: 543.758 ops/ms -Iteration 4: 544.266 ops/ms -Iteration 5: 544.294 ops/ms -Iteration 6: 543.711 ops/ms -Iteration 7: 541.672 ops/ms -Iteration 8: 542.018 ops/ms -Iteration 9: 542.206 ops/ms -Iteration 10: 542.150 ops/ms - -# Run progress: 91.43% complete, ETA 01:00:10 -# Fork: 2 of 5 -# Warmup Iteration 1: 551.494 ops/ms -# Warmup Iteration 2: 560.189 ops/ms -# Warmup Iteration 3: 562.946 ops/ms -# Warmup Iteration 4: 561.429 ops/ms -# Warmup Iteration 5: 562.786 ops/ms -# Warmup Iteration 6: 561.624 ops/ms -# Warmup Iteration 7: 562.618 ops/ms -# Warmup Iteration 8: 562.230 ops/ms -# Warmup Iteration 9: 562.240 ops/ms -# Warmup Iteration 10: 561.966 ops/ms -Iteration 1: 562.113 ops/ms -Iteration 2: 562.443 ops/ms -Iteration 3: 563.336 ops/ms -Iteration 4: 562.691 ops/ms -Iteration 5: 563.134 ops/ms -Iteration 6: 562.152 ops/ms -Iteration 7: 557.290 ops/ms -Iteration 8: 556.683 ops/ms -Iteration 9: 556.708 ops/ms -Iteration 10: 556.262 ops/ms - -# Run progress: 92.38% complete, ETA 00:53:29 -# Fork: 3 of 5 -# Warmup Iteration 1: 557.204 ops/ms -# Warmup Iteration 2: 576.852 ops/ms -# Warmup Iteration 3: 570.713 ops/ms -# Warmup Iteration 4: 570.709 ops/ms -# Warmup Iteration 5: 570.457 ops/ms -# Warmup Iteration 6: 570.508 ops/ms -# Warmup Iteration 7: 570.360 ops/ms -# Warmup Iteration 8: 570.504 ops/ms -# Warmup Iteration 9: 570.057 ops/ms -# Warmup Iteration 10: 569.930 ops/ms -Iteration 1: 570.497 ops/ms -Iteration 2: 570.700 ops/ms -Iteration 3: 570.959 ops/ms -Iteration 4: 571.111 ops/ms -Iteration 5: 570.818 ops/ms -Iteration 6: 570.567 ops/ms -Iteration 7: 576.793 ops/ms -Iteration 8: 576.430 ops/ms -Iteration 9: 576.307 ops/ms -Iteration 10: 576.468 ops/ms - -# Run progress: 93.33% complete, ETA 00:46:48 -# Fork: 4 of 5 -# Warmup Iteration 1: 567.322 ops/ms -# Warmup Iteration 2: 585.424 ops/ms -# Warmup Iteration 3: 582.046 ops/ms -# Warmup Iteration 4: 582.700 ops/ms -# Warmup Iteration 5: 584.687 ops/ms -# Warmup Iteration 6: 582.838 ops/ms -# Warmup Iteration 7: 585.508 ops/ms -# Warmup Iteration 8: 581.788 ops/ms -# Warmup Iteration 9: 583.876 ops/ms -# Warmup Iteration 10: 583.505 ops/ms -Iteration 1: 583.863 ops/ms -Iteration 2: 583.229 ops/ms -Iteration 3: 582.765 ops/ms -Iteration 4: 583.484 ops/ms -Iteration 5: 583.840 ops/ms -Iteration 6: 583.558 ops/ms -Iteration 7: 584.770 ops/ms -Iteration 8: 584.107 ops/ms -Iteration 9: 584.604 ops/ms -Iteration 10: 583.438 ops/ms - -# Run progress: 94.29% complete, ETA 00:40:06 -# Fork: 5 of 5 -# Warmup Iteration 1: 552.230 ops/ms -# Warmup Iteration 2: 557.255 ops/ms -# Warmup Iteration 3: 559.884 ops/ms -# Warmup Iteration 4: 560.204 ops/ms -# Warmup Iteration 5: 559.436 ops/ms -# Warmup Iteration 6: 558.909 ops/ms -# Warmup Iteration 7: 559.537 ops/ms -# Warmup Iteration 8: 559.250 ops/ms -# Warmup Iteration 9: 559.918 ops/ms -# Warmup Iteration 10: 559.781 ops/ms -Iteration 1: 559.995 ops/ms -Iteration 2: 559.292 ops/ms -Iteration 3: 560.061 ops/ms -Iteration 4: 559.463 ops/ms -Iteration 5: 559.969 ops/ms -Iteration 6: 559.677 ops/ms -Iteration 7: 560.088 ops/ms -Iteration 8: 560.808 ops/ms -Iteration 9: 559.997 ops/ms -Iteration 10: 561.092 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.classicMediaItemSer": - 564.082 ±(99.9%) 6.896 ops/ms [Average] - (min, avg, max) = (541.672, 564.082, 584.770), stdev = 13.931 - CI (99.9%): [557.185, 570.978] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: 10 iterations, 10 s each -# Measurement: 10 iterations, 30 s each -# Timeout: 10 min per iteration -# Threads: 1 thread, will synchronize iterations -# Benchmark mode: Throughput, ops/time -# Benchmark: com.fasterxml.jackson.module.blackbird.Vanilla.constructorArrayDeser - -# Run progress: 95.24% complete, ETA 00:33:25 -# Fork: 1 of 5 -# Warmup Iteration 1: 1.040 ops/ms -# Warmup Iteration 2: 1.101 ops/ms -# Warmup Iteration 3: 1.102 ops/ms -# Warmup Iteration 4: 1.102 ops/ms -# Warmup Iteration 5: 1.101 ops/ms -# Warmup Iteration 6: 1.098 ops/ms -# Warmup Iteration 7: 1.102 ops/ms -# Warmup Iteration 8: 1.102 ops/ms -# Warmup Iteration 9: 1.101 ops/ms -# Warmup Iteration 10: 1.097 ops/ms -Iteration 1: 1.102 ops/ms -Iteration 2: 1.102 ops/ms -Iteration 3: 1.102 ops/ms -Iteration 4: 1.102 ops/ms -Iteration 5: 1.102 ops/ms -Iteration 6: 1.102 ops/ms -Iteration 7: 1.102 ops/ms -Iteration 8: 1.101 ops/ms -Iteration 9: 1.102 ops/ms -Iteration 10: 1.102 ops/ms - -# Run progress: 96.19% complete, ETA 00:26:44 -# Fork: 2 of 5 -# Warmup Iteration 1: 1.054 ops/ms -# Warmup Iteration 2: 1.119 ops/ms -# Warmup Iteration 3: 1.120 ops/ms -# Warmup Iteration 4: 1.120 ops/ms -# Warmup Iteration 5: 1.120 ops/ms -# Warmup Iteration 6: 1.118 ops/ms -# Warmup Iteration 7: 1.121 ops/ms -# Warmup Iteration 8: 1.120 ops/ms -# Warmup Iteration 9: 1.121 ops/ms -# Warmup Iteration 10: 1.120 ops/ms -Iteration 1: 1.120 ops/ms -Iteration 2: 1.120 ops/ms -Iteration 3: 1.121 ops/ms -Iteration 4: 1.120 ops/ms -Iteration 5: 1.119 ops/ms -Iteration 6: 1.120 ops/ms -Iteration 7: 1.121 ops/ms -Iteration 8: 1.122 ops/ms -Iteration 9: 1.122 ops/ms -Iteration 10: 1.122 ops/ms - -# Run progress: 97.14% complete, ETA 00:20:03 -# Fork: 3 of 5 -# Warmup Iteration 1: 1.060 ops/ms -# Warmup Iteration 2: 1.116 ops/ms -# Warmup Iteration 3: 1.116 ops/ms -# Warmup Iteration 4: 1.117 ops/ms -# Warmup Iteration 5: 1.115 ops/ms -# Warmup Iteration 6: 1.112 ops/ms -# Warmup Iteration 7: 1.116 ops/ms -# Warmup Iteration 8: 1.115 ops/ms -# Warmup Iteration 9: 1.116 ops/ms -# Warmup Iteration 10: 1.112 ops/ms -Iteration 1: 1.116 ops/ms -Iteration 2: 1.116 ops/ms -Iteration 3: 1.116 ops/ms -Iteration 4: 1.116 ops/ms -Iteration 5: 1.116 ops/ms -Iteration 6: 1.115 ops/ms -Iteration 7: 1.115 ops/ms -Iteration 8: 1.115 ops/ms -Iteration 9: 1.115 ops/ms -Iteration 10: 1.115 ops/ms - -# Run progress: 98.10% complete, ETA 00:13:22 -# Fork: 4 of 5 -# Warmup Iteration 1: 1.062 ops/ms -# Warmup Iteration 2: 1.110 ops/ms -# Warmup Iteration 3: 1.111 ops/ms -# Warmup Iteration 4: 1.110 ops/ms -# Warmup Iteration 5: 1.111 ops/ms -# Warmup Iteration 6: 1.106 ops/ms -# Warmup Iteration 7: 1.115 ops/ms -# Warmup Iteration 8: 1.115 ops/ms -# Warmup Iteration 9: 1.115 ops/ms -# Warmup Iteration 10: 1.112 ops/ms -Iteration 1: 1.116 ops/ms -Iteration 2: 1.115 ops/ms -Iteration 3: 1.115 ops/ms -Iteration 4: 1.116 ops/ms -Iteration 5: 1.116 ops/ms -Iteration 6: 1.114 ops/ms -Iteration 7: 1.118 ops/ms -Iteration 8: 1.118 ops/ms -Iteration 9: 1.118 ops/ms -Iteration 10: 1.117 ops/ms - -# Run progress: 99.05% complete, ETA 00:06:41 -# Fork: 5 of 5 -# Warmup Iteration 1: 1.061 ops/ms -# Warmup Iteration 2: 1.114 ops/ms -# Warmup Iteration 3: 1.115 ops/ms -# Warmup Iteration 4: 1.114 ops/ms -# Warmup Iteration 5: 1.114 ops/ms -# Warmup Iteration 6: 1.107 ops/ms -# Warmup Iteration 7: 1.114 ops/ms -# Warmup Iteration 8: 1.114 ops/ms -# Warmup Iteration 9: 1.114 ops/ms -# Warmup Iteration 10: 1.111 ops/ms -Iteration 1: 1.114 ops/ms -Iteration 2: 1.114 ops/ms -Iteration 3: 1.114 ops/ms -Iteration 4: 1.115 ops/ms -Iteration 5: 1.114 ops/ms -Iteration 6: 1.114 ops/ms -Iteration 7: 1.115 ops/ms -Iteration 8: 1.115 ops/ms -Iteration 9: 1.114 ops/ms -Iteration 10: 1.114 ops/ms - - -Result "com.fasterxml.jackson.module.blackbird.Vanilla.constructorArrayDeser": - 1.114 ±(99.9%) 0.003 ops/ms [Average] - (min, avg, max) = (1.101, 1.114, 1.122), stdev = 0.006 - CI (99.9%): [1.111, 1.117] (assumes normal distribution) - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: -# Measurement: 1 iterations, single-shot each -# Timeout: 10 min per iteration -# Threads: 1 thread -# Benchmark mode: Single shot invocation time -# Benchmark: com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.afterburner - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 1 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -314.001 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 2 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -307.692 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 3 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -284.860 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 4 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -315.952 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 5 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -312.115 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 6 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -292.334 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 7 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -286.988 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 8 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -257.177 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 9 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -307.035 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 10 of 10 -Iteration 1: WARNING: An illegal reflective access operation has occurred -WARNING: Illegal reflective access by com.fasterxml.jackson.module.afterburner.util.MyClassLoader (file:/home/steven/code/jackson-modules-base/blackbird/benchmarks/target/benchmarks.jar) to method java.lang.ClassLoader.findLoadedClass(java.lang.String) -WARNING: Please consider reporting this to the maintainers of com.fasterxml.jackson.module.afterburner.util.MyClassLoader -WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations -WARNING: All illegal access operations will be denied in a future release -287.438 ms/op - - -Result "com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.afterburner": - N = 10 - mean = 296.559 ±(99.9%) 27.770 ms/op - - Histogram, ms/op: - [250.000, 255.000) = 0 - [255.000, 260.000) = 1 - [260.000, 265.000) = 0 - [265.000, 270.000) = 0 - [270.000, 275.000) = 0 - [275.000, 280.000) = 0 - [280.000, 285.000) = 1 - [285.000, 290.000) = 2 - [290.000, 295.000) = 1 - [295.000, 300.000) = 0 - [300.000, 305.000) = 0 - [305.000, 310.000) = 2 - [310.000, 315.000) = 2 - - Percentiles, ms/op: - p(0.0000) = 257.177 ms/op - p(50.0000) = 299.685 ms/op - p(90.0000) = 315.757 ms/op - p(95.0000) = 315.952 ms/op - p(99.0000) = 315.952 ms/op - p(99.9000) = 315.952 ms/op - p(99.9900) = 315.952 ms/op - p(99.9990) = 315.952 ms/op - p(99.9999) = 315.952 ms/op - p(100.0000) = 315.952 ms/op - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: -# Measurement: 1 iterations, single-shot each -# Timeout: 10 min per iteration -# Threads: 1 thread -# Benchmark mode: Single shot invocation time -# Benchmark: com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.blackbird - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 1 of 10 -Iteration 1: 265.623 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 2 of 10 -Iteration 1: 312.177 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 3 of 10 -Iteration 1: 292.352 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 4 of 10 -Iteration 1: 299.969 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 5 of 10 -Iteration 1: 267.998 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 6 of 10 -Iteration 1: 286.434 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 7 of 10 -Iteration 1: 291.120 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 8 of 10 -Iteration 1: 300.891 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 9 of 10 -Iteration 1: 320.951 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 10 of 10 -Iteration 1: 282.491 ms/op - - -Result "com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.blackbird": - N = 10 - mean = 292.001 ±(99.9%) 26.581 ms/op - - Histogram, ms/op: - [260.000, 265.000) = 0 - [265.000, 270.000) = 2 - [270.000, 275.000) = 0 - [275.000, 280.000) = 0 - [280.000, 285.000) = 1 - [285.000, 290.000) = 1 - [290.000, 295.000) = 2 - [295.000, 300.000) = 1 - [300.000, 305.000) = 1 - [305.000, 310.000) = 0 - [310.000, 315.000) = 1 - [315.000, 320.000) = 0 - [320.000, 325.000) = 1 - - Percentiles, ms/op: - p(0.0000) = 265.623 ms/op - p(50.0000) = 291.736 ms/op - p(90.0000) = 320.074 ms/op - p(95.0000) = 320.951 ms/op - p(99.0000) = 320.951 ms/op - p(99.9000) = 320.951 ms/op - p(99.9900) = 320.951 ms/op - p(99.9990) = 320.951 ms/op - p(99.9999) = 320.951 ms/op - p(100.0000) = 320.951 ms/op - - -# JMH version: 1.25 -# VM version: JDK 14.0.2, OpenJDK 64-Bit Server VM, 14.0.2+12 -# VM invoker: /usr/lib/jvm/java-14-openjdk-14.0.2.12-1.rolling.fc32.x86_64/bin/java -# VM options: -# Warmup: -# Measurement: 1 iterations, single-shot each -# Timeout: 10 min per iteration -# Threads: 1 thread -# Benchmark mode: Single shot invocation time -# Benchmark: com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.vanilla - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 1 of 10 -Iteration 1: 249.109 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 2 of 10 -Iteration 1: 268.947 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 3 of 10 -Iteration 1: 270.761 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 4 of 10 -Iteration 1: 241.265 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 5 of 10 -Iteration 1: 254.474 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 6 of 10 -Iteration 1: 261.242 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 7 of 10 -Iteration 1: 253.752 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 8 of 10 -Iteration 1: 260.014 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 9 of 10 -Iteration 1: 259.565 ms/op - -# Run progress: 100.00% complete, ETA 00:00:00 -# Fork: 10 of 10 -Iteration 1: 247.064 ms/op - - -Result "com.fasterxml.jackson.module.blackbird.StartupTimeBenchmark.vanilla": - N = 10 - mean = 256.619 ±(99.9%) 14.163 ms/op - - Histogram, ms/op: - [240.000, 242.500) = 1 - [242.500, 245.000) = 0 - [245.000, 247.500) = 1 - [247.500, 250.000) = 1 - [250.000, 252.500) = 0 - [252.500, 255.000) = 2 - [255.000, 257.500) = 0 - [257.500, 260.000) = 1 - [260.000, 262.500) = 2 - [262.500, 265.000) = 0 - [265.000, 267.500) = 0 - [267.500, 270.000) = 1 - [270.000, 272.500) = 1 - [272.500, 275.000) = 0 - [275.000, 277.500) = 0 - - Percentiles, ms/op: - p(0.0000) = 241.265 ms/op - p(50.0000) = 257.020 ms/op - p(90.0000) = 270.580 ms/op - p(95.0000) = 270.761 ms/op - p(99.0000) = 270.761 ms/op - p(99.9000) = 270.761 ms/op - p(99.9900) = 270.761 ms/op - p(99.9990) = 270.761 ms/op - p(99.9999) = 270.761 ms/op - p(100.0000) = 270.761 ms/op - - -# Run complete. Total time: 11:42:19 - -REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on -why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial -experiments, perform baseline and negative tests that provide experimental control, make sure -the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. -Do not assume the numbers tell you what you want them to tell. - -Benchmark Mode Cnt Score Error Units -Afterburner.beanArrayDeser thrpt 50 1.299 ± 0.009 ops/ms -Afterburner.beanArraySer thrpt 50 2.193 ± 0.010 ops/ms -Afterburner.classicBeanItemDeser thrpt 50 1.398 ± 0.024 ops/ms -Afterburner.classicBeanItemSer thrpt 50 2.325 ± 0.024 ops/ms -Afterburner.classicMediaItemDeser thrpt 50 403.335 ± 1.337 ops/ms -Afterburner.classicMediaItemSer thrpt 50 610.446 ± 1.996 ops/ms -Afterburner.constructorArrayDeser thrpt 50 1.124 ± 0.004 ops/ms -Blackbird.beanArrayDeser thrpt 50 1.301 ± 0.005 ops/ms -Blackbird.beanArraySer thrpt 50 2.115 ± 0.009 ops/ms -Blackbird.classicBeanItemDeser thrpt 50 1.250 ± 0.017 ops/ms -Blackbird.classicBeanItemSer thrpt 50 2.140 ± 0.017 ops/ms -Blackbird.classicMediaItemDeser thrpt 50 387.160 ± 1.507 ops/ms -Blackbird.classicMediaItemSer thrpt 50 575.804 ± 4.887 ops/ms -Blackbird.constructorArrayDeser thrpt 50 1.122 ± 0.004 ops/ms -Vanilla.beanArrayDeser thrpt 50 1.184 ± 0.005 ops/ms -Vanilla.beanArraySer thrpt 50 2.041 ± 0.014 ops/ms -Vanilla.classicBeanItemDeser thrpt 50 1.070 ± 0.008 ops/ms -Vanilla.classicBeanItemSer thrpt 50 1.949 ± 0.020 ops/ms -Vanilla.classicMediaItemDeser thrpt 50 324.917 ± 3.942 ops/ms -Vanilla.classicMediaItemSer thrpt 50 564.082 ± 6.896 ops/ms -Vanilla.constructorArrayDeser thrpt 50 1.114 ± 0.003 ops/ms -StartupTimeBenchmark.afterburner ss 10 296.559 ± 27.770 ms/op -StartupTimeBenchmark.blackbird ss 10 292.001 ± 26.581 ms/op -StartupTimeBenchmark.vanilla ss 10 256.619 ± 14.163 ms/op diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Afterburner.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Afterburner.java deleted file mode 100644 index 795503bb3..000000000 --- a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Afterburner.java +++ /dev/null @@ -1,24 +0,0 @@ -package tools.jackson.module.blackbird; - -import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.json.JsonMapper; -import tools.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -public class Afterburner extends BaseBenchmark { - public static void main(String[] args) throws RunnerException { - Options options = new OptionsBuilder() - .include(Afterburner.class.getSimpleName()) - .forks(1) - .build(); - new Runner(options).run(); - } - - @Override - protected ObjectMapper createObjectMapper() { - return JsonMapper.builder().addModule(new AfterburnerModule()).build(); - } -} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Blackbird.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Blackbird.java deleted file mode 100644 index f5af04ba5..000000000 --- a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Blackbird.java +++ /dev/null @@ -1,23 +0,0 @@ -package tools.jackson.module.blackbird; - -import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.json.JsonMapper; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -public class Blackbird extends BaseBenchmark { - public static void main(String[] args) throws RunnerException { - Options options = new OptionsBuilder() - .include(Blackbird.class.getSimpleName()) - .forks(1) - .build(); - new Runner(options).run(); - } - - @Override - protected ObjectMapper createObjectMapper() { - return JsonMapper.builder().addModule(new BlackbirdModule()).build(); - } -} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BuilderBenchmark.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BuilderBenchmark.java new file mode 100644 index 000000000..5f2d385d3 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BuilderBenchmark.java @@ -0,0 +1,55 @@ +package tools.jackson.module.blackbird; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.ObjectWriter; + +/** + * Builder-based bean: one {@link Invoice} with eight scalar properties. + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class BuilderBenchmark +{ + @Param({"stock", "blackbird"}) + public String mode; + + private ObjectReader reader; + private ObjectWriter writer; + private byte[] json; + + @Setup + public void setup() { + var mapper = Mappers.create(mode); + reader = mapper.readerFor(Invoice.class); + writer = mapper.writerFor(Invoice.class); + json = writer.writeValueAsBytes(Invoice.SAMPLE); + } + + @Benchmark + public Invoice builderDeser() { + return reader.readValue(json); + } + + @Benchmark + public byte[] builderSer() { + return writer.writeValueAsBytes(Invoice.SAMPLE); + } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdPojo.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdPojo.java new file mode 100644 index 000000000..c67e74f4f --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdPojo.java @@ -0,0 +1,34 @@ +package tools.jackson.module.blackbird; + +/** + * Properties of the setter beans in {@link ColdTypes}, with the same names + * and types as the components of the records there. + */ +public abstract class ColdPojo +{ + private String a; + private int b; + private long c; + private boolean d; + private String e; + private Integer f; + private double g; + private String h; + + public String getA() { return a; } + public void setA(String v) { a = v; } + public int getB() { return b; } + public void setB(int v) { b = v; } + public long getC() { return c; } + public void setC(long v) { c = v; } + public boolean isD() { return d; } + public void setD(boolean v) { d = v; } + public String getE() { return e; } + public void setE(String v) { e = v; } + public Integer getF() { return f; } + public void setF(Integer v) { f = v; } + public double getG() { return g; } + public void setG(double v) { g = v; } + public String getH() { return h; } + public void setH(String v) { h = v; } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdStartBenchmark.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdStartBenchmark.java new file mode 100644 index 000000000..db1e93a73 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdStartBenchmark.java @@ -0,0 +1,58 @@ +package tools.jackson.module.blackbird; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import tools.jackson.databind.ObjectMapper; + +/** + * First use of 500 distinct types ({@link ColdTypes}) in a fresh JVM: one + * read and one write of each type, which includes introspection and, with + * Blackbird, code generation. Divide the score by 500 for the cost per type. + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.SingleShotTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 0) +@Measurement(iterations = 1) +@Fork(20) +public class ColdStartBenchmark +{ + private static final byte[] JSON = ("{\"a\":\"s\",\"b\":7,\"c\":9,\"d\":true," + + "\"e\":\"t\",\"f\":3,\"g\":1.5,\"h\":\"u\"}").getBytes(); + + @Param({"stock", "blackbird"}) + public String mode; + + private ObjectMapper mapper; + private Class[] types; + + @Setup + public void setup() { + mapper = Mappers.create(mode); + types = ColdTypes.class.getClasses(); + Arrays.sort(types, Comparator.comparing(Class::getName)); + } + + @Benchmark + public void firstUse(Blackhole bh) { + for (Class type : types) { + Object value = mapper.readValue(JSON, type); + bh.consume(mapper.writeValueAsBytes(value)); + } + } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdTypes.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdTypes.java new file mode 100644 index 000000000..c11b021cc --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdTypes.java @@ -0,0 +1,513 @@ +package tools.jackson.module.blackbird; + +/** + * 250 records and 250 setter beans, each a distinct type for databind + * introspection and for Blackbird code generation. The beans share their + * properties through {@link ColdPojo}; the records declare their own. + */ +public final class ColdTypes +{ + private ColdTypes() { } + + public record R000(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R001(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R002(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R003(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R004(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R005(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R006(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R007(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R008(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R009(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R010(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R011(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R012(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R013(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R014(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R015(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R016(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R017(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R018(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R019(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R020(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R021(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R022(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R023(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R024(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R025(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R026(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R027(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R028(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R029(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R030(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R031(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R032(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R033(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R034(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R035(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R036(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R037(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R038(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R039(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R040(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R041(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R042(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R043(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R044(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R045(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R046(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R047(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R048(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R049(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R050(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R051(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R052(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R053(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R054(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R055(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R056(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R057(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R058(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R059(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R060(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R061(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R062(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R063(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R064(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R065(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R066(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R067(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R068(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R069(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R070(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R071(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R072(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R073(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R074(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R075(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R076(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R077(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R078(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R079(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R080(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R081(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R082(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R083(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R084(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R085(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R086(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R087(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R088(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R089(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R090(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R091(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R092(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R093(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R094(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R095(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R096(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R097(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R098(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R099(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R100(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R101(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R102(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R103(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R104(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R105(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R106(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R107(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R108(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R109(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R110(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R111(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R112(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R113(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R114(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R115(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R116(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R117(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R118(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R119(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R120(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R121(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R122(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R123(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R124(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R125(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R126(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R127(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R128(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R129(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R130(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R131(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R132(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R133(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R134(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R135(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R136(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R137(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R138(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R139(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R140(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R141(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R142(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R143(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R144(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R145(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R146(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R147(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R148(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R149(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R150(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R151(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R152(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R153(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R154(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R155(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R156(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R157(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R158(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R159(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R160(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R161(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R162(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R163(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R164(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R165(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R166(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R167(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R168(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R169(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R170(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R171(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R172(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R173(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R174(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R175(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R176(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R177(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R178(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R179(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R180(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R181(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R182(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R183(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R184(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R185(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R186(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R187(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R188(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R189(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R190(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R191(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R192(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R193(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R194(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R195(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R196(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R197(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R198(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R199(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R200(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R201(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R202(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R203(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R204(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R205(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R206(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R207(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R208(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R209(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R210(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R211(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R212(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R213(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R214(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R215(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R216(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R217(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R218(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R219(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R220(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R221(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R222(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R223(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R224(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R225(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R226(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R227(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R228(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R229(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R230(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R231(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R232(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R233(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R234(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R235(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R236(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R237(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R238(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R239(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R240(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R241(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R242(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R243(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R244(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R245(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R246(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R247(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R248(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + public record R249(String a, int b, long c, boolean d, String e, Integer f, double g, String h) { } + + public static class P000 extends ColdPojo { } + public static class P001 extends ColdPojo { } + public static class P002 extends ColdPojo { } + public static class P003 extends ColdPojo { } + public static class P004 extends ColdPojo { } + public static class P005 extends ColdPojo { } + public static class P006 extends ColdPojo { } + public static class P007 extends ColdPojo { } + public static class P008 extends ColdPojo { } + public static class P009 extends ColdPojo { } + public static class P010 extends ColdPojo { } + public static class P011 extends ColdPojo { } + public static class P012 extends ColdPojo { } + public static class P013 extends ColdPojo { } + public static class P014 extends ColdPojo { } + public static class P015 extends ColdPojo { } + public static class P016 extends ColdPojo { } + public static class P017 extends ColdPojo { } + public static class P018 extends ColdPojo { } + public static class P019 extends ColdPojo { } + public static class P020 extends ColdPojo { } + public static class P021 extends ColdPojo { } + public static class P022 extends ColdPojo { } + public static class P023 extends ColdPojo { } + public static class P024 extends ColdPojo { } + public static class P025 extends ColdPojo { } + public static class P026 extends ColdPojo { } + public static class P027 extends ColdPojo { } + public static class P028 extends ColdPojo { } + public static class P029 extends ColdPojo { } + public static class P030 extends ColdPojo { } + public static class P031 extends ColdPojo { } + public static class P032 extends ColdPojo { } + public static class P033 extends ColdPojo { } + public static class P034 extends ColdPojo { } + public static class P035 extends ColdPojo { } + public static class P036 extends ColdPojo { } + public static class P037 extends ColdPojo { } + public static class P038 extends ColdPojo { } + public static class P039 extends ColdPojo { } + public static class P040 extends ColdPojo { } + public static class P041 extends ColdPojo { } + public static class P042 extends ColdPojo { } + public static class P043 extends ColdPojo { } + public static class P044 extends ColdPojo { } + public static class P045 extends ColdPojo { } + public static class P046 extends ColdPojo { } + public static class P047 extends ColdPojo { } + public static class P048 extends ColdPojo { } + public static class P049 extends ColdPojo { } + public static class P050 extends ColdPojo { } + public static class P051 extends ColdPojo { } + public static class P052 extends ColdPojo { } + public static class P053 extends ColdPojo { } + public static class P054 extends ColdPojo { } + public static class P055 extends ColdPojo { } + public static class P056 extends ColdPojo { } + public static class P057 extends ColdPojo { } + public static class P058 extends ColdPojo { } + public static class P059 extends ColdPojo { } + public static class P060 extends ColdPojo { } + public static class P061 extends ColdPojo { } + public static class P062 extends ColdPojo { } + public static class P063 extends ColdPojo { } + public static class P064 extends ColdPojo { } + public static class P065 extends ColdPojo { } + public static class P066 extends ColdPojo { } + public static class P067 extends ColdPojo { } + public static class P068 extends ColdPojo { } + public static class P069 extends ColdPojo { } + public static class P070 extends ColdPojo { } + public static class P071 extends ColdPojo { } + public static class P072 extends ColdPojo { } + public static class P073 extends ColdPojo { } + public static class P074 extends ColdPojo { } + public static class P075 extends ColdPojo { } + public static class P076 extends ColdPojo { } + public static class P077 extends ColdPojo { } + public static class P078 extends ColdPojo { } + public static class P079 extends ColdPojo { } + public static class P080 extends ColdPojo { } + public static class P081 extends ColdPojo { } + public static class P082 extends ColdPojo { } + public static class P083 extends ColdPojo { } + public static class P084 extends ColdPojo { } + public static class P085 extends ColdPojo { } + public static class P086 extends ColdPojo { } + public static class P087 extends ColdPojo { } + public static class P088 extends ColdPojo { } + public static class P089 extends ColdPojo { } + public static class P090 extends ColdPojo { } + public static class P091 extends ColdPojo { } + public static class P092 extends ColdPojo { } + public static class P093 extends ColdPojo { } + public static class P094 extends ColdPojo { } + public static class P095 extends ColdPojo { } + public static class P096 extends ColdPojo { } + public static class P097 extends ColdPojo { } + public static class P098 extends ColdPojo { } + public static class P099 extends ColdPojo { } + public static class P100 extends ColdPojo { } + public static class P101 extends ColdPojo { } + public static class P102 extends ColdPojo { } + public static class P103 extends ColdPojo { } + public static class P104 extends ColdPojo { } + public static class P105 extends ColdPojo { } + public static class P106 extends ColdPojo { } + public static class P107 extends ColdPojo { } + public static class P108 extends ColdPojo { } + public static class P109 extends ColdPojo { } + public static class P110 extends ColdPojo { } + public static class P111 extends ColdPojo { } + public static class P112 extends ColdPojo { } + public static class P113 extends ColdPojo { } + public static class P114 extends ColdPojo { } + public static class P115 extends ColdPojo { } + public static class P116 extends ColdPojo { } + public static class P117 extends ColdPojo { } + public static class P118 extends ColdPojo { } + public static class P119 extends ColdPojo { } + public static class P120 extends ColdPojo { } + public static class P121 extends ColdPojo { } + public static class P122 extends ColdPojo { } + public static class P123 extends ColdPojo { } + public static class P124 extends ColdPojo { } + public static class P125 extends ColdPojo { } + public static class P126 extends ColdPojo { } + public static class P127 extends ColdPojo { } + public static class P128 extends ColdPojo { } + public static class P129 extends ColdPojo { } + public static class P130 extends ColdPojo { } + public static class P131 extends ColdPojo { } + public static class P132 extends ColdPojo { } + public static class P133 extends ColdPojo { } + public static class P134 extends ColdPojo { } + public static class P135 extends ColdPojo { } + public static class P136 extends ColdPojo { } + public static class P137 extends ColdPojo { } + public static class P138 extends ColdPojo { } + public static class P139 extends ColdPojo { } + public static class P140 extends ColdPojo { } + public static class P141 extends ColdPojo { } + public static class P142 extends ColdPojo { } + public static class P143 extends ColdPojo { } + public static class P144 extends ColdPojo { } + public static class P145 extends ColdPojo { } + public static class P146 extends ColdPojo { } + public static class P147 extends ColdPojo { } + public static class P148 extends ColdPojo { } + public static class P149 extends ColdPojo { } + public static class P150 extends ColdPojo { } + public static class P151 extends ColdPojo { } + public static class P152 extends ColdPojo { } + public static class P153 extends ColdPojo { } + public static class P154 extends ColdPojo { } + public static class P155 extends ColdPojo { } + public static class P156 extends ColdPojo { } + public static class P157 extends ColdPojo { } + public static class P158 extends ColdPojo { } + public static class P159 extends ColdPojo { } + public static class P160 extends ColdPojo { } + public static class P161 extends ColdPojo { } + public static class P162 extends ColdPojo { } + public static class P163 extends ColdPojo { } + public static class P164 extends ColdPojo { } + public static class P165 extends ColdPojo { } + public static class P166 extends ColdPojo { } + public static class P167 extends ColdPojo { } + public static class P168 extends ColdPojo { } + public static class P169 extends ColdPojo { } + public static class P170 extends ColdPojo { } + public static class P171 extends ColdPojo { } + public static class P172 extends ColdPojo { } + public static class P173 extends ColdPojo { } + public static class P174 extends ColdPojo { } + public static class P175 extends ColdPojo { } + public static class P176 extends ColdPojo { } + public static class P177 extends ColdPojo { } + public static class P178 extends ColdPojo { } + public static class P179 extends ColdPojo { } + public static class P180 extends ColdPojo { } + public static class P181 extends ColdPojo { } + public static class P182 extends ColdPojo { } + public static class P183 extends ColdPojo { } + public static class P184 extends ColdPojo { } + public static class P185 extends ColdPojo { } + public static class P186 extends ColdPojo { } + public static class P187 extends ColdPojo { } + public static class P188 extends ColdPojo { } + public static class P189 extends ColdPojo { } + public static class P190 extends ColdPojo { } + public static class P191 extends ColdPojo { } + public static class P192 extends ColdPojo { } + public static class P193 extends ColdPojo { } + public static class P194 extends ColdPojo { } + public static class P195 extends ColdPojo { } + public static class P196 extends ColdPojo { } + public static class P197 extends ColdPojo { } + public static class P198 extends ColdPojo { } + public static class P199 extends ColdPojo { } + public static class P200 extends ColdPojo { } + public static class P201 extends ColdPojo { } + public static class P202 extends ColdPojo { } + public static class P203 extends ColdPojo { } + public static class P204 extends ColdPojo { } + public static class P205 extends ColdPojo { } + public static class P206 extends ColdPojo { } + public static class P207 extends ColdPojo { } + public static class P208 extends ColdPojo { } + public static class P209 extends ColdPojo { } + public static class P210 extends ColdPojo { } + public static class P211 extends ColdPojo { } + public static class P212 extends ColdPojo { } + public static class P213 extends ColdPojo { } + public static class P214 extends ColdPojo { } + public static class P215 extends ColdPojo { } + public static class P216 extends ColdPojo { } + public static class P217 extends ColdPojo { } + public static class P218 extends ColdPojo { } + public static class P219 extends ColdPojo { } + public static class P220 extends ColdPojo { } + public static class P221 extends ColdPojo { } + public static class P222 extends ColdPojo { } + public static class P223 extends ColdPojo { } + public static class P224 extends ColdPojo { } + public static class P225 extends ColdPojo { } + public static class P226 extends ColdPojo { } + public static class P227 extends ColdPojo { } + public static class P228 extends ColdPojo { } + public static class P229 extends ColdPojo { } + public static class P230 extends ColdPojo { } + public static class P231 extends ColdPojo { } + public static class P232 extends ColdPojo { } + public static class P233 extends ColdPojo { } + public static class P234 extends ColdPojo { } + public static class P235 extends ColdPojo { } + public static class P236 extends ColdPojo { } + public static class P237 extends ColdPojo { } + public static class P238 extends ColdPojo { } + public static class P239 extends ColdPojo { } + public static class P240 extends ColdPojo { } + public static class P241 extends ColdPojo { } + public static class P242 extends ColdPojo { } + public static class P243 extends ColdPojo { } + public static class P244 extends ColdPojo { } + public static class P245 extends ColdPojo { } + public static class P246 extends ColdPojo { } + public static class P247 extends ColdPojo { } + public static class P248 extends ColdPojo { } + public static class P249 extends ColdPojo { } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Invoice.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Invoice.java new file mode 100644 index 000000000..dce8c2348 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Invoice.java @@ -0,0 +1,78 @@ +package tools.jackson.module.blackbird; + +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.annotation.JsonPOJOBuilder; + +/** + * Immutable bean with a fluent builder, in the shape that Immutables and + * Lombok generate. + */ +@JsonDeserialize(builder = Invoice.Builder.class) +public final class Invoice +{ + static final Invoice SAMPLE = new Builder() + .number("INV-2026-000417") + .customerId("cust-88412") + .currency("EUR") + .totalCents(1_250_075L) + .taxCents(237_514L) + .lineCount(14) + .paid(true) + .note("Net 30") + .build(); + + private final String number; + private final String customerId; + private final String currency; + private final long totalCents; + private final long taxCents; + private final int lineCount; + private final boolean paid; + private final String note; + + private Invoice(Builder b) { + number = b.number; + customerId = b.customerId; + currency = b.currency; + totalCents = b.totalCents; + taxCents = b.taxCents; + lineCount = b.lineCount; + paid = b.paid; + note = b.note; + } + + public String getNumber() { return number; } + public String getCustomerId() { return customerId; } + public String getCurrency() { return currency; } + public long getTotalCents() { return totalCents; } + public long getTaxCents() { return taxCents; } + public int getLineCount() { return lineCount; } + public boolean isPaid() { return paid; } + public String getNote() { return note; } + + @JsonPOJOBuilder(withPrefix = "") + public static final class Builder + { + private String number; + private String customerId; + private String currency; + private long totalCents; + private long taxCents; + private int lineCount; + private boolean paid; + private String note; + + public Builder number(String v) { number = v; return this; } + public Builder customerId(String v) { customerId = v; return this; } + public Builder currency(String v) { currency = v; return this; } + public Builder totalCents(long v) { totalCents = v; return this; } + public Builder taxCents(long v) { taxCents = v; return this; } + public Builder lineCount(int v) { lineCount = v; return this; } + public Builder paid(boolean v) { paid = v; return this; } + public Builder note(String v) { note = v; return this; } + + public Invoice build() { + return new Invoice(this); + } + } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Mappers.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Mappers.java new file mode 100644 index 000000000..d86f24fbe --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Mappers.java @@ -0,0 +1,26 @@ +package tools.jackson.module.blackbird; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.module.afterburner.AfterburnerModule; + +/** + * Creates the mapper for the {@code mode} parameter of each benchmark: + * {@code stock} (no module), {@code blackbird} or {@code afterburner}. + * Which Blackbird engine {@code blackbird} measures depends on the jar: + * {@code benchmarks.jar} has this build, {@code benchmarks-classic.jar} + * has the last release of the LambdaMetafactory engine. + */ +final class Mappers +{ + private Mappers() { } + + static ObjectMapper create(String mode) { + return switch (mode) { + case "stock" -> JsonMapper.builder().build(); + case "blackbird" -> JsonMapper.builder().addModule(new BlackbirdModule()).build(); + case "afterburner" -> JsonMapper.builder().addModule(new AfterburnerModule()).build(); + default -> throw new IllegalArgumentException("Unknown mode: " + mode); + }; + } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Order.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Order.java new file mode 100644 index 000000000..ce31ed373 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Order.java @@ -0,0 +1,19 @@ +package tools.jackson.module.blackbird; + +import java.util.List; + +/** + * Record graph: nested records and a list of records. + */ +public record Order(String id, long amountCents, boolean expedited, Customer customer, List lines) +{ + public record Customer(String name, String email, int tier) { } + + public record Line(String sku, int quantity, long priceCents) { } + + static final Order SAMPLE = new Order("ord-20261014-0042", 1_234_599L, true, + new Customer("Ada Lovelace", "ada@example.com", 3), + List.of(new Line("sku-1001", 2, 19_999L), + new Line("sku-2002", 1, 1_049_900L), + new Line("sku-3003", 12, 5_475L))); +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BaseBenchmark.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/PojoBenchmark.java similarity index 52% rename from blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BaseBenchmark.java rename to blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/PojoBenchmark.java index f2f7daa89..9bf4a142c 100644 --- a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BaseBenchmark.java +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/PojoBenchmark.java @@ -5,106 +5,101 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; -import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.ObjectReader; -import tools.jackson.databind.ObjectWriter; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.ObjectWriter; + +/** + * Setter POJOs: arrays of 1000 small beans, the same beans through a + * property-based creator, and the media item bean. + */ @State(Scope.Thread) @BenchmarkMode(Mode.Throughput) -@Measurement(time = 30, iterations = 10) -@Warmup(time = 10, iterations = 10) @OutputTimeUnit(TimeUnit.MILLISECONDS) -@Fork(5) -public abstract class BaseBenchmark { - - Random random = new Random(1337); - ObjectMapper mapper; - SomeBean[] beans; - byte[] beansBytes; - byte[] mediaItemJson; - ObjectWriter mediaItemWriter; - ObjectReader mediaItemReader; - ClassicBean[] classicBeans; - byte[] classicBeansJson; - ObjectWriter classicBeanWriter; - ObjectReader classicBeanReader; - - public static void main(final String[] args) throws RunnerException { - final Options options = new OptionsBuilder() - .include(BaseBenchmark.class.getSimpleName()) - .build(); - new Runner(options).run(); - } - - protected abstract ObjectMapper createObjectMapper(); +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class PojoBenchmark +{ + @Param({"stock", "blackbird"}) + public String mode; + + private ObjectMapper mapper; + private SomeBean[] beans; + private byte[] beansJson; + private ObjectReader mediaItemReader; + private ObjectWriter mediaItemWriter; + private byte[] mediaItemJson; + private ObjectReader classicBeanReader; + private ObjectWriter classicBeanWriter; + private ClassicBean[] classicBeans; + private byte[] classicBeansJson; @Setup - public void setup() throws Exception { - mapper = createObjectMapper(); + public void setup() { + mapper = Mappers.create(mode); + Random random = new Random(1337); beans = IntStream.range(0, 1000) .mapToObj(i -> SomeBean.random(random)) .toArray(SomeBean[]::new); + beansJson = mapper.writeValueAsBytes(beans); - beansBytes = mapper.writeValueAsBytes(beans); mediaItemReader = mapper.readerFor(MediaItem.class); mediaItemWriter = mapper.writerFor(MediaItem.class); mediaItemJson = mediaItemWriter.writeValueAsBytes(MediaItem.SAMPLE); classicBeanReader = mapper.readerFor(ClassicBean[].class); classicBeanWriter = mapper.writerFor(ClassicBean[].class); - final ClassicBean classicBean = new ClassicBean(); - classicBean.setUp(); - classicBeans = Collections.nCopies(1000, classicBean).toArray(new ClassicBean[0]); + classicBeans = Collections.nCopies(1000, new ClassicBean().setUp()) + .toArray(new ClassicBean[0]); classicBeansJson = classicBeanWriter.writeValueAsBytes(classicBeans); } @Benchmark - public byte[] beanArraySer() throws Exception { + public byte[] beanArraySer() { return mapper.writeValueAsBytes(beans); } @Benchmark - public SomeBean[] beanArrayDeser() throws Exception { - return mapper.readValue(beansBytes, SomeBean[].class); + public SomeBean[] beanArrayDeser() { + return mapper.readValue(beansJson, SomeBean[].class); } @Benchmark - public BeanWithPropertyConstructor[] constructorArrayDeser() throws Exception { - return mapper.readValue(beansBytes, BeanWithPropertyConstructor[].class); + public BeanWithPropertyConstructor[] constructorArrayDeser() { + return mapper.readValue(beansJson, BeanWithPropertyConstructor[].class); } @Benchmark - public void classicMediaItemSer(final Blackhole bh) throws Exception { + public void mediaItemSer(Blackhole bh) { mediaItemWriter.writeValue(new NopOutputStream(bh), MediaItem.SAMPLE); } @Benchmark - public MediaItem classicMediaItemDeser() throws Exception { + public MediaItem mediaItemDeser() { return mediaItemReader.readValue(mediaItemJson); } @Benchmark - public void classicBeanItemSer(final Blackhole bh) throws Exception { + public void classicBeanSer(Blackhole bh) { classicBeanWriter.writeValue(new NopOutputStream(bh), classicBeans); } @Benchmark - public ClassicBean[] classicBeanItemDeser() throws Exception { + public ClassicBean[] classicBeanDeser() { return classicBeanReader.readValue(classicBeansJson); } } diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/RecordBenchmark.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/RecordBenchmark.java new file mode 100644 index 000000000..b3efd5eb8 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/RecordBenchmark.java @@ -0,0 +1,55 @@ +package tools.jackson.module.blackbird; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.ObjectWriter; + +/** + * Records: one {@link Order} with a nested record and a list of three records. + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class RecordBenchmark +{ + @Param({"stock", "blackbird"}) + public String mode; + + private ObjectReader reader; + private ObjectWriter writer; + private byte[] json; + + @Setup + public void setup() { + var mapper = Mappers.create(mode); + reader = mapper.readerFor(Order.class); + writer = mapper.writerFor(Order.class); + json = writer.writeValueAsBytes(Order.SAMPLE); + } + + @Benchmark + public Order recordDeser() { + return reader.readValue(json); + } + + @Benchmark + public byte[] recordSer() { + return writer.writeValueAsBytes(Order.SAMPLE); + } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/StartupTimeBenchmark.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/StartupTimeBenchmark.java deleted file mode 100644 index 56c3171df..000000000 --- a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/StartupTimeBenchmark.java +++ /dev/null @@ -1,58 +0,0 @@ -package tools.jackson.module.blackbird; - -import java.util.List; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -import tools.jackson.core.JacksonException; -import tools.jackson.databind.ObjectMapper; -import tools.jackson.databind.json.JsonMapper; -import tools.jackson.module.afterburner.AfterburnerModule; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Warmup; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -@BenchmarkMode(Mode.SingleShotTime) -@Measurement(iterations = 1) -@Warmup(iterations = 0) -@OutputTimeUnit(TimeUnit.MILLISECONDS) -@Fork(10) -public class StartupTimeBenchmark { - public static void main(String[] args) throws RunnerException { - Options options = new OptionsBuilder() - .include(StartupTimeBenchmark.class.getSimpleName()) - .build(); - new Runner(options).run(); - } - - @Benchmark - public byte[] vanilla() throws Exception { - return singleShot(new ObjectMapper()); - } - - @Benchmark - public byte[] blackbird() throws Exception { - return singleShot(JsonMapper.builder().addModule(new BlackbirdModule()).build()); - } - - @Benchmark - public byte[] afterburner() throws Exception { - return singleShot(JsonMapper.builder().addModule(new AfterburnerModule()).build()); - } - - private static byte[] singleShot(ObjectMapper mapper) throws JacksonException { - final Random random = new Random(); - return mapper.writeValueAsBytes(List.of( - SomeBean.random(random), - new ClassicBean().setUp(), - new BeanWithPropertyConstructor(42, "foo", 8675309, SomeBean.random(random), SomeEnum.EB))); - } -} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Vanilla.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Vanilla.java deleted file mode 100644 index a4cbe3c90..000000000 --- a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Vanilla.java +++ /dev/null @@ -1,22 +0,0 @@ -package tools.jackson.module.blackbird; - -import tools.jackson.databind.ObjectMapper; -import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; -import org.openjdk.jmh.runner.options.Options; -import org.openjdk.jmh.runner.options.OptionsBuilder; - -public class Vanilla extends BaseBenchmark { - public static void main(String[] args) throws RunnerException { - Options options = new OptionsBuilder() - .include(Vanilla.class.getSimpleName()) - .forks(1) - .build(); - new Runner(options).run(); - } - - @Override - protected ObjectMapper createObjectMapper() { - return new ObjectMapper(); - } -} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide200.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide200.java new file mode 100644 index 000000000..1bebf5e52 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide200.java @@ -0,0 +1,610 @@ +package tools.jackson.module.blackbird; + +/** + * Setter bean with 200 properties. Property {@code pI} has type + * String, int, long or boolean for {@code I % 4} = 0, 1, 2 or 3. + */ +public class Wide200 +{ + private String p0; + private int p1; + private long p2; + private boolean p3; + private String p4; + private int p5; + private long p6; + private boolean p7; + private String p8; + private int p9; + private long p10; + private boolean p11; + private String p12; + private int p13; + private long p14; + private boolean p15; + private String p16; + private int p17; + private long p18; + private boolean p19; + private String p20; + private int p21; + private long p22; + private boolean p23; + private String p24; + private int p25; + private long p26; + private boolean p27; + private String p28; + private int p29; + private long p30; + private boolean p31; + private String p32; + private int p33; + private long p34; + private boolean p35; + private String p36; + private int p37; + private long p38; + private boolean p39; + private String p40; + private int p41; + private long p42; + private boolean p43; + private String p44; + private int p45; + private long p46; + private boolean p47; + private String p48; + private int p49; + private long p50; + private boolean p51; + private String p52; + private int p53; + private long p54; + private boolean p55; + private String p56; + private int p57; + private long p58; + private boolean p59; + private String p60; + private int p61; + private long p62; + private boolean p63; + private String p64; + private int p65; + private long p66; + private boolean p67; + private String p68; + private int p69; + private long p70; + private boolean p71; + private String p72; + private int p73; + private long p74; + private boolean p75; + private String p76; + private int p77; + private long p78; + private boolean p79; + private String p80; + private int p81; + private long p82; + private boolean p83; + private String p84; + private int p85; + private long p86; + private boolean p87; + private String p88; + private int p89; + private long p90; + private boolean p91; + private String p92; + private int p93; + private long p94; + private boolean p95; + private String p96; + private int p97; + private long p98; + private boolean p99; + private String p100; + private int p101; + private long p102; + private boolean p103; + private String p104; + private int p105; + private long p106; + private boolean p107; + private String p108; + private int p109; + private long p110; + private boolean p111; + private String p112; + private int p113; + private long p114; + private boolean p115; + private String p116; + private int p117; + private long p118; + private boolean p119; + private String p120; + private int p121; + private long p122; + private boolean p123; + private String p124; + private int p125; + private long p126; + private boolean p127; + private String p128; + private int p129; + private long p130; + private boolean p131; + private String p132; + private int p133; + private long p134; + private boolean p135; + private String p136; + private int p137; + private long p138; + private boolean p139; + private String p140; + private int p141; + private long p142; + private boolean p143; + private String p144; + private int p145; + private long p146; + private boolean p147; + private String p148; + private int p149; + private long p150; + private boolean p151; + private String p152; + private int p153; + private long p154; + private boolean p155; + private String p156; + private int p157; + private long p158; + private boolean p159; + private String p160; + private int p161; + private long p162; + private boolean p163; + private String p164; + private int p165; + private long p166; + private boolean p167; + private String p168; + private int p169; + private long p170; + private boolean p171; + private String p172; + private int p173; + private long p174; + private boolean p175; + private String p176; + private int p177; + private long p178; + private boolean p179; + private String p180; + private int p181; + private long p182; + private boolean p183; + private String p184; + private int p185; + private long p186; + private boolean p187; + private String p188; + private int p189; + private long p190; + private boolean p191; + private String p192; + private int p193; + private long p194; + private boolean p195; + private String p196; + private int p197; + private long p198; + private boolean p199; + + public String getP0() { return p0; } + public void setP0(String v) { p0 = v; } + public int getP1() { return p1; } + public void setP1(int v) { p1 = v; } + public long getP2() { return p2; } + public void setP2(long v) { p2 = v; } + public boolean getP3() { return p3; } + public void setP3(boolean v) { p3 = v; } + public String getP4() { return p4; } + public void setP4(String v) { p4 = v; } + public int getP5() { return p5; } + public void setP5(int v) { p5 = v; } + public long getP6() { return p6; } + public void setP6(long v) { p6 = v; } + public boolean getP7() { return p7; } + public void setP7(boolean v) { p7 = v; } + public String getP8() { return p8; } + public void setP8(String v) { p8 = v; } + public int getP9() { return p9; } + public void setP9(int v) { p9 = v; } + public long getP10() { return p10; } + public void setP10(long v) { p10 = v; } + public boolean getP11() { return p11; } + public void setP11(boolean v) { p11 = v; } + public String getP12() { return p12; } + public void setP12(String v) { p12 = v; } + public int getP13() { return p13; } + public void setP13(int v) { p13 = v; } + public long getP14() { return p14; } + public void setP14(long v) { p14 = v; } + public boolean getP15() { return p15; } + public void setP15(boolean v) { p15 = v; } + public String getP16() { return p16; } + public void setP16(String v) { p16 = v; } + public int getP17() { return p17; } + public void setP17(int v) { p17 = v; } + public long getP18() { return p18; } + public void setP18(long v) { p18 = v; } + public boolean getP19() { return p19; } + public void setP19(boolean v) { p19 = v; } + public String getP20() { return p20; } + public void setP20(String v) { p20 = v; } + public int getP21() { return p21; } + public void setP21(int v) { p21 = v; } + public long getP22() { return p22; } + public void setP22(long v) { p22 = v; } + public boolean getP23() { return p23; } + public void setP23(boolean v) { p23 = v; } + public String getP24() { return p24; } + public void setP24(String v) { p24 = v; } + public int getP25() { return p25; } + public void setP25(int v) { p25 = v; } + public long getP26() { return p26; } + public void setP26(long v) { p26 = v; } + public boolean getP27() { return p27; } + public void setP27(boolean v) { p27 = v; } + public String getP28() { return p28; } + public void setP28(String v) { p28 = v; } + public int getP29() { return p29; } + public void setP29(int v) { p29 = v; } + public long getP30() { return p30; } + public void setP30(long v) { p30 = v; } + public boolean getP31() { return p31; } + public void setP31(boolean v) { p31 = v; } + public String getP32() { return p32; } + public void setP32(String v) { p32 = v; } + public int getP33() { return p33; } + public void setP33(int v) { p33 = v; } + public long getP34() { return p34; } + public void setP34(long v) { p34 = v; } + public boolean getP35() { return p35; } + public void setP35(boolean v) { p35 = v; } + public String getP36() { return p36; } + public void setP36(String v) { p36 = v; } + public int getP37() { return p37; } + public void setP37(int v) { p37 = v; } + public long getP38() { return p38; } + public void setP38(long v) { p38 = v; } + public boolean getP39() { return p39; } + public void setP39(boolean v) { p39 = v; } + public String getP40() { return p40; } + public void setP40(String v) { p40 = v; } + public int getP41() { return p41; } + public void setP41(int v) { p41 = v; } + public long getP42() { return p42; } + public void setP42(long v) { p42 = v; } + public boolean getP43() { return p43; } + public void setP43(boolean v) { p43 = v; } + public String getP44() { return p44; } + public void setP44(String v) { p44 = v; } + public int getP45() { return p45; } + public void setP45(int v) { p45 = v; } + public long getP46() { return p46; } + public void setP46(long v) { p46 = v; } + public boolean getP47() { return p47; } + public void setP47(boolean v) { p47 = v; } + public String getP48() { return p48; } + public void setP48(String v) { p48 = v; } + public int getP49() { return p49; } + public void setP49(int v) { p49 = v; } + public long getP50() { return p50; } + public void setP50(long v) { p50 = v; } + public boolean getP51() { return p51; } + public void setP51(boolean v) { p51 = v; } + public String getP52() { return p52; } + public void setP52(String v) { p52 = v; } + public int getP53() { return p53; } + public void setP53(int v) { p53 = v; } + public long getP54() { return p54; } + public void setP54(long v) { p54 = v; } + public boolean getP55() { return p55; } + public void setP55(boolean v) { p55 = v; } + public String getP56() { return p56; } + public void setP56(String v) { p56 = v; } + public int getP57() { return p57; } + public void setP57(int v) { p57 = v; } + public long getP58() { return p58; } + public void setP58(long v) { p58 = v; } + public boolean getP59() { return p59; } + public void setP59(boolean v) { p59 = v; } + public String getP60() { return p60; } + public void setP60(String v) { p60 = v; } + public int getP61() { return p61; } + public void setP61(int v) { p61 = v; } + public long getP62() { return p62; } + public void setP62(long v) { p62 = v; } + public boolean getP63() { return p63; } + public void setP63(boolean v) { p63 = v; } + public String getP64() { return p64; } + public void setP64(String v) { p64 = v; } + public int getP65() { return p65; } + public void setP65(int v) { p65 = v; } + public long getP66() { return p66; } + public void setP66(long v) { p66 = v; } + public boolean getP67() { return p67; } + public void setP67(boolean v) { p67 = v; } + public String getP68() { return p68; } + public void setP68(String v) { p68 = v; } + public int getP69() { return p69; } + public void setP69(int v) { p69 = v; } + public long getP70() { return p70; } + public void setP70(long v) { p70 = v; } + public boolean getP71() { return p71; } + public void setP71(boolean v) { p71 = v; } + public String getP72() { return p72; } + public void setP72(String v) { p72 = v; } + public int getP73() { return p73; } + public void setP73(int v) { p73 = v; } + public long getP74() { return p74; } + public void setP74(long v) { p74 = v; } + public boolean getP75() { return p75; } + public void setP75(boolean v) { p75 = v; } + public String getP76() { return p76; } + public void setP76(String v) { p76 = v; } + public int getP77() { return p77; } + public void setP77(int v) { p77 = v; } + public long getP78() { return p78; } + public void setP78(long v) { p78 = v; } + public boolean getP79() { return p79; } + public void setP79(boolean v) { p79 = v; } + public String getP80() { return p80; } + public void setP80(String v) { p80 = v; } + public int getP81() { return p81; } + public void setP81(int v) { p81 = v; } + public long getP82() { return p82; } + public void setP82(long v) { p82 = v; } + public boolean getP83() { return p83; } + public void setP83(boolean v) { p83 = v; } + public String getP84() { return p84; } + public void setP84(String v) { p84 = v; } + public int getP85() { return p85; } + public void setP85(int v) { p85 = v; } + public long getP86() { return p86; } + public void setP86(long v) { p86 = v; } + public boolean getP87() { return p87; } + public void setP87(boolean v) { p87 = v; } + public String getP88() { return p88; } + public void setP88(String v) { p88 = v; } + public int getP89() { return p89; } + public void setP89(int v) { p89 = v; } + public long getP90() { return p90; } + public void setP90(long v) { p90 = v; } + public boolean getP91() { return p91; } + public void setP91(boolean v) { p91 = v; } + public String getP92() { return p92; } + public void setP92(String v) { p92 = v; } + public int getP93() { return p93; } + public void setP93(int v) { p93 = v; } + public long getP94() { return p94; } + public void setP94(long v) { p94 = v; } + public boolean getP95() { return p95; } + public void setP95(boolean v) { p95 = v; } + public String getP96() { return p96; } + public void setP96(String v) { p96 = v; } + public int getP97() { return p97; } + public void setP97(int v) { p97 = v; } + public long getP98() { return p98; } + public void setP98(long v) { p98 = v; } + public boolean getP99() { return p99; } + public void setP99(boolean v) { p99 = v; } + public String getP100() { return p100; } + public void setP100(String v) { p100 = v; } + public int getP101() { return p101; } + public void setP101(int v) { p101 = v; } + public long getP102() { return p102; } + public void setP102(long v) { p102 = v; } + public boolean getP103() { return p103; } + public void setP103(boolean v) { p103 = v; } + public String getP104() { return p104; } + public void setP104(String v) { p104 = v; } + public int getP105() { return p105; } + public void setP105(int v) { p105 = v; } + public long getP106() { return p106; } + public void setP106(long v) { p106 = v; } + public boolean getP107() { return p107; } + public void setP107(boolean v) { p107 = v; } + public String getP108() { return p108; } + public void setP108(String v) { p108 = v; } + public int getP109() { return p109; } + public void setP109(int v) { p109 = v; } + public long getP110() { return p110; } + public void setP110(long v) { p110 = v; } + public boolean getP111() { return p111; } + public void setP111(boolean v) { p111 = v; } + public String getP112() { return p112; } + public void setP112(String v) { p112 = v; } + public int getP113() { return p113; } + public void setP113(int v) { p113 = v; } + public long getP114() { return p114; } + public void setP114(long v) { p114 = v; } + public boolean getP115() { return p115; } + public void setP115(boolean v) { p115 = v; } + public String getP116() { return p116; } + public void setP116(String v) { p116 = v; } + public int getP117() { return p117; } + public void setP117(int v) { p117 = v; } + public long getP118() { return p118; } + public void setP118(long v) { p118 = v; } + public boolean getP119() { return p119; } + public void setP119(boolean v) { p119 = v; } + public String getP120() { return p120; } + public void setP120(String v) { p120 = v; } + public int getP121() { return p121; } + public void setP121(int v) { p121 = v; } + public long getP122() { return p122; } + public void setP122(long v) { p122 = v; } + public boolean getP123() { return p123; } + public void setP123(boolean v) { p123 = v; } + public String getP124() { return p124; } + public void setP124(String v) { p124 = v; } + public int getP125() { return p125; } + public void setP125(int v) { p125 = v; } + public long getP126() { return p126; } + public void setP126(long v) { p126 = v; } + public boolean getP127() { return p127; } + public void setP127(boolean v) { p127 = v; } + public String getP128() { return p128; } + public void setP128(String v) { p128 = v; } + public int getP129() { return p129; } + public void setP129(int v) { p129 = v; } + public long getP130() { return p130; } + public void setP130(long v) { p130 = v; } + public boolean getP131() { return p131; } + public void setP131(boolean v) { p131 = v; } + public String getP132() { return p132; } + public void setP132(String v) { p132 = v; } + public int getP133() { return p133; } + public void setP133(int v) { p133 = v; } + public long getP134() { return p134; } + public void setP134(long v) { p134 = v; } + public boolean getP135() { return p135; } + public void setP135(boolean v) { p135 = v; } + public String getP136() { return p136; } + public void setP136(String v) { p136 = v; } + public int getP137() { return p137; } + public void setP137(int v) { p137 = v; } + public long getP138() { return p138; } + public void setP138(long v) { p138 = v; } + public boolean getP139() { return p139; } + public void setP139(boolean v) { p139 = v; } + public String getP140() { return p140; } + public void setP140(String v) { p140 = v; } + public int getP141() { return p141; } + public void setP141(int v) { p141 = v; } + public long getP142() { return p142; } + public void setP142(long v) { p142 = v; } + public boolean getP143() { return p143; } + public void setP143(boolean v) { p143 = v; } + public String getP144() { return p144; } + public void setP144(String v) { p144 = v; } + public int getP145() { return p145; } + public void setP145(int v) { p145 = v; } + public long getP146() { return p146; } + public void setP146(long v) { p146 = v; } + public boolean getP147() { return p147; } + public void setP147(boolean v) { p147 = v; } + public String getP148() { return p148; } + public void setP148(String v) { p148 = v; } + public int getP149() { return p149; } + public void setP149(int v) { p149 = v; } + public long getP150() { return p150; } + public void setP150(long v) { p150 = v; } + public boolean getP151() { return p151; } + public void setP151(boolean v) { p151 = v; } + public String getP152() { return p152; } + public void setP152(String v) { p152 = v; } + public int getP153() { return p153; } + public void setP153(int v) { p153 = v; } + public long getP154() { return p154; } + public void setP154(long v) { p154 = v; } + public boolean getP155() { return p155; } + public void setP155(boolean v) { p155 = v; } + public String getP156() { return p156; } + public void setP156(String v) { p156 = v; } + public int getP157() { return p157; } + public void setP157(int v) { p157 = v; } + public long getP158() { return p158; } + public void setP158(long v) { p158 = v; } + public boolean getP159() { return p159; } + public void setP159(boolean v) { p159 = v; } + public String getP160() { return p160; } + public void setP160(String v) { p160 = v; } + public int getP161() { return p161; } + public void setP161(int v) { p161 = v; } + public long getP162() { return p162; } + public void setP162(long v) { p162 = v; } + public boolean getP163() { return p163; } + public void setP163(boolean v) { p163 = v; } + public String getP164() { return p164; } + public void setP164(String v) { p164 = v; } + public int getP165() { return p165; } + public void setP165(int v) { p165 = v; } + public long getP166() { return p166; } + public void setP166(long v) { p166 = v; } + public boolean getP167() { return p167; } + public void setP167(boolean v) { p167 = v; } + public String getP168() { return p168; } + public void setP168(String v) { p168 = v; } + public int getP169() { return p169; } + public void setP169(int v) { p169 = v; } + public long getP170() { return p170; } + public void setP170(long v) { p170 = v; } + public boolean getP171() { return p171; } + public void setP171(boolean v) { p171 = v; } + public String getP172() { return p172; } + public void setP172(String v) { p172 = v; } + public int getP173() { return p173; } + public void setP173(int v) { p173 = v; } + public long getP174() { return p174; } + public void setP174(long v) { p174 = v; } + public boolean getP175() { return p175; } + public void setP175(boolean v) { p175 = v; } + public String getP176() { return p176; } + public void setP176(String v) { p176 = v; } + public int getP177() { return p177; } + public void setP177(int v) { p177 = v; } + public long getP178() { return p178; } + public void setP178(long v) { p178 = v; } + public boolean getP179() { return p179; } + public void setP179(boolean v) { p179 = v; } + public String getP180() { return p180; } + public void setP180(String v) { p180 = v; } + public int getP181() { return p181; } + public void setP181(int v) { p181 = v; } + public long getP182() { return p182; } + public void setP182(long v) { p182 = v; } + public boolean getP183() { return p183; } + public void setP183(boolean v) { p183 = v; } + public String getP184() { return p184; } + public void setP184(String v) { p184 = v; } + public int getP185() { return p185; } + public void setP185(int v) { p185 = v; } + public long getP186() { return p186; } + public void setP186(long v) { p186 = v; } + public boolean getP187() { return p187; } + public void setP187(boolean v) { p187 = v; } + public String getP188() { return p188; } + public void setP188(String v) { p188 = v; } + public int getP189() { return p189; } + public void setP189(int v) { p189 = v; } + public long getP190() { return p190; } + public void setP190(long v) { p190 = v; } + public boolean getP191() { return p191; } + public void setP191(boolean v) { p191 = v; } + public String getP192() { return p192; } + public void setP192(String v) { p192 = v; } + public int getP193() { return p193; } + public void setP193(int v) { p193 = v; } + public long getP194() { return p194; } + public void setP194(long v) { p194 = v; } + public boolean getP195() { return p195; } + public void setP195(boolean v) { p195 = v; } + public String getP196() { return p196; } + public void setP196(String v) { p196 = v; } + public int getP197() { return p197; } + public void setP197(int v) { p197 = v; } + public long getP198() { return p198; } + public void setP198(long v) { p198 = v; } + public boolean getP199() { return p199; } + public void setP199(boolean v) { p199 = v; } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide40.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide40.java new file mode 100644 index 000000000..a264f0cf7 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide40.java @@ -0,0 +1,130 @@ +package tools.jackson.module.blackbird; + +/** + * Setter bean with 40 properties. Property {@code pI} has type + * String, int, long or boolean for {@code I % 4} = 0, 1, 2 or 3. + */ +public class Wide40 +{ + private String p0; + private int p1; + private long p2; + private boolean p3; + private String p4; + private int p5; + private long p6; + private boolean p7; + private String p8; + private int p9; + private long p10; + private boolean p11; + private String p12; + private int p13; + private long p14; + private boolean p15; + private String p16; + private int p17; + private long p18; + private boolean p19; + private String p20; + private int p21; + private long p22; + private boolean p23; + private String p24; + private int p25; + private long p26; + private boolean p27; + private String p28; + private int p29; + private long p30; + private boolean p31; + private String p32; + private int p33; + private long p34; + private boolean p35; + private String p36; + private int p37; + private long p38; + private boolean p39; + + public String getP0() { return p0; } + public void setP0(String v) { p0 = v; } + public int getP1() { return p1; } + public void setP1(int v) { p1 = v; } + public long getP2() { return p2; } + public void setP2(long v) { p2 = v; } + public boolean getP3() { return p3; } + public void setP3(boolean v) { p3 = v; } + public String getP4() { return p4; } + public void setP4(String v) { p4 = v; } + public int getP5() { return p5; } + public void setP5(int v) { p5 = v; } + public long getP6() { return p6; } + public void setP6(long v) { p6 = v; } + public boolean getP7() { return p7; } + public void setP7(boolean v) { p7 = v; } + public String getP8() { return p8; } + public void setP8(String v) { p8 = v; } + public int getP9() { return p9; } + public void setP9(int v) { p9 = v; } + public long getP10() { return p10; } + public void setP10(long v) { p10 = v; } + public boolean getP11() { return p11; } + public void setP11(boolean v) { p11 = v; } + public String getP12() { return p12; } + public void setP12(String v) { p12 = v; } + public int getP13() { return p13; } + public void setP13(int v) { p13 = v; } + public long getP14() { return p14; } + public void setP14(long v) { p14 = v; } + public boolean getP15() { return p15; } + public void setP15(boolean v) { p15 = v; } + public String getP16() { return p16; } + public void setP16(String v) { p16 = v; } + public int getP17() { return p17; } + public void setP17(int v) { p17 = v; } + public long getP18() { return p18; } + public void setP18(long v) { p18 = v; } + public boolean getP19() { return p19; } + public void setP19(boolean v) { p19 = v; } + public String getP20() { return p20; } + public void setP20(String v) { p20 = v; } + public int getP21() { return p21; } + public void setP21(int v) { p21 = v; } + public long getP22() { return p22; } + public void setP22(long v) { p22 = v; } + public boolean getP23() { return p23; } + public void setP23(boolean v) { p23 = v; } + public String getP24() { return p24; } + public void setP24(String v) { p24 = v; } + public int getP25() { return p25; } + public void setP25(int v) { p25 = v; } + public long getP26() { return p26; } + public void setP26(long v) { p26 = v; } + public boolean getP27() { return p27; } + public void setP27(boolean v) { p27 = v; } + public String getP28() { return p28; } + public void setP28(String v) { p28 = v; } + public int getP29() { return p29; } + public void setP29(int v) { p29 = v; } + public long getP30() { return p30; } + public void setP30(long v) { p30 = v; } + public boolean getP31() { return p31; } + public void setP31(boolean v) { p31 = v; } + public String getP32() { return p32; } + public void setP32(String v) { p32 = v; } + public int getP33() { return p33; } + public void setP33(int v) { p33 = v; } + public long getP34() { return p34; } + public void setP34(long v) { p34 = v; } + public boolean getP35() { return p35; } + public void setP35(boolean v) { p35 = v; } + public String getP36() { return p36; } + public void setP36(String v) { p36 = v; } + public int getP37() { return p37; } + public void setP37(int v) { p37 = v; } + public long getP38() { return p38; } + public void setP38(long v) { p38 = v; } + public boolean getP39() { return p39; } + public void setP39(boolean v) { p39 = v; } +} diff --git a/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/WideBeanBenchmark.java b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/WideBeanBenchmark.java new file mode 100644 index 000000000..ed9687948 --- /dev/null +++ b/blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/WideBeanBenchmark.java @@ -0,0 +1,84 @@ +package tools.jackson.module.blackbird; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import tools.jackson.databind.ObjectReader; +import tools.jackson.databind.ObjectWriter; + +/** + * Wide setter beans ({@link Wide40}, {@link Wide200}) with every property set. + * The 200-property bean is large enough that generated methods can pass the + * HotSpot limits for JIT compilation of huge methods. + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(3) +public class WideBeanBenchmark +{ + @Param({"stock", "blackbird"}) + public String mode; + + @Param({"40", "200"}) + public int width; + + private ObjectReader reader; + private ObjectWriter writer; + private byte[] json; + private Object value; + + @Setup + public void setup() { + Class type = switch (width) { + case 40 -> Wide40.class; + case 200 -> Wide200.class; + default -> throw new IllegalArgumentException("Unknown width: " + width); + }; + var mapper = Mappers.create(mode); + reader = mapper.readerFor(type); + writer = mapper.writerFor(type); + json = sampleJson(width).getBytes(); + value = reader.readValue(json); + } + + private static String sampleJson(int width) { + StringBuilder sb = new StringBuilder("{"); + for (int i = 0; i < width; i++) { + if (i > 0) { + sb.append(','); + } + sb.append("\"p").append(i).append("\":"); + switch (i % 4) { + case 0 -> sb.append("\"value-").append(i).append('"'); + case 1 -> sb.append(i * 31); + case 2 -> sb.append(1_000_000_007L * i); + default -> sb.append(i % 3 == 0); + } + } + return sb.append('}').toString(); + } + + @Benchmark + public Object wideDeser() { + return reader.readValue(json); + } + + @Benchmark + public byte[] wideSer() { + return writer.writeValueAsBytes(value); + } +} diff --git a/blackbird/pom.xml b/blackbird/pom.xml index e673dd2e3..d3d2a8526 100644 --- a/blackbird/pom.xml +++ b/blackbird/pom.xml @@ -14,7 +14,8 @@ Jackson module: Blackbird jar Jackson (https://github.com/FasterXML/jackson) extension module -that uses LambdaMetafactory based code generation to replace reflection calls. +that generates a deserializer and a serializer class per bean with the JDK ClassFile API +(Java 25 or later at run time). https://github.com/FasterXML/jackson-modules-base @@ -27,6 +28,13 @@ that uses LambdaMetafactory based code generation to replace reflection calls. + + 25 + + tools.jackson.module.blackbird;version=${project.version} tools/jackson/module/blackbird ${project.groupId}.blackbird @@ -51,6 +59,131 @@ that uses LambdaMetafactory based code generation to replace reflection calls. com.google.code.maven-replacer-plugin replacer + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + add-java25-sources + generate-sources + + add-source + + + + ${project.basedir}/src/main/java25 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + + + tools/jackson/module/blackbird/codegen/BeanReaderGenerator.java + tools/jackson/module/blackbird/codegen/BeanWriterGenerator.java + + + + + compile-java25 + compile + + compile + + + 25 + true + + ${project.basedir}/src/main/java25 + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-java25-classes + process-classes + + copy-resources + + + ${project.build.outputDirectory} + + + ${project.build.outputDirectory}/META-INF/versions/25 + + tools/jackson/module/blackbird/codegen/BeanReaderGenerator*.class + tools/jackson/module/blackbird/codegen/BeanWriterGenerator*.class + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + tools/jackson/module/blackbird/codegen/BeanReaderGenerator*.class + tools/jackson/module/blackbird/codegen/BeanWriterGenerator*.class + + + + + org.apache.felix + maven-bundle-plugin + + + + <_noee>true + osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=17))" + true + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + 25 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + @{argLine} --patch-module tools.jackson.module.blackbird=${project.build.outputDirectory} + + org.cyclonedx diff --git a/blackbird/src/main/java/module-info.java b/blackbird/src/main/java/module-info.java index cbe9b3422..a7c4b8a44 100644 --- a/blackbird/src/main/java/module-info.java +++ b/blackbird/src/main/java/module-info.java @@ -1,13 +1,11 @@ // Blackbird Main artifact Module descriptor module tools.jackson.module.blackbird { - requires java.logging; - requires tools.jackson.core; requires transitive tools.jackson.databind; exports tools.jackson.module.blackbird; provides tools.jackson.databind.JacksonModule with - tools.jackson.module.blackbird.BlackbirdModule; + tools.jackson.module.blackbird.internal.DiscoveredBlackbirdModule; } diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java b/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java index 01e109943..29898d258 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java @@ -2,6 +2,7 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.function.Supplier; @@ -10,14 +11,34 @@ import tools.jackson.databind.JacksonModule; import tools.jackson.databind.util.NativeImageUtil; +import tools.jackson.module.blackbird.codegen.ClassFileEmitters; import tools.jackson.module.blackbird.deser.BBDeserializerModifier; +import tools.jackson.module.blackbird.internal.DiscoveredBlackbirdModule; import tools.jackson.module.blackbird.ser.BBSerializerModifier; +/** + * Registers Blackbird's generated per-bean codecs. No access configuration is + * needed: generated codecs reach members through handles unreflected after + * databind's own access checks, so whatever stock databind can read or write + * accelerates (on the module path, the same {@code opens ... to + * tools.jackson.databind} stock needs). The lookup-supplying constructors and + * the {@code findLookup}/{@code findLookupSupplier} overrides remain + * supported released API, but no acceleration requires a lookup any more. + *

+ * Code generation needs Java 25 or later: the generators use the ClassFile + * API (final in Java 24) and compile at release 25. On Java 17 to 24, module + * discovery ({@code MapperBuilder.findAndAddModules()}) skips this module with + * one logged warning, so that discovery still registers every other module. + * An explicit registration fails with an {@link IllegalStateException}, + * whether or not discovery runs too. + */ public class BlackbirdModule extends JacksonModule implements java.io.Serializable // @since 3.1 { private static final long serialVersionUID = 3L; + private static final AtomicBoolean WARNED_INACTIVE = new AtomicBoolean(); + // 13-Feb-2026, tatu: [blackbird#334] This is a mess due to need for // backwards-compatibility... but has to be @@ -95,10 +116,34 @@ public void setupModule(SetupContext context) { return; } + if (!ClassFileEmitters.available()) { + // Discovery finds the jar wherever it is on the path, also where + // nobody asked for Blackbird; an explicit registration did ask. + if (this instanceof DiscoveredBlackbirdModule) { + if (WARNED_INACTIVE.compareAndSet(false, true)) { + System.getLogger("tools.jackson.module.blackbird").log( + System.Logger.Level.WARNING, + "jackson-module-blackbird, found through module discovery, is inactive: " + + _unavailableReason() + ". Mappers work without it."); + } + return; + } + throw new IllegalStateException("Cannot register BlackbirdModule: " + + _unavailableReason() + ". Remove the registration on this runtime" + + " (jackson-module-afterburner supports Java 17 and later)."); + } Function, Lookup> lookup = findLookup(); - CrossLoaderAccess openSesame = new CrossLoaderAccess(); - context.addDeserializerModifier(new BBDeserializerModifier(lookup, openSesame)); - context.addSerializerModifier(new BBSerializerModifier(lookup, openSesame)); + context.addDeserializerModifier(new BBDeserializerModifier(lookup)); + context.addSerializerModifier(new BBSerializerModifier(lookup)); + } + + private static String _unavailableReason() { + Runtime.Version version = Runtime.version(); + if (version.feature() < 25) { + return "it requires Java 25 or later, and this runtime is Java " + version; + } + return "its Java 25 classes (META-INF/versions/25 of the jar) are not visible on Java " + + version + "; the jar was probably repackaged without multi-release support"; } @Override diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/CrossLoaderAccess.java b/blackbird/src/main/java/tools/jackson/module/blackbird/CrossLoaderAccess.java deleted file mode 100644 index 2c064276d..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/CrossLoaderAccess.java +++ /dev/null @@ -1,184 +0,0 @@ -package tools.jackson.module.blackbird; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.function.UnaryOperator; - -class CrossLoaderAccess implements UnaryOperator { - private static final MethodHandle DEFINE_CLASS, HAS_FULL_ACCESS; - - /** - * @deprecated Since 3.2. Only referenced by {@link #accessClassIn}, which - * is dead code on JDK 9+ for all known inputs. Scheduled for removal - * together with the companion-class slow path. - */ - @Deprecated(since = "3.2", forRemoval = true) - private static final String CLASS_NAME = "$$JacksonBlackbirdAccess"; - - // Pre-compiled Java 8 bytecode: - // package ; - // public class BlackbirdAccess { - // public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); - // } - - /** - * @deprecated Since 3.2. See {@link #accessClassIn}. - */ - @Deprecated(since = "3.2", forRemoval = true) - private static final int[] HEADER = new int[] { - 0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x34, 0x00, 0x1c, 0x0a, 0x00, - 0x02, 0x00, 0x03, 0x07, 0x00, 0x04, 0x0c, 0x00, 0x05, 0x00, 0x06, 0x01, - 0x00, 0x10, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x01, 0x00, 0x06, 0x3c, 0x69, 0x6e, - 0x69, 0x74, 0x3e, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x0a, 0x00, 0x08, - 0x00, 0x09, 0x07, 0x00, 0x0a, 0x0c, 0x00, 0x0b, 0x00, 0x0c, 0x01, 0x00, - 0x1e, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x69, - 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x2f, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x01, 0x00, 0x06, 0x6c, 0x6f, - 0x6f, 0x6b, 0x75, 0x70, 0x01, 0x00, 0x29, 0x28, 0x29, 0x4c, 0x6a, 0x61, - 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x76, 0x6f, - 0x6b, 0x65, 0x2f, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x48, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x24, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x3b, - 0x09, 0x00, 0x0e, 0x00, 0x0f, 0x07, 0x00, 0x10, 0x0c, 0x00, 0x11, 0x00, - 0x12, 0x01 - }; - /** - * @deprecated Since 3.2. See {@link #accessClassIn}. - */ - @Deprecated(since = "3.2", forRemoval = true) - private static final int[] FOOTER = new int[] { - 0x01, 0x00, 0x06, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x01, 0x00, 0x27, - 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x69, - 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x2f, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x24, 0x4c, 0x6f, 0x6f, 0x6b, - 0x75, 0x70, 0x3b, 0x01, 0x00, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x01, 0x00, - 0x0f, 0x4c, 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x54, - 0x61, 0x62, 0x6c, 0x65, 0x01, 0x00, 0x08, 0x3c, 0x63, 0x6c, 0x69, 0x6e, - 0x69, 0x74, 0x3e, 0x01, 0x00, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x46, 0x69, 0x6c, 0x65, 0x01, 0x00, 0x14, 0x42, 0x6c, 0x61, 0x63, 0x6b, - 0x62, 0x69, 0x72, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2e, 0x6a, - 0x61, 0x76, 0x61, 0x01, 0x00, 0x0c, 0x49, 0x6e, 0x6e, 0x65, 0x72, 0x43, - 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x07, 0x00, 0x1a, 0x01, 0x00, 0x25, - 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x69, 0x6e, - 0x76, 0x6f, 0x6b, 0x65, 0x2f, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x48, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x24, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, - 0x70, 0x01, 0x00, 0x06, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x00, 0x21, - 0x00, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x11, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x06, - 0x00, 0x01, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x05, 0x2a, 0xb7, 0x00, 0x01, 0xb1, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x08, 0x00, 0x15, 0x00, 0x06, 0x00, 0x01, 0x00, 0x13, 0x00, - 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, - 0x00, 0x07, 0xb3, 0x00, 0x0d, 0xb1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, - 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, 0x08, 0x00, 0x1b, 0x00, 0x19 - }; - - static { - MethodHandle defineClass = null, hasFullAccess = null; - try { - defineClass = MethodHandles.lookup().findVirtual(MethodHandles.Lookup.class, "defineClass", - MethodType.methodType(Class.class, byte[].class)); - hasFullAccess = MethodHandles.lookup().findVirtual(MethodHandles.Lookup.class, "hasFullPrivilegeAccess", - MethodType.methodType(boolean.class)); - } catch (ReflectiveOperationException ign) { } - DEFINE_CLASS = defineClass; - HAS_FULL_ACCESS = hasFullAccess; - } - - @Override - public MethodHandles.Lookup apply(MethodHandles.Lookup lookup) { - try { - return grantAccess(lookup); - } catch (IOException | ReflectiveOperationException | RuntimeException e) { - e.printStackTrace(); - return lookup; - } - } - - private static MethodHandles.Lookup grantAccess(MethodHandles.Lookup lookup) throws IOException, ReflectiveOperationException { - // Fast path: on JDK 9+, privateLookupIn always yields a full-privilege - // lookup for any bean class Blackbird can see, so this branch is taken - // for every real-world call and the companion-class slow path below - // is effectively unreachable. Verified by `blackbird-tests` - // CrossLoaderAccessTest; see that test's javadoc for the full rationale. - if (DEFINE_CLASS == null || hasFullAccess(lookup)) { - return lookup; - } - // Legacy slow path, retained while we finish verifying it has no live - // callers in any supported JDK / lookup configuration. Scheduled for - // removal — see `accessClassIn` javadoc. - return (MethodHandles.Lookup) accessClassIn(lookup).getField("LOOKUP").get(null); - } - - private static boolean hasFullAccess(MethodHandles.Lookup lookup) { - try { - return HAS_FULL_ACCESS != null && (boolean) HAS_FULL_ACCESS.invokeExact(lookup); - } catch (RuntimeException | Error e) { - throw e; - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - /** - * Defines a {@code $$JacksonBlackbirdAccess} companion class in the same - * package as {@code lookup.lookupClass()} and returns it. The companion - * class exposes a {@code public static final MethodHandles.Lookup LOOKUP} - * field (initialized from a class-initializer calling - * {@link MethodHandles#lookup()}), which callers then use as a - * full-privilege lookup for operations in that package. - * - * @deprecated Since 3.2. This is legacy Java 8 / pre-{@code privateLookupIn} - * fallback logic that is no longer reachable for any known input on - * JDK 9+. The {@link #grantAccess} fast path always wins because - * {@link MethodHandles#privateLookupIn} returns a lookup with - * {@link MethodHandles.Lookup#hasFullPrivilegeAccess()} {@code == true} - * for any bean class Blackbird can see. The - * {@code jackson-module-blackbird-tests} module - * {@code CrossLoaderAccessTest} pins this behavior. If a real live - * caller for this slow path is identified before 4.0, remove the - * {@link Deprecated} marker and keep the method; otherwise, delete - * along with {@link #CLASS_NAME}, {@link #HEADER}, and {@link #FOOTER}. - */ - @Deprecated(since = "3.2", forRemoval = true) - private static Class accessClassIn(MethodHandles.Lookup lookup) throws IOException, ReflectiveOperationException { - Package pkg = lookup.lookupClass().getPackage(); - final String pkgName = pkg.getName(); - String accessClassName = pkgName.isEmpty() ? CLASS_NAME : (pkgName + "." + CLASS_NAME); - ClassLoader lookupClassLoader = lookup.lookupClass().getClassLoader(); - try { - return Class.forName(accessClassName, true, lookupClassLoader); - } catch (ClassNotFoundException ign) { } - synchronized (CrossLoaderAccess.class) { - try { - return Class.forName(accessClassName, true, lookupClassLoader); - } catch (ClassNotFoundException ign) { } - String fqcn = pkgName.isEmpty() ? CLASS_NAME - : (pkgName.replace('.', '/') + "/" + CLASS_NAME); - - ByteArrayOutputStream classBytes = new ByteArrayOutputStream(HEADER.length + FOOTER.length + fqcn.length() + 16); - DataOutputStream dataOut = new DataOutputStream(classBytes); - for (int b : HEADER) { - dataOut.writeByte(b); - } - dataOut.writeUTF(fqcn); - for (int b : FOOTER) { - dataOut.writeByte(b); - } - try { - return (Class) DEFINE_CLASS.invokeExact(lookup, classBytes.toByteArray()); - } catch (RuntimeException | Error | IOException | ReflectiveOperationException e) { - throw e; - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java new file mode 100644 index 000000000..05081d3ef --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java @@ -0,0 +1,89 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +/** + * Locates the class-file emitters, {@code BeanReaderGenerator} and + * {@code BeanWriterGenerator}, and the shared write helpers that + * {@code BeanWriterGenerator} emits. They use the ClassFile API + * ({@code java.lang.classfile}) and compile at release 25 into + * {@code META-INF/versions/25} of the multi-release jar, so the rest of the + * module can stay at release 17. On an older runtime the JVM does not see + * them, and Blackbird cannot generate codecs (see + * {@code BlackbirdModule.setupModule}). The base classes find them by name + * because they cannot reference them at compile time. + */ +public final class ClassFileEmitters +{ + /** + * The largest generated method, in bytecode bytes, that the emitters + * return; a larger class comes back as {@code null} and the bean keeps + * the stock (de)serializer. HotSpot does not JIT-compile methods over + * 8000 bytes ({@code -XX:+DontCompileHugeMethods}, the default), and an + * interpreted codec is several times slower than stock; the margin + * leaves room for the HotSpot limit to be counted differently. + */ + public static final int MAX_METHOD_BYTES = 7000; + + private static final MethodHandle READER = find("BeanReaderGenerator", "emit", + MethodType.methodType(byte[].class, ReaderCodegen.Plan.class)); + private static final MethodHandle WRITER = find("BeanWriterGenerator", "emit", + MethodType.methodType(byte[].class, WriterCodegen.Plan.class)); + private static final MethodHandle WRITE_HELPERS = find("BeanWriterGenerator", + "emitWriteHelpers", MethodType.methodType(byte[].class)); + + private ClassFileEmitters() {} + + /** + * Whether this runtime sees the release-25 emitters: false on Java 17 to + * 24, and on any Java version when the jar was repackaged without its + * {@code META-INF/versions/25} entries. + */ + public static boolean available() { + return (READER != null) && (WRITER != null) && (WRITE_HELPERS != null); + } + + // Both return null when a generated method is over MAX_METHOD_BYTES. + static byte[] emit(ReaderCodegen.Plan plan) { + return invoke(READER, plan); + } + + static byte[] emit(WriterCodegen.Plan plan) { + return invoke(WRITER, plan); + } + + // The class WriterCodegen.HELPERS_CLASS, never over MAX_METHOD_BYTES. + static byte[] emitWriteHelpers() { + return invoke(WRITE_HELPERS); + } + + private static byte[] invoke(MethodHandle emitter, Object... args) { + if (emitter == null) { + throw new IllegalStateException("Blackbird class-file emitters are not available" + + " on this runtime"); + } + try { + return (byte[]) emitter.invokeWithArguments(args); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable t) { + throw new IllegalStateException(t); + } + } + + // A class from META-INF/versions/25 is absent on Java 17-24 + // (ClassNotFoundException); if a repackaged jar moved it to the base + // directory, loading it there fails with UnsupportedClassVersionError. + private static MethodHandle find(String simpleName, String method, MethodType type) { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + Class emitter = lookup.findClass( + ClassFileEmitters.class.getPackageName() + "." + simpleName); + return lookup.findStatic(emitter, method, type); + } catch (ReflectiveOperationException | LinkageError e) { + return null; + } + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDebug.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDebug.java new file mode 100644 index 000000000..3a7a88183 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDebug.java @@ -0,0 +1,47 @@ +package tools.jackson.module.blackbird.codegen; + +import java.util.function.Supplier; + +/** + * The debug switches for code generation, read once. {@link #CODEGEN_PROPERTY} + * enables the generation trace on stderr (gate decisions, generated class + * names); {@link #DUMP_DIR_PROPERTY} names a directory that receives every + * generated class file (see {@link CodegenDump}). + */ +public final class CodegenDebug +{ + public static final String CODEGEN_PROPERTY = "blackbird.debug.codegen"; + public static final String DUMP_DIR_PROPERTY = "blackbird.debug.dumpDir"; + + public static final boolean ENABLED = Boolean.getBoolean(CODEGEN_PROPERTY); + + private CodegenDebug() {} + + // A supplier, so that no caller builds its message while tracing is off. + public static void log(Supplier message) { + if (ENABLED) { + System.err.println("bbdebug " + message.get()); + } + } + + /** + * Traces a whole-bean demotion. The modifiers see every type the mapper + * resolves, and the great majority are scalars, collections and other JDK + * types that could never be beans; tracing those buries the one bean the + * trace is being read for, so they are left out. + */ + public static void logSkip(String side, Class beanClass, String reason) { + if (ENABLED && !isBulkType(beanClass)) { + log(() -> side + " skip " + beanClass.getName() + ": " + reason); + } + } + + private static boolean isBulkType(Class type) { + if (type.isPrimitive() || type.isArray() || type.isEnum()) { + return true; + } + String name = type.getName(); + return name.startsWith("java.") || name.startsWith("javax.") + || name.startsWith("jdk.") || name.startsWith("sun."); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java new file mode 100644 index 000000000..4190935d2 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java @@ -0,0 +1,40 @@ +package tools.jackson.module.blackbird.codegen; + +import java.io.IOException; +import java.lang.System.Logger.Level; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Debug aid: when the {@code blackbird.debug.dumpDir} system property names a + * directory, every generated class's bytes are written there as + * {@code -.class} before the hidden class is defined, + * for inspection with javap or a decompiler. The shared write helpers are + * written as {@code .class}. A dump failure never affects + * generation. + */ +final class CodegenDump +{ + private CodegenDump() {} + + // Read per class (generation is rare and costly anyway), so that tests + // can turn dumping on and off. + static void dump(Class beanClass, String kind, byte[] bytes) { + dump(beanClass.getName() + "-" + kind, bytes); + } + + static void dump(String fileStem, byte[] bytes) { + String dir = System.getProperty(CodegenDebug.DUMP_DIR_PROPERTY); + if (dir == null) { + return; + } + try { + Path path = Path.of(dir); + Files.createDirectories(path); + Files.write(path.resolve(fileStem + ".class"), bytes); + } catch (IOException e) { + System.getLogger("tools.jackson.module.blackbird").log(Level.DEBUG, + "Generated-class dump failed for " + fileStem, e); + } + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenFallbacks.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenFallbacks.java new file mode 100644 index 000000000..81b17abe5 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenFallbacks.java @@ -0,0 +1,46 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.System.Logger.Level; + +/** + * Reports codec-generation fallbacks. Both kinds leave the stock + * (de)serializer in place, so they log at DEBUG ({@code FINE} through + * java.util.logging) and stay silent by default. Gate failures are expected + * world diversity: exotic classes make reflective gate checks throw. + * Generation failures are Blackbird bugs: the type passed every gate and the + * generator still failed. With the + * {@code tools.jackson.module.blackbird.failOnCodegenError} system property + * set, generation failures are rethrown instead of demoted, which the + * Blackbird test suites enable so a masked generation bug fails the build. + */ +public final class CodegenFallbacks +{ + private static final System.Logger LOGGER = + System.getLogger("tools.jackson.module.blackbird"); + + public static final String FAIL_ON_ERROR_PROPERTY = + "tools.jackson.module.blackbird.failOnCodegenError"; + + private CodegenFallbacks() {} + + public static void gateFailure(Class beanClass, Throwable cause) { + LOGGER.log(Level.DEBUG, () -> "Blackbird gate check failed for " + beanClass.getName() + + "; the stock (de)serializer stays in place", cause); + } + + public static void generationFailure(Class beanClass, Throwable cause) { + if (Boolean.getBoolean(FAIL_ON_ERROR_PROPERTY)) { + if (cause instanceof RuntimeException re) { + throw re; + } + if (cause instanceof Error err) { + throw err; + } + throw new IllegalStateException( + "Blackbird codec generation failed for " + beanClass.getName(), cause); + } + LOGGER.log(Level.DEBUG, () -> "Blackbird codec generation failed for " + + beanClass.getName() + "; the stock (de)serializer stays in place." + + " This is likely a jackson-module-blackbird bug; please report it.", cause); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/Descs.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/Descs.java new file mode 100644 index 000000000..3a41fec67 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/Descs.java @@ -0,0 +1,20 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.constant.ClassDesc; + +/** + * Class descriptors for generated code, derived from class literals so that + * every referenced type is compile-checked and follows renames. Two build + * facts rely on the literals as well: javac's {@code --patch-module} test + * build emits only compile-time-referenced classes into the test module, and + * a descriptor built from a name would silently point at a class that build + * left out. + */ +final class Descs +{ + private Descs() {} + + static ClassDesc of(Class cls) { + return cls.describeConstable().orElseThrow(); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java new file mode 100644 index 000000000..3fc53f437 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java @@ -0,0 +1,102 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Member access for generated code: every member is unreflected and adapted + * to an erased shape (primitives kept, references widened to Object), so + * generated descriptors never name a bean class. Databind calls fixAccess on + * every member before any modifier runs, and unreflection of a member whose + * accessible flag is set does no access check, so this reaches exactly what + * stock databind can invoke - private members, non-public classes, foreign + * classloaders, and JPMS packages open only to tools.jackson.databind + * included. Unreflection uses the public lookup, so this class grants no + * access of its own: a member nobody made accessible unreflects only when it + * is public in an exported package. An IllegalAccessException here means + * databind could not open the member either (CAN_OVERRIDE_ACCESS_MODIFIERS + * disabled, or a module not open to databind); the caller demotes to the + * stock path, which fails the same way at first use. + */ +public final class MemberHandles +{ + private static final MethodHandles.Lookup LOOKUP = MethodHandles.publicLookup(); + + private MemberHandles() {} + + public static Class erase(Class type) { + return type.isPrimitive() ? type : Object.class; + } + + /** Setter adapted to {@code (Object, eV)void}; a fluent return is discarded. */ + public static MethodHandle setter(Method setter, Class valueType) + throws IllegalAccessException { + return LOOKUP.unreflect(setter) + .asType(MethodType.methodType(void.class, Object.class, erase(valueType))); + } + + /** + * Builder setter adapted to {@code (Object, eV)Object}: the fluent return + * when there is one, the receiver for a void setter, so generated code + * applies every setter uniformly. Generated code keeps the receiver when + * a fluent setter returns null, as stock does. + */ + public static MethodHandle builderSetter(Method setter, Class valueType) + throws IllegalAccessException { + MethodHandle raw = LOOKUP.unreflect(setter); + Class ev = erase(valueType); + if (setter.getReturnType() == void.class) { + MethodHandle keepReceiver = MethodHandles.dropArguments( + MethodHandles.identity(Object.class), 1, ev); + return MethodHandles.foldArguments(keepReceiver, + raw.asType(MethodType.methodType(void.class, Object.class, ev))); + } + return raw.asType(MethodType.methodType(Object.class, Object.class, ev)); + } + + /** Field store adapted to {@code (Object, eV)void}. */ + public static MethodHandle fieldSetter(Field field) throws IllegalAccessException { + return LOOKUP.unreflectSetter(field) + .asType(MethodType.methodType(void.class, Object.class, erase(field.getType()))); + } + + /** Getter adapted to {@code (Object)eV}. */ + public static MethodHandle getter(Method getter) throws IllegalAccessException { + return LOOKUP.unreflect(getter).asType( + MethodType.methodType(erase(getter.getReturnType()), Object.class)); + } + + /** Field load adapted to {@code (Object)eV}. */ + public static MethodHandle fieldGetter(Field field) throws IllegalAccessException { + return LOOKUP.unreflectGetter(field) + .asType(MethodType.methodType(erase(field.getType()), Object.class)); + } + + /** No-arg constructor adapted to {@code ()Object}. */ + public static MethodHandle defaultConstructor(Constructor ctor) + throws IllegalAccessException { + return LOOKUP.unreflectConstructor(ctor) + .asType(MethodType.methodType(Object.class)); + } + + /** Constructor adapted to {@code (e1..en)Object}. */ + public static MethodHandle constructor(Constructor ctor) throws IllegalAccessException { + MethodHandle raw = LOOKUP.unreflectConstructor(ctor); + Class[] params = ctor.getParameterTypes(); + Class[] erased = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + erased[i] = erase(params[i]); + } + return raw.asType(MethodType.methodType(Object.class, erased)); + } + + /** Method adapted to {@code (Object)Object} (build methods). */ + public static MethodHandle unary(Method method) throws IllegalAccessException { + return LOOKUP.unreflect(method) + .asType(MethodType.methodType(Object.class, Object.class)); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ReaderCodegen.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ReaderCodegen.java new file mode 100644 index 000000000..c9155b9a8 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ReaderCodegen.java @@ -0,0 +1,214 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +import tools.jackson.core.sym.PropertyNameMatcher; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.deser.ValueInstantiator; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.databind.deser.bean.BeanDeserializerBase; +import tools.jackson.databind.deser.bean.BeanPropertyMap; +import tools.jackson.databind.deser.bean.BuilderBasedDeserializer; + +/** + * Defines the hidden-class deserializer for one bean: assembles the class + * data (the name matcher, stock properties, child codecs and member handles, + * as named constants), has {@code BeanReaderGenerator} emit the class bytes, + * and defines the class in this package. Member access and construction go + * through constant MethodHandles with erased descriptors (see + * {@link MemberHandles}), so the generated bytecode never names the bean + * class and every codec defines in the module's own context, whatever the + * bean's package, module, or classloader. + */ +public final class ReaderCodegen +{ + public enum Kind { STRING, INT, LONG, BOOLEAN, CHILD, STOCK } + + // How a generated codec treats an active view. NONE: views cannot affect + // this bean (the stock (de)serializer would ignore them too), no view code. + // DELEGATE: an active view hands the whole call to the stock fallback + // (beans with more than 64 properties). MASK: the codec resolves a cached + // per-view visibility bitmask and each property arm tests its bit, so + // view-active calls keep the generated fast path. + public enum ViewStrategy { NONE, DELEGATE, MASK } + + // One arm per property. handle stores the value into the bean: + // (Object, eV)void in POJO mode, (Object, eV)Object in builder mode (the + // returned builder is kept), and null in record mode (values collect into + // locals) and for STOCK arms. type is the record component type in record + // mode and the child value type for CHILD; child is the linked generated + // codec for CHILD (a GeneratedReaderBase or GeneratedBuilderBase). + public record GenProp(String name, Kind kind, SettableBeanProperty stock, + Class type, ValueDeserializer child, MethodHandle handle) { + public GenProp(String name, Kind kind, SettableBeanProperty stock) { + this(name, kind, stock, null, null, null); + } + } + + /** How the generated deserialize method obtains the value. */ + public enum Mode { + /** POJO, built by the no-arg constructor handle. */ + CONSTRUCTOR, + /** POJO, built by ValueInstantiator.createUsingDefault. */ + INSTANTIATOR, + /** Builder created by the instantiator, finished by the build method. */ + BUILDER, + /** Record: typed locals, then the canonical constructor handle. */ + RECORD + } + + /** + * The construction side of a codec: the handle is the no-arg constructor + * ({@code ()Object}), the build method ({@code (Object)Object}) or the + * canonical constructor ({@code (e1..en)Object}); the instantiator + * creates the bean (INSTANTIATOR) or the builder (BUILDER). + */ + public record Construction(Mode mode, MethodHandle handle, ValueInstantiator instantiator) { + public static Construction constructor(MethodHandle ctor) { + return new Construction(Mode.CONSTRUCTOR, ctor, null); + } + + public static Construction instantiator(ValueInstantiator inst) { + return new Construction(Mode.INSTANTIATOR, null, inst); + } + + public static Construction builder(ValueInstantiator inst, MethodHandle build) { + return new Construction(Mode.BUILDER, build, inst); + } + + public static Construction record(MethodHandle ctor) { + return new Construction(Mode.RECORD, ctor, null); + } + } + + /** + * Everything the bytecode emitter needs. The String arrays hold the + * class-data entry name per arm ({@code null} where an arm has no such + * entry); matchArms gives the arm for each index of the name matcher (an + * arm can have several names, aliases, or none, an ignored record + * component); localNames are the record component names (record mode + * only), which name the component locals in the debug table. + */ + record Plan(String className, List props, String[] stockName, + String[] childName, String[] handleName, Mode mode, boolean injectables, + ViewStrategy views, int[] matchArms, String[] localNames) {} + + private ReaderCodegen() {} + + /** + * Generates and defines the codec, or returns {@code null} when the + * generated code would be too large for the JIT to compile (see + * {@link ClassFileEmitters#MAX_METHOD_BYTES}). + */ + @SuppressWarnings("unchecked") + public static ValueDeserializer generate(Class beanClass, List props, + PropertyNameMatcher matcher, int[] matchArms, BeanDeserializerBase source, + BeanPropertyMap beanProps, Construction construction, boolean injectables, + ViewStrategy views, String[] localNames) + throws ReflectiveOperationException { + Map classData = new LinkedHashMap<>(); + classData.put("matcher", matcher); + // Tier-A properties also carry their SettableBeanProperty: their + // VALUE_NULL branch runs through it, since null handling is + // configuration-dependent per property. + String[] stockName = new String[props.size()]; + String[] childName = new String[props.size()]; + String[] handleName = new String[props.size()]; + StringJoiner layout = new StringJoiner(","); + for (int i = 0; i < props.size(); i++) { + GenProp gp = props.get(i); + layout.add(gp.name() + "=" + gp.kind()); + stockName[i] = dataName(classData, gp.name() + "Prop"); + classData.put(stockName[i], gp.stock()); + if (gp.child() != null) { + childName[i] = dataName(classData, gp.name() + "Codec"); + classData.put(childName[i], gp.child()); + } + if (gp.handle() != null) { + handleName[i] = dataName(classData, gp.name() + "Set"); + classData.put(handleName[i], gp.handle()); + } + } + Mode mode = construction.mode(); + switch (mode) { + case CONSTRUCTOR, RECORD -> classData.put("constructor", construction.handle()); + case INSTANTIATOR -> classData.put("instantiator", construction.instantiator()); + case BUILDER -> { + classData.put("instantiator", construction.instantiator()); + classData.put("buildMethod", construction.handle()); + } + } + + MethodHandles.Lookup definer = MethodHandles.lookup(); + // A hidden class must be named in its define context's package. + String className = definer.lookupClass().getPackageName() + + ".BBReader_" + codecName(beanClass); + byte[] bytes = ClassFileEmitters.emit(new Plan(className, props, stockName, childName, + handleName, mode, injectables, views, matchArms, localNames)); + if (bytes == null) { + CodegenDebug.log(() -> "reader gate: a generated method is over " + + ClassFileEmitters.MAX_METHOD_BYTES + " bytes"); + return null; + } + CodegenDump.dump(beanClass, "reader", bytes); + // No ClassOption.STRONG: the codec instance held by the mapper's + // deserializer cache anchors the class, so codecs unload with the + // mapper instead of pinning metaspace for the loader's lifetime. + // Module-context defines never legitimately fail: any throw here is a + // generator bug, surfaced through the placeholder's failure handling. + MethodHandles.Lookup hidden = definer.defineHiddenClassWithClassData( + bytes, Map.copyOf(classData), true); + // The codec is a copy of the resolved source (see the generated + // reader base classes). + Class sourceType = (mode == Mode.BUILDER) + ? BuilderBasedDeserializer.class : BeanDeserializer.class; + MethodHandle ctor = hidden.findConstructor(hidden.lookupClass(), + MethodType.methodType(void.class, sourceType, BeanPropertyMap.class, + String.class)); + try { + return (ValueDeserializer) ctor.invoke(source, beanProps, layout.toString()); + } catch (Throwable t) { + throw new IllegalStateException("cannot instantiate generated codec", t); + } + } + + // The bean's short name without Class.getSimpleName(): that call consults + // InnerClasses metadata, which raises IncompatibleClassChangeError for a + // member class redefined in a foreign classloader, and such beans + // accelerate. Class.getName() reads no metadata. + static String codecName(Class beanClass) { + String name = beanClass.getName(); + return name.substring(name.lastIndexOf('.') + 1); + } + + // Condy names are JVM unqualified names ('.', ';', '[', '/' forbidden) and + // JSON property names are arbitrary, so sanitize and dedupe. + static String dataName(Map data, String base) { + String name = unqualifiedName(base); + String candidate = name; + for (int n = 2; data.containsKey(candidate); n++) { + candidate = name + "$" + n; + } + return candidate; + } + + // A JVM unqualified name (JVMS 4.2.2) derived from an arbitrary string. + static String unqualifiedName(String base) { + if (base.isEmpty()) { + return "$"; + } + StringBuilder sb = new StringBuilder(base.length()); + for (int i = 0; i < base.length(); i++) { + char c = base.charAt(i); + sb.append(c == '.' || c == ';' || c == '[' || c == '/' ? '$' : c); + } + return sb.toString(); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/WriterCodegen.java b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/WriterCodegen.java new file mode 100644 index 000000000..490b9e10d --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/WriterCodegen.java @@ -0,0 +1,140 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; + +import tools.jackson.core.SerializableString; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.ser.PropertyWriter; +import tools.jackson.databind.ser.bean.BeanSerializerBase; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.ViewStrategy; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; + +/** + * Defines the hidden-class serializer for one bean: assembles the class data + * (pre-encoded names, stock writers, child writers and getter handles, as + * named constants), has {@code BeanWriterGenerator} emit the class bytes, and + * defines the class in this package. Property values load through constant + * (Object)eV MethodHandles (see {@link MemberHandles}), so the generated + * bytecode never names the bean class. + */ +public final class WriterCodegen +{ + public enum WKind { STRING, INT, LONG, BOOLEAN, CHILD, STOCK } + + // handle loads the property value from the bean, erased to (Object)eV + // (a getter call or a field read); null for STOCK. + public record GenWProp(WKind kind, PropertyWriter stock, + SerializableString name, GeneratedWriterBase child, MethodHandle handle) {} + + /** + * Everything the bytecode emitter needs; the String arrays hold the + * class-data entry name per property ({@code null} where a property has + * no such entry). nativePolyWrites: emit a serializeWithType override + * matching stock BeanSerializerBase's WritableTypeId flow; false (a + * {@code @JsonTypeId} property exists, whose value feeds the type id) + * keeps the base forwarding. + */ + record Plan(String className, List props, String[] stockName, + String[] nameName, String[] childName, String[] handleName, + ViewStrategy views, boolean includeByDefault, boolean nativePolyWrites) {} + + /** + * The binary name of the class that holds the write helpers of all + * generated writers. It is defined once in this package, before the + * first writer, and the writers call it by name. + */ + static final String HELPERS_CLASS = WriterCodegen.class.getPackageName() + ".WriteHelpers"; + + private WriterCodegen() {} + + /** + * Generates and defines the writer, or returns {@code null} when the + * generated code would be too large for the JIT to compile (see + * {@link ClassFileEmitters#MAX_METHOD_BYTES}). + */ + @SuppressWarnings("unchecked") + public static ValueSerializer generate(Class beanClass, List props, + BeanSerializerBase fallback, ViewStrategy views, boolean includeByDefault, + boolean nativePolyWrites) + throws ReflectiveOperationException { + Map classData = new LinkedHashMap<>(); + String[] stockName = new String[props.size()]; + String[] nameName = new String[props.size()]; + String[] childName = new String[props.size()]; + String[] handleName = new String[props.size()]; + StringJoiner layout = new StringJoiner(","); + for (int i = 0; i < props.size(); i++) { + GenWProp p = props.get(i); + String base = p.stock().getName(); + layout.add(base + "=" + p.kind()); + stockName[i] = ReaderCodegen.dataName(classData, base + "Writer"); + classData.put(stockName[i], p.stock()); + if (p.name() != null) { + nameName[i] = ReaderCodegen.dataName(classData, base + "Name"); + classData.put(nameName[i], p.name()); + } + if (p.child() != null) { + childName[i] = ReaderCodegen.dataName(classData, base + "Codec"); + classData.put(childName[i], p.child()); + } + if (p.handle() != null) { + handleName[i] = ReaderCodegen.dataName(classData, base + "Get"); + classData.put(handleName[i], p.handle()); + } + } + + HelpersHolder.ensureDefined(); + MethodHandles.Lookup definer = MethodHandles.lookup(); + // A hidden class must be named in its define context's package. + String className = definer.lookupClass().getPackageName() + + ".BBWriter_" + ReaderCodegen.codecName(beanClass); + byte[] bytes = ClassFileEmitters.emit(new Plan(className, props, stockName, nameName, + childName, handleName, views, includeByDefault, nativePolyWrites)); + if (bytes == null) { + CodegenDebug.log(() -> "writer gate: a generated method is over " + + ClassFileEmitters.MAX_METHOD_BYTES + " bytes"); + return null; + } + CodegenDump.dump(beanClass, "writer", bytes); + // Module-context defines never legitimately fail: any throw here is a + // generator bug, surfaced through the modifier's failure handling. + MethodHandles.Lookup hidden = definer.defineHiddenClassWithClassData( + bytes, Map.copyOf(classData), true); + MethodHandle ctor = hidden.findConstructor(hidden.lookupClass(), + MethodType.methodType(void.class, BeanSerializerBase.class, String.class)); + try { + return (ValueSerializer) ctor.invoke(fallback, layout.toString()); + } catch (Throwable t) { + throw new IllegalStateException("cannot instantiate generated writer", t); + } + } + + // A normal class, not a hidden one, so that the hidden writers can name + // it. The class initialization defines it at most once per class loader + // of Blackbird; a second define of the same name would fail. + private static final class HelpersHolder + { + private static final Class DEFINED = define(); + + static void ensureDefined() { + Objects.requireNonNull(DEFINED); + } + + private static Class define() { + byte[] bytes = ClassFileEmitters.emitWriteHelpers(); + CodegenDump.dump(HELPERS_CLASS, bytes); + try { + return MethodHandles.lookup().defineClass(bytes); + } catch (IllegalAccessException e) { + throw new IllegalStateException("cannot define " + HELPERS_CLASS, e); + } + } + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBBuilderPlaceholder.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBBuilderPlaceholder.java new file mode 100644 index 000000000..aaef2b5c8 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBBuilderPlaceholder.java @@ -0,0 +1,56 @@ +package tools.jackson.module.blackbird.deser; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.bean.BuilderBasedDeserializer; +import tools.jackson.module.blackbird.codegen.CodegenDebug; + +/** + * The builder-based counterpart of {@link BBReaderPlaceholder}: a copy of + * the stock {@link BuilderBasedDeserializer} that generates the builder + * codec when resolved. + */ +final class BBBuilderPlaceholder extends BuilderBasedDeserializer +{ + private final boolean _declaresViews; + + private volatile ValueDeserializer _codec; + + BBBuilderPlaceholder(BuilderBasedDeserializer src, boolean declaresViews) { + super(src); + _declaresViews = declaresViews; + } + + @Override + public void resolve(DeserializationContext ctxt) { + CodegenDebug.log(() -> "reader resolve " + handledType().getName()); + super.resolve(ctxt); + // A builder without a build method (the builder is the value) keeps + // the stock path. + if (_buildMethod == null) { + return; + } + _codec = BBReaderFactory.tryGenerate(new BBReaderFactory.Source(this, _anySetter, + _injectables != null, _beanProperties, _propertyBasedCreator, + _nonStandardCreation, + (_delegateDeserializer != null) || (_arrayDelegateDeserializer != null), + _externalTypeIdHandler != null, _unwrappedPropertyHandler != null, + _buildMethod), + ctxt, _declaresViews); + } + + @Override + public ValueDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) { + ValueDeserializer contextual = super.createContextual(ctxt, property); + ValueDeserializer codec = _codec; + return (contextual == this && codec != null) ? codec : contextual; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) { + ValueDeserializer codec = _codec; + return (codec != null) ? codec.deserialize(p, ctxt) : super.deserialize(p, ctxt); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBDeserializerModifier.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBDeserializerModifier.java index 7ab360529..65d949d25 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBDeserializerModifier.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBDeserializerModifier.java @@ -1,282 +1,110 @@ package tools.jackson.module.blackbird.deser; -import java.lang.invoke.LambdaConversionException; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; import java.util.function.Function; -import java.util.function.ObjIntConsumer; -import java.util.function.ObjLongConsumer; -import java.util.function.UnaryOperator; - -import tools.jackson.databind.*; -import tools.jackson.databind.deser.*; -import tools.jackson.databind.deser.impl.MethodProperty; -import tools.jackson.databind.deser.std.StdValueInstantiator; -import tools.jackson.databind.introspect.*; -import tools.jackson.databind.util.ClassUtil; -import tools.jackson.module.blackbird.util.ReflectionHack; +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.databind.deser.bean.BuilderBasedDeserializer; +import tools.jackson.databind.introspect.BeanPropertyDefinition; +import tools.jackson.module.blackbird.codegen.CodegenDebug; +import tools.jackson.module.blackbird.codegen.CodegenFallbacks; + +/** + * Wraps eligible stock bean deserializers in a placeholder that generates a + * per-bean codec at resolve time (ClassFile API hidden class). Every gate that + * fails leaves the stock deserializer in place, so behavior never changes for + * beans the generator does not fully understand. Most gates run at resolve + * time, on the resolved stock deserializer (see BBReaderFactory); this + * modifier only filters out what cannot become a codec at all. + */ public class BBDeserializerModifier extends ValueDeserializerModifier { private static final long serialVersionUID = 1L; - private static final MethodHandle TRAMPOLINE, BOOLEAN_TRAMPOLINE, LONG_TRAMPOLINE, INT_TRAMPOLINE; - - static { - try { - TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "trampoline", - MethodType.methodType(void.class, BiFunction.class, Object.class, Object.class)); - BOOLEAN_TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "booleanTrampoline", - MethodType.methodType(void.class, ObjBooleanBiFunction.class, Object.class, boolean.class)); - LONG_TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "longTrampoline", - MethodType.methodType(void.class, ObjLongBiFunction.class, Object.class, long.class)); - INT_TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "intTrampoline", - MethodType.methodType(void.class, ObjIntBiFunction.class, Object.class, int.class)); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } - private final Function, Lookup> _lookups; - private final UnaryOperator _accessGrant; + // Kept for the released BlackbirdModule(Function) contract; the codec + // no longer needs it. Member access rides databind's own fixAccess (see + // MemberHandles), so a user lookup is not required for acceleration. + private final Function, MethodHandles.Lookup> _lookups; - public BBDeserializerModifier(Function, MethodHandles.Lookup> lookups, UnaryOperator accessGrant) - { + public BBDeserializerModifier(Function, MethodHandles.Lookup> lookups) { _lookups = lookups; - _accessGrant = accessGrant; } - /* - /********************************************************************** - /* BeanDeserializerModifier methods - /********************************************************************** - */ - @Override - public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, - BeanDescription.Supplier beanDescRef, BeanDeserializerBuilder builder) + public ValueDeserializer modifyDeserializer(DeserializationConfig config, + BeanDescription.Supplier beanDescRef, ValueDeserializer deserializer) { - final Class beanClass = beanDescRef.getBeanClass(); - MethodHandles.Lookup lookup = _lookups.apply(beanClass); - if (lookup == null) { - return builder; - } + // Gate failures of any kind leave the stock deserializer in place. The + // reflective gates can throw for exotic classes (a bean from a foreign + // classloader with inconsistent InnerClasses metadata raises + // IncompatibleClassChangeError from getEnclosingClass), and an + // acceleration modifier must never break a bean stock databind handles. try { - lookup = ReflectionHack.privateLookupIn(beanClass, lookup); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - /* Hmmh. Can we access stuff from private classes? - * Possibly, if we can use parent class loader. - * (should probably skip all non-public?) - */ - if (Modifier.isPrivate(beanClass.getModifiers())) { // TODO?? - return builder; + return doModify(config, beanDescRef, deserializer); + } catch (RuntimeException | LinkageError e) { + CodegenFallbacks.gateFailure(beanDescRef.getBeanClass(), e); + return deserializer; } - lookup = _accessGrant.apply(lookup); - List> newProps = findOptimizableProperties( - lookup, config, builder.getProperties()); - // and if we found any, create mutator proxy, replace property objects - if (!newProps.isEmpty()) { - for (OptimizedSettableBeanProperty prop : newProps) { - builder.addOrReplaceProperty(prop, true); - } - } - // Second thing: see if we could (re)generate Creator(s): - ValueInstantiator inst = builder.getValueInstantiator(); - /* Hmmh. Probably better to require exact default implementation - * and not sub-class; chances are sub-class uses its own - * construction anyway. - */ - if (inst.getClass() == StdValueInstantiator.class) { - // also, only override if using default creator (no-arg ctor, no-arg static factory) - if (inst.canCreateUsingDefault() || inst.canCreateFromObjectWith()) { - inst = new CreatorOptimizer(beanClass, lookup, (StdValueInstantiator) inst).createOptimized(); - if (inst != null) { - builder.setValueInstantiator(inst); - } - } - } - - // also: may want to replace actual BeanDeserializer as well? For this, need to replace builder - // (but only if builder is the original standard one; don't want to break other impls) - if (builder.getClass() == BeanDeserializerBuilder.class) { - return new SuperSonicDeserializerBuilder(builder); - } - return builder; } - /* - /********************************************************************** - /* Internal methods - /********************************************************************** - */ - - protected List> findOptimizableProperties( - Lookup lookup, DeserializationConfig config, - Iterator propIterator) + private ValueDeserializer doModify(DeserializationConfig config, + BeanDescription.Supplier beanDescRef, ValueDeserializer deserializer) { - ArrayList> newProps = new ArrayList>(); - - // Ok, then, find any properties for which we could generate accessors - while (propIterator.hasNext()) { - try { - nextProperty(propIterator.next(), lookup, newProps); - } catch (Throwable e) { - if (e instanceof Error) { - throw (Error) e; - } - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } - throw new RuntimeException(e); - } - } - return newProps; - } - - @SuppressWarnings("unchecked") - private void nextProperty(SettableBeanProperty prop, - Lookup lookup, - ArrayList> newProps) throws Throwable - { - AnnotatedMember member = prop.getMember(); - Member jdkMember = member.getMember(); - - // if we ever support virtual properties, this would be null, so check, skip - if (jdkMember == null) { - return; + if (deserializer.getClass() != BeanDeserializer.class + && deserializer.getClass() != BuilderBasedDeserializer.class) { + return skip(beanDescRef, deserializer, "not a stock bean deserializer"); } - // First: we can't access private fields or methods.... - if (Modifier.isPrivate(jdkMember.getModifiers())) { - return; + if (Boolean.TRUE.equals(config.getDefaultMergeable())) { + return skip(beanDescRef, deserializer, "mapper default mergeable"); } - // (although, interestingly enough, can seem to access private classes...) - - // 30-Jul-2012, tatu: [module-afterburner#6]: Needs to skip custom deserializers, if any. - if (prop.hasValueDeserializer()) { - if (!isDefaultDeserializer(prop.getValueDeserializer())) { - return; - } + BeanDescription beanDesc = beanDescRef.get(); + Class beanClass = beanDesc.getBeanClass(); + // Non-static inner classes construct against an enclosing instance, + // which the generated loop does not model. The static check runs + // first: for a static member class redefined in a foreign classloader, + // getEnclosingClass raises IncompatibleClassChangeError, and such + // beans accelerate. + if (!Modifier.isStatic(beanClass.getModifiers()) + && beanClass.getEnclosingClass() != null) { + return skip(beanDescRef, deserializer, "non-static inner class"); } - - MethodHandle setter; - Class type; - if (jdkMember instanceof Method && prop instanceof MethodProperty) { - final Method method = (Method) jdkMember; - setter = lookup.unreflect(method); - type = ((AnnotatedMethod) member).getRawParameterType(0); - } else { - return; - //setter = lookup.unreflectGetter((Field) jdkMember); + if (deserializer.getClass() == BeanDeserializer.class && !beanClass.isRecord() + && Modifier.isAbstract(beanClass.getModifiers())) { + return skip(beanDescRef, deserializer, "abstract"); } - - if (type.isPrimitive()) { - if (type == Integer.TYPE) { - newProps.add(new SettableIntProperty(prop, - createSetter(lookup, ObjIntConsumer.class, ObjIntBiFunction.class, - INT_TRAMPOLINE, int.class, setter))); - } else if (type == Long.TYPE) { - newProps.add(new SettableLongProperty(prop, - createSetter(lookup, ObjLongConsumer.class, ObjLongBiFunction.class, - LONG_TRAMPOLINE, long.class, setter))); - } else if (type == Boolean.TYPE) { - newProps.add(new SettableBooleanProperty(prop, - createSetter(lookup, ObjBooleanConsumer.class, ObjBooleanBiFunction.class, - BOOLEAN_TRAMPOLINE, boolean.class, setter))); + // Whether the bean or any property declares @JsonView explicitly: the + // resolved properties cannot tell a declared view from the empty view + // set that disabled DEFAULT_VIEW_INCLUSION forces onto unannotated + // properties, so the property definitions decide here. + boolean declaresViews = config.getAnnotationIntrospector() + .findViews(config, beanDesc.getClassInfo()) != null; + for (BeanPropertyDefinition def : beanDesc.findProperties()) { + if (def.findViews() != null) { + declaresViews = true; + } + if (def.getPrimaryMember() != null + && config.getAnnotationIntrospector() + .findUnwrappingNameTransformer(config, def.getPrimaryMember()) != null) { + return skip(beanDescRef, deserializer, "unwrapped property"); } - } else { - if (type == String.class) { - newProps.add(new SettableStringProperty(prop, - createSetter(lookup, BiConsumer.class, BiFunction.class, TRAMPOLINE, Object.class, setter))); - } else { - newProps.add(new SettableObjectProperty(prop, - createSetter(lookup, BiConsumer.class, BiFunction.class, TRAMPOLINE, Object.class, setter))); + if (def.getMetadata() != null && def.getMetadata().getMergeInfo() != null) { + return skip(beanDescRef, deserializer, "property merge"); } } - } - - private T createSetter(Lookup lookup, Class iface, Class thunkType, MethodHandle trampoline, Class valueType, MethodHandle setter) - throws Throwable, LambdaConversionException { - if (setter.type().returnType() == void.class) { - return iface.cast(LambdaMetafactory.metafactory( - lookup, - "accept", - MethodType.methodType(iface), - MethodType.methodType(void.class, Object.class, valueType), - setter, - setter.type()) - .getTarget().invoke()); + if (deserializer instanceof BuilderBasedDeserializer builder) { + return new BBBuilderPlaceholder(builder, declaresViews); } - Object builtThunk = LambdaMetafactory.metafactory( - lookup, - "apply", - MethodType.methodType(thunkType), - MethodType.methodType(Object.class, Object.class, valueType), - setter, - setter.type()) - .getTarget().invoke(); - return iface.cast(LambdaMetafactory.metafactory( - MethodHandles.lookup(), - "accept", - MethodType.methodType(iface, thunkType), - MethodType.methodType(void.class, Object.class, valueType), - trampoline, - MethodType.methodType(void.class, Object.class, valueType)) - .getTarget().invoke(builtThunk)); - } - - /** - * Helper method used to check whether given deserializer is the default - * deserializer implementation: this is necessary to avoid overriding other - * kinds of deserializers. - */ - protected boolean isDefaultDeserializer(ValueDeserializer deser) { - return ClassUtil.isJacksonStdImpl(deser) - // 07-May-2018, tatu: Probably can't happen but just in case - || (deser instanceof SuperSonicBeanDeserializer); - - } - - // These trampolines adapt BiFunction-like setters (i.e. returns a value) - // to BiConsumer style through a generated thunk lambda. - - @FunctionalInterface - public interface ObjIntBiFunction { - Object apply(Object bean, int value); - } - - @FunctionalInterface - public interface ObjLongBiFunction { - Object apply(Object bean, long value); - } - - @FunctionalInterface - public interface ObjBooleanBiFunction { - Object apply(Object bean, boolean value); - } - - static void intTrampoline(ObjIntBiFunction thunk, Object bean, int value) { - thunk.apply(bean, value); - } - - static void longTrampoline(ObjLongBiFunction thunk, Object bean, long value) { - thunk.apply(bean, value); - } - - static void booleanTrampoline(ObjBooleanBiFunction thunk, Object bean, boolean value) { - thunk.apply(bean, value); + return new BBReaderPlaceholder((BeanDeserializer) deserializer, declaresViews); } - static void trampoline(BiFunction thunk, Object bean, Object value) { - thunk.apply(bean, value); + private static ValueDeserializer skip(BeanDescription.Supplier beanDescRef, + ValueDeserializer deserializer, String reason) { + CodegenDebug.logSkip("reader", beanDescRef.getBeanClass(), reason); + return deserializer; } } diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderFactory.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderFactory.java new file mode 100644 index 000000000..4f823eec9 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderFactory.java @@ -0,0 +1,515 @@ +package tools.jackson.module.blackbird.deser; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.RecordComponent; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import tools.jackson.core.sym.PropertyNameMatcher; +import tools.jackson.core.util.Named; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.PropertyName; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.CreatorProperty; +import tools.jackson.databind.deser.SettableAnyProperty; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.deser.bean.BeanDeserializerBase; +import tools.jackson.databind.deser.bean.BeanPropertyMap; +import tools.jackson.databind.deser.bean.PropertyBasedCreator; +import tools.jackson.databind.deser.impl.MethodProperty; +import tools.jackson.databind.deser.std.StdValueInstantiator; +import tools.jackson.databind.introspect.AnnotatedConstructor; +import tools.jackson.databind.introspect.AnnotatedField; +import tools.jackson.databind.introspect.AnnotatedMethod; +import tools.jackson.module.blackbird.codegen.CodegenDebug; +import tools.jackson.module.blackbird.codegen.CodegenFallbacks; +import tools.jackson.module.blackbird.codegen.MemberHandles; +import tools.jackson.module.blackbird.codegen.ReaderCodegen; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.Construction; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.GenProp; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.Kind; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.ViewStrategy; +import tools.jackson.module.blackbird.internal.GeneratedBuilderBase; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; + +/** + * Builds a generated codec from a resolved stock bean deserializer, or returns + * null when the bean does not qualify. The codec handles a bean only when + * every feature of the bean has a generated path that matches stock; anything + * else keeps the stock deserializer (a gate) or the stock property inside the + * codec (a STOCK arm). Every decision stock databind has already made - which + * names are ignored or included, how names match (aliases, case + * insensitivity), which creator builds the bean - is read from the resolved + * stock deserializer, not computed again. Member access goes through + * {@link MemberHandles}, which reaches exactly what stock databind can + * invoke, so accessibility never gates a bean the stock path handles - an + * access failure demotes to stock, which fails the same way. + */ +final class BBReaderFactory +{ + private static final Set STOCK_SCALAR_DESERS = Set.of( + "tools.jackson.databind.deser.jdk.StringDeserializer", + "tools.jackson.databind.deser.jdk.NumberDeserializers$IntegerDeserializer", + "tools.jackson.databind.deser.jdk.NumberDeserializers$LongDeserializer", + "tools.jackson.databind.deser.jdk.NumberDeserializers$BooleanDeserializer"); + + private BBReaderFactory() {} + + /** + * The resolved stock state a codec is generated from, read by the + * placeholder (a stock deserializer subclass) from its own fields. + * buildMethod is non-null exactly for builder-based beans. + */ + record Source(BeanDeserializerBase deser, SettableAnyProperty anySetter, + boolean injectables, BeanPropertyMap beanProperties, PropertyBasedCreator creator, + boolean nonStandardCreation, boolean hasDelegateCreator, boolean hasExternalTypeId, + boolean hasUnwrapped, AnnotatedMethod buildMethod) {} + + static ValueDeserializer tryGenerate(Source stock, DeserializationContext ctxt, + boolean declaresViews) { + BeanDeserializerBase delegate = stock.deser(); + try { + ValueDeserializer codec = generate(stock, ctxt, declaresViews); + CodegenDebug.log(() -> "reader tryGenerate " + delegate.handledType().getName() + + " -> " + (codec == null ? "null (gated)" : codec.getClass().getName())); + return codec; + } catch (Throwable t) { + if (CodegenDebug.ENABLED) { + CodegenDebug.log(() -> "reader tryGenerate " + delegate.handledType().getName() + " threw:"); + t.printStackTrace(); + } + CodegenFallbacks.generationFailure(delegate.handledType(), t); + return null; + } + } + + private static ValueDeserializer gate(String reason) { + CodegenDebug.log(() -> "reader gate: " + reason); + return null; + } + + private static ValueDeserializer generate(Source stock, DeserializationContext ctxt, + boolean declaresViews) + throws ReflectiveOperationException { + BeanDeserializerBase delegate = stock.deser(); + // Deliberately no delegate.hasViews() gate: 3.x disables + // DEFAULT_VIEW_INCLUSION by default, which marks every bean as needing + // view processing; the generated codec instead checks + // ctxt.getActiveView() per call and delegates when a view is active. + if (delegate.getObjectIdReader(ctxt) != null) { + return gate("object id"); + } + Class beanClass = delegate.handledType(); + if (Modifier.isAbstract(beanClass.getModifiers())) { + return gate("abstract"); + } + if (stock.buildMethod() != null) { + return generateBuilder(delegate, ctxt, beanClass, stock, declaresViews); + } + if (beanClass.isRecord()) { + return generateRecord(delegate, ctxt, beanClass, stock, declaresViews); + } + // Stock builds the bean with the default creator and runs the + // property loop only without "non-standard creation": a delegating + // or property-based creator (also beside a default constructor), an + // external type id, or unwrapped properties all take other stock + // paths, which the generated loop does not have. + if (stock.nonStandardCreation()) { + return gate("non-standard creation (creator, external type id or unwrapped)"); + } + // A constant constructor handle is only equivalent when the + // instantiator is the stock one and its default creator IS the + // no-arg constructor (its failure handling is mirrored for exactly + // that instantiator). Everything else that still reports + // canCreateUsingDefault - a no-arg @JsonCreator factory, a custom + // instantiator, a constructor the lookup cannot unreflect - constructs + // through the instantiator held in class data instead. + Construction construction = Construction.instantiator(delegate.getValueInstantiator()); + if (delegate.getValueInstantiator().getClass() == StdValueInstantiator.class + && delegate.getValueInstantiator().getDefaultCreator() + instanceof AnnotatedConstructor ac) { + try { + construction = Construction.constructor( + MemberHandles.defaultConstructor(ac.getAnnotated())); + } catch (IllegalAccessException e) { + CodegenDebug.log(() -> "reader demote: ctor access, instantiator mode"); + } + } + return generateWithStockMatcher(delegate, ctxt, beanClass, stock, construction, + declaresViews, null); + } + + // Builder-based beans: the stock ValueInstantiator creates the builder, + // properties apply through normalized (Object, eV)Object setter handles, + // and the build method - a constant (Object)Object handle - produces the + // value. + private static ValueDeserializer generateBuilder(BeanDeserializerBase delegate, + DeserializationContext ctxt, Class builderClass, Source stock, + boolean declaresViews) + throws ReflectiveOperationException { + if (stock.nonStandardCreation()) { + return gate("builder with non-standard creation"); + } + Method build = stock.buildMethod().getAnnotated(); + if (build == null) { + return gate("builder without build method"); + } + if (Modifier.isPrivate(builderClass.getModifiers()) || build.getParameterCount() != 0) { + return gate("builder shape"); + } + MethodHandle buildMH; + try { + buildMH = MemberHandles.unary(build); + } catch (IllegalAccessException e) { + return gate("build method access"); + } + return generateWithStockMatcher(delegate, ctxt, builderClass, stock, + Construction.builder(delegate.getValueInstantiator(), buildMH), declaresViews, + builderClass); + } + + // POJO and builder codecs reuse the stock name matcher: the bean + // property map's matcher, with aliases after the primary names and the + // stock case-insensitivity. Stock createContextual initializes it right + // after resolve; initializing it here builds the same matcher. + private static ValueDeserializer generateWithStockMatcher( + BeanDeserializerBase delegate, DeserializationContext ctxt, Class beanClass, + Source stock, Construction construction, + boolean declaresViews, Class builderClass) + throws ReflectiveOperationException { + BeanPropertyMap beanProps = stock.beanProperties(); + beanProps.initMatcher(ctxt.tokenStreamFactory()); + SettableBeanProperty[] byIndex = beanProps.getNameMatcherProperties(); + int primaries = beanProps.size(); + if (primaries == 0) { + return gate("no props"); + } + List props = new ArrayList<>(primaries); + for (int i = 0; i < primaries; i++) { + SettableBeanProperty prop = byIndex[i]; + if (prop.getMetadata().getMergeInfo() != null) { + return gate("merge prop " + prop.getName()); + } + props.add(builderClass == null ? classify(prop) + : classifyBuilder(prop, builderClass)); + } + int[] matchArms = matchArms(byIndex, primaries); + if (matchArms == null) { + return gate("matcher layout"); + } + return ReaderCodegen.generate(beanClass, props, beanProps.getNameMatcher(), matchArms, + delegate, beanProps, construction, stock.injectables(), + viewStrategy(ctxt, props, declaresViews), null); + } + + // The stock matcher has one index per primary name (the arms, in order), + // then alias entries that each hold their primary property; returns the + // arm per matcher index. + private static int[] matchArms(SettableBeanProperty[] byIndex, int primaries) { + Map arm = new IdentityHashMap<>(); + int[] out = new int[byIndex.length]; + for (int i = 0; i < byIndex.length; i++) { + Integer a = (i < primaries) ? Integer.valueOf(i) : arm.get(byIndex[i]); + if (a == null) { + return null; + } + arm.putIfAbsent(byIndex[i], a); + out[i] = a; + } + return out; + } + + // Beans that declare no @JsonView anywhere (read from the property + // definitions at modify time; the resolved matchers cannot distinguish a + // declared view from the empty set that disabled DEFAULT_VIEW_INCLUSION + // forces onto every unannotated property) emit no per-arm view code: + // with inclusion on, views cannot affect them at all (NONE); with it off, + // an active view hides every property, so the degenerate view-active call + // DELEGATEs to stock and the no-view hot path stays free of mask tests. + // Declared views take MASK (the fast path under views) up to 64 + // properties, DELEGATE beyond. Mask visibility is read per property from + // SettableBeanProperty.visibleInView, so semantics stay exactly stock. + private static ViewStrategy viewStrategy(DeserializationContext ctxt, + List props, boolean declaresViews) { + if (!declaresViews) { + return ctxt.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION) + ? ViewStrategy.NONE : ViewStrategy.DELEGATE; + } + return (props.size() > 64) ? ViewStrategy.DELEGATE : ViewStrategy.MASK; + } + + // Records collect components into typed locals and construct through the + // canonical constructor; every property must be a plain CreatorProperty + // whose creator index and type line up with the record components. + private static ValueDeserializer generateRecord(BeanDeserializerBase delegate, + DeserializationContext ctxt, Class beanClass, Source stock, + boolean declaresViews) + throws ReflectiveOperationException { + PropertyBasedCreator creator = stock.creator(); + // Stock prefers a delegating creator to the property-based one, and + // external type ids, unwrapped properties, any-setters (also as a + // creator parameter) and injectables take paths the typed-locals loop + // does not model. + if (creator == null || stock.hasDelegateCreator() || stock.hasExternalTypeId() + || stock.hasUnwrapped() || stock.anySetter() != null + || stock.injectables()) { + return gate("record creation is not the plain canonical constructor"); + } + // The generated code constructs through the canonical constructor and + // mirrors the failure handling of the stock instantiator. A + // @JsonCreator factory or a custom instantiator must construct + // through the instantiator, so such records stay on the stock path. + // An AnnotatedConstructor that also passes the per-component index + // and type match below is the canonical constructor: a second + // constructor with the same signature cannot exist. + if (delegate.getValueInstantiator().getClass() != StdValueInstantiator.class + || !(delegate.getValueInstantiator().getWithArgsCreator() + instanceof AnnotatedConstructor ctor)) { + return gate("record creator is not the constructor"); + } + RecordComponent[] comps = beanClass.getRecordComponents(); + // The seen-component mask in the generated code is one long. + if (comps.length > 64 || comps.length == 0) { + return gate("record size"); + } + SettableBeanProperty[] creatorProps = creator.allPropertiesInOrder(); + if (creatorProps.length != comps.length) { + return gate("record creator arity"); + } + // Stock reads names through the creator, then through the bean + // properties for non-creator ones; records whose bean properties are + // not all creator properties keep the stock path. + for (SettableBeanProperty prop : stock.beanProperties()) { + if (!(prop instanceof CreatorProperty)) { + return gate("record with non-creator property " + prop.getName()); + } + } + for (int i = 0; i < comps.length; i++) { + SettableBeanProperty prop = creatorProps[i]; + // Wrapper classes (managed references among them) do work at + // construction that the typed-locals loop does not. + if (prop == null || prop.getClass() != CreatorProperty.class + || prop.getCreatorIndex() != i + || prop.getMetadata().getMergeInfo() != null + || prop.getInjectionDefinition() != null + || prop.getType().getRawClass() != comps[i].getType()) { + return gate("record component " + i); + } + } + MethodHandle recordCtor; + try { + recordCtor = MemberHandles.constructor(ctor.getAnnotated()); + } catch (IllegalAccessException e) { + // The lookup cannot reach the canonical constructor, so databind + // could not open it either: demote, stock fails the same way at + // first use. + return gate("record ctor access"); + } + List props = new ArrayList<>(comps.length); + String[] localNames = new String[comps.length]; + for (int i = 0; i < comps.length; i++) { + Class compType = comps[i].getType(); + SettableBeanProperty prop = creatorProps[i]; + localNames[i] = comps[i].getName(); + ValueDeserializer valueDeser = prop.getValueDeserializer(); + Kind kind; + ValueDeserializer child = null; + if (prop.getValueTypeDeserializer() != null) { + kind = Kind.STOCK; + } else if (isCodec(valueDeser)) { + kind = Kind.CHILD; + child = valueDeser; + } else { + kind = scalarKind(compType); + if (kind == null || valueDeser == null + || !STOCK_SCALAR_DESERS.contains(valueDeser.getClass().getName())) { + kind = Kind.STOCK; + } + } + props.add(new GenProp(prop.getName(), kind, prop, compType, child, null)); + } + // The stock fallback paths of the codec read non-creator names + // through the bean property matcher, which createContextual would + // initialize next. + BeanPropertyMap beanProps = stock.beanProperties(); + beanProps.initMatcher(ctxt.tokenStreamFactory()); + RecordNames names = recordNames(ctxt, creator, beanProps, props); + return ReaderCodegen.generate(beanClass, props, names.matcher(), names.matchArms(), + delegate, beanProps, Construction.record(recordCtor), false, + viewStrategy(ctxt, props, declaresViews), localNames); + } + + private record RecordNames(PropertyNameMatcher matcher, int[] matchArms) {} + + // Stock reads a record's names through PropertyBasedCreator + // .findCreatorProperty: a map of primary names and aliases (lower-cased + // with Locale.ROOT when case-insensitive) without the names to ignore. + // The matcher here holds exactly the names that map finds for each + // component. A component whose name it does not find (an ignored one) + // gets no name: the document's value takes the unknown path, as in + // stock, and the component keeps the stock absent value. + private static RecordNames recordNames(DeserializationContext ctxt, + PropertyBasedCreator creator, BeanPropertyMap beanProps, List props) { + List names = new ArrayList<>(); + List arms = new ArrayList<>(); + for (int i = 0; i < props.size(); i++) { + SettableBeanProperty prop = props.get(i).stock(); + if (creator.findCreatorProperty(prop.getName()) == prop) { + names.add(Named.fromString(prop.getName())); + arms.add(i); + } + } + for (int i = 0; i < props.size(); i++) { + SettableBeanProperty prop = props.get(i).stock(); + for (PropertyName alias : prop.findAliases(ctxt.getConfig())) { + String name = alias.getSimpleName(); + if (creator.findCreatorProperty(name) == prop) { + names.add(Named.fromString(name)); + arms.add(i); + } + } + } + // false: the matcher interns the names itself. + PropertyNameMatcher matcher = beanProps.isCaseInsensitive() + ? ctxt.tokenStreamFactory().constructCINameMatcher(names, false, Locale.ROOT) + : ctxt.tokenStreamFactory().constructNameMatcher(names, false); + int[] matchArms = new int[arms.size()]; + for (int k = 0; k < matchArms.length; k++) { + matchArms[k] = arms.get(k); + } + return new RecordNames(matcher, matchArms); + } + + // Generated stores are only equivalent for the plain stock property + // class (MethodProperty, which backs setters and fields alike): + // subclasses and wrappers (managed and back references, object id + // references, setterless and merging properties, user wrappers from + // updateBuilder) do their work in deserializeAndSet, so they keep it. + // A value type deserializer (polymorphic values, default typing) is + // applied only by the stock property. + private static GenProp classify(SettableBeanProperty prop) { + if (prop.getClass() != MethodProperty.class || prop.getValueTypeDeserializer() != null) { + return stock(prop); + } + Class raw = prop.getType().getRawClass(); + MethodHandle handle = storeHandle(prop, raw); + if (handle == null) { + return stock(prop); + } + return withValueKind(prop, raw, handle); + } + + // Builder setters must return void or the builder class exactly (the + // normalized handle keeps the fluent return as the new builder, which + // is what stock deserializeSetAndReturn does with a non-null result). + private static GenProp classifyBuilder(SettableBeanProperty prop, Class builderClass) { + if (prop.getClass() != MethodProperty.class || prop.getValueTypeDeserializer() != null) { + return stock(prop); + } + Class raw = prop.getType().getRawClass(); + Method setter = setterOf(prop, raw); + if (setter == null + || (setter.getReturnType() != void.class + && setter.getReturnType() != builderClass)) { + return stock(prop); + } + MethodHandle handle; + try { + handle = MemberHandles.builderSetter(setter, raw); + } catch (IllegalAccessException | RuntimeException e) { + return stock(prop); + } + return withValueKind(prop, raw, handle); + } + + private static GenProp withValueKind(SettableBeanProperty prop, Class raw, + MethodHandle handle) { + ValueDeserializer valueDeser = prop.getValueDeserializer(); + if (isCodec(valueDeser)) { + return new GenProp(prop.getName(), Kind.CHILD, prop, raw, valueDeser, handle); + } + Kind kind = scalarKind(raw); + if (kind == null || valueDeser == null + || !STOCK_SCALAR_DESERS.contains(valueDeser.getClass().getName())) { + return stock(prop); + } + return new GenProp(prop.getName(), kind, prop, raw, null, handle); + } + + // The property's store as an erased (Object, eV)void handle: a setter + // call or a field put, whichever backs the property. Final fields stay on + // the stock path so their (stock-defined) behavior is preserved exactly; + // an access failure demotes the property to the stock path. + private static MethodHandle storeHandle(SettableBeanProperty prop, Class raw) { + try { + Method setter = setterOf(prop, raw); + if (setter != null) { + return MemberHandles.setter(setter, raw); + } + Field field = fieldOf(prop, raw); + if (field != null && !Modifier.isFinal(field.getModifiers())) { + return MemberHandles.fieldSetter(field); + } + return null; + } catch (IllegalAccessException | RuntimeException e) { + return null; + } + } + + private static Field fieldOf(SettableBeanProperty prop, Class raw) { + if (!(prop.getMember() instanceof AnnotatedField af)) { + return null; + } + Field field = af.getAnnotated(); + if (field == null || Modifier.isStatic(field.getModifiers()) + || field.getType() != raw) { + return null; + } + return field; + } + + private static Method setterOf(SettableBeanProperty prop, Class raw) { + if (!(prop.getMember() instanceof AnnotatedMethod am)) { + return null; + } + Method setter = am.getAnnotated(); + if (setter == null || setter.getParameterCount() != 1 + || setter.getParameterTypes()[0] != raw) { + return null; + } + return setter; + } + + private static GenProp stock(SettableBeanProperty prop) { + CodegenDebug.log(() -> "reader prop stock: " + prop.getName()); + return new GenProp(prop.getName(), Kind.STOCK, prop); + } + + private static boolean isCodec(ValueDeserializer deser) { + return (deser instanceof GeneratedReaderBase) || (deser instanceof GeneratedBuilderBase); + } + + private static Kind scalarKind(Class raw) { + if (raw == String.class) { + return Kind.STRING; + } + if (raw == int.class) { + return Kind.INT; + } + if (raw == long.class) { + return Kind.LONG; + } + if (raw == boolean.class) { + return Kind.BOOLEAN; + } + return null; + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderPlaceholder.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderPlaceholder.java new file mode 100644 index 000000000..af9137047 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderPlaceholder.java @@ -0,0 +1,78 @@ +package tools.jackson.module.blackbird.deser; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.databind.util.NameTransformer; +import tools.jackson.module.blackbird.codegen.CodegenDebug; + +/** + * Takes the place of a stock {@link BeanDeserializer} (POJOs and records) + * from the modifier on: it is a copy of that deserializer, so other + * modifiers and databind's own checks see a {@code BeanDeserializer}, and + * it behaves as stock until resolution. Resolving it resolves the stock + * state and generates the codec, which needs the resolved properties and + * the context's TokenStreamFactory. Contextualization then hands out the + * codec, so steady-state call sites reference it with no extra hop. + */ +final class BBReaderPlaceholder extends BeanDeserializer +{ + // Whether the bean or any property declares @JsonView explicitly, read + // from the property definitions at modify time (the resolved properties + // cannot distinguish a declared view from the empty view set that + // disabled DEFAULT_VIEW_INCLUSION forces onto unannotated properties). + private final boolean _declaresViews; + + private volatile ValueDeserializer _codec; + + BBReaderPlaceholder(BeanDeserializer src, boolean declaresViews) { + super(src); + _declaresViews = declaresViews; + } + + @Override + public void resolve(DeserializationContext ctxt) { + CodegenDebug.log(() -> "reader resolve " + handledType().getName()); + super.resolve(ctxt); + _codec = BBReaderFactory.tryGenerate(new BBReaderFactory.Source(this, _anySetter, + _injectables != null, _beanProperties, _propertyBasedCreator, + _nonStandardCreation, + (_delegateDeserializer != null) || (_arrayDelegateDeserializer != null), + _externalTypeIdHandler != null, _unwrappedPropertyHandler != null, null), + ctxt, _declaresViews); + } + + // Contextualization is the stock one; when it leaves this deserializer + // as it is, the codec takes its place. + @Override + public ValueDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) { + ValueDeserializer contextual = super.createContextual(ctxt, property); + ValueDeserializer codec = _codec; + return (contextual == this && codec != null) ? codec : contextual; + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) { + ValueDeserializer codec = _codec; + return (codec != null) ? codec.deserialize(p, ctxt) : super.deserialize(p, ctxt); + } + + // BeanDeserializer unwraps only for its exact class (subclasses "MUST + // override"); an exact-class copy of this state does what it would. The + // field guards against recursive unwrapping, as in stock. + @Override + public ValueDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer transformer) { + if (_currentlyTransforming == transformer) { + return this; + } + _currentlyTransforming = transformer; + try { + return withBeanProperties(_beanProperties).unwrappingDeserializer(ctxt, transformer); + } finally { + _currentlyTransforming = null; + } + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/CreatorOptimizer.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/CreatorOptimizer.java deleted file mode 100644 index 67911d35f..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/CreatorOptimizer.java +++ /dev/null @@ -1,140 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import tools.jackson.databind.deser.ValueInstantiator; -import tools.jackson.databind.deser.std.StdValueInstantiator; -import tools.jackson.databind.introspect.AnnotatedWithParams; -import tools.jackson.module.blackbird.util.Unchecked; - -import static java.lang.invoke.MethodHandles.lookup; -import static java.lang.invoke.MethodType.methodType; - -/** - * Helper class that tries to generate {@link ValueInstantiator} class - * that calls constructors and/or factory methods directly, instead - * of using Reflection. - */ -public class CreatorOptimizer -{ - protected final Class _valueClass; - - protected final StdValueInstantiator _originalInstantiator; - - private final MethodHandles.Lookup _lookup; - - public CreatorOptimizer(Class valueClass, MethodHandles.Lookup lookup, StdValueInstantiator orig) - { - _valueClass = valueClass; - _originalInstantiator = orig; - _lookup = lookup; - } - - public ValueInstantiator createOptimized() - { - /* [Issue#11]: Need to avoid optimizing if we use delegate-based creators. */ - if (_originalInstantiator.canCreateUsingDelegate()) { - return null; - } - - if (_lookup == null) { - return null; - } - - AnnotatedWithParams argsCreator = _originalInstantiator.getWithArgsCreator(); - Function optimizedArgsCreator = null; - if (argsCreator != null) { - // The LambdaMetafactory requires a specifying interface, which is not possible to provide - // for methods with arbitrary parameter lists. So we have to use a spread invoker instead, - // which is not a valid target for the metafactory. Instead, we wrap it in a trampoline to - // avoid creating megamorphic code paths and hope that code inlining covers up our reflective sins. - MethodHandle argsCreatorHandle = directHandle(argsCreator.getAnnotated()); - if (argsCreatorHandle != null) { - optimizedArgsCreator = Unchecked.supplier(() -> - (Function) LambdaMetafactory.metafactory( - _lookup, - "apply", - methodType(Function.class, MethodHandle.class), - methodType(Object.class, Object.class), - lookup().findStatic( - CreatorOptimizer.class, - "invokeTrampoline", - methodType(Object.class, MethodHandle.class, Object[].class)), - methodType(Object.class, Object[].class)) - .getTarget().invokeExact( - argsCreatorHandle - .asFixedArity() - .asSpreader( - Object[].class, - argsCreatorHandle.type().parameterCount()) - .asType(methodType(Object.class, Object[].class)))) - .get(); - } - } - - AnnotatedWithParams defaultCreator = _originalInstantiator.getDefaultCreator(); - Supplier optimizedFactory = null; - if (defaultCreator != null) { - MethodHandle defaultCreatorHandle = directHandle(defaultCreator.getAnnotated()); - if (defaultCreatorHandle != null) { - optimizedFactory = Unchecked.supplier(() -> - (Supplier) LambdaMetafactory.metafactory( - _lookup, - "get", - methodType(Supplier.class), - methodType(Object.class), - defaultCreatorHandle, - defaultCreatorHandle.type()) - .getTarget().invokeExact()) - .get(); - } - } - if (optimizedArgsCreator != null || optimizedFactory != null) { - return new OptimizedValueInstantiator(_originalInstantiator, optimizedFactory, optimizedArgsCreator); - } - return null; - } - - private MethodHandle directHandle(AnnotatedElement element) { - return Stream.concat( - Stream.of(element) - .filter(Constructor.class::isInstance) - .map(Constructor.class::cast) - .filter(c -> !Modifier.isPrivate(c.getModifiers())) - .flatMap(t -> { - try { - return Stream.of(_lookup.unreflectConstructor(t)); - } catch (IllegalAccessException e) { - return Stream.empty(); - } - }), - Stream.of(element) - .filter(Method.class::isInstance) - .map(Method.class::cast) - .filter(m -> { - int mods = m.getModifiers(); - return Modifier.isStatic(mods) && !Modifier.isPrivate(mods); - }) - .flatMap(t -> { - try { - return Stream.of(_lookup.unreflect(t)); - } catch (IllegalAccessException e) { - return Stream.empty(); - } - })) - .findFirst().orElse(null); - } - - public static Object invokeTrampoline(MethodHandle delegate, Object[] args) throws Throwable { - return delegate.invokeExact(args); - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/ObjBooleanConsumer.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/ObjBooleanConsumer.java deleted file mode 100644 index 43411f9e4..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/ObjBooleanConsumer.java +++ /dev/null @@ -1,6 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -@FunctionalInterface -public interface ObjBooleanConsumer { - void accept(Object bean, boolean value); -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedSettableBeanProperty.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedSettableBeanProperty.java deleted file mode 100644 index 4fd8f642f..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedSettableBeanProperty.java +++ /dev/null @@ -1,162 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import tools.jackson.core.*; - -import tools.jackson.databind.*; -import tools.jackson.databind.deser.*; -import tools.jackson.databind.deser.impl.NullsConstantProvider; -import tools.jackson.databind.util.ClassUtil; - -/** - * Base class for concrete type-specific {@link SettableBeanProperty} - * implementations. - */ -abstract class OptimizedSettableBeanProperty> - extends SettableBeanProperty.Delegating -{ - private static final long serialVersionUID = 1L; - - /** - * @since 2.9 - */ - final protected boolean _skipNulls; - - /** - * Marker that we set if mutator turns out to be broken in a systematic - * way that we can handle by redirecting it back to standard one. - */ - private volatile boolean broken = false; - - /* - /********************************************************************** - /* Life-cycle - /********************************************************************** - */ - - protected OptimizedSettableBeanProperty(SettableBeanProperty src) - { - super(src); - _skipNulls = NullsConstantProvider.isSkipper(_nullProvider); - } - - // Base impl of `withName()` fine as-is: - // public SettableBeanProperty withName(PropertyName name); - - // But value deserializer handling needs some more care - @Override - public final SettableBeanProperty withValueDeserializer(ValueDeserializer deser) { - SettableBeanProperty newDelegate = delegate.withValueDeserializer(deser); - if (newDelegate == delegate) { - return this; - } - if (!_isDefaultDeserializer(deser)) { - return newDelegate; - } - return withDelegate(newDelegate); - } - - @Override - protected abstract SettableBeanProperty withDelegate(SettableBeanProperty d); - - /* - /********************************************************************** - /* Deserialization, regular - /********************************************************************** - */ - - @Override - public abstract void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object arg2) - throws JacksonException; - - @Override - public abstract void set(DeserializationContext ctxt, Object bean, Object value); - - /* - /********************************************************************** - /* Deserialization, builders - /********************************************************************** - */ - - /* !!! 19-Feb-2012, tatu: - * - * We do not yet generate code for these methods: it would not be hugely - * difficult to add them, but let's first see if anyone needs them... - * (it is non-trivial, adds code etc, so not without cost). - * - * That is: we'll use Reflection fallback for Builder-based deserialization, - * so it will not be significantly faster. - */ - - @Override - public abstract Object deserializeSetAndReturn(JsonParser p, - DeserializationContext ctxt, Object instance) - throws JacksonException; - - - @Override - public Object setAndReturn(DeserializationContext ctxt, Object instance, Object value) { - return delegate.setAndReturn(ctxt, instance, value); - } - - /* - /********************************************************************** - /* Error handling - /********************************************************************** - */ - - /** - * Helper method called when an exception is throw from mutator, to figure - * out what to do. - */ - protected void _reportProblem(DeserializationContext ctxt, Object bean, Object value, Throwable e) - throws JacksonException - { - if ((e instanceof IllegalAccessError) - || (e instanceof SecurityException)) { - synchronized (this) { - // yes, double-locking, so not guaranteed; but all we want here is to reduce likelihood - // of multiple logging of same underlying problem. Not guaranteed, just improved. - if (!broken) { - broken = true; - String msg = String.format("Disabling Blackbird deserialization for %s (field %s; mutator %s), due to access error (type %s, message=%s)%n", - bean.getClass(), _propName, getClass().getName(), - e.getClass().getName(), e.getMessage()); - Logger.getLogger(getClass().getName()).log(Level.WARNING, msg, e); - } - } - delegate.set(ctxt, bean, value); - return; - } - if (e instanceof JacksonException) { - throw (JacksonException) e; - } - if (e instanceof Error) { - throw (Error) e; - } - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } - // how could this ever happen? - throw new RuntimeException(e); - } - - /* - /********************************************************************** - /* Helper methods - /********************************************************************** - */ - - /** - * Helper method used to check whether given deserializer is the default - * deserializer implementation: this is necessary to avoid overriding custom - * deserializers. - */ - protected boolean _isDefaultDeserializer(ValueDeserializer deser) { - return (deser == null) - || (deser instanceof SuperSonicBeanDeserializer) - || ClassUtil.isJacksonStdImpl(deser); - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedValueInstantiator.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedValueInstantiator.java deleted file mode 100644 index 37c555c32..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedValueInstantiator.java +++ /dev/null @@ -1,55 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.function.Function; -import java.util.function.Supplier; - -import tools.jackson.core.JacksonException; -import tools.jackson.databind.DeserializationContext; -import tools.jackson.databind.deser.std.StdValueInstantiator; - -class OptimizedValueInstantiator extends StdValueInstantiator -{ - private final Supplier _optimizedDefaultCreator; - private final Function _optimizedArgsCreator; - - protected OptimizedValueInstantiator(StdValueInstantiator original, - Supplier defaultCreator, Function argsCreator) { - super(original); - this._optimizedDefaultCreator = defaultCreator; - this._optimizedArgsCreator = argsCreator; - } - - @Override - public boolean canCreateUsingDefault() { - return _optimizedDefaultCreator != null || super.canCreateUsingDefault(); - } - - @Override - public boolean canCreateFromObjectWith() { - return _optimizedArgsCreator != null || super.canCreateFromObjectWith(); - } - - @Override - public Object createUsingDefault(DeserializationContext ctxt) throws JacksonException { - if (_optimizedDefaultCreator != null) { - try { - return _optimizedDefaultCreator.get(); - } catch (Exception e) { - return ctxt.handleInstantiationProblem(_valueClass, null, e); - } - } - return super.createUsingDefault(ctxt); - } - - @Override - public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) throws JacksonException { - if (_optimizedArgsCreator != null) { - try { - return _optimizedArgsCreator.apply(args); - } catch (Exception e) { - return ctxt.handleInstantiationProblem(_valueClass, args, e); - } - } - return super.createFromObjectWith(ctxt, args); - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableBooleanProperty.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableBooleanProperty.java deleted file mode 100644 index 0c69807f3..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableBooleanProperty.java +++ /dev/null @@ -1,78 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import tools.jackson.core.*; - -import tools.jackson.databind.*; -import tools.jackson.databind.deser.SettableBeanProperty; - -final class SettableBooleanProperty - extends OptimizedSettableBeanProperty -{ - private static final long serialVersionUID = 1L; - private ObjBooleanConsumer _optimizedSetter; - - public SettableBooleanProperty(SettableBeanProperty src, ObjBooleanConsumer optimizedSetter) - { - super(src); - _optimizedSetter = optimizedSetter; - } - - @Override - protected SettableBeanProperty withDelegate(SettableBeanProperty del) { - return new SettableBooleanProperty(del, _optimizedSetter); - } - - /* - /********************************************************************** - /* Deserialization - /********************************************************************** - */ - - @Override - public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) - throws JacksonException - { - boolean b; - JsonToken t = p.currentToken(); - if (t == JsonToken.VALUE_TRUE) { - b = true; - } else if (t == JsonToken.VALUE_FALSE) { - b = false; - } else { - delegate.deserializeAndSet(p, ctxt, bean); - return; - } - try { - _optimizedSetter.accept(bean, b); - } catch (Throwable e) { - _reportProblem(ctxt, bean, b, e); - } - } - - @Override - public Object deserializeSetAndReturn(JsonParser p, - DeserializationContext ctxt, Object instance) - throws JacksonException - { - JsonToken t = p.currentToken(); - if (t == JsonToken.VALUE_TRUE) { - return setAndReturn(ctxt, instance, Boolean.TRUE); - } - if (t == JsonToken.VALUE_FALSE) { - return setAndReturn(ctxt, instance, Boolean.FALSE); - } - return delegate.deserializeSetAndReturn(p, ctxt, instance); - } - - @Override - public void set(DeserializationContext ctxt, Object bean, Object value) - { - // not optimal (due to boxing), but better than using reflection: - final boolean b = ((Boolean) value).booleanValue(); - try { - _optimizedSetter.accept(bean, b); - } catch (Throwable e) { - _reportProblem(ctxt, bean, b, e); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableIntProperty.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableIntProperty.java deleted file mode 100644 index efbcc49f3..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableIntProperty.java +++ /dev/null @@ -1,70 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.function.ObjIntConsumer; - -import tools.jackson.core.*; - -import tools.jackson.databind.*; -import tools.jackson.databind.deser.SettableBeanProperty; - -final class SettableIntProperty - extends OptimizedSettableBeanProperty -{ - private static final long serialVersionUID = 1L; - private ObjIntConsumer _optimizedSetter; - - public SettableIntProperty(SettableBeanProperty src, ObjIntConsumer optimizedSetter) - { - super(src); - _optimizedSetter = optimizedSetter; - } - - @Override - protected SettableBeanProperty withDelegate(SettableBeanProperty del) { - return new SettableIntProperty(del, _optimizedSetter); - } - - /* - /********************************************************************** - /* Deserialization - /********************************************************************** - */ - - @Override - public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) - throws JacksonException - { - if (!p.isExpectedNumberIntToken()) { - delegate.deserializeAndSet(p, ctxt, bean); - return; - } - final int v = p.getIntValue(); - try { - _optimizedSetter.accept(bean, v); - } catch (Throwable e) { - _reportProblem(ctxt, bean, v, e); - } - } - - @Override - public Object deserializeSetAndReturn(JsonParser p, - DeserializationContext ctxt, Object instance) - throws JacksonException - { - if (p.isExpectedNumberIntToken()) { - return setAndReturn(ctxt, instance, p.getIntValue()); - } - return delegate.deserializeSetAndReturn(p, ctxt, instance); - } - - @Override - public void set(DeserializationContext ctxt, Object bean, Object value) { - // not optimal (due to boxing), but better than using reflection: - int v = ((Number) value).intValue(); - try { - _optimizedSetter.accept(bean, v); - } catch (Throwable e) { - _reportProblem(ctxt, bean, v, e); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableLongProperty.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableLongProperty.java deleted file mode 100644 index e66b4c134..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableLongProperty.java +++ /dev/null @@ -1,70 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.function.ObjLongConsumer; - -import tools.jackson.core.*; -import tools.jackson.databind.*; -import tools.jackson.databind.deser.SettableBeanProperty; - -final class SettableLongProperty - extends OptimizedSettableBeanProperty -{ - private static final long serialVersionUID = 1L; - private ObjLongConsumer _optimizedSetter; - - public SettableLongProperty(SettableBeanProperty src, ObjLongConsumer optimizedSetter) - { - super(src); - _optimizedSetter = optimizedSetter; - } - - @Override - protected SettableBeanProperty withDelegate(SettableBeanProperty del) { - return new SettableLongProperty(del, _optimizedSetter); - } - - /* - /********************************************************************** - /* Deserialization - /********************************************************************** - */ - - @Override - public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) - throws JacksonException - { - if (!p.isExpectedNumberIntToken()) { - delegate.deserializeAndSet(p, ctxt, bean); - return; - } - final long v = p.getLongValue(); - try { - _optimizedSetter.accept(bean, v); - } catch (Throwable e) { - _reportProblem(ctxt, bean, v, e); - } - } - - @Override - public Object deserializeSetAndReturn(JsonParser p, - DeserializationContext ctxt, Object instance) - throws JacksonException - { - if (p.isExpectedNumberIntToken()) { - return setAndReturn(ctxt, instance, p.getLongValue()); - } - return delegate.deserializeSetAndReturn(p, ctxt, instance); - } - - @Override - public void set(DeserializationContext ctxt, Object bean, Object value) - { - // not optimal (due to boxing), but better than using reflection: - final long v = ((Number) value).longValue(); - try { - _optimizedSetter.accept(bean, v); - } catch (Throwable e) { - _reportProblem(ctxt, bean, v, e); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableObjectProperty.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableObjectProperty.java deleted file mode 100644 index cdca67f31..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableObjectProperty.java +++ /dev/null @@ -1,92 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.function.BiConsumer; - -import tools.jackson.core.*; -import tools.jackson.databind.*; -import tools.jackson.databind.deser.SettableBeanProperty; - -final class SettableObjectProperty - extends OptimizedSettableBeanProperty -{ - private static final long serialVersionUID = 1L; - private BiConsumer _optimizedSetter; - - public SettableObjectProperty(SettableBeanProperty src, BiConsumer optimizedSetter) - { - super(src); - this._optimizedSetter = optimizedSetter; - } - - @Override - protected SettableBeanProperty withDelegate(SettableBeanProperty del) { - return new SettableObjectProperty(del, _optimizedSetter); - } - - /* - /********************************************************************** - /* Deserialization - /********************************************************************** - */ - - @Override - public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) - throws JacksonException - { - Object value; - if (p.hasToken(JsonToken.VALUE_NULL)) { - if (_skipNulls) { - return; - } - value = _nullProvider.getNullValue(ctxt); - } else if (_valueTypeDeserializer == null) { - value = _valueDeserializer.deserialize(p, ctxt); - // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null - if (value == null) { - if (_skipNulls) { - return; - } - value = _nullProvider.getNullValue(ctxt); - } - } else { - value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); - } - set(ctxt, bean, value); - } - - @Override - public Object deserializeSetAndReturn(JsonParser p, - DeserializationContext ctxt, Object instance) - throws JacksonException - { - Object value; - if (p.hasToken(JsonToken.VALUE_NULL)) { - if (_skipNulls) { - return instance; - } - value = _nullProvider.getNullValue(ctxt); - } else if (_valueTypeDeserializer == null) { - value = _valueDeserializer.deserialize(p, ctxt); - // 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null - if (value == null) { - if (_skipNulls) { - return instance; - } - value = _nullProvider.getNullValue(ctxt); - } - } else { - value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer); - } - return setAndReturn(ctxt, instance, value); - } - - @Override - public void set(DeserializationContext ctxt, Object bean, Object v) - { - try { - _optimizedSetter.accept(bean, v); - } catch (Throwable e) { - _reportProblem(ctxt, bean, v, e); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableStringProperty.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableStringProperty.java deleted file mode 100644 index 4af3fb878..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableStringProperty.java +++ /dev/null @@ -1,62 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.function.BiConsumer; - -import tools.jackson.core.*; -import tools.jackson.databind.*; -import tools.jackson.databind.deser.SettableBeanProperty; - -final class SettableStringProperty - extends OptimizedSettableBeanProperty -{ - private static final long serialVersionUID = 1L; - private final BiConsumer _optimizedSetter; - - public SettableStringProperty(SettableBeanProperty src, BiConsumer optimizedSetter) - { - super(src); - _optimizedSetter = optimizedSetter; - } - - @Override - protected SettableBeanProperty withDelegate(SettableBeanProperty del) { - return new SettableStringProperty(del, _optimizedSetter); - } - - /* - /********************************************************************** - /* Deserialization - /********************************************************************** - */ - - // Copied from StdDeserializer.StringDeserializer: - @Override - public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) - throws JacksonException - { - if (!p.hasToken(JsonToken.VALUE_STRING)) { - delegate.deserializeAndSet(p, ctxt, bean); - return; - } - set(ctxt, bean, p.getString()); - } - - @Override - public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance) - throws JacksonException - { - if (p.hasToken(JsonToken.VALUE_STRING)) { - return setAndReturn(ctxt, instance, p.getString()); - } - return delegate.deserializeSetAndReturn(p, ctxt, instance); - } - - @Override - public void set(DeserializationContext ctxt, Object bean, Object value) { - try { - _optimizedSetter.accept(bean, (String) value); - } catch (Throwable e) { - _reportProblem(ctxt, bean, value, e); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicBeanDeserializer.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicBeanDeserializer.java deleted file mode 100644 index 6b7e34117..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicBeanDeserializer.java +++ /dev/null @@ -1,342 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.*; - -import tools.jackson.core.*; -import tools.jackson.core.io.SerializedString; - -import tools.jackson.databind.*; -import tools.jackson.databind.deser.*; -import tools.jackson.databind.deser.bean.BeanDeserializer; -import tools.jackson.databind.deser.bean.BeanPropertyMap; -import tools.jackson.databind.deser.bean.PropertyBasedCreator; -import tools.jackson.databind.deser.impl.UnwrappedPropertyHandler; -import tools.jackson.databind.util.NameTransformer; - -final class SuperSonicBeanDeserializer extends BeanDeserializer -{ - /** - * Names of properties being deserialized, in ordered they are - * expected to have been written (as per serialization settings); - * used for speculative order-based optimizations - */ - protected final SerializedString[] _orderedPropertyNames; - - /** - * Properties matching names in {@code _orderedPropertyNames}, - * assigned after resolution when property instances are finalized. - */ - protected SettableBeanProperty[] _orderedProperties; - - /* - /********************************************************************** - /* Life-cycle, construction, initialization - /********************************************************************** - */ - - public SuperSonicBeanDeserializer(BeanDeserializer src, List props) - { - super(src); - final int len = props.size(); - _orderedPropertyNames = new SerializedString[len]; - for (int i = 0; i < len; ++i) { - _orderedPropertyNames[i] = new SerializedString(props.get(i).getName()); - } - } - - protected SuperSonicBeanDeserializer(SuperSonicBeanDeserializer src, - UnwrappedPropertyHandler unwrapHandler, PropertyBasedCreator propertyBasedCreator, - BeanPropertyMap renamedProperties, boolean ignoreAllUnknown) - { - super(src, unwrapHandler, propertyBasedCreator, renamedProperties, ignoreAllUnknown); - _orderedPropertyNames = src._orderedPropertyNames; - _orderedProperties = src._orderedProperties; - } - - @Override - public ValueDeserializer unwrappingDeserializer(DeserializationContext ctxt, - NameTransformer transformer) - { - // 17-Dec-2020, tatu: Was like so: -// return new SuperSonicBeanDeserializer(this, unwrapper); - // but Afterburner 3.0 had this: - - // NOTE: copied verbatim from `BeanDeserializer` - - if (_currentlyTransforming == transformer) { // from [databind#383] - return this; - } - _currentlyTransforming = transformer; - try { - UnwrappedPropertyHandler uwHandler = _unwrappedPropertyHandler; - if (uwHandler != null) { - uwHandler = uwHandler.renameAll(ctxt, transformer); - } - PropertyBasedCreator pbCreator = _propertyBasedCreator; - if (pbCreator != null) { - pbCreator = pbCreator.renameAll(ctxt, transformer); - } - return new SuperSonicBeanDeserializer(this, uwHandler, pbCreator, - _beanProperties.renameAll(ctxt, transformer), true); - } finally { _currentlyTransforming = null; } - } - - // // Others, let's just leave as is; will not be optimized? - - //public BeanDeserializer withObjectIdReader(ObjectIdReader oir) { - - //public BeanDeserializer withIgnorableProperties(HashSet ignorableProps) - - //protected BeanDeserializerBase asArrayDeserializer() - - /* - /********************************************************************** - /* BenaDeserializer overrides - /********************************************************************** - */ - - /** - * This method is overridden as we need to know expected order of - * properties. - */ - @Override - public void resolve(DeserializationContext ctxt) - { - super.resolve(ctxt); - // Ok, now; need to find actual property instances to go with order - // defined based on property names. - - // 20-Sep-2014, tatu: As per [afterburner#43], use of `JsonTypeInfo.As.EXTERNAL_PROPERTY` - // will "hide" matching property, leading to no match below. - // But since we don't use optimized path if that case, let's just bail out. - if (_externalTypeIdHandler != null || _unwrappedPropertyHandler != null) { - // should we assign empty array or... ? - return; - } - - int len = _orderedPropertyNames.length; - ArrayList props = new ArrayList(len); - int i = 0; - for (; i < len; ++i) { - SettableBeanProperty prop = _beanProperties.findDefinition(_orderedPropertyNames[i].toString()); - if (prop != null) { - props.add(prop); - } - } - // should usually get at least one property; let's for now consider it an error if not - // (may need to revisit in future) - if (i == 0) { - throw new IllegalStateException("Blackbird internal error: BeanDeserializer for " - +_beanType+" has no properties that match expected ordering (should have "+len+") -- can not create optimized deserializer"); - } - _orderedProperties = props.toArray(new SettableBeanProperty[0]); - } - - @Override - public Object deserialize(JsonParser p, DeserializationContext ctxt) - throws JacksonException - { - if (!_vanillaProcessing || (_objectIdReader != null)) { - // should we ever get here? Just in case - return super.deserialize(p, ctxt); - } - // common case first: - if (!p.isExpectedStartObjectToken()) { - return _deserializeOther(p, ctxt, p.currentToken()); - } - if (_nonStandardCreation) { - p.nextToken(); - if (_unwrappedPropertyHandler != null) { - return deserializeWithUnwrapped(p, ctxt); - } - if (_externalTypeIdHandler != null) { - return deserializeWithExternalTypeId(p, ctxt); - } - Object bean = deserializeFromObjectUsingNonDefault(p, ctxt); - if (_injectables != null) { - injectValues(ctxt, bean); - } - return bean; - } - final Object bean = _valueInstantiator.createUsingDefault(ctxt); - // [databind#631]: Assign current value, to be accessible by custom serializers - p.assignCurrentValue(bean); - if (p.canReadObjectId()) { - Object id = p.getObjectId(); - if (id != null) { - _handleTypedObjectId(p, ctxt, bean, id); - } - } - if (_injectables != null) { - injectValues(ctxt, bean); - } - for (int i = 0, len = _orderedProperties.length; i < len; ++i) { - SettableBeanProperty prop = _orderedProperties[i]; - if (!p.nextName(_orderedPropertyNames[i])) { // miss... - if (p.currentToken() == JsonToken.END_OBJECT) { - return bean; - } - // we likely point to PROPERTY_NAME, so can just call parent impl - return super.deserialize(p, ctxt, bean); - } - p.nextToken(); // skip field, returns value token - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); - } - } - // also, need to ensure we get closing END_OBJECT... - if (p.nextToken() != JsonToken.END_OBJECT) { - return super.deserialize(p, ctxt, bean); - } - return bean; - } - - // much of below is cut'n pasted from BeanSerializer - @Override - public final Object deserialize(JsonParser p, DeserializationContext ctxt, Object bean) - throws JacksonException - { - // [modules-base#339]: Need to delegate to base implementation for - // proper @JsonView handling during deserialization - if (_needViewProcesing) { - return super.deserialize(p, ctxt, bean); - } - // [databind#631]: Assign current value, to be accessible by custom deserializers - p.assignCurrentValue(bean); - if (_injectables != null) { - injectValues(ctxt, bean); - } - if (_unwrappedPropertyHandler != null) { - return deserializeWithUnwrapped(p, ctxt, bean); - } - if (_externalTypeIdHandler != null) { - return deserializeWithExternalTypeId(p, ctxt, bean); - } - SettableBeanProperty prop = _orderedProperties[0]; - // First: verify that first name is expected - if (p.isExpectedStartObjectToken()) { - if (!p.nextName(_orderedPropertyNames[0])) { - return super.deserialize(p, ctxt, bean); - } - } else if (!p.hasToken(JsonToken.PROPERTY_NAME) - || !prop.getName().equals(p.currentName())) { - // no, something funky, use base impl for special cases - return super.deserialize(p, ctxt, bean); - } - p.nextToken(); - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); - } - // then rest of properties - for (int i = 1, len = _orderedProperties.length; i < len; ++i) { - if (!p.nextName(_orderedPropertyNames[i])) { // miss... - if (p.hasToken(JsonToken.END_OBJECT)) { - return bean; - } - // we likely point to PROPERTY_NAME, so can just call parent impl - return super.deserialize(p, ctxt, bean); - } - prop = _orderedProperties[i]; - p.nextToken(); // skip field, returns value token - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); - } - } - // also, need to ensure we get closing END_OBJECT... - if (p.nextToken() != JsonToken.END_OBJECT) { - return super.deserialize(p, ctxt, bean); - } - return bean; - } - - // much of below is cut'n pasted from BeanSerializer - @Override - public final Object deserializeFromObject(JsonParser p, DeserializationContext ctxt) - throws JacksonException - { - // [modules-base#339]: Need to delegate to base implementation for - // proper @JsonView handling during deserialization - if (_needViewProcesing) { - return super.deserializeFromObject(p, ctxt); - } - // See BeanDeserializer.deserializeFromObject [databind#622] - // Allow Object Id references to come in as JSON Objects as well... - if ((_objectIdReader != null) && _objectIdReader.maySerializeAsObject()) { - if (p.hasTokenId(JsonTokenId.ID_PROPERTY_NAME) - && _objectIdReader.isValidReferencePropertyName(p.currentName(), p)) { - return deserializeFromObjectId(p, ctxt); - } - } - if (_nonStandardCreation) { - if (_unwrappedPropertyHandler != null) { - return deserializeWithUnwrapped(p, ctxt); - } - if (_externalTypeIdHandler != null) { - return deserializeWithExternalTypeId(p, ctxt); - } - Object bean = deserializeFromObjectUsingNonDefault(p, ctxt); - if (_injectables != null) { - injectValues(ctxt, bean); - } - return bean; - } - final Object bean = _valueInstantiator.createUsingDefault(ctxt); - // [databind#631]: Assign current value, to be accessible by custom serializers - p.assignCurrentValue(bean); - if (p.canReadObjectId()) { - Object id = p.getObjectId(); - if (id != null) { - _handleTypedObjectId(p, ctxt, bean, id); - } - } - if (_injectables != null) { - injectValues(ctxt, bean); - } - SettableBeanProperty prop = _orderedProperties[0]; - // First: verify that first name is expected - if (p.isExpectedStartObjectToken()) { - if (!p.nextName(_orderedPropertyNames[0])) { - return super.deserialize(p, ctxt, bean); - } - } else if (!p.hasToken(JsonToken.PROPERTY_NAME) - || !prop.getName().equals(p.currentName())) { - return super.deserialize(p, ctxt, bean); - } - // and deserialize - p.nextToken(); - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); - } - - // then rest of properties - for (int i = 1, len = _orderedProperties.length; i < len; ++i) { - prop = _orderedProperties[i]; - if (!p.nextName(_orderedPropertyNames[i])) { // miss... - if (p.hasToken(JsonToken.END_OBJECT)) { - return bean; - } - // we likely point to PROPERTY_NAME, so can just call parent impl - return super.deserialize(p, ctxt, bean); - } - p.nextToken(); // skip field, returns value token - try { - prop.deserializeAndSet(p, ctxt, bean); - } catch (Exception e) { - wrapAndThrow(e, bean, prop.getName(), ctxt); - } - } - // also, need to ensure we get closing END_OBJECT... - if (p.nextToken() != JsonToken.END_OBJECT) { - return super.deserialize(p, ctxt, bean); - } - return bean; - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicDeserializerBuilder.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicDeserializerBuilder.java deleted file mode 100644 index e56cf341c..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicDeserializerBuilder.java +++ /dev/null @@ -1,35 +0,0 @@ -package tools.jackson.module.blackbird.deser; - -import java.util.*; - -import tools.jackson.databind.ValueDeserializer; -import tools.jackson.databind.deser.*; -import tools.jackson.databind.deser.bean.BeanDeserializer; - -class SuperSonicDeserializerBuilder extends BeanDeserializerBuilder -{ - public SuperSonicDeserializerBuilder(BeanDeserializerBuilder base) { - super(base); - } - - @Override - public ValueDeserializer build() - { - BeanDeserializer deser = (BeanDeserializer) super.build(); - // only create custom one, if existing one is standard deserializer; - if (deser.getClass() == BeanDeserializer.class) { - BeanDeserializer beanDeser = (BeanDeserializer) deser; - Iterator it = getProperties(); - // also: only build custom one for non-empty beans: - if (it.hasNext()) { - // So let's find actual order of properties, necessary for optimal access - ArrayList props = new ArrayList(); - do { - props.add(it.next()); - } while (it.hasNext()); - return new SuperSonicBeanDeserializer(beanDeser, props); - } - } - return deser; - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModule.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModule.java new file mode 100644 index 000000000..57edfd044 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModule.java @@ -0,0 +1,35 @@ +package tools.jackson.module.blackbird.internal; + +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.codegen.ClassFileEmitters; + +/** + * The service provider that module discovery instantiates + * ({@code MapperBuilder.findAndAddModules()}, or any other reader of + * {@code META-INF/services/tools.jackson.databind.JacksonModule}). On a + * runtime where Blackbird cannot generate codecs (Java 17 to 24), this + * instance registers nothing and logs one warning, so discovery keeps working + * for every other module; an explicitly registered {@link BlackbirdModule} + * fails instead. Not API: register {@code new BlackbirdModule()}. + */ +public final class DiscoveredBlackbirdModule extends BlackbirdModule +{ + private static final long serialVersionUID = 1L; + + // Databind keeps one module per registration id, the one added last. + // Where Blackbird works, the id is that of an explicit registration, so + // discovery plus an explicit registration installs Blackbird once. Where + // it cannot work, the id is distinct, so that discovery cannot replace an + // explicit registration, which must fail. + @Override + public Object getRegistrationId() { + return ClassFileEmitters.available() + ? BlackbirdModule.class.getName() + : DiscoveredBlackbirdModule.class.getName(); + } + + @Override + public String getModuleName() { + return BlackbirdModule.class.getSimpleName(); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedBuilderBase.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedBuilderBase.java new file mode 100644 index 000000000..5b1039290 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedBuilderBase.java @@ -0,0 +1,90 @@ +package tools.jackson.module.blackbird.internal; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.deser.bean.BeanPropertyMap; +import tools.jackson.databind.deser.bean.BuilderBasedDeserializer; + +/** + * Superclass for generated builder codecs: a copy of the resolved stock + * {@link BuilderBasedDeserializer} whose {@code deserialize} method the + * generated class overrides; the counterpart of {@link GeneratedReaderBase}, + * whose comments apply. Not API. + */ +public abstract class GeneratedBuilderBase extends BuilderBasedDeserializer +{ + private final String _layout; + + private final ViewMasks _viewMasks = new ViewMasks(this::_computeViewMask); + + protected GeneratedBuilderBase(BuilderBasedDeserializer src, BeanPropertyMap props, + String layout) { + super(src, props); + _layout = layout; + _propertiesContextualized = true; + } + + /** The generated arms, as "name=KIND" entries in arm order. */ + public final String codecLayout() { + return _layout; + } + + @Override + public String toString() { + return "BBReader[" + handledType().getName() + ": " + _layout + "]"; + } + + @Override + public void resolve(DeserializationContext ctxt) { } + + protected final Object _stockDeserialize(JsonParser p, DeserializationContext ctxt) { + return super.deserialize(p, ctxt); + } + + // The build method threw: what BuilderBasedDeserializer.finishBuild does. + protected final Object _buildFailure(DeserializationContext ctxt, Throwable t) { + return wrapInstantiationProblem(ctxt, t); + } + + protected final RuntimeException _propertyException(Throwable t, Object builder, + SettableBeanProperty prop, DeserializationContext ctxt) { + throw wrapAndThrow(t, builder, prop.getName(), ctxt); + } + + protected final RuntimeException _storeFailure(Throwable t, Object builder, + Object childValue, SettableBeanProperty prop, JsonParser p, + DeserializationContext ctxt) { + throw wrapAndThrow(ReaderSupport.storeException(t, childValue, prop, p), builder, + prop.getName(), ctxt); + } + + protected final void _injectValues(DeserializationContext ctxt, Object builder) { + injectValues(ctxt, builder); + } + + protected final void _handleUnknown(JsonParser p, DeserializationContext ctxt, + Object builder) { + String name = p.currentName(); + p.nextToken(); + handleUnknownVanilla(p, ctxt, builder, name); + } + + protected final Object _unexpectedToken(JsonParser p, DeserializationContext ctxt, + Object builder) { + return _handleUnexpectedWithin(p, ctxt, builder); + } + + protected final long _viewMask(Class view) { + return _viewMasks.maskFor(view); + } + + protected long _computeViewMask(Class view) { + throw new UnsupportedOperationException("codec has no view mask"); + } + + protected final void _hiddenView(JsonParser p, DeserializationContext ctxt, + SettableBeanProperty prop) { + handleUnexpectedView(p, ctxt, prop, ctxt.getActiveView()); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedReaderBase.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedReaderBase.java new file mode 100644 index 000000000..9374d9f18 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedReaderBase.java @@ -0,0 +1,259 @@ +package tools.jackson.module.blackbird.internal; + +import java.lang.constant.ConstantDescs; +import java.lang.invoke.MethodHandles; +import java.util.Map; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.databind.deser.bean.BeanPropertyMap; +import tools.jackson.databind.deser.bean.PropertyValueBuffer; +import tools.jackson.databind.deser.std.StdValueInstantiator; +import tools.jackson.databind.util.IgnorePropertiesUtil; +import tools.jackson.databind.util.NameTransformer; +import tools.jackson.databind.util.TokenBuffer; + +/** + * Superclass for generated POJO and record codecs (builder codecs extend + * {@link GeneratedBuilderBase}). A codec is a copy of the resolved stock + * {@link BeanDeserializer} whose {@code deserialize} method the generated + * class overrides with the per-bean loop: everything else, and every input + * the loop does not model, is the stock implementation, and databind sees a + * {@code BeanDeserializer} wherever it checks for one. The helpers below are + * the cold paths of the generated loop; each one calls the stock code for + * the same situation. Not API. + */ +public abstract class GeneratedReaderBase extends BeanDeserializer +{ + // One "name=KIND" entry per arm, for diagnostics and tests. + private final String _layout; + + private final ViewMasks _viewMasks = new ViewMasks(this::_computeViewMask); + + protected GeneratedReaderBase(BeanDeserializer src, BeanPropertyMap props, String layout) { + // Built from the resolved deserializer, with the bean property map + // whose name matcher is initialized. + super(src, props); + _layout = layout; + _propertiesContextualized = true; + } + + /** The generated arms, as "name=KIND" entries in arm order. */ + public final String codecLayout() { + return _layout; + } + + @Override + public String toString() { + return "BBReader[" + handledType().getName() + ": " + _layout + "]"; + } + + // A copy of a resolved deserializer: nothing left to resolve. + @Override + public void resolve(DeserializationContext ctxt) { } + + // The generated deserialize hands inputs it does not model here. + protected final Object _stockDeserialize(JsonParser p, DeserializationContext ctxt) { + return super.deserialize(p, ctxt); + } + + // BeanDeserializer unwraps only for its exact class (subclasses "MUST + // override"); an exact-class copy of this state does what it would. The + // field guards against recursive unwrapping, as in stock. + @Override + public ValueDeserializer unwrappingDeserializer(DeserializationContext ctxt, + NameTransformer transformer) { + if (_currentlyTransforming == transformer) { + return this; + } + _currentlyTransforming = transformer; + try { + return withBeanProperties(_beanProperties).unwrappingDeserializer(ctxt, transformer); + } finally { + _currentlyTransforming = null; + } + } + + /* + /********************************************************************** + /* Failures + /********************************************************************** + */ + + // The no-arg constructor handle threw: what StdValueInstantiator + // .createUsingDefault does (the factory uses the handle only with exactly + // that instantiator). Returns a problem handler's replacement value. + protected final Object _instantiationFailure(DeserializationContext ctxt, Throwable t) { + return ReaderSupport.creatorProblem((StdValueInstantiator) _valueInstantiator, ctxt, + null, t); + } + + // Called from the generated exception handler that covers every property + // arm: the stock loop's wrapAndThrow, so property errors carry the + // reference path. Always throws; the return type only satisfies the + // generated athrow. + protected final RuntimeException _propertyException(Throwable t, Object beanOrNull, + SettableBeanProperty prop, DeserializationContext ctxt) { + Object ref = (beanOrNull == null) ? handledType() : beanOrNull; + throw wrapAndThrow(t, ref, prop.getName(), ctxt); + } + + // A generated store (setter call or field write) threw: what the stock + // property throws for it, through the stock loop's wrapAndThrow. + protected final RuntimeException _storeFailure(Throwable t, Object bean, + Object childValue, SettableBeanProperty prop, JsonParser p, + DeserializationContext ctxt) { + throw wrapAndThrow(ReaderSupport.storeException(t, childValue, prop, p), bean, + prop.getName(), ctxt); + } + + /* + /********************************************************************** + /* POJO loop helpers + /********************************************************************** + */ + + // Right after construction, before the property loop: the placement + // stock deserializeFromObject uses, so values from the document override + // injected values exactly like stock. + protected final void _injectValues(DeserializationContext ctxt, Object bean) { + injectValues(ctxt, bean); + } + + // A name the matcher does not know: stock handleUnknownVanilla (ignored + // names, then the any-setter, then unknown-property handling). + protected final void _handleUnknown(JsonParser p, DeserializationContext ctxt, + Object bean) { + String name = p.currentName(); + p.nextToken(); + handleUnknownVanilla(p, ctxt, bean, name); + } + + // Neither a name nor the end of the object where a name was expected. + protected final Object _unexpectedToken(JsonParser p, DeserializationContext ctxt, + Object bean) { + return _handleUnexpectedWithin(p, ctxt, bean); + } + + /* + /********************************************************************** + /* Record loop helpers + /********************************************************************** + */ + + // A name no component takes: the order of the stock property-based + // loop. Ignored names go through ignored-property handling at once; + // other names are skipped when stock would skip them, and otherwise + // buffered, to be reported against the record once it exists (see + // _recordReplayUnknown). Returns the buffer, created on first use. + protected final Object _recordUnknown(JsonParser p, DeserializationContext ctxt, + Object buffered) { + String name = p.currentName(); + p.nextToken(); + if (IgnorePropertiesUtil.shouldIgnore(name, _ignorableProps, _includableProps)) { + handleIgnoredProperty(p, ctxt, handledType(), name); + return buffered; + } + if (_shouldSkipUnknowns(ctxt)) { + p.skipChildren(); + return buffered; + } + TokenBuffer tb = (buffered == null) ? ctxt.bufferForInputBuffering(p) + : (TokenBuffer) buffered; + tb.writeName(name); + tb.copyCurrentStructure(p); + return tb; + } + + protected final Object _recordReplayUnknown(DeserializationContext ctxt, Object bean, + Object buffered) { + return handleUnknownProperties(ctxt, bean, (TokenBuffer) buffered); + } + + // Record codecs call the canonical constructor directly only when the + // document supplied every component, no primitive component received a + // null from a custom deserializer, and FAIL_ON_NULL_CREATOR_PROPERTIES is + // off. Everything else ends here: values holds the components (boxed, in + // creator-index order), seen marks the ones the document supplied, and + // nullPrimitives marks primitive components whose deserializer returned + // null. Without a constructor failure, the stock creator completes the + // arguments (absent values, required and FAIL_ON_MISSING / + // FAIL_ON_NULL_CREATOR_PROPERTIES checks) and constructs; a failure from + // the fast path goes where StdValueInstantiator sends it. Either way a + // failure ends in wrapInstantiationProblem, as in the stock loop. + protected final Object _recordBuild(JsonParser p, DeserializationContext ctxt, long seen, + long nullPrimitives, Object[] values, Throwable ctorFailure) { + for (int i = 0; i < values.length; i++) { + if ((nullPrimitives & (1L << i)) != 0) { + values[i] = null; + } + } + if (ctorFailure != null) { + try { + return ReaderSupport.creatorProblem((StdValueInstantiator) _valueInstantiator, + ctxt, values, ctorFailure); + } catch (Exception e) { + return wrapInstantiationProblem(ctxt, e); + } + } + SettableBeanProperty[] props = _propertyBasedCreator.allPropertiesInOrder(); + PropertyValueBuffer buffer = _propertyBasedCreator.startBuilding(p, ctxt, null, false); + for (int i = 0; i < props.length; i++) { + if ((seen & (1L << i)) != 0) { + buffer.assignParameter(props[i], values[i]); + } + } + try { + return _propertyBasedCreator.build(ctxt, buffer); + } catch (Exception e) { + return wrapInstantiationProblem(ctxt, e); + } + } + + /* + /********************************************************************** + /* Views and class data + /********************************************************************** + */ + + // Called by generated code once per view-active call. + protected final long _viewMask(Class view) { + return _viewMasks.maskFor(view); + } + + // Overridden by generated codecs that filter per view: bit i reports + // whether property arm i is visible in the given view. Codecs that + // delegate view-active calls whole (more than 64 properties) never call + // the mask machinery. + protected long _computeViewMask(Class view) { + throw new UnsupportedOperationException("codec has no view mask"); + } + + // An arm hidden in the active view, the parser on the value token. + protected final void _hiddenView(JsonParser p, DeserializationContext ctxt, + SettableBeanProperty prop) { + handleUnexpectedView(p, ctxt, prop, ctxt.getActiveView()); + } + + // Constant bootstrap for generated code (reader and builder codecs): + // resolves one named entry of the hidden class's class data, a Map built + // at generation time. The JDK's classDataAt bootstrap requires the condy + // name to be "_", so named entries need this owner. The generated + // classes are defined in this module (in the codegen package), so they + // resolve it like any other class of the module. A condy links once and + // then constant-folds the same as classDataAt; the names exist for dump + // readability and to remove positional-index bookkeeping from the + // generators. + public static Object classDataEntry(MethodHandles.Lookup lookup, String name, Class type) + throws IllegalAccessException { + Map data = MethodHandles.classData(lookup, ConstantDescs.DEFAULT_NAME, Map.class); + Object value = data.get(name); + if (value == null) { + throw new IllegalStateException("no class data entry named " + name); + } + return type.cast(value); + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedWriterBase.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedWriterBase.java new file mode 100644 index 000000000..065a8d77e --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedWriterBase.java @@ -0,0 +1,122 @@ +package tools.jackson.module.blackbird.internal; + +import java.lang.constant.ConstantDescs; +import java.lang.invoke.MethodHandles; +import java.util.Map; + +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.DatabindException; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; +import tools.jackson.databind.ser.BeanPropertyWriter; +import tools.jackson.databind.ser.BeanSerializer; +import tools.jackson.databind.ser.PropertyWriter; +import tools.jackson.databind.ser.bean.BeanSerializerBase; + +/** + * Superclass for generated serializers: a copy of the resolved stock bean + * serializer (as a {@link BeanSerializer}) whose {@code serialize} method + * (and, for most beans, {@code serializeWithType}) the generated class + * overrides. Everything else, and every call the generated code does not + * model, is the stock implementation, and databind sees a bean serializer + * wherever it checks for one (the self-reference check of + * BeanPropertyWriter among them). Not API. + */ +public abstract class GeneratedWriterBase extends BeanSerializer +{ + // One "name=KIND" entry per property, for diagnostics and tests. + private final String _layout; + + private final ViewMasks _viewMasks = new ViewMasks(this::_computeViewMask); + + protected GeneratedWriterBase(BeanSerializerBase src, String layout) { + super(src); + _layout = layout; + } + + /** The generated properties, as "name=KIND" entries in write order. */ + public final String codecLayout() { + return _layout; + } + + @Override + public String toString() { + return "BBWriter[" + handledType().getName() + ": " + _layout + "]"; + } + + // A copy of a resolved serializer: nothing left to resolve. + @Override + public void resolve(SerializationContext ctxt) { } + + /* + /********************************************************************** + /* Support for generated code + /********************************************************************** + */ + + // The stock implementations, for calls the generated code hands back. + protected final void _stockSerialize(Object bean, JsonGenerator g, SerializationContext ctxt) { + super.serialize(bean, g, ctxt); + } + + protected final void _stockSerializeWithType(Object bean, JsonGenerator g, + SerializationContext ctxt, TypeSerializer typeSer) { + super.serializeWithType(bean, g, ctxt, typeSer); + } + + // Called from the generated handler around the property section: what + // stock _serializePropertiesNoView does with an exception from the + // property at index (the generated properties are the stock ones, in + // stock order). Always throws; the return type only satisfies the + // generated athrow. + protected final RuntimeException _propertyFailure(Throwable t, Object bean, int index, + JsonGenerator g, SerializationContext ctxt) { + String name = _props[index].getName(); + if (t instanceof StackOverflowError soe) { + throw DatabindException.from(g, "Infinite recursion (StackOverflowError)", soe) + .prependPath(bean, name); + } + wrapAndThrow(ctxt, t, bean, name); + throw new IllegalStateException("wrapAndThrow returned", t); + } + + // Constant bootstrap for generated code; see + // GeneratedReaderBase.classDataEntry. + public static Object classDataEntry(MethodHandles.Lookup lookup, String name, Class type) + throws IllegalAccessException { + Map data = MethodHandles.classData(lookup, ConstantDescs.DEFAULT_NAME, Map.class); + Object value = data.get(name); + if (value == null) { + throw new IllegalStateException("no class data entry named " + name); + } + return type.cast(value); + } + + protected final long _viewMask(Class view) { + return _viewMasks.maskFor(view); + } + + // Overridden by generated writers that filter per view: bit i reports + // whether property i is visible in the given view. + protected long _computeViewMask(Class view) { + throw new UnsupportedOperationException("writer has no view mask"); + } + + // Visibility of one property in a view, matching the rule the stock + // factory uses to build the filtered writer array: no view annotations + // means DEFAULT_VIEW_INCLUSION decides (captured at generation time), + // otherwise any declared view assignable from the active view. + protected static boolean _propVisible(PropertyWriter w, Class view, + boolean includeByDefault) { + Class[] views = (w instanceof BeanPropertyWriter bpw) ? bpw.getViews() : null; + if (views == null || views.length == 0) { + return includeByDefault; + } + for (Class v : views) { + if (v.isAssignableFrom(view)) { + return true; + } + } + return false; + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/ReaderSupport.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/ReaderSupport.java new file mode 100644 index 000000000..e0b5a9eb6 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/ReaderSupport.java @@ -0,0 +1,74 @@ +package tools.jackson.module.blackbird.internal; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DatabindException; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.deser.std.StdValueInstantiator; +import tools.jackson.databind.util.ClassUtil; + +/** + * Cold-path helpers shared by the generated reader bases: each reproduces + * what a stock class does in the same situation, where that code is not + * reachable through inheritance. + */ +final class ReaderSupport +{ + private ReaderSupport() {} + + /** + * The exception that the stock MethodProperty throws when its setter or + * field store throws t (SettableBeanProperty._throwAsJacksonE). A scalar + * arm no longer has its value, but the message needs only the value's + * class, which the property type fixes; a child arm passes its value. + */ + static Throwable storeException(Throwable t, Object childValue, SettableBeanProperty prop, + JsonParser p) { + if (t instanceof IllegalArgumentException) { + Class raw = prop.getType().getRawClass(); + Object value = raw.isPrimitive() ? ClassUtil.wrapperType(raw) + : (raw == String.class) ? String.class : childValue; + StringBuilder msg = new StringBuilder("Problem deserializing property '") + .append(prop.getName()) + .append("' (expected type: ") + .append(prop.getType()) + .append("; actual type: ") + .append(ClassUtil.classNameOf(value)).append(")"); + String origMsg = ClassUtil.exceptionMessage(t); + if (origMsg != null) { + msg.append(", problem: ").append(origMsg); + } else { + msg.append(" (no error message provided)"); + } + return DatabindException.from(p, msg.toString(), t); + } + if (t instanceof RuntimeException) { + return t; + } + return DatabindException.from(p, ClassUtil.exceptionMessage(t), t); + } + + /** + * What StdValueInstantiator does with an exception from the creator it + * calls (the instantiator must be exactly that class): problem handlers + * may supply a value, otherwise the wrapped exception is thrown. + */ + static Object creatorProblem(StdValueInstantiator inst, DeserializationContext ctxt, + Object[] args, Throwable t) { + return ctxt.handleInstantiationProblem(inst.getValueClass(), args, + InstantiatorProbe.rewrapCtorProblem(inst, ctxt, t)); + } + + // StdValueInstantiator.rewrapCtorProblem is protected; the copy + // constructor carries the instantiator's state into this probe. + private static final class InstantiatorProbe extends StdValueInstantiator { + private InstantiatorProbe(StdValueInstantiator src) { + super(src); + } + + static DatabindException rewrapCtorProblem(StdValueInstantiator src, + DeserializationContext ctxt, Throwable t) { + return new InstantiatorProbe(src).rewrapCtorProblem(ctxt, t); + } + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/ViewMasks.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/ViewMasks.java new file mode 100644 index 000000000..4d198ff2c --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/ViewMasks.java @@ -0,0 +1,55 @@ +package tools.jackson.module.blackbird.internal; + +import java.util.function.ToLongFunction; + +/** + * Copy-on-write cache of view to visibility bitmask (bit i: property arm i + * is visible in the view) for one codec. View sets are small and stable per + * application, so lookups are a reference scan. The cached view classes + * unload with the codec and its mapper. + */ +final class ViewMasks +{ + private static final Class[] NO_VIEWS = new Class[0]; + private static final long[] NO_MASKS = new long[0]; + + private final ToLongFunction> _compute; + + private volatile Class[] _views = NO_VIEWS; + private volatile long[] _masks = NO_MASKS; + + ViewMasks(ToLongFunction> compute) { + _compute = compute; + } + + long maskFor(Class view) { + Class[] views = _views; + long[] masks = _masks; + for (int i = 0; i < views.length && i < masks.length; i++) { + if (views[i] == view) { + return masks[i]; + } + } + return add(view); + } + + private synchronized long add(Class view) { + Class[] views = _views; + for (int i = 0; i < views.length; i++) { + if (views[i] == view) { + return _masks[i]; + } + } + long mask = _compute.applyAsLong(view); + Class[] newViews = new Class[views.length + 1]; + long[] newMasks = new long[views.length + 1]; + System.arraycopy(views, 0, newViews, 0, views.length); + System.arraycopy(_masks, 0, newMasks, 0, views.length); + newViews[views.length] = view; + newMasks[views.length] = mask; + // Masks first: a reader that sees the new views array also sees them. + _masks = newMasks; + _views = newViews; + return mask; + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/internal/package-info.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/package-info.java new file mode 100644 index 000000000..52b0c7171 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/package-info.java @@ -0,0 +1,7 @@ +/** + * Blackbird internals: the superclasses of the generated codecs and the + * module discovery provider. The package is not exported from the module + * and is not API: the generated codecs are defined in this module (in the + * codegen package), so nothing outside it links to these types. + */ +package tools.jackson.module.blackbird.internal; diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBSerializerModifier.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBSerializerModifier.java index 4644a0aee..9c2c6629f 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBSerializerModifier.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBSerializerModifier.java @@ -1,168 +1,80 @@ package tools.jackson.module.blackbird.ser; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; import java.util.function.Function; -import java.util.function.ToIntFunction; -import java.util.function.ToLongFunction; -import java.util.function.UnaryOperator; - -import tools.jackson.databind.*; -import tools.jackson.databind.annotation.JacksonStdImpl; -import tools.jackson.databind.introspect.AnnotatedMember; -import tools.jackson.databind.introspect.AnnotatedMethod; -import tools.jackson.databind.ser.*; -import tools.jackson.module.blackbird.util.ReflectionHack; -import tools.jackson.module.blackbird.util.Unchecked; - -import static java.lang.invoke.MethodType.*; +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.SerializationConfig; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.ser.BeanSerializer; +import tools.jackson.databind.ser.UnrolledBeanSerializer; +import tools.jackson.databind.ser.ValueSerializerModifier; +import tools.jackson.databind.ser.bean.BeanSerializerBase; +import tools.jackson.module.blackbird.codegen.CodegenDebug; +import tools.jackson.module.blackbird.codegen.CodegenFallbacks; + +/** + * Wraps eligible stock bean serializers in a placeholder that generates a + * per-bean writer at resolve time. Every gate that fails leaves the stock + * serializer in place. + */ public class BBSerializerModifier extends ValueSerializerModifier { private static final long serialVersionUID = 1L; - private final Function, Lookup> _lookups; - private final UnaryOperator _accessGrant; + // Kept for the released BlackbirdModule(Function) contract; the codec + // no longer needs it. Member access rides databind's own fixAccess (see + // MemberHandles), so a user lookup is not required for acceleration. + private final Function, MethodHandles.Lookup> _lookups; - public BBSerializerModifier(Function, MethodHandles.Lookup> lookups, UnaryOperator accessGrant) - { + public BBSerializerModifier(Function, MethodHandles.Lookup> lookups) { _lookups = lookups; - _accessGrant = accessGrant; } @Override - public List changeProperties(SerializationConfig config, - BeanDescription.Supplier beanDescRef, List beanProperties) + public ValueSerializer modifySerializer(SerializationConfig config, + BeanDescription.Supplier beanDescRef, ValueSerializer serializer) { - final Class beanClass = beanDescRef.getBeanClass(); - - /* Hmmh. Can we access stuff from private classes? - * Possibly, if we can use parent class loader. - * (should probably skip all non-public?) - */ - if (Modifier.isPrivate(beanClass.getModifiers())) { // TODO? - return beanProperties; + // Gate failures of any kind leave the stock serializer in place. The + // reflective gates can throw for exotic classes (a bean from a foreign + // classloader with inconsistent InnerClasses metadata raises + // IncompatibleClassChangeError from getEnclosingClass), and an + // acceleration modifier must never break a bean stock databind handles. + try { + return doModify(beanDescRef, serializer); + } catch (RuntimeException | LinkageError e) { + CodegenFallbacks.gateFailure(beanDescRef.getBeanClass(), e); + return serializer; } - - findProperties(beanClass, config, beanProperties); - return beanProperties; } - protected void findProperties(Class beanClass, - SerializationConfig config, List beanProperties) + private ValueSerializer doModify(BeanDescription.Supplier beanDescRef, + ValueSerializer serializer) { - MethodHandles.Lookup lookup = _lookups.apply(beanClass); - if (lookup == null) { - return; + if (serializer.getClass() != BeanSerializer.class + && serializer.getClass() != UnrolledBeanSerializer.class) { + return skip(beanDescRef, serializer, "not a stock bean serializer"); } - - ListIterator it = beanProperties.listIterator(); - while (it.hasNext()) { - Unchecked.runnable(() -> - createProperty(it, ReflectionHack.privateLookupIn(beanClass, lookup), config)) - .run(); + // Non-static inner classes stay stock for symmetry with the reader + // side; no other class-shape gate remains, since generated writers + // never name the bean class. The static check runs first: for a + // static member class redefined in a foreign classloader, + // getEnclosingClass raises IncompatibleClassChangeError (its + // InnerClasses metadata resolves to the parent-loaded owner), and + // such beans accelerate now. + Class beanClass = beanDescRef.getBeanClass(); + if (!Modifier.isStatic(beanClass.getModifiers()) + && beanClass.getEnclosingClass() != null) { + return skip(beanDescRef, serializer, "non-static inner class"); } + return new BBWriterPlaceholder((BeanSerializerBase) serializer); } - protected void createProperty(ListIterator it, Lookup lookup, SerializationConfig config) throws Throwable { - BeanPropertyWriter bpw = it.next(); - AnnotatedMember member = bpw.getMember(); - - Member jdkMember = member.getMember(); - // 11-Sep-2015, tatu: Let's skip virtual members (related to #57) - if (jdkMember == null) { - return; - } - // We can't access private fields or methods, skip: - if (Modifier.isPrivate(jdkMember.getModifiers())) { - return; - } - // (although, interestingly enough, can seem to access private classes...) - - // 30-Jul-2012, tatu: [#6]: Needs to skip custom serializers, if any. - if (bpw.hasSerializer() && !SerializerUtil.isDefaultSerializer(bpw.getSerializer())) { - return; - } - // [#9]: also skip unwrapping stuff... - if (bpw.isUnwrapping()) { - return; - } - - if (!bpw.getClass().isAnnotationPresent(JacksonStdImpl.class)) { - return; - } - - Class type = bpw.getMember().getRawType(); - MethodHandle getter; - if (member instanceof AnnotatedMethod) { - getter = lookup.unreflect((Method) member.getMember()); - } else { - // TODO: currently VarHandles aren't considered direct MH - return; - //getter = lookup.unreflectGetter((Field) member.getMember()); - } - lookup = _accessGrant.apply(lookup); - - if (type.isPrimitive()) { - if (type == Integer.TYPE) { - ToIntFunction accessor = (ToIntFunction) LambdaMetafactory.metafactory( - lookup, - "applyAsInt", - methodType(ToIntFunction.class), - methodType(int.class, Object.class), - getter, - getter.type()) - .getTarget().invokeExact(); - it.set(new IntPropertyWriter(bpw, accessor, null)); - } else if (type == Long.TYPE) { - ToLongFunction accessor = (ToLongFunction) LambdaMetafactory.metafactory( - lookup, - "applyAsLong", - methodType(ToLongFunction.class), - methodType(long.class, Object.class), - getter, - getter.type()) - .getTarget().invokeExact(); - it.set(new LongPropertyWriter(bpw, accessor, null)); - } else if (type == Boolean.TYPE) { - ToBooleanFunction accessor = (ToBooleanFunction) LambdaMetafactory.metafactory( - lookup, - "applyAsBoolean", - methodType(ToBooleanFunction.class), - methodType(boolean.class, Object.class), - getter, - getter.type()) - .getTarget().invokeExact(); - it.set(new BooleanPropertyWriter(bpw, accessor, null)); - } - } else { - if (type == String.class) { - Function accessor = (Function) LambdaMetafactory.metafactory( - lookup, - "apply", - methodType(Function.class), - methodType(Object.class, Object.class), - getter, - getter.type()) - .getTarget().invokeExact(); - it.set(new StringPropertyWriter(bpw, accessor, null)); - } else { - Function accessor = (Function) LambdaMetafactory.metafactory( - lookup, - "apply", - methodType(Function.class), - methodType(Object.class, Object.class), - getter, - getter.type()) - .getTarget().invokeExact(); - it.set(new ObjectPropertyWriter(bpw, accessor, null)); - } - } + private static ValueSerializer skip(BeanDescription.Supplier beanDescRef, + ValueSerializer serializer, String reason) { + CodegenDebug.logSkip("writer", beanDescRef.getBeanClass(), reason); + return serializer; } + } diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBWriterFactory.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBWriterFactory.java new file mode 100644 index 000000000..cd7476804 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBWriterFactory.java @@ -0,0 +1,205 @@ +package tools.jackson.module.blackbird.ser; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.introspect.AnnotatedField; +import tools.jackson.databind.introspect.AnnotatedMethod; +import tools.jackson.databind.ser.BeanPropertyWriter; +import tools.jackson.databind.ser.PropertyWriter; +import tools.jackson.databind.ser.bean.BeanSerializerBase; +import tools.jackson.databind.ser.std.NullSerializer; +import tools.jackson.databind.MapperFeature; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.ViewStrategy; +import tools.jackson.module.blackbird.codegen.WriterCodegen; +import tools.jackson.module.blackbird.codegen.WriterCodegen.GenWProp; +import tools.jackson.module.blackbird.codegen.WriterCodegen.WKind; +import tools.jackson.module.blackbird.codegen.MemberHandles; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; +import tools.jackson.module.blackbird.codegen.CodegenDebug; +import tools.jackson.module.blackbird.codegen.CodegenFallbacks; + +/** + * Builds a generated serializer from a resolved stock bean serializer, or + * returns null when the bean does not qualify. Same philosophy as the + * deserializer side: anything not cheaply verifiable rides the stock + * PropertyWriter from generated code, and member access goes through + * {@link MemberHandles} - databind ran fixAccess on every accessor before + * this factory sees it, so an unreflect failure means the stock path could + * not read the property either, and the property demotes. + */ +final class BBWriterFactory +{ + private static final Set STOCK_SCALAR_SERS = Set.of( + "tools.jackson.databind.ser.jdk.StringSerializer", + "tools.jackson.databind.ser.jdk.NumberSerializers$IntegerSerializer", + "tools.jackson.databind.ser.jdk.NumberSerializers$LongSerializer", + "tools.jackson.databind.ser.jdk.BooleanSerializer"); + + private BBWriterFactory() {} + + static ValueSerializer tryGenerate(BeanSerializerBase delegate, boolean hasTypeId, + SerializationContext ctxt) { + try { + ValueSerializer codec = generate(delegate, hasTypeId, ctxt); + CodegenDebug.log(() -> "writer tryGenerate " + delegate.handledType().getName() + + " -> " + (codec == null ? "null (gated)" : codec.getClass().getName())); + return codec; + } catch (Throwable t) { + if (CodegenDebug.ENABLED) { + CodegenDebug.log(() -> "writer tryGenerate " + delegate.handledType().getName() + " threw:"); + t.printStackTrace(); + } + CodegenFallbacks.generationFailure(delegate.handledType(), t); + return null; + } + } + + private static ValueSerializer generate(BeanSerializerBase delegate, + boolean hasTypeId, SerializationContext ctxt) + throws ReflectiveOperationException { + if (delegate.usesObjectId() || delegate.getFilterId() != null) { + CodegenDebug.log(() -> "writer gate: object id or filter id"); + return null; + } + Class beanClass = delegate.handledType(); + List props = new ArrayList<>(); + for (Iterator it = delegate.properties(); it.hasNext(); ) { + PropertyWriter writer = it.next(); + props.add(classify(writer)); + } + if (props.isEmpty()) { + CodegenDebug.log(() -> "writer gate: no props"); + return null; + } + boolean includeByDefault = ctxt.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); + // A @JsonTypeId property replaces the resolver-computed type id, which + // the generated serializeWithType does not model; such beans keep the + // base class's whole-call forwarding for typed writes. + boolean nativePolyWrites = !hasTypeId; + // null when the writer would be too large to JIT-compile. + return WriterCodegen.generate(beanClass, props, delegate, + viewStrategy(props, includeByDefault), includeByDefault, nativePolyWrites); + } + + // Matches the stock filtered-writer-array rule. No property declares a + // view: with default inclusion on, stock ignores views entirely (NONE, no + // view code); with it off, an active view hides every property, so the + // rare view-active call DELEGATEs to stock's write-nothing path and the + // no-view hot path stays free of per-property tests. Properties with + // views take MASK (fast path under views) up to 64 properties, DELEGATE + // beyond. Visibility is read per property from BeanPropertyWriter.getViews. + private static ViewStrategy viewStrategy(List props, boolean includeByDefault) { + boolean viewsFound = false; + for (GenWProp p : props) { + if (p.stock() instanceof BeanPropertyWriter bpw + && bpw.getViews() != null && bpw.getViews().length > 0) { + viewsFound = true; + break; + } + } + if (!viewsFound) { + return includeByDefault ? ViewStrategy.NONE : ViewStrategy.DELEGATE; + } + return (props.size() > 64) ? ViewStrategy.DELEGATE : ViewStrategy.MASK; + } + + // A generated arm writes the value directly, so every per-property + // feature of BeanPropertyWriter.serializeAsProperty that it skips must be + // absent: a value type serializer (polymorphic values, default typing), + // an applied view, suppression of nulls or values, and, for values that + // can be null, a null serializer other than the default one (the arm + // writes JSON null itself). + private static GenWProp classify(PropertyWriter writer) { + if (writer.getClass() != BeanPropertyWriter.class) { + return stock(writer); + } + BeanPropertyWriter bpw = (BeanPropertyWriter) writer; + WriterProbe.State state = WriterProbe.stateOf(bpw); + if (bpw.willSuppressNulls() || state.suppressableValue() != null + || bpw.getTypeSerializer() != null || state.applyView() != null) { + return stock(writer); + } + Class raw; + MethodHandle handle; + try { + if (bpw.getMember() instanceof AnnotatedMethod am + && am.getAnnotated() != null + && am.getAnnotated().getParameterCount() == 0) { + Method getter = am.getAnnotated(); + raw = getter.getReturnType(); + handle = MemberHandles.getter(getter); + } else if (bpw.getMember() instanceof AnnotatedField af + && af.getAnnotated() != null + && !Modifier.isStatic(af.getAnnotated().getModifiers())) { + Field field = af.getAnnotated(); + raw = field.getType(); + handle = MemberHandles.fieldGetter(field); + } else { + return stock(writer); + } + } catch (IllegalAccessException | RuntimeException e) { + return stock(writer); + } + if (!raw.isPrimitive() && (state.nullSerializer() == null + || state.nullSerializer().getClass() != NullSerializer.class)) { + return stock(writer); + } + ValueSerializer valueSer = bpw.getSerializer(); + if (valueSer instanceof GeneratedWriterBase child && !raw.isPrimitive()) { + return new GenWProp(WKind.CHILD, writer, bpw.getSerializedName(), child, handle); + } + WKind kind = scalarKind(raw); + if (kind == null || valueSer == null + || !STOCK_SCALAR_SERS.contains(valueSer.getClass().getName())) { + return stock(writer); + } + return new GenWProp(kind, writer, bpw.getSerializedName(), null, handle); + } + + // BeanPropertyWriter keeps these in protected fields with no accessor; + // the copy constructor carries them into this probe, whose own inherited + // fields are readable. + private static final class WriterProbe extends BeanPropertyWriter { + record State(Object suppressableValue, ValueSerializer nullSerializer, + Class applyView) {} + + private WriterProbe(BeanPropertyWriter base) { + super(base); + } + + static State stateOf(BeanPropertyWriter base) { + WriterProbe p = new WriterProbe(base); + return new State(p._suppressableValue, p._nullSerializer, p._applyView); + } + } + + private static GenWProp stock(PropertyWriter writer) { + CodegenDebug.log(() -> "writer prop stock: " + writer.getName()); + return new GenWProp(WKind.STOCK, writer, null, null, null); + } + + private static WKind scalarKind(Class raw) { + if (raw == String.class) { + return WKind.STRING; + } + if (raw == int.class) { + return WKind.INT; + } + if (raw == long.class) { + return WKind.LONG; + } + if (raw == boolean.class) { + return WKind.BOOLEAN; + } + return null; + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBWriterPlaceholder.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBWriterPlaceholder.java new file mode 100644 index 000000000..ad2de3e1c --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BBWriterPlaceholder.java @@ -0,0 +1,51 @@ +package tools.jackson.module.blackbird.ser; + +import tools.jackson.core.JsonGenerator; +import tools.jackson.databind.BeanProperty; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.ser.BeanSerializer; +import tools.jackson.databind.ser.bean.BeanSerializerBase; +import tools.jackson.module.blackbird.codegen.CodegenDebug; + +/** + * Takes the place of a stock bean serializer from the modifier on: it is a + * copy of that serializer (as a {@link BeanSerializer}), so other modifiers + * and databind's own checks see a bean serializer, and it behaves as stock + * until resolution. Resolving it resolves the stock state and generates the + * writer, which contextualization then hands out. + */ +final class BBWriterPlaceholder extends BeanSerializer +{ + private volatile ValueSerializer _codec; + + BBWriterPlaceholder(BeanSerializerBase src) { + super(src); + } + + @Override + public void resolve(SerializationContext ctxt) { + CodegenDebug.log(() -> "writer resolve " + handledType().getName()); + super.resolve(ctxt); + _codec = BBWriterFactory.tryGenerate(this, _typeId != null, ctxt); + } + + // Contextualization is the stock one; when it leaves this serializer as + // it is, the generated writer takes its place. + @Override + public ValueSerializer createContextual(SerializationContext ctxt, BeanProperty property) { + ValueSerializer contextual = super.createContextual(ctxt, property); + ValueSerializer codec = _codec; + return (contextual == this && codec != null) ? codec : contextual; + } + + @Override + public void serialize(Object value, JsonGenerator g, SerializationContext ctxt) { + ValueSerializer codec = _codec; + if (codec != null) { + codec.serialize(value, g, ctxt); + } else { + super.serialize(value, g, ctxt); + } + } +} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BooleanPropertyWriter.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BooleanPropertyWriter.java deleted file mode 100644 index 7ebdf15b2..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/BooleanPropertyWriter.java +++ /dev/null @@ -1,97 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import tools.jackson.core.JsonGenerator; -import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.PropertyName; -import tools.jackson.databind.SerializationContext; -import tools.jackson.databind.ser.BeanPropertyWriter; - -final class BooleanPropertyWriter - extends OptimizedBeanPropertyWriter -{ - private static final long serialVersionUID = 1L; - - private final boolean _suppressableSet; - private final boolean _suppressableBoolean; - - private final ToBooleanFunction _acc; - - public BooleanPropertyWriter(BeanPropertyWriter src, ToBooleanFunction acc, ValueSerializer ser) { - super(src, ser); - _acc = acc; - - if (_suppressableValue instanceof Boolean) { - _suppressableBoolean = ((Boolean)_suppressableValue).booleanValue(); - _suppressableSet = true; - } else { - _suppressableBoolean = false; - _suppressableSet = false; - } - } - - protected BooleanPropertyWriter(BooleanPropertyWriter base, PropertyName name) { - super(base, name); - _suppressableSet = base._suppressableSet; - _suppressableBoolean = base._suppressableBoolean; - _acc = base._acc; - } - - @Override - protected BeanPropertyWriter _new(PropertyName newName) { - return new BooleanPropertyWriter(this, newName); - } - - @Override - public BeanPropertyWriter withSerializer(ValueSerializer ser) { - return new BooleanPropertyWriter(this, _acc, ser); - } - - /* - /********************************************************************** - /* Overrides - /********************************************************************** - */ - - @Override - public final void serializeAsProperty(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsProperty(bean, g, ctxt); - return; - } - boolean value; - try { - value = _acc.applyAsBoolean(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, false); - return; - } - if (!_suppressableSet || _suppressableBoolean != value) { - g.writeName(_fastName); - g.writeBoolean(value); - } - } - - @Override - public final void serializeAsElement(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsElement(bean, g, ctxt); - return; - } - boolean value; - try { - value = _acc.applyAsBoolean(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, true); - return; - } - if (!_suppressableSet || _suppressableBoolean != value) { - g.writeBoolean(value); - } else { // important: MUST output a placeholder - serializeAsOmittedElement(bean, g, ctxt); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/IntPropertyWriter.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/IntPropertyWriter.java deleted file mode 100644 index 078d80035..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/IntPropertyWriter.java +++ /dev/null @@ -1,99 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import java.util.function.ToIntFunction; - -import tools.jackson.core.JsonGenerator; -import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.PropertyName; -import tools.jackson.databind.SerializationContext; -import tools.jackson.databind.ser.BeanPropertyWriter; - -final class IntPropertyWriter - extends OptimizedBeanPropertyWriter -{ - private static final long serialVersionUID = 1L; - - private final int _suppressableInt; - private final boolean _suppressableIntSet; - - private final ToIntFunction _acc; - - public IntPropertyWriter(BeanPropertyWriter src, ToIntFunction acc, ValueSerializer ser) { - super(src, ser); - _acc = acc; - - if (_suppressableValue instanceof Integer) { - _suppressableInt = (Integer)_suppressableValue; - _suppressableIntSet = true; - } else { - _suppressableInt = 0; - _suppressableIntSet = false; - } - } - - protected IntPropertyWriter(IntPropertyWriter base, PropertyName name) { - super(base, name); - _suppressableInt = base._suppressableInt; - _suppressableIntSet = base._suppressableIntSet; - _acc = base._acc; - } - - @Override - protected BeanPropertyWriter _new(PropertyName newName) { - return new IntPropertyWriter(this, newName); - } - - @Override - public BeanPropertyWriter withSerializer(ValueSerializer ser) { - return new IntPropertyWriter(this, _acc, ser); - } - - /* - /********************************************************************** - /* Overrides - /********************************************************************** - */ - - @Override - public final void serializeAsProperty(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsProperty(bean, g, ctxt); - return; - } - int value; - try { - value = _acc.applyAsInt(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, false); - return; - } - if (!_suppressableIntSet || _suppressableInt != value) { - g.writeName(_fastName); - g.writeNumber(value); - } - } - - @Override - public final void serializeAsElement(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsElement(bean, g, ctxt); - return; - } - int value; - try { - value = _acc.applyAsInt(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, true); - return; - } - if (!_suppressableIntSet || _suppressableInt != value) { - g.writeNumber(value); - } else { // important: MUST output a placeholder - serializeAsOmittedElement(bean, g, ctxt); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/LongPropertyWriter.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/LongPropertyWriter.java deleted file mode 100644 index 02db15de6..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/LongPropertyWriter.java +++ /dev/null @@ -1,99 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import java.util.function.ToLongFunction; - -import tools.jackson.core.JsonGenerator; -import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.PropertyName; -import tools.jackson.databind.SerializationContext; -import tools.jackson.databind.ser.BeanPropertyWriter; - -final class LongPropertyWriter - extends OptimizedBeanPropertyWriter -{ - private static final long serialVersionUID = 1L; - - private final long _suppressableLong; - private final boolean _suppressableSet; - - private final ToLongFunction _acc; - - public LongPropertyWriter(BeanPropertyWriter src, ToLongFunction acc, ValueSerializer ser) { - super(src, ser); - _acc = acc; - - if (_suppressableValue instanceof Long) { - _suppressableLong = (Long)_suppressableValue; - _suppressableSet = true; - } else { - _suppressableLong = 0L; - _suppressableSet = false; - } - } - - protected LongPropertyWriter(LongPropertyWriter base, PropertyName name) { - super(base, name); - _suppressableSet = base._suppressableSet; - _suppressableLong = base._suppressableLong; - _acc = base._acc; - } - - @Override - protected BeanPropertyWriter _new(PropertyName newName) { - return new LongPropertyWriter(this, newName); - } - - @Override - public BeanPropertyWriter withSerializer(ValueSerializer ser) { - return new LongPropertyWriter(this, _acc, ser); - } - - /* - /********************************************************************** - /* Overrides - /********************************************************************** - */ - - @Override - public final void serializeAsProperty(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsProperty(bean, g, ctxt); - return; - } - long value; - try { - value = _acc.applyAsLong(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, false); - return; - } - if (!_suppressableSet || _suppressableLong != value) { - g.writeName(_fastName); - g.writeNumber(value); - } - } - - @Override - public final void serializeAsElement(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsElement(bean, g, ctxt); - return; - } - long value; - try { - value = _acc.applyAsLong(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, false); - return; - } - if (!_suppressableSet || _suppressableLong != value) { - g.writeNumber(value); - } else { // important: MUST output a placeholder - serializeAsOmittedElement(bean, g, ctxt); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/ObjectPropertyWriter.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/ObjectPropertyWriter.java deleted file mode 100644 index 2a0a63c6f..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/ObjectPropertyWriter.java +++ /dev/null @@ -1,162 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import java.util.function.Function; - -import tools.jackson.core.JsonGenerator; -import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.PropertyName; -import tools.jackson.databind.SerializationContext; -import tools.jackson.databind.ser.BeanPropertyWriter; -import tools.jackson.databind.ser.impl.PropertySerializerMap; - -final class ObjectPropertyWriter - extends OptimizedBeanPropertyWriter -{ - private static final long serialVersionUID = 1L; - private final Function _acc; - - public ObjectPropertyWriter(BeanPropertyWriter src, Function acc, ValueSerializer ser) { - super(src, ser); - _acc = acc; - } - - protected ObjectPropertyWriter(ObjectPropertyWriter base, PropertyName name) { - super(base, name); - _acc = base._acc; - } - - @Override - protected BeanPropertyWriter _new(PropertyName newName) { - return new ObjectPropertyWriter(this, newName); - } - - @Override - public BeanPropertyWriter withSerializer(ValueSerializer ser) { - return new ObjectPropertyWriter(this, _acc, ser); - } - - /* - /********************************************************************** - /* Overrides - /********************************************************************** - */ - - @Override - public final void serializeAsProperty(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsProperty(bean, g, ctxt); - return; - } - Object value; - try { - value = _acc.apply(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, false); - return; - } - // Null (etc) handling; copied from super-class impl - if (value == null) { - // 20-Jun-2022, tatu: Defer checking of null, see [databind#3481] - if ((_suppressableValue != null) - && ctxt.includeFilterSuppressNulls(_suppressableValue)) { - return; - } - if (_nullSerializer != null) { - g.writeName(_fastName); - _nullSerializer.serialize(null, g, ctxt); - } - return; - } - ValueSerializer ser = _serializer; - if (ser == null) { - Class cls = value.getClass(); - PropertySerializerMap map = _dynamicSerializers; - ser = map.serializerFor(cls); - if (ser == null) { - ser = _findAndAddDynamic(map, cls, ctxt); - } - } - if (_suppressableValue != null) { - if (MARKER_FOR_EMPTY == _suppressableValue) { - if (ser.isEmpty(ctxt, value)) { - return; - } - } else if (_suppressableValue.equals(value)) { - return; - } - } - if (value == bean) { - // three choices: exception; handled by call; or pass-through - if (_handleSelfReference(bean, g, ctxt, ser)) { - return; - } - } - g.writeName(_fastName); - if (_typeSerializer == null) { - ser.serialize(value, g, ctxt); - } else { - ser.serializeWithType(value, g, ctxt, _typeSerializer); - } - } - - @Override - public final void serializeAsElement(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsElement(bean, g, ctxt); - return; - } - Object value; - try { - value = _acc.apply(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, true); - return; - } - // Null (etc) handling; copied from super-class impl - if (value == null) { - if (_nullSerializer != null) { - _nullSerializer.serialize(null, g, ctxt); - } else if (_suppressNulls) { - serializeAsOmittedElement(bean, g, ctxt); - } else { - ctxt.defaultSerializeNullValue(g); - } - return; - } - ValueSerializer ser = _serializer; - if (ser == null) { - Class cls = value.getClass(); - PropertySerializerMap map = _dynamicSerializers; - ser = map.serializerFor(cls); - if (ser == null) { - ser = _findAndAddDynamic(map, cls, ctxt); - } - } - if (_suppressableValue != null) { - if (MARKER_FOR_EMPTY == _suppressableValue) { - if (ser.isEmpty(ctxt, value)) { - serializeAsOmittedElement(bean, g, ctxt); - return; - } - } else if (_suppressableValue.equals(value)) { - serializeAsOmittedElement(bean, g, ctxt); - return; - } - } - if (value == bean) { - // three choices: exception; handled by call; or pass-through - if (_handleSelfReference(bean, g, ctxt, ser)) { - return; - } - } - if (_typeSerializer == null) { - ser.serialize(value, g, ctxt); - } else { - ser.serializeWithType(value, g, ctxt, _typeSerializer); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/OptimizedBeanPropertyWriter.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/OptimizedBeanPropertyWriter.java deleted file mode 100644 index ada422a1e..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/OptimizedBeanPropertyWriter.java +++ /dev/null @@ -1,130 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import tools.jackson.core.JsonGenerator; -import tools.jackson.core.SerializableString; - -import tools.jackson.databind.*; -import tools.jackson.databind.jsontype.TypeSerializer; -import tools.jackson.databind.ser.BeanPropertyWriter; - -/** - * Intermediate base class that is used for concrete - * per-type implementations - */ -@SuppressWarnings("serial") -abstract class OptimizedBeanPropertyWriter> - extends BeanPropertyWriter -{ - /** - * Locally stored version of efficiently serializable name. - * Used to work around earlier problems with typing between - * interface, implementation - */ - protected final SerializableString _fastName; - - protected final BeanPropertyWriter fallbackWriter; - // Not volatile to prevent overhead, worst case is we trip the exception a few extra times - protected boolean broken = false; - - protected OptimizedBeanPropertyWriter(BeanPropertyWriter src, ValueSerializer ser) - { - super(src); - this.fallbackWriter = unwrapFallbackWriter(src); - // either use the passed on serializer or the original one - _serializer = (ser != null) ? ser : src.getSerializer(); - _fastName = src.getSerializedName(); - } - - protected OptimizedBeanPropertyWriter(OptimizedBeanPropertyWriter base, PropertyName name) { - super(base, name); - // 24-Oct-2017, tatu: Should this be changed too? - fallbackWriter = base.fallbackWriter; - _serializer = base._serializer; - _fastName = getSerializedName(); - } - - private BeanPropertyWriter unwrapFallbackWriter(BeanPropertyWriter srcIn) - { - while (srcIn instanceof OptimizedBeanPropertyWriter) { - srcIn = ((OptimizedBeanPropertyWriter)srcIn).fallbackWriter; - } - return srcIn; - } - - // Ensure it gets defined - @Override - protected abstract BeanPropertyWriter _new(PropertyName newName); - - @Override - public void assignTypeSerializer(TypeSerializer typeSer) { - super.assignTypeSerializer(typeSer); - if (fallbackWriter != null) { - fallbackWriter.assignTypeSerializer(typeSer); - } - // 04-Oct-2015, tatu: Should we handle this wrt [module-afterburner#59]? - // Seems unlikely, as String/long/int/boolean are final types; and for - // basic 'Object' we delegate to deserializer as expected - } - - @Override - public void assignSerializer(ValueSerializer ser) { - super.assignSerializer(ser); - if (fallbackWriter != null) { - fallbackWriter.assignSerializer(ser); - } - // 04-Oct-2015, tatu: To fix [module-afterburner#59], need to disable use of - // fully optimized variant - if (!SerializerUtil.isDefaultSerializer(ser)) { - broken = true; - } - } - - @Override - public void assignNullSerializer(ValueSerializer nullSer) { - super.assignNullSerializer(nullSer); - if (fallbackWriter != null) { - fallbackWriter.assignNullSerializer(nullSer); - } - } - - public abstract BeanPropertyWriter withSerializer(ValueSerializer ser); - - @Override - public abstract void serializeAsProperty(Object bean, JsonGenerator g, SerializationContext ctxt) throws Exception; - - @Override - public abstract void serializeAsElement(Object bean, JsonGenerator g, SerializationContext ctxt) throws Exception; - - // note: synchronized used to try to minimize race conditions; also, should NOT - // be a performance problem - protected synchronized void _handleProblem(Object bean, JsonGenerator g, SerializationContext ctxt, - Throwable t, boolean element) throws Exception - { - if ((t instanceof IllegalAccessError) - || (t instanceof SecurityException)) { - _reportProblem(bean, t); - if (element) { - fallbackWriter.serializeAsElement(bean, g, ctxt); - } else { - fallbackWriter.serializeAsProperty(bean, g, ctxt); - } - return; - } - if (t instanceof Error) { - throw (Error) t; - } - throw (Exception) t; - } - - protected void _reportProblem(Object bean, Throwable e) - { - broken = true; - String msg = String.format("Disabling Blackbird serialization for %s (field %s; mutator %s), due to access error (type %s, message=%s)%n", - bean.getClass(), _name, getClass().getName(), - e.getClass().getName(), e.getMessage()); - Logger.getLogger(OptimizedBeanPropertyWriter.class.getName()).log(Level.WARNING, msg, e); - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/SerializerUtil.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/SerializerUtil.java deleted file mode 100644 index 74aa75657..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/SerializerUtil.java +++ /dev/null @@ -1,35 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.ser.std.ToStringSerializer; -import tools.jackson.databind.util.ClassUtil; - -/** - * Helper class that contains utility methods needed by various other classes - * in this package. - * - * @since 2.12 - */ -class SerializerUtil -{ - /** - * Helper method used to check whether given serializer is the default - * serializer implementation: this is necessary to avoid overriding other - * kinds of serializers. - */ - public static boolean isDefaultSerializer(ValueSerializer ser) - { - if (ser == null) { - return true; - } - if (ClassUtil.isJacksonStdImpl(ser)) { - // 20-Nov-2020, tatu: As per [modules-base#117], need to consider - // one standard serializer that should not be replaced... - if (ser instanceof ToStringSerializer) { - return false; - } - return true; - } - return false; - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/StringPropertyWriter.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/StringPropertyWriter.java deleted file mode 100644 index 33b274019..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/StringPropertyWriter.java +++ /dev/null @@ -1,123 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -import java.util.function.Function; - -import tools.jackson.core.JsonGenerator; -import tools.jackson.databind.ValueSerializer; -import tools.jackson.databind.PropertyName; -import tools.jackson.databind.SerializationContext; -import tools.jackson.databind.ser.BeanPropertyWriter; - -final class StringPropertyWriter - extends OptimizedBeanPropertyWriter -{ - private static final long serialVersionUID = 1L; - - private final Function _acc; - - public StringPropertyWriter(BeanPropertyWriter src, Function acc, ValueSerializer ser) { - super(src, ser); - _acc = acc; - } - - protected StringPropertyWriter(StringPropertyWriter base, PropertyName name) { - super(base, name); - _acc = base._acc; - } - - @Override - protected BeanPropertyWriter _new(PropertyName newName) { - return new StringPropertyWriter(this, newName); - } - - @Override - public BeanPropertyWriter withSerializer(ValueSerializer ser) { - return new StringPropertyWriter(this, _acc, ser); - } - - /* - /********************************************************************** - /* Overrides - /********************************************************************** - */ - - @Override - public final void serializeAsProperty(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsProperty(bean, g, ctxt); - return; - } - String value; - try { - value = _acc.apply(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, false); - return; - } - // Null (etc) handling; copied from super-class impl - if (value == null) { - // 20-Jun-2022, tatu: Defer checking of null, see [databind#3481] - if ((_suppressableValue != null) - && ctxt.includeFilterSuppressNulls(_suppressableValue)) { - return; - } - if (_nullSerializer != null) { - g.writeName(_fastName); - _nullSerializer.serialize(null, g, ctxt); - } - return; - } - if (_suppressableValue != null) { - if (MARKER_FOR_EMPTY == _suppressableValue) { - if (value.length() == 0) { - return; - } - } else if (_suppressableValue.equals(value)) { - return; - } - } - g.writeName(_fastName); - g.writeString(value); - } - - @Override - public final void serializeAsElement(Object bean, JsonGenerator g, SerializationContext ctxt) - throws Exception - { - if (broken) { - fallbackWriter.serializeAsElement(bean, g, ctxt); - return; - } - - String value; - try { - value = _acc.apply(bean); - } catch (Throwable t) { - _handleProblem(bean, g, ctxt, t, true); - return; - } - // Null (etc) handling; copied from super-class impl - if (value == null) { - if (_suppressNulls) { - serializeAsOmittedElement(bean, g, ctxt); - } else { - ctxt.defaultSerializeNullValue(g); - } - return; - } - if (_suppressableValue != null) { - if (MARKER_FOR_EMPTY == _suppressableValue) { - if (value.length() == 0) { - serializeAsOmittedElement(bean, g, ctxt); - return; - } - } else if (_suppressableValue.equals(value)) { - serializeAsOmittedElement(bean, g, ctxt); - return; - } - } - g.writeString(value); - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/ToBooleanFunction.java b/blackbird/src/main/java/tools/jackson/module/blackbird/ser/ToBooleanFunction.java deleted file mode 100644 index be50ab935..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/ser/ToBooleanFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package tools.jackson.module.blackbird.ser; - -@FunctionalInterface -public interface ToBooleanFunction { - boolean applyAsBoolean(Object bean); -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedFunction.java b/blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedFunction.java deleted file mode 100644 index 53d45863c..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedFunction.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c), Data Geekery GmbH, contact@datageekery.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package tools.jackson.module.blackbird.util; - -@FunctionalInterface -public interface CheckedFunction { - T apply(X x) throws Throwable; -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedSupplier.java b/blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedSupplier.java deleted file mode 100644 index c6b3c8b39..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedSupplier.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c), Data Geekery GmbH, contact@datageekery.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package tools.jackson.module.blackbird.util; - -@FunctionalInterface -public interface CheckedSupplier { - T get() throws Throwable; -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/util/ReflectionHack.java b/blackbird/src/main/java/tools/jackson/module/blackbird/util/ReflectionHack.java deleted file mode 100644 index 2817f384e..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/util/ReflectionHack.java +++ /dev/null @@ -1,87 +0,0 @@ -package tools.jackson.module.blackbird.util; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.reflect.Constructor; -import java.util.logging.Level; -import java.util.logging.Logger; - -import tools.jackson.module.blackbird.BlackbirdModule; - -import java.lang.invoke.MethodType; - -/** - * Allow private access to fields across using a JVM version-appropriate strategy - * without having a compile-time dependency on Java 9+. - * - * This entire class can be replaced by a direct call to JDK 9+ - * {@link MethodHandles}{@code .privateLookupIn} - * once Jackson targets Java >= 9. - */ -public class ReflectionHack { - @SuppressWarnings("synthetic-access") - public static Lookup privateLookupIn(Class lookup, MethodHandles.Lookup orig) throws IllegalAccessException { - if (Java9Up.FACTORY != null) { - return Java9Up.privateLookupIn(lookup, orig); - } - return Java8.privateLookupIn(lookup); - } - - static class Java9Up { - static final MethodHandle FACTORY; - - static { - FACTORY = init(); - } - - private static MethodHandle init() { - try { - return MethodHandles.lookup().findStatic( - MethodHandles.class, - "privateLookupIn", - MethodType.methodType(Lookup.class, Class.class, MethodHandles.Lookup.class)); - } catch (ReflectiveOperationException e) { - Logger l = Logger.getLogger(BlackbirdModule.class.getName()); - // 02-Nov-2022, tatu: as per [modules-base#187] only log exception at trace - l.log(Level.WARNING, - "Unable to find Java 9+ MethodHandles.privateLookupIn. Blackbird is not performing optimally!"); - l.log(Level.FINE, - "Failure reason for `MethodHandles.privateLookupIn()` not being found: "+e.getMessage(), e); - return null; - } - } - - public static Lookup privateLookupIn(Class lookup, Lookup orig) { - try { - return (Lookup) FACTORY.invokeExact(lookup, orig); - } catch (ReflectiveOperationException e) { - return orig; - } catch (Throwable t) { - throw Sneaky.throwAnyway(t); - } - } - } - - static class Java8 { - private static final Constructor FACTORY; - - static { - try { - FACTORY = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); - } catch (ReflectiveOperationException e) { - throw new RuntimeException( - "Unable to use private Lookup constructor", - e); - } - - if (!FACTORY.isAccessible()) { - FACTORY.setAccessible(true); - } - } - - public static Lookup privateLookupIn(Class lookup) { - return Unchecked.function(FACTORY::newInstance).apply(lookup); - } - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/util/Sneaky.java b/blackbird/src/main/java/tools/jackson/module/blackbird/util/Sneaky.java deleted file mode 100644 index 75cdd6713..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/util/Sneaky.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c), Data Geekery GmbH, contact@datageekery.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package tools.jackson.module.blackbird.util; - -import java.lang.reflect.InvocationTargetException; - -public class Sneaky { - private Sneaky() { - throw new RuntimeException(); - } - - /** - * Will always throw an exception, so the caller should also always throw the dummy return value to make sure the control flow remains clear. - */ - public static RuntimeException throwAnyway(Throwable t) { - if (t instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - - if (t instanceof InvocationTargetException) { - throw throwAnyway(t.getCause()); - } - - throwEvadingChecks(t); - - // never reached - return null; - } - - @SuppressWarnings("unchecked") - private static void throwEvadingChecks(Throwable throwable) throws E { - throw (E) throwable; - } -} diff --git a/blackbird/src/main/java/tools/jackson/module/blackbird/util/Unchecked.java b/blackbird/src/main/java/tools/jackson/module/blackbird/util/Unchecked.java deleted file mode 100644 index 76ea1186e..000000000 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/util/Unchecked.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c), Data Geekery GmbH, contact@datageekery.com - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package tools.jackson.module.blackbird.util; - -import java.util.concurrent.Callable; -import java.util.function.Function; -import java.util.function.Supplier; - -public class Unchecked { - private Unchecked() { - throw new RuntimeException(); - } - - @SuppressWarnings("PMD.DoNotUseThreads") - public static Runnable runnable(CheckedRunnable checkedRunnable) { - return () -> { - try { - checkedRunnable.run(); - } catch (Throwable t) { - throw Sneaky.throwAnyway(t); - } - }; - } - - public static Supplier supplier(CheckedSupplier checkedSupplier) { - return () -> { - try { - return checkedSupplier.get(); - } catch (Throwable t) { - throw Sneaky.throwAnyway(t); - } - }; - } - - public static Function function(CheckedFunction checkedFunction) { - return x -> { - try { - return checkedFunction.apply(x); - } catch (Throwable t) { - throw Sneaky.throwAnyway(t); - } - }; - } - - public interface SneakyCallable extends Callable { - @Override - T call(); // no 'throws Exception' - } - - public interface CheckedRunnable { - void run() throws Throwable; - } -} diff --git a/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanReaderGenerator.java b/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanReaderGenerator.java new file mode 100644 index 000000000..be29245b3 --- /dev/null +++ b/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanReaderGenerator.java @@ -0,0 +1,1071 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassHierarchyResolver; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.Label; +import java.lang.classfile.MethodModel; +import java.lang.classfile.attribute.CodeAttribute; +import java.lang.classfile.attribute.MethodParameterInfo; +import java.lang.classfile.attribute.MethodParametersAttribute; +import java.lang.classfile.instruction.SwitchCase; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.core.sym.PropertyNameMatcher; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.deser.SettableBeanProperty; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueInstantiator; +import tools.jackson.databind.deser.bean.BeanDeserializer; +import tools.jackson.databind.deser.bean.BeanPropertyMap; +import tools.jackson.databind.deser.bean.BuilderBasedDeserializer; +import tools.jackson.databind.util.ClassUtil; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.GenProp; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.Mode; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.ViewStrategy; +import tools.jackson.module.blackbird.internal.GeneratedBuilderBase; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; + +/** + * Emits the class file of a hidden-class deserializer for one bean (see + * {@link ReaderCodegen}, which defines it): a loop on nextNameMatch, a + * tableswitch on the property index, inlined scalar reads for eligible + * properties, and the stock SettableBeanProperty (a named classData constant) + * for everything else. Compiled at release 25 into META-INF/versions/25, + * since it uses the ClassFile API; reached through {@link ClassFileEmitters}. + */ +final class BeanReaderGenerator +{ + private static final ClassDesc CD_JSON_PARSER = Descs.of(JsonParser.class); + private static final ClassDesc CD_JSON_TOKEN = Descs.of(JsonToken.class); + private static final ClassDesc CD_DESER_CONTEXT = Descs.of(DeserializationContext.class); + private static final ClassDesc CD_DESER_FEATURE = Descs.of(DeserializationFeature.class); + private static final ClassDesc CD_NAME_MATCHER = Descs.of(PropertyNameMatcher.class); + private static final ClassDesc CD_SETTABLE_PROP = Descs.of(SettableBeanProperty.class); + private static final ClassDesc CD_READER_BASE = Descs.of(GeneratedReaderBase.class); + private static final ClassDesc CD_BUILDER_BASE = Descs.of(GeneratedBuilderBase.class); + private static final ClassDesc CD_VALUE_DESERIALIZER = Descs.of(ValueDeserializer.class); + private static final ClassDesc CD_BEAN_PROPERTY_MAP = Descs.of(BeanPropertyMap.class); + private static final ClassDesc CD_VALUE_INSTANTIATOR = Descs.of(ValueInstantiator.class); + private static final ClassDesc CD_ISE = Descs.of(IllegalStateException.class); + private static final ClassDesc CD_RTE = Descs.of(RuntimeException.class); + private static final ClassDesc CD_EXCEPTION = ConstantDescs.CD_Exception; + private static final ClassDesc CD_THROWABLE = ConstantDescs.CD_Throwable; + private static final ClassDesc CD_OBJECT = ConstantDescs.CD_Object; + + private static final MethodTypeDesc MTD_READER_CTOR = MethodTypeDesc.of( + ConstantDescs.CD_void, Descs.of(BeanDeserializer.class), CD_BEAN_PROPERTY_MAP, + ConstantDescs.CD_String); + private static final MethodTypeDesc MTD_BUILDER_CTOR = MethodTypeDesc.of( + ConstantDescs.CD_void, Descs.of(BuilderBasedDeserializer.class), CD_BEAN_PROPERTY_MAP, + ConstantDescs.CD_String); + private static final MethodTypeDesc MTD_UNEXPECTED = MethodTypeDesc.of(CD_OBJECT, + CD_JSON_PARSER, CD_DESER_CONTEXT, CD_OBJECT); + private static final MethodTypeDesc MTD_NEXT_NAME_MATCH = + MethodTypeDesc.of(ConstantDescs.CD_int, CD_NAME_MATCHER); + private static final MethodTypeDesc MTD_NEXT_TOKEN = MethodTypeDesc.of(CD_JSON_TOKEN); + private static final MethodTypeDesc MTD_GET_STRING = MethodTypeDesc.of(ConstantDescs.CD_String); + private static final MethodTypeDesc MTD_GET_INT = MethodTypeDesc.of(ConstantDescs.CD_int); + private static final MethodTypeDesc MTD_GET_LONG = MethodTypeDesc.of(ConstantDescs.CD_long); + private static final MethodTypeDesc MTD_GET_BOOLEAN = MethodTypeDesc.of(ConstantDescs.CD_boolean); + private static final MethodTypeDesc MTD_ASSIGN_CURRENT = + MethodTypeDesc.of(ConstantDescs.CD_void, CD_OBJECT); + private static final MethodTypeDesc MTD_DESERIALIZE_AND_SET = MethodTypeDesc.of( + ConstantDescs.CD_void, CD_JSON_PARSER, CD_DESER_CONTEXT, CD_OBJECT); + private static final MethodTypeDesc MTD_DESERIALIZE = + MethodTypeDesc.of(CD_OBJECT, CD_JSON_PARSER, CD_DESER_CONTEXT); + private static final MethodTypeDesc MTD_DESER_SET_RETURN = MethodTypeDesc.of( + CD_OBJECT, CD_JSON_PARSER, CD_DESER_CONTEXT, CD_OBJECT); + private static final MethodTypeDesc MTD_CREATE_DEFAULT = + MethodTypeDesc.of(CD_OBJECT, CD_DESER_CONTEXT); + private static final MethodTypeDesc MTD_NEW_INSTANCE = MethodTypeDesc.of(CD_OBJECT); + private static final MethodTypeDesc MTD_BUILD_INVOKE = MethodTypeDesc.of(CD_OBJECT, CD_OBJECT); + private static final MethodTypeDesc MTD_INJECT = MethodTypeDesc.of( + ConstantDescs.CD_void, CD_DESER_CONTEXT, CD_OBJECT); + private static final MethodTypeDesc MTD_HANDLE_UNKNOWN = MethodTypeDesc.of( + ConstantDescs.CD_void, CD_JSON_PARSER, CD_DESER_CONTEXT, CD_OBJECT); + private static final MethodTypeDesc MTD_RECORD_UNKNOWN = MethodTypeDesc.of( + CD_OBJECT, CD_JSON_PARSER, CD_DESER_CONTEXT, CD_OBJECT); + private static final MethodTypeDesc MTD_RECORD_REPLAY = MethodTypeDesc.of( + CD_OBJECT, CD_DESER_CONTEXT, CD_OBJECT, CD_OBJECT); + private static final MethodTypeDesc MTD_RECORD_BUILD = MethodTypeDesc.of(CD_OBJECT, + CD_JSON_PARSER, CD_DESER_CONTEXT, ConstantDescs.CD_long, ConstantDescs.CD_long, + CD_OBJECT.arrayType(), CD_THROWABLE); + private static final MethodTypeDesc MTD_CREATION_FAILURE = + MethodTypeDesc.of(CD_OBJECT, CD_DESER_CONTEXT, CD_THROWABLE); + private static final MethodTypeDesc MTD_PROP_WRAP = MethodTypeDesc.of( + CD_RTE, CD_THROWABLE, CD_OBJECT, CD_SETTABLE_PROP, CD_DESER_CONTEXT); + private static final MethodTypeDesc MTD_STORE_FAILURE = MethodTypeDesc.of(CD_RTE, + CD_THROWABLE, CD_OBJECT, CD_OBJECT, CD_SETTABLE_PROP, CD_JSON_PARSER, + CD_DESER_CONTEXT); + private static final MethodTypeDesc MTD_IS_ENABLED = + MethodTypeDesc.of(ConstantDescs.CD_boolean, CD_DESER_FEATURE); + private static final MethodTypeDesc MTD_GET_ACTIVE_VIEW = + MethodTypeDesc.of(ConstantDescs.CD_Class); + private static final MethodTypeDesc MTD_VIEW_MASK = + MethodTypeDesc.of(ConstantDescs.CD_long, ConstantDescs.CD_Class); + private static final MethodTypeDesc MTD_HIDDEN_VIEW = MethodTypeDesc.of( + ConstantDescs.CD_void, CD_JSON_PARSER, CD_DESER_CONTEXT, CD_SETTABLE_PROP); + private static final MethodTypeDesc MTD_VISIBLE_IN_VIEW = + MethodTypeDesc.of(ConstantDescs.CD_boolean, ConstantDescs.CD_Class); + + // Generated code resolves class-data entries by NAME through the + // classDataEntry bootstrap on the base class (the JDK classDataAt + // bootstrap rejects any condy name but "_"); see ReaderCodegen.dataName. + private static final java.lang.constant.DirectMethodHandleDesc BSM_DATA_ENTRY = + ConstantDescs.ofConstantBootstrap(CD_READER_BASE, "classDataEntry", CD_OBJECT); + + // Stack map frames can need the supertypes of the types the code uses. + // The default resolver reads class files through the system class + // loader, which does not see Jackson in an application server, an OSGi + // container or a fat jar. The lookup of this module sees it. + static final ClassFile CLASS_FILE = ClassFile.of( + ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.defaultResolver() + .orElse(ClassHierarchyResolver.ofClassLoading(MethodHandles.lookup())))); + + private BeanReaderGenerator() {} + + // Builder codecs extend GeneratedBuilderBase, all others + // GeneratedReaderBase; both declare the helpers the generated code calls. + private static ClassDesc baseOf(ReaderCodegen.Plan plan) { + return (plan.mode() == Mode.BUILDER) ? CD_BUILDER_BASE : CD_READER_BASE; + } + + static byte[] emit(ReaderCodegen.Plan plan) { + return withinSizeLimit(build(plan)); + } + + // The generated class, or null when one of its methods is too large for + // the JIT (see ClassFileEmitters.MAX_METHOD_BYTES); measured on the + // emitted bytes rather than estimated. + static byte[] withinSizeLimit(byte[] bytes) { + for (MethodModel method : ClassFile.of().parse(bytes).methods()) { + Optional code = method.findAttribute(Attributes.code()); + if (code.isPresent() + && code.get().codeLength() > ClassFileEmitters.MAX_METHOD_BYTES) { + return null; + } + } + return bytes; + } + + private static byte[] build(ReaderCodegen.Plan plan) { + ClassDesc thisClass = ClassDesc.of(plan.className()); + ClassDesc base = baseOf(plan); + MethodTypeDesc ctor = (plan.mode() == Mode.BUILDER) ? MTD_BUILDER_CTOR : MTD_READER_CTOR; + return CLASS_FILE.build(thisClass, clb -> { + clb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_FINAL | ClassFile.ACC_SUPER); + clb.withSuperclass(base); + clb.withMethod(ConstantDescs.INIT_NAME, ctor, ClassFile.ACC_PUBLIC, + mb -> mb.with(params("src", "beanProperties", "layout")) + .withCode(cob -> cob.aload(0).aload(1).aload(2).aload(3) + .invokespecial(base, ConstantDescs.INIT_NAME, ctor) + .return_())); + clb.withMethod("deserialize", MTD_DESERIALIZE, ClassFile.ACC_PUBLIC, + mb -> mb.with(params("p", "ctxt")).withCode(cob -> { + if (plan.mode() == Mode.RECORD) { + buildRecordDeserialize(cob, plan); + } else { + buildDeserialize(cob, plan); + } + })); + if (plan.views() == ViewStrategy.MASK) { + emitComputeViewMask(clb, plan.props(), plan.stockName()); + } + }); + } + + static void ldcData(CodeBuilder cob, String name, ClassDesc type) { + cob.ldc(DynamicConstantDesc.ofNamed(BSM_DATA_ENTRY, name, type)); + } + + // Debug metadata: parameter names and named locals make dumped codecs + // (the blackbird.debug.dumpDir output) read like source in a decompiler; + // the verifier ignores both attributes. + static MethodParametersAttribute params(String... names) { + MethodParameterInfo[] infos = new MethodParameterInfo[names.length]; + for (int i = 0; i < names.length; i++) { + infos[i] = MethodParameterInfo.ofParameter(Optional.of(names[i]), 0); + } + return MethodParametersAttribute.of(infos); + } + + // Overrides GeneratedReaderBase._computeViewMask: bit i set when arm i's + // stock property is visible in the view, which keeps visibility semantics + // (matchers, default-view inclusion) exactly the stock ones. + private static void emitComputeViewMask(java.lang.classfile.ClassBuilder clb, + List props, String[] stockName) { + clb.withMethod("_computeViewMask", MTD_VIEW_MASK, ClassFile.ACC_PROTECTED, + mb -> mb.with(params("activeView")).withCode(cob -> { + final int maskSlot = 2; + Label scopeStart = cob.newBoundLabel(); + cob.lconst_0().lstore(maskSlot); + for (int i = 0; i < props.size(); i++) { + Label skip = cob.newLabel(); + ldcData(cob, stockName[i], CD_SETTABLE_PROP); + cob.aload(1); + cob.invokevirtual(CD_SETTABLE_PROP, "visibleInView", MTD_VISIBLE_IN_VIEW); + cob.ifeq(skip); + cob.lload(maskSlot); + cob.loadConstant(1L << i); + cob.lor(); + cob.lstore(maskSlot); + cob.labelBinding(skip); + } + cob.lload(maskSlot).lreturn(); + Label scopeEnd = cob.newBoundLabel(); + cob.localVariable(1, "activeView", ConstantDescs.CD_Class, scopeStart, scopeEnd); + cob.localVariable(maskSlot, "mask", ConstantDescs.CD_long, scopeStart, scopeEnd); + })); + } + + // MASK strategy: resolves the visibility bitmask for the call (all-ones + // when no view is active) into maskSlot. + private static void emitViewMask(CodeBuilder cob, ClassDesc base, int ctxt, int maskSlot) { + Label nullView = cob.newLabel(); + Label haveMask = cob.newLabel(); + cob.aload(ctxt).invokevirtual(CD_DESER_CONTEXT, "getActiveView", MTD_GET_ACTIVE_VIEW); + cob.dup(); + cob.ifnull(nullView); + cob.aload(0); + cob.swap(); + cob.invokevirtual(base, "_viewMask", MTD_VIEW_MASK); + cob.lstore(maskSlot); + cob.goto_(haveMask); + cob.labelBinding(nullView); + cob.pop(); + cob.loadConstant(-1L); + cob.lstore(maskSlot); + cob.labelBinding(haveMask); + } + + // MASK strategy, start of each arm: a hidden property consumes its value + // with stock semantics (advance to the value token, then the base helper + // reports or skips) and continues the loop without touching the bean. + private static void emitArmViewCheck(CodeBuilder cob, ClassDesc base, int parser, + int ctxt, int maskSlot, + int matcherSlot, int ixSlot, int armIndex, String stockName, Label loop) { + Label visible = cob.newLabel(); + cob.lload(maskSlot); + cob.loadConstant(1L << armIndex); + cob.land(); + cob.lconst_0(); + cob.lcmp(); + cob.ifne(visible); + // The fused match already advanced to the value token, so _hiddenView + // consumes the current value without advancing. + cob.aload(0).aload(parser).aload(ctxt); + ldcData(cob, stockName, CD_SETTABLE_PROP); + cob.invokevirtual(base, "_hiddenView", MTD_HIDDEN_VIEW); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(loop); + cob.labelBinding(visible); + } + + // The loop head shared by all modes: dispatch on the match index (arms, + // end of object, unknown name, anything else) with a tableswitch that + // sends each matcher index to its arm. Returns the case label per arm, + // null for an arm no name reaches (its code is not emitted). + private static Label[] emitLoopHead(CodeBuilder cob, ReaderCodegen.Plan plan, int ixSlot, + Label loop, Label endObject, Label unknown, Label oddToken, Label defaultCase) { + List props = plan.props(); + int[] matchArms = plan.matchArms(); + Label switchPart = cob.newLabel(); + cob.labelBinding(loop); + cob.iload(ixSlot).ifge(switchPart); + cob.iload(ixSlot).iconst_m1().if_icmpeq(endObject); + cob.iload(ixSlot).ldc(-2).if_icmpeq(unknown); + cob.goto_(oddToken); + + cob.labelBinding(switchPart); + Label[] caseLabels = new Label[props.size()]; + List cases = new ArrayList<>(matchArms.length); + for (int k = 0; k < matchArms.length; k++) { + int arm = matchArms[k]; + if (caseLabels[arm] == null) { + caseLabels[arm] = cob.newLabel(); + } + cases.add(SwitchCase.of(k, caseLabels[arm])); + } + cob.iload(ixSlot); + if (cases.isEmpty()) { + cob.pop(); + cob.goto_(defaultCase); + } else { + cob.tableswitch(0, matchArms.length - 1, defaultCase, cases); + } + return caseLabels; + } + + // POJO and builder modes. The receiver local holds the bean, or the + // builder whose setters may return a replacement builder (kept, like + // stock deserializeSetAndReturn); the build method ends builder mode. + private static void buildDeserialize(CodeBuilder cob, ReaderCodegen.Plan plan) { + final boolean builderMode = plan.mode() == Mode.BUILDER; + final ClassDesc base = baseOf(plan); + final List props = plan.props(); + final ViewStrategy views = plan.views(); + final int parser = 1; + final int ctxt = 2; + final int beanSlot = 3; + final int matcherSlot = 4; + final int ixSlot = 5; + final int propSlot = 6; + final int excSlot = 7; + final int maskSlot = 8; + final int valueSlot = 10; + Label propHandler = cob.newLabel(); + Label storeHandler = cob.newLabel(); + + // A failing setter or field store gets the stock property's message, + // so each generated store has its own exception region. The regions + // nest inside the arm regions, and the JVM takes the first matching + // exception-table entry, so they register before any arm region. + Label[] storeStart = new Label[props.size()]; + Label[] storeEnd = new Label[props.size()]; + boolean anyStore = false; + for (int i = 0; i < props.size(); i++) { + if (props.get(i).handle() != null) { + storeStart[i] = cob.newLabel(); + storeEnd[i] = cob.newLabel(); + cob.exceptionCatch(storeStart[i], storeEnd[i], storeHandler, CD_EXCEPTION); + anyStore = true; + } + } + + Label scopeStart = cob.newBoundLabel(); + Label delegate = emitEntryGuard(cob, parser, ctxt, views); + if (views == ViewStrategy.MASK) { + emitViewMask(cob, base, ctxt, maskSlot); + } + if (anyStore) { + // Read by the store handler; child arms set it before storing. + cob.aconst_null().astore(valueSlot); + } + + Label ctorHandler = null; + Label haveBean = null; + if (plan.mode() == Mode.CONSTRUCTOR) { + ctorHandler = cob.newLabel(); + haveBean = cob.newLabel(); + Label ctorStart = cob.newLabel(); + Label ctorEnd = cob.newLabel(); + cob.exceptionCatch(ctorStart, ctorEnd, ctorHandler, CD_EXCEPTION); + cob.labelBinding(ctorStart); + ldcData(cob, "constructor", ConstantDescs.CD_MethodHandle); + cob.invokevirtual(ConstantDescs.CD_MethodHandle, "invokeExact", MTD_NEW_INSTANCE); + cob.labelBinding(ctorEnd); + cob.labelBinding(haveBean); + } else { + ldcData(cob, "instantiator", CD_VALUE_INSTANTIATOR); + cob.aload(ctxt); + cob.invokevirtual(CD_VALUE_INSTANTIATOR, "createUsingDefault", MTD_CREATE_DEFAULT); + } + cob.astore(beanSlot); + if (!builderMode) { + cob.aload(parser).aload(beanSlot) + .invokevirtual(CD_JSON_PARSER, "assignCurrentValue", MTD_ASSIGN_CURRENT); + } + if (plan.injectables()) { + cob.aload(0).aload(ctxt).aload(beanSlot) + .invokevirtual(base, "_injectValues", MTD_INJECT); + } + + ldcData(cob, "matcher", CD_NAME_MATCHER); + cob.astore(matcherSlot); + + Label loop = cob.newLabel(); + Label endObject = cob.newLabel(); + Label unknown = cob.newLabel(); + Label oddToken = cob.newLabel(); + Label defaultCase = cob.newLabel(); + + emitFirstMatch(cob, parser, matcherSlot, ixSlot); + Label[] caseLabels = emitLoopHead(cob, plan, ixSlot, loop, endObject, unknown, + oddToken, defaultCase); + + for (int i = 0; i < props.size(); i++) { + cob.labelBinding(caseLabels[i]); + if (views == ViewStrategy.MASK) { + emitArmViewCheck(cob, base, parser, ctxt, maskSlot, matcherSlot, ixSlot, i, + plan.stockName()[i], loop); + } + Label armEnd = beginArm(cob, plan.stockName()[i], propSlot, propHandler); + GenProp prop = props.get(i); + Store store = (prop.handle() == null) ? null + : new Store(plan.handleName()[i], storeStart[i], storeEnd[i], builderMode); + String stockName = plan.stockName()[i]; + switch (prop.kind()) { + case STRING -> emitScalar(cob, beanSlot, parser, ctxt, "getString", + MTD_GET_STRING, CD_OBJECT, stockName, store, "VALUE_STRING"); + case INT -> emitScalar(cob, beanSlot, parser, ctxt, "getIntValue", + MTD_GET_INT, ConstantDescs.CD_int, stockName, store, "VALUE_NUMBER_INT"); + case LONG -> emitScalar(cob, beanSlot, parser, ctxt, "getLongValue", + MTD_GET_LONG, ConstantDescs.CD_long, stockName, store, "VALUE_NUMBER_INT"); + case BOOLEAN -> emitScalar(cob, beanSlot, parser, ctxt, "getBooleanValue", + MTD_GET_BOOLEAN, ConstantDescs.CD_boolean, stockName, store, null); + case CHILD -> emitChildArm(cob, beanSlot, parser, ctxt, valueSlot, + plan.childName()[i], stockName, store); + case STOCK -> + // The fused match left the value token current; the stock + // property consumes it. + emitStockSet(cob, parser, ctxt, beanSlot, stockName, builderMode); + } + cob.labelBinding(armEnd); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(loop); + } + + cob.labelBinding(defaultCase); + throwIse(cob, "bad property index"); + + cob.labelBinding(endObject); + if (builderMode) { + emitBuild(cob, base, ctxt, beanSlot, excSlot); + } else { + cob.aload(beanSlot).areturn(); + } + + cob.labelBinding(unknown); + cob.aload(0).aload(parser).aload(ctxt).aload(beanSlot); + cob.invokevirtual(base, "_handleUnknown", MTD_HANDLE_UNKNOWN); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(loop); + + cob.labelBinding(oddToken); + cob.aload(0).aload(parser).aload(ctxt).aload(beanSlot) + .invokevirtual(base, "_unexpectedToken", MTD_UNEXPECTED) + .areturn(); + + emitPropertyHandler(cob, base, propHandler, ctxt, propSlot, excSlot, beanSlot); + if (anyStore) { + cob.labelBinding(storeHandler); + cob.astore(excSlot); + cob.aload(0).aload(excSlot).aload(beanSlot).aload(valueSlot).aload(propSlot) + .aload(parser).aload(ctxt); + cob.invokevirtual(base, "_storeFailure", MTD_STORE_FAILURE); + cob.athrow(); + } + if (ctorHandler != null) { + // StdValueInstantiator.createUsingDefault semantics; a problem + // handler's replacement value continues as the bean. + cob.labelBinding(ctorHandler); + cob.astore(excSlot); + cob.aload(0).aload(ctxt).aload(excSlot); + cob.invokevirtual(base, "_instantiationFailure", MTD_CREATION_FAILURE); + cob.goto_(haveBean); + } + emitDelegateTail(cob, base, parser, ctxt, delegate); + + Label scopeEnd = cob.newBoundLabel(); + cob.localVariable(parser, "p", CD_JSON_PARSER, scopeStart, scopeEnd); + cob.localVariable(ctxt, "ctxt", CD_DESER_CONTEXT, scopeStart, scopeEnd); + cob.localVariable(beanSlot, builderMode ? "builder" : "bean", CD_OBJECT, + scopeStart, scopeEnd); + cob.localVariable(matcherSlot, "matcher", CD_NAME_MATCHER, scopeStart, scopeEnd); + cob.localVariable(ixSlot, "ix", ConstantDescs.CD_int, scopeStart, scopeEnd); + cob.localVariable(propSlot, "prop", CD_SETTABLE_PROP, scopeStart, scopeEnd); + cob.localVariable(excSlot, "e", CD_THROWABLE, scopeStart, scopeEnd); + if (views == ViewStrategy.MASK) { + cob.localVariable(maskSlot, "viewMask", ConstantDescs.CD_long, scopeStart, scopeEnd); + } + if (anyStore) { + cob.localVariable(valueSlot, "value", CD_OBJECT, scopeStart, scopeEnd); + } + } + + // BuilderBasedDeserializer.finishBuild: a failing build method goes + // through wrapInstantiationProblem. + private static void emitBuild(CodeBuilder cob, ClassDesc base, int ctxt, int builderSlot, + int excSlot) { + Label start = cob.newLabel(); + Label end = cob.newLabel(); + Label handler = cob.newLabel(); + cob.exceptionCatch(start, end, handler, CD_EXCEPTION); + cob.labelBinding(start); + ldcData(cob, "buildMethod", ConstantDescs.CD_MethodHandle); + cob.aload(builderSlot); + cob.invokevirtual(ConstantDescs.CD_MethodHandle, "invokeExact", MTD_BUILD_INVOKE); + cob.labelBinding(end); + cob.areturn(); + cob.labelBinding(handler); + cob.astore(excSlot); + cob.aload(0).aload(ctxt).aload(excSlot); + cob.invokevirtual(base, "_buildFailure", MTD_CREATION_FAILURE); + cob.areturn(); + } + + // A generated store: the class-data name of its (Object, eV)void handle + // ((Object, eV)Object in builder mode, whose non-null result becomes the + // new receiver) and the labels of its exception region. + private record Store(String handleName, Label start, Label end, boolean builderMode) {} + + private static void emitStore(CodeBuilder cob, Store store, int receiverSlot, + ClassDesc erasedDesc) { + cob.labelBinding(store.start()); + if (store.builderMode()) { + cob.invokevirtual(ConstantDescs.CD_MethodHandle, "invokeExact", MethodTypeDesc.of( + CD_OBJECT, CD_OBJECT, erasedDesc)); + cob.labelBinding(store.end()); + // Stock deserializeSetAndReturn keeps the builder when a fluent + // setter returns null. + Label replace = cob.newLabel(); + cob.dup(); + cob.ifnonnull(replace); + cob.pop(); + cob.aload(receiverSlot); + cob.labelBinding(replace); + cob.astore(receiverSlot); + } else { + cob.invokevirtual(ConstantDescs.CD_MethodHandle, "invokeExact", MethodTypeDesc.of( + ConstantDescs.CD_void, CD_OBJECT, erasedDesc)); + cob.labelBinding(store.end()); + } + } + + // The null branch (and any other unexpected token) enters the stock + // property with the parser on that token, which is the position its + // deserializeAndSet expects. Without a generated store (the property + // keeps its stock arm whatever the token), only the stock call remains. + private static void emitScalar(CodeBuilder cob, int receiverSlot, int parser, int ctxt, + String getter, MethodTypeDesc getterType, ClassDesc erasedDesc, + String stockName, Store store, String expectedToken) { + boolean builderMode = (store != null) && store.builderMode(); + Label useStock = cob.newLabel(); + Label done = cob.newLabel(); + emitExpectedTokenCheck(cob, parser, expectedToken, useStock); + ldcData(cob, store.handleName(), ConstantDescs.CD_MethodHandle); + cob.aload(receiverSlot); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, getter, getterType); + emitStore(cob, store, receiverSlot, erasedDesc); + cob.goto_(done); + cob.labelBinding(useStock); + emitStockSet(cob, parser, ctxt, receiverSlot, stockName, builderMode); + cob.labelBinding(done); + } + + // A child bean on START_OBJECT goes to the linked child codec, anything + // else to the stock property. The child value passes through valueSlot so + // that a failing store can name its class, as stock does. + private static void emitChildArm(CodeBuilder cob, int receiverSlot, int parser, int ctxt, + int valueSlot, String childName, String stockName, Store store) { + Label childStock = cob.newLabel(); + Label childDone = cob.newLabel(); + // The fused match left the value token current. + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "currentToken", MTD_NEXT_TOKEN); + cob.getstatic(CD_JSON_TOKEN, "START_OBJECT", CD_JSON_TOKEN); + cob.if_acmpne(childStock); + emitChildCall(cob, parser, ctxt, childName); + cob.astore(valueSlot); + ldcData(cob, store.handleName(), ConstantDescs.CD_MethodHandle); + cob.aload(receiverSlot); + cob.aload(valueSlot); + emitStore(cob, store, receiverSlot, CD_OBJECT); + cob.goto_(childDone); + cob.labelBinding(childStock); + emitStockSet(cob, parser, ctxt, receiverSlot, stockName, store.builderMode()); + cob.labelBinding(childDone); + } + + // Child codecs are reader or builder codecs; the constant receiver makes + // the call monomorphic either way. + private static void emitChildCall(CodeBuilder cob, int parser, int ctxt, String childName) { + ldcData(cob, childName, CD_VALUE_DESERIALIZER); + cob.aload(parser).aload(ctxt); + cob.invokevirtual(CD_VALUE_DESERIALIZER, "deserialize", MTD_DESERIALIZE); + } + + // The stock property reads and stores the value: deserializeAndSet, or + // in builder mode deserializeSetAndReturn, whose result (a fluent + // builder may return a replacement instance) becomes the new builder. + private static void emitStockSet(CodeBuilder cob, int parser, int ctxt, int receiverSlot, + String stockName, boolean builderMode) { + ldcData(cob, stockName, CD_SETTABLE_PROP); + cob.aload(parser).aload(ctxt).aload(receiverSlot); + if (builderMode) { + cob.invokevirtual(CD_SETTABLE_PROP, "deserializeSetAndReturn", MTD_DESER_SET_RETURN); + cob.astore(receiverSlot); + } else { + cob.invokevirtual(CD_SETTABLE_PROP, "deserializeAndSet", MTD_DESERIALIZE_AND_SET); + } + } + + // Record mode: components collect into typed locals in canonical order. + // At the end of the object the canonical constructor runs directly when + // the document supplied every component and nothing needs stock + // argument handling; otherwise, and when the constructor throws, the base + // finishes through the stock creator (see GeneratedReaderBase + // ._recordBuild). Names no component takes are handled, or buffered and + // replayed against the record, the way the stock property-based loop + // handles them. + private static void buildRecordDeserialize(CodeBuilder cob, ReaderCodegen.Plan plan) { + final ClassDesc base = CD_READER_BASE; + final List props = plan.props(); + final ViewStrategy views = plan.views(); + final int n = props.size(); + final int parser = 1; + final int ctxt = 2; + + int next = 3; + int[] componentSlot = new int[n]; + boolean anyPrimitive = false; + for (int i = 0; i < n; i++) { + componentSlot[i] = next; + Class t = props.get(i).type(); + next += (t == long.class || t == double.class) ? 2 : 1; + anyPrimitive |= t.isPrimitive(); + } + final int matcherSlot = next++; + final int ixSlot = next++; + final int seenSlot = next; + next += 2; + final int nullPrimSlot = next; + next += 2; + final int maskSlot = next; + next += 2; + final int propSlot = next++; + final int excSlot = next++; + final int unknownSlot = next++; + final int beanSlot = next++; + final int pendingSlot = next; + Label propHandler = cob.newLabel(); + + Label scopeStart = cob.newBoundLabel(); + Label delegate = emitEntryGuard(cob, parser, ctxt, views); + if (views == ViewStrategy.MASK) { + emitViewMask(cob, base, ctxt, maskSlot); + } + + for (int i = 0; i < n; i++) { + Class t = props.get(i).type(); + if (t == long.class) { + cob.lconst_0().lstore(componentSlot[i]); + } else if (t == double.class) { + cob.dconst_0().dstore(componentSlot[i]); + } else if (t == float.class) { + cob.fconst_0().fstore(componentSlot[i]); + } else if (t.isPrimitive()) { + cob.iconst_0().istore(componentSlot[i]); + } else { + cob.aconst_null().astore(componentSlot[i]); + } + } + cob.lconst_0().lstore(seenSlot); + cob.lconst_0().lstore(nullPrimSlot); + cob.aconst_null().astore(unknownSlot); + + ldcData(cob, "matcher", CD_NAME_MATCHER); + cob.astore(matcherSlot); + + Label loop = cob.newLabel(); + Label endObject = cob.newLabel(); + Label unknown = cob.newLabel(); + Label oddToken = cob.newLabel(); + Label defaultCase = cob.newLabel(); + + emitFirstMatch(cob, parser, matcherSlot, ixSlot); + Label[] caseLabels = emitLoopHead(cob, plan, ixSlot, loop, endObject, unknown, + oddToken, defaultCase); + + for (int i = 0; i < n; i++) { + if (caseLabels[i] == null) { + continue; + } + cob.labelBinding(caseLabels[i]); + if (views == ViewStrategy.MASK) { + // A hidden component stays unseen, so the stock creator gives + // it the absent value, as for a stock hidden creator property. + emitArmViewCheck(cob, base, parser, ctxt, maskSlot, matcherSlot, ixSlot, i, + plan.stockName()[i], loop); + } + Label armEnd = beginArm(cob, plan.stockName()[i], propSlot, propHandler); + GenProp prop = props.get(i); + Class t = prop.type(); + String stockName = plan.stockName()[i]; + ComponentLocal local = new ComponentLocal(componentSlot[i], t, nullPrimSlot, i); + switch (prop.kind()) { + case STRING -> emitRecordScalar(cob, parser, ctxt, local, + "getString", MTD_GET_STRING, stockName, "VALUE_STRING"); + case INT -> emitRecordScalar(cob, parser, ctxt, local, + "getIntValue", MTD_GET_INT, stockName, "VALUE_NUMBER_INT"); + case LONG -> emitRecordScalar(cob, parser, ctxt, local, + "getLongValue", MTD_GET_LONG, stockName, "VALUE_NUMBER_INT"); + case BOOLEAN -> emitRecordScalar(cob, parser, ctxt, local, + "getBooleanValue", MTD_GET_BOOLEAN, stockName, null); + case CHILD -> { + Label childStock = cob.newLabel(); + Label childDone = cob.newLabel(); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "currentToken", MTD_NEXT_TOKEN); + cob.getstatic(CD_JSON_TOKEN, "START_OBJECT", CD_JSON_TOKEN); + cob.if_acmpne(childStock); + emitChildCall(cob, parser, ctxt, plan.childName()[i]); + cob.astore(componentSlot[i]); + cob.goto_(childDone); + cob.labelBinding(childStock); + emitStockValueToLocal(cob, parser, ctxt, local, stockName); + cob.labelBinding(childDone); + } + case STOCK -> emitStockValueToLocal(cob, parser, ctxt, local, stockName); + } + cob.labelBinding(armEnd); + cob.lload(seenSlot); + cob.loadConstant(1L << i); + cob.lor(); + cob.lstore(seenSlot); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(loop); + } + + cob.labelBinding(defaultCase); + throwIse(cob, "bad property index"); + + cob.labelBinding(endObject); + Label slow = cob.newLabel(); + Label finish = cob.newLabel(); + cob.aconst_null().astore(pendingSlot); + long allSeen = (n == 64) ? -1L : (1L << n) - 1; + cob.lload(seenSlot); + cob.loadConstant(allSeen); + cob.lcmp(); + cob.ifne(slow); + if (anyPrimitive) { + cob.lload(nullPrimSlot); + cob.lconst_0(); + cob.lcmp(); + cob.ifne(slow); + } + cob.aload(ctxt); + cob.getstatic(CD_DESER_FEATURE, "FAIL_ON_NULL_CREATOR_PROPERTIES", CD_DESER_FEATURE); + cob.invokevirtual(CD_DESER_CONTEXT, "isEnabled", MTD_IS_ENABLED); + cob.ifne(slow); + // The constructor handle is erased ((e1..en)Object, see MemberHandles), + // so the call descriptor uses Object for reference components. + Label ctorStart = cob.newLabel(); + Label ctorEnd = cob.newLabel(); + Label ctorHandler = cob.newLabel(); + cob.exceptionCatch(ctorStart, ctorEnd, ctorHandler, CD_EXCEPTION); + cob.labelBinding(ctorStart); + ldcData(cob, "constructor", ConstantDescs.CD_MethodHandle); + ClassDesc[] paramDescs = new ClassDesc[n]; + for (int i = 0; i < n; i++) { + Class t = props.get(i).type(); + paramDescs[i] = t.isPrimitive() ? Descs.of(t) : CD_OBJECT; + loadLocal(cob, t, componentSlot[i]); + } + cob.invokevirtual(ConstantDescs.CD_MethodHandle, "invokeExact", + MethodTypeDesc.of(CD_OBJECT, paramDescs)); + cob.labelBinding(ctorEnd); + cob.astore(beanSlot); + cob.goto_(finish); + // The constructor failed with every component present: the base + // reports it the way the stock instantiator would. + cob.labelBinding(ctorHandler); + cob.astore(pendingSlot); + cob.labelBinding(slow); + cob.aload(0).aload(parser).aload(ctxt).lload(seenSlot).lload(nullPrimSlot); + cob.loadConstant(n); + cob.anewarray(CD_OBJECT); + for (int i = 0; i < n; i++) { + Class t = props.get(i).type(); + cob.dup(); + cob.loadConstant(i); + loadLocal(cob, t, componentSlot[i]); + if (t.isPrimitive()) { + ClassDesc boxDesc = Descs.of(ClassUtil.wrapperType(t)); + cob.invokestatic(boxDesc, "valueOf", MethodTypeDesc.of(boxDesc, Descs.of(t))); + } + cob.aastore(); + } + cob.aload(pendingSlot); + cob.invokevirtual(base, "_recordBuild", MTD_RECORD_BUILD); + cob.astore(beanSlot); + cob.labelBinding(finish); + Label noUnknown = cob.newLabel(); + cob.aload(unknownSlot).ifnull(noUnknown); + cob.aload(0).aload(ctxt).aload(beanSlot).aload(unknownSlot); + cob.invokevirtual(base, "_recordReplayUnknown", MTD_RECORD_REPLAY); + cob.astore(beanSlot); + cob.labelBinding(noUnknown); + cob.aload(beanSlot).areturn(); + + cob.labelBinding(unknown); + cob.aload(0).aload(parser).aload(ctxt).aload(unknownSlot); + cob.invokevirtual(base, "_recordUnknown", MTD_RECORD_UNKNOWN); + cob.astore(unknownSlot); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(loop); + + cob.labelBinding(oddToken); + cob.aload(0).aload(parser).aload(ctxt).aconst_null() + .invokevirtual(base, "_unexpectedToken", MTD_UNEXPECTED) + .areturn(); + + emitPropertyHandler(cob, base, propHandler, ctxt, propSlot, excSlot, -1); + emitDelegateTail(cob, base, parser, ctxt, delegate); + + Label scopeEnd = cob.newBoundLabel(); + cob.localVariable(parser, "p", CD_JSON_PARSER, scopeStart, scopeEnd); + cob.localVariable(ctxt, "ctxt", CD_DESER_CONTEXT, scopeStart, scopeEnd); + for (int i = 0; i < n; i++) { + Class t = props.get(i).type(); + // Record component names: Java identifiers, unlike JSON names. + cob.localVariable(componentSlot[i], plan.localNames()[i], + t.isPrimitive() ? Descs.of(t) : CD_OBJECT, scopeStart, scopeEnd); + } + cob.localVariable(matcherSlot, "matcher", CD_NAME_MATCHER, scopeStart, scopeEnd); + cob.localVariable(ixSlot, "ix", ConstantDescs.CD_int, scopeStart, scopeEnd); + cob.localVariable(seenSlot, "seen", ConstantDescs.CD_long, scopeStart, scopeEnd); + cob.localVariable(nullPrimSlot, "nullPrimitives", ConstantDescs.CD_long, + scopeStart, scopeEnd); + cob.localVariable(propSlot, "prop", CD_SETTABLE_PROP, scopeStart, scopeEnd); + cob.localVariable(excSlot, "e", CD_THROWABLE, scopeStart, scopeEnd); + cob.localVariable(unknownSlot, "unknown", CD_OBJECT, scopeStart, scopeEnd); + cob.localVariable(beanSlot, "bean", CD_OBJECT, scopeStart, scopeEnd); + cob.localVariable(pendingSlot, "ctorFailure", CD_THROWABLE, scopeStart, scopeEnd); + if (views == ViewStrategy.MASK) { + cob.localVariable(maskSlot, "viewMask", ConstantDescs.CD_long, scopeStart, scopeEnd); + } + } + + // A record component local: its slot and type, and its bit in the + // nullPrimitives mask (primitive components only). + private record ComponentLocal(int slot, Class type, int nullPrimSlot, int index) {} + + private static void emitRecordScalar(CodeBuilder cob, int parser, int ctxt, + ComponentLocal local, String getter, MethodTypeDesc getterType, String stockName, + String expectedToken) { + Label useStock = cob.newLabel(); + Label done = cob.newLabel(); + emitExpectedTokenCheck(cob, parser, expectedToken, useStock); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, getter, getterType); + storeLocal(cob, local.type(), local.slot()); + if (local.type().isPrimitive()) { + emitNullPrimitive(cob, local, false); + } + cob.goto_(done); + cob.labelBinding(useStock); + emitStockValueToLocal(cob, parser, ctxt, local, stockName); + cob.labelBinding(done); + } + + // Reads the whole value through the stock property (parser positioned on + // the value token). Reference components stay Object, since the erased + // constructor handle casts on invocation. Primitive components unbox; a + // null (a custom deserializer may return one) leaves the local alone and + // marks the component, so that the stock creator receives the null, as + // it would without Blackbird. + private static void emitStockValueToLocal(CodeBuilder cob, int parser, int ctxt, + ComponentLocal local, String stockName) { + ldcData(cob, stockName, CD_SETTABLE_PROP); + cob.aload(parser).aload(ctxt); + cob.invokevirtual(CD_SETTABLE_PROP, "deserialize", MTD_DESERIALIZE); + Class type = local.type(); + if (!type.isPrimitive()) { + cob.astore(local.slot()); + return; + } + Label isNull = cob.newLabel(); + Label done = cob.newLabel(); + cob.dup(); + cob.ifnull(isNull); + ClassDesc boxDesc = Descs.of(ClassUtil.wrapperType(type)); + cob.checkcast(boxDesc); + cob.invokevirtual(boxDesc, type.getName() + "Value", MethodTypeDesc.of(Descs.of(type))); + storeLocal(cob, type, local.slot()); + emitNullPrimitive(cob, local, false); + cob.goto_(done); + cob.labelBinding(isNull); + cob.pop(); + emitNullPrimitive(cob, local, true); + cob.labelBinding(done); + } + + // Sets or clears the component's bit in the nullPrimitives mask: a later + // value for a duplicate name replaces an earlier one, as in stock. + private static void emitNullPrimitive(CodeBuilder cob, ComponentLocal local, boolean isNull) { + long bit = 1L << local.index(); + cob.lload(local.nullPrimSlot()); + if (isNull) { + cob.loadConstant(bit); + cob.lor(); + } else { + cob.loadConstant(~bit); + cob.land(); + } + cob.lstore(local.nullPrimSlot()); + } + + private static void storeLocal(CodeBuilder cob, Class type, int slot) { + if (type == long.class) { + cob.lstore(slot); + } else if (type == double.class) { + cob.dstore(slot); + } else if (type == float.class) { + cob.fstore(slot); + } else if (type.isPrimitive()) { + cob.istore(slot); + } else { + cob.astore(slot); + } + } + + private static void loadLocal(CodeBuilder cob, Class type, int slot) { + if (type == long.class) { + cob.lload(slot); + } else if (type == double.class) { + cob.dload(slot); + } else if (type == float.class) { + cob.fload(slot); + } else if (type.isPrimitive()) { + cob.iload(slot); + } else { + cob.aload(slot); + } + } + + // Guards the generated loop's entry: anything the loop does not model - a + // stream positioned on neither START_OBJECT nor PROPERTY_NAME (the + // AsProperty polymorphic path hands subtype deserializers a stream on the + // property after the type id) and, under the DELEGATE strategy only, an + // active view - branches to the returned label, whose stock-delegation + // tail the caller emits at the end of the method so the main body + // decompiles un-nested. The MASK strategy keeps view-active calls on the + // generated path through the visibility bitmask. + private static Label emitEntryGuard(CodeBuilder cob, int parser, int ctxt, + ViewStrategy views) { + Label delegate = cob.newLabel(); + Label entryOk = cob.newLabel(); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "currentToken", MTD_NEXT_TOKEN); + cob.getstatic(CD_JSON_TOKEN, "START_OBJECT", CD_JSON_TOKEN); + cob.if_acmpeq(entryOk); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "currentToken", MTD_NEXT_TOKEN); + cob.getstatic(CD_JSON_TOKEN, "PROPERTY_NAME", CD_JSON_TOKEN); + cob.if_acmpne(delegate); + cob.labelBinding(entryOk); + if (views == ViewStrategy.DELEGATE) { + cob.aload(ctxt).invokevirtual(CD_DESER_CONTEXT, "getActiveView", + MTD_GET_ACTIVE_VIEW); + cob.ifnonnull(delegate); + } + return delegate; + } + + // The stock deserialize of the codec's own (copied) stock state. + private static void emitDelegateTail(CodeBuilder cob, ClassDesc base, int parser, int ctxt, + Label delegate) { + cob.labelBinding(delegate); + cob.aload(0).aload(parser).aload(ctxt); + cob.invokevirtual(base, "_stockDeserialize", MTD_DESERIALIZE); + cob.areturn(); + } + + // Branches to useStock unless the current (value) token is the one the + // inline read expects. Null, quoted scalars, and mismatched shapes all + // take the stock property, which owns coercion and null handling. + // expectedToken null selects the boolean pair. + private static void emitExpectedTokenCheck(CodeBuilder cob, int parser, + String expectedToken, Label useStock) { + // The fused match already advanced to the value token, so read it + // with currentToken rather than advancing again. + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "currentToken", MTD_NEXT_TOKEN); + if (expectedToken != null) { + cob.getstatic(CD_JSON_TOKEN, expectedToken, CD_JSON_TOKEN); + cob.if_acmpne(useStock); + } else { + Label isTrue = cob.newLabel(); + Label fast = cob.newLabel(); + cob.dup(); + cob.getstatic(CD_JSON_TOKEN, "VALUE_TRUE", CD_JSON_TOKEN); + cob.if_acmpeq(isTrue); + cob.getstatic(CD_JSON_TOKEN, "VALUE_FALSE", CD_JSON_TOKEN); + cob.if_acmpne(useStock); + cob.goto_(fast); + cob.labelBinding(isTrue); + cob.pop(); + cob.labelBinding(fast); + } + } + + // Loads the arm's stock property into propSlot and opens its exception + // region: any Exception from a property arm is rethrown with the property + // reference prepended, like the stock loop's wrapAndThrow. + private static Label beginArm(CodeBuilder cob, String stockName, int propSlot, Label handler) { + ldcData(cob, stockName, CD_SETTABLE_PROP); + cob.astore(propSlot); + Label armStart = cob.newLabel(); + Label armEnd = cob.newLabel(); + cob.exceptionCatch(armStart, armEnd, handler, CD_EXCEPTION); + cob.labelBinding(armStart); + return armEnd; + } + + // receiverSlot < 0 selects aconst_null (record mode: no instance yet). + private static void emitPropertyHandler(CodeBuilder cob, ClassDesc base, Label handler, + int ctxt, + int propSlot, int excSlot, int receiverSlot) { + cob.labelBinding(handler); + cob.astore(excSlot); + cob.aload(0); + cob.aload(excSlot); + if (receiverSlot < 0) { + cob.aconst_null(); + } else { + cob.aload(receiverSlot); + } + cob.aload(propSlot); + cob.aload(ctxt); + cob.invokevirtual(base, "_propertyException", MTD_PROP_WRAP); + cob.athrow(); + } + + // The fused name match: on a non-negative result the current token is the + // property's value token, so the arms consume it directly; negative + // results leave the stream on the name exactly as nextNameMatch does. + private static void nextNameMatch(CodeBuilder cob, int parser, int matcherSlot, int ixSlot) { + cob.aload(parser).aload(matcherSlot) + .invokevirtual(CD_JSON_PARSER, "nextNameMatchAndToken", MTD_NEXT_NAME_MATCH) + .istore(ixSlot); + } + + // First match of the loop. The entry guard admits START_OBJECT and + // PROPERTY_NAME; a name entry matches the CURRENT name, mirroring stock + // BeanDeserializer's currentNameMatch loop head. The fused arms consume + // the current token, so a matched name entry advances to its value here; + // fusing the entry itself would double-advance (the current name is + // already consumed). Negative results stay on the name, as the unknown + // arm expects. + private static void emitFirstMatch(CodeBuilder cob, int parser, int matcherSlot, int ixSlot) { + Label nameEntry = cob.newLabel(); + Label done = cob.newLabel(); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "currentToken", MTD_NEXT_TOKEN); + cob.getstatic(CD_JSON_TOKEN, "PROPERTY_NAME", CD_JSON_TOKEN); + cob.if_acmpeq(nameEntry); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(done); + cob.labelBinding(nameEntry); + cob.aload(parser).aload(matcherSlot) + .invokevirtual(CD_JSON_PARSER, "currentNameMatch", MTD_NEXT_NAME_MATCH) + .istore(ixSlot); + cob.iload(ixSlot).iflt(done); + cob.aload(parser).invokevirtual(CD_JSON_PARSER, "nextToken", MTD_NEXT_TOKEN).pop(); + cob.labelBinding(done); + } + + private static void throwIse(CodeBuilder cob, String message) { + cob.new_(CD_ISE).dup().ldc(message) + .invokespecial(CD_ISE, ConstantDescs.INIT_NAME, + MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String)) + .athrow(); + } +} diff --git a/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanWriterGenerator.java b/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanWriterGenerator.java new file mode 100644 index 000000000..01b2520c0 --- /dev/null +++ b/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanWriterGenerator.java @@ -0,0 +1,529 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.Label; +import java.lang.classfile.attribute.MethodParametersAttribute; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.DynamicConstantDesc; +import java.lang.constant.MethodTypeDesc; +import java.util.List; + +import tools.jackson.core.JsonGenerator; +import tools.jackson.core.JsonToken; +import tools.jackson.core.SerializableString; +import tools.jackson.core.type.WritableTypeId; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.jsontype.TypeSerializer; +import tools.jackson.databind.ser.PropertyWriter; +import tools.jackson.databind.ser.bean.BeanSerializerBase; +import tools.jackson.module.blackbird.codegen.ReaderCodegen.ViewStrategy; +import tools.jackson.module.blackbird.codegen.WriterCodegen.GenWProp; +import tools.jackson.module.blackbird.codegen.WriterCodegen.WKind; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; + +/** + * Emits the class file of a hidden-class serializer for one bean (see + * {@link WriterCodegen}, which defines it): a straight-line sequence of + * writeName/write-value pairs with pre-encoded name constants for eligible + * properties and the stock PropertyWriter (a named classData constant) for + * everything else. Beans that declare views resolve a per-view visibility + * bitmask and each property tests its bit, so view-active writes stay on the + * generated path; beans with more than 64 properties delegate view-active + * calls to the stock serializer instead. Compiled at release 25 into + * META-INF/versions/25, since it uses the ClassFile API; reached through + * {@link ClassFileEmitters}. + */ +final class BeanWriterGenerator +{ + private static final ClassDesc CD_JSON_GENERATOR = Descs.of(JsonGenerator.class); + private static final ClassDesc CD_SER_CONTEXT = Descs.of(SerializationContext.class); + private static final ClassDesc CD_SERIALIZABLE_STRING = Descs.of(SerializableString.class); + private static final ClassDesc CD_PROPERTY_WRITER = Descs.of(PropertyWriter.class); + private static final ClassDesc CD_WRITER_BASE = + Descs.of(GeneratedWriterBase.class); + private static final ClassDesc CD_BEAN_SER_BASE = Descs.of(BeanSerializerBase.class); + private static final ClassDesc CD_WRITE_HELPERS = ClassDesc.of(WriterCodegen.HELPERS_CLASS); + + private static final MethodTypeDesc MTD_SERIALIZE = MethodTypeDesc.of(ConstantDescs.CD_void, + ConstantDescs.CD_Object, CD_JSON_GENERATOR, CD_SER_CONTEXT); + private static final MethodTypeDesc MTD_WRITE_NAME = + MethodTypeDesc.of(CD_JSON_GENERATOR, CD_SERIALIZABLE_STRING); + private static final MethodTypeDesc MTD_WRITE_STRING = + MethodTypeDesc.of(CD_JSON_GENERATOR, ConstantDescs.CD_String); + private static final MethodTypeDesc MTD_WRITE_INT = + MethodTypeDesc.of(CD_JSON_GENERATOR, ConstantDescs.CD_int); + private static final MethodTypeDesc MTD_WRITE_LONG = + MethodTypeDesc.of(CD_JSON_GENERATOR, ConstantDescs.CD_long); + private static final MethodTypeDesc MTD_WRITE_BOOLEAN = + MethodTypeDesc.of(CD_JSON_GENERATOR, ConstantDescs.CD_boolean); + private static final MethodTypeDesc MTD_WRITE_NULL = MethodTypeDesc.of(CD_JSON_GENERATOR); + private static final MethodTypeDesc MTD_WRITE_START_OBJECT = + MethodTypeDesc.of(CD_JSON_GENERATOR, ConstantDescs.CD_Object); + private static final MethodTypeDesc MTD_WRITE_END = MethodTypeDesc.of(CD_JSON_GENERATOR); + private static final MethodTypeDesc MTD_SERIALIZE_AS_PROPERTY = MethodTypeDesc.of( + ConstantDescs.CD_void, ConstantDescs.CD_Object, CD_JSON_GENERATOR, CD_SER_CONTEXT); + private static final MethodTypeDesc MTD_CTOR = MethodTypeDesc.of(ConstantDescs.CD_void, + CD_BEAN_SER_BASE, ConstantDescs.CD_String); + private static final MethodTypeDesc MTD_PROP_FAILURE = MethodTypeDesc.of( + Descs.of(RuntimeException.class), ConstantDescs.CD_Throwable, + ConstantDescs.CD_Object, ConstantDescs.CD_int, CD_JSON_GENERATOR, CD_SER_CONTEXT); + private static final ClassDesc CD_STACK_OVERFLOW = Descs.of(StackOverflowError.class); + private static final MethodTypeDesc MTD_GET_ACTIVE_VIEW = + MethodTypeDesc.of(ConstantDescs.CD_Class); + private static final MethodTypeDesc MTD_VIEW_MASK = + MethodTypeDesc.of(ConstantDescs.CD_long, ConstantDescs.CD_Class); + private static final MethodTypeDesc MTD_PROP_VISIBLE = MethodTypeDesc.of( + ConstantDescs.CD_boolean, CD_PROPERTY_WRITER, ConstantDescs.CD_Class, + ConstantDescs.CD_boolean); + private static final ClassDesc CD_JSON_TOKEN = Descs.of(JsonToken.class); + private static final ClassDesc CD_TYPE_SERIALIZER = + Descs.of(TypeSerializer.class); + private static final ClassDesc CD_WRITABLE_TYPE_ID = + Descs.of(WritableTypeId.class); + private static final MethodTypeDesc MTD_TYPE_ID = MethodTypeDesc.of( + CD_WRITABLE_TYPE_ID, ConstantDescs.CD_Object, CD_JSON_TOKEN); + private static final MethodTypeDesc MTD_WRITE_TYPE_PART = MethodTypeDesc.of( + CD_WRITABLE_TYPE_ID, CD_JSON_GENERATOR, CD_SER_CONTEXT, CD_WRITABLE_TYPE_ID); + private static final MethodTypeDesc MTD_ASSIGN_CURRENT = + MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Object); + private static final MethodTypeDesc MTD_SERIALIZE_WITH_TYPE = MethodTypeDesc.of( + ConstantDescs.CD_void, ConstantDescs.CD_Object, CD_JSON_GENERATOR, CD_SER_CONTEXT, + CD_TYPE_SERIALIZER); + + // Named class-data entries through the writer base's classDataEntry + // bootstrap; rationale in BeanReaderGenerator. + private static final java.lang.constant.DirectMethodHandleDesc BSM_DATA_ENTRY = + ConstantDescs.ofConstantBootstrap(CD_WRITER_BASE, "classDataEntry", + ConstantDescs.CD_Object); + + private BeanWriterGenerator() {} + + private static void ldcData(CodeBuilder cob, String name, ClassDesc type) { + cob.ldc(DynamicConstantDesc.ofNamed(BSM_DATA_ENTRY, name, type)); + } + + static byte[] emit(WriterCodegen.Plan plan) { + return BeanReaderGenerator.withinSizeLimit(buildClass(plan.className(), plan.props(), + plan.stockName(), plan.nameName(), plan.childName(), plan.handleName(), + plan.views(), plan.includeByDefault(), plan.nativePolyWrites())); + } + + // HotSpot C2 measure. The nops make each write helper larger than + // FreqInlineSize (325 bytes, C2) and C1MaxInlineSize (35 bytes, C1), so + // neither compiler inlines it into serialize() and it compiles once as + // its own unit. On aarch64, C2 copies of writeName and the NumberOutput + // digit code inlined into writers that hold list loops spill loop state + // to the stack. All writers share one WriteHelpers class: with a copy per + // writer class, each copy compiles separately, and on x86_64 the extra + // machine code causes instruction-cache misses. With the padding, + // RecordBenchmark.recordSer is 24-45% faster and PojoBenchmark.mediaItemSer + // 12-31% faster on aarch64 (JDK 25 and 27-ea), and + // BuilderBenchmark.builderSer 23% and mediaItemSer 6% faster on x86_64 + // (JDK 25). On x86_64, shared helpers make recordSer 12% and mediaItemSer + // 7% faster than a copy per writer class. Graal uses other inlining rules, + // and the interpreter executes the nops until tier-up. + private static final int INLINE_PAD = 384; + + private static final MethodTypeDesc MTD_HELP_STRING = MethodTypeDesc.of(ConstantDescs.CD_void, + CD_JSON_GENERATOR, CD_SERIALIZABLE_STRING, ConstantDescs.CD_String); + private static final MethodTypeDesc MTD_HELP_INT = MethodTypeDesc.of(ConstantDescs.CD_void, + CD_JSON_GENERATOR, CD_SERIALIZABLE_STRING, ConstantDescs.CD_int); + private static final MethodTypeDesc MTD_HELP_LONG = MethodTypeDesc.of(ConstantDescs.CD_void, + CD_JSON_GENERATOR, CD_SERIALIZABLE_STRING, ConstantDescs.CD_long); + private static final MethodTypeDesc MTD_HELP_BOOLEAN = MethodTypeDesc.of(ConstantDescs.CD_void, + CD_JSON_GENERATOR, CD_SERIALIZABLE_STRING, ConstantDescs.CD_boolean); + private static final MethodTypeDesc MTD_HELP_NAME = MethodTypeDesc.of(ConstantDescs.CD_void, + CD_JSON_GENERATOR, CD_SERIALIZABLE_STRING); + + private static byte[] buildClass(String className, List props, + String[] stockName, String[] nameName, String[] childName, String[] handleName, + ViewStrategy views, boolean includeByDefault, boolean nativePolyWrites) { + ClassDesc thisClass = ClassDesc.of(className); + return BeanReaderGenerator.CLASS_FILE.build(thisClass, clb -> { + clb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_FINAL | ClassFile.ACC_SUPER); + clb.withSuperclass(CD_WRITER_BASE); + clb.withMethod(ConstantDescs.INIT_NAME, MTD_CTOR, ClassFile.ACC_PUBLIC, + mb -> mb.with(BeanReaderGenerator.params("fallback", "layout")) + .withCode(cob -> cob.aload(0).aload(1).aload(2) + .invokespecial(CD_WRITER_BASE, ConstantDescs.INIT_NAME, MTD_CTOR) + .return_())); + clb.withMethod("serialize", MTD_SERIALIZE, ClassFile.ACC_PUBLIC, + mb -> mb.with(BeanReaderGenerator.params("value", "g", "ctxt")) + .withCode(cob -> buildSerialize(cob, props, + stockName, nameName, childName, handleName, views))); + if (nativePolyWrites) { + clb.withMethod("serializeWithType", MTD_SERIALIZE_WITH_TYPE, ClassFile.ACC_PUBLIC, + mb -> mb.with(BeanReaderGenerator.params("value", "g", "ctxt", "typeSer")) + .withCode(cob -> buildSerializeWithType(cob, props, + stockName, nameName, childName, handleName, views))); + } + if (views == ViewStrategy.MASK) { + emitComputeViewMask(clb, props, stockName, includeByDefault); + } + }); + } + + /** + * Emits {@link WriterCodegen#HELPERS_CLASS}, the padded write helpers + * that every generated writer calls (see {@link #INLINE_PAD}). + */ + static byte[] emitWriteHelpers() { + return BeanReaderGenerator.CLASS_FILE.build(CD_WRITE_HELPERS, clb -> { + clb.withFlags(ClassFile.ACC_FINAL | ClassFile.ACC_SUPER); + emitPadded(clb, "$str", MTD_HELP_STRING, cob -> { + cob.aload(0).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "writeName", MTD_WRITE_NAME).pop(); + Label isNull = cob.newLabel(); + Label done = cob.newLabel(); + cob.aload(2).ifnull(isNull); + cob.aload(0).aload(2) + .invokevirtual(CD_JSON_GENERATOR, "writeString", MTD_WRITE_STRING).pop(); + cob.goto_(done); + cob.labelBinding(isNull); + cob.aload(0).invokevirtual(CD_JSON_GENERATOR, "writeNull", MTD_WRITE_NULL).pop(); + cob.labelBinding(done); + cob.return_(); + }); + emitPadded(clb, "$int", MTD_HELP_INT, cob -> cob.aload(0).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "writeName", MTD_WRITE_NAME).pop() + .aload(0).iload(2) + .invokevirtual(CD_JSON_GENERATOR, "writeNumber", MTD_WRITE_INT).pop() + .return_()); + emitPadded(clb, "$long", MTD_HELP_LONG, cob -> cob.aload(0).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "writeName", MTD_WRITE_NAME).pop() + .aload(0).lload(2) + .invokevirtual(CD_JSON_GENERATOR, "writeNumber", MTD_WRITE_LONG).pop() + .return_()); + emitPadded(clb, "$bool", MTD_HELP_BOOLEAN, cob -> cob.aload(0).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "writeName", MTD_WRITE_NAME).pop() + .aload(0).iload(2) + .invokevirtual(CD_JSON_GENERATOR, "writeBoolean", MTD_WRITE_BOOLEAN).pop() + .return_()); + emitPadded(clb, "$name", MTD_HELP_NAME, cob -> cob.aload(0).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "writeName", MTD_WRITE_NAME).pop() + .return_()); + }); + } + + private static void emitPadded(java.lang.classfile.ClassBuilder clb, String name, + MethodTypeDesc type, java.util.function.Consumer body) { + MethodParametersAttribute helperParams = (type.parameterCount() == 2) + ? BeanReaderGenerator.params("g", "name") + : BeanReaderGenerator.params("g", "name", "value"); + clb.withMethod(name, type, ClassFile.ACC_STATIC, + mb -> mb.with(helperParams).withCode(cob -> { + for (int i = 0; i < INLINE_PAD; i++) { + cob.nop(); + } + body.accept(cob); + })); + } + + private static void buildSerialize(CodeBuilder cob, + List props, String[] stockName, String[] nameName, String[] childName, + String[] handleName, ViewStrategy views) { + final int gen = 2; + final int ctxt = 3; + final int refSlot = 5; + final int maskSlot = 6; + final int ixSlot = 8; + final int excSlot = 9; + + Label scopeStart = cob.newBoundLabel(); + Label delegate = null; + if (views == ViewStrategy.DELEGATE) { + // View-active calls (declared views with more than 64 properties, + // or the write-nothing inclusion-off case) go to the stock + // serializer, whose tail sits after the body so the main path + // decompiles un-nested. + delegate = cob.newLabel(); + cob.aload(ctxt).invokevirtual(CD_SER_CONTEXT, "getActiveView", MTD_GET_ACTIVE_VIEW); + cob.ifnonnull(delegate); + } else if (views == ViewStrategy.MASK) { + emitViewMask(cob, ctxt, maskSlot); + } + + cob.aload(gen).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "writeStartObject", MTD_WRITE_START_OBJECT).pop(); + + Label handler = emitPropsWithHandler(cob, props, stockName, nameName, + childName, handleName, views, gen, ctxt, refSlot, maskSlot, ixSlot); + + cob.aload(gen).invokevirtual(CD_JSON_GENERATOR, "writeEndObject", MTD_WRITE_END).pop(); + cob.return_(); + emitPropertyHandler(cob, handler, gen, ctxt, ixSlot, excSlot); + if (delegate != null) { + cob.labelBinding(delegate); + cob.aload(0).aload(1).aload(gen).aload(ctxt); + cob.invokevirtual(CD_WRITER_BASE, "_stockSerialize", MTD_SERIALIZE); + cob.return_(); + } + + Label scopeEnd = cob.newBoundLabel(); + cob.localVariable(1, "value", ConstantDescs.CD_Object, scopeStart, scopeEnd); + cob.localVariable(gen, "g", CD_JSON_GENERATOR, scopeStart, scopeEnd); + cob.localVariable(ctxt, "ctxt", CD_SER_CONTEXT, scopeStart, scopeEnd); + // Slot entries must stay within max_locals, so name the child slot + // only when some CHILD arm actually stores it. + if (props.stream().anyMatch(pr -> pr.kind() == WKind.CHILD)) { + cob.localVariable(refSlot, "child", ConstantDescs.CD_Object, scopeStart, scopeEnd); + } + cob.localVariable(ixSlot, "propIndex", ConstantDescs.CD_int, scopeStart, scopeEnd); + cob.localVariable(excSlot, "e", ConstantDescs.CD_Throwable, scopeStart, scopeEnd); + if (views == ViewStrategy.MASK) { + cob.localVariable(maskSlot, "viewMask", ConstantDescs.CD_long, scopeStart, scopeEnd); + } + } + + // The serializeWithType override: stock BeanSerializerBase's flow with + // the object-id and filter-id branches omitted (both gate at the factory) + // and the type id always typeSer.typeId(value, START_OBJECT) - beans with + // a @JsonTypeId property keep the base forwarding instead. The props body + // is emitted again rather than shared through a method so the plain + // serialize path keeps its measured shape. + private static void buildSerializeWithType(CodeBuilder cob, + List props, String[] stockName, String[] nameName, String[] childName, + String[] handleName, ViewStrategy views) { + final int gen = 2; + final int ctxt = 3; + final int typeSer = 4; + final int typeIdSlot = 5; + final int refSlot = 6; + final int maskSlot = 7; + final int ixSlot = 9; + final int excSlot = 10; + + Label scopeStart = cob.newBoundLabel(); + Label delegate = null; + if (views == ViewStrategy.DELEGATE) { + delegate = cob.newLabel(); + cob.aload(ctxt).invokevirtual(CD_SER_CONTEXT, "getActiveView", MTD_GET_ACTIVE_VIEW); + cob.ifnonnull(delegate); + } else if (views == ViewStrategy.MASK) { + emitViewMask(cob, ctxt, maskSlot); + } + + cob.aload(typeSer).aload(1); + cob.getstatic(CD_JSON_TOKEN, "START_OBJECT", CD_JSON_TOKEN); + cob.invokevirtual(CD_TYPE_SERIALIZER, "typeId", MTD_TYPE_ID); + cob.astore(typeIdSlot); + cob.aload(typeSer).aload(gen).aload(ctxt).aload(typeIdSlot); + cob.invokevirtual(CD_TYPE_SERIALIZER, "writeTypePrefix", MTD_WRITE_TYPE_PART).pop(); + cob.aload(gen).aload(1) + .invokevirtual(CD_JSON_GENERATOR, "assignCurrentValue", MTD_ASSIGN_CURRENT); + + Label handler = emitPropsWithHandler(cob, props, stockName, nameName, + childName, handleName, views, gen, ctxt, refSlot, maskSlot, ixSlot); + + cob.aload(typeSer).aload(gen).aload(ctxt).aload(typeIdSlot); + cob.invokevirtual(CD_TYPE_SERIALIZER, "writeTypeSuffix", MTD_WRITE_TYPE_PART).pop(); + cob.return_(); + emitPropertyHandler(cob, handler, gen, ctxt, ixSlot, excSlot); + if (delegate != null) { + cob.labelBinding(delegate); + cob.aload(0).aload(1).aload(gen).aload(ctxt).aload(typeSer); + cob.invokevirtual(CD_WRITER_BASE, "_stockSerializeWithType", MTD_SERIALIZE_WITH_TYPE); + cob.return_(); + } + + Label scopeEnd = cob.newBoundLabel(); + cob.localVariable(1, "value", ConstantDescs.CD_Object, scopeStart, scopeEnd); + cob.localVariable(gen, "g", CD_JSON_GENERATOR, scopeStart, scopeEnd); + cob.localVariable(ctxt, "ctxt", CD_SER_CONTEXT, scopeStart, scopeEnd); + cob.localVariable(typeSer, "typeSer", CD_TYPE_SERIALIZER, scopeStart, scopeEnd); + cob.localVariable(typeIdSlot, "typeIdDef", CD_WRITABLE_TYPE_ID, scopeStart, scopeEnd); + if (props.stream().anyMatch(pr -> pr.kind() == WKind.CHILD)) { + cob.localVariable(refSlot, "child", ConstantDescs.CD_Object, scopeStart, scopeEnd); + } + cob.localVariable(ixSlot, "propIndex", ConstantDescs.CD_int, scopeStart, scopeEnd); + cob.localVariable(excSlot, "e", ConstantDescs.CD_Throwable, scopeStart, scopeEnd); + if (views == ViewStrategy.MASK) { + cob.localVariable(maskSlot, "viewMask", ConstantDescs.CD_long, scopeStart, scopeEnd); + } + } + + // MASK strategy: resolves the visibility bitmask for the call (all-ones + // when no view is active) into maskSlot. + private static void emitViewMask(CodeBuilder cob, int ctxt, int maskSlot) { + Label nullView = cob.newLabel(); + Label haveMask = cob.newLabel(); + cob.aload(ctxt).invokevirtual(CD_SER_CONTEXT, "getActiveView", MTD_GET_ACTIVE_VIEW); + cob.dup(); + cob.ifnull(nullView); + cob.aload(0); + cob.swap(); + cob.invokevirtual(CD_WRITER_BASE, "_viewMask", MTD_VIEW_MASK); + cob.lstore(maskSlot); + cob.goto_(haveMask); + cob.labelBinding(nullView); + cob.pop(); + cob.loadConstant(-1L); + cob.lstore(maskSlot); + cob.labelBinding(haveMask); + } + + // The property section of stock _serializePropertiesNoView: an Exception + // goes through wrapAndThrow and a StackOverflowError becomes the + // "Infinite recursion" DatabindException, both with the reference of the + // property being written (ixSlot). Returns the handler label, which the + // caller binds after its return (see emitPropertyHandler). + private static Label emitPropsWithHandler(CodeBuilder cob, + List props, String[] stockName, String[] nameName, String[] childName, + String[] handleName, ViewStrategy views, int gen, int ctxt, int refSlot, + int maskSlot, int ixSlot) { + Label start = cob.newLabel(); + Label end = cob.newLabel(); + Label handler = cob.newLabel(); + cob.exceptionCatch(start, end, handler, ConstantDescs.CD_Exception); + cob.exceptionCatch(start, end, handler, CD_STACK_OVERFLOW); + // Assigned before the region so the handler can read it from any + // instruction in it. + cob.iconst_0().istore(ixSlot); + cob.labelBinding(start); + emitProps(cob, props, stockName, nameName, childName, handleName, views, + gen, ctxt, refSlot, maskSlot, ixSlot); + cob.labelBinding(end); + return handler; + } + + private static void emitPropertyHandler(CodeBuilder cob, Label handler, int gen, int ctxt, + int ixSlot, int excSlot) { + cob.labelBinding(handler); + cob.astore(excSlot); + cob.aload(0).aload(excSlot).aload(1).iload(ixSlot).aload(gen).aload(ctxt); + cob.invokevirtual(CD_WRITER_BASE, "_propertyFailure", MTD_PROP_FAILURE); + cob.athrow(); + } + + // One writeName/write-value pair per property, the bean in slot 1. + private static void emitProps(CodeBuilder cob, List props, + String[] stockName, String[] nameName, String[] childName, String[] handleName, + ViewStrategy views, int gen, int ctxt, int refSlot, int maskSlot, int ixSlot) { + for (int i = 0; i < props.size(); i++) { + GenWProp prop = props.get(i); + if (i > 0) { + cob.loadConstant(i); + cob.istore(ixSlot); + } + Label hidden = null; + if (views == ViewStrategy.MASK) { + hidden = cob.newLabel(); + cob.lload(maskSlot); + cob.loadConstant(1L << i); + cob.land(); + cob.lconst_0(); + cob.lcmp(); + cob.ifeq(hidden); + } + switch (prop.kind()) { + case INT -> emitScalar(cob, gen, nameName[i], handleName[i], + "$int", MTD_HELP_INT, ConstantDescs.CD_int); + case LONG -> emitScalar(cob, gen, nameName[i], handleName[i], + "$long", MTD_HELP_LONG, ConstantDescs.CD_long); + case BOOLEAN -> emitScalar(cob, gen, nameName[i], handleName[i], + "$bool", MTD_HELP_BOOLEAN, ConstantDescs.CD_boolean); + case STRING -> { + // The getter handle is erased to (Object)Object; the value + // is a String at run time (java.lang, not a bean name), so + // a checkcast recovers the helper's descriptor. + cob.aload(gen); + ldcData(cob, nameName[i], CD_SERIALIZABLE_STRING); + emitLoad(cob, handleName[i], ConstantDescs.CD_Object); + cob.checkcast(ConstantDescs.CD_String); + cob.invokestatic(CD_WRITE_HELPERS, "$str", MTD_HELP_STRING); + } + case CHILD -> { + // A value that is the bean itself goes to the stock + // writer, which owns the self-reference handling + // (FAIL_ON_SELF_REFERENCES, WRITE_SELF_REFERENCES_AS_NULL) + // and reads the value again. + Label isNull = cob.newLabel(); + Label self = cob.newLabel(); + Label done = cob.newLabel(); + emitLoad(cob, handleName[i], ConstantDescs.CD_Object); + cob.astore(refSlot); + cob.aload(refSlot).ifnull(isNull); + cob.aload(refSlot).aload(1).if_acmpeq(self); + cob.aload(gen); + ldcData(cob, nameName[i], CD_SERIALIZABLE_STRING); + cob.invokestatic(CD_WRITE_HELPERS, "$name", MTD_HELP_NAME); + ldcData(cob, childName[i], CD_WRITER_BASE); + cob.aload(refSlot).aload(gen).aload(ctxt); + cob.invokevirtual(CD_WRITER_BASE, "serialize", MTD_SERIALIZE); + cob.goto_(done); + cob.labelBinding(isNull); + cob.aload(gen); + ldcData(cob, nameName[i], CD_SERIALIZABLE_STRING); + cob.invokestatic(CD_WRITE_HELPERS, "$name", MTD_HELP_NAME); + cob.aload(gen).invokevirtual(CD_JSON_GENERATOR, "writeNull", MTD_WRITE_NULL).pop(); + cob.goto_(done); + cob.labelBinding(self); + ldcData(cob, stockName[i], CD_PROPERTY_WRITER); + cob.aload(1).aload(gen).aload(ctxt); + cob.invokevirtual(CD_PROPERTY_WRITER, "serializeAsProperty", + MTD_SERIALIZE_AS_PROPERTY); + cob.labelBinding(done); + } + case STOCK -> { + ldcData(cob, stockName[i], CD_PROPERTY_WRITER); + cob.aload(1).aload(gen).aload(ctxt); + cob.invokevirtual(CD_PROPERTY_WRITER, "serializeAsProperty", + MTD_SERIALIZE_AS_PROPERTY); + } + } + if (hidden != null) { + cob.labelBinding(hidden); + } + } + } + + // Overrides GeneratedWriterBase._computeViewMask: bit i set when property + // i is visible in the view, per the same rule the stock factory uses to + // build the filtered writer array. + private static void emitComputeViewMask(java.lang.classfile.ClassBuilder clb, + List props, String[] stockName, boolean includeByDefault) { + clb.withMethod("_computeViewMask", MTD_VIEW_MASK, ClassFile.ACC_PROTECTED, + mb -> mb.with(BeanReaderGenerator.params("activeView")).withCode(cob -> { + final int maskSlot = 2; + Label scopeStart = cob.newBoundLabel(); + cob.lconst_0().lstore(maskSlot); + for (int i = 0; i < props.size(); i++) { + Label skip = cob.newLabel(); + ldcData(cob, stockName[i], CD_PROPERTY_WRITER); + cob.aload(1); + cob.loadConstant(includeByDefault ? 1 : 0); + cob.invokestatic(CD_WRITER_BASE, "_propVisible", MTD_PROP_VISIBLE); + cob.ifeq(skip); + cob.lload(maskSlot); + cob.loadConstant(1L << i); + cob.lor(); + cob.lstore(maskSlot); + cob.labelBinding(skip); + } + cob.lload(maskSlot).lreturn(); + Label scopeEnd = cob.newBoundLabel(); + cob.localVariable(1, "activeView", ConstantDescs.CD_Class, scopeStart, scopeEnd); + cob.localVariable(maskSlot, "mask", ConstantDescs.CD_long, scopeStart, scopeEnd); + })); + } + + private static void emitScalar(CodeBuilder cob, int gen, + String nameEntry, String handleEntry, String helper, + MethodTypeDesc helperType, ClassDesc erasedDesc) { + cob.aload(gen); + ldcData(cob, nameEntry, CD_SERIALIZABLE_STRING); + emitLoad(cob, handleEntry, erasedDesc); + cob.invokestatic(CD_WRITE_HELPERS, helper, helperType); + } + + // Loads the property value from the bean (parameter 1) through the + // constant (Object)eV handle. + private static void emitLoad(CodeBuilder cob, String handleEntry, ClassDesc erasedDesc) { + ldcData(cob, handleEntry, ConstantDescs.CD_MethodHandle); + cob.aload(1); + cob.invokevirtual(ConstantDescs.CD_MethodHandle, "invokeExact", + MethodTypeDesc.of(erasedDesc, ConstantDescs.CD_Object)); + } +} diff --git a/blackbird/src/main/resources/META-INF/services/tools.jackson.databind.JacksonModule b/blackbird/src/main/resources/META-INF/services/tools.jackson.databind.JacksonModule index 09536df46..f71b80042 100644 --- a/blackbird/src/main/resources/META-INF/services/tools.jackson.databind.JacksonModule +++ b/blackbird/src/main/resources/META-INF/services/tools.jackson.databind.JacksonModule @@ -1,2 +1 @@ -tools.jackson.module.blackbird.BlackbirdModule - +tools.jackson.module.blackbird.internal.DiscoveredBlackbirdModule diff --git a/blackbird/src/moditect/module-info.java b/blackbird/src/moditect/module-info.java deleted file mode 100644 index bfb48f5ae..000000000 --- a/blackbird/src/moditect/module-info.java +++ /dev/null @@ -1,11 +0,0 @@ -module tools.jackson.module.blackbird { - requires java.logging; - - requires tools.jackson.core; - requires tools.jackson.databind; - - exports tools.jackson.module.blackbird; - - provides tools.jackson.databind.JacksonModule with - tools.jackson.module.blackbird.BlackbirdModule; -} diff --git a/blackbird/src/test/java/module-info.java b/blackbird/src/test/java/module-info.java index bd51578dc..e9fdab59f 100644 --- a/blackbird/src/test/java/module-info.java +++ b/blackbird/src/test/java/module-info.java @@ -4,8 +4,6 @@ // Since we are not split from Main artifact, will not // need to depend on Main artifact -- but need its dependencies - requires java.logging; - requires com.fasterxml.jackson.annotation; requires tools.jackson.core; requires tools.jackson.databind; @@ -31,16 +29,16 @@ opens tools.jackson.module.blackbird.deser.merge; opens tools.jackson.module.blackbird.deser.struct; opens tools.jackson.module.blackbird.format; + opens tools.jackson.module.blackbird.internal; opens tools.jackson.module.blackbird.misc; opens tools.jackson.module.blackbird.objectid; opens tools.jackson.module.blackbird.roundtrip; opens tools.jackson.module.blackbird.ser; opens tools.jackson.module.blackbird.ser.filter; - opens tools.jackson.module.blackbird.tofix; opens tools.jackson.module.blackbird.testutil; opens tools.jackson.module.blackbird.testutil.failure; provides tools.jackson.databind.JacksonModule with - tools.jackson.module.blackbird.BlackbirdModule; + tools.jackson.module.blackbird.internal.DiscoveredBlackbirdModule; uses tools.jackson.databind.JacksonModule; } diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java b/blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java index f2bbc4c87..f865b1f77 100644 --- a/blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java @@ -7,10 +7,18 @@ import tools.jackson.core.*; +import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationContext; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.ValueSerializer; import tools.jackson.databind.json.JsonMapper; import tools.jackson.module.blackbird.testutil.NoCheckSubTypeValidator; +import tools.jackson.module.blackbird.codegen.CodegenFallbacks; +import tools.jackson.module.blackbird.internal.GeneratedBuilderBase; +import tools.jackson.module.blackbird.internal.GeneratedReaderBase; +import tools.jackson.module.blackbird.internal.GeneratedWriterBase; import static org.junit.jupiter.api.Assertions.*; @@ -213,6 +221,13 @@ protected static JsonMapper.Builder blackbirdMapperBuilder() { return mapperBuilder(); } + static { + // The suite runs strict: a codec-generation failure fails the test + // instead of demoting to the stock path, so the lenient production + // fallback cannot mask a generator bug here. + System.setProperty(CodegenFallbacks.FAIL_ON_ERROR_PROPERTY, "true"); + } + protected static JsonMapper.Builder mapperBuilder() { return JsonMapper.builder() .polymorphicTypeValidator(new NoCheckSubTypeValidator()) @@ -223,6 +238,75 @@ protected static JsonMapper newVanillaJSONMapper() { return new JsonMapper(); } + /* + /********************************************************** + /* Helper methods; codec engagement + /********************************************************** + */ + + /** The (contextualized) deserializer the mapper uses for a root value of the type. */ + protected static ValueDeserializer rootReader(ObjectMapper mapper, Class type) { + DeserializationContext ctxt = mapper._deserializationContext(); + return ctxt.findRootValueDeserializer(ctxt.constructType(type)); + } + + /** The (contextualized) serializer the mapper uses for a root value of the type. */ + protected static ValueSerializer rootWriter(ObjectMapper mapper, Class type) { + SerializationContext ctxt = mapper._serializationContext(); + return ctxt.handleRootContextualization(ctxt.findValueSerializer(type)); + } + + /** + * Whether a (de)serializer that a modifier captured resolved to a + * generated codec: the placeholder stays in place either way, and only + * its codec field tells a generated bean from a gated one. + */ + protected static boolean hasGeneratedCodec(Object captured) { + try { + java.lang.reflect.Field codec = captured.getClass().getDeclaredField("_codec"); + codec.setAccessible(true); + return codec.get(captured) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + /** Asserts that the generated reader handles the type, and returns its arm layout. */ + protected static String assertGeneratedReader(ObjectMapper mapper, Class type) { + ValueDeserializer d = rootReader(mapper, type); + if (d instanceof GeneratedReaderBase r) { + return r.codecLayout(); + } + if (d instanceof GeneratedBuilderBase b) { + return b.codecLayout(); + } + return fail(type.getSimpleName() + " should use a generated reader, uses " + d); + } + + /** Asserts that the stock deserializer handles the type. */ + protected static void assertStockReader(ObjectMapper mapper, Class type) { + ValueDeserializer d = rootReader(mapper, type); + assertFalse(d instanceof GeneratedReaderBase || d instanceof GeneratedBuilderBase, + type.getSimpleName() + " should keep the stock deserializer, uses " + d); + } + + /** Asserts that the generated writer handles the type, and returns its layout. */ + protected static String assertGeneratedWriter(ObjectMapper mapper, Class type) { + ValueSerializer s = rootWriter(mapper, type); + assertTrue(s instanceof GeneratedWriterBase, + type.getSimpleName() + " should use a generated writer, uses " + s); + return ((GeneratedWriterBase) s).codecLayout(); + } + + /** Asserts that the stock serializer handles the type. */ + protected static void assertStockWriter(ObjectMapper mapper, Class type) { + ValueSerializer s = rootWriter(mapper, type); + assertFalse(s instanceof GeneratedWriterBase, + type.getSimpleName() + " should keep the stock serializer, uses " + s); + } + /* /********************************************************** /* Helper methods; assertions diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/ModuleSPIMetadataTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/ModuleSPIMetadataTest.java index af6acafe2..792ec5c70 100644 --- a/blackbird/src/test/java/tools/jackson/module/blackbird/ModuleSPIMetadataTest.java +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/ModuleSPIMetadataTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.databind.JacksonModule; +import tools.jackson.module.blackbird.internal.DiscoveredBlackbirdModule; import static org.junit.jupiter.api.Assertions.*; @@ -17,7 +18,9 @@ void testModuleSPIMetadata() { = ServiceLoader.load(JacksonModule.class); assertTrue(loader.iterator().hasNext(), "Expected at least one `Module` implementation to be found via `ServiceLoader`"); - final String exp = BlackbirdModule.class.getName(); + // Discovery instantiates the dedicated provider subclass (see + // DiscoveredBlackbirdModule), not BlackbirdModule itself. + final String exp = DiscoveredBlackbirdModule.class.getName(); int count = 0; try { diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/codegen/SharedWriteHelpersTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/codegen/SharedWriteHelpersTest.java new file mode 100644 index 000000000..a5ed40fb3 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/codegen/SharedWriteHelpersTest.java @@ -0,0 +1,94 @@ +package tools.jackson.module.blackbird.codegen; + +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * All generated writers call one class of write helpers, padded past the + * HotSpot inline limits (see {@code BeanWriterGenerator.INLINE_PAD}). + */ +public class SharedWriteHelpersTest extends BlackbirdTestBase +{ + // The HotSpot default of FreqInlineSize (C2) on every platform. The C1 + // limit, C1MaxInlineSize, is 35 bytes. + private static final int FREQ_INLINE_SIZE = 325; + + private static final List HELPERS = List.of("$bool", "$int", "$long", "$name", "$str"); + + public static class Point { + public String name = "p"; + public int x = 1; + public long y = 2L; + public boolean on = true; + public Point next; + } + + public static class Tag { + public String text; + public int size = 3; + public Point at = new Point(); + } + + @Test + public void writersCallOneSharedHelperClass() throws Exception { + ObjectMapper mapper = newObjectMapper(); + assertGeneratedWriter(mapper, Point.class); + assertGeneratedWriter(mapper, Tag.class); + Class pointWriter = rootWriter(mapper, Point.class).getClass(); + Class tagWriter = rootWriter(mapper, Tag.class).getClass(); + assertEquals(List.of(), helperMethods(pointWriter)); + assertEquals(List.of(), helperMethods(tagWriter)); + + Class helpers = Class.forName(WriterCodegen.HELPERS_CLASS, false, + pointWriter.getClassLoader()); + assertFalse(helpers.isHidden()); + assertSame(pointWriter.getModule(), helpers.getModule()); + assertEquals(pointWriter.getPackageName(), helpers.getPackageName()); + assertEquals(HELPERS, helperMethods(helpers)); + } + + @Test + public void helpersWriteAsStock() { + Tag tag = new Tag(); + assertEquals(newVanillaJSONMapper().writeValueAsString(tag), + newObjectMapper().writeValueAsString(tag)); + } + + @Test + public void helpersExceedTheInlineLimits() { + ClassModel model = ClassFile.of().parse(ClassFileEmitters.emitWriteHelpers()); + Map codeLengths = new TreeMap<>(); + for (MethodModel m : model.methods()) { + codeLengths.put(m.methodName().stringValue(), + m.findAttribute(Attributes.code()).orElseThrow().codeLength()); + } + assertEquals(HELPERS, List.copyOf(codeLengths.keySet())); + codeLengths.forEach((name, length) -> assertTrue(length > FREQ_INLINE_SIZE, + name + " has " + length + " bytes of bytecode, so C2 can inline it")); + } + + // Synthetic methods are left out: a coverage agent such as JaCoCo adds + // $jacocoInit to the helper class when it is defined. + private static List helperMethods(Class type) { + return Arrays.stream(type.getDeclaredMethods()) + .filter(m -> !m.isSynthetic()) + .map(Method::getName) + .filter(n -> n.startsWith("$")) + .sorted() + .toList(); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecAnySetterTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecAnySetterTest.java new file mode 100644 index 000000000..f251ccc8c --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecAnySetterTest.java @@ -0,0 +1,175 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.IgnoredPropertyException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Any-setter parity: beans with {@code @JsonAnySetter} engage codecs, and the + * unknown arm feeds the stock any-setter in handleUnknownVanilla's exact + * order (explicit ignorals first, then the any-setter, which also consumes + * names that ignoreUnknown would otherwise skip). Every case compares against + * vanilla. Records with an any-setter stay on the stock path: their values + * buffer before construction, which the typed-locals loop does not model. + */ +public class BBCodecAnySetterTest extends BlackbirdTestBase +{ + public static class AnyBean { + public int i; + public String s; + public Map extra = new HashMap<>(); + + @JsonAnySetter + public void set(String name, Object value) { + extra.put(name, value); + } + } + + @JsonIgnoreProperties(value = { "secret" }, ignoreUnknown = true) + public static class AnyWithIgnoralsBean { + public int i; + public Map extra = new HashMap<>(); + + @JsonAnySetter + public void set(String name, Object value) { + extra.put(name, value); + } + } + + public static class AnyFieldBean { + public int i; + + @JsonAnySetter + public Map extra = new HashMap<>(); + } + + public record AnyRecord(String name) { + @JsonAnySetter + public void set(String n, Object v) { } + } + + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + private static ObjectMapper capturingMapper(Map, ValueDeserializer> seen) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + seen.put(ref.getBeanClass(), d); + return d; + } + }); + } + }; + return JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()) + .build(); + } + + private static void assertEngaged(Map, ValueDeserializer> seen, Class cls, + boolean engaged) { + ValueDeserializer d = seen.get(cls); + assertNotNull(d, "no deserializer captured for " + cls); + assertEquals(engaged, hasGeneratedCodec(d), + cls.getSimpleName() + " engagement"); + } + + @Test + public void testAnySetterCollectsUnknowns() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'x':'a','s':'known','deep':{'k':[1,2]},'n':null}"); + AnyBean v = vanilla.readValue(doc, AnyBean.class); + AnyBean m = mapper.readValue(doc, AnyBean.class); + assertEngaged(seen, AnyBean.class, true); + assertEquals(v.i, m.i); + assertEquals(v.s, m.s); + assertEquals(v.extra, m.extra); + } + + @Test + public void testAnySetterBeatsStrictUnknowns() throws Exception { + // With an any-setter, nothing is "unknown": per-call + // FAIL_ON_UNKNOWN_PROPERTIES never fires, exactly like stock. + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'x':'a'}"); + AnyBean v = vanilla.reader(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .forType(AnyBean.class).readValue(doc); + AnyBean m = mapper.reader(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .forType(AnyBean.class).readValue(doc); + assertEngaged(seen, AnyBean.class, true); + assertEquals(v.extra, m.extra); + } + + @Test + public void testIgnoredNameNeverReachesAnySetter() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'secret':'boo','x':'a'}"); + AnyWithIgnoralsBean v = vanilla.readValue(doc, AnyWithIgnoralsBean.class); + AnyWithIgnoralsBean m = mapper.readValue(doc, AnyWithIgnoralsBean.class); + assertEngaged(seen, AnyWithIgnoralsBean.class, true); + assertEquals(v.i, m.i); + assertEquals(v.extra, m.extra); + assertFalse(m.extra.containsKey("secret")); + + IgnoredPropertyException ve = assertThrows(IgnoredPropertyException.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES) + .forType(AnyWithIgnoralsBean.class).readValue(doc)); + IgnoredPropertyException me = assertThrows(IgnoredPropertyException.class, + () -> mapper.reader(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES) + .forType(AnyWithIgnoralsBean.class).readValue(doc)); + assertEquals(ve.getPropertyName(), me.getPropertyName()); + } + + @Test + public void testAnySetterField() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':2,'a':1,'b':'two'}"); + AnyFieldBean v = vanilla.readValue(doc, AnyFieldBean.class); + AnyFieldBean m = mapper.readValue(doc, AnyFieldBean.class); + assertEngaged(seen, AnyFieldBean.class, true); + assertEquals(v.i, m.i); + assertEquals(v.extra, m.extra); + } + + @Test + public void testRecordWithAnySetterStaysStock() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'name':'a','other':1}"); + AnyRecord v = vanilla.readValue(doc, AnyRecord.class); + AnyRecord m = mapper.readValue(doc, AnyRecord.class); + // The placeholder wraps the record; the factory gates it at resolve. + assertStockReader(mapper, AnyRecord.class); + assertEquals(v, m); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecCompatibilityTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecCompatibilityTest.java new file mode 100644 index 000000000..087770a0b --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecCompatibilityTest.java @@ -0,0 +1,261 @@ +package tools.jackson.module.blackbird.deser; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.JsonParser; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.DeserializationProblemHandler; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.exc.UnrecognizedPropertyException; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Behavior-parity regression tests against stock databind: quoted-scalar + * coercion, unknown-property handling under per-call config and problem + * handlers, record required and missing components, {@code @JsonUnwrapped} + * with codec-eligible children, PROPERTY_NAME entry, and modifier + * serializability. Every case compares against a vanilla mapper. + */ +public class BBCodecCompatibilityTest extends BlackbirdTestBase +{ + public static class ScalarBean { + public int i; + public long l; + public boolean b; + public String s; + } + + public record ReqRecord(@JsonProperty(required = true) String name, int count) { } + + public record PlainRecord(String name, int count) { } + + public static class Child { + public int value; + public String tag; + } + + public static class UnwrappedParent { + public int a; + @JsonUnwrapped + public Child child; + } + + public static class PrefixedParent { + public int a; + @JsonUnwrapped(prefix = "foo.") + public Child child; + } + + @JsonIgnoreProperties({ "skipMe" }) + public static class IgnoralBean { + public int i; + } + + private final ObjectMapper mapper = newObjectMapper(); + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + @Test + public void testQuotedScalarCoercion() throws Exception { + String doc = a2q("{'i':'123','l':'456','b':'true','s':'x'}"); + ScalarBean exp = vanilla.readValue(doc, ScalarBean.class); + ScalarBean act = mapper.readValue(doc, ScalarBean.class); + assertEquals(exp.i, act.i); + assertEquals(exp.l, act.l); + assertEquals(exp.b, act.b); + assertEquals(exp.s, act.s); + } + + @Test + public void testFloatToIntCoercion() throws Exception { + String doc = a2q("{'i':1.5,'l':2.5}"); + ScalarBean exp = vanilla.readValue(doc, ScalarBean.class); + ScalarBean act = mapper.readValue(doc, ScalarBean.class); + assertEquals(exp.i, act.i); + assertEquals(exp.l, act.l); + } + + @Test + public void testScalarFromNumberToken() throws Exception { + // String property fed a number token: stock coerces, so must we. + String doc = a2q("{'s':42,'b':true,'i':7}"); + ScalarBean exp = vanilla.readValue(doc, ScalarBean.class); + ScalarBean act = mapper.readValue(doc, ScalarBean.class); + assertEquals(exp.s, act.s); + assertEquals(exp.b, act.b); + assertEquals(exp.i, act.i); + } + + @Test + public void testPerCallFailOnUnknownProperties() throws Exception { + String doc = a2q("{'i':1,'nope':{'x':2},'s':'y'}"); + // Default (disabled): unknown skips, other properties land. + ScalarBean ok = mapper.readValue(doc, ScalarBean.class); + assertEquals(1, ok.i); + assertEquals("y", ok.s); + // Per-call enable must fail, exactly like vanilla. + assertThrows(UnrecognizedPropertyException.class, () -> vanilla + .readerFor(ScalarBean.class) + .with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .readValue(doc)); + assertThrows(UnrecognizedPropertyException.class, () -> mapper + .readerFor(ScalarBean.class) + .with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .readValue(doc)); + } + + @Test + public void testProblemHandlerSeesUnknownProperty() throws Exception { + List seen = new ArrayList<>(); + ObjectMapper m = mapperBuilder().addHandler(new DeserializationProblemHandler() { + @Override + public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, + ValueDeserializer deserializer, Object beanOrClass, String propertyName) { + seen.add(propertyName); + p.skipChildren(); + return true; + } + }).build(); + ScalarBean bean = m.readValue(a2q("{'i':1,'mystery':[1,2]}"), ScalarBean.class); + assertEquals(1, bean.i); + assertEquals(List.of("mystery"), seen); + } + + @Test + public void testIgnoredPropertiesKeepStockSemantics() throws Exception { + String doc = a2q("{'i':1,'skipMe':{'x':2}}"); + IgnoralBean exp = vanilla.readerFor(IgnoralBean.class) + .with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).readValue(doc); + IgnoralBean act = mapper.readerFor(IgnoralBean.class) + .with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).readValue(doc); + assertEquals(exp.i, act.i); + assertThrows(UnrecognizedPropertyException.class, () -> mapper + .readerFor(IgnoralBean.class) + .with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .readValue(a2q("{'i':1,'other':2}"))); + } + + @Test + public void testRequiredRecordComponentMissing() throws Exception { + String doc = a2q("{'count':3}"); + MismatchedInputException expEx = assertThrows(MismatchedInputException.class, + () -> vanilla.readValue(doc, ReqRecord.class)); + MismatchedInputException actEx = assertThrows(MismatchedInputException.class, + () -> mapper.readValue(doc, ReqRecord.class)); + assertTrue(expEx.getMessage().contains("Missing required creator property 'name'"), + expEx.getMessage()); + assertTrue(actEx.getMessage().contains("Missing required creator property 'name'"), + actEx.getMessage()); + // Present case unaffected. + assertEquals(vanilla.readValue(a2q("{'name':'x','count':3}"), ReqRecord.class), + mapper.readValue(a2q("{'name':'x','count':3}"), ReqRecord.class)); + } + + @Test + public void testPerCallFailOnMissingCreatorProperties() throws Exception { + String doc = a2q("{'name':'x'}"); + // Default: missing component takes its default value. + assertEquals(vanilla.readValue(doc, PlainRecord.class), + mapper.readValue(doc, PlainRecord.class)); + assertThrows(MismatchedInputException.class, () -> vanilla + .readerFor(PlainRecord.class) + .with(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES) + .readValue(doc)); + assertThrows(MismatchedInputException.class, () -> mapper + .readerFor(PlainRecord.class) + .with(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES) + .readValue(doc)); + } + + @Test + public void testUnwrappedChildWithCodec() throws Exception { + UnwrappedParent p = new UnwrappedParent(); + p.a = 1; + p.child = new Child(); + p.child.value = 3; + p.child.tag = "t"; + String expJson = vanilla.writeValueAsString(p); + assertEquals(expJson, mapper.writeValueAsString(p)); + + UnwrappedParent exp = vanilla.readValue(expJson, UnwrappedParent.class); + UnwrappedParent act = mapper.readValue(expJson, UnwrappedParent.class); + assertNotNull(act.child); + assertEquals(exp.a, act.a); + assertEquals(exp.child.value, act.child.value); + assertEquals(exp.child.tag, act.child.tag); + } + + @Test + public void testPrefixedUnwrappedChildWithCodec() throws Exception { + PrefixedParent p = new PrefixedParent(); + p.a = 1; + p.child = new Child(); + p.child.value = 3; + p.child.tag = "t"; + String expJson = vanilla.writeValueAsString(p); + assertEquals(expJson, mapper.writeValueAsString(p)); + + PrefixedParent exp = vanilla.readValue(expJson, PrefixedParent.class); + PrefixedParent act = mapper.readValue(expJson, PrefixedParent.class); + assertNotNull(act.child); + assertEquals(exp.a, act.a); + assertEquals(exp.child.value, act.child.value); + assertEquals(exp.child.tag, act.child.tag); + } + + @Test + public void testPropertyNameEntry() throws Exception { + String doc = a2q("{'i':7,'l':8,'b':true,'s':'y'}"); + ScalarBean exp; + try (JsonParser p = vanilla.createParser(doc)) { + p.nextToken(); + p.nextToken(); + exp = vanilla.readValue(p, ScalarBean.class); + } + ScalarBean act; + try (JsonParser p = mapper.createParser(doc)) { + p.nextToken(); + p.nextToken(); + act = mapper.readValue(p, ScalarBean.class); + } + assertEquals(exp.i, act.i); + assertEquals(exp.l, act.l); + assertEquals(exp.b, act.b); + assertEquals(exp.s, act.s); + } + + @Test + public void testMapperUsableAfterJdkSerialization() throws Exception { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (ObjectOutputStream out = new ObjectOutputStream(bytes)) { + out.writeObject(newObjectMapper()); + } + ObjectMapper revived; + try (ObjectInputStream in = new ObjectInputStream( + new ByteArrayInputStream(bytes.toByteArray()))) { + revived = (ObjectMapper) in.readObject(); + } + // Building a deserializer runs the modifier's updateBuilder, which + // uses the transient ThreadLocal that deserialization nulled out. + ScalarBean bean = revived.readValue(a2q("{'i':5,'s':'z'}"), ScalarBean.class); + assertEquals(5, bean.i); + assertEquals("z", bean.s); + assertEquals(newObjectMapper().writeValueAsString(bean), + revived.writeValueAsString(bean)); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecCreatorTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecCreatorTest.java new file mode 100644 index 000000000..53294a34d --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecCreatorTest.java @@ -0,0 +1,101 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +// The value instantiator's default creator is not always the no-arg +// constructor: a no-arg @JsonCreator factory takes its place, and databind +// then constructs through the factory. Generated POJO codecs construct +// through the stock instantiator in that case (and still engage); record +// codecs construct through the canonical constructor only, so a record with +// a factory creator stays on the stock path. +public class BBCodecCreatorTest extends BlackbirdTestBase +{ + public static class FactoryBean { + public String name; + public boolean viaFactory; + + public FactoryBean() { } + + @JsonCreator + public static FactoryBean create() { + FactoryBean bean = new FactoryBean(); + bean.viaFactory = true; + return bean; + } + } + + public record FactoryRec(String name, int count) { + @JsonCreator + public static FactoryRec create(@JsonProperty("name") String name, + @JsonProperty("count") int count) { + return new FactoryRec(name + "!", count); + } + } + + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + private static ObjectMapper capturingMapper(Map, ValueDeserializer> seen) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + seen.put(ref.getBeanClass(), d); + return d; + } + }); + } + }; + return JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()) + .build(); + } + + @Test + public void testRecordFactoryCreatorIsUsed() throws Exception { + FactoryRec expected = vanilla.readValue("{\"name\":\"a\",\"count\":2}", FactoryRec.class); + + FactoryRec actual = newBlackbirdMapper().readValue("{\"name\":\"a\",\"count\":2}", FactoryRec.class); + assertEquals(expected, actual, + "record construction path differs from stock (creator is the factory)"); + } + + @Test + public void testNoArgFactoryCreatorIsUsedAndEngages() throws Exception { + FactoryBean expected = vanilla.readValue("{\"name\":\"a\"}", FactoryBean.class); + + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + FactoryBean actual = mapper.readValue("{\"name\":\"a\"}", FactoryBean.class); + assertTrue(hasGeneratedCodec(seen.get(FactoryBean.class)), + "factory-creator bean did not engage a codec"); + assertEquals("a", actual.name); + assertEquals(expected.viaFactory, actual.viaFactory, + "construction path differs from stock (default creator is the factory)"); + assertTrue(actual.viaFactory); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecDemotionTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecDemotionTest.java new file mode 100644 index 000000000..3185aa8ee --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecDemotionTest.java @@ -0,0 +1,218 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonMerge; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.InjectableValues; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; +import tools.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Documented demotions: beans whose features the generator does not model + * (object ids, per-property merge, injectables on records) stay on the stock + * deserializer and behave exactly like vanilla. These pins keep the demotions + * deliberate; if the generator grows support, the engagement assertion flips + * - as it did for any-setter beans and injectable POJOs, whose engagement + * pins live here too. + */ +public class BBCodecDemotionTest extends BlackbirdTestBase +{ + @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) + public static class IdBean { + public int value; + public IdBean next; + } + + public static class MergeTarget { + public int a = 1; + public int b = 2; + } + + public static class MergeBean { + @JsonMerge + public MergeTarget child = new MergeTarget(); + } + + public static class AnySetterBean { + public int known; + public Map rest = new LinkedHashMap<>(); + + @JsonAnySetter + public void any(String name, Object value) { + rest.put(name, value); + } + } + + public static class InjectBean { + public int i; + @JacksonInject("who") + public String who; + } + + public static class InjectOverrideBean { + public int i; + @JacksonInject("who") + public String who; + @JacksonInject("count") + public int count; + } + + public record InjectRecord(String name, @JacksonInject("who") String who) { } + + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + private static ObjectMapper capturingMapper(Map, ValueDeserializer> seen) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + seen.put(ref.getBeanClass(), d); + return d; + } + }); + } + }; + return JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()) + .build(); + } + + // Stock comes in two shapes: the modifier demoted (raw stock captured), + // or the factory gated at resolve time (placeholder captured, codec never + // generated). Both mean every call runs the stock deserializer. + private static void assertStock(Map, ValueDeserializer> seen, Class cls) + throws Exception { + ValueDeserializer d = seen.get(cls); + assertNotNull(d, "no deserializer captured for " + cls); + if (d instanceof BBReaderPlaceholder placeholder) { + java.lang.reflect.Field f = BBReaderPlaceholder.class.getDeclaredField("_codec"); + f.setAccessible(true); + assertNull(f.get(placeholder), + cls.getSimpleName() + " unexpectedly generated a codec; if support" + + " was added deliberately, flip this pin to assert engagement"); + } + } + + @Test + public void testObjectIdBeanStaysStock() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'@id':1,'value':5,'next':1}"); + IdBean v = vanilla.readValue(doc, IdBean.class); + IdBean m = mapper.readValue(doc, IdBean.class); + assertStock(seen, IdBean.class); + assertEquals(v.value, m.value); + assertSame(m, m.next); + } + + @Test + public void testMergePropertyBeanStaysStock() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'child':{'a':9}}"); + MergeBean v = vanilla.readValue(doc, MergeBean.class); + MergeBean m = mapper.readValue(doc, MergeBean.class); + assertStock(seen, MergeBean.class); + assertEquals(v.child.a, m.child.a); + assertEquals(v.child.b, m.child.b); + assertEquals(2, m.child.b); + } + + @Test + public void testAnySetterBeanEngages() throws Exception { + // Widened from a demotion pin: the unknown arm feeds the stock + // any-setter, so these beans accelerate (BBCodecAnySetterTest has the + // full parity matrix; records with an any-setter still demote). + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'known':1,'x':'a','y':2}"); + AnySetterBean v = vanilla.readValue(doc, AnySetterBean.class); + AnySetterBean m = mapper.readValue(doc, AnySetterBean.class); + assertTrue(hasGeneratedCodec(seen.get(AnySetterBean.class)), + "any-setter bean did not engage a codec"); + assertEquals(v.known, m.known); + assertEquals(v.rest, m.rest); + assertEquals(2, m.rest.size()); + } + + @Test + public void testInjectableBeanEngages() throws Exception { + // Widened from a demotion pin: injected values apply through the base + // helper right after construction, the stock placement. + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper base = capturingMapper(seen); + InjectableValues inject = + new InjectableValues.Std().addValue("who", "injected"); + String doc = a2q("{'i':7}"); + InjectBean v = vanilla.reader(inject).forType(InjectBean.class).readValue(doc); + InjectBean m = base.reader(inject).forType(InjectBean.class).readValue(doc); + assertTrue(hasGeneratedCodec(seen.get(InjectBean.class)), + "injectable bean did not engage a codec"); + assertEquals(v.i, m.i); + assertEquals(v.who, m.who); + assertEquals("injected", m.who); + } + + @Test + public void testDocumentOverridesInjectedValue() throws Exception { + // Injection runs before the property loop, so a document value wins - + // exactly like stock. + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper base = capturingMapper(seen); + InjectableValues inject = new InjectableValues.Std() + .addValue("who", "injected").addValue("count", 41); + String doc = a2q("{'i':7,'who':'from-doc'}"); + InjectOverrideBean v = vanilla.reader(inject) + .forType(InjectOverrideBean.class).readValue(doc); + InjectOverrideBean m = base.reader(inject) + .forType(InjectOverrideBean.class).readValue(doc); + assertTrue(hasGeneratedCodec(seen.get(InjectOverrideBean.class)), + "injectable bean did not engage a codec"); + assertEquals(v.who, m.who); + assertEquals("from-doc", m.who); + assertEquals(v.count, m.count); + assertEquals(41, m.count); + } + + @Test + public void testInjectableRecordStaysStock() throws Exception { + // Record codecs have no instance until the end of the document, so + // injection cannot take its stock place; records with injectables + // demote. + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper base = capturingMapper(seen); + InjectableValues inject = + new InjectableValues.Std().addValue("who", "injected"); + String doc = a2q("{'name':'a'}"); + InjectRecord v = vanilla.reader(inject).forType(InjectRecord.class).readValue(doc); + InjectRecord m = base.reader(inject).forType(InjectRecord.class).readValue(doc); + assertStock(seen, InjectRecord.class); + assertEquals(v, m); + assertEquals("injected", m.who()); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecEngagementTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecEngagementTest.java new file mode 100644 index 000000000..a7534cab7 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecEngagementTest.java @@ -0,0 +1,225 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.exc.MismatchedInputException; + +import tools.jackson.module.blackbird.BlackbirdTestBase; +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.annotation.JsonPOJOBuilder; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; + +import static org.junit.jupiter.api.Assertions.*; + +public class BBCodecEngagementTest extends BlackbirdTestBase +{ + public static class PublicBean { + private String name; + private int count; + private long total; + private boolean active; + private List tags; + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public int getCount() { return count; } + public void setCount(int count) { this.count = count; } + public long getTotal() { return total; } + public void setTotal(long total) { this.total = total; } + public boolean isActive() { return active; } + public void setActive(boolean active) { this.active = active; } + public List getTags() { return tags; } + public void setTags(List tags) { this.tags = tags; } + } + + public record PublicRec(String name, int count, long total, boolean active, List tags) {} + + @JsonDeserialize(builder = BuilderBean.Builder.class) + public static class BuilderBean { + private final String name; + private final int count; + private final List tags; + + private BuilderBean(String name, int count, List tags) { + this.name = name; + this.count = count; + this.tags = tags; + } + + public String getName() { return name; } + public int getCount() { return count; } + public List getTags() { return tags; } + + @JsonPOJOBuilder(withPrefix = "") + public static class Builder { + private String name; + private int count; + private List tags; + + public Builder name(String name) { this.name = name; return this; } + public Builder count(int count) { this.count = count; return this; } + public Builder tags(List tags) { this.tags = tags; return this; } + public BuilderBean build() { return new BuilderBean(name, count, tags); } + } + } + + public static class PublicParent { + private String label; + private PublicBean child; + private PublicRec rec; + + public String getLabel() { return label; } + public void setLabel(String label) { this.label = label; } + public PublicBean getChild() { return child; } + public void setChild(PublicBean child) { this.child = child; } + public PublicRec getRec() { return rec; } + public void setRec(PublicRec rec) { this.rec = rec; } + } + + private final ObjectMapper MAPPER = newObjectMapper(); + + @Test + public void testNestedChildCodecs() throws Exception { + String doc = "{\"label\":\"p\",\"child\":{\"name\":\"c\",\"count\":2}," + + "\"rec\":{\"name\":\"r\",\"count\":5,\"total\":6,\"active\":true,\"tags\":[]}}"; + PublicParent parent = MAPPER.readValue(doc, PublicParent.class); + assertEquals("p", parent.getLabel()); + assertEquals("c", parent.getChild().getName()); + assertEquals(2, parent.getChild().getCount()); + assertEquals(new PublicRec("r", 5, 6L, true, List.of()), parent.getRec()); + + PublicParent nulls = MAPPER.readValue("{\"child\":null,\"rec\":null}", PublicParent.class); + assertNull(nulls.getChild()); + assertNull(nulls.getRec()); + + PublicParent vanillaParent = newVanillaJSONMapper().readValue(doc, PublicParent.class); + assertEquals(vanillaParent.getRec(), parent.getRec()); + assertEquals(vanillaParent.getChild().getName(), parent.getChild().getName()); + } + + @Test + public void testGeneratedRecordCodecValues() throws Exception { + PublicRec rec = MAPPER.readValue( + "{\"name\":\"a\",\"count\":3,\"total\":9000000000,\"active\":true,\"tags\":[\"x\"]}", + PublicRec.class); + assertEquals(new PublicRec("a", 3, 9000000000L, true, List.of("x")), rec); + + PublicRec reversed = MAPPER.readValue( + "{\"tags\":[\"x\"],\"active\":true,\"total\":9000000000,\"count\":3,\"name\":\"a\"}", + PublicRec.class); + assertEquals(rec, reversed); + } + + @Test + public void testGeneratedRecordCodecNullsUnknownsMissing() throws Exception { + ObjectMapper vanilla = newVanillaJSONMapper(); + String doc = "{\"name\":null,\"junk\":{\"a\":[1]},\"count\":3}"; + assertEquals(vanilla.readValue(doc, PublicRec.class), + MAPPER.readValue(doc, PublicRec.class)); + String primNull = "{\"count\":null,\"name\":\"n\"}"; + assertThrows(MismatchedInputException.class, + () -> vanilla.readValue(primNull, PublicRec.class)); + assertThrows(MismatchedInputException.class, + () -> MAPPER.readValue(primNull, PublicRec.class)); + } + + @Test + public void testGeneratedBuilderCodecValues() throws Exception { + String doc = "{\"name\":\"b\",\"junk\":[{}],\"count\":7,\"tags\":[\"t\"]}"; + BuilderBean bean = MAPPER.readValue(doc, BuilderBean.class); + assertEquals("b", bean.getName()); + assertEquals(7, bean.getCount()); + assertEquals(List.of("t"), bean.getTags()); + BuilderBean vanillaBean = newVanillaJSONMapper().readValue(doc, BuilderBean.class); + assertEquals(vanillaBean.getName(), bean.getName()); + assertEquals(vanillaBean.getCount(), bean.getCount()); + assertEquals(vanillaBean.getTags(), bean.getTags()); + } + + // A non-object where a bean is expected reports MismatchedInputException + // from the generated codecs, same as stock. + @Test + public void testOddTokenMatchesStock() throws Exception { + for (Class type : new Class[] { + PublicBean.class, PublicRec.class, BuilderBean.class }) { + assertThrows(MismatchedInputException.class, + () -> MAPPER.readValue("[1]", type)); + assertThrows(MismatchedInputException.class, + () -> newVanillaJSONMapper().readValue("[1]", type)); + } + } + + @Test + public void testEngineEngagesForAllShapes() throws Exception { + java.util.Map, tools.jackson.databind.ValueDeserializer> seen = + new java.util.concurrent.ConcurrentHashMap<>(); + tools.jackson.databind.deser.ValueDeserializerModifier capture = + new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + + @Override + public tools.jackson.databind.ValueDeserializer modifyDeserializer( + tools.jackson.databind.DeserializationConfig config, + BeanDescription.Supplier beanDescRef, + tools.jackson.databind.ValueDeserializer deserializer) { + seen.put(beanDescRef.getBeanClass(), deserializer); + return deserializer; + } + }; + ObjectMapper m = JsonMapper.builder() + .addModule(new SimpleModule("capture") + .setDeserializerModifier(capture)) + .addModule(new BlackbirdModule()) + .build(); + m.readValue("{\"name\":\"a\"}", PublicBean.class); + m.readValue("{\"name\":\"a\"}", PublicRec.class); + m.readValue("{\"name\":\"a\"}", BuilderBean.class); + assertEquals("BBReaderPlaceholder", + seen.get(PublicBean.class).getClass().getSimpleName()); + assertEquals("BBReaderPlaceholder", + seen.get(PublicRec.class).getClass().getSimpleName()); + // Builder flow keys the modifier by the builder class. + assertEquals("BBBuilderPlaceholder", + seen.get(BuilderBean.Builder.class).getClass().getSimpleName()); + // The placeholder only stands in until resolution; the generated + // codec is what the mapper then uses. + assertGeneratedReader(m, PublicBean.class); + assertGeneratedReader(m, PublicRec.class); + assertGeneratedReader(m, BuilderBean.class); + } + + @Test + public void testGeneratedCodecValues() throws Exception { + PublicBean bean = MAPPER.readValue( + "{\"name\":\"a\",\"count\":3,\"total\":9000000000,\"active\":true,\"tags\":[\"x\",\"y\"]}", + PublicBean.class); + assertEquals("a", bean.getName()); + assertEquals(3, bean.getCount()); + assertEquals(9000000000L, bean.getTotal()); + assertTrue(bean.isActive()); + assertEquals(List.of("x", "y"), bean.getTags()); + } + + @Test + public void testGeneratedCodecNullsAndUnknowns() throws Exception { + PublicBean bean = MAPPER.readValue( + "{\"name\":null,\"junk\":{\"deep\":[1,2]},\"active\":false}", + PublicBean.class); + assertNull(bean.getName()); + assertEquals(0, bean.getCount()); + assertFalse(bean.isActive()); + assertNull(bean.getTags()); + assertThrows(MismatchedInputException.class, + () -> MAPPER.readValue("{\"count\":null}", PublicBean.class)); + assertThrows(MismatchedInputException.class, + () -> newVanillaJSONMapper().readValue("{\"count\":null}", PublicBean.class)); + } + +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecFieldTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecFieldTest.java new file mode 100644 index 000000000..a4e1bc825 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecFieldTest.java @@ -0,0 +1,195 @@ +package tools.jackson.module.blackbird.deser; + +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; + +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +// Field-backed properties: the generator stores through putfield / reads +// through getfield for public fields, and reaches a non-public field through a +// lookup-derived handle. Behavior must match stock databind on every path. +public class BBCodecFieldTest extends BlackbirdTestBase +{ + @JsonPropertyOrder({ "name", "count", "total", "active", "tags", "child" }) + public static class FieldBean { + public String name; + public int count; + public long total; + public boolean active; + public List tags; + public FieldChild child; + } + + public static class FieldChild { + public String label; + } + + public static class FinalFieldBean { + public final String name; + public int count; + + public FinalFieldBean() { + this("", 0); + } + + @JsonCreator + public FinalFieldBean( + @JsonProperty("name") String name, + @JsonProperty("count") int count) { + this.name = name; + this.count = count; + } + } + + public static class MixedBean { + public String field; + private int viaSetter; + + public int getViaSetter() { return viaSetter; } + public void setViaSetter(int v) { viaSetter = v; } + } + + public static class PrivateFieldBean { + @JsonProperty("name") + private String name; + @JsonProperty("count") + private int count; + + public String name() { return name; } + public int count() { return count; } + } + + private final ObjectMapper MAPPER = newObjectMapper(); + private final ObjectMapper VANILLA = newVanillaJSONMapper(); + + @Test + public void testPublicFieldValues() throws Exception { + String json = a2q("{'name':'a','count':3,'total':9000000000,'active':true," + + "'tags':['x','y'],'child':{'label':'c'}}"); + FieldBean bean = MAPPER.readValue(json, FieldBean.class); + assertEquals("a", bean.name); + assertEquals(3, bean.count); + assertEquals(9000000000L, bean.total); + assertTrue(bean.active); + assertEquals(List.of("x", "y"), bean.tags); + assertEquals("c", bean.child.label); + } + + @Test + public void testPublicFieldReversedOrder() throws Exception { + String json = a2q("{'child':{'label':'c'},'tags':['x'],'active':true," + + "'total':7,'count':2,'name':'a'}"); + FieldBean bean = MAPPER.readValue(json, FieldBean.class); + FieldBean vanilla = VANILLA.readValue(json, FieldBean.class); + assertEquals(vanilla.name, bean.name); + assertEquals(vanilla.count, bean.count); + assertEquals(vanilla.child.label, bean.child.label); + } + + @Test + public void testPublicFieldNullsAndUnknowns() throws Exception { + String json = a2q("{'name':null,'extra':{'deep':1},'count':5," + + "'tags':null,'child':null}"); + FieldBean bean = MAPPER.readValue(json, FieldBean.class); + assertNull(bean.name); + assertEquals(5, bean.count); + assertNull(bean.tags); + assertNull(bean.child); + } + + @Test + public void testNullToPrimitiveFieldThrows() { + assertThrows(MismatchedInputException.class, + () -> MAPPER.readValue(a2q("{'count':null}"), FieldBean.class)); + } + + @Test + public void testPublicFieldRoundTripMatchesVanilla() throws Exception { + FieldBean bean = MAPPER.readValue(a2q("{'name':'a','count':3,'total':7," + + "'active':true,'tags':['x','y'],'child':{'label':'c'}}"), FieldBean.class); + assertEquals(VANILLA.writeValueAsString(bean), MAPPER.writeValueAsString(bean)); + } + + @Test + public void testFinalFieldMatchesVanilla() throws Exception { + String json = a2q("{'name':'a','count':3}"); + FinalFieldBean bean = MAPPER.readValue(json, FinalFieldBean.class); + assertEquals("a", bean.name); + assertEquals(3, bean.count); + assertEquals(VANILLA.writeValueAsString(bean), MAPPER.writeValueAsString(bean)); + } + + @Test + public void testMixedFieldAndSetter() throws Exception { + MixedBean bean = MAPPER.readValue(a2q("{'field':'a','viaSetter':7}"), MixedBean.class); + assertEquals("a", bean.field); + assertEquals(7, bean.getViaSetter()); + assertEquals(VANILLA.writeValueAsString(bean), MAPPER.writeValueAsString(bean)); + } + + @Test + public void testPrivateFieldWithLookupAccelerates() throws Exception { + Supplier here = MethodHandles::lookup; + ObjectMapper m = JsonMapper.builder() + .addModule(new BlackbirdModule() { + @Override + protected Supplier findLookupSupplier() { + return here; + } + }) + .build(); + PrivateFieldBean bean = m.readValue(a2q("{'name':'a','count':3}"), PrivateFieldBean.class); + assertEquals("a", bean.name()); + assertEquals(3, bean.count()); + } + + @Test + public void testPrivateFieldWithoutLookupStillWorks() throws Exception { + // The default lookup cannot reach the private field, so the property + // demotes to the stock path; the value must still round-trip. + PrivateFieldBean bean = MAPPER.readValue(a2q("{'name':'a','count':3}"), + PrivateFieldBean.class); + assertEquals("a", bean.name()); + assertEquals(3, bean.count()); + } + + @Test + public void testFieldBeanEngagesCodec() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ValueDeserializerModifier capture = new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig config, + BeanDescription.Supplier ref, ValueDeserializer deser) { + seen.put(ref.getBeanClass(), deser); + return deser; + } + }; + ObjectMapper m = JsonMapper.builder() + .addModule(new SimpleModule("capture").setDeserializerModifier(capture)) + .addModule(new BlackbirdModule()) + .build(); + m.readValue(a2q("{'name':'a'}"), FieldBean.class); + assertTrue(hasGeneratedCodec(seen.get(FieldBean.class))); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecIgnoralsTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecIgnoralsTest.java new file mode 100644 index 000000000..039c49f5f --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecIgnoralsTest.java @@ -0,0 +1,198 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonIncludeProperties; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.IgnoredPropertyException; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; +import tools.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Ignoral parity: beans with {@code @JsonIgnoreProperties}, + * {@code @JsonIncludeProperties}, or {@code @JsonIgnore} now engage codecs, + * and the unknown arm consults the ignoral sets in the stock loop's exact + * order. Every case compares against vanilla. + */ +public class BBCodecIgnoralsTest extends BlackbirdTestBase +{ + @JsonIgnoreProperties(ignoreUnknown = true) + public static class LenientBean { + public int i; + public String s; + } + + @JsonIgnoreProperties({ "secret" }) + public static class IgnoredSetBean { + public int i; + public String s; + } + + @JsonIncludeProperties({ "i" }) + public static class IncludedSetBean { + public int i; + public String s; + } + + public static class JsonIgnoreBean { + public int i; + @JsonIgnore + public String hidden; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public record LenientRecord(String name, int count) { } + + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + private static String message(Exception e) { + String m = e.getMessage(); + int nl = m.indexOf('\n'); + return (nl < 0) ? m : m.substring(0, nl); + } + + private static ObjectMapper capturingMapper(Map, ValueDeserializer> seen) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + seen.put(ref.getBeanClass(), d); + return d; + } + }); + } + }; + return JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()) + .build(); + } + + private static void assertEngaged(Map, ValueDeserializer> seen, Class cls) { + ValueDeserializer d = seen.get(cls); + assertNotNull(d, "no deserializer captured for " + cls); + assertTrue(hasGeneratedCodec(d), + cls.getSimpleName() + " did not engage a codec"); + } + + @Test + public void testIgnoreUnknownSkips() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'nope':{'deep':[1,2]},'s':'x','more':true}"); + LenientBean v = vanilla.readValue(doc, LenientBean.class); + LenientBean m = mapper.readValue(doc, LenientBean.class); + assertEngaged(seen, LenientBean.class); + assertEquals(v.i, m.i); + assertEquals(v.s, m.s); + } + + @Test + public void testIgnoreUnknownBeatsStrictPerCall() throws Exception { + // Stock skips silently under ignoreUnknown even when + // FAIL_ON_UNKNOWN_PROPERTIES is enabled per call. + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'nope':true}"); + LenientBean v = vanilla.reader(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .forType(LenientBean.class).readValue(doc); + LenientBean m = mapper.reader(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .forType(LenientBean.class).readValue(doc); + assertEngaged(seen, LenientBean.class); + assertEquals(v.i, m.i); + } + + @Test + public void testIgnoredSetSkipsAndFailOnIgnored() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'secret':'boo','s':'x'}"); + IgnoredSetBean v = vanilla.readValue(doc, IgnoredSetBean.class); + IgnoredSetBean m = mapper.readValue(doc, IgnoredSetBean.class); + assertEngaged(seen, IgnoredSetBean.class); + assertEquals(v.i, m.i); + assertEquals(v.s, m.s); + + IgnoredPropertyException ve = assertThrows(IgnoredPropertyException.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES) + .forType(IgnoredSetBean.class).readValue(doc)); + IgnoredPropertyException me = assertThrows(IgnoredPropertyException.class, + () -> mapper.reader(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES) + .forType(IgnoredSetBean.class).readValue(doc)); + assertEquals(message(ve), message(me)); + } + + @Test + public void testIncludedSetIgnoresOthers() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'s':'x'}"); + IncludedSetBean v = vanilla.readValue(doc, IncludedSetBean.class); + IncludedSetBean m = mapper.readValue(doc, IncludedSetBean.class); + assertEngaged(seen, IncludedSetBean.class); + assertEquals(v.i, m.i); + assertEquals(v.s, m.s); + assertNull(m.s); + } + + @Test + public void testJsonIgnoredNameInDocument() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'hidden':'boo'}"); + JsonIgnoreBean v = vanilla.readValue(doc, JsonIgnoreBean.class); + JsonIgnoreBean m = mapper.readValue(doc, JsonIgnoreBean.class); + assertEngaged(seen, JsonIgnoreBean.class); + assertEquals(v.i, m.i); + assertNull(m.hidden); + } + + @Test + public void testUnknownStillReportedWithIgnoredSet() throws Exception { + // An unknown name outside the ignored set still runs the stock + // unknown handling (throws under per-call FAIL_ON_UNKNOWN). + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'i':1,'other':true}"); + Exception ve = assertThrows(Exception.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .forType(IgnoredSetBean.class).readValue(doc)); + Exception me = assertThrows(Exception.class, + () -> mapper.reader(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .forType(IgnoredSetBean.class).readValue(doc)); + assertEngaged(seen, IgnoredSetBean.class); + assertEquals(ve.getClass(), me.getClass()); + assertEquals(message(ve), message(me)); + } + + @Test + public void testIgnoreUnknownRecord() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + String doc = a2q("{'name':'a','junk':[1,{'x':2}],'count':3}"); + assertEquals(vanilla.readValue(doc, LenientRecord.class), + mapper.readValue(doc, LenientRecord.class)); + assertEngaged(seen, LenientRecord.class); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecMatcherTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecMatcherTest.java new file mode 100644 index 000000000..b2fd036d7 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecMatcherTest.java @@ -0,0 +1,171 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; +import tools.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Name-matching parity: case-insensitive mappers use the core CI matcher + * instead of demoting, and property aliases map to the same generated arm the + * way stock BeanPropertyMap appends them. Every case compares against vanilla. + */ +public class BBCodecMatcherTest extends BlackbirdTestBase +{ + public static class AliasBean { + @JsonAlias({ "identifier", "the_id" }) + public int id; + public String name; + } + + public record AliasRecord(@JsonAlias("nm") @JsonProperty(required = true) String name, + int count) { } + + public static class PlainBean { + public int count; + public String label; + } + + @JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) + public static class FormatCIBean { + public int count; + public String label; + } + + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + private static ObjectMapper capturingMapper(Map, ValueDeserializer> seen, + boolean caseInsensitive) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + seen.put(ref.getBeanClass(), d); + return d; + } + }); + } + }; + JsonMapper.Builder b = + JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()); + if (caseInsensitive) { + b = b.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); + } + return b.build(); + } + + private static void assertEngaged(Map, ValueDeserializer> seen, Class cls) { + ValueDeserializer d = seen.get(cls); + assertNotNull(d, "no deserializer captured for " + cls); + assertTrue(hasGeneratedCodec(d), + cls.getSimpleName() + " did not engage a codec"); + } + + @Test + public void testCaseInsensitiveMapper() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen, true); + ObjectMapper vci = JsonMapper.builder() + .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) + .build(); + String doc = a2q("{'COUNT':7,'Label':'x'}"); + PlainBean v = vci.readValue(doc, PlainBean.class); + PlainBean m = mapper.readValue(doc, PlainBean.class); + assertEngaged(seen, PlainBean.class); + assertEquals(v.count, m.count); + assertEquals(v.label, m.label); + assertEquals(7, m.count); + } + + @Test + public void testCaseInsensitiveUnknownStillUnknown() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen, true); + String doc = a2q("{'COUNT':7,'zzz':1}"); + PlainBean m = mapper.readValue(doc, PlainBean.class); + assertEquals(7, m.count); + assertEngaged(seen, PlainBean.class); + } + + @Test + public void testAliasNamesHitTheSameArm() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen, false); + for (String key : new String[] { "id", "identifier", "the_id" }) { + String doc = a2q("{'" + key + "':42,'name':'x'}"); + AliasBean v = vanilla.readValue(doc, AliasBean.class); + AliasBean m = mapper.readValue(doc, AliasBean.class); + assertEquals(v.id, m.id, "via " + key); + assertEquals(42, m.id, "via " + key); + assertEquals("x", m.name); + } + assertEngaged(seen, AliasBean.class); + } + + @Test + public void testAliasSatisfiesRequiredRecordComponent() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen, false); + String doc = a2q("{'nm':'a','count':3}"); + assertEquals(vanilla.readValue(doc, AliasRecord.class), + mapper.readValue(doc, AliasRecord.class)); + assertEngaged(seen, AliasRecord.class); + + assertThrows(MismatchedInputException.class, + () -> mapper.readValue(a2q("{'count':3}"), AliasRecord.class)); + } + + @Test + public void testAliasUnderCaseInsensitiveMapper() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen, true); + ObjectMapper vci = JsonMapper.builder() + .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) + .build(); + String doc = a2q("{'IDENTIFIER':42,'NAME':'x'}"); + AliasBean v = vci.readValue(doc, AliasBean.class); + AliasBean m = mapper.readValue(doc, AliasBean.class); + assertEngaged(seen, AliasBean.class); + assertEquals(v.id, m.id); + assertEquals(v.name, m.name); + } + + @Test + public void testClassLevelFormatCaseInsensitivity() throws Exception { + // Class-level @JsonFormat CI arrives through createContextual; the + // placeholder falls back to the stock contextual instance, so behavior + // must match vanilla whichever path serves it. + ObjectMapper mapper = newObjectMapper(); + String doc = a2q("{'CoUnT':5,'LABEL':'y'}"); + FormatCIBean v = vanilla.readValue(doc, FormatCIBean.class); + FormatCIBean m = mapper.readValue(doc, FormatCIBean.class); + assertEquals(v.count, m.count); + assertEquals(v.label, m.label); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecStrictFeaturesTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecStrictFeaturesTest.java new file mode 100644 index 000000000..a9e89e2b5 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecStrictFeaturesTest.java @@ -0,0 +1,212 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.exc.UnrecognizedPropertyException; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; +import tools.jackson.databind.json.JsonMapper; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Strict-feature parity with vanilla, both per-call and enabled at build: + * FAIL_ON_UNKNOWN_PROPERTIES, FAIL_ON_MISSING_CREATOR_PROPERTIES, and + * FAIL_ON_NULL_CREATOR_PROPERTIES. Per-call cases warm the codec with a + * default read first, so the strict read runs through the cached generated + * codec rather than deciding generation. Every case compares against vanilla. + */ +public class BBCodecStrictFeaturesTest extends BlackbirdTestBase +{ + public static class SimpleBean { + public int i; + public String s; + } + + public record RefRecord(String name, int count) { } + + public record ReqRecord(@JsonProperty(required = true) String name, int count) { } + + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + private static String message(Exception e) { + String m = e.getMessage(); + int nl = m.indexOf('\n'); + return (nl < 0) ? m : m.substring(0, nl); + } + + /** Blackbird mapper with a capture modifier, so engagement is assertable. */ + private static ObjectMapper capturingMapper(Map, ValueDeserializer> seen, + DeserializationFeature... enable) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + seen.put(ref.getBeanClass(), d); + return d; + } + }); + } + }; + JsonMapper.Builder b = + JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()); + for (DeserializationFeature f : enable) { + b = b.enable(f); + } + return b.build(); + } + + private static void assertEngaged(Map, ValueDeserializer> seen, Class cls) { + ValueDeserializer d = seen.get(cls); + assertNotNull(d, "no deserializer captured for " + cls); + assertTrue(hasGeneratedCodec(d), + cls.getSimpleName() + " did not engage a codec"); + } + + // FAIL_ON_UNKNOWN_PROPERTIES enabled at build time: the bean still + // engages a codec, and the unknown property throws exactly like vanilla. + + @Test + public void testBuildTimeFailOnUnknown() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper strict = capturingMapper(seen, + DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + ObjectMapper vstrict = JsonMapper.builder() + .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .build(); + + SimpleBean ok = strict.readValue(a2q("{'i':1,'s':'x'}"), SimpleBean.class); + assertEquals(1, ok.i); + assertEngaged(seen, SimpleBean.class); + + String doc = a2q("{'i':1,'nope':true,'s':'x'}"); + UnrecognizedPropertyException ve = assertThrows(UnrecognizedPropertyException.class, + () -> vstrict.readValue(doc, SimpleBean.class)); + UnrecognizedPropertyException me = assertThrows(UnrecognizedPropertyException.class, + () -> strict.readValue(doc, SimpleBean.class)); + assertEquals(message(ve), message(me)); + assertEquals(ve.getPath().toString(), me.getPath().toString()); + } + + // FAIL_ON_MISSING_CREATOR_PROPERTIES + + @Test + public void testBuildTimeFailOnMissingCreator() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper strict = capturingMapper(seen, + DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES); + String doc = a2q("{'name':'x'}"); + MismatchedInputException ve = assertThrows(MismatchedInputException.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + MismatchedInputException me = assertThrows(MismatchedInputException.class, + () -> strict.readValue(doc, RefRecord.class)); + assertEquals(ve.getClass(), me.getClass()); + assertEngaged(seen, RefRecord.class); + } + + @Test + public void testPerCallFailOnMissingCreator() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + // Warm: the codec generates under default config and stays cached. + assertEquals(new RefRecord("a", 1), + mapper.readValue(a2q("{'name':'a','count':1}"), RefRecord.class)); + assertEngaged(seen, RefRecord.class); + + String doc = a2q("{'name':'x'}"); + MismatchedInputException ve = assertThrows(MismatchedInputException.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + MismatchedInputException me = assertThrows(MismatchedInputException.class, + () -> mapper.reader(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + assertEquals(ve.getClass(), me.getClass()); + } + + // FAIL_ON_NULL_CREATOR_PROPERTIES + + @Test + public void testPerCallFailOnNullCreatorExplicitNull() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + assertEquals(new RefRecord("a", 1), + mapper.readValue(a2q("{'name':'a','count':1}"), RefRecord.class)); + assertEngaged(seen, RefRecord.class); + + String doc = a2q("{'name':null,'count':3}"); + MismatchedInputException ve = assertThrows(MismatchedInputException.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + MismatchedInputException me = assertThrows(MismatchedInputException.class, + () -> mapper.reader(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + assertEquals(message(ve), message(me)); + } + + @Test + public void testPerCallFailOnNullCreatorMissingComponent() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + assertEquals(new RefRecord("a", 1), + mapper.readValue(a2q("{'name':'a','count':1}"), RefRecord.class)); + assertEngaged(seen, RefRecord.class); + + String doc = a2q("{'count':3}"); + MismatchedInputException ve = assertThrows(MismatchedInputException.class, + () -> vanilla.reader(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + MismatchedInputException me = assertThrows(MismatchedInputException.class, + () -> mapper.reader(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES) + .forType(RefRecord.class).readValue(doc)); + assertEquals(message(ve), message(me)); + } + + @Test + public void testFailOnNullCreatorOffMatchesVanilla() throws Exception { + ObjectMapper mapper = newObjectMapper(); + String doc = a2q("{'name':null,'count':3}"); + assertEquals(vanilla.readValue(doc, RefRecord.class), + mapper.readValue(doc, RefRecord.class)); + } + + @Test + public void testBuildTimeFailOnNullCreator() throws Exception { + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ObjectMapper strict = capturingMapper(seen, + DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES); + assertThrows(MismatchedInputException.class, + () -> strict.readValue(a2q("{'name':null,'count':3}"), RefRecord.class)); + assertEngaged(seen, RefRecord.class); + } + + // Required stays enforced regardless of features + + @Test + public void testRequiredComponentStillEnforced() throws Exception { + ObjectMapper mapper = newObjectMapper(); + assertThrows(MismatchedInputException.class, + () -> mapper.readValue(a2q("{'count':3}"), ReqRecord.class)); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecViewTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecViewTest.java new file mode 100644 index 000000000..7b8d418aa --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecViewTest.java @@ -0,0 +1,277 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonView; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.MapperFeature; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * View-parity regression tests: with an active view the generated codecs stay + * on the fast path and test a per-view visibility bitmask per property + * (visibility read from the stock property, so DEFAULT_VIEW_INCLUSION and + * matcher semantics are exactly stock); a hidden property consumes its value + * or is omitted like stock. Beans with more than 64 properties instead + * delegate the whole view-active call to the stock fallback. Every case + * compares against a vanilla mapper, in both directions, with and without an + * active view, so view semantics come from stock rather than hardcoded + * expectations. + */ +public class BBCodecViewTest extends BlackbirdTestBase +{ + public static class ViewA { } + public static class ViewB { } + + public static class ViewBean { + private String a; + private int b; + private String plain; + + @JsonView(ViewA.class) + public String getA() { return a; } + public void setA(String v) { a = v; } + + @JsonView(ViewB.class) + public int getB() { return b; } + public void setB(int v) { b = v; } + + public String getPlain() { return plain; } + public void setPlain(String v) { plain = v; } + } + + public static class ViewFieldBean { + @JsonView(ViewA.class) + public String a; + @JsonView(ViewB.class) + public int b; + } + + public record ViewRecord( + @JsonView(ViewA.class) String a, + @JsonView(ViewB.class) int b) { } + + public static class Parent { + private ViewBean child; + // Visible in both views, so the child itself deserializes and its own + // per-property view filtering is what the test exercises. + @JsonView({ ViewA.class, ViewB.class }) + public ViewBean getChild() { return child; } + public void setChild(ViewBean c) { child = c; } + } + + private static final ObjectMapper VANILLA = new JsonMapper(); + private static final ObjectMapper MODULE = newObjectMapper(); + + private static final String DOC = "{\"a\":\"x\",\"b\":7,\"plain\":\"p\"}"; + + private static ViewBean bean() { + ViewBean bean = new ViewBean(); + bean.setA("x"); + bean.setB(7); + bean.setPlain("p"); + return bean; + } + + @Test + public void testSerializeMatchesVanillaPerView() throws Exception { + for (Class view : new Class[] { ViewA.class, ViewB.class }) { + assertEquals(VANILLA.writerWithView(view).writeValueAsString(bean()), + MODULE.writerWithView(view).writeValueAsString(bean()), + "view " + view.getSimpleName()); + } + assertEquals(VANILLA.writeValueAsString(bean()), MODULE.writeValueAsString(bean()), + "no active view"); + } + + @Test + public void testDeserializeMatchesVanillaPerView() throws Exception { + for (Class view : new Class[] { ViewA.class, ViewB.class }) { + ViewBean expected = VANILLA.readerWithView(view).forType(ViewBean.class).readValue(DOC); + ViewBean actual = MODULE.readerWithView(view).forType(ViewBean.class).readValue(DOC); + assertEquals(expected.getA(), actual.getA(), "a under " + view.getSimpleName()); + assertEquals(expected.getB(), actual.getB(), "b under " + view.getSimpleName()); + assertEquals(expected.getPlain(), actual.getPlain(), + "plain under " + view.getSimpleName()); + } + } + + @Test + public void testViewReadConsumesSkippedValues() throws Exception { + // Non-visible properties must be consumed exactly as stock consumes + // them: a top-level array derails if the skip leaves the stream + // mispositioned. + String doc = "[" + DOC + "," + DOC + "]"; + List beans = MODULE.readerWithView(ViewA.class) + .forType(MODULE.getTypeFactory() + .constructCollectionType(List.class, ViewBean.class)) + .readValue(doc); + assertEquals(2, beans.size()); + assertEquals("x", beans.get(1).getA()); + assertEquals(0, beans.get(1).getB(), + "@JsonView(ViewB) property must stay default with ViewA active"); + } + + @Test + public void testFieldBeanMatchesVanillaPerView() throws Exception { + ViewFieldBean bean = new ViewFieldBean(); + bean.a = "x"; + bean.b = 7; + String doc = "{\"a\":\"x\",\"b\":7}"; + for (Class view : new Class[] { ViewA.class, ViewB.class }) { + assertEquals(VANILLA.writerWithView(view).writeValueAsString(bean), + MODULE.writerWithView(view).writeValueAsString(bean)); + ViewFieldBean expected = VANILLA.readerWithView(view) + .forType(ViewFieldBean.class).readValue(doc); + ViewFieldBean actual = MODULE.readerWithView(view) + .forType(ViewFieldBean.class).readValue(doc); + assertEquals(expected.a, actual.a); + assertEquals(expected.b, actual.b); + } + } + + @Test + public void testRecordMatchesVanillaPerView() throws Exception { + String doc = "{\"a\":\"x\",\"b\":7}"; + for (Class view : new Class[] { ViewA.class, ViewB.class }) { + ViewRecord expected = VANILLA.readerWithView(view) + .forType(ViewRecord.class).readValue(doc); + ViewRecord actual = MODULE.readerWithView(view) + .forType(ViewRecord.class).readValue(doc); + assertEquals(expected, actual, "record under " + view.getSimpleName()); + assertEquals(VANILLA.writerWithView(view).writeValueAsString(expected), + MODULE.writerWithView(view).writeValueAsString(actual)); + } + } + + @Test + public void testNestedCodecChildMatchesVanillaPerView() throws Exception { + Parent parent = new Parent(); + parent.setChild(bean()); + String doc = "{\"child\":" + DOC + "}"; + for (Class view : new Class[] { ViewA.class, ViewB.class }) { + assertEquals(VANILLA.writerWithView(view).writeValueAsString(parent), + MODULE.writerWithView(view).writeValueAsString(parent)); + Parent expected = VANILLA.readerWithView(view).forType(Parent.class).readValue(doc); + Parent actual = MODULE.readerWithView(view).forType(Parent.class).readValue(doc); + assertEquals(expected.getChild().getA(), actual.getChild().getA()); + assertEquals(expected.getChild().getB(), actual.getChild().getB()); + } + } + + @Test + public void testViewBeansEngageCodecs() throws Exception { + // The cases above only pin view parity if the codecs actually engage + // for these beans when no view is active. + Map, ValueDeserializer> seen = new ConcurrentHashMap<>(); + ValueDeserializerModifier capture = new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig config, + BeanDescription.Supplier beanDescRef, ValueDeserializer deserializer) { + seen.put(beanDescRef.getBeanClass(), deserializer); + return deserializer; + } + }; + ObjectMapper m = JsonMapper.builder() + .addModule(new SimpleModule("capture").setDeserializerModifier(capture)) + .addModule(new BlackbirdModule()) + .build(); + m.readValue(DOC, ViewBean.class); + m.readValue("{\"a\":\"x\",\"b\":7}", ViewRecord.class); + assertTrue(hasGeneratedCodec(seen.get(ViewBean.class))); + assertTrue(hasGeneratedCodec(seen.get(ViewRecord.class))); + } + + // A plain bean with no @JsonView annotations: with DEFAULT_VIEW_INCLUSION + // off, stock hides every property under any active view on the write side + // and keeps them all on the read side (the deser matcher is null). The + // codec must reproduce both, which forces the MASK strategy for a bean + // that carries no view annotations at all. + public static class PlainBean { + private String a; + private int b; + public String getA() { return a; } + public void setA(String v) { a = v; } + public int getB() { return b; } + public void setB(int v) { b = v; } + } + + @Test + public void testDefaultViewInclusionOffMatchesVanilla() throws Exception { + ObjectMapper vanilla = JsonMapper.builder() + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .build(); + ObjectMapper module = JsonMapper.builder() + .disable(MapperFeature.DEFAULT_VIEW_INCLUSION) + .addModule(new BlackbirdModule()) + .build(); + PlainBean bean = new PlainBean(); + bean.setA("x"); + bean.setB(7); + String doc = "{\"a\":\"x\",\"b\":7}"; + assertEquals(vanilla.writerWithView(ViewA.class).writeValueAsString(bean), + module.writerWithView(ViewA.class).writeValueAsString(bean), + "write, inclusion off, active view: both should omit all"); + PlainBean ev = vanilla.readerWithView(ViewA.class).forType(PlainBean.class).readValue(doc); + PlainBean am = module.readerWithView(ViewA.class).forType(PlainBean.class).readValue(doc); + assertEquals(ev.getA(), am.getA()); + assertEquals(ev.getB(), am.getB()); + } + + // More than 64 properties: the mask cannot fit, so an active view delegates + // the whole call. Output must still match vanilla in both directions. + public static class WideViewBean { + public int p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15; + public int p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31; + public int p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47; + public int p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63; + @JsonView(ViewA.class) + public int p64; + public int p65; + } + + @Test + public void testWideBeanDelegatesUnderView() throws Exception { + WideViewBean bean = new WideViewBean(); + bean.p64 = 64; + bean.p65 = 65; + for (Class view : new Class[] { ViewA.class, ViewB.class }) { + assertEquals(VANILLA.writerWithView(view).writeValueAsString(bean), + MODULE.writerWithView(view).writeValueAsString(bean), + "wide bean under " + view.getSimpleName()); + } + assertEquals(VANILLA.writeValueAsString(bean), MODULE.writeValueAsString(bean), + "wide bean, no view"); + } + + // A codec whose bean has 64 or fewer properties keeps view-active calls on + // the generated mask path (only the wide bean above delegates). This + // check pins that the mask read is byte-faithful to stock: the ViewB + // property is hidden under ViewA, the visible ones match, and the skipped + // value leaves the stream correctly positioned for the trailing property. + @Test + public void testViewActiveMaskMatchesVanilla() throws Exception { + ViewBean expected = VANILLA.readerWithView(ViewA.class) + .forType(ViewBean.class).readValue(DOC); + ViewBean actual = MODULE.readerWithView(ViewA.class) + .forType(ViewBean.class).readValue(DOC); + assertEquals(expected.getA(), actual.getA()); + assertEquals(expected.getB(), actual.getB()); + assertEquals(expected.getPlain(), actual.getPlain()); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderEntryTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderEntryTest.java new file mode 100644 index 000000000..50cb96b19 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderEntryTest.java @@ -0,0 +1,214 @@ +package tools.jackson.module.blackbird.deser; + +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import org.junit.jupiter.api.Test; + +import tools.jackson.core.JsonParser; +import tools.jackson.core.JsonToken; +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.deser.std.DelegatingDeserializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * PROPERTY_NAME entry through generated readers. The AsProperty polymorphic + * path hands a subtype deserializer a stream positioned on the property after + * the type id (or a buffered-replay sequence starting on one), so generated + * readers accept that entry natively instead of delegating. Every behavior + * case compares against a vanilla mapper. + */ +public class BBReaderEntryTest extends BlackbirdTestBase +{ + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") + @JsonSubTypes({ + @JsonSubTypes.Type(value = Dog.class, name = "dog"), + @JsonSubTypes.Type(value = RecCat.class, name = "cat") + }) + public interface Animal { } + + public static class Dog implements Animal { + public String name; + public int age; + public boolean good; + } + + public record RecCat(@JsonProperty(required = true) String name, int lives) + implements Animal { } + + public static class Plain { + public String s; + public int i; + } + + private final ObjectMapper mapper = newObjectMapper(); + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + // Pins the databind contract the native entry depends on: the AsProperty + // type deserializer invokes the subtype deserializer with the stream on + // PROPERTY_NAME, for the id-first (live stream) and id-last (buffered + // sequence) orders both. Runs against vanilla, no Blackbird. + @Test + public void databindHandsPropertyNameEntry() throws Exception + { + ConcurrentMap entries = new ConcurrentHashMap<>(); + ObjectMapper spied = JsonMapper.builder() + .addModule(new SimpleModule("spy") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer( + DeserializationConfig config, + BeanDescription.Supplier beanDescRef, + ValueDeserializer deser) { + if (beanDescRef.getBeanClass() != Dog.class) { + return deser; + } + return new EntrySpy(deser, entries); + } + }); + } + }) + .build(); + + spied.readValue(a2q("{'type':'dog','name':'Rex','age':3,'good':true}"), Animal.class); + assertEquals(JsonToken.PROPERTY_NAME, entries.get("entry"), "id-first entry"); + + spied.readValue(a2q("{'name':'Rex','age':3,'good':true,'type':'dog'}"), Animal.class); + assertEquals(JsonToken.PROPERTY_NAME, entries.get("entry"), "id-last (buffered) entry"); + } + + @Test + public void polymorphicTypeIdFirst() throws Exception + { + String doc = a2q("{'type':'dog','name':'Rex','age':3,'good':true}"); + Dog d = (Dog) mapper.readValue(doc, Animal.class); + Dog v = (Dog) vanilla.readValue(doc, Animal.class); + assertEquals(v.name, d.name); + assertEquals(v.age, d.age); + assertEquals(v.good, d.good); + } + + @Test + public void polymorphicTypeIdLast() throws Exception + { + String doc = a2q("{'name':'Rex','age':3,'good':true,'type':'dog'}"); + Dog d = (Dog) mapper.readValue(doc, Animal.class); + Dog v = (Dog) vanilla.readValue(doc, Animal.class); + assertEquals(v.name, d.name); + assertEquals(v.age, d.age); + assertEquals(v.good, d.good); + } + + @Test + public void polymorphicRecordSubtype() throws Exception + { + String doc = a2q("{'type':'cat','name':'Mia','lives':9}"); + assertEquals(vanilla.readValue(doc, Animal.class), mapper.readValue(doc, Animal.class)); + + String middle = a2q("{'name':'Mia','type':'cat','lives':9}"); + assertEquals(vanilla.readValue(middle, Animal.class), + mapper.readValue(middle, Animal.class)); + + // Required component missing: same failure both stacks, through the + // PROPERTY_NAME entry. + String missing = a2q("{'type':'cat','lives':9}"); + MismatchedInputException expected = assertThrows(MismatchedInputException.class, + () -> vanilla.readValue(missing, Animal.class)); + MismatchedInputException actual = assertThrows(MismatchedInputException.class, + () -> mapper.readValue(missing, Animal.class)); + assertEquals(expected.getClass(), actual.getClass()); + } + + @Test + public void polymorphicIdOnlyEmptyRemainder() throws Exception + { + // Nothing after the type id: the subtype deserializer enters at + // END_OBJECT, which stays on the stock delegation path. + String doc = a2q("{'type':'dog'}"); + Dog d = (Dog) mapper.readValue(doc, Animal.class); + Dog v = (Dog) vanilla.readValue(doc, Animal.class); + assertEquals(v.name, d.name); + assertEquals(v.age, d.age); + } + + @Test + public void readValueAtPropertyName() throws Exception + { + String doc = a2q("{'s':'x','i':7}"); + Plain p1 = readPositioned(mapper, doc); + Plain p2 = readPositioned(vanilla, doc); + assertEquals(p2.s, p1.s); + assertEquals(p2.i, p1.i); + } + + @Test + public void unknownFirstNameAtEntry() throws Exception + { + String doc = a2q("{'mystery':[1,2,{'x':3}],'s':'x','i':7}"); + Plain p1 = readPositioned(mapper, doc); + Plain p2 = readPositioned(vanilla, doc); + assertEquals(p2.s, p1.s); + assertEquals(p2.i, p1.i); + } + + @Test + public void polymorphicList() throws Exception + { + String doc = a2q("[{'type':'dog','name':'a','age':1,'good':false}," + + "{'type':'cat','name':'b','lives':2}]"); + List got = mapper.readerForListOf(Animal.class).readValue(doc); + List want = vanilla.readerForListOf(Animal.class).readValue(doc); + assertEquals(want.size(), got.size()); + assertEquals(((Dog) want.get(0)).name, ((Dog) got.get(0)).name); + assertEquals(want.get(1), got.get(1)); + } + + static class EntrySpy extends DelegatingDeserializer { + private final ConcurrentMap entries; + + EntrySpy(ValueDeserializer d, ConcurrentMap entries) { + super(d); + this.entries = entries; + } + + @Override + protected ValueDeserializer newDelegatingInstance(ValueDeserializer newDelegatee) { + return new EntrySpy(newDelegatee, entries); + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) { + entries.put("entry", p.currentToken()); + return super.deserialize(p, ctxt); + } + } + + private static Plain readPositioned(ObjectMapper m, String doc) throws Exception + { + try (JsonParser p = m.createParser(doc)) { + assertEquals(JsonToken.START_OBJECT, p.nextToken()); + assertEquals(JsonToken.PROPERTY_NAME, p.nextToken()); + return m.readValue(p, Plain.class); + } + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderUpdatingTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderUpdatingTest.java new file mode 100644 index 000000000..19d51c98f --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderUpdatingTest.java @@ -0,0 +1,60 @@ +package tools.jackson.module.blackbird.deser; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.ObjectMapper; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * readerForUpdating parity: the generated reader forwards update-value reads + * to the stock deserializer, so updating must behave exactly like vanilla. + */ +public class BBReaderUpdatingTest extends BlackbirdTestBase +{ + public static class Mutable { + private int count; + private String name; + private String keep; + + public int getCount() { return count; } + public void setCount(int c) { count = c; } + public String getName() { return name; } + public void setName(String n) { name = n; } + public String getKeep() { return keep; } + public void setKeep(String k) { keep = k; } + } + + private final ObjectMapper mapper = newObjectMapper(); + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + @Test + public void testUpdatingReadMatchesVanilla() throws Exception { + String doc = a2q("{'count':42,'name':'updated'}"); + + Mutable mine = new Mutable(); + mine.setKeep("original"); + mine.setName("old"); + Object result = mapper.readerForUpdating(mine).readValue(doc); + assertSame(mine, result); + + Mutable theirs = new Mutable(); + theirs.setKeep("original"); + theirs.setName("old"); + vanilla.readerForUpdating(theirs).readValue(doc); + + assertEquals(theirs.getCount(), mine.getCount()); + assertEquals(theirs.getName(), mine.getName()); + assertEquals(theirs.getKeep(), mine.getKeep()); + assertEquals("original", mine.getKeep()); + } + + @Test + public void testPlainReadStillGenerated() throws Exception { + Mutable m = mapper.readValue(a2q("{'count':7,'name':'n','keep':'k'}"), Mutable.class); + assertEquals(7, m.getCount()); + assertEquals("n", m.getName()); + assertEquals("k", m.getKeep()); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/OptionalDeser355Test.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/OptionalDeser355Test.java index 0c8948940..4556fa524 100644 --- a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/OptionalDeser355Test.java +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/OptionalDeser355Test.java @@ -112,35 +112,34 @@ public ValueDeserializer modifyDeserializer(DeserializationConfig config, return deserializer; } - public SettableBeanProperty propertyFor(Class beanType, String propertyName) { + public ValueDeserializer deserializerFor(Class beanType) { ValueDeserializer deserializer = deserializers.get(beanType); - BeanDeserializerBase beanDeserializer = assertInstanceOf( - BeanDeserializerBase.class, deserializer); - Iterator properties = beanDeserializer.properties(); - while (properties.hasNext()) { - SettableBeanProperty property = properties.next(); - if (property.getName().equals(propertyName)) { - return property; - } - } - return fail("No property '"+propertyName+"' found for "+beanType.getName()); + assertNotNull(deserializer, "No deserializer captured for " + beanType.getName()); + return deserializer; } } + // The new engine never swaps individual properties: eligible beans - + // these package-private ones included - get a whole generated codec, and + // every Optional-typed property rides its stock SettableBeanProperty + // inside it (the issue-355 revert cannot recur by construction). The + // original per-property assertions named the old engine's classes; their + // equivalent here is codec engagement plus behavior. @Test - public void testKeepsOptimizedPropertyForBuiltInOptionalDeserializer() throws Exception { + public void testOptionalPropertyWithBuiltInDeserializer() throws Exception { DeserializerCapture capture = new DeserializerCapture(); ObjectMapper mapper = mapperWithCapture(capture); OptionalBean bean = mapper.readValue("{\"value\":\"test\"}", OptionalBean.class); assertEquals(Optional.of("test"), bean.getValue()); - assertInstanceOf(SettableObjectProperty.class, - capture.propertyFor(OptionalBean.class, "value")); + assertTrue(hasGeneratedCodec(capture.deserializerFor(OptionalBean.class))); + assertEquals(Optional.empty(), + mapper.readValue("{\"value\":null}", OptionalBean.class).getValue()); } @Test - public void testFallsBackForCustomOptionalDeserializer() throws Exception { + public void testOptionalPropertyWithCustomDeserializer() throws Exception { DeserializerCapture capture = new DeserializerCapture(); ObjectMapper mapper = mapperWithCapture(capture); @@ -149,18 +148,18 @@ public void testFallsBackForCustomOptionalDeserializer() throws Exception { assertEquals(Optional.of("test"), bean.getValue()); assertEquals(Optional.of(42), bean.getNumber()); - assertInstanceOf(MethodProperty.class, - capture.propertyFor(CustomOptionalBean.class, "value")); - assertInstanceOf(MethodProperty.class, - capture.propertyFor(CustomOptionalBean.class, "number")); + // The custom-deserializer properties ride the codec's stock arms. + assertTrue(hasGeneratedCodec(capture.deserializerFor(CustomOptionalBean.class))); } private ObjectMapper mapperWithCapture(DeserializerCapture capture) { SimpleModule captureModule = new SimpleModule() .setDeserializerModifier(capture); + // Modifiers run in reverse registration order: the capture module + // registers first so it observes what Blackbird installed. return JsonMapper.builder() - .addModule(new BlackbirdModule()) .addModule(captureModule) + .addModule(new BlackbirdModule()) .build(); } } diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/deser/PackagePrivateBeanTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/PackagePrivateBeanTest.java new file mode 100644 index 000000000..eff05db57 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/deser/PackagePrivateBeanTest.java @@ -0,0 +1,219 @@ +package tools.jackson.module.blackbird.deser; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.DeserializationConfig; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationConfig; +import tools.jackson.databind.ValueDeserializer; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.deser.ValueDeserializerModifier; +import tools.jackson.databind.exc.MismatchedInputException; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.ser.ValueSerializerModifier; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Non-public classes accelerate: package-private, protected, and private + * static nested beans (and their non-private members) get generated codecs. + * Member access rides databind's fixAccess through unreflected constant + * handles, so the generated class never names the bean and accessibility + * never gates a bean stock databind handles. Every behavioral check compares + * against a vanilla mapper. + */ +public class PackagePrivateBeanTest extends BlackbirdTestBase +{ + static class PkgBean { + private int count; + private String name; + + public int getCount() { return count; } + public void setCount(int v) { count = v; } + public String getName() { return name; } + public void setName(String v) { name = v; } + } + + static class PkgMembersBean { + int direct; + private long viaSetter; + + int getDirect() { return direct; } + void setDirect(int v) { direct = v; } + long getViaSetter() { return viaSetter; } + void setViaSetter(long v) { viaSetter = v; } + } + + private static class PrivateBean { + public int x; + private String s; + + // Explicit annotations: private accessors are not auto-detected, and + // this pin is exactly about reaching them once databind selects them. + @JsonProperty("s") + private String getS() { return s; } + @JsonProperty("s") + private void setS(String v) { s = v; } + } + + protected static class ProtectedBean { + public int x; + public String s; + } + + record PkgRecord(@JsonProperty(required = true) String id, int qty) { } + + static class Capture { + final Map, ValueDeserializer> desers = new ConcurrentHashMap<>(); + final Map, ValueSerializer> sers = new ConcurrentHashMap<>(); + } + + private static ObjectMapper mapperWith(Capture capture) { + SimpleModule captureModule = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addDeserializerModifier(new ValueDeserializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueDeserializer modifyDeserializer(DeserializationConfig cfg, + BeanDescription.Supplier ref, ValueDeserializer d) { + capture.desers.put(ref.getBeanClass(), d); + return d; + } + }); + ctxt.addSerializerModifier(new ValueSerializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueSerializer modifySerializer(SerializationConfig cfg, + BeanDescription.Supplier ref, ValueSerializer s) { + capture.sers.put(ref.getBeanClass(), s); + return s; + } + }); + } + }; + return JsonMapper.builder() + .addModule(captureModule) + .addModule(new BlackbirdModule()) + .build(); + } + + // The placeholder resolves its codec at resolve time; a null codec means + // the generator gated the bean, so the assertion distinguishes "engaged + // and generated" from "engaged but silently demoted". + private static void assertCodecGenerated(Object placeholder) throws Exception { + assertEquals("BBReaderPlaceholder", placeholder.getClass().getSimpleName(), + "expected the deserializer placeholder, got " + + placeholder.getClass().getName()); + Field codec = placeholder.getClass().getDeclaredField("_codec"); + codec.setAccessible(true); + assertNotNull(codec.get(placeholder), "placeholder engaged but no codec generated"); + } + + private static void assertWriterGenerated(Object placeholder) throws Exception { + assertEquals("BBWriterPlaceholder", placeholder.getClass().getSimpleName(), + "expected the serializer placeholder, got " + + placeholder.getClass().getName()); + Field codec = placeholder.getClass().getDeclaredField("_codec"); + codec.setAccessible(true); + assertNotNull(codec.get(placeholder), "placeholder engaged but no writer generated"); + } + + @Test + public void testPackagePrivateBeanAccelerates() throws Exception { + Capture capture = new Capture(); + ObjectMapper mapper = mapperWith(capture); + ObjectMapper vanilla = newVanillaJSONMapper(); + + String doc = a2q("{'count':7,'name':'x'}"); + PkgBean act = mapper.readValue(doc, PkgBean.class); + PkgBean exp = vanilla.readValue(doc, PkgBean.class); + assertEquals(exp.getCount(), act.getCount()); + assertEquals(exp.getName(), act.getName()); + assertEquals(vanilla.writeValueAsString(exp), mapper.writeValueAsString(act)); + + assertCodecGenerated(capture.desers.get(PkgBean.class)); + assertWriterGenerated(capture.sers.get(PkgBean.class)); + } + + @Test + public void testPackagePrivateMembersAccelerate() throws Exception { + Capture capture = new Capture(); + ObjectMapper mapper = mapperWith(capture); + ObjectMapper vanilla = newVanillaJSONMapper(); + + String doc = a2q("{'direct':3,'viaSetter':9000000000}"); + PkgMembersBean act = mapper.readValue(doc, PkgMembersBean.class); + PkgMembersBean exp = vanilla.readValue(doc, PkgMembersBean.class); + assertEquals(exp.getDirect(), act.getDirect()); + assertEquals(exp.getViaSetter(), act.getViaSetter()); + assertEquals(vanilla.writeValueAsString(exp), mapper.writeValueAsString(act)); + + assertCodecGenerated(capture.desers.get(PkgMembersBean.class)); + } + + @Test + public void testPrivateClassAccelerates() throws Exception { + // Widened from a demotion pin: private classes, private setters, and + // the private constructor all reach the codec through fixAccess'd + // handles now. + Capture capture = new Capture(); + ObjectMapper mapper = mapperWith(capture); + ObjectMapper vanilla = newVanillaJSONMapper(); + + String doc = a2q("{'x':5,'s':'in'}"); + PrivateBean act = mapper.readValue(doc, PrivateBean.class); + PrivateBean exp = vanilla.readValue(doc, PrivateBean.class); + assertEquals(exp.x, act.x); + assertEquals(exp.getS(), act.getS()); + assertEquals("in", act.getS()); + assertEquals(vanilla.writeValueAsString(exp), mapper.writeValueAsString(act)); + + assertCodecGenerated(capture.desers.get(PrivateBean.class)); + assertWriterGenerated(capture.sers.get(PrivateBean.class)); + } + + @Test + public void testProtectedNestedBeanAccelerates() throws Exception { + Capture capture = new Capture(); + ObjectMapper mapper = mapperWith(capture); + ObjectMapper vanilla = newVanillaJSONMapper(); + + String doc = a2q("{'x':1,'s':'y'}"); + ProtectedBean act = mapper.readValue(doc, ProtectedBean.class); + assertEquals(1, act.x); + assertEquals("y", act.s); + assertEquals(vanilla.writeValueAsString(act), mapper.writeValueAsString(act)); + + assertCodecGenerated(capture.desers.get(ProtectedBean.class)); + } + + @Test + public void testPackagePrivateRecord() throws Exception { + Capture capture = new Capture(); + ObjectMapper mapper = mapperWith(capture); + ObjectMapper vanilla = newVanillaJSONMapper(); + + String doc = a2q("{'id':'a','qty':2}"); + assertEquals(vanilla.readValue(doc, PkgRecord.class), + mapper.readValue(doc, PkgRecord.class)); + assertCodecGenerated(capture.desers.get(PkgRecord.class)); + + MismatchedInputException e = assertThrows(MismatchedInputException.class, + () -> mapper.readValue(a2q("{'qty':2}"), PkgRecord.class)); + assertTrue(e.getMessage().contains("Missing required creator property 'id'"), + e.getMessage()); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModuleTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModuleTest.java new file mode 100644 index 000000000..2eddbab63 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModuleTest.java @@ -0,0 +1,49 @@ +package tools.jackson.module.blackbird.internal; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.JacksonModule; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.module.blackbird.BlackbirdModule; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * On a runtime that generates codecs, the discovery provider must look like + * an explicit registration to databind: same registration id (so discovery + * plus an explicit registration installs Blackbird once) and the same module + * name. (On Java 17 to 24 the ids differ, so that discovery cannot replace + * an explicit registration, which must fail.) + */ +public class DiscoveredBlackbirdModuleTest +{ + @Test + public void sameIdentityAsExplicitModule() { + BlackbirdModule explicit = new BlackbirdModule(); + DiscoveredBlackbirdModule discovered = new DiscoveredBlackbirdModule(); + assertEquals(explicit.getRegistrationId(), discovered.getRegistrationId()); + assertEquals(explicit.getModuleName(), discovered.getModuleName()); + } + + @Test + public void discoveryPlusExplicitRegistersOnce() { + assertRegisteredOnce(JsonMapper.builder() + .addModule(new DiscoveredBlackbirdModule()) + .addModule(new BlackbirdModule()) + .build()); + assertRegisteredOnce(JsonMapper.builder() + .addModule(new BlackbirdModule()) + .addModule(new DiscoveredBlackbirdModule()) + .build()); + } + + private static void assertRegisteredOnce(ObjectMapper mapper) { + List blackbirds = mapper.registeredModules().stream() + .filter(m -> m instanceof BlackbirdModule) + .toList(); + assertEquals(1, blackbirds.size(), "registered: " + blackbirds); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/ser/BBWriterPolymorphicTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/ser/BBWriterPolymorphicTest.java new file mode 100644 index 000000000..6f6a17b65 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/ser/BBWriterPolymorphicTest.java @@ -0,0 +1,213 @@ +package tools.jackson.module.blackbird.ser; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeId; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationConfig; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.ser.ValueSerializerModifier; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Polymorphic serialization parity: databind calls serializeWithType on the + * subtype's generated serializer, whose native override replicates stock + * BeanSerializerBase's WritableTypeId flow (typeId, writeTypePrefix, + * assignCurrentValue, properties, writeTypeSuffix), so output must equal + * vanilla for every inclusion mechanism. Beans with a @JsonTypeId property + * keep the whole-call forwarding to stock. + */ +public class BBWriterPolymorphicTest extends BlackbirdTestBase +{ + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type") + @JsonSubTypes({ + @JsonSubTypes.Type(value = Cat.class, name = "cat"), + @JsonSubTypes.Type(value = Dog.class, name = "dog") + }) + public static abstract class Animal { + public String name; + } + + public static class Cat extends Animal { + public int lives; + public boolean indoor; + + static Cat of(String name, int lives, boolean indoor) { + Cat c = new Cat(); + c.name = name; + c.lives = lives; + c.indoor = indoor; + return c; + } + } + + public static class Dog extends Animal { + public long barks; + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) + @JsonSubTypes({ + @JsonSubTypes.Type(value = WrappedLeaf.class, name = "leaf") + }) + public static abstract class Wrapped { + } + + public static class WrappedLeaf extends Wrapped { + public String label; + public int size; + + static WrappedLeaf of(String label, int size) { + WrappedLeaf w = new WrappedLeaf(); + w.label = label; + w.size = size; + return w; + } + } + + public static class Zoo { + public String city; + public Animal star; + } + + private final ObjectMapper mapper = newObjectMapper(); + private final ObjectMapper vanilla = newVanillaJSONMapper(); + + @Test + public void testPropertyInclusionRootWrite() throws Exception { + Animal cat = Cat.of("mia", 9, true); + assertEquals(vanilla.writerFor(Animal.class).writeValueAsString(cat), + mapper.writerFor(Animal.class).writeValueAsString(cat)); + } + + @Test + public void testPropertyInclusionNestedWrite() throws Exception { + Zoo zoo = new Zoo(); + zoo.city = "sf"; + zoo.star = Cat.of("mia", 9, false); + assertEquals(vanilla.writeValueAsString(zoo), mapper.writeValueAsString(zoo)); + } + + @Test + public void testPropertyInclusionListWrite() throws Exception { + List animals = List.of(Cat.of("a", 1, true), new Dog()); + assertEquals(vanilla.writerFor(vanilla.getTypeFactory() + .constructCollectionType(List.class, Animal.class)) + .writeValueAsString(animals), + mapper.writerFor(mapper.getTypeFactory() + .constructCollectionType(List.class, Animal.class)) + .writeValueAsString(animals)); + } + + @Test + public void testWrapperObjectInclusionWrite() throws Exception { + Wrapped w = WrappedLeaf.of("x", 3); + assertEquals(vanilla.writerFor(Wrapped.class).writeValueAsString(w), + mapper.writerFor(Wrapped.class).writeValueAsString(w)); + } + + @Test + public void testPolymorphicRoundTrip() throws Exception { + Animal cat = Cat.of("mia", 9, true); + String doc = mapper.writerFor(Animal.class).writeValueAsString(cat); + Animal back = mapper.readValue(doc, Animal.class); + assertInstanceOf(Cat.class, back); + assertEquals("mia", back.name); + assertEquals(9, ((Cat) back).lives); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_ARRAY) + @JsonSubTypes({ + @JsonSubTypes.Type(value = ArrayLeaf.class, name = "leaf") + }) + public static abstract class ArrayWrapped { + } + + public static class ArrayLeaf extends ArrayWrapped { + public String label; + public long count; + } + + @Test + public void testWrapperArrayInclusionWrite() throws Exception { + ArrayLeaf leaf = new ArrayLeaf(); + leaf.label = "x"; + leaf.count = 12; + assertEquals(vanilla.writerFor(ArrayWrapped.class).writeValueAsString(leaf), + mapper.writerFor(ArrayWrapped.class).writeValueAsString(leaf)); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type") + @JsonSubTypes({ + @JsonSubTypes.Type(value = TypeIdLeaf.class, name = "unused") + }) + public static abstract class TypeIdBase { + } + + public static class TypeIdLeaf extends TypeIdBase { + @JsonTypeId + public String kind = "custom-id"; + public int n; + } + + @Test + public void testJsonTypeIdPropertyWrite() throws Exception { + // @JsonTypeId beans keep the base forwarding (the property value + // replaces the resolver's id); output still equals vanilla. + TypeIdLeaf leaf = new TypeIdLeaf(); + leaf.n = 5; + String v = vanilla.writerFor(TypeIdBase.class).writeValueAsString(leaf); + assertEquals(v, mapper.writerFor(TypeIdBase.class).writeValueAsString(leaf)); + assertTrue(v.contains("custom-id"), v); + } + + // The engaged writer must actually generate a codec: byte-equal output + // alone cannot distinguish native poly writes from forwarding. + @Test + public void testSubtypeWriterGeneratesCodec() throws Exception { + Map, ValueSerializer> seen = new ConcurrentHashMap<>(); + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addSerializerModifier(new ValueSerializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueSerializer modifySerializer(SerializationConfig cfg, + BeanDescription.Supplier ref, ValueSerializer s) { + seen.put(ref.getBeanClass(), s); + return s; + } + }); + } + }; + ObjectMapper capturing = JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()) + .build(); + Animal cat = Cat.of("mia", 9, true); + assertEquals(vanilla.writerFor(Animal.class).writeValueAsString(cat), + capturing.writerFor(Animal.class).writeValueAsString(cat)); + ValueSerializer captured = seen.get(Cat.class); + assertNotNull(captured, "no serializer captured for Cat"); + assertTrue(hasGeneratedCodec(captured), + "poly subtype did not engage a codec"); + Field codec = captured.getClass().getDeclaredField("_codec"); + codec.setAccessible(true); + assertNotNull(codec.get(captured), "poly subtype engaged but no writer generated"); + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/ser/ChildClassloaderTest.java b/blackbird/src/test/java/tools/jackson/module/blackbird/ser/ChildClassloaderTest.java new file mode 100644 index 000000000..53ef1d474 --- /dev/null +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/ser/ChildClassloaderTest.java @@ -0,0 +1,126 @@ +package tools.jackson.module.blackbird.ser; + +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.jupiter.api.Test; + +import tools.jackson.databind.BeanDescription; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.SerializationConfig; +import tools.jackson.databind.ValueSerializer; +import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.module.SimpleModule; +import tools.jackson.databind.ser.ValueSerializerModifier; +import tools.jackson.module.blackbird.BlackbirdModule; +import tools.jackson.module.blackbird.BlackbirdTestBase; + +import static org.junit.jupiter.api.Assertions.*; + +// Cross-loader regression test (formerly tofix/TestBBClassloaders): a bean +// class redefined in a child classloader serializes correctly - and now +// ACCELERATES - with the module registered. Generated code never names the +// bean class (values load through unreflected constant handles bound to the +// child-loaded Class), so the foreign loader no longer gates codec +// generation; this widened pin holds that capability. +// +// Old Blackbird failed this on the module path only because the test read the +// class bytes through classloader getResource, which JPMS encapsulation nulls; +// Class#getResourceAsStream resolves inside this module and works in both +// modes. +public class ChildClassloaderTest extends BlackbirdTestBase +{ + @Test + public void testLoadInChildClassloader() throws Exception + { + TestLoader loader = new TestLoader(getClass().getClassLoader()); + Class clazz = Class.forName(Data.class.getName(), true, loader); + assertNotSame(Data.class, clazz); + Map, ValueSerializer> seen = new ConcurrentHashMap<>(); + ObjectMapper mapper = capturingMapper(seen); + Constructor constructor = clazz.getConstructor(int.class); + Object data = constructor.newInstance(42); + assertEquals("{\"field\":42}", mapper.writeValueAsString(data)); + ValueSerializer captured = seen.get(clazz); + assertNotNull(captured, "no serializer captured for the child-loaded class"); + assertTrue(hasGeneratedCodec(captured), + "child-loaded bean did not engage a codec"); + Field codec = captured.getClass().getDeclaredField("_codec"); + codec.setAccessible(true); + assertNotNull(codec.get(captured), + "child-loaded bean engaged but no writer generated"); + } + + private static ObjectMapper capturingMapper(Map, ValueSerializer> seen) { + SimpleModule capture = new SimpleModule("capture") { + private static final long serialVersionUID = 1L; + @Override + public void setupModule(SetupContext ctxt) { + super.setupModule(ctxt); + ctxt.addSerializerModifier(new ValueSerializerModifier() { + private static final long serialVersionUID = 1L; + @Override + public ValueSerializer modifySerializer(SerializationConfig cfg, + BeanDescription.Supplier ref, ValueSerializer s) { + seen.put(ref.getBeanClass(), s); + return s; + } + }); + } + }; + return JsonMapper.builder() + .addModule(capture) + .addModule(new BlackbirdModule()) + .build(); + } + + public static class Data { + private int field; + + public Data(int field) { + this.field = field; + } + + public int getField() { + return field; + } + + public void setField(int field) { + this.field = field; + } + } + + public class TestLoader extends ClassLoader { + public TestLoader(final ClassLoader parent) { + super(parent); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock(name)) { + try { + Class clazz; + if (Data.class.getName().equals(name)) { + byte[] bytes; + try (InputStream in = ChildClassloaderTest.class + .getResourceAsStream("ChildClassloaderTest$Data.class")) { + bytes = in.readAllBytes(); + } + clazz = defineClass(name, bytes, 0, bytes.length); + } else { + clazz = super.loadClass(name, resolve); + } + if (resolve) { + resolveClass(clazz); + } + return clazz; + } catch (Exception e) { + throw new ClassNotFoundException("Unable to load class", e); + } + } + } + } +} diff --git a/blackbird/src/test/java/tools/jackson/module/blackbird/tofix/TestBBClassloaders.java b/blackbird/src/test/java/tools/jackson/module/blackbird/tofix/TestBBClassloaders.java deleted file mode 100644 index 296e9a4ab..000000000 --- a/blackbird/src/test/java/tools/jackson/module/blackbird/tofix/TestBBClassloaders.java +++ /dev/null @@ -1,90 +0,0 @@ -package tools.jackson.module.blackbird.tofix; - -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.lang.reflect.Constructor; - -import org.junit.jupiter.api.Test; - -import tools.jackson.databind.ObjectMapper; -import tools.jackson.module.blackbird.BlackbirdTestBase; -import tools.jackson.module.blackbird.testutil.failure.JacksonTestFailureExpected; - -import static org.junit.jupiter.api.Assertions.*; - -public class TestBBClassloaders extends BlackbirdTestBase -{ - protected final String resourceName = - (TestBBClassloaders.class.getName() + "$Data") - .replace('.', '/').concat(".class"); - - // Note: looks this test: passes on JDKs OTHER than 11 for Jackson 2.x, - // but fails for JDK 17+ for Jackson 3.x. - @Test - @JacksonTestFailureExpected - public void testLoadInChildClassloader() throws Exception - { - // 19-Jan-2025, tatu: only fails on JDK 11 specifically, not on JDK 17 or later - // - // So just skip on that - if (System.getProperty("java.version").startsWith("11.")) { - System.out.println("Skipping `testLoadInChildClassloader()` on JDK 11"); - return; - } - TestLoader loader = new TestLoader(getClass().getClassLoader()); - Class clazz = Class.forName(Data.class.getName(), true, loader); - ObjectMapper mapper = newObjectMapper(); - Constructor constructor = clazz.getConstructor(int.class); - Object data = constructor.newInstance(42); - assertEquals("{\"field\":42}", mapper.writeValueAsString(data)); - } - - public static class Data { - private int field; - - public Data(int field) { - this.field = field; - } - - public int getField() { - return field; - } - - public void setField(int field) { - this.field = field; - } - } - - public class TestLoader extends ClassLoader { - public TestLoader(final ClassLoader parent) { - super(parent); - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - synchronized (getClassLoadingLock(name)) { - try { - Class clazz; - if (Data.class.getName().equals(name)) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream in = getResource(resourceName).openStream(); - int i; - while ((i = in.read()) != -1) { - baos.write(i); - } - byte[] bytes = baos.toByteArray(); - clazz = defineClass(name, bytes, 0, bytes.length); - } else { - clazz = super.loadClass(name, resolve); - } - if (resolve) { - resolveClass(clazz); - } - return clazz; - } catch (Exception e) { - throw new ClassNotFoundException("Unable to load class", e); - } - } - } - } -} diff --git a/pom.xml b/pom.xml index ada677def..dd78dfd88 100644 --- a/pom.xml +++ b/pom.xml @@ -26,9 +26,7 @@ not datatype, data format, or JAX-RS provider modules. afterburner-tests android-record - blackbird - - blackbird-tests + guice guice7 jakarta-xmlbind @@ -46,6 +44,57 @@ not datatype, data format, or JAX-RS provider modules. spi-subtypes + + + + blackbird-jdk25 + + [25,) + + + blackbird + + blackbird-tests + + blackbird-jpms-tests + + + + + release + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-jdk25-for-release + validate + + enforce + + + + + [25,) + Release builds need JDK 25 or later: on older JDKs the reactor leaves out jackson-module-blackbird. + + + + + + + + + + + https://github.com/FasterXML/jackson-modules-base scm:git:git@github.com:FasterXML/jackson-modules-base.git @@ -58,6 +107,10 @@ not datatype, data format, or JAX-RS provider modules. + + 17 UTF-8 diff --git a/release-notes/VERSION b/release-notes/VERSION index 3ec3bade4..3425e77cf 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -24,6 +24,24 @@ Active maintainers: 3.3.0 (not yet released) +#359: (blackbird) New code generation engine: per-bean codec classes, generated with + the JDK ClassFile API, replace the LambdaMetafactory accessors. The codecs also + cover records (through the canonical constructor) and builder-based beans. + Blackbird needs Java 25 or later at run time: the generators use the ClassFile API + (final in Java 24) and compile at release 25, the current LTS release. The jar is + a multi-release jar whose base classes stay at Java 17: on Java 17-24, module + discovery (`findAndAddModules()`) skips Blackbird with one logged warning, and an + explicit `addModule(new BlackbirdModule())` throws `IllegalStateException`. The + build needs JDK 25 for Blackbird, and a release build on an older JDK fails. + Removed public types: `deser.CreatorOptimizer`, `deser.ObjBooleanConsumer`, the + `ObjIntBiFunction`, `ObjLongBiFunction` and `ObjBooleanBiFunction` interfaces of + `BBDeserializerModifier`, `ser.ToBooleanFunction`, and all of `util` + (`CheckedFunction`, `CheckedSupplier`, `ReflectionHack`, `Sneaky`, `Unchecked`). + `BBDeserializerModifier` and `BBSerializerModifier` take only the lookup function + now. Blackbird no longer uses the lookup that `BlackbirdModule` constructors and + `findLookup()` overrides supply. The OSGi bundle exports only + `tools.jackson.module.blackbird`, as the JPMS module does. + (contributed by @stevenschlansker) #362: Bump `byte-buddy` dependency from 1.17.8 to 1.18.15 3.2.3 (21-Sep-2026)