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]

Reply via email to