Skip to content

Commit 261e842

Browse files
committed
address API review feedback: expose LibraryName via TypeName.AssemblyName
1 parent c1a0926 commit 261e842

15 files changed

+73
-59
lines changed

System.Runtime.Serialization.BinaryFormat.Tests/ReadExactTypesTests.cs

+8-4
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ public void CanRead_CustomTypeWithStringFields(string? text)
7878
Assert.True(classRecord.HasMember(nameof(CustomTypeWithStringField.Text)));
7979
Assert.Equal(input.Text, classRecord.GetString(nameof(CustomTypeWithStringField.Text)));
8080
Assert.Equal(typeof(CustomTypeWithStringField).FullName, classRecord.TypeName.FullName);
81-
Assert.Equal(typeof(CustomTypeWithStringField).Assembly.FullName, classRecord.LibraryName.FullName);
81+
Assert.Equal(typeof(CustomTypeWithStringField).AssemblyQualifiedName, classRecord.TypeName.AssemblyQualifiedName);
82+
Assert.Equal(typeof(CustomTypeWithStringField).Assembly.FullName, classRecord.TypeName.AssemblyName!.FullName);
8283
Assert.False(classRecord.HasMember("NotPresent"));
8384
}
8485

@@ -232,7 +233,7 @@ public void CanRead_ComplexSystemType()
232233
Assert.True(classRecord.HasMember(nameof(Exception.Message)));
233234
Assert.Equal(input.Message, classRecord.GetString(nameof(Exception.Message)));
234235
Assert.Equal(typeof(Exception).FullName, classRecord.TypeName.FullName);
235-
Assert.Equal("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", classRecord.LibraryName.FullName);
236+
Assert.Equal("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", classRecord.TypeName.AssemblyName!.FullName);
236237
Assert.False(classRecord.HasMember("NotPresent"));
237238
}
238239

@@ -264,7 +265,7 @@ public void CanRead_ArraysOfComplexTypes()
264265
ArrayRecord<ClassRecord> arrayRecord = ((ArrayRecord<ClassRecord>)PayloadReader.Read(Serialize(input)));
265266

266267
Assert.Equal(typeof(CustomTypeWithPrimitiveFields).FullName, arrayRecord.ElementTypeName.FullName);
267-
Assert.Equal(typeof(CustomTypeWithPrimitiveFields).Assembly.FullName, arrayRecord.ElementTypeLibraryName.FullName);
268+
Assert.Equal(typeof(CustomTypeWithPrimitiveFields).Assembly.FullName, arrayRecord.ElementTypeName.AssemblyName!.FullName);
268269
ClassRecord?[] classRecords = arrayRecord.ToArray();
269270
for (int i = 0; i < input.Length; i++)
270271
{
@@ -334,7 +335,7 @@ public void CanRead_ArraysOfObjects()
334335
ArrayRecord arrayRecord = (ArrayRecord)PayloadReader.Read(Serialize(input));
335336

336337
Assert.Equal(typeof(object).FullName, arrayRecord.ElementTypeName.FullName);
337-
Assert.Equal("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", arrayRecord.ElementTypeLibraryName.FullName);
338+
Assert.Equal("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", arrayRecord.ElementTypeName.AssemblyName!.FullName);
338339
Assert.Equal(input, ((ArrayRecord<object>)arrayRecord).ToArray());
339340
}
340341

@@ -437,5 +438,8 @@ public void CanReadStruct()
437438

438439
Assert.Equal(input.Integer, classRecord.GetInt32(nameof(SerializableStruct.Integer)));
439440
Assert.Equal(input.Text, classRecord.GetString(nameof(SerializableStruct.Text)));
441+
Assert.Equal(typeof(SerializableStruct).FullName, classRecord.TypeName.FullName);
442+
Assert.Equal(typeof(SerializableStruct).AssemblyQualifiedName, classRecord.TypeName.AssemblyQualifiedName);
443+
Assert.Equal(typeof(SerializableStruct).Assembly.FullName, classRecord.TypeName.AssemblyName!.FullName);
440444
}
441445
}

System.Runtime.Serialization.BinaryFormat.Tests/TypeMatchTests.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ private static void VerifySZArray<T>(T input) where T : notnull
374374
ArrayRecord arrayRecord = (ArrayRecord)PayloadReader.Read(Serialize(array));
375375

376376
Assert.Equal(FormatterServices.GetTypeFullNameIncludingTypeForwards(typeof(T)), arrayRecord.ElementTypeName.FullName);
377-
Assert.Equal(FormatterServices.GetAssemblyNameIncludingTypeForwards(typeof(T)), arrayRecord.ElementTypeLibraryName.FullName);
377+
Assert.Equal(FormatterServices.GetAssemblyNameIncludingTypeForwards(typeof(T)), arrayRecord.ElementTypeName.AssemblyName!.FullName);
378378

379379
if (PrimitiveTypes.Contains(typeof(T)))
380380
{
@@ -384,6 +384,7 @@ private static void VerifySZArray<T>(T input) where T : notnull
384384
{
385385
Assert.True(arrayRecord is ArrayRecord<ClassRecord>, userMessage: typeof(T).Name);
386386
Assert.True(arrayRecord.IsTypeNameMatching(typeof(T[])));
387+
Assert.Equal(arrayRecord.ElementTypeName.AssemblyName.FullName, FormatterServices.GetAssemblyNameIncludingTypeForwards(typeof(T)));
387388
}
388389

389390
foreach (Type type in PrimitiveTypes)

System.Runtime.Serialization.BinaryFormat/Infos/MemberTypeInfo.cs

+10-22
Original file line numberDiff line numberDiff line change
@@ -206,20 +206,20 @@ internal bool ShouldBeRepresentedAsArrayOfClassRecords()
206206
return false;
207207
}
208208

209-
internal TypeName GetElementTypeName()
209+
internal TypeName GetElementTypeName(RecordMap recordMap)
210210
{
211211
(BinaryType binaryType, object? additionalInfo) = Infos[0];
212212

213213
switch (binaryType)
214214
{
215215
case BinaryType.String:
216-
return TypeName.Parse(typeof(string).FullName.AsSpan());
216+
return TypeName.Parse(typeof(string).FullName.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName);
217217
case BinaryType.StringArray:
218-
return TypeName.Parse(typeof(string[]).FullName.AsSpan());
218+
return TypeName.Parse(typeof(string[]).FullName.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName); ;
219219
case BinaryType.Object:
220-
return TypeName.Parse(typeof(object).FullName.AsSpan());
220+
return TypeName.Parse(typeof(object).FullName.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName); ;
221221
case BinaryType.ObjectArray:
222-
return TypeName.Parse(typeof(object[]).FullName.AsSpan());
222+
return TypeName.Parse(typeof(object[]).FullName.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName); ;
223223
case BinaryType.Primitive:
224224
case BinaryType.PrimitiveArray:
225225
string? name = ((PrimitiveType)additionalInfo!) switch
@@ -243,29 +243,17 @@ internal TypeName GetElementTypeName()
243243
};
244244

245245
return binaryType is BinaryType.PrimitiveArray
246-
? TypeName.Parse($"{name}[]".AsSpan())
247-
: TypeName.Parse(name.AsSpan());
246+
? TypeName.Parse($"{name}[], {FormatterServices.CoreLibAssemblyName.FullName}".AsSpan())
247+
: TypeName.Parse(name.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName);
248248

249249
case BinaryType.SystemClass:
250-
return (TypeName)additionalInfo!;
250+
return ((TypeName)additionalInfo!).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName);
251251
case BinaryType.Class:
252252
ClassTypeInfo typeInfo = (ClassTypeInfo)additionalInfo!;
253-
return typeInfo.TypeName;
253+
AssemblyNameInfo libraryName = ((BinaryLibraryRecord)recordMap[typeInfo.LibraryId]).LibraryName;
254+
return typeInfo.TypeName.WithAssemblyName(libraryName.FullName);
254255
default:
255256
throw new NotSupportedException();
256257
}
257258
}
258-
259-
internal AssemblyNameInfo GetElementLibraryName(RecordMap recordMap)
260-
{
261-
(BinaryType binaryType, object? additionalInfo) = Infos[0];
262-
263-
if (binaryType is BinaryType.Class)
264-
{
265-
ClassTypeInfo typeInfo = (ClassTypeInfo)additionalInfo!;
266-
return ((BinaryLibraryRecord)recordMap[typeInfo.LibraryId]).LibraryName;
267-
}
268-
269-
return FormatterServices.CoreLibAssemblyName;
270-
}
271259
}

System.Runtime.Serialization.BinaryFormat/Records/ArrayOfClassesRecord.cs

+2-5
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ namespace System.Runtime.Serialization.BinaryFormat;
77

88
internal sealed class ArrayOfClassesRecord : ArrayRecord<ClassRecord>
99
{
10-
private AssemblyNameInfo? _elementTypeLibraryName;
10+
private TypeName? _elementTypeName;
1111

1212
internal ArrayOfClassesRecord(ArrayInfo arrayInfo, MemberTypeInfo memberTypeInfo, RecordMap recordMap)
1313
: base(arrayInfo)
1414
{
1515
MemberTypeInfo = memberTypeInfo;
1616
RecordMap = recordMap;
17-
ElementTypeName = MemberTypeInfo.GetElementTypeName();
1817
Records = [];
1918
}
2019

@@ -26,9 +25,7 @@ internal ArrayOfClassesRecord(ArrayInfo arrayInfo, MemberTypeInfo memberTypeInfo
2625

2726
private RecordMap RecordMap { get; }
2827

29-
public override TypeName ElementTypeName { get; }
30-
31-
public override AssemblyNameInfo ElementTypeLibraryName => _elementTypeLibraryName ??= MemberTypeInfo.GetElementLibraryName(RecordMap);
28+
public override TypeName ElementTypeName => _elementTypeName ??= MemberTypeInfo.GetElementTypeName(RecordMap);
3229

3330
protected override ClassRecord?[] ToArrayOfT(bool allowNulls)
3431
{

System.Runtime.Serialization.BinaryFormat/Records/ArrayRecord.cs

-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ private protected ArrayRecord(ArrayInfo arrayInfo)
3232

3333
public abstract TypeName ElementTypeName { get; }
3434

35-
public abstract AssemblyNameInfo ElementTypeLibraryName { get; }
36-
3735
public override int ObjectId => ArrayInfo.ObjectId;
3836

3937
internal long ValuesToRead { get; private protected set; }

System.Runtime.Serialization.BinaryFormat/Records/ArraySingleObjectRecord.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ internal sealed class ArraySingleObjectRecord : ArrayRecord<object?>
2323

2424
public override RecordType RecordType => RecordType.ArraySingleObject;
2525

26-
public override TypeName ElementTypeName => s_elementTypeName ??= TypeName.Parse(typeof(object).FullName.AsSpan());
27-
28-
public override AssemblyNameInfo ElementTypeLibraryName => FormatterServices.CoreLibAssemblyName;
26+
public override TypeName ElementTypeName
27+
=> s_elementTypeName ??= TypeName.Parse(typeof(object).FullName.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName);
2928

3029
private List<SerializationRecord> Records { get; }
3130

System.Runtime.Serialization.BinaryFormat/Records/ArraySinglePrimitiveRecord.cs

+2-4
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ internal sealed class ArraySinglePrimitiveRecord<T> : ArrayRecord<T>
2121
where T : unmanaged
2222
{
2323
private static TypeName? s_elementTypeName;
24-
private static AssemblyNameInfo? s_elementTypeLibraryName;
2524

2625
internal ArraySinglePrimitiveRecord(ArrayInfo arrayInfo, IReadOnlyList<T> values) : base(arrayInfo)
2726
{
@@ -31,9 +30,8 @@ internal ArraySinglePrimitiveRecord(ArrayInfo arrayInfo, IReadOnlyList<T> values
3130

3231
public override RecordType RecordType => RecordType.ArraySinglePrimitive;
3332

34-
public override TypeName ElementTypeName => s_elementTypeName ??= TypeName.Parse(typeof(T).FullName.AsSpan());
35-
36-
public override AssemblyNameInfo ElementTypeLibraryName => s_elementTypeLibraryName ??= AssemblyNameInfo.Parse(FormatterServices.GetAssemblyNameIncludingTypeForwards(typeof(T)).AsSpan());
33+
public override TypeName ElementTypeName
34+
=> s_elementTypeName ??= TypeName.Parse(typeof(T).FullName.AsSpan()).WithAssemblyName(FormatterServices.GetAssemblyNameIncludingTypeForwards(typeof(T)));
3735

3836
internal IReadOnlyList<T> Values { get; }
3937

System.Runtime.Serialization.BinaryFormat/Records/ArraySingleStringRecord.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ internal sealed class ArraySingleStringRecord : ArrayRecord<string?>
2323

2424
public override RecordType RecordType => RecordType.ArraySingleString;
2525

26-
public override TypeName ElementTypeName => s_elementTypeName ??= TypeName.Parse(typeof(string).FullName.AsSpan());
27-
28-
public override AssemblyNameInfo ElementTypeLibraryName => FormatterServices.CoreLibAssemblyName;
26+
public override TypeName ElementTypeName
27+
=> s_elementTypeName ??= TypeName.Parse(typeof(string).FullName.AsSpan()).WithAssemblyName(FormatterServices.CoreLibAssemblyName.FullName);
2928

3029
private List<SerializationRecord> Records { get; }
3130

System.Runtime.Serialization.BinaryFormat/Records/BinaryArrayRecord.cs

+3-5
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,20 @@ internal sealed class BinaryArrayRecord : ArrayRecord
1616
typeof(TimeSpan), typeof(string), typeof(object)
1717
];
1818

19-
private AssemblyNameInfo? _elementTypeLibraryName;
19+
private TypeName? _elementTypeName;
2020

2121
private BinaryArrayRecord(ArrayInfo arrayInfo, MemberTypeInfo memberTypeInfo, RecordMap recordMap)
2222
: base(arrayInfo)
2323
{
2424
MemberTypeInfo = memberTypeInfo;
2525
RecordMap = recordMap;
26-
ElementTypeName = memberTypeInfo.GetElementTypeName();
2726
Values = [];
2827
}
2928

3029
public override RecordType RecordType => RecordType.BinaryArray;
3130

32-
public override TypeName ElementTypeName { get; }
33-
34-
public override AssemblyNameInfo ElementTypeLibraryName => _elementTypeLibraryName ??= MemberTypeInfo.GetElementLibraryName(RecordMap);
31+
public override TypeName ElementTypeName
32+
=> _elementTypeName ??= MemberTypeInfo.GetElementTypeName(RecordMap);
3533

3634
private MemberTypeInfo MemberTypeInfo { get; }
3735

System.Runtime.Serialization.BinaryFormat/Records/ClassRecord.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ public abstract class ClassRecord : SerializationRecord
2020
{
2121
private const int MaxLength = ArrayRecord.DefaultMaxArrayLength;
2222

23+
private TypeName? _typeName;
24+
2325
private protected ClassRecord(ClassInfo classInfo)
2426
{
2527
ClassInfo = classInfo;
2628
MemberValues = [];
2729
}
2830

29-
public TypeName TypeName => ClassInfo.Name;
31+
public TypeName TypeName => _typeName ??= ClassInfo.Name.WithAssemblyName(LibraryName.FullName);
3032

31-
public abstract AssemblyNameInfo LibraryName { get; }
33+
internal abstract AssemblyNameInfo LibraryName { get; }
3234

3335
// Currently we don't expose raw values, so we are not preserving the order here.
3436
public IEnumerable<string> MemberNames => ClassInfo.MemberNames.Keys;

System.Runtime.Serialization.BinaryFormat/Records/ClassWithIdRecord.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private ClassWithIdRecord(int objectId, ClassRecord metadataClass) : base(metada
2525

2626
public override RecordType RecordType => RecordType.ClassWithId;
2727

28-
public override AssemblyNameInfo LibraryName => MetadataClass.LibraryName;
28+
internal override AssemblyNameInfo LibraryName => MetadataClass.LibraryName;
2929

3030
public override int ObjectId { get; }
3131

System.Runtime.Serialization.BinaryFormat/Records/ClassWithMembersAndTypesRecord.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ private ClassWithMembersAndTypesRecord(ClassInfo classInfo, BinaryLibraryRecord
2626

2727
public override RecordType RecordType => RecordType.ClassWithMembersAndTypes;
2828

29-
public override AssemblyNameInfo LibraryName => Library.LibraryName;
29+
internal override AssemblyNameInfo LibraryName => Library.LibraryName;
3030

3131
internal BinaryLibraryRecord Library { get; }
3232

System.Runtime.Serialization.BinaryFormat/Records/RectangularOrCustomOffsetArrayRecord.cs

+3-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.BinaryFormat;
99

1010
internal sealed class RectangularOrCustomOffsetArrayRecord : ArrayRecord
1111
{
12-
private AssemblyNameInfo? _elementTypeLibraryName;
12+
private TypeName? _elementTypeName;
1313

1414
private RectangularOrCustomOffsetArrayRecord(Type elementType, ArrayInfo arrayInfo,
1515
MemberTypeInfo memberTypeInfo, int[] lengths, int[] offsets, RecordMap recordMap) : base(arrayInfo)
@@ -19,15 +19,13 @@ private RectangularOrCustomOffsetArrayRecord(Type elementType, ArrayInfo arrayIn
1919
Lengths = lengths;
2020
Offsets = offsets;
2121
RecordMap = recordMap;
22-
ElementTypeName = memberTypeInfo.GetElementTypeName();
2322
Values = new();
2423
}
2524

2625
public override RecordType RecordType => RecordType.BinaryArray;
2726

28-
public override TypeName ElementTypeName { get; }
29-
30-
public override AssemblyNameInfo ElementTypeLibraryName => _elementTypeLibraryName ??= MemberTypeInfo.GetElementLibraryName(RecordMap);
27+
public override TypeName ElementTypeName
28+
=> _elementTypeName ??= MemberTypeInfo.GetElementTypeName(RecordMap);
3129

3230
private Type ElementType { get; }
3331

System.Runtime.Serialization.BinaryFormat/Records/SystemClassWithMembersAndTypesRecord.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ private SystemClassWithMembersAndTypesRecord(ClassInfo classInfo, MemberTypeInfo
1515

1616
public override RecordType RecordType => RecordType.SystemClassWithMembersAndTypes;
1717

18-
public override AssemblyNameInfo LibraryName => FormatterServices.CoreLibAssemblyName;
18+
internal override AssemblyNameInfo LibraryName => FormatterServices.CoreLibAssemblyName;
1919

2020
internal MemberTypeInfo MemberTypeInfo { get; }
2121

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Buffers;
2+
using System.Reflection.Metadata;
3+
4+
namespace System.Runtime.Serialization.BinaryFormat;
5+
6+
internal static class TypeNameExtensions
7+
{
8+
internal static TypeName WithAssemblyName(this TypeName typeName, string assemblyName)
9+
{
10+
// For ClassWithMembersAndTypesRecord, the TypeName and LibraryName and provided separately,
11+
// and the LibraryName may not be known when parsing TypeName.
12+
// For SystemClassWithMembersAndTypesRecord, the LibraryName is not provided, it's always mscorlib.
13+
// Ideally, we would just create TypeName with new AssemblyNameInfo.
14+
// This will be possible once https://github.com/dotnet/runtime/issues/102263 is done.
15+
16+
int length = typeName.FullName.Length + 1 + assemblyName.Length;
17+
char[] rented = ArrayPool<char>.Shared.Rent(length);
18+
19+
try
20+
{
21+
typeName.FullName.AsSpan().CopyTo(rented);
22+
rented[typeName.FullName.Length] = ',';
23+
assemblyName.AsSpan().CopyTo(rented.AsSpan(typeName.FullName.Length + 1));
24+
25+
return TypeName.Parse(rented.AsSpan(0, length));
26+
}
27+
finally
28+
{
29+
ArrayPool<char>.Shared.Return(rented);
30+
}
31+
}
32+
}

0 commit comments

Comments
 (0)