diff --git a/src/StreamJsonRpc/JsonRpc.cs b/src/StreamJsonRpc/JsonRpc.cs index 830c9dda4..cac25775e 100644 --- a/src/StreamJsonRpc/JsonRpc.cs +++ b/src/StreamJsonRpc/JsonRpc.cs @@ -1866,120 +1866,14 @@ protected Task InvokeCoreAsync(RequestId id, string targetName return this.InvokeCoreAsync(id, targetName, arguments, null, null, cancellationToken, isParameterObject); } -#pragma warning disable CA1200 // Avoid using cref tags with a prefix - /// - /// Invokes a given method on a JSON-RPC server. - /// + /// /// RPC method return type. - /// An identifier established by the Client. If the default value is given, it is assumed to be a notification. - /// Name of the method to invoke. Must not be null or empty. - /// Arguments to pass to the invoked method. They must be serializable using the selected . If , no arguments are passed. - /// - /// A list of objects that describe how each element in is expected by the server to be typed. - /// If specified, this must have exactly the same length as and contain no elements. - /// This value is ignored when is true. - /// - /// - /// A dictionary of objects that describe how each entry in the provided in the only element of is expected by the server to be typed. - /// If specified, this must have exactly the same set of keys as the dictionary contained in the first element of , and contain no values. - /// - /// The token whose cancellation should signal the server to stop processing this request. - /// Value which indicates if parameter should be passed as an object. - /// A task that completes with the response from the JSON-RPC server. - /// - /// Thrown after is canceled. - /// If the request has already been transmitted, the exception is only thrown after the server has received the cancellation notification and responded to it. - /// If the server completes the request instead of cancelling, this exception will not be thrown. - /// When the connection drops before receiving a response, this exception is thrown if has been canceled. - /// - /// - /// A common base class for a variety of RPC exceptions that may be thrown. Some common derived types are listed individually. - /// - /// - /// Thrown when an error is returned from the server in consequence of executing the requested method. - /// - /// - /// Thrown when the server reports that no matching method was found to invoke. - /// - /// If is null. - /// Thrown when is empty. - /// If this instance of has already been disposed prior to this call. - /// - /// Thrown when the connection is terminated (by either side) before the request or while the request is in progress, - /// unless is already signaled. - /// - /// - /// Any exception thrown by the (typically due to serialization failures). - /// When using this should be . - /// When using this should be . - /// #pragma warning disable CA1068 // CancellationToken parameters must come last protected async Task InvokeCoreAsync(RequestId id, string targetName, IReadOnlyList? arguments, IReadOnlyList? positionalArgumentDeclaredTypes, IReadOnlyDictionary? namedArgumentDeclaredTypes, CancellationToken cancellationToken, bool isParameterObject) #pragma warning restore CA1068 // CancellationToken parameters must come last -#pragma warning restore CA1200 // Avoid using cref tags with a prefix { - Requires.NotNullOrEmpty(targetName, nameof(targetName)); - - cancellationToken.ThrowIfCancellationRequested(); - Verify.NotDisposed(this); - - JsonRpcRequest request = (this.MessageHandler.Formatter as IJsonRpcMessageFactory)?.CreateRequestMessage() ?? new JsonRpcRequest(); - request.RequestId = id; - request.Method = targetName; - this.ActivityTracingStrategy?.ApplyOutboundActivity(request); - - if (isParameterObject) - { - object? argument = arguments; - if (arguments is not null) - { - if (arguments.Count != 1 || arguments[0] is null) - { - throw new ArgumentException(Resources.ParameterNotObject); - } - - argument = arguments[0]; - } - - request.Arguments = argument; - if (namedArgumentDeclaredTypes is object) - { - Requires.Argument(namedArgumentDeclaredTypes.Count == request.ArgumentCount, nameof(namedArgumentDeclaredTypes), Resources.TypedArgumentsLengthMismatch); - request.NamedArgumentDeclaredTypes = namedArgumentDeclaredTypes; - } - } - else - { - request.Arguments = arguments ?? Array.Empty(); - - if (positionalArgumentDeclaredTypes is object) - { - Requires.Argument(positionalArgumentDeclaredTypes.Count == request.ArgumentCount, nameof(positionalArgumentDeclaredTypes), Resources.TypedArgumentsLengthMismatch); - request.ArgumentListDeclaredTypes = positionalArgumentDeclaredTypes; - } - } - - JsonRpcMessage? response = await this.InvokeCoreAsync(request, typeof(TResult), cancellationToken).ConfigureAwait(false); - - if (request.IsResponseExpected) - { - if (response is JsonRpcError error) - { - throw this.CreateExceptionFromRpcError(request, error); - } - else if (response is JsonRpcResult result) - { - return result.GetResult(); - } - else - { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.ResponseUnexpectedFormat, response?.GetType().FullName ?? "(null)")); - } - } - else - { - return default!; - } + JsonRpcResult? result = await this.InvokeCoreAsync(id, targetName, arguments, positionalArgumentDeclaredTypes, namedArgumentDeclaredTypes, typeof(TResult), isParameterObject, cancellationToken).ConfigureAwait(false); + return result is null ? default! : result.GetResult(); } /// @@ -2241,6 +2135,121 @@ private static bool TryGetTaskFromValueTask(object? result, [NotNullWhen(true)] return false; } + /// + /// Invokes a given method on a JSON-RPC server. + /// + /// An identifier established by the Client. If the default value is given, it is assumed to be a notification. + /// Name of the method to invoke. Must not be null or empty. + /// Arguments to pass to the invoked method. They must be serializable using the selected . If , no arguments are passed. + /// + /// A list of objects that describe how each element in is expected by the server to be typed. + /// If specified, this must have exactly the same length as and contain no elements. + /// This value is ignored when is true. + /// + /// + /// A dictionary of objects that describe how each entry in the provided in the only element of is expected by the server to be typed. + /// If specified, this must have exactly the same set of keys as the dictionary contained in the first element of , and contain no values. + /// + /// The expected type of the result returned by the server. + /// Value which indicates if parameter should be passed as an object. + /// The token whose cancellation should signal the server to stop processing this request. + /// A task that completes with the response from the JSON-RPC server. + /// + /// Thrown after is canceled. + /// If the request has already been transmitted, the exception is only thrown after the server has received the cancellation notification and responded to it. + /// If the server completes the request instead of cancelling, this exception will not be thrown. + /// When the connection drops before receiving a response, this exception is thrown if has been canceled. + /// + /// + /// A common base class for a variety of RPC exceptions that may be thrown. Some common derived types are listed individually. + /// + /// + /// Thrown when an error is returned from the server in consequence of executing the requested method. + /// + /// + /// Thrown when the server reports that no matching method was found to invoke. + /// + /// If is null. + /// Thrown when is empty. + /// If this instance of has already been disposed prior to this call. + /// + /// Thrown when the connection is terminated (by either side) before the request or while the request is in progress, + /// unless is already signaled. + /// + /// + /// Any exception thrown by the (typically due to serialization failures). + /// When using this should be . + /// When using this should be . + /// +#if NET + [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] +#endif + private async ValueTask InvokeCoreAsync(RequestId id, string targetName, IReadOnlyList? arguments, IReadOnlyList? positionalArgumentDeclaredTypes, IReadOnlyDictionary? namedArgumentDeclaredTypes, Type expectedResultType, bool isParameterObject, CancellationToken cancellationToken) + { + Requires.NotNullOrEmpty(targetName, nameof(targetName)); + + cancellationToken.ThrowIfCancellationRequested(); + Verify.NotDisposed(this); + + JsonRpcRequest request = (this.MessageHandler.Formatter as IJsonRpcMessageFactory)?.CreateRequestMessage() ?? new JsonRpcRequest(); + request.RequestId = id; + request.Method = targetName; + this.ActivityTracingStrategy?.ApplyOutboundActivity(request); + + if (isParameterObject) + { + object? argument = arguments; + if (arguments is not null) + { + if (arguments.Count != 1 || arguments[0] is null) + { + throw new ArgumentException(Resources.ParameterNotObject); + } + + argument = arguments[0]; + } + + request.Arguments = argument; + if (namedArgumentDeclaredTypes is object) + { + Requires.Argument(namedArgumentDeclaredTypes.Count == request.ArgumentCount, nameof(namedArgumentDeclaredTypes), Resources.TypedArgumentsLengthMismatch); + request.NamedArgumentDeclaredTypes = namedArgumentDeclaredTypes; + } + } + else + { + request.Arguments = arguments ?? Array.Empty(); + + if (positionalArgumentDeclaredTypes is object) + { + Requires.Argument(positionalArgumentDeclaredTypes.Count == request.ArgumentCount, nameof(positionalArgumentDeclaredTypes), Resources.TypedArgumentsLengthMismatch); + request.ArgumentListDeclaredTypes = positionalArgumentDeclaredTypes; + } + } + + JsonRpcMessage? response = await this.InvokeCoreAsync(request, expectedResultType, cancellationToken).ConfigureAwait(false); + + if (request.IsResponseExpected) + { + if (response is JsonRpcError error) + { + throw this.CreateExceptionFromRpcError(request, error); + } + else if (response is JsonRpcResult result) + { + return result; + } + else + { + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Resources.ResponseUnexpectedFormat, response?.GetType().FullName ?? "(null)")); + } + } + else + { + return null; + } + } + private JsonRpcError CreateCancellationResponse(JsonRpcRequest request) { JsonRpcError errorMessage = (this.MessageHandler.Formatter as IJsonRpcMessageFactory)?.CreateErrorMessage() ?? new JsonRpcError();