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); }