Skip to content

Commit 62977ec

Browse files
committed
Implement label validation and typedef resolution in C# ilasm
- Consolidate instruction tokens into main grammar to establish proper lexer precedence over DOTTEDNAME and ID; reorder methodDecl alternatives to match instructions first; add custom attribute handling in method bodies - Implement visitor stubs for debug/symbol directives: VisitLanguageDecl, VisitEsHead, VisitExportHead, VisitExtSourceSpec, VisitFieldInit - Add typedef resolution in ResolveTypeDef for type aliases - Implement label validation: track declared vs referenced labels, report errors for undefined labels at method end - Allow recoverable errors in method bodies to still emit assembly - Add tests for label validation, typedef resolution, and error handling
1 parent d967910 commit 62977ec

File tree

6 files changed

+885
-74
lines changed

6 files changed

+885
-74
lines changed

src/tools/ilasm/src/ILAssembler/CIL.g4

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ The .NET Foundation licenses this file to you under the MIT license.
55

66
grammar CIL;
77

8-
import Instructions;
9-
108
tokens { IncludedFileEof, SyntheticIncludedFileEof }
119

1210
INT32: '-'? ('0x' [0-9A-Fa-f]+ | [0-9]+);
@@ -122,6 +120,22 @@ PP_ENDIF: '#endif';
122120
PP_INCLUDE: '#include';
123121
MRESOURCE: '.mresource';
124122

123+
// Instruction tokens MUST be defined before DOTTEDNAME and ID to ensure they take precedence
124+
// For example, "ldc.r8" must be recognized as INSTR_R token, not as DOTTEDNAME
125+
INSTR_NONE: ('nop'|'break'|'ldarg.0'|'ldarg.1'|'ldarg.2'|'ldarg.3'|'ldloc.0'|'ldloc.1'|'ldloc.2'|'ldloc.3'|'stloc.0'|'stloc.1'|'stloc.2'|'stloc.3'|'ldnull'|'ldc.i4.m1'|'ldc.i4.0'|'ldc.i4.1'|'ldc.i4.2'|'ldc.i4.3'|'ldc.i4.4'|'ldc.i4.5'|'ldc.i4.6'|'ldc.i4.7'|'ldc.i4.8'|'dup'|'pop'|'ret'|'ldind.i1'|'ldind.u1'|'ldind.i2'|'ldind.u2'|'ldind.i4'|'ldind.u4'|'ldind.i8'|'ldind.i'|'ldind.r4'|'ldind.r8'|'ldind.ref'|'stind.ref'|'stind.i1'|'stind.i2'|'stind.i4'|'stind.i8'|'stind.r4'|'stind.r8'|'add'|'sub'|'mul'|'div'|'div.un'|'rem'|'rem.un'|'and'|'or'|'xor'|'shl'|'shr'|'shr.un'|'neg'|'not'|'conv.i1'|'conv.i2'|'conv.i4'|'conv.i8'|'conv.r4'|'conv.r8'|'conv.u4'|'conv.u8'|'conv.r.un'|'throw'|'conv.ovf.i1.un'|'conv.ovf.i2.un'|'conv.ovf.i4.un'|'conv.ovf.i8.un'|'conv.ovf.u1.un'|'conv.ovf.u2.un'|'conv.ovf.u4.un'|'conv.ovf.u8.un'|'conv.ovf.i.un'|'conv.ovf.u.un'|'ldlen'|'ldelem.i1'|'ldelem.u1'|'ldelem.i2'|'ldelem.u2'|'ldelem.i4'|'ldelem.u4'|'ldelem.i8'|'ldelem.i'|'ldelem.r4'|'ldelem.r8'|'ldelem.ref'|'stelem.i'|'stelem.i1'|'stelem.i2'|'stelem.i4'|'stelem.i8'|'stelem.r4'|'stelem.r8'|'stelem.ref'|'conv.ovf.i1'|'conv.ovf.u1'|'conv.ovf.i2'|'conv.ovf.u2'|'conv.ovf.i4'|'conv.ovf.u4'|'conv.ovf.i8'|'conv.ovf.u8'|'ckfinite'|'conv.u2'|'conv.u1'|'conv.i'|'conv.ovf.i'|'conv.ovf.u'|'add.ovf'|'add.ovf.un'|'mul.ovf'|'mul.ovf.un'|'sub.ovf'|'sub.ovf.un'|'endfinally'|'stind.i'|'conv.u'|'prefix7'|'prefix6'|'prefix5'|'prefix4'|'prefix3'|'prefix2'|'prefix1'|'prefixref'|'arglist'|'ceq'|'cgt'|'cgt.un'|'clt'|'clt.un'|'localloc'|'endfilter'|'volatile.'|'tail.'|'cpblk'|'initblk'|'rethrow'|'refanytype'|'readonly.'|'illegal'|'endmac');
126+
INSTR_VAR: ('ldarg.s'|'ldarga.s'|'starg.s'|'ldloc.s'|'ldloca.s'|'stloc.s'|'ldarg'|'ldarga'|'starg'|'ldloc'|'ldloca'|'stloc');
127+
INSTR_I: ('ldc.i4.s'|'ldc.i4'|'unaligned.'|'no.');
128+
INSTR_I8: ('ldc.i8');
129+
INSTR_R: ('ldc.r4'|'ldc.r8');
130+
INSTR_METHOD: ('jmp'|'call'|'callvirt'|'newobj'|'ldftn'|'ldvirtftn');
131+
INSTR_SIG: ('calli');
132+
INSTR_BRTARGET: ('br.s'|'brfalse.s'|'brtrue.s'|'beq.s'|'bge.s'|'bgt.s'|'ble.s'|'blt.s'|'bne.un.s'|'bge.un.s'|'bgt.un.s'|'ble.un.s'|'blt.un.s'|'br'|'brfalse'|'brtrue'|'beq'|'bge'|'bgt'|'ble'|'blt'|'bne.un'|'bge.un'|'bgt.un'|'ble.un'|'blt.un'|'leave'|'leave.s');
133+
INSTR_SWITCH: ('switch');
134+
INSTR_TYPE: ('cpobj'|'ldobj'|'castclass'|'isinst'|'unbox'|'stobj'|'box'|'newarr'|'ldelema'|'ldelem'|'stelem'|'unbox.any'|'refanyval'|'mkrefany'|'initobj'|'constrained.'|'sizeof');
135+
INSTR_STRING: ('ldstr');
136+
INSTR_FIELD: ('ldfld'|'ldflda'|'stfld'|'ldsfld'|'ldsflda'|'stsfld');
137+
INSTR_TOK: ('ldtoken');
138+
125139
// ID needs to be last to ensure it doesn't take priority over other token types
126140
fragment IDSTART: [A-Za-z_#$@];
127141
fragment IDCONT: [A-Za-z0-9_#?$@`];
@@ -414,6 +428,7 @@ instr:
414428
| instr_r float64
415429
| instr_r int64
416430
| instr_r '(' bytes ')'
431+
| instr_r 'bytearray' '(' bytes ')' // Support bytearray syntax for floating point instructions
417432
| instr_brtarget int32
418433
| instr_brtarget id
419434
| instr_method methodRef
@@ -922,20 +937,20 @@ VTENTRY: '.vtentry';
922937
methodDecls: methodDecl*;
923938

924939
methodDecl:
925-
EMITBYTE int32
940+
instr // MOVED TO TOP - instructions must be matched first!
941+
| EMITBYTE int32
926942
| sehBlock
927943
| MAXSTACK int32
928944
| LOCALS sigArgs
929945
| LOCALS 'init' sigArgs
930946
| ENTRYPOINT
931947
| ZEROINIT
932948
| dataDecl
933-
| instr
934-
| id ':'
949+
| labelDecl
935950
| secDecl
936951
| extSourceSpec // Leave for later when I get to generating symbols.
937952
| languageDecl // Leave for later when I get to generating symbols.
938-
| customAttrDecl
953+
| customDescrInMethodBody // Only customDescr and customDescrWithOwner, NOT bare typedefs
939954
| compControl
940955
| EXPORT '[' int32 ']'
941956
| EXPORT '[' int32 ']' 'as' id
@@ -949,6 +964,12 @@ methodDecl:
949964
| PARAM CONSTRAINT dottedName ',' typeSpec customAttrDecl*
950965
| PARAM '[' int32 ']' initOpt customAttrDecl*;
951966

967+
labelDecl: id ':';
968+
969+
customDescrInMethodBody:
970+
customDescr
971+
| customDescrWithOwner;
972+
952973
scopeBlock: '{' methodDecls '}';
953974

954975
/* Structured exception handling directives */

src/tools/ilasm/src/ILAssembler/Diagnostic.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ public static class DiagnosticIds
4040
public const string ArgumentNotFound = "ILA0018";
4141
public const string LocalNotFound = "ILA0019";
4242
public const string TypedefNotFound = "ILA0020";
43+
public const string AbstractMethodNotInAbstractType = "ILA0021";
44+
public const string InvalidPInvokeSignature = "ILA0022";
45+
public const string MissingInstanceCallConv = "ILA0023";
46+
public const string DeprecatedNativeType = "ILA0024";
47+
public const string DeprecatedCustomMarshaller = "ILA0025";
48+
public const string UnsupportedSecurityDeclaration = "ILA0026";
49+
public const string GenericParameterIndexOutOfRange = "ILA0027";
50+
public const string UnknownGenericParameter = "ILA0028";
51+
public const string ParameterIndexOutOfRange = "ILA0029";
52+
public const string DuplicateMethod = "ILA0030";
4353
}
4454

4555
internal static class DiagnosticMessageTemplates
@@ -64,4 +74,14 @@ internal static class DiagnosticMessageTemplates
6474
public const string ArgumentNotFound = "Argument '{0}' not found";
6575
public const string LocalNotFound = "Local variable '{0}' not found";
6676
public const string TypedefNotFound = "Typedef '{0}' not found";
77+
public const string AbstractMethodNotInAbstractType = "Abstract method '{0}' cannot be declared in a non-abstract type";
78+
public const string InvalidPInvokeSignature = "Invalid P/Invoke signature: module name is required";
79+
public const string MissingInstanceCallConv = "Instance call convention required for method reference";
80+
public const string DeprecatedNativeType = "Native type '{0}' is deprecated";
81+
public const string DeprecatedCustomMarshaller = "The 4-string form of custom marshaller is deprecated";
82+
public const string UnsupportedSecurityDeclaration = "Individual SecurityAttribute permissions are not supported; use PermissionSet instead";
83+
public const string GenericParameterIndexOutOfRange = "Generic parameter index {0} is out of range";
84+
public const string UnknownGenericParameter = "Unknown generic parameter '{0}'";
85+
public const string ParameterIndexOutOfRange = "Parameter index {0} is out of range";
86+
public const string DuplicateMethod = "Duplicate method definition";
6787
}

src/tools/ilasm/src/ILAssembler/EntityRegistry.cs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,15 @@ public void WriteContentTo(MetadataBuilder builder, BlobBuilder ilStream, IReadO
177177
builder.GetOrAddString(type.Namespace),
178178
builder.GetOrAddString(type.Name),
179179
type.BaseType is null ? default : type.BaseType.Handle,
180-
(FieldDefinitionHandle)GetHandleForList(type.Fields, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Fields, i, TableIndex.Field),
181-
(MethodDefinitionHandle)GetHandleForList(type.Methods, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Methods, i, TableIndex.MethodDef));
180+
GetFieldHandleForList(type.Fields, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Fields, i),
181+
GetMethodHandleForList(type.Methods, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Methods, i));
182182

183183
builder.AddEventMap(
184184
(TypeDefinitionHandle)type.Handle,
185-
(EventDefinitionHandle)GetHandleForList(type.Events, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Events, i, TableIndex.Event));
185+
GetEventHandleForList(type.Events, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Events, i));
186186
builder.AddPropertyMap(
187187
(TypeDefinitionHandle)type.Handle,
188-
(PropertyDefinitionHandle)GetHandleForList(type.Properties, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Properties, i, TableIndex.Property));
188+
GetPropertyHandleForList(type.Properties, GetSeenEntities(TableIndex.TypeDef), type => ((TypeDefinitionEntity)type).Properties, i));
189189

190190
if (type.PackingSize is not null || type.ClassSize is not null)
191191
{
@@ -241,7 +241,7 @@ public void WriteContentTo(MetadataBuilder builder, BlobBuilder ilStream, IReadO
241241
builder.GetOrAddString(methodDef.Name),
242242
builder.GetOrAddBlob(methodDef.MethodSignature!),
243243
rva,
244-
(ParameterHandle)GetHandleForList(methodDef.Parameters, GetSeenEntities(TableIndex.MethodDef), method => ((MethodDefinitionEntity)method).Parameters, i, TableIndex.Param));
244+
GetParameterHandleForList(methodDef.Parameters, GetSeenEntities(TableIndex.MethodDef), method => ((MethodDefinitionEntity)method).Parameters, i));
245245

246246
if (methodDef.MethodImportInformation is not null)
247247
{
@@ -385,6 +385,36 @@ public void WriteContentTo(MetadataBuilder builder, BlobBuilder ilStream, IReadO
385385
builder.AddMethodSpecification(methodSpec.Parent.Handle, builder.GetOrAddBlob(methodSpec.Signature));
386386
}
387387

388+
static FieldDefinitionHandle GetFieldHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyList<EntityBase> listOwner, Func<EntityBase, IReadOnlyList<EntityBase>> getList, int ownerIndex)
389+
{
390+
var handle = GetHandleForList(list, listOwner, getList, ownerIndex, TableIndex.Field);
391+
return handle.IsNil ? default : (FieldDefinitionHandle)handle;
392+
}
393+
394+
static MethodDefinitionHandle GetMethodHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyList<EntityBase> listOwner, Func<EntityBase, IReadOnlyList<EntityBase>> getList, int ownerIndex)
395+
{
396+
var handle = GetHandleForList(list, listOwner, getList, ownerIndex, TableIndex.MethodDef);
397+
return handle.IsNil ? default : (MethodDefinitionHandle)handle;
398+
}
399+
400+
static PropertyDefinitionHandle GetPropertyHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyList<EntityBase> listOwner, Func<EntityBase, IReadOnlyList<EntityBase>> getList, int ownerIndex)
401+
{
402+
var handle = GetHandleForList(list, listOwner, getList, ownerIndex, TableIndex.Property);
403+
return handle.IsNil ? default : (PropertyDefinitionHandle)handle;
404+
}
405+
406+
static EventDefinitionHandle GetEventHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyList<EntityBase> listOwner, Func<EntityBase, IReadOnlyList<EntityBase>> getList, int ownerIndex)
407+
{
408+
var handle = GetHandleForList(list, listOwner, getList, ownerIndex, TableIndex.Event);
409+
return handle.IsNil ? default : (EventDefinitionHandle)handle;
410+
}
411+
412+
static ParameterHandle GetParameterHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyList<EntityBase> listOwner, Func<EntityBase, IReadOnlyList<EntityBase>> getList, int ownerIndex)
413+
{
414+
var handle = GetHandleForList(list, listOwner, getList, ownerIndex, TableIndex.Param);
415+
return handle.IsNil ? default : (ParameterHandle)handle;
416+
}
417+
388418
static EntityHandle GetHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyList<EntityBase> listOwner, Func<EntityBase, IReadOnlyList<EntityBase>> getList, int ownerIndex, TableIndex tokenType)
389419
{
390420
// Return the first entry in the list.
@@ -412,7 +442,8 @@ static EntityHandle GetHandleForList(IReadOnlyList<EntityBase> list, IReadOnlyLi
412442
return MetadataTokens.EntityHandle(tokenType, MetadataTokens.GetRowNumber(otherList[otherList.Count - 1].Handle) + 1);
413443
}
414444
}
415-
return MetadataTokens.EntityHandle(tokenType, 0);
445+
// If all lists are empty, return a nil handle
446+
return default(EntityHandle);
416447
}
417448
}
418449

0 commit comments

Comments
 (0)