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 c5c0569753d7c30c9abf987bd0edf121be5b9818 Author: chaokunyang <[email protected]> AuthorDate: Thu May 14 15:48:40 2026 +0000 🔄 synced local 'docs/guide/' with remote 'docs/guide/' --- docs/guide/dart/basic-serialization.md | 2 +- docs/guide/dart/configuration.md | 2 +- docs/guide/java/static-generated-serializers.md | 13 +- docs/guide/kotlin/android-support.md | 122 +++++++++ docs/guide/kotlin/index.md | 1 + docs/guide/kotlin/static-generated-serializers.md | 288 ++++++++++++++-------- 6 files changed, 326 insertions(+), 102 deletions(-) diff --git a/docs/guide/dart/basic-serialization.md b/docs/guide/dart/basic-serialization.md index e05761e275..87ea752df2 100644 --- a/docs/guide/dart/basic-serialization.md +++ b/docs/guide/dart/basic-serialization.md @@ -1,6 +1,6 @@ --- title: Basic Serialization -sidebar_position: 1 +sidebar_position: 2 id: dart_basic_serialization license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/dart/configuration.md b/docs/guide/dart/configuration.md index 3798712187..46d87556d3 100644 --- a/docs/guide/dart/configuration.md +++ b/docs/guide/dart/configuration.md @@ -1,6 +1,6 @@ --- title: Configuration -sidebar_position: 2 +sidebar_position: 1 id: dart_configuration license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/java/static-generated-serializers.md b/docs/guide/java/static-generated-serializers.md index 428b3eff35..0d84a635e0 100644 --- a/docs/guide/java/static-generated-serializers.md +++ b/docs/guide/java/static-generated-serializers.md @@ -78,11 +78,11 @@ public class Order { The processor generates serializer classes in the same Java package as the annotated class. For `Order`, the generated classes are: -- `Order__ForySerializer__` for cross-language mode. -- `Order__ForyNativeSerializer__` for Java native mode. +- `Order_ForySerializer` for cross-language mode. +- `Order_ForyNativeSerializer` for Java native mode. For a static nested type such as `Outer.Inner`, the generated top-level classes are -`Outer$Inner__ForySerializer__` and `Outer$Inner__ForyNativeSerializer__`. +`Outer_Inner_ForySerializer` and `Outer_Inner_ForyNativeSerializer`. ## Field Debug Tracing @@ -114,6 +114,9 @@ Fory uses static generated serializers when they are available on: On an ordinary JVM with `codegen=true`, Fory continues to prefer runtime-generated serializers. +The runtime resolves generated serializers from the registered target class name. Application code +should not reference generated serializer classes directly. + ## Field Access Rules Generated serializers must be able to access serialized fields or their accessors at compile time. @@ -151,6 +154,10 @@ Without the generated serializer metadata, Android may not expose enough nested Fory to preserve annotations such as `@Ref`, `@Int8Type`, `@UInt8Type`, `@Float16Type`, or `@BFloat16Type`. +The annotation processor emits generated consumer R8/ProGuard rules under `META-INF/proguard/` for +the exact serializer constructors used by Fory. Android applications should not add broad generated +serializer keep rules by hand. + ## Compatible Reads Static generated serializers support both normal serialization and compatible-mode reads. Compatible diff --git a/docs/guide/kotlin/android-support.md b/docs/guide/kotlin/android-support.md new file mode 100644 index 0000000000..78db86bf6c --- /dev/null +++ b/docs/guide/kotlin/android-support.md @@ -0,0 +1,122 @@ +--- +title: Android Support +sidebar_position: 5 +id: android_support +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Apache Fory Kotlin supports Kotlin/JVM and Android. Android support is built on +the existing Fory Java runtime plus Kotlin runtime serializers from +`fory-kotlin`. Kotlin schema serializers are generated by `fory-kotlin-ksp` at +build time. + +Use this page for Android setup and release-build constraints. Use +[Static Generated Serializers](static-generated-serializers.md) for the Kotlin +KSP serializer model itself. If your Android project also contains Java +`@ForyStruct` classes, use the Java annotation processor documented in +[Java Static Generated Serializers](../java/static-generated-serializers.md). + +## Dependencies + +Add `fory-kotlin` to the Android module that uses Fory. Add +`fory-kotlin-ksp` to the module that compiles Kotlin `@ForyStruct` model +classes. + +```kotlin +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("com.google.devtools.ksp") +} + +dependencies { + implementation("org.apache.fory:fory-kotlin:<fory-version>") + ksp("org.apache.fory:fory-kotlin-ksp:<fory-version>") +} +``` + +For Android library modules, apply KSP in the library module that owns the +annotated Kotlin classes. The generated serializers and generated consumer R8 +rules must be packaged with that library artifact. + +## Runtime Setup + +Register the Kotlin runtime serializers once for each Fory instance, then +register application classes through the normal Fory Java registration APIs. + +```kotlin +import org.apache.fory.Fory +import org.apache.fory.serializer.kotlin.KotlinSerializers + +val fory = Fory.builder() + .withXlang(true) + .requireClassRegistration(true) + .build() + +KotlinSerializers.registerSerializers(fory) +fory.register(User::class.java, "example", "User") +``` + +Do not reference generated serializer classes from application code. The runtime +resolves generated serializers from the registered target class. + +## Xlang Schema Mode + +Android Kotlin structs that participate in Fory cross-language schema +serialization should use KSP generated serializers. Generated serializers avoid +using runtime reflection as the source of Kotlin schema metadata and call the +same Fory Java runtime infrastructure used by other generated serializers. + +Kotlin KSP generated serializers are xlang/schema serializers only. They do not +replace Java native object serializers and do not preserve concrete JVM +collection implementation identity. For example, a Kotlin `List<String>` field +is schema `list<string>`; deserialization only guarantees a value assignable to +the declared field type. + +## Minified Release Builds + +Validate Fory Android behavior with a minified release build. Debug builds do +not prove that generated serializers, generated constructor entry points, or +Kotlin metadata survive R8. + +KSP emits generated consumer R8/ProGuard rules under `META-INF/proguard/` for +the generated serializer constructors and Kotlin metadata required by Fory. +Android apps should not need broad user-written keep rules for generated Kotlin +serializers. If a custom packaging setup drops generated `META-INF/proguard/` +resources, fix that packaging path instead of adding broad keep rules for every +generated serializer. + +The Apache Fory repository validates this path with +`integration_tests/android_tests`, including release-minified instrumented +tests. + +## Java Models In Android Apps + +Kotlin KSP only processes Kotlin source. If your Android app contains Java +classes annotated with `@ForyStruct`, configure the Java +`fory-annotation-processor` for those Java sources. + +Static generated Java serializers are also important on Android when Java model +classes use Fory type-use annotations on nested types, such as +`List<@UInt8Type Integer>`. See +[Java Static Generated Serializers](../java/static-generated-serializers.md) +for that path. + +## Unsupported Targets + +`fory-kotlin` and `fory-kotlin-ksp` target Kotlin/JVM and Android only. +Kotlin/Native and Kotlin/JS are not supported. diff --git a/docs/guide/kotlin/index.md b/docs/guide/kotlin/index.md index af8f6bf106..0b65c2a5d1 100644 --- a/docs/guide/kotlin/index.md +++ b/docs/guide/kotlin/index.md @@ -108,3 +108,4 @@ Fory Kotlin is built on top of Fory Java. Most configuration options, features, - [Type Serialization](type-serialization.md) - Serializing Kotlin types - [Default Values](default-values.md) - Kotlin data class default values support - [Static Generated Serializers](static-generated-serializers.md) - KSP xlang/schema serializer generation +- [Android Support](android-support.md) - Android setup, R8 behavior, and release-build validation diff --git a/docs/guide/kotlin/static-generated-serializers.md b/docs/guide/kotlin/static-generated-serializers.md index f2a2e48b1b..8836ac44e1 100644 --- a/docs/guide/kotlin/static-generated-serializers.md +++ b/docs/guide/kotlin/static-generated-serializers.md @@ -19,48 +19,39 @@ license: | limitations under the License. --- -`fory-kotlin-ksp` generates Kotlin source serializers for Fory xlang/schema -mode. The generated serializers use the existing Java runtime -`WriteContext`, `ReadContext`, and `MemoryBuffer`; they do not define a -Kotlin-only protocol or buffer abstraction. - -## Scope - -KSP generation is only for xlang/schema mode. It does not generate Fory Java -native object serializers and does not preserve concrete JVM runtime identity -for object graphs. Collection declarations are schema carriers: `List<T>` is -encoded as `list<T>`, `Map<K, V>` as `map<K, V>`, and deserialization only -guarantees a value assignable to the declared Kotlin field type. - -Mutable collection interface fields are supported by reconstructing a mutable -implementation assignable to the declared type. Sorted collection declarations -without an explicit comparator, such as `TreeSet` and `ConcurrentSkipListSet`, -are accepted only for non-null scalar or string elements. Concurrent map -declarations are accepted only with non-null values because the JVM concurrent -map implementations reject null entries. - -Kotlin/JVM and Android are supported. Kotlin/Native and Kotlin/JS are not -supported by this module. - -Phase 1 KSP generation emits serializers for public concrete, non-generic -`@ForyStruct` classes with primary-constructor fields and a public or internal -primary constructor. Kotlin `private` and `internal` struct classes are rejected -because generated serializers and SPI providers are public runtime entry -points. Kotlin `object` declarations, annotated interfaces, abstract classes, -sealed declarations, and generic struct targets are rejected during KSP -processing because they are not single constructor-field serializer targets for -the phase 1 generator. Dense primitive and unsigned array types, such as -`IntArray` and `UIntArray`, are supported as top-level fields. Nested dense -arrays, such as `List<UIntArray>` or `Map<String, IntArray>`, are rejected in -phase 1. Fory Java `@ArrayType` is also supported on top-level `List<T>` fields -when `T` is a non-null bool or numeric dense-array element domain. The generated -serializer writes the field as dense `array<T>` schema and converts decoded JVM -list elements back to the declared Kotlin carrier, such as `Int`, `UInt`, or -`Double`. - -## Annotations - -Reuse Java annotations for Fory concepts: +Use `fory-kotlin-ksp` when Kotlin classes must participate in Fory +cross-language schema serialization. The processor generates Kotlin source +serializers at build time. Those serializers call the existing Fory Java +runtime, including `WriteContext`, `ReadContext`, and `MemoryBuffer`; there is +no Kotlin-only protocol. + +Static generated Kotlin serializers are for Kotlin/JVM and Android xlang/schema +mode. They are not Java native object serializers and do not preserve JVM object +graph implementation details such as the exact concrete collection class. + +## Add KSP + +Add `fory-kotlin` at runtime and run `fory-kotlin-ksp` as a KSP processor in +the module that compiles your `@ForyStruct` Kotlin classes. + +```kotlin +plugins { + id("com.google.devtools.ksp") version "<ksp-version>" +} + +dependencies { + implementation("org.apache.fory:fory-kotlin:<fory-version>") + ksp("org.apache.fory:fory-kotlin-ksp:<fory-version>") +} +``` + +For Android, configure KSP in the Android module or library module that owns +the Kotlin model classes. + +## Define A Struct + +Reuse the Java Fory annotations for schema concepts. Use Kotlin type-use +annotations only when you need to override integer encoding. ```kotlin import org.apache.fory.annotation.ForyField @@ -75,27 +66,126 @@ data class User( @ForyField(id = 2) val score: @VarInt Long, + + @ForyField(id = 3) + val tags: List<String>, +) +``` + +Use `@ForyField(id = 1)` on constructor properties. `@field:ForyField(id = 1)` +is also accepted for field-backed properties. Do not use `@get:ForyField` or +`@set:ForyField`; accessors are not schema fields and the processor rejects +them. + +## Supported Structs + +The processor generates serializers for public or internal, concrete, +non-generic classes in named packages. A supported class must have a primary +constructor whose serialized parameters are `val` or `var` properties. `data +class` is the common case, but it is not required. + +Internal Kotlin struct classes are supported when KSP runs in the same Kotlin +module that owns the struct. The generated Kotlin serializer is also internal, +so it can call the internal constructor and expose the internal type in +overrides while still producing a JVM class that the Fory Java runtime can load. +Application code outside that Kotlin module still cannot refer to the internal +struct directly, so registration must happen from code that can see the class. + +The processor rejects these declarations: + +- `private` struct classes. +- local, anonymous, or nested `@ForyStruct` classes. +- Kotlin `object` declarations. +- interfaces, abstract classes, and sealed classes as serializer targets. +- generic `@ForyStruct` classes. +- private constructor properties. +- private or protected primary constructors. + +Kotlin default constructor arguments are supported for compatible reads. A +struct can have up to 12 defaulted constructor fields. + +## Nullability + +Use Kotlin `?` to describe nullable schema positions. Nullability is preserved +inside collections and maps. + +```kotlin +@ForyStruct +data class NullabilityExample( + @ForyField(id = 1) + val a: List<String>, + + @ForyField(id = 2) + val b: List<String?>, + + @ForyField(id = 3) + val c: List<String>?, + + @ForyField(id = 4) + val d: List<String?>?, ) ``` -Use `@ForyField(id = 1)`. `@field:ForyField(id = 1)` is also accepted for -field-backed properties. `@get:ForyField` and `@set:ForyField` are rejected -because accessors are not schema fields. +Do not use Fory `@Nullable` in Kotlin source. The KSP processor rejects it so +the schema is always read from Kotlin source nullability. + +## References + +`@ForyField(ref = true)` is not supported by Kotlin xlang generated +serializers. Generated reads construct Kotlin values through primary +constructors, so they cannot publish partially constructed objects for cyclic +back-references. Use non-cyclic schemas for Kotlin xlang structs. + +## Collections + +Collection declarations carry schema shape, not JVM implementation identity. +For example, `List<String>` is encoded as `list<string>` and +`Map<String, Int>` is encoded as `map<string, int32>`. + +Deserialization only guarantees that the result is assignable to the declared +field type. Fory does not preserve whether the original runtime value was an +`ArrayList`, `LinkedList`, `Collections.unmodifiableList`, synchronized +collection wrapper, or another JVM-specific collection implementation. + +Supported collection declarations include Kotlin and Java list, set, and map +types. Mutable collection interface fields are deserialized to mutable +implementations assignable to the declared type. Sorted collections without an +explicit comparator, such as `TreeSet` and `ConcurrentSkipListSet`, are accepted +only for non-null scalar or string elements. Concurrent map declarations are +accepted only with non-null values because JVM concurrent map implementations +reject null entries. + +`Set<*>`, `Map<*, T>`, `Map<*, *>`, and raw Java collections are rejected. +`List<*>` and `Map<K, *>` are accepted and use dynamic nullable values. + +## Dense Arrays + +Kotlin dense primitive and unsigned array fields are supported: -Kotlin nullability is expressed with `?`, including nested positions such as -`List<String?>?`. Do not use Fory `@Nullable` in Kotlin source. +- `BooleanArray` +- `ByteArray` +- `ShortArray` +- `IntArray` +- `LongArray` +- `FloatArray` +- `DoubleArray` +- `UByteArray` +- `UShortArray` +- `UIntArray` +- `ULongArray` -`@ForyField(ref = true)` is rejected by the KSP xlang processor. Phase 1 -generated serializers construct Kotlin values through primary constructors and -therefore cannot publish partially constructed objects for cyclic -back-references. Use non-cyclic xlang schemas for Kotlin KSP serializers. +Dense arrays are supported as top-level fields. Nested dense arrays, such as +`List<UIntArray>` or `Map<String, IntArray>`, are rejected. -Kotlin default constructor arguments are supported for compatible reads by -generating constructor calls that omit missing defaulted arguments. Because -Kotlin source generation must emit these calls statically, the first KSP -implementation supports at most 12 defaulted constructor fields per struct. +`ByteArray` is encoded as Fory `binary` unless you explicitly annotate the +field with Java `@ArrayType`. -## Encoding +`@ArrayType` is also supported on top-level `List<T>` fields when `T` is a +non-null boolean or numeric dense-array element type. In that case the field is +encoded as dense `array<T>` schema, and generated reads convert decoded JVM list +elements back to the declared Kotlin element carrier. + +## Integer Encoding Kotlin type-use encoding annotations map to Fory xlang integer encodings: @@ -109,10 +199,11 @@ Without an annotation, xlang `Int`, `Long`, `UInt`, and `ULong` use varint encoding. This is required by xlang mode and is not controlled by Java native mode numeric compression options. -## Registration +## Register Classes -Users register Kotlin struct classes with the normal Fory Java registration -APIs. Do not reference generated serializer class names. +Register Kotlin struct classes with the normal Fory Java registration APIs. You +choose the xlang namespace and type name; generated serializers do not choose +IDs or names for you. ```kotlin val fory = Fory.builder() @@ -124,42 +215,45 @@ KotlinSerializers.registerSerializers(fory) fory.register(User::class.java, "example", "User") ``` -The KSP processor emits a service provider that maps target classes to their -generated serializers. On Android and for Kotlin classes, the Java runtime -requires this provider mapping for xlang structs. Missing KSP/SPI metadata is a -configuration error, not a reflective fallback path. This avoids generated-name -`Class.forName` lookups and works as an R8-compatible discovery path when the -generated service resource and serializer classes are kept by the application -build. - -Android builds that run R8 must preserve the generated SPI resource and the -generated provider/serializer classes. Application shrinker rules should keep: - -```text --keep class * implements org.apache.fory.resolver.StaticGeneratedSerializerProvider { *; } --keep class * implements org.apache.fory.resolver.StaticGeneratedSerializerProvider$KotlinSymbolProcessor { *; } --keep class * implements org.apache.fory.resolver.StaticGeneratedSerializerProvider$JavaAnnotationProcessor { *; } --keep class **.*__ForySerializer__ { *; } --keep class org.apache.fory.resolver.StaticGeneratedSerializerProvider { *; } --keep class org.apache.fory.resolver.StaticGeneratedSerializerProvider$* { *; } -``` +`KotlinSerializers.registerSerializers(fory)` installs the Kotlin runtime +serializers used by Kotlin-specific carriers such as unsigned types. The +`fory.register(...)` call registers your xlang schema type name. + +Do not register or reference generated serializer classes in application code. +The runtime resolves them from the registered target class. + +## Generated Names + +The generated serializer is emitted in the same package as the target class. +Its name is `<target>_ForySerializer`. For nested binary names, `$` is encoded +as `_`; source underscores are encoded as `_u_`. + +These names are an implementation detail. They matter for diagnostics and +Android shrinking, but user code should only register target classes. + +If a Kotlin xlang struct is registered but its KSP generated serializer is +missing, Fory fails with a configuration error. It does not fall back to +runtime reflection for Kotlin schema metadata. + +## Android And R8 + +Android apps should not need user-written keep rules for generated Kotlin +serializers. KSP emits generated consumer R8/ProGuard rules under +`META-INF/proguard/` for the generated serializer constructors used by Fory and +the Kotlin metadata needed to detect required Kotlin generated serializers. + +For library modules, package the generated `META-INF/proguard/` resources into +the produced artifact. For Android application modules, make sure your KSP +setup includes generated resources in the minified variant. + +See [Android Support](android-support.md) for Android Gradle setup and +release-minified validation guidance. + +## Native Object Mode + +Kotlin KSP generated serializers are only for xlang/schema mode. They do not +replace Fory Java native object serializers and do not preserve JVM object graph +identity. If you use Fory with `withXlang(false)`, Fory uses the normal Java and +Kotlin runtime serializers instead. -The Kotlin KSP generated service resource -`META-INF/services/org.apache.fory.resolver.StaticGeneratedSerializerProvider$KotlinSymbolProcessor` -must also be packaged. Java annotation-processor generated serializers use the -separate -`META-INF/services/org.apache.fory.resolver.StaticGeneratedSerializerProvider$JavaAnnotationProcessor` -resource so mixed Java/Kotlin artifacts do not overwrite one another's provider -lists. If a build plugin strips service resources, configure the application -packaging step to retain these files. - -The KSP processor emits one aggregate provider for the compilation module in -`org.apache.fory.kotlin.generated`. The provider class name includes a stable -hash of the generated target mappings, so multiple Kotlin artifacts can publish -providers on the same classpath without binary-name collisions. That provider -contains mappings for all annotated Kotlin structs in the module, across source -packages. - -Applications must package the generated service resource. There is no manual -provider registration API; users still register only target classes and their -IDs or xlang names through the normal Fory Java registration APIs. +Kotlin/Native and Kotlin/JS are not supported by this module. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
