This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit 278d4c3de81654d53df7caf846e323ff8d7ab38b Author: chaokunyang <[email protected]> AuthorDate: Thu May 14 05:21:45 2026 +0000 🔄 synced local 'docs/specification/' with remote 'docs/specification/' --- docs/specification/xlang_implementation_guide.md | 34 ++++++++++++++++++++++++ docs/specification/xlang_serialization_spec.md | 20 ++++++++------ docs/specification/xlang_type_mapping.md | 24 +++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/docs/specification/xlang_implementation_guide.md b/docs/specification/xlang_implementation_guide.md index ba1ee0a9c3..6fd32e15d8 100644 --- a/docs/specification/xlang_implementation_guide.md +++ b/docs/specification/xlang_implementation_guide.md @@ -107,6 +107,40 @@ Generated and hand-written serializers should treat these contexts as the only source of operation-local services. Serializers must not keep ambient runtime state in thread locals, globals, or serializer instance fields. +### Static generated serializer discovery + +The Java runtime discovers build-time generated xlang serializers through +`StaticGeneratedSerializerProvider` service providers. A provider maps a target +class to the generated serializer class and construction functions. Runtime +registration still belongs to the user: users register target classes and +their IDs or names with normal Fory registration APIs; generated providers must +not choose user IDs or registered names. + +Generated-name `Class.forName` lookup is not the owner for static serializer +discovery. Service-provider lookup is required for Android/R8 and is preferred +for Kotlin classes. Kotlin xlang structs require a KSP-generated SPI mapping; +missing static serializer metadata is a configuration error. When a registered +type is a Kotlin class, or when the runtime is Android, the Java type resolver +checks the static provider registry first. The static provider registry is +resolver-owned shared metadata: it is held by `SharedRegistry`, not by +serializer classes or by individual `TypeResolver` instances. The registry must +be GraalVM build-time initialized so build-time `Fory` instances can embed their +shared resolver metadata in the native-image heap without runtime-initialization +conflicts. Generated Java annotation-processor providers and Kotlin KSP +providers use separate marker service descriptors under +`StaticGeneratedSerializerProvider` so mixed Java/Kotlin artifacts can package +both provider lists without resource overwrite. The registry must load +providers visible from the resolver class loader, the target class loader, and +the context class loader, so generated serializers packaged beside plugin or +child-loader classes can be found. + +Static generated serializers must expose descriptor metadata through an +instance `getGeneratedDescriptors()` method and must have a provider-callable +no-argument construction path for descriptor reads. That construction path is +not a user registration API. The runtime creates the descriptor instance from +the provider; it does not parse Kotlin metadata or Java fields at runtime to +recover the generated schema. + ### `WriteContext` `WriteContext` owns all write-side per-operation state: diff --git a/docs/specification/xlang_serialization_spec.md b/docs/specification/xlang_serialization_spec.md index bb5b2c1598..6343392c7e 100644 --- a/docs/specification/xlang_serialization_spec.md +++ b/docs/specification/xlang_serialization_spec.md @@ -208,13 +208,17 @@ Users can also provide meta hints for fields of a type, or the type whole. Here annotation to provide such information. ```java -@ForyStruct(fieldsNullable = false, trackingRef = false) +@ForyStruct class Foo { - @ForyField(trackingRef = false) + @ArrayType + @ForyField(id = 0) int[] intArray; - @ForyField(polymorphic = true) + + @ForyField(id = 1, dynamic = ForyField.Dynamic.TRUE) Object object; - @ForyField(tagId = 1, nullable = true) + + @Nullable + @ForyField(id = 2) List<Object> objectList; } ``` @@ -442,10 +446,10 @@ In xlang mode, for cross-language compatibility: ```java // Java: use @ForyField annotation public class MyClass { - @ForyField(nullable = true, ref = true) + @Nullable + @ForyField(ref = true) private Object refField; - @ForyField(nullable = false) private String requiredField; } ``` @@ -1489,8 +1493,8 @@ Field identifiers compare as follows: Assign each field to exactly one group in the following order: -1. **Primitive (non-nullable)**: primitive or boxed numeric/boolean types with `nullable=false`. -2. **Primitive (nullable)**: primitive or boxed numeric/boolean types with `nullable=true`. +1. **Primitive (non-nullable)**: primitive or boxed numeric/boolean types without nullable metadata. +2. **Primitive (nullable)**: primitive or boxed numeric/boolean types with nullable metadata. 3. **Non-primitive**: every other field, including strings, time/date/duration/decimal/binary values, unions, primitive arrays, collections, maps, enums, structs, ext/user-defined types, UNKNOWN fields, object arrays, and all other non-primitive schemas. diff --git a/docs/specification/xlang_type_mapping.md b/docs/specification/xlang_type_mapping.md index 5cd7adc969..654fa14cfc 100644 --- a/docs/specification/xlang_type_mapping.md +++ b/docs/specification/xlang_type_mapping.md @@ -125,6 +125,30 @@ Notes: of the current xlang type-mapping surface. - Current xlang uses `*_ARRAY` for one-dimensional primitive arrays and nested `list` for multi-dimensional arrays. +- Kotlin KSP xlang maps `UByte`, `UShort`, `UInt`, and `ULong` to `uint8`, + `uint16`, `uint32`, and `uint64` respectively. The generated descriptor uses + the JVM primitive carrier plus explicit Fory type metadata; it does not add + unsigned wrapper wire types. `ByteArray` maps to `binary` by default and to + `array<int8>` only when the field is explicitly marked with Fory `ArrayType`. + `UByteArray`, `UShortArray`, `UIntArray`, and `ULongArray` map to the dense + unsigned array schema types. Dense Kotlin numeric array component metadata + uses the fixed-width element type (`int32`, `int64`, `uint32`, or `uint64`); + scalar field defaults such as varint do not apply to dense Kotlin array + components. For `@ArrayType List<Int>`, `@ArrayType List<Long>`, + `@ArrayType List<UInt>`, and `@ArrayType List<ULong>`, Kotlin KSP records a + dense array schema on the list field and fixed-width dense element domain + metadata on the list element type. Generated Kotlin reads must restore the + declared Kotlin element carrier after decoding the erased JVM list. +- Kotlin collection declarations in KSP xlang mode carry schema shape, not JVM + implementation identity. `List<T>`, mutable list declarations, and supported + concrete list declarations encode as `list<T>`; `Set<T>` encodes as `set<T>`; + `Map<K, V>` encodes as `map<K, V>`. Deserialization may construct a different + concrete implementation if it is assignable to the declared field type. + Sorted JVM collections reconstructed with natural ordering require non-null + scalar or string elements or keys. JVM concurrent map declarations require + non-null values. `@ArrayType List<T>` maps to dense `array<T>` only for + non-null bool or numeric element domains; unsupported element domains are KSP + errors. - `list<T>` and `array<T>` remain distinct schema kinds. In schema-compatible struct/class field matching only, a direct top-level `list<T>` field may be read as a direct top-level `array<T>` field, and a direct top-level `array<T>` field may be read as a direct top-level `list<T>` field, --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
