GithubHelp home page GithubHelp logo

inlineil.fody's Introduction

๐Ÿ‘‹ Hey there, I'm Lucas. I work at ABC Arbitrage on our .NET trading platform. Some topics I like:

  • Low-level stuff in C#
  • High-performance code and optimization
  • Language parsing and processing, compilers

Check out some of my projects below. ๐Ÿ™‚

inlineil.fody's People

Contributors

ltrzesniewski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

inlineil.fody's Issues

Error in .NET 7 Preview 7

This is the same method we use in Disruptor and have discussed a lot recently.

ArrayUtil.cs(91, 13): [] Fody/InlineIL: Unexpected instruction, expected newarr or call to Array.Empty but was: IL_0003: call !!0[] System.GC::AllocateUninitializedArray<InlineIL.LocalVar>(System.Int32,System.Boolean) - InlineIL requires that arguments to IL-emitting methods be constructed in place. (in T& ArrayUtil::UnsafeGetAt(T[],System.Int32) at instruction IL_0003: call !!0[] System.GC::AllocateUninitializedArray<InlineIL.LocalVar>(System.Int32,System.Boolean))
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static ref T UnsafeGetAt<T>(this T[] array, int index)
        {
            IL.DeclareLocals(false, typeof(T[]).MakeByRefType());
            Ldarg_0();
            Stloc_0(); // convert the object pointer to a byref
            Ldloc_0(); // load the object pointer as a byref

            Ldarg(nameof(index));
            Conv_U(); // zero extend

            Sizeof(typeof(T));
            Mul(); // index x sizeof(T)

            Call(MethodRef.PropertyGet(typeof(ArrayUtil), nameof(ArrayDataOffset)));
            
            Add(); // index x sizeof(T) +  ArrayDataOffset

            Add(); // array + index x sizeof(T) +  ArrayDataOffset

            Ret();

            throw IL.Unreachable();
        }

IL.Push(expression) leads to invalid program

.NET SDK version: 5.0.202
InlineIL.Fody version: 1.6.0
Command line options: dotnet build -c Release

Example:

struct MyStruct
{
    public int int32;
    public object obj;
}

static ref MyStruct M(int i) => throw new NotImplementedException();

static void Add()
{
    ref MyStruct a = ref M(-1);
    ref MyStruct b = ref M(0);
    Push(a.int32);
    Push(b.int32);
    Emit.Add();
    Pop(out int result);
    a.int32 = result;
}

Add compiles to:

.method private hidebysig static 
	void Add () cil managed 
{
	.maxstack 2
	.locals init (
		[0] valuetype MyStruct& a,
		[1] int32 result
	)

	IL_0000: ldc.i4.m1
	IL_0001: call      valuetype MyStruct& C::M(int32)
	IL_0006: stloc.0
	IL_0007: ldc.i4.0
	IL_0008: call      valuetype MyStruct& C::M(int32)
	IL_000D: ldloc.0
	IL_000E: ldfld     int32 MyStruct::'int32'
	IL_0013: ldfld     int32 MyStruct::'int32'
	IL_0018: add
	IL_0019: stloc.1
	IL_001A: ldloc.0
	IL_001B: ldloc.1
	IL_001C: stfld     int32 MyStruct::'int32'
	IL_0021: ret
} // end of method C::Add

Notice there are 2 ldfld instructions at 000E and 0013.

Support .NET Standard 1.1

I think you can support the netstandard1.1 framework profile by just adding this target framework to the InlineIL project, a one-line change. I only needed to drop support to 1.4, but just happened to try 1.1 too.
This opens up usage of the project under some some reduced functionality (sandboxed) environments.

I made a copy of that (sans t4) in my repo and linked against it. Fody appears to be able to process it properly.

This would also inadvertently, indirectly support .NET 4.5 and other downlevel platforms.

Proposal: macroassembler-like support

Hi Lucas!

Now I'm working on the set of factory methods that allow to create delegates from function pointers:

public static unsafe Action? CreateDelegate(delegate*<void> ptr)
        {
            if (ptr == null)
                return null;

            Ldnull();
            Push(ptr);
            Newobj(Constructor(Type<Action>(), Type<object>(), Type<IntPtr>()));
            return Return<Action>();
        }

public static unsafe Action<T>? CreateDelegate<T>(delegate*<T, void> ptr)
        {
            if (ptr == null)
                return null;

            Ldnull();
            Push(ptr);
            Newobj(Constructor(Type<Action<T>>(), Type<object>(), Type<IntPtr>()));
            return Return<Action<T>>();
        }

As you can see, I have to use code duplication. I would like to propose the support of macros by your library. The macro is a method with the following restrictions:

  • Must be private (or internal ??), static
  • Must be marked with ILMacroAttribute which is declared in your library
  • Must not be async (can be detected using presence of S.R.CS.AsyncStateMachineAttribute attribute)
  • Must not be generator (can be detected using presence of S.R.CS.IteratorStateMachineAttribute attribute)
  • The body of the macro is always inlined during code transformation
  • Must be removed during code transformation

With the macro functionality, I'll be able to rewrite the methods shown above as follows:

[ILMacro]
private static unsafe TDelegate? CreateDelegateMacro<TDelegate>(void* ptr)
  where TDelegate : Delegate
{
  if (ptr == null)
    return null;

            Ldnull();
            Push(ptr);
            Newobj(Constructor(Type<TDelegate>(), Type<object>(), Type<IntPtr>()));
            return Return<TDelegate>();
}

public static unsafe Action? CreateDelegate(delegate*<void> ptr)
  => CreateDelegateMacro<Action>(ptr);

public static unsafe Action<T>? CreateDelegate<T>(delegate*<T, void> ptr)
  => CreateDelegateMacro<Action<T>(ptr);

I understand that the feature requires a lot of efforts.

InlineIL produces corrupted portable PDB file

I trying to build symbol package in Release mode for my project and attach it to the main package. The produced output of dotnet pack -c Release consists of two files: .nupkg and .snupkg. My library uses InlineIL for implementation of several methods. However, NuGet telling me that your PDB file is not portable
image
If I remove InlineIL.Fody from csproj and leave other weavers then everything is fine. I've tried different combinations but without success.

Workaround: At this moment I decided to use <DebugType>embedded</DebugType> for my project.

[Proposal] Referencing user-defined operators

MethodRef contains brilliant static methods for referencing property getter/setter, constructor etc. But there is no way to reference user-defined operators, such as op_Implicit. Could you please add methods for that?

[Proposal] Simplified ldftn/ldvirtftn

Hi Lucas!

I would like to propose simplified way to use ldftn and ldvirtftn instructions:

public static void Ldftn<TMethod>(TMethod method) where TMethod : Delegate;
public static void Ldvirtftn<TMethod>(TMethod method) where TMethod : Delegate;

With these methods I can specify method directly without construction of MethodRef. In case of ldvirtftn it easily to analyze whether the specified method is virtual/abstract or not.

Alternative approach is to use factory method declared in MethodRef class:

public static MethodRef Create<TMethod>(TMethod method) where TMethod : Delegate;

Usage Suggestion

This is a nice tool that I have used successfully. However, my usage required that I comment out the validation for the definition of locals and then removed the local index translation calls. I wanted to replace a single expression in a method that already had a lot of C#. My IL leveraged variables already existing. I used ILSpy to identify the index values. A proposed feature might be to allow a user to add a list of names of locals they already know to be present. Once the method is compiled we should be able to find the variable names in the compiled metadata and match up the names and identify the index from the locals metadata. I don't know enough about Fody or your assembly to know if this is possible, but I figure that Fody is an injector using Mono and works after the assembly has been compiled, but I could be mistaken and maybe this idea is too much work.

My usage is below. I thought that fewer instructions would be faster, it was about 15% slower, no doubt because of the types of opcodes I'm using. The compiler generated many more instructions, but most were arithmetic, which, I guess, is very fast compared with using the evaluation stack. I guess that is why it is the compiler we use, because it really does know how to optimize.

image

image

image

Loading of invalid method token

Hi Lucas!

I found interesting issue when trying to obtain RuntimeMethodHandle:

Ldtoken(PropertyGet(Type<IReadOnlyList<T>>(), "Item"));
Pop(out RuntimeMethodHandle handle);
var getter = MethodBase.GetMethodFromHandle(handle);

The exception is

System.ArgumentException : Cannot resolve method Int64 get_Item(Int32) because the declaring type of the method handle System.Collections.Generic.IReadOnlyList`1[T] is generic. Explicitly provide the declaring type to GetMethodFromHandle.

There are two possible roots of this problem:

  • Incorrect code gen by InlineIL
  • Bug in CoreCLR

Issue with calling T[] Span<T>.ToArray()

The following code produces an error that indicates there's an issue with either this library or Mono.Cecil (or I've severly misunderstood the error):

IL.DeclareLocals(true, new LocalVar(typeof(Span<int>)));
IL.Emit.Sizeof<int>();
IL.Emit.Ldc_I4(10);
IL.Emit.Mul();
IL.Emit.Conv_U();
IL.Emit.Localloc();
IL.Emit.Ldc_I4(10);
IL.Emit.Newobj(MethodRef.Constructor(typeof(Span<int>), typeof(void).MakePointerType(), typeof(int)));
IL.Emit.Stloc_0();
IL.Emit.Ldloca(0);
IL.Emit.Call(MethodRef.Method(typeof(Span<int>), "ToArray", TypeRef.TypeGenericParameters[0].MakeArrayType(), 0, Array.Empty<TypeRef>()));
int[] arr;
IL.Pop(out arr);

(This should generate something very similar to)

Span<int> span = stackalloc int[10];
int[] arr = span.ToArray();

Generates the error:

Severity Code Description Project File Line Suppression State
Error ย  Fody/InlineIL: Unexpected error occured while processing method System.Void CSTest.Program::Main(System.String[]) at instruction IL_00d2: call System.Void InlineIL.IL/Emit::Call(InlineIL.MethodRef): System.NullReferenceException: Object reference not set to an instance of an object. ย  at Mono.Cecil.ImportGenericContext.TypeParameter(String type, Int32 position) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 94 ย  at Mono.Cecil.DefaultMetadataImporter.ImportTypeSpecification(TypeReference type, ImportGenericContext context) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 645 ย  at Mono.Cecil.DefaultMetadataImporter.ImportType(TypeReference type, ImportGenericContext context) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 492 ย  at Mono.Cecil.DefaultMetadataImporter.ImportReference(TypeReference type, IGenericParameterProvider context) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 732 ย  at InlineIL.Fody.Model.TypeRefBuilder.GenericParameterTypeRefResolver.TryResolve(ModuleDefinition module, IGenericParameterProvider context) ย  at InlineIL.Fody.Model.TypeRefBuilder.TypeSpecTypeRefResolver.TryResolve(ModuleDefinition module, IGenericParameterProvider context) ย  at InlineIL.Fody.Model.MethodRefBuilder.<>c__DisplayClass7_0.b__3(MethodDefinition m) ย  at System.Linq.Enumerable.<>c__DisplayClass6_0`1.b__0(TSource x) ย  at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext() ย  at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) ย  at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) ย  at InlineIL.Fody.Model.MethodRefBuilder.FindMethod(TypeReference typeRef, String methodName, Nullable`1 genericArity, TypeRefBuilder returnType, IReadOnlyList`1 paramTypes) ย  at InlineIL.Fody.Model.MethodRefBuilder.MethodByNameAndSignature(ModuleDefinition module, TypeReference typeRef, String methodName, Nullable`1 genericArity, TypeRefBuilder returnType, IReadOnlyList`1 paramTypes) ย  at InlineIL.Fody.Processing.ArgumentConsumer.g__ConsumeArgMethodRefBuilder|15_0(Instruction instruction) ย  at InlineIL.Fody.Processing.MethodWeaver.g__CreateInstructionToEmit|25_0(<>c__DisplayClass25_0& ) ย  at InlineIL.Fody.Processing.MethodWeaver.ProcessIlEmitMethodCall(Instruction emitCallInstruction, Instruction& nextInstruction) ย  at InlineIL.Fody.Processing.MethodWeaver.ProcessMethodCalls(CallInstructionHandler callHandler) CSTest A:\Code\Tests\TestInlineILFody\CSTest\Program.cs 19 ย 

It's this line that seems to cause the issue:

IL.Emit.Call(MethodRef.Method(typeof(Span<int>), "ToArray", TypeRef.TypeGenericParameters[0].MakeArrayType(), 0, Array.Empty<TypeRef>()));
Stack trace in a more readable form
Fody/InlineIL: Unexpected error occured while processing method System.Void CSTest.Program::Main(System.String[]) at instruction IL_00d2: call System.Void InlineIL.IL/Emit::Call(InlineIL.MethodRef): System.NullReferenceException: Object reference not set to an instance of an object.
 ย  at Mono.Cecil.ImportGenericContext.TypeParameter(String type, Int32 position) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 94
 ย  at Mono.Cecil.DefaultMetadataImporter.ImportTypeSpecification(TypeReference type, ImportGenericContext context) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 645
 ย  at Mono.Cecil.DefaultMetadataImporter.ImportType(TypeReference type, ImportGenericContext context) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 492
 ย  at Mono.Cecil.DefaultMetadataImporter.ImportReference(TypeReference type, IGenericParameterProvider context) in C:\projects\fody\cecil\Mono.Cecil\Import.cs:line 732
 ย  at InlineIL.Fody.Model.TypeRefBuilder.GenericParameterTypeRefResolver.TryResolve(ModuleDefinition module, IGenericParameterProvider context)
 ย  at InlineIL.Fody.Model.TypeRefBuilder.TypeSpecTypeRefResolver.TryResolve(ModuleDefinition module, IGenericParameterProvider context)
 ย  at InlineIL.Fody.Model.MethodRefBuilder.<>c__DisplayClass7_0.b__3(MethodDefinition m)
 ย  at System.Linq.Enumerable.<>c__DisplayClass6_0`1.b__0(TSource x)
 ย  at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
 ย  at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
 ย  at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
 ย  at InlineIL.Fody.Model.MethodRefBuilder.FindMethod(TypeReference typeRef, String methodName, Nullable`1 genericArity, TypeRefBuilder returnType, IReadOnlyList`1 paramTypes)
 ย  at InlineIL.Fody.Model.MethodRefBuilder.MethodByNameAndSignature(ModuleDefinition module, TypeReference typeRef, String methodName, Nullable`1 genericArity, TypeRefBuilder returnType, IReadOnlyList`1 paramTypes)
 ย  at InlineIL.Fody.Processing.ArgumentConsumer.g__ConsumeArgMethodRefBuilder|15_0(Instruction instruction)
 ย  at InlineIL.Fody.Processing.MethodWeaver.g__CreateInstructionToEmit|25_0(<>c__DisplayClass25_0& )
 ย  at InlineIL.Fody.Processing.MethodWeaver.ProcessIlEmitMethodCall(Instruction emitCallInstruction, Instruction& nextInstruction)
 ย  at InlineIL.Fody.Processing.MethodWeaver.ProcessMethodCalls(CallInstructionHandler callHandler)

.NET 8 and static BLOB pattern using ROS<T>

Starting from .NET 8, it's possible to use pattern like this:

private static ReadOnlySpan<int> Integers => [10, 20, 30];

which is compiled to

ldtoken field valuetype '<PrivateImplementationDetails>'/'__StaticArrayInitTypeSize=12_Align=4' '<PrivateImplementationDetails>'::'97CA1592048640A5368B4EC7C6934311567E09D50E7639918EC82C3D2A187CDA4'
call valuetype [System.Runtime]System.ReadOnlySpan`1<!!0> [System.Runtime]System.Runtime.CompilerServices.RuntimeHelpers::CreateSpan<int32>(valuetype [System.Runtime]System.RuntimeFieldHandle)
ret

.class private auto ansi sealed '<PrivateImplementationDetails>'
    extends [System.Runtime]System.Object
{
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
        01 00 00 00
    )
    // Nested Types
    .class nested private explicit ansi sealed '__StaticArrayInitTypeSize=12_Align=4'
        extends [System.Runtime]System.ValueType
    {
        .pack 4
        .size 12

    } // end of class __StaticArrayInitTypeSize=12_Align=4


    // Fields
    .field assembly static initonly valuetype '<PrivateImplementationDetails>'/'__StaticArrayInitTypeSize=12_Align=4' '97CA1592048640A5368B4EC7C6934311567E09D50E7639918EC82C3D2A187CDA4' at I_000027F0
    .data cil I_000027F0 = bytearray (
        0a 00 00 00 14 00 00 00 1e 00 00 00
    )

}

Previously, it was allowed for ROS<byte> only. There was a bug in Cecil library: dotnet/cecil#61 that leads to the following exception:

System.ArgumentException : Value does not fall within the expected range.
  Stack Trace:
     at System.Runtime.CompilerServices.RuntimeHelpers.GetSpanDataFrom(RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, Int32& count)
   at System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan[T](RuntimeFieldHandle fldHandle)

In my case, this exception is always reproducible. I think it happens because InlineIL uses outdated Cecil library. I'm not sure for 100% but it's a main suspicion. Could you release a new InlineIL with updated Fody and Cecil dependencies? Latest Fody version uses updated Cecil (6.8.0).

[Feature] Would it be possible to declare fields?

Hey, just discovered this package, looks amazing!
Thank you for your time building this! ๐Ÿ˜„

I have a question/proposal, would it be possible to support declaring fields? This would be useful when the type of such fields is not accessible from C# (eg. with internal types from other assemblies). Accessing and using those fields from IL would be easy, but from the readme it doesn't look like it's currently possible to declare the first right now? ๐Ÿค”

Rationale

In the Microsoft.Toolkit.HighPerformance package (source here, API browser here) we have a number of types that basically act equivalent for the System.ByReference<T> type from CoreCLR, which is unfortunately internal (made a proposal about making it public a while back (here), but that's not really planned). The workaround I came up with is to just use a Span<T> as proxy for the ByReference<T> type, using MemoryMarshal.CreateSpan<T>(ref T, int). This is kinda ok when we can reuse the Span<T>.Length property for other purposes (basically as if it was just another int field in the parent type), but it's just unnecessary overhead when that's not needed. Specifically, in the Ref<T> and ReadOnlyRef<T> types.

I was toying with the idea of just writing a part of that type directly in IL, declaring a ByReference<T> field and simply using that directly from the various available APIs. Of course, this would only be for .NET Core 2.1, .NET Core 3.1 and .NET 5, and not for the other targets. Would something like this be possible? I'm actually curious in general even outside of this specific case ๐Ÿ˜„

Something like (just the idea):

[LocalField("System.ByReference`1", "reference")]
public readonly ref struct Ref<T>
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Ref(ref T value)
    {
        Type
            byRefType = Type.GetType("System.ByReference`1").MakeGenericType(typeof(T)),
            parameterType = typeof(T).MakeByRefType();
        MethodRef cctor = MethodRef.Constructor(byRefType, parameterType);

        Ldarg_0();
        Ldarg_1();
        Newobj(cctor);
        Stfld(FieldRef.Field(byRefType, "reference"));
    }

    public ref T Value
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get
        {
            Type byRefType = Type.GetType("System.ByReference`1").MakeGenericType(typeof(T));

            Ldarg_0();
            Ldflda(FieldRef.Field(byRefType, "reference"));
            Call(MethodRef.PropertyGet(byRefType, "Value"));

            return ref IL.ReturnRef<T>();
        }
    }
}

Again congrats again for building this, I've been looking for something like this for a while! ๐Ÿš€

OutOfMemoryException due to an invalid IL produced for accessing to the field in a generic class

Accessing to the member in a generic class produces an invalid IL that makes the Runtime throws an OutOfMemoryException

public class Program
{
    public static void Main() => Console.WriteLine(Sample<int>.GetValue());
}
public static class Sample<T>
{
    public static readonly int Value = 10;

    public static int GetValue()
    {
        Ldsfld(new FieldRef(typeof(Sample<T>), nameof(Value)));
        return IL.Return<int>();
    }
}

.NET Core 2.2.1 (SDK 2.2.102)

MethodRef to overloaded generic method

I'm probably just missing the obvious, but I can't figure out how to emit a call to a specific overload of a generic method.

For instance, given something like the following:

static class C
{
    static ref T M<T>(T[] a, int b); // #1
    static ref T M<T>(T[] a, uint b); // #2
    static ref T M<T>(T[] a, IntPtr b); / /#3
    static ref T M<T>(T[] a, UIntPtr b); // #4
}

How would I emit a call to overload 4, instantiated for, say, string?

Emit.Call(new MethodRef(typeof(C), nameof(C.M)).MakeGenericMethod(typeof(string))); 
//error: ambiguous function, as expected

Emit.Call(new MethodRef(typeof(C), nameof(C.M), typeof(???), typeof(UIntPtr)).MakeGenericMethod(typeof(string))); 
//what goes in the ??? - string[] doesn't work (as expected)

I'm trying to avoid popping variables, calling through regular C# code, and then pushing the result as this generates unnecessary locals and instructions, and I seem to be right at the edge of what the JIT will optimize well and want to stay under that mark.

Thanks.

Generate debug info

It might be useful having sequence points for each of the IL intrinsics to make finding the relevant disassembly a bit easier.

Proposal: simplified arguments passing and first-class support of in parameters

Hi Lucas!

I'm using InlineIL widely in my project .NEXT. Your add-in helping me a lot to write performance-critical code. However, I feel some inconvenience with it.
The first one is an absence of support in-modifiers. Let's take a look at this code:

 public static void Bitcast<T, TResult>(in T input, out TResult output)
            where T : unmanaged
            where TResult : unmanaged
        {
            const string slowPath = "slow";
            Ldarg(nameof(output));
            Sizeof(typeof(T));
            Sizeof(typeof(TResult));
            Blt_Un(slowPath);
            //copy from input into output as-is
            Ldarg(nameof(input));
            Ldobj(typeof(TResult));
            Stobj(typeof(TResult));
            Ret();

            MarkLabel(slowPath);
            Dup();
            Initobj(typeof(TResult));
            Ldarg(nameof(input));
            Ldobj(typeof(T));
            Stobj(typeof(T));
            Ret();
            throw Unreachable();    //output must be defined within scope
        }

I have to use Ldarg(nameof(input)) and Ldarg(nameof(output)) because there is no overloads with out and in parameters. This cause unwanted warnings from Roslyn about unused parameters.

The second issue is a method call. It's verbose. I like Push helper that allows to push result of parameterless method or property on the evaluation stack. However, this approach is not working for call sites when arguments are not locals or parameters. In this case I have to use Call with call site descriptor object. I would like to propose T Arg<T>() and ref T RefArg<T>() helper which allow to load argument from the evaluation stack:

Ldc_I4(10);
Ldc_I4(2);
Push(Math.Max(Arg<int>(), Arg<int>());
return Return<int>();

//equivalent to
ldc.i4 10
ldc.i4 2
call Math.Max(int, int)
ret

Additionally, it would be great if InlineIL will be able to recognize params correctly:

Ldstr("Hello, {0}!");
Ldstr("Albert Wesker");
Push(Console.WriteLine(Arg<string>(), Arg<string>()); //automatically creates object[] array in resulted IL code
Ret();

Add a way to get a TypeRef from an assembly at provided path

It would be useful to be able to able to get a TypeRef to an assembly that's not referenced by the project, maybe by adding a new TypeRef constructor that takes a path to a dll and a type name there and adds it as a reference to the output assembly.

Invalid assembly after weaving

Not sure if this is an InlineIL.Fody, Fody or Mono.Cecil bug.

Repro: https://github.com/CosmosOS/Cosmos/tree/inline-il

The verification fails, and if I try to load the assembly at runtime it fails with BadImageFormatException. ILSpy loads it correctly.

I tried adding ExcludeAssets="All" here: Cosmos.Core_Asm.csproj#L13, and then used ildasm on both assemblies, and the main difference (ignoring the parts which were not weaved) is the .imagebase, which is 0x00400000 when weaved, and 0x10000000 when not.

Any help would be appreciated.

Using Calli() causes a build error in Debug configuration

Writing InlineIL to call a function pointer with Calli will trigger a build error. This only happens in the Debug configuration. Release builds and runs as expected.

For example, this code:

public int AddOne(int x)
{
    Ldarg_S(nameof(x));
    Ldarg_0();
    Ldfld(new FieldRef(typeof(Binding), "addOne"));
    var sig = new StandAloneMethodSig(CallingConvention.Cdecl, typeof(int), new TypeRef[] { typeof(int) });
    Calli(sig);
    Ret();

    return 0;
}

causes this:

2>C:\dev\snippets\InlineILTest\src\Test\Binding.cs(39,13,39,24): error : Fody/InlineIL: Unexpected instruction, expected a method call but was: IL_005f: ldloc V_0 (in System.Int32 InlineILTest.Binding::AddOne(System.Int32) at instruction IL_005f: ldloc V_0)
2>MSBUILD : error : Fody: An unhandled exception occurred:
2>MSBUILD : error : Exception:
2>MSBUILD : error : Value does not fall within the expected range.
2>MSBUILD : error : Type:
2>MSBUILD : error : System.ArgumentException
2>MSBUILD : error : StackTrace:
2>MSBUILD : error :    at Mono.Cecil.Pdb.ISymUnmanagedWriter2.DefineLocalVariable2(String name, Int32 attributes, Int32 sigToken, Int32 addrKind, Int32 addr1, Int32 addr2, Int32 addr3, Int32 startOffset, Int32 endOffset)
2>MSBUILD : error :    at Mono.Cecil.Pdb.NativePdbWriter.DefineLocalVariable(VariableDebugInformation variable, Int32 local_var_token, Int32 start_offset, Int32 end_offset) in C:\Code\Fody\cecil\symbols\pdb\Mono.Cecil.Pdb\NativePdbWriter.cs:line 215
2>MSBUILD : error :    at Mono.Cecil.Pdb.NativePdbWriter.DefineScope(ScopeDebugInformation scope, MethodDebugInformation info, MetadataToken& import_parent) in C:\Code\Fody\cecil\symbols\pdb\Mono.Cecil.Pdb\NativePdbWriter.cs:line 177
2>MSBUILD : error :    at Mono.Cecil.Pdb.NativePdbWriter.Write(MethodDebugInformation info) in C:\Code\Fody\cecil\symbols\pdb\Mono.Cecil.Pdb\NativePdbWriter.cs:line 69
2>MSBUILD : error :    at Mono.Cecil.Cil.CodeWriter.WriteResolvedMethodBody(MethodDefinition method) in C:\Code\Fody\cecil\Mono.Cecil.Cil\CodeWriter.cs:line 134
2>MSBUILD : error :    at Mono.Cecil.Cil.CodeWriter.WriteMethodBody(MethodDefinition method) in C:\Code\Fody\cecil\Mono.Cecil.Cil\CodeWriter.cs:line 54
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddMethod(MethodDefinition method) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1658
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddMethods(TypeDefinition type) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1650
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddType(TypeDefinition type) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1440
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.AddTypes() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1412
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.BuildTypes() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1266
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.BuildModule() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1036
2>MSBUILD : error :    at Mono.Cecil.MetadataBuilder.BuildMetadata() in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 1006
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.<>c.<BuildMetadata>b__2_0(MetadataBuilder builder, MetadataReader _) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 144
2>MSBUILD : error :    at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TItem item, Func`3 read) in C:\Code\Fody\cecil\Mono.Cecil\ModuleDefinition.cs:line 947
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.BuildMetadata(ModuleDefinition module, MetadataBuilder metadata) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 143
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 119
2>MSBUILD : error :    at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters) in C:\Code\Fody\cecil\Mono.Cecil\AssemblyWriter.cs:line 78
2>MSBUILD : error :    at Mono.Cecil.ModuleDefinition.Write(String fileName, WriterParameters parameters) in C:\Code\Fody\cecil\Mono.Cecil\ModuleDefinition.cs:line 1136
2>MSBUILD : error :    at InnerWeaver.WriteModule() in C:\projects\fody\FodyIsolated\ModuleWriter.cs:line 19
2>MSBUILD : error :    at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 111
2>MSBUILD : error : Source:
2>MSBUILD : error : Mono.Cecil.Pdb
2>MSBUILD : error : TargetSite:
2>MSBUILD : error : Void DefineLocalVariable2(System.String, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32)

Can this actually be built for Net 4.5?

Hey!

I just wanted to ask if it is possible to built this library for .Net 4.5?
What is the reason for the use of 4.52?

It's fine if you do not plan to provide a 4.5 build.
I am just curious and would like to target against 4.5

I really like this project. Good job!

Mixing C# and IL locals

Hello Lucas!

I tried to use InlineIL for a painful case of working with ref locals, and found some limitations. A simplified sample below. I want to mix locals defined normally and via InlineIL. For ref locals, I really miss reference to reference similar to pointer to pointer and C# does not allow to initialize ref local in efficient way in my case. But after initialization, I would rather continue to use C#, not IL. The type under question if a struct with objects, so using Unsafe.AsPointer is dangerous and questionable.

Here we define baz in C# and then add two additional locals via IL. The output locals will be merged as expected. I want to have access to baz from IL.

        public int UseLocalVariables(int value)
        {
#pragma warning disable 219
            var baz = 10;
#pragma warning restore 219
            IL.DeclareLocals(
                new LocalVar("foo", typeof(int)),
                new LocalVar("bar", typeof(int))
            );

            IL.Push(value);
            Stloc("foo");

            Ldc_I4(42);
            Stloc("bar");

            IncrBaz();

            Ldloc("foo");

            // We want to be able to load baz, which is defined normally in C#
            // Ldloc("baz"); -- will not work, and local names are not available in Mono.Cecil: https://groups.google.com/g/mono-cecil/c/GwDG0bQ8TXk/m/mIr6QtXTQNsJ
            // But we could use an index. There is one auto local for local function IncrBaz,
            // so the baz has index 1. Or we could look the index in actual IL after weaving.
            Ldloc_1();
            Add();
            return IL.Return<int>();

            void IncrBaz()
            {
                baz++;
            }
        }

Looking into MethodLocals implementation I think the fix could be simple. Even though Mono.Cecil does not support names of locals (or technically there are no names in IL), we could use indices.

A potential fix is to add this:
image

This will create some complexity with indexing, because the required index will be the one from combined locals, not from IL-defined locals. And in the presence of local functions, like in the sample, there could be autogenerated locals. To get the right indices one will have to look into generated IL. Thankfully, in Rider, IL View is one click away and it shows IL after weaving. So this indexing behavior could be left as "by design".

But since it's the first time I look into this source code, I don't know if something else could break after the proposed change.


The real use case looks like this:
image

This is one of the hottest methods, and GetLoadRef2 is not trivial. But it's still faster to have it as AggressivelyInlined. However, this generates twice as much of the assembly code. So the idea is to initialize ref locals in a loop to have the GetLoadRef2 inlined once.

using ldarga.s makes compile error

I was testing some stuffs on InlineIL.Fody, and I found that there are some problems on IL.Emit.Ldarga_S . After some tests, I found that using IL.Emit.Ldarga_S will always make some error.

This is my test code:

public void Foo(Object obj)
{
    IL.Emit.Ldarga_S(nameof(obj));
}
public void Bar(Object obj)
{
    IL.Emit.Ldarga(nameof(obj));
}

and emitted IL was (disassembled with ildasm):

.method public hidebysig instance void  Foo(object obj) cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  IL_0000:  ldstr      "InlineIL processing failed: Opcode ldarga.s expect"
  + "s an operand of type Int32 (in System.Void ILSniffets.Program::Foo(Syst"
  + "em.Object) at instruction IL_0006: call System.Void InlineIL.IL/Emit::L"
  + "darga_S(System.String))"
  IL_0005:  newobj     instance void [mscorlib]System.InvalidProgramException::.ctor(string)
  IL_000a:  throw
} // end of method Program::Foo

and

.method public hidebysig instance void  Bar(object obj) cil managed
{
  // Code size       5 (0x5)
  .maxstack  1
  IL_0000:  nop
  IL_0001:  ldarga.s   obj
  IL_0003:  nop
  IL_0004:  ret
} // end of method Program::Bar

According to the exception which method Foo throws, InlineIL expected Int32 typed operand after ldarga.s opcode, but as far as I know, ldargs.a opcode uses Unsigned Int8 typed operand. So I think there are some problem in code.

nop after any Il

Hello
nice extension!
I just saw it add nop after any instruction, is it wanted?

thx

Better discoverability of IL.Push/IL.Pop

DeclareLocals is really useful to have but it'd also be nice to reference normal C# locals too. I just noticed there's also Push and Pop so maybe the error could point to those, but maybe the names can be changed to Load and Store to match IL opcode names more?

Creating labels in otherwise unreachable code fails

If I try to use IL.MarkLabel() inside an area that would normally be unreachable code, any attempts to branch to that label can't find it.
Here's a small example:

public int Example(bool test) {
    if(test) {
        IL.Emit.Br("Label");
    }
    return 0;
    IL.MarkLabel("Label");
    return 1;
}

This gives the following error:
error : Fody/InlineIL: Undefined label: 'Label' (in System.Int32 DecompileMe.Class1::Example(System.Boolean) at instruction IL_0000: br IL_0000)

I have a suspicion this isn't easily fixable, but I'm making this issue anyway in case I'm wrong

IL.Emit.Tail() produces wrong IL

As tail. is a prefix instruction, the only valid code sequence is tail. call (or calli or callvirt) (see link).

Tail();
Calli(new StandAloneMethodSig(CallingConvention.Cdecl, typeof(void)));

produces

IL_0007: tail.
IL_0009: calli void()

instead of (the expected)

IL_0007: tail. calli void()

(the space after the . is sensitive though).

Some IL instructions "always" generate unverifiable code

Hey.

It seem like IL code generated for initblk and copyblk always generate unverfiable code.

This is also the case for the https://github.com/ltrzesniewski/InlineIL.Fody/blob/master/src/InlineIL.Examples/Unsafe.cs class provided as an example.
(opcodes are the same as in the original. .maxstack is 8 for InlineIL and 3 for Unsafe.)

The original Unsafe library does not produce non verifiable code for these instructions.

Is this a problem with InlineIL which can be fixed?

InlineIL.Fody and .NET Core 3.1

Is it possible to use InlineIL.Fody in .NET Core 3.1 (or higher)? I get some weird error regarding System.ValueTuple not being found.

Error		Fody/InlineIL: Unexpected error occured while processing method T HookManager.Program::Read(System.Void*) at instruction IL_001b: call System.Void InlineIL.IL/Emit::Ldobj(InlineIL.TypeRef): System.IO.FileNotFoundException: Could not load file or assembly 'System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.
File name: 'System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' ---> System.IO.FileNotFoundException: Could not load file or assembly 'System.ValueTuple, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies. The system cannot find the file specified.
File name: 'System.ValueTuple, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'

   at InlineIL.Fody.Model.TypeRefBuilder.AddModifiers(TypeReference type)
   at InlineIL.Fody.Processing.ArgumentConsumer.ConsumeArgTypeRef(Instruction typeRefInstruction)
   at InlineIL.Fody.Processing.MethodWeaver.<ProcessIlEmitMethodCall>g__CreateInstructionToEmit|22_0(<>c__DisplayClass22_0& )
   at InlineIL.Fody.Processing.MethodWeaver.ProcessIlEmitMethodCall(Instruction emitCallInstruction, Instruction& nextInstruction)
   at InlineIL.Fody.Processing.MethodWeaver.ProcessMethodCalls()

	HookManager	D:\Repos\ConsoleApp2\HookManager\Program.cs	173	

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.