Skip to content

Commit d8b1554

Browse files
authored
Merge branch 'release/9.0' into release/9.0-staging
2 parents fcee4ed + 3c456bc commit d8b1554

39 files changed

+1740
-841
lines changed

NuGet.config

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
1111
<!-- Begin: Package sources from dotnet-emsdk -->
1212
<add key="darc-pub-dotnet-emsdk-2c27e40" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-emsdk-2c27e405/nuget/v3/index.json" />
13+
<add key="darc-pub-dotnet-emsdk-4c9d1b1" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-emsdk-4c9d1b11/nuget/v3/index.json" />
1314
<!-- End: Package sources from dotnet-emsdk -->
1415
<!-- Begin: Package sources from dotnet-sdk -->
1516
<add key="darc-pub-dotnet-sdk-a345a00" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-sdk-a345a003/nuget/v3/index.json" />

eng/Versions.props

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<Project>
22
<PropertyGroup>
33
<!-- The .NET product branding version -->
4-
<ProductVersion>9.0.1</ProductVersion>
4+
<ProductVersion>9.0.2</ProductVersion>
55
<!-- File version numbers -->
66
<MajorVersion>9</MajorVersion>
77
<MinorVersion>0</MinorVersion>
8-
<PatchVersion>1</PatchVersion>
8+
<PatchVersion>2</PatchVersion>
99
<SdkBandVersion>9.0.100</SdkBandVersion>
10-
<PackageVersionNet8>8.0.11</PackageVersionNet8>
10+
<PackageVersionNet8>8.0.$([MSBuild]::Add($(PatchVersion),11))</PackageVersionNet8>
1111
<PackageVersionNet7>7.0.20</PackageVersionNet7>
1212
<PackageVersionNet6>6.0.36</PackageVersionNet6>
1313
<PreReleaseVersionLabel>servicing</PreReleaseVersionLabel>

src/libraries/System.Formats.Nrbf/ref/System.Formats.Nrbf.cs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ namespace System.Formats.Nrbf
99
public abstract partial class ArrayRecord : System.Formats.Nrbf.SerializationRecord
1010
{
1111
internal ArrayRecord() { }
12-
public virtual long FlattenedLength { get { throw null; } }
1312
public override System.Formats.Nrbf.SerializationRecordId Id { get { throw null; } }
1413
public abstract System.ReadOnlySpan<int> Lengths { get; }
1514
public int Rank { get { throw null; } }

src/libraries/System.Formats.Nrbf/src/PACKAGE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ There are more than a dozen different serialization [record types](https://learn
5454
- `PrimitiveTypeRecord<T>` derives from the non-generic [PrimitiveTypeRecord](https://learn.microsoft.com/dotnet/api/system.formats.nrbf.primitivetyperecord), which also exposes a [Value](https://learn.microsoft.com/dotnet/api/system.formats.nrbf.primitivetyperecord.value) property. But on the base class, the value is returned as `object` (which introduces boxing for value types).
5555
- [ClassRecord](https://learn.microsoft.com/dotnet/api/system.formats.nrbf.classrecord): describes all `class` and `struct` besides the aforementioned primitive types.
5656
- [ArrayRecord](https://learn.microsoft.com/dotnet/api/system.formats.nrbf.arrayrecord): describes all array records, including jagged and multi-dimensional arrays.
57-
- [`SZArrayRecord<T>`](https://learn.microsoft.com/dotnet/api/system.formats.nrbf.szarrayrecord-1): describes single-dimensional, zero-indexed array records, where `T` can be either a primitive type or a `ClassRecord`.
57+
- [`SZArrayRecord<T>`](https://learn.microsoft.com/dotnet/api/system.formats.nrbf.szarrayrecord-1): describes single-dimensional, zero-indexed array records, where `T` can be either a primitive type or a `SerializationRecord`.
5858

5959
```csharp
6060
SerializationRecord rootObject = NrbfDecoder.Decode(payload); // payload is a Stream

src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/AllowedRecordType.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ internal enum AllowedRecordTypes : uint
2828
ArraySingleString = 1 << SerializationRecordType.ArraySingleString,
2929

3030
Nulls = ObjectNull | ObjectNullMultiple256 | ObjectNullMultiple,
31+
Arrays = ArraySingleObject | ArraySinglePrimitive | ArraySingleString | BinaryArray,
3132

3233
/// <summary>
3334
/// Any .NET object (a primitive, a reference type, a reference or single null).
3435
/// </summary>
3536
AnyObject = MemberPrimitiveTyped
36-
| ArraySingleObject | ArraySinglePrimitive | ArraySingleString | BinaryArray
37+
| Arrays
3738
| ClassWithId | ClassWithMembersAndTypes | SystemClassWithMembersAndTypes
3839
| BinaryObjectString
3940
| MemberReference

src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArrayRecord.cs

+85-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
using System.Diagnostics.CodeAnalysis;
55
using System.Reflection.Metadata;
66
using System.Formats.Nrbf.Utils;
7+
using System.Collections.Generic;
8+
using System.Diagnostics;
9+
using System.Runtime.Serialization;
710

811
namespace System.Formats.Nrbf;
912

@@ -27,12 +30,6 @@ private protected ArrayRecord(ArrayInfo arrayInfo)
2730
/// <value>A buffer of integers that represent the number of elements in every dimension.</value>
2831
public abstract ReadOnlySpan<int> Lengths { get; }
2932

30-
/// <summary>
31-
/// When overridden in a derived class, gets the total number of all elements in every dimension.
32-
/// </summary>
33-
/// <value>A number that represent the total number of all elements in every dimension.</value>
34-
public virtual long FlattenedLength => ArrayInfo.FlattenedLength;
35-
3633
/// <summary>
3734
/// Gets the rank of the array.
3835
/// </summary>
@@ -118,4 +115,86 @@ private void HandleNext(object value, NextInfo info, int size)
118115
}
119116

120117
internal abstract (AllowedRecordTypes allowed, PrimitiveType primitiveType) GetAllowedRecordType();
118+
119+
internal static void Populate(List<SerializationRecord> source, Array destination, int[] lengths, AllowedRecordTypes allowedRecordTypes, bool allowNulls)
120+
{
121+
int[] indices = new int[lengths.Length];
122+
nuint numElementsWritten = 0; // only for debugging; not used in release builds
123+
124+
foreach (SerializationRecord record in source)
125+
{
126+
object? value = GetActualValue(record, allowedRecordTypes, out int incrementCount);
127+
if (value is not null)
128+
{
129+
// null is a default element for all array of reference types, so we don't call SetValue for nulls.
130+
destination.SetValue(value, indices);
131+
Debug.Assert(incrementCount == 1, "IncrementCount other than 1 is allowed only for null records.");
132+
}
133+
else if (!allowNulls)
134+
{
135+
ThrowHelper.ThrowArrayContainedNulls();
136+
}
137+
138+
while (incrementCount > 0)
139+
{
140+
incrementCount--;
141+
numElementsWritten++;
142+
int dimension = indices.Length - 1;
143+
while (dimension >= 0)
144+
{
145+
indices[dimension]++;
146+
if (indices[dimension] < lengths[dimension])
147+
{
148+
break;
149+
}
150+
indices[dimension] = 0;
151+
dimension--;
152+
}
153+
154+
if (dimension < 0)
155+
{
156+
break;
157+
}
158+
}
159+
}
160+
161+
Debug.Assert(numElementsWritten == (uint)source.Count, "We should have traversed the entirety of the source records collection.");
162+
Debug.Assert(numElementsWritten == (ulong)destination.LongLength, "We should have traversed the entirety of the destination array.");
163+
}
164+
165+
private static object? GetActualValue(SerializationRecord record, AllowedRecordTypes allowedRecordTypes, out int repeatCount)
166+
{
167+
repeatCount = 1;
168+
169+
if (record is NullsRecord nullsRecord)
170+
{
171+
repeatCount = nullsRecord.NullCount;
172+
return null;
173+
}
174+
else if (record.RecordType == SerializationRecordType.MemberReference)
175+
{
176+
record = ((MemberReferenceRecord)record).GetReferencedRecord();
177+
}
178+
179+
if (allowedRecordTypes == AllowedRecordTypes.BinaryObjectString)
180+
{
181+
if (record is not BinaryObjectStringRecord stringRecord)
182+
{
183+
throw new SerializationException(SR.Serialization_InvalidReference);
184+
}
185+
186+
return stringRecord.Value;
187+
}
188+
else if (allowedRecordTypes == AllowedRecordTypes.Arrays)
189+
{
190+
if (record is not ArrayRecord arrayRecord)
191+
{
192+
throw new SerializationException(SR.Serialization_InvalidReference);
193+
}
194+
195+
return arrayRecord;
196+
}
197+
198+
return record;
199+
}
121200
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Diagnostics.CodeAnalysis;
8+
using System.Formats.Nrbf.Utils;
9+
using System.Linq;
10+
using System.Reflection.Metadata;
11+
using System.Runtime.CompilerServices;
12+
using System.Runtime.InteropServices;
13+
using System.Text;
14+
using System.Threading.Tasks;
15+
16+
namespace System.Formats.Nrbf
17+
{
18+
internal sealed class ArrayRectangularPrimitiveRecord<T> : ArrayRecord where T : unmanaged
19+
{
20+
private readonly int[] _lengths;
21+
private readonly IReadOnlyList<T> _values;
22+
private TypeName? _typeName;
23+
24+
internal ArrayRectangularPrimitiveRecord(ArrayInfo arrayInfo, int[] lengths, IReadOnlyList<T> values) : base(arrayInfo)
25+
{
26+
_lengths = lengths;
27+
_values = values;
28+
ValuesToRead = 0; // there is nothing to read anymore
29+
}
30+
31+
public override ReadOnlySpan<int> Lengths => _lengths;
32+
33+
public override SerializationRecordType RecordType => SerializationRecordType.BinaryArray;
34+
35+
public override TypeName TypeName
36+
=> _typeName ??= TypeNameHelpers.GetPrimitiveTypeName(TypeNameHelpers.GetPrimitiveType<T>()).MakeArrayTypeName(Rank);
37+
38+
internal override (AllowedRecordTypes allowed, PrimitiveType primitiveType) GetAllowedRecordType() => throw new InvalidOperationException();
39+
40+
private protected override void AddValue(object value) => throw new InvalidOperationException();
41+
42+
[RequiresDynamicCode("May call Array.CreateInstance().")]
43+
private protected override Array Deserialize(Type arrayType, bool allowNulls)
44+
{
45+
Array result =
46+
#if NET9_0_OR_GREATER
47+
Array.CreateInstanceFromArrayType(arrayType, _lengths);
48+
#else
49+
Array.CreateInstance(typeof(T), _lengths);
50+
#endif
51+
int[] indices = new int[_lengths.Length];
52+
nuint numElementsWritten = 0; // only for debugging; not used in release builds
53+
54+
for (int i = 0; i < _values.Count; i++)
55+
{
56+
result.SetValue(_values[i], indices);
57+
numElementsWritten++;
58+
59+
int dimension = indices.Length - 1;
60+
while (dimension >= 0)
61+
{
62+
indices[dimension]++;
63+
if (indices[dimension] < Lengths[dimension])
64+
{
65+
break;
66+
}
67+
indices[dimension] = 0;
68+
dimension--;
69+
}
70+
71+
if (dimension < 0)
72+
{
73+
break;
74+
}
75+
}
76+
77+
Debug.Assert(numElementsWritten == (uint)_values.Count, "We should have traversed the entirety of the source values collection.");
78+
Debug.Assert(numElementsWritten == (ulong)result.LongLength, "We should have traversed the entirety of the destination array.");
79+
80+
return result;
81+
}
82+
}
83+
}

src/libraries/System.Formats.Nrbf/src/System/Formats/Nrbf/ArraySingleObjectRecord.cs

+14-12
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ namespace System.Formats.Nrbf;
1515
/// <remarks>
1616
/// ArraySingleObject records are described in <see href="https://learn.microsoft.com/openspecs/windows_protocols/ms-nrbf/982b2f50-6367-402a-aaf2-44ee96e2a5e0">[MS-NRBF] 2.4.3.2</see>.
1717
/// </remarks>
18-
internal sealed class ArraySingleObjectRecord : SZArrayRecord<object?>
18+
internal sealed class ArraySingleObjectRecord : SZArrayRecord<SerializationRecord>
1919
{
20-
private ArraySingleObjectRecord(ArrayInfo arrayInfo) : base(arrayInfo) => Records = [];
20+
internal ArraySingleObjectRecord(ArrayInfo arrayInfo) : base(arrayInfo) => Records = [];
2121

2222
public override SerializationRecordType RecordType => SerializationRecordType.ArraySingleObject;
2323

@@ -27,25 +27,26 @@ public override TypeName TypeName
2727
private List<SerializationRecord> Records { get; }
2828

2929
/// <inheritdoc/>
30-
public override object?[] GetArray(bool allowNulls = true)
31-
=> (object?[])(allowNulls ? _arrayNullsAllowed ??= ToArray(true) : _arrayNullsNotAllowed ??= ToArray(false));
30+
public override SerializationRecord?[] GetArray(bool allowNulls = true)
31+
=> (SerializationRecord?[])(allowNulls ? _arrayNullsAllowed ??= ToArray(true) : _arrayNullsNotAllowed ??= ToArray(false));
3232

33-
private object?[] ToArray(bool allowNulls)
33+
private SerializationRecord?[] ToArray(bool allowNulls)
3434
{
35-
object?[] values = new object?[Length];
35+
SerializationRecord?[] values = new SerializationRecord?[Length];
3636

3737
int valueIndex = 0;
3838
for (int recordIndex = 0; recordIndex < Records.Count; recordIndex++)
3939
{
4040
SerializationRecord record = Records[recordIndex];
4141

42-
int nullCount = record is NullsRecord nullsRecord ? nullsRecord.NullCount : 0;
43-
if (nullCount == 0)
42+
if (record is MemberReferenceRecord referenceRecord)
4443
{
45-
// "new object[] { <SELF> }" is special cased because it allows for storing reference to itself.
46-
values[valueIndex++] = record is MemberReferenceRecord referenceRecord && referenceRecord.Reference.Equals(Id)
47-
? values // a reference to self, and a way to get StackOverflow exception ;)
48-
: record.GetValue();
44+
record = referenceRecord.GetReferencedRecord();
45+
}
46+
47+
if (record is not NullsRecord nullsRecord)
48+
{
49+
values[valueIndex++] = record;
4950
continue;
5051
}
5152

@@ -54,6 +55,7 @@ public override TypeName TypeName
5455
ThrowHelper.ThrowArrayContainedNulls();
5556
}
5657

58+
int nullCount = nullsRecord.NullCount;
5759
do
5860
{
5961
values[valueIndex++] = null;

0 commit comments

Comments
 (0)