|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Roslyn.Utilities;
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Reflection.Metadata;
namespace Microsoft.CodeAnalysis.RuntimeMembers
{
[Flags()]
internal enum MemberFlags : byte
{
// BEGIN Mutually exclusive Member kinds:
Method = 0x01,
Field = 0x02,
Constructor = 0x04,
PropertyGet = 0x08,
Property = 0x10,
// END Mutually exclusive Member kinds
KindMask = 0x1F,
Static = 0x20,
Virtual = 0x40, // Virtual in CLR terms, i.e. sealed should be accepted.
}
/// <summary>
/// Structure that describes a member of a type.
/// </summary>
internal readonly struct MemberDescriptor
{
public readonly MemberFlags Flags;
/// <summary>
/// Id/token of containing type, usually value from some enum.
/// For example from SpecialType enum.
/// I am not using SpecialType as the type for this field because
/// VB runtime types are not part of SpecialType.
///
/// So, the implication is that any type ids we use outside of the SpecialType
/// (either for the VB runtime classes, or types like System.Task etc.) will need
/// to use IDs that are all mutually disjoint.
/// </summary>
private readonly short _declaringTypeId;
public bool IsSpecialTypeMember => _declaringTypeId < (int)InternalSpecialType.NextAvailable;
public ExtendedSpecialType DeclaringSpecialType
{
get
{
Debug.Assert(_declaringTypeId < (int)InternalSpecialType.NextAvailable);
return (ExtendedSpecialType)_declaringTypeId;
}
}
public WellKnownType DeclaringWellKnownType
{
get
{
Debug.Assert(_declaringTypeId >= (int)WellKnownType.First);
return (WellKnownType)_declaringTypeId;
}
}
public string DeclaringTypeMetadataName
{
get
{
return IsSpecialTypeMember
? DeclaringSpecialType.GetMetadataName()!
: DeclaringWellKnownType.GetMetadataName();
}
}
public readonly ushort Arity;
public readonly string Name;
/// <summary>
/// Signature of the field or method, similar to metadata signature,
/// but with the following exceptions:
/// 1) Truncated on the left, for methods starts at [ParamCount], for fields at [Type]
/// 2) Type tokens are not compressed
/// 3) BOOLEAN | CHAR | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I | U | Void types are encoded by
/// using VALUETYPE+typeId notation.
/// 4) array bounds are not included.
/// 5) modifiers are not included.
/// 6) (CLASS | VALUETYPE) are omitted after GENERICINST
/// </summary>
public readonly ImmutableArray<byte> Signature;
/// <summary>
/// Applicable only to properties and methods, throws otherwise.
/// </summary>
public int ParametersCount
{
get
{
MemberFlags memberKind = Flags & MemberFlags.KindMask;
switch (memberKind)
{
case MemberFlags.Constructor:
case MemberFlags.Method:
case MemberFlags.PropertyGet:
case MemberFlags.Property:
return Signature[0];
default:
throw ExceptionUtilities.UnexpectedValue(memberKind);
}
}
}
public MemberDescriptor(
MemberFlags Flags,
short DeclaringTypeId,
string Name,
ImmutableArray<byte> Signature,
ushort Arity = 0)
{
this.Flags = Flags;
this._declaringTypeId = DeclaringTypeId;
this.Name = Name;
this.Arity = Arity;
this.Signature = Signature;
}
internal static ImmutableArray<MemberDescriptor> InitializeFromStream(Stream stream, string[] nameTable)
{
int count = nameTable.Length;
var builder = ImmutableArray.CreateBuilder<MemberDescriptor>(count);
var signatureBuilder = ImmutableArray.CreateBuilder<byte>();
for (int i = 0; i < count; i++)
{
MemberFlags flags = (MemberFlags)stream.ReadByte();
short declaringTypeId = ReadTypeId(stream);
ushort arity = (ushort)stream.ReadByte();
if ((flags & MemberFlags.Field) != 0)
{
ParseType(signatureBuilder, stream);
}
else
{
// Property, PropertyGet, Method or Constructor
ParseMethodOrPropertySignature(signatureBuilder, stream);
}
builder.Add(new MemberDescriptor(flags, declaringTypeId, nameTable[i], signatureBuilder.ToImmutable(), arity));
signatureBuilder.Clear();
}
return builder.ToImmutable();
}
/// <summary>
/// The type Id may be:
/// (1) encoded in a single byte (for types below 255)
/// (2) encoded in two bytes (255 + extension byte) for types below 512
/// </summary>
private static short ReadTypeId(Stream stream)
{
var firstByte = (byte)stream.ReadByte();
if (firstByte == (byte)WellKnownType.ExtSentinel)
{
return (short)(stream.ReadByte() + WellKnownType.ExtSentinel);
}
else
{
return firstByte;
}
}
private static void ParseMethodOrPropertySignature(ImmutableArray<byte>.Builder builder, Stream stream)
{
int paramCount = stream.ReadByte();
builder.Add((byte)paramCount);
// Return type
ParseType(builder, stream, allowByRef: true);
// Parameters
for (int i = 0; i < paramCount; i++)
{
ParseType(builder, stream, allowByRef: true);
}
}
private static void ParseType(ImmutableArray<byte>.Builder builder, Stream stream, bool allowByRef = false)
{
while (true)
{
var typeCode = (SignatureTypeCode)stream.ReadByte();
builder.Add((byte)typeCode);
switch (typeCode)
{
default:
throw ExceptionUtilities.UnexpectedValue(typeCode);
case SignatureTypeCode.TypeHandle:
ParseTypeHandle(builder, stream);
return;
case SignatureTypeCode.GenericTypeParameter:
case SignatureTypeCode.GenericMethodParameter:
builder.Add((byte)stream.ReadByte());
return;
case SignatureTypeCode.ByReference:
if (!allowByRef) goto default;
break;
case SignatureTypeCode.SZArray:
break;
case SignatureTypeCode.Pointer:
break;
case SignatureTypeCode.GenericTypeInstance:
ParseGenericTypeInstance(builder, stream);
return;
}
allowByRef = false;
}
}
/// <summary>
/// Read a type Id from the stream and copy it into the builder.
/// This may copy one or two bytes depending on the first one.
/// </summary>
private static void ParseTypeHandle(ImmutableArray<byte>.Builder builder, Stream stream)
{
var firstByte = (byte)stream.ReadByte();
builder.Add(firstByte);
if (firstByte == (byte)WellKnownType.ExtSentinel)
{
var secondByte = (byte)stream.ReadByte();
builder.Add(secondByte);
}
}
private static void ParseGenericTypeInstance(ImmutableArray<byte>.Builder builder, Stream stream)
{
ParseType(builder, stream);
// Generic type parameters
int argumentCount = stream.ReadByte();
builder.Add((byte)argumentCount);
for (int i = 0; i < argumentCount; i++)
{
ParseType(builder, stream);
}
}
}
}
|