3

uint64 プロパティ値を設定するための IL の発行に問題があります。以下は、問題を再現するための最小限のコードです。

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
        new AssemblyName("test"), AssemblyBuilderAccess.RunAndSave);
      ModuleBuilder m_moduleBuilder = assemblyBuilder.DefineDynamicModule("test.dll",
        "test.dll");
      TypeBuilder typeBuilder = m_moduleBuilder.DefineType("Class1",
        TypeAttributes.Public |
        TypeAttributes.Class |
        TypeAttributes.AutoClass |
        TypeAttributes.AnsiClass |
        TypeAttributes.BeforeFieldInit |
        TypeAttributes.AutoLayout, null);

      FieldBuilder fieldBuilder = typeBuilder.DefineField("m_prop1",
          typeof(ulong), FieldAttributes.Private);

      MethodBuilder getMethodBuilder = typeBuilder.DefineMethod(
        "get_prop1",
        MethodAttributes.Public | MethodAttributes.SpecialName |
        MethodAttributes.HideBySig,
        typeof(ulong), Type.EmptyTypes);
      ILGenerator getIlGen = getMethodBuilder.GetILGenerator();
      getIlGen.Emit(OpCodes.Ldarg_0);
      getIlGen.Emit(OpCodes.Ldfld, fieldBuilder);
      getIlGen.Emit(OpCodes.Ret);

      MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
        "set_prop1",
        MethodAttributes.Public | MethodAttributes.SpecialName |
        MethodAttributes.HideBySig,
        null, new[] { typeof(ulong) });
      ILGenerator setIlGen = setMethodBuilder.GetILGenerator();
      setIlGen.Emit(OpCodes.Ldarg_0);
      setIlGen.Emit(OpCodes.Ldarg_1);
      setIlGen.Emit(OpCodes.Stfld, fieldBuilder);
      setIlGen.Emit(OpCodes.Ret);

      PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
        "prop1", PropertyAttributes.HasDefault, typeof(ulong),
        null);
      propertyBuilder.SetGetMethod(getMethodBuilder);
      propertyBuilder.SetSetMethod(setMethodBuilder);

      ConstructorBuilder constructorBuilder =
        typeBuilder.DefineConstructor(MethodAttributes.Public,
        CallingConventions.Standard, Type.EmptyTypes);
      ILGenerator ilGenerator = constructorBuilder.GetILGenerator();
      ilGenerator.Emit(OpCodes.Ldarg_0);
      ilGenerator.Emit(OpCodes.Call,
        typeBuilder.BaseType.GetConstructor(Type.EmptyTypes));
      ilGenerator.Emit(OpCodes.Ldarg_0);
      ilGenerator.Emit(OpCodes.Ldc_I8, ulong.Parse("0"));
      ilGenerator.Emit(OpCodes.Call, propertyBuilder.GetSetMethod());
      ilGenerator.Emit(OpCodes.Ret);

      Type class1Type = typeBuilder.CreateType();

      assemblyBuilder.Save("test.dll");
    }
  }
}

作成した .dll に対して peverify を実行すると、次のようになります。

[IL]: Error: [C:\code\ConsoleApplication1\ConsoleApplication1\bin\Debug\test.dl
 : Class1::.ctor][offset 0x00000010] Unrecognized local variable number.
1 Error(s) Verifying test.dll

コンストラクタを逆アセンブルすると、次のようになります。

.method public specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       18 (0x12)
  .maxstack  4
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  ldarg.0
  IL_0007:  ldc.i8     0x22800000000
  IL_0010:  ldloc.0
  IL_0011:  ret
} // end of method Class1::.ctor

では、なぜプロパティ セットの呼び出しに変更ilGenerator.Emit(OpCodes.Ldc_I8, ulong.Parse("0"));され IL_0007: ldc.i8 0x22800000000、何が起こったのでしょうか?

ulong.Parse を long.Parse に変更すると、これらのエラーのないアセンブリが作成されますが、long.MaxValue より大きい値をフィードすると long.Parse がクラッシュします。

4

2 に答える 2

4

元の質問を投稿してから発見したように、 -aware オーバーロードがないため、 への特定の呼び出しEmitは、 a を受け入れるオーバーロードを使用することになります。ILDASM に見られる奇妙に見える定数は、主にそのオーバーロードが 0 値に対して 4 バイトしか出力しないためですが、8 バイトの定数が予期されていたため、次のオペコードを定数の上位バイトとして解釈しました (これもプロパティアクセサー呼び出しが明らかに完全に異なるオペコードに置き換えられた理由を説明しています)。floatulong

于 2013-08-07T16:56:03.953 に答える
0

秘訣は、 unchecked キーワードを使用して ulong を long に変換し、発行操作を満足させることです。

  ilGenerator.Emit(OpCodes.Ldc_I8, unchecked((long)ulong.Parse("0")));
于 2013-08-07T16:46:59.687 に答える