From 0774ad4c75f62bd9b957e56f532565628e677b8e Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 1/9] Build Blackbird on JDK 25 as a multi-release jar The next commits replace the Blackbird engine with code generators that use the ClassFile API (JEP 484, final in Java 24). Blackbird requires Java 25, the current LTS release. Set up the build for that first: - Pin the reactor to --release 17. The parent chain sets only source and target, so a build on a newer JDK could link against newer APIs. - Build blackbird and blackbird-tests only on JDK 25 or later (profile blackbird-jdk25). The other modules still build on JDK 17. - Fail a release build (-Prelease) on an older JDK, so that a release cannot leave out jackson-module-blackbird without an error. - Lay out blackbird as a multi-release jar: src/main/java25 compiles at release 25 into META-INF/versions/25, and all other classes stay at release 17. The directory is empty until the generators arrive. - Declare osgi.ee JavaSE 17 explicitly. bnd derives it from the newest class file, and bnd 6 maps class file 69 to UNKNOWN, which no framework resolves. - Run the release build of CI on JDK 25, so the deployed reactor has Blackbird. --- .github/workflows/main.yml | 4 +- blackbird/pom.xml | 130 +++++++++++++++++++++++++++++++++++++ pom.xml | 57 +++++++++++++++- 3 files changed, 187 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fad8c7fd5..15ac67867 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,9 @@ 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" diff --git a/blackbird/pom.xml b/blackbird/pom.xml index e673dd2e3..dbedfbcef 100644 --- a/blackbird/pom.xml +++ b/blackbird/pom.xml @@ -27,6 +27,11 @@ that uses LambdaMetafactory based code generation to replace reflection calls. + + 25 tools/jackson/module/blackbird ${project.groupId}.blackbird @@ -51,6 +56,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/pom.xml b/pom.xml index ada677def..c9e60dc12 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,55 @@ not datatype, data format, or JAX-RS provider modules. spi-subtypes + + + + blackbird-jdk25 + + [25,) + + + blackbird + + blackbird-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 +105,10 @@ not datatype, data format, or JAX-RS provider modules. + + 17 UTF-8 From a92af0ddf813695e9531676c9561de5bce63e0a3 Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 2/9] Replace the Blackbird deserializer engine with generated per-bean readers The LambdaMetafactory engine replaced individual property setters inside the stock BeanDeserializer loop. The new engine replaces the loop: for each eligible bean it emits one hidden class with the ClassFile API, which matches names with the parser's PropertyNameMatcher (the fused nextNameMatchAndToken call), dispatches on the match index with a tableswitch, reads String, int, long and boolean values with direct parser calls, stores through constant MethodHandles, and calls the codec of a nested bean directly. This commit covers POJOs that stock databind builds with its default creator. Records and builder-based beans keep the stock deserializer until the next commit. Design: - The codec is a stock BeanDeserializer. GeneratedReaderBase extends it and is a copy of the resolved stock deserializer, and the generated class overrides only deserialize(). Inputs the loop does not model go to the stock implementation of the same instance. Other modifiers, which often cast to BeanDeserializer, and the instanceof checks of databind see the type they expect. A placeholder (also a BeanDeserializer) stands in from the modifier to resolution, because generation needs the resolved properties. - One rule decides what the codec handles: a bean or property gets generated code only where that code does what stock does. Everything else keeps the stock deserializer (a gate) or the stock SettableBeanProperty inside the codec (a STOCK arm): Object Ids, non-standard creation (creators, external type ids, unwrapped properties), merge, polymorphic values, custom deserializers, property wrappers such as managed references, final fields. Ignorals, aliases and case-insensitive matching come from the resolved stock state. - Failures go through the stock helpers (wrapAndThrow, the instantiator failure handling, the stock property message), so exceptions and their reference paths match stock. - Member access uses handles unreflected with the public lookup after databind's own fixAccess, so Blackbird reaches exactly what stock can invoke and grants no access of its own. Codecs are defined in Blackbird's own module and unload with the mapper. - @JsonView: a codec of a bean that declares views keeps view-active calls on the generated path through a per-view property bitmask. - A generated method over 7000 bytes is discarded, and the bean keeps the stock deserializer: HotSpot does not compile methods over 8000 bytes. - Generation failures log at DEBUG through System.Logger and leave the stock deserializer. The test suites set tools.jackson.module.blackbird.failOnCodegenError, so a generator bug fails the build. blackbird.debug.codegen traces the gate decisions and blackbird.debug.dumpDir writes the generated class files. The generator is in src/main/java25. On Java 17 to 24 it is not visible, and Blackbird cannot generate codecs. A separate service provider, internal.DiscoveredBlackbirdModule, makes module discovery (findAndAddModules) skip Blackbird there with one warning, so discovery still registers every other module. An explicit registration of BlackbirdModule throws IllegalStateException, also when discovery runs before or after it: the provider has the registration id of BlackbirdModule only on a runtime that generates codecs, so elsewhere it cannot replace the explicit registration. blackbird-jpms-tests adds a foreign named module that requires Blackbird and checks codec engagement for its package-private beans on the module path. --- blackbird-jpms-tests/pom.xml | 58 ++ .../java/blackbird/jpms/test/beans/Beans.java | 16 + .../blackbird/jpms/test/beans/PkgBean.java | 14 + .../src/main/java/module-info.java | 15 + .../test/beans/JpmsPackagePrivateTest.java | 93 +++ .../inject/BlackbirdInjectionTestBase.java | 146 +--- .../inject/CrossLoaderAccessTest.java | 88 --- .../inject/FieldAccessNotOptimizedTest.java | 53 -- .../blackbird/inject/FieldAccessTest.java | 37 + .../inject/SetterOptimizationTest.java | 36 +- blackbird/src/main/java/module-info.java | 2 +- .../module/blackbird/BlackbirdModule.java | 40 +- .../blackbird/codegen/ClassFileEmitters.java | 75 ++ .../blackbird/codegen/CodegenDebug.java | 47 ++ .../module/blackbird/codegen/CodegenDump.java | 35 + .../blackbird/codegen/CodegenFallbacks.java | 46 ++ .../module/blackbird/codegen/Descs.java | 20 + .../blackbird/codegen/MemberHandles.java | 54 ++ .../blackbird/codegen/ReaderCodegen.java | 188 +++++ .../deser/BBDeserializerModifier.java | 322 ++------- .../blackbird/deser/BBReaderFactory.java | 300 ++++++++ .../blackbird/deser/BBReaderPlaceholder.java | 74 ++ .../blackbird/deser/CreatorOptimizer.java | 140 ---- .../blackbird/deser/ObjBooleanConsumer.java | 6 - .../deser/OptimizedSettableBeanProperty.java | 162 ----- .../deser/OptimizedValueInstantiator.java | 55 -- .../deser/SettableBooleanProperty.java | 78 --- .../blackbird/deser/SettableIntProperty.java | 70 -- .../blackbird/deser/SettableLongProperty.java | 70 -- .../deser/SettableObjectProperty.java | 92 --- .../deser/SettableStringProperty.java | 62 -- .../deser/SuperSonicBeanDeserializer.java | 342 ---------- .../deser/SuperSonicDeserializerBuilder.java | 35 - .../internal/DiscoveredBlackbirdModule.java | 35 + .../internal/GeneratedReaderBase.java | 180 +++++ .../blackbird/internal/ReaderSupport.java | 74 ++ .../module/blackbird/internal/ViewMasks.java | 55 ++ .../blackbird/internal/package-info.java | 7 + .../codegen/BeanReaderGenerator.java | 643 ++++++++++++++++++ .../tools.jackson.databind.JacksonModule | 3 +- blackbird/src/test/java/module-info.java | 3 +- .../module/blackbird/BlackbirdTestBase.java | 56 ++ .../blackbird/ModuleSPIMetadataTest.java | 5 +- .../blackbird/deser/BBCodecFieldTest.java | 195 ++++++ .../blackbird/deser/BBReaderUpdatingTest.java | 60 ++ .../blackbird/deser/OptionalDeser355Test.java | 39 +- .../DiscoveredBlackbirdModuleTest.java | 49 ++ pom.xml | 2 + 48 files changed, 2611 insertions(+), 1666 deletions(-) create mode 100644 blackbird-jpms-tests/pom.xml create mode 100644 blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/Beans.java create mode 100644 blackbird-jpms-tests/src/main/java/blackbird/jpms/test/beans/PkgBean.java create mode 100644 blackbird-jpms-tests/src/main/java/module-info.java create mode 100644 blackbird-jpms-tests/src/test/java/blackbird/jpms/test/beans/JpmsPackagePrivateTest.java delete mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/CrossLoaderAccessTest.java delete mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessNotOptimizedTest.java create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/FieldAccessTest.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDebug.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenFallbacks.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/Descs.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ReaderCodegen.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderFactory.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderPlaceholder.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/CreatorOptimizer.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/ObjBooleanConsumer.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedSettableBeanProperty.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/OptimizedValueInstantiator.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableBooleanProperty.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableIntProperty.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableLongProperty.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableObjectProperty.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SettableStringProperty.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicBeanDeserializer.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/deser/SuperSonicDeserializerBuilder.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModule.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedReaderBase.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/internal/ReaderSupport.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/internal/ViewMasks.java create mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/internal/package-info.java create mode 100644 blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanReaderGenerator.java create mode 100644 blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBCodecFieldTest.java create mode 100644 blackbird/src/test/java/tools/jackson/module/blackbird/deser/BBReaderUpdatingTest.java create mode 100644 blackbird/src/test/java/tools/jackson/module/blackbird/internal/DiscoveredBlackbirdModuleTest.java 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/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..4c10a8c6a 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 @@ -3,95 +3,54 @@ 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 tools.jackson.module.blackbird.internal.GeneratedReaderBase; -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. +// 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.) Serializer checks +// look for the optimized property writers of the LambdaMetafactory engine, +// which are package-private, by simple name. 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; + SerializationContext ctxt = mapper._serializationContext(); + return ctxt.handleRootContextualization(ctxt.findValueSerializer(cls)); } } - /** 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()); - } - return (SettableBeanProperty[]) reflectField(deser, "_propsByIndex"); + /** True if the deserializer is a generated Blackbird codec. */ + protected static boolean isBlackbirdReader(ValueDeserializer deser) { + return deser instanceof GeneratedReaderBase; } /** Returns the BeanPropertyWriter[] from a bean serializer, as a list. */ @@ -105,12 +64,9 @@ protected static List writersOf(ValueSerializer ser) { } /** 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. */ + * named {@code fieldName}. */ protected static Object reflectField(Object instance, String fieldName) { - Class origClass = instance.getClass(); - Class c = origClass; + Class c = instance.getClass(); while (c != null) { try { Field f = c.getDeclaredField(fieldName); @@ -120,56 +76,18 @@ protected static Object reflectField(Object instance, String fieldName) { c = c.getSuperclass(); } catch (IllegalAccessException e) { throw new AssertionError("cannot read field '" + fieldName + "' on " - + origClass.getName(), e); + + instance.getClass().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"); + + instance.getClass().getName()); } /** 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; + Class c = writer.getClass(); while (c != null) { - if (simpleName.equals(c.getSimpleName()) + if ("OptimizedBeanPropertyWriter".equals(c.getSimpleName()) && c.getPackageName().startsWith("tools.jackson.module.blackbird")) { return true; } 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/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/src/main/java/module-info.java b/blackbird/src/main/java/module-info.java index cbe9b3422..38c1f0110 100644 --- a/blackbird/src/main/java/module-info.java +++ b/blackbird/src/main/java/module-info.java @@ -9,5 +9,5 @@ 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..71abdf63b 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,26 @@ 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; +/** + * 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,12 +108,37 @@ 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.addDeserializerModifier(new BBDeserializerModifier(lookup)); context.addSerializerModifier(new BBSerializerModifier(lookup, openSesame)); } + 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 public String getModuleName() { return getClass().getSimpleName(); 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..c02812bb2 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java @@ -0,0 +1,75 @@ +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 emitter, {@code BeanReaderGenerator}. It uses the + * ClassFile API ({@code java.lang.classfile}) and compiles 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 + * it, and Blackbird cannot generate codecs (see + * {@code BlackbirdModule.setupModule}). The base classes find it by name + * because they cannot reference it 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", ReaderCodegen.Plan.class); + + private ClassFileEmitters() {} + + /** + * Whether this runtime sees the release-25 emitter: 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; + } + + // Returns null when a generated method is over MAX_METHOD_BYTES. + static byte[] emit(ReaderCodegen.Plan plan) { + return invoke(READER, plan); + } + + private static byte[] invoke(MethodHandle emitter, Object plan) { + if (emitter == null) { + throw new IllegalStateException("Blackbird class-file emitters are not available" + + " on this runtime"); + } + try { + return (byte[]) emitter.invoke(plan); + } 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, Class planType) { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + Class emitter = lookup.findClass( + ClassFileEmitters.class.getPackageName() + "." + simpleName); + return lookup.findStatic(emitter, "emit", + MethodType.methodType(byte[].class, planType)); + } 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..e922d3215 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java @@ -0,0 +1,35 @@ +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. 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) { + 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(beanClass.getName() + "-" + kind + ".class"), bytes); + } catch (IOException e) { + System.getLogger("tools.jackson.module.blackbird").log(Level.DEBUG, + "Generated-class dump failed for " + beanClass.getName(), 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..44a63f4b0 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java @@ -0,0 +1,54 @@ +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))); + } + + /** 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()))); + } + + /** No-arg constructor adapted to {@code ()Object}. */ + public static MethodHandle defaultConstructor(Constructor ctor) + throws IllegalAccessException { + return LOOKUP.unreflectConstructor(ctor) + .asType(MethodType.methodType(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..6d4cd3293 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ReaderCodegen.java @@ -0,0 +1,188 @@ +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; + +/** + * 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 as an + // (Object, eV)void handle, and is null for STOCK arms. type is the child + // value type for CHILD; child is the linked generated codec for CHILD + // (a GeneratedReaderBase). + 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 + } + + /** + * The construction side of a codec: the handle is the no-arg constructor + * ({@code ()Object}); the instantiator creates the bean (INSTANTIATOR). + */ + 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); + } + } + + /** + * 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, its aliases). + */ + record Plan(String className, List props, String[] stockName, + String[] childName, String[] handleName, Mode mode, boolean injectables, + ViewStrategy views, int[] matchArms) {} + + 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) + 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 -> classData.put("constructor", construction.handle()); + case INSTANTIATOR -> classData.put("instantiator", construction.instantiator()); + } + + 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)); + 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 GeneratedReaderBase). + MethodHandle ctor = hidden.findConstructor(hidden.lookupClass(), + MethodType.methodType(void.class, BeanDeserializer.class, 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/deser/BBDeserializerModifier.java b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBDeserializerModifier.java index 7ab360529..fbe23ee6b 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,104 @@ 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.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; - } - 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 doModify(config, beanDescRef, deserializer); + } catch (RuntimeException | LinkageError e) { + CodegenFallbacks.gateFailure(beanDescRef.getBeanClass(), e); + return deserializer; } - 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); - } + if (deserializer.getClass() != BeanDeserializer.class) { + return skip(beanDescRef, deserializer, "not a stock bean deserializer"); } - 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 (Boolean.TRUE.equals(config.getDefaultMergeable())) { + return skip(beanDescRef, deserializer, "mapper default mergeable"); } - // First: we can't access private fields or methods.... - if (Modifier.isPrivate(jdkMember.getModifiers())) { - 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"); } - // (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; - } + if (Modifier.isAbstract(beanClass.getModifiers())) { + return skip(beanDescRef, deserializer, "abstract"); } - - 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 (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; } - } 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.getPrimaryMember() != null + && config.getAnnotationIntrospector() + .findUnwrappingNameTransformer(config, def.getPrimaryMember()) != null) { + return skip(beanDescRef, deserializer, "unwrapped property"); + } + if (def.getMetadata() != null && def.getMetadata().getMergeInfo() != null) { + return skip(beanDescRef, deserializer, "property merge"); } } + return new BBReaderPlaceholder((BeanDeserializer) deserializer, declaresViews); } - 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()); - } - 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); - } - - 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..70b18b268 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderFactory.java @@ -0,0 +1,300 @@ +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.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import tools.jackson.databind.DeserializationContext; +import tools.jackson.databind.MapperFeature; +import tools.jackson.databind.ValueDeserializer; +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.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.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. + */ + record Source(BeanDeserializerBase deser, boolean injectables, + BeanPropertyMap beanProperties, boolean nonStandardCreation) {} + + 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"); + } + // 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); + } + + // 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) + 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(classify(prop)); + } + 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)); + } + + // 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; + } + + // 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); + } + + 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; + } + + 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..1f898436e --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/deser/BBReaderPlaceholder.java @@ -0,0 +1,74 @@ +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} 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, + _injectables != null, _beanProperties, _nonStandardCreation), + 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/GeneratedReaderBase.java b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedReaderBase.java new file mode 100644 index 000000000..f9d244f19 --- /dev/null +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/internal/GeneratedReaderBase.java @@ -0,0 +1,180 @@ +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.std.StdValueInstantiator; +import tools.jackson.databind.util.NameTransformer; + +/** + * Superclass for generated codecs. 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); + } + + /* + /********************************************************************** + /* 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: + // 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/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/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..2fc0a15ae --- /dev/null +++ b/blackbird/src/main/java25/tools/jackson/module/blackbird/codegen/BeanReaderGenerator.java @@ -0,0 +1,643 @@ +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.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.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.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_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_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_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_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_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_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_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() {} + + 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 = CD_READER_BASE; + MethodTypeDesc 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 -> 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: 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; + } + + private static void buildDeserialize(CodeBuilder cob, ReaderCodegen.Plan plan) { + final ClassDesc base = CD_READER_BASE; + 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); + 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]); + 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); + } + cob.labelBinding(armEnd); + nextNameMatch(cob, parser, matcherSlot, ixSlot); + cob.goto_(loop); + } + + cob.labelBinding(defaultCase); + throwIse(cob, "bad property index"); + + cob.labelBinding(endObject); + 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, "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); + } + } + + // A generated store: the class-data name of its (Object, eV)void handle + // and the labels of its exception region. + private record Store(String handleName, Label start, Label end) {} + + private static void emitStore(CodeBuilder cob, Store store, ClassDesc erasedDesc) { + cob.labelBinding(store.start()); + 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) { + 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, erasedDesc); + cob.goto_(done); + cob.labelBinding(useStock); + emitStockSet(cob, parser, ctxt, receiverSlot, stockName); + 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, CD_OBJECT); + cob.goto_(childDone); + cob.labelBinding(childStock); + emitStockSet(cob, parser, ctxt, receiverSlot, stockName); + cob.labelBinding(childDone); + } + + // The constant receiver makes the call monomorphic. + 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). + private static void emitStockSet(CodeBuilder cob, int parser, int ctxt, int receiverSlot, + String stockName) { + ldcData(cob, stockName, CD_SETTABLE_PROP); + cob.aload(parser).aload(ctxt).aload(receiverSlot); + cob.invokevirtual(CD_SETTABLE_PROP, "deserializeAndSet", MTD_DESERIALIZE_AND_SET); + } + + // 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; + } + + 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); + 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/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/test/java/module-info.java b/blackbird/src/test/java/module-info.java index bd51578dc..38610c204 100644 --- a/blackbird/src/test/java/module-info.java +++ b/blackbird/src/test/java/module-info.java @@ -31,6 +31,7 @@ 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; @@ -41,6 +42,6 @@ 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..1dd6bfe4d 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,14 @@ import tools.jackson.core.*; +import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.ValueDeserializer; 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.GeneratedReaderBase; import static org.junit.jupiter.api.Assertions.*; @@ -213,6 +217,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 +234,51 @@ 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)); + } + + /** + * 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(); + } + 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, + type.getSimpleName() + " should keep the stock deserializer, uses " + d); + } + /* /********************************************************** /* 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/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/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/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/pom.xml b/pom.xml index c9e60dc12..dd78dfd88 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,8 @@ not datatype, data format, or JAX-RS provider modules. blackbird blackbird-tests + + blackbird-jpms-tests + 25 + 25 + 25 + + + + tools.jackson.module @@ -158,6 +167,10 @@ and is never published. maven-surefire-plugin false + + + ${blackbird.test.oldJdks} + 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 c99655b26..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,26 +1,20 @@ package tools.jackson.module.blackbird.inject; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.SerializationContext; import tools.jackson.databind.ValueDeserializer; import tools.jackson.databind.ValueSerializer; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.ser.BeanPropertyWriter; 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; // 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.) Serializer checks -// look for the optimized property writers of the LambdaMetafactory engine, -// which are package-private, by simple name. +// placeholder that stands in for either until resolution.) abstract class BlackbirdInjectionTestBase { static { @@ -54,46 +48,8 @@ protected static boolean isBlackbirdReader(ValueDeserializer deser) { return (deser instanceof GeneratedReaderBase) || (deser instanceof GeneratedBuilderBase); } - /** 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; - } - - /** Walks the class chain of {@code instance} looking for a declared field - * named {@code fieldName}. */ - protected static Object reflectField(Object instance, String fieldName) { - Class c = instance.getClass(); - 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 " - + instance.getClass().getName(), e); - } - } - throw new AssertionError("field '" + fieldName + "' not found on " - + instance.getClass().getName()); - } - - /** True if `writer`'s class chain contains Blackbird's OptimizedBeanPropertyWriter. */ - protected static boolean isOptimizedWriter(BeanPropertyWriter writer) { - Class c = writer.getClass(); - while (c != null) { - if ("OptimizedBeanPropertyWriter".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/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/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/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java b/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java index 71abdf63b..29898d258 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/BlackbirdModule.java @@ -17,6 +17,14 @@ 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 @@ -125,9 +133,8 @@ public void setupModule(SetupContext context) + " (jackson-module-afterburner supports Java 17 and later)."); } Function, Lookup> lookup = findLookup(); - CrossLoaderAccess openSesame = new CrossLoaderAccess(); context.addDeserializerModifier(new BBDeserializerModifier(lookup)); - context.addSerializerModifier(new BBSerializerModifier(lookup, openSesame)); + context.addSerializerModifier(new BBSerializerModifier(lookup)); } private static String _unavailableReason() { 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 index c02812bb2..05081d3ef 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/ClassFileEmitters.java @@ -5,13 +5,15 @@ import java.lang.invoke.MethodType; /** - * Locates the class-file emitter, {@code BeanReaderGenerator}. It uses the - * ClassFile API ({@code java.lang.classfile}) and compiles at release 25 into + * 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 - * it, and Blackbird cannot generate codecs (see - * {@code BlackbirdModule.setupModule}). The base classes find it by name - * because they cannot reference it at compile time. + * 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 { @@ -25,32 +27,45 @@ public final class ClassFileEmitters */ public static final int MAX_METHOD_BYTES = 7000; - private static final MethodHandle READER = - find("BeanReaderGenerator", ReaderCodegen.Plan.class); + 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 emitter: false on Java 17 to + * 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; + return (READER != null) && (WRITER != null) && (WRITE_HELPERS != null); } - // Returns null when a generated method is over MAX_METHOD_BYTES. + // Both return null when a generated method is over MAX_METHOD_BYTES. static byte[] emit(ReaderCodegen.Plan plan) { return invoke(READER, plan); } - private static byte[] invoke(MethodHandle emitter, Object 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.invoke(plan); + return (byte[]) emitter.invokeWithArguments(args); } catch (RuntimeException | Error e) { throw e; } catch (Throwable t) { @@ -61,13 +76,12 @@ private static byte[] invoke(MethodHandle emitter, Object plan) { // 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, Class planType) { + 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, "emit", - MethodType.methodType(byte[].class, planType)); + return lookup.findStatic(emitter, method, type); } catch (ReflectiveOperationException | LinkageError e) { return null; } 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 index e922d3215..4190935d2 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/CodegenDump.java @@ -9,7 +9,8 @@ * 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. A dump failure never affects + * 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 @@ -19,6 +20,10 @@ 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; @@ -26,10 +31,10 @@ static void dump(Class beanClass, String kind, byte[] bytes) { try { Path path = Path.of(dir); Files.createDirectories(path); - Files.write(path.resolve(beanClass.getName() + "-" + kind + ".class"), bytes); + Files.write(path.resolve(fileStem + ".class"), bytes); } catch (IOException e) { System.getLogger("tools.jackson.module.blackbird").log(Level.DEBUG, - "Generated-class dump failed for " + beanClass.getName(), e); + "Generated-class dump failed for " + fileStem, e); } } } 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 index 7a281676c..3fc53f437 100644 --- a/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java +++ b/blackbird/src/main/java/tools/jackson/module/blackbird/codegen/MemberHandles.java @@ -64,6 +64,18 @@ public static MethodHandle fieldSetter(Field field) throws IllegalAccessExceptio .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 { 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/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/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/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/test/java/module-info.java b/blackbird/src/test/java/module-info.java index 38610c204..813d8b4b2 100644 --- a/blackbird/src/test/java/module-info.java +++ b/blackbird/src/test/java/module-info.java @@ -37,7 +37,6 @@ 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; 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 50f6d5b4c..f865b1f77 100644 --- a/blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java +++ b/blackbird/src/test/java/tools/jackson/module/blackbird/BlackbirdTestBase.java @@ -9,13 +9,16 @@ 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.*; @@ -247,6 +250,12 @@ protected static ValueDeserializer rootReader(ObjectMapper mapper, Class 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 @@ -283,6 +292,21 @@ protected static void assertStockReader(ObjectMapper mapper, Class type) { 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/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/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/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); - } - } - } - } -} From 01bdb869b6beec18d340b9a640cb7dfe9b3a49c2 Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 5/9] Remove the rest of the LambdaMetafactory engine Remove CrossLoaderAccess and its generator, the util package, and the moditect descriptor that the build no longer reads. Nothing in the module uses java.util.logging now, so the module no longer requires java.logging. Export only tools.jackson.module.blackbird from the OSGi bundle, as the JPMS descriptor does. The codecs are defined in Blackbird's own module, so no user code links to the internal packages. PackagingTest checks the module descriptor, the bundle exports, the release guard of the root POM, and that the member handles grant no access of their own. --- .../module/blackbird/jdk/PackagingTest.java | 155 ++++++++++ blackbird/GenBlackbirdAccess.java | 276 ------------------ blackbird/pom.xml | 2 + blackbird/src/main/java/module-info.java | 2 - .../module/blackbird/CrossLoaderAccess.java | 184 ------------ .../blackbird/util/CheckedFunction.java | 21 -- .../blackbird/util/CheckedSupplier.java | 21 -- .../module/blackbird/util/ReflectionHack.java | 87 ------ .../jackson/module/blackbird/util/Sneaky.java | 47 --- .../module/blackbird/util/Unchecked.java | 66 ----- blackbird/src/moditect/module-info.java | 11 - blackbird/src/test/java/module-info.java | 2 - 12 files changed, 157 insertions(+), 717 deletions(-) create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/jdk/PackagingTest.java delete mode 100644 blackbird/GenBlackbirdAccess.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/CrossLoaderAccess.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedFunction.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/util/CheckedSupplier.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/util/ReflectionHack.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/util/Sneaky.java delete mode 100644 blackbird/src/main/java/tools/jackson/module/blackbird/util/Unchecked.java delete mode 100644 blackbird/src/moditect/module-info.java 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/pom.xml b/blackbird/pom.xml index dbedfbcef..c33e91a89 100644 --- a/blackbird/pom.xml +++ b/blackbird/pom.xml @@ -32,6 +32,8 @@ that uses LambdaMetafactory based code generation to replace reflection calls. compile at release 25 and ship under META-INF/versions/25 (see the build section). Tests run on JDK 25+ only. --> 25 + + tools.jackson.module.blackbird;version=${project.version} tools/jackson/module/blackbird ${project.groupId}.blackbird diff --git a/blackbird/src/main/java/module-info.java b/blackbird/src/main/java/module-info.java index 38c1f0110..a7c4b8a44 100644 --- a/blackbird/src/main/java/module-info.java +++ b/blackbird/src/main/java/module-info.java @@ -1,8 +1,6 @@ // Blackbird Main artifact Module descriptor module tools.jackson.module.blackbird { - requires java.logging; - requires tools.jackson.core; requires transitive tools.jackson.databind; 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/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/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 813d8b4b2..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; From fae8c0a84b3af9002181f14f40e23165b29c4c89 Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 6/9] Add differential tests of Blackbird against stock databind Each scenario runs through a stock mapper and a Blackbird mapper built from the same configuration. The two must give the same value, or the same exception class and message with the same reference path. Then the scenario asserts which path Blackbird took: the generated codec, a stock property inside it, or the stock (de)serializer of the whole bean. The check looks at the (de)serializer that the mapper uses, not at the placeholder that a modifier sees. The scenarios cover polymorphic values and default typing, non-standard creation, property wrappers, ignoral sets, absent record components, construction, setter and build failures, builder setters that return null, null serializers, self-references, and beans whose generated methods would be too large for the JIT. --- .../blackbird/diff/DifferentialTestBase.java | 177 +++++++++ .../blackbird/diff/ReaderErrorsDiffTest.java | 294 +++++++++++++++ .../blackbird/diff/ReaderGatesDiffTest.java | 348 ++++++++++++++++++ .../blackbird/diff/RecordsDiffTest.java | 300 +++++++++++++++ .../module/blackbird/diff/WideBeanTest.java | 134 +++++++ .../module/blackbird/diff/WriterDiffTest.java | 310 ++++++++++++++++ 6 files changed, 1563 insertions(+) create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/DifferentialTestBase.java create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderErrorsDiffTest.java create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/ReaderGatesDiffTest.java create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/RecordsDiffTest.java create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WideBeanTest.java create mode 100644 blackbird-tests/src/test/java/tools/jackson/module/blackbird/diff/WriterDiffTest.java 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()); + } +} From a3849dd73b8fd99081447bc3b02c6aff656cc55c Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 7/9] Update the Blackbird benchmarks for the generated-codec engine Build the benchmarks against the current version, and select the engine with a mode parameter (stock, blackbird, afterburner) instead of one subclass per engine. A classic-blackbird profile builds the same benchmarks against Blackbird 3.2.2, the last release of the LambdaMetafactory engine, for comparison. The uber jar is a multi-release jar, so that Blackbird sees its release-25 generators. Add benchmarks for records, builder-based beans, beans with 40 and 200 properties, and the cold start of 500 types in a fresh JVM. Remove the JDK 14 result files of the old engine. --- blackbird/benchmarks/README.md | 233 +- blackbird/benchmarks/pom.xml | 36 +- .../benchmarks/results/profile_graal.txt | 978 ----- .../benchmarks/results/results_graal.txt | 3308 ---------------- blackbird/benchmarks/results/results_hs.txt | 3310 ----------------- .../jackson/module/blackbird/Afterburner.java | 24 - .../jackson/module/blackbird/Blackbird.java | 23 - .../module/blackbird/BuilderBenchmark.java | 55 + .../jackson/module/blackbird/ColdPojo.java | 34 + .../module/blackbird/ColdStartBenchmark.java | 58 + .../jackson/module/blackbird/ColdTypes.java | 513 +++ .../jackson/module/blackbird/Invoice.java | 78 + .../jackson/module/blackbird/Mappers.java | 26 + .../tools/jackson/module/blackbird/Order.java | 19 + ...{BaseBenchmark.java => PojoBenchmark.java} | 89 +- .../module/blackbird/RecordBenchmark.java | 55 + .../blackbird/StartupTimeBenchmark.java | 58 - .../jackson/module/blackbird/Vanilla.java | 22 - .../jackson/module/blackbird/Wide200.java | 610 +++ .../jackson/module/blackbird/Wide40.java | 130 + .../module/blackbird/WideBeanBenchmark.java | 84 + 21 files changed, 1829 insertions(+), 7914 deletions(-) delete mode 100644 blackbird/benchmarks/results/profile_graal.txt delete mode 100644 blackbird/benchmarks/results/results_graal.txt delete mode 100644 blackbird/benchmarks/results/results_hs.txt delete mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Afterburner.java delete mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Blackbird.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/BuilderBenchmark.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdPojo.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdStartBenchmark.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/ColdTypes.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Invoice.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Mappers.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Order.java rename blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/{BaseBenchmark.java => PojoBenchmark.java} (52%) create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/RecordBenchmark.java delete mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/StartupTimeBenchmark.java delete mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Vanilla.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide200.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/Wide40.java create mode 100644 blackbird/benchmarks/src/main/java/tools/jackson/module/blackbird/WideBeanBenchmark.java 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); + } +} From 3cfed62779a838f97ddaad26de50f3a6822b8b21 Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 8/9] Document the new Blackbird engine Rewrite the Blackbird README for the generated codecs: requirements (Java 25 at run time, the behavior on Java 17-24, JDK 25 for the build, Multi-Release: true in an uber jar), what the codecs handle and where Blackbird keeps stock code, debugging switches, and benchmark results against stock databind and Blackbird 3.2.2. Add release notes that also list the removed public types, and update the POM descriptions and the blackbird-tests README. --- CLAUDE.md | 4 +- blackbird-tests/README.md | 112 ++++++------------- blackbird-tests/pom.xml | 18 +-- blackbird/README.md | 228 +++++++++++++++++++++++++++----------- blackbird/pom.xml | 3 +- release-notes/VERSION | 18 +++ 6 files changed, 227 insertions(+), 156 deletions(-) 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-tests/README.md b/blackbird-tests/README.md index 545450afb..38fdc2408 100644 --- a/blackbird-tests/README.md +++ b/blackbird-tests/README.md @@ -5,50 +5,46 @@ 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. +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. -**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: +## What is covered -- **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. +- `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. ## 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 +53,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/pom.xml b/blackbird-tests/pom.xml index b71c845a6..18c31701a 100644 --- a/blackbird-tests/pom.xml +++ b/blackbird-tests/pom.xml @@ -10,16 +10,9 @@ 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. @@ -159,9 +152,8 @@ 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 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/pom.xml b/blackbird/pom.xml index c33e91a89..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 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) From 1d91be5d7bd870a03cb33ee03672432969fc0659 Mon Sep 17 00:00:00 2001 From: Steven Schlansker Date: Wed, 14 Oct 2026 18:18:39 -0700 Subject: [PATCH 9/9] Add the databind test corpus as a manual check for Blackbird changes blackbird-tests/corpus runs the whole jackson-databind test suite with BlackbirdModule registered in every mapper, and compares the failures with a list of expected failures. The hook is a 10-line patch to the ObjectMapper(MapperBuilder) constructor of databind, active only with -Dblackbird.corpus=true. The runner passes the Blackbird jar, the module exports and the strict code generation switch to the databind build on the Maven command line, and changes no POM. The build does not run the corpus: databind publishes no test jar, and a CI job that patches databind main code breaks when databind changes that constructor. Maintainers run it manually before they submit a Blackbird change. The blackbird-tests README says how. With databind f75dc9039fe0 the only failures are 7 tests that assert the exact list or number of registered modules, which the harness changes. --- blackbird-tests/README.md | 82 ++++++++++++++ blackbird-tests/corpus/corpus.sh | 101 ++++++++++++++++++ .../corpus/databind-register-blackbird.patch | 20 ++++ blackbird-tests/corpus/expected-failures.txt | 11 ++ blackbird-tests/corpus/failures.py | 28 +++++ blackbird-tests/corpus/stripmod.py | 10 ++ 6 files changed, 252 insertions(+) create mode 100755 blackbird-tests/corpus/corpus.sh create mode 100644 blackbird-tests/corpus/databind-register-blackbird.patch create mode 100644 blackbird-tests/corpus/expected-failures.txt create mode 100644 blackbird-tests/corpus/failures.py create mode 100644 blackbird-tests/corpus/stripmod.py diff --git a/blackbird-tests/README.md b/blackbird-tests/README.md index 38fdc2408..a24fbd4e7 100644 --- a/blackbird-tests/README.md +++ b/blackbird-tests/README.md @@ -41,6 +41,88 @@ 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 Adding a module descriptor would put the test beans back into a named module 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))