diff --git a/src/tests/JIT/Directed/Directed_d.csproj b/src/tests/JIT/Directed/Directed_d.csproj
index 65944a198ae2fc..fc2213d756ad44 100644
--- a/src/tests/JIT/Directed/Directed_d.csproj
+++ b/src/tests/JIT/Directed/Directed_d.csproj
@@ -3,8 +3,6 @@
Full
False
true
-
- true
diff --git a/src/tests/JIT/Directed/Directed_do.csproj b/src/tests/JIT/Directed/Directed_do.csproj
index 02b48ccea5742e..9ef81e4ada860d 100644
--- a/src/tests/JIT/Directed/Directed_do.csproj
+++ b/src/tests/JIT/Directed/Directed_do.csproj
@@ -3,8 +3,6 @@
Full
True
true
-
- true
<_UseOutOfProcessPhysicalPromotionTests Condition="'$(TargetsMobile)' != 'true' or ('$(TargetsBrowser)' == 'true' and '$(RuntimeFlavor)' == 'coreclr')">true
diff --git a/src/tests/JIT/Directed/Directed_r.csproj b/src/tests/JIT/Directed/Directed_r.csproj
index 3fc2ffd54012c9..ad30659efe933c 100644
--- a/src/tests/JIT/Directed/Directed_r.csproj
+++ b/src/tests/JIT/Directed/Directed_r.csproj
@@ -3,8 +3,6 @@
None
False
true
-
- true
diff --git a/src/tests/JIT/Directed/Directed_ro.csproj b/src/tests/JIT/Directed/Directed_ro.csproj
index 662ad48e6a69d7..2a45dcf6270529 100644
--- a/src/tests/JIT/Directed/Directed_ro.csproj
+++ b/src/tests/JIT/Directed/Directed_ro.csproj
@@ -3,8 +3,6 @@
None
True
true
-
- true
diff --git a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_r.csproj b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_r.csproj
index 1885d5ef329133..aed29001c5e8e8 100644
--- a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_r.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_r.csproj
@@ -4,9 +4,6 @@
true
-
- true
-
true
diff --git a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_ro.csproj b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_ro.csproj
index 4aa7b1f941c03a..7e0006c3cec031 100644
--- a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_ro.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_Arm_ro.csproj
@@ -4,9 +4,6 @@
true
-
- true
-
true
diff --git a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_r.csproj
index 7dfd49ab54e66e..f71813ac20c237 100644
--- a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_r.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_r.csproj
@@ -2,8 +2,6 @@
X86_Avx512F_r
true
-
- true
Embedded
diff --git a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_ro.csproj
index 5d4779a30ab13c..93c7658d728a7c 100644
--- a/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_ro.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/X86_Avx512/Avx512F/Avx512F_ro.csproj
@@ -2,8 +2,6 @@
X86_Avx512F_ro
true
-
- true
Embedded
diff --git a/src/tests/JIT/jit64/opt/cse/HugeArray1.csproj b/src/tests/JIT/jit64/opt/cse/HugeArray1.csproj
index 475424b7e8e7d3..1015fcfda8755e 100644
--- a/src/tests/JIT/jit64/opt/cse/HugeArray1.csproj
+++ b/src/tests/JIT/jit64/opt/cse/HugeArray1.csproj
@@ -2,8 +2,6 @@
Full
False
-
- true
true
diff --git a/src/tests/JIT/jit64/opt/cse/HugeField1.csproj b/src/tests/JIT/jit64/opt/cse/HugeField1.csproj
index 5c1f3f2ea0dfc0..58467b877d729c 100644
--- a/src/tests/JIT/jit64/opt/cse/HugeField1.csproj
+++ b/src/tests/JIT/jit64/opt/cse/HugeField1.csproj
@@ -2,8 +2,6 @@
Full
False
-
- true
diff --git a/src/tests/JIT/jit64/opt/cse/HugeField2.csproj b/src/tests/JIT/jit64/opt/cse/HugeField2.csproj
index ce96c76a55ddff..c98953ec035a69 100644
--- a/src/tests/JIT/jit64/opt/cse/HugeField2.csproj
+++ b/src/tests/JIT/jit64/opt/cse/HugeField2.csproj
@@ -5,8 +5,6 @@
Full
False
-
- true
diff --git a/src/tests/JIT/jit64/opt/cse/hugeSimpleExpr1.csproj b/src/tests/JIT/jit64/opt/cse/hugeSimpleExpr1.csproj
index 3d532636c72dbc..dae88dfe2ff587 100644
--- a/src/tests/JIT/jit64/opt/cse/hugeSimpleExpr1.csproj
+++ b/src/tests/JIT/jit64/opt/cse/hugeSimpleExpr1.csproj
@@ -2,8 +2,6 @@
Full
True
-
- true
diff --git a/src/tools/ilasm/README.md b/src/tools/ilasm/README.md
index 203eaecda412cf..1c10671b27bded 100644
--- a/src/tools/ilasm/README.md
+++ b/src/tools/ilasm/README.md
@@ -1,25 +1,112 @@
-# ILAssembler Build Workflow
+# ILAssembler
-This directory contains the ILAssembler tool and its build instructions.
+ILAssembler compiles declarations while ANTLR parses the input. The parser uses
+`UnbufferedTokenStream` with parse-tree construction disabled. Namespace, type, top-level,
+class-member, method-body and shared-directive structure is action-driven. A complete document,
+declaration body or method body is never retained. Rules such as `bytes` stream their content into
+an accumulator instead of building a subtree at all. The generator emits neither listeners nor
+visitors; parser actions own traversal.
-## Build Instructions
+`GrammarActions` is a single `internal sealed partial class` split across
+`src/ILAssembler/Actions/GrammarActions.*.cs`:
-### Regular Builds
-For everyday development and regular builds, simply run:
+| File | Contents |
+| ---- | -------- |
+| `GrammarActions.cs` | Per-document lifecycle. |
+| `GrammarActions.BuildImage.cs` | PE and portable PDB construction. |
+| `GrammarActions.Bytes.cs` | `bytearray` accumulation. |
+| `GrammarActions.Conversions.cs` | Diagnostics and shared state. |
+| `GrammarActions.CustomAttributes.Actions.cs` | Custom attribute descriptors, declarations and blob lists. |
+| `GrammarActions.CustomAttributes.Sequences.cs` | Custom attribute scalar-array sequence synthesis. |
+| `GrammarActions.CustomAttributes.Serialization.cs` | Serialized attribute values and field/parameter initializers. |
+| `GrammarActions.Data.cs` | Streaming mapped-data declarations, labels and reference fixups. |
+| `GrammarActions.Debug.cs` | Direct source-location, document and language directives. |
+| `GrammarActions.Declarations.Actions.cs` | Direct top-level declarations and shared-directive dispatch. |
+| `GrammarActions.Instructions.cs` | Tree-free value instruction and method-item actions. |
+| `GrammarActions.Instructions.References.cs` | Reference and signature instruction actions. |
+| `GrammarActions.Literals.cs` | Literals, names and strings. |
+| `GrammarActions.Manifest.Assembly.cs` | Assembly definitions, identity, keys, security and attributes. |
+| `GrammarActions.Manifest.ExportedTypes.cs` | Exported-type headers, implementations and attributes. |
+| `GrammarActions.Manifest.Files.cs` | Assembly file declarations and entry points. |
+| `GrammarActions.Manifest.References.cs` | Assembly references, identities, keys and hashes. |
+| `GrammarActions.Manifest.Resources.cs` | Embedded and external manifest resources. |
+| `GrammarActions.Manifest.Typedefs.cs` | Type, member and custom-attribute aliases. |
+| `GrammarActions.Manifest.VTable.cs` | Vtable fixup declarations and flags. |
+| `GrammarActions.Marshalling.Actions.cs` | Synthesized native type and marshalling descriptor actions. |
+| `GrammarActions.Members.Class.cs` | Class directives, generic parameter annotations and method overrides. |
+| `GrammarActions.Members.Fields.cs` | Field declarations, attributes, layout, constants, marshalling and RVA data. |
+| `GrammarActions.Members.PropertiesEvents.cs` | Property and event headers, bodies and accessors. |
+| `GrammarActions.MethodHeaders.cs` | Method definition and signature materialization. |
+| `GrammarActions.MethodHeaders.Actions.cs` | Method header, attribute, P/Invoke and generic parser actions. |
+| `GrammarActions.MethodHeaders.Generics.cs` | Generic parameter and constraint synthesis and materialization. |
+| `GrammarActions.MethodBodies.cs` | Label validation and method-name parsing. |
+| `GrammarActions.MethodBodies.Directives.cs` | Direct method-body directives and parameter ownership. |
+| `GrammarActions.MethodBodies.ExceptionHandling.cs` | Lexical scopes and synthesized exception regions. |
+| `GrammarActions.Security.cs` | Synthesized declarative-security values and permission sets. |
+| `GrammarActions.Signatures.cs` | Member and type signature materialization helpers. |
+| `GrammarActions.Signatures.Actions.cs` | Signature grammar actions and typed aggregation helpers. |
+| `GrammarActions.Signatures.References.cs` | Member-reference synthesis and materialization. |
+| `GrammarActions.Signatures.Types.cs` | Type-signature materialization and encoding. |
+| `GrammarActions.Types.cs` | Namespace and type scope ownership and shared type conversion. |
+| `GrammarActions.Types.Headers.cs` | Namespace and type-header materialization. |
+| `GrammarActions.Types.Headers.Actions.cs` | Namespace, type attribute, base and interface parser actions. |
+| `GrammarActions.Types.References.cs` | Type-name synthesis and resolution. |
+
+The hand-written `public partial CILParser` semantic model is split by feature:
+
+| File | Contents |
+| ---- | -------- |
+| `CILParser.SemanticValues.CustomAttributes.cs` | Custom attribute, serialization and initializer values. |
+| `CILParser.SemanticValues.Declarations.cs` | Type/member headers and their context-owned builders. |
+| `CILParser.SemanticValues.Manifest.cs` | Assembly, file, exported-type, resource and typedef values. |
+| `CILParser.SemanticValues.Marshalling.cs` | Native, variant and marshalling values and builders. |
+| `CILParser.SemanticValues.MethodBodies.cs` | Debug, data, security, exception and instruction values. |
+| `CILParser.SemanticValues.Signatures.cs` | Managed types, signatures, names, owners and member references. |
+
+These types are public because ANTLR emits public rule-context return and local fields, but they are
+implementation details and are not a supported API contract.
+
+## Rules for grammar actions
+
+Parser actions in `src/ILAssembler/gen/CIL.g4` must remain thin. They pass concrete child-rule
+values to `GrammarActions`; mechanical assignments and typed builder additions may happen directly
+in the grammar. Compilation orchestration belongs in the `GrammarActions` partial-class files.
+
+`DocumentCompiler` disables parse-tree construction when it creates the parser, and no parser action
+changes that setting. ANTLR generates neither listeners nor visitors. All semantics come from parser
+actions and concrete synthesized values. Rule contexts own their typed builders and pass finalized
+child values to their parents; parser semantic data is never erased to `object`.
+
+All namespace, type-header, top-level, type, signature, reference, marshalling, class-member,
+method-header, method-body directive, exception-handling, data, security, source, language,
+assembly, manifest, vtable and typedef structure is action-driven. `scopeBlock` records offsets
+under its context key without inspecting children. `BuildParseTree` remains disabled throughout.
+
+Rule-local builders are finalized from that rule's `finally` clause when error recovery requires a
+value. Semantic roots capture the initial syntax-error count in a context local instead of a global
+frame stack. The remaining stacks model active compiler nesting: namespaces, types, declaration
+owners and lexical method scopes. Their owning declaration or scope releases them from `finally`
+because ANTLR skips `@after` actions after a syntax error.
+
+Only parser actions process structural rules. There is no parse-tree walker or mode toggling.
+
+## Build
```
./dotnet.sh build src/tools/ilasm/src/ILAssembler
```
-### Updating Generated Files
-If you modify any `.g4` grammar files (rare), you must regenerate the parser and related files:
+On Windows, use `.\dotnet.cmd` instead of `./dotnet.sh`.
+
+## Updating generated parser files
+
+After modifying `CIL.g4`, regenerate the checked-in ANTLR output before building ILAssembler:
```
./dotnet.sh build src/tools/ilasm/src/ILAssembler/gen
+./dotnet.sh build src/tools/ilasm/src/ILAssembler
```
-This will update the generated files before building the main project.
-
----
-
-For more details, see the main repository README or contact the maintainers.
+Do not edit generated `CIL*.cs` or `.interp` files manually.
+Regeneration produces `CILLexer.cs` and `CILParser.cs`; it does not produce visitor or listener
+types.
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.BuildImage.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.BuildImage.cs
new file mode 100644
index 00000000000000..8bbfeadcf808cb
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.BuildImage.cs
@@ -0,0 +1,790 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Text;
+using Antlr4.Runtime;
+using Antlr4.Runtime.Misc;
+
+namespace ILAssembler
+{
+ internal sealed partial class GrammarActions
+ {
+ public (ImmutableArray Diagnostics, CompilationResult? Image) BuildImage()
+ {
+ // Default module name to output filename if no .module directive was provided
+ if (_entityRegistry.Module.Name is null && _options.OutputFileName is not null)
+ {
+ _entityRegistry.Module.Name = _options.OutputFileName;
+ }
+
+ // Apply DebuggableAttribute AFTER all source declarations have been processed,
+ // so that GetCoreLibAssemblyReference() can find the correct corelib assembly ref
+ // declared in the source (e.g., System.Runtime) instead of creating a fallback mscorlib.
+ if (_entityRegistry.Assembly is not null && (_options.Debug || _options.DebugMode is not null))
+ {
+ ApplyDebuggableAttribute();
+ }
+
+ Machine machine = VTableFixupSupport.GetEffectiveMachine(_options.Machine ?? Machine.I386);
+ ImmutableArray validatedVTableFixups =
+ ValidateVTableFixups(machine);
+ ImmutableArray validatedVTableAssociations =
+ ValidateVTableAssociations(validatedVTableFixups);
+ ImmutableArray validatedDataLabelFixups =
+ ValidateDataLabelFixups();
+ ImmutableArray validatedExports =
+ ValidateExports(validatedVTableAssociations, machine);
+
+ // 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
+ // emit the assembly with the errors reported.
+ // In error-tolerant mode, continue despite errors.
+ var structuralErrors = _diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error && !IsRecoverableError(d.Id));
+ if (structuralErrors.Any() && !_options.ErrorTolerant)
+ {
+ return (_diagnostics.ToImmutable(), null);
+ }
+
+ BlobBuilder ilStream = new();
+ Blob mvidFixup = _entityRegistry.WriteContentTo(_metadataBuilder, ilStream, _mappedFieldDataNames, _options.Deterministic);
+ // MetadataRootBuilder only supports module-wide validation suppression, which is
+ // required because wrapped GenericParam numbers intentionally violate table ordering.
+ bool suppressMetadataValidation =
+ _options.ErrorTolerant &&
+ _diagnostics.Any(diagnostic => diagnostic.Id == DiagnosticIds.TooManyGenericParameters);
+ MetadataRootBuilder rootBuilder = new(
+ _metadataBuilder,
+ _options.MetadataVersion,
+ suppressValidation: suppressMetadataValidation);
+
+ // Apply command-line overrides
+ Subsystem subsystem = _options.Subsystem ?? _subsystem;
+ int fileAlignment = _options.FileAlignment ?? _alignment;
+ long imageBase = _options.ImageBase ?? _imageBase;
+ ushort majorSubsystemVersion = _options.SubsystemVersion?.Major ?? 4;
+ ushort minorSubsystemVersion = _options.SubsystemVersion?.Minor ?? 0;
+
+ // Build DllCharacteristics from options
+ DllCharacteristics dllCharacteristics = DllCharacteristics.DynamicBase | DllCharacteristics.NxCompatible | DllCharacteristics.NoSeh | DllCharacteristics.TerminalServerAware;
+ if (_options.AppContainer)
+ {
+ dllCharacteristics |= DllCharacteristics.AppContainer;
+ }
+ if (_options.HighEntropyVA)
+ {
+ dllCharacteristics |= DllCharacteristics.HighEntropyVirtualAddressSpace;
+ }
+ if (_options.StripReloc)
+ {
+ dllCharacteristics &= ~DllCharacteristics.DynamicBase;
+ }
+
+ Characteristics imageCharacteristics = Characteristics.ExecutableImage |
+ (machine is Machine.Amd64 or Machine.IA64 or Machine.Arm64 or Machine.LoongArch64 or Machine.RiscV64
+ ? Characteristics.LargeAddressAware
+ : Characteristics.Bit32Machine);
+ if (_options.Dll)
+ {
+ imageCharacteristics |= Characteristics.Dll;
+ }
+
+ // Compute stack reserve: command-line option overrides directive, which overrides default
+ ulong sizeOfStackReserve = (ulong)(_options.StackReserve ?? (_stackReserve != 0 ? _stackReserve : 0x00100000));
+
+ PEHeaderBuilder header = new(
+ machine: machine,
+ fileAlignment: fileAlignment,
+ imageBase: (ulong)imageBase,
+ subsystem: subsystem,
+ majorSubsystemVersion: majorSubsystemVersion,
+ minorSubsystemVersion: minorSubsystemVersion,
+ dllCharacteristics: dllCharacteristics,
+ imageCharacteristics: imageCharacteristics,
+ sizeOfStackReserve: sizeOfStackReserve);
+
+ MethodDefinitionHandle entryPoint = default;
+ if (_entityRegistry.EntryPoint is not null)
+ {
+ entryPoint = (MethodDefinitionHandle)_entityRegistry.EntryPoint.Handle;
+ }
+
+ // Build debug directory if we have any debug info
+ DebugDirectoryBuilder? debugDirectoryBuilder = BuildDebugDirectory(entryPoint, out _);
+
+ Func, BlobContentId>? deterministicIdProvider = _options.Deterministic
+ ? GetDeterministicContentId
+ : null;
+
+ // Use custom PE builder if we have vtable fixups, exports, or data label reference fixups
+ if (validatedVTableFixups.Length > 0 ||
+ validatedExports.Length > 0 ||
+ validatedDataLabelFixups.Length > 0)
+ {
+ ImmutableArray vtableFixupInfos =
+ BuildVTableFixupInfos(
+ validatedVTableFixups,
+ validatedVTableAssociations);
+ ImmutableArray exports =
+ BuildExportInfos(validatedExports);
+
+ // Apply CorFlags from options or directive
+ CorFlags corFlags = _options.CorFlags ?? _corflags;
+ if (_options.Prefer32Bit)
+ {
+ corFlags |= CorFlags.Prefers32Bit;
+ }
+
+ VTableExportPEBuilder peBuilder = new(
+ header,
+ rootBuilder,
+ ilStream,
+ _mappedFieldData,
+ _manifestResources,
+ debugDirectoryBuilder: debugDirectoryBuilder,
+ entryPoint: entryPoint,
+ flags: corFlags,
+ deterministicIdProvider: deterministicIdProvider,
+ vtableFixups: vtableFixupInfos,
+ exports: exports,
+ dataLabelFixups: validatedDataLabelFixups);
+
+ return (_diagnostics.ToImmutable(), new CompilationResult(peBuilder, mvidFixup));
+ }
+
+ // Apply CorFlags from options or directive
+ CorFlags standardCorFlags = _options.CorFlags ?? _corflags;
+ if (_options.Prefer32Bit)
+ {
+ standardCorFlags |= CorFlags.Prefers32Bit;
+ }
+
+ ManagedPEBuilder standardBuilder = new(
+ header,
+ rootBuilder,
+ ilStream,
+ _mappedFieldData,
+ _manifestResources,
+ flags: standardCorFlags,
+ entryPoint: entryPoint,
+ debugDirectoryBuilder: debugDirectoryBuilder,
+ deterministicIdProvider: deterministicIdProvider);
+
+ return (_diagnostics.ToImmutable(), new CompilationResult(standardBuilder, mvidFixup));
+ }
+
+ private static BlobContentId GetDeterministicContentId(IEnumerable content)
+ {
+ using IncrementalHash hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
+ foreach (Blob blob in content)
+ {
+ hash.AppendData(blob.GetBytes());
+ }
+
+ return BlobContentId.FromHash(hash.GetHashAndReset());
+ }
+
+ private ImmutableArray ValidateVTableFixups(Machine machine)
+ {
+ if (_vtableFixups.Count == 0)
+ {
+ return ImmutableArray.Empty;
+ }
+
+ var builder = ImmutableArray.CreateBuilder(_vtableFixups.Count);
+
+ for (int i = 0; i < _vtableFixups.Count; i++)
+ {
+ VTableFixupDeclaration declaration = _vtableFixups[i];
+ if (!declaration.HasValidSlotCount)
+ {
+ continue;
+ }
+
+ VTableFixupSupport.VTableFixupEntry entry = declaration.Entry;
+ const ushort WidthMask =
+ VTableFixupSupport.COR_VTABLE_32BIT |
+ VTableFixupSupport.COR_VTABLE_64BIT;
+ ushort width = (ushort)(entry.Flags & WidthMask);
+ ushort expectedWidth = VTableFixupSupport.GetPointerSize(machine) == sizeof(long)
+ ? VTableFixupSupport.COR_VTABLE_64BIT
+ : VTableFixupSupport.COR_VTABLE_32BIT;
+ if (width != expectedWidth)
+ {
+ ReportError(
+ DiagnosticIds.InvalidVTableWidth,
+ string.Format(
+ DiagnosticMessageTemplates.InvalidVTableWidth,
+ width,
+ machine,
+ expectedWidth == VTableFixupSupport.COR_VTABLE_64BIT
+ ? "int64"
+ : "int32"),
+ declaration.Context);
+ continue;
+ }
+
+ if (!_mappedFieldDataNames.TryGetValue(entry.DataLabel, out int dataOffset))
+ {
+ ReportError(
+ DiagnosticIds.LabelNotFound,
+ string.Format(DiagnosticMessageTemplates.LabelNotFound, entry.DataLabel),
+ declaration.Context);
+ continue;
+ }
+
+ int availableBytes = GetAvailableMappedFieldDataBytes(dataOffset);
+ int requiredBytes = checked(
+ entry.SlotCount * VTableFixupSupport.GetSlotSize(entry.Flags));
+ if (requiredBytes > availableBytes)
+ {
+ ReportError(
+ DiagnosticIds.InsufficientVTableData,
+ string.Format(
+ DiagnosticMessageTemplates.InsufficientVTableData,
+ entry.DataLabel,
+ availableBytes,
+ requiredBytes),
+ declaration.Context);
+ continue;
+ }
+
+ builder.Add(new ValidatedVTableFixup(i + 1, entry, dataOffset));
+ }
+
+ return builder.ToImmutable();
+ }
+
+ private int GetAvailableMappedFieldDataBytes(int dataOffset)
+ {
+ int endOffset = _mappedFieldData.Count;
+ foreach (int otherOffset in _mappedFieldDataNames.Values)
+ {
+ if (otherOffset > dataOffset && otherOffset < endOffset)
+ {
+ endOffset = otherOffset;
+ }
+ }
+
+ return endOffset - dataOffset;
+ }
+
+ private ImmutableArray ValidateDataLabelFixups()
+ {
+ var builder =
+ ImmutableArray.CreateBuilder(
+ _mappedFieldDataReferenceFixups.Count);
+
+ foreach (CILParser.DataLabelReferenceValue reference in _mappedFieldDataReferenceFixups)
+ {
+ if (!_mappedFieldDataNames.TryGetValue(reference.TargetLabel, out int targetOffset))
+ {
+ ReportError(
+ DiagnosticIds.LabelNotFound,
+ string.Format(
+ DiagnosticMessageTemplates.LabelNotFound,
+ reference.TargetLabel),
+ reference.Location);
+ continue;
+ }
+
+ builder.Add(new VTableExportPEBuilder.DataLabelFixup(
+ reference.DataOffset,
+ targetOffset,
+ reference.PointerSize));
+ }
+
+ return builder.ToImmutable();
+ }
+
+ private ImmutableArray ValidateVTableAssociations(
+ ImmutableArray validatedVTableFixups)
+ {
+ var fixupsByOriginalIndex = new Dictionary(
+ validatedVTableFixups.Length);
+ for (int i = 0; i < validatedVTableFixups.Length; i++)
+ {
+ fixupsByOriginalIndex.Add(
+ validatedVTableFixups[i].OriginalIndex,
+ (i + 1, validatedVTableFixups[i]));
+ }
+
+ var builder = ImmutableArray.CreateBuilder();
+ foreach (EntityRegistry.MethodDefinitionEntity method in GetParsedMethods())
+ {
+ if (!_vtableEntryDirectiveContexts.TryGetValue(method, out ParserRuleContext? context))
+ {
+ continue;
+ }
+
+ if (!fixupsByOriginalIndex.TryGetValue(
+ method.VTableEntry,
+ out (int SerializedIndex, ValidatedVTableFixup Fixup) fixup))
+ {
+ ReportError(
+ DiagnosticIds.InvalidVTableEntry,
+ string.Format(
+ DiagnosticMessageTemplates.InvalidVTableEntry,
+ method.Name,
+ method.VTableEntry),
+ context);
+ continue;
+ }
+
+ if (method.VTableSlot <= 0 || method.VTableSlot > fixup.Fixup.Entry.SlotCount)
+ {
+ ReportError(
+ DiagnosticIds.InvalidVTableEntry,
+ string.Format(
+ DiagnosticMessageTemplates.InvalidVTableSlot,
+ method.Name,
+ method.VTableSlot,
+ method.VTableEntry,
+ fixup.Fixup.Entry.SlotCount),
+ context);
+ continue;
+ }
+
+ builder.Add(new ValidatedVTableAssociation(method, fixup.SerializedIndex));
+ }
+
+ return builder.ToImmutable();
+ }
+
+ private ImmutableArray ValidateExports(
+ ImmutableArray validatedVTableAssociations,
+ Machine machine)
+ {
+ if (_vtableFixups.Count == 0)
+ {
+ return ImmutableArray.Empty;
+ }
+
+ var associationsByMethod =
+ new Dictionary(
+ validatedVTableAssociations.Length);
+ foreach (ValidatedVTableAssociation association in validatedVTableAssociations)
+ {
+ associationsByMethod.Add(association.Method, association);
+ }
+
+ var candidates = ImmutableArray.CreateBuilder();
+ foreach (EntityRegistry.MethodDefinitionEntity method in GetParsedMethods())
+ {
+ if (method.ExportOrdinal < 0)
+ {
+ continue;
+ }
+
+ ParserRuleContext context = _exportDirectiveContexts[method];
+ string exportName = method.ExportAlias ?? method.Name;
+ if (!VTableExportPEBuilder.IsExportMachineSupported(machine))
+ {
+ ReportError(
+ DiagnosticIds.UnsupportedNativeExportMachine,
+ string.Format(
+ DiagnosticMessageTemplates.UnsupportedNativeExportMachine,
+ machine),
+ context);
+ continue;
+ }
+
+ if (!associationsByMethod.TryGetValue(
+ method,
+ out ValidatedVTableAssociation association))
+ {
+ if (!_vtableEntryDirectiveContexts.ContainsKey(method))
+ {
+ ReportError(
+ DiagnosticIds.InvalidVTableExport,
+ string.Format(
+ DiagnosticMessageTemplates.InvalidVTableExport,
+ exportName),
+ context);
+ }
+
+ continue;
+ }
+
+ candidates.Add(new ValidatedExport(method, association.VTableEntryIndex));
+ }
+
+ if (candidates.Count == 0)
+ {
+ return ImmutableArray.Empty;
+ }
+
+ var exportsByOrdinal = new Dictionary();
+ var nonConflictingExports = ImmutableArray.CreateBuilder(candidates.Count);
+ foreach (ValidatedExport export in candidates)
+ {
+ if (exportsByOrdinal.TryGetValue(
+ export.Method.ExportOrdinal,
+ out ValidatedExport existingExport) &&
+ (existingExport.VTableEntryIndex != export.VTableEntryIndex ||
+ existingExport.Method.VTableSlot != export.Method.VTableSlot))
+ {
+ ReportError(
+ DiagnosticIds.DuplicateExportOrdinal,
+ string.Format(
+ DiagnosticMessageTemplates.DuplicateExportOrdinal,
+ export.Method.ExportAlias ?? export.Method.Name,
+ export.Method.ExportOrdinal),
+ _exportDirectiveContexts[export.Method]);
+ continue;
+ }
+
+ exportsByOrdinal.TryAdd(export.Method.ExportOrdinal, export);
+ nonConflictingExports.Add(export);
+ }
+
+ int baseOrdinal = nonConflictingExports.Min(export => export.Method.ExportOrdinal);
+ var validatedExports =
+ ImmutableArray.CreateBuilder(nonConflictingExports.Count);
+ foreach (ValidatedExport export in nonConflictingExports)
+ {
+ long ordinalIndex = (long)export.Method.ExportOrdinal - baseOrdinal;
+ if (ordinalIndex > ushort.MaxValue)
+ {
+ ReportError(
+ DiagnosticIds.ExportOrdinalRangeTooLarge,
+ string.Format(
+ DiagnosticMessageTemplates.ExportOrdinalRangeTooLarge,
+ export.Method.ExportOrdinal,
+ baseOrdinal,
+ ushort.MaxValue),
+ _exportDirectiveContexts[export.Method]);
+ continue;
+ }
+
+ validatedExports.Add(export);
+ }
+
+ return validatedExports.ToImmutable();
+ }
+
+ private static ImmutableArray BuildVTableFixupInfos(
+ ImmutableArray validatedVTableFixups,
+ ImmutableArray validatedVTableAssociations)
+ {
+ var builder =
+ ImmutableArray.CreateBuilder(
+ validatedVTableFixups.Length);
+
+ for (int i = 0; i < validatedVTableFixups.Length; i++)
+ {
+ ValidatedVTableFixup fixup = validatedVTableFixups[i];
+ var methodTokens = ImmutableArray.CreateBuilder(fixup.Entry.SlotCount);
+ methodTokens.Count = fixup.Entry.SlotCount;
+
+ foreach (ValidatedVTableAssociation association in validatedVTableAssociations)
+ {
+ if (association.VTableEntryIndex == i + 1)
+ {
+ methodTokens[association.Method.VTableSlot - 1] =
+ MetadataTokens.GetToken(association.Method.Handle);
+ }
+ }
+
+ builder.Add(new VTableExportPEBuilder.VTableFixupInfo(
+ fixup.DataOffset,
+ fixup.Entry.SlotCount,
+ fixup.Entry.Flags,
+ methodTokens.MoveToImmutable()));
+ }
+
+ return builder.MoveToImmutable();
+ }
+
+ private static ImmutableArray BuildExportInfos(
+ ImmutableArray validatedExports)
+ {
+ var builder =
+ ImmutableArray.CreateBuilder(
+ validatedExports.Length);
+
+ foreach (ValidatedExport export in validatedExports)
+ {
+ builder.Add(new VTableExportPEBuilder.ExportInfo(
+ export.Method.ExportOrdinal,
+ export.Method.ExportAlias ?? export.Method.Name,
+ export.VTableEntryIndex,
+ export.Method.VTableSlot));
+ }
+
+ return builder.MoveToImmutable();
+ }
+
+ private IEnumerable GetParsedMethods()
+ {
+ foreach (EntityRegistry.TypeDefinitionEntity type in _entityRegistry.GetSeenEntities(TableIndex.TypeDef))
+ {
+ foreach (EntityRegistry.MethodDefinitionEntity method in type.Methods)
+ {
+ yield return method;
+ }
+ }
+ }
+
+ private DebugDirectoryBuilder? BuildDebugDirectory(MethodDefinitionHandle entryPoint, out int debugDataSize)
+ {
+ debugDataSize = 0;
+
+ // Check if we have any methods with debug info
+ bool hasDebugInfo = false;
+ foreach (var entity in _entityRegistry.GetSeenEntities(TableIndex.MethodDef))
+ {
+ if (entity is EntityRegistry.MethodDefinitionEntity method &&
+ method.DebugInfo.SequencePoints.Count > 0)
+ {
+ hasDebugInfo = true;
+ break;
+ }
+ }
+
+ // Generate PDB if we have debug info OR if --debug/--pdb options are set
+ bool generatePdb = hasDebugInfo || _options.Debug || _options.Pdb;
+ if (!generatePdb)
+ {
+ return null;
+ }
+
+ // Build PDB metadata
+ BuildPdbMetadata();
+
+ // Get row counts from main metadata for the portable PDB
+ var typeSystemRowCounts = _metadataBuilder.GetRowCounts();
+
+ Func, BlobContentId> pdbIdProvider = _options.Deterministic
+ ? GetDeterministicContentId
+ : _ => new BlobContentId(Guid.NewGuid(), 0x04030201);
+
+ // Create the portable PDB
+ var pdbBuilder = new PortablePdbBuilder(
+ _pdbBuilder,
+ typeSystemRowCounts,
+ entryPoint,
+ idProvider: pdbIdProvider);
+
+ var pdbBlob = new BlobBuilder();
+ var pdbContentId = pdbBuilder.Serialize(pdbBlob);
+
+ // Create debug directory with embedded PDB
+ var debugDirectoryBuilder = new DebugDirectoryBuilder();
+ debugDirectoryBuilder.AddCodeViewEntry(
+ $"assembly.pdb",
+ pdbContentId,
+ pdbBuilder.FormatVersion);
+ debugDirectoryBuilder.AddEmbeddedPortablePdbEntry(pdbBlob, pdbBuilder.FormatVersion);
+
+ // Calculate debug data size:
+ // 2 debug directory entries (28 bytes each) + CodeView data (~24 bytes) + Embedded PDB data (compressed pdbBlob + 8 header)
+ // CodeView entry: signature (4) + guid (16) + age (4) + path (variable, ~12 for "assembly.pdb\0")
+ const int debugDirEntrySize = 28;
+ int codeViewDataSize = 4 + 16 + 4 + "assembly.pdb".Length + 1; // signature + guid + age + path + null
+ int embeddedPdbHeaderSize = 8; // MPDB signature (4) + uncompressed size (4)
+ // The embedded PDB is compressed, estimate conservatively as same size
+ int embeddedPdbDataSize = embeddedPdbHeaderSize + pdbBlob.Count;
+
+ debugDataSize = (2 * debugDirEntrySize) + codeViewDataSize + embeddedPdbDataSize;
+
+ return debugDirectoryBuilder;
+ }
+
+ private void BuildPdbMetadata()
+ {
+ // Add documents and sequence points to the PDB metadata builder
+ foreach (var entity in _entityRegistry.GetSeenEntities(TableIndex.MethodDef))
+ {
+ if (entity is not EntityRegistry.MethodDefinitionEntity method)
+ {
+ continue;
+ }
+
+ var debugInfo = method.DebugInfo;
+ if (debugInfo.SequencePoints.Count == 0)
+ {
+ // Add empty debug info entry for methods without sequence points
+ _pdbBuilder.AddMethodDebugInformation(default, default);
+ continue;
+ }
+
+ // Get or create document handle
+ DocumentHandle documentHandle = default;
+ if (debugInfo.DocumentPath is not null)
+ {
+ (string Path, Guid LanguageGuid) documentKey =
+ (debugInfo.DocumentPath, debugInfo.LanguageGuid);
+ if (!_documentHandles.TryGetValue(documentKey, out documentHandle))
+ {
+ var nameHandle = _pdbBuilder.GetOrAddDocumentName(debugInfo.DocumentPath);
+ var languageGuidHandle = debugInfo.LanguageGuid != Guid.Empty
+ ? _pdbBuilder.GetOrAddGuid(debugInfo.LanguageGuid)
+ : default;
+ documentHandle = _pdbBuilder.AddDocument(
+ nameHandle,
+ default, // hash algorithm
+ default, // hash
+ languageGuidHandle);
+ _documentHandles[documentKey] = documentHandle;
+ }
+ }
+
+ // Encode sequence points
+ var sequencePointsBlob = EncodeSequencePoints(debugInfo.SequencePoints);
+ var sequencePointsBlobHandle = _pdbBuilder.GetOrAddBlob(sequencePointsBlob);
+
+ _pdbBuilder.AddMethodDebugInformation(documentHandle, sequencePointsBlobHandle);
+ }
+ }
+
+ private static BlobBuilder EncodeSequencePoints(List sequencePoints)
+ {
+ var builder = new BlobBuilder();
+
+ if (sequencePoints.Count == 0)
+ {
+ return builder;
+ }
+
+ // LocalSignature (not used here, write 0)
+ builder.WriteCompressedInteger(0);
+
+ int previousOffset = 0;
+ int previousStartLine = -1;
+ int previousStartColumn = -1;
+
+ foreach (var sp in sequencePoints)
+ {
+ // IL offset delta
+ int offsetDelta = sp.ILOffset - previousOffset;
+ builder.WriteCompressedInteger(offsetDelta);
+ previousOffset = sp.ILOffset;
+
+ if (sp.IsHidden)
+ {
+ // Hidden sequence point: delta lines = 0, delta columns = 0
+ builder.WriteCompressedInteger(0);
+ builder.WriteCompressedInteger(0);
+ }
+ else
+ {
+ // Delta lines
+ int deltaLines = sp.EndLine - sp.StartLine;
+ builder.WriteCompressedInteger(deltaLines);
+
+ // Delta columns
+ int deltaColumns = sp.EndColumn - sp.StartColumn;
+ if (deltaLines == 0)
+ {
+ builder.WriteCompressedInteger(deltaColumns);
+ }
+ else
+ {
+ builder.WriteCompressedSignedInteger(deltaColumns);
+ }
+
+ // Start line delta (signed)
+ if (previousStartLine < 0)
+ {
+ builder.WriteCompressedInteger(sp.StartLine);
+ }
+ else
+ {
+ builder.WriteCompressedSignedInteger(sp.StartLine - previousStartLine);
+ }
+
+ // Start column delta (signed)
+ if (previousStartColumn < 0)
+ {
+ builder.WriteCompressedInteger(sp.StartColumn);
+ }
+ else
+ {
+ builder.WriteCompressedSignedInteger(sp.StartColumn - previousStartColumn);
+ }
+
+ previousStartLine = sp.StartLine;
+ previousStartColumn = sp.StartColumn;
+ }
+ }
+
+ return builder;
+ }
+
+ ///
+ /// Add DebuggableAttribute to the assembly based on debug options.
+ /// - /DEBUG: 0x101 = Default | DisableOptimizations
+ /// - /DEBUG=OPT: 0x03 = Default | IgnoreSymbolStoreSequencePoints
+ /// - /DEBUG=IMPL: 0x103 = Default | DisableOptimizations | EnableEditAndContinue
+ ///
+ private void ApplyDebuggableAttribute()
+ {
+ if (_entityRegistry.Assembly is null)
+ {
+ return;
+ }
+
+ // DebuggingModes enum values from System.Diagnostics.DebuggableAttribute:
+ // None = 0x00, Default = 0x01, IgnoreSymbolStoreSequencePoints = 0x02,
+ // EnableEditAndContinue = 0x04, DisableOptimizations = 0x100
+ const int DebuggingModesDefault = 0x101; // Default | DisableOptimizations
+ const int DebuggingModesOpt = 0x03; // Default | IgnoreSymbolStoreSequencePoints
+ const int DebuggingModesImpl = 0x103; // Default | DisableOptimizations | EnableEditAndContinue
+
+ int debuggingModes = _options.DebugMode switch
+ {
+ DebugMode.Opt => DebuggingModesOpt,
+ DebugMode.Impl => DebuggingModesImpl,
+ _ => DebuggingModesDefault
+ };
+
+ // Get reference to core library
+ var coreAsmRef = _entityRegistry.GetCoreLibAssemblyReference();
+
+ // Create reference to System.Diagnostics.DebuggableAttribute
+ var debuggableAttrType = _entityRegistry.GetOrCreateTypeReference(
+ coreAsmRef,
+ new TypeName(null, "System.Diagnostics.DebuggableAttribute"));
+
+ // Create reference to nested type DebuggingModes
+ var debuggingModesType = _entityRegistry.GetOrCreateTypeReference(
+ debuggableAttrType,
+ new TypeName(null, "DebuggingModes"));
+
+ // Create constructor signature: .ctor(DebuggingModes)
+ BlobBuilder ctorSig = new();
+ var sigEncoder = new BlobEncoder(ctorSig);
+ sigEncoder.MethodSignature(SignatureCallingConvention.Default, 0, isInstanceMethod: true)
+ .Parameters(1,
+ returnType => returnType.Void(),
+ parameters => parameters.AddParameter().Type().Type(debuggingModesType.Handle, isValueType: true));
+
+ var ctor = _entityRegistry.CreateLazilyRecordedMemberReference(debuggableAttrType, ".ctor", ctorSig);
+
+ // Create custom attribute blob: prolog (0x0001) + int32 value + named args count (0x0000)
+ BlobBuilder attrValue = new();
+ attrValue.WriteUInt16(0x0001); // Prolog
+ attrValue.WriteInt32(debuggingModes); // DebuggingModes value
+ attrValue.WriteUInt16(0x0000); // No named arguments
+
+ // Create and attach the custom attribute
+ var customAttr = _entityRegistry.CreateCustomAttribute(ctor, attrValue);
+ customAttr.Owner = _entityRegistry.Assembly;
+ }
+
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Bytes.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Bytes.cs
new file mode 100644
index 00000000000000..50ea76d4ff2830
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Bytes.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Immutable;
+using System.Globalization;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal ImmutableArray.Builder CreateByteAccumulator()
+ => ImmutableArray.CreateBuilder();
+
+ internal void AddByte(ImmutableArray.Builder accumulator, byte value)
+ => accumulator.Add(value);
+
+ internal ImmutableArray EndBytes(ImmutableArray.Builder accumulator)
+ => accumulator.DrainToImmutable();
+#pragma warning restore CA1822
+
+ ///
+ /// Parses a single hexbyte token.
+ ///
+ internal static byte ParseHexbyte(IToken token)
+ {
+ // hexbyte can be HEXBYTE, INT32, or ID token (due to lexer ambiguity).
+ ReadOnlySpan text = token.Text.AsSpan();
+ bool isNegative = text.StartsWith("-");
+ if (isNegative)
+ {
+ text = text.Slice(1);
+ }
+
+ if (text.StartsWith("0x"))
+ {
+ text = text.Slice(2);
+ }
+
+ if (!uint.TryParse(
+ text,
+ NumberStyles.AllowHexSpecifier,
+ CultureInfo.InvariantCulture,
+ out uint value))
+ {
+ return 0;
+ }
+
+ if (isNegative)
+ {
+ value = unchecked(0u - value);
+ }
+
+ return (byte)(value & byte.MaxValue);
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Conversions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Conversions.cs
new file mode 100644
index 00000000000000..aa20dedaf531ea
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Conversions.cs
@@ -0,0 +1,229 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Text;
+using Antlr4.Runtime;
+using Antlr4.Runtime.Misc;
+
+namespace ILAssembler
+{
+ internal sealed partial class GrammarActions
+ {
+ private const int MaximumGenericParameterCount = ushort.MaxValue + 1;
+ private readonly ImmutableArray.Builder _diagnostics = ImmutableArray.CreateBuilder();
+ private readonly EntityRegistry _entityRegistry = new();
+ private readonly IReadOnlyDictionary _documents;
+ private readonly Options _options;
+ private readonly MetadataBuilder _metadataBuilder = new();
+ private readonly Func _resourceLocator;
+
+ // Record the mapped field data directly into the blob to ensure we preserve ordering
+ private readonly BlobBuilder _mappedFieldData = new();
+ private readonly Dictionary _mappedFieldDataNames = new();
+ private readonly List _mappedFieldDataReferenceFixups = new();
+ private readonly BlobBuilder _manifestResources = new();
+ private int _syntaxErrorCount;
+
+ // Debug info tracking
+ private Guid _currentLanguageGuid = Guid.Empty;
+ private Guid _currentLanguageVendorGuid = Guid.Empty;
+ private Guid _currentDocumentTypeGuid = Guid.Empty;
+ private string? _currentDocumentPath;
+ private readonly Dictionary<(string Path, Guid LanguageGuid), DocumentHandle> _documentHandles = new();
+ private readonly MetadataBuilder _pdbBuilder = new();
+ private readonly List _vtableFixups = new();
+ private readonly Dictionary _exportDirectiveContexts = new();
+ private readonly Dictionary _vtableEntryDirectiveContexts = new();
+
+ internal GrammarActions(IReadOnlyDictionary documents, Options options, Func resourceLocator)
+ {
+ _documents = documents;
+ _options = options;
+ _resourceLocator = resourceLocator;
+ }
+
+ private sealed record VTableFixupDeclaration(
+ VTableFixupSupport.VTableFixupEntry Entry,
+ CILParser.VtfixupDeclContext Context,
+ bool HasValidSlotCount);
+
+ private readonly record struct ValidatedVTableFixup(
+ int OriginalIndex,
+ VTableFixupSupport.VTableFixupEntry Entry,
+ int DataOffset);
+
+ private readonly record struct ValidatedVTableAssociation(
+ EntityRegistry.MethodDefinitionEntity Method,
+ int VTableEntryIndex);
+
+ private readonly record struct ValidatedExport(
+ EntityRegistry.MethodDefinitionEntity Method,
+ int VTableEntryIndex);
+
+ private void ReportDiagnostic(DiagnosticSeverity severity, string id, string message, Antlr4.Runtime.ParserRuleContext context)
+ {
+ var location = Location.From(context.Start, _documents);
+ _diagnostics.Add(new Diagnostic(id, severity, message, location));
+ }
+
+ private void ReportError(string id, string message, Antlr4.Runtime.ParserRuleContext context)
+ => ReportDiagnostic(DiagnosticSeverity.Error, id, message, context);
+
+ private void ReportGenericParameterEncodingErrors(
+ ImmutableArray genericParameters,
+ ParserRuleContext context)
+ {
+ if (genericParameters.Length <= MaximumGenericParameterCount)
+ {
+ return;
+ }
+
+ ReportError(
+ DiagnosticIds.TooManyGenericParameters,
+ string.Format(
+ DiagnosticMessageTemplates.TooManyGenericParameters,
+ genericParameters.Length,
+ MaximumGenericParameterCount),
+ genericParameters[MaximumGenericParameterCount].Location ?? context.Start);
+
+ for (int i = MaximumGenericParameterCount; i < genericParameters.Length; i++)
+ {
+ GenericParameterDeclarationValue parameter = genericParameters[i];
+ if (parameter.Constraints.IsEmpty)
+ {
+ continue;
+ }
+
+ ReportError(
+ DiagnosticIds.GenericParameterConstraintOwnerOutOfRange,
+ string.Format(
+ DiagnosticMessageTemplates.GenericParameterConstraintOwnerOutOfRange,
+ i,
+ ushort.MaxValue),
+ parameter.ConstraintLocation ?? parameter.Location ?? context.Start);
+ }
+ }
+
+ private void ReportError(string id, string message, IToken token)
+ {
+ _diagnostics.Add(new Diagnostic(
+ id,
+ DiagnosticSeverity.Error,
+ message,
+ Location.From(token, _documents)));
+ }
+
+ private void ReportWarning(string id, string message, Antlr4.Runtime.ParserRuleContext context)
+ => ReportDiagnostic(DiagnosticSeverity.Warning, id, message, context);
+
+ private void ReportWarning(string id, string message, IToken token)
+ {
+ _diagnostics.Add(new Diagnostic(
+ id,
+ DiagnosticSeverity.Warning,
+ message,
+ Location.From(token, _documents)));
+ }
+
+ internal void RecordSyntaxError() => _syntaxErrorCount++;
+
+ internal int SyntaxErrorCount => _syntaxErrorCount;
+
+ internal bool HasSyntaxErrorsSince(int initialSyntaxErrorCount)
+ => initialSyntaxErrorCount != _syntaxErrorCount;
+
+ private static T ApplyAttribute(
+ T current,
+ CILParser.AttributeValue attribute)
+ where T : struct, Enum
+ {
+ if (!attribute.ShouldAppend)
+ {
+ return attribute.Value;
+ }
+
+ int currentValue = Convert.ToInt32(current);
+ int groupMask = Convert.ToInt32(attribute.GroupMask);
+ int attributeValue = Convert.ToInt32(attribute.Value);
+ return (T)Enum.ToObject(
+ typeof(T),
+ (currentValue & ~groupMask) | attributeValue);
+ }
+
+ private static bool IsRecoverableError(string diagnosticId)
+ {
+ // Method body and signature diagnostics are recoverable - we emit the assembly but report the error.
+ // This matches native ilasm behavior where errors during method/field emission don't prevent
+ // the assembly from being written when the /ERR (OnErrGo) flag is set.
+ return diagnosticId is DiagnosticIds.ByteArrayTooShort
+ or DiagnosticIds.ArgumentNotFound
+ or DiagnosticIds.LocalNotFound
+ or DiagnosticIds.LabelNotFound
+ or DiagnosticIds.GenericParameterIndexOutOfRange
+ or DiagnosticIds.ParameterIndexOutOfRange
+ or DiagnosticIds.GenericParameterNotFound
+ or DiagnosticIds.UnknownGenericParameter
+ or DiagnosticIds.MissingInstanceCallConv;
+ }
+
+ private sealed class CurrentMethodContext
+ {
+ public CurrentMethodContext(EntityRegistry.MethodDefinitionEntity definition)
+ {
+ Definition = definition;
+ // Populate argument names from the method's parameter definitions
+ foreach (var param in definition.Parameters)
+ {
+ if (param.Name is not null && param.Sequence > 0)
+ {
+ ArgumentNames[param.Name] = param.Sequence - 1;
+ }
+ }
+ }
+
+ public EntityRegistry.MethodDefinitionEntity Definition { get; }
+
+ public Dictionary Labels { get; } = new();
+
+ public Dictionary UndefinedLabelReferences { get; } = new();
+
+ public Dictionary ArgumentNames { get; } = new();
+
+ public List> LocalsScopes { get; } = new();
+
+ public List AllLocals { get; } = new();
+ }
+
+ private CurrentMethodContext? _currentMethod;
+ private EntityRegistry.FieldDefinitionEntity? _lastFieldDefinition;
+ private EntityRegistry.EntityBase? _pendingClassCustomAttributeOwner;
+
+ private const ushort CustomAttributeBlobFormatVersion = 1;
+
+ // These stacks are the active nested compiler scopes, not parser-value accumulators.
+ private readonly Stack _currentNamespace = new();
+ private readonly Stack _currentTypeDefinition = new();
+
+ private bool _expectInstance;
+ private Subsystem _subsystem = Subsystem.WindowsCui;
+ private CorFlags _corflags = CorFlags.ILOnly;
+ private int _alignment = 0x200;
+ private long _imageBase = 0x00400000;
+ private long _stackReserve;
+
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Actions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Actions.cs
new file mode 100644
index 00000000000000..f6263647ff62d0
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Actions.cs
@@ -0,0 +1,226 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal MethodReferenceValue CreateCustomAttributeType(MethodReferenceValue constructor)
+ => constructor;
+
+ internal CustomAttributeDescriptorValue CreateDefaultCustomAttribute(
+ MethodReferenceValue constructor)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ new RawCustomAttributeBlobValue(CreateDefaultCustomAttributeBlob()),
+ null);
+
+ internal CustomAttributeDescriptorValue CreateStringCustomAttribute(
+ MethodReferenceValue constructor,
+ string value)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ new RawCustomAttributeBlobValue(CreateStringBlob(value)),
+ null);
+
+ internal CustomAttributeDescriptorValue CreateStructuredCustomAttribute(
+ MethodReferenceValue constructor,
+ CustomAttributeBlobValue value)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ value,
+ null);
+
+ internal CustomAttributeDescriptorValue CreateRawCustomAttribute(
+ MethodReferenceValue constructor,
+ ImmutableArray value)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ new RawCustomAttributeBlobValue(CreateRawBlob(value)),
+ null);
+
+ internal CustomAttributeDescriptorValue CreateDefaultOwnedCustomAttribute(
+ OwnerTypeValue owner,
+ MethodReferenceValue constructor)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ new RawCustomAttributeBlobValue(CreateDefaultCustomAttributeBlob()),
+ owner);
+
+ internal CustomAttributeDescriptorValue CreateStringOwnedCustomAttribute(
+ OwnerTypeValue owner,
+ MethodReferenceValue constructor,
+ string value)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ new RawCustomAttributeBlobValue(CreateStringBlob(value)),
+ owner);
+
+ internal CustomAttributeDescriptorValue CreateStructuredOwnedCustomAttribute(
+ OwnerTypeValue owner,
+ MethodReferenceValue constructor,
+ CustomAttributeBlobValue value)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ value,
+ owner);
+
+ internal CustomAttributeDescriptorValue CreateRawOwnedCustomAttribute(
+ OwnerTypeValue owner,
+ MethodReferenceValue constructor,
+ ImmutableArray value)
+ => CreateCustomAttributeDescriptor(
+ constructor,
+ new RawCustomAttributeBlobValue(CreateRawBlob(value)),
+ owner);
+
+ internal CustomAttributeDeclarationValue CreateCustomAttributeDeclaration(
+ CustomAttributeDescriptorValue value)
+ => value;
+
+ internal CustomAttributeDeclarationValue CreateCustomAttributeTypedef(string alias)
+ => new CustomAttributeTypedefValue(alias);
+
+ private static CustomAttributeDescriptorValue CreateCustomAttributeDescriptor(
+ MethodReferenceValue constructor,
+ CustomAttributeBlobValue value,
+ OwnerTypeValue? owner)
+ => new(constructor, value, owner);
+
+ private static BlobBuilder CreateStringBlob(string value)
+ {
+ BlobBuilder blob = new();
+ blob.WriteUTF8(value);
+ return blob;
+ }
+
+ private static BlobBuilder CreateRawBlob(ImmutableArray value)
+ {
+ BlobBuilder blob = new(value.Length);
+ blob.WriteBytes(value);
+ return blob;
+ }
+
+ private static BlobBuilder CreateDefaultCustomAttributeBlob()
+ {
+ BlobBuilder value = new();
+ value.WriteUInt16(CustomAttributeBlobFormatVersion);
+ value.WriteUInt16(0);
+ return value;
+ }
+
+ internal CustomAttributeBlobValue CreateCustomAttributeBlob(
+ ImmutableArray arguments,
+ ImmutableArray namedArguments)
+ => new StructuredCustomAttributeBlobValue(arguments, namedArguments);
+
+ internal CustomAttributeNamedArgumentValue CreateCustomBlobNamedArgument(
+ byte kind,
+ SerializationTypeValue type,
+ string name,
+ SerializedInitializerValue value)
+ => new(kind, type, name, value);
+
+ private BlobBuilder MaterializeCustomAttributeBlob(CustomAttributeBlobValue value)
+ {
+ if (value is RawCustomAttributeBlobValue raw)
+ {
+ return raw.Value;
+ }
+
+ if (value is not StructuredCustomAttributeBlobValue structured)
+ {
+ return new BlobBuilder();
+ }
+
+ BlobBuilder result = new();
+ result.WriteUInt16(CustomAttributeBlobFormatVersion);
+ foreach (SerializedInitializerValue argument in structured.Arguments)
+ {
+ MaterializeSerializedInitializer(argument).WriteContentTo(result);
+ }
+
+ WriteCustomBlobNamedArguments(result, structured.NamedArguments);
+ return result;
+ }
+
+ private void WriteCustomBlobNamedArguments(
+ BlobBuilder result,
+ ImmutableArray namedArguments)
+ {
+ result.WriteInt16((short)namedArguments.Length);
+ foreach (CustomAttributeNamedArgumentValue argument in namedArguments)
+ {
+ result.WriteByte(argument.Kind);
+ MaterializeSerializationType(argument.Type).WriteContentTo(result);
+ result.WriteSerializedString(argument.Name);
+ MaterializeSerializedInitializer(argument.Value).WriteContentTo(result);
+ }
+ }
+
+ private EntityRegistry.CustomAttributeEntity MaterializeCustomAttribute(
+ CustomAttributeDescriptorValue descriptor)
+ {
+ EntityRegistry.EntityBase constructor = MaterializeMethodReference(descriptor.Constructor);
+ BlobBuilder value = MaterializeCustomAttributeBlob(descriptor.Value);
+ EntityRegistry.CustomAttributeEntity attribute =
+ _entityRegistry.CreateCustomAttribute(constructor, value);
+ if (descriptor.Owner is { } owner)
+ {
+ attribute.Owner = MaterializeOwnerType(owner);
+ }
+
+ return attribute;
+ }
+
+ private EntityRegistry.CustomAttributeEntity? MaterializeCustomAttributeDeclaration(
+ CustomAttributeDeclarationValue? value,
+ IToken location)
+ {
+ if (value is CustomAttributeTypedefValue typedef)
+ {
+ if (TryResolveTypedefAsCustomAttribute(typedef.Alias) is not { } resolved)
+ {
+ ReportError(
+ DiagnosticIds.TypedefNotFound,
+ string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
+ location);
+ return null;
+ }
+
+ EntityRegistry.CustomAttributeEntity typedefAttribute =
+ _entityRegistry.CreateCustomAttribute(resolved.Constructor, resolved.Value);
+ typedefAttribute.Owner = resolved.Owner;
+ return typedefAttribute;
+ }
+
+ if (value is not CustomAttributeDescriptorValue descriptor)
+ {
+ return null;
+ }
+
+ EntityRegistry.CustomAttributeEntity attribute = MaterializeCustomAttribute(descriptor);
+ return descriptor.Owner is null ? attribute : null;
+ }
+
+ internal EntityRegistry.CustomAttributeEntity? MaterializeCustomAttributeDeclaration(
+ CILParser.CustomAttrDeclContext context)
+ => MaterializeCustomAttributeDeclaration(context.Value, context.Start);
+
+ internal EntityRegistry.CustomAttributeEntity MaterializeCustomAttributeDescriptor(
+ CILParser.CustomDescrContext context)
+ => MaterializeCustomAttribute(context.Value);
+
+ internal EntityRegistry.CustomAttributeEntity? MaterializeMethodBodyCustomAttributeDeclaration(
+ CILParser.CustomDescrInMethodBodyContext context)
+ => MaterializeCustomAttributeDeclaration(context.Value, context.Start);
+
+ internal EntityRegistry.EntityBase MaterializeOwnerType(
+ CILParser.OwnerTypeContext context)
+ => MaterializeOwnerType(context.Value);
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Sequences.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Sequences.cs
new file mode 100644
index 00000000000000..06c356deb44c33
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Sequences.cs
@@ -0,0 +1,100 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal void AddFloat32SequenceValue(BlobBuilder builder, double value)
+ => builder.WriteSingle((float)value);
+
+ internal void AddFloat32SequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteSingle(ParseInt32(value));
+
+ internal void AddFloat64SequenceValue(BlobBuilder builder, double value)
+ => builder.WriteDouble(value);
+
+ internal void AddFloat64SequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteDouble(ParseInt64(value));
+
+ internal void AddInt64SequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteInt64(ParseInt64(value));
+
+ internal void AddInt32SequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteInt32(ParseInt32(value));
+
+ internal void AddInt16SequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteInt16((short)ParseInt32(value));
+
+ internal void AddInt8SequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteByte((byte)ParseInt32(value));
+
+ internal void AddBooleanSequenceValue(BlobBuilder builder, bool value)
+ => builder.WriteBoolean(value);
+
+ internal void AddStringSequenceValue(BlobBuilder builder, IToken value)
+ => builder.WriteSerializedString(
+ value.Type == CILParser.NULLREF
+ ? null
+ : StringHelpers.ParseQuotedString(value.Text));
+
+ internal ClassSequenceElementValue CreateNullClassSequenceValue()
+ => new StringClassSequenceElementValue(null);
+
+ internal ClassSequenceElementValue CreateQuotedClassSequenceValue(IToken value)
+ => new StringClassSequenceElementValue(StringHelpers.ParseQuotedString(value.Text));
+
+ internal ClassSequenceElementValue CreateClassSequenceValue(ClassNameValue className)
+ => new TypeClassSequenceElementValue(className);
+
+ private BlobBuilder MaterializeSerializedSequence(SerializedSequenceValue sequence)
+ {
+ if (sequence is RawSerializedSequenceValue raw)
+ {
+ return raw.Value;
+ }
+
+ BlobBuilder blob = new();
+ switch (sequence)
+ {
+ case ClassSerializedSequenceValue classes:
+ foreach (ClassSequenceElementValue value in classes.Values)
+ {
+ MaterializeClassSequenceElement(value).WriteContentTo(blob);
+ }
+ break;
+ case ObjectSerializedSequenceValue objects:
+ foreach (SerializedInitializerValue value in objects.Values)
+ {
+ SerializedInitializerValue initializer = value;
+ while (initializer is ObjectSerializedInitializerValue boxed)
+ {
+ initializer = boxed.Value;
+ }
+
+ MaterializeSerializationType(initializer.Type).WriteContentTo(blob);
+ MaterializeSerializedInitializer(initializer).WriteContentTo(blob);
+ }
+ break;
+ }
+
+ return blob;
+ }
+
+ private BlobBuilder MaterializeClassSequenceElement(ClassSequenceElementValue value)
+ {
+ BlobBuilder blob = new();
+ blob.WriteSerializedString(value switch
+ {
+ StringClassSequenceElementValue text => text.Value,
+ TypeClassSequenceElementValue type => GetReflectionNotation(type.ClassName),
+ _ => null
+ });
+ return blob;
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Serialization.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Serialization.cs
new file mode 100644
index 00000000000000..0b365293139ced
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.CustomAttributes.Serialization.cs
@@ -0,0 +1,410 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Text;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal byte GetCustomAttributeNamedArgumentKind(IToken token)
+ => (byte)(token.Text == "field"
+ ? CustomAttributeNamedArgumentKind.Field
+ : CustomAttributeNamedArgumentKind.Property);
+
+ internal SerializationTypeValue CreateSerializationType(
+ SerializationTypeValue element,
+ IToken? array)
+ {
+ return array is null ? element : new ArraySerializationTypeValue(element);
+ }
+
+ internal SerializationTypeValue CreatePrimitiveSerializationType(byte type)
+ => new SimpleSerializationTypeValue((SerializationTypeCode)type);
+
+ internal SerializationTypeValue CreateSerializationTypeTypedef(
+ CILParser.SerializTypeElementContext context,
+ string alias)
+ => new TypedefSerializationTypeValue(context.Start, alias);
+
+ internal SerializationTypeValue CreateSimpleSerializationType(IToken type)
+ => new SimpleSerializationTypeValue(GetSerializationTypeCode(type.Type));
+
+ internal SerializationTypeValue CreateEnumSerializationType(IToken name)
+ => new StringEnumSerializationTypeValue(StringHelpers.ParseQuotedString(name.Text));
+
+ internal SerializationTypeValue CreateEnumSerializationType(ClassNameValue className)
+ => new ClassEnumSerializationTypeValue(className);
+
+ internal BlobBuilder CreateFloat32SerializedInitializer(
+ CILParser.Float64Context context,
+ double value)
+ {
+ float serializedValue = IsPlainInteger(context) &&
+ ParseIntegerValue(context.Start.Text.AsSpan(), out long rawValue)
+ ? BitConverter.Int32BitsToSingle((int)rawValue)
+ : (float)value;
+ BlobBuilder blob = CreateSerializedInitializer(SerializationTypeCode.Single);
+ blob.WriteSingle(serializedValue);
+ return blob;
+ }
+
+ internal BlobBuilder CreateFloat64SerializedInitializer(
+ CILParser.Float64Context context,
+ double value)
+ {
+ double serializedValue = IsPlainInteger(context) &&
+ ParseIntegerValue(context.Start.Text.AsSpan(), out long rawValue)
+ ? BitConverter.Int64BitsToDouble(rawValue)
+ : value;
+ BlobBuilder blob = CreateSerializedInitializer(SerializationTypeCode.Double);
+ blob.WriteDouble(serializedValue);
+ return blob;
+ }
+
+ private static bool IsPlainInteger(CILParser.Float64Context context)
+ => context.Start.Type == CILParser.INT32 &&
+ context.Stop is { Type: CILParser.INT32 };
+
+ internal BlobBuilder CreateFloat32BitsSerializedInitializer(IToken value)
+ {
+ BlobBuilder blob = CreateSerializedInitializer(SerializationTypeCode.Single);
+ blob.WriteSingle(BitConverter.Int32BitsToSingle(ParseInt32(value)));
+ return blob;
+ }
+
+ internal BlobBuilder CreateFloat64BitsSerializedInitializer(IToken value)
+ {
+ BlobBuilder blob = CreateSerializedInitializer(SerializationTypeCode.Double);
+ blob.WriteDouble(BitConverter.Int64BitsToDouble(ParseInt64(value)));
+ return blob;
+ }
+
+ internal BlobBuilder CreateIntegerSerializedInitializer(IToken type, IToken value)
+ {
+ BlobBuilder blob = CreateSerializedInitializer(GetSerializationTypeCode(type.Type));
+ switch (type.Type)
+ {
+ case CILParser.INT8:
+ case CILParser.UINT8:
+ blob.WriteByte((byte)ParseInt32(value));
+ break;
+ case CILParser.CHAR:
+ case CILParser.INT16:
+ case CILParser.UINT16:
+ blob.WriteInt16((short)ParseInt32(value));
+ break;
+ case CILParser.INT32_:
+ case CILParser.UINT32:
+ blob.WriteInt32(ParseInt32(value));
+ break;
+ case CILParser.INT64_:
+ case CILParser.UINT64:
+ blob.WriteInt64(ParseInt64(value));
+ break;
+ default:
+ throw new UnreachableException();
+ }
+
+ return blob;
+ }
+
+ internal BlobBuilder CreateBooleanSerializedInitializer(IToken type, bool value)
+ {
+ Debug.Assert(type.Type == CILParser.BOOL);
+ BlobBuilder blob = CreateSerializedInitializer(SerializationTypeCode.Boolean);
+ blob.WriteBoolean(value);
+ return blob;
+ }
+
+ internal BlobBuilder CreateByteArraySerializedInitializer(ImmutableArray value)
+ {
+ BlobBuilder blob = CreateSerializedInitializer(
+ SerializationTypeCode.String,
+ value.Length + 1);
+ blob.WriteBytes(value);
+ return blob;
+ }
+
+ private static BlobBuilder CreateSerializedInitializer(
+ SerializationTypeCode type,
+ int capacity = 9)
+ {
+ BlobBuilder blob = new(capacity);
+ blob.WriteByte((byte)type);
+ return blob;
+ }
+
+ internal FieldInitializerValue CreateFieldInitializer(BlobBuilder value)
+ => new(true, ExtractConstantFromSerInit(value));
+
+ internal FieldInitializerValue CreateFieldInitializer(string value)
+ => new(true, value);
+
+ internal FieldInitializerValue CreateNullFieldInitializer()
+ => new(true, null);
+
+ internal SerializedInitializerValue CreateScalarSerializedValue(
+ CILParser.SerInitContext context,
+ CILParser.FieldSerInitContext initializer,
+ BlobBuilder value)
+ {
+ if (initializer.Start.Text == "bytearray")
+ {
+ return new InvalidByteArraySerializedInitializerValue(context.Start);
+ }
+
+ ImmutableArray encodedValue = value.ToImmutableArray();
+ BlobBuilder serializedValue = new(Math.Max(0, encodedValue.Length - 1));
+ if (encodedValue.Length > 1)
+ {
+ serializedValue.WriteBytes(encodedValue.AsSpan().Slice(1).ToArray());
+ }
+
+ SerializationTypeValue type = encodedValue.Length == 0
+ ? new RawSerializationTypeValue(new BlobBuilder())
+ : new SimpleSerializationTypeValue((SerializationTypeCode)encodedValue[0]);
+ return new RawSerializedInitializerValue(type, serializedValue);
+ }
+
+ internal SerializedInitializerValue CreateStringSerializedValue()
+ => CreateSerializedStringValue(SerializationTypeCode.String, null);
+
+ internal SerializedInitializerValue CreateStringSerializedValue(IToken value)
+ => CreateSerializedStringValue(
+ SerializationTypeCode.String,
+ StringHelpers.ParseQuotedString(value.Text));
+
+ internal SerializedInitializerValue CreateTypeSerializedValue(IToken value)
+ => CreateSerializedStringValue(
+ SerializationTypeCode.Type,
+ StringHelpers.ParseQuotedString(value.Text));
+
+ internal SerializedInitializerValue CreateTypeSerializedValue(ClassNameValue className)
+ => new ClassNameSerializedInitializerValue(className);
+
+ internal SerializedInitializerValue CreateNullTypeSerializedValue()
+ => CreateSerializedStringValue(SerializationTypeCode.Type, null);
+
+ private static RawSerializedInitializerValue CreateSerializedStringValue(
+ SerializationTypeCode type,
+ string? value)
+ {
+ BlobBuilder serializedValue = new();
+ serializedValue.WriteSerializedString(value);
+ return new RawSerializedInitializerValue(
+ new SimpleSerializationTypeValue(type),
+ serializedValue);
+ }
+
+ internal SerializedInitializerValue CreateObjectSerializedValue(
+ SerializedInitializerValue value)
+ => new ObjectSerializedInitializerValue(value);
+
+ internal SerializedInitializerValue CreateArraySerializedValue(
+ IToken elementType,
+ IToken length,
+ SerializedSequenceValue values)
+ => new ArraySerializedInitializerValue(
+ new ArraySerializationTypeValue(
+ new SimpleSerializationTypeValue(GetSerializationTypeCode(elementType.Type))),
+ ParseInt32(length),
+ values);
+
+ internal SerializedInitializerValue CreateArraySerializedValue(
+ IToken elementType,
+ IToken length,
+ BlobBuilder values)
+ => CreateArraySerializedValue(
+ elementType,
+ length,
+ new RawSerializedSequenceValue(values));
+
+ private BlobBuilder MaterializeSerializationType(SerializationTypeValue value)
+ {
+ if (value is RawSerializationTypeValue raw)
+ {
+ return raw.Value;
+ }
+
+ BlobBuilder blob = new();
+ switch (value)
+ {
+ case SimpleSerializationTypeValue simple:
+ blob.WriteByte((byte)simple.Type);
+ break;
+ case ArraySerializationTypeValue array:
+ blob.WriteByte((byte)SerializationTypeCode.SZArray);
+ MaterializeSerializationType(array.ElementType).WriteContentTo(blob);
+ break;
+ case StringEnumSerializationTypeValue stringEnum:
+ blob.WriteByte((byte)SerializationTypeCode.Enum);
+ blob.WriteSerializedString(stringEnum.Name);
+ break;
+ case ClassEnumSerializationTypeValue classEnum:
+ blob.WriteByte((byte)SerializationTypeCode.Enum);
+ blob.WriteSerializedString(GetReflectionNotation(classEnum.ClassName));
+ break;
+ case TypedefSerializationTypeValue typedef:
+ if (TryResolveTypedefAsTypeBlob(typedef.Alias) is { } resolved)
+ {
+ resolved.WriteContentTo(blob);
+ }
+ else
+ {
+ ReportError(
+ DiagnosticIds.TypedefNotFound,
+ string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
+ typedef.Token);
+ }
+ break;
+ }
+
+ return blob;
+ }
+
+ private BlobBuilder MaterializeSerializedInitializer(SerializedInitializerValue value)
+ {
+ if (value is RawSerializedInitializerValue raw)
+ {
+ return raw.Value;
+ }
+
+ BlobBuilder blob = new();
+ switch (value)
+ {
+ case ClassNameSerializedInitializerValue className:
+ blob.WriteSerializedString(GetReflectionNotation(className.ClassName));
+ break;
+ case ObjectSerializedInitializerValue boxed:
+ MaterializeSerializationType(boxed.Value.Type).WriteContentTo(blob);
+ MaterializeSerializedInitializer(boxed.Value).WriteContentTo(blob);
+ break;
+ case InvalidByteArraySerializedInitializerValue invalid:
+ ReportError(
+ DiagnosticIds.InvalidMetadataToken,
+ "bytearray is not a valid structured custom attribute value",
+ invalid.Token);
+ blob.WriteSerializedString(null);
+ break;
+ case ArraySerializedInitializerValue array:
+ blob.WriteInt32(array.Length);
+ MaterializeSerializedSequence(array.Values).WriteContentTo(blob);
+ break;
+ }
+
+ return blob;
+ }
+
+ private string GetReflectionNotation(ClassNameValue className)
+ {
+ EntityRegistry.TypeEntity type = ResolveClassName(className);
+ return (type as EntityRegistry.IHasReflectionNotation)?.ReflectionNotation ?? string.Empty;
+ }
+
+ private static SerializationTypeCode GetSerializationTypeCode(int tokenType)
+ => tokenType switch
+ {
+ CILParser.INT8 => SerializationTypeCode.SByte,
+ CILParser.UINT8 => SerializationTypeCode.Byte,
+ CILParser.INT16 => SerializationTypeCode.Int16,
+ CILParser.UINT16 => SerializationTypeCode.UInt16,
+ CILParser.INT32_ => SerializationTypeCode.Int32,
+ CILParser.UINT32 => SerializationTypeCode.UInt32,
+ CILParser.INT64_ => SerializationTypeCode.Int64,
+ CILParser.UINT64 => SerializationTypeCode.UInt64,
+ CILParser.FLOAT32 => SerializationTypeCode.Single,
+ CILParser.FLOAT64_ => SerializationTypeCode.Double,
+ CILParser.CHAR => SerializationTypeCode.Char,
+ CILParser.BOOL => SerializationTypeCode.Boolean,
+ CILParser.STRING => SerializationTypeCode.String,
+ CILParser.TYPE => SerializationTypeCode.Type,
+ CILParser.OBJECT => SerializationTypeCode.TaggedObject,
+ _ => throw new UnreachableException()
+ };
+
+ private static object? ExtractConstantFromSerInit(BlobBuilder blob)
+ {
+ ImmutableArray bytes = blob.ToImmutableArray();
+ if (bytes.Length == 0)
+ {
+ return null;
+ }
+
+ SerializationTypeCode typeCode = (SerializationTypeCode)bytes[0];
+ ReadOnlySpan valueBytes = bytes.AsSpan().Slice(1);
+ return typeCode switch
+ {
+ SerializationTypeCode.Boolean => valueBytes.Length >= 1 && valueBytes[0] != 0,
+ SerializationTypeCode.Char => valueBytes.Length >= 2 ? BitConverter.ToChar(valueBytes) : '\0',
+ SerializationTypeCode.SByte => valueBytes.Length >= 1 ? (sbyte)valueBytes[0] : (sbyte)0,
+ SerializationTypeCode.Byte => valueBytes.Length >= 1 ? valueBytes[0] : (byte)0,
+ SerializationTypeCode.Int16 => valueBytes.Length >= 2 ? BitConverter.ToInt16(valueBytes) : (short)0,
+ SerializationTypeCode.UInt16 => valueBytes.Length >= 2 ? BitConverter.ToUInt16(valueBytes) : (ushort)0,
+ SerializationTypeCode.Int32 => valueBytes.Length >= 4 ? BitConverter.ToInt32(valueBytes) : 0,
+ SerializationTypeCode.UInt32 => valueBytes.Length >= 4 ? BitConverter.ToUInt32(valueBytes) : 0u,
+ SerializationTypeCode.Int64 => valueBytes.Length >= 8 ? BitConverter.ToInt64(valueBytes) : 0L,
+ SerializationTypeCode.UInt64 => valueBytes.Length >= 8 ? BitConverter.ToUInt64(valueBytes) : 0uL,
+ SerializationTypeCode.Single => valueBytes.Length >= 4 ? BitConverter.ToSingle(valueBytes) : 0f,
+ SerializationTypeCode.Double => valueBytes.Length >= 8 ? BitConverter.ToDouble(valueBytes) : 0d,
+ SerializationTypeCode.String => Encoding.Unicode.GetString(valueBytes),
+ SerializationTypeCode.Type => ExtractSerString(valueBytes),
+ SerializationTypeCode.SZArray => valueBytes.ToArray(),
+ SerializationTypeCode.TaggedObject => valueBytes.ToArray(),
+ SerializationTypeCode.Enum => valueBytes.ToArray(),
+ _ => bytes.AsSpan().ToArray()
+ };
+ }
+
+ private static string? ExtractSerString(ReadOnlySpan bytes)
+ {
+ if (bytes.Length == 0 || bytes[0] == 0xFF)
+ {
+ return null;
+ }
+
+ int length;
+ int bytesRead;
+ if ((bytes[0] & 0x80) == 0)
+ {
+ length = bytes[0];
+ bytesRead = 1;
+ }
+ else if ((bytes[0] & 0xC0) == 0x80)
+ {
+ if (bytes.Length < 2)
+ {
+ return null;
+ }
+
+ length = ((bytes[0] & 0x3F) << 8) | bytes[1];
+ bytesRead = 2;
+ }
+ else
+ {
+ if (bytes.Length < 4)
+ {
+ return null;
+ }
+
+ length = ((bytes[0] & 0x1F) << 24) |
+ (bytes[1] << 16) |
+ (bytes[2] << 8) |
+ bytes[3];
+ bytesRead = 4;
+ }
+
+ return bytes.Length < bytesRead + length
+ ? null
+ : Encoding.UTF8.GetString(bytes.Slice(bytesRead, length));
+ }
+
+ internal static FieldInitializerValue GetInitializerValue(CILParser.InitOptContext context)
+ => context.Value;
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Data.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Data.cs
new file mode 100644
index 00000000000000..75f140c4ebb804
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Data.cs
@@ -0,0 +1,218 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ internal CILParser.DataDeclarationBuilder CreateDataDeclaration(
+ CILParser.DataDeclContext context)
+ => new(
+ !IsDeclarationSuppressed &&
+ (context.Parent is not CILParser.MethodDeclContext ||
+ _currentMethod is not null));
+
+ internal void EndDataDeclaration(
+ CILParser.DataDeclContext context,
+ CILParser.DataDeclarationBuilder builder,
+ int initialSyntaxErrorCount)
+ {
+ bool hasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ context.HasSyntaxError = hasSyntaxError;
+
+ if (hasSyntaxError || !builder.ShouldCommit)
+ {
+ return;
+ }
+
+ int declarationOffset = _mappedFieldData.Count;
+ if (builder.Name is not null && !_mappedFieldDataNames.ContainsKey(builder.Name))
+ {
+ _mappedFieldDataNames.Add(builder.Name, declarationOffset);
+ }
+
+ _mappedFieldData.LinkSuffix(builder.Data);
+ if (builder.ReferenceFixups is null)
+ {
+ return;
+ }
+
+ foreach (CILParser.DataLabelReferenceValue reference in builder.ReferenceFixups)
+ {
+ _mappedFieldDataReferenceFixups.Add(reference with
+ {
+ DataOffset = declarationOffset + reference.DataOffset,
+ });
+ }
+ }
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal void SetDataDeclarationHeader(
+ CILParser.DataDeclarationBuilder builder,
+ byte section,
+ IToken name)
+ {
+ _ = section;
+ builder.Name = ParseIdentifier(name);
+ }
+
+ internal void SetAnonymousDataDeclarationHeader(
+ CILParser.DataDeclarationBuilder builder,
+ byte section)
+ {
+ _ = builder;
+ _ = section;
+ }
+
+ internal byte GetMappedDataSection() => 0;
+
+ internal byte GetTlsDataSection(CILParser.TlsContext context)
+ {
+ ReportError(
+ DiagnosticIds.UnsupportedTlsData,
+ DiagnosticMessageTemplates.UnsupportedTlsData,
+ context);
+ return 1;
+ }
+
+ internal byte GetCilDataSection() => 2;
+ internal int ParseDataItemCount(IToken token) => ParseInt32(token);
+
+ internal void AddDataString(CILParser.DataDeclarationBuilder builder, string value)
+ => builder.Data.WriteUTF16(value);
+
+ internal void AddDataReference(
+ CILParser.DataDeclarationBuilder builder,
+ IToken targetToken)
+ {
+ string target = ParseIdentifier(targetToken);
+ int pointerSize = VTableFixupSupport.GetPointerSize(
+ _options.Machine ?? Machine.I386);
+ builder.ReferenceFixups ??= new List();
+ builder.ReferenceFixups.Add(new CILParser.DataLabelReferenceValue(
+ target,
+ builder.Data.Count,
+ pointerSize,
+ targetToken));
+ builder.Data.ReserveBytes(pointerSize);
+ }
+
+ internal void AddDataBytes(
+ CILParser.DataDeclarationBuilder builder,
+ ImmutableArray value)
+ => builder.Data.WriteBytes(value);
+
+ internal void AddFloatingPointData(
+ CILParser.DataDeclarationBuilder builder,
+ IToken kind,
+ double value,
+ int count)
+ {
+ if (count <= 0)
+ {
+ return;
+ }
+
+ if (kind.Text == "float32")
+ {
+ float single = (float)value;
+ for (int i = 0; i < count; i++)
+ {
+ builder.Data.WriteSingle(single);
+ }
+ }
+ else
+ {
+ Debug.Assert(kind.Text == "float64");
+ for (int i = 0; i < count; i++)
+ {
+ builder.Data.WriteDouble(value);
+ }
+ }
+ }
+
+ internal void AddInt64Data(
+ CILParser.DataDeclarationBuilder builder,
+ IToken kind,
+ IToken value,
+ int count)
+ {
+ Debug.Assert(kind.Text == "int64");
+ if (count <= 0)
+ {
+ return;
+ }
+
+ long parsedValue = ParseInt64(value);
+ for (int i = 0; i < count; i++)
+ {
+ builder.Data.WriteInt64(parsedValue);
+ }
+ }
+
+ internal void AddIntegerData(
+ CILParser.DataDeclarationBuilder builder,
+ IToken kind,
+ IToken value,
+ int count)
+ {
+ if (count <= 0)
+ {
+ return;
+ }
+
+ int parsedValue = ParseInt32(value);
+ switch (kind.Text)
+ {
+ case "int8":
+ builder.Data.WriteBytes((byte)parsedValue, count);
+ break;
+ case "int16":
+ for (int i = 0; i < count; i++)
+ {
+ builder.Data.WriteInt16((short)parsedValue);
+ }
+ break;
+ default:
+ Debug.Assert(kind.Text == "int32");
+ for (int i = 0; i < count; i++)
+ {
+ builder.Data.WriteInt32(parsedValue);
+ }
+ break;
+ }
+ }
+
+ internal void AddZeroData(
+ CILParser.DataDeclarationBuilder builder,
+ IToken kind,
+ int count)
+ {
+ if (count <= 0)
+ {
+ return;
+ }
+
+ int elementSize = kind.Text switch
+ {
+ "int8" => sizeof(byte),
+ "int16" => sizeof(short),
+ "int32" or "float32" => sizeof(int),
+ "int64" or "float64" => sizeof(long),
+ _ => throw new UnreachableException(),
+ };
+ builder.Data.WriteBytes(0, checked(elementSize * count));
+ }
+#pragma warning restore CA1822
+
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Debug.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Debug.cs
new file mode 100644
index 00000000000000..5ef604eaace2b1
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Debug.cs
@@ -0,0 +1,227 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal bool IsAutoIncrementSourceDirective(IToken token) => token.Text == "#line";
+#pragma warning restore CA1822
+
+ internal SourceDirectiveValue CreateSourceLine(
+ bool autoIncrement,
+ IToken line,
+ IToken? path)
+ {
+ int lineNumber = ParseInt32(line);
+ return CreateSourceDirective(autoIncrement, lineNumber, 0, lineNumber, 0, path);
+ }
+
+ internal SourceDirectiveValue CreateSourceColumn(
+ bool autoIncrement,
+ IToken line,
+ IToken column,
+ IToken? path)
+ {
+ int lineNumber = ParseInt32(line);
+ int columnNumber = ParseInt32(column);
+ return CreateSourceDirective(
+ autoIncrement,
+ lineNumber,
+ columnNumber,
+ lineNumber,
+ columnNumber,
+ path);
+ }
+
+ internal SourceDirectiveValue CreateSourceColumnRange(
+ bool autoIncrement,
+ IToken line,
+ IToken startColumn,
+ IToken endColumn,
+ IToken? path)
+ {
+ int lineNumber = ParseInt32(line);
+ return CreateSourceDirective(
+ autoIncrement,
+ lineNumber,
+ ParseInt32(startColumn),
+ lineNumber,
+ ParseInt32(endColumn),
+ path);
+ }
+
+ internal SourceDirectiveValue CreateSourceLineRange(
+ bool autoIncrement,
+ IToken startLine,
+ IToken endLine,
+ IToken column,
+ IToken? path)
+ {
+ int columnNumber = ParseInt32(column);
+ return CreateSourceDirective(
+ autoIncrement,
+ ParseInt32(startLine),
+ columnNumber,
+ ParseInt32(endLine),
+ columnNumber,
+ path);
+ }
+
+ internal SourceDirectiveValue CreateSourceRange(
+ bool autoIncrement,
+ IToken startLine,
+ IToken endLine,
+ IToken startColumn,
+ IToken endColumn,
+ IToken? path)
+ => CreateSourceDirective(
+ autoIncrement,
+ ParseInt32(startLine),
+ ParseInt32(startColumn),
+ ParseInt32(endLine),
+ ParseInt32(endColumn),
+ path);
+
+ private static SourceDirectiveValue CreateSourceDirective(
+ bool autoIncrement,
+ int startLine,
+ int startColumn,
+ int endLine,
+ int endColumn,
+ IToken? path)
+ => new(
+ autoIncrement,
+ startLine,
+ startColumn,
+ endLine,
+ endColumn,
+ path is null ? null : StringHelpers.ParseQuotedString(path.Text));
+
+ internal void EndSourceDirective(
+ CILParser.ExtSourceSpecContext context,
+ int initialSyntaxErrorCount)
+ {
+ context.HasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (context.HasSyntaxError ||
+ context.Value is not { } value ||
+ !CanApplySharedDirective(context))
+ {
+ context.Value = null;
+ return;
+ }
+
+ ApplySourceDirective(value);
+ }
+
+ private void ApplySourceDirective(SourceDirectiveValue value)
+ {
+ if (value.DocumentPath is not null)
+ {
+ _currentDocumentPath = value.DocumentPath;
+ }
+
+ if (_currentMethod is null || _currentDocumentPath is null)
+ {
+ return;
+ }
+
+ int ilOffset = _currentMethod.Definition.MethodBody.Offset;
+ EntityRegistry.MethodDebugInfo debugInfo = _currentMethod.Definition.DebugInfo;
+ if (debugInfo.DocumentPath is null)
+ {
+ debugInfo.DocumentPath = _currentDocumentPath;
+ debugInfo.LanguageGuid = _currentLanguageGuid;
+ }
+
+ EntityRegistry.SequencePoint sequencePoint;
+ if (value.StartLine == 0xFEEFEE)
+ {
+ sequencePoint = EntityRegistry.SequencePoint.Hidden(ilOffset);
+ }
+ else
+ {
+ int endColumn = value.EndColumn;
+ if (value.EndLine == value.StartLine && endColumn == value.StartColumn)
+ {
+ endColumn++;
+ }
+
+ sequencePoint = new EntityRegistry.SequencePoint(
+ ilOffset,
+ value.StartLine,
+ value.StartColumn,
+ value.EndLine,
+ endColumn);
+ }
+
+ List sequencePoints = debugInfo.SequencePoints;
+ if (sequencePoints.Count > 0 && sequencePoints[^1].ILOffset == ilOffset)
+ {
+ sequencePoints[^1] = sequencePoint;
+ }
+ else
+ {
+ sequencePoints.Add(sequencePoint);
+ }
+ }
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal string ParseLanguageString(IToken token)
+ => StringHelpers.ParseQuotedString(token.Text);
+
+ internal LanguageDirectiveValue CreateLanguageDirective(string language)
+ => new LanguageDirectiveValue(language, null, null);
+
+ internal LanguageDirectiveValue CreateLanguageDirective(string language, string vendor)
+ => new LanguageDirectiveValue(language, vendor, null);
+
+ internal LanguageDirectiveValue CreateLanguageDirective(
+ string language,
+ string vendor,
+ string documentType)
+ => new LanguageDirectiveValue(language, vendor, documentType);
+#pragma warning restore CA1822
+
+ internal void EndLanguageDirective(
+ CILParser.LanguageDeclContext context,
+ int initialSyntaxErrorCount)
+ {
+ context.HasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (context.HasSyntaxError ||
+ context.Value is not { } value ||
+ !CanApplySharedDirective(context))
+ {
+ context.Value = null;
+ return;
+ }
+
+ if (Guid.TryParse(value.Language, out Guid language))
+ {
+ _currentLanguageGuid = language;
+ }
+ if (value.Vendor is not null && Guid.TryParse(value.Vendor, out Guid vendor))
+ {
+ _currentLanguageVendorGuid = vendor;
+ }
+ if (value.DocumentType is not null &&
+ Guid.TryParse(value.DocumentType, out Guid documentType))
+ {
+ _currentDocumentTypeGuid = documentType;
+ }
+ }
+
+ private bool CanApplySharedDirective(ParserRuleContext context)
+ => !IsDeclarationSuppressed &&
+ (context.Parent is not CILParser.MethodDeclContext || _currentMethod is not null);
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Declarations.Actions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Declarations.Actions.cs
new file mode 100644
index 00000000000000..6aaf24e8af6faf
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Declarations.Actions.cs
@@ -0,0 +1,198 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Reflection.PortableExecutable;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal void BeginTopLevelDirective() => PrepareTopLevelDeclaration();
+
+ internal void ProcessTopLevelDataDeclaration(CILParser.DataDeclContext context)
+ => _ = context;
+
+ internal void ProcessTopLevelVTableDeclaration(CILParser.VtableDeclContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeVTable(context);
+ }
+ }
+
+ internal void ProcessTopLevelVTableFixupDeclaration(CILParser.VtfixupDeclContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeVTableFixup(context);
+ }
+ }
+
+ internal void ProcessTopLevelSourceDirective(CILParser.ExtSourceSpecContext context)
+ => _ = context;
+
+ internal void ProcessTopLevelFileDeclaration(CILParser.FileDeclContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ _ = MaterializeFileDeclaration(context);
+ }
+ }
+
+ internal void ProcessTopLevelAssembly(CILParser.AssemblyBlockContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeAssemblyDefinition(context);
+ }
+ }
+
+ internal void ProcessTopLevelAssemblyReference(CILParser.AssemblyRefBlockContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeAssemblyReference(context);
+ }
+ }
+
+ internal void ProcessTopLevelExportedType(CILParser.ExptypeBlockContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeExportedType(context);
+ }
+ }
+
+ internal void ProcessTopLevelManifestResource(CILParser.ManifestResBlockContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeManifestResource(context);
+ }
+ }
+
+ internal void SetModuleHeader(
+ CILParser.ModuleHeadContext context,
+ string name,
+ bool isExternal)
+ {
+ context.Value = name;
+ context.HasName = true;
+ context.IsExternal = isExternal;
+ }
+
+ internal void SetEmptyModuleHeader(CILParser.ModuleHeadContext context)
+ {
+ context.Value = string.Empty;
+ context.HasName = false;
+ context.IsExternal = false;
+ }
+
+ internal void ProcessTopLevelModule(string? name, bool hasName, bool isExternal)
+ {
+ if (IsDeclarationSuppressed)
+ {
+ return;
+ }
+
+ if (!hasName)
+ {
+ _entityRegistry.Module.Name = null;
+ }
+ else if (isExternal)
+ {
+ _entityRegistry.GetOrCreateModuleReference(name ?? string.Empty, _ => { });
+ }
+ else
+ {
+ _entityRegistry.Module.Name = name;
+ }
+ }
+
+ internal void ProcessTopLevelSecurityDeclaration(CILParser.SecDeclContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ EntityRegistry.DeclarativeSecurityAttributeEntity? security =
+ MaterializeSecurityDeclaration(context);
+ security?.Parent = _entityRegistry.Assembly;
+ }
+ }
+
+ internal void ProcessTopLevelCustomAttribute(CILParser.CustomAttrDeclContext context)
+ {
+ if (!IsDeclarationSuppressed &&
+ !context.HasSyntaxError &&
+ MaterializeCustomAttributeDeclaration(context) is { } customAttribute)
+ {
+ customAttribute.Owner ??=
+ (EntityRegistry.EntityBase?)_lastFieldDefinition ?? _entityRegistry.Module;
+ }
+ }
+
+ internal void ProcessTopLevelSubsystem(IToken value)
+ {
+ if (!IsDeclarationSuppressed)
+ {
+ _subsystem = (Subsystem)ParseInt32(value);
+ }
+ }
+
+ internal void ProcessTopLevelCorFlags(IToken value)
+ {
+ if (!IsDeclarationSuppressed)
+ {
+ _corflags = (CorFlags)ParseInt32(value);
+ }
+ }
+
+ internal void ProcessTopLevelAlignment(IToken value)
+ {
+ if (!IsDeclarationSuppressed)
+ {
+ _alignment = ParseInt32(value);
+ }
+ }
+
+ internal void ProcessTopLevelImageBase(IToken value)
+ {
+ if (!IsDeclarationSuppressed)
+ {
+ _imageBase = ParseInt64(value);
+ }
+ }
+
+ internal void ProcessTopLevelStackReserve(IToken value)
+ {
+ if (!IsDeclarationSuppressed)
+ {
+ _stackReserve = ParseInt64(value);
+ }
+ }
+
+ internal void ProcessTopLevelLanguageDirective(CILParser.LanguageDeclContext context)
+ => _ = context;
+
+ internal void ProcessTopLevelTypedef(CILParser.TypedefDeclContext context)
+ {
+ if (!IsDeclarationSuppressed && !context.HasSyntaxError)
+ {
+ MaterializeTypedef(context);
+ }
+ }
+
+ internal void BeginTopLevelTypeList() => PrepareTopLevelDeclaration();
+
+ internal void ProcessTopLevelTypeListEntry(ClassNameValue value)
+ {
+ if (!IsDeclarationSuppressed)
+ {
+ _ = ResolveClassName(value);
+ }
+ }
+
+ private void PrepareTopLevelDeclaration() => ClearPendingCustomAttributeOwners();
+}
+#pragma warning restore CA1822
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Instructions.References.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Instructions.References.cs
new file mode 100644
index 00000000000000..5bda6c6db7710b
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Instructions.References.cs
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ internal void EmitMethodReferenceInstruction(IToken opcodeToken, CILParser.MethodRefContext context)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ ILOpCode opcode = instruction.OpCode;
+ CurrentMethodContext method = instruction.Method;
+ bool expectInstance = opcode is ILOpCode.Callvirt or ILOpCode.Newobj;
+ _expectInstance = expectInstance;
+ try
+ {
+ method.Definition.MethodBody.OpCode(opcode);
+ WriteInstructionToken(method, MaterializeMethodReference(context));
+ }
+ finally
+ {
+ if (expectInstance)
+ {
+ _expectInstance = false;
+ }
+ }
+ }
+
+ internal void EmitFieldReferenceInstruction(IToken opcodeToken, CILParser.FieldRefContext context)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ WriteInstructionToken(instruction.Method, MaterializeFieldReference(context));
+ }
+
+ internal void EmitMetadataTokenInstruction(IToken opcodeToken, CILParser.MdtokenContext context)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ WriteInstructionToken(instruction.Method, ResolveMetadataToken(context));
+ }
+
+ internal void EmitTypeReferenceInstruction(IToken opcodeToken, CILParser.TypeSpecContext context)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ WriteInstructionToken(instruction.Method, ResolveTypeSpecification(context));
+ }
+
+ internal void EmitCalliInstruction(IToken opcodeToken, CILParser.CalliSignatureContext context)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ Debug.Assert(instruction.OpCode == ILOpCode.Calli);
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ instruction.Method.Definition.MethodBody.Token(
+ _entityRegistry.GetOrCreateStandaloneSignature(
+ MaterializeCalliSignature(context.Value)).Handle);
+ }
+
+ internal void EmitOwnerTokenInstruction(IToken opcodeToken, CILParser.OwnerTypeContext context)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ WriteInstructionToken(instruction.Method, MaterializeOwnerType(context));
+ }
+
+ private static void WriteInstructionToken(CurrentMethodContext method, EntityRegistry.EntityBase entity)
+ {
+ if (entity is EntityRegistry.TypeReferenceEntity typeReference)
+ {
+ typeReference.RecordBlobToWriteResolvedToken(
+ method.Definition.MethodBody.CodeBuilder.ReserveBytes(sizeof(int)));
+ }
+ else if (entity is EntityRegistry.MemberReferenceEntity memberReference)
+ {
+ memberReference.RecordBlobToWriteResolvedHandle(
+ method.Definition.MethodBody.CodeBuilder.ReserveBytes(sizeof(int)));
+ }
+ else
+ {
+ method.Definition.MethodBody.Token(entity.Handle);
+ }
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Instructions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Instructions.cs
new file mode 100644
index 00000000000000..9161542663e550
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Instructions.cs
@@ -0,0 +1,415 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Runtime.InteropServices;
+using System.Text;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ internal void EmitNoOperandInstruction(IToken opcodeToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ }
+
+ internal void EmitVariableIndexInstruction(IToken opcodeToken, IToken indexToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ WriteVariableIndex(instruction.Method, instruction.OpCode, ParseInt32(indexToken));
+ }
+
+ internal void EmitVariableNameInstruction(IToken opcodeToken, IToken nameToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ CurrentMethodContext method = instruction.Method;
+ ILOpCode opcode = instruction.OpCode;
+ string instructionName = opcode.ToString();
+ string variableName = ParseIdentifier(nameToken);
+ int? index = null;
+
+ if (instructionName.Contains("arg", StringComparison.Ordinal))
+ {
+ if (method.ArgumentNames.TryGetValue(variableName, out int argumentIndex))
+ {
+ index = method.Definition.SignatureHeader.IsInstance ? argumentIndex + 1 : argumentIndex;
+ }
+ else
+ {
+ ReportError(
+ DiagnosticIds.ArgumentNotFound,
+ string.Format(DiagnosticMessageTemplates.ArgumentNotFound, variableName),
+ opcodeToken);
+ }
+ }
+ else
+ {
+ for (int i = method.LocalsScopes.Count - 1; i >= 0; i--)
+ {
+ if (method.LocalsScopes[i].TryGetValue(variableName, out int localIndex))
+ {
+ index = localIndex;
+ break;
+ }
+ }
+
+ if (index is null)
+ {
+ ReportError(
+ DiagnosticIds.LocalNotFound,
+ string.Format(DiagnosticMessageTemplates.LocalNotFound, variableName),
+ opcodeToken);
+ }
+ }
+
+ WriteVariableIndex(method, opcode, index ?? -1);
+ }
+
+ internal void EmitInt32Instruction(IToken opcodeToken, IToken valueToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ int value = ParseInt32(valueToken);
+ if (instruction.OpCode is ILOpCode.Ldc_i4 or ILOpCode.Ldc_i4_s)
+ {
+ instruction.Method.Definition.MethodBody.LoadConstantI4(value);
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ instruction.Method.Definition.MethodBody.CodeBuilder.WriteByte((byte)value);
+ }
+
+ internal void EmitInt64Instruction(IToken opcodeToken, IToken valueToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ Debug.Assert(instruction.OpCode == ILOpCode.Ldc_i8);
+ instruction.Method.Definition.MethodBody.LoadConstantI8(ParseInt64(valueToken));
+ }
+
+ internal void EmitFloatingInstruction(IToken opcodeToken, double value)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ WriteFloatingInstruction(instruction.Method, instruction.OpCode, value);
+ }
+
+ internal void EmitFloatingInstruction(IToken opcodeToken, IToken valueToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ WriteFloatingInstruction(instruction.Method, instruction.OpCode, ParseInt64(valueToken));
+ }
+
+ internal void EmitBranchOffsetInstruction(IToken opcodeToken, IToken offsetToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ InstructionEncoder body = instruction.Method.Definition.MethodBody;
+ int offset = ParseInt32(offsetToken);
+ body.OpCode(instruction.OpCode);
+ if (instruction.OpCode.GetBranchOperandSize() == 1)
+ {
+ body.CodeBuilder.WriteSByte(unchecked((sbyte)offset));
+ }
+ else
+ {
+ body.CodeBuilder.WriteInt32(offset);
+ }
+ }
+
+ internal void EmitBranchLabelInstruction(IToken opcodeToken, IToken labelToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ CurrentMethodContext method = instruction.Method;
+ string labelName = ParseIdentifier(labelToken);
+ LabelHandle label = GetOrCreateMethodLabel(method, labelName, opcodeToken);
+
+ method.Definition.MethodBody.Branch(instruction.OpCode, label);
+ }
+
+ internal void EmitRawFloatingInstruction(
+ IToken opcodeToken,
+ ImmutableArray bytes,
+ IToken bytesToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ double value;
+ ReadOnlySpan byteSpan = bytes.AsSpan();
+ if (byteSpan.Length >= sizeof(double))
+ {
+ value = BitConverter.ToDouble(byteSpan);
+ }
+ else if (byteSpan.Length >= sizeof(float))
+ {
+ value = BitConverter.ToSingle(byteSpan);
+ }
+ else
+ {
+ ReportError(
+ DiagnosticIds.ByteArrayTooShort,
+ DiagnosticMessageTemplates.ByteArrayTooShort,
+ bytesToken);
+ value = 0;
+ }
+
+ WriteFloatingInstruction(instruction.Method, instruction.OpCode, value);
+ }
+
+ internal void EmitRawStringInstruction(IToken opcodeToken, ImmutableArray bytes)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ string value = MemoryMarshal.Cast(bytes.AsSpan()).ToString();
+ instruction.Method.Definition.MethodBody.LoadString(_metadataBuilder.GetOrAddUserString(value));
+ }
+
+ internal void EmitStringInstruction(IToken opcodeToken, string value)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.LoadString(_metadataBuilder.GetOrAddUserString(value));
+ }
+
+ internal void EmitAnsiStringInstruction(IToken opcodeToken, string value)
+ {
+ int byteCount = Encoding.UTF8.GetByteCount(value);
+ if ((byteCount % 2) != 0)
+ {
+ byteCount++;
+ }
+
+ Span utf8Bytes = new byte[byteCount];
+ Encoding.UTF8.GetBytes(value, utf8Bytes);
+ EmitStringInstruction(opcodeToken, new string(MemoryMarshal.Cast(utf8Bytes)));
+ }
+
+ internal void EmitRawTokenInstruction(IToken opcodeToken, IToken valueToken)
+ {
+ if (StartInstruction(opcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ instruction.Method.Definition.MethodBody.OpCode(instruction.OpCode);
+ instruction.Method.Definition.MethodBody.CodeBuilder.WriteInt32(ParseInt32(valueToken));
+ }
+
+ internal void EmitByte(IToken valueToken)
+ {
+ _currentMethod?.Definition.MethodBody.CodeBuilder.WriteByte((byte)ParseInt32(valueToken));
+ }
+
+ internal void SetMaxStack(IToken valueToken)
+ {
+ if (_currentMethod is not null)
+ {
+ _currentMethod.Definition.MaxStack = ParseInt32(valueToken);
+ }
+ }
+
+ internal void SetEntryPoint()
+ {
+ if (_currentMethod is not null)
+ {
+ _entityRegistry.EntryPoint = _currentMethod.Definition;
+ }
+ }
+
+ internal void SetZeroInit()
+ {
+ if (_currentMethod is not null)
+ {
+ _currentMethod.Definition.BodyAttributes = MethodBodyAttributes.InitLocals;
+ }
+ }
+
+ internal void DefineLabel(IToken nameToken)
+ {
+ if (_currentMethod is not { } method)
+ {
+ return;
+ }
+
+ string labelName = ParseIdentifier(nameToken);
+ method.UndefinedLabelReferences.Remove(labelName);
+ if (!method.Labels.TryGetValue(labelName, out LabelHandle label))
+ {
+ label = method.Definition.MethodBody.DefineLabel();
+ method.Labels[labelName] = label;
+ }
+
+ method.Definition.MethodBody.MarkLabel(label);
+ }
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal CILParser.SwitchInstructionBuilder CreateSwitchInstruction(IToken opcodeToken)
+ => new(opcodeToken);
+
+ internal void AddSwitchLabel(CILParser.SwitchInstructionBuilder builder, IToken labelToken)
+ => builder.Operands.Add((labelToken, false));
+
+ internal void AddSwitchOffset(CILParser.SwitchInstructionBuilder builder, IToken offsetToken)
+ => builder.Operands.Add((offsetToken, true));
+#pragma warning restore CA1822
+
+ internal void CompleteSwitchInstruction(CILParser.SwitchInstructionBuilder? builder)
+ {
+ if (builder is null ||
+ StartInstruction(builder.OpcodeToken) is not { } instruction)
+ {
+ return;
+ }
+
+ Debug.Assert(instruction.OpCode == ILOpCode.Switch);
+ CurrentMethodContext method = instruction.Method;
+ List<(LabelHandle Label, int? Offset)> labels = new(builder.Operands.Count);
+ foreach ((IToken token, bool isOffset) in builder.Operands)
+ {
+ if (isOffset)
+ {
+ labels.Add((method.Definition.MethodBody.DefineLabel(), ParseInt32(token)));
+ continue;
+ }
+
+ string labelName = ParseIdentifier(token);
+ LabelHandle label = GetOrCreateMethodLabel(
+ method,
+ labelName,
+ builder.OpcodeToken);
+
+ labels.Add((label, null));
+ }
+
+ if (labels.Count > 0)
+ {
+ SwitchInstructionEncoder switchEncoder = method.Definition.MethodBody.Switch(labels.Count);
+ foreach ((LabelHandle label, _) in labels)
+ {
+ switchEncoder.Branch(label);
+ }
+ }
+ else
+ {
+ method.Definition.MethodBody.OpCode(ILOpCode.Switch);
+ method.Definition.MethodBody.CodeBuilder.WriteInt32(0);
+ }
+
+ foreach ((LabelHandle label, int? offset) in labels)
+ {
+ if (offset is int value)
+ {
+ method.Definition.MethodBody.MarkLabel(label, method.Definition.MethodBody.Offset + value);
+ }
+ }
+ }
+
+ private (CurrentMethodContext Method, ILOpCode OpCode)? StartInstruction(IToken opcodeToken)
+ {
+ if (_currentMethod is not { } method)
+ {
+ return null;
+ }
+
+ ILOpCode opcode = ParseOpCodeFromToken(opcodeToken);
+ return (method, opcode);
+ }
+
+ private static void WriteVariableIndex(CurrentMethodContext method, ILOpCode opcode, int index)
+ {
+ method.Definition.MethodBody.OpCode(opcode);
+ if (opcode.ToString().EndsWith("_s", StringComparison.Ordinal))
+ {
+ method.Definition.MethodBody.CodeBuilder.WriteByte((byte)index);
+ }
+ else
+ {
+ method.Definition.MethodBody.CodeBuilder.WriteInt32(index);
+ }
+ }
+
+ private static void WriteFloatingInstruction(CurrentMethodContext method, ILOpCode opcode, double value)
+ {
+ if (opcode == ILOpCode.Ldc_r4)
+ {
+ method.Definition.MethodBody.LoadConstantR4((float)value);
+ }
+ else
+ {
+ method.Definition.MethodBody.LoadConstantR8(value);
+ }
+ }
+
+ private static ILOpCode ParseOpCodeFromToken(IToken token)
+ {
+ string text = token.Text.TrimEnd('.');
+ if (text == "unused")
+ {
+ return ILOpCode.Unused;
+ }
+
+ string normalized = text.Replace('.', '_');
+ normalized = normalized switch
+ {
+ "ldelem_u8" => "ldelem_i8",
+ "ldind_u8" => "ldind_i8",
+ "endfault" => "endfinally",
+ _ => normalized
+ };
+
+ return (ILOpCode)Enum.Parse(typeof(ILOpCode), normalized, ignoreCase: true);
+ }
+
+ internal EntityRegistry.EntityBase ResolveMetadataToken(CILParser.MdtokenContext context)
+ => ResolveMetadataToken(context.Value);
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Literals.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Literals.cs
new file mode 100644
index 00000000000000..2687f69bb9d8bc
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Literals.cs
@@ -0,0 +1,201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Binary;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Text;
+using Antlr4.Runtime;
+using Antlr4.Runtime.Misc;
+
+namespace ILAssembler
+{
+#pragma warning disable CA1822 // Mark members as static
+ internal sealed partial class GrammarActions
+ {
+ internal void AddComposedStringPart(StringBuilder builder, IToken token)
+ => builder.Append(StringHelpers.ParseQuotedString(token.Text));
+
+ internal string EndComposedString(StringBuilder builder)
+ => builder.ToString();
+
+ internal void AddDottedNamePart(CILParser.DottedNameBuilder builder, string value)
+ {
+ if (builder.HasPart)
+ {
+ builder.Value.Append('.');
+ }
+
+ builder.Value.Append(value);
+ builder.HasPart = true;
+ }
+
+ internal void AddDottedNameToken(CILParser.DottedNameBuilder builder, IToken token)
+ => AddDottedNamePart(builder, ParseIdentifier(token));
+
+ internal string EndDottedName(CILParser.DottedNameBuilder builder)
+ => builder.Value.ToString();
+
+ internal string ParseDottedNamePart(IToken token)
+ => token.Text.Length >= 2 && token.Text[0] == '\''
+ ? StringHelpers.ParseQuotedString(token.Text)
+ : token.Text;
+
+ internal double ParseFloatingLiteral(IToken token)
+ {
+ string text = token.Text;
+ bool neg = text.StartsWith('-');
+ if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out double result))
+ {
+ result = neg ? double.MinValue : double.MaxValue;
+ }
+
+ return result;
+ }
+
+ internal double ParseFloatingInteger(IToken token)
+ {
+ if (!ParseIntegerValue(token.Text.AsSpan(), out long value))
+ {
+ ReportLiteralOutOfRange(token);
+ value = 0;
+ }
+
+ return value;
+ }
+
+ internal double ParseFloat32Bits(IToken token)
+ => BitConverter.Int32BitsToSingle(ParseInt32(token));
+
+ internal double ParseFloat64Bits(IToken token)
+ => BitConverter.Int64BitsToDouble(ParseInt64(token));
+
+ internal static string GetIdentifier(CILParser.IdContext context)
+ => ParseIdentifier(context.Start);
+
+ private static string ParseIdentifier(IToken token)
+ {
+ string text = token.Text;
+ return text.Length >= 2 && text[0] == '\''
+ ? text.Substring(1, text.Length - 2)
+ : text;
+ }
+
+ private static bool ParseIntegerValue(ReadOnlySpan value, out long result)
+ {
+ NumberStyles parseStyle = NumberStyles.None;
+ bool negate = false;
+ if (value.StartsWith("-".AsSpan()))
+ {
+ negate = true;
+ value = value.Slice(1);
+ }
+
+ if (value.StartsWith("0x".AsSpan()))
+ {
+ parseStyle = NumberStyles.AllowHexSpecifier;
+ value = value.Slice(2);
+ }
+ else if (value.StartsWith("0".AsSpan()))
+ {
+ // Octal support isn't built-in, so we'll do it manually.
+ result = 0;
+ for (int i = 0; i < value.Length; i++)
+ {
+ if (i != 0)
+ {
+ result *= 8;
+ }
+
+ int digitValue = value[i] - '0';
+ if (digitValue < 0 || digitValue > 7)
+ {
+ // COMPAT: native ilasm skips invalid digits silently
+ continue;
+ }
+ result += digitValue;
+ }
+ if (negate) result = -result;
+ return true;
+ }
+
+ bool success = long.TryParse(value.ToString(), parseStyle, CultureInfo.InvariantCulture, out result);
+ if (!success)
+ {
+ // Try parsing as unsigned to handle values like:
+ // - Decimal overflow with negation: 9223372036854775808 (= -Int64.MinValue)
+ // - Large unsigned decimal: 18444492274432737280
+ if (ulong.TryParse(value.ToString(), parseStyle, CultureInfo.InvariantCulture, out ulong uresult))
+ {
+ result = unchecked((long)uresult);
+ if (negate) result = unchecked(-result);
+ return true;
+ }
+ // Handle oversized hex values (>64 bits) by truncating to low 64 bits,
+ // matching native ilasm behavior for values like 0x94188556b24089e8b90c9c61f9f3088
+ if (parseStyle == NumberStyles.AllowHexSpecifier && value.Length > 16)
+ {
+ var truncated = value.Slice(value.Length - 16);
+ if (ulong.TryParse(truncated.ToString(), parseStyle, CultureInfo.InvariantCulture, out uresult))
+ {
+ result = unchecked((long)uresult);
+ if (negate) result = unchecked(-result);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (negate) result = -result;
+ return true;
+ }
+
+ internal int ParseInt32(IToken token)
+ {
+ ReadOnlySpan value = token.Text.AsSpan();
+ if (!ParseIntegerValue(value, out long num))
+ {
+ ReportLiteralOutOfRange(token);
+ return 0;
+ }
+
+ return (int)num;
+ }
+
+
+ private long ParseInt64(IToken token)
+ {
+ ReadOnlySpan value = token.Text.AsSpan();
+ if (!ParseIntegerValue(value, out long num))
+ {
+ ReportLiteralOutOfRange(token);
+ return 0;
+ }
+
+ return num;
+ }
+
+ private void ReportLiteralOutOfRange(IToken token)
+ {
+ _diagnostics.Add(new Diagnostic(
+ DiagnosticIds.LiteralOutOfRange,
+ DiagnosticSeverity.Error,
+ string.Format(DiagnosticMessageTemplates.LiteralOutOfRange, token.Text),
+ Location.From(token, _documents)));
+ }
+
+ internal bool ParseBoolean(IToken token) => token.Text == "true";
+
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Assembly.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Assembly.cs
new file mode 100644
index 00000000000000..96b3a08663edf0
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Assembly.cs
@@ -0,0 +1,205 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Text;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal void SetAssemblyAttribute(CILParser.AsmAttrAnyContext context)
+ {
+ (AssemblyFlags value, AssemblyFlags mask) = context.Start.Text switch
+ {
+ "retargetable" => (AssemblyFlags.Retargetable, (AssemblyFlags)0),
+ "windowsruntime" => (AssemblyFlags.WindowsRuntime, (AssemblyFlags)0),
+ "noplatform" => (AssemblyFlags.NoPlatform, (AssemblyFlags)0),
+ "legacy library" => ((AssemblyFlags)0, (AssemblyFlags)0),
+ "cil" => (GetFlagForArch(ProcessorArchitecture.MSIL), AssemblyFlags.ArchitectureMask),
+ "x86" => (GetFlagForArch(ProcessorArchitecture.X86), AssemblyFlags.ArchitectureMask),
+ "amd64" => (GetFlagForArch(ProcessorArchitecture.Amd64), AssemblyFlags.ArchitectureMask),
+ "arm" => (GetFlagForArch(ProcessorArchitecture.Arm), AssemblyFlags.ArchitectureMask),
+ "arm64" => (GetFlagForArch((ProcessorArchitecture)6), AssemblyFlags.ArchitectureMask),
+ _ => ((AssemblyFlags)0, (AssemblyFlags)0)
+ };
+ context.Value = value;
+ context.Mask = mask;
+ }
+
+ internal AssemblyFlags AddAssemblyAttribute(
+ AssemblyFlags attributes,
+ AssemblyFlags value,
+ AssemblyFlags mask)
+ => mask == 0 ? attributes | value : (attributes & ~mask) | value;
+
+ internal AssemblyDefinitionValue CreateAssemblyDefinition(
+ AssemblyFlags attributes,
+ string name,
+ ImmutableArray declarations)
+ => new(attributes, name, declarations);
+
+ internal AssemblyDeclarationValue CreateAssemblyHashAlgorithmDeclaration(IToken value)
+ => new AssemblyHashAlgorithmDirectiveValue((AssemblyHashAlgorithm)ParseInt32(value));
+
+ internal AssemblyDeclarationValue CreateAssemblySecurityDeclaration(
+ SecurityDeclarationValue? value,
+ IToken location)
+ => new AssemblySecurityDirectiveValue(value, location);
+
+ internal AssemblyDeclarationValue CreateAssemblyPublicKeyDeclaration(
+ ImmutableArray value)
+ => new AssemblyPublicKeyDirectiveValue(value);
+
+ internal AssemblyDeclarationValue CreateAssemblyVersionDeclaration(
+ int? major,
+ int? minor,
+ int? build,
+ int? revision)
+ => new AssemblyVersionDirectiveValue(new(
+ major ?? 0,
+ minor ?? 0,
+ build ?? 0,
+ revision ?? 0));
+
+ internal AssemblyDeclarationValue CreateAssemblyLocaleDeclaration(string value)
+ => new AssemblyLocaleDirectiveValue(value);
+
+ internal AssemblyDeclarationValue CreateAssemblyLocaleDeclaration(
+ ImmutableArray value)
+ => new AssemblyLocaleDirectiveValue(Encoding.Unicode.GetString(value.AsSpan()));
+
+ internal AssemblyDeclarationValue CreateAssemblyCustomAttributeDeclaration(
+ CustomAttributeDeclarationValue? value,
+ IToken location)
+ => new AssemblyCustomAttributeDirectiveValue(value, location);
+
+ private static AssemblyFlags GetFlagForArch(ProcessorArchitecture architecture)
+ => (AssemblyFlags)((int)architecture << 4);
+
+ private static (ProcessorArchitecture Architecture, AssemblyFlags Flags) GetArchAndFlags(
+ AssemblyFlags flags)
+ {
+ ProcessorArchitecture architecture =
+ (ProcessorArchitecture)(((int)flags & 0xF0) >> 4);
+ return (architecture, flags & ~GetFlagForArch(architecture));
+ }
+
+ private void MaterializeAssemblyDefinition(AssemblyDefinitionValue definition)
+ {
+ string assemblyName = _options.AssemblyName ?? definition.Name;
+ _entityRegistry.Assembly ??= new EntityRegistry.AssemblyEntity(assemblyName);
+ EntityRegistry.AssemblyEntity assembly = _entityRegistry.Assembly;
+ (assembly.ProcessorArchitecture, assembly.Flags) =
+ GetArchAndFlags(definition.Attributes);
+
+ foreach (AssemblyDeclarationValue declaration in definition.Declarations)
+ {
+ switch (declaration)
+ {
+ case AssemblyHashAlgorithmDirectiveValue hashAlgorithm:
+ assembly.HashAlgorithm = hashAlgorithm.Value;
+ break;
+ case AssemblySecurityDirectiveValue security:
+ if (security.Value is { } securityValue &&
+ MaterializeSecurityDeclaration(securityValue, security.Location) is { } entity)
+ {
+ entity.Parent = assembly;
+ }
+ break;
+ default:
+ ApplyAssemblyOrReferenceDirective(assembly, declaration);
+ break;
+ }
+ }
+
+ if (_options.KeyFile is not null)
+ {
+ ApplyKeyFile(_options.KeyFile);
+ }
+ }
+
+ private void ApplyAssemblyOrReferenceDirective(
+ EntityRegistry.AssemblyOrRefEntity target,
+ AssemblyDeclarationValue declaration)
+ {
+ switch (declaration)
+ {
+ case AssemblyPublicKeyDirectiveValue publicKey:
+ // COMPAT: A reference's public key token wins regardless of declaration order.
+ if (target is not EntityRegistry.AssemblyReferenceEntity assemblyReference ||
+ assemblyReference.PublicKeyOrToken is null ||
+ assemblyReference.Flags.HasFlag(AssemblyFlags.PublicKey))
+ {
+ target.PublicKeyOrToken = CreateManifestBlob(publicKey.Value);
+ target.Flags |= AssemblyFlags.PublicKey;
+ }
+ break;
+ case AssemblyVersionDirectiveValue version:
+ target.Version = version.Value;
+ break;
+ case AssemblyLocaleDirectiveValue locale:
+ target.Culture = locale.Value;
+ break;
+ case AssemblyCustomAttributeDirectiveValue customAttribute:
+ if (MaterializeCustomAttributeDeclaration(
+ customAttribute.Value,
+ customAttribute.Location) is { } attribute)
+ {
+ attribute.Owner ??= target;
+ }
+ break;
+ }
+ }
+
+ private static BlobBuilder CreateManifestBlob(ImmutableArray value)
+ {
+ BlobBuilder blob = new(value.Length);
+ blob.WriteBytes(value);
+ return blob;
+ }
+
+ private void ApplyKeyFile(string keyFilePath)
+ {
+ if (_entityRegistry.Assembly is null)
+ {
+ return;
+ }
+
+ try
+ {
+ byte[] keyBytes = File.ReadAllBytes(keyFilePath);
+ BlobBuilder blob = new(keyBytes.Length);
+ blob.WriteBytes(keyBytes);
+ _entityRegistry.Assembly.PublicKeyOrToken = blob;
+ _entityRegistry.Assembly.Flags |= AssemblyFlags.PublicKey;
+ }
+ catch (Exception ex)
+ {
+ SourceText? firstDocument = _documents.Values.FirstOrDefault();
+ Location location = firstDocument is not null
+ ? new Location(new SourceSpan(0, 0), firstDocument)
+ : new Location(new SourceSpan(0, 0), new SourceText(string.Empty, keyFilePath));
+ _diagnostics.Add(new Diagnostic(
+ DiagnosticIds.KeyFileError,
+ DiagnosticSeverity.Error,
+ $"Failed to read key file '{keyFilePath}': {ex.Message}",
+ location));
+ }
+ }
+
+ internal void MaterializeAssemblyDefinition(CILParser.AssemblyBlockContext context)
+ {
+ if (context.Value is AssemblyDefinitionValue definition)
+ {
+ MaterializeAssemblyDefinition(definition);
+ }
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.ExportedTypes.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.ExportedTypes.cs
new file mode 100644
index 00000000000000..67f778d26b1477
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.ExportedTypes.cs
@@ -0,0 +1,302 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Reflection;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal void SetExportedTypeAttribute(CILParser.ExptAttrContext context)
+ {
+ string attribute = context.Start.Text == "nested"
+ ? $"nested{context.Stop?.Text}"
+ : context.Start.Text;
+ (TypeAttributes value, TypeAttributes mask) = attribute switch
+ {
+ "private" => (TypeAttributes.NotPublic, TypeAttributes.VisibilityMask),
+ "public" => (TypeAttributes.Public, TypeAttributes.VisibilityMask),
+ "forwarder" => (TypeAttributes.Forwarder, (TypeAttributes)0),
+ "nestedpublic" => (TypeAttributes.NestedPublic, TypeAttributes.VisibilityMask),
+ "nestedprivate" => (TypeAttributes.NestedPrivate, TypeAttributes.VisibilityMask),
+ "nestedfamily" => (TypeAttributes.NestedFamily, TypeAttributes.VisibilityMask),
+ "nestedassembly" => (TypeAttributes.NestedAssembly, TypeAttributes.VisibilityMask),
+ "nestedfamandassem" => (TypeAttributes.NestedFamANDAssem, TypeAttributes.VisibilityMask),
+ "nestedfamorassem" => (TypeAttributes.NestedFamORAssem, TypeAttributes.VisibilityMask),
+ _ => ((TypeAttributes)0, (TypeAttributes)0)
+ };
+ context.Value = value;
+ context.Mask = mask;
+ }
+
+ internal TypeAttributes AddExportedTypeAttribute(
+ TypeAttributes attributes,
+ TypeAttributes value,
+ TypeAttributes mask)
+ => mask == 0 ? attributes | value : (attributes & ~mask) | value;
+
+ internal ExportedTypeHeaderValue CreateExportedTypeHeader(
+ TypeAttributes attributes,
+ string name,
+ IToken location)
+ => new ExportedTypeHeaderValue(true, attributes, name, location);
+
+ internal ExportedTypeValue CreateExportedType(
+ ExportedTypeHeaderValue header,
+ ImmutableArray declarations)
+ => new ExportedTypeValue(
+ header,
+ declarations);
+
+ internal ExportedTypeDeclarationValue CreateExportedTypeFileDeclaration(
+ string name,
+ IToken location)
+ => new ExportedTypeFileDirectiveValue(name, location);
+
+ internal ExportedTypeDeclarationValue CreateNestedExportedTypeDeclaration(
+ TypeName name,
+ IToken location)
+ => new NestedExportedTypeDirectiveValue(name, location);
+
+ internal ExportedTypeDeclarationValue CreateExportedTypeAssemblyDeclaration(
+ string name,
+ IToken location)
+ => new ExportedTypeAssemblyDirectiveValue(name, location);
+
+ internal ExportedTypeDeclarationValue CreateExportedTypeMetadataTokenDeclaration(
+ int token,
+ IToken location)
+ => new ExportedTypeMetadataTokenDirectiveValue(token, location);
+
+ internal ExportedTypeDeclarationValue CreateExportedTypeDefinitionIdDeclaration(
+ IToken value)
+ => new ExportedTypeDefinitionIdDirectiveValue(ParseInt32(value));
+
+ internal ExportedTypeDeclarationValue CreateExportedTypeCustomAttributeDeclaration(
+ CustomAttributeDeclarationValue? value,
+ IToken location)
+ => new ExportedTypeCustomAttributeDirectiveValue(value, location);
+
+ private void MaterializeExportedType(ExportedTypeValue value)
+ {
+ ExportedTypeHeaderValue header = value.Header;
+ if (!header.IsValid ||
+ header.Location is not IToken location)
+ {
+ return;
+ }
+
+ (string typeNamespace, string name) =
+ NameHelpers.SplitDottedNameToNamespaceAndName(header.Name);
+ (
+ EntityRegistry.EntityBase? implementation,
+ int typeDefinitionId,
+ ImmutableArray customAttributes
+ ) = MaterializeExportedTypeDeclarations(value.Declarations);
+
+ if (implementation is null)
+ {
+ ReportWarning(
+ DiagnosticIds.MissingExportedTypeImplementation,
+ string.Format(
+ DiagnosticMessageTemplates.MissingExportedTypeImplementation,
+ header.Name),
+ location);
+ return;
+ }
+
+ EntityRegistry.ExportedTypeEntity exportedType =
+ _entityRegistry.GetOrCreateExportedType(
+ implementation,
+ typeNamespace,
+ name,
+ entity =>
+ {
+ entity.Attributes = header.Attributes;
+ entity.TypeDefinitionId = typeDefinitionId;
+ });
+ foreach (EntityRegistry.CustomAttributeEntity attribute in customAttributes)
+ {
+ attribute.Owner ??= exportedType;
+ }
+ }
+
+ private (
+ EntityRegistry.EntityBase? Implementation,
+ int TypeDefinitionId,
+ ImmutableArray CustomAttributes
+ ) MaterializeExportedTypeDeclarations(
+ ImmutableArray declarations)
+ {
+ EntityRegistry.EntityBase? implementation = null;
+ int typeDefinitionId = 0;
+ ImmutableArray.Builder customAttributes =
+ ImmutableArray.CreateBuilder();
+
+ foreach (ExportedTypeDeclarationValue declaration in declarations)
+ {
+ switch (declaration)
+ {
+ case ExportedTypeCustomAttributeDirectiveValue customAttribute:
+ if (MaterializeCustomAttributeDeclaration(
+ customAttribute.Value,
+ customAttribute.Location) is { } attribute)
+ {
+ customAttributes.Add(attribute);
+ }
+ break;
+ case ExportedTypeMetadataTokenDirectiveValue metadataToken:
+ EntityRegistry.EntityBase entity = ResolveMetadataToken(metadataToken.Token);
+ if (entity is EntityRegistry.FakeTypeEntity)
+ {
+ ReportError(
+ DiagnosticIds.InvalidMetadataToken,
+ DiagnosticMessageTemplates.InvalidMetadataToken,
+ metadataToken.Location);
+ }
+ implementation = ResolveBetterExportedTypeImplementation(
+ implementation,
+ entity);
+ break;
+ case ExportedTypeFileDirectiveValue file:
+ implementation = _entityRegistry.FindFile(file.Name);
+ if (implementation is null)
+ {
+ ReportError(
+ DiagnosticIds.FileNotFound,
+ string.Format(DiagnosticMessageTemplates.FileNotFound, file.Name),
+ file.Location);
+ }
+ break;
+ case ExportedTypeAssemblyDirectiveValue assembly:
+ implementation = _entityRegistry.FindAssemblyReference(assembly.Name);
+ if (implementation is null)
+ {
+ ReportError(
+ DiagnosticIds.AssemblyNotFound,
+ string.Format(
+ DiagnosticMessageTemplates.AssemblyNotFound,
+ assembly.Name),
+ assembly.Location);
+ }
+ break;
+ case NestedExportedTypeDirectiveValue nested:
+ EntityRegistry.ExportedTypeEntity? containingType =
+ ResolveExportedType(nested.Name, nested.Location);
+ if (containingType is null)
+ {
+ ReportError(
+ DiagnosticIds.ExportedTypeNotFound,
+ string.Format(
+ DiagnosticMessageTemplates.ExportedTypeNotFound,
+ GetExportedTypeDisplayName(nested.Name)),
+ nested.Location);
+ }
+ else
+ {
+ implementation = ResolveBetterExportedTypeImplementation(
+ implementation,
+ containingType);
+ }
+ break;
+ case ExportedTypeDefinitionIdDirectiveValue definitionId:
+ typeDefinitionId = definitionId.Value;
+ break;
+ }
+ }
+
+ return (implementation, typeDefinitionId, customAttributes.ToImmutable());
+ }
+
+ private static EntityRegistry.EntityBase? ResolveBetterExportedTypeImplementation(
+ EntityRegistry.EntityBase? current,
+ EntityRegistry.EntityBase? candidate)
+ {
+ if (candidate is null)
+ {
+ return current;
+ }
+
+ if (current is null)
+ {
+ return candidate;
+ }
+
+ return GetImplementationPriority(candidate) >= GetImplementationPriority(current)
+ ? candidate
+ : current;
+
+ static int GetImplementationPriority(EntityRegistry.EntityBase entity)
+ => entity switch
+ {
+ EntityRegistry.FileEntity => 4,
+ EntityRegistry.AssemblyReferenceEntity => 3,
+ EntityRegistry.ExportedTypeEntity => 2,
+ _ => 1
+ };
+ }
+
+ private EntityRegistry.ExportedTypeEntity? ResolveExportedType(
+ TypeName typeName,
+ IToken location)
+ {
+ Stack containingTypes = new();
+ for (TypeName? containingType = typeName;
+ containingType is not null;
+ containingType = containingType.ContainingTypeName)
+ {
+ containingTypes.Push(containingType);
+ }
+
+ EntityRegistry.ExportedTypeEntity? exportedType = null;
+ while (containingTypes.Count != 0)
+ {
+ TypeName containingType = containingTypes.Pop();
+ (string typeNamespace, string name) =
+ NameHelpers.SplitDottedNameToNamespaceAndName(containingType.DottedName);
+ exportedType = _entityRegistry.FindExportedType(
+ exportedType,
+ typeNamespace,
+ name);
+ if (exportedType is null)
+ {
+ ReportError(
+ DiagnosticIds.ExportedTypeNotFound,
+ string.Format(
+ DiagnosticMessageTemplates.ExportedTypeNotFound,
+ containingType.DottedName),
+ location);
+ return null;
+ }
+ }
+
+ return exportedType;
+ }
+
+ private static string GetExportedTypeDisplayName(TypeName typeName)
+ {
+ Stack names = new();
+ for (TypeName? current = typeName;
+ current is not null;
+ current = current.ContainingTypeName)
+ {
+ names.Push(current.DottedName);
+ }
+
+ return string.Join("/", names);
+ }
+
+ internal void MaterializeExportedType(CILParser.ExptypeBlockContext context)
+ {
+ if (context.Value is ExportedTypeValue value)
+ {
+ MaterializeExportedType(value);
+ }
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Files.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Files.cs
new file mode 100644
index 00000000000000..822528ae5e6da8
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Files.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal bool ParseFileAttribute(IToken token)
+ => token.Text != "nometadata";
+
+ internal bool ParseFileEntry(IToken token)
+ {
+ Debug.Assert(token.Text == ".entrypoint");
+ return true;
+ }
+
+ internal void AddFileAttribute(
+ CILParser.FileDeclarationBuilder builder,
+ bool hasMetadata)
+ => builder.HasMetadata &= hasMetadata;
+
+ internal void SetFileName(CILParser.FileDeclarationBuilder builder, string name)
+ => builder.Name = name;
+
+ internal void AddFileEntry(
+ CILParser.FileDeclarationBuilder builder,
+ bool isEntryPoint)
+ => builder.IsEntryPoint |= isEntryPoint;
+
+ internal void SetFileHash(
+ CILParser.FileDeclarationBuilder builder,
+ ImmutableArray hash)
+ => builder.Hash = hash;
+
+ internal void EndFileDeclaration(
+ CILParser.FileDeclContext context,
+ CILParser.FileDeclarationBuilder builder,
+ int initialSyntaxErrorCount)
+ {
+ context.HasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ context.Value = context.HasSyntaxError
+ ? null
+ : new FileDeclarationValue(
+ builder.Name,
+ builder.HasMetadata,
+ builder.IsEntryPoint,
+ builder.Hash);
+ }
+
+ private EntityRegistry.FileEntity MaterializeFileDeclaration(FileDeclarationValue declaration)
+ {
+ BlobBuilder? hash = declaration.Hash is { } value ? CreateManifestBlob(value) : null;
+ EntityRegistry.FileEntity entity =
+ _entityRegistry.GetOrCreateFile(declaration.Name, declaration.HasMetadata, hash);
+ if (declaration.IsEntryPoint)
+ {
+ _entityRegistry.EntryPoint = entity;
+ }
+
+ return entity;
+ }
+
+ internal EntityRegistry.FileEntity MaterializeFileDeclaration(
+ CILParser.FileDeclContext context)
+ {
+ Debug.Assert(context.Value is not null);
+ FileDeclarationValue declaration =
+ context.Value ?? new(string.Empty, HasMetadata: true, IsEntryPoint: false, Hash: null);
+ return MaterializeFileDeclaration(declaration);
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.References.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.References.cs
new file mode 100644
index 00000000000000..3e9ecb258284ef
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.References.cs
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Reflection;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal AssemblyReferenceHeaderValue CreateAssemblyReferenceHeader(
+ AssemblyFlags attributes,
+ string name,
+ string alias)
+ => new AssemblyReferenceHeaderValue(true, attributes, name, alias);
+
+ internal AssemblyReferenceValue CreateAssemblyReference(
+ AssemblyReferenceHeaderValue header,
+ ImmutableArray declarations)
+ => new AssemblyReferenceValue(
+ header,
+ declarations);
+
+ internal AssemblyDeclarationValue CreateAssemblyReferenceHashDeclaration(
+ ImmutableArray value)
+ => new AssemblyReferenceHashDirectiveValue(value);
+
+ internal AssemblyDeclarationValue CreateAssemblyReferencePublicKeyTokenDeclaration(
+ ImmutableArray value)
+ => new AssemblyReferencePublicKeyTokenDirectiveValue(value);
+
+ internal AssemblyDeclarationValue? CreateAssemblyReferenceAutoDeclaration() => null;
+
+ private EntityRegistry.AssemblyReferenceEntity MaterializeAssemblyReferenceHeader(
+ AssemblyReferenceHeaderValue header)
+ {
+ (ProcessorArchitecture architecture, AssemblyFlags flags) =
+ GetArchAndFlags(header.Attributes);
+ return _entityRegistry.GetOrCreateAssemblyReference(
+ header.Alias,
+ assemblyReference =>
+ {
+ assemblyReference.Name = header.Name;
+ assemblyReference.Flags = flags;
+ assemblyReference.ProcessorArchitecture = architecture;
+ });
+ }
+
+ private void MaterializeAssemblyReference(AssemblyReferenceValue reference)
+ {
+ if (!reference.Header.IsValid)
+ {
+ return;
+ }
+
+ EntityRegistry.AssemblyReferenceEntity entity =
+ MaterializeAssemblyReferenceHeader(reference.Header);
+ foreach (AssemblyDeclarationValue declaration in reference.Declarations)
+ {
+ switch (declaration)
+ {
+ case AssemblyReferenceHashDirectiveValue hash:
+ entity.Hash = CreateManifestBlob(hash.Value);
+ break;
+ case AssemblyReferencePublicKeyTokenDirectiveValue publicKeyToken:
+ entity.PublicKeyOrToken = CreateManifestBlob(publicKeyToken.Value);
+ entity.Flags &= ~AssemblyFlags.PublicKey;
+ break;
+ default:
+ ApplyAssemblyOrReferenceDirective(entity, declaration);
+ break;
+ }
+ }
+ }
+
+ internal void MaterializeAssemblyReference(CILParser.AssemblyRefBlockContext context)
+ {
+ if (context.Value is AssemblyReferenceValue reference)
+ {
+ MaterializeAssemblyReference(reference);
+ }
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Resources.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Resources.cs
new file mode 100644
index 00000000000000..33f95c6fa066a6
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Resources.cs
@@ -0,0 +1,158 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Reflection;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal ManifestResourceAttributes AddManifestResourceAttribute(
+ ManifestResourceAttributes attributes,
+ ManifestResourceAttributes value)
+ => attributes | value;
+
+ internal ManifestResourceAttributes ParseManifestResourceAttribute(IToken token)
+ => token.Text switch
+ {
+ "public" => ManifestResourceAttributes.Public,
+ "private" => ManifestResourceAttributes.Private,
+ _ => 0
+ };
+
+ internal ManifestResourceHeaderValue CreateManifestResourceHeader(
+ ManifestResourceAttributes attributes,
+ string name,
+ string alias,
+ IToken location)
+ => new ManifestResourceHeaderValue(true, attributes, name, alias, location);
+
+ internal ManifestResourceValue CreateManifestResource(
+ ManifestResourceHeaderValue header,
+ ImmutableArray declarations)
+ => new ManifestResourceValue(
+ header,
+ declarations);
+
+ internal ManifestResourceDeclarationValue CreateManifestResourceFileDeclaration(
+ string name,
+ IToken offset,
+ IToken location)
+ => new ManifestResourceFileDirectiveValue(name, (uint)ParseInt32(offset), location);
+
+ internal ManifestResourceDeclarationValue CreateManifestResourceAssemblyDeclaration(
+ string name)
+ => new ManifestResourceAssemblyDirectiveValue(name);
+
+ internal ManifestResourceDeclarationValue CreateManifestResourceCustomAttributeDeclaration(
+ CustomAttributeDeclarationValue? value,
+ IToken location)
+ => new ManifestResourceCustomAttributeDirectiveValue(value, location);
+
+ private void MaterializeManifestResource(ManifestResourceValue value)
+ {
+ ManifestResourceHeaderValue header = value.Header;
+ if (!header.IsValid ||
+ header.Location is not IToken location)
+ {
+ return;
+ }
+
+ (
+ EntityRegistry.EntityBase? implementation,
+ uint offset,
+ ImmutableArray customAttributes
+ ) = MaterializeManifestResourceDeclarations(value.Declarations);
+
+ if (implementation is null)
+ {
+ offset = (uint)_manifestResources.Count;
+ byte[]? resourceData = _resourceLocator(header.Alias);
+ if (resourceData is null)
+ {
+ ReportError(
+ DiagnosticIds.FileNotFound,
+ string.Format(
+ DiagnosticMessageTemplates.FileNotFound,
+ header.Alias),
+ location);
+ }
+ else
+ {
+ _manifestResources.WriteInt32(resourceData.Length);
+ _manifestResources.WriteBytes(resourceData);
+ }
+ }
+
+ EntityRegistry.ManifestResourceEntity resource =
+ _entityRegistry.CreateManifestResource(header.Name, offset);
+ resource.Attributes = header.Attributes;
+ resource.Implementation = implementation;
+ foreach (EntityRegistry.CustomAttributeEntity customAttribute in customAttributes)
+ {
+ customAttribute.Owner ??= resource;
+ }
+ }
+
+ private (
+ EntityRegistry.EntityBase? Implementation,
+ uint Offset,
+ ImmutableArray CustomAttributes
+ ) MaterializeManifestResourceDeclarations(
+ ImmutableArray declarations)
+ {
+ EntityRegistry.EntityBase? implementation = null;
+ uint offset = 0;
+ ImmutableArray.Builder customAttributes =
+ ImmutableArray.CreateBuilder();
+
+ foreach (ManifestResourceDeclarationValue declaration in declarations)
+ {
+ switch (declaration)
+ {
+ case ManifestResourceCustomAttributeDirectiveValue customAttribute:
+ if (MaterializeCustomAttributeDeclaration(
+ customAttribute.Value,
+ customAttribute.Location) is { } attribute)
+ {
+ customAttributes.Add(attribute);
+ }
+ break;
+ case ManifestResourceFileDirectiveValue file
+ when implementation is not EntityRegistry.AssemblyReferenceEntity:
+ EntityRegistry.FileEntity? fileEntity = _entityRegistry.FindFile(file.Name);
+ if (fileEntity is null)
+ {
+ ReportError(
+ DiagnosticIds.FileNotFound,
+ string.Format(DiagnosticMessageTemplates.FileNotFound, file.Name),
+ file.Location);
+ }
+ else
+ {
+ implementation = fileEntity;
+ offset = file.Offset;
+ }
+ break;
+ case ManifestResourceAssemblyDirectiveValue assembly:
+ implementation =
+ _entityRegistry.GetOrCreateAssemblyReference(assembly.Name, _ => { });
+ break;
+ }
+ }
+
+ return (implementation, offset, customAttributes.ToImmutable());
+ }
+
+ internal void MaterializeManifestResource(CILParser.ManifestResBlockContext context)
+ {
+ if (context.Value is ManifestResourceValue value)
+ {
+ MaterializeManifestResource(value);
+ }
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Typedefs.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Typedefs.cs
new file mode 100644
index 00000000000000..2507487a15cfe0
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.Typedefs.cs
@@ -0,0 +1,134 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ private readonly Dictionary _typedefs = new();
+
+ internal TypedefDeclarationValue CreateTypeSignatureTypedef(
+ TypeValue type,
+ string alias)
+ => new TypeSignatureTypedefDeclarationValue(type, alias);
+
+ internal TypedefDeclarationValue CreateClassTypedef(
+ ClassNameValue type,
+ string alias)
+ => new ClassTypedefDeclarationValue(type, alias);
+
+ internal TypedefDeclarationValue CreateMemberTypedef(
+ MemberReferenceValue member,
+ string alias)
+ => new MemberTypedefDeclarationValue(member, alias);
+
+ internal TypedefDeclarationValue CreateCustomAttributeTypedefDeclaration(
+ CustomAttributeDescriptorValue attribute,
+ IToken location,
+ string alias)
+ => new CustomAttributeTypedefDeclarationValue(
+ attribute,
+ location,
+ alias);
+
+ private void MaterializeTypedef(TypedefDeclarationValue declaration)
+ {
+ switch (declaration)
+ {
+ case TypeSignatureTypedefDeclarationValue type:
+ BlobBuilder typeBlob = MaterializeType(type.Type);
+ BlobBuilder copy = new(typeBlob.Count);
+ typeBlob.WriteContentTo(copy);
+ _typedefs[type.Alias] = new TypedefEntry.TypeBlob(copy);
+ break;
+ case ClassTypedefDeclarationValue type:
+ _typedefs[type.Alias] = new TypedefEntry.Type(ResolveClassName(type.Type));
+ break;
+ case MemberTypedefDeclarationValue member:
+ _typedefs[member.Alias] =
+ new TypedefEntry.Member(MaterializeMemberReference(member.Member));
+ break;
+ case CustomAttributeTypedefDeclarationValue customAttribute:
+ EntityRegistry.CustomAttributeEntity attribute =
+ MaterializeCustomAttribute(customAttribute.Attribute);
+ EntityRegistry.EntityBase? owner = attribute.Owner;
+ attribute.Owner = null;
+ _typedefs[customAttribute.Alias] =
+ new TypedefEntry.CustomAttribute(attribute.Constructor, attribute.Value, owner);
+ break;
+ }
+ }
+
+ internal void MaterializeTypedef(CILParser.TypedefDeclContext context)
+ {
+ if (context.Value is TypedefDeclarationValue declaration)
+ {
+ MaterializeTypedef(declaration);
+ }
+ }
+
+ private EntityRegistry.TypeEntity? TryResolveTypedefAsType(string alias)
+ {
+ if (_typedefs.TryGetValue(alias, out TypedefEntry? entry) &&
+ entry is TypedefEntry.Type type)
+ {
+ return type.Entity;
+ }
+
+ return null;
+ }
+
+ private BlobBuilder? TryResolveTypedefAsTypeBlob(string alias)
+ {
+ if (!_typedefs.TryGetValue(alias, out TypedefEntry? entry))
+ {
+ return null;
+ }
+
+ if (entry is TypedefEntry.TypeBlob blob)
+ {
+ return blob.Blob;
+ }
+
+ if (entry is TypedefEntry.Type type)
+ {
+ BlobBuilder result = new(5);
+ result.WriteByte((byte)SignatureTypeKind.Class);
+ result.WriteTypeEntity(type.Entity);
+ return result;
+ }
+
+ return null;
+ }
+
+ private EntityRegistry.EntityBase? TryResolveTypedefAsMember(string alias)
+ {
+ if (_typedefs.TryGetValue(alias, out TypedefEntry? entry) &&
+ entry is TypedefEntry.Member member)
+ {
+ return member.Entity;
+ }
+
+ return null;
+ }
+
+ private (
+ EntityRegistry.EntityBase Constructor,
+ BlobBuilder Value,
+ EntityRegistry.EntityBase? Owner)?
+ TryResolveTypedefAsCustomAttribute(string alias)
+ {
+ if (_typedefs.TryGetValue(alias, out TypedefEntry? entry) &&
+ entry is TypedefEntry.CustomAttribute customAttribute)
+ {
+ return (customAttribute.Constructor, customAttribute.Value, customAttribute.Owner);
+ }
+
+ return null;
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.VTable.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.VTable.cs
new file mode 100644
index 00000000000000..dc0fae847bd2a6
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Manifest.VTable.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal ushort ParseVTableFixupAttribute(IToken token)
+ => token.Text switch
+ {
+ "int32" => VTableFixupSupport.COR_VTABLE_32BIT,
+ "int64" => VTableFixupSupport.COR_VTABLE_64BIT,
+ "fromunmanaged" => VTableFixupSupport.COR_VTABLE_FROM_UNMANAGED,
+ "callmostderived" => VTableFixupSupport.COR_VTABLE_CALL_MOST_DERIVED,
+ "retainappdomain" =>
+ VTableFixupSupport.COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN,
+ _ => 0
+ };
+
+ internal ushort AddVTableFixupAttribute(ushort attributes, ushort value)
+ => (ushort)(attributes | value);
+
+ internal ushort CompleteVTableFixupAttributes(ushort attributes) => attributes;
+
+ internal VTableFixupValue CreateVTableFixup(
+ IToken slotCount,
+ ushort flags,
+ IToken dataLabel)
+ {
+ int diagnosticCount = _diagnostics.Count;
+ int count = ParseInt32(slotCount);
+ bool hasValidSlotCount = _diagnostics.Count == diagnosticCount;
+ if (hasValidSlotCount && (uint)count > ushort.MaxValue)
+ {
+ ReportError(
+ DiagnosticIds.InvalidVTableSlotCount,
+ string.Format(
+ DiagnosticMessageTemplates.InvalidVTableSlotCount,
+ count,
+ ushort.MaxValue),
+ slotCount);
+ hasValidSlotCount = false;
+ }
+
+ return new VTableFixupValue(
+ count,
+ flags,
+ ParseIdentifier(dataLabel),
+ hasValidSlotCount);
+ }
+
+ internal RawVTableValue CreateRawVTable(ImmutableArray value)
+ => new(value);
+
+ internal void MaterializeVTable(CILParser.VtableDeclContext context)
+ => ReportError(
+ DiagnosticIds.UnsupportedVTableDeclaration,
+ DiagnosticMessageTemplates.UnsupportedVTableDeclaration,
+ context);
+
+ internal void MaterializeVTableFixup(CILParser.VtfixupDeclContext context)
+ {
+ if (context.Value is VTableFixupValue value)
+ {
+ _vtableFixups.Add(new VTableFixupDeclaration(
+ new VTableFixupSupport.VTableFixupEntry(
+ value.SlotCount,
+ value.Flags,
+ value.DataLabel),
+ context,
+ value.HasValidSlotCount));
+ }
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Marshalling.Actions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Marshalling.Actions.cs
new file mode 100644
index 00000000000000..4eb060b9e92bd5
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Marshalling.Actions.cs
@@ -0,0 +1,514 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Runtime.InteropServices;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ private const byte NativeTypeVoid = 0x01;
+ private const byte NativeTypeSysChar = 0x0D;
+ private const byte NativeTypeVariant = 0x0E;
+ private const byte NativeTypePointer = 0x10;
+ private const byte NativeTypeDecimal = 0x11;
+ private const byte NativeTypeDate = 0x12;
+ private const byte NativeTypeObjectReference = 0x18;
+ private const byte NativeTypeNestedStruct = 0x21;
+ private const byte NativeTypeMax = 0x50;
+
+ internal MarshallingDescriptorValue CreateEmptyMarshallingDescriptor()
+ => MarshallingDescriptorValue.Empty;
+
+ internal MarshallingDescriptorValue CompleteMarshalClause(
+ MarshallingDescriptorValue value)
+ => value;
+
+ internal void SetMarshalBlobNativeType(
+ CILParser.MarshalBlobBuilder builder,
+ NativeTypeValue value)
+ => builder.NativeType = value;
+
+ internal void AddMarshalBlobByte(CILParser.MarshalBlobBuilder builder, byte value)
+ => (builder.RawBytes ??= new BlobBuilder()).WriteByte(value);
+
+ internal MarshallingDescriptorValue CreateMarshallingDescriptor(
+ CILParser.MarshalBlobBuilder builder)
+ => new(builder.RawBytes, builder.NativeType);
+
+ internal void SetNativeTypeElement(
+ CILParser.NativeTypeBuilder builder,
+ NativeTypeElementValue value)
+ => builder.Element = value;
+
+ internal void AddNativeTypeArrayPointerInfo(
+ CILParser.NativeTypeBuilder builder,
+ NativeTypeArrayPointerInfoValue value)
+ => (builder.ArrayPointerInfo ??= new List())
+ .Add(value);
+
+ internal NativeTypeValue CreateNativeType(
+ IToken token,
+ CILParser.NativeTypeBuilder builder)
+ => new(
+ token,
+ builder.Element,
+ builder.ArrayPointerInfo?.ToImmutableArray() ?? []);
+
+ internal NativeTypeArrayPointerInfoValue CreatePointerNativeType()
+ => new NativeTypeArrayPointerInfoValue(NativeTypeArrayPointerInfoKind.Pointer);
+
+ internal NativeTypeArrayPointerInfoValue CreatePointerArrayTypeNoSizeData()
+ => new NativeTypeArrayPointerInfoValue(NativeTypeArrayPointerInfoKind.ArrayNoSizeData);
+
+ internal NativeTypeArrayPointerInfoValue CreatePointerArrayTypeSize(IToken size)
+ => new NativeTypeArrayPointerInfoValue(NativeTypeArrayPointerInfoKind.ArraySize, Size: size);
+
+ internal NativeTypeArrayPointerInfoValue CreatePointerArrayTypeSizeParamIndex(
+ IToken size,
+ IToken parameterIndex)
+ => new NativeTypeArrayPointerInfoValue(
+ NativeTypeArrayPointerInfoKind.ArraySizeParamIndex,
+ size,
+ parameterIndex);
+
+ internal NativeTypeArrayPointerInfoValue CreatePointerArrayTypeParamIndex(
+ IToken parameterIndex)
+ => new NativeTypeArrayPointerInfoValue(
+ NativeTypeArrayPointerInfoKind.ArrayParamIndex,
+ ParameterIndex: parameterIndex);
+
+ internal NativeTypeElementValue CreateEmptyNativeType()
+ => EmptyNativeTypeElementValue.Instance;
+
+ internal NativeTypeElementValue CreateDeprecatedCustomMarshallerNativeType(
+ CILParser.NativeTypeElementContext context,
+ string guid,
+ string nativeTypeName,
+ string marshallerType,
+ string cookie)
+ => new CustomMarshallerNativeTypeElementValue(
+ context.Start,
+ guid,
+ nativeTypeName,
+ marshallerType,
+ cookie);
+
+ internal NativeTypeElementValue CreateCustomMarshallerNativeType(
+ string marshallerType,
+ string cookie)
+ => new CustomMarshallerNativeTypeElementValue(
+ null,
+ null,
+ null,
+ marshallerType,
+ cookie);
+
+ internal NativeTypeElementValue CreateFixedSysStringNativeType(IToken size)
+ => new FixedSysStringNativeTypeElementValue(size);
+
+ internal NativeTypeElementValue CreateFixedArrayNativeType(
+ IToken size,
+ NativeTypeValue element)
+ => new FixedArrayNativeTypeElementValue(size, element);
+
+ internal NativeTypeElementValue CreateDeprecatedNativeType(
+ CILParser.NativeTypeElementContext context,
+ IToken nativeType)
+ => new DeprecatedNativeTypeElementValue(context.Start, nativeType.Type);
+
+ internal NativeTypeElementValue CreateSimpleNativeType(IToken nativeType)
+ => new SimpleNativeTypeElementValue(nativeType.Type);
+
+ internal NativeTypeElementValue CreateIidNativeType(
+ IToken nativeType,
+ IidParamIndexValue index)
+ => new IidNativeTypeElementValue(nativeType.Type, index);
+
+ internal NativeTypeElementValue CreateSafeArrayNativeType(
+ VariantTypeValue variantType,
+ string? userDefinedType)
+ => new SafeArrayNativeTypeElementValue(variantType, userDefinedType);
+
+ internal NativeTypeElementValue CreateUnsignedNativeType(IToken nativeType)
+ => new UnsignedNativeTypeElementValue(nativeType.Type);
+
+ internal NativeTypeElementValue CreateNestedStructNativeType(
+ CILParser.NativeTypeElementContext context)
+ => new NestedStructNativeTypeElementValue(context.Start);
+
+ internal NativeTypeElementValue CreateAnsiBstrNativeType()
+ => AnsiBstrNativeTypeElementValue.Instance;
+
+ internal NativeTypeElementValue CreateVariantBoolNativeType()
+ => VariantBoolNativeTypeElementValue.Instance;
+
+ internal NativeTypeElementValue CreateNativeTypeTypedef(
+ CILParser.NativeTypeElementContext context,
+ string alias)
+ => new NativeTypeTypedefValue(context.Start, alias);
+
+ internal IidParamIndexValue GetIidParamIndex(IToken index)
+ => new(index);
+
+ internal void SetVariantTypeElement(
+ CILParser.VariantTypeBuilder builder,
+ VariantTypeElementValue value)
+ => builder.Element = value;
+
+ internal void AddVariantTypeModifier(
+ CILParser.VariantTypeBuilder builder,
+ IToken modifier)
+ {
+ builder.Modifiers |= modifier.Type switch
+ {
+ CILParser.ARRAY_TYPE_NO_BOUNDS => VarEnum.VT_ARRAY,
+ CILParser.VECTOR => VarEnum.VT_VECTOR,
+ CILParser.REF => VarEnum.VT_BYREF,
+ _ => throw new UnreachableException()
+ };
+ }
+
+ internal VariantTypeValue CreateVariantType(CILParser.VariantTypeBuilder builder)
+ => new(builder.Element, builder.Modifiers);
+
+ internal VariantTypeElementValue GetVariantTypeElement(IToken variantType)
+ => new VariantTypeElementValue(variantType.Type);
+
+ private BlobBuilder MaterializeMarshallingDescriptor(MarshallingDescriptorValue? value)
+ {
+ if (value?.RawBytes is BlobBuilder rawBytes)
+ {
+ return rawBytes;
+ }
+
+ return MaterializeNativeType(value?.NativeType ?? NativeTypeValue.Empty);
+ }
+
+ private BlobBuilder MaterializeNativeType(NativeTypeValue value)
+ {
+ if (value.Element is null)
+ {
+ return new BlobBuilder();
+ }
+
+ BlobBuilder element = MaterializeNativeTypeElement(value.Element);
+ if (value.ArrayPointerInfo.IsDefaultOrEmpty)
+ {
+ return element;
+ }
+
+ BlobBuilder prefix = new(value.ArrayPointerInfo.Length);
+ BlobBuilder suffix = new();
+
+ for (int i = value.ArrayPointerInfo.Length - 1; i >= 0; i--)
+ {
+ NativeTypeArrayPointerInfoValue info = value.ArrayPointerInfo[i];
+ if (info.Kind == NativeTypeArrayPointerInfoKind.Pointer)
+ {
+ if (value.Token is IToken token)
+ {
+ ReportWarning(
+ DiagnosticIds.DeprecatedNativeType,
+ string.Format(DiagnosticMessageTemplates.DeprecatedNativeType, "pointer in array"),
+ token);
+ }
+ prefix.WriteByte(NativeTypePointer);
+ }
+ else
+ {
+ prefix.WriteByte((byte)UnmanagedType.LPArray);
+ if (element.Count == 0)
+ {
+ element.WriteByte(NativeTypeMax);
+ }
+ }
+ }
+
+ foreach (NativeTypeArrayPointerInfoValue info in value.ArrayPointerInfo)
+ {
+ switch (info.Kind)
+ {
+ case NativeTypeArrayPointerInfoKind.ArraySize:
+ suffix.WriteCompressedInteger(0);
+ suffix.WriteCompressedInteger(ParseMarshallingInt32(info.Size));
+ suffix.WriteCompressedInteger(0);
+ break;
+ case NativeTypeArrayPointerInfoKind.ArraySizeParamIndex:
+ suffix.WriteCompressedInteger(ParseMarshallingInt32(info.ParameterIndex));
+ suffix.WriteCompressedInteger(ParseMarshallingInt32(info.Size));
+ suffix.WriteCompressedInteger(1);
+ break;
+ case NativeTypeArrayPointerInfoKind.ArrayParamIndex:
+ suffix.WriteCompressedInteger(ParseMarshallingInt32(info.ParameterIndex));
+ break;
+ }
+ }
+
+ prefix.LinkSuffix(element);
+ prefix.LinkSuffix(suffix);
+ return prefix;
+ }
+
+ private BlobBuilder MaterializeNativeTypeElement(NativeTypeElementValue value)
+ {
+ switch (value)
+ {
+ case EmptyNativeTypeElementValue:
+ return new BlobBuilder();
+ case CustomMarshallerNativeTypeElementValue customMarshaller:
+ return MaterializeCustomMarshallerNativeType(customMarshaller);
+ case FixedSysStringNativeTypeElementValue fixedSysString:
+ {
+ BlobBuilder blob = CreateNativeTypeBlob(UnmanagedType.ByValTStr);
+ blob.WriteCompressedInteger(ParseInt32(fixedSysString.Size));
+ return blob;
+ }
+ case FixedArrayNativeTypeElementValue fixedArray:
+ {
+ BlobBuilder blob = CreateNativeTypeBlob(UnmanagedType.ByValArray);
+ blob.WriteCompressedInteger(ParseInt32(fixedArray.Size));
+ MaterializeNativeType(fixedArray.Element).WriteContentTo(blob);
+ return blob;
+ }
+ case DeprecatedNativeTypeElementValue deprecated:
+ return MaterializeDeprecatedNativeType(deprecated);
+ case SimpleNativeTypeElementValue simple:
+ return CreateNativeTypeBlob(GetSimpleNativeType(simple.TokenType));
+ case IidNativeTypeElementValue iid:
+ return MaterializeIidNativeType(iid);
+ case SafeArrayNativeTypeElementValue safeArray:
+ return MaterializeSafeArrayNativeType(safeArray);
+ case UnsignedNativeTypeElementValue unsigned:
+ return CreateNativeTypeBlob(GetUnsignedNativeType(unsigned.TokenType));
+ case NestedStructNativeTypeElementValue nestedStruct:
+ ReportWarning(
+ DiagnosticIds.DeprecatedNativeType,
+ string.Format(DiagnosticMessageTemplates.DeprecatedNativeType, "NESTEDSTRUCT"),
+ nestedStruct.Token);
+ return CreateNativeTypeBlob(NativeTypeNestedStruct);
+ case AnsiBstrNativeTypeElementValue:
+#pragma warning disable CS0618 // Preserve the legacy IL native type spelling.
+ return CreateNativeTypeBlob(UnmanagedType.AnsiBStr);
+#pragma warning restore CS0618
+ case VariantBoolNativeTypeElementValue:
+ return CreateNativeTypeBlob(UnmanagedType.VariantBool);
+ case NativeTypeTypedefValue typedef:
+ if (TryResolveTypedefAsTypeBlob(typedef.Alias) is { } resolved)
+ {
+ BlobBuilder blob = new(resolved.Count);
+ resolved.WriteContentTo(blob);
+ return blob;
+ }
+
+ ReportError(
+ DiagnosticIds.TypedefNotFound,
+ string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
+ typedef.Token);
+ return new BlobBuilder();
+ default:
+ throw new UnreachableException();
+ }
+ }
+
+ private BlobBuilder MaterializeCustomMarshallerNativeType(
+ CustomMarshallerNativeTypeElementValue customMarshaller)
+ {
+ BlobBuilder blob = CreateNativeTypeBlob(UnmanagedType.CustomMarshaler);
+ if (customMarshaller.Guid is not null)
+ {
+ if (customMarshaller.Token is IToken token)
+ {
+ ReportWarning(
+ DiagnosticIds.DeprecatedCustomMarshaller,
+ DiagnosticMessageTemplates.DeprecatedCustomMarshaller,
+ token);
+ }
+ blob.WriteSerializedString(customMarshaller.Guid);
+ blob.WriteSerializedString(customMarshaller.NativeTypeName);
+ }
+ else
+ {
+ blob.WriteCompressedInteger(0);
+ blob.WriteCompressedInteger(0);
+ }
+
+ blob.WriteSerializedString(customMarshaller.MarshallerType);
+ blob.WriteSerializedString(customMarshaller.Cookie);
+ return blob;
+ }
+
+ private BlobBuilder MaterializeDeprecatedNativeType(DeprecatedNativeTypeElementValue deprecated)
+ {
+ (byte value, string name) = deprecated.TokenType switch
+ {
+ CILParser.VARIANT => (NativeTypeVariant, "VARIANT"),
+ CILParser.SYSCHAR => (NativeTypeSysChar, "SYSCHAR"),
+ CILParser.VOID => (NativeTypeVoid, "VOID"),
+ CILParser.DECIMAL => (NativeTypeDecimal, "DECIMAL"),
+ CILParser.DATE => (NativeTypeDate, "DATE"),
+ CILParser.OBJECTREF => (NativeTypeObjectReference, "OBJECTREF"),
+ _ => throw new UnreachableException()
+ };
+
+ ReportWarning(
+ DiagnosticIds.DeprecatedNativeType,
+ string.Format(DiagnosticMessageTemplates.DeprecatedNativeType, name),
+ deprecated.Token);
+ return CreateNativeTypeBlob(value);
+ }
+
+ private BlobBuilder MaterializeIidNativeType(IidNativeTypeElementValue iid)
+ {
+ UnmanagedType nativeType = iid.TokenType switch
+ {
+ CILParser.IUNKNOWN => UnmanagedType.IUnknown,
+ CILParser.IDISPATCH => UnmanagedType.IDispatch,
+ CILParser.INTERFACE => UnmanagedType.Interface,
+ _ => throw new UnreachableException()
+ };
+
+ BlobBuilder blob = CreateNativeTypeBlob(nativeType);
+ if (MaterializeIidParamIndex(iid.IidParamIndex) is int parameterIndex)
+ {
+ blob.WriteCompressedInteger(parameterIndex);
+ }
+ return blob;
+ }
+
+ private BlobBuilder MaterializeSafeArrayNativeType(SafeArrayNativeTypeElementValue safeArray)
+ {
+ BlobBuilder blob = CreateNativeTypeBlob(UnmanagedType.SafeArray);
+ blob.WriteCompressedInteger((int)MaterializeVariantType(safeArray.VariantType));
+ if (safeArray.UserDefinedType is null)
+ {
+ blob.WriteCompressedInteger(0);
+ }
+ else
+ {
+ blob.WriteSerializedString(safeArray.UserDefinedType);
+ }
+ return blob;
+ }
+
+ private int? MaterializeIidParamIndex(IidParamIndexValue value)
+ => value.Index is null ? null : ParseInt32(value.Index);
+
+ private VarEnum MaterializeVariantType(VariantTypeValue value)
+ => value.Element is null
+ ? VarEnum.VT_EMPTY
+ : MaterializeVariantTypeElement(value.Element) | value.Modifiers;
+
+ private static VarEnum MaterializeVariantTypeElement(VariantTypeElementValue value)
+ => value.TokenType switch
+ {
+ CILParser.NULL => VarEnum.VT_EMPTY,
+ CILParser.VARIANT => VarEnum.VT_VARIANT,
+ CILParser.CURRENCY => VarEnum.VT_CY,
+ CILParser.VOID => VarEnum.VT_VOID,
+ CILParser.BOOL => VarEnum.VT_BOOL,
+ CILParser.INT8 => VarEnum.VT_I1,
+ CILParser.INT16 => VarEnum.VT_I2,
+ CILParser.INT32_ => VarEnum.VT_I4,
+ CILParser.INT64_ => VarEnum.VT_I8,
+ CILParser.FLOAT32 => VarEnum.VT_R4,
+ CILParser.FLOAT64_ => VarEnum.VT_R8,
+ CILParser.UINT8 => VarEnum.VT_UI1,
+ CILParser.UINT16 => VarEnum.VT_UI2,
+ CILParser.UINT32 => VarEnum.VT_UI4,
+ CILParser.UINT64 => VarEnum.VT_UI8,
+ CILParser.PTR => VarEnum.VT_PTR,
+ CILParser.DECIMAL => VarEnum.VT_DECIMAL,
+ CILParser.DATE => VarEnum.VT_DATE,
+ CILParser.BSTR => VarEnum.VT_BSTR,
+ CILParser.LPSTR => VarEnum.VT_LPSTR,
+ CILParser.LPWSTR => VarEnum.VT_LPWSTR,
+ CILParser.IUNKNOWN => VarEnum.VT_UNKNOWN,
+ CILParser.IDISPATCH => VarEnum.VT_DISPATCH,
+ CILParser.SAFEARRAY => VarEnum.VT_SAFEARRAY,
+ CILParser.INT => VarEnum.VT_INT,
+ CILParser.UINT => VarEnum.VT_UINT,
+ CILParser.ERROR => VarEnum.VT_ERROR,
+ CILParser.HRESULT => VarEnum.VT_HRESULT,
+ CILParser.CARRAY => VarEnum.VT_CARRAY,
+ CILParser.USERDEFINED => VarEnum.VT_USERDEFINED,
+ CILParser.RECORD => VarEnum.VT_RECORD,
+ CILParser.FILETIME => VarEnum.VT_FILETIME,
+ CILParser.BLOB => VarEnum.VT_BLOB,
+ CILParser.STREAM => VarEnum.VT_STREAM,
+ CILParser.STORAGE => VarEnum.VT_STORAGE,
+ CILParser.STREAMED_OBJECT => VarEnum.VT_STREAMED_OBJECT,
+ CILParser.STORED_OBJECT => VarEnum.VT_STORED_OBJECT,
+ CILParser.BLOB_OBJECT => VarEnum.VT_BLOB_OBJECT,
+ CILParser.CF => VarEnum.VT_CF,
+ CILParser.CLSID => VarEnum.VT_CLSID,
+ TokenConstants.InvalidType => VarEnum.VT_EMPTY,
+ _ => throw new UnreachableException()
+ };
+
+ private static UnmanagedType GetSimpleNativeType(int tokenType)
+ {
+#pragma warning disable CS0618 // Preserve the legacy IL native type spellings.
+ return tokenType switch
+ {
+ CILParser.CURRENCY => UnmanagedType.Currency,
+ CILParser.BOOL => UnmanagedType.Bool,
+ CILParser.INT8 => UnmanagedType.I1,
+ CILParser.INT16 => UnmanagedType.I2,
+ CILParser.INT32_ => UnmanagedType.I4,
+ CILParser.INT64_ => UnmanagedType.I8,
+ CILParser.FLOAT32 => UnmanagedType.R4,
+ CILParser.FLOAT64_ => UnmanagedType.R8,
+ CILParser.ERROR => UnmanagedType.Error,
+ CILParser.UINT8 => UnmanagedType.U1,
+ CILParser.UINT16 => UnmanagedType.U2,
+ CILParser.UINT32 => UnmanagedType.U4,
+ CILParser.UINT64 => UnmanagedType.U8,
+ CILParser.BSTR => UnmanagedType.BStr,
+ CILParser.LPSTR => UnmanagedType.LPStr,
+ CILParser.LPWSTR => UnmanagedType.LPWStr,
+ CILParser.LPTSTR => UnmanagedType.LPTStr,
+ CILParser.STRUCT => UnmanagedType.Struct,
+ CILParser.INT => UnmanagedType.SysInt,
+ CILParser.UINT => UnmanagedType.SysUInt,
+ CILParser.BYVALSTR => UnmanagedType.VBByRefStr,
+ CILParser.TBSTR => UnmanagedType.TBStr,
+ CILParser.METHOD => UnmanagedType.FunctionPtr,
+ CILParser.LPSTRUCT => UnmanagedType.LPStruct,
+ CILParser.ANY => UnmanagedType.AsAny,
+ _ => throw new UnreachableException()
+ };
+#pragma warning restore CS0618
+ }
+
+ private static UnmanagedType GetUnsignedNativeType(int tokenType)
+ => tokenType switch
+ {
+ CILParser.INT8 => UnmanagedType.U1,
+ CILParser.INT16 => UnmanagedType.U2,
+ CILParser.INT32_ => UnmanagedType.U4,
+ CILParser.INT64_ => UnmanagedType.U8,
+ _ => throw new UnreachableException()
+ };
+
+ private static BlobBuilder CreateNativeTypeBlob(UnmanagedType value)
+ => CreateNativeTypeBlob((byte)value);
+
+ private static BlobBuilder CreateNativeTypeBlob(byte value)
+ {
+ BlobBuilder blob = new(1);
+ blob.WriteByte(value);
+ return blob;
+ }
+
+ private int ParseMarshallingInt32(IToken? token)
+ => token is null ? 0 : ParseInt32(token);
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.Class.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.Class.cs
new file mode 100644
index 00000000000000..000a81d67bcf7d
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.Class.cs
@@ -0,0 +1,512 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ private readonly Dictionary<
+ EntityRegistry.TypeDefinitionEntity,
+ List> _pendingClassMethodOverrides = new();
+
+ private sealed record PendingClassMethodOverride(
+ EntityRegistry.MemberReferenceEntity Declaration,
+ EntityRegistry.MemberReferenceEntity? ReferencedBody,
+ string BodyName,
+ BlobBuilder BodySignature,
+ IToken Location);
+
+ private bool PrepareClassMember()
+ {
+ _pendingClassCustomAttributeOwner = null;
+ return !IsDeclarationSuppressed;
+ }
+
+ internal void ProcessClassDataDeclaration(CILParser.DataDeclContext context)
+ {
+ PrepareClassMember();
+ _ = context;
+ }
+
+ internal void ProcessClassSecurityDeclaration(CILParser.SecDeclContext context)
+ {
+ if (!PrepareClassMember() || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ EntityRegistry.DeclarativeSecurityAttributeEntity? security = MaterializeSecurityDeclaration(context);
+ security?.Parent = _currentTypeDefinition.PeekOrDefault();
+ }
+
+ internal void ProcessClassSourceDirective(CILParser.ExtSourceSpecContext context)
+ {
+ PrepareClassMember();
+ _ = context;
+ }
+
+ internal void ProcessClassLanguageDirective(CILParser.LanguageDeclContext context)
+ {
+ PrepareClassMember();
+ _ = context;
+ }
+
+ internal void ProcessClassCustomAttribute(CILParser.CustomAttrDeclContext context)
+ {
+ if (IsDeclarationSuppressed || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ if (MaterializeCustomAttributeDeclaration(context) is { } customAttribute)
+ {
+ customAttribute.Owner ??=
+ _pendingClassCustomAttributeOwner ??
+ _currentTypeDefinition.PeekOrDefault();
+ }
+ }
+
+ internal void SetClassSize(IToken token)
+ {
+ if (PrepareClassMember() &&
+ _currentTypeDefinition.PeekOrDefault() is { } currentType)
+ {
+ currentType.ClassSize = ParseInt32(token);
+ }
+ }
+
+ internal void SetClassPackingSize(IToken token)
+ {
+ if (PrepareClassMember() &&
+ _currentTypeDefinition.PeekOrDefault() is { } currentType)
+ {
+ currentType.PackingSize = ParseInt32(token);
+ }
+ }
+
+ internal void ProcessClassExport(
+ CILParser.ExportHeadContext export,
+ CILParser.ExptypeDeclsContext declarations)
+ {
+ PrepareClassMember();
+ _ = export;
+ _ = declarations;
+ }
+
+ internal void ProcessClassCompilerControl()
+ {
+ PrepareClassMember();
+ }
+
+ internal void AddClassMethodOverride(
+ CILParser.ClassDeclContext context,
+ TypeSpecificationValue declarationOwner,
+ string declarationName,
+ byte bodyCallingConvention,
+ TypeValue bodyReturnType,
+ TypeSpecificationValue bodyOwner,
+ string bodyName,
+ ImmutableArray bodyArguments)
+ {
+ if (!PrepareClassMember())
+ {
+ return;
+ }
+
+ AddClassMethodOverrideCore(
+ context,
+ bodyCallingConvention,
+ bodyReturnType,
+ declarationOwner,
+ declarationName,
+ 0,
+ bodyArguments,
+ bodyCallingConvention,
+ bodyReturnType,
+ bodyOwner,
+ bodyName,
+ 0,
+ bodyArguments);
+ }
+
+ internal void AddClassMethodOverride(
+ CILParser.ClassDeclContext context,
+ byte declarationCallingConvention,
+ TypeValue declarationReturnType,
+ TypeSpecificationValue declarationOwner,
+ string declarationName,
+ int declarationArity,
+ ImmutableArray declarationArguments,
+ byte bodyCallingConvention,
+ TypeValue bodyReturnType,
+ TypeSpecificationValue bodyOwner,
+ string bodyName,
+ int bodyArity,
+ ImmutableArray bodyArguments)
+ {
+ if (!PrepareClassMember())
+ {
+ return;
+ }
+
+ AddClassMethodOverrideCore(
+ context,
+ declarationCallingConvention,
+ declarationReturnType,
+ declarationOwner,
+ declarationName,
+ declarationArity,
+ declarationArguments,
+ bodyCallingConvention,
+ bodyReturnType,
+ bodyOwner,
+ bodyName,
+ bodyArity,
+ bodyArguments);
+ }
+
+ private void AddClassMethodOverrideCore(
+ CILParser.ClassDeclContext context,
+ byte declarationCallingConvention,
+ TypeValue declarationReturnType,
+ TypeSpecificationValue declarationOwner,
+ string declarationName,
+ int declarationArity,
+ ImmutableArray declarationArguments,
+ byte bodyCallingConvention,
+ TypeValue bodyReturnType,
+ TypeSpecificationValue bodyOwner,
+ string bodyName,
+ int bodyArity,
+ ImmutableArray bodyArguments)
+ {
+ if (_currentTypeDefinition.PeekOrDefault() is not { } currentType)
+ {
+ return;
+ }
+
+ BlobBuilder declarationSignature = BuildClassMethodOverrideSignature(
+ declarationCallingConvention,
+ declarationReturnType,
+ declarationArguments,
+ declarationArity);
+ BlobBuilder bodySignature = BuildClassMethodOverrideSignature(
+ bodyCallingConvention,
+ bodyReturnType,
+ bodyArguments,
+ bodyArity);
+
+ EntityRegistry.MemberReferenceEntity declaration =
+ _entityRegistry.CreateLazilyRecordedMemberReference(
+ ResolveTypeSpecification(declarationOwner),
+ declarationName,
+ declarationSignature);
+ EntityRegistry.TypeEntity resolvedBodyOwner =
+ ResolveTypeSpecification(bodyOwner);
+ EntityRegistry.MemberReferenceEntity? referencedBody =
+ ReferenceEquals(resolvedBodyOwner, currentType)
+ ? null
+ : _entityRegistry.CreateLazilyRecordedMemberReference(
+ resolvedBodyOwner,
+ bodyName,
+ bodySignature);
+
+ if (!_pendingClassMethodOverrides.TryGetValue(
+ currentType,
+ out List? pendingOverrides))
+ {
+ pendingOverrides = new();
+ _pendingClassMethodOverrides.Add(currentType, pendingOverrides);
+ }
+
+ pendingOverrides.Add(
+ new(
+ declaration,
+ referencedBody,
+ bodyName,
+ bodySignature,
+ context.Start));
+ }
+
+ private void CompleteClassMethodOverrides(EntityRegistry.TypeDefinitionEntity type)
+ {
+ if (!_pendingClassMethodOverrides.Remove(
+ type,
+ out List? pendingOverrides))
+ {
+ return;
+ }
+
+ foreach (PendingClassMethodOverride pending in pendingOverrides)
+ {
+ if (pending.ReferencedBody is { } referencedBody)
+ {
+ type.MethodImplementations.Add(
+ EntityRegistry.CreateUnrecordedMethodImplementation(
+ type,
+ referencedBody,
+ pending.Declaration));
+ continue;
+ }
+
+ EntityRegistry.MethodDefinitionEntity? bodyMethod = null;
+ bool isAmbiguous = false;
+ foreach (EntityRegistry.MethodDefinitionEntity candidate in type.Methods)
+ {
+ if (candidate.Name != pending.BodyName ||
+ candidate.MethodSignature is null ||
+ !candidate.MethodSignature.ContentEquals(pending.BodySignature))
+ {
+ continue;
+ }
+
+ if (bodyMethod is not null)
+ {
+ isAmbiguous = true;
+ break;
+ }
+
+ bodyMethod = candidate;
+ }
+
+ if (bodyMethod is null || isAmbiguous)
+ {
+ ReportError(
+ DiagnosticIds.InvalidMetadataToken,
+ $"Override body method '{pending.BodyName}' could not be resolved uniquely",
+ pending.Location);
+ continue;
+ }
+
+ type.MethodImplementations.Add(
+ EntityRegistry.CreateUnrecordedMethodImplementation(
+ bodyMethod,
+ pending.Declaration));
+ }
+ }
+
+ private BlobBuilder BuildClassMethodOverrideSignature(
+ byte callingConvention,
+ TypeValue returnType,
+ ImmutableArray arguments,
+ int genericArity)
+ {
+ BlobBuilder signature = new();
+ byte header = callingConvention;
+ if (genericArity > 0)
+ {
+ header |= (byte)SignatureAttributes.Generic;
+ }
+ signature.WriteByte(header);
+ if (genericArity > 0)
+ {
+ signature.WriteCompressedInteger(genericArity);
+ }
+
+ ImmutableArray materializedArguments =
+ MaterializeSignatureArguments(arguments);
+ int parameterCount = 0;
+ foreach (SignatureArg argument in materializedArguments)
+ {
+ if (!argument.IsSentinel)
+ {
+ parameterCount++;
+ }
+ }
+ signature.WriteCompressedInteger(parameterCount);
+ MaterializeType(returnType).WriteContentTo(signature);
+ foreach (SignatureArg argument in materializedArguments)
+ {
+ argument.SignatureBlob.WriteContentTo(signature);
+ }
+
+ return signature;
+ }
+
+ internal CILParser.CustomAttributeOwnerValue BeginClassGenericParameterDirective(
+ CILParser.ClassDeclContext context,
+ IToken index)
+ {
+ if (!PrepareClassMember())
+ {
+ return BeginClassGenericDirective(null);
+ }
+
+ return BeginClassGenericDirective(
+ FindClassGenericParameter(context, ParseInt32(index)));
+ }
+
+ internal CILParser.CustomAttributeOwnerValue BeginClassGenericParameterDirective(
+ string name)
+ {
+ if (!PrepareClassMember())
+ {
+ return BeginClassGenericDirective(null);
+ }
+
+ return BeginClassGenericDirective(FindClassGenericParameter(name));
+ }
+
+ internal CILParser.CustomAttributeOwnerValue BeginClassGenericConstraintDirective(
+ CILParser.ClassDeclContext context,
+ IToken index,
+ TypeSpecificationValue constraintType)
+ {
+ if (!PrepareClassMember())
+ {
+ return BeginClassGenericDirective(null);
+ }
+
+ return BeginClassGenericDirective(
+ FindOrCreateClassGenericConstraint(
+ FindClassGenericParameter(context, ParseInt32(index)),
+ constraintType));
+ }
+
+ internal CILParser.CustomAttributeOwnerValue BeginClassGenericConstraintDirective(
+ string name,
+ TypeSpecificationValue constraintType)
+ {
+ if (!PrepareClassMember())
+ {
+ return BeginClassGenericDirective(null);
+ }
+
+ return BeginClassGenericDirective(
+ FindOrCreateClassGenericConstraint(
+ FindClassGenericParameter(name),
+ constraintType));
+ }
+
+ private CILParser.CustomAttributeOwnerValue BeginClassGenericDirective(
+ EntityRegistry.EntityBase? owner)
+ {
+ _pendingClassCustomAttributeOwner = owner;
+ return new CILParser.CustomAttributeOwnerValue(owner);
+ }
+
+ internal void AddClassGenericDirectiveAttribute(
+ CILParser.CustomAttributeOwnerValue ownerValue,
+ CILParser.CustomAttrDeclContext attribute)
+ {
+ if (attribute.HasSyntaxError ||
+ ownerValue.Owner is not { } owner)
+ {
+ return;
+ }
+
+ if (MaterializeCustomAttributeDeclaration(attribute) is { } customAttribute)
+ {
+ customAttribute.Owner ??= owner;
+ }
+ }
+
+ private EntityRegistry.GenericParameterEntity? FindClassGenericParameter(
+ CILParser.ClassDeclContext context,
+ int index)
+ {
+ EntityRegistry.TypeDefinitionEntity? currentType = _currentTypeDefinition.PeekOrDefault();
+ if (currentType is not null &&
+ index >= 0 &&
+ index < currentType.GenericParameters.Count)
+ {
+ return currentType.GenericParameters[index];
+ }
+
+ ReportError(
+ DiagnosticIds.GenericParameterIndexOutOfRange,
+ string.Format(DiagnosticMessageTemplates.GenericParameterIndexOutOfRange, index),
+ context);
+ return null;
+ }
+
+ private EntityRegistry.GenericParameterEntity? FindClassGenericParameter(string name)
+ {
+ EntityRegistry.TypeDefinitionEntity? currentType = _currentTypeDefinition.PeekOrDefault();
+ if (currentType is null)
+ {
+ return null;
+ }
+
+ foreach (EntityRegistry.GenericParameterEntity parameter in currentType.GenericParameters)
+ {
+ if (parameter.Name == name)
+ {
+ return parameter;
+ }
+ }
+
+ return null;
+ }
+
+ private EntityRegistry.GenericParameterConstraintEntity? FindOrCreateClassGenericConstraint(
+ EntityRegistry.GenericParameterEntity? parameter,
+ TypeSpecificationValue constraintType)
+ {
+ if (parameter is null ||
+ _currentTypeDefinition.PeekOrDefault() is not { } currentType)
+ {
+ return null;
+ }
+
+ EntityRegistry.TypeEntity baseType =
+ ResolveTypeSpecification(constraintType);
+ foreach (EntityRegistry.GenericParameterConstraintEntity constraint in parameter.Constraints)
+ {
+ if (constraint.BaseType == baseType)
+ {
+ return constraint;
+ }
+ }
+
+ EntityRegistry.GenericParameterConstraintEntity newConstraint =
+ EntityRegistry.CreateGenericConstraint(baseType);
+ newConstraint.Owner = parameter;
+ parameter.Constraints.Add(newConstraint);
+ currentType.GenericParameterConstraints.Add(newConstraint);
+ return newConstraint;
+ }
+
+ internal void AddInterfaceImplementationAttribute(
+ CILParser.ClassDeclContext context,
+ TypeSpecificationValue interfaceType,
+ CILParser.CustomDescrContext attribute)
+ {
+ if (!PrepareClassMember() ||
+ attribute.HasSyntaxError ||
+ _currentTypeDefinition.PeekOrDefault() is not { } currentType)
+ {
+ return;
+ }
+
+ EntityRegistry.TypeEntity resolvedInterface =
+ ResolveTypeSpecification(interfaceType);
+ EntityRegistry.InterfaceImplementationEntity? implementation = null;
+ foreach (EntityRegistry.InterfaceImplementationEntity candidate in currentType.InterfaceImplementations)
+ {
+ if (candidate.InterfaceType == resolvedInterface)
+ {
+ implementation = candidate;
+ break;
+ }
+ }
+
+ if (implementation is null)
+ {
+ implementation =
+ EntityRegistry.CreateUnrecordedInterfaceImplementation(currentType, resolvedInterface);
+ currentType.InterfaceImplementations.Add(implementation);
+ }
+
+ MaterializeCustomAttributeDescriptor(attribute).Owner = implementation;
+ _ = context;
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.Fields.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.Fields.cs
new file mode 100644
index 00000000000000..c73e937517fb58
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.Fields.cs
@@ -0,0 +1,166 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal CILParser.FieldDeclarationBuilder PrepareFieldDeclaration()
+ {
+ ClearPendingCustomAttributeOwners();
+ return new CILParser.FieldDeclarationBuilder();
+ }
+
+ internal void AddFieldAttribute(
+ CILParser.FieldDeclarationBuilder builder,
+ CILParser.AttributeValue value)
+ => builder.Attributes = ApplyAttribute(builder.Attributes, value);
+
+ internal void SetFieldMarshalling(
+ CILParser.FieldDeclarationBuilder builder,
+ MarshallingDescriptorValue value)
+ => builder.Marshalling = value;
+
+ internal FieldDeclarationValue CreateFieldDeclaration(
+ CILParser.FieldDeclContext context,
+ CILParser.FieldDeclarationBuilder builder,
+ int initialSyntaxErrorCount,
+ CILParser.RepeatOptContext offset,
+ TypeValue fieldType,
+ string name,
+ string? dataDeclarationName,
+ FieldInitializerValue initializer)
+ {
+ if (HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null)
+ {
+ return FieldDeclarationValue.Error;
+ }
+
+ FieldAttributes attributes = builder.Attributes;
+ if (attributes.HasFlag(FieldAttributes.RTSpecialName))
+ {
+ attributes |= FieldAttributes.SpecialName;
+ }
+
+ return new FieldDeclarationValue(
+ true,
+ attributes,
+ fieldType,
+ name,
+ builder.Marshalling,
+ dataDeclarationName,
+ offset.HasValue ? offset.Value : null,
+ initializer);
+ }
+
+ internal void DefineField(
+ CILParser.FieldDeclContext context,
+ FieldDeclarationValue value)
+ {
+ _ = context;
+ if (IsDeclarationSuppressed || !value.IsValid)
+ {
+ return;
+ }
+
+ FieldDeclarationValue declaration = value;
+
+ BlobBuilder signature = new();
+ _ = new BlobEncoder(signature).Field();
+ MaterializeType(declaration.FieldType).WriteContentTo(signature);
+
+ EntityRegistry.FieldDefinitionEntity? field =
+ EntityRegistry.CreateUnrecordedFieldDefinition(
+ declaration.Attributes,
+ _currentTypeDefinition.PeekOrDefault() ?? _entityRegistry.ModuleType,
+ declaration.Name,
+ signature);
+ _lastFieldDefinition = field;
+ _pendingClassCustomAttributeOwner = field;
+
+ if (field is null)
+ {
+ return;
+ }
+
+ field.MarshallingDescriptor = MaterializeMarshallingDescriptor(declaration.Marshalling);
+ field.DataDeclarationName = declaration.DataDeclarationName;
+ field.Offset = declaration.Offset;
+ if (declaration.Initializer.HasValue)
+ {
+ field.ConstantValue = declaration.Initializer.ConstantValue;
+ field.HasConstant = true;
+ }
+ }
+
+ internal CILParser.AttributeValue CreateFieldAttribute(IToken token)
+ => token.Text switch
+ {
+ "static" => new CILParser.AttributeValue(FieldAttributes.Static, 0, true),
+ "public" => new CILParser.AttributeValue(
+ FieldAttributes.Public,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "private" => new CILParser.AttributeValue(
+ FieldAttributes.Private,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "family" => new CILParser.AttributeValue(
+ FieldAttributes.Family,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "initonly" => new CILParser.AttributeValue(FieldAttributes.InitOnly, 0, true),
+ "rtspecialname" => new CILParser.AttributeValue(FieldAttributes.RTSpecialName, 0, true),
+ "specialname" => new CILParser.AttributeValue(FieldAttributes.SpecialName, 0, true),
+ "assembly" => new CILParser.AttributeValue(
+ FieldAttributes.Assembly,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "famandassem" => new CILParser.AttributeValue(
+ FieldAttributes.FamANDAssem,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "famorassem" => new CILParser.AttributeValue(
+ FieldAttributes.FamORAssem,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "privatescope" => new CILParser.AttributeValue(
+ FieldAttributes.PrivateScope,
+ FieldAttributes.FieldAccessMask,
+ true),
+ "literal" => new CILParser.AttributeValue(FieldAttributes.Literal, 0, true),
+#pragma warning disable SYSLIB0050
+ "notserialized" => new CILParser.AttributeValue(FieldAttributes.NotSerialized, 0, true),
+#pragma warning restore SYSLIB0050
+ "volatile" => new CILParser.AttributeValue(0, 0, true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.AttributeValue CreateRawFieldAttribute(IToken token)
+ => new((FieldAttributes)ParseInt32(token), 0, false);
+
+ internal string GetFieldDataName(IToken token)
+ => ParseIdentifier(token);
+
+ internal string GetFieldDataOffset(IToken token)
+ => ParseInt32(token).ToString(CultureInfo.InvariantCulture);
+
+ internal void SetFieldOffset(CILParser.RepeatOptContext context, IToken token)
+ {
+ context.Value = ParseInt32(token);
+ context.HasValue = true;
+ }
+
+ internal EntityRegistry.EntityBase MaterializeFieldReference(
+ CILParser.FieldRefContext context)
+ => MaterializeFieldReference(context.Value);
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.PropertiesEvents.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.PropertiesEvents.cs
new file mode 100644
index 00000000000000..4af6e7321c03e1
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Members.PropertiesEvents.cs
@@ -0,0 +1,258 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Reflection;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal void AddPropertyAttribute(
+ CILParser.PropertyHeaderBuilder builder,
+ CILParser.AttributeValue value)
+ => builder.Attributes = ApplyAttribute(builder.Attributes, value);
+
+ internal PropertyHeaderValue CreatePropertyHeader(
+ CILParser.PropHeadContext context,
+ CILParser.PropertyHeaderBuilder builder,
+ int initialSyntaxErrorCount,
+ byte callingConvention,
+ TypeValue propertyType,
+ string name,
+ System.Collections.Immutable.ImmutableArray arguments,
+ FieldInitializerValue initializer)
+ {
+ if (HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null)
+ {
+ return PropertyHeaderValue.Error;
+ }
+
+ return new PropertyHeaderValue(
+ true,
+ builder.Attributes,
+ callingConvention,
+ propertyType,
+ name,
+ arguments,
+ initializer);
+ }
+
+ internal CILParser.AttributeValue CreatePropertyAttribute(IToken token)
+ => token.Text switch
+ {
+ "specialname" => new CILParser.AttributeValue(
+ PropertyAttributes.SpecialName,
+ 0,
+ true),
+ "rtspecialname" => new CILParser.AttributeValue(0, 0, true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.PropertyBodyValue BeginProperty(PropertyHeaderValue value)
+ {
+ PrepareClassMember();
+ EntityRegistry.PropertyEntity? property = null;
+ if (!IsDeclarationSuppressed &&
+ value.IsValid &&
+ _currentTypeDefinition.PeekOrDefault() is { } currentType)
+ {
+ BlobBuilder signature = new();
+ signature.WriteByte(
+ (byte)(value.CallingConvention | (byte)SignatureKind.Property));
+ signature.WriteCompressedInteger(value.Arguments.Length);
+ MaterializeType(value.PropertyType).WriteContentTo(signature);
+ foreach (SignatureArgumentValue argument in value.Arguments)
+ {
+ MaterializeSignatureArgument(argument).SignatureBlob.WriteContentTo(signature);
+ }
+
+ property = new EntityRegistry.PropertyEntity(value.Attributes, signature, value.Name);
+ if (value.Initializer.HasValue)
+ {
+ property.ConstantValue = value.Initializer.ConstantValue;
+ property.HasConstant = true;
+ property.Attributes |= PropertyAttributes.HasDefault;
+ }
+
+ currentType.Properties.Add(property);
+ }
+
+ return new CILParser.PropertyBodyValue(property);
+ }
+
+ internal void AddPropertySetter(
+ CILParser.PropertyBodyValue body,
+ MethodReferenceValue value)
+ => AddPropertyAccessor(body, MethodSemanticsAttributes.Setter, value);
+
+ internal void AddPropertyGetter(
+ CILParser.PropertyBodyValue body,
+ MethodReferenceValue value)
+ => AddPropertyAccessor(body, MethodSemanticsAttributes.Getter, value);
+
+ internal void AddPropertyOther(
+ CILParser.PropertyBodyValue body,
+ MethodReferenceValue value)
+ => AddPropertyAccessor(body, MethodSemanticsAttributes.Other, value);
+
+ private void AddPropertyAccessor(
+ CILParser.PropertyBodyValue body,
+ MethodSemanticsAttributes semantics,
+ MethodReferenceValue value)
+ {
+ if (body.Property is { } property)
+ {
+ property.Accessors.Add(
+ (semantics, MaterializeMethodReference(value)));
+ }
+ }
+
+ internal void AddPropertyCustomAttribute(
+ CILParser.PropertyBodyValue body,
+ CILParser.CustomAttrDeclContext attribute)
+ {
+ if (attribute.HasSyntaxError ||
+ body.Property is not { } property)
+ {
+ return;
+ }
+
+ if (MaterializeCustomAttributeDeclaration(attribute) is { } customAttribute)
+ {
+ customAttribute.Owner ??= property;
+ }
+ }
+
+ internal void ProcessPropertySourceDirective(
+ CILParser.PropertyBodyValue body,
+ CILParser.ExtSourceSpecContext context)
+ {
+ _ = body;
+ _ = context;
+ }
+
+ internal void ProcessPropertyLanguageDirective(
+ CILParser.PropertyBodyValue body,
+ CILParser.LanguageDeclContext context)
+ {
+ _ = body;
+ _ = context;
+ }
+
+ internal void AddEventAttribute(
+ CILParser.EventHeaderBuilder builder,
+ CILParser.AttributeValue value)
+ => builder.Attributes = ApplyAttribute(builder.Attributes, value);
+
+ internal EventHeaderValue CreateEventHeader(
+ CILParser.EventHeadContext context,
+ CILParser.EventHeaderBuilder builder,
+ int initialSyntaxErrorCount,
+ TypeSpecificationValue? eventType,
+ string name)
+ {
+ if (HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null)
+ {
+ return EventHeaderValue.Error;
+ }
+
+ return new EventHeaderValue(
+ true,
+ builder.Attributes,
+ eventType,
+ name);
+ }
+
+ internal CILParser.AttributeValue CreateEventAttribute(IToken token)
+ => token.Text switch
+ {
+ "specialname" => new CILParser.AttributeValue(
+ EventAttributes.SpecialName,
+ 0,
+ true),
+ "rtspecialname" => new CILParser.AttributeValue(0, 0, true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.EventBodyValue BeginEvent(EventHeaderValue value)
+ {
+ PrepareClassMember();
+ EntityRegistry.EventEntity? @event = null;
+ if (!IsDeclarationSuppressed &&
+ value.IsValid &&
+ _currentTypeDefinition.PeekOrDefault() is { } currentType)
+ {
+ @event = new EntityRegistry.EventEntity(
+ value.Attributes,
+ value.EventType is null
+ ? null
+ : ResolveTypeSpecification(value.EventType),
+ value.Name);
+ currentType.Events.Add(@event);
+ }
+
+ return new CILParser.EventBodyValue(@event);
+ }
+
+ internal void AddEventAdder(CILParser.EventBodyValue body, MethodReferenceValue value)
+ => AddEventAccessor(body, MethodSemanticsAttributes.Adder, value);
+
+ internal void AddEventRemover(CILParser.EventBodyValue body, MethodReferenceValue value)
+ => AddEventAccessor(body, MethodSemanticsAttributes.Remover, value);
+
+ internal void AddEventRaiser(CILParser.EventBodyValue body, MethodReferenceValue value)
+ => AddEventAccessor(body, MethodSemanticsAttributes.Raiser, value);
+
+ internal void AddEventOther(CILParser.EventBodyValue body, MethodReferenceValue value)
+ => AddEventAccessor(body, MethodSemanticsAttributes.Other, value);
+
+ private void AddEventAccessor(
+ CILParser.EventBodyValue body,
+ MethodSemanticsAttributes semantics,
+ MethodReferenceValue value)
+ {
+ if (body.Event is { } @event)
+ {
+ @event.Accessors.Add(
+ (semantics, MaterializeMethodReference(value)));
+ }
+ }
+
+ internal void AddEventCustomAttribute(
+ CILParser.EventBodyValue body,
+ CILParser.CustomAttrDeclContext attribute)
+ {
+ if (attribute.HasSyntaxError ||
+ body.Event is not { } @event)
+ {
+ return;
+ }
+
+ if (MaterializeCustomAttributeDeclaration(attribute) is { } customAttribute)
+ {
+ customAttribute.Owner ??= @event;
+ }
+ }
+
+ internal void ProcessEventSourceDirective(
+ CILParser.EventBodyValue body,
+ CILParser.ExtSourceSpecContext context)
+ {
+ _ = body;
+ _ = context;
+ }
+
+ internal void ProcessEventLanguageDirective(
+ CILParser.EventBodyValue body,
+ CILParser.LanguageDeclContext context)
+ {
+ _ = body;
+ _ = context;
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.Directives.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.Directives.cs
new file mode 100644
index 00000000000000..60fcbf9770fa6d
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.Directives.cs
@@ -0,0 +1,401 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ internal void EndLocalsDirective(
+ CILParser.LocalsDeclContext context,
+ int initialSyntaxErrorCount)
+ {
+ bool hasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (hasSyntaxError || _currentMethod is null || context.arguments is null)
+ {
+ return;
+ }
+
+ if (context.initialize is not null)
+ {
+ _currentMethod.Definition.BodyAttributes = MethodBodyAttributes.InitLocals;
+ }
+
+ Dictionary localsScope;
+ if (_currentMethod.LocalsScopes.Count > 0)
+ {
+ localsScope = _currentMethod.LocalsScopes[^1];
+ }
+ else
+ {
+ localsScope = new();
+ _currentMethod.LocalsScopes.Add(localsScope);
+ }
+
+ ImmutableArray locals =
+ MaterializeSignatureArguments(context.arguments.Value);
+ foreach (SignatureArg local in locals)
+ {
+ if (local.Name is not null)
+ {
+ localsScope.TryAdd(local.Name, _currentMethod.AllLocals.Count);
+ }
+
+ _currentMethod.AllLocals.Add(local);
+ }
+ }
+
+ internal void EndExportDirective(
+ CILParser.ExportDeclContext context,
+ int initialSyntaxErrorCount)
+ {
+ bool hasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (_currentMethod is null)
+ {
+ return;
+ }
+
+ _currentMethod.Definition.ExportOrdinal = -1;
+ _currentMethod.Definition.ExportAlias = null;
+ _exportDirectiveContexts.Remove(_currentMethod.Definition);
+ if (hasSyntaxError || context.ordinal is null)
+ {
+ return;
+ }
+
+ int diagnosticCount = _diagnostics.Count;
+ int ordinal = ParseInt32(context.ordinal.Start);
+ string? alias = context.alias is null ? null : ParseIdentifier(context.alias.Start);
+ if (_diagnostics.Count != diagnosticCount)
+ {
+ return;
+ }
+
+ if (ordinal <= 0)
+ {
+ ReportError(
+ DiagnosticIds.InvalidExportOrdinal,
+ string.Format(DiagnosticMessageTemplates.InvalidExportOrdinal, ordinal),
+ context);
+ return;
+ }
+
+ _currentMethod.Definition.ExportOrdinal = ordinal;
+ _currentMethod.Definition.ExportAlias = alias;
+ _exportDirectiveContexts[_currentMethod.Definition] = context;
+ }
+
+ internal void EndVTableEntryDirective(
+ CILParser.VtentryDeclContext context,
+ int initialSyntaxErrorCount)
+ {
+ bool hasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (_currentMethod is null)
+ {
+ return;
+ }
+
+ _currentMethod.Definition.VTableEntry = 0;
+ _currentMethod.Definition.VTableSlot = 0;
+ _vtableEntryDirectiveContexts.Remove(_currentMethod.Definition);
+ if (hasSyntaxError || context.table is null || context.slot is null)
+ {
+ return;
+ }
+
+ int diagnosticCount = _diagnostics.Count;
+ int vtableEntry = ParseInt32(context.table.Start);
+ int vtableSlot = ParseInt32(context.slot.Start);
+ if (_diagnostics.Count != diagnosticCount)
+ {
+ return;
+ }
+
+ _currentMethod.Definition.VTableEntry = vtableEntry;
+ _currentMethod.Definition.VTableSlot = vtableSlot;
+ _vtableEntryDirectiveContexts[_currentMethod.Definition] = context;
+ }
+
+ internal void EndOverrideDirective(
+ CILParser.OverrideDeclContext context,
+ int initialSyntaxErrorCount)
+ {
+ bool hasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (hasSyntaxError ||
+ _currentMethod is null ||
+ context.owner is null ||
+ context.name is null ||
+ _currentTypeDefinition.PeekOrDefault() is not { } currentType)
+ {
+ return;
+ }
+
+ BlobBuilder signature = _currentMethod.Definition.MethodSignature!;
+ if (context.convention is not null)
+ {
+ if (context.returnType is null || context.arity is null || context.arguments is null)
+ {
+ return;
+ }
+
+ signature = BuildOverrideSignature(
+ context.convention.Value,
+ context.returnType.Value,
+ context.arity.Value,
+ context.arguments.Value);
+ }
+
+ EntityRegistry.TypeEntity owner =
+ ResolveTypeSpecification(context.owner.Value);
+ EntityRegistry.MemberReferenceEntity declaration =
+ _entityRegistry.CreateLazilyRecordedMemberReference(owner, context.name.Value, signature);
+ currentType.MethodImplementations.Add(
+ EntityRegistry.CreateUnrecordedMethodImplementation(_currentMethod.Definition, declaration));
+ }
+
+ private BlobBuilder BuildOverrideSignature(
+ byte callingConvention,
+ TypeValue returnType,
+ int genericArity,
+ ImmutableArray signatureArguments)
+ {
+ BlobBuilder signature = new();
+ byte header = callingConvention;
+ if (genericArity > 0)
+ {
+ header |= (byte)SignatureAttributes.Generic;
+ }
+
+ signature.WriteByte(header);
+ if (genericArity > 0)
+ {
+ signature.WriteCompressedInteger(genericArity);
+ }
+
+ ImmutableArray arguments =
+ MaterializeSignatureArguments(signatureArguments);
+ signature.WriteCompressedInteger(arguments.Length);
+ MaterializeType(returnType).WriteContentTo(signature);
+ foreach (SignatureArg argument in arguments)
+ {
+ argument.SignatureBlob.WriteContentTo(signature);
+ }
+
+ return signature;
+ }
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal void AddCustomAttributeApplication(
+ ImmutableArray.Builder attributes,
+ CILParser.CustomAttrDeclContext attribute)
+ => attributes.Add(new(
+ attribute.Value,
+ attribute.Start,
+ attribute.HasSyntaxError));
+#pragma warning restore CA1822
+
+ internal void EndParameterDirective(
+ CILParser.ParameterDeclContext context,
+ ImmutableArray attributes,
+ int initialSyntaxErrorCount)
+ {
+ bool hasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (hasSyntaxError || _currentMethod is null)
+ {
+ return;
+ }
+
+ if (context.genericIndex is not null || context.genericName is not null)
+ {
+ EntityRegistry.GenericParameterEntity? parameter = ResolveMethodGenericParameter(
+ context.genericIndex,
+ context.genericName?.Value,
+ context);
+ if (parameter is not null)
+ {
+ ApplyCustomAttributes(attributes, parameter);
+ }
+
+ return;
+ }
+
+ if (context.constraintIndex is not null || context.constraintName is not null)
+ {
+ EntityRegistry.GenericParameterEntity? parameter = ResolveMethodGenericParameter(
+ context.constraintIndex,
+ context.constraintName?.Value,
+ context);
+ if (parameter is null || context.constraintType is null)
+ {
+ return;
+ }
+
+ EntityRegistry.TypeEntity baseType =
+ ResolveTypeSpecification(context.constraintType.Value);
+ EntityRegistry.GenericParameterConstraintEntity? constraint =
+ parameter.Constraints.FirstOrDefault(candidate => candidate.BaseType == baseType);
+ if (constraint is null)
+ {
+ constraint = EntityRegistry.CreateGenericConstraint(baseType);
+ constraint.Owner = parameter;
+ parameter.Constraints.Add(constraint);
+ _currentMethod.Definition.GenericParameterConstraints.Add(constraint);
+ }
+
+ ApplyCustomAttributes(attributes, constraint);
+ return;
+ }
+
+ if (context.parameterIndex is null || context.initializer is null)
+ {
+ return;
+ }
+
+ int index = ParseInt32(context.parameterIndex.Start);
+ if ((uint)index >= (uint)_currentMethod.Definition.Parameters.Count)
+ {
+ ReportError(
+ DiagnosticIds.ParameterIndexOutOfRange,
+ string.Format(DiagnosticMessageTemplates.ParameterIndexOutOfRange, index),
+ context);
+ return;
+ }
+
+ EntityRegistry.ParameterEntity parameterEntity = _currentMethod.Definition.Parameters[index];
+ FieldInitializerValue initializer = GetInitializerValue(context.initializer);
+ if (initializer.HasValue)
+ {
+ parameterEntity.ConstantValue = initializer.ConstantValue;
+ parameterEntity.HasConstant = true;
+ }
+
+ foreach (CustomAttributeApplicationValue application in attributes)
+ {
+ if (application.HasSyntaxError)
+ {
+ continue;
+ }
+
+ EntityRegistry.CustomAttributeEntity? attribute =
+ MaterializeCustomAttributeDeclaration(
+ application.Value,
+ application.Location);
+ if (attribute is not null)
+ {
+ attribute.Owner ??= parameterEntity;
+ parameterEntity.HasCustomAttributes = true;
+ }
+ }
+ }
+
+ private EntityRegistry.GenericParameterEntity? ResolveMethodGenericParameter(
+ CILParser.Int32Context? indexContext,
+ string? name,
+ CILParser.ParameterDeclContext diagnosticContext)
+ {
+ Debug.Assert(_currentMethod is not null);
+ if (indexContext is not null)
+ {
+ int index = ParseInt32(indexContext.Start);
+ if ((uint)index >= (uint)_currentMethod.Definition.GenericParameters.Count)
+ {
+ ReportError(
+ DiagnosticIds.GenericParameterIndexOutOfRange,
+ string.Format(DiagnosticMessageTemplates.GenericParameterIndexOutOfRange, index),
+ diagnosticContext);
+ return null;
+ }
+
+ return _currentMethod.Definition.GenericParameters[index];
+ }
+
+ EntityRegistry.GenericParameterEntity? parameter =
+ _currentMethod.Definition.GenericParameters.FirstOrDefault(candidate => candidate.Name == name);
+ if (parameter is null)
+ {
+ ReportError(
+ DiagnosticIds.UnknownGenericParameter,
+ string.Format(DiagnosticMessageTemplates.UnknownGenericParameter, name),
+ diagnosticContext);
+ }
+
+ return parameter;
+ }
+
+ private void ApplyCustomAttributes(
+ ImmutableArray attributes,
+ EntityRegistry.EntityBase owner)
+ {
+ foreach (CustomAttributeApplicationValue application in attributes)
+ {
+ if (application.HasSyntaxError)
+ {
+ continue;
+ }
+
+ EntityRegistry.CustomAttributeEntity? attribute =
+ MaterializeCustomAttributeDeclaration(
+ application.Value,
+ application.Location);
+ if (attribute is not null)
+ {
+ attribute.Owner ??= owner;
+ }
+ }
+ }
+
+#pragma warning disable CA1822 // Parser actions own these directive side effects.
+ internal void ProcessMethodDataDeclaration(CILParser.DataDeclContext context)
+ => _ = context;
+#pragma warning restore CA1822
+
+ internal void ProcessMethodSecurityDeclaration(CILParser.SecDeclContext context)
+ {
+ if (_currentMethod is null || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ EntityRegistry.DeclarativeSecurityAttributeEntity? security = MaterializeSecurityDeclaration(context);
+ security?.Parent = _currentMethod.Definition;
+ }
+
+#pragma warning disable CA1822 // Parser actions own these directive side effects.
+ internal void ProcessMethodSourceDirective(CILParser.ExtSourceSpecContext context)
+ => _ = context;
+
+ internal void ProcessMethodLanguageDirective(CILParser.LanguageDeclContext context)
+ => _ = context;
+#pragma warning restore CA1822
+
+ internal void ProcessMethodCustomAttribute(CILParser.CustomDescrInMethodBodyContext context)
+ {
+ if (_currentMethod is null || context.HasSyntaxError)
+ {
+ return;
+ }
+
+ EntityRegistry.CustomAttributeEntity? attribute = MaterializeMethodBodyCustomAttributeDeclaration(context);
+ if (attribute is not null)
+ {
+ attribute.Owner ??= _currentMethod.Definition;
+ }
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.ExceptionHandling.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.ExceptionHandling.cs
new file mode 100644
index 00000000000000..11b9578005ad2f
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.ExceptionHandling.cs
@@ -0,0 +1,275 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ private readonly Dictionary _scopeRanges = new();
+ // Method scopes nest while IL and local declarations are emitted, so their live offsets and
+ // local-scope depth must be restored when each lexical scope exits.
+ private readonly Stack _scopeStack = new();
+ private RuleContext? _methodOwner;
+
+ private void EndMethod()
+ {
+ Debug.Assert(
+ _scopeStack.Count == 0,
+ "Lexical scope state must be released by its owning rule.");
+
+ _methodOwner = null;
+ if (_currentMethod is not null)
+ {
+ if (_currentMethod.AllLocals.Count > 0)
+ {
+ BlobBuilder localsSignature = new();
+ BlobEncoder encoder = new(localsSignature);
+ LocalVariablesEncoder localsEncoder =
+ encoder.LocalVariableSignature(_currentMethod.AllLocals.Count);
+ foreach (SignatureArg local in _currentMethod.AllLocals)
+ {
+ local.SignatureBlob.WriteContentTo(localsEncoder.AddVariable().Builder);
+ }
+
+ _currentMethod.Definition.LocalsSignature =
+ _entityRegistry.GetOrCreateStandaloneSignature(localsSignature);
+ }
+
+ ValidateLabelReferences();
+ _currentMethod = null;
+ }
+
+ ResetMethodBodyState();
+ ClearPendingCustomAttributeOwners();
+ }
+
+ private void ResetMethodBodyState()
+ {
+ _scopeRanges.Clear();
+ _scopeStack.Clear();
+ }
+
+ internal void BeginScope(CILParser.ScopeBlockContext context)
+ {
+ if (_currentMethod is not null)
+ {
+ _scopeStack.Push(
+ new ScopeFrame(context, CurrentMethodBodyOffset, _currentMethod.LocalsScopes.Count));
+ }
+ }
+
+ internal void EndScope(CILParser.ScopeBlockContext context)
+ {
+ if (_scopeStack.Count == 0 || !ReferenceEquals(_scopeStack.Peek().Context, context))
+ {
+ return;
+ }
+
+ ScopeFrame frame = _scopeStack.Pop();
+ if (_currentMethod is not null && frame.LocalsScopeCount < _currentMethod.LocalsScopes.Count)
+ {
+ _currentMethod.LocalsScopes.RemoveRange(
+ frame.LocalsScopeCount,
+ _currentMethod.LocalsScopes.Count - frame.LocalsScopeCount);
+ }
+
+ _scopeRanges[context] = (frame.Start, CurrentMethodBodyOffset);
+ }
+
+ internal void EndExceptionBlock(
+ CILParser.SehBlockContext context,
+ int initialSyntaxErrorCount)
+ {
+ if (_currentMethod is null ||
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null ||
+ context.tryRange is null ||
+ context.clauses is null)
+ {
+ return;
+ }
+
+ ExceptionRangeValue tryRangeValue = context.tryRange.Value;
+ (LabelHandle Start, LabelHandle End)? tryRange = ResolveExceptionRange(tryRangeValue);
+ if (tryRange is null)
+ {
+ return;
+ }
+
+ foreach (ExceptionClauseValue clause in context.clauses.Value)
+ {
+ (LabelHandle Start, LabelHandle End)? handlerRange = ResolveExceptionRange(clause.Handler);
+ if (handlerRange is null)
+ {
+ continue;
+ }
+
+ switch (clause)
+ {
+ case FinallyExceptionClauseValue:
+ AddExceptionRegion(new EntityRegistry.ExceptionRegion.FinallyRegion(
+ tryRange.Value.Start,
+ tryRange.Value.End,
+ handlerRange.Value.Start,
+ handlerRange.Value.End));
+ break;
+ case FaultExceptionClauseValue:
+ AddExceptionRegion(new EntityRegistry.ExceptionRegion.FaultRegion(
+ tryRange.Value.Start,
+ tryRange.Value.End,
+ handlerRange.Value.Start,
+ handlerRange.Value.End));
+ break;
+ case CatchExceptionClauseValue { CatchType: { IsValid: true, Type: not null } catchType }:
+ AddExceptionRegion(new EntityRegistry.ExceptionRegion.CatchRegion(
+ tryRange.Value.Start,
+ tryRange.Value.End,
+ handlerRange.Value.Start,
+ handlerRange.Value.End,
+ catchType.Type));
+ break;
+ case FilterExceptionClauseValue filterClause
+ when ResolveExceptionFilter(filterClause.Filter) is LabelHandle filterStart:
+ AddExceptionRegion(new EntityRegistry.ExceptionRegion.FilterRegion(
+ tryRange.Value.Start,
+ tryRange.Value.End,
+ handlerRange.Value.Start,
+ handlerRange.Value.End,
+ filterStart));
+ break;
+ }
+ }
+ }
+
+ internal CatchTypeValue EndCatchClause(
+ CILParser.CatchClauseContext context,
+ int initialSyntaxErrorCount)
+ {
+ if (_currentMethod is null ||
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null ||
+ context.catchType is null ||
+ context.catchType.HasSyntaxError)
+ {
+ return CatchTypeValue.Invalid;
+ }
+
+ EntityRegistry.TypeEntity catchType =
+ ResolveTypeSpecification(context.catchType.Value);
+ return new CatchTypeValue(catchType, isValid: true);
+ }
+
+ internal ExceptionRangeValue CreateScopeExceptionRange(CILParser.ScopeBlockContext scope)
+ => new ScopeExceptionRangeValue(scope);
+
+ internal ExceptionRangeValue CreateLabelExceptionRange(IToken start, IToken end)
+ => new LabelExceptionRangeValue(
+ ParseIdentifier(start),
+ start,
+ ParseIdentifier(end),
+ end);
+
+ internal ExceptionRangeValue CreateOffsetExceptionRange(IToken start, IToken end)
+ => new OffsetExceptionRangeValue(ParseInt32(start), ParseInt32(end));
+
+ internal ExceptionFilterValue CreateScopeFilter(CILParser.ScopeBlockContext scope)
+ => new ScopeExceptionFilterValue(scope);
+
+ internal ExceptionFilterValue CreateLabelFilter(IToken label)
+ => new LabelExceptionFilterValue(ParseIdentifier(label), label);
+
+ internal ExceptionFilterValue CreateOffsetFilter(IToken offset)
+ => new OffsetExceptionFilterValue(ParseInt32(offset));
+
+ internal ExceptionClauseValue CreateCatchExceptionClause(
+ CatchTypeValue catchType,
+ ExceptionRangeValue handler)
+ => new CatchExceptionClauseValue(
+ catchType,
+ handler);
+
+ internal ExceptionClauseValue CreateFilterExceptionClause(
+ ExceptionFilterValue filter,
+ ExceptionRangeValue handler)
+ => new FilterExceptionClauseValue(
+ filter,
+ handler);
+
+ internal ExceptionClauseValue CreateFinallyExceptionClause(ExceptionRangeValue handler)
+ => new FinallyExceptionClauseValue(handler);
+
+ internal ExceptionClauseValue CreateFaultExceptionClause(ExceptionRangeValue handler)
+ => new FaultExceptionClauseValue(handler);
+
+ private void AddExceptionRegion(EntityRegistry.ExceptionRegion region)
+ {
+ Debug.Assert(_currentMethod is not null);
+ _currentMethod.Definition.ExceptionRegions.Add(region);
+ }
+
+ private (LabelHandle Start, LabelHandle End)? ResolveExceptionRange(ExceptionRangeValue range)
+ => range switch
+ {
+ ScopeExceptionRangeValue scope => GetScopeRange(scope.Scope),
+ LabelExceptionRangeValue labels => (
+ GetOrCreateMethodLabel(labels.Start, labels.StartToken),
+ GetOrCreateMethodLabel(labels.End, labels.EndToken)),
+ OffsetExceptionRangeValue offsets => (
+ DefineMethodLabelAtOffset(offsets.Start),
+ DefineMethodLabelAtOffset(offsets.End)),
+ _ => null,
+ };
+
+ private LabelHandle? ResolveExceptionFilter(ExceptionFilterValue filter)
+ => filter switch
+ {
+ ScopeExceptionFilterValue scope => GetScopeRange(scope.Scope).Start,
+ LabelExceptionFilterValue label => GetOrCreateMethodLabel(label.Label, label.Token),
+ OffsetExceptionFilterValue offset => DefineMethodLabelAtOffset(offset.Offset),
+ _ => null,
+ };
+
+ private (LabelHandle Start, LabelHandle End) GetScopeRange(CILParser.ScopeBlockContext context)
+ {
+ if (!_scopeRanges.TryGetValue(context, out (int Start, int End) range))
+ {
+ int offset = CurrentMethodBodyOffset;
+ range = (offset, offset);
+ }
+
+ return (
+ DefineMethodLabelAtOffset(range.Start),
+ DefineMethodLabelAtOffset(range.End));
+ }
+
+ private LabelHandle GetOrCreateMethodLabel(string name, IToken reference)
+ {
+ Debug.Assert(_currentMethod is not null);
+ return GetOrCreateMethodLabel(_currentMethod, name, reference);
+ }
+
+ private LabelHandle DefineMethodLabelAtOffset(int offset)
+ {
+ Debug.Assert(_currentMethod is not null);
+ LabelHandle label = _currentMethod.Definition.MethodBody.DefineLabel();
+ _currentMethod.Definition.MethodBody.MarkLabel(label, offset);
+ return label;
+ }
+
+ private int CurrentMethodBodyOffset => _currentMethod?.Definition.MethodBody.Offset ?? 0;
+
+ private readonly record struct ScopeFrame(
+ CILParser.ScopeBlockContext Context,
+ int Start,
+ int LocalsScopeCount);
+}
+#pragma warning restore CA1822
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.cs
new file mode 100644
index 00000000000000..d3b0c30fe9c79d
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodBodies.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Reflection.Metadata.Ecma335;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ private void ValidateLabelReferences()
+ {
+ if (_currentMethod is null)
+ {
+ return;
+ }
+
+ // Report errors for any labels that were referenced but never declared
+ foreach (var undefinedLabel in _currentMethod.UndefinedLabelReferences)
+ {
+ ReportError(
+ DiagnosticIds.LabelNotFound,
+ string.Format(DiagnosticMessageTemplates.LabelNotFound, undefinedLabel.Key),
+ undefinedLabel.Value);
+ }
+ }
+
+ private static LabelHandle GetOrCreateMethodLabel(
+ CurrentMethodContext method,
+ string name,
+ IToken reference)
+ {
+ if (!method.Labels.TryGetValue(name, out LabelHandle label))
+ {
+ label = method.Definition.MethodBody.DefineLabel();
+ method.Labels[name] = label;
+ method.UndefinedLabelReferences.TryAdd(name, reference);
+ }
+
+ return label;
+ }
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+ internal string GetMethodName(IToken token) => token.Text;
+#pragma warning restore CA1822
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.Actions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.Actions.cs
new file mode 100644
index 00000000000000..ebdc8097432f89
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.Actions.cs
@@ -0,0 +1,312 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal CILParser.MethodHeaderBuilder PrepareMethodHeader()
+ {
+ ClearPendingCustomAttributeOwners();
+ return new CILParser.MethodHeaderBuilder();
+ }
+
+ internal void AddMethodAttribute(
+ CILParser.MethodHeaderBuilder builder,
+ CILParser.AttributeValue value)
+ => builder.Attributes = ApplyAttribute(builder.Attributes, value);
+
+ internal void AddPInvoke(CILParser.MethodHeaderBuilder builder, PInvokeValue value)
+ => builder.PInvokes.Add(value);
+
+ internal void AddMethodImplementationAttribute(
+ CILParser.MethodHeaderBuilder builder,
+ CILParser.AttributeValue value)
+ => builder.ImplementationAttributes = ApplyAttribute(
+ builder.ImplementationAttributes,
+ value);
+
+ internal MethodHeaderValue CreateMethodHeader(
+ CILParser.MethodHeadContext context,
+ CILParser.MethodHeaderBuilder builder,
+ int initialSyntaxErrorCount,
+ byte callingConvention,
+ int returnAttributes,
+ TypeValue returnType,
+ MarshallingDescriptorValue returnMarshalling,
+ string name,
+ ImmutableArray genericParameters,
+ ImmutableArray arguments)
+ {
+ if (HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null)
+ {
+ return MethodHeaderValue.Error;
+ }
+
+ return new MethodHeaderValue(
+ true,
+ builder.Attributes,
+ builder.PInvokes.ToImmutable(),
+ callingConvention,
+ returnAttributes,
+ returnType,
+ returnMarshalling,
+ name,
+ genericParameters,
+ arguments,
+ builder.ImplementationAttributes);
+ }
+
+ internal CILParser.AttributeValue CreateMethodAttribute(IToken token)
+ => token.Text switch
+ {
+ "static" => new CILParser.AttributeValue(MethodAttributes.Static, 0, true),
+ "public" => new CILParser.AttributeValue(
+ MethodAttributes.Public,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "private" => new CILParser.AttributeValue(
+ MethodAttributes.Private,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "family" => new CILParser.AttributeValue(
+ MethodAttributes.Family,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "final" => new CILParser.AttributeValue(MethodAttributes.Final, 0, true),
+ "specialname" => new CILParser.AttributeValue(MethodAttributes.SpecialName, 0, true),
+ "virtual" => new CILParser.AttributeValue(MethodAttributes.Virtual, 0, true),
+ "strict" => new CILParser.AttributeValue(
+ MethodAttributes.CheckAccessOnOverride,
+ 0,
+ true),
+ "abstract" => new CILParser.AttributeValue(MethodAttributes.Abstract, 0, true),
+ "assembly" => new CILParser.AttributeValue(
+ MethodAttributes.Assembly,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "famandassem" => new CILParser.AttributeValue(
+ MethodAttributes.FamANDAssem,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "famorassem" => new CILParser.AttributeValue(
+ MethodAttributes.FamORAssem,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "privatescope" => new CILParser.AttributeValue(
+ MethodAttributes.PrivateScope,
+ MethodAttributes.MemberAccessMask,
+ true),
+ "hidebysig" => new CILParser.AttributeValue(MethodAttributes.HideBySig, 0, true),
+ "newslot" => new CILParser.AttributeValue(MethodAttributes.NewSlot, 0, true),
+ "rtspecialname" => new CILParser.AttributeValue(MethodAttributes.RTSpecialName, 0, true),
+ "unmanagedexp" => new CILParser.AttributeValue(MethodAttributes.UnmanagedExport, 0, true),
+ "reqsecobj" => new CILParser.AttributeValue(MethodAttributes.RequireSecObject, 0, true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.AttributeValue CreateRawMethodAttribute(IToken token)
+ => new((MethodAttributes)ParseInt32(token), 0, false);
+
+ internal CILParser.AttributeValue
+ CreateMethodImplementationAttribute(IToken token)
+ => token.Text switch
+ {
+ "native" => new CILParser.AttributeValue(
+ MethodImplAttributes.Native,
+ MethodImplAttributes.CodeTypeMask,
+ true),
+ "cil" or "il" => new CILParser.AttributeValue(
+ MethodImplAttributes.IL,
+ MethodImplAttributes.CodeTypeMask,
+ true),
+ "optil" => new CILParser.AttributeValue(
+ MethodImplAttributes.OPTIL,
+ MethodImplAttributes.CodeTypeMask,
+ true),
+ "managed" => new CILParser.AttributeValue(
+ MethodImplAttributes.Managed,
+ MethodImplAttributes.ManagedMask,
+ true),
+ "unmanaged" => new CILParser.AttributeValue(
+ MethodImplAttributes.Unmanaged,
+ MethodImplAttributes.ManagedMask,
+ true),
+ "forwardref" => new CILParser.AttributeValue(MethodImplAttributes.ForwardRef, 0, true),
+ "preservesig" => new CILParser.AttributeValue(MethodImplAttributes.PreserveSig, 0, true),
+ "runtime" => new CILParser.AttributeValue(
+ MethodImplAttributes.Runtime,
+ MethodImplAttributes.CodeTypeMask,
+ true),
+ "internalcall" => new CILParser.AttributeValue(MethodImplAttributes.InternalCall, 0, true),
+ "synchronized" => new CILParser.AttributeValue(MethodImplAttributes.Synchronized, 0, true),
+ "noinlining" => new CILParser.AttributeValue(MethodImplAttributes.NoInlining, 0, true),
+ "aggressiveinlining" => new CILParser.AttributeValue(
+ MethodImplAttributes.AggressiveInlining,
+ 0,
+ true),
+ "nooptimization" => new CILParser.AttributeValue(MethodImplAttributes.NoOptimization, 0, true),
+ "aggressiveoptimization" => new CILParser.AttributeValue(
+ MethodImplAttributes.AggressiveOptimization,
+ 0,
+ true),
+ "async" => new CILParser.AttributeValue(MethodImplAttributes.Async, 0, true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.AttributeValue
+ CreateRawMethodImplementationAttribute(IToken token)
+ => new((MethodImplAttributes)ParseInt32(token), 0, false);
+
+ internal void SetPInvokeModule(CILParser.PInvokeBuilder builder, string moduleName)
+ => builder.ModuleName = moduleName;
+
+ internal void SetPInvokeEntryPoint(
+ CILParser.PInvokeBuilder builder,
+ string entryPointName)
+ => builder.EntryPointName = entryPointName;
+
+ internal void AddPInvokeAttribute(
+ CILParser.PInvokeBuilder builder,
+ CILParser.AttributeValue value)
+ => builder.Attributes = ApplyAttribute(builder.Attributes, value);
+
+ internal PInvokeValue CreatePInvoke(CILParser.PInvokeBuilder builder)
+ => new(builder.ModuleName, builder.EntryPointName, builder.Attributes);
+
+ internal CILParser.AttributeValue
+ CreatePInvokeAttribute(IToken token)
+ => token.Text switch
+ {
+ "nomangle" => new CILParser.AttributeValue(
+ MethodImportAttributes.ExactSpelling,
+ 0,
+ true),
+ "ansi" => new CILParser.AttributeValue(
+ MethodImportAttributes.CharSetAnsi,
+ 0,
+ true),
+ "unicode" => new CILParser.AttributeValue(
+ MethodImportAttributes.CharSetUnicode,
+ 0,
+ true),
+ "autochar" => new CILParser.AttributeValue(
+ MethodImportAttributes.CharSetAuto,
+ 0,
+ true),
+ "lasterr" => new CILParser.AttributeValue(
+ MethodImportAttributes.SetLastError,
+ 0,
+ true),
+ "winapi" => new CILParser.AttributeValue(
+ MethodImportAttributes.CallingConventionWinApi,
+ 0,
+ true),
+ "cdecl" => new CILParser.AttributeValue(
+ MethodImportAttributes.CallingConventionCDecl,
+ 0,
+ true),
+ "stdcall" => new CILParser.AttributeValue(
+ MethodImportAttributes.CallingConventionStdCall,
+ 0,
+ true),
+ "thiscall" => new CILParser.AttributeValue(
+ MethodImportAttributes.CallingConventionThisCall,
+ 0,
+ true),
+ "fastcall" => new CILParser.AttributeValue(
+ MethodImportAttributes.CallingConventionFastCall,
+ 0,
+ true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.AttributeValue
+ CreateBestFitPInvokeAttribute(IToken setting)
+ => new(
+ setting.Text == "on"
+ ? MethodImportAttributes.BestFitMappingEnable
+ : MethodImportAttributes.BestFitMappingDisable,
+ 0,
+ true);
+
+ internal CILParser.AttributeValue
+ CreateCharMapErrorPInvokeAttribute(IToken setting)
+ => new(
+ setting.Text == "on"
+ ? MethodImportAttributes.ThrowOnUnmappableCharEnable
+ : MethodImportAttributes.ThrowOnUnmappableCharDisable,
+ 0,
+ true);
+
+ internal CILParser.AttributeValue
+ CreateRawPInvokeAttribute(IToken token)
+ => new(
+ (MethodImportAttributes)ParseInt32(token),
+ 0,
+ false);
+
+ internal CILParser.AttributeValue
+ CreateGenericParameterAttribute(IToken token)
+ => token.Text switch
+ {
+ "+" => new CILParser.AttributeValue(
+ GenericParameterAttributes.Covariant,
+ 0,
+ true),
+ "-" => new CILParser.AttributeValue(
+ GenericParameterAttributes.Contravariant,
+ 0,
+ true),
+ "class" => new CILParser.AttributeValue(
+ GenericParameterAttributes.ReferenceTypeConstraint,
+ 0,
+ true),
+ "valuetype" => new CILParser.AttributeValue(
+ GenericParameterAttributes.NotNullableValueTypeConstraint,
+ 0,
+ true),
+ "byreflike" => new CILParser.AttributeValue(
+ GenericParameterAttributes.AllowByRefLike,
+ 0,
+ true),
+ ".ctor" => new CILParser.AttributeValue(
+ GenericParameterAttributes.DefaultConstructorConstraint,
+ 0,
+ true),
+ _ => throw new UnreachableException(),
+ };
+
+ internal CILParser.AttributeValue
+ CreateRawGenericParameterAttribute(IToken token)
+ => new(
+ (GenericParameterAttributes)ParseInt32(token),
+ 0,
+ true);
+
+ internal GenericParameterAttributes AddGenericParameterAttribute(
+ GenericParameterAttributes attributes,
+ CILParser.AttributeValue value)
+ => ApplyAttribute(attributes, value);
+
+ internal GenericParameterDeclarationValue CreateGenericParameterDeclaration(
+ IToken location,
+ GenericParameterAttributes attributes,
+ CILParser.TyBoundContext? constraints,
+ string name)
+ => new GenericParameterDeclarationValue(
+ attributes,
+ name,
+ constraints?.Value ?? [])
+ {
+ Location = location,
+ ConstraintLocation = constraints?.Start
+ };
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.Generics.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.Generics.cs
new file mode 100644
index 00000000000000..9a08cd78532d69
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.Generics.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ private static void RegisterGenericParameterNames(
+ EntityRegistry.EntityBase owner,
+ NamedElementList parameters,
+ ImmutableArray declarations)
+ {
+ for (int i = 0; i < declarations.Length; i++)
+ {
+ GenericParameterDeclarationValue declaration = declarations[i];
+ EntityRegistry.GenericParameterEntity parameter =
+ EntityRegistry.CreateGenericParameter(declaration.Attributes, declaration.Name);
+ parameter.Owner = owner;
+ parameter.Index = i;
+ parameters.Add(parameter);
+ }
+ }
+ private void MaterializeGenericParameterConstraints(
+ NamedElementList parameters,
+ List constraints,
+ ImmutableArray declarations)
+ {
+ Debug.Assert(parameters.Count >= declarations.Length);
+ int count = System.Math.Min(parameters.Count, declarations.Length);
+ for (int i = 0; i < count; i++)
+ {
+ EntityRegistry.GenericParameterEntity parameter = parameters[i];
+ foreach (TypeSpecificationValue constraintType in declarations[i].Constraints)
+ {
+ EntityRegistry.GenericParameterConstraintEntity constraint =
+ EntityRegistry.CreateGenericConstraint(ResolveTypeSpecification(constraintType));
+ constraint.Owner = parameter;
+ parameter.Constraints.Add(constraint);
+ constraints.Add(constraint);
+ }
+ }
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.cs
new file mode 100644
index 00000000000000..c91a72d8c24c86
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.MethodHeaders.cs
@@ -0,0 +1,177 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Reflection;
+using System.Reflection.Metadata;
+
+namespace ILAssembler;
+
+internal sealed partial class GrammarActions
+{
+ internal void BeginMethod(CILParser.MethodHeadContext context, MethodHeaderValue value)
+ {
+ ResetMethodBodyState();
+ if (IsDeclarationSuppressed || !value.IsValid)
+ {
+ return;
+ }
+
+ MethodHeaderValue header = value;
+
+ EntityRegistry.TypeDefinitionEntity containingType =
+ _currentTypeDefinition.PeekOrDefault() ?? _entityRegistry.ModuleType;
+ EntityRegistry.MethodDefinitionEntity methodDefinition =
+ EntityRegistry.CreateUnrecordedMethodDefinition(containingType, header.Name);
+
+ _currentMethod = new(methodDefinition);
+ try
+ {
+ ReportGenericParameterEncodingErrors(header.GenericParameters, context);
+ RegisterGenericParameterNames(
+ methodDefinition,
+ methodDefinition.GenericParameters,
+ header.GenericParameters);
+
+ methodDefinition.MethodAttributes = header.Attributes;
+ ApplyImplicitMethodAttributes(methodDefinition);
+
+ if (methodDefinition.MethodAttributes.HasFlag(MethodAttributes.Abstract) &&
+ !methodDefinition.ContainingType.Attributes.HasFlag(TypeAttributes.Abstract))
+ {
+ ReportWarning(
+ DiagnosticIds.AbstractMethodNotInAbstractType,
+ string.Format(
+ DiagnosticMessageTemplates.AbstractMethodNotInAbstractType,
+ methodDefinition.Name),
+ context);
+ }
+
+ ApplyPInvokeInformation(methodDefinition, header, context);
+
+ byte signatureHeader = header.CallingConvention;
+ if (header.GenericParameters.Length != 0)
+ {
+ signatureHeader |= (byte)SignatureAttributes.Generic;
+ }
+
+ SignatureHeader parsedHeader = new(signatureHeader);
+ if (!methodDefinition.MethodAttributes.HasFlag(MethodAttributes.Static) &&
+ !parsedHeader.IsInstance &&
+ _currentTypeDefinition.Count > 0)
+ {
+ signatureHeader |= (byte)SignatureAttributes.Instance;
+ parsedHeader = new(signatureHeader);
+ }
+ if (parsedHeader.HasExplicitThis && !parsedHeader.IsInstance)
+ {
+ signatureHeader |= (byte)SignatureAttributes.Instance;
+ parsedHeader = new(signatureHeader);
+ }
+
+ BlobBuilder methodSignature = new();
+ methodSignature.WriteByte(signatureHeader);
+ if (header.GenericParameters.Length != 0)
+ {
+ methodSignature.WriteCompressedInteger(header.GenericParameters.Length);
+ }
+
+ MaterializeGenericParameterConstraints(
+ methodDefinition.GenericParameters,
+ methodDefinition.GenericParameterConstraints,
+ header.GenericParameters);
+
+ ImmutableArray arguments = MaterializeSignatureArguments(header.Arguments);
+ methodSignature.WriteCompressedInteger(arguments.Length);
+
+ SignatureArg returnValue = new(
+ (ParameterAttributes)header.ReturnAttributes,
+ MaterializeType(header.ReturnType),
+ MaterializeMarshallingDescriptor(header.ReturnMarshalling),
+ null);
+ returnValue.SignatureBlob.WriteContentTo(methodSignature);
+ methodDefinition.Parameters.Add(
+ EntityRegistry.CreateParameter(
+ returnValue.Attributes,
+ returnValue.Name,
+ returnValue.MarshallingDescriptor,
+ 0));
+
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ SignatureArg argument = arguments[i];
+ argument.SignatureBlob.WriteContentTo(methodSignature);
+ string? parameterName = argument.Name ?? $"A_{i}";
+ methodDefinition.Parameters.Add(
+ EntityRegistry.CreateParameter(
+ argument.Attributes,
+ parameterName,
+ argument.MarshallingDescriptor,
+ i + 1));
+ }
+
+ methodDefinition.SignatureHeader = parsedHeader;
+ methodDefinition.MethodSignature = methodSignature;
+ methodDefinition.ImplementationAttributes = header.ImplementationAttributes;
+
+ if (!EntityRegistry.TryAddMethodDefinitionToContainingType(methodDefinition))
+ {
+ ReportError(
+ DiagnosticIds.DuplicateMethod,
+ DiagnosticMessageTemplates.DuplicateMethod,
+ context);
+ }
+
+ _currentMethod = new(methodDefinition);
+ _methodOwner = context.Parent;
+ }
+ catch
+ {
+ _currentMethod = null;
+ _methodOwner = null;
+ ResetMethodBodyState();
+ throw;
+ }
+ }
+
+ private static void ApplyImplicitMethodAttributes(EntityRegistry.MethodDefinitionEntity method)
+ {
+ if (method.Name is ".ctor" or ".cctor")
+ {
+ method.MethodAttributes |= MethodAttributes.RTSpecialName | MethodAttributes.SpecialName;
+ }
+ else if (method.MethodAttributes.HasFlag(MethodAttributes.RTSpecialName))
+ {
+ method.MethodAttributes |= MethodAttributes.SpecialName;
+ }
+ }
+
+ private void ApplyPInvokeInformation(
+ EntityRegistry.MethodDefinitionEntity method,
+ MethodHeaderValue header,
+ CILParser.MethodHeadContext context)
+ {
+ (EntityRegistry.ModuleReferenceEntity Module, string? EntryPoint, MethodImportAttributes Attributes)?
+ pInvokeInformation = null;
+
+ foreach (PInvokeValue pInvoke in header.PInvokes)
+ {
+ if (pInvoke.ModuleName is null)
+ {
+ ReportError(
+ DiagnosticIds.InvalidPInvokeSignature,
+ DiagnosticMessageTemplates.InvalidPInvokeSignature,
+ context);
+ continue;
+ }
+
+ pInvokeInformation = (
+ _entityRegistry.GetOrCreateModuleReference(pInvoke.ModuleName, _ => { }),
+ pInvoke.EntryPointName ?? header.Name,
+ pInvoke.Attributes);
+ }
+
+ method.MethodImportInformation = pInvokeInformation;
+ }
+
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Security.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Security.cs
new file mode 100644
index 00000000000000..e3cda018d4c0ef
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Security.cs
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Reflection;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal DeclarativeSecurityAction ParseSecurityAction(IToken token)
+ => token.Text switch
+ {
+ "request" => DeclarativeSecurityAction.Request,
+ "demand" => DeclarativeSecurityAction.Demand,
+ "assert" => DeclarativeSecurityAction.Assert,
+ "deny" => DeclarativeSecurityAction.Deny,
+ "permitonly" => DeclarativeSecurityAction.PermitOnly,
+ "linkcheck" => DeclarativeSecurityAction.LinkDemand,
+ "inheritcheck" => DeclarativeSecurityAction.InheritanceDemand,
+ "reqmin" => DeclarativeSecurityAction.RequestMinimum,
+ "reqopt" => DeclarativeSecurityAction.RequestOptional,
+ "reqrefuse" => DeclarativeSecurityAction.RequestRefuse,
+ "prejitgrant" => DeclarativeSecurityAction.PrejitGrant,
+ "prejitdeny" => DeclarativeSecurityAction.PrejitDeny,
+ "noncasdemand" => DeclarativeSecurityAction.NonCasDemand,
+ "noncaslinkdemand" => DeclarativeSecurityAction.NonCasLinkDemand,
+ "noncasinheritance" => DeclarativeSecurityAction.NonCasInheritanceDemand,
+ _ => DeclarativeSecurityAction.Request,
+ };
+
+ internal SecurityDeclarationValue CreateNamedPermissionDeclaration(
+ DeclarativeSecurityAction action,
+ TypeSpecificationValue permissionType,
+ ImmutableArray pairs)
+ => new NamedPermissionDeclarationValue(
+ action,
+ permissionType,
+ pairs);
+
+ internal SecurityDeclarationValue CreateStructuredPermissionDeclaration(
+ DeclarativeSecurityAction action,
+ TypeSpecificationValue permissionType,
+ CustomAttributeBlobValue value)
+ => new StructuredPermissionDeclarationValue(
+ action,
+ permissionType,
+ value);
+
+ internal SecurityDeclarationValue CreateEmptyPermissionDeclaration(
+ DeclarativeSecurityAction action,
+ TypeSpecificationValue permissionType)
+ => new EmptyPermissionDeclarationValue(
+ action,
+ permissionType);
+
+ internal SecurityDeclarationValue CreateRawPermissionSetDeclaration(
+ DeclarativeSecurityAction action,
+ ImmutableArray value)
+ => new RawPermissionSetValue(action, value);
+
+ internal SecurityDeclarationValue CreateStringPermissionSetDeclaration(
+ DeclarativeSecurityAction action,
+ string value)
+ => new StringPermissionSetValue(action, value);
+
+ internal SecurityDeclarationValue CreateAttributePermissionSetDeclaration(
+ DeclarativeSecurityAction action,
+ ImmutableArray value)
+ => new AttributePermissionSetValue(action, value);
+
+ internal void EndSecurityDeclaration(
+ CILParser.SecDeclContext context,
+ int initialSyntaxErrorCount)
+ {
+ context.HasSyntaxError =
+ HasSyntaxErrorsSince(initialSyntaxErrorCount) ||
+ context.exception is not null;
+ if (context.HasSyntaxError)
+ {
+ context.Value = null;
+ }
+ }
+
+ internal SecurityAttributeValue CreateNamedSecurityAttribute(
+ IToken name,
+ ImmutableArray arguments)
+ => new SecurityAttributeValue(
+ StringHelpers.ParseQuotedString(name.Text),
+ null,
+ arguments);
+
+ internal SecurityAttributeValue CreateTypedSecurityAttribute(
+ TypeSpecificationValue type,
+ ImmutableArray arguments)
+ => new SecurityAttributeValue(
+ null,
+ type,
+ arguments);
+
+ internal SecurityNameValuePairValue CreateSecurityNameValuePair(
+ string name,
+ SecurityCaValue value)
+ => new(name, value);
+
+ internal SecurityCaValue CreateSecurityBooleanValue(bool value)
+ => new SecurityBooleanValue(value);
+
+ internal SecurityCaValue CreateSecurityInt32Value(IToken value)
+ => new SecurityInt32Value(ParseInt32(value));
+
+ internal SecurityCaValue CreateSecurityStringValue(string value)
+ => new SecurityStringValue(value);
+
+ internal SecurityCaValue CreateSecurityEnumValue(
+ ClassNameValue type,
+ IToken kind,
+ IToken value)
+ => new SecurityEnumValue(
+ type,
+ kind.Text switch
+ {
+ "int8" => 1,
+ "int16" => 2,
+ "int32" => 4,
+ _ => throw new UnreachableException(),
+ },
+ ParseInt32(value));
+
+ internal SecurityCaValue CreateSecurityEnumValue(
+ ClassNameValue type,
+ IToken value)
+ => new SecurityEnumValue(type, 4, ParseInt32(value));
+
+ private EntityRegistry.DeclarativeSecurityAttributeEntity? MaterializeSecurityDeclaration(
+ SecurityDeclarationValue value,
+ IToken location)
+ {
+ if (value is PermissionDeclarationValue)
+ {
+ ReportError(
+ DiagnosticIds.UnsupportedSecurityDeclaration,
+ DiagnosticMessageTemplates.UnsupportedSecurityDeclaration,
+ location);
+ return null;
+ }
+
+ BlobBuilder permissionSet = value switch
+ {
+ RawPermissionSetValue raw => CreateRawPermissionSet(raw.Value),
+ StringPermissionSetValue text => CreateStringPermissionSet(text.Value),
+ AttributePermissionSetValue attributes =>
+ MaterializeSecurityAttributeSet(attributes.Attributes),
+ _ => throw new UnreachableException(),
+ };
+ return _entityRegistry.CreateDeclarativeSecurityAttribute(value.Action, permissionSet);
+ }
+
+ private static BlobBuilder CreateRawPermissionSet(ImmutableArray value)
+ {
+ BlobBuilder blob = new(value.Length);
+ blob.WriteBytes(value);
+ return blob;
+ }
+
+ private static BlobBuilder CreateStringPermissionSet(string value)
+ {
+ BlobBuilder blob = new();
+ blob.WriteUTF16(value);
+ blob.WriteUTF16("\0");
+ return blob;
+ }
+
+ private BlobBuilder MaterializeSecurityAttributeSet(
+ ImmutableArray attributes)
+ {
+ BlobBuilder blob = new();
+ blob.WriteByte((byte)'.');
+ blob.WriteCompressedInteger(attributes.Length);
+ foreach (SecurityAttributeValue attribute in attributes)
+ {
+ MaterializeSecurityAttribute(attribute).WriteContentTo(blob);
+ }
+
+ return blob;
+ }
+
+ private BlobBuilder MaterializeSecurityAttribute(SecurityAttributeValue attribute)
+ {
+ string attributeName = attribute.Name ?? string.Empty;
+ if (attribute.Type is { } type &&
+ ResolveTypeSpecification(type) is EntityRegistry.IHasReflectionNotation reflectionNotation)
+ {
+ attributeName = reflectionNotation.ReflectionNotation;
+ }
+
+ BlobBuilder blob = new();
+ blob.WriteSerializedString(attributeName);
+ WriteCustomBlobNamedArguments(blob, attribute.Arguments);
+ return blob;
+ }
+
+ internal EntityRegistry.DeclarativeSecurityAttributeEntity? MaterializeSecurityDeclaration(
+ CILParser.SecDeclContext context)
+ => context.Value is SecurityDeclarationValue value
+ ? MaterializeSecurityDeclaration(value, context.Start)
+ : null;
+
+}
+#pragma warning restore CA1822
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Signatures.Actions.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Signatures.Actions.cs
new file mode 100644
index 00000000000000..229b47ae2637f3
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Signatures.Actions.cs
@@ -0,0 +1,246 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal byte AddInstanceCallingConvention(byte callingConvention)
+ => (byte)(callingConvention | (byte)SignatureAttributes.Instance);
+
+ internal byte AddExplicitCallingConvention(byte callingConvention)
+ => (byte)(callingConvention | (byte)(SignatureAttributes.ExplicitThis | SignatureAttributes.Instance));
+
+ internal byte GetRawCallingConvention(IToken token) => (byte)ParseInt32(token);
+
+ internal byte GetDefaultCallingConvention() => (byte)SignatureCallingConvention.Default;
+
+ internal byte GetCallingConvention(IToken token)
+ => (byte)(token.Type switch
+ {
+ CILParser.DEFAULT => SignatureCallingConvention.Default,
+ CILParser.VARARG => SignatureCallingConvention.VarArgs,
+ CILParser.CDECL => SignatureCallingConvention.CDecl,
+ CILParser.STDCALL => SignatureCallingConvention.StdCall,
+ CILParser.THISCALL => SignatureCallingConvention.ThisCall,
+ CILParser.FASTCALL => SignatureCallingConvention.FastCall,
+ CILParser.UNMANAGED => SignatureCallingConvention.Unmanaged,
+ _ => throw new UnreachableException()
+ });
+
+ internal void InitializeBound(CILParser.BoundContext context)
+ {
+ context.Lower = 0;
+ context.Upper = 0;
+ context.HasLower = false;
+ context.HasUpper = false;
+ }
+
+ internal void SetBoundSize(CILParser.BoundContext context, IToken sizeToken)
+ {
+ context.Lower = 0;
+ context.Upper = ParseInt32(sizeToken);
+ context.HasLower = true;
+ context.HasUpper = true;
+ }
+
+ internal void SetBoundRange(CILParser.BoundContext context, IToken lowerToken, IToken upperToken)
+ {
+ int lower = ParseInt32(lowerToken);
+ context.Lower = lower;
+ context.Upper = ParseInt32(upperToken) - lower + 1;
+ context.HasLower = true;
+ context.HasUpper = true;
+ }
+
+ internal void SetBoundLower(CILParser.BoundContext context, IToken lowerToken)
+ {
+ context.Lower = ParseInt32(lowerToken);
+ context.HasLower = true;
+ }
+
+ internal ArrayBoundValue CreateArrayBound(CILParser.BoundContext bound)
+ => new(
+ bound.HasLower ? bound.Lower : null,
+ bound.HasUpper ? bound.Upper : null);
+
+ internal SignatureArgumentValue CreateSentinelSignatureArgument()
+ => new SignatureArgumentValue(true, 0, null, null, null);
+
+ internal SignatureArgumentValue CreateSignatureArgument(
+ int attributes,
+ TypeValue type,
+ MarshallingDescriptorValue marshalling,
+ CILParser.IdContext? name)
+ => new SignatureArgumentValue(
+ false,
+ attributes,
+ type,
+ marshalling,
+ name is null ? null : GetIdentifier(name));
+
+ internal void SetParameterAttributeElement(CILParser.ParamAttrElementContext context, IToken attribute)
+ {
+ context.Value = attribute.Text switch
+ {
+ "in" => (int)ParameterAttributes.In,
+ "out" => (int)ParameterAttributes.Out,
+ "opt" => (int)ParameterAttributes.Optional,
+ _ => throw new UnreachableException()
+ };
+ context.ShouldAppend = true;
+ }
+
+ internal void SetRawParameterAttributeElement(CILParser.ParamAttrElementContext context, IToken token)
+ {
+ context.Value = ParseInt32(token) + 1;
+ context.ShouldAppend = false;
+ }
+
+ internal int AddParameterAttribute(int attributes, int value, bool shouldAppend)
+ => shouldAppend ? attributes | value : value;
+
+ internal ElementTypeValue CreateClassElementType(ClassNameValue className, bool isValueType)
+ => new ClassElementTypeValue(className, isValueType);
+
+ internal ElementTypeValue CreateObjectElementType()
+ => new PrimitiveElementTypeValue((byte)SignatureTypeCode.Object);
+
+ internal ElementTypeValue CreateTypedReferenceElementType()
+ => new PrimitiveElementTypeValue((byte)SignatureTypeCode.TypedReference);
+
+ internal ElementTypeValue CreateVoidElementType()
+ => new PrimitiveElementTypeValue((byte)SignatureTypeCode.Void);
+
+ internal ElementTypeValue CreatePrimitiveElementType(byte typeCode)
+ => new PrimitiveElementTypeValue(typeCode);
+
+ internal ElementTypeValue CreateFunctionPointerElementType(
+ byte callingConvention,
+ TypeValue returnType,
+ ImmutableArray arguments)
+ => new FunctionPointerElementTypeValue(
+ callingConvention,
+ returnType,
+ arguments);
+
+ internal ElementTypeValue CreateIndexedGenericParameterElementType(
+ bool isMethodParameter,
+ IToken token)
+ => new IndexedGenericParameterElementTypeValue(isMethodParameter, ParseInt32(token));
+
+ internal ElementTypeValue CreateNamedGenericParameterElementType(
+ IToken token,
+ bool isMethodParameter,
+ string name)
+ => new NamedGenericParameterElementTypeValue(token, isMethodParameter, name);
+
+ internal ElementTypeValue CreateTypedefElementType(IToken token, string alias)
+ => new TypedefElementTypeValue(token, alias);
+
+ internal ElementTypeValue CreateSentinelElementType(TypeValue type)
+ => new SentinelElementTypeValue(type);
+
+ internal TypeModifierValue CreateSzArrayTypeModifier()
+ => new SimpleTypeModifierValue(SimpleTypeModifierKind.SzArray);
+
+ internal TypeModifierValue CreateByReferenceTypeModifier()
+ => new SimpleTypeModifierValue(SimpleTypeModifierKind.ByReference);
+
+ internal TypeModifierValue CreatePointerTypeModifier()
+ => new SimpleTypeModifierValue(SimpleTypeModifierKind.Pointer);
+
+ internal TypeModifierValue CreatePinnedTypeModifier()
+ => new SimpleTypeModifierValue(SimpleTypeModifierKind.Pinned);
+
+ internal TypeModifierValue CreateArrayTypeModifier(ImmutableArray bounds)
+ => new ArrayTypeModifierValue(bounds);
+
+ internal TypeModifierValue CreateCustomTypeModifier(
+ TypeSpecificationValue type,
+ bool isRequired)
+ => new CustomTypeModifierValue(type, isRequired);
+
+ internal TypeModifierValue CreateGenericArgumentsModifier(
+ ImmutableArray arguments)
+ => new GenericArgumentsTypeModifierValue(arguments);
+
+ internal byte GetSimpleType(IToken token, bool isUnsigned)
+ {
+ SignatureTypeCode typeCode = (token.Type, isUnsigned) switch
+ {
+ (CILParser.CHAR, false) => SignatureTypeCode.Char,
+ (CILParser.STRING, false) => SignatureTypeCode.String,
+ (CILParser.BOOL, false) => SignatureTypeCode.Boolean,
+ (CILParser.INT8, false) => SignatureTypeCode.SByte,
+ (CILParser.INT16, false) => SignatureTypeCode.Int16,
+ (CILParser.INT32_, false) => SignatureTypeCode.Int32,
+ (CILParser.INT64_, false) => SignatureTypeCode.Int64,
+ (CILParser.FLOAT32, false) => SignatureTypeCode.Single,
+ (CILParser.FLOAT64_, false) => SignatureTypeCode.Double,
+ (CILParser.UINT8, false) => SignatureTypeCode.Byte,
+ (CILParser.UINT16, false) => SignatureTypeCode.UInt16,
+ (CILParser.UINT32, false) => SignatureTypeCode.UInt32,
+ (CILParser.UINT64, false) => SignatureTypeCode.UInt64,
+ (CILParser.INT8, true) => SignatureTypeCode.Byte,
+ (CILParser.INT16, true) => SignatureTypeCode.UInt16,
+ (CILParser.INT32_, true) => SignatureTypeCode.UInt32,
+ (CILParser.INT64_, true) => SignatureTypeCode.UInt64,
+ _ => throw new UnreachableException()
+ };
+
+ return (byte)typeCode;
+ }
+
+ internal byte GetNativeIntType() => (byte)SignatureTypeCode.IntPtr;
+
+ internal byte GetNativeUIntType() => (byte)SignatureTypeCode.UIntPtr;
+
+ internal TypeSpecificationValue CreateClassTypeSpecification(ClassNameValue className)
+ => new ClassTypeSpecificationValue(className);
+
+ internal TypeSpecificationValue CreateAssemblyTypeSpecification(string assemblyName)
+ => new AssemblyTypeSpecificationValue(assemblyName);
+
+ internal TypeSpecificationValue CreateModuleTypeSpecification(string moduleName)
+ => new ModuleTypeSpecificationValue(moduleName);
+
+ internal TypeSpecificationValue CreateSignatureTypeSpecification(TypeValue type)
+ => new SignatureTypeSpecificationValue(type);
+
+ internal int GetGenericArity(CILParser.GenArityNotEmptyContext? context)
+ => context?.Value ?? 0;
+
+ internal CalliSignatureValue CreateCalliSignature(
+ byte callingConvention,
+ TypeValue returnType,
+ ImmutableArray arguments)
+ => new CalliSignatureValue(
+ callingConvention,
+ returnType,
+ arguments);
+
+ private BlobBuilder MaterializeCalliSignature(CalliSignatureValue calliSignature)
+ {
+ BlobBuilder signature = new();
+ signature.WriteByte(calliSignature.CallingConvention);
+ ImmutableArray materializedArguments = MaterializeSignatureArguments(calliSignature.Arguments);
+ signature.WriteCompressedInteger(materializedArguments.Count(argument => !argument.IsSentinel));
+ MaterializeType(calliSignature.ReturnType).WriteContentTo(signature);
+ foreach (SignatureArg argument in materializedArguments)
+ {
+ argument.SignatureBlob.WriteContentTo(signature);
+ }
+
+ return signature;
+ }
+}
diff --git a/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Signatures.References.cs b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Signatures.References.cs
new file mode 100644
index 00000000000000..2080f0721e640d
--- /dev/null
+++ b/src/tools/ilasm/src/ILAssembler/Actions/GrammarActions.Signatures.References.cs
@@ -0,0 +1,212 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Linq;
+using System.Reflection.Metadata;
+using Antlr4.Runtime;
+
+namespace ILAssembler;
+
+#pragma warning disable CA1822 // Parser actions are invoked through the per-parser GrammarActions instance.
+internal sealed partial class GrammarActions
+{
+ internal MethodReferenceValue CreateMethodReference(
+ IToken token,
+ byte callingConvention,
+ TypeValue returnType,
+ TypeSpecificationValue? owner,
+ string name,
+ ImmutableArray? genericArguments,
+ int? genericArity,
+ ImmutableArray arguments)
+ => new ParsedMethodReferenceValue(
+ token,
+ callingConvention,
+ returnType,
+ owner,
+ name,
+ genericArguments,
+ genericArity.GetValueOrDefault(),
+ arguments);
+
+ internal MethodReferenceValue CreateTokenMethodReference(int token)
+ => new TokenMethodReferenceValue(token);
+
+ internal MethodReferenceValue CreateTypedefMethodReference(IToken token, string alias)
+ => new TypedefMethodReferenceValue(token, alias);
+
+ internal FieldReferenceValue CreateFieldReference(
+ TypeValue fieldType,
+ TypeSpecificationValue? owner,
+ string name)
+ => new ParsedFieldReferenceValue(
+ fieldType,
+ owner,
+ name);
+
+ internal FieldReferenceValue CreateTypedefFieldReference(IToken token, string alias)
+ => new TypedefFieldReferenceValue(token, alias);
+
+ internal MemberReferenceValue CreateMethodMemberReference(MethodReferenceValue method)
+ => new MethodMemberReferenceValue(method);
+
+ internal MemberReferenceValue CreateFieldMemberReference(FieldReferenceValue field)
+ => new FieldMemberReferenceValue(field);
+
+ internal MemberReferenceValue CreateTokenMemberReference(int token)
+ => new TokenMemberReferenceValue(token);
+
+ internal OwnerTypeValue CreateTypeOwner(TypeSpecificationValue type)
+ => new TypeOwnerValue(type);
+
+ internal OwnerTypeValue CreateMemberOwner(MemberReferenceValue member)
+ => new MemberOwnerValue(member);
+
+ private EntityRegistry.EntityBase MaterializeMethodReference(MethodReferenceValue methodReference)
+ {
+ switch (methodReference)
+ {
+ case TokenMethodReferenceValue token:
+ return ResolveMetadataToken(token.Token);
+ case TypedefMethodReferenceValue typedef:
+ if (TryResolveTypedefAsMember(typedef.Alias) is { } resolved)
+ {
+ return resolved;
+ }
+ ReportError(
+ DiagnosticIds.TypedefNotFound,
+ string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
+ typedef.Token);
+ return CreateErrorMethodReference(typedef.Alias);
+ case ParsedMethodReferenceValue parsed:
+ return MaterializeParsedMethodReference(parsed);
+ default:
+ return CreateErrorMethodReference("");
+ }
+ }
+
+ private EntityRegistry.EntityBase MaterializeParsedMethodReference(ParsedMethodReferenceValue methodReference)
+ {
+ byte callingConvention = methodReference.CallingConvention;
+ EntityRegistry.TypeEntity owner = methodReference.Owner is null
+ ? _entityRegistry.ModuleType
+ : ResolveTypeSpecification(methodReference.Owner);
+
+ BlobBuilder? methodSpecificationSignature = null;
+ int genericArity = methodReference.GenericArity;
+ if (methodReference.GenericArguments is { } genericArguments)
+ {
+ genericArity = genericArguments.Length;
+ if (genericArity != 0)
+ {
+ methodSpecificationSignature = new BlobBuilder();
+ methodSpecificationSignature.WriteByte((byte)SignatureKind.MethodSpecification);
+ MaterializeTypeArguments(genericArguments).WriteContentTo(methodSpecificationSignature);
+ }
+ }
+
+ if (genericArity != 0)
+ {
+ callingConvention |= (byte)SignatureAttributes.Generic;
+ }
+
+ if (_expectInstance && (callingConvention & (byte)SignatureAttributes.Instance) == 0)
+ {
+ ReportWarning(
+ DiagnosticIds.MissingInstanceCallConv,
+ DiagnosticMessageTemplates.MissingInstanceCallConv,
+ methodReference.Token);
+ callingConvention |= (byte)SignatureAttributes.Instance;
+ }
+
+ BlobBuilder signature = new();
+ signature.WriteByte(callingConvention);
+ if (genericArity != 0)
+ {
+ signature.WriteCompressedInteger(genericArity);
+ }
+
+ ImmutableArray arguments = MaterializeSignatureArguments(methodReference.Arguments);
+ signature.WriteCompressedInteger(arguments.Count(argument => !argument.IsSentinel));
+ MaterializeType(methodReference.ReturnType).WriteContentTo(signature);
+ foreach (SignatureArg argument in arguments)
+ {
+ argument.SignatureBlob.WriteContentTo(signature);
+ }
+
+ EntityRegistry.MemberReferenceEntity memberReference =
+ _entityRegistry.CreateLazilyRecordedMemberReference(owner, methodReference.Name, signature);
+ return methodSpecificationSignature is null
+ ? memberReference
+ : _entityRegistry.GetOrCreateMethodSpecification(memberReference, methodSpecificationSignature);
+ }
+
+ private EntityRegistry.EntityBase MaterializeFieldReference(FieldReferenceValue fieldReference)
+ {
+ switch (fieldReference)
+ {
+ case TypedefFieldReferenceValue typedef:
+ if (TryResolveTypedefAsMember(typedef.Alias) is { } resolved)
+ {
+ return resolved;
+ }
+ ReportError(
+ DiagnosticIds.TypedefNotFound,
+ string.Format(DiagnosticMessageTemplates.TypedefNotFound, typedef.Alias),
+ typedef.Token);
+ return CreateErrorFieldReference(typedef.Alias);
+ case ParsedFieldReferenceValue parsed:
+ BlobBuilder fieldType = MaterializeType(parsed.FieldType);
+ EntityRegistry.TypeEntity owner = parsed.Owner is null
+ ? _entityRegistry.ModuleType
+ : ResolveTypeSpecification(parsed.Owner);
+ BlobBuilder signature = new(fieldType.Count + 1);
+ signature.WriteByte((byte)SignatureKind.Field);
+ fieldType.WriteContentTo(signature);
+ return _entityRegistry.CreateLazilyRecordedMemberReference(owner, parsed.Name, signature);
+ default:
+ return CreateErrorFieldReference("