Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion .agents/languages/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,30 @@ Load this file when changing anything under `java/` or when Java drives a cross-
- For GraalVM, use `fory codegen` to generate serializers when building native images. Do not add reflection configuration except for JDK `proxy`.
- In Java native mode (`xlang=false`), only `Types.BOOL` through `Types.STRING` share type IDs with xlang mode. Other native-mode type IDs differ.
- Choose one serializer ownership location per logical Java type family. Add native/xlang serializer variants only when the wire format or constructor contract truly differs.
- Do not add normal-JVM process-global caches keyed by user classes, generated classes, serializer classes, classloaders, or class-bound method handles. Prefer per-runtime state, immutable shared metadata, or build-time-only template data.
- Fory JSON must keep one immutable static set of exact Java types whose default representation is
implemented by type-specific `JsonReader` or `JsonWriter` methods. The set contains all primitive
scalar types and their boxed classes, `String`, `CharSequence`, `Number`, `BigInteger`,
`BigDecimal`, `UUID`, `LocalDate`, `LocalTime`, `LocalDateTime`, `Instant`, `Duration`, `ZoneOffset`,
`ZonedDateTime`, `Year`, `YearMonth`, `MonthDay`, `Period`, `OffsetTime`, `OffsetDateTime`,
`byte[]`, `String[]`, and `long[]`. Exact codec registration, exact codec-factory registration,
and exact factory handled-runtime-class claims for these types must fail before registry mutation
on every Java runtime, not only in GraalVM. Do not expand this set to every default codec: enums,
other arrays, collections, maps, atomics, optionals, `File`, `URI`, `Path`, `ByteBuffer`, calendar
and locale types, `Float16`, `BFloat16`, and user-defined types remain registerable. Field/type
`@JsonCodec`, `@JsonFormat`, and semantic metadata remain separate from exact registry mutation
and are fixed by the target class or effective Mixin.
- Fory JSON `ObjectCodec` instances are resolver-owned and must not be registered directly. A
language module that supplies a custom object model must use a `JsonCodecFactory`. A configurable
factory's stable key must cover every option that can change its created codec class, object
model, or generated operations.
- Do not add normal-JVM process-global caches keyed by user classes, generated classes, serializer
classes, classloaders, or class-bound method handles. Prefer per-runtime state, immutable shared
metadata, or build-time-only template data. The only exception is Fory JSON's generated-role
class cache in `JsonCodegen`, backed by `ClassValueCache.newClassKeySoftCache`: ordinary-JVM
values may contain only generated-class keys, binary names, and completed generated classes, not
codec instances, resolvers, configured classloaders, or `CodeGenerator`. Its GraalVM branch may
be strong only during hosted analysis and must be reset after the frozen Native registry is
published. Do not extend this exception to another cache or retained value.
- Concrete serializers may opt into sharing only after auditing retained fields. Treat serializers retaining `TypeResolver`, `RefResolver`, mutable scratch buffers, runtime state, or classloader-sensitive state as non-shareable unless that state is externalized.
- Resolver and serializer hot paths should keep the fast-path/null-slow-path shape obvious. Hoist repeated buffer or cache-state access into locals for multi-step operations and keep rebuild/restoration logic cold.
- Remote metadata and class-token paths that materialize Java classes must keep
Expand Down
5 changes: 3 additions & 2 deletions docs/json/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ construction operations. In Android builds that use R8 or ProGuard, Kotlin KSP e
retention rules for Kotlin `@JsonType` models. It also processes an exact Mixin declared in
application source when either the Mixin or its exact target is Kotlin. KSP does not generate codecs
or construction operations. GraalVM Native Image discovers reachable Java and Kotlin `JsonType`
declarations directly, and provider-selected configurations generate codecs while the image is
built. See the [GraalVM guide](graalvm.md) and [Android guide](android.md) for the platform workflows.
declarations directly. It generates codecs with the default configuration and each reachable
provider configuration; models without a matching generated codec use interpreted codecs. See the
[GraalVM guide](graalvm.md) and [Android guide](android.md) for the platform workflows.

## Kotlin use-site targets

Expand Down
25 changes: 23 additions & 2 deletions docs/json/custom-codecs.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@ ForyJson json =
.build();
```

Exact `registerCodec` and exact-class factory registration are not allowed for types with
dedicated reader/writer operations:

- `boolean`, `byte`, `short`, `int`, `long`, `float`, `double`, and `char`, including their boxed
classes
- `String`, `CharSequence`, `Number`, `BigInteger`, `BigDecimal`, and `UUID`
- `LocalDate`, `LocalTime`, `LocalDateTime`, `Instant`, `Duration`, `ZoneOffset`, `ZonedDateTime`,
`Year`, `YearMonth`, `MonthDay`, `Period`, `OffsetTime`, and `OffsetDateTime`
- `byte[]`, `String[]`, and `long[]`

The restriction is exact; it does not include application subclasses. It also does not disable
occurrence-level `JsonCodec`, `JsonFormat`, or other semantic mappings. Use those mechanisms when a
field or parameter of one of these types needs a different representation.

`ObjectCodec` instances belong to the resolver that created them and cannot be registered directly.
Use an exact `JsonCodecFactory` when a language module needs to supply an object model.

Use `JsonCodecFactory` when one factory owns a family of declared or parameterized types:

```java
Expand All @@ -87,6 +104,10 @@ ForyJson json =
.build();
```

A configurable factory must override `factoryKey()` with a deterministic value covering every
option that can change the created codec class, object model, or generated operations. The default
factory class name is sufficient only for a configuration-free factory.

`runtimeType` is `true` only when the factory is selecting a codec for the actual class of a value
during a dynamic write. Declared roots and composite child types receive `false`. A composite codec
that needs this distinction after construction must retain the flag for its later `resolveTypes`
Expand Down Expand Up @@ -310,8 +331,8 @@ decoded keys must match the declared key type.

An annotation codec class must be public, concrete, top-level or static nested, and have a public
no-argument constructor. One instance is shared by all annotated sites and concurrent operations of
the built `ForyJson`, so it must be thread-safe. Use `registerCodec(Target.class, instance)` when a
complete-value codec needs configuration.
the built `ForyJson`, so it must be thread-safe. For an eligible type, use
`registerCodec(Target.class, instance)` when a complete-value codec needs configuration.

Outside GraalVM Native Image, a named Java module must export or open the codec package to
`org.apache.fory.json`. Native Image prepares annotation-codec constructors during image
Expand Down
38 changes: 20 additions & 18 deletions docs/json/graalvm.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ public class JsonExample {
```

This is sufficient for correct native execution. During image construction, Fory JSON retains the
model metadata and prepares its field, property, creator, record, and `JsonAnySetter` access. At
runtime, `ForyJson.builder().build()` can therefore use interpreted codecs without application
reflection configuration, package exports or opens, or build-time initialization.
model metadata and prepares its field, property, creator, record, and `JsonAnySetter` access. It
also generates codecs for reachable models under the default configuration. At runtime,
`ForyJson.builder().build()` uses those generated codecs and falls back to interpreted codecs when
no matching generated codec is available, without application reflection configuration, package
exports or opens, or build-time initialization.

An application class configured for build-time initialization may retain a static `ForyJson` in the
image heap. Set `withConcurrencyLevel` explicitly when the runtime may have a different processor
Expand All @@ -69,8 +71,8 @@ it does not create the runtime instance.

## Generated Codecs

To include generated codecs for a configuration, return that completed configuration from a
reachable `@ForyJsonProvider`:
The default configuration is generated automatically. To add generated codecs for a custom
configuration, return that completed configuration from a reachable `@ForyJsonProvider`:

```java
import org.apache.fory.json.ForyJson;
Expand All @@ -97,19 +99,19 @@ public final class JsonConfigs {
The provider class must be public and concrete and have a public no-argument constructor. Provider
members are public, non-static, zero-argument instance methods whose exact return type is
`ForyJson`. Inherited superclass methods and public interface default methods are included. A
provider may return multiple configurations, and multiple providers may be reachable. Equivalent
configurations are generated once.
provider may return multiple configurations, and multiple providers may be reachable.

Provider objects exist only while the image is built. Prefer a dedicated configuration class with
instance fields and methods as shown above; no application `native-image.properties` entry is
needed, and the provider package does not need to be exported or opened to Fory. Static provider
methods and fields are not supported.

Only configurations returned by a provider receive generated codecs. The default configuration is
not generated implicitly. If a codegen-enabled `ForyJson` configuration was not included, ordinary
Java models and complete value codecs use their prepared interpreted codecs, and Fory JSON logs one
process-wide warning recommending a reachable `@ForyJsonProvider`. Language-module object models
that require hosted capabilities fail before reading or writing a value. `withCodegen(false)`
Default-configuration codecs remain available when providers are present, and every reachable
provider adds codecs for its configuration. A codegen-enabled runtime uses an interpreted codec
whenever no matching generated codec is available. Reflection metadata remains available in either
case.

`withCodegen(false)`
explicitly selects interpreted codecs and does not request generated-codec lookup. Asynchronous
compilation is disabled in a native executable.

Expand All @@ -131,14 +133,14 @@ class JsonConfigs {

Annotate each reachable concrete Kotlin model with `@JsonType`, or register an exact reachable
Mixin for a third-party target. Fory reads and validates Kotlin metadata while building the image,
then generates the provider-selected codecs. A provider configuration with disabled code
generation or an unsupported metadata ABI fails image construction. A Kotlin-enabled runtime
configuration that was not returned by a provider fails before it reads or writes a Kotlin object;
it never falls back to reflective construction.
then generates codecs for each reachable Kotlin-enabled provider configuration. A provider
configuration with disabled code generation or an unsupported metadata ABI fails image
construction. A Kotlin-enabled runtime configuration with no matching generated codec uses its
prepared interpreted codec.

An exact generic Kotlin root is available only when its complete binding is reached through a
property, constructor argument, container/map child, or closed subtype of a provider-selected
concrete root. Keep using `jsonTypeRef<T>()` at the direct root call; no public root registry or
property, constructor argument, container/map child, or closed subtype of a reachable concrete
root. Keep using `jsonTypeRef<T>()` at the direct root call; no public root registry or
reflection configuration is needed.

## Mixins
Expand Down
7 changes: 4 additions & 3 deletions docs/json/kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,10 @@ See [Security](security.md) before decoding untrusted input.
On GraalVM Native Image, use the existing `@ForyJsonProvider` workflow, install
`ForyJsonKotlin`, and enable code generation in the returned configuration. Annotate each reachable
concrete Kotlin model with `@JsonType`, or register an exact reachable Mixin for a third-party
target. Fory reads the Kotlin metadata and prepares generated codecs while building the image.
Only exact generic bindings reachable through provider-selected concrete roots are available. Do
not add reflection configuration or package-wide opens.
target. Fory reads the Kotlin metadata and adds generated codecs for each reachable Kotlin-enabled
provider configuration while building the image. A model without a matching generated codec uses
the interpreted codec. Only exact generic bindings reached through concrete roots are available.
Do not add reflection configuration or package-wide opens.

On Android, use API 26 or later. The runtime reads Kotlin metadata in both debug and release builds,
and runtime JSON code generation remains disabled. Follow the [installation](#installation) above
Expand Down
11 changes: 6 additions & 5 deletions docs/json/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ concurrent operations and must be thread-safe.

Codec implementations and `JsonCodecFactory` behavior are documented in
[Custom Codecs](custom-codecs.md). Modules only package those registrations for installation.
Exact module registrations reject the same dedicated scalar and array types listed there; use an
occurrence annotation or semantic mapping for those representations.

Application registrations made directly on `ForyJsonBuilder` take precedence over module exact
registrations. Conflicting module registrations fail during `build()` instead of depending on
Expand All @@ -91,12 +93,11 @@ precedence. See [Kotlin](kotlin.md) for type tokens and optional Android minific

## Module Identity

`moduleKey()` identifies the module configuration for generated-code reuse and conflict checking.
The default key is the module class name and is sufficient for a configuration-free module.
`moduleKey()` identifies the module configuration for installation conflict checking. The default
key is the module class name and is sufficient for a configuration-free module.

A configurable module must return a deterministic key that includes every option affecting codec
selection or generated code. Do not include secrets, mutable process state, or values unrelated to
the installed JSON behavior.
A configurable module must return a deterministic key that includes every option affecting its
installed JSON behavior. Do not include secrets, mutable process state, or unrelated values.

```java
public final class ConfiguredJsonModule implements ForyJsonModule {
Expand Down
10 changes: 5 additions & 5 deletions docs/json/object-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ original key type. Null map keys are rejected.
| `withMaxCachedFieldNames(int)` | `DEFAULT_MAX_CACHED_FIELD_NAMES` (`8192`) | Field-name cache entries per reader; zero disables caching |
| `withConcurrencyLevel(int)` | `max(1, 2 * processors)` | Maximum concurrent root operations |
| `withBufferSizeLimitBytes(int)` | 2 MiB | Maximum reusable capacity retained by each pooled writer |
| `registerCodec(type, codec)` | None | Replace the exact class's complete JSON codec |
| `registerCodec(type, codec)` | None | Replace an eligible exact class's complete JSON codec |
| `registerMixin(mixinType)` | None | Apply one annotation Mixin to its exact declared target |

Concurrency-level and buffer-retention limits must be positive. The cached-field-name limit
Expand All @@ -229,7 +229,7 @@ see [Fory JSON Security](security.md).
Builder mutation after `build()` does not modify an existing `ForyJson` instance.

On Android, runtime code generation and asynchronous compilation are disabled. In a GraalVM native
image, runtime compilation is unavailable; configurations returned by a reachable
`ForyJsonProvider` use codecs generated while the image is built, and other configurations use
interpreted codecs with build-time-prepared access metadata. Every other builder option keeps the
behavior described above.
image, runtime compilation is unavailable. Fory JSON generates codecs for reachable models with the
default configuration and each reachable `ForyJsonProvider` configuration. A model without a
matching generated codec uses an interpreted codec. Every other builder option keeps the behavior
described above.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import org.apache.fory.json.annotation.ForyJsonProvider;
import org.apache.fory.json.kotlin.ForyJsonKotlin;

/** Native Image acceptance application for provider-selected Kotlin JSON capabilities. */
/** Native Image acceptance application for provider-added Kotlin JSON capabilities. */
public final class Main {
private Main() {}

Expand Down
5 changes: 2 additions & 3 deletions integration_tests/graalvm_tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

Examples and tests for Fory serialization in GraalVM Native Image. The Fory JSON entry point is
compiled with annotation processing disabled. It covers direct `JsonType` models, exact
`JsonMixin` target/source mappings, provider-selected hosted codec generation, configuration
fallback to interpreted codecs, and hosted access metadata for unprovided configurations in one
native image.
`JsonMixin` target/source mappings, default and provider-added generated codecs, exact-key fallback
to interpreted codecs, and hosted access metadata for unmatched configurations in one native image.

## Test

Expand Down
Loading
Loading