-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: remove unauthorized spec #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
818b306
fc69abe
ce039a9
6fae063
7aa3119
e55f884
9622466
11bec98
22129ce
79b07ab
2c5478b
ab9bb92
a065c58
a00371e
8b24985
1479b5b
936a51e
f9c5a5e
c3b7112
3df67eb
7c19ea2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -328,6 +328,7 @@ | |
| } | ||
|
|
||
| // Roslyn's member order is the wire declaration order; never infer a different order from file paths or spans. | ||
| int blittableByteOffset = 0; | ||
| foreach (ISymbol declaredMember in serializableType.GetMembers()) | ||
| { | ||
| // Only public getter properties define the wire contract; fields, setters, and indexers must never leak into it. | ||
|
|
@@ -362,7 +363,9 @@ | |
| continue; | ||
| } | ||
|
|
||
| propertyModel.BlittableByteOffset = blittableByteOffset; | ||
| generationModel.Fields.Add(propertyModel); | ||
| blittableByteOffset += propertyModel.ElementByteCount; | ||
|
Comment on lines
+366
to
+368
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 5 \
'InaccessibleSerializableField|BlittableByteOffset|TryGetFixedTypeByteCount' \
src/ZeroSerializerGenerator.cs src/FieldGenerationModel.cs
rg -n -C 8 \
'\[StructLayout\(LayoutKind\.Sequential, Pack = 1\)' \
tests tests-unity benchmarkRepository: sator-imaging/ZeroSerializer Length of output: 20453 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- generation model construction ---'
sed -n '250,375p' src/ZeroSerializerGenerator.cs
printf '%s\n' '--- fixed-size and blittable checks ---'
sed -n '550,710p' src/ZeroSerializerGenerator.cs
printf '%s\n' '--- diagnostic references ---'
rg -n -C 8 'InaccessibleSerializableField|ZEROS002|IsBlittableStruct|IsBlittable' src tests
printf '%s\n' '--- serialization and view generation ---'
sed -n '1120,1250p' src/ZeroSerializerGenerator.csRepository: sator-imaging/ZeroSerializer Length of output: 36320 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- ZEROS002 diagnostic test ---'
sed -n '1,115p' tests/DiagnosticTests.cs
printf '%s\n' '--- property model classification ---'
sed -n '375,550p' src/ZeroSerializerGenerator.cs
printf '%s\n' '--- all diagnostic descriptor usages ---'
python3 - <<'PY'
from pathlib import Path
text = Path("src/ZeroSerializerGenerator.cs").read_text()
name = "InaccessibleSerializableField"
print("descriptor occurrences:", text.count(name))
for i, line in enumerate(text.splitlines(), 1):
if name in line:
print(f"{i}: {line}")
PY
printf '%s\n' '--- relevant test model declarations ---'
sed -n '1,115p' tests/SerializationModels.csRepository: sator-imaging/ZeroSerializer Length of output: 13141 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
source = Path("src/ZeroSerializerGenerator.cs").read_text()
# The descriptor must be declared and reported to enforce the proposed rejection.
descriptor_uses = source.count("InaccessibleSerializableField")
print("InaccessibleSerializableField occurrences:", descriptor_uses)
# Confirm the two independent offset models in the generator.
property_offset_model = bool(re.search(
r"propertyModel\.BlittableByteOffset\s*=\s*blittableByteOffset;\s*"
r"generationModel\.Fields\.Add\(propertyModel\);\s*"
r"blittableByteOffset\s*\+=\s*propertyModel\.ElementByteCount;",
source,
re.S,
))
physical_size_model = bool(re.search(
r"declaredMember\s+in\s+structType\.GetMembers\(\).*?"
r"nestedField\.IsStatic.*?"
r"TryGetFixedTypeByteCount\(nestedField\.Type",
source,
re.S,
))
raw_write_model = "MemoryMarshal.Write(destination, source);" in source
nested_slice_model = "serializedMemory.Slice({field.BlittableByteOffset}, {field.ElementByteCount})" in source
print("property-only offset model:", property_offset_model)
print("all-instance-field size model:", physical_size_model)
print("raw blittable write model:", raw_write_model)
print("nested view slice model:", nested_slice_model)
# Minimal reachable layout counterexample:
# private int _ignored precedes a 4-byte nested blittable property.
physical_field_sizes = [4, 4] # _ignored, Nested property's backing field
generated_property_sizes = [4] # public Nested property
physical_nested_offset = sum(physical_field_sizes[:-1])
generated_nested_offset = 0
print("counterexample physical nested offset:", physical_nested_offset)
print("counterexample generated nested offset:", generated_nested_offset)
print("offset mismatch:", physical_nested_offset != generated_nested_offset)
assert descriptor_uses == 1
assert property_offset_model and physical_size_model
assert raw_write_model and nested_slice_model
assert physical_nested_offset != generated_nested_offset
PYRepository: sator-imaging/ZeroSerializer Length of output: 447 Reject blittable structs with unrepresented instance fields. When an instance field precedes a nested blittable property, 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| return generationModel; | ||
|
|
@@ -1194,14 +1197,28 @@ | |
| propertyType = field.Symbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); | ||
| } | ||
|
|
||
| sourceBuilder.AppendLine($"{propertyAccessibility} {propertyType} {EscapeIdentifier(field.Symbol.Name)}"); | ||
| var propertyReturnType | ||
| = field.Kind is FieldSerializationKind.BlittableStruct or FieldSerializationKind.Nested | ||
| ? (field.NullableUnderlyingType is not null || field.Symbol.Type.TypeKind is TypeKind.Class) | ||
| ? GetQualifiedViewName(field.NestedSerializableType) + "?" | ||
| : GetQualifiedViewName(field.NestedSerializableType) | ||
| : propertyType; | ||
| sourceBuilder.AppendLine($"{propertyAccessibility} {propertyReturnType} {EscapeIdentifier(field.Symbol.Name)}"); | ||
| sourceBuilder.OpenBlock(); | ||
| sourceBuilder.AppendLine("get"); | ||
| sourceBuilder.OpenBlock(); | ||
| if (containingModel.IsBlittableStruct) | ||
| { | ||
| sourceBuilder.AppendLine($"{containingModel.QualifiedSourceTypeName} blittableSourceValue = MemoryMarshal.Read<{containingModel.QualifiedSourceTypeName}>(serializedMemory.Span);"); | ||
| sourceBuilder.AppendLine($"return blittableSourceValue.{EscapeIdentifier(field.Symbol.Name)};"); | ||
| if (field.Kind == FieldSerializationKind.BlittableStruct | ||
| && field.NestedSerializableType is not null) | ||
| { | ||
| sourceBuilder.AppendLine($"return new {GetQualifiedViewName(field.NestedSerializableType)}(serializedMemory.Slice({field.BlittableByteOffset}, {field.ElementByteCount}));"); | ||
| } | ||
| else | ||
| { | ||
| sourceBuilder.AppendLine($"{containingModel.QualifiedSourceTypeName} blittableSourceValue = MemoryMarshal.Read<{containingModel.QualifiedSourceTypeName}>(serializedMemory.Span);"); | ||
| sourceBuilder.AppendLine($"return blittableSourceValue.{EscapeIdentifier(field.Symbol.Name)};"); | ||
| } | ||
| sourceBuilder.CloseBlock(); | ||
| sourceBuilder.CloseBlock(); | ||
| return; | ||
|
|
@@ -1214,14 +1231,8 @@ | |
| // Null is represented entirely by the offset table; no property payload marker is read. | ||
| sourceBuilder.AppendLine("if (fieldDataOffset == 0)"); | ||
| sourceBuilder.OpenBlock(); | ||
| if (field.NullableUnderlyingType is not null && field.Kind != FieldSerializationKind.Nested) | ||
| { | ||
| sourceBuilder.AppendLine("return null;"); | ||
| } | ||
| else | ||
| { | ||
| sourceBuilder.AppendLine("return default;"); | ||
| } | ||
| // Always use 'default' instead of 'null' for reference types. | ||
| sourceBuilder.AppendLine("return default;"); | ||
| sourceBuilder.CloseBlock(); | ||
| } | ||
|
|
||
|
|
@@ -1268,8 +1279,8 @@ | |
| } | ||
|
|
||
| sourceBuilder.CloseBlock(); | ||
| sourceBuilder.CloseBlock(); | ||
|
Check warning on line 1282 in src/ZeroSerializerGenerator.cs
|
||
| } | ||
|
Check warning on line 1283 in src/ZeroSerializerGenerator.cs
|
||
|
|
||
| private static void EmitViewCollectionHeader( | ||
| GeneratedSourceBuilder sourceBuilder, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -246,12 +246,12 @@ public void VariableDataRoundTrip() | |
|
|
||
| int expectedRequiredByteLength = -(24 + (4 * IntPtr.Size)); | ||
| TestAssert.Equal(expectedRequiredByteLength, VariableRecordView.RequiredByteLength, nameof(VariableRecordView.RequiredByteLength)); | ||
| TestAssert.Equal(source.Text, view.Text.ToString(), nameof(view.Text)); | ||
| TestAssert.SequenceEqual<int>(source.Values, view.Values, nameof(view.Values)); | ||
| TestAssert.Equal(source.OptionalNumber, view.OptionalNumber, nameof(view.OptionalNumber)); | ||
| TestAssert.Equal(source.Child.Identifier, view.Child.Identifier, nameof(view.Child.Identifier)); | ||
| TestAssert.Equal(source.Child.State, view.Child.State, nameof(view.Child.State)); | ||
| TestAssert.Equal(source.Tail, view.Tail, nameof(view.Tail)); | ||
| TestAssert.Equal(source.Text, view.Text.ToString(), nameof(source.Text)); | ||
| TestAssert.SequenceEqual<int>(source.Values, view.Values, nameof(source.Values)); | ||
| TestAssert.Equal(source.OptionalNumber, view.OptionalNumber, nameof(source.OptionalNumber)); | ||
| TestAssert.Equal(source.Child.Identifier, view.Child?.Identifier ?? -1, nameof(source.Child.Identifier)); | ||
| TestAssert.Equal(source.Child.State, view.Child?.State ?? ByteState.None, nameof(source.Child.State)); | ||
| TestAssert.Equal(source.Tail, view.Tail, nameof(source.Tail)); | ||
| int textFieldOffset = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(0, 4)); | ||
| int valuesFieldOffset = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(4, 4)); | ||
| int optionalNumberFieldOffset = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(8, 4)); | ||
|
|
@@ -284,8 +284,8 @@ public void VariableViewOnlyRequiresCorrectSerializedStart() | |
| TestAssert.Equal(source.Text, view.Text.ToString(), nameof(view.Text)); | ||
| TestAssert.SequenceEqual<int>(source.Values, view.Values, nameof(view.Values)); | ||
| TestAssert.Equal(source.OptionalNumber, view.OptionalNumber, nameof(view.OptionalNumber)); | ||
| TestAssert.Equal(source.Child.Identifier, view.Child.Identifier, nameof(view.Child.Identifier)); | ||
| TestAssert.Equal(source.Child.State, view.Child.State, nameof(view.Child.State)); | ||
| TestAssert.Equal(source.Child.Identifier, view.Child?.Identifier ?? -1, nameof(FixedClassView.Identifier)); | ||
| TestAssert.Equal(source.Child.State, view.Child?.State ?? ByteState.None, nameof(FixedClassView.State)); | ||
| TestAssert.Equal(source.Tail, view.Tail, nameof(view.Tail)); | ||
|
|
||
| ReadOnlyMemory<byte> borrowedSerializedMemory = view; | ||
|
|
@@ -521,9 +521,9 @@ public void EveryTruncatedSerializedBufferThrowsStandardBoundsExceptionWhenRead( | |
| _ = view.Text.Length; | ||
| _ = view.Values.Length; | ||
| _ = view.OptionalNumber; | ||
| FixedClassView childView = view.Child; | ||
| _ = childView.Identifier; | ||
| _ = childView.State; | ||
| FixedClassView? childView = view.Child; | ||
| _ = childView?.Identifier; | ||
| _ = childView?.State; | ||
| _ = view.Tail; | ||
| }, | ||
| nameof(VariableRecord)); | ||
|
|
@@ -777,7 +777,7 @@ public void NestedTypesReturnViewsTest() | |
| // 2. Assert that nested non-blittable type returns view | ||
| PropertyInfo? childProperty = typeof(VariableRecordView).GetProperty(nameof(VariableRecordView.Child)); | ||
| Assert.NotNull(childProperty); | ||
| Assert.Equal(typeof(FixedClassView), childProperty!.PropertyType); | ||
| Assert.Equal(typeof(FixedClassView?), childProperty!.PropertyType); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win Test the nullable child behavior at runtime. The reflection assertion verifies only that 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| public void StrictBlittableStructTests() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: sator-imaging/ZeroSerializer
Length of output: 9845
🏁 Script executed:
Repository: sator-imaging/ZeroSerializer
Length of output: 2992
🏁 Script executed:
Repository: sator-imaging/ZeroSerializer
Length of output: 25531
🏁 Script executed:
Repository: sator-imaging/ZeroSerializer
Length of output: 41906
🏁 Script executed:
Repository: sator-imaging/ZeroSerializer
Length of output: 21519
🏁 Script executed:
Repository: sator-imaging/ZeroSerializer
Length of output: 2672
Use concrete fallbacks for absent nested data.
When
nestedis null, both default views read empty memory and throw. UseReadOnlySpan<char>.Emptyand scalarint/longfallbacks. ReadLabel,Summary.Number, andSummary.Amountonly inside a branch wherenestedis present, then use those scalar values in the hash.🤖 Prompt for AI Agents