Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ internal sealed partial class GrammarActions
ValidateDataLabelFixups();
ImmutableArray<ValidatedExport> validatedExports =
ValidateExports(validatedVTableAssociations, machine);
PseudoCustomAttributes.Lower(_entityRegistry, _diagnostics);

// Return early if there are structural errors that prevent building valid metadata.
// However, allow errors in method bodies (ILA0016-0019) to pass through so we can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,14 @@ private void WriteCustomBlobNamedArguments(
}

private EntityRegistry.CustomAttributeEntity MaterializeCustomAttribute(
CustomAttributeDescriptorValue descriptor)
CustomAttributeDescriptorValue descriptor,
IToken location)
{
EntityRegistry.EntityBase constructor = MaterializeMethodReference(descriptor.Constructor);
BlobBuilder value = MaterializeCustomAttributeBlob(descriptor.Value);
EntityRegistry.CustomAttributeEntity attribute =
_entityRegistry.CreateCustomAttribute(constructor, value);
attribute.Location = Location.From(location, _documents);
if (descriptor.Owner is { } owner)
{
attribute.Owner = MaterializeOwnerType(owner);
Expand All @@ -196,6 +198,7 @@ private EntityRegistry.CustomAttributeEntity MaterializeCustomAttribute(
EntityRegistry.CustomAttributeEntity typedefAttribute =
_entityRegistry.CreateCustomAttribute(resolved.Constructor, resolved.Value);
typedefAttribute.Owner = resolved.Owner;
typedefAttribute.Location = Location.From(location, _documents);
return typedefAttribute;
}

Expand All @@ -204,7 +207,7 @@ private EntityRegistry.CustomAttributeEntity MaterializeCustomAttribute(
return null;
}

EntityRegistry.CustomAttributeEntity attribute = MaterializeCustomAttribute(descriptor);
EntityRegistry.CustomAttributeEntity attribute = MaterializeCustomAttribute(descriptor, location);
return descriptor.Owner is null ? attribute : null;
}

Expand All @@ -214,7 +217,7 @@ private EntityRegistry.CustomAttributeEntity MaterializeCustomAttribute(

internal EntityRegistry.CustomAttributeEntity MaterializeCustomAttributeDescriptor(
CILParser.CustomDescrContext context)
=> MaterializeCustomAttribute(context.Value);
=> MaterializeCustomAttribute(context.Value, context.Start);

internal EntityRegistry.CustomAttributeEntity? MaterializeMethodBodyCustomAttributeDeclaration(
CILParser.CustomDescrInMethodBodyContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private void MaterializeTypedef(TypedefDeclarationValue declaration)
break;
case CustomAttributeTypedefDeclarationValue customAttribute:
EntityRegistry.CustomAttributeEntity attribute =
MaterializeCustomAttribute(customAttribute.Attribute);
MaterializeCustomAttribute(customAttribute.Attribute, customAttribute.Location);
EntityRegistry.EntityBase? owner = attribute.Owner;
attribute.Owner = null;
_typedefs[customAttribute.Alias] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ internal void SetClassSize(IToken token)
_currentTypeDefinition.PeekOrDefault() is { } currentType)
{
currentType.ClassSize = ParseInt32(token);
currentType.HasExplicitClassSize = true;
}
}

Expand All @@ -88,6 +89,7 @@ internal void SetClassPackingSize(IToken token)
_currentTypeDefinition.PeekOrDefault() is { } currentType)
{
currentType.PackingSize = ParseInt32(token);
currentType.HasExplicitPackingSize = true;
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/tools/ilasm/src/ILAssembler/Diagnostic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public static class DiagnosticIds
public const string DuplicateExportOrdinal = "ILA0044";
public const string UnsupportedVTableDeclaration = "ILA0045";
public const string InvalidOctalLiteral = "ILA0046";
public const string PseudoCustomAttributeInvalidTarget = "ILA0047";
public const string PseudoCustomAttributeInvalidValue = "ILA0048";
public const string PseudoCustomAttributeInvalidBlob = "ILA0049";
public const string PseudoCustomAttributeInvalidGuid = "ILA0050";
public const string PseudoCustomAttributeUnknownArgument = "ILA0051";
public const string PseudoCustomAttributeRepeatedArgument = "ILA0052";
}

internal static class DiagnosticMessageTemplates
Expand Down Expand Up @@ -114,4 +120,10 @@ internal static class DiagnosticMessageTemplates
public const string DuplicateExportOrdinal = "Export '{0}' uses ordinal {1}, which is already assigned to a different VTable target";
public const string UnsupportedVTableDeclaration = "Raw .vtable declarations are not supported; use .vtfixup instead";
public const string InvalidOctalLiteral = "The value '{0}' is not a valid octal literal";
public const string PseudoCustomAttributeInvalidTarget = "Custom attribute '{0}' cannot be applied to this target";
public const string PseudoCustomAttributeInvalidValue = "Custom attribute '{0}' has an invalid argument value";
public const string PseudoCustomAttributeInvalidBlob = "Custom attribute '{0}' has a malformed value blob";
public const string PseudoCustomAttributeInvalidGuid = "Custom attribute '{0}' does not specify a valid GUID";
public const string PseudoCustomAttributeUnknownArgument = "Custom attribute '{0}' does not have a field or property named '{1}'";
public const string PseudoCustomAttributeRepeatedArgument = "Custom attribute '{0}' specifies '{1}' more than once";
}
67 changes: 64 additions & 3 deletions src/tools/ilasm/src/ILAssembler/EntityRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,34 @@ public TypeDefinitionEntity GetOrCreateTypeDefinition(TypeDefinitionEntity? cont
return null;
}

/// <summary>
/// Finds the local type definition that a type reference refers to, without assigning or
/// reading any handles. Passes that run before <see cref="WriteContentTo"/> resolves
/// references need this, because handles are not assigned until emission.
/// </summary>
public TypeDefinitionEntity? FindLocalTypeDefinition(TypeReferenceEntity typeReference)
{
switch (typeReference.ResolutionScope)
{
case TypeReferenceEntity enclosing:
return FindLocalTypeDefinition(enclosing) is { } enclosingTypeDef
? FindTypeDefinition(enclosingTypeDef, typeReference.Namespace, typeReference.Name)
: null;

case AssemblyReferenceEntity assemblyReference:
return Assembly is not null
&& string.Equals(assemblyReference.Name, Assembly.Name, StringComparison.OrdinalIgnoreCase)
? FindTypeDefinition(null, typeReference.Namespace, typeReference.Name)
: null;

case ModuleEntity:
return FindTypeDefinition(null, typeReference.Namespace, typeReference.Name);

default:
return null;
}
}

public AssemblyReferenceEntity GetOrCreateAssemblyReference(string name, Action<AssemblyReferenceEntity> onCreateAssemblyReference)
{
return GetOrCreateEntity(name, TableIndex.AssemblyRef, _seenAssemblyRefs, _ => new(name), onCreateAssemblyReference);
Expand Down Expand Up @@ -1678,6 +1706,31 @@ public CustomAttributeEntity CreateCustomAttribute(EntityBase constructor, BlobB
return entity;
}

/// <summary>
/// Removes custom attributes that were lowered to metadata bits or auxiliary table rows
/// by the pseudo custom attribute pass, and renumbers the handles of the survivors.
/// </summary>
/// <remarks>
/// Nothing in metadata refers to a CustomAttribute handle (a CustomAttribute can never be
/// the parent of another custom attribute), so removing rows is safe. Handles are still
/// reassigned so that they stay consistent with the emitted row numbers.
/// </remarks>
public void RemoveCustomAttributes(IReadOnlyCollection<CustomAttributeEntity> toRemove)
{
if (toRemove.Count == 0 || !_seenEntities.TryGetValue(TableIndex.CustomAttribute, out List<EntityBase>? entities))
{
return;
}

var removeSet = new HashSet<CustomAttributeEntity>(toRemove);
entities.RemoveAll(entity => entity is CustomAttributeEntity customAttribute && removeSet.Contains(customAttribute));

for (int i = 0; i < entities.Count; i++)
{
((IHasHandle)entities[i]).SetHandle(MetadataTokens.EntityHandle(TableIndex.CustomAttribute, i + 1));
}
}

public static MethodImplementationEntity CreateUnrecordedMethodImplementation(MethodDefinitionEntity methodBody, MemberReferenceEntity methodDeclaration)
{
return new MethodImplementationEntity(methodBody.ContainingType, methodBody, methodDeclaration);
Expand Down Expand Up @@ -1872,6 +1925,8 @@ static string CreateReflectionNotation(TypeDefinitionEntity typeDefinition)
// ClassLayout table fields
public int? PackingSize { get; set; }
public int? ClassSize { get; set; }
public bool HasExplicitPackingSize { get; set; }
public bool HasExplicitClassSize { get; set; }
}

public sealed class TypeReferenceEntity(EntityBase resolutionScope, string @namespace, string name) : TypeEntity, IHasReflectionNotation
Expand Down Expand Up @@ -2023,7 +2078,7 @@ public sealed class MethodDefinitionEntity(TypeDefinitionEntity containingType,

public sealed class ParameterEntity(ParameterAttributes attributes, string? name, BlobBuilder marshallingDescriptor, int sequence) : EntityBase
{
public ParameterAttributes Attributes { get; } = attributes;
public ParameterAttributes Attributes { get; set; } = attributes;
public string? Name { get; } = name;
public BlobBuilder MarshallingDescriptor { get; set; } = marshallingDescriptor;
public bool HasCustomAttributes { get; set; }
Expand Down Expand Up @@ -2088,6 +2143,12 @@ public sealed class CustomAttributeEntity(EntityBase constructor, BlobBuilder va
public EntityBase? Owner { get; set; }
public EntityBase Constructor { get; } = constructor;
public BlobBuilder Value { get; } = value;

/// <summary>
/// Source location of the originating <c>.custom</c> directive, used to report
/// diagnostics from the pseudo custom attribute lowering pass, which runs after parsing.
/// </summary>
public Location? Location { get; set; }
}

public sealed class MethodImplementationEntity(TypeDefinitionEntity type, EntityBase methodBody, MemberReferenceEntity methodDeclaration) : EntityBase
Expand All @@ -2099,7 +2160,7 @@ public sealed class MethodImplementationEntity(TypeDefinitionEntity type, Entity

public sealed class FieldDefinitionEntity(FieldAttributes attributes, TypeDefinitionEntity type, string name, BlobBuilder signature) : EntityBase
{
public FieldAttributes Attributes { get; } = attributes;
public FieldAttributes Attributes { get; set; } = attributes;
public TypeDefinitionEntity ContainingType { get; } = type;
public string Name { get; } = name;
public BlobBuilder Signature { get; } = signature;
Expand All @@ -2123,7 +2184,7 @@ public sealed class InterfaceImplementationEntity(TypeDefinitionEntity type, Typ

public sealed class EventEntity(EventAttributes attributes, TypeEntity? type, string name) : EntityBase
{
public EventAttributes Attributes { get; } = attributes;
public EventAttributes Attributes { get; set; } = attributes;
public TypeEntity? Type { get; } = type;
public string Name { get; } = name;

Expand Down
39 changes: 39 additions & 0 deletions src/tools/ilasm/src/ILAssembler/MetadataExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,40 @@

using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;

namespace ILAssembler;

internal static class MetadataExtensions
{
extension(MethodImplAttributes)
{
public static MethodImplAttributes UserMask =>
MethodImplAttributes.ManagedMask |
MethodImplAttributes.ForwardRef |
MethodImplAttributes.PreserveSig |
MethodImplAttributes.InternalCall |
MethodImplAttributes.Synchronized |
MethodImplAttributes.NoInlining |
MethodImplAttributes.AggressiveInlining |
MethodImplAttributes.NoOptimization |
MethodImplAttributes.AggressiveOptimization |
MethodImplAttributes.Async;
}

extension(TypeAttributes)
{
public static TypeAttributes ExtendedLayout => (TypeAttributes)0x18;
public static TypeAttributes Forwarder => (TypeAttributes)0x00200000;
}

extension(UnmanagedType)
{
public static int ArraySizeParamIndexSpecified => 0x0001;
public static UnmanagedType ByValStr => (UnmanagedType)0x22;
public static UnmanagedType Max => (UnmanagedType)0x50;
}

extension(DeclarativeSecurityAction)
{
public static DeclarativeSecurityAction Request => (DeclarativeSecurityAction)1;
Expand All @@ -35,3 +58,19 @@ internal static class MetadataExtensions
public static ILOpCode Unused => (ILOpCode)0xFE22;
}
}

internal static class ClassInterfaceTypeExtensions
{
extension(ClassInterfaceType)
{
public static ClassInterfaceType Last => (ClassInterfaceType)3;
}
}

internal static class ComInterfaceTypeExtensions
{
extension(ComInterfaceType)
{
public static ComInterfaceType Last => (ComInterfaceType)4;
}
}
Loading