/// <summary> /// Logs method invocation information /// </summary> private void LogMethodInvocationInfo(HostMethod method, object instance) { var parameters = method.Parameters; var str = new StringBuilder(); str.Append($"Exception Invoking: [Method: {method.Method.Name}]"); str.Append("\n[Parameters]\n"); for (var ii = 0; ii < parameters.Length; ++ii) { var p = parameters[ii]; str.Append($" [Name: {p.Name}, Type: {p.ParameterType}, Optional: {p.IsOptional}"); if (p.IsOptional) { str.Append($", Default: {p.DefaultValue}"); } str.Append("]\n"); } str.Append("[Object Instance: ").Append(instance).Append("]"); Log.Info(this, str.ToString()); }
/// <summary> /// Regular serial parameter passing. /// </summary> private object[] ToSerialParameters(HostMethod method, JavaScriptValue[] args, ushort argCount) { var totalParameters = argCount - 1; var parameters = method.Parameters; var realParams = new object[parameters.Length]; var argIndex = 1; for (int j = 0; j < parameters.Length; ++j) { var param = parameters[j]; // If we run out of JS parameters passed in compared to the total number of host parameters, // we have either picked the wrong method or the host method has optional parameters with defaults. if (argIndex >= argCount) { if (!param.IsOptional) { Log.Warning(this, "Interop chose the wrong method to execute. Too few JS parameters, no optional C# parameters."); break; } realParams[j] = param.DefaultValue; } else { var arg = args[argIndex++]; realParams[j] = _interop.ToHostObject(arg, param.ParameterType); } } return(realParams); }
/// <summary> /// Generates a parameter array based on the target method parameters. /// </summary> private object[] ToParameters(HostMethod method, JavaScriptValue[] args, ushort argCount) { // Build the parameters up to params, then build params if (method.IsVarArgs) { return(ToVarArgParams(method, args, argCount)); } return(ToSerialParameters(method, args, argCount)); }
/// <inheritdoc/> public bool TryGetInvocation(string invokeKey, out HostMethod method) => _methodInvocations.TryGetValue(invokeKey, out method);
/// <inheritdoc/> public void CacheInvocation(string invokeKey, HostMethod method) => _methodInvocations[invokeKey] = method;
/// <summary> /// Handles the scenario with variable arguments in the host method via <c>params</c>. /// </summary> private object[] ToVarArgParams(HostMethod method, JavaScriptValue[] args, ushort argCount) { if (!method.IsVarArgs) { throw new Exception("Not a variable argument parameter set"); } var totalParameters = argCount - 1; var parameters = method.Parameters; var realParams = new object[parameters.Length]; var vaIndex = method.VarArgIndex; var vaType = method.VarArgType; // Non VarArg Parameters var argIndex = 1; var paramIndex = 0; for (int j = 0; j < vaIndex; ++j) { var param = parameters[j]; // If we run out of JS parameters passed in compared to the total number of host parameters, // we have either picked the wrong method or the host method has optional parameters with defaults. if (argIndex >= argCount) { if (!param.IsOptional) { Log.Warning(this, "Interop chose the wrong method to execute. Too few JS parameters, no optional C# parameters."); break; } realParams[paramIndex++] = param.DefaultValue; } else { var arg = args[argIndex++]; realParams[paramIndex++] = _interop.ToHostObject(arg, param.ParameterType); } } // Determine if we have enough JS parameters to put into var args if (vaIndex > totalParameters) { realParams[paramIndex++] = Array.CreateInstance(vaType, 0); return(realParams); } // Put remaining JS args into var args var remainingLength = totalParameters - vaIndex; var paramsArray = Array.CreateInstance(vaType, remainingLength); for (var j = 0; j < remainingLength; ++j) { var arg = args[argIndex++]; paramsArray.SetValue(_interop.ToHostObject(arg, vaType), j); } // Set last parameter to var arg array realParams[paramIndex++] = paramsArray; return(realParams); }
/// <summary> /// This method type checks each parameter passed by javascript to determine if the method is a suitable /// match for execution. At this point, we can assume that the provided arguments qualify for the method /// signature in terms of number of parameters, accounting for optional parameters and var args. /// </summary> private bool IsCompatibleMethod(HostMethod method, JavaScriptValue[] args, ushort argLength) { var parameters = method.Parameters; var isVarArg = method.IsVarArgs; // Parameter Index Pointers - JS parameters include the callee at the first index, so we start after that var argIndex = 1; var pIndex = 0; // The maximum number of iterations we'll have to run to complete type checking each parameter // This is typically parameters.Length unless there are optional parameters or var args var iterations = Math.Max(parameters.Length, argLength - 1); // Loop Max Argument Count while (iterations-- > 0) { // Case 1. We've Type Checked All JS Parameters Against C# Parameters if (argIndex >= argLength) { // For a Var Arg C# Parameter Method, ensure we've indexed into the var arg index if (isVarArg) { // If we haven't, then we ensure that we have an optional parameter. Otherwise, // we're short JS parameters, so we can't call this method. if (pIndex != method.VarArgIndex) { return(parameters[pIndex].IsOptional); } return(true); } // For methods without var args, we simply ensure we've typed checked against all required // parameters. if (pIndex < parameters.Length) { return(parameters[pIndex].IsOptional); } return(true); } // Case 2. We reach the end of C# Method Parameters, but still have JS Parameters left to check if (pIndex >= parameters.Length) { // This case is only possible with var args (since the params []) counts as a single index if (!method.IsVarArgs) { return(false); } // Ensure that pIndex stays at the var arg index for type checking the element type pIndex = method.VarArgIndex; } // Case 3. TypeCheck JS Argument Against C# Parameter var arg = args[argIndex]; var parameter = parameters[pIndex]; // For Var Args, we need to ensure that we use the var arg type to check against the JS arg. var paramType = (isVarArg && pIndex >= method.VarArgIndex) ? method.VarArgType : parameter.ParameterType; // Increment index pointers argIndex++; pIndex++; // Run type conversion checking, early return on failure if (!JsConversions.IsAssignable(arg, _binder, paramType)) { return(false); } } return(true); }