private void BuildMethodBody(MethodDefinition method, CustomAttribute functionAttr)
        {
            var ilProcessor = method.Body.GetILProcessor();

            var writerVar   = ilProcessor.AddVariable(_typeHelper.MsgPackWriter.TypeRef);
            var functionPtr = BuildInitializer(method, functionAttr);


            ilProcessor.Emit(OpCodes.Ldloca_S, writerVar);
            ilProcessor.Emit(OpCodes.Ldc_I4, (int)Allocator.Temp);
            ilProcessor.Emit(OpCodes.Call, _typeHelper.MsgPackWriter.Ctor);

            ilProcessor.CallMethod(writerVar, _typeHelper.MsgPackWriter.WriteArrayHeader, method.Parameters.Count);

            foreach (var param in method.Parameters)
            {
                var serializeMethod = _serialization.GetTypeSerializeMethod(param.ParameterType);
                ilProcessor.CallStatic(serializeMethod, param, writerVar);
            }

            var paramsPtr = ilProcessor.AddVariable(_typeHelper.MainModule.TypeSystem.IntPtr);
            var paramsLen = ilProcessor.AddVariable(_typeHelper.MainModule.TypeSystem.Int32);

            var returnPtr = ilProcessor.AddVariable(_typeHelper.MainModule.TypeSystem.IntPtr);

            ilProcessor.CallMethod(writerVar, _typeHelper.MsgPackWriter.GetUnsafeBufferPtr).Store(paramsPtr);
            ilProcessor.CallMethod(writerVar, _typeHelper.MsgPackWriter.GetBufferLength).Store(paramsLen);
            ilProcessor.CallStatic(_typeHelper.JsBridge.CallJsFunction, functionPtr, paramsPtr, paramsLen).Store(returnPtr);

            var retAsArrayVar = ilProcessor.AddVariable(_typeHelper.NativeByteArray.TypeRef);

            ilProcessor.CallStatic(_typeHelper.JsInteropUtils.JsDataPointerToNativeArray, returnPtr).Store(retAsArrayVar);

            var readerVar = ilProcessor.AddVariable(_typeHelper.MsgPackReader.TypeRef);

            ilProcessor.Emit(OpCodes.Ldloc, retAsArrayVar);
            ilProcessor.Emit(OpCodes.Newobj, _typeHelper.MsgPackReader.Ctor);
            ilProcessor.Emit(OpCodes.Stloc, readerVar);

            ilProcessor.CallStatic(_typeHelper.JsInteropUtils.DecodeErrorAndThrow, readerVar, retAsArrayVar, returnPtr);

            if (method.ReturnType.FullName != "System.Void")
            {
                var retValueVar = ilProcessor.AddVariable(method.ReturnType);
                ilProcessor.CallStatic(_serialization.GetTypeDeserializationMethod(method.ReturnType), readerVar);
            }

            ilProcessor.Emit(OpCodes.Ret);
        }
        private MethodDefinition CreateWrapperMethod(MethodDefinition method, TypeDefinition wrapper)
        {
            var wrapperMethod = new MethodDefinition("CallbackWrapper", MethodAttributes.Public | MethodAttributes.Static,
                                                     _typeHelper.MainModule.TypeSystem.IntPtr);

            wrapper.Methods.Add(wrapperMethod);
            wrapperMethod.CustomAttributes.Add(new CustomAttribute(_typeHelper.MonoPInvokeCallbackAttributeConstructor));

            var argsPtrParam =
                new ParameterDefinition("argsPtr", ParameterAttributes.None, _typeHelper.MainModule.TypeSystem.IntPtr);

            wrapperMethod.Parameters.Add(argsPtrParam);

            var ilProcessor = wrapperMethod.Body.GetILProcessor();

            if (method.ReturnType.FullName != "System.Void")
            {
                var resultVar         = ilProcessor.AddVariable(method.ReturnType);
                var exceptionCatchVar = ilProcessor.AddVariable(_typeHelper.MainModule.ImportReference(typeof(Exception)));
                var errorVar          = ilProcessor.AddVariable(_typeHelper.MainModule.ImportReference(typeof(Exception)));
                var arrayVar          = ilProcessor.AddVariable(_typeHelper.NativeByteArray.TypeRef);
                var readerVar         = ilProcessor.AddVariable(_typeHelper.MsgPackReader.TypeRef);
                var writerVar         = ilProcessor.AddVariable(_typeHelper.MsgPackWriter.TypeRef);

                OperandBase[] argsVars = new OperandBase[method.Parameters.Count];
                for (int i = 0; i < argsVars.Length; i++)
                {
                    argsVars[i] = ilProcessor.AddVariable(method.Parameters[i].ParameterType);
                }

                if (method.ReturnType.IsValueType)
                {
                    ilProcessor.Emit(OpCodes.Ldloca, resultVar);
                    ilProcessor.Emit(OpCodes.Initobj, method.ReturnType);
                }
                else
                {
                    ilProcessor.Emit(OpCodes.Ldnull);
                    ilProcessor.Emit(OpCodes.Stloc, resultVar);
                }


                ilProcessor.Emit(OpCodes.Ldnull);
                ilProcessor.Emit(OpCodes.Stloc, errorVar);

                // Try block start
                ilProcessor.Emit(OpCodes.Ldarg, argsPtrParam);
                var tryBlockStart = ilProcessor.Body.Instructions.Last();
                ilProcessor.Emit(OpCodes.Call, _typeHelper.JsInteropUtils.JsDataPointerToNativeArray);
                ilProcessor.Emit(OpCodes.Stloc, arrayVar);

                ilProcessor.Emit(OpCodes.Ldloc, arrayVar);
                ilProcessor.Emit(OpCodes.Newobj, _typeHelper.MsgPackReader.Ctor);
                ilProcessor.Emit(OpCodes.Stloc, readerVar);

                ilProcessor.CallStatic(_typeHelper.JsInteropUtils.AssertArgumentsCount, readerVar, method.Parameters.Count);

                for (int i = 0; i < argsVars.Length; i++)
                {
                    var deserializeMethod = _serialization.GetTypeDeserializationMethod(argsVars[i].Type);
                    ilProcessor.CallStatic(deserializeMethod, readerVar).Store(argsVars[i]);
                }

                ilProcessor.CallStatic(_typeHelper.JsInteropUtils.FreeJsDataAndArray, arrayVar, argsPtrParam);
                ilProcessor.CallStatic(method, argsVars).Store(resultVar);;

                var tryBlockEnd = ilProcessor.Body.Instructions.Last();
                // catch block
                ilProcessor.Emit(OpCodes.Stloc, exceptionCatchVar);
                var catchBlockStart = ilProcessor.Body.Instructions.Last();
                ilProcessor.Emit(OpCodes.Ldloc, exceptionCatchVar);
                ilProcessor.Emit(OpCodes.Stloc, errorVar);
                var catchBlockEnd = ilProcessor.Body.Instructions.Last();
                // exception end
                var exceptionBlockEnd = Instruction.Create(OpCodes.Nop);
                ilProcessor.Append(exceptionBlockEnd);
                ilProcessor.InsertAfter(tryBlockEnd, Instruction.Create(OpCodes.Leave_S, exceptionBlockEnd));
                ilProcessor.InsertAfter(catchBlockEnd, Instruction.Create(OpCodes.Leave_S, exceptionBlockEnd));

                // Initialize result writer

                ilProcessor.Emit(OpCodes.Ldloca_S, writerVar);
                ilProcessor.Emit(OpCodes.Ldc_I4, (int)Allocator.Temp);
                ilProcessor.Emit(OpCodes.Call, _typeHelper.MsgPackWriter.Ctor);

                ilProcessor.Emit(OpCodes.Ldloca_S, writerVar);
                ilProcessor.Emit(OpCodes.Ldc_I4, int.MaxValue);
                ilProcessor.Emit(OpCodes.Call, _typeHelper.MsgPackWriter.WriteRawBigEndian32);

                ilProcessor.Emit(OpCodes.Ldloca_S, writerVar);
                ilProcessor.Emit(OpCodes.Ldc_I4_2);
                ilProcessor.Emit(OpCodes.Call, _typeHelper.MsgPackWriter.WriteArrayHeader);

                ilProcessor.CallStatic(_typeHelper.JsInteropUtils.WriteException, writerVar, errorVar);

                ilProcessor.If(errorVar).Null().Then(() => {
                    var serializationMethod = _serialization.GetTypeSerializeMethod(resultVar.VariableType);
                    ilProcessor.CallStatic(serializationMethod, resultVar, writerVar);
                }).Else(() => {
                    ilProcessor.Emit(OpCodes.Ldloca_S, writerVar);
                    ilProcessor.Emit(OpCodes.Call, _typeHelper.MsgPackWriter.WriteNil);
                });
                // Write output data to heap
                ilProcessor.CallStatic(_typeHelper.JsInteropUtils.PackSerializedData, writerVar);

                ilProcessor.Emit(OpCodes.Ret);


                wrapperMethod.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch)
                {
                    TryStart     = tryBlockStart,
                    TryEnd       = tryBlockEnd.Next.Next,
                    HandlerStart = catchBlockStart,
                    HandlerEnd   = catchBlockEnd.Next.Next,
                    CatchType    = _typeHelper.MainModule.ImportReference(typeof(Exception))
                });
            }
            else
            {
                throw new NotImplementedException($"Void return type is not implemented for JsCallback");
            }

            return(wrapperMethod);
        }