Exemplo n.º 1
0
        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);
        }