Skip to content

Remove CrossLoaderAccess companion-class slow path from Blackbird (deprecated in 3.2) #350

Description

@cowtowncoder

Follow-up to #349 (blackbird-tests module). Deprecated in 3.2, targeting removal in 3.3.

What to remove

In blackbird/src/main/java/tools/jackson/module/blackbird/CrossLoaderAccess.java:

  • private static final String CLASS_NAME — the $$JacksonBlackbirdAccess constant
  • private static final int[] HEADER — pre-compiled Java 8 bytecode template
  • private static final int[] FOOTER — pre-compiled Java 8 bytecode template
  • private static Class<?> accessClassIn(MethodHandles.Lookup) — the method that defines the companion class at runtime via Lookup.defineClass(byte[])
  • The fall-through branch in grantAccess, after which grantAccess simplifies to:
    private static MethodHandles.Lookup grantAccess(MethodHandles.Lookup lookup) {
        return lookup;
    }
    (or remove grantAccess entirely and make CrossLoaderAccess.apply the identity function)

All four members are already annotated @Deprecated(since = "3.2", forRemoval = true) with javadoc pointing at this issue's rationale.

Why it's safe to remove

The slow path is never reached on JDK 9+ for any real input:

  1. BBDeserializerModifier.updateBuilder and BBSerializerModifier.findProperties both call ReflectionHack.privateLookupIn(beanClass, lookup) before delegating to CrossLoaderAccess.apply.
  2. On JDK 9+, MethodHandles.privateLookupIn always returns a lookup with hasFullPrivilegeAccess() == true whenever it succeeds. And it succeeds for every bean class Blackbird can see (including classpath / unnamed-module beans — the unnamed module is considered "exported to all" for privateLookupIn purposes).
  3. CrossLoaderAccess.grantAccess short-circuits as soon as hasFullAccess(lookup) is true, returning the lookup unchanged and never reaching the accessClassIn(...) call.
  4. Blackbird's 3.x baseline is Java 17, so the DEFINE_CLASS == null branch (the Java 8 fallback) is also unreachable.

The slow path was written in the Java 8 era when privateLookupIn didn't exist and callers had to synthesize a full-privilege lookup by defining a companion class in the target package. That scaffolding is no longer needed.

Verification

blackbird-tests/src/test/java/tools/jackson/module/blackbird/inject/CrossLoaderAccessTest.java (added in #349) pins the current behavior:

@Test
public void testFastPathWins_NoCompanionClassDefined() throws Exception {
    Harness h = newHarness();
    XLoaderBean bean = h.mapper.readValue(\"{\\\"a\\\":1,\\\"b\\\":\\\"hi\\\"}\", XLoaderBean.class);
    // ... setters are optimized, deserialization works ...
    assertFalse(companionClassExists(),
            \"CrossLoaderAccess unexpectedly defined $$JacksonBlackbirdAccess ...\");
}

This test currently passes on JDK 17. The removal PR should keep the test passing — if removing the slow path makes the test fail, that would be evidence the slow path was actually being hit and the deprecation was wrong; in that case the fix is to keep the method, remove the @Deprecated marker, and update the test to cover whichever scenario was hitting the slow path.

During the 3.2 → 3.3 development cycle, before starting the removal:

  1. Run blackbird-tests/CrossLoaderAccessTest on JDK 17, 21, and whatever is the latest LTS at that time. Confirm the fast-path assertion still holds.
  2. Grep for any downstream reports of classes named $$JacksonBlackbirdAccess appearing in production — that would be an existence proof that the slow path is live somewhere we haven't mapped.
  3. If any user has overridden BlackbirdModule.findLookup() or findLookupSupplier() to return a non-full-privilege Lookup (e.g. MethodHandles.publicLookup() or a deliberately reduced one), document the requirement that the returned lookup must satisfy hasFullPrivilegeAccess() after privateLookupIn — or add a runtime check that throws with a clear message, rather than silently degrading.

Scope

Small PR: ~100 lines deleted (most of them are the HEADER / FOOTER byte arrays), no new tests required (existing CrossLoaderAccessTest already covers the post-removal behavior), release notes entry.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    blackbirdIssue related to Blackbird modulegen-aiPR created with Generative AI (whole or assisted) (or issue for which gen-ai seems suitable)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions