Skip to content

Commit bf90e1d

Browse files
authored
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.
1 parent 8ef0bf5 commit bf90e1d

3 files changed

Lines changed: 258 additions & 1 deletion

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import 'package:fory/fory.dart';
21+
22+
part '../generated/uint_annotated_struct.g.dart';
23+
24+
/// Test struct for uint type annotations.
25+
/// Uses native int types with annotations to specify serialization format.
26+
@ForyClass(promiseAcyclic: true)
27+
class UIntAnnotatedStruct with _$UIntAnnotatedStructFory {
28+
@Uint8Type()
29+
final int age;
30+
31+
@Uint16Type()
32+
final int port;
33+
34+
@Uint32Type()
35+
final int count;
36+
37+
@Uint32Type(encoding: UintEncoding.varint)
38+
final int varCount;
39+
40+
@Uint64Type()
41+
final int id;
42+
43+
@Uint64Type(encoding: UintEncoding.varint)
44+
final int varId;
45+
46+
@Uint64Type(encoding: UintEncoding.tagged)
47+
final int taggedId;
48+
49+
const UIntAnnotatedStruct({
50+
required this.age,
51+
required this.port,
52+
required this.count,
53+
required this.varCount,
54+
required this.id,
55+
required this.varId,
56+
required this.taggedId,
57+
});
58+
59+
@override
60+
bool operator ==(Object other) {
61+
return identical(this, other) ||
62+
(other is UIntAnnotatedStruct &&
63+
runtimeType == other.runtimeType &&
64+
age == other.age &&
65+
port == other.port &&
66+
count == other.count &&
67+
varCount == other.varCount &&
68+
id == other.id &&
69+
varId == other.varId &&
70+
taggedId == other.taggedId);
71+
}
72+
73+
@override
74+
int get hashCode =>
75+
age.hashCode ^
76+
port.hashCode ^
77+
count.hashCode ^
78+
varCount.hashCode ^
79+
id.hashCode ^
80+
varId.hashCode ^
81+
taggedId.hashCode;
82+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
library;
21+
22+
import 'package:fory/fory.dart';
23+
import 'package:fory_test/entity/uint_annotated_struct.dart';
24+
import 'package:test/test.dart';
25+
26+
void main() {
27+
group('UInt annotated struct serialization', () {
28+
test('serializes and deserializes annotated uint fields correctly', () {
29+
// Create test instance with native int types
30+
var original = UIntAnnotatedStruct(
31+
age: 25,
32+
port: 8080,
33+
count: 1000000,
34+
varCount: 500000,
35+
id: 9223372036854775807,
36+
varId: 4611686018427387903,
37+
taggedId: 1152921504606846975,
38+
);
39+
40+
// Serialize
41+
var fory = Fory();
42+
fory.register($UIntAnnotatedStruct);
43+
var bytes = fory.toFory(original);
44+
45+
expect(bytes.isNotEmpty, isTrue);
46+
47+
// Deserialize
48+
var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
49+
50+
// Verify values
51+
expect(decoded.age, 25);
52+
expect(decoded.port, 8080);
53+
expect(decoded.count, 1000000);
54+
expect(decoded.varCount, 500000);
55+
expect(decoded.id, 9223372036854775807);
56+
expect(decoded.varId, 4611686018427387903);
57+
expect(decoded.taggedId, 1152921504606846975);
58+
});
59+
60+
test('handles uint8 max value correctly', () {
61+
var original = UIntAnnotatedStruct(
62+
age: 255, // Max UInt8
63+
port: 100,
64+
count: 100,
65+
varCount: 100,
66+
id: 100,
67+
varId: 100,
68+
taggedId: 100,
69+
);
70+
71+
var fory = Fory();
72+
fory.register($UIntAnnotatedStruct);
73+
var bytes = fory.toFory(original);
74+
var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
75+
76+
expect(decoded.age, 255);
77+
});
78+
79+
test('handles uint16 max value correctly', () {
80+
var original = UIntAnnotatedStruct(
81+
age: 100,
82+
port: 65535, // Max UInt16
83+
count: 100,
84+
varCount: 100,
85+
id: 100,
86+
varId: 100,
87+
taggedId: 100,
88+
);
89+
90+
var fory = Fory();
91+
fory.register($UIntAnnotatedStruct);
92+
var bytes = fory.toFory(original);
93+
var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
94+
95+
expect(decoded.port, 65535);
96+
});
97+
98+
test('handles uint32 max value correctly', () {
99+
var original = UIntAnnotatedStruct(
100+
age: 100,
101+
port: 100,
102+
count: 4294967295, // Max UInt32
103+
varCount: 4294967295, // Max UInt32
104+
id: 100,
105+
varId: 100,
106+
taggedId: 100,
107+
);
108+
109+
var fory = Fory();
110+
fory.register($UIntAnnotatedStruct);
111+
var bytes = fory.toFory(original);
112+
var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
113+
114+
expect(decoded.count, 4294967295);
115+
expect(decoded.varCount, 4294967295);
116+
});
117+
118+
test('handles min values correctly', () {
119+
var original = UIntAnnotatedStruct(
120+
age: 0,
121+
port: 0,
122+
count: 0,
123+
varCount: 0,
124+
id: 0,
125+
varId: 0,
126+
taggedId: 0,
127+
);
128+
129+
var fory = Fory();
130+
fory.register($UIntAnnotatedStruct);
131+
var bytes = fory.toFory(original);
132+
var decoded = fory.fromFory(bytes) as UIntAnnotatedStruct;
133+
134+
expect(decoded.age, 0);
135+
expect(decoded.port, 0);
136+
expect(decoded.count, 0);
137+
expect(decoded.varCount, 0);
138+
expect(decoded.id, 0);
139+
expect(decoded.varId, 0);
140+
expect(decoded.taggedId, 0);
141+
});
142+
143+
test('varint encoding uses less space for small values', () {
144+
var smallValues = UIntAnnotatedStruct(
145+
age: 1,
146+
port: 1,
147+
count: 1,
148+
varCount: 1,
149+
id: 1,
150+
varId: 1,
151+
taggedId: 1,
152+
);
153+
154+
var largeValues = UIntAnnotatedStruct(
155+
age: 1,
156+
port: 1,
157+
count: 4294967295,
158+
varCount: 4294967295,
159+
id: 1,
160+
varId: 1,
161+
taggedId: 1,
162+
);
163+
164+
var fory = Fory();
165+
fory.register($UIntAnnotatedStruct);
166+
167+
var smallBytes = fory.toFory(smallValues);
168+
var largeBytes = fory.toFory(largeValues);
169+
170+
// Varint should use less space for small values
171+
// Fixed-length uint32 always uses 4 bytes regardless of value
172+
expect(smallBytes.length, lessThan(largeBytes.length));
173+
});
174+
});
175+
}

dart/packages/fory/lib/src/codegen/analyze/impl/annotation/key_annotation_analyzer.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class KeyAnnotationAnalyzer {
5757
// If there is no annotation, both includeFromFory and includeToFory default to true.
5858
bool includeFromFory = true;
5959
bool includeToFory = true;
60-
if (anno != null){
60+
if (getMeta && anno != null){
6161
includeFromFory = anno.getField("includeFromFory")!.toBoolValue()!;
6262
includeToFory = anno.getField("includeToFory")!.toBoolValue()!;
6363
// serializeToVar = anno.getField("serializeTo")?.variable;

0 commit comments

Comments
 (0)