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.git
The following commit(s) were added to refs/heads/main by this push:
new bf90e1d14 feat(dart): add uint struct support to the codegen system
(#3192)
bf90e1d14 is described below
commit bf90e1d14594e574e6f4ec41a9fa278b54e645a6
Author: Ayush Kumar <[email protected]>
AuthorDate: Fri Jan 23 08:20:51 2026 +0530
feat(dart): add uint struct support to the codegen system (#3192)
## Why?
While the uint annotation types were integrated into the code generation
system in the PR #3181 , there were two remaining issues:
1. **No annotated struct support and test coverage for annotated
structs**: The annotation system was integrated but not tested with
actual struct fields using the annotations
2. **Null check error in key_annotation_analyzer**: When fields didn't
have `@ForyKey` annotations, the code generator crashed with a null
check error at `key_annotation_analyzer.dart`
## What does this PR do?
### 1. Created Test Entity with Annotated Uint Fields
Added `uint_annotated_struct.dart` demonstrating all uint annotation
variants:
```dart
@ForyClass(promiseAcyclic: true)
class UIntAnnotatedStruct with _$UIntAnnotatedStructFory {
@Uint8Type()
final int age;
@Uint16Type()
final int port;
@Uint32Type()
final int count;
@Uint32Type(encoding: UintEncoding.varint)
final int varCount;
@Uint64Type()
final int id;
@Uint64Type(encoding: UintEncoding.varint)
final int varId;
@Uint64Type(encoding: UintEncoding.tagged)
final int taggedId;
}
```
### 2. Added Comprehensive Test Suite
Created `uint_annotated_struct_test.dart` with test coverage for:
* Basic serialization/deserialization with annotated fields
* Max value handling for uint8 (255), uint16 (65535), uint32
(4294967295)
* Min value handling (0 for all types)
* Varint encoding efficiency verification
**Run tests:**
```bash
cd dart/packages/fory-test
dart run build_runner build --delete-conflicting-outputs
dart test test/struct_test/uint_annotated_struct_test.dart
```
### 3. Fixed Null Check Error in Key Annotation Analyzer
**Problem:** The `key_annotation_analyzer.dart` was attempting to access
annotation fields even when no `@ForyKey` annotation was present,
causing a null check operator error.
**Solution:** Added additional check to ensure we only access annotation
fields when a `@ForyKey` annotation is actually found:
Before
```dart
if (anno != null){
```
After
```dart
if (getMeta && anno != null){
```
This ensures that:
- We only try to read annotation fields when `getMeta` is true (meaning
we found a `@ForyKey` annotation)
- Fields without `@ForyKey` annotations correctly default to
`includeFromFory: true` and `includeToFory: true`
- Code generation no longer crashes on structs with unannotated fields
## Related issues
Completes the uint annotation testing and fixes a critical bug that
prevented code generation from working with fields lacking `@ForyKey`
annotations.
## Does this PR introduce any user-facing change?
* [ ] Does this PR introduce any public API change?
* No new APIs - only adds test coverage and fixes a bug
* [ ] Does this PR introduce any binary protocol compatibility change?
* No changes to binary encoding format
* Bug fix only affects code generation, not runtime serialization
## Benchmark
N/A - This PR adds test coverage and fixes a code generation bug. No
runtime performance impact.
---
.../lib/entity/uint_annotated_struct.dart | 82 ++++++++++
.../struct_test/uint_annotated_struct_test.dart | 175 +++++++++++++++++++++
.../impl/annotation/key_annotation_analyzer.dart | 2 +-
3 files changed, 258 insertions(+), 1 deletion(-)
diff --git a/dart/packages/fory-test/lib/entity/uint_annotated_struct.dart
b/dart/packages/fory-test/lib/entity/uint_annotated_struct.dart
new file mode 100644
index 000000000..dcc8b0595
--- /dev/null
+++ b/dart/packages/fory-test/lib/entity/uint_annotated_struct.dart
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+import 'package:fory/fory.dart';
+
+part '../generated/uint_annotated_struct.g.dart';
+
+/// Test struct for uint type annotations.
+/// Uses native int types with annotations to specify serialization format.
+@ForyClass(promiseAcyclic: true)
+class UIntAnnotatedStruct with _$UIntAnnotatedStructFory {
+ @Uint8Type()
+ final int age;
+
+ @Uint16Type()
+ final int port;
+
+ @Uint32Type()
+ final int count;
+
+ @Uint32Type(encoding: UintEncoding.varint)
+ final int varCount;
+
+ @Uint64Type()
+ final int id;
+
+ @Uint64Type(encoding: UintEncoding.varint)
+ final int varId;
+
+ @Uint64Type(encoding: UintEncoding.tagged)
+ final int taggedId;
+
+ const UIntAnnotatedStruct({
+ required this.age,
+ required this.port,
+ required this.count,
+ required this.varCount,
+ required this.id,
+ required this.varId,
+ required this.taggedId,
+ });
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other is UIntAnnotatedStruct &&
+ runtimeType == other.runtimeType &&
+ age == other.age &&
+ port == other.port &&
+ count == other.count &&
+ varCount == other.varCount &&
+ id == other.id &&
+ varId == other.varId &&
+ taggedId == other.taggedId);
+ }
+
+ @override
+ int get hashCode =>
+ age.hashCode ^
+ port.hashCode ^
+ count.hashCode ^
+ varCount.hashCode ^
+ id.hashCode ^
+ varId.hashCode ^
+ taggedId.hashCode;
+}
diff --git
a/dart/packages/fory-test/test/struct_test/uint_annotated_struct_test.dart
b/dart/packages/fory-test/test/struct_test/uint_annotated_struct_test.dart
new file mode 100644
index 000000000..91fad5f98
--- /dev/null
+++ b/dart/packages/fory-test/test/struct_test/uint_annotated_struct_test.dart
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+library;
+
+import 'package:fory/fory.dart';
+import 'package:fory_test/entity/uint_annotated_struct.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('UInt annotated struct serialization', () {
+ test('serializes and deserializes annotated uint fields correctly', () {
+ // Create test instance with native int types
+ var original = UIntAnnotatedStruct(
+ age: 25,
+ port: 8080,
+ count: 1000000,
+ varCount: 500000,
+ id: 9223372036854775807,
+ varId: 4611686018427387903,
+ taggedId: 1152921504606846975,
+ );
+
+ // Serialize
+ var fory = Fory();
+ fory.register($UIntAnnotatedStruct);
+ var bytes = fory.toFory(original);
+
+ expect(bytes.isNotEmpty, isTrue);
+
+ // Deserialize
+ var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
+
+ // Verify values
+ expect(decoded.age, 25);
+ expect(decoded.port, 8080);
+ expect(decoded.count, 1000000);
+ expect(decoded.varCount, 500000);
+ expect(decoded.id, 9223372036854775807);
+ expect(decoded.varId, 4611686018427387903);
+ expect(decoded.taggedId, 1152921504606846975);
+ });
+
+ test('handles uint8 max value correctly', () {
+ var original = UIntAnnotatedStruct(
+ age: 255, // Max UInt8
+ port: 100,
+ count: 100,
+ varCount: 100,
+ id: 100,
+ varId: 100,
+ taggedId: 100,
+ );
+
+ var fory = Fory();
+ fory.register($UIntAnnotatedStruct);
+ var bytes = fory.toFory(original);
+ var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
+
+ expect(decoded.age, 255);
+ });
+
+ test('handles uint16 max value correctly', () {
+ var original = UIntAnnotatedStruct(
+ age: 100,
+ port: 65535, // Max UInt16
+ count: 100,
+ varCount: 100,
+ id: 100,
+ varId: 100,
+ taggedId: 100,
+ );
+
+ var fory = Fory();
+ fory.register($UIntAnnotatedStruct);
+ var bytes = fory.toFory(original);
+ var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
+
+ expect(decoded.port, 65535);
+ });
+
+ test('handles uint32 max value correctly', () {
+ var original = UIntAnnotatedStruct(
+ age: 100,
+ port: 100,
+ count: 4294967295, // Max UInt32
+ varCount: 4294967295, // Max UInt32
+ id: 100,
+ varId: 100,
+ taggedId: 100,
+ );
+
+ var fory = Fory();
+ fory.register($UIntAnnotatedStruct);
+ var bytes = fory.toFory(original);
+ var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
+
+ expect(decoded.count, 4294967295);
+ expect(decoded.varCount, 4294967295);
+ });
+
+ test('handles min values correctly', () {
+ var original = UIntAnnotatedStruct(
+ age: 0,
+ port: 0,
+ count: 0,
+ varCount: 0,
+ id: 0,
+ varId: 0,
+ taggedId: 0,
+ );
+
+ var fory = Fory();
+ fory.register($UIntAnnotatedStruct);
+ var bytes = fory.toFory(original);
+ var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
+
+ expect(decoded.age, 0);
+ expect(decoded.port, 0);
+ expect(decoded.count, 0);
+ expect(decoded.varCount, 0);
+ expect(decoded.id, 0);
+ expect(decoded.varId, 0);
+ expect(decoded.taggedId, 0);
+ });
+
+ test('varint encoding uses less space for small values', () {
+ var smallValues = UIntAnnotatedStruct(
+ age: 1,
+ port: 1,
+ count: 1,
+ varCount: 1,
+ id: 1,
+ varId: 1,
+ taggedId: 1,
+ );
+
+ var largeValues = UIntAnnotatedStruct(
+ age: 1,
+ port: 1,
+ count: 4294967295,
+ varCount: 4294967295,
+ id: 1,
+ varId: 1,
+ taggedId: 1,
+ );
+
+ var fory = Fory();
+ fory.register($UIntAnnotatedStruct);
+
+ var smallBytes = fory.toFory(smallValues);
+ var largeBytes = fory.toFory(largeValues);
+
+ // Varint should use less space for small values
+ // Fixed-length uint32 always uses 4 bytes regardless of value
+ expect(smallBytes.length, lessThan(largeBytes.length));
+ });
+ });
+}
diff --git
a/dart/packages/fory/lib/src/codegen/analyze/impl/annotation/key_annotation_analyzer.dart
b/dart/packages/fory/lib/src/codegen/analyze/impl/annotation/key_annotation_analyzer.dart
index 3e5c3344f..d89ec47ae 100644
---
a/dart/packages/fory/lib/src/codegen/analyze/impl/annotation/key_annotation_analyzer.dart
+++
b/dart/packages/fory/lib/src/codegen/analyze/impl/annotation/key_annotation_analyzer.dart
@@ -57,7 +57,7 @@ class KeyAnnotationAnalyzer {
// If there is no annotation, both includeFromFory and includeToFory
default to true.
bool includeFromFory = true;
bool includeToFory = true;
- if (anno != null){
+ if (getMeta && anno != null){
includeFromFory = anno.getField("includeFromFory")!.toBoolValue()!;
includeToFory = anno.getField("includeToFory")!.toBoolValue()!;
// serializeToVar = anno.getField("serializeTo")?.variable;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]