ReflectionTools v4.0.0
and ReflectionTools.Harmony v4.0.0
- Added
DynamicMethodHelper.Createto createDynamicMethodobjects around a delegate type. - Added
EmitterExtensionswith type-safe extensions for emitting opcodes. - Added
ExceptionBlockBuilderfor more readable exception block emitting. - Added
AddLocalandAddLabelextensions so you don't have to remember which starts with 'Define' and which starts with 'Declare'. - Added
AddLazyLabelwhich returns a label that isn't declared until it's first use (Lazy<Label>). It can also be marked usingMarkLabel.
DynamicMethodInfo<Func<int, int, int>> dynMethod = DynamicMethodHelper.Create<Func<int, int, int>>("TryDivide");
IOpCodeEmitter emit = dynMethod.GetEmitter();
emit.AddLocal<int>(out LocalBuilder lclResult)
.Try(emit =>
{
emit.LoadArgument(0)
.LoadArgument(1)
.Divide()
.SetLocalValue(lclResult);
})
.Catch<DivideByZeroException>(emit =>
{
emit.PopFromStack() // pop exception object
.SetLocalToDefaultValue<int>(lclResult);
})
.End()
.LoadLocalValue(lclResult)
.Return();- Added
Operatorsclass with utilities for working with user-defined operators and conversions.
// math and logic operatorse
// BigInteger + BigInteger operator
MethodInfo? addOperator = Operators.Find<BigInteger>(OperatorType.Addition, preferCheckedOperator: true);
// BigInteger == long operator
MethodInfo? equalsInt64Operator = Operators.Find<BigInteger, long>(OperatorType.Equality);
// Operator data structure
// gets basic info about op_LessThan '<', see below.
Operator subtractInfo = Operators.GetOperator(OperatorTypes.LessThan);
// returns "op_UnaryNegation"
string negateOperatorMethodName = Operators.UnaryNegation.MethodName;
// returns true
bool hasOneArgument = Operators.Decrement.IsUnary;
// returns true
bool canBeChecked = Operators.GetOperator(OperatorTypes.Addition).CanDefineCheckedVariant;
// conversion operators
// conversion of Span<byte> to ReadOnlySpan<byte>
MethodInfo? spanToReadOnlySpanCast = Operators.FindCast<Span<byte>, ReadOnlySpan<byte>>();
// conversion of string to ReadOnlySpan<char>
MethodInfo? stringToReadOnlySpanCast = Operators.FindCast<string, ReadOnlySpan<char>>();
// conversion of Int128 to byte with overflow check (if it exists, otherwise the unchecked one is returned)
MethodInfo? ovfInt128ToByteCast = Operators.FindCast<Int128, byte>(preferCheckedOperator: true);Operatordata structure
- Changes to TranspileContext
- Use
Emit[After/Below/BelowLast](Action<IOpCodeEmitter>)orReplace(int, Action<IOpCodeEmitter>)functions instead now.EmitBelowLastis the same as the oldEmit(OpCode)methods, where the current instruction is pushed down and it keeps it's labels and blocks.EmitAftermoves the current instruction's labels to the first emitted instruction and includes it in it's block.EmitBelowis new and inserts instructions after the current instruction, leaving it where it is.- Older
Emit(OpCode)methods were marked obsolete in favor of these new methods.
- Skips over prefix instructions, plus has a
Prefixesproperty now which enumerates all the prefixes on the current instruction (usually none). - Handles blocks better.
- Blocks are extended to include prefixes on the starting instruction and the prefixed instruction if the ending instruction is a prefix.
- Use
public void TranspilerTarget()
{
if (1 == int.Parse("2"))
return;
Console.WriteLine("Test {0}", "Test2");
}
public static IEnumerable<CodeInstruction> WriteInstructions(IEnumerable<CodeInstruction> instructions, MethodBase method, ILGenerator generator)
{
TranspileContext ctx = new TranspileContext(method, generator, instructions);
MethodInfo? logInfo = typeof(IReflectionToolsLogger).GetMethod("LogInfo", BindingFlags.Public | BindingFlags.Instance, null, [ typeof(string), typeof(string) ], null);
if (logInfo == null)
{
return ctx.Fail(new MethodDefinition("LogInfo")
.DeclaredIn<IReflectionToolsLogger>(false)
.WithParameter<string>("source")
.WithParameter<string>("message")
);
}
MethodInfo? getLogger = typeof(Accessor).GetProperty("Logger", BindingFlags.Public | BindingFlags.Static)?.GetMethod;
if (getLogger == null)
{
return ctx.Fail(new PropertyDefinition("Logger")
.DeclaredIn(typeof(Accessor), true)
.WithNoSetter()
);
}
while (ctx.MoveNext())
{
if (PatchUtility.TryReplacePattern(ctx,
emit =>
{
emit.Invoke(getLogger)
.LoadConstantString("Test Source")
.LoadConstantString("Test Value")
.Invoke(logInfo);
},
new PatternMatch[]
{
x => x.LoadsConstant("Test {0}"),
x => x.LoadsConstant("Value"),
null
}
))
{
ctx.LogDebug("Patched arguments to LogInfo.");
}
}
return ctx;
}
{ // enum value "Type": LessThan, // this operator must also be present if LessThan is present "RequiredPair": GreaterThan, // only has one argument "IsUnary": false, // only has two arguments "IsBinary": true, // is the 'checked' keyword valid on this operator "CanDefineCheckedVariant": false, // name of the underlying method "MethodName": "op_LessThan" }