and ReflectionTools.Harmony v4.0.0
- Added
DynamicMethodHelper.Create
to createDynamicMethod
objects around a delegate type. - Added
EmitterExtensions
with type-safe extensions for emitting opcodes. - Added
ExceptionBlockBuilder
for more readable exception block emitting. - Added
AddLocal
andAddLabel
extensions so you don't have to remember which starts with 'Define' and which starts with 'Declare'. - Added
AddLazyLabel
which 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
Operators
class 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);
Operator
data structure
- Changes to TranspileContext
- Use
Emit[After/Below/BelowLast](Action<IOpCodeEmitter>)
orReplace(int, Action<IOpCodeEmitter>)
functions instead now.EmitBelowLast
is the same as the oldEmit(OpCode)
methods, where the current instruction is pushed down and it keeps it's labels and blocks.EmitAfter
moves the current instruction's labels to the first emitted instruction and includes it in it's block.EmitBelow
is 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
Prefixes
property 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;
}