protected internal override JsValue Call(JsValue thisObject, JsValue[] jsArguments) { JsValue[] ArgumentProvider(MethodDescriptor method) { if (method.IsExtensionMethod) { var jsArgumentsTemp = new JsValue[1 + jsArguments.Length]; jsArgumentsTemp[0] = thisObject; Array.Copy(jsArguments, 0, jsArgumentsTemp, 1, jsArguments.Length); return(method.HasParams ? ProcessParamsArrays(jsArgumentsTemp, method) : jsArgumentsTemp); } return(method.HasParams ? ProcessParamsArrays(jsArguments, method) : jsArguments); } var converter = Engine.ClrTypeConverter; var thisObj = thisObject.ToObject(); object[] parameters = null; foreach (var(method, arguments, _) in TypeConverter.FindBestMatch(_engine, _methods, ArgumentProvider)) { var methodParameters = method.Parameters; if (parameters == null || parameters.Length != methodParameters.Length) { parameters = new object[methodParameters.Length]; } var argumentsMatch = true; var resolvedMethod = ResolveMethod(method.Method, methodParameters, thisObj, arguments); // TPC: if we're concerned about cost of MethodInfo.GetParameters() - we could only invoke it if this ends up being a generic method (i.e. they will be different in that scenario) methodParameters = resolvedMethod.GetParameters(); for (var i = 0; i < parameters.Length; i++) { var methodParameter = methodParameters[i]; var parameterType = methodParameter.ParameterType; var argument = arguments.Length > i ? arguments[i] : null; if (typeof(JsValue).IsAssignableFrom(parameterType)) { parameters[i] = argument; } else if (argument is null) { // optional parameters[i] = System.Type.Missing; } else if (IsGenericParameter(argument.ToObject(), parameterType, i)) // don't think we need the condition preface of (argument == null) because of earlier condition { parameters[i] = argument.ToObject(); } else if (parameterType == typeof(JsValue[]) && argument.IsArray()) { // Handle specific case of F(params JsValue[]) var arrayInstance = argument.AsArray(); var len = TypeConverter.ToInt32(arrayInstance.Get(CommonProperties.Length, this)); var result = new JsValue[len]; for (uint k = 0; k < len; k++) { result[k] = arrayInstance.TryGetValue(k, out var value) ? value : Undefined; } parameters[i] = result; } else { if (!ReflectionExtensions.TryConvertViaTypeCoercion(parameterType, _engine.Options.Interop.ValueCoercion, argument, out parameters[i]) && !converter.TryConvert(argument.ToObject(), parameterType, CultureInfo.InvariantCulture, out parameters[i])) { argumentsMatch = false; break; } if (parameters[i] is LambdaExpression lambdaExpression) { parameters[i] = lambdaExpression.Compile(); } } } if (!argumentsMatch) { continue; } // todo: cache method info try { if (method.Method.IsGenericMethodDefinition && method.Method is MethodInfo) { var genericMethodInfo = resolvedMethod; var result = genericMethodInfo.Invoke(thisObj, parameters); return(FromObject(Engine, result)); } return(FromObject(Engine, method.Method.Invoke(thisObj, parameters))); } catch (TargetInvocationException exception) { ExceptionHelper.ThrowMeaningfulException(_engine, exception); } } if (_fallbackClrFunctionInstance is not null) { return(_fallbackClrFunctionInstance.Call(thisObject, jsArguments)); } ExceptionHelper.ThrowTypeError(_engine.Realm, "No public methods with the specified arguments were found."); return(null); }