Giter Site home page Giter Site logo

gremit's Introduction

GrEmit

NuGet Status Build status

GrEmit is a library containing different helpers for generating code using Reflection.Emit with the main one being GroboIL - a smart wrapper over ILGenerator.

Usage

GroboIL is a replacement for ILGenerator. Instead of calling ILGenerator.Emit(OpCode, ..), one may call GroboIL.OpCode(..).

Example

ILGenerator:

// .. creating DynamicMethod, MethodBuilder or ConstructorBuilder
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_i4_4);
il.EmitCall(OpCodes.Callvirt, someMethod, null);
il.Emit(OpCodes.Ret);

GroboIL:

// .. creating DynamicMethod, MethodBuilder or ConstructorBuilder
using(var il = new GroboIL(method))
{
    il.Ldarg(0);
    il.Ldc_I4(4);
    il.Call(someMethod);
    il.Ret();
}

Advantages

Besides more beautiful interface GroboIL has some more advantages over ILGenerator:

  • GroboIL has a single method for all instructions from the same family, for instance, instead of 11 instructions OpCodes.Ldelem_* there is one method GroboIL.Ldelem(Type type).
  • During code generation GroboIL builds the content of the evaluation stack and validates instructions arguments, and if something is not OK, immediately throws an Exception.
  • There is a debug ouput of the code being generated.
  • Full generics support.
  • It is possible to debug MethodBuilders (use constructor of GroboIL with parameter ISymbolDocumentWriter).
  • Appropriate performance. For instance, once I had to compile a program with 500000 instructions and it was verified by GroboIL in 3 seconds (I guess there is a way to break performance, but in practice such a program won't occure).

Example of debug output:

GroboIL.GetILCode()

        ldarg.0              // [List<T>]
        dup                  // [List<T>, List<T>]
        brtrue notNull_0     // [null]
        pop                  // []
        ldc.i4.0             // [Int32]
        newarr T             // [T[]]
notNull_0:                   // [{Object: IList, IList<T>, IReadOnlyList<T>}]
        ldarg.1              // [{Object: IList, IList<T>, IReadOnlyList<T>}, Func<T, Int32>]
        call Int32 Enumerable.Sum<T>(IEnumerable<T>, Func<T, Int32>)
                             // [Int32]
        ret                  // []

Release Notes

See CHANGELOG.

gremit's People

Contributors

aldobrynin avatar andrewkostousov avatar anton92nd avatar beevee avatar cspwizard avatar dgorlov avatar fakefeik avatar fornever avatar homuroll avatar kunga avatar minya avatar troublenadiagirl 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gremit's Issues

Sign assembly

Hard to use not signed GrEmit assembly in production

Add localloc opcode

This opcode is used when doing a stackalloc and it would be really useful when making Span<>s

IL modification in .NET Core

Hi,

Is it possible to use GroboIL to modify the IL code of a .net core application?
I am trying to use the profiling API together with GrEmit to modify certain calls from my .net core app but I don't see how to do it with GroboIL.

I have seen the example of GroboTrace but I think it only works with full framework apps.

I have also read that the MethodBodyParsing namespace has been removed in the latest versions. What problems would I encounter when using or implementing MethodBodyParsing related methods for .net core apps? It would be very hard to perform?

InvalidOperationException when trying to read dynamic method body

Hi,
I am trying to use the methods in MethodBodyParsing to look at the IL code of a compiled Expression.
It's failing with:

InvalidOperationException: Unable to set a value of type 'DynamicILInfo' to an instance of type 'DynamicILInfo'
        ldarg.0                                           // [DynamicMethod]
        ldarg.1                                           // [DynamicMethod, Object]
        castclass DynamicScope                            // [DynamicMethod, DynamicScope]
        call DynamicILInfo DynamicMethod.GetDynamicILInfo(DynamicScope)
                                                          // [DynamicILInfo]
        ret                                               // 

I think it's the following method:
GrEmit.MethodBodyParsing.DynamicMethodWrapper.BuildGetDynamicILInfo

My code is:

using System;
using System.Linq.Expressions;

namespace Test
{
    static class Program
    {
        struct Test
        {
            public override string ToString()
            {
                return "12345";
            }
        }

        static void Main(string[] args)
        {

            var obj = Expression.New(typeof(Test));
            var call = Expression.Call(obj, typeof(object).GetMethod("ToString"));
            var lambda = Expression.Lambda<Func<string>>(call);
            var method = lambda.Compile();
            var instructions = GrEmit.MethodBodyParsing.MethodBody.Read(method.Method, true).Instructions;
            foreach (var inst in instructions)
            {
                Console.WriteLine(inst);
            }
        }
    }
}

Am I doing something wrong or is it just not working anymore with newer .NET framework versions? I know you did some magic there with internal functions but I don't want to use it for production code either way. It's just for experiments and learning.

NullReferenceException in scenario with branches & Ldelem

Hello!

The following code leads to a crash:

                using (var il = new GroboIL(reset))
                {
                    var index = il.DeclareLocal(typeof(int));
                    
                    il.Ldc_I4(0);
                    il.Stloc(index);
                    
                    var start = il.DefineLabel("start");
                    var compare = il.DefineLabel("compare");
                    
                    il.Br(compare);
                    il.MarkLabel(start);
                    il.Ldarg(0);
                    il.Ldfld(elements);
                    il.Ldloc(index);
                    il.Ldelem(OurElementType); // OurElementType is a reference type.
                    
                    il.Ldloc(index);
                    il.Ldc_I4(1);
                    il.Add();
                    il.Stloc(index);
                    
                    il.MarkLabel(compare);
                    il.Ldloc(index);
                    il.Ldarg(0);
                    il.Ldfld(machines);
                    il.Ldlen();
                    il.Conv<int>();
                    il.Blt(start, false); // FAIL HERE
                    
                    il.Ret();
                }

It fails with:

   at GrEmit.StackMutators.LdelemStackMutator.Mutate(GroboIL il, ILInstructionParameter parameter, EvaluationStack& stack) in C:\projects\gremit\GrEmit\StackMutators\LdelemStackMutator.cs:line 11
   at GrEmit.StackMutatorCollection.Mutate(OpCode opCode, GroboIL il, ILInstructionParameter parameter, EvaluationStack& stack) in C:\projects\gremit\GrEmit\StackMutatorCollection.cs:line 18
   at GrEmit.StackMutator.Propogate(GroboIL il, Int32 lineNumber, EvaluationStack stack) in C:\projects\gremit\GrEmit\StackMutator.cs:line 336
   at GrEmit.StackMutator.SaveOrCheck(GroboIL il, EvaluationStack stack, Label label) in C:\projects\gremit\GrEmit\StackMutator.cs:line 145
   at GrEmit.StackMutators.BinOpStackMutator.Mutate(GroboIL il, ILInstructionParameter parameter, EvaluationStack& stack) in C:\projects\gremit\GrEmit\StackMutators\BinOpStackMutator.cs:line 24
   at GrEmit.StackMutatorCollection.Mutate(OpCode opCode, GroboIL il, ILInstructionParameter parameter, EvaluationStack& stack) in C:\projects\gremit\GrEmit\StackMutatorCollection.cs:line 18
   at GrEmit.GroboIL.Emit(OpCode opCode, Label label) in C:\projects\gremit\GrEmit\GroboIL.cs:line 2256
   at GrEmit.GroboIL.Blt(Label label, Boolean unsigned) in C:\projects\gremit\GrEmit\GroboIL.cs:line 574

Some initial debugging shows, that:

  • il.MarkLabel(start) resets GroboIL.stack to null
  • il.LdElem doesn't mutate stack (as it is null now), so its parameter (with element type) is thrown away
  • ...
  • il.Blt(start, false) starts to recover the stack for the jump location and it goes through recorded instructions accessing corresponding parameters
  • we get NRE in LdelemStackMutator as ldelem parameter is not available (== null).

OpCodes.Ubox not Supported

Coverage of OpCodes is pretty good, but I noticed that Unbox isn't supported, even though Unbox_Any is there. Is this just an oversight?

dll с подписью

Данную библиотеку невозможно использовать в подписанных сборках, поскольку подписанные сборки должны ссылаться только на другие подписанные сборки. Мы используем formats, который у которого в зависимости flash-props boundle, который в свою очередь ссылается на GrEmit. Раньше, когда GrEmit был в цементе мы просто изменяли csproj'и и добавляли в InternalsVisibleTo соответствующий публичный ключ и после этого шла компиляция. Теперь же, когда GrEmit закачивается через NuGet, данный метод не работает, поскольку он не подписан. Нам бы хотелось, либо чтобы сборка при выкачивании из NuGet была подписана, либо чтобы для Лайта собрали отдельную подписанную сборку, которую мы будем использовать при сборке formats.

Add netcoreapp as target framework

I know you usually cover this by including netstandard as a target, but I am missing the Calli method with native calling convention. It is not part of netstandard2.0 but it seems to be part of netcoreapp2.2.

So could you please add this as part of your NuGet package?

Load value type as reference when constructing it

When constructing an instance of a value type with the newobj opcode, a reference to the type should be pushed instead of the value type itself. This would allow to directly ldfld like the compiler does:

IL_0001: ldc.i4.1
IL_0002: conv.i8
IL_0003: ldc.i4.2
IL_0004: newobj instance void [LogicScript]LogicScript.Data.BitsValue::.ctor(uint64, int32) /* 0A00000B */
IL_0009: ldfld uint64 [LogicScript]LogicScript.Data.BitsValue::Number /* 0A00000C */

Can't emit Ldftn with symbolDocumentWriter

We are trying to emit Ldftn instruction. In some cases we see exception System.ArgumentException.

Look at this code:

public class Dummy
{
  public static int Add10(int x)
  {
    return x + 10;
  }
}
[TestFixture]
public class Tests
{
  [TestCase(true)]
  [TestCase(false)]
  public void TestLdftn(bool useDocumentWriter)
  {
    var id = $"asm-{Guid.NewGuid()}";
    var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(id), AssemblyBuilderAccess.RunAndCollect);
    var module = assembly.DefineDynamicModule(name: id, fileName: id + ".dll", emitSymbolInfo: true);
    var fooTypeBuilder = module.DefineType("Foo", TypeAttributes.Public | TypeAttributes.Class);
    var symWriter = module.GetSymWriter();
    var barMethod = fooTypeBuilder.DefineMethod("Bar", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new[] { typeof(int) });
    var documentName = fooTypeBuilder.Name + "." + barMethod.Name + ".cil";
    var documentWriter = symWriter.DefineDocument(documentName, SymDocumentType.Text, SymLanguageType.ILAssembly, Guid.Empty);
    using (var il = useDocumentWriter
      ? new GroboIL(barMethod, documentWriter)
      : new GroboIL(barMethod))
    {
      il.Ldnull(); // stack: [null]
      il.Ldftn(typeof(Dummy).GetMethod("Add10")); // stack: [null, Add10*]
      il.Newobj(typeof(Func<int, int>).GetConstructor(new[] { typeof(object), typeof(IntPtr) })); // stack: [func]
      il.Ldc_I4(5); // stack: [func, 5]
      il.Call(typeof(Func<int, int>).GetMethod("Invoke")); // stack: [int]
      il.Ret();
    }
    var fooType = fooTypeBuilder.CreateType();

    var result = fooType.GetMethod("Bar").Invoke(null, new object[] { 5 });

    result.Should().Be(15);
  }
}

We can succesfully emit and run code without document writer. But we see that exception in opposite scenario:

System.ArgumentException : Не удается передать указанный код операции в метод EmitCall.
Имя параметра: opcode
   в System.Reflection.Emit.ILGenerator.EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes)
   в GrEmit.GroboIL.Emit(OpCode opCode, ILInstructionParameter parameter) в C:\BuildAgent\work\249acc80a2fd7042\GrEmit\GrEmit\GroboIL.cs:строка 2000
   в GrEmit.GroboIL.Dispose() в C:\BuildAgent\work\249acc80a2fd7042\GrEmit\GrEmit\GroboIL.cs:строка 147
   в GremitBugs.Tests.TestLdftn(Boolean useDocumentWriter) в C:\Users\****\Documents\Visual Studio 2017\Projects\GremitWeaks\GremitBugs\Tests.cs:строка 45

We assume that bug is located here:

else if(parameter is MethodILInstructionParameter)
il.EmitCall(opCode, ((MethodILInstructionParameter)parameter).Method, null);

Sometimes MethodILInstructionParameter leads to emit non call opcodes :)

TypeInitializerException on Mono 4.6.1

On Mono 4.6.1 (currently aliased as latest on Travis) GrEmit is unable to initialize. It throws the following exception:

System.TypeInitializationException : The type initializer for 'MethodBuilderWrapper' threw an exception.
---- System.NotSupportedException : Unable to compare instances of 'System.RuntimeType'
Stack Trace:
    at <0x00000 + 0x00000> <unknown method>
    at GrEmit.Utils.ReflectionExtensions.<.cctor>b__d (System.Reflection.MethodBase method) [0x00000] in <359bd6fd97be437f89b1397f61cb891e>:0 
    at GrEmit.Utils.ReflectionExtensions.GetParameterTypes (System.Reflection.MethodBase method) [0x00031] in <359bd6fd97be437f89b1397f61cb891e>:0 
    at GrEmit.GroboIL..ctor (System.Reflection.Emit.MethodBuilder method, System.Boolean analyzeStack) [0x00036] in <359bd6fd97be437f89b1397f61cb891e>:0 

See sample build log for my project.

Unfortunately, GrEmit have no public CI server, otherwise you'll be able to notice the problem yourself. If you need any help to set up Travis for GrEmit, I'm ready to help.

How to change Activator.CreateInstance to emit

image

IRemoteMessageSenderDelegate BuildSenderDelegate(MethodInfo methodInfo, Type inputType)
{
return (IRemoteMessageSenderDelegate)Activator.CreateInstance(typeof(RemoteMessageSenderDelegate<,>).MakeGenericType(inputType ?? typeof(object), methodInfo.ReturnType), methodInfo, OxygenIocContainer.Resolve());
}

Add license info in nuget package

GrEmit uses MIT license that says

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

Would be great if you add license info (license.txt or link to license page) because it's hard to monitor license on any changes and update it manually in products that uses your library.

Ldfld и Stfld в новой версии

В проекте решили обновить Gremit на новую версию. Старая версия неизвестна и выяснить нет возможности(кто-то сбилдил gremit и положил библиотеку в проект). При переходе на новую версию сломался код c инструкциями Ldfld и Stfld. Туда передается FieldBuilder и на старой версии это работало без ошибок. На новой версии выдает ошибку при проверке стэка.

Необработанное исключение: System.InvalidOperationException: Unable to set a value of type 'Context' to an instance of type 'Context'
        ldarg.1                    // [Context]
        ldarg.3                    // [Context, TestClass]
        stfld Context.field        //

   в GrEmit.StackMutator.ThrowError(GroboIL il, String message) в C:\projects\gremit\GrEmit\StackMutator.cs:строка 101
   в GrEmit.StackMutator.CheckCanBeAssigned(GroboIL il, Type to, Type from) в C:\projects\gremit\GrEmit\StackMutator.cs:строка 132
   в GrEmit.StackMutators.StfldStackMutator.Mutate(GroboIL il, ILInstructionParameter parameter, EvaluationStack& stack) в C:\projects\gremit\GrEmit\StackMutators\StfldStackMutator.cs:строка 24
   в GrEmit.StackMutatorCollection.Mutate(OpCode opCode, GroboIL il, ILInstructionParameter parameter, EvaluationStack& stack) в C:\projects\gremit\GrEmit\StackMutatorCollection.cs:строка 18
   в GrEmit.GroboIL.Emit(OpCode opCode, FieldInfo field) в C:\projects\gremit\GrEmit\GroboIL.cs:строка 2273
   в GrEmit.GroboIL.Stfld(FieldInfo field, Boolean isVolatile, Nullable`1 unaligned) в C:\projects\gremit\GrEmit\GroboIL.cs:строка 996

Прикладываю сэмпл кода. Проект TestCase на новой версии и выдает ошибку. Проект TestCaseOld на старой версии и отрабатывает без ошибок.
TestCase.zip

GrEmit does not work on mono 5.0.0

System.InvalidOperationException: Type 'MonoGenericClass' is not found
  at GrEmit.Utils.ReflectionExtensions.FindType (System.Collections.Generic.IEnumerable`1[T] types, System.String name) [0x0001b] in <50b0a39ad43f431d8c63c630195b4ddb>:0 
  at GrEmit.Utils.ReflectionExtensions..cctor () [0x000bc] in <50b0a39ad43f431d8c63c630195b4ddb>:0 
   --- End of inner exception stack trace ---
  at GrEmit.GroboIL..ctor (System.Reflection.Emit.ConstructorBuilder constructor, System.Boolean analyzeStack) [0x0001f] in <50b0a39ad43f431d8c63c630195b4ddb>:0

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.