Releases: DanielWillett/ReflectionTools
Releases · DanielWillett/ReflectionTools
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;
}ReflectionTools v3.2.1
- Added
TryMarkLabeltoEmitUtilitywhich marks a nullable label if it has a value. - Fixed bug with
DefaultOpCodeFormatterwhich causes it to throw an error when there's a nested type as a parameter of a generic type.
ReflectionTools v3.2.0
Added bool Accessor.TrySetUnderlyingArray<T>(this List<T> list, T[] underlyingArray, int size) and void Accessor.SetUnderlyingArray<T>(this List<T> list, T[] underlyingArray, int size) for setting the underlying array and size of an existing list (so long as it has a _size and _items field.
ReflectionTools v3.1.0
- Added generic overloads Format(...) to Format(Type, ...) to .NET standard 2.1+, .NET Core, and .NET targets only to avoid breaking API change in
IOpCodeFormatter. - Added a catch for NotSupportedExceptions thrown by the Initialize method of
DebuggableEmitterwhen used with method builders.
ReflectionTools v3.0.0
Major API-breaking update including many changes and clean-ups:
Core
- Variety of small fixes
- Full suite of string formatting tools for both existing members and dynamically created members, along with emitted op-codes available in the
IOpCodeFormatterinterface or atAccessor.Formatter/Accessor.ExceptionFormatter. - Fluent API for creating a OOP representation of any method, type, field, property, or event your heart desires, which can be used by string formatting or for your own libraries.
- Lib.Harmony dependency for .NET Standard 2.1 was split off into it's own library, both to clean up this library and because redist of Lib.Harmony is no longer needed for .NET Standard 2.1 support with the recent update to Lib.Harmony, version 2.3.3.
- Accessor and logging is now available as an interface for dependency injection support.
- IOpCodeEmitter interface for abstracting
ILGenerator.- DebuggableEmitter class which can write instructions to the logger as they're emitted for debugging purposes.
- IVariable abstraction merging fields and properties into one type, also added are generic strongly-typed variants,
IInstanceVariable<,>, andIStaticVariable<>.
Harmony Module
- TranspilerContext for easier transpiling with a style similar to
ILGenerator, which implementsIOpCodeEmitter. - HarmonyLog class for easily managing an auto-wiping harmony debug log.
- Some methods relying on Lib.Harmony are moved to a new
PatchUtilityclass.
{ // 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" }