From da6496142eba5a8a954d48018801fcebfe70990d Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Fri, 17 Jul 2026 09:54:59 -0400 Subject: [PATCH 1/2] Add @Strategy / @StrategyConsumer marker annotations for static-polymorphism strategies Marker-only (no enforcement yet): telegraphs the static-polymorphism strategy pattern and gives a future checker targets. @Strategy marks strategy types/parameters; @StrategyConsumer marks the higher-order methods that must inline for them to specialize. Contracts live in the javadoc. Applications land in stacked PRs (FlatHashtable first). Co-Authored-By: Claude Opus 4.8 --- .../datadog/trace/api/function/Strategy.java | 38 +++++++++++++++++++ .../trace/api/function/StrategyConsumer.java | 22 +++++++++++ 2 files changed, 60 insertions(+) create mode 100644 internal-api/src/main/java/datadog/trace/api/function/Strategy.java create mode 100644 internal-api/src/main/java/datadog/trace/api/function/StrategyConsumer.java diff --git a/internal-api/src/main/java/datadog/trace/api/function/Strategy.java b/internal-api/src/main/java/datadog/trace/api/function/Strategy.java new file mode 100644 index 00000000000..f7ca5852122 --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/function/Strategy.java @@ -0,0 +1,38 @@ +package datadog.trace.api.function; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a static-polymorphism strategy — a stateless, concrete-typed policy object the JIT + * can devirtualize and inline, so one shared algorithm specializes to straight-line code per caller + * (see the "static polymorphism" note on {@code FlatHashtable}). + * + *

This is a documentation-and-tooling marker; it changes no behavior. It exists to telegraph the + * pattern to readers and to give a future checker something to verify. The discipline it names is + * not yet enforced — hold to it by hand until the checker lands. + * + *

On a type ({@link ElementType#TYPE}): this type is a strategy. To get the + * specialization a caller must hold it in a {@code static final} field declared with the + * concrete type (not an abstract base or interface), and the consuming method must inline so + * the call site sees the exact type. Keep the methods small so they inline. + * + *

On a parameter ({@link ElementType#PARAMETER}): this parameter is a strategy slot. The + * argument at each call site should be a {@code static final} constant or a non-capturing + * lambda, so it stays a single monomorphic, allocation-free instance. A parameter can carry this + * marker even when its type cannot — e.g. a {@code java.util.function.Function} slot we don't own. + * + *

The failure mode is silent. Held at an abstract/interface type, filled with a capturing + * lambda, or called from a site that doesn't inline, it still compiles and runs correctly — it just + * stays megamorphic and/or allocates, quietly losing the win. Verify the hot ones with {@code + * -XX:+PrintInlining}. + */ +@Documented +@Inherited +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.PARAMETER}) +public @interface Strategy {} diff --git a/internal-api/src/main/java/datadog/trace/api/function/StrategyConsumer.java b/internal-api/src/main/java/datadog/trace/api/function/StrategyConsumer.java new file mode 100644 index 00000000000..448eba1ea7b --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/function/StrategyConsumer.java @@ -0,0 +1,22 @@ +package datadog.trace.api.function; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a higher-order method that consumes {@link Strategy} objects — one whose strategy + * parameters only specialize if this method itself inlines, so each call site sees the exact + * strategy type (see {@link Strategy}). Keep it small so it inlines. + * + *

Documentation-and-tooling marker; it changes no behavior. It pairs with {@link Strategy}: a + * strategy type/parameter says "I am a strategy / a strategy slot," while this says "I am the site + * where they must specialize." A future checker can enforce that the arguments filling those slots + * at these call sites are {@code static final} constants or non-capturing lambdas. + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface StrategyConsumer {} From e8b1f8f998d2ae156c244af6fc462a1cf1d8ba01 Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Mon, 20 Jul 2026 10:15:43 -0400 Subject: [PATCH 2/2] Explain static polymorphism inline in @Strategy javadoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review: describe 'static polymorphism' directly in the annotation rather than referring to FlatHashtable, which is a consumer of @Strategy — the reference was a conceptual back-reference that left the base annotation unable to stand on its own. Co-Authored-By: Claude Opus 4.8 --- .../datadog/trace/api/function/Strategy.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/function/Strategy.java b/internal-api/src/main/java/datadog/trace/api/function/Strategy.java index f7ca5852122..ef4329ac5e4 100644 --- a/internal-api/src/main/java/datadog/trace/api/function/Strategy.java +++ b/internal-api/src/main/java/datadog/trace/api/function/Strategy.java @@ -8,9 +8,21 @@ import java.lang.annotation.Target; /** - * Marks a static-polymorphism strategy — a stateless, concrete-typed policy object the JIT - * can devirtualize and inline, so one shared algorithm specializes to straight-line code per caller - * (see the "static polymorphism" note on {@code FlatHashtable}). + * Marks a static-polymorphism strategy: a stateless, concrete-typed policy object that lets + * one shared algorithm specialize to straight-line code per caller, without runtime virtual + * dispatch. + * + *

What "static polymorphism" means here. Ordinary (dynamic) polymorphism resolves the + * implementation at run time — an {@code invokevirtual}/{@code invokeinterface} that can go + * megamorphic on a shared call site. Static polymorphism instead makes the implementation known to + * the JIT: hold the strategy in a {@code static final} field of its concrete type (a stable + * constant of exact type), keep its methods small, and let the consuming method inline. The call + * site then sees the exact type, so the JIT devirtualizes the strategy's calls and inlines them, + * and the one generic algorithm compiles to specialized, monomorphic, allocation-free code per + * caller — C++-template-like specialization, driven by the JIT rather than a code generator. The + * win is structural (it follows from the exact-typed constant), not a speculative bet on + * class-hierarchy analysis or type profiling that a second implementation or a polluted profile + * could quietly undo. * *

This is a documentation-and-tooling marker; it changes no behavior. It exists to telegraph the * pattern to readers and to give a future checker something to verify. The discipline it names is