Skip to content

Releases: DanielWillett/ReflectionTools

ReflectionTools v4.0.0

08 Sep 06:40
ab5cf08

Choose a tag to compare

and ReflectionTools.Harmony v4.0.0

  • Added DynamicMethodHelper.Create to create DynamicMethod objects around a delegate type.
  • Added EmitterExtensions with type-safe extensions for emitting opcodes.
  • Added ExceptionBlockBuilder for more readable exception block emitting.
  • Added AddLocal and AddLabel 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 using MarkLabel.
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
{ 
    // 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"
}
  • Changes to TranspileContext
    • Use Emit[After/Below/BelowLast](Action<IOpCodeEmitter>) or Replace(int, Action<IOpCodeEmitter>) functions instead now.
      • EmitBelowLast is the same as the old Emit(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.
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

09 Jun 05:15
ff58690

Choose a tag to compare

  • Added TryMarkLabel to EmitUtility which marks a nullable label if it has a value.
  • Fixed bug with DefaultOpCodeFormatter which causes it to throw an error when there's a nested type as a parameter of a generic type.

ReflectionTools v3.2.0

26 May 07:40
8378756

Choose a tag to compare

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

02 May 16:22
08c812e

Choose a tag to compare

  • 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 DebuggableEmitter when used with method builders.

ReflectionTools v3.0.0

01 May 03:26
f8f0d52

Choose a tag to compare

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 IOpCodeFormatter interface or at Accessor.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<,>, and IStaticVariable<>.

Harmony Module

  • TranspilerContext for easier transpiling with a style similar to ILGenerator, which implements IOpCodeEmitter.
  • HarmonyLog class for easily managing an auto-wiping harmony debug log.
  • Some methods relying on Lib.Harmony are moved to a new PatchUtility class.