/// <summary> /// Returns a delegate that will attempt to coerce supplied arguments into the specified delegate and return the result. /// </summary> /// <param name="func"></param> /// <returns></returns> private JavaScriptNativeFunction CreateNativeFunctionForDelegate(Delegate func) { //This is crazy fun. var funcParams = func.Method.GetParameters(); JavaScriptNativeFunction fnDelegate = (IntPtr callee, bool isConstructCall, IntPtr[] arguments, ushort argumentCount, IntPtr callbackData) => { //Make sure that we have argument values for each parameter. var nativeArgs = new object[funcParams.Length]; for (int i = 0; i < funcParams.Length; i++) { var targetParameterType = funcParams[i].ParameterType; var currentArgument = arguments.ElementAtOrDefault(i); if (currentArgument == default(IntPtr)) { //If the argument wasn't specified, use the default value for the target parameter. nativeArgs[i] = targetParameterType.GetDefaultValue(); } else { //As the argument has been specified, convert the JsValue back to an Object using //the conversion strategy associated with the context. var argValueHandle = new JavaScriptValueSafeHandle(currentArgument); var jsValue = CreateValue(argValueHandle); //Keep the first argument as the this JsObject. if (i == 0) { nativeArgs[i] = jsValue as JsObject; } //If the target type is the same as the value type (The delegate expects a JsValue) don't convert. else if (targetParameterType.IsSameOrSubclass(jsValue.GetType())) { nativeArgs[i] = jsValue; } else if (Context.Converter.TryToObject(Context, jsValue, out object obj)) { try { nativeArgs[i] = Convert.ChangeType(obj, targetParameterType); } catch (Exception) { //Something went wrong, use the default value. nativeArgs[i] = targetParameterType.GetDefaultValue(); } } else { //If we couldn't convert the type, use the default value. nativeArgs[i] = targetParameterType.GetDefaultValue(); } } } try { var nativeResult = func.DynamicInvoke(nativeArgs); if (Context.Converter.TryFromObject(Context, nativeResult, out JsValue valueResult)) { return(valueResult.Handle.DangerousGetHandle()); } else { return(Context.Undefined.Handle.DangerousGetHandle()); } } catch (TargetInvocationException exceptionResult) { var jsError = CreateError(exceptionResult.InnerException); m_engine.JsSetException(jsError.Handle); return(Context.Undefined.Handle.DangerousGetHandle()); } }; return(fnDelegate); }