This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch release_fory_1.2.0 in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit aa9674b72c9e53a1cce38e425d495bcf2ea397e8 Author: 慕白 <[email protected]> AuthorDate: Tue Jun 16 21:40:20 2026 +0800 Add Fory 1.2.0 release blog --- blog/2026-06-16-fory_1_2_0_release.md | 378 ++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) diff --git a/blog/2026-06-16-fory_1_2_0_release.md b/blog/2026-06-16-fory_1_2_0_release.md new file mode 100644 index 0000000000..42e976344a --- /dev/null +++ b/blog/2026-06-16-fory_1_2_0_release.md @@ -0,0 +1,378 @@ +--- +slug: fory_1_2_0_release +title: Fory v1.2.0 Released +authors: [chaokunyang] +tags: [fory, java, kotlin, scala, android, python, rust, c++, go, c#, swift, dart, compiler] +--- + +The Apache Fory team is pleased to announce the 1.2.0 release. This release includes [38 PRs](https://github.com/apache/fory/compare/v1.1.0...v1.2.0) from 9 distinct contributors and continues to improve the cross-language runtime across supported languages. See the [Install](https://fory.apache.org/docs/start/install) page to get the libraries for your platform. + +## Highlights + +* Expanded generated gRPC support across Go, Rust, Kotlin, Scala, C#, and JavaScript, including Node.js and browser gRPC-Web support for JavaScript. +* Improved cross-language compatibility with refined register-by-name APIs, compatible scalar read conversions, and default compatible mode for native serialization. +* Strengthened Java platform support by adding Java 9/16 module-info generation and removing `sun.misc.Unsafe` usage for JDK 25. +* Improved runtime safety and robustness with additional read checks, deflater leak fixes, and safer serializer/type-info error handling. +* Optimized compatible-mode and row-format performance through faster compatible reads, compact row layout caching, and inlined custom-codec dispatch. +* Enhanced compiler output quality across Rust, C++, and service generation with better identifier escaping, name-collision handling, nested container reference handling, and map code generation. + +## Java 25+ Without `sun.misc.Unsafe` + +JDK 25 continues the platform shift away from `sun.misc.Unsafe`. Fory 1.2.0 +adds a Java 25 multi-release implementation for the core runtime so applications +can run on JDK 25+ without resolving `sun.misc.Unsafe` from Fory's active class +graph. + +The implementation keeps the fast JDK 8-24 paths for older runtimes, but JDK 25+ +loads replacement classes from the versioned jar area. Those replacements move +field access and memory-buffer primitives to supported JVM mechanisms such as +`VarHandle`, `MethodHandle`, arrays, and `ByteBuffer`. Constructor-bypassing +allocation is also handled explicitly: on JDK 25+ classes that previously +depended on Unsafe allocation should provide an accessible no-arg constructor, +use record construction, or register a custom serializer. + +This is an important compatibility milestone for Java users because the runtime +no longer depends on a terminally deprecated Unsafe API path when the application +runs on Java 25 and later. + +## Compatible Scalar Field Reads + +Compatible mode already allows readers and writers to add, remove, and reorder +fields. Fory 1.2.0 extends that model to selected scalar type changes: when a +matched top-level field changes between boolean, string, numeric, and decimal +types, the reader can deserialize the value if the conversion is lossless. + +Examples include reading `"123"` as an integer field, reading `1` or `0` as a +boolean field, reading booleans as `1`/`0`, reading numbers or decimals as +canonical strings, and widening or narrowing numeric values only when no range +or precision is lost. Invalid strings, out-of-range values, lossy float/integer +conversions, and reference-tracked scalar type changes fail during +deserialization. The conversion applies to matched compatible fields, not to +root values or collection elements. + +The examples below show Rust, Python, Java, and C++, but compatible scalar field +conversion is supported across Fory's compatible-mode runtimes: Java, Python, +Rust, C++, Go, C#, Swift, Dart, JavaScript/TypeScript, Kotlin, and Scala. + +Compatible mode is enabled by default in the Java and Python runtimes for both +xlang and native serialization. You can still make the setting explicit: + +```java +Fory fory = Fory.builder() + .withXlang(true) + .withCompatible(true) + .build(); +``` + +```python +import pyfory + +fory = pyfory.Fory(xlang=True, compatible=True) +native_fory = pyfory.Fory(xlang=False, compatible=True) +``` + +Rust example: + +```rust +use fory::{Fory, ForyStruct}; + +#[derive(ForyStruct)] +struct MetricV1 { + value: i64, +} + +#[derive(ForyStruct)] +struct MetricV2 { + value: String, +} + +let mut writer = Fory::builder().xlang(true).compatible(true).build(); +writer.register_by_name::<MetricV1>("example.Metric")?; + +let mut reader = Fory::builder().xlang(true).compatible(true).build(); +reader.register_by_name::<MetricV2>("example.Metric")?; + +let bytes = writer.serialize(&MetricV1 { value: 42 })?; +let value: MetricV2 = reader.deserialize(&bytes)?; +assert_eq!(value.value, "42"); +``` + +Python example: + +```python +from dataclasses import dataclass +import pyfory + +@dataclass +class MetricV1: + value: pyfory.Int64 + +@dataclass +class MetricV2: + value: str + +writer = pyfory.Fory(xlang=True, compatible=True) +writer.register(MetricV1, name="example.Metric") + +reader = pyfory.Fory(xlang=True, compatible=True) +reader.register(MetricV2, name="example.Metric") + +data = writer.dumps(MetricV1(42)) +assert reader.loads(data).value == "42" +``` + +Java example: + +```java +public class MetricV1 { + public long value; +} + +public class MetricV2 { + public String value; +} + +Fory writer = Fory.builder().withXlang(true).withCompatible(true).build(); +writer.register(MetricV1.class, "example", "Metric"); + +Fory reader = Fory.builder().withXlang(true).withCompatible(true).build(); +reader.register(MetricV2.class, "example", "Metric"); + +MetricV1 source = new MetricV1(); +source.value = 42L; +byte[] bytes = writer.serialize(source); +MetricV2 value = reader.deserialize(bytes, MetricV2.class); +assert value.value.equals("42"); +``` + +C++ example: + +```cpp +#include <cassert> +#include <string> +#include "fory/serialization/fory.h" + +using namespace fory::serialization; + +struct MetricV1 { + int64_t value; +}; +FORY_STRUCT(MetricV1, value); + +struct MetricV2 { + std::string value; +}; +FORY_STRUCT(MetricV2, value); + +auto writer = Fory::builder().xlang(true).compatible(true).build(); +auto reader = Fory::builder().xlang(true).compatible(true).build(); + +writer.register_struct<MetricV1>("example", "Metric"); +reader.register_struct<MetricV2>("example", "Metric"); + +auto bytes = writer.serialize(MetricV1{42}).value(); +auto value = reader.deserialize<MetricV2>(bytes).value(); +assert(value.value == "42"); +``` + +The same rule works in the other direction, for example reading a `String` +field value such as `"42"` as `int64`, when the string uses Fory's strict +finite decimal grammar and the target range can represent the value exactly. + +## Generated gRPC Support + +Fory 1.2.0 expands compiler-generated gRPC service companions. The generated +services use standard gRPC transports, channels, deadlines, metadata, +interceptors, status codes, and streaming shapes, while request and response +objects are encoded with Fory instead of protobuf message bytes. Use this mode +when both sides of the RPC are generated from the same Fory IDL, protobuf IDL, +or FlatBuffers IDL and you want gRPC operational semantics with Fory payload +encoding. + +Generated gRPC support now covers Java, Python, Go, Rust, C#, Scala, Kotlin, +and JavaScript/TypeScript. JavaScript includes Node.js gRPC support and browser +gRPC-Web client generation. + +The examples below use this shared schema: + +```protobuf +package demo.greeter; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string reply = 1; +} + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} +``` + +Rust generation emits tonic-based service API and binding modules: + +```bash +foryc service.fdl --rust_out=./generated/rust --grpc +``` + +```rust +use demo_greeter::{HelloReply, HelloRequest}; +use demo_greeter_service::Greeter; +use demo_greeter_service_grpc::greeter_client::GreeterClient; +use demo_greeter_service_grpc::greeter_server::GreeterServer; + +tonic::transport::Server::builder() + .add_service(GreeterServer::new(MyGreeter::default())) + .serve(addr) + .await?; + +let mut client = GreeterClient::connect("http://[::1]:50051").await?; +let reply = client.say_hello(HelloRequest { name: "Fory".into() }).await?; +``` + +Go generation emits grpc-go interfaces and a Fory-backed `CodecV2`: + +```bash +foryc service.fdl --go_out=./generated/go --grpc +``` + +```go +server := grpc.NewServer( + grpc.ForceServerCodecV2(greeter.CodecV2{}), +) +greeter.RegisterGreeterServer(server, greeterService{}) + +client := greeter.NewGreeterClient(conn) +reply, err := client.SayHello(ctx, &greeter.HelloRequest{Name: "Fory"}) +``` + +JavaScript generation supports both Node.js gRPC and browser gRPC-Web: + +```bash +foryc service.fdl --javascript_out=./generated/javascript --grpc --grpc-web +``` + +```ts +import * as grpc from "@grpc/grpc-js"; +import { addGreeterService, createGreeterClient } from "./service_grpc"; +import { createGreeterWebPromiseClient } from "./service_grpc_web"; + +addGreeterService(server, greeterHandlers); + +const nodeClient = createGreeterClient( + "localhost:50051", + grpc.credentials.createInsecure(), +); +nodeClient.sayHello({ name: "Fory" }, callback); + +const webClient = createGreeterWebPromiseClient("https://api.example.com"); +const reply = await webClient.sayHello({ name: "Fory" }); +``` + +Browser gRPC-Web follows gRPC-Web transport limits: unary and server-streaming +methods are supported, while client-streaming and bidirectional streaming remain +Node.js/native gRPC shapes. + +Kotlin generation emits grpc-kotlin coroutine companions: + +```bash +foryc service.fdl --kotlin_out=./generated/kotlin --grpc +``` + +```kotlin +class GreeterService : GreeterGrpcKt.GreeterCoroutineImplBase() { + override suspend fun sayHello(request: HelloRequest): HelloReply = + HelloReply(reply = "Hello, ${request.name}") +} + +val server = ServerBuilder + .forPort(50051) + .addService(GreeterService()) + .build() + .start() + +val stub = GreeterGrpcKt.GreeterCoroutineStub(channel) +val reply = stub.sayHello(HelloRequest(name = "Fory")) +``` + +Scala generation emits Scala 3 grpc-java companions with Scala-friendly client +handles: + +```bash +foryc service.fdl --scala_out=./generated/scala --grpc +``` + +```scala +final class GreeterService extends GreeterGrpc.GreeterImplBase { + override def sayHello(request: HelloRequest): HelloReply = + HelloReply(s"Hello, ${request.name}") +} + +val server = ServerBuilder + .forPort(50051) + .addService(new GreeterService) + .build() + .start() + +val client = GreeterGrpc.newClient(channel) +val reply = Await.result(client.sayHello(HelloRequest("Fory")).asFuture, 30.seconds) +``` + +The generated gRPC companions intentionally do not make gRPC a hard dependency +of the core Fory language packages. Applications add the transport libraries +they use: grpc-java for Java and Scala, `grpcio` for Python, grpc-go for Go, +`tonic`/`bytes` for Rust, .NET gRPC packages for C#, `@grpc/grpc-js` or +`grpc-web` for JavaScript, and grpc-java/grpc-kotlin for Kotlin. + +## Features + +* feat(java): add java9/16 module-info support by @chaokunyang in https://github.com/apache/fory/pull/3721 +* refactor(format): inline custom-codec dispatch in row codecs by @stevenschlansker in https://github.com/apache/fory/pull/3716 +* perf(format): cache compact row layout per nested slot by @stevenschlansker in https://github.com/apache/fory/pull/3717 +* feat(java): remove sun.misc.Unsafe for jdk25 by @chaokunyang in https://github.com/apache/fory/pull/3702 +* feat(rust): support thread safe `Arc<dyn Any + Send + Sync>` type by @chaokunyang in https://github.com/apache/fory/pull/3736 +* refactor(rust): refactor sync send type by @chaokunyang in https://github.com/apache/fory/pull/3737 +* feat(xlang): refine register by name api by @chaokunyang in https://github.com/apache/fory/pull/3739 +* feat(xlang): support compatible scalar read conversions by @chaokunyang in https://github.com/apache/fory/pull/3740 +* feat: default compatible mode for native serialization by @chaokunyang in https://github.com/apache/fory/pull/3742 +* perf: optimize compatible mode read performance by @chaokunyang in https://github.com/apache/fory/pull/3743 +* feat(compiler): handle Rust identifier escaping and name collisions by @BaldDemian in https://github.com/apache/fory/pull/3744 +* feat(go): implement grpc stub generation by @ayush00git in https://github.com/apache/fory/pull/3698 +* refactor(compiler): generate C++ unordered map for Fory map by @BaldDemian in https://github.com/apache/fory/pull/3745 +* feat(compiler): handle nested container ref pointer options in C++ compiler correctly by @BaldDemian in https://github.com/apache/fory/pull/3735 +* feat: add more read checks by @chaokunyang in https://github.com/apache/fory/pull/3748 +* feat(compiler): support Rust gRPC code generation by @BaldDemian in https://github.com/apache/fory/pull/3738 +* feat(cpp): support struct property accessors by @chaokunyang in https://github.com/apache/fory/pull/3751 +* feat(python): make scalar wire markers typing-friendly by @chaokunyang in https://github.com/apache/fory/pull/3756 +* feat(kotlin): add kotlin grpc support by @chaokunyang in https://github.com/apache/fory/pull/3757 +* feat(rust): make fory-derive generated code use exported api in fory rust lib by @chaokunyang in https://github.com/apache/fory/pull/3759 +* feat(scala): add generated grpc service support for scala by @chaokunyang in https://github.com/apache/fory/pull/3762 +* feat(csharp): add generated grpc support for C# by @chaokunyang in https://github.com/apache/fory/pull/3761 +* feat(javascript): add javascript gRPC support for nodejs/browser by @chaokunyang in https://github.com/apache/fory/pull/3760 + +## Bug Fix + +* fix(go): return nil serializer on getTypeInfo err by @ayush00git in https://github.com/apache/fory/pull/3719 +* fix(benchmarks): uses outdated google-java-format, upgrade spotless by @stevenschlansker in https://github.com/apache/fory/pull/3722 +* fix(format): pass row body size, not full payload size, to BinaryRow.pointTo by @stevenschlansker in https://github.com/apache/fory/pull/3715 +* fix(java): fix deflater memory leak by @MNTMDEV in https://github.com/apache/fory/pull/3726 +* fix(compiler): handle nested container ref pointer options in Rust compiler correctly by @BaldDemian in https://github.com/apache/fory/pull/3731 +* fix(java): ignore non-Scala/Lombok-style default helper methods by @mandrean in https://github.com/apache/fory/pull/3733 +* fix(c++): std::unordered_map cannot be used in struct. (#3727) by @ruoruoniao in https://github.com/apache/fory/pull/3728 +* fix(grpc): fix rust/go grpc support by @chaokunyang in https://github.com/apache/fory/pull/3753 +* fix(cpp): align unsigned struct default encoding by @chaokunyang in https://github.com/apache/fory/pull/3754 + +## Other Improvements + +* chore(deps): fix vulnerable dependencies by @chaokunyang in https://github.com/apache/fory/pull/3741 +* chore: Bump MessagePack from 2.5.187 to 2.5.301 by @dependabot[bot] in https://github.com/apache/fory/pull/3750 +* chore(deps): bump Go gRPC test dependencies by @chaokunyang in https://github.com/apache/fory/pull/3763 + +## New Contributors + +* @MNTMDEV made their first contribution in https://github.com/apache/fory/pull/3726 +* @ruoruoniao made their first contribution in https://github.com/apache/fory/pull/3728 + +**Full Changelog**: https://github.com/apache/fory/compare/v1.1.0...v1.2.0 --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
