Skip to content
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

Creating new TypeName without reparsing + usage in NRBF #103713

Merged
merged 21 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
86595de
new TypeName methods
adamsitnik Jun 18, 2024
d906606
do not join the type and library name strings when UndoTruncatedTypeN…
adamsitnik Jun 19, 2024
448f01e
cache known primitive type names (and SZArrays of these)
adamsitnik Jun 19, 2024
b37b8da
add more tests and finish WithAssemblyName implementation
adamsitnik Jun 19, 2024
6b933d9
finish the implementation
adamsitnik Jun 28, 2024
f707dfd
Merge remote-tracking branch 'upstream/main' into extendTypeName
adamsitnik Jun 28, 2024
1736b24
add missing IntPtr and UIntPtr support (discovered once I've re-enabl…
adamsitnik Jun 28, 2024
a54e55f
use TypeNameMatches in more places
adamsitnik Jun 28, 2024
7d2d0d6
Merge remote-tracking branch 'upstream/main' into extendTypeName
adamsitnik Aug 2, 2024
9a0cd58
address API review feedback
adamsitnik Aug 2, 2024
aabe51b
remove CreateSimpleTypeName, introduce MakeSimpleTypeNameThrowsForNon…
adamsitnik Aug 2, 2024
e207e0f
handle nested names properly
adamsitnik Aug 6, 2024
19f1e82
update the code after reading it again on GH
adamsitnik Aug 6, 2024
6d03c00
Merge remote-tracking branch 'upstream/main' into extendTypeName
adamsitnik Aug 12, 2024
3abda02
address code review feedback
adamsitnik Aug 12, 2024
50a58fb
address code review feedback
adamsitnik Aug 12, 2024
ebfc35e
address code review feedback
adamsitnik Aug 13, 2024
f50ae19
rename MakeSimpleTypeName to WithAssemblyName
adamsitnik Aug 13, 2024
69cd1fa
Apply suggestions from code review
adamsitnik Aug 14, 2024
aa0870f
don't use sbyte to store array rank, the limit of 32 can be changed i…
adamsitnik Aug 14, 2024
30be71e
fix the build
adamsitnik Aug 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libraries/System.Formats.Nrbf/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,7 @@
<data name="NotSupported_NonZeroOffsets" xml:space="preserve">
<value>Only arrays with zero offsets are supported.</value>
</data>
<data name="Serialization_InvalidAssemblyName" xml:space="preserve">
<value>Invalid assembly name: `{0}`.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ namespace System.Formats.Nrbf;
/// </remarks>
internal sealed class ArraySingleObjectRecord : SZArrayRecord<object?>
{
private static TypeName? s_typeName;

private ArraySingleObjectRecord(ArrayInfo arrayInfo) : base(arrayInfo) => Records = [];

public override SerializationRecordType RecordType => SerializationRecordType.ArraySingleObject;

public override TypeName TypeName
=> s_typeName ??= TypeName.Parse(("System.Object[], " + TypeNameExtensions.CoreLibAssemblyName).AsSpan());
=> TypeNameHelpers.GetPrimitiveSZArrayTypeName(TypeNameHelpers.ObjectPrimitiveType);

private List<SerializationRecord> Records { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ namespace System.Formats.Nrbf;
internal sealed class ArraySinglePrimitiveRecord<T> : SZArrayRecord<T>
where T : unmanaged
{
private static TypeName? s_typeName;

internal ArraySinglePrimitiveRecord(ArrayInfo arrayInfo, IReadOnlyList<T> values) : base(arrayInfo)
{
Values = values;
Expand All @@ -35,8 +33,7 @@ internal ArraySinglePrimitiveRecord(ArrayInfo arrayInfo, IReadOnlyList<T> values
public override SerializationRecordType RecordType => SerializationRecordType.ArraySinglePrimitive;

/// <inheritdoc />
public override TypeName TypeName
=> s_typeName ??= TypeName.Parse((typeof(T[]).FullName + "," + TypeNameExtensions.CoreLibAssemblyName).AsSpan());
public override TypeName TypeName => TypeNameHelpers.GetPrimitiveSZArrayTypeName(TypeNameHelpers.GetPrimitiveType<T>());

internal IReadOnlyList<T> Values { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@ namespace System.Formats.Nrbf;
/// </remarks>
internal sealed class ArraySingleStringRecord : SZArrayRecord<string?>
{
private static TypeName? s_typeName;

private ArraySingleStringRecord(ArrayInfo arrayInfo) : base(arrayInfo) => Records = [];

public override SerializationRecordType RecordType => SerializationRecordType.ArraySingleString;

/// <inheritdoc />
public override TypeName TypeName
=> s_typeName ??= TypeName.Parse(("System.String[], " + TypeNameExtensions.CoreLibAssemblyName).AsSpan());
public override TypeName TypeName => TypeNameHelpers.GetPrimitiveSZArrayTypeName(PrimitiveType.String);

private List<SerializationRecord> Records { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Formats.Nrbf.Utils;
using System.IO;
using System.Reflection.Metadata;

Expand All @@ -15,7 +16,13 @@ namespace System.Formats.Nrbf;
/// </remarks>
internal sealed class BinaryLibraryRecord : SerializationRecord
{
private BinaryLibraryRecord(SerializationRecordId libraryId, string libraryName)
private BinaryLibraryRecord(SerializationRecordId libraryId, string rawLibraryName)
{
Id = libraryId;
RawLibraryName = rawLibraryName;
}

private BinaryLibraryRecord(SerializationRecordId libraryId, AssemblyNameInfo libraryName)
{
Id = libraryId;
LibraryName = libraryName;
Expand All @@ -32,11 +39,27 @@ public override TypeName TypeName
}
}

internal string LibraryName { get; }
internal string? RawLibraryName { get; }

internal AssemblyNameInfo? LibraryName { get; }

/// <inheritdoc />
public override SerializationRecordId Id { get; }

internal static BinaryLibraryRecord Decode(BinaryReader reader)
=> new(SerializationRecordId.Decode(reader), reader.ReadString());
internal static BinaryLibraryRecord Decode(BinaryReader reader, PayloadOptions options)
{
SerializationRecordId id = SerializationRecordId.Decode(reader);
string rawName = reader.ReadString();

if (AssemblyNameInfo.TryParse(rawName.AsSpan(), out AssemblyNameInfo? assemblyNameInfo))
{
return new BinaryLibraryRecord(id, assemblyNameInfo);
}
else if (!options.UndoTruncatedTypeNames)
{
ThrowHelper.ThrowInvalidAssemblyName(rawName);
}

return new BinaryLibraryRecord(id, rawName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,63 +142,26 @@ internal TypeName GetArrayTypeName(ArrayInfo arrayInfo)
{
(BinaryType binaryType, object? additionalInfo) = Infos[0];

switch (binaryType)
TypeName elementTypeName = binaryType switch
{
case BinaryType.String:
return typeof(string).BuildCoreLibArrayTypeName(arrayInfo.Rank);
case BinaryType.StringArray:
return typeof(string[]).BuildCoreLibArrayTypeName(arrayInfo.Rank);
case BinaryType.Object:
return typeof(object).BuildCoreLibArrayTypeName(arrayInfo.Rank);
case BinaryType.ObjectArray:
return typeof(object[]).BuildCoreLibArrayTypeName(arrayInfo.Rank);
case BinaryType.Primitive:
Type primitiveType = ((PrimitiveType)additionalInfo!) switch
{
PrimitiveType.Boolean => typeof(bool),
PrimitiveType.Byte => typeof(byte),
PrimitiveType.Char => typeof(char),
PrimitiveType.Decimal => typeof(decimal),
PrimitiveType.Double => typeof(double),
PrimitiveType.Int16 => typeof(short),
PrimitiveType.Int32 => typeof(int),
PrimitiveType.Int64 => typeof(long),
PrimitiveType.SByte => typeof(sbyte),
PrimitiveType.Single => typeof(float),
PrimitiveType.TimeSpan => typeof(TimeSpan),
PrimitiveType.DateTime => typeof(DateTime),
PrimitiveType.UInt16 => typeof(ushort),
PrimitiveType.UInt32 => typeof(uint),
_ => typeof(ulong),
};

return primitiveType.BuildCoreLibArrayTypeName(arrayInfo.Rank);
case BinaryType.PrimitiveArray:
Type primitiveArrayType = ((PrimitiveType)additionalInfo!) switch
{
PrimitiveType.Boolean => typeof(bool[]),
PrimitiveType.Byte => typeof(byte[]),
PrimitiveType.Char => typeof(char[]),
PrimitiveType.Decimal => typeof(decimal[]),
PrimitiveType.Double => typeof(double[]),
PrimitiveType.Int16 => typeof(short[]),
PrimitiveType.Int32 => typeof(int[]),
PrimitiveType.Int64 => typeof(long[]),
PrimitiveType.SByte => typeof(sbyte[]),
PrimitiveType.Single => typeof(float[]),
PrimitiveType.TimeSpan => typeof(TimeSpan[]),
PrimitiveType.DateTime => typeof(DateTime[]),
PrimitiveType.UInt16 => typeof(ushort[]),
PrimitiveType.UInt32 => typeof(uint[]),
_ => typeof(ulong[]),
};

return primitiveArrayType.BuildCoreLibArrayTypeName(arrayInfo.Rank);
case BinaryType.SystemClass:
return ((TypeName)additionalInfo!).BuildArrayTypeName(arrayInfo.Rank);
default:
Debug.Assert(binaryType is BinaryType.Class, "The parsers should reject other inputs");
return (((ClassTypeInfo)additionalInfo!).TypeName).BuildArrayTypeName(arrayInfo.Rank);
}
BinaryType.String => TypeNameHelpers.GetPrimitiveTypeName(PrimitiveType.String),
BinaryType.StringArray => TypeNameHelpers.GetPrimitiveSZArrayTypeName(PrimitiveType.String),
BinaryType.Primitive => TypeNameHelpers.GetPrimitiveTypeName((PrimitiveType)additionalInfo!),
BinaryType.PrimitiveArray => TypeNameHelpers.GetPrimitiveSZArrayTypeName((PrimitiveType)additionalInfo!),
BinaryType.Object => TypeNameHelpers.GetPrimitiveTypeName(TypeNameHelpers.ObjectPrimitiveType),
BinaryType.ObjectArray => TypeNameHelpers.GetPrimitiveSZArrayTypeName(TypeNameHelpers.ObjectPrimitiveType),
BinaryType.SystemClass => (TypeName)additionalInfo!,
BinaryType.Class => ((ClassTypeInfo)additionalInfo!).TypeName,
_ => throw new ArgumentOutOfRangeException(paramName: nameof(binaryType), actualValue: binaryType, message: null)
};

// In general, arrayRank == 1 may have two different meanings:
// - [] is a single-dimensional array with a zero lower bound (SZArray),
// - [*] is a single-dimensional array with an arbitrary lower bound (variable bound array).
// Variable bound arrays are not supported by design, so in our case it's always SZArray.
// That is why we don't call TypeName.MakeArrayTypeName(1) because it would create [*] instead of [] name.
return arrayInfo.Rank == 1
? elementTypeName.MakeSZArrayTypeName()
: elementTypeName.MakeArrayTypeName(arrayInfo.Rank);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ private static SerializationRecord DecodeNext(BinaryReader reader, RecordMap rec
SerializationRecordType.ArraySinglePrimitive => DecodeArraySinglePrimitiveRecord(reader),
SerializationRecordType.ArraySingleString => ArraySingleStringRecord.Decode(reader),
SerializationRecordType.BinaryArray => BinaryArrayRecord.Decode(reader, recordMap, options),
SerializationRecordType.BinaryLibrary => BinaryLibraryRecord.Decode(reader),
SerializationRecordType.BinaryLibrary => BinaryLibraryRecord.Decode(reader, options),
SerializationRecordType.BinaryObjectString => BinaryObjectStringRecord.Decode(reader),
SerializationRecordType.ClassWithId => ClassWithIdRecord.Decode(reader, recordMap),
SerializationRecordType.ClassWithMembersAndTypes => ClassWithMembersAndTypesRecord.Decode(reader, recordMap, options),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ namespace System.Formats.Nrbf;
[DebuggerDisplay("{Value}")]
public abstract class PrimitiveTypeRecord<T> : PrimitiveTypeRecord
{
private static TypeName? s_typeName;

private protected PrimitiveTypeRecord(T value) => Value = value;

/// <summary>
Expand All @@ -36,8 +34,7 @@ public abstract class PrimitiveTypeRecord<T> : PrimitiveTypeRecord
public new T Value { get; }

/// <inheritdoc />
public override TypeName TypeName
=> s_typeName ??= TypeName.Parse(typeof(T).FullName.AsSpan()).WithCoreLibAssemblyName();
public override TypeName TypeName => TypeNameHelpers.GetPrimitiveTypeName(TypeNameHelpers.GetPrimitiveType<T>());

internal override object? GetValue() => Value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,30 @@ internal override (AllowedRecordTypes allowed, PrimitiveType primitiveType) GetN
// to get a single primitive value!
internal SerializationRecord TryToMapToUserFriendly()
{
if (!TypeName.IsSimple)
{
return this;
}

if (MemberValues.Count == 1)
{
if (HasMember("m_value"))
{
return MemberValues[0] switch
{
// there can be a value match, but no TypeName match
bool value when TypeName.FullName == typeof(bool).FullName => Create(value),
byte value when TypeName.FullName == typeof(byte).FullName => Create(value),
sbyte value when TypeName.FullName == typeof(sbyte).FullName => Create(value),
char value when TypeName.FullName == typeof(char).FullName => Create(value),
short value when TypeName.FullName == typeof(short).FullName => Create(value),
ushort value when TypeName.FullName == typeof(ushort).FullName => Create(value),
int value when TypeName.FullName == typeof(int).FullName => Create(value),
uint value when TypeName.FullName == typeof(uint).FullName => Create(value),
long value when TypeName.FullName == typeof(long).FullName => Create(value),
ulong value when TypeName.FullName == typeof(ulong).FullName => Create(value),
float value when TypeName.FullName == typeof(float).FullName => Create(value),
double value when TypeName.FullName == typeof(double).FullName => Create(value),
bool value when TypeNameMatches(typeof(bool)) => Create(value),
byte value when TypeNameMatches(typeof(byte)) => Create(value),
sbyte value when TypeNameMatches(typeof(sbyte)) => Create(value),
char value when TypeNameMatches(typeof(char)) => Create(value),
short value when TypeNameMatches(typeof(short)) => Create(value),
ushort value when TypeNameMatches(typeof(ushort)) => Create(value),
int value when TypeNameMatches(typeof(int)) => Create(value),
uint value when TypeNameMatches(typeof(uint)) => Create(value),
long value when TypeNameMatches(typeof(long)) => Create(value),
ulong value when TypeNameMatches(typeof(ulong)) => Create(value),
float value when TypeNameMatches(typeof(float)) => Create(value),
double value when TypeNameMatches(typeof(double)) => Create(value),
_ => this
};
}
Expand All @@ -65,12 +70,12 @@ internal SerializationRecord TryToMapToUserFriendly()
return MemberValues[0] switch
{
// there can be a value match, but no TypeName match
long value when TypeName.FullName == typeof(IntPtr).FullName => Create(new IntPtr(value)),
ulong value when TypeName.FullName == typeof(UIntPtr).FullName => Create(new UIntPtr(value)),
long value when TypeNameMatches(typeof(IntPtr)) => Create(new IntPtr(value)),
ulong value when TypeNameMatches(typeof(UIntPtr)) => Create(new UIntPtr(value)),
_ => this
};
}
else if (HasMember("_ticks") && MemberValues[0] is long ticks && TypeName.FullName == typeof(TimeSpan).FullName)
else if (HasMember("_ticks") && MemberValues[0] is long ticks && TypeNameMatches(typeof(TimeSpan)))
{
return Create(new TimeSpan(ticks));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ internal static void ThrowInvalidValue(object value)
internal static void ThrowInvalidReference()
=> throw new SerializationException(SR.Serialization_InvalidReference);

internal static void ThrowInvalidTypeName(string name)
=> throw new SerializationException(SR.Format(SR.Serialization_InvalidTypeName, name));

internal static void ThrowUnexpectedNullRecordCount()
=> throw new SerializationException(SR.Serialization_UnexpectedNullRecordCount);

Expand All @@ -23,6 +26,9 @@ internal static void ThrowMaxArrayLength(long limit, long actual)
internal static void ThrowArrayContainedNulls()
=> throw new SerializationException(SR.Serialization_ArrayContainedNulls);

internal static void ThrowInvalidAssemblyName(string rawName)
=> throw new SerializationException(SR.Format(SR.Serialization_InvalidAssemblyName, rawName));

internal static void ThrowEndOfStreamException()
=> throw new EndOfStreamException();

Expand Down
Loading
Loading