private static void CreateMethod(AmObjectClassBuildingContext context, IAmBindingDescription bindingDesc, MethodInfo method)
        {
            var methodBuilder = context.TypeBuilder.DefineMethod(method.Name,
                                                                 MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig |
                                                                 MethodAttributes.NewSlot | MethodAttributes.Virtual);

            methodBuilder.SetParameters(method.GetParameters().Select(x => x.ParameterType).ToArray());
            methodBuilder.SetReturnType(method.ReturnType);
            methodBuilder.SetImplementationFlags(MethodImplAttributes.Managed);
            var il    = new CilEmitterSugar(methodBuilder.GetILGenerator());
            var field = context.Fields.GetOrAddBindingField(bindingDesc);

            if (method.Name.StartsWith("get_"))
            {
                il.Ldarg(0);
                il.Ldfld(field);
                il.Call(bindingDesc.MakePropertyGetMethod(context.AmClass));
                il.Ret();
            }
            else if (method.Name.StartsWith("set_"))
            {
                il.Ldarg(0);
                il.Ldfld(field);
                il.Ldarg(1);
                il.Call(bindingDesc.MakePropertySetMethod(context.AmClass));
                il.Ret();
            }
            else
            {
                throw new Exception($"Either getter or setter expected, but '{method.Name}' found.");
            }
            context.TypeBuilder.DefineMethodOverride(methodBuilder, method);
        }
        private void CreateInitBindingsMethod(AmObjectClassBuildingContext context)
        {
            var methodBuilder = context.TypeBuilder.DefineMethod("AmInitBindings", MethodAttributes.Public | MethodAttributes.Virtual);

            methodBuilder.SetParameters(Type.EmptyTypes);
            methodBuilder.SetReturnType(typeof(List <IAmBinding>));
            methodBuilder.SetImplementationFlags(MethodImplAttributes.Managed);
            var il      = new CilEmitterSugar(methodBuilder.GetILGenerator());
            var varList = il.DeclareLocal(typeof(List <IAmBinding>));

            il.Newobj(typeof(List <IAmBinding>).GetConstructor(Type.EmptyTypes));
            il.Stloc(varList);
            foreach (var bindingDesc in context.Desc.Bindings)
            {
                var field = context.Fields.GetOrAddBindingField(bindingDesc);
                il.Ldarg(0);                         // [this]
                il.Dup();                            // [this, this]
                il.Ldstr(bindingDesc.Property.Name); // [this, this, propName]
                il.Ldc_I4((int)bindingDesc.Flags);   // [this, this, propName, flags]
                var bindingCtor = field.FieldType.GetConstructors().Single();
                il.Newobj(bindingCtor);              // [this, binding]
                il.Stfld(field);                     // []
                il.Ldloc(varList);                   // [list]
                il.Ldarg(0);                         // [list, this]
                il.Ldfld(field);                     // [list, binding]
                var addMethod = typeof(List <IAmBinding>).GetMethod(nameof(List <IAmBinding> .Add));
                il.Callvirt(addMethod);              // []
            }
            il.Ldloc(varList);
            il.Ret();
        }
        // int SizeOf<T>()
        private static void DefineSizeOfGenericMethod(TypeBuilder typeBuilder)
        {
            var methodBuilder = typeBuilder.DefineMethod("SizeOf",
                                                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);
            var genericTypeParams = methodBuilder.DefineGenericParameters("T");
            var genericParam      = genericTypeParams.Single();

            methodBuilder.SetReturnType(typeof(int));
            methodBuilder.SetParameters(Type.EmptyTypes);
            methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
            var il = new CilEmitterSugar(methodBuilder.GetILGenerator());

            il.Sizeof(genericParam);
            il.Conv_I4();
            il.Ret();
        }
        // T Read<T>(byte* src)
        private static void DefineReadMethod(TypeBuilder typeBuilder)
        {
            var methodBuilder = typeBuilder.DefineMethod("Read",
                                                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);
            var genericTypeParams = methodBuilder.DefineGenericParameters("T");
            var genericParam      = genericTypeParams.Single();

            methodBuilder.SetReturnType(genericParam);
            methodBuilder.SetParameters(typeof(byte *));
            methodBuilder.DefineParameter(1, 0, "src");
            methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
            var il = new CilEmitterSugar(methodBuilder.GetILGenerator());

            il.Ldarg(0);
            il.Ldobj(genericParam);
            il.Ret();
        }
        // void Write<T>(byte* dst, T[] array)
        private static void DefineWriteArrayMethod(TypeBuilder typeBuilder)
        {
            var methodBuilder = typeBuilder.DefineMethod("Write",
                                                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);
            var genericTypeParams = methodBuilder.DefineGenericParameters("T");
            var genericParam      = genericTypeParams.Single();

            methodBuilder.SetReturnType(typeof(void));
            methodBuilder.SetParameters(typeof(byte *), genericParam.MakeArrayType());
            methodBuilder.DefineParameter(1, 0, "dst");
            methodBuilder.DefineParameter(2, 0, "src");
            methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
            var il = new CilEmitterSugar(methodBuilder.GetILGenerator());

            var okLabel  = il.DefineLabel();
            var endLabel = il.DefineLabel();

            il.Ldarg(1);
            il.Brtrue(okLabel);

            il.Ldstr("src");
            il.Newobj(typeof(ArgumentNullException).GetConstructor(new[] { typeof(string) }));
            il.Throw();

            il.MarkLabel(okLabel);

            il.Ldarg(1);
            il.Ldlen();
            il.Conv_I4();
            il.Brfalse(endLabel);

            var pinnedVar = il.PinArray(genericParam, 1);

            il.Ldarg(0);
            il.Ldloc(pinnedVar);
            il.Ldarg(1);
            il.Ldlen();
            il.Conv_I4();
            il.Sizeof(genericParam);
            il.Mul();
            il.Cpblk();
            il.UnpinArray(pinnedVar);

            il.MarkLabel(endLabel);
            il.Ret();
        }
        // int SizeOf(Type type)
        //private static void DefineSizeOfMethod(TypeBuilder typeBuilder)
        //{
        //    var methodBuilder = typeBuilder.DefineMethod("SizeOf",
        //            MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);
        //    methodBuilder.SetReturnType(typeof(int));
        //    methodBuilder.SetParameters(typeof(Type));
        //    methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
        //    var il = new CilEmitterSugar(methodBuilder.GetILGenerator());
        //
        //    il.Ldarg(0);
        //    var sizeOfMethod = typeof(Marshal).GetMethod("SizeOf", new[] { typeof(Type) });
        //    il.Call(sizeOfMethod);
        //    il.Ret();
        //}

        // void CopyBulk(byte* dst, byte* src, int numBytes)
        private static void DefineCopyBulkMethod(TypeBuilder typeBuilder)
        {
            var methodBuilder = typeBuilder.DefineMethod("CopyBulk",
                                                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);

            methodBuilder.SetReturnType(typeof(void));
            methodBuilder.SetParameters(typeof(byte *), typeof(byte *), typeof(int));
            methodBuilder.DefineParameter(1, 0, "dst");
            methodBuilder.DefineParameter(2, 0, "src");
            methodBuilder.DefineParameter(3, 0, "numBytes");
            methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
            var il = new CilEmitterSugar(methodBuilder.GetILGenerator());

            il.Ldarg(0);
            il.Ldarg(1);
            il.Ldarg(2);
            il.Cpblk();
            il.Ret();
        }
        private void CreateConstructor(AmObjectClassBuildingContext context)
        {
            var baseConstructor    = context.AmClass.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance).Single();
            var parameterTypes     = baseConstructor.GetParameters().Select(x => x.ParameterType).ToArray();
            var constructorBuilder = context.TypeBuilder.DefineConstructor(
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                CallingConventions.Standard, parameterTypes);

            var il = new CilEmitterSugar(constructorBuilder.GetILGenerator());

            il.Ldarg(0);                // [this]
            for (int i = 0; i < parameterTypes.Length; i++)
            {
                il.Ldarg(i + 1);
            }
            il.Call(baseConstructor);   // []

            //il.Ldarg(0);
            //il.Callvirt(typeof(IAmObject).GetMethod(nameof(IAmObject.AmOnFinalized)));

            il.Ret();
        }
        // void CopyBulkUniversal(byte* dst, byte* src, int numBytes)
        private static void DefineCopyBulkUniversalMethod(TypeBuilder typeBuilder)
        {
            var methodBuilder = typeBuilder.DefineMethod("CopyBulkUniversal",
                                                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);

            methodBuilder.SetReturnType(typeof(void));
            methodBuilder.SetParameters(typeof(byte *), typeof(byte *), typeof(int));
            methodBuilder.DefineParameter(1, 0, "dst");
            methodBuilder.DefineParameter(2, 0, "src");
            methodBuilder.DefineParameter(3, 0, "numBytes");
            methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
            var il            = new CilEmitterSugar(methodBuilder.GetILGenerator());
            var toInt64Method = typeof(IntPtr).GetMethod("ToInt64");
            var getSizeMethod = typeof(IntPtr).GetMethod("get_Size");

            il.Ldarg(0);
            il.Call(toInt64Method);
            il.Ldarg(1);
            il.Call(toInt64Method);
            il.Or();                    // [(dst.ToInt64() | src.ToInt64())]

            il.Call(getSizeMethod);
            il.Ldc_I4(1);
            il.Sub();
            il.Conv_I8();
            il.And();                   // [((dst.ToInt64() | src.ToInt64()) & (IntPtr.Size - 1))]

            var cpblkLabel = il.DefineLabel();

            il.Brfalse(cpblkLabel);
            il.Unaligned(1);
            il.MarkLabel(cpblkLabel);
            il.Ldarg(0);
            il.Ldarg(1);
            il.Ldarg(2);
            il.Cpblk();
            il.Ret();
        }
        // void Read<T>(T[] dst, byte* src, int startElem, int numElems)
        private static void DefineReadArrayRangeMethod(TypeBuilder typeBuilder)
        {
            var methodBuilder = typeBuilder.DefineMethod("Read",
                                                         MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static);
            var genericTypeParams = methodBuilder.DefineGenericParameters("T");
            var genericParam      = genericTypeParams.Single();

            methodBuilder.SetReturnType(typeof(void));
            methodBuilder.SetParameters(genericParam.MakeArrayType(), typeof(byte *), typeof(int), typeof(int));
            methodBuilder.DefineParameter(1, 0, "dst");
            methodBuilder.DefineParameter(2, 0, "src");
            methodBuilder.DefineParameter(3, 0, "startElem");
            methodBuilder.DefineParameter(4, 0, "numElems");
            methodBuilder.SetImplementationFlags(MethodImplAttributes.AggressiveInlining);
            var il = new CilEmitterSugar(methodBuilder.GetILGenerator());

            var throwLabel = il.DefineLabel();
            var okLabel    = il.DefineLabel();
            var endLabel   = il.DefineLabel();

            il.Ldarg(0);
            il.Brfalse(throwLabel);

            il.Ldarg(2);
            il.Ldc_I4(0);
            il.Blt(throwLabel);

            il.Ldarg(0);
            il.Ldlen();
            il.Ldarg(2);
            il.Ldarg(3);
            il.Add();
            il.Bge(okLabel);

            il.MarkLabel(throwLabel);
            il.Ldstr("Array is either null or the range is outside its bounds.");
            il.Newobj(typeof(ArgumentException).GetConstructor(new[] { typeof(string) }));
            il.Throw();

            il.MarkLabel(okLabel);

            il.Ldarg(3);
            il.Ldc_I4(1);
            il.Blt(endLabel);

            var pinnedVar = il.PinArray(genericParam, 0);

            il.Ldloc(pinnedVar);
            il.Ldarg(2);
            il.Sizeof(genericParam);
            il.Mul();
            il.Add();
            il.Ldarg(1);
            il.Ldarg(3);
            il.Sizeof(genericParam);
            il.Mul();
            il.Cpblk();
            il.UnpinArray(pinnedVar);

            il.MarkLabel(endLabel);
            il.Ret();
        }