Работа кода IL может дестабилизировать среду выполнения

В настоящее время у меня есть следующий код, когда я запускаю его, я получаю код ошибки. Операция может дестабилизировать среду выполнения (этот мой обновленный код il соответствует символу, но он все еще не работает)

public static Func<IDataReader, Object> TestMethod<T>()
    {
        var method = new DynamicMethod("", typeof(Object), new[]
        {
            typeof ( IDataReader )
        });
        var il = method.GetILGenerator();
        Label whileIf = il.DefineLabel();
        Label whileStart = il.DefineLabel();
        Label methodEnd = il.DefineLabel();
        il.Emit(OpCodes.Newobj, typeof(List<string>).GetConstructor(new Type[0]));
        il.Emit(OpCodes.Stloc_0);
        il.Emit(OpCodes.Br_S, whileIf);
        il.MarkLabel(whileStart);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldc_I4_0);
        il.Emit(OpCodes.Callvirt, typeof(IDataRecord).GetMethod("GetString"));
        il.Emit(OpCodes.Callvirt, typeof(List<string>).GetMethod("Add"));
        il.MarkLabel(whileIf);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Callvirt, typeof(IDataReader).GetMethod("Read"));
        il.Emit(OpCodes.Brtrue_S, whileStart);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Ret);

        return (Func<IDataReader, Object>)method.CreateDelegate(typeof(Func<IDataReader, Object>));
    }

Он был создан из этого кода:

public List<string> method(IDataReader dataReader)
    {
        var result = new List<string>();
        while (dataReader.Read())
            result.Add(dataReader.GetString(0));
        return result;
    }

Что генерирует следующий код IL:

IL_0000:  nop         
IL_0001:  newobj      System.Collections.Generic.List<System.String>..ctor
IL_0006:  stloc.0     // result
IL_0007:  br.s        IL_0017
IL_0009:  ldloc.0     // result
IL_000A:  ldarg.1     
IL_000B:  ldc.i4.0    
IL_000C:  callvirt    System.Data.IDataRecord.GetString
IL_0011:  callvirt    System.Collections.Generic.List<System.String>.Add
IL_0016:  nop         
IL_0017:  ldarg.1     
IL_0018:  callvirt    System.Data.IDataReader.Read
IL_001D:  stloc.2     // CS$4$0001
IL_001E:  ldloc.2     // CS$4$0001
IL_001F:  brtrue.s    IL_0009
IL_0021:  ldloc.0     // result
IL_0022:  stloc.1     // CS$1$0000
IL_0023:  br.s        IL_0025
IL_0025:  ldloc.1     // CS$1$0000
IL_0026:  ret  

Я пишу этот код в IL, так как в конечном итоге я буду расширять код, который динамически создает этот код для заполнения типа из TArg. Извините, у меня нет больше информации, которую мне дает вся визуальная студия.

Это мой сигильский код, который работает и работает правильно:

var emiter = Emit<Func<IDataReader, List<String>>>.NewDynamicMethod("MyMethod");

            var whileIf = emiter.DefineLabel("whileIf");
            var whileStart = emiter.DefineLabel("whileStart");

            emiter.DeclareLocal(typeof(List<string>), "0");

            emiter.NewObject<List<String>>();
            emiter.StoreLocal("0");
            emiter.Branch(whileIf);
            emiter.MarkLabel(whileStart);
            emiter.LoadLocal("0");
            emiter.LoadArgument(0);
            emiter.LoadConstant(0);
            emiter.CallVirtual(typeof(IDataRecord).GetMethod("GetString"));
            emiter.CallVirtual(typeof(List<string>).GetMethod("Add"));
            emiter.MarkLabel(whileIf);
            emiter.LoadArgument(0);
            emiter.CallVirtual(typeof(IDataReader).GetMethod("Read"));
            emiter.BranchIfTrue(whileStart);
            emiter.LoadLocal("0");
            emiter.Return();
            Func<IDataReader, List<string>> result = emiter.CreateDelegate();

            Disassembler<Func<IDataReader, List<string>>>.Disassemble( result );

            return result;

person Jake Rote    schedule 27.11.2013    source источник
comment
Вы знакомы с AssemblyBuilder? Если вы используете это для создания своего метода, вы можете сохранить сборку на диск и запустить PEVerify для созданной сборки. Обычно это говорит вам, что не так.   -  person    schedule 28.11.2013
comment
нет, но спасибо, я посмотрю, у вас есть небольшой фрагмент кода в качестве примера?   -  person Jake Rote    schedule 28.11.2013
comment
Не сейчас, но когда я смогу, я вернусь и опубликую что-нибудь, если у вас еще нет хорошего решения.   -  person    schedule 28.11.2013
comment
Совет для профессионалов: при компиляции для проверки IL создавайте в режиме релиза, а не отлаживайте   -  person Marc Gravell    schedule 28.11.2013
comment
Второй профессиональный совет: если вы не делаете это для развлечения, рассмотрите возможность использования таких инструментов, как dapper, которые сделают все это за вас.   -  person Marc Gravell    schedule 28.11.2013
comment
В настоящее время я делаю это для удовольствия, так как у меня уже есть код, который генерирует это на С#, а затем компилирует его на лету, что быстрее, чем собственный код после компиляции кода, но я хочу сделать это в il, чтобы сократить время компиляции.   -  person Jake Rote    schedule 28.11.2013


Ответы (2)


Обычно это означает либо дисбаланс стека, либо неправильный тип вызова (статический или виртуальный). Я не за компьютером, чтобы проверить состояние стека, но: я настоятельно рекомендую использовать такой инструмент, как "sigil", если вы не уверены. Он разработан специально для того, чтобы сообщать вам как только вы сделаете ошибку в генерировании, а не когда вы запустите его позже. Он расскажет вам точно о проблеме, как только вы ее сделаете.

Однако это определенно неправильный путь:

    il.Emit( OpCodes.Ldarg_0 );
    il.MarkLabel( whileIf );

В первый раз через него ветки направляются к whileIf, в котором вы выполняете вызов в пустом стеке.

person Marc Gravell    schedule 27.11.2013
comment
Что такое call vs virt.? - person Gabe; 28.11.2013
comment
Статический вызов @Gabe против виртуального вызова - person Marc Gravell; 28.11.2013
comment
Я просмотрел сигил и написал в нем свой il, и он отлично работает с любыми идеями, в чем может быть проблема, спасибо. - person Jake Rote; 28.11.2013
comment
@ Джейк, ты починил сломанную ветку, о которой я говорил? - person Marc Gravell; 28.11.2013
comment
Я немного смущен тем, что вы имеете в виду, почему он вызывает пустой стек? И если это проблема, почему тот же код il работает в сигиле? Спасибо - person Jake Rote; 28.11.2013
comment
@Jake callvirt требует по крайней мере один элемент в стеке - ссылку на целевой объект. Также требуются все параметры (для чтения их нет) — вам нужно загрузить значение перед вызовом callvirt, а исходный IL — нет. Я не могу комментировать код сигилы, не видя его, но я предполагаю, что вы перепутали две строки, которые я процитировал. - person Marc Gravell; 28.11.2013
comment
@Rote да, именно то, что я сказал: в коде сигилы вы отмечаете, а затем загружаете, а не загружаете, а затем отмечаете; позвольте мне на мгновение выглядеть самодовольным... - person Marc Gravell; 28.11.2013
comment
@MarcGravell я только что обновил вопрос с моим текущим кодом, он все еще не работает:/ - person Jake Rote; 28.11.2013
comment
@ Джейк сейчас не за компьютером - ему нужен отладчик... Посмотрю, когда у меня будет компьютер - person Marc Gravell; 28.11.2013
comment
@jake Я определенно могу его найти - я просто не за ПК (для информации, я сумасшедший человек, стоящий за IL-эмитом в dapper и protobuf-net) - сообщу вам, когда у меня будет отладчик - person Marc Gravell; 28.11.2013
comment
@JakeRote вы используете ldloc_0 / stloc_0, но не объявляете никаких местных жителей ... добавьте il.DeclareLocal(typeof(List<string>)); вверху, и вы должны быть отсортированы (примечание: вы объявляете это в версии сигила) - person Marc Gravell; 28.11.2013
comment
@MarcGravell Спасибо, как глупо с моей стороны, как ты мог этого не заметить - person Jake Rote; 28.11.2013
comment
@ Джейк, если бы у меня была монета каждый раз, когда я не мог видеть что-то, что вторая пара глаз могла бы тривиально увидеть ... - person Marc Gravell; 28.11.2013

Когда я писал намного больше IL, я написал эти вспомогательные методы, которые позволили мне создавать делегаты, а также в то же время генерировать отладочные сборки. Наличие сборки полезно для запуска инструментов рефлектора, а также PEVerify. https://bitbucket.org/mburbea/delegatedproxy/src/483a0120ff845379898ffad790082c62e43efb4e/Utilities/CodeGen.cs?at=master

Вот моя реализация, я практически никогда не менял ее между своими различными проектами.

person Michael B    schedule 03.12.2013