Skip to content

Commit

Permalink
Clean up auto-generated doc comments for ILOpcodes
Browse files Browse the repository at this point in the history
  • Loading branch information
nike4613 committed Jun 10, 2024
1 parent 5ff9a73 commit 128f82d
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 48 deletions.
54 changes: 34 additions & 20 deletions src/MonoMod.SourceGen.Internal/Cil/ILOverloadGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private sealed record ParsedDefFile(
EquatableArray<SkipDefSet> SkipDefs,
EquatableArray<SkipDefSet> SkipBaseOnlyDefs,
EquatableArray<OpcodeDef> SForms,
EquatableArray<(OpcodeDef Op, string Doc)> Opcodes);
EquatableArray<(OpcodeDef Op, string? Doc)> Opcodes);

public void Initialize(IncrementalGeneratorInitializationContext context)
{
Expand Down Expand Up @@ -146,7 +146,7 @@ private static (string Path, ParsedDefFile Defs) ParseDefsFile((string Path, Sou
using var reader = new SourceTextReader(text.Text);

using var usingsBuilder = ImmutableArrayBuilder<string>.Rent();
using var opcodeDefsBuilder = ImmutableArrayBuilder<(OpcodeDef, string)>.Rent();
using var opcodeDefsBuilder = ImmutableArrayBuilder<(OpcodeDef, string?)>.Rent();
using var valueTypesBuilder = ImmutableArrayBuilder<string>.Rent();
using var sformsBuilder = ImmutableArrayBuilder<OpcodeDef>.Rent();

Expand Down Expand Up @@ -275,7 +275,7 @@ private static (string Path, ParsedDefFile Defs) ParseDefsFile((string Path, Sou
}
}

static OpcodeDef? ParseOpcodeLine(string line, out string doc)
static OpcodeDef? ParseOpcodeLine(string line, out string? doc)
{
var spcIdx = line.IndexOf(' ');
var tslashIdx = spcIdx < 0 ? -1 : line.IndexOf("///", spcIdx, StringComparison.Ordinal);
Expand All @@ -291,8 +291,7 @@ private static (string Path, ParsedDefFile Defs) ParseDefsFile((string Path, Sou
if (string.IsNullOrEmpty(argType))
argType = null;
doc = line.Substring(tslashIdx < line.Length ? tslashIdx + 3 : tslashIdx).Trim();
if (string.IsNullOrEmpty(doc))
doc = $"<see cref=\"OpCodes.{opcodeName}\"/>";
if (string.IsNullOrEmpty(doc)) doc = null;

return new OpcodeDef(opcodeName, opcodeName.Replace("_", ""), argType);
}
Expand Down Expand Up @@ -371,6 +370,16 @@ out ImmutableArray<OpcodeDef> skipBaseOnlies
skipBaseOnlies = ImmutableArray.Create<OpcodeDef>();
}

private static string GetOpcodeDoc(OpcodeDef def, bool hasSForm)
{
var str = $"<see cref=\"OpCodes.{def.Opcode}\"/>";
if (hasSForm)
{
str += $" or <see cref=\"OpCodes.{def.Opcode}_S\"/>";
}
return str;
}

private static void GenerateCursorKind(SourceProductionContext spc, (TypeWithEmitOverloads type, ParsedDefFile defs) t)
{
var (type, defs) = t;
Expand All @@ -384,15 +393,18 @@ private static void GenerateCursorKind(SourceProductionContext spc, (TypeWithEmi
type.Type.AppendEnterContext(builder);
GetConversionsAndSkips(type, defs, out var conversions, out var skips, out _);

foreach (var (op, doc) in defs.Opcodes)
foreach (var (op, explDoc) in defs.Opcodes)
{
if (skips.Contains(op))
continue;

var doc = explDoc?? GetOpcodeDoc(op, defs.SForms.AsImmutableArray().Contains(op));

if (op.ArgumentType is null)
{
_ = builder
.WriteLine($"""/// <summary>Emits a {doc} opcode to the current cursor position.</summary>""")
.WriteLine("/// <returns>this</returns>")
.WriteLine($"/// <summary>Emits a {doc} opcode to the current cursor position.</summary>")
.WriteLine("/// <returns><see langword=\"this\"/></returns>")
.WriteLine($"public {type.Type.InnermostType.FqName} Emit{op.Formatted}() => _Insert(IL.Create(OpCodes.{op.Opcode}));")
.WriteLine();
}
Expand All @@ -417,11 +429,11 @@ private static void GenerateCursorKind(SourceProductionContext spc, (TypeWithEmi
static void EmitMethodWithArg(CodeBuilder builder, string selfFqName, OpcodeDef op, string doc, string argType, string targetType, string argExpr)
{
_ = builder
.WriteLine($"/// <summary>Emits a {doc} opcode with a <see cref=\"T:{argType}\"/> operand to the current cursor position.</summary>")
.WriteLine($"/// <summary>Emits a {doc} opcode with a <see cref=\"{argType}\"/> operand to the current cursor position.</summary>")
.Write("""/// <param name="operand">The emitted instruction's operand.""");
if (argType != targetType)
{
_ = builder.Write($$""" Will be automatically converted to a <see cref="T:{{targetType}}" />.""");
_ = builder.Write($$""" Will be automatically converted to a <see cref="{{targetType.Trim(['[', ']'])}}" />.""");
}
_ = builder
.WriteLine("</param>")
Expand Down Expand Up @@ -451,15 +463,17 @@ private static void GenerateMatcherKind(SourceProductionContext spc, (TypeWithEm

GetConversionsAndSkips(type, defs, out var conversions, out var skips, out var skipBaseOnlies);

foreach (var (op, doc) in defs.Opcodes)
foreach (var (op, explDoc) in defs.Opcodes)
{
if (skips.Contains(op))
continue;

var hasSForm = defs.SForms.AsImmutableArray().Contains(op);

var normalCond = $"global::MonoMod.Utils.Helpers.ThrowIfNull(instr).OpCode == OpCodes.{op.Opcode}";
var sformCond = defs.SForms.AsImmutableArray().Contains(op)
? $" || instr.OpCode == OpCodes.{op.Opcode}_S"
: "";
var sformCond = hasSForm ? $" || instr.OpCode == OpCodes.{op.Opcode}_S" : "";

var doc = explDoc ?? GetOpcodeDoc(op, hasSForm);

var matchCond = normalCond + sformCond;

Expand Down Expand Up @@ -488,22 +502,22 @@ private static void GenerateMatcherKind(SourceProductionContext spc, (TypeWithEm
.WriteLine("/// <returns><see langword=\"true\"/> if the instruction matches; <see langword=\"false\"/> otherwise.</returns>")
.WriteLine($"public static bool Match{op.Formatted}(this Instruction instr, [global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out {op.ArgumentType} value)")
.OpenBlock()
.WriteLine($"if ({matchCond})")
.WriteLine($"if (({matchCond}) && instr.Operand is {op.ArgumentType} op)")
.OpenBlock()
.WriteLine($"value = ({op.ArgumentType})instr.Operand;")
.WriteLine("return true;")
.WriteLine("value = op;")
.WriteLine("return true;")
.CloseBlock()
.WriteLine("else")
.OpenBlock()
.WriteLine("value = default;")
.WriteLine("return false;")
.WriteLine("value = default;")
.WriteLine("return false;")
.CloseBlock()
.CloseBlock()
.WriteLine();
}

_ = builder
.WriteLine($"/// <summary>Matches an instruction with opcode {doc} .</summary>")
.WriteLine($"/// <summary>Matches an instruction with opcode {doc}.</summary>")
.WriteLine("/// <param name=\"instr\">The instruction to try to match.</param>")
.WriteLine("/// <param name=\"value\">The operand value required for the instruction to match.</param>")
.WriteLine("/// <returns><see langword=\"true\"/> if the instruction matches; <see langword=\"false\"/> otherwise.</returns>")
Expand Down
44 changes: 22 additions & 22 deletions src/MonoMod.Utils/Cil/ILOpcodes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,31 @@ double
#
# This definition is for ILCursor generation. They will only be used for that.
[Conversions ILCursor]
Type -> TypeReference : Context.Import(operand)
Type -> IMetadataTokenProvider : Context.Import(operand)
FieldInfo -> FieldReference : Context.Import(operand)
FieldInfo -> IMetadataTokenProvider : Context.Import(operand)
MethodBase -> MethodReference : Context.Import(operand)
MethodBase -> IMetadataTokenProvider : Context.Import(operand)
uint -> int : unchecked((int)operand)
ulong -> long : unchecked((long)operand)
Instruction -> ILLabel : MarkLabel(operand)
Instruction[] -> ILLabel[] : operand.Select(MarkLabel).ToArray()
Type -> TypeReference : Context.Import(operand)
Type -> IMetadataTokenProvider : Context.Import(operand)
FieldInfo -> FieldReference : Context.Import(operand)
FieldInfo -> IMetadataTokenProvider : Context.Import(operand)
MethodBase -> MethodReference : Context.Import(operand)
MethodBase -> IMetadataTokenProvider : Context.Import(operand)
uint -> int : unchecked((int)operand)
ulong -> long : unchecked((long)operand)
Instruction -> ILLabel : MarkLabel(operand)
Instruction[] -> ILLabel[] : operand.Select(MarkLabel).ToArray()

# ILMatcher definitions don't have a convertExpr, because we don't need to create
# instances of the target type. Instead, we comparee against them, using IsEquivalent
# instances of the target type. Instead, we compare against them, using IsEquivalent
# definitions in the type being generated into.
[Conversions ILMatcher]
Type -> TypeReference :
Type -> IMetadataTokenProvider :
FieldInfo -> FieldReference :
FieldInfo -> IMetadataTokenProvider :
MethodBase -> MethodReference :
MethodBase -> IMetadataTokenProvider :
uint -> int :
ulong -> long :
Instruction -> ILLabel :
Instruction[] -> ILLabel[] :
Type -> TypeReference :
Type -> IMetadataTokenProvider :
FieldInfo -> FieldReference :
FieldInfo -> IMetadataTokenProvider :
MethodBase -> MethodReference :
MethodBase -> IMetadataTokenProvider :
uint -> int :
ulong -> long :
Instruction -> ILLabel :
Instruction[] -> ILLabel[] :

# Skip definitions.
# These are Opcode definitions which will get skipped for the respective generator kinds.
Expand Down Expand Up @@ -83,7 +83,7 @@ Ldloc VariableReference
Ldloca VariableReference
Stloc VariableReference

# S-Form list
# S-Form list (instructions which have _S variants)
[SForm]
Ldarg int
Ldarg ParameterReference
Expand Down
38 changes: 33 additions & 5 deletions src/MonoMod.Utils/Cil/ILPatternMatchingExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,20 @@ public static bool MatchLdarg(this Instruction instr, out int value)
}
}

private static int ParameterToIndex(object? obj)
=> obj switch
{
null => 0,
int i => i,
short i => i,
uint i => (int)i,
ushort i => i,
byte i => i,
sbyte i => i,
ParameterReference pr => pr.Index,
_ => throw new InvalidCastException(),
};

/// <summary>Matches an instruction with opcode <see cref="OpCodes.Starg"/>.</summary>
/// <param name="instr">The instruction to try to match.</param>
/// <param name="value">The operand value of the instruction.</param>
Expand All @@ -223,7 +237,7 @@ public static bool MatchStarg(this Instruction instr, out int value)
Helpers.ThrowIfArgumentNull(instr);
if (instr.OpCode == OpCodes.Starg || instr.OpCode == OpCodes.Starg_S)
{
value = ((ParameterReference)instr.Operand).Index;
value = ParameterToIndex(instr.Operand);
return true;
}
else
Expand All @@ -242,7 +256,7 @@ public static bool MatchLdarga(this Instruction instr, out int value)
Helpers.ThrowIfArgumentNull(instr);
if (instr.OpCode == OpCodes.Ldarga || instr.OpCode == OpCodes.Ldarga_S)
{
value = ((ParameterReference)instr.Operand).Index;
value = ParameterToIndex(instr.Operand);
return true;
}
else
Expand All @@ -252,6 +266,20 @@ public static bool MatchLdarga(this Instruction instr, out int value)
}
}

private static int VarToIndex(object? obj)
=> obj switch
{
null => 0,
int i => i,
short i => i,
uint i => (int)i,
ushort i => i,
byte i => i,
sbyte i => i,
VariableReference vr => vr.Index,
_ => throw new InvalidCastException(),
};

/// <summary>Matches an instruction with opcode <see cref="OpCodes.Ldloc"/>.</summary>
/// <param name="instr">The instruction to try to match.</param>
/// <param name="value">The operand value of the instruction.</param>
Expand All @@ -261,7 +289,7 @@ public static bool MatchLdloc(this Instruction instr, out int value)
Helpers.ThrowIfArgumentNull(instr);
if (instr.OpCode == OpCodes.Ldloc || instr.OpCode == OpCodes.Ldloc_S)
{
value = ((VariableReference)instr.Operand).Index;
value = VarToIndex(instr.Operand);
return true;
}
else if (instr.OpCode == OpCodes.Ldloc_0)
Expand Down Expand Up @@ -300,7 +328,7 @@ public static bool MatchStloc(this Instruction instr, out int value)
Helpers.ThrowIfArgumentNull(instr);
if (instr.OpCode == OpCodes.Stloc || instr.OpCode == OpCodes.Stloc_S)
{
value = ((VariableReference)instr.Operand).Index;
value = VarToIndex(instr.Operand);
return true;
}
else if (instr.OpCode == OpCodes.Stloc_0)
Expand Down Expand Up @@ -339,7 +367,7 @@ public static bool MatchLdloca(this Instruction instr, out int value)
Helpers.ThrowIfArgumentNull(instr);
if (instr.OpCode == OpCodes.Ldloca || instr.OpCode == OpCodes.Ldloca_S)
{
value = ((VariableReference)instr.Operand).Index;
value = VarToIndex(instr.Operand);
return true;
}
else
Expand Down
2 changes: 1 addition & 1 deletion tools/Common.CS.props
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<NewCecilVersion>0.11.5</NewCecilVersion>
<OldCecilVersion>0.10.4</OldCecilVersion>

<RoslynVersion>4.8.0</RoslynVersion>
<RoslynVersion>4.9.2</RoslynVersion>
<MSBuildRequiredVersion>17.8.3</MSBuildRequiredVersion>
</PropertyGroup>

Expand Down

0 comments on commit 128f82d

Please sign in to comment.