Skip to content

Commit

Permalink
Creating new TypeName without reparsing + usage in NRBF (#103713)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
  • Loading branch information
3 people authored Aug 14, 2024
1 parent 6086fe1 commit cb42ed8
Show file tree
Hide file tree
Showing 18 changed files with 568 additions and 218 deletions.
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

0 comments on commit cb42ed8

Please sign in to comment.