From d17e863ff89e5a77921c5004d2ca319544670448 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 12:15:08 +0200 Subject: [PATCH 01/99] Add Browser specific files to the project --- .../src/System.Net.Http.csproj | 218 +++++++++++++----- 1 file changed, 163 insertions(+), 55 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index cebece24f4e13b..277f670d0c55e1 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -7,12 +7,15 @@ $(NoWarn);0436;CS1573 true $(DefineConstants);HTTP_DLL - $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS + $(NetCoreAppCurrent)-Windows_NT;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-Browser enable $(DefineConstants);SYSNETHTTP_NO_OPENSSL + + $(DefineConstants);BROWSER_DOES_NOT_SUPPORT_QUIC + @@ -120,61 +123,9 @@ Common\System\Net\ArrayBuffer.cs - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Common\System\Net\NTAuthentication.Common.cs - - - Common\System\Net\ContextFlagsPal.cs - Common\System\Net\Http\aspnetcore\IHttpHeadersHandler.cs @@ -250,6 +201,63 @@ Common\System\Net\Http\aspnetcore\Http3\QPack\H3StaticTable.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Common\System\Net\NTAuthentication.Common.cs + + + Common\System\Net\ContextFlagsPal.cs + Common\System\Net\Http\aspnetcore\Quic\Implementations\MsQuic\Internal\MsQuicAddressHelpers.cs @@ -392,6 +400,106 @@ Common\System\Text\ValueStringBuilder.cs + + + + + + Common\System\CharArrayHelpers.cs + + + Common\System\StringExtensions.cs + + + Common\System\Net\HttpKnownHeaderNames.cs + + + Common\System\Net\HttpKnownHeaderNames.TryGetHeaderName.cs + + + Common\System\Net\HttpStatusDescription.cs + + + Common\System\Net\Http\HttpHandlerDefaults.cs + + + Common\System\Net\SecurityProtocol.cs + + + Common\System\Net\UriScheme.cs + + + Common\System\Text\ValueStringBuilder.cs + + + + Common\System\Net\Security\CertificateHelper.cs + + + Common\System\Net\Security\CertificateHelper.Unix.cs + + + Common\System\Threading\Tasks\TaskToApm.cs + + + + + + + + + + + + + + Common\Interop\Browser\Interop.Runtime.cs + + + Common\Interop\Browser\Interop.JavaScript.JSException.cs + + + Common\Interop\Browser\Interop.JavaScript.JSObject.cs + + + Common\Interop\Browser\Interop.JavaScript.Uint8Array.cs + + + Common\Interop\Browser\Interop.JavaScript.Uint8Array.cs + + + Common\Interop\Browser\Interop.JavaScript.TypedArray.cs + + + Common\Interop\Browser\Interop.JavaScript.Array.cs + + + Common\Interop\Browser\Interop.JavaScript.ArrayBuffer.cs + + + Common\Interop\Browser\Interop.JavaScript.SharedArrayBuffer.cs + + + Common\Interop\Browser\Interop.JavaScript.CoreObject.cs + + + Common\Interop\Browser\Interop.JavaScript.HostObject.cs + + + Common\Interop\Browser\Interop.JavaScript.Function.cs + + + Common\Interop\Browser\Interop.Runtime.AnyRef.cs + + + Common\Interop\Browser\Interop.HttpHandlerService.cs + + + + Common\Interop\Browser\Interop.HttpHandlerService.cs + + + From aafa4b0c23da70bb1ee3afce187ec9bfd6fbbddf Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 12:15:57 +0200 Subject: [PATCH 02/99] Browser specific file modification to not include Quic support --- .../src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index 7f88b005bfdfd6..e9c0a3bfa2a470 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -4,7 +4,9 @@ using System.Diagnostics; using System.IO; +#if !BROWSER_DOES_NOT_SUPPORT_QUIC using System.Net.Quic; +#endif using System.Net.Security; using System.Net.Sockets; using System.Runtime.CompilerServices; @@ -178,6 +180,7 @@ private static async ValueTask EstablishSslConnectionAsyncCore(Stream return sslStream; } +#if !BROWSER_DOES_NOT_SUPPORT_QUIC public static async ValueTask ConnectQuicAsync(string host, int port, SslClientAuthenticationOptions? clientAuthenticationOptions, CancellationToken cancellationToken) { IPAddress[] addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false); @@ -207,7 +210,7 @@ public static async ValueTask ConnectQuicAsync(string host, int // TODO: find correct exception to throw here. throw new HttpRequestException("No host found."); } - +#endif private static Exception CreateWrappedException(Exception error, CancellationToken cancellationToken) { return CancellationHelper.ShouldWrapInOperationCanceledException(error, cancellationToken) ? From 7d52436f490da540ab53322bd59a25b619263f61 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 12:17:11 +0200 Subject: [PATCH 03/99] Add Browser specific support files for Http Handler code --- .../BrowserHttpMessageHandler.cs | 46 ++++ .../BrowserHttpHandler/SocketsHttpHandler.cs | 238 ++++++++++++++++++ .../SystemProxyInfo.Browser.cs | 15 ++ 3 files changed, 299 insertions(+) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs new file mode 100644 index 00000000000000..a91720480be0f3 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using System.Collections.Generic; +using System.Net.Security; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + internal partial class BrowserHttpMessageHandler : HttpMessageHandler + { + // This partial implementation contains members common to Browser WebAssembly running on .NET Core. + internal Interop.Browser.IHttpHandlerService? wasmHandler; + + public BrowserHttpMessageHandler() + { + wasmHandler = new Interop.Browser.BrowserHttpHandlerService(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (wasmHandler != null) + { + wasmHandler.Dispose(); + wasmHandler = null; + } + } + base.Dispose(disposing); + } + + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (wasmHandler == null) + throw new ObjectDisposedException(GetType().ToString()); + + return wasmHandler.SendAsync(request, cancellationToken); + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs new file mode 100644 index 00000000000000..64894aee268f89 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Net.Security; +using System.Threading; +using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + +namespace System.Net.Http +{ + public sealed class SocketsHttpHandler : HttpMessageHandler + { + private HttpMessageHandler? _handler; + private bool _disposed; + + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(SocketsHttpHandler)); + } + } + + private void CheckDisposedOrStarted() + { + CheckDisposed(); + if (_handler != null) + { + throw new InvalidOperationException(SR.net_http_operation_started); + } + } + + public bool UseCookies + { + get => throw new PlatformNotSupportedException("Property UseCookies is not supported."); + set => throw new PlatformNotSupportedException("Property UseCookies is not supported."); + } + + [AllowNull] + public CookieContainer CookieContainer + { + get => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + set => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + } + + public DecompressionMethods AutomaticDecompression + { + get => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + set => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + } + + public bool UseProxy + { + get => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + set => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + } + + public IWebProxy? Proxy + { + get => throw new PlatformNotSupportedException("Property Proxy is not supported."); + set => throw new PlatformNotSupportedException("Property Proxy is not supported."); + } + + public ICredentials? DefaultProxyCredentials + { + get => throw new PlatformNotSupportedException("Property Credentials is not supported."); + set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + } + + public bool PreAuthenticate + { + get => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + set => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + } + + public ICredentials? Credentials + { + get => throw new PlatformNotSupportedException("Property Credentials is not supported."); + set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + } + + public bool AllowAutoRedirect + { + get => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + set => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + } + + public int MaxAutomaticRedirections + { + get => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + set => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + } + + public int MaxConnectionsPerServer + { + get => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + set => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + } + + public int MaxResponseDrainSize + { + get => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); + set => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); + } + + public TimeSpan ResponseDrainTimeout + { + get => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); + } + + public int MaxResponseHeadersLength + { + get => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + set => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + } + + [AllowNull] + public SslClientAuthenticationOptions SslOptions + { + get => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + set => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + } + + public TimeSpan PooledConnectionLifetime + { + get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + } + + public TimeSpan PooledConnectionIdleTimeout + { + get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + } + + public TimeSpan ConnectTimeout + { + get => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); + } + + public TimeSpan Expect100ContinueTimeout + { + get => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); + } + + public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + _disposed = true; + _handler?.Dispose(); + _handler = null; + } + + base.Dispose(disposing); + } + + private HttpMessageHandler SetupHandlerChain() + { + + HttpMessageHandler handler = new BrowserHttpMessageHandler(); + + // Ensure a single handler is used for all requests. + if (Interlocked.CompareExchange(ref _handler, handler, null) != null) + { + handler.Dispose(); + } + + return _handler; + } + + protected internal override Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) + { + CheckDisposed(); + HttpMessageHandler handler = _handler ?? SetupHandlerChain(); + + Exception? error = ValidateAndNormalizeRequest(request); + if (error != null) + { + return Task.FromException(error); + } + + return handler.SendAsync(request, cancellationToken); + } + + private Exception? ValidateAndNormalizeRequest(HttpRequestMessage request) + { + if (request.Version.Major == 0) + { + return new NotSupportedException(SR.net_http_unsupported_version); + } + + // Add headers to define content transfer, if not present + if (request.HasHeaders && request.Headers.TransferEncodingChunked.GetValueOrDefault()) + { + if (request.Content == null) + { + return new HttpRequestException(SR.net_http_client_execution_error, + new InvalidOperationException(SR.net_http_chunked_not_allowed_with_empty_content)); + } + + // Since the user explicitly set TransferEncodingChunked to true, we need to remove + // the Content-Length header if present, as sending both is invalid. + request.Content.Headers.ContentLength = null; + } + else if (request.Content != null && request.Content.Headers.ContentLength == null) + { + // We have content, but neither Transfer-Encoding nor Content-Length is set. + request.Headers.TransferEncodingChunked = true; + } + + if (request.Version.Minor == 0 && request.Version.Major == 1 && request.HasHeaders) + { + // HTTP 1.0 does not support chunking + if (request.Headers.TransferEncodingChunked == true) + { + return new NotSupportedException(SR.net_http_unsupported_chunking); + } + + // HTTP 1.0 does not support Expect: 100-continue; just disable it. + if (request.Headers.ExpectContinue == true) + { + request.Headers.ExpectContinue = false; + } + } + + return null; + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs new file mode 100644 index 00000000000000..6e907eb7b5e39b --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Http +{ + internal static partial class SystemProxyInfo + { + // On Browser we do not support proxy. + public static IWebProxy ConstructSystemProxy() + { + throw new PlatformNotSupportedException ("WebProxy is not supported."); + } + } +} From 242ade65f85af181874a8df46d798a8417615977 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 12:25:47 +0200 Subject: [PATCH 04/99] Add Interop code for JavaScript support --- .../Browser/Interop.JavaScript.Array.cs | 40 ++ .../Browser/Interop.JavaScript.ArrayBuffer.cs | 25 + .../Browser/Interop.JavaScript.CoreObject.cs | 21 + .../Browser/Interop.JavaScript.Function.cs | 34 + .../Browser/Interop.JavaScript.HostObject.cs | 33 + .../Browser/Interop.JavaScript.JSException.cs | 12 + .../Browser/Interop.JavaScript.JSObject.cs | 168 +++++ .../Interop.JavaScript.SharedArrayBuffer.cs | 18 + .../Browser/Interop.JavaScript.TypedArray.cs | 285 ++++++++ .../Browser/Interop.JavaScript.Uint8Array.cs | 43 ++ .../Interop.JavaScript.Uint8ClampedArray.cs | 42 ++ .../Interop/Browser/Interop.Runtime.AnyRef.cs | 28 + .../src/Interop/Browser/Interop.Runtime.cs | 625 ++++++++++++++++++ 13 files changed, 1374 insertions(+) create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs new file mode 100644 index 00000000000000..497a27e90b6c31 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs @@ -0,0 +1,40 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + public class Array : CoreObject + { + public Array(params object[] _params) : base(Runtime.New(_params)) + { } + internal Array(IntPtr js_handle) : base(js_handle) + { } + public int Push(params object[] elements) => (int)Invoke("push", elements); + public object Pop() => (object)Invoke("pop"); + public object Shift() => Invoke("shift"); + public int UnShift(params object[] elements) => (int)Invoke("unshift", elements); + public int IndexOf(object searchElement, int fromIndex = 0) => (int)Invoke("indexOf", searchElement, fromIndex); + public int LastIndexOf(object searchElement) => (int)Invoke("lastIndexOf", searchElement); + public int LastIndexOf(object searchElement, int endIndex) => (int)Invoke("lastIndexOf", searchElement, endIndex); + public object this[int i] + { + get + { + var indexValue = Runtime.GetByIndex(JSHandle, i, out int exception); + + if (exception != 0) + throw new JSException((string)indexValue); + return indexValue; + } + set + { + var res = Runtime.SetByIndex(JSHandle, i, value, out int exception); + + if (exception != 0) + throw new JSException((string)res); + + } + } + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs new file mode 100644 index 00000000000000..842e59acc200a7 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs @@ -0,0 +1,25 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + public class ArrayBuffer : CoreObject + { + + public ArrayBuffer() : base(Runtime.New()) + { } + + public ArrayBuffer(int length) : base(Runtime.New(length)) + { } + + internal ArrayBuffer(IntPtr js_handle) : base(js_handle) + { } + + public int ByteLength => (int)GetObjectProperty("byteLength"); + public bool IsView => (bool)GetObjectProperty("isView"); + public ArrayBuffer Slice(int begin) => (ArrayBuffer)Invoke("slice", begin); + public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)Invoke("slice", begin, end); + + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs new file mode 100644 index 00000000000000..b156f041335b57 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -0,0 +1,21 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + public abstract class CoreObject : JSObject + { + + protected CoreObject(int js_handle) : base(js_handle) + { + var result = Runtime.BindCoreObject(js_handle, (int)(IntPtr)Handle, out int exception); + if (exception != 0) + throw new JSException($"CoreObject Error binding: {result.ToString()}"); + + } + + internal CoreObject(IntPtr js_handle) : base(js_handle) + { } + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs new file mode 100644 index 00000000000000..4d6652c6d6b4c6 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + public class Function : CoreObject + { + public Function(params object[] args) : base(Runtime.New(args)) + { } + + internal Function(IntPtr js_handle) : base(js_handle) + { } + + + public object Apply(object? thisArg = null, object[]? argsArray = null) => Invoke("apply", thisArg, argsArray); + + public Function Bind(object? thisArg = null, object[]? argsArray = null) => (Function)Invoke("bind", thisArg, argsArray); + + public object Call(object? thisArg = null, params object[] argsArray) + { + object?[] argsList = new object[argsArray.Length + 1]; + argsList[0] = thisArg; + System.Array.Copy(argsArray, 0, argsList, 1, argsArray.Length); + return Invoke("call", argsList); + } + + + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs new file mode 100644 index 00000000000000..d4bd2e0f57ad85 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs @@ -0,0 +1,33 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + public interface IHostObject + { + + } + public class HostObject : HostObjectBase + { + public HostObject(string hostName, params object[] _params) : base(Runtime.New(hostName, _params)) + { } + } + + public abstract class HostObjectBase : JSObject, IHostObject + { + + protected HostObjectBase(int js_handle) : base(js_handle) + { + var result = Runtime.BindHostObject(js_handle, (int)(IntPtr)Handle, out int exception); + if (exception != 0) + throw new JSException($"HostObject Error binding: {result.ToString()}"); + + } + + internal HostObjectBase(IntPtr js_handle) : base(js_handle) + { } + + + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs new file mode 100644 index 00000000000000..113d77dd70476d --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs @@ -0,0 +1,12 @@ +using System; +internal static partial class Interop +{ + + internal static partial class JavaScript + { + public class JSException : Exception + { + public JSException(string msg) : base(msg) { } + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs new file mode 100644 index 00000000000000..45c8ba00d6d7dc --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; + +internal static partial class Interop +{ + internal static partial class JavaScript + { + public interface IJSObject + { + int JSHandle { get; } + int Length { get; } + } + } + internal static partial class JavaScript + { + /// + /// JSObjects are wrappers for a native JavaScript object, and + /// they retain a reference to the JavaScript object for the lifetime of this C# object. + /// + public class JSObject : Interop.Runtime.AnyRef, IJSObject, IDisposable + { + internal object? RawObject; + + // to detect redundant calls + public bool IsDisposed { get; internal set; } + + public JSObject() : this(Runtime.New()) + { + var result = Runtime.BindCoreObject(JSHandle, (int)(IntPtr)Handle, out int exception); + if (exception != 0) + throw new JSException($"JSObject Error binding: {result.ToString()}"); + + } + + internal JSObject(IntPtr js_handle) : base(js_handle) + { + //Console.WriteLine ($"JSObject: {js_handle}"); + } + + internal JSObject(int js_handle) : base((IntPtr)js_handle) + { + //Console.WriteLine ($"JSObject: {js_handle}"); + } + + internal JSObject(int js_handle, object raw_obj) : base(js_handle) + { + RawObject = raw_obj; + } + + public object Invoke(string method, params object?[] args) + { + var res = Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); + if (exception != 0) + throw new JSException((string)res); + return res; + } + + public object GetObjectProperty(string name) + { + + var propertyValue = Runtime.GetObjectProperty(JSHandle, name, out int exception); + + if (exception != 0) + throw new JSException((string)propertyValue); + + return propertyValue; + + } + + public void SetObjectProperty(string name, object value, bool createIfNotExists = true, bool hasOwnProperty = false) + { + + var setPropResult = Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); + if (exception != 0) + throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"); + + } + + /// + /// Gets or sets the length. + /// + /// The length. + public int Length + { + get => Convert.ToInt32(GetObjectProperty("length")); + set => SetObjectProperty("length", value, false); + } + + /// + /// Returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it). + /// + /// true, if the object has the specified property as own property, false otherwise. + /// The String name or Symbol of the property to test. + public bool HasOwnProperty(string prop) => (bool)Invoke("hasOwnProperty", prop); + + /// + /// Returns a boolean indicating whether the specified property is enumerable. + /// + /// true, if the specified property is enumerable, false otherwise. + /// The String name or Symbol of the property to test. + public bool PropertyIsEnumerable(string prop) => (bool)Invoke("propertyIsEnumerable", prop); + + protected void FreeHandle() + { + Runtime.ReleaseHandle(JSHandle, out int exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"); + } + + public override bool Equals(object? obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + return JSHandle == (obj as JSObject)?.JSHandle; + } + + public override int GetHashCode() + { + return JSHandle; + } + + ~JSObject() + { + Dispose(false); + } + + public void Dispose() + { + // Dispose of unmanaged resources. + Dispose(true); + // Suppress finalization. + GC.SuppressFinalize(this); + } + + // Protected implementation of Dispose pattern. + protected virtual void Dispose(bool disposing) + { + + if (!IsDisposed) + { + if (disposing) + { + + // Free any other managed objects here. + // + RawObject = null; + } + + IsDisposed = true; + + // Free any unmanaged objects here. + FreeHandle(); + + } + } + + public override string ToString() + { + return $"(js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"; + } + + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs new file mode 100644 index 00000000000000..19fc4b13854505 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs @@ -0,0 +1,18 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + public class SharedArrayBuffer : CoreObject + { + public SharedArrayBuffer(int length) : base(Runtime.New(length)) + { } + + internal SharedArrayBuffer(IntPtr js_handle) : base(js_handle) + { } + + public int ByteLength => (int)GetObjectProperty("byteLength"); + public SharedArrayBuffer Slice(int begin, int end) => (SharedArrayBuffer)Invoke("slice", begin, end); + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs new file mode 100644 index 00000000000000..45f565b94e089a --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs @@ -0,0 +1,285 @@ +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class JavaScript + { + public interface ITypedArray + { + int BytesPerElement { get; } + string Name { get; } + int ByteLength { get; } + ArrayBuffer Buffer { get; } + + void Set(Array array); + void Set(Array array, int offset); + + void Set(ITypedArray typedArray); + void Set(ITypedArray typedArray, int offset); + //TypedArrayTypeCode GetTypedArrayType(); + } + + public interface ITypedArray where U : struct + { + + T Slice(); + T Slice(int begin); + T Slice(int begin, int end); + + T SubArray(); + T SubArray(int begin); + T SubArray(int begin, int end); + + } + + public enum TypedArrayTypeCode + { + Int8Array = 5, + Uint8Array = 6, + Int16Array = 7, + Uint16Array = 8, + Int32Array = 9, + Uint32Array = 10, + Float32Array = 13, + Float64Array = 14, + Uint8ClampedArray = 0xF, + } + + public abstract class TypedArray : CoreObject, ITypedArray, ITypedArray where U : struct + { + protected TypedArray() : base(Runtime.New()) + { } + protected TypedArray(int length) : base(Runtime.New(length)) + { } + + protected TypedArray(ArrayBuffer buffer) : base(Runtime.New(buffer)) + { } + + protected TypedArray(ArrayBuffer buffer, int byteOffset) : base(Runtime.New(buffer, byteOffset)) + { } + + protected TypedArray(ArrayBuffer buffer, int byteOffset, int length) : base(Runtime.New(buffer, byteOffset, length)) + { } + + protected TypedArray(SharedArrayBuffer buffer) : base(Runtime.New(buffer)) + { } + + protected TypedArray(SharedArrayBuffer buffer, int byteOffset) : base(Runtime.New(buffer, byteOffset)) + { } + + protected TypedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(Runtime.New(buffer, byteOffset, length)) + { } + + internal TypedArray(IntPtr js_handle) : base(js_handle) + { } + + // public TypedArrayTypeCode GetTypedArrayType() + // { + // switch (this) + // { + // case Int8Array _: + // return TypedArrayTypeCode.Int8Array; + // case Uint8Array _: + // return TypedArrayTypeCode.Uint8Array; + // case Uint8ClampedArray _: + // return TypedArrayTypeCode.Uint8ClampedArray; + // case Int16Array _: + // return TypedArrayTypeCode.Int16Array; + // case Uint16Array _: + // return TypedArrayTypeCode.Uint16Array; + // case Int32Array _: + // return TypedArrayTypeCode.Int32Array; + // case Uint32Array _: + // return TypedArrayTypeCode.Uint32Array; + // case Float32Array _: + // return TypedArrayTypeCode.Float32Array; + // case Float64Array _: + // return TypedArrayTypeCode.Float64Array; + // default: + // throw new ArrayTypeMismatchException("TypedArray is not of correct type."); + // } + // } + + public int BytesPerElement => (int)GetObjectProperty("BYTES_PER_ELEMENT"); + public string Name => (string)GetObjectProperty("name"); + public int ByteLength => (int)GetObjectProperty("byteLength"); + public ArrayBuffer Buffer => (ArrayBuffer)GetObjectProperty("buffer"); + + public void Fill(U value) => Invoke("fill", value); + public void Fill(U value, int start) => Invoke("fill", value, start); + public void Fill(U value, int start, int end) => Invoke("fill", value, start, end); + + public void Set(Array array) => Invoke("set", array); + public void Set(Array array, int offset) => Invoke("set", array, offset); + public void Set(ITypedArray typedArray) => Invoke("set", typedArray); + public void Set(ITypedArray typedArray, int offset) => Invoke("set", typedArray, offset); + + public T Slice() => (T)Invoke("slice"); + public T Slice(int begin) => (T)Invoke("slice", begin); + public T Slice(int begin, int end) => (T)Invoke("slice", begin, end); + + public T SubArray() => (T)Invoke("subarray"); + public T SubArray(int begin) => (T)Invoke("subarray", begin); + public T SubArray(int begin, int end) => (T)Invoke("subarray", begin, end); + + public U? this[int i] + { + get + { + var jsValue = Runtime.GetByIndex(JSHandle, i, out int exception); + + if (exception != 0) + throw new JSException((string)jsValue); + + // The value returned from the index. + return UnBoxValue(jsValue); + } + set + { + object res = Runtime.SetByIndex(JSHandle, i, value, out int exception); + + if (exception != 0) + throw new JSException((string)res); + + } + } + + private U? UnBoxValue(object jsValue) + { + if (jsValue != null) + { + var type = jsValue.GetType(); + if (type.IsPrimitive) + { + return (U)Convert.ChangeType(jsValue, typeof(U)); + } + else + { + throw new InvalidCastException($"Unable to cast object of type {type} to type {typeof(U)}."); + } + + } + else + return null; + } + + public U[] ToArray() + { + var res = Runtime.TypedArrayToArray(JSHandle, out int exception); + + if (exception != 0) + throw new JSException((string)res); + return (U[])res; + } + + public static unsafe T From(ReadOnlySpan span) + { + // source has to be instantiated. + ValidateFromSource(span); + + TypedArrayTypeCode type = (TypedArrayTypeCode)Type.GetTypeCode(typeof(U)); + // Special case for Uint8ClampedArray, a clamped array which represents an array of 8-bit unsigned integers clamped to 0-255; + if (type == TypedArrayTypeCode.Uint8Array && typeof(T) == typeof(Uint8ClampedArray)) + type = TypedArrayTypeCode.Uint8ClampedArray; // This is only passed to the JavaScript side so it knows it will be a Uint8ClampedArray + + var bytes = MemoryMarshal.AsBytes(span); + fixed (byte* ptr = bytes) + { + var res = Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Marshal.SizeOf(), (int)type, out int exception); + if (exception != 0) + throw new JSException((string)res); + return (T)res; + } + + } + + public unsafe int CopyTo(Span span) + { + var bytes = MemoryMarshal.AsBytes(span); + fixed (byte* ptr = bytes) + { + var res = Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res / Marshal.SizeOf(); + } + } + + private unsafe int CopyFrom(void* ptrSource, int offset, int count) + { + var res = Runtime.TypedArrayCopyFrom(JSHandle, (int)ptrSource, offset, offset + count, Marshal.SizeOf(), out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res / Marshal.SizeOf(); + + } + + public unsafe int CopyFrom(ReadOnlySpan span) + { + ValidateSource(span); + var bytes = MemoryMarshal.AsBytes(span); + fixed (byte* ptr = bytes) + { + var res = Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res / Marshal.SizeOf(); + } + } + + protected void ValidateTarget(Span target) + { + // target array has to be instantiated. + if (target == null || target.Length == 0) + { + throw new System.ArgumentException($"Invalid argument: {nameof(target)} can not be null and must have a length"); + } + + } + + protected void ValidateSource(ReadOnlySpan source) + { + // target has to be instantiated. + if (source == null || source.Length == 0) + { + throw new System.ArgumentException($"Invalid argument: {nameof(source)} can not be null and must have a length"); + } + + } + + protected static void ValidateFromSource(ReadOnlySpan source) + { + // target array has to be instantiated. + if (source == null) + { + throw new System.ArgumentException($"Invalid argument: {nameof(source)} can not be null."); + } + + } + + + protected static void ValidateFromSource(U[] source, int offset, int count) + { + // target array has to be instantiated. + if (source == null) + { + throw new System.ArgumentException($"Invalid argument: {nameof(source)} can not be null."); + } + + // offset can not be past the end of the array + if (offset > source.Length) + { + throw new System.ArgumentException($"Invalid argument: {nameof(offset)} can not be greater than length of '{nameof(source)}'"); + } + // offset plus count can not pass the end of the array. + if (offset + count > source.Length) + { + throw new System.ArgumentException($"Invalid argument: {nameof(offset)} plus {nameof(count)} can not be greater than length of '{nameof(source)}'"); + } + + } + + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs new file mode 100644 index 00000000000000..bc9634a76d327a --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs @@ -0,0 +1,43 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + + public sealed class Uint8Array : TypedArray + { + + public Uint8Array() + { } + + public Uint8Array(int length) : base(length) + { } + + + public Uint8Array(ArrayBuffer buffer) : base(buffer) + { } + + public Uint8Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + public Uint8Array(SharedArrayBuffer buffer) : base(buffer) + { } + + public Uint8Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + internal Uint8Array(IntPtr js_handle) : base(js_handle) + { } + + public static implicit operator Span(Uint8Array typedarray) => typedarray.ToArray(); + + public static implicit operator Uint8Array(Span span) => From(span); + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs new file mode 100644 index 00000000000000..54175c647546e0 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs @@ -0,0 +1,42 @@ +using System; +internal static partial class Interop +{ + internal static partial class JavaScript + { + + public sealed class Uint8ClampedArray : TypedArray + { + public Uint8ClampedArray() + { } + + public Uint8ClampedArray(int length) : base(length) + { } + + + public Uint8ClampedArray(ArrayBuffer buffer) : base(buffer) + { } + + public Uint8ClampedArray(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8ClampedArray(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + public Uint8ClampedArray(SharedArrayBuffer buffer) : base(buffer) + { } + + public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + internal Uint8ClampedArray(IntPtr js_handle) : base(js_handle) + { } + + public static implicit operator Span(Uint8ClampedArray typedarray) => typedarray.ToArray(); + + public static implicit operator Uint8ClampedArray(Span span) => From(span); + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs new file mode 100644 index 00000000000000..e193c254ca8f39 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; +internal static partial class Interop +{ + + internal static partial class Runtime + { + public class AnyRef + { + + public int JSHandle { get; internal set; } + internal GCHandle Handle; + + internal AnyRef(int js_handle) + { + //Console.WriteLine ($"AnyRef: {js_handle}"); + this.JSHandle = js_handle; + this.Handle = GCHandle.Alloc(this); + } + + internal AnyRef(IntPtr js_handle) + { + this.JSHandle = (int)js_handle; + this.Handle = GCHandle.Alloc(this); + } + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs new file mode 100644 index 00000000000000..677617dd821d78 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -0,0 +1,625 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using JSObject = Interop.JavaScript.JSObject; +using JSException = Interop.JavaScript.JSException; + +internal static partial class Interop +{ + internal static partial class Runtime + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern string InvokeJS(string str, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object CompileFunction(string str, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object InvokeJSWithArgs(int js_obj_handle, string method, object?[] _params, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object GetObjectProperty(int js_obj_handle, string propertyName, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object SetObjectProperty(int js_obj_handle, string propertyName, object value, bool createIfNotExists, bool hasOwnProperty, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object GetByIndex(int js_obj_handle, int index, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object SetByIndex(int js_obj_handle, int index, object? value, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object GetGlobalObject(string? globalName, out int exceptional_result); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object ReleaseHandle(int js_obj_handle, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object ReleaseObject(int js_obj_handle, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object NewObjectJS(int js_obj_handle, object[] _params, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object BindCoreObject(int js_obj_handle, int gc_handle, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object BindHostObject(int js_obj_handle, int gc_handle, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object New(string className, object[] _params, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object TypedArrayToArray(int js_obj_handle, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object TypedArrayCopyTo(int js_obj_handle, int array_ptr, int begin, int end, int bytes_per_element, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object TypedArrayFrom(int array_ptr, int begin, int end, int bytes_per_element, int type, out int exceptional_result); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern object TypedArrayCopyFrom(int js_obj_handle, int array_ptr, int begin, int end, int bytes_per_element, out int exceptional_result); + + // / + // / Execute the provided string in the JavaScript context + // / + // / The js. + // / String. + public static string InvokeJS(string str) + { + var res = InvokeJS(str, out int exception); + if (exception != 0) + throw new JSException(res); + return res; + } + + public static Interop.JavaScript.Function? CompileFunction(string snippet) + { + var res = CompileFunction(snippet, out int exception); + if (exception != 0) + throw new JSException((string)res); + return res as Interop.JavaScript.Function; + } + + private static Dictionary bound_objects = new Dictionary(); + private static Dictionary raw_to_js = new Dictionary(); + + static Runtime() + { } + + public static int New(params object[] _params) + { + var res = New(typeof(T).Name, _params, out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res; + } + + public static int New(string hostClassName, params object[] _params) + { + var res = New(hostClassName, _params, out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res; + } + + public static JSObject? NewJSObject(JSObject? js_func_ptr = null, params object[] _params) + { + var res = NewObjectJS(js_func_ptr?.JSHandle ?? 0, _params, out int exception); + if (exception != 0) + throw new JSException((string)res); + return res as JSObject; + } + + private static int BindJSObject(int js_id, Type mappedType) + { + if (!bound_objects.TryGetValue(js_id, out JSObject? obj)) + { + if (mappedType != null) + { + return BindJSType(js_id, mappedType); + } + else + { + bound_objects[js_id] = obj = new JSObject((IntPtr)js_id); + } + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + private static int BindCoreCLRObject(int js_id, int gcHandle) + { + //Console.WriteLine ($"Registering CLR Object {js_id} with handle {gcHandle}"); + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? obj = h.Target as JSObject; + + if (bound_objects.TryGetValue(js_id, out var existingObj)) + { + if (existingObj?.Handle != h && h.IsAllocated) + throw new JSException($"Multiple handles pointing at js_id: {js_id}"); + + obj = existingObj; + } + else + bound_objects[js_id] = obj; + + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + private static int BindJSType(int js_id, Type mappedType) + { + if (!bound_objects.TryGetValue(js_id, out JSObject? obj)) + { + var jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, + null, new Type[] { typeof(IntPtr) }, null); + bound_objects[js_id] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)js_id }); + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + private static int UnBindJSObject(int js_id) + { + if (bound_objects.TryGetValue(js_id, out var obj)) + { + bound_objects.Remove(js_id); + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + return 0; + } + + private static void UnBindJSObjectAndFree(int js_id) + { + if (bound_objects.TryGetValue(js_id, out var obj)) + { + if (bound_objects[js_id] != null) + { + //bound_objects[js_id].RawObject = null; + bound_objects.Remove(js_id); + } + if (obj != null) + { + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + obj.Handle.Free(); + } + } + + } + + + private static void UnBindRawJSObjectAndFree(int gcHandle) + { + + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? obj = h.Target as JSObject; + if (obj != null && obj.RawObject != null) + { + raw_to_js.Remove(obj.RawObject); + + int exception; + ReleaseHandle(obj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' mono '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); + + // Calling Release Handle above only removes the reference from the JavaScript side but does not + // release the bridged JSObject associated with the raw object so we have to do that ourselves. + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + + obj.Handle.Free(); + } + + } + + public static void FreeObject(object obj) + { + if (raw_to_js.TryGetValue(obj, out JSObject? jsobj)) + { + //raw_to_js [obj].RawObject = null; + raw_to_js.Remove(obj); + if (jsobj != null) + { + int exception; + Runtime.ReleaseObject(jsobj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing object on (raw-obj)"); + + jsobj.JSHandle = -1; + jsobj.RawObject = null; + jsobj.IsDisposed = true; + jsobj.Handle.Free(); + } + + } + else + { + throw new JSException($"Error releasing object on (obj)"); + } + } + + private static object CreateTaskSource(int js_id) + { + return new TaskCompletionSource(); + } + + private static void SetTaskSourceResult(TaskCompletionSource tcs, object result) + { + tcs.SetResult(result); + } + + private static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) + { + tcs.SetException(new JSException(reason)); + } + + private static int GetTaskAndBind(TaskCompletionSource tcs, int js_id) + { + return BindExistingObject(tcs.Task, js_id); + } + + private static int BindExistingObject(object raw_obj, int js_id) + { + JSObject? obj = raw_obj as JSObject; + + if (obj == null && !raw_to_js.TryGetValue(raw_obj, out obj)) + raw_to_js[raw_obj] = obj = new JSObject(js_id, raw_obj); + + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + private static int GetJSObjectId(object raw_obj) + { + JSObject? obj = raw_obj as JSObject; + + if (obj == null && !raw_to_js.TryGetValue(raw_obj, out obj)) + return -1; + + return obj != null ? obj.JSHandle : -1; + } + + private static object? GetMonoObject(int gc_handle) + { + GCHandle h = (GCHandle)(IntPtr)gc_handle; + JSObject? o = h.Target as JSObject; + if (o != null && o.RawObject != null) + return o.RawObject; + return o; + } + + private static object BoxInt(int i) + { + return i; + } + private static object BoxDouble(double d) + { + return d; + } + + private static object BoxBool(int b) + { + return b == 0 ? false : true; + } + + private static bool IsSimpleArray(object a) + { + if (a is Array arr) + { + if (arr.Rank == 1 && arr.GetLowerBound(0) == 0) + return true; + } + return false; + + } + + private static object? GetCoreType(string coreObj) + { + Assembly asm = typeof(Runtime).Assembly; + Type? type = asm.GetType(coreObj); + return type; + + } + + [StructLayout(LayoutKind.Explicit)] + internal struct IntPtrAndHandle + { + [FieldOffset(0)] + internal IntPtr ptr; + + [FieldOffset(0)] + internal RuntimeMethodHandle handle; + } + + //FIXME this probably won't handle generics + private static string GetCallSignature(IntPtr method_handle) + { + IntPtrAndHandle tmp = default(IntPtrAndHandle); + tmp.ptr = method_handle; + + var mb = MethodBase.GetMethodFromHandle(tmp.handle); + + string res = ""; + var parms = mb?.GetParameters(); + if (parms == null) + return res; + foreach (var p in parms) + { + var t = p.ParameterType; + + switch (Type.GetTypeCode(t)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Boolean: + // Enums types have the same code as their underlying numeric types + if (t.IsEnum) + res += "j"; + else + res += "i"; + break; + case TypeCode.Int64: + case TypeCode.UInt64: + // Enums types have the same code as their underlying numeric types + if (t.IsEnum) + res += "k"; + else + res += "l"; + break; + case TypeCode.Single: + res += "f"; + break; + case TypeCode.Double: + res += "d"; + break; + case TypeCode.String: + res += "s"; + break; + default: + if (t == typeof(IntPtr)) + { + res += "i"; + } + else if (t == typeof(Uri)) + { + res += "u"; + } + else + { + if (t.IsValueType) + throw new Exception("Can't handle VT arguments"); + res += "o"; + } + break; + } + } + + return res; + } + private static void SetupJSContinuation(Task task, JSObject cont_obj) + { + if (task.IsCompleted) + Complete(); + else + task.GetAwaiter().OnCompleted(Complete); + + void Complete() + { + try + { + if (task.Exception == null) + { + var resultProperty = task.GetType().GetProperty("Result"); + + if (resultProperty == null) + cont_obj.Invoke("resolve", Array.Empty()); + else + cont_obj.Invoke("resolve", resultProperty.GetValue(task) ?? Array.Empty()); + } + else + { + cont_obj.Invoke("reject", task.Exception.ToString()); + } + } + catch (Exception e) + { + cont_obj.Invoke("reject", e.ToString()); + } + finally + { + cont_obj.Dispose(); + FreeObject(task); + } + } + } + + public static object GetGlobalObject(string? str = null) + { + int exception; + var globalHandle = Runtime.GetGlobalObject(str, out exception); + + if (exception != 0) + throw new JSException($"Error obtaining a handle to global {str}"); + + return globalHandle; + } + + private static string ObjectToString(object o) + { + + //if (o == null) + // return null; + //if (o is Enum) + // return EnumToExportContract((Enum)o).ToString(); + + return o.ToString() ?? string.Empty; + } + + private static double GetDateValue(object dtv) + { + if (dtv == null) + throw new ArgumentNullException(nameof(dtv), "Value can not be null"); + if (!(dtv is DateTime)) + { + throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); + } + var dt = (DateTime)dtv; + if (dt.Kind == DateTimeKind.Local) + dt = dt.ToUniversalTime(); + else if (dt.Kind == DateTimeKind.Unspecified) + dt = new DateTime(dt.Ticks, DateTimeKind.Utc); + return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); + } + + private static DateTime CreateDateTime(double ticks) + { + var unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); + return unixTime.DateTime; + } + private static Uri CreateUri(string uri) + { + return new Uri(uri); + } + + // This is simple right now and will include FlagsAttribute later. + // public static Enum EnumFromExportContract(Type enumType, object value) + // { + + // if (!enumType.IsEnum) + // { + // throw new ArgumentException("Type provided must be an Enum.", nameof(enumType)); + // } + + // if (value is string) + // { + + // var fields = enumType.GetFields(); + // foreach (var fi in fields) + // { + // // Do not process special names + // if (fi.IsSpecialName) + // continue; + + // Interop.Runtime.ExportAttribute[] attributes = + // (Interop.Runtime.ExportAttribute[])fi.GetCustomAttributes(typeof(Interop.Runtime.ExportAttribute), false); + + // var enumConversionType = ConvertEnum.Default; + + // object contractName = null; + + // if (attributes != null && attributes.Length > 0) + // { + // enumConversionType = attributes[0].EnumValue; + // if (enumConversionType != ConvertEnum.Numeric) + // contractName = attributes[0].ContractName; + + // } + + // if (contractName == null) + // contractName = fi.Name; + + // switch (enumConversionType) + // { + // case ConvertEnum.ToLower: + // contractName = contractName.ToString().ToLower(); + // break; + // case ConvertEnum.ToUpper: + // contractName = contractName.ToString().ToUpper(); + // break; + // case ConvertEnum.Numeric: + // contractName = (int)Enum.Parse(value.GetType(), contractName.ToString()); + // break; + // default: + // contractName = contractName.ToString(); + // break; + // } + + // if (contractName.ToString() == value.ToString()) + // { + // return (Enum)Enum.Parse(enumType, fi.Name); + // } + + // } + + // throw new ArgumentException($"Value is a name, but not one of the named constants defined for the enum of type: {enumType}.", nameof(value)); + // } + // else + // { + // return (Enum)Enum.ToObject(enumType, value); + // } + + // } + + // // This is simple right now and will include FlagsAttribute later. + // public static object EnumToExportContract(Enum value) + // { + + // FieldInfo fi = value.GetType().GetField(value.ToString()); + + // ExportAttribute[] attributes = + // (ExportAttribute[])fi.GetCustomAttributes(typeof(ExportAttribute), false); + + // var enumConversionType = ConvertEnum.Default; + + // object contractName = null; + + // if (attributes != null && attributes.Length > 0) + // { + // enumConversionType = attributes[0].EnumValue; + // if (enumConversionType != ConvertEnum.Numeric) + // contractName = attributes[0].ContractName; + + // } + + // if (contractName == null) + // contractName = value; + + // switch (enumConversionType) + // { + // case ConvertEnum.ToLower: + // contractName = contractName.ToString().ToLower(); + // break; + // case ConvertEnum.ToUpper: + // contractName = contractName.ToString().ToUpper(); + // break; + // case ConvertEnum.Numeric: + // contractName = (int)Enum.Parse(value.GetType(), contractName.ToString()); + // break; + // default: + // contractName = contractName.ToString(); + // break; + // } + + // return contractName; + // } + + // + // Can be called by the app to stop profiling + // + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static void StopProfile() + { + } + + // Called by the AOT profiler to save profile data into Module.aot_profile_data + internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) + { + var arr = new byte[len]; + fixed (void* p = &buf) + { + var span = new ReadOnlySpan(p, len); + + // Send it to JS + var js_dump = (JSObject)Runtime.GetGlobalObject("Module"); + //js_dump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); + } + } + + // Called by the coverage profiler to save profile data into Module.coverage_profile_data + internal static void DumpCoverageProfileData(string data, string s) + { + // Send it to JS + var js_dump = (JSObject)Runtime.GetGlobalObject("Module"); + js_dump.SetObjectProperty("coverage_profile_data", data); + } + } +} From 2ca93d86de369b256b67b66d94738a64d0d13a53 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 12:26:38 +0200 Subject: [PATCH 05/99] Remove unused reference --- src/libraries/System.Net.Http/src/System.Net.Http.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 277f670d0c55e1..0677b91637da09 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -491,9 +491,6 @@ Common\Interop\Browser\Interop.Runtime.AnyRef.cs - - Common\Interop\Browser\Interop.HttpHandlerService.cs - Common\Interop\Browser\Interop.HttpHandlerService.cs From ab0e0dec64c9b34c4cb9835e1bb009c801667afe Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 12:27:53 +0200 Subject: [PATCH 06/99] Add Http handler bindings implementation - This still needs to have the code implement nullable --- .../Browser/Interop.HttpHandlerService.cs | 542 ++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs new file mode 100644 index 00000000000000..d7dbe17cda876c --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -0,0 +1,542 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#nullable disable +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +using JSObject = Interop.JavaScript.JSObject; +using JSException = Interop.JavaScript.JSException; +using HostObject = Interop.JavaScript.HostObject; +using Uint8Array = Interop.JavaScript.Uint8Array; +using Function = Interop.JavaScript.Function; + +internal static partial class Interop +{ + internal static partial class Browser + { + internal interface IHttpHandlerService + { + Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); + void Dispose(); + } + + public class BrowserHttpHandlerService : IHttpHandlerService, IDisposable + { + + private static JSObject fetch; + private static JSObject window; + + /// + /// Gets whether the current Browser supports streaming responses + /// + private static bool StreamingSupported { get; } + + public BrowserHttpHandlerService() + { } + + static BrowserHttpHandlerService() + { + using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) + StreamingSupported = (bool)streamingSupported.Call(); + handlerInit(); + } + + private static void handlerInit() + { + window = (JSObject)Interop.Runtime.GetGlobalObject("window"); + fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); + } + + public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + // There is a race condition on Safari as a result of using TaskCompletionSource that + // causes a stack exceeded error being thrown. More information can be found here: + // https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/ + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + using (cancellationToken.Register(() => tcs.TrySetCanceled())) + { +#pragma warning disable 4014 + doFetch(tcs, request, cancellationToken).ConfigureAwait(false); +#pragma warning restore 4014 + + return await tcs.Task.ConfigureAwait(true); + } + } + + private async Task doFetch(TaskCompletionSource tcs, HttpRequestMessage request, CancellationToken cancellationToken) + { + try + { + var requestObject = new JSObject(); + + if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) && + fetchOoptionsValue is IDictionary fetchOptions) + { + foreach (var item in fetchOptions) + { + requestObject.SetObjectProperty(item.Key, item.Value); + } + } + + requestObject.SetObjectProperty("method", request.Method.Method); + + // We need to check for body content + if (request.Content != null) + { + if (request.Content is StringContent) + { + requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(true)); + } + else + { + // 2.1.801 seems to have a problem with the line + // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) + // so we split it up into two lines. + var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); + using (var uint8Buffer = Uint8Array.From(byteAsync)) + { + requestObject.SetObjectProperty("body", uint8Buffer); + } + } + } + + // Process headers + // Cors has it's own restrictions on headers. + // https://developer.mozilla.org/en-US/docs/Web/API/Headers + using (var jsHeaders = new HostObject("Headers")) + { + if (request.Headers != null) + { + foreach (var header in request.Headers) + { + foreach (var value in header.Value) + { + jsHeaders.Invoke("append", header.Key, value); + } + } + } + if (request.Content?.Headers != null) + { + foreach (var header in request.Content.Headers) + { + foreach (var value in header.Value) + { + jsHeaders.Invoke("append", header.Key, value); + } + } + } + requestObject.SetObjectProperty("headers", jsHeaders); + } + + WasmHttpReadStream wasmHttpReadStream = null; + + JSObject abortController = new HostObject("AbortController"); + JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); + requestObject.SetObjectProperty("signal", signal); + signal.Dispose(); + + CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => + { + if (abortController.JSHandle != -1) + { + abortController.Invoke("abort"); + abortController?.Dispose(); + } + wasmHttpReadStream?.Dispose(); + })); + + var args = new Interop.JavaScript.Array(); + args.Push(request.RequestUri.ToString()); + args.Push(requestObject); + + requestObject.Dispose(); + + var response = fetch.Invoke("apply", window, args) as Task; + args.Dispose(); + if (response == null) + throw new Exception("Internal error marshalling the response Promise from `fetch`."); + + var t = await response.ConfigureAwait(true); + + var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); + + //Console.WriteLine($"bodyUsed: {status.IsBodyUsed}"); + //Console.WriteLine($"ok: {status.IsOK}"); + //Console.WriteLine($"redirected: {status.IsRedirected}"); + //Console.WriteLine($"status: {status.Status}"); + //Console.WriteLine($"statusText: {status.StatusText}"); + //Console.WriteLine($"type: {status.ResponseType}"); + //Console.WriteLine($"url: {status.Url}"); + + HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); + + var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out var streamingEnabledValue) && (bool)streamingEnabledValue; + + httpresponse.Content = StreamingSupported && streamingEnabled + ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) + : (HttpContent)new BrowserHttpContent(status); + + // Fill the response headers + // CORS will only allow access to certain headers. + // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. + // cors and basic responses are almost identical except that a cors response restricts the headers you can view to + // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. + // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types + // + // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation + using (var respHeaders = (JSObject)status.Headers) + { + if (respHeaders != null) + { + using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) + { + JSObject nextResult = null; + try + { + nextResult = (JSObject)entriesIterator.Invoke("next"); + while (!(bool)nextResult.GetObjectProperty("done")) + { + using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) + { + var name = (string)resultValue[0]; + var value = (string)resultValue[1]; + if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) + if (httpresponse.Content != null) + // if (!httpresponse.Content.Headers.TryAddWithoutValidation(name, value)) + // Console.WriteLine($"Warning: Can not add response header for name: {name} value: {value}"); + httpresponse.Content.Headers.TryAddWithoutValidation(name, value); + } + nextResult?.Dispose(); + nextResult = (JSObject)entriesIterator.Invoke("next"); + } + } + finally + { + nextResult?.Dispose(); + } + } + } + } + + tcs.SetResult(httpresponse); + } + catch (JSException jsExc) + { + var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message); + tcs.SetException(httpExc); + } + catch (Exception exception) + { + tcs.SetException(exception); + } + } + + private class WasmFetchResponse : IDisposable + { + private JSObject fetchResponse; + private JSObject abortController; + private readonly CancellationTokenSource abortCts; + private readonly CancellationTokenRegistration abortRegistration; + + public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenSource abortCts, CancellationTokenRegistration abortRegistration) + { + this.fetchResponse = fetchResponse; + this.abortController = abortController; + this.abortCts = abortCts; + this.abortRegistration = abortRegistration; + } + + public bool IsOK => (bool)fetchResponse.GetObjectProperty("ok"); + public bool IsRedirected => (bool)fetchResponse.GetObjectProperty("redirected"); + public int Status => (int)fetchResponse.GetObjectProperty("status"); + public string StatusText => (string)fetchResponse.GetObjectProperty("statusText"); + public string ResponseType => (string)fetchResponse.GetObjectProperty("type"); + public string Url => (string)fetchResponse.GetObjectProperty("url"); + //public bool IsUseFinalURL => (bool)managedJSObject.GetObjectProperty("useFinalUrl"); + public bool IsBodyUsed => (bool)fetchResponse.GetObjectProperty("bodyUsed"); + public JSObject Headers => (JSObject)fetchResponse.GetObjectProperty("headers"); + public JSObject Body => (JSObject)fetchResponse.GetObjectProperty("body"); + + public Task ArrayBuffer() => (Task)fetchResponse.Invoke("arrayBuffer"); + public Task Text() => (Task)fetchResponse.Invoke("text"); + public Task JSON() => (Task)fetchResponse.Invoke("json"); + + public void Dispose() + { + // Dispose of unmanaged resources. + Dispose(true); + // Suppress finalization. + GC.SuppressFinalize(this); + } + + // Protected implementation of Dispose pattern. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // Free any other managed objects here. + // + abortCts.Cancel(); + + abortRegistration.Dispose(); + } + + // Free any unmanaged objects here. + // + fetchResponse?.Dispose(); + fetchResponse = null; + + abortController?.Dispose(); + abortController = null; + } + + } + + private class BrowserHttpContent : HttpContent + { + private byte[] _data; + private WasmFetchResponse _status; + + public BrowserHttpContent(WasmFetchResponse status) + { + _status = status; + } + + private async Task GetResponseData() + { + if (_data != null) + { + return _data; + } + + using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(true)) + { + using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) + { + _data = dataBinView.ToArray(); + _status.Dispose(); + _status = null; + + } + } + + return _data; + } + + protected override async Task CreateContentReadStreamAsync() + { + var data = await GetResponseData().ConfigureAwait(true); + return new MemoryStream(data, writable: false); + } + + protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) + { + var data = await GetResponseData().ConfigureAwait(true); + await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); + } + + protected internal override bool TryComputeLength(out long length) + { + if (_data != null) + { + length = _data.Length; + return true; + } + + length = 0; + return false; + } + + protected override void Dispose(bool disposing) + { + _status?.Dispose(); + base.Dispose(disposing); + } + } + + private class WasmHttpReadStream : Stream + { + private WasmFetchResponse _status; + private JSObject _reader; + + private byte[] _bufferedBytes; + private int _position; + + public WasmHttpReadStream(WasmFetchResponse status) + { + _status = status; + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if (count < 0 || buffer.Length - offset < count) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_reader == null) + { + // If we've read everything, then _reader and _status will be null + if (_status == null) + { + return 0; + } + + try + { + using (var body = _status.Body) + { + _reader = (JSObject)body.Invoke("getReader"); + } + } + catch (JSException) + { + cancellationToken.ThrowIfCancellationRequested(); + throw; + } + } + + if (_bufferedBytes != null && _position < _bufferedBytes.Length) + { + return ReadBuffered(); + } + + try + { + var t = (Task)_reader.Invoke("read"); + using (var read = (JSObject)await t.ConfigureAwait(true)) + { + if ((bool)read.GetObjectProperty("done")) + { + _reader.Dispose(); + _reader = null; + + _status.Dispose(); + _status = null; + return 0; + } + + _position = 0; + // value for fetch streams is a Uint8Array + using (Uint8Array binValue = (Uint8Array)read.GetObjectProperty("value")) + _bufferedBytes = binValue.ToArray(); + } + } + catch (JSException) + { + cancellationToken.ThrowIfCancellationRequested(); + throw; + } + + return ReadBuffered(); + + int ReadBuffered() + { + int n = _bufferedBytes.Length - _position; + if (n > count) + n = count; + if (n <= 0) + return 0; + + Buffer.BlockCopy(_bufferedBytes, _position, buffer, offset, n); + _position += n; + + return n; + } + } + + protected override void Dispose(bool disposing) + { + _reader?.Dispose(); + _status?.Dispose(); + } + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new PlatformNotSupportedException("Synchronous reads are not supported, use ReadAsync instead"); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects). + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~WebAssemblyHttpHandlerService() + // { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion + } + } +} From bc395637ad4ababa06627ae6328da71405d4e206 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 14:36:04 +0200 Subject: [PATCH 07/99] Nullable support --- .../Browser/Interop.HttpHandlerService.cs | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs index d7dbe17cda876c..6532e28503edc8 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable using System; using System.Collections.Generic; using System.Net; @@ -30,8 +29,8 @@ internal interface IHttpHandlerService public class BrowserHttpHandlerService : IHttpHandlerService, IDisposable { - private static JSObject fetch; - private static JSObject window; + private static JSObject? fetch; + private static JSObject? window; /// /// Gets whether the current Browser supports streaming responses @@ -72,6 +71,11 @@ public async Task SendAsync(HttpRequestMessage request, Can private async Task doFetch(TaskCompletionSource tcs, HttpRequestMessage request, CancellationToken cancellationToken) { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + try { var requestObject = new JSObject(); @@ -135,7 +139,7 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe requestObject.SetObjectProperty("headers", jsHeaders); } - WasmHttpReadStream wasmHttpReadStream = null; + WasmHttpReadStream? wasmHttpReadStream = null; JSObject abortController = new HostObject("AbortController"); JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); @@ -154,12 +158,14 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe })); var args = new Interop.JavaScript.Array(); - args.Push(request.RequestUri.ToString()); - args.Push(requestObject); + if (request.RequestUri != null) { + args.Push(request.RequestUri.ToString()); + args.Push(requestObject); + } requestObject.Dispose(); - var response = fetch.Invoke("apply", window, args) as Task; + var response = fetch?.Invoke("apply", window, args) as Task; args.Dispose(); if (response == null) throw new Exception("Internal error marshalling the response Promise from `fetch`."); @@ -168,17 +174,9 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); - //Console.WriteLine($"bodyUsed: {status.IsBodyUsed}"); - //Console.WriteLine($"ok: {status.IsOK}"); - //Console.WriteLine($"redirected: {status.IsRedirected}"); - //Console.WriteLine($"status: {status.Status}"); - //Console.WriteLine($"statusText: {status.StatusText}"); - //Console.WriteLine($"type: {status.ResponseType}"); - //Console.WriteLine($"url: {status.Url}"); - HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); - var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out var streamingEnabledValue) && (bool)streamingEnabledValue; + var streamingEnabled = request.Properties.TryGetValue ("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); httpresponse.Content = StreamingSupported && streamingEnabled ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) @@ -198,7 +196,7 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe { using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) { - JSObject nextResult = null; + JSObject? nextResult = null; try { nextResult = (JSObject)entriesIterator.Invoke("next"); @@ -248,8 +246,8 @@ private class WasmFetchResponse : IDisposable public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenSource abortCts, CancellationTokenRegistration abortRegistration) { - this.fetchResponse = fetchResponse; - this.abortController = abortController; + this.fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse), $"{nameof(fetchResponse)} cannot be null"); + this.abortController = abortController ?? throw new ArgumentNullException(nameof(abortController), $"{nameof(abortController)} cannot be null"); ; this.abortCts = abortCts; this.abortRegistration = abortRegistration; } @@ -292,22 +290,19 @@ protected virtual void Dispose(bool disposing) // Free any unmanaged objects here. // fetchResponse?.Dispose(); - fetchResponse = null; - abortController?.Dispose(); - abortController = null; } } private class BrowserHttpContent : HttpContent { - private byte[] _data; + private byte[]? _data; private WasmFetchResponse _status; public BrowserHttpContent(WasmFetchResponse status) { - _status = status; + _status = status ?? throw new ArgumentNullException(nameof(status), $"{nameof(status)} cannot be null"); } private async Task GetResponseData() @@ -323,8 +318,6 @@ private async Task GetResponseData() { _data = dataBinView.ToArray(); _status.Dispose(); - _status = null; - } } @@ -337,7 +330,7 @@ protected override async Task CreateContentReadStreamAsync() return new MemoryStream(data, writable: false); } - protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) + protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) { var data = await GetResponseData().ConfigureAwait(true); await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); @@ -364,10 +357,10 @@ protected override void Dispose(bool disposing) private class WasmHttpReadStream : Stream { - private WasmFetchResponse _status; - private JSObject _reader; + private WasmFetchResponse? _status; + private JSObject? _reader; - private byte[] _bufferedBytes; + private byte[]? _bufferedBytes; private int _position; public WasmHttpReadStream(WasmFetchResponse status) @@ -437,7 +430,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, _reader.Dispose(); _reader = null; - _status.Dispose(); + _status?.Dispose(); _status = null; return 0; } From 92f74c4e031a0b2478261aa9992fe7af7ed07556 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 15:07:45 +0200 Subject: [PATCH 08/99] Add browser files back after upstream merge conflict --- .../src/System.Net.Http.csproj | 228 ++++++++++++++---- 1 file changed, 177 insertions(+), 51 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index dc856e97b140eb..1156e46c4b222e 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -115,7 +115,7 @@ - + @@ -167,56 +167,6 @@ Link="Common\System\Net\NTAuthentication.Common.cs" /> - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Common\System\Net\Http\aspnetcore\IHttpHeadersHandler.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\DynamicTable.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\HeaderField.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackDecoder.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackDecodingException.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackEncoder.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\HPackEncodingException.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\Huffman.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\HuffmanDecodingException.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\IntegerDecoder.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\IntegerEncoder.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\H2StaticTable.cs + + + Common\System\Net\Http\aspnetcore\Http2\Hpack\StatusCodes.cs + + + Common\System\Net\Http\aspnetcore\Http3\Http3SettingType.cs + + + Common\System\Net\Http\aspnetcore\Http3\Http3StreamType.cs + + + Common\System\Net\Http\aspnetcore\Http3\Helpers\VariableLengthIntegerHelper.cs + + + Common\System\Net\Http\aspnetcore\Http3\Frames\Http3ErrorCode.cs + + + Common\System\Net\Http\aspnetcore\Http3\Frames\Http3Frame.cs + + + Common\System\Net\Http\aspnetcore\Http3\Frames\Http3FrameType.cs + + + Common\System\Net\Http\aspnetcore\Http3\QPack\HeaderField.cs + + + Common\System\Net\Http\aspnetcore\Http3\QPack\QPackDecoder.cs + + + Common\System\Net\Http\aspnetcore\Http3\QPack\QPackDecodingException.cs + + + Common\System\Net\Http\aspnetcore\Http3\QPack\QPackEncoder.cs + + + Common\System\Net\Http\aspnetcore\Http3\QPack\QPackEncodingException.cs + + + Common\System\Net\Http\aspnetcore\Http3\QPack\H3StaticTable.cs + + + + + + + Common\System\CharArrayHelpers.cs + + + Common\System\StringExtensions.cs + + + Common\System\Net\HttpKnownHeaderNames.cs + + + Common\System\Net\HttpKnownHeaderNames.TryGetHeaderName.cs + + + Common\System\Net\HttpStatusDescription.cs + + + Common\System\Net\Http\HttpHandlerDefaults.cs + + + Common\System\Net\SecurityProtocol.cs + + + Common\System\Net\UriScheme.cs + + + Common\System\Text\ValueStringBuilder.cs + + + + Common\System\Net\Security\CertificateHelper.cs + + + Common\System\Net\Security\CertificateHelper.Unix.cs + + + Common\System\Threading\Tasks\TaskToApm.cs + + + + + + + + + + + + + + Common\Interop\Browser\Interop.Runtime.cs + + + Common\Interop\Browser\Interop.JavaScript.JSException.cs + + + Common\Interop\Browser\Interop.JavaScript.JSObject.cs + + + Common\Interop\Browser\Interop.JavaScript.Uint8Array.cs + + + Common\Interop\Browser\Interop.JavaScript.Uint8Array.cs + + + Common\Interop\Browser\Interop.JavaScript.TypedArray.cs + + + Common\Interop\Browser\Interop.JavaScript.Array.cs + + + Common\Interop\Browser\Interop.JavaScript.ArrayBuffer.cs + + + Common\Interop\Browser\Interop.JavaScript.SharedArrayBuffer.cs + + + Common\Interop\Browser\Interop.JavaScript.CoreObject.cs + + + Common\Interop\Browser\Interop.JavaScript.HostObject.cs + + + Common\Interop\Browser\Interop.JavaScript.Function.cs + + + Common\Interop\Browser\Interop.Runtime.AnyRef.cs + + + Common\Interop\Browser\Interop.HttpHandlerService.cs + + + + Common\Interop\Browser\Interop.HttpHandlerService.cs + + \ No newline at end of file From a9248de4eb4e78de5795e3fe7b4981a382c6f73d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 28 Apr 2020 18:14:49 +0200 Subject: [PATCH 09/99] Use attribute Link syntax for Common files to bring in sync with existing format --- .../src/System.Net.Http.csproj | 134 +++++++----------- 1 file changed, 52 insertions(+), 82 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 1156e46c4b222e..0640959cde49ef 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -699,43 +699,30 @@ - - Common\System\CharArrayHelpers.cs - - - Common\System\StringExtensions.cs - - - Common\System\Net\HttpKnownHeaderNames.cs - - - Common\System\Net\HttpKnownHeaderNames.TryGetHeaderName.cs - - - Common\System\Net\HttpStatusDescription.cs - - - Common\System\Net\Http\HttpHandlerDefaults.cs - - - Common\System\Net\SecurityProtocol.cs - - - Common\System\Net\UriScheme.cs - - - Common\System\Text\ValueStringBuilder.cs - - - - Common\System\Net\Security\CertificateHelper.cs - - - Common\System\Net\Security\CertificateHelper.Unix.cs - - - Common\System\Threading\Tasks\TaskToApm.cs - + + + + + + + + + + + + @@ -747,51 +734,34 @@ - - Common\Interop\Browser\Interop.Runtime.cs - - - Common\Interop\Browser\Interop.JavaScript.JSException.cs - - - Common\Interop\Browser\Interop.JavaScript.JSObject.cs - - - Common\Interop\Browser\Interop.JavaScript.Uint8Array.cs - - - Common\Interop\Browser\Interop.JavaScript.Uint8Array.cs - - - Common\Interop\Browser\Interop.JavaScript.TypedArray.cs - - - Common\Interop\Browser\Interop.JavaScript.Array.cs - - - Common\Interop\Browser\Interop.JavaScript.ArrayBuffer.cs - - - Common\Interop\Browser\Interop.JavaScript.SharedArrayBuffer.cs - - - Common\Interop\Browser\Interop.JavaScript.CoreObject.cs - - - Common\Interop\Browser\Interop.JavaScript.HostObject.cs - - - Common\Interop\Browser\Interop.JavaScript.Function.cs - - - Common\Interop\Browser\Interop.Runtime.AnyRef.cs - - - Common\Interop\Browser\Interop.HttpHandlerService.cs - + + + + + + + + + + + + + - - Common\Interop\Browser\Interop.HttpHandlerService.cs - + \ No newline at end of file From dc82e45a70bae3dc76f45da1648feb72244023f2 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 06:04:55 +0200 Subject: [PATCH 10/99] Address Missing license header --- .../Common/src/Interop/Browser/Interop.JavaScript.Array.cs | 3 +++ .../src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs | 3 +++ .../src/Interop/Browser/Interop.JavaScript.CoreObject.cs | 3 +++ .../src/Interop/Browser/Interop.JavaScript.Function.cs | 1 - .../src/Interop/Browser/Interop.JavaScript.HostObject.cs | 3 +++ .../src/Interop/Browser/Interop.JavaScript.JSException.cs | 5 ++++- .../Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs | 3 +++ .../src/Interop/Browser/Interop.JavaScript.TypedArray.cs | 3 +++ .../src/Interop/Browser/Interop.JavaScript.Uint8Array.cs | 3 +++ .../Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs | 3 +++ .../Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs | 5 ++++- src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs | 1 - 12 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs index 497a27e90b6c31..c7fb4f9022dacd 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs index 842e59acc200a7..cc8cb931e59c9e 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs index b156f041335b57..469bbfa37286a3 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs index 4d6652c6d6b4c6..36ada05edbf68f 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs index d4bd2e0f57ad85..4ee7e7f0c50774 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs index 113d77dd70476d..eb043e01a4bd0c 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs @@ -1,4 +1,7 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs index 19fc4b13854505..b274b3b6fbe89c 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs index 45f565b94e089a..57f3c662a7f5aa 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; using System.Runtime.InteropServices; diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs index bc9634a76d327a..ed0a8b6c5c2b01 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs index 54175c647546e0..5d4f99970d7f70 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs index e193c254ca8f39..0a4840503dad97 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs @@ -1,4 +1,7 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; using System.Runtime.InteropServices; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 677617dd821d78..28049eb7709aff 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - using System; using System.Collections.Generic; using System.Reflection; From cc70c9fe8033e4de7f46b58565c7ad67b22f5fba Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 06:05:29 +0200 Subject: [PATCH 11/99] Code formatting and removal of comment code. --- .../src/Interop/Browser/Interop.HttpHandlerService.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs index 6532e28503edc8..e1bde49da0d65f 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -158,7 +158,8 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe })); var args = new Interop.JavaScript.Array(); - if (request.RequestUri != null) { + if (request.RequestUri != null) + { args.Push(request.RequestUri.ToString()); args.Push(requestObject); } @@ -176,7 +177,7 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); - var streamingEnabled = request.Properties.TryGetValue ("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); + var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); httpresponse.Content = StreamingSupported && streamingEnabled ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) @@ -208,8 +209,6 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe var value = (string)resultValue[1]; if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) if (httpresponse.Content != null) - // if (!httpresponse.Content.Headers.TryAddWithoutValidation(name, value)) - // Console.WriteLine($"Warning: Can not add response header for name: {name} value: {value}"); httpresponse.Content.Headers.TryAddWithoutValidation(name, value); } nextResult?.Dispose(); From c96a6f1ff3d4a7b95e7b6680a0e3f0bc6346e8b0 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 06:58:18 +0200 Subject: [PATCH 12/99] Address PR comments. --- .../Browser/Interop.HttpHandlerService.cs | 18 +++++------------- .../src/Interop/Browser/Interop.Runtime.cs | 3 --- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs index e1bde49da0d65f..35414f268510e0 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -29,8 +29,8 @@ internal interface IHttpHandlerService public class BrowserHttpHandlerService : IHttpHandlerService, IDisposable { - private static JSObject? fetch; - private static JSObject? window; + private static readonly JSObject? fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); + private static readonly JSObject? window = (JSObject)Interop.Runtime.GetGlobalObject("window"); /// /// Gets whether the current Browser supports streaming responses @@ -44,15 +44,7 @@ static BrowserHttpHandlerService() { using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) StreamingSupported = (bool)streamingSupported.Call(); - handlerInit(); } - - private static void handlerInit() - { - window = (JSObject)Interop.Runtime.GetGlobalObject("window"); - fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); - } - public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // There is a race condition on Safari as a result of using TaskCompletionSource that @@ -245,8 +237,8 @@ private class WasmFetchResponse : IDisposable public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenSource abortCts, CancellationTokenRegistration abortRegistration) { - this.fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse), $"{nameof(fetchResponse)} cannot be null"); - this.abortController = abortController ?? throw new ArgumentNullException(nameof(abortController), $"{nameof(abortController)} cannot be null"); ; + this.fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse)); + this.abortController = abortController ?? throw new ArgumentNullException(nameof(abortController)); this.abortCts = abortCts; this.abortRegistration = abortRegistration; } @@ -301,7 +293,7 @@ private class BrowserHttpContent : HttpContent public BrowserHttpContent(WasmFetchResponse status) { - _status = status ?? throw new ArgumentNullException(nameof(status), $"{nameof(status)} cannot be null"); + _status = status ?? throw new ArgumentNullException(nameof(status)); } private async Task GetResponseData() diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 28049eb7709aff..3c7a4b6cd5db92 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -77,9 +77,6 @@ public static string InvokeJS(string str) private static Dictionary bound_objects = new Dictionary(); private static Dictionary raw_to_js = new Dictionary(); - static Runtime() - { } - public static int New(params object[] _params) { var res = New(typeof(T).Name, _params, out int exception); From bb9ff248d4b24c29a0970913e48ec7ed8909d46d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 09:54:18 +0200 Subject: [PATCH 13/99] Address commit comments --- .../Browser/Interop.HttpHandlerService.cs | 50 +++---------------- .../BrowserHttpMessageHandler.cs | 2 +- 2 files changed, 9 insertions(+), 43 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs index 35414f268510e0..eb7c439758da9a 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -20,13 +20,7 @@ internal static partial class Interop { internal static partial class Browser { - internal interface IHttpHandlerService - { - Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); - void Dispose(); - } - - public class BrowserHttpHandlerService : IHttpHandlerService, IDisposable + public class BrowserHttpHandlerService : IDisposable { private static readonly JSObject? fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); @@ -47,9 +41,6 @@ static BrowserHttpHandlerService() } public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - // There is a race condition on Safari as a result of using TaskCompletionSource that - // causes a stack exceeded error being thrown. More information can be found here: - // https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/ var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); using (cancellationToken.Register(() => tcs.TrySetCanceled())) { @@ -230,8 +221,8 @@ private async Task doFetch(TaskCompletionSource tcs, HttpRe private class WasmFetchResponse : IDisposable { - private JSObject fetchResponse; - private JSObject abortController; + private readonly JSObject fetchResponse; + private readonly JSObject abortController; private readonly CancellationTokenSource abortCts; private readonly CancellationTokenRegistration abortRegistration; @@ -262,8 +253,6 @@ public void Dispose() { // Dispose of unmanaged resources. Dispose(true); - // Suppress finalization. - GC.SuppressFinalize(this); } // Protected implementation of Dispose pattern. @@ -286,10 +275,10 @@ protected virtual void Dispose(bool disposing) } - private class BrowserHttpContent : HttpContent + private sealed class BrowserHttpContent : HttpContent { private byte[]? _data; - private WasmFetchResponse _status; + private readonly WasmFetchResponse _status; public BrowserHttpContent(WasmFetchResponse status) { @@ -346,7 +335,7 @@ protected override void Dispose(bool disposing) } } - private class WasmHttpReadStream : Stream + private sealed class WasmHttpReadStream : Stream { private WasmFetchResponse? _status; private JSObject? _reader; @@ -486,39 +475,16 @@ public override void Write(byte[] buffer, int offset, int count) } } - #region IDisposable Support - private bool disposedValue = false; // To detect redundant calls + #region IDisposable Members protected virtual void Dispose(bool disposing) { - if (!disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects). - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } + // Nothing to do in base class. } - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~WebAssemblyHttpHandlerService() - // { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. public void Dispose() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); } #endregion } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs index a91720480be0f3..e8ca6356de63c1 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs @@ -15,7 +15,7 @@ namespace System.Net.Http internal partial class BrowserHttpMessageHandler : HttpMessageHandler { // This partial implementation contains members common to Browser WebAssembly running on .NET Core. - internal Interop.Browser.IHttpHandlerService? wasmHandler; + internal Interop.Browser.BrowserHttpHandlerService? wasmHandler; public BrowserHttpMessageHandler() { From 2f7ace0fee4112e671da510fbe6ae1e2b083a380 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 09:59:39 +0200 Subject: [PATCH 14/99] Add blank line between License and first line of code. Address comments --- .../Common/src/Interop/Browser/Interop.HttpHandlerService.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.Array.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.Function.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.JSException.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs | 1 + .../src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs | 1 + .../Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs | 1 + .../src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs | 1 + .../Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs | 1 + src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs | 1 + 14 files changed, 14 insertions(+) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs index eb7c439758da9a..84a28c213f4a3f 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; using System.Collections.Generic; using System.Net; diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs index c7fb4f9022dacd..df1c585d929f40 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs index cc8cb931e59c9e..40bb22c35ddeed 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs index 469bbfa37286a3..e0bc34f6b3225b 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs index 36ada05edbf68f..4d6652c6d6b4c6 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs index 4ee7e7f0c50774..f182cf62676542 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs index eb043e01a4bd0c..b7908e96270280 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs index 45c8ba00d6d7dc..55538ce9822804 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs index b274b3b6fbe89c..aad5280012d4a3 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs index 57f3c662a7f5aa..5443c5fdf6c266 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; using System.Runtime.InteropServices; diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs index ed0a8b6c5c2b01..c05dd4039fdf1a 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs index 5d4f99970d7f70..d0fe32ae190dc7 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs index 0a4840503dad97..76c82e73e4ed64 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; using System.Runtime.InteropServices; internal static partial class Interop diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 3c7a4b6cd5db92..9c5ca9a27778d3 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System; using System.Collections.Generic; using System.Reflection; From ced64372fa599279c3d12c6ebb8ba8c4ba1825fe Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 10:26:36 +0200 Subject: [PATCH 15/99] Replace SocketsHttpHandler build for Browser. - Throws `PlatformNotSupportedException` for properties and methods of the HttpMessageHandler abstract implementation. --- .../BrowserHttpHandler/SocketsHttpHandler.cs | 101 +----------------- 1 file changed, 1 insertion(+), 100 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index 64894aee268f89..379e250aa5df5b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -12,26 +12,6 @@ namespace System.Net.Http { public sealed class SocketsHttpHandler : HttpMessageHandler { - private HttpMessageHandler? _handler; - private bool _disposed; - - private void CheckDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(nameof(SocketsHttpHandler)); - } - } - - private void CheckDisposedOrStarted() - { - CheckDisposed(); - if (_handler != null) - { - throw new InvalidOperationException(SR.net_http_operation_started); - } - } - public bool UseCookies { get => throw new PlatformNotSupportedException("Property UseCookies is not supported."); @@ -150,89 +130,10 @@ public TimeSpan Expect100ContinueTimeout public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); - protected override void Dispose(bool disposing) - { - if (disposing && !_disposed) - { - _disposed = true; - _handler?.Dispose(); - _handler = null; - } - - base.Dispose(disposing); - } - - private HttpMessageHandler SetupHandlerChain() - { - - HttpMessageHandler handler = new BrowserHttpMessageHandler(); - - // Ensure a single handler is used for all requests. - if (Interlocked.CompareExchange(ref _handler, handler, null) != null) - { - handler.Dispose(); - } - - return _handler; - } - protected internal override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { - CheckDisposed(); - HttpMessageHandler handler = _handler ?? SetupHandlerChain(); - - Exception? error = ValidateAndNormalizeRequest(request); - if (error != null) - { - return Task.FromException(error); - } - - return handler.SendAsync(request, cancellationToken); - } - - private Exception? ValidateAndNormalizeRequest(HttpRequestMessage request) - { - if (request.Version.Major == 0) - { - return new NotSupportedException(SR.net_http_unsupported_version); - } - - // Add headers to define content transfer, if not present - if (request.HasHeaders && request.Headers.TransferEncodingChunked.GetValueOrDefault()) - { - if (request.Content == null) - { - return new HttpRequestException(SR.net_http_client_execution_error, - new InvalidOperationException(SR.net_http_chunked_not_allowed_with_empty_content)); - } - - // Since the user explicitly set TransferEncodingChunked to true, we need to remove - // the Content-Length header if present, as sending both is invalid. - request.Content.Headers.ContentLength = null; - } - else if (request.Content != null && request.Content.Headers.ContentLength == null) - { - // We have content, but neither Transfer-Encoding nor Content-Length is set. - request.Headers.TransferEncodingChunked = true; - } - - if (request.Version.Minor == 0 && request.Version.Major == 1 && request.HasHeaders) - { - // HTTP 1.0 does not support chunking - if (request.Headers.TransferEncodingChunked == true) - { - return new NotSupportedException(SR.net_http_unsupported_chunking); - } - - // HTTP 1.0 does not support Expect: 100-continue; just disable it. - if (request.Headers.ExpectContinue == true) - { - request.Headers.ExpectContinue = false; - } - } - - return null; + throw new PlatformNotSupportedException("Method SendAsync is not supported."); } } } From 5c89333be5d863e352034b3415a863d365fe71fa Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 10:54:34 +0200 Subject: [PATCH 16/99] Cleanup SendAsync code when doing the call out to JavaScript Fetch. - Addresses commit comments. --- .../Browser/Interop.HttpHandlerService.cs | 244 +++++++++--------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs index 84a28c213f4a3f..3f6083ccf1d294 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs @@ -40,183 +40,183 @@ static BrowserHttpHandlerService() using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) StreamingSupported = (bool)streamingSupported.Call(); } - public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - using (cancellationToken.Register(() => tcs.TrySetCanceled())) - { -#pragma warning disable 4014 - doFetch(tcs, request, cancellationToken).ConfigureAwait(false); -#pragma warning restore 4014 - - return await tcs.Task.ConfigureAwait(true); - } + return DoFetch(request, cancellationToken); } - private async Task doFetch(TaskCompletionSource tcs, HttpRequestMessage request, CancellationToken cancellationToken) + private async Task DoFetch(HttpRequestMessage request, CancellationToken cancellationToken) { if (request == null) { throw new ArgumentNullException(nameof(request)); } - try - { - var requestObject = new JSObject(); + var tcs = new TaskCompletionSource(); - if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) && - fetchOoptionsValue is IDictionary fetchOptions) + // Wrap the cancellationToken in a using so that it can be disposed of whether + // we successfully fetched or failed trying. + using (cancellationToken.Register(() => tcs.TrySetCanceled())) + { + try { - foreach (var item in fetchOptions) + var requestObject = new JSObject(); + + if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) && + fetchOoptionsValue is IDictionary fetchOptions) { - requestObject.SetObjectProperty(item.Key, item.Value); + foreach (var item in fetchOptions) + { + requestObject.SetObjectProperty(item.Key, item.Value); + } } - } - requestObject.SetObjectProperty("method", request.Method.Method); + requestObject.SetObjectProperty("method", request.Method.Method); - // We need to check for body content - if (request.Content != null) - { - if (request.Content is StringContent) + // We need to check for body content + if (request.Content != null) { - requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(true)); - } - else - { - // 2.1.801 seems to have a problem with the line - // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) - // so we split it up into two lines. - var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); - using (var uint8Buffer = Uint8Array.From(byteAsync)) + if (request.Content is StringContent) { - requestObject.SetObjectProperty("body", uint8Buffer); + requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(true)); + } + else + { + // 2.1.801 seems to have a problem with the line + // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) + // so we split it up into two lines. + var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); + using (var uint8Buffer = Uint8Array.From(byteAsync)) + { + requestObject.SetObjectProperty("body", uint8Buffer); + } } } - } - // Process headers - // Cors has it's own restrictions on headers. - // https://developer.mozilla.org/en-US/docs/Web/API/Headers - using (var jsHeaders = new HostObject("Headers")) - { - if (request.Headers != null) + // Process headers + // Cors has it's own restrictions on headers. + // https://developer.mozilla.org/en-US/docs/Web/API/Headers + using (var jsHeaders = new HostObject("Headers")) { - foreach (var header in request.Headers) + if (request.Headers != null) { - foreach (var value in header.Value) + foreach (var header in request.Headers) { - jsHeaders.Invoke("append", header.Key, value); + foreach (var value in header.Value) + { + jsHeaders.Invoke("append", header.Key, value); + } } } - } - if (request.Content?.Headers != null) - { - foreach (var header in request.Content.Headers) + if (request.Content?.Headers != null) { - foreach (var value in header.Value) + foreach (var header in request.Content.Headers) { - jsHeaders.Invoke("append", header.Key, value); + foreach (var value in header.Value) + { + jsHeaders.Invoke("append", header.Key, value); + } } } + requestObject.SetObjectProperty("headers", jsHeaders); } - requestObject.SetObjectProperty("headers", jsHeaders); - } - WasmHttpReadStream? wasmHttpReadStream = null; + WasmHttpReadStream? wasmHttpReadStream = null; - JSObject abortController = new HostObject("AbortController"); - JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); - requestObject.SetObjectProperty("signal", signal); - signal.Dispose(); + JSObject abortController = new HostObject("AbortController"); + JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); + requestObject.SetObjectProperty("signal", signal); + signal.Dispose(); - CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => - { - if (abortController.JSHandle != -1) + CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => { - abortController.Invoke("abort"); - abortController?.Dispose(); - } - wasmHttpReadStream?.Dispose(); - })); + if (abortController.JSHandle != -1) + { + abortController.Invoke("abort"); + abortController?.Dispose(); + } + wasmHttpReadStream?.Dispose(); + })); - var args = new Interop.JavaScript.Array(); - if (request.RequestUri != null) - { - args.Push(request.RequestUri.ToString()); - args.Push(requestObject); - } + var args = new Interop.JavaScript.Array(); + if (request.RequestUri != null) + { + args.Push(request.RequestUri.ToString()); + args.Push(requestObject); + } - requestObject.Dispose(); + requestObject.Dispose(); - var response = fetch?.Invoke("apply", window, args) as Task; - args.Dispose(); - if (response == null) - throw new Exception("Internal error marshalling the response Promise from `fetch`."); + var response = fetch?.Invoke("apply", window, args) as Task; + args.Dispose(); + if (response == null) + throw new Exception("Internal error marshalling the response Promise from `fetch`."); - var t = await response.ConfigureAwait(true); + var t = await response.ConfigureAwait(true); - var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); + var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); - HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); + HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); - var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); + var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); - httpresponse.Content = StreamingSupported && streamingEnabled - ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) - : (HttpContent)new BrowserHttpContent(status); + httpresponse.Content = StreamingSupported && streamingEnabled + ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) + : (HttpContent)new BrowserHttpContent(status); - // Fill the response headers - // CORS will only allow access to certain headers. - // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. - // cors and basic responses are almost identical except that a cors response restricts the headers you can view to - // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. - // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types - // - // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation - using (var respHeaders = (JSObject)status.Headers) - { - if (respHeaders != null) + // Fill the response headers + // CORS will only allow access to certain headers. + // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. + // cors and basic responses are almost identical except that a cors response restricts the headers you can view to + // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. + // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types + // + // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation + using (var respHeaders = (JSObject)status.Headers) { - using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) + if (respHeaders != null) { - JSObject? nextResult = null; - try + using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) { - nextResult = (JSObject)entriesIterator.Invoke("next"); - while (!(bool)nextResult.GetObjectProperty("done")) + JSObject? nextResult = null; + try { - using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) + nextResult = (JSObject)entriesIterator.Invoke("next"); + while (!(bool)nextResult.GetObjectProperty("done")) { - var name = (string)resultValue[0]; - var value = (string)resultValue[1]; - if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) - if (httpresponse.Content != null) - httpresponse.Content.Headers.TryAddWithoutValidation(name, value); + using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) + { + var name = (string)resultValue[0]; + var value = (string)resultValue[1]; + if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) + if (httpresponse.Content != null) + httpresponse.Content.Headers.TryAddWithoutValidation(name, value); + } + nextResult?.Dispose(); + nextResult = (JSObject)entriesIterator.Invoke("next"); } + } + finally + { nextResult?.Dispose(); - nextResult = (JSObject)entriesIterator.Invoke("next"); } } - finally - { - nextResult?.Dispose(); - } } } - } - tcs.SetResult(httpresponse); - } - catch (JSException jsExc) - { - var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message); - tcs.SetException(httpExc); - } - catch (Exception exception) - { - tcs.SetException(exception); + tcs.SetResult(httpresponse); + } + catch (JSException jsExc) + { + var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message); + tcs.SetException(httpExc); + } + catch (Exception exception) + { + tcs.SetException(exception); + } + return await tcs.Task.ConfigureAwait(true); } } From 4eb5dd1c7f8eb44434b1c4992094867262eb5073 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 14:08:41 +0200 Subject: [PATCH 17/99] Refactor BrowserHttpHandler code. --- .../Browser/Interop.HttpHandlerService.cs | 493 -------------- .../src/System.Net.Http.csproj | 7 +- .../BrowserHttpHandler/BrowserHttpHandler.cs | 618 ++++++++++++++++++ .../BrowserHttpMessageHandler.cs | 46 -- .../SystemProxyInfo.Browser.cs | 2 +- .../src/System/Net/Http/HttpClientHandler.cs | 8 + 6 files changed, 629 insertions(+), 545 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs delete mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs b/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs deleted file mode 100644 index 3f6083ccf1d294..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.HttpHandlerService.cs +++ /dev/null @@ -1,493 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -using JSObject = Interop.JavaScript.JSObject; -using JSException = Interop.JavaScript.JSException; -using HostObject = Interop.JavaScript.HostObject; -using Uint8Array = Interop.JavaScript.Uint8Array; -using Function = Interop.JavaScript.Function; - -internal static partial class Interop -{ - internal static partial class Browser - { - public class BrowserHttpHandlerService : IDisposable - { - - private static readonly JSObject? fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); - private static readonly JSObject? window = (JSObject)Interop.Runtime.GetGlobalObject("window"); - - /// - /// Gets whether the current Browser supports streaming responses - /// - private static bool StreamingSupported { get; } - - public BrowserHttpHandlerService() - { } - - static BrowserHttpHandlerService() - { - using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) - StreamingSupported = (bool)streamingSupported.Call(); - } - public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - return DoFetch(request, cancellationToken); - } - - private async Task DoFetch(HttpRequestMessage request, CancellationToken cancellationToken) - { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } - - var tcs = new TaskCompletionSource(); - - // Wrap the cancellationToken in a using so that it can be disposed of whether - // we successfully fetched or failed trying. - using (cancellationToken.Register(() => tcs.TrySetCanceled())) - { - try - { - var requestObject = new JSObject(); - - if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) && - fetchOoptionsValue is IDictionary fetchOptions) - { - foreach (var item in fetchOptions) - { - requestObject.SetObjectProperty(item.Key, item.Value); - } - } - - requestObject.SetObjectProperty("method", request.Method.Method); - - // We need to check for body content - if (request.Content != null) - { - if (request.Content is StringContent) - { - requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(true)); - } - else - { - // 2.1.801 seems to have a problem with the line - // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) - // so we split it up into two lines. - var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); - using (var uint8Buffer = Uint8Array.From(byteAsync)) - { - requestObject.SetObjectProperty("body", uint8Buffer); - } - } - } - - // Process headers - // Cors has it's own restrictions on headers. - // https://developer.mozilla.org/en-US/docs/Web/API/Headers - using (var jsHeaders = new HostObject("Headers")) - { - if (request.Headers != null) - { - foreach (var header in request.Headers) - { - foreach (var value in header.Value) - { - jsHeaders.Invoke("append", header.Key, value); - } - } - } - if (request.Content?.Headers != null) - { - foreach (var header in request.Content.Headers) - { - foreach (var value in header.Value) - { - jsHeaders.Invoke("append", header.Key, value); - } - } - } - requestObject.SetObjectProperty("headers", jsHeaders); - } - - WasmHttpReadStream? wasmHttpReadStream = null; - - JSObject abortController = new HostObject("AbortController"); - JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); - requestObject.SetObjectProperty("signal", signal); - signal.Dispose(); - - CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => - { - if (abortController.JSHandle != -1) - { - abortController.Invoke("abort"); - abortController?.Dispose(); - } - wasmHttpReadStream?.Dispose(); - })); - - var args = new Interop.JavaScript.Array(); - if (request.RequestUri != null) - { - args.Push(request.RequestUri.ToString()); - args.Push(requestObject); - } - - requestObject.Dispose(); - - var response = fetch?.Invoke("apply", window, args) as Task; - args.Dispose(); - if (response == null) - throw new Exception("Internal error marshalling the response Promise from `fetch`."); - - var t = await response.ConfigureAwait(true); - - var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); - - HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); - - var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); - - httpresponse.Content = StreamingSupported && streamingEnabled - ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) - : (HttpContent)new BrowserHttpContent(status); - - // Fill the response headers - // CORS will only allow access to certain headers. - // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. - // cors and basic responses are almost identical except that a cors response restricts the headers you can view to - // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. - // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types - // - // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation - using (var respHeaders = (JSObject)status.Headers) - { - if (respHeaders != null) - { - using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) - { - JSObject? nextResult = null; - try - { - nextResult = (JSObject)entriesIterator.Invoke("next"); - while (!(bool)nextResult.GetObjectProperty("done")) - { - using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) - { - var name = (string)resultValue[0]; - var value = (string)resultValue[1]; - if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) - if (httpresponse.Content != null) - httpresponse.Content.Headers.TryAddWithoutValidation(name, value); - } - nextResult?.Dispose(); - nextResult = (JSObject)entriesIterator.Invoke("next"); - } - } - finally - { - nextResult?.Dispose(); - } - } - } - } - - tcs.SetResult(httpresponse); - } - catch (JSException jsExc) - { - var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message); - tcs.SetException(httpExc); - } - catch (Exception exception) - { - tcs.SetException(exception); - } - return await tcs.Task.ConfigureAwait(true); - } - } - - private class WasmFetchResponse : IDisposable - { - private readonly JSObject fetchResponse; - private readonly JSObject abortController; - private readonly CancellationTokenSource abortCts; - private readonly CancellationTokenRegistration abortRegistration; - - public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenSource abortCts, CancellationTokenRegistration abortRegistration) - { - this.fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse)); - this.abortController = abortController ?? throw new ArgumentNullException(nameof(abortController)); - this.abortCts = abortCts; - this.abortRegistration = abortRegistration; - } - - public bool IsOK => (bool)fetchResponse.GetObjectProperty("ok"); - public bool IsRedirected => (bool)fetchResponse.GetObjectProperty("redirected"); - public int Status => (int)fetchResponse.GetObjectProperty("status"); - public string StatusText => (string)fetchResponse.GetObjectProperty("statusText"); - public string ResponseType => (string)fetchResponse.GetObjectProperty("type"); - public string Url => (string)fetchResponse.GetObjectProperty("url"); - //public bool IsUseFinalURL => (bool)managedJSObject.GetObjectProperty("useFinalUrl"); - public bool IsBodyUsed => (bool)fetchResponse.GetObjectProperty("bodyUsed"); - public JSObject Headers => (JSObject)fetchResponse.GetObjectProperty("headers"); - public JSObject Body => (JSObject)fetchResponse.GetObjectProperty("body"); - - public Task ArrayBuffer() => (Task)fetchResponse.Invoke("arrayBuffer"); - public Task Text() => (Task)fetchResponse.Invoke("text"); - public Task JSON() => (Task)fetchResponse.Invoke("json"); - - public void Dispose() - { - // Dispose of unmanaged resources. - Dispose(true); - } - - // Protected implementation of Dispose pattern. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - // Free any other managed objects here. - // - abortCts.Cancel(); - - abortRegistration.Dispose(); - } - - // Free any unmanaged objects here. - // - fetchResponse?.Dispose(); - abortController?.Dispose(); - } - - } - - private sealed class BrowserHttpContent : HttpContent - { - private byte[]? _data; - private readonly WasmFetchResponse _status; - - public BrowserHttpContent(WasmFetchResponse status) - { - _status = status ?? throw new ArgumentNullException(nameof(status)); - } - - private async Task GetResponseData() - { - if (_data != null) - { - return _data; - } - - using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(true)) - { - using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) - { - _data = dataBinView.ToArray(); - _status.Dispose(); - } - } - - return _data; - } - - protected override async Task CreateContentReadStreamAsync() - { - var data = await GetResponseData().ConfigureAwait(true); - return new MemoryStream(data, writable: false); - } - - protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) - { - var data = await GetResponseData().ConfigureAwait(true); - await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); - } - - protected internal override bool TryComputeLength(out long length) - { - if (_data != null) - { - length = _data.Length; - return true; - } - - length = 0; - return false; - } - - protected override void Dispose(bool disposing) - { - _status?.Dispose(); - base.Dispose(disposing); - } - } - - private sealed class WasmHttpReadStream : Stream - { - private WasmFetchResponse? _status; - private JSObject? _reader; - - private byte[]? _bufferedBytes; - private int _position; - - public WasmHttpReadStream(WasmFetchResponse status) - { - _status = status; - } - - public override bool CanRead => true; - public override bool CanSeek => false; - public override bool CanWrite => false; - public override long Length => throw new NotSupportedException(); - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - if (count < 0 || buffer.Length - offset < count) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - if (_reader == null) - { - // If we've read everything, then _reader and _status will be null - if (_status == null) - { - return 0; - } - - try - { - using (var body = _status.Body) - { - _reader = (JSObject)body.Invoke("getReader"); - } - } - catch (JSException) - { - cancellationToken.ThrowIfCancellationRequested(); - throw; - } - } - - if (_bufferedBytes != null && _position < _bufferedBytes.Length) - { - return ReadBuffered(); - } - - try - { - var t = (Task)_reader.Invoke("read"); - using (var read = (JSObject)await t.ConfigureAwait(true)) - { - if ((bool)read.GetObjectProperty("done")) - { - _reader.Dispose(); - _reader = null; - - _status?.Dispose(); - _status = null; - return 0; - } - - _position = 0; - // value for fetch streams is a Uint8Array - using (Uint8Array binValue = (Uint8Array)read.GetObjectProperty("value")) - _bufferedBytes = binValue.ToArray(); - } - } - catch (JSException) - { - cancellationToken.ThrowIfCancellationRequested(); - throw; - } - - return ReadBuffered(); - - int ReadBuffered() - { - int n = _bufferedBytes.Length - _position; - if (n > count) - n = count; - if (n <= 0) - return 0; - - Buffer.BlockCopy(_bufferedBytes, _position, buffer, offset, n); - _position += n; - - return n; - } - } - - protected override void Dispose(bool disposing) - { - _reader?.Dispose(); - _status?.Dispose(); - } - - public override void Flush() - { - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new PlatformNotSupportedException("Synchronous reads are not supported, use ReadAsync instead"); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - } - - #region IDisposable Members - - protected virtual void Dispose(bool disposing) - { - // Nothing to do in base class. - } - - public void Dispose() - { - Dispose(true); - } - #endregion - } - } -} diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 0640959cde49ef..6fb575841176cc 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -14,7 +14,7 @@ $(DefineConstants);SYSNETHTTP_NO_OPENSSL - $(DefineConstants);BROWSER_DOES_NOT_SUPPORT_QUIC + $(DefineConstants);BROWSER_DOES_NOT_SUPPORT_QUIC;TargetsBrowser @@ -732,7 +732,7 @@ - + @@ -760,8 +760,5 @@ Link="Common\Interop\Browser\Interop.JavaScript.Function.cs" /> - - \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs new file mode 100644 index 00000000000000..4852040e5ea75e --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -0,0 +1,618 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Security; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + +using JSObject = Interop.JavaScript.JSObject; +using JSException = Interop.JavaScript.JSException; +using HostObject = Interop.JavaScript.HostObject; +using Uint8Array = Interop.JavaScript.Uint8Array; +using Function = Interop.JavaScript.Function; + +namespace System.Net.Http +{ + internal partial class BrowserHttpHandler : HttpMessageHandler + { + // This partial implementation contains members common to Browser WebAssembly running on .NET Core. + + private static readonly JSObject? fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); + private static readonly JSObject? window = (JSObject)Interop.Runtime.GetGlobalObject("window"); + + /// + /// Gets whether the current Browser supports streaming responses + /// + private static bool StreamingSupported { get; } + + + public bool UseCookies + { + get => throw new PlatformNotSupportedException("Property UseCookies is not supported."); + set => throw new PlatformNotSupportedException("Property UseCookies is not supported."); + } + + [AllowNull] + public CookieContainer CookieContainer + { + get => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + set => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + } + + public DecompressionMethods AutomaticDecompression + { + get => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + set => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + } + + public bool UseProxy + { + get => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + set => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + } + + public IWebProxy? Proxy + { + get => throw new PlatformNotSupportedException("Property Proxy is not supported."); + set => throw new PlatformNotSupportedException("Property Proxy is not supported."); + } + + public ICredentials? DefaultProxyCredentials + { + get => throw new PlatformNotSupportedException("Property Credentials is not supported."); + set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + } + + public bool PreAuthenticate + { + get => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + set => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + } + + public ICredentials? Credentials + { + get => throw new PlatformNotSupportedException("Property Credentials is not supported."); + set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + } + + public bool AllowAutoRedirect + { + get => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + set => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + } + + public int MaxAutomaticRedirections + { + get => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + set => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + } + + public int MaxConnectionsPerServer + { + get => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + set => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + } + + public int MaxResponseDrainSize + { + get => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); + set => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); + } + + public TimeSpan ResponseDrainTimeout + { + get => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); + } + + public int MaxResponseHeadersLength + { + get => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + set => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + } + + [AllowNull] + public SslClientAuthenticationOptions SslOptions + { + get => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + set => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + } + + public TimeSpan PooledConnectionLifetime + { + get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + } + + public TimeSpan PooledConnectionIdleTimeout + { + get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + } + + public TimeSpan ConnectTimeout + { + get => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); + } + + public TimeSpan Expect100ContinueTimeout + { + get => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); + } + + public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); + + // protected override void Dispose(bool disposing) + // { + // if (disposing) + // { + // if (wasmHandler != null) + // { + // wasmHandler.Dispose(); + // wasmHandler = null; + // } + // } + // base.Dispose(disposing); + // } + + // protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + // { + // if (wasmHandler == null) + // throw new ObjectDisposedException(GetType().ToString()); + + // return wasmHandler.SendAsync(request, cancellationToken); + // } + + static BrowserHttpHandler() + { + using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) + StreamingSupported = (bool)streamingSupported.Call(); + } + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + return DoFetch(request, cancellationToken); + } + + private async Task DoFetch(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request == null) + { + throw new ArgumentNullException(nameof(request)); + } + + var tcs = new TaskCompletionSource(); + + // Wrap the cancellationToken in a using so that it can be disposed of whether + // we successfully fetched or failed trying. + using (cancellationToken.Register(() => tcs.TrySetCanceled())) + { + try + { + var requestObject = new JSObject(); + + if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) && + fetchOoptionsValue is IDictionary fetchOptions) + { + foreach (var item in fetchOptions) + { + requestObject.SetObjectProperty(item.Key, item.Value); + } + } + + requestObject.SetObjectProperty("method", request.Method.Method); + + // We need to check for body content + if (request.Content != null) + { + if (request.Content is StringContent) + { + requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(true)); + } + else + { + // 2.1.801 seems to have a problem with the line + // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) + // so we split it up into two lines. + var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); + using (var uint8Buffer = Uint8Array.From(byteAsync)) + { + requestObject.SetObjectProperty("body", uint8Buffer); + } + } + } + + // Process headers + // Cors has it's own restrictions on headers. + // https://developer.mozilla.org/en-US/docs/Web/API/Headers + using (var jsHeaders = new HostObject("Headers")) + { + if (request.Headers != null) + { + foreach (var header in request.Headers) + { + foreach (var value in header.Value) + { + jsHeaders.Invoke("append", header.Key, value); + } + } + } + if (request.Content?.Headers != null) + { + foreach (var header in request.Content.Headers) + { + foreach (var value in header.Value) + { + jsHeaders.Invoke("append", header.Key, value); + } + } + } + requestObject.SetObjectProperty("headers", jsHeaders); + } + + WasmHttpReadStream? wasmHttpReadStream = null; + + JSObject abortController = new HostObject("AbortController"); + JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); + requestObject.SetObjectProperty("signal", signal); + signal.Dispose(); + + CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => + { + if (abortController.JSHandle != -1) + { + abortController.Invoke("abort"); + abortController?.Dispose(); + } + wasmHttpReadStream?.Dispose(); + })); + + var args = new Interop.JavaScript.Array(); + if (request.RequestUri != null) + { + args.Push(request.RequestUri.ToString()); + args.Push(requestObject); + } + + requestObject.Dispose(); + + var response = fetch?.Invoke("apply", window, args) as Task; + args.Dispose(); + if (response == null) + throw new Exception("Internal error marshalling the response Promise from `fetch`."); + + var t = await response.ConfigureAwait(true); + + var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); + + HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); + + var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); + + httpresponse.Content = StreamingSupported && streamingEnabled + ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) + : (HttpContent)new BrowserHttpContent(status); + + // Fill the response headers + // CORS will only allow access to certain headers. + // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. + // cors and basic responses are almost identical except that a cors response restricts the headers you can view to + // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. + // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types + // + // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation + using (var respHeaders = (JSObject)status.Headers) + { + if (respHeaders != null) + { + using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) + { + JSObject? nextResult = null; + try + { + nextResult = (JSObject)entriesIterator.Invoke("next"); + while (!(bool)nextResult.GetObjectProperty("done")) + { + using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) + { + var name = (string)resultValue[0]; + var value = (string)resultValue[1]; + if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) + if (httpresponse.Content != null) + httpresponse.Content.Headers.TryAddWithoutValidation(name, value); + } + nextResult?.Dispose(); + nextResult = (JSObject)entriesIterator.Invoke("next"); + } + } + finally + { + nextResult?.Dispose(); + } + } + } + } + + tcs.SetResult(httpresponse); + } + catch (JSException jsExc) + { + var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message); + tcs.SetException(httpExc); + } + catch (Exception exception) + { + tcs.SetException(exception); + } + return await tcs.Task.ConfigureAwait(true); + } + } + + private class WasmFetchResponse : IDisposable + { + private readonly JSObject fetchResponse; + private readonly JSObject abortController; + private readonly CancellationTokenSource abortCts; + private readonly CancellationTokenRegistration abortRegistration; + + public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenSource abortCts, CancellationTokenRegistration abortRegistration) + { + this.fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse)); + this.abortController = abortController ?? throw new ArgumentNullException(nameof(abortController)); + this.abortCts = abortCts; + this.abortRegistration = abortRegistration; + } + + public bool IsOK => (bool)fetchResponse.GetObjectProperty("ok"); + public bool IsRedirected => (bool)fetchResponse.GetObjectProperty("redirected"); + public int Status => (int)fetchResponse.GetObjectProperty("status"); + public string StatusText => (string)fetchResponse.GetObjectProperty("statusText"); + public string ResponseType => (string)fetchResponse.GetObjectProperty("type"); + public string Url => (string)fetchResponse.GetObjectProperty("url"); + //public bool IsUseFinalURL => (bool)managedJSObject.GetObjectProperty("useFinalUrl"); + public bool IsBodyUsed => (bool)fetchResponse.GetObjectProperty("bodyUsed"); + public JSObject Headers => (JSObject)fetchResponse.GetObjectProperty("headers"); + public JSObject Body => (JSObject)fetchResponse.GetObjectProperty("body"); + + public Task ArrayBuffer() => (Task)fetchResponse.Invoke("arrayBuffer"); + public Task Text() => (Task)fetchResponse.Invoke("text"); + public Task JSON() => (Task)fetchResponse.Invoke("json"); + + public void Dispose() + { + // Dispose of unmanaged resources. + Dispose(true); + } + + // Protected implementation of Dispose pattern. + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // Free any other managed objects here. + // + abortCts.Cancel(); + + abortRegistration.Dispose(); + } + + // Free any unmanaged objects here. + // + fetchResponse?.Dispose(); + abortController?.Dispose(); + } + + } + + private sealed class BrowserHttpContent : HttpContent + { + private byte[]? _data; + private readonly WasmFetchResponse _status; + + public BrowserHttpContent(WasmFetchResponse status) + { + _status = status ?? throw new ArgumentNullException(nameof(status)); + } + + private async Task GetResponseData() + { + if (_data != null) + { + return _data; + } + + using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(true)) + { + using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) + { + _data = dataBinView.ToArray(); + _status.Dispose(); + } + } + + return _data; + } + + protected override async Task CreateContentReadStreamAsync() + { + var data = await GetResponseData().ConfigureAwait(true); + return new MemoryStream(data, writable: false); + } + + protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) + { + var data = await GetResponseData().ConfigureAwait(true); + await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); + } + + protected internal override bool TryComputeLength(out long length) + { + if (_data != null) + { + length = _data.Length; + return true; + } + + length = 0; + return false; + } + + protected override void Dispose(bool disposing) + { + _status?.Dispose(); + base.Dispose(disposing); + } + } + + private sealed class WasmHttpReadStream : Stream + { + private WasmFetchResponse? _status; + private JSObject? _reader; + + private byte[]? _bufferedBytes; + private int _position; + + public WasmHttpReadStream(WasmFetchResponse status) + { + _status = status; + } + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if (count < 0 || buffer.Length - offset < count) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + if (_reader == null) + { + // If we've read everything, then _reader and _status will be null + if (_status == null) + { + return 0; + } + + try + { + using (var body = _status.Body) + { + _reader = (JSObject)body.Invoke("getReader"); + } + } + catch (JSException) + { + cancellationToken.ThrowIfCancellationRequested(); + throw; + } + } + + if (_bufferedBytes != null && _position < _bufferedBytes.Length) + { + return ReadBuffered(); + } + + try + { + var t = (Task)_reader.Invoke("read"); + using (var read = (JSObject)await t.ConfigureAwait(true)) + { + if ((bool)read.GetObjectProperty("done")) + { + _reader.Dispose(); + _reader = null; + + _status?.Dispose(); + _status = null; + return 0; + } + + _position = 0; + // value for fetch streams is a Uint8Array + using (Uint8Array binValue = (Uint8Array)read.GetObjectProperty("value")) + _bufferedBytes = binValue.ToArray(); + } + } + catch (JSException) + { + cancellationToken.ThrowIfCancellationRequested(); + throw; + } + + return ReadBuffered(); + + int ReadBuffered() + { + int n = _bufferedBytes.Length - _position; + if (n > count) + n = count; + if (n <= 0) + return 0; + + Buffer.BlockCopy(_bufferedBytes, _position, buffer, offset, n); + _position += n; + + return n; + } + } + + protected override void Dispose(bool disposing) + { + _reader?.Dispose(); + _status?.Dispose(); + } + + public override void Flush() + { + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new PlatformNotSupportedException("Synchronous reads are not supported, use ReadAsync instead"); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } + } + +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs deleted file mode 100644 index e8ca6356de63c1..00000000000000 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpMessageHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Globalization; -using System.Collections.Generic; -using System.Net.Security; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; - -namespace System.Net.Http -{ - internal partial class BrowserHttpMessageHandler : HttpMessageHandler - { - // This partial implementation contains members common to Browser WebAssembly running on .NET Core. - internal Interop.Browser.BrowserHttpHandlerService? wasmHandler; - - public BrowserHttpMessageHandler() - { - wasmHandler = new Interop.Browser.BrowserHttpHandlerService(); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (wasmHandler != null) - { - wasmHandler.Dispose(); - wasmHandler = null; - } - } - base.Dispose(disposing); - } - - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - if (wasmHandler == null) - throw new ObjectDisposedException(GetType().ToString()); - - return wasmHandler.SendAsync(request, cancellationToken); - } - } -} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs index 6e907eb7b5e39b..f86bcf0c25de39 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs @@ -9,7 +9,7 @@ internal static partial class SystemProxyInfo // On Browser we do not support proxy. public static IWebProxy ConstructSystemProxy() { - throw new PlatformNotSupportedException ("WebProxy is not supported."); + throw new PlatformNotSupportedException("WebProxy is not supported."); } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 1851c5aa3da6aa..b8c1b84fbf55b8 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -13,13 +13,21 @@ namespace System.Net.Http { public partial class HttpClientHandler : HttpMessageHandler { +#if TargetsBrowser + private readonly BrowserHttpHandler _socketsHttpHandler; +#else private readonly SocketsHttpHandler _socketsHttpHandler; +#endif private readonly DiagnosticsHandler _diagnosticsHandler; private ClientCertificateOption _clientCertificateOptions; public HttpClientHandler() { +#if TargetsBrowser + _socketsHttpHandler = new BrowserHttpHandler(); +#else _socketsHttpHandler = new SocketsHttpHandler(); +#endif _diagnosticsHandler = new DiagnosticsHandler(_socketsHttpHandler); ClientCertificateOptions = ClientCertificateOption.Manual; } From d5360b6c0911446cc125f3f182dbcb345bc3672d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 14:20:34 +0200 Subject: [PATCH 18/99] Cleanup --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 4852040e5ea75e..0e17f11edc4eb6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -24,7 +24,6 @@ namespace System.Net.Http internal partial class BrowserHttpHandler : HttpMessageHandler { // This partial implementation contains members common to Browser WebAssembly running on .NET Core. - private static readonly JSObject? fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); private static readonly JSObject? window = (JSObject)Interop.Runtime.GetGlobalObject("window"); @@ -33,6 +32,11 @@ internal partial class BrowserHttpHandler : HttpMessageHandler /// private static bool StreamingSupported { get; } + static BrowserHttpHandler() + { + using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) + StreamingSupported = (bool)streamingSupported.Call(); + } public bool UseCookies { @@ -151,33 +155,6 @@ public TimeSpan Expect100ContinueTimeout } public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); - - // protected override void Dispose(bool disposing) - // { - // if (disposing) - // { - // if (wasmHandler != null) - // { - // wasmHandler.Dispose(); - // wasmHandler = null; - // } - // } - // base.Dispose(disposing); - // } - - // protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - // { - // if (wasmHandler == null) - // throw new ObjectDisposedException(GetType().ToString()); - - // return wasmHandler.SendAsync(request, cancellationToken); - // } - - static BrowserHttpHandler() - { - using (var streamingSupported = new Function("return typeof Response !== 'undefined' && 'body' in Response.prototype && typeof ReadableStream === 'function'")) - StreamingSupported = (bool)streamingSupported.Call(); - } protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return DoFetch(request, cancellationToken); From d1b537d02d1313e6f8ebc02d69f44748e5caac1b Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 29 Apr 2020 14:55:24 +0200 Subject: [PATCH 19/99] Remove null check as it should be checked in outer classes --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 0e17f11edc4eb6..054df4b05b3dee 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -162,10 +162,6 @@ protected internal override Task SendAsync(HttpRequestMessa private async Task DoFetch(HttpRequestMessage request, CancellationToken cancellationToken) { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } var tcs = new TaskCompletionSource(); From 2d8e12401155f24b7ee4fc928fb7e2d228c69506 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 06:58:27 +0200 Subject: [PATCH 20/99] Cleanup var usage to use explicit type. Address commit comments - For all of these vars, please replace them with the actual type, except when the type is obvious from a new or explicit cast on the right-hand side. --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 054df4b05b3dee..1de97e4487e1be 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -173,10 +173,10 @@ private async Task DoFetch(HttpRequestMessage request, Canc { var requestObject = new JSObject(); - if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out var fetchOoptionsValue) && + if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out object? fetchOoptionsValue) && fetchOoptionsValue is IDictionary fetchOptions) { - foreach (var item in fetchOptions) + foreach (KeyValuePair item in fetchOptions) { requestObject.SetObjectProperty(item.Key, item.Value); } @@ -197,7 +197,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) // so we split it up into two lines. var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); - using (var uint8Buffer = Uint8Array.From(byteAsync)) + using (Uint8Array uint8Buffer = Uint8Array.From(byteAsync)) { requestObject.SetObjectProperty("body", uint8Buffer); } @@ -207,13 +207,13 @@ private async Task DoFetch(HttpRequestMessage request, Canc // Process headers // Cors has it's own restrictions on headers. // https://developer.mozilla.org/en-US/docs/Web/API/Headers - using (var jsHeaders = new HostObject("Headers")) + using (HostObject jsHeaders = new HostObject("Headers")) { if (request.Headers != null) { - foreach (var header in request.Headers) + foreach (KeyValuePair> header in request.Headers) { - foreach (var value in header.Value) + foreach (string value in header.Value) { jsHeaders.Invoke("append", header.Key, value); } @@ -221,9 +221,9 @@ private async Task DoFetch(HttpRequestMessage request, Canc } if (request.Content?.Headers != null) { - foreach (var header in request.Content.Headers) + foreach (KeyValuePair> header in request.Content.Headers) { - foreach (var value in header.Value) + foreach (string value in header.Value) { jsHeaders.Invoke("append", header.Key, value); } @@ -264,13 +264,13 @@ private async Task DoFetch(HttpRequestMessage request, Canc if (response == null) throw new Exception("Internal error marshalling the response Promise from `fetch`."); - var t = await response.ConfigureAwait(true); + JSObject t = (JSObject)await response.ConfigureAwait(true); - var status = new WasmFetchResponse((JSObject)t, abortController, abortCts, abortRegistration); + var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); - var streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); + bool streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); httpresponse.Content = StreamingSupported && streamingEnabled ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) @@ -284,7 +284,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types // // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation - using (var respHeaders = (JSObject)status.Headers) + using (JSObject respHeaders = status.Headers) { if (respHeaders != null) { @@ -352,7 +352,6 @@ public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, Cance public string StatusText => (string)fetchResponse.GetObjectProperty("statusText"); public string ResponseType => (string)fetchResponse.GetObjectProperty("type"); public string Url => (string)fetchResponse.GetObjectProperty("url"); - //public bool IsUseFinalURL => (bool)managedJSObject.GetObjectProperty("useFinalUrl"); public bool IsBodyUsed => (bool)fetchResponse.GetObjectProperty("bodyUsed"); public JSObject Headers => (JSObject)fetchResponse.GetObjectProperty("headers"); public JSObject Body => (JSObject)fetchResponse.GetObjectProperty("body"); @@ -418,13 +417,13 @@ private async Task GetResponseData() protected override async Task CreateContentReadStreamAsync() { - var data = await GetResponseData().ConfigureAwait(true); + byte[] data = await GetResponseData().ConfigureAwait(true); return new MemoryStream(data, writable: false); } protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) { - var data = await GetResponseData().ConfigureAwait(true); + byte[] data = await GetResponseData().ConfigureAwait(true); await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); } @@ -495,7 +494,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, try { - using (var body = _status.Body) + using (JSObject body = _status.Body) { _reader = (JSObject)body.Invoke("getReader"); } From d443cfde22b8d2fac5d5577a625062f8bbb23f7c Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 07:14:38 +0200 Subject: [PATCH 21/99] Move `.ConfigureAwait(true)` to `.ConfigureAwait(false)` - Address review comments --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 1de97e4487e1be..5e2d7c0dcd10f8 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -189,14 +189,14 @@ private async Task DoFetch(HttpRequestMessage request, Canc { if (request.Content is StringContent) { - requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(true)); + requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(false)); } else { // 2.1.801 seems to have a problem with the line // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) // so we split it up into two lines. - var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(true); + var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false); using (Uint8Array uint8Buffer = Uint8Array.From(byteAsync)) { requestObject.SetObjectProperty("body", uint8Buffer); @@ -264,7 +264,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc if (response == null) throw new Exception("Internal error marshalling the response Promise from `fetch`."); - JSObject t = (JSObject)await response.ConfigureAwait(true); + JSObject t = (JSObject)await response.ConfigureAwait(false); var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); @@ -327,7 +327,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc { tcs.SetException(exception); } - return await tcs.Task.ConfigureAwait(true); + return await tcs.Task.ConfigureAwait(false); } } @@ -403,7 +403,7 @@ private async Task GetResponseData() return _data; } - using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(true)) + using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(false)) { using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) { @@ -417,14 +417,14 @@ private async Task GetResponseData() protected override async Task CreateContentReadStreamAsync() { - byte[] data = await GetResponseData().ConfigureAwait(true); + byte[] data = await GetResponseData().ConfigureAwait(false); return new MemoryStream(data, writable: false); } protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) { - byte[] data = await GetResponseData().ConfigureAwait(true); - await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); + byte[] data = await GetResponseData().ConfigureAwait(false); + await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(false); } protected internal override bool TryComputeLength(out long length) @@ -514,7 +514,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, try { var t = (Task)_reader.Invoke("read"); - using (var read = (JSObject)await t.ConfigureAwait(true)) + using (var read = (JSObject)await t.ConfigureAwait(false)) { if ((bool)read.GetObjectProperty("done")) { From 0d63762d39c48504abfb3fd851d3f4e8792ff9e1 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 08:42:02 +0200 Subject: [PATCH 22/99] Change accessor of Runtime javascript interop methods. --- .../src/Interop/Browser/Interop.Runtime.cs | 159 +++--------------- 1 file changed, 23 insertions(+), 136 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 9c5ca9a27778d3..a32501daab8b9e 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -137,7 +137,7 @@ private static int BindCoreCLRObject(int js_id, int gcHandle) return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - private static int BindJSType(int js_id, Type mappedType) + internal static int BindJSType(int js_id, Type mappedType) { if (!bound_objects.TryGetValue(js_id, out JSObject? obj)) { @@ -148,7 +148,7 @@ private static int BindJSType(int js_id, Type mappedType) return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - private static int UnBindJSObject(int js_id) + internal static int UnBindJSObject(int js_id) { if (bound_objects.TryGetValue(js_id, out var obj)) { @@ -159,7 +159,7 @@ private static int UnBindJSObject(int js_id) return 0; } - private static void UnBindJSObjectAndFree(int js_id) + internal static void UnBindJSObjectAndFree(int js_id) { if (bound_objects.TryGetValue(js_id, out var obj)) { @@ -180,7 +180,7 @@ private static void UnBindJSObjectAndFree(int js_id) } - private static void UnBindRawJSObjectAndFree(int gcHandle) + internal static void UnBindRawJSObjectAndFree(int gcHandle) { GCHandle h = (GCHandle)(IntPtr)gcHandle; @@ -231,27 +231,27 @@ public static void FreeObject(object obj) } } - private static object CreateTaskSource(int js_id) + internal static object CreateTaskSource(int js_id) { return new TaskCompletionSource(); } - private static void SetTaskSourceResult(TaskCompletionSource tcs, object result) + internal static void SetTaskSourceResult(TaskCompletionSource tcs, object result) { tcs.SetResult(result); } - private static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) + internal static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) { tcs.SetException(new JSException(reason)); } - private static int GetTaskAndBind(TaskCompletionSource tcs, int js_id) + internal static int GetTaskAndBind(TaskCompletionSource tcs, int js_id) { return BindExistingObject(tcs.Task, js_id); } - private static int BindExistingObject(object raw_obj, int js_id) + internal static int BindExistingObject(object raw_obj, int js_id) { JSObject? obj = raw_obj as JSObject; @@ -261,7 +261,7 @@ private static int BindExistingObject(object raw_obj, int js_id) return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - private static int GetJSObjectId(object raw_obj) + internal static int GetJSObjectId(object raw_obj) { JSObject? obj = raw_obj as JSObject; @@ -271,7 +271,7 @@ private static int GetJSObjectId(object raw_obj) return obj != null ? obj.JSHandle : -1; } - private static object? GetMonoObject(int gc_handle) + internal static object? GetMonoObject(int gc_handle) { GCHandle h = (GCHandle)(IntPtr)gc_handle; JSObject? o = h.Target as JSObject; @@ -280,21 +280,21 @@ private static int GetJSObjectId(object raw_obj) return o; } - private static object BoxInt(int i) + internal static object BoxInt(int i) { return i; } - private static object BoxDouble(double d) + internal static object BoxDouble(double d) { return d; } - private static object BoxBool(int b) + internal static object BoxBool(int b) { return b == 0 ? false : true; } - private static bool IsSimpleArray(object a) + internal static bool IsSimpleArray(object a) { if (a is Array arr) { @@ -305,7 +305,7 @@ private static bool IsSimpleArray(object a) } - private static object? GetCoreType(string coreObj) + internal static object? GetCoreType(string coreObj) { Assembly asm = typeof(Runtime).Assembly; Type? type = asm.GetType(coreObj); @@ -323,8 +323,7 @@ internal struct IntPtrAndHandle internal RuntimeMethodHandle handle; } - //FIXME this probably won't handle generics - private static string GetCallSignature(IntPtr method_handle) + internal static string GetCallSignature(IntPtr method_handle) { IntPtrAndHandle tmp = default(IntPtrAndHandle); tmp.ptr = method_handle; @@ -392,7 +391,7 @@ private static string GetCallSignature(IntPtr method_handle) return res; } - private static void SetupJSContinuation(Task task, JSObject cont_obj) + internal static void SetupJSContinuation(Task task, JSObject cont_obj) { if (task.IsCompleted) Complete(); @@ -429,7 +428,7 @@ void Complete() } } - public static object GetGlobalObject(string? str = null) + internal static object GetGlobalObject(string? str = null) { int exception; var globalHandle = Runtime.GetGlobalObject(str, out exception); @@ -440,7 +439,7 @@ public static object GetGlobalObject(string? str = null) return globalHandle; } - private static string ObjectToString(object o) + internal static string ObjectToString(object o) { //if (o == null) @@ -451,7 +450,7 @@ private static string ObjectToString(object o) return o.ToString() ?? string.Empty; } - private static double GetDateValue(object dtv) + internal static double GetDateValue(object dtv) { if (dtv == null) throw new ArgumentNullException(nameof(dtv), "Value can not be null"); @@ -467,128 +466,16 @@ private static double GetDateValue(object dtv) return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); } - private static DateTime CreateDateTime(double ticks) + internal static DateTime CreateDateTime(double ticks) { var unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); return unixTime.DateTime; } - private static Uri CreateUri(string uri) + internal static Uri CreateUri(string uri) { return new Uri(uri); } - // This is simple right now and will include FlagsAttribute later. - // public static Enum EnumFromExportContract(Type enumType, object value) - // { - - // if (!enumType.IsEnum) - // { - // throw new ArgumentException("Type provided must be an Enum.", nameof(enumType)); - // } - - // if (value is string) - // { - - // var fields = enumType.GetFields(); - // foreach (var fi in fields) - // { - // // Do not process special names - // if (fi.IsSpecialName) - // continue; - - // Interop.Runtime.ExportAttribute[] attributes = - // (Interop.Runtime.ExportAttribute[])fi.GetCustomAttributes(typeof(Interop.Runtime.ExportAttribute), false); - - // var enumConversionType = ConvertEnum.Default; - - // object contractName = null; - - // if (attributes != null && attributes.Length > 0) - // { - // enumConversionType = attributes[0].EnumValue; - // if (enumConversionType != ConvertEnum.Numeric) - // contractName = attributes[0].ContractName; - - // } - - // if (contractName == null) - // contractName = fi.Name; - - // switch (enumConversionType) - // { - // case ConvertEnum.ToLower: - // contractName = contractName.ToString().ToLower(); - // break; - // case ConvertEnum.ToUpper: - // contractName = contractName.ToString().ToUpper(); - // break; - // case ConvertEnum.Numeric: - // contractName = (int)Enum.Parse(value.GetType(), contractName.ToString()); - // break; - // default: - // contractName = contractName.ToString(); - // break; - // } - - // if (contractName.ToString() == value.ToString()) - // { - // return (Enum)Enum.Parse(enumType, fi.Name); - // } - - // } - - // throw new ArgumentException($"Value is a name, but not one of the named constants defined for the enum of type: {enumType}.", nameof(value)); - // } - // else - // { - // return (Enum)Enum.ToObject(enumType, value); - // } - - // } - - // // This is simple right now and will include FlagsAttribute later. - // public static object EnumToExportContract(Enum value) - // { - - // FieldInfo fi = value.GetType().GetField(value.ToString()); - - // ExportAttribute[] attributes = - // (ExportAttribute[])fi.GetCustomAttributes(typeof(ExportAttribute), false); - - // var enumConversionType = ConvertEnum.Default; - - // object contractName = null; - - // if (attributes != null && attributes.Length > 0) - // { - // enumConversionType = attributes[0].EnumValue; - // if (enumConversionType != ConvertEnum.Numeric) - // contractName = attributes[0].ContractName; - - // } - - // if (contractName == null) - // contractName = value; - - // switch (enumConversionType) - // { - // case ConvertEnum.ToLower: - // contractName = contractName.ToString().ToLower(); - // break; - // case ConvertEnum.ToUpper: - // contractName = contractName.ToString().ToUpper(); - // break; - // case ConvertEnum.Numeric: - // contractName = (int)Enum.Parse(value.GetType(), contractName.ToString()); - // break; - // default: - // contractName = contractName.ToString(); - // break; - // } - - // return contractName; - // } - // // Can be called by the app to stop profiling // From a91637c33480a97464d3edd179b6c74843624716 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 08:51:27 +0200 Subject: [PATCH 23/99] Change accessor of Runtime javascript interop methods. --- src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index a32501daab8b9e..c1824fcd0f2184 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -102,7 +102,7 @@ public static int New(string hostClassName, params object[] _params) return res as JSObject; } - private static int BindJSObject(int js_id, Type mappedType) + internal static int BindJSObject(int js_id, Type mappedType) { if (!bound_objects.TryGetValue(js_id, out JSObject? obj)) { @@ -118,7 +118,7 @@ private static int BindJSObject(int js_id, Type mappedType) return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - private static int BindCoreCLRObject(int js_id, int gcHandle) + internal static int BindCoreCLRObject(int js_id, int gcHandle) { //Console.WriteLine ($"Registering CLR Object {js_id} with handle {gcHandle}"); GCHandle h = (GCHandle)(IntPtr)gcHandle; From cdbbf0fad2ed5efe5073818e9a658d6e88cd720c Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 09:30:47 +0200 Subject: [PATCH 24/99] Address review comments for unused code --- .../Browser/Interop.JavaScript.TypedArray.cs | 92 +++---------------- 1 file changed, 14 insertions(+), 78 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs index 5443c5fdf6c266..1071c31f101064 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs @@ -151,21 +151,11 @@ public U? this[int i] private U? UnBoxValue(object jsValue) { - if (jsValue != null) - { - var type = jsValue.GetType(); - if (type.IsPrimitive) - { - return (U)Convert.ChangeType(jsValue, typeof(U)); - } - else - { - throw new InvalidCastException($"Unable to cast object of type {type} to type {typeof(U)}."); - } - - } - else + if (jsValue == null) return null; + + var type = jsValue.GetType(); + return (U)Convert.ChangeType(jsValue, typeof(U)); } public U[] ToArray() @@ -180,7 +170,10 @@ public U[] ToArray() public static unsafe T From(ReadOnlySpan span) { // source has to be instantiated. - ValidateFromSource(span); + if (span == null) + { + throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null."); + } TypedArrayTypeCode type = (TypedArrayTypeCode)Type.GetTypeCode(typeof(U)); // Special case for Uint8ClampedArray, a clamped array which represents an array of 8-bit unsigned integers clamped to 0-255; @@ -210,18 +203,14 @@ public unsafe int CopyTo(Span span) } } - private unsafe int CopyFrom(void* ptrSource, int offset, int count) - { - var res = Runtime.TypedArrayCopyFrom(JSHandle, (int)ptrSource, offset, offset + count, Marshal.SizeOf(), out int exception); - if (exception != 0) - throw new JSException((string)res); - return (int)res / Marshal.SizeOf(); - - } - public unsafe int CopyFrom(ReadOnlySpan span) { - ValidateSource(span); + // source has to be instantiated. + if (span == null || span.Length == 0) + { + throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null and must have a length"); + } + var bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { @@ -231,59 +220,6 @@ public unsafe int CopyFrom(ReadOnlySpan span) return (int)res / Marshal.SizeOf(); } } - - protected void ValidateTarget(Span target) - { - // target array has to be instantiated. - if (target == null || target.Length == 0) - { - throw new System.ArgumentException($"Invalid argument: {nameof(target)} can not be null and must have a length"); - } - - } - - protected void ValidateSource(ReadOnlySpan source) - { - // target has to be instantiated. - if (source == null || source.Length == 0) - { - throw new System.ArgumentException($"Invalid argument: {nameof(source)} can not be null and must have a length"); - } - - } - - protected static void ValidateFromSource(ReadOnlySpan source) - { - // target array has to be instantiated. - if (source == null) - { - throw new System.ArgumentException($"Invalid argument: {nameof(source)} can not be null."); - } - - } - - - protected static void ValidateFromSource(U[] source, int offset, int count) - { - // target array has to be instantiated. - if (source == null) - { - throw new System.ArgumentException($"Invalid argument: {nameof(source)} can not be null."); - } - - // offset can not be past the end of the array - if (offset > source.Length) - { - throw new System.ArgumentException($"Invalid argument: {nameof(offset)} can not be greater than length of '{nameof(source)}'"); - } - // offset plus count can not pass the end of the array. - if (offset + count > source.Length) - { - throw new System.ArgumentException($"Invalid argument: {nameof(offset)} plus {nameof(count)} can not be greater than length of '{nameof(source)}'"); - } - - } - } } } From 5814d1d96f2fc23f9d32d9b26c8c68e4f37b3705 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 10:05:20 +0200 Subject: [PATCH 25/99] Cleanup leftover debug WriteLines --- .../Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs index 76c82e73e4ed64..a24469a0eb6bbb 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs @@ -17,7 +17,6 @@ public class AnyRef internal AnyRef(int js_handle) { - //Console.WriteLine ($"AnyRef: {js_handle}"); this.JSHandle = js_handle; this.Handle = GCHandle.Alloc(this); } From 146b6d7ed784e8798dab93c7dee18a6727ace31f Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 10:11:55 +0200 Subject: [PATCH 26/99] Remove the AllowNull attributes as per review comments. --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 5e2d7c0dcd10f8..55962334969b35 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -11,7 +11,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using System.Diagnostics.CodeAnalysis; using JSObject = Interop.JavaScript.JSObject; using JSException = Interop.JavaScript.JSException; @@ -44,7 +43,6 @@ public bool UseCookies set => throw new PlatformNotSupportedException("Property UseCookies is not supported."); } - [AllowNull] public CookieContainer CookieContainer { get => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); @@ -123,7 +121,6 @@ public int MaxResponseHeadersLength set => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); } - [AllowNull] public SslClientAuthenticationOptions SslOptions { get => throw new PlatformNotSupportedException("Property SslOptions is not supported."); From 1f510f4067d88bfc6b8d2c3c6805b4894b033549 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 10:13:27 +0200 Subject: [PATCH 27/99] Update src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs Co-Authored-By: Marek Safar --- src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index c1824fcd0f2184..c1fc05760586df 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -185,7 +185,7 @@ internal static void UnBindRawJSObjectAndFree(int gcHandle) GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj = h.Target as JSObject; - if (obj != null && obj.RawObject != null) + if (obj?.RawObject != null) { raw_to_js.Remove(obj.RawObject); From 90b83f6a27119323eb17a12889f0de9388bf5615 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 10:27:05 +0200 Subject: [PATCH 28/99] throw different exception type as per review comment --- src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index c1fc05760586df..6ed3cde50a0d2e 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -382,7 +382,7 @@ internal static string GetCallSignature(IntPtr method_handle) else { if (t.IsValueType) - throw new Exception("Can't handle VT arguments"); + throw new NotSupportedException("ValueType arguments are not supported."); res += "o"; } break; From da394d60c0db4cb896f29242698c19755572b28c Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 10:58:51 +0200 Subject: [PATCH 29/99] use char[] array instead and return new string(array). - Address review comments --- .../src/Interop/Browser/Interop.Runtime.cs | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 6ed3cde50a0d2e..280e4689ff3a8e 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -330,11 +330,9 @@ internal static string GetCallSignature(IntPtr method_handle) var mb = MethodBase.GetMethodFromHandle(tmp.handle); - string res = ""; - var parms = mb?.GetParameters(); - if (parms == null) - return res; - foreach (var p in parms) + char[] res = new char[20]; + var charIndex = 0; + foreach (var p in mb.GetParameters()) { var t = p.ParameterType; @@ -349,47 +347,48 @@ internal static string GetCallSignature(IntPtr method_handle) case TypeCode.Boolean: // Enums types have the same code as their underlying numeric types if (t.IsEnum) - res += "j"; + res[charIndex++] = 'j'; else - res += "i"; + res[charIndex++] = 'i'; break; case TypeCode.Int64: case TypeCode.UInt64: // Enums types have the same code as their underlying numeric types if (t.IsEnum) - res += "k"; + res[charIndex++] = 'k'; else - res += "l"; + res[charIndex++] = 'l'; break; case TypeCode.Single: - res += "f"; + res[charIndex++] = 'f'; break; case TypeCode.Double: - res += "d"; + res[charIndex++] = 'd'; break; case TypeCode.String: - res += "s"; + res[charIndex++] = 's'; break; default: if (t == typeof(IntPtr)) { - res += "i"; + res[charIndex++] = 'i'; } else if (t == typeof(Uri)) { - res += "u"; + res[charIndex++] = 'u'; } else { if (t.IsValueType) throw new NotSupportedException("ValueType arguments are not supported."); - res += "o"; + res[charIndex++] = 'o'; } break; } } - return res; + return charIndex == 0 ? string.Empty : new string(res, 0, charIndex); + } internal static void SetupJSContinuation(Task task, JSObject cont_obj) { From 95af576d53a9ce5ca6528400ff5f23ed71830a77 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 11:37:42 +0200 Subject: [PATCH 30/99] Initial addition of Browser Interop library --- .../System.Runtime.Interop.JavaScript.csproj | 32 +++++++++++++++++++ .../System.Runtime.Interop.JavaScript.sln | 17 ++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj create mode 100644 src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.sln diff --git a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj new file mode 100644 index 00000000000000..1317085d0db2fe --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj @@ -0,0 +1,32 @@ + + + Library + System.Runtime.Interop.JavaScript + win + + $(NoWarn);0436;CS1573 + true + $(DefineConstants) + $(NetCoreAppCurrent)-Browser + enable + + + $(DefineConstants);TargetsBrowser + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.sln b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.sln new file mode 100644 index 00000000000000..e2575e6a36c6f2 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Interop.JavaScript", "System.Runtime.Interop.JavaScript.csproj", "{8871E4FB-3682-4DB3-B5B6-C4386860CA50}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From ada3198d546899cbd8079355267776cb871d4513 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 30 Apr 2020 18:09:44 +0200 Subject: [PATCH 31/99] use char[] array instead and return new string(array). - Address review comments --- .../src/Interop/Browser/Interop.Runtime.cs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 280e4689ff3a8e..cc4cfe46375e74 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -329,13 +329,19 @@ internal static string GetCallSignature(IntPtr method_handle) tmp.ptr = method_handle; var mb = MethodBase.GetMethodFromHandle(tmp.handle); + if (mb == null) + return string.Empty; - char[] res = new char[20]; - var charIndex = 0; - foreach (var p in mb.GetParameters()) - { - var t = p.ParameterType; + ParameterInfo[] parms = mb.GetParameters(); + var parmsLength = parms.Length; + if (parmsLength == 0) + return string.Empty; + + var res = new char[parmsLength]; + for (int c = 0; c < parmsLength; c++) + { + var t = parms[c].ParameterType; switch (Type.GetTypeCode(t)) { case TypeCode.Byte: @@ -347,48 +353,46 @@ internal static string GetCallSignature(IntPtr method_handle) case TypeCode.Boolean: // Enums types have the same code as their underlying numeric types if (t.IsEnum) - res[charIndex++] = 'j'; + res[c] = 'j'; else - res[charIndex++] = 'i'; + res[c] = 'i'; break; case TypeCode.Int64: case TypeCode.UInt64: // Enums types have the same code as their underlying numeric types if (t.IsEnum) - res[charIndex++] = 'k'; + res[c] = 'k'; else - res[charIndex++] = 'l'; + res[c] = 'l'; break; case TypeCode.Single: - res[charIndex++] = 'f'; + res[c] = 'f'; break; case TypeCode.Double: - res[charIndex++] = 'd'; + res[c] = 'd'; break; case TypeCode.String: - res[charIndex++] = 's'; + res[c] = 's'; break; default: if (t == typeof(IntPtr)) { - res[charIndex++] = 'i'; + res[c] = 'i'; } else if (t == typeof(Uri)) { - res[charIndex++] = 'u'; + res[c] = 'u'; } else { if (t.IsValueType) throw new NotSupportedException("ValueType arguments are not supported."); - res[charIndex++] = 'o'; + res[c] = 'o'; } break; } } - - return charIndex == 0 ? string.Empty : new string(res, 0, charIndex); - + return new string(res); } internal static void SetupJSContinuation(Task task, JSObject cont_obj) { From daa02cc3cd37c8ccf62fbf4f88819c1a6dd2a70b Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 07:20:14 +0200 Subject: [PATCH 32/99] Add project references so the Interop.JavaScript project builds --- .../Browser/System.Runtime.Interop.JavaScript.csproj | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj index 1317085d0db2fe..9131e4fffc356d 100644 --- a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj +++ b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj @@ -13,6 +13,13 @@ $(DefineConstants);TargetsBrowser + + + + + + + From d45f9a9f1dd4237ebf2730775b207b77c92f7d08 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 10:18:34 +0200 Subject: [PATCH 33/99] Coding Style update --- .../src/Interop/Browser/Interop.Runtime.cs | 145 +++++++++--------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index cc4cfe46375e74..043d661d11ad90 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -17,42 +17,42 @@ internal static partial class Interop internal static partial class Runtime { [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern string InvokeJS(string str, out int exceptional_result); + internal static extern string InvokeJS(string str, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object CompileFunction(string str, out int exceptional_result); + internal static extern object CompileFunction(string str, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object InvokeJSWithArgs(int js_obj_handle, string method, object?[] _params, out int exceptional_result); + internal static extern object InvokeJSWithArgs(int jsObjHandle, string method, object?[] parms, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object GetObjectProperty(int js_obj_handle, string propertyName, out int exceptional_result); + internal static extern object GetObjectProperty(int jsObjHandle, string propertyName, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object SetObjectProperty(int js_obj_handle, string propertyName, object value, bool createIfNotExists, bool hasOwnProperty, out int exceptional_result); + internal static extern object SetObjectProperty(int jsObjHandle, string propertyName, object value, bool createIfNotExists, bool hasOwnProperty, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object GetByIndex(int js_obj_handle, int index, out int exceptional_result); + internal static extern object GetByIndex(int jsObjHandle, int index, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object SetByIndex(int js_obj_handle, int index, object? value, out int exceptional_result); + internal static extern object SetByIndex(int jsObjHandle, int index, object? value, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object GetGlobalObject(string? globalName, out int exceptional_result); + internal static extern object GetGlobalObject(string? globalName, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object ReleaseHandle(int js_obj_handle, out int exceptional_result); + internal static extern object ReleaseHandle(int jsObjHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object ReleaseObject(int js_obj_handle, out int exceptional_result); + internal static extern object ReleaseObject(int jsObjHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object NewObjectJS(int js_obj_handle, object[] _params, out int exceptional_result); + internal static extern object NewObjectJS(int jsObjHandle, object[] parms, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object BindCoreObject(int js_obj_handle, int gc_handle, out int exceptional_result); + internal static extern object BindCoreObject(int jsObjHandle, int gcHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object BindHostObject(int js_obj_handle, int gc_handle, out int exceptional_result); + internal static extern object BindHostObject(int jsObjHandle, int gcHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object New(string className, object[] _params, out int exceptional_result); + internal static extern object New(string className, object[] parms, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object TypedArrayToArray(int js_obj_handle, out int exceptional_result); + internal static extern object TypedArrayToArray(int jsObjHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object TypedArrayCopyTo(int js_obj_handle, int array_ptr, int begin, int end, int bytes_per_element, out int exceptional_result); + internal static extern object TypedArrayCopyTo(int jsObjHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object TypedArrayFrom(int array_ptr, int begin, int end, int bytes_per_element, int type, out int exceptional_result); + internal static extern object TypedArrayFrom(int arrayPtr, int begin, int end, int bytesPerElement, int type, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object TypedArrayCopyFrom(int js_obj_handle, int array_ptr, int begin, int end, int bytes_per_element, out int exceptional_result); + internal static extern object TypedArrayCopyFrom(int jsObjHandle, int arrayPtr, int begin, int end, int bytesPerElement, out int exceptionalResult); // / // / Execute the provided string in the JavaScript context @@ -75,98 +75,97 @@ public static string InvokeJS(string str) return res as Interop.JavaScript.Function; } - private static Dictionary bound_objects = new Dictionary(); - private static Dictionary raw_to_js = new Dictionary(); + private static Dictionary _boundObjects = new Dictionary(); + private static Dictionary _rawToJS = new Dictionary(); - public static int New(params object[] _params) + public static int New(params object[] parms) { - var res = New(typeof(T).Name, _params, out int exception); + var res = New(typeof(T).Name, parms, out int exception); if (exception != 0) throw new JSException((string)res); return (int)res; } - public static int New(string hostClassName, params object[] _params) + public static int New(string hostClassName, params object[] parms) { - var res = New(hostClassName, _params, out int exception); + var res = New(hostClassName, parms, out int exception); if (exception != 0) throw new JSException((string)res); return (int)res; } - public static JSObject? NewJSObject(JSObject? js_func_ptr = null, params object[] _params) + public static JSObject? NewJSObject(JSObject? jsFuncPtr = null, params object[] parms) { - var res = NewObjectJS(js_func_ptr?.JSHandle ?? 0, _params, out int exception); + var res = NewObjectJS(jsFuncPtr?.JSHandle ?? 0, parms, out int exception); if (exception != 0) throw new JSException((string)res); return res as JSObject; } - internal static int BindJSObject(int js_id, Type mappedType) + internal static int BindJSObject(int jsId, Type mappedType) { - if (!bound_objects.TryGetValue(js_id, out JSObject? obj)) + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) { if (mappedType != null) { - return BindJSType(js_id, mappedType); + return BindJSType(jsId, mappedType); } else { - bound_objects[js_id] = obj = new JSObject((IntPtr)js_id); + _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); } } return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - internal static int BindCoreCLRObject(int js_id, int gcHandle) + internal static int BindCoreCLRObject(int jsId, int gcHandle) { - //Console.WriteLine ($"Registering CLR Object {js_id} with handle {gcHandle}"); GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj = h.Target as JSObject; - if (bound_objects.TryGetValue(js_id, out var existingObj)) + if (_boundObjects.TryGetValue(jsId, out var existingObj)) { if (existingObj?.Handle != h && h.IsAllocated) - throw new JSException($"Multiple handles pointing at js_id: {js_id}"); + throw new JSException($"Multiple handles pointing at js_id: {jsId}"); obj = existingObj; } else - bound_objects[js_id] = obj; + _boundObjects[jsId] = obj; return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - internal static int BindJSType(int js_id, Type mappedType) + internal static int BindJSType(int jsId, Type mappedType) { - if (!bound_objects.TryGetValue(js_id, out JSObject? obj)) + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) { var jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, null, new Type[] { typeof(IntPtr) }, null); - bound_objects[js_id] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)js_id }); + _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); } return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - internal static int UnBindJSObject(int js_id) + internal static int UnBindJSObject(int jsId) { - if (bound_objects.TryGetValue(js_id, out var obj)) + if (_boundObjects.TryGetValue(jsId, out var obj)) { - bound_objects.Remove(js_id); + _boundObjects.Remove(jsId); return obj == null ? 0 : (int)(IntPtr)obj.Handle; } return 0; } - internal static void UnBindJSObjectAndFree(int js_id) + internal static void UnBindJSObjectAndFree(int jsId) { - if (bound_objects.TryGetValue(js_id, out var obj)) + if (_boundObjects.TryGetValue(jsId, out var obj)) { - if (bound_objects[js_id] != null) + if (_boundObjects[jsId] != null) { - //bound_objects[js_id].RawObject = null; - bound_objects.Remove(js_id); + //bound_objects[jsIs].RawObject = null; + _boundObjects.Remove(jsId); } if (obj != null) { @@ -187,7 +186,7 @@ internal static void UnBindRawJSObjectAndFree(int gcHandle) JSObject? obj = h.Target as JSObject; if (obj?.RawObject != null) { - raw_to_js.Remove(obj.RawObject); + _rawToJS.Remove(obj.RawObject); int exception; ReleaseHandle(obj.JSHandle, out exception); @@ -207,10 +206,10 @@ internal static void UnBindRawJSObjectAndFree(int gcHandle) public static void FreeObject(object obj) { - if (raw_to_js.TryGetValue(obj, out JSObject? jsobj)) + if (_rawToJS.TryGetValue(obj, out JSObject? jsobj)) { //raw_to_js [obj].RawObject = null; - raw_to_js.Remove(obj); + _rawToJS.Remove(obj); if (jsobj != null) { int exception; @@ -231,7 +230,7 @@ public static void FreeObject(object obj) } } - internal static object CreateTaskSource(int js_id) + internal static object CreateTaskSource(int jsId) { return new TaskCompletionSource(); } @@ -246,34 +245,34 @@ internal static void SetTaskSourceFailure(TaskCompletionSource tcs, stri tcs.SetException(new JSException(reason)); } - internal static int GetTaskAndBind(TaskCompletionSource tcs, int js_id) + internal static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) { - return BindExistingObject(tcs.Task, js_id); + return BindExistingObject(tcs.Task, jsId); } - internal static int BindExistingObject(object raw_obj, int js_id) + internal static int BindExistingObject(object rawObj, int jsId) { - JSObject? obj = raw_obj as JSObject; + JSObject? obj = rawObj as JSObject; - if (obj == null && !raw_to_js.TryGetValue(raw_obj, out obj)) - raw_to_js[raw_obj] = obj = new JSObject(js_id, raw_obj); + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - internal static int GetJSObjectId(object raw_obj) + internal static int GetJSObjectId(object rawObj) { - JSObject? obj = raw_obj as JSObject; + JSObject? obj = rawObj as JSObject; - if (obj == null && !raw_to_js.TryGetValue(raw_obj, out obj)) + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) return -1; return obj != null ? obj.JSHandle : -1; } - internal static object? GetMonoObject(int gc_handle) + internal static object? GetMonoObject(int gcHandle) { - GCHandle h = (GCHandle)(IntPtr)gc_handle; + GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? o = h.Target as JSObject; if (o != null && o.RawObject != null) return o.RawObject; @@ -323,10 +322,10 @@ internal struct IntPtrAndHandle internal RuntimeMethodHandle handle; } - internal static string GetCallSignature(IntPtr method_handle) + internal static string GetCallSignature(IntPtr methodHandle) { IntPtrAndHandle tmp = default(IntPtrAndHandle); - tmp.ptr = method_handle; + tmp.ptr = methodHandle; var mb = MethodBase.GetMethodFromHandle(tmp.handle); if (mb == null) @@ -394,7 +393,7 @@ internal static string GetCallSignature(IntPtr method_handle) } return new string(res); } - internal static void SetupJSContinuation(Task task, JSObject cont_obj) + internal static void SetupJSContinuation(Task task, JSObject continuationObj) { if (task.IsCompleted) Complete(); @@ -410,22 +409,22 @@ void Complete() var resultProperty = task.GetType().GetProperty("Result"); if (resultProperty == null) - cont_obj.Invoke("resolve", Array.Empty()); + continuationObj.Invoke("resolve", Array.Empty()); else - cont_obj.Invoke("resolve", resultProperty.GetValue(task) ?? Array.Empty()); + continuationObj.Invoke("resolve", resultProperty.GetValue(task) ?? Array.Empty()); } else { - cont_obj.Invoke("reject", task.Exception.ToString()); + continuationObj.Invoke("reject", task.Exception.ToString()); } } catch (Exception e) { - cont_obj.Invoke("reject", e.ToString()); + continuationObj.Invoke("reject", e.ToString()); } finally { - cont_obj.Dispose(); + continuationObj.Dispose(); FreeObject(task); } } @@ -496,8 +495,8 @@ internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) var span = new ReadOnlySpan(p, len); // Send it to JS - var js_dump = (JSObject)Runtime.GetGlobalObject("Module"); - //js_dump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); + var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); + //jsDump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); } } @@ -505,8 +504,8 @@ internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) internal static void DumpCoverageProfileData(string data, string s) { // Send it to JS - var js_dump = (JSObject)Runtime.GetGlobalObject("Module"); - js_dump.SetObjectProperty("coverage_profile_data", data); + var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); + jsDump.SetObjectProperty("coverage_profile_data", data); } } } From c1190d2c3ee14f38e7fc4ca0e2d671a2f7e3e606 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 11:22:05 +0200 Subject: [PATCH 34/99] Split the runtime methods into two modules. - .Api contains the methods that are only used internally from JavaScript bindings code. --- .../Interop/Browser/Interop.Runtime.Api.cs | 386 ++++++++++++++++++ .../src/Interop/Browser/Interop.Runtime.cs | 373 +---------------- .../System.Runtime.Interop.JavaScript.csproj | 1 + .../src/System.Net.Http.csproj | 2 + 4 files changed, 390 insertions(+), 372 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs new file mode 100644 index 00000000000000..10ef0bb1df5e72 --- /dev/null +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs @@ -0,0 +1,386 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using JSObject = Interop.JavaScript.JSObject; +using JSException = Interop.JavaScript.JSException; + +internal static partial class Interop +{ + internal static partial class Runtime + { + private static Dictionary _boundObjects = new Dictionary(); + private static Dictionary _rawToJS = new Dictionary(); + + internal static int BindJSObject(int jsId, Type mappedType) + { + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + if (mappedType != null) + { + return BindJSType(jsId, mappedType); + } + else + { + _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); + } + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + internal static int BindCoreCLRObject(int jsId, int gcHandle) + { + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? obj = h.Target as JSObject; + + if (_boundObjects.TryGetValue(jsId, out var existingObj)) + { + if (existingObj?.Handle != h && h.IsAllocated) + throw new JSException($"Multiple handles pointing at js_id: {jsId}"); + + obj = existingObj; + } + else + _boundObjects[jsId] = obj; + + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + internal static int BindJSType(int jsId, Type mappedType) + { + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + var jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, + null, new Type[] { typeof(IntPtr) }, null); + _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + internal static int UnBindJSObject(int jsId) + { + if (_boundObjects.TryGetValue(jsId, out var obj)) + { + _boundObjects.Remove(jsId); + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + + return 0; + } + + internal static void UnBindJSObjectAndFree(int jsId) + { + if (_boundObjects.TryGetValue(jsId, out var obj)) + { + if (_boundObjects[jsId] != null) + { + //bound_objects[jsIs].RawObject = null; + _boundObjects.Remove(jsId); + } + if (obj != null) + { + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + obj.Handle.Free(); + } + } + + } + internal static void UnBindRawJSObjectAndFree(int gcHandle) + { + + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? obj = h.Target as JSObject; + if (obj?.RawObject != null) + { + _rawToJS.Remove(obj.RawObject); + + int exception; + ReleaseHandle(obj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' mono '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); + + // Calling Release Handle above only removes the reference from the JavaScript side but does not + // release the bridged JSObject associated with the raw object so we have to do that ourselves. + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + + obj.Handle.Free(); + } + + } + internal static object CreateTaskSource(int jsId) + { + return new TaskCompletionSource(); + } + + internal static void SetTaskSourceResult(TaskCompletionSource tcs, object result) + { + tcs.SetResult(result); + } + + internal static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) + { + tcs.SetException(new JSException(reason)); + } + + internal static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) + { + return BindExistingObject(tcs.Task, jsId); + } + + internal static int BindExistingObject(object rawObj, int jsId) + { + JSObject? obj = rawObj as JSObject; + + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); + + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + internal static int GetJSObjectId(object rawObj) + { + JSObject? obj = rawObj as JSObject; + + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + return -1; + + return obj != null ? obj.JSHandle : -1; + } + + internal static object? GetMonoObject(int gcHandle) + { + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? o = h.Target as JSObject; + if (o != null && o.RawObject != null) + return o.RawObject; + return o; + } + + internal static object BoxInt(int i) + { + return i; + } + internal static object BoxDouble(double d) + { + return d; + } + + internal static object BoxBool(int b) + { + return b == 0 ? false : true; + } + + internal static bool IsSimpleArray(object a) + { + if (a is Array arr) + { + if (arr.Rank == 1 && arr.GetLowerBound(0) == 0) + return true; + } + return false; + + } + internal static object? GetCoreType(string coreObj) + { + Assembly asm = typeof(Runtime).Assembly; + Type? type = asm.GetType(coreObj); + return type; + + } + + [StructLayout(LayoutKind.Explicit)] + internal struct IntPtrAndHandle + { + [FieldOffset(0)] + internal IntPtr ptr; + + [FieldOffset(0)] + internal RuntimeMethodHandle handle; + } + + internal static string GetCallSignature(IntPtr methodHandle) + { + IntPtrAndHandle tmp = default(IntPtrAndHandle); + tmp.ptr = methodHandle; + + var mb = MethodBase.GetMethodFromHandle(tmp.handle); + if (mb == null) + return string.Empty; + + ParameterInfo[] parms = mb.GetParameters(); + var parmsLength = parms.Length; + if (parmsLength == 0) + return string.Empty; + + var res = new char[parmsLength]; + + for (int c = 0; c < parmsLength; c++) + { + var t = parms[c].ParameterType; + switch (Type.GetTypeCode(t)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Boolean: + // Enums types have the same code as their underlying numeric types + if (t.IsEnum) + res[c] = 'j'; + else + res[c] = 'i'; + break; + case TypeCode.Int64: + case TypeCode.UInt64: + // Enums types have the same code as their underlying numeric types + if (t.IsEnum) + res[c] = 'k'; + else + res[c] = 'l'; + break; + case TypeCode.Single: + res[c] = 'f'; + break; + case TypeCode.Double: + res[c] = 'd'; + break; + case TypeCode.String: + res[c] = 's'; + break; + default: + if (t == typeof(IntPtr)) + { + res[c] = 'i'; + } + else if (t == typeof(Uri)) + { + res[c] = 'u'; + } + else + { + if (t.IsValueType) + throw new NotSupportedException("ValueType arguments are not supported."); + res[c] = 'o'; + } + break; + } + } + return new string(res); + } + internal static void SetupJSContinuation(Task task, JSObject continuationObj) + { + if (task.IsCompleted) + Complete(); + else + task.GetAwaiter().OnCompleted(Complete); + + void Complete() + { + try + { + if (task.Exception == null) + { + var resultProperty = task.GetType().GetProperty("Result"); + + if (resultProperty == null) + continuationObj.Invoke("resolve", Array.Empty()); + else + continuationObj.Invoke("resolve", resultProperty.GetValue(task) ?? Array.Empty()); + } + else + { + continuationObj.Invoke("reject", task.Exception.ToString()); + } + } + catch (Exception e) + { + continuationObj.Invoke("reject", e.ToString()); + } + finally + { + continuationObj.Dispose(); + FreeObject(task); + } + } + } + + internal static string ObjectToString(object o) + { + + //if (o == null) + // return null; + //if (o is Enum) + // return EnumToExportContract((Enum)o).ToString(); + + return o.ToString() ?? string.Empty; + } + + internal static double GetDateValue(object dtv) + { + if (dtv == null) + throw new ArgumentNullException(nameof(dtv), "Value can not be null"); + if (!(dtv is DateTime)) + { + throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); + } + var dt = (DateTime)dtv; + if (dt.Kind == DateTimeKind.Local) + dt = dt.ToUniversalTime(); + else if (dt.Kind == DateTimeKind.Unspecified) + dt = new DateTime(dt.Ticks, DateTimeKind.Utc); + return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); + } + + internal static DateTime CreateDateTime(double ticks) + { + var unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); + return unixTime.DateTime; + } + internal static Uri CreateUri(string uri) + { + return new Uri(uri); + } + + // + // Can be called by the app to stop profiling + // + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static void StopProfile() + { + } + + // Called by the AOT profiler to save profile data into Module.aot_profile_data + internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) + { + var arr = new byte[len]; + fixed (void* p = &buf) + { + var span = new ReadOnlySpan(p, len); + + // Send it to JS + var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); + //jsDump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); + } + } + + // Called by the coverage profiler to save profile data into Module.coverage_profile_data + internal static void DumpCoverageProfileData(string data, string s) + { + // Send it to JS + var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); + jsDump.SetObjectProperty("coverage_profile_data", data); + } + } +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 043d661d11ad90..1bf488b8267265 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -75,9 +75,6 @@ public static string InvokeJS(string str) return res as Interop.JavaScript.Function; } - private static Dictionary _boundObjects = new Dictionary(); - private static Dictionary _rawToJS = new Dictionary(); - public static int New(params object[] parms) { var res = New(typeof(T).Name, parms, out int exception); @@ -102,108 +99,6 @@ public static int New(string hostClassName, params object[] parms) return res as JSObject; } - internal static int BindJSObject(int jsId, Type mappedType) - { - if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) - { - if (mappedType != null) - { - return BindJSType(jsId, mappedType); - } - else - { - _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); - } - } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - - internal static int BindCoreCLRObject(int jsId, int gcHandle) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? obj = h.Target as JSObject; - - if (_boundObjects.TryGetValue(jsId, out var existingObj)) - { - if (existingObj?.Handle != h && h.IsAllocated) - throw new JSException($"Multiple handles pointing at js_id: {jsId}"); - - obj = existingObj; - } - else - _boundObjects[jsId] = obj; - - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - - internal static int BindJSType(int jsId, Type mappedType) - { - if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) - { - var jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, - null, new Type[] { typeof(IntPtr) }, null); - _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); - } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - - internal static int UnBindJSObject(int jsId) - { - if (_boundObjects.TryGetValue(jsId, out var obj)) - { - _boundObjects.Remove(jsId); - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - - return 0; - } - - internal static void UnBindJSObjectAndFree(int jsId) - { - if (_boundObjects.TryGetValue(jsId, out var obj)) - { - if (_boundObjects[jsId] != null) - { - //bound_objects[jsIs].RawObject = null; - _boundObjects.Remove(jsId); - } - if (obj != null) - { - obj.JSHandle = -1; - obj.IsDisposed = true; - obj.RawObject = null; - obj.Handle.Free(); - } - } - - } - - - internal static void UnBindRawJSObjectAndFree(int gcHandle) - { - - GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? obj = h.Target as JSObject; - if (obj?.RawObject != null) - { - _rawToJS.Remove(obj.RawObject); - - int exception; - ReleaseHandle(obj.JSHandle, out exception); - if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' mono '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); - - // Calling Release Handle above only removes the reference from the JavaScript side but does not - // release the bridged JSObject associated with the raw object so we have to do that ourselves. - obj.JSHandle = -1; - obj.IsDisposed = true; - obj.RawObject = null; - - obj.Handle.Free(); - } - - } - public static void FreeObject(object obj) { if (_rawToJS.TryGetValue(obj, out JSObject? jsobj)) @@ -230,207 +125,7 @@ public static void FreeObject(object obj) } } - internal static object CreateTaskSource(int jsId) - { - return new TaskCompletionSource(); - } - - internal static void SetTaskSourceResult(TaskCompletionSource tcs, object result) - { - tcs.SetResult(result); - } - - internal static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) - { - tcs.SetException(new JSException(reason)); - } - - internal static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) - { - return BindExistingObject(tcs.Task, jsId); - } - - internal static int BindExistingObject(object rawObj, int jsId) - { - JSObject? obj = rawObj as JSObject; - - if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) - _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); - - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - - internal static int GetJSObjectId(object rawObj) - { - JSObject? obj = rawObj as JSObject; - - if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) - return -1; - - return obj != null ? obj.JSHandle : -1; - } - - internal static object? GetMonoObject(int gcHandle) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? o = h.Target as JSObject; - if (o != null && o.RawObject != null) - return o.RawObject; - return o; - } - - internal static object BoxInt(int i) - { - return i; - } - internal static object BoxDouble(double d) - { - return d; - } - - internal static object BoxBool(int b) - { - return b == 0 ? false : true; - } - - internal static bool IsSimpleArray(object a) - { - if (a is Array arr) - { - if (arr.Rank == 1 && arr.GetLowerBound(0) == 0) - return true; - } - return false; - - } - - internal static object? GetCoreType(string coreObj) - { - Assembly asm = typeof(Runtime).Assembly; - Type? type = asm.GetType(coreObj); - return type; - - } - - [StructLayout(LayoutKind.Explicit)] - internal struct IntPtrAndHandle - { - [FieldOffset(0)] - internal IntPtr ptr; - - [FieldOffset(0)] - internal RuntimeMethodHandle handle; - } - - internal static string GetCallSignature(IntPtr methodHandle) - { - IntPtrAndHandle tmp = default(IntPtrAndHandle); - tmp.ptr = methodHandle; - - var mb = MethodBase.GetMethodFromHandle(tmp.handle); - if (mb == null) - return string.Empty; - - ParameterInfo[] parms = mb.GetParameters(); - var parmsLength = parms.Length; - if (parmsLength == 0) - return string.Empty; - - var res = new char[parmsLength]; - - for (int c = 0; c < parmsLength; c++) - { - var t = parms[c].ParameterType; - switch (Type.GetTypeCode(t)) - { - case TypeCode.Byte: - case TypeCode.SByte: - case TypeCode.Int16: - case TypeCode.UInt16: - case TypeCode.Int32: - case TypeCode.UInt32: - case TypeCode.Boolean: - // Enums types have the same code as their underlying numeric types - if (t.IsEnum) - res[c] = 'j'; - else - res[c] = 'i'; - break; - case TypeCode.Int64: - case TypeCode.UInt64: - // Enums types have the same code as their underlying numeric types - if (t.IsEnum) - res[c] = 'k'; - else - res[c] = 'l'; - break; - case TypeCode.Single: - res[c] = 'f'; - break; - case TypeCode.Double: - res[c] = 'd'; - break; - case TypeCode.String: - res[c] = 's'; - break; - default: - if (t == typeof(IntPtr)) - { - res[c] = 'i'; - } - else if (t == typeof(Uri)) - { - res[c] = 'u'; - } - else - { - if (t.IsValueType) - throw new NotSupportedException("ValueType arguments are not supported."); - res[c] = 'o'; - } - break; - } - } - return new string(res); - } - internal static void SetupJSContinuation(Task task, JSObject continuationObj) - { - if (task.IsCompleted) - Complete(); - else - task.GetAwaiter().OnCompleted(Complete); - - void Complete() - { - try - { - if (task.Exception == null) - { - var resultProperty = task.GetType().GetProperty("Result"); - - if (resultProperty == null) - continuationObj.Invoke("resolve", Array.Empty()); - else - continuationObj.Invoke("resolve", resultProperty.GetValue(task) ?? Array.Empty()); - } - else - { - continuationObj.Invoke("reject", task.Exception.ToString()); - } - } - catch (Exception e) - { - continuationObj.Invoke("reject", e.ToString()); - } - finally - { - continuationObj.Dispose(); - FreeObject(task); - } - } - } - - internal static object GetGlobalObject(string? str = null) + public static object GetGlobalObject(string? str = null) { int exception; var globalHandle = Runtime.GetGlobalObject(str, out exception); @@ -441,71 +136,5 @@ internal static object GetGlobalObject(string? str = null) return globalHandle; } - internal static string ObjectToString(object o) - { - - //if (o == null) - // return null; - //if (o is Enum) - // return EnumToExportContract((Enum)o).ToString(); - - return o.ToString() ?? string.Empty; - } - - internal static double GetDateValue(object dtv) - { - if (dtv == null) - throw new ArgumentNullException(nameof(dtv), "Value can not be null"); - if (!(dtv is DateTime)) - { - throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); - } - var dt = (DateTime)dtv; - if (dt.Kind == DateTimeKind.Local) - dt = dt.ToUniversalTime(); - else if (dt.Kind == DateTimeKind.Unspecified) - dt = new DateTime(dt.Ticks, DateTimeKind.Utc); - return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); - } - - internal static DateTime CreateDateTime(double ticks) - { - var unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); - return unixTime.DateTime; - } - internal static Uri CreateUri(string uri) - { - return new Uri(uri); - } - - // - // Can be called by the app to stop profiling - // - [MethodImplAttribute(MethodImplOptions.NoInlining)] - public static void StopProfile() - { - } - - // Called by the AOT profiler to save profile data into Module.aot_profile_data - internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) - { - var arr = new byte[len]; - fixed (void* p = &buf) - { - var span = new ReadOnlySpan(p, len); - - // Send it to JS - var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); - //jsDump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); - } - } - - // Called by the coverage profiler to save profile data into Module.coverage_profile_data - internal static void DumpCoverageProfileData(string data, string s) - { - // Send it to JS - var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); - jsDump.SetObjectProperty("coverage_profile_data", data); - } } } diff --git a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj index 9131e4fffc356d..46ea2ef5d49075 100644 --- a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj +++ b/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj @@ -23,6 +23,7 @@ + diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 15ec0b54dfcedd..02623c120702e8 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -741,6 +741,8 @@ + Date: Mon, 4 May 2020 11:33:13 +0200 Subject: [PATCH 35/99] Move System.Runtime.Interop.JavaScript to \src\libraries - Address review comments --- .../System.Runtime.Interop.JavaScript.csproj | 10 +++++----- .../Browser => }/System.Runtime.Interop.JavaScript.sln | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename src/libraries/{Common/src/Interop/Browser => }/System.Runtime.Interop.JavaScript.csproj (85%) rename src/libraries/{Common/src/Interop/Browser => }/System.Runtime.Interop.JavaScript.sln (100%) diff --git a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj b/src/libraries/System.Runtime.Interop.JavaScript.csproj similarity index 85% rename from src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj rename to src/libraries/System.Runtime.Interop.JavaScript.csproj index 46ea2ef5d49075..4ffab78a7f87f7 100644 --- a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.csproj +++ b/src/libraries/System.Runtime.Interop.JavaScript.csproj @@ -14,11 +14,11 @@ $(DefineConstants);TargetsBrowser - - - - - + + + + + diff --git a/src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.sln b/src/libraries/System.Runtime.Interop.JavaScript.sln similarity index 100% rename from src/libraries/Common/src/Interop/Browser/System.Runtime.Interop.JavaScript.sln rename to src/libraries/System.Runtime.Interop.JavaScript.sln From 5845ee384ba43060b6990bcedf0b82a31c6b728f Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 11:56:01 +0200 Subject: [PATCH 36/99] Change preprocessor to upper case TARGETS_BROWSER - Address review comments --- src/libraries/System.Net.Http/src/System.Net.Http.csproj | 2 +- .../System.Net.Http/src/System/Net/Http/HttpClientHandler.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 02623c120702e8..5aeef21b562404 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -14,7 +14,7 @@ $(DefineConstants);SYSNETHTTP_NO_OPENSSL - $(DefineConstants);BROWSER_DOES_NOT_SUPPORT_QUIC;TargetsBrowser + $(DefineConstants);BROWSER_DOES_NOT_SUPPORT_QUIC;TARGETS_BROWSER diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index b8c1b84fbf55b8..d46dd98a410b80 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -13,7 +13,7 @@ namespace System.Net.Http { public partial class HttpClientHandler : HttpMessageHandler { -#if TargetsBrowser +#if TARGETS_BROWSER private readonly BrowserHttpHandler _socketsHttpHandler; #else private readonly SocketsHttpHandler _socketsHttpHandler; @@ -23,7 +23,7 @@ public partial class HttpClientHandler : HttpMessageHandler public HttpClientHandler() { -#if TargetsBrowser +#if TARGETS_BROWSER _socketsHttpHandler = new BrowserHttpHandler(); #else _socketsHttpHandler = new SocketsHttpHandler(); From 3c75797fbcaeb4daa1f5c28b5c2d07d57904de06 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 18:14:35 +0200 Subject: [PATCH 37/99] Update src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs Co-authored-by: Marek Safar --- .../Common/src/Interop/Browser/Interop.Runtime.Api.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs index 10ef0bb1df5e72..015f73dd0895d7 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs @@ -331,7 +331,8 @@ internal static double GetDateValue(object dtv) { if (dtv == null) throw new ArgumentNullException(nameof(dtv), "Value can not be null"); - if (!(dtv is DateTime)) + if (!(dtv is DateTime dt)) + throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); { throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); } From c7c80bc74920869b501a5fe3fc8e9151b30f2861 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 18:14:54 +0200 Subject: [PATCH 38/99] Update src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs Co-authored-by: Marek Safar --- src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs index 015f73dd0895d7..9588daf5b30141 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs @@ -154,7 +154,7 @@ internal static int GetJSObjectId(object rawObj) if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) return -1; - return obj != null ? obj.JSHandle : -1; + return obj?.JSHandle ?? -1; } internal static object? GetMonoObject(int gcHandle) From 568211efab6dc92e914f21aa6e45d9da8646b4b3 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 4 May 2020 18:15:08 +0200 Subject: [PATCH 39/99] Update src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs Co-authored-by: Marek Safar --- .../Common/src/Interop/Browser/Interop.Runtime.Api.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs index 9588daf5b30141..92c9f759bae499 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs @@ -161,9 +161,7 @@ internal static int GetJSObjectId(object rawObj) { GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? o = h.Target as JSObject; - if (o != null && o.RawObject != null) - return o.RawObject; - return o; + return o?.RawObject ?? null; } internal static object BoxInt(int i) From 282efa03cdd0f2a78331956c4cbc24365f8ba5e8 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 5 May 2020 08:31:28 +0200 Subject: [PATCH 40/99] Rename from .Api to .Bridge to address review comments --- .../{Interop.Runtime.Api.cs => Interop.Runtime.Bridge.cs} | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) rename src/libraries/Common/src/Interop/Browser/{Interop.Runtime.Api.cs => Interop.Runtime.Bridge.cs} (97%) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs similarity index 97% rename from src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs rename to src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 92c9f759bae499..26f429da550a8d 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Api.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -38,7 +38,7 @@ internal static int BindJSObject(int jsId, Type mappedType) internal static int BindCoreCLRObject(int jsId, int gcHandle) { GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? obj = h.Target as JSObject; + JSObject? obj; if (_boundObjects.TryGetValue(jsId, out var existingObj)) { @@ -48,7 +48,7 @@ internal static int BindCoreCLRObject(int jsId, int gcHandle) obj = existingObj; } else - _boundObjects[jsId] = obj; + obj = h.Target as JSObject; return obj == null ? 0 : (int)(IntPtr)obj.Handle; } @@ -331,10 +331,6 @@ internal static double GetDateValue(object dtv) throw new ArgumentNullException(nameof(dtv), "Value can not be null"); if (!(dtv is DateTime dt)) throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); - { - throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); - } - var dt = (DateTime)dtv; if (dt.Kind == DateTimeKind.Local) dt = dt.ToUniversalTime(); else if (dt.Kind == DateTimeKind.Unspecified) From fa165aaff5022da93e92ed55ac0800c8fdd0bc0b Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 5 May 2020 09:27:16 +0200 Subject: [PATCH 41/99] Modify .csproj files to reflect .Bridge.cs change. --- src/libraries/System.Net.Http/src/System.Net.Http.csproj | 2 +- src/libraries/System.Runtime.Interop.JavaScript.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 5aeef21b562404..1d05165e640b59 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -741,7 +741,7 @@ - diff --git a/src/libraries/System.Runtime.Interop.JavaScript.csproj b/src/libraries/System.Runtime.Interop.JavaScript.csproj index 4ffab78a7f87f7..354e07cc869e57 100644 --- a/src/libraries/System.Runtime.Interop.JavaScript.csproj +++ b/src/libraries/System.Runtime.Interop.JavaScript.csproj @@ -23,7 +23,7 @@ - + From b57fef9626da8bf6c9cb39b6eec24b50ebdf1ea9 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 5 May 2020 11:51:39 +0200 Subject: [PATCH 42/99] Remove unnecessary constant --- src/libraries/System.Runtime.Interop.JavaScript.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Interop.JavaScript.csproj b/src/libraries/System.Runtime.Interop.JavaScript.csproj index 354e07cc869e57..2cc201ddb914d4 100644 --- a/src/libraries/System.Runtime.Interop.JavaScript.csproj +++ b/src/libraries/System.Runtime.Interop.JavaScript.csproj @@ -11,7 +11,7 @@ enable - $(DefineConstants);TargetsBrowser + $(DefineConstants) From a4753c097ca552dd67736b83607c2e08b8b8fff3 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 6 May 2020 06:07:29 +0200 Subject: [PATCH 43/99] Add docs --- .../Browser/Interop.JavaScript.Function.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs index 4d6652c6d6b4c6..6343e5f808ae03 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -5,6 +5,9 @@ using System; internal static partial class Interop { + /// + /// The Function constructor creates a new Function object. Calling the constructor directly can create functions dynamically, but suffers from security and similar (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor allows executing code in the global scope, prompting better programming habits and allowing for more efficient code minification. + /// internal static partial class JavaScript { public class Function : CoreObject @@ -15,11 +18,28 @@ public Function(params object[] args) : base(Runtime.New(args)) internal Function(IntPtr js_handle) : base(js_handle) { } - + /// + /// The Apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object). + /// + /// The apply. + /// This argument. + /// Arguments. public object Apply(object? thisArg = null, object[]? argsArray = null) => Invoke("apply", thisArg, argsArray); + /// + /// Creates a new Function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. + /// + /// The bind. + /// This argument. + /// Arguments. public Function Bind(object? thisArg = null, object[]? argsArray = null) => (Function)Invoke("bind", thisArg, argsArray); + /// + /// Calls a function with a given `this` value and arguments provided individually. + /// + /// The result of calling the function with the specified `this` value and arguments. + /// Optional (null value). The value of this provided for the call to a function. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects. + /// Optional. Arguments for the function. public object Call(object? thisArg = null, params object[] argsArray) { object?[] argsList = new object[argsArray.Length + 1]; @@ -27,8 +47,6 @@ public object Call(object? thisArg = null, params object[] argsArray) System.Array.Copy(argsArray, 0, argsList, 1, argsArray.Length); return Invoke("call", argsList); } - - } } } From ee9c8d9f6c559180ce1f8da902cd2543d3a6a98d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 6 May 2020 06:08:28 +0200 Subject: [PATCH 44/99] Update Bridge link --- src/libraries/System.Net.Http/src/System.Net.Http.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 1d05165e640b59..1bdb7ecb76f5c5 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -742,7 +742,7 @@ + Link="Common\Interop\Browser\Interop.Runtime.Bridge.cs" /> Date: Wed, 6 May 2020 06:11:01 +0200 Subject: [PATCH 45/99] Collapse code as per review comment. --- .../Common/src/Interop/Browser/Interop.Runtime.Bridge.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 26f429da550a8d..462ed2eca6a350 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -180,13 +180,7 @@ internal static object BoxBool(int b) internal static bool IsSimpleArray(object a) { - if (a is Array arr) - { - if (arr.Rank == 1 && arr.GetLowerBound(0) == 0) - return true; - } - return false; - + return a is Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0; } internal static object? GetCoreType(string coreObj) { From 49193b875df4e1cf3dc0177c124460ef69dd7750 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 6 May 2020 09:19:44 +0200 Subject: [PATCH 46/99] Address review comments on `Task.ConfigureAwait(continueOnCapturedContext: true)`. - Added comment in source code. --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 55962334969b35..15aa75fd19fe4f 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -20,6 +20,14 @@ namespace System.Net.Http { + + // **Note** on `Task.ConfigureAwait(continueOnCapturedContext: true)` for the WebAssembly Browser. + // The current implementation of WebAssembly for the Browser does not have a SynchronizationContext nor a Scheduler + // thus forcing the callbacks to run on the main browser thread. When threading is eventually implemented using + // emscripten's threading model of remote worker threads, via SharedArrayBuffer, any API calls will have to be + // remoted back to the main thread. Most APIs only work on the main browser thread. + // During discussions the concensus has been that it will not matter right now which value is used for ConfigureAwait + // we should put this in place now. internal partial class BrowserHttpHandler : HttpMessageHandler { // This partial implementation contains members common to Browser WebAssembly running on .NET Core. @@ -186,14 +194,14 @@ private async Task DoFetch(HttpRequestMessage request, Canc { if (request.Content is StringContent) { - requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(false)); + requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: true)); } else { // 2.1.801 seems to have a problem with the line // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) // so we split it up into two lines. - var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false); + var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: true); using (Uint8Array uint8Buffer = Uint8Array.From(byteAsync)) { requestObject.SetObjectProperty("body", uint8Buffer); @@ -261,7 +269,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc if (response == null) throw new Exception("Internal error marshalling the response Promise from `fetch`."); - JSObject t = (JSObject)await response.ConfigureAwait(false); + JSObject t = (JSObject)await response.ConfigureAwait(continueOnCapturedContext: true); var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); @@ -324,7 +332,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc { tcs.SetException(exception); } - return await tcs.Task.ConfigureAwait(false); + return await tcs.Task.ConfigureAwait(continueOnCapturedContext: true); } } @@ -400,7 +408,7 @@ private async Task GetResponseData() return _data; } - using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(false)) + using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(continueOnCapturedContext: true)) { using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) { @@ -414,14 +422,14 @@ private async Task GetResponseData() protected override async Task CreateContentReadStreamAsync() { - byte[] data = await GetResponseData().ConfigureAwait(false); + byte[] data = await GetResponseData().ConfigureAwait(continueOnCapturedContext: true); return new MemoryStream(data, writable: false); } protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) { - byte[] data = await GetResponseData().ConfigureAwait(false); - await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(false); + byte[] data = await GetResponseData().ConfigureAwait(continueOnCapturedContext: true); + await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(continueOnCapturedContext: true); } protected internal override bool TryComputeLength(out long length) @@ -511,7 +519,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, try { var t = (Task)_reader.Invoke("read"); - using (var read = (JSObject)await t.ConfigureAwait(false)) + using (var read = (JSObject)await t.ConfigureAwait(continueOnCapturedContext: true)) { if ((bool)read.GetObjectProperty("done")) { From 1a655e5bed20ab148de668441b6c3d4913704447 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 6 May 2020 11:16:22 +0200 Subject: [PATCH 47/99] Address review comment about using PropertyInfo reflection. - GetMethod("get_Result") is less expensive to use. --- .../src/Interop/Browser/Interop.Runtime.Bridge.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 462ed2eca6a350..59999d29a59556 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -284,12 +284,17 @@ void Complete() { if (task.Exception == null) { - var resultProperty = task.GetType().GetProperty("Result"); - - if (resultProperty == null) - continuationObj.Invoke("resolve", Array.Empty()); + object? result; + Type task_type = task.GetType(); + if (task_type == typeof(Task)) + { + result = Array.Empty(); + } else - continuationObj.Invoke("resolve", resultProperty.GetValue(task) ?? Array.Empty()); + { + result = task_type.GetMethod("get_Result")?.Invoke(task, Array.Empty()); + } + continuationObj.Invoke("resolve", result); } else { From 463686704a477695f2fb4ba387ade1ab053b0d57 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 7 May 2020 10:52:24 +0200 Subject: [PATCH 48/99] Coding Style - Address review comments --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 15aa75fd19fe4f..e000ff8dbe1d94 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -31,8 +31,8 @@ namespace System.Net.Http internal partial class BrowserHttpHandler : HttpMessageHandler { // This partial implementation contains members common to Browser WebAssembly running on .NET Core. - private static readonly JSObject? fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); - private static readonly JSObject? window = (JSObject)Interop.Runtime.GetGlobalObject("window"); + private static readonly JSObject? s_fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); + private static readonly JSObject? s_window = (JSObject)Interop.Runtime.GetGlobalObject("window"); /// /// Gets whether the current Browser supports streaming responses @@ -264,7 +264,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc requestObject.Dispose(); - var response = fetch?.Invoke("apply", window, args) as Task; + var response = s_fetch?.Invoke("apply", s_window, args) as Task; args.Dispose(); if (response == null) throw new Exception("Internal error marshalling the response Promise from `fetch`."); @@ -338,32 +338,32 @@ private async Task DoFetch(HttpRequestMessage request, Canc private class WasmFetchResponse : IDisposable { - private readonly JSObject fetchResponse; - private readonly JSObject abortController; - private readonly CancellationTokenSource abortCts; - private readonly CancellationTokenRegistration abortRegistration; + private readonly JSObject _fetchResponse; + private readonly JSObject _abortController; + private readonly CancellationTokenSource _abortCts; + private readonly CancellationTokenRegistration _abortRegistration; public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, CancellationTokenSource abortCts, CancellationTokenRegistration abortRegistration) { - this.fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse)); - this.abortController = abortController ?? throw new ArgumentNullException(nameof(abortController)); - this.abortCts = abortCts; - this.abortRegistration = abortRegistration; + _fetchResponse = fetchResponse ?? throw new ArgumentNullException(nameof(fetchResponse)); + _abortController = abortController ?? throw new ArgumentNullException(nameof(abortController)); + _abortCts = abortCts; + _abortRegistration = abortRegistration; } - public bool IsOK => (bool)fetchResponse.GetObjectProperty("ok"); - public bool IsRedirected => (bool)fetchResponse.GetObjectProperty("redirected"); - public int Status => (int)fetchResponse.GetObjectProperty("status"); - public string StatusText => (string)fetchResponse.GetObjectProperty("statusText"); - public string ResponseType => (string)fetchResponse.GetObjectProperty("type"); - public string Url => (string)fetchResponse.GetObjectProperty("url"); - public bool IsBodyUsed => (bool)fetchResponse.GetObjectProperty("bodyUsed"); - public JSObject Headers => (JSObject)fetchResponse.GetObjectProperty("headers"); - public JSObject Body => (JSObject)fetchResponse.GetObjectProperty("body"); + public bool IsOK => (bool)_fetchResponse.GetObjectProperty("ok"); + public bool IsRedirected => (bool)_fetchResponse.GetObjectProperty("redirected"); + public int Status => (int)_fetchResponse.GetObjectProperty("status"); + public string StatusText => (string)_fetchResponse.GetObjectProperty("statusText"); + public string ResponseType => (string)_fetchResponse.GetObjectProperty("type"); + public string Url => (string)_fetchResponse.GetObjectProperty("url"); + public bool IsBodyUsed => (bool)_fetchResponse.GetObjectProperty("bodyUsed"); + public JSObject Headers => (JSObject)_fetchResponse.GetObjectProperty("headers"); + public JSObject Body => (JSObject)_fetchResponse.GetObjectProperty("body"); - public Task ArrayBuffer() => (Task)fetchResponse.Invoke("arrayBuffer"); - public Task Text() => (Task)fetchResponse.Invoke("text"); - public Task JSON() => (Task)fetchResponse.Invoke("json"); + public Task ArrayBuffer() => (Task)_fetchResponse.Invoke("arrayBuffer"); + public Task Text() => (Task)_fetchResponse.Invoke("text"); + public Task JSON() => (Task)_fetchResponse.Invoke("json"); public void Dispose() { @@ -378,15 +378,15 @@ protected virtual void Dispose(bool disposing) { // Free any other managed objects here. // - abortCts.Cancel(); + _abortCts.Cancel(); - abortRegistration.Dispose(); + _abortRegistration.Dispose(); } // Free any unmanaged objects here. // - fetchResponse?.Dispose(); - abortController?.Dispose(); + _fetchResponse?.Dispose(); + _abortController?.Dispose(); } } @@ -591,5 +591,4 @@ public override void Write(byte[] buffer, int offset, int count) } } } - } From 35380b5669c0d487fd40c79203c5f4c343cd4027 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 7 May 2020 11:56:13 +0200 Subject: [PATCH 49/99] Part of code style and object documentation to partially address review comments. --- .../Browser/Interop.JavaScript.Array.cs | 62 ++++++++++++++++++- .../Browser/Interop.JavaScript.ArrayBuffer.cs | 40 ++++++++++-- .../Browser/Interop.JavaScript.CoreObject.cs | 17 ++++- .../Browser/Interop.JavaScript.Function.cs | 1 + .../Browser/Interop.JavaScript.HostObject.cs | 8 +-- .../Browser/Interop.JavaScript.JSException.cs | 5 +- .../Browser/Interop.JavaScript.JSObject.cs | 9 +-- .../Interop.JavaScript.SharedArrayBuffer.cs | 1 + .../Browser/Interop.JavaScript.TypedArray.cs | 36 ++--------- .../Browser/Interop.JavaScript.Uint8Array.cs | 3 +- .../Interop.JavaScript.Uint8ClampedArray.cs | 2 +- .../Interop/Browser/Interop.Runtime.AnyRef.cs | 15 +++-- .../Interop/Browser/Interop.Runtime.Bridge.cs | 13 ++-- .../src/Interop/Browser/Interop.Runtime.cs | 1 - 14 files changed, 143 insertions(+), 70 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs index df1c585d929f40..7432308f6d3ec7 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs @@ -3,23 +3,83 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript { + /// + /// Initializes a new instance of the Array class. + /// public class Array : CoreObject { + /// + /// Initializes a new instance of the Array class. + /// + /// Parameters. public Array(params object[] _params) : base(Runtime.New(_params)) { } - internal Array(IntPtr js_handle) : base(js_handle) + + /// + /// Initializes a new instance of the Array/> class. + /// + /// Js handle. + internal Array(IntPtr jsHandle) : base(jsHandle) { } + + /// + /// Push the specified elements. + /// + /// The new length of the Array push was called on + /// Elements. public int Push(params object[] elements) => (int)Invoke("push", elements); + + /// + /// Pop this instance. + /// + /// The element removed from the array or null if the array was empty public object Pop() => (object)Invoke("pop"); + + /// + /// Remove the first element of the Array and return that element + /// + /// The removed element public object Shift() => Invoke("shift"); + + /// + /// Add to the array starting at index 0 + /// + /// The length after shift. + /// Elements. public int UnShift(params object[] elements) => (int)Invoke("unshift", elements); + + /// + /// Index of the search element. + /// + /// The index of first occurrence of searchElement in the Array or -1 if not Found + /// Search element. + /// The index to start the search from public int IndexOf(object searchElement, int fromIndex = 0) => (int)Invoke("indexOf", searchElement, fromIndex); + + /// + /// Finds the index of the last occurrence of + /// + /// The index of the last occurrence + /// Search element. public int LastIndexOf(object searchElement) => (int)Invoke("lastIndexOf", searchElement); + + /// + /// Finds the index of the last occurrence of between 0 and . + /// + /// The index of the last occurrence. + /// Search element. + /// End index. public int LastIndexOf(object searchElement, int endIndex) => (int)Invoke("lastIndexOf", searchElement, endIndex); + + /// + /// Gets or sets the Array with the index specified by . + /// + /// The index. public object this[int i] { get diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs index 40bb22c35ddeed..3464fd63ec8d09 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs @@ -3,27 +3,59 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript { public class ArrayBuffer : CoreObject { - + /// + /// Initializes a new instance of the ArrayBuffer class. + /// public ArrayBuffer() : base(Runtime.New()) { } + /// + /// Initializes a new instance of the ArrayBuffer class. + /// + /// Length. public ArrayBuffer(int length) : base(Runtime.New(length)) { } - internal ArrayBuffer(IntPtr js_handle) : base(js_handle) + /// + /// Initializes a new instance of the ArrayBuffer class. + /// + /// Js handle. + internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle) { } + /// + /// The length of an ArrayBuffer in bytes. + /// + /// The length of the underlying ArrayBuffer in bytes. public int ByteLength => (int)GetObjectProperty("byteLength"); + + /// + /// Gets a value indicating whether this ArrayBuffer is view. + /// + /// true if is view; otherwise, false. public bool IsView => (bool)GetObjectProperty("isView"); + + /// + /// Slice the specified begin. + /// + /// The slice. + /// Begin. public ArrayBuffer Slice(int begin) => (ArrayBuffer)Invoke("slice", begin); - public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)Invoke("slice", begin, end); + /// + /// Slice the specified begin and end. + /// + /// The slice. + /// Begin. + /// End. + public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)Invoke("slice", begin, end); } } -} +} \ No newline at end of file diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs index e0bc34f6b3225b..45e344c55a72c3 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -3,16 +3,27 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript { + /// + /// Core objects are the standard built-in objects and functions. These + /// objects are part of the JavaScript environment. Not to be confused + /// with objects provided by the host application or the browser context + /// such as DOM. For more information about the distinction between the + /// DOM and core JavaScript, see JavaScript technologies overview: + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview + /// + /// Core objects are treated differently in the bridge code as they are + /// guaranteed to be there. + /// public abstract class CoreObject : JSObject { - - protected CoreObject(int js_handle) : base(js_handle) + protected CoreObject(int jsHandle) : base(jsHandle) { - var result = Runtime.BindCoreObject(js_handle, (int)(IntPtr)Handle, out int exception); + var result = Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); if (exception != 0) throw new JSException($"CoreObject Error binding: {result.ToString()}"); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs index 6343e5f808ae03..4f747579486f8f 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { /// diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs index f182cf62676542..5404f6012ee258 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs @@ -3,14 +3,14 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript { public interface IHostObject - { + { } - } public class HostObject : HostObjectBase { public HostObject(string hostName, params object[] _params) : base(Runtime.New(hostName, _params)) @@ -19,19 +19,15 @@ public HostObject(string hostName, params object[] _params) : base(Runtime.New(h public abstract class HostObjectBase : JSObject, IHostObject { - protected HostObjectBase(int js_handle) : base(js_handle) { var result = Runtime.BindHostObject(js_handle, (int)(IntPtr)Handle, out int exception); if (exception != 0) throw new JSException($"HostObject Error binding: {result.ToString()}"); - } internal HostObjectBase(IntPtr js_handle) : base(js_handle) { } - - } } } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs index b7908e96270280..48fa2d503d1afa 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs @@ -3,11 +3,14 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { - internal static partial class JavaScript { + /// + /// Represents an Exception initiated from the JavaScript interop code. + /// public class JSException : Exception { public JSException(string msg) : base(msg) { } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs index 55538ce9822804..6f504e27a6c437 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -14,6 +14,7 @@ public interface IJSObject int Length { get; } } } + internal static partial class JavaScript { /// @@ -36,14 +37,10 @@ public JSObject() : this(Runtime.New()) } internal JSObject(IntPtr js_handle) : base(js_handle) - { - //Console.WriteLine ($"JSObject: {js_handle}"); - } + { } internal JSObject(int js_handle) : base((IntPtr)js_handle) - { - //Console.WriteLine ($"JSObject: {js_handle}"); - } + { } internal JSObject(int js_handle, object raw_obj) : base(js_handle) { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs index aad5280012d4a3..f8821aae299277 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs index 1071c31f101064..4bb8521bcc01d7 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs @@ -21,12 +21,10 @@ public interface ITypedArray void Set(ITypedArray typedArray); void Set(ITypedArray typedArray, int offset); - //TypedArrayTypeCode GetTypedArrayType(); } public interface ITypedArray where U : struct { - T Slice(); T Slice(int begin); T Slice(int begin, int end); @@ -34,7 +32,6 @@ public interface ITypedArray where U : struct T SubArray(); T SubArray(int begin); T SubArray(int begin, int end); - } public enum TypedArrayTypeCode @@ -50,10 +47,14 @@ public enum TypedArrayTypeCode Uint8ClampedArray = 0xF, } + /// + /// Represents a JavaScript TypedArray. + /// public abstract class TypedArray : CoreObject, ITypedArray, ITypedArray where U : struct { protected TypedArray() : base(Runtime.New()) { } + protected TypedArray(int length) : base(Runtime.New(length)) { } @@ -75,36 +76,9 @@ protected TypedArray(SharedArrayBuffer buffer, int byteOffset) : base(Runtime.Ne protected TypedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(Runtime.New(buffer, byteOffset, length)) { } - internal TypedArray(IntPtr js_handle) : base(js_handle) + internal TypedArray(IntPtr jsHandle) : base(jsHandle) { } - // public TypedArrayTypeCode GetTypedArrayType() - // { - // switch (this) - // { - // case Int8Array _: - // return TypedArrayTypeCode.Int8Array; - // case Uint8Array _: - // return TypedArrayTypeCode.Uint8Array; - // case Uint8ClampedArray _: - // return TypedArrayTypeCode.Uint8ClampedArray; - // case Int16Array _: - // return TypedArrayTypeCode.Int16Array; - // case Uint16Array _: - // return TypedArrayTypeCode.Uint16Array; - // case Int32Array _: - // return TypedArrayTypeCode.Int32Array; - // case Uint32Array _: - // return TypedArrayTypeCode.Uint32Array; - // case Float32Array _: - // return TypedArrayTypeCode.Float32Array; - // case Float64Array _: - // return TypedArrayTypeCode.Float64Array; - // default: - // throw new ArrayTypeMismatchException("TypedArray is not of correct type."); - // } - // } - public int BytesPerElement => (int)GetObjectProperty("BYTES_PER_ELEMENT"); public string Name => (string)GetObjectProperty("name"); public int ByteLength => (int)GetObjectProperty("byteLength"); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs index c05dd4039fdf1a..41be87e832bdd4 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript { - public sealed class Uint8Array : TypedArray { - public Uint8Array() { } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs index d0fe32ae190dc7..16c5301a71e42d 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System; + internal static partial class Interop { internal static partial class JavaScript { - public sealed class Uint8ClampedArray : TypedArray { public Uint8ClampedArray() diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs index a24469a0eb6bbb..feb442514c9943 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs @@ -4,27 +4,26 @@ using System; using System.Runtime.InteropServices; + internal static partial class Interop { - internal static partial class Runtime { public class AnyRef { - public int JSHandle { get; internal set; } internal GCHandle Handle; - internal AnyRef(int js_handle) + internal AnyRef(int jsHandle) { - this.JSHandle = js_handle; - this.Handle = GCHandle.Alloc(this); + JSHandle = jsHandle; + Handle = GCHandle.Alloc(this); } - internal AnyRef(IntPtr js_handle) + internal AnyRef(IntPtr jsHandle) { - this.JSHandle = (int)js_handle; - this.Handle = GCHandle.Alloc(this); + JSHandle = (int)jsHandle; + Handle = GCHandle.Alloc(this); } } } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 59999d29a59556..ee6a53521dab77 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -94,6 +94,7 @@ internal static void UnBindJSObjectAndFree(int jsId) } } + internal static void UnBindRawJSObjectAndFree(int gcHandle) { @@ -118,6 +119,7 @@ internal static void UnBindRawJSObjectAndFree(int gcHandle) } } + internal static object CreateTaskSource(int jsId) { return new TaskCompletionSource(); @@ -147,6 +149,7 @@ internal static int BindExistingObject(object rawObj, int jsId) return obj == null ? 0 : (int)(IntPtr)obj.Handle; } + internal static int GetJSObjectId(object rawObj) { JSObject? obj = rawObj as JSObject; @@ -168,6 +171,7 @@ internal static object BoxInt(int i) { return i; } + internal static object BoxDouble(double d) { return d; @@ -182,6 +186,7 @@ internal static bool IsSimpleArray(object a) { return a is Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0; } + internal static object? GetCoreType(string coreObj) { Assembly asm = typeof(Runtime).Assembly; @@ -271,6 +276,7 @@ internal static string GetCallSignature(IntPtr methodHandle) } return new string(res); } + internal static void SetupJSContinuation(Task task, JSObject continuationObj) { if (task.IsCompleted) @@ -315,12 +321,6 @@ void Complete() internal static string ObjectToString(object o) { - - //if (o == null) - // return null; - //if (o is Enum) - // return EnumToExportContract((Enum)o).ToString(); - return o.ToString() ?? string.Empty; } @@ -342,6 +342,7 @@ internal static DateTime CreateDateTime(double ticks) var unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); return unixTime.DateTime; } + internal static Uri CreateUri(string uri) { return new Uri(uri); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 1bf488b8267265..f1725484c05df7 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -117,7 +117,6 @@ public static void FreeObject(object obj) jsobj.IsDisposed = true; jsobj.Handle.Free(); } - } else { From 233c3b31602b850af869fc7957c2edaef0f4149b Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:21:34 +0200 Subject: [PATCH 50/99] Update src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs Co-authored-by: Stephen Toub --- .../Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs index 6f504e27a6c437..02f5458f8dc131 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -13,10 +13,6 @@ public interface IJSObject int JSHandle { get; } int Length { get; } } - } - - internal static partial class JavaScript - { /// /// JSObjects are wrappers for a native JavaScript object, and /// they retain a reference to the JavaScript object for the lifetime of this C# object. From 5e0ebf28e8ab50a05ae2b9355bdd5b83290f5815 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:23:12 +0200 Subject: [PATCH 51/99] Update src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs Co-authored-by: Stephen Toub --- .../Common/src/Interop/Browser/Interop.Runtime.Bridge.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index ee6a53521dab77..d25ac598350e4a 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -48,7 +48,9 @@ internal static int BindCoreCLRObject(int jsId, int gcHandle) obj = existingObj; } else + { obj = h.Target as JSObject; + } return obj == null ? 0 : (int)(IntPtr)obj.Handle; } From 117c9efe290c4f2a214f13c6d31538876eb161e8 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:25:12 +0200 Subject: [PATCH 52/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index e000ff8dbe1d94..16fb319bfd5c9e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -20,7 +20,6 @@ namespace System.Net.Http { - // **Note** on `Task.ConfigureAwait(continueOnCapturedContext: true)` for the WebAssembly Browser. // The current implementation of WebAssembly for the Browser does not have a SynchronizationContext nor a Scheduler // thus forcing the callbacks to run on the main browser thread. When threading is eventually implemented using From 86042807e7ace0b83a67f0c5f6d1d6c0e65383b8 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:26:24 +0200 Subject: [PATCH 53/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 16fb319bfd5c9e..98ccbbffc96ce0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -167,7 +167,7 @@ protected internal override Task SendAsync(HttpRequestMessa private async Task DoFetch(HttpRequestMessage request, CancellationToken cancellationToken) { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); // Wrap the cancellationToken in a using so that it can be disposed of whether // we successfully fetched or failed trying. From 65c1f2294103e774ec34a1e44365ac8ad12302fe Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:27:19 +0200 Subject: [PATCH 54/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 98ccbbffc96ce0..836cd2b71a6acd 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -159,6 +159,7 @@ public TimeSpan Expect100ContinueTimeout } public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return DoFetch(request, cancellationToken); From 3d6ce31a9a4c2398e37d25fff39da1c3214fe392 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:28:49 +0200 Subject: [PATCH 55/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 836cd2b71a6acd..57dfe34546b382 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -178,8 +178,8 @@ private async Task DoFetch(HttpRequestMessage request, Canc { var requestObject = new JSObject(); - if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out object? fetchOoptionsValue) && - fetchOoptionsValue is IDictionary fetchOptions) + if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out object? fetchOptionsValue) && + fetchOptionsValue is IDictionary fetchOptions) { foreach (KeyValuePair item in fetchOptions) { From de114a5573a1baae34e84bc8c54fd23544f43e28 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 05:31:30 +0200 Subject: [PATCH 56/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 57dfe34546b382..1893061208a98f 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -572,7 +572,7 @@ public override void Flush() public override int Read(byte[] buffer, int offset, int count) { - throw new PlatformNotSupportedException("Synchronous reads are not supported, use ReadAsync instead"); + throw new NotSupportedException("Synchronous reads are not supported, use ReadAsync instead"); } public override long Seek(long offset, SeekOrigin origin) From 9244cb482719619e4922121a84207f9827174559 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 07:14:33 +0200 Subject: [PATCH 57/99] Add class docs and code style updates - Address review comments --- .../Interop/Browser/Interop.JavaScript.Array.cs | 2 +- .../Browser/Interop.JavaScript.ArrayBuffer.cs | 8 ++++---- .../Browser/Interop.JavaScript.CoreObject.cs | 8 +++++--- .../Browser/Interop.JavaScript.Function.cs | 8 +++++++- .../Browser/Interop.JavaScript.HostObject.cs | 11 +++++++++++ .../Interop.JavaScript.SharedArrayBuffer.cs | 17 +++++++++++++++++ .../Browser/Interop.JavaScript.Uint8Array.cs | 6 ++++++ .../Interop.JavaScript.Uint8ClampedArray.cs | 6 ++++++ .../System.Net.Http/src/System.Net.Http.csproj | 2 +- .../BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 10 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs index 7432308f6d3ec7..590a72628351a4 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class JavaScript { /// - /// Initializes a new instance of the Array class. + /// Initializes a new instance of JavaScript Core Array class. /// public class Array : CoreObject { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs index 3464fd63ec8d09..e3c75647eb511b 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs @@ -11,20 +11,20 @@ internal static partial class JavaScript public class ArrayBuffer : CoreObject { /// - /// Initializes a new instance of the ArrayBuffer class. + /// Initializes a new instance of the JavaScript Core ArrayBuffer class. /// public ArrayBuffer() : base(Runtime.New()) { } /// - /// Initializes a new instance of the ArrayBuffer class. + /// Initializes a new instance of the JavaScript Core ArrayBuffer class. /// /// Length. public ArrayBuffer(int length) : base(Runtime.New(length)) { } /// - /// Initializes a new instance of the ArrayBuffer class. + /// Initializes a new instance of the JavaScript Core ArrayBuffer class. /// /// Js handle. internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle) @@ -58,4 +58,4 @@ internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle) public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)Invoke("slice", begin, end); } } -} \ No newline at end of file +} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs index 45e344c55a72c3..86d2c93044e2a0 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -9,8 +9,10 @@ internal static partial class Interop internal static partial class JavaScript { /// - /// Core objects are the standard built-in objects and functions. These - /// objects are part of the JavaScript environment. Not to be confused + /// Core objects are the standard built-in objects and functions. + /// + /// + /// These objects are part of the JavaScript environment. Not to be confused /// with objects provided by the host application or the browser context /// such as DOM. For more information about the distinction between the /// DOM and core JavaScript, see JavaScript technologies overview: @@ -18,7 +20,7 @@ internal static partial class JavaScript /// /// Core objects are treated differently in the bridge code as they are /// guaranteed to be there. - /// + /// public abstract class CoreObject : JSObject { protected CoreObject(int jsHandle) : base(jsHandle) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs index 4f747579486f8f..eb86f87579bfc0 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -7,8 +7,14 @@ internal static partial class Interop { /// - /// The Function constructor creates a new Function object. Calling the constructor directly can create functions dynamically, but suffers from security and similar (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor allows executing code in the global scope, prompting better programming habits and allowing for more efficient code minification. + /// The Function constructor creates a new Function object. /// + /// + /// Calling the constructor directly can create functions dynamically, but suffers from security and similar + /// (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor + /// allows executing code in the global scope, prompting better programming habits and allowing for more efficient + /// code minification. + /// internal static partial class JavaScript { public class Function : CoreObject diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs index 5404f6012ee258..c58959aa298f7f 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs @@ -8,6 +8,17 @@ internal static partial class Interop { internal static partial class JavaScript { + /// + /// Host objects are object supplied by the host environment. + /// + /// + /// These objects are not part of the JavaScript environment and provided by the host application + /// or the browser context such as DOM. For more information about the distinction between the + /// DOM and core JavaScript, see JavaScript technologies overview: + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview + /// + /// Host objects are treated differently in the bridge code as they are not guaranteed to exist. + /// public interface IHostObject { } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs index f8821aae299277..644458d1e04118 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs @@ -10,13 +10,30 @@ internal static partial class JavaScript { public class SharedArrayBuffer : CoreObject { + /// + /// Initializes a new instance of the JavaScript Core SharedArrayBuffer class. + /// + /// The size, in bytes, of the array buffer to create. public SharedArrayBuffer(int length) : base(Runtime.New(length)) { } internal SharedArrayBuffer(IntPtr js_handle) : base(js_handle) { } + /// + /// The size, in bytes, of the array. This is established when the array is constructed and cannot be changed. + /// + /// The size, in bytes, of the array. public int ByteLength => (int)GetObjectProperty("byteLength"); + + /// + /// Returns a new JavaScript Core SharedArrayBuffer whose contents are a copy of this SharedArrayBuffer's bytes from begin, + /// inclusive, up to end, exclusive. If either begin or end is negative, it refers to an index from the end + /// of the array, as opposed to from the beginning. + /// + /// a new JavaScript Core SharedArrayBuffer + /// Beginning index of copy. + /// Ending index, exclusive. public SharedArrayBuffer Slice(int begin, int end) => (SharedArrayBuffer)Invoke("slice", begin, end); } } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs index 41be87e832bdd4..c2dcd2acfa0b4d 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs @@ -10,6 +10,9 @@ internal static partial class JavaScript { public sealed class Uint8Array : TypedArray { + /// + /// Initializes a new instance of the JavaScript Core Uint8Array class. + /// public Uint8Array() { } @@ -38,6 +41,9 @@ public Uint8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(b internal Uint8Array(IntPtr js_handle) : base(js_handle) { } + /// + /// Defines an implicit conversion of JavaScript Core Uint8Array class to a + /// public static implicit operator Span(Uint8Array typedarray) => typedarray.ToArray(); public static implicit operator Uint8Array(Span span) => From(span); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs index 16c5301a71e42d..1a5f4e705cf439 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs @@ -10,6 +10,9 @@ internal static partial class JavaScript { public sealed class Uint8ClampedArray : TypedArray { + /// + /// Initializes a new instance of the JavaScript Core Uint8ClampedArray class. + /// public Uint8ClampedArray() { } @@ -38,6 +41,9 @@ public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset, int length) : internal Uint8ClampedArray(IntPtr js_handle) : base(js_handle) { } + /// + /// Defines an implicit conversion of JavaScript Core Uint8ClampedArray class to a Span of byte + /// public static implicit operator Span(Uint8ClampedArray typedarray) => typedarray.ToArray(); public static implicit operator Uint8ClampedArray(Span span) => From(span); diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 1bdb7ecb76f5c5..3b3a38e28dc6cb 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -750,7 +750,7 @@ + Link="Common\Interop\Browser\Interop.JavaScript.Uint8ClampedArray.cs" /> Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); - + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return DoFetch(request, cancellationToken); From fb95c39c4ae5a1c418f3eec2eda0422368f3b25d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 07:15:07 +0200 Subject: [PATCH 58/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 07f100b07aad1d..4e13186b380452 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -210,7 +210,7 @@ private async Task DoFetch(HttpRequestMessage request, Canc } // Process headers - // Cors has it's own restrictions on headers. + // Cors has its own restrictions on headers. // https://developer.mozilla.org/en-US/docs/Web/API/Headers using (HostObject jsHeaders = new HostObject("Headers")) { From 22f5ea2956fafd514acd6d2a65a6819782696b1e Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 08:23:27 +0200 Subject: [PATCH 59/99] Fix badly formed XML --- .../Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs | 2 +- .../src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs index c2dcd2acfa0b4d..8f2930593ed063 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs @@ -42,7 +42,7 @@ internal Uint8Array(IntPtr js_handle) : base(js_handle) { } /// - /// Defines an implicit conversion of JavaScript Core Uint8Array class to a + /// Defines an implicit conversion of JavaScript Core Uint8Array class to a Span<byte> /// public static implicit operator Span(Uint8Array typedarray) => typedarray.ToArray(); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs index 1a5f4e705cf439..c8159439849af2 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs @@ -42,7 +42,7 @@ internal Uint8ClampedArray(IntPtr js_handle) : base(js_handle) { } /// - /// Defines an implicit conversion of JavaScript Core Uint8ClampedArray class to a Span of byte + /// Defines an implicit conversion of JavaScript Core Uint8ClampedArray class to a Span<byte> /// public static implicit operator Span(Uint8ClampedArray typedarray) => typedarray.ToArray(); From 37a2b42462bc068b6700dba1a924cedc4ace4e77 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 08:26:28 +0200 Subject: [PATCH 60/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 4e13186b380452..e6709d3bebf358 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -160,14 +160,8 @@ public TimeSpan Expect100ContinueTimeout public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - return DoFetch(request, cancellationToken); - } - - private async Task DoFetch(HttpRequestMessage request, CancellationToken cancellationToken) - { - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); // Wrap the cancellationToken in a using so that it can be disposed of whether From b20d6b5a072b857d313028d239041ce9965186c6 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 08:33:56 +0200 Subject: [PATCH 61/99] Remove unnecessary Property implementations. --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index e6709d3bebf358..b561cecf39869c 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -110,18 +110,6 @@ public int MaxConnectionsPerServer set => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); } - public int MaxResponseDrainSize - { - get => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); - set => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); - } - - public TimeSpan ResponseDrainTimeout - { - get => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); - set => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); - } - public int MaxResponseHeadersLength { get => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); @@ -134,30 +122,6 @@ public SslClientAuthenticationOptions SslOptions set => throw new PlatformNotSupportedException("Property SslOptions is not supported."); } - public TimeSpan PooledConnectionLifetime - { - get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - } - - public TimeSpan PooledConnectionIdleTimeout - { - get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - } - - public TimeSpan ConnectTimeout - { - get => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); - set => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); - } - - public TimeSpan Expect100ContinueTimeout - { - get => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); - set => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); - } - public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) From fc302f3977fd5601fede984c0de8d2e62aa5031b Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 08:38:32 +0200 Subject: [PATCH 62/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index b561cecf39869c..ebe2d90d69d03a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -182,7 +182,7 @@ protected internal override async Task SendAsync(HttpReques } } } - if (request.Content?.Headers != null) + if (request.Content != null) { foreach (KeyValuePair> header in request.Content.Headers) { From 3a930433d59d8a3ff5714bfbd833c2cadd2617fc Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 08:58:57 +0200 Subject: [PATCH 63/99] Update code style involving the use of `var` --- .../Browser/Interop.JavaScript.Array.cs | 4 +-- .../Browser/Interop.JavaScript.CoreObject.cs | 4 +-- .../Browser/Interop.JavaScript.Function.cs | 2 +- .../Browser/Interop.JavaScript.HostObject.cs | 6 ++--- .../Browser/Interop.JavaScript.JSObject.cs | 10 +++---- .../Browser/Interop.JavaScript.TypedArray.cs | 18 ++++++------- .../Interop/Browser/Interop.Runtime.Bridge.cs | 26 +++++++++---------- .../src/Interop/Browser/Interop.Runtime.cs | 12 ++++----- .../BrowserHttpHandler/BrowserHttpHandler.cs | 6 +---- 9 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs index 590a72628351a4..0c03508ac57afa 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs @@ -84,7 +84,7 @@ public object this[int i] { get { - var indexValue = Runtime.GetByIndex(JSHandle, i, out int exception); + object indexValue = Runtime.GetByIndex(JSHandle, i, out int exception); if (exception != 0) throw new JSException((string)indexValue); @@ -92,7 +92,7 @@ public object this[int i] } set { - var res = Runtime.SetByIndex(JSHandle, i, value, out int exception); + object res = Runtime.SetByIndex(JSHandle, i, value, out int exception); if (exception != 0) throw new JSException((string)res); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs index 86d2c93044e2a0..18aeeb3e017bd8 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -25,9 +25,9 @@ public abstract class CoreObject : JSObject { protected CoreObject(int jsHandle) : base(jsHandle) { - var result = Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); + object result = Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); if (exception != 0) - throw new JSException($"CoreObject Error binding: {result.ToString()}"); + throw new JSException($"CoreObject Error binding: {result}"); } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs index eb86f87579bfc0..d9f8b976b34d8d 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs @@ -22,7 +22,7 @@ public class Function : CoreObject public Function(params object[] args) : base(Runtime.New(args)) { } - internal Function(IntPtr js_handle) : base(js_handle) + internal Function(IntPtr jHandle) : base(jHandle) { } /// diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs index c58959aa298f7f..c38d58f48e7230 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs @@ -30,11 +30,11 @@ public HostObject(string hostName, params object[] _params) : base(Runtime.New(h public abstract class HostObjectBase : JSObject, IHostObject { - protected HostObjectBase(int js_handle) : base(js_handle) + protected HostObjectBase(int jHandle) : base(jHandle) { - var result = Runtime.BindHostObject(js_handle, (int)(IntPtr)Handle, out int exception); + object result = Runtime.BindHostObject(jHandle, (int)(IntPtr)Handle, out int exception); if (exception != 0) - throw new JSException($"HostObject Error binding: {result.ToString()}"); + throw new JSException($"HostObject Error binding: {result}"); } internal HostObjectBase(IntPtr js_handle) : base(js_handle) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs index 02f5458f8dc131..6d74cd97ab64f0 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -26,9 +26,9 @@ public class JSObject : Interop.Runtime.AnyRef, IJSObject, IDisposable public JSObject() : this(Runtime.New()) { - var result = Runtime.BindCoreObject(JSHandle, (int)(IntPtr)Handle, out int exception); + object result = Runtime.BindCoreObject(JSHandle, (int)(IntPtr)Handle, out int exception); if (exception != 0) - throw new JSException($"JSObject Error binding: {result.ToString()}"); + throw new JSException($"JSObject Error binding: {result}"); } @@ -45,7 +45,7 @@ internal JSObject(int js_handle, object raw_obj) : base(js_handle) public object Invoke(string method, params object?[] args) { - var res = Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); + object res = Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); if (exception != 0) throw new JSException((string)res); return res; @@ -54,7 +54,7 @@ public object Invoke(string method, params object?[] args) public object GetObjectProperty(string name) { - var propertyValue = Runtime.GetObjectProperty(JSHandle, name, out int exception); + object propertyValue = Runtime.GetObjectProperty(JSHandle, name, out int exception); if (exception != 0) throw new JSException((string)propertyValue); @@ -66,7 +66,7 @@ public object GetObjectProperty(string name) public void SetObjectProperty(string name, object value, bool createIfNotExists = true, bool hasOwnProperty = false) { - var setPropResult = Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); + object setPropResult = Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); if (exception != 0) throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs index 4bb8521bcc01d7..aee87d4b48e7db 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs @@ -105,7 +105,7 @@ public U? this[int i] { get { - var jsValue = Runtime.GetByIndex(JSHandle, i, out int exception); + object jsValue = Runtime.GetByIndex(JSHandle, i, out int exception); if (exception != 0) throw new JSException((string)jsValue); @@ -128,13 +128,13 @@ public U? this[int i] if (jsValue == null) return null; - var type = jsValue.GetType(); + Type type = jsValue.GetType(); return (U)Convert.ChangeType(jsValue, typeof(U)); } public U[] ToArray() { - var res = Runtime.TypedArrayToArray(JSHandle, out int exception); + object res = Runtime.TypedArrayToArray(JSHandle, out int exception); if (exception != 0) throw new JSException((string)res); @@ -154,10 +154,10 @@ public static unsafe T From(ReadOnlySpan span) if (type == TypedArrayTypeCode.Uint8Array && typeof(T) == typeof(Uint8ClampedArray)) type = TypedArrayTypeCode.Uint8ClampedArray; // This is only passed to the JavaScript side so it knows it will be a Uint8ClampedArray - var bytes = MemoryMarshal.AsBytes(span); + ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { - var res = Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Marshal.SizeOf(), (int)type, out int exception); + object res = Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Marshal.SizeOf(), (int)type, out int exception); if (exception != 0) throw new JSException((string)res); return (T)res; @@ -167,10 +167,10 @@ public static unsafe T From(ReadOnlySpan span) public unsafe int CopyTo(Span span) { - var bytes = MemoryMarshal.AsBytes(span); + ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { - var res = Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + object res = Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); if (exception != 0) throw new JSException((string)res); return (int)res / Marshal.SizeOf(); @@ -185,10 +185,10 @@ public unsafe int CopyFrom(ReadOnlySpan span) throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null and must have a length"); } - var bytes = MemoryMarshal.AsBytes(span); + ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { - var res = Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + object res = Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); if (exception != 0) throw new JSException((string)res); return (int)res / Marshal.SizeOf(); diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index d25ac598350e4a..924aaa7b589084 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -40,7 +40,7 @@ internal static int BindCoreCLRObject(int jsId, int gcHandle) GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj; - if (_boundObjects.TryGetValue(jsId, out var existingObj)) + if (_boundObjects.TryGetValue(jsId, out JSObject? existingObj)) { if (existingObj?.Handle != h && h.IsAllocated) throw new JSException($"Multiple handles pointing at js_id: {jsId}"); @@ -59,7 +59,7 @@ internal static int BindJSType(int jsId, Type mappedType) { if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) { - var jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, + ConstructorInfo? jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, null, new Type[] { typeof(IntPtr) }, null); _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); } @@ -68,7 +68,7 @@ internal static int BindJSType(int jsId, Type mappedType) internal static int UnBindJSObject(int jsId) { - if (_boundObjects.TryGetValue(jsId, out var obj)) + if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) { _boundObjects.Remove(jsId); return obj == null ? 0 : (int)(IntPtr)obj.Handle; @@ -79,7 +79,7 @@ internal static int UnBindJSObject(int jsId) internal static void UnBindJSObjectAndFree(int jsId) { - if (_boundObjects.TryGetValue(jsId, out var obj)) + if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) { if (_boundObjects[jsId] != null) { @@ -212,20 +212,20 @@ internal static string GetCallSignature(IntPtr methodHandle) IntPtrAndHandle tmp = default(IntPtrAndHandle); tmp.ptr = methodHandle; - var mb = MethodBase.GetMethodFromHandle(tmp.handle); + MethodBase? mb = MethodBase.GetMethodFromHandle(tmp.handle); if (mb == null) return string.Empty; ParameterInfo[] parms = mb.GetParameters(); - var parmsLength = parms.Length; + int parmsLength = parms.Length; if (parmsLength == 0) return string.Empty; - var res = new char[parmsLength]; + char[] res = new char[parmsLength]; for (int c = 0; c < parmsLength; c++) { - var t = parms[c].ParameterType; + Type t = parms[c].ParameterType; switch (Type.GetTypeCode(t)) { case TypeCode.Byte: @@ -341,7 +341,7 @@ internal static double GetDateValue(object dtv) internal static DateTime CreateDateTime(double ticks) { - var unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); + DateTimeOffset unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); return unixTime.DateTime; } @@ -361,13 +361,13 @@ public static void StopProfile() // Called by the AOT profiler to save profile data into Module.aot_profile_data internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) { - var arr = new byte[len]; + byte[] arr = new byte[len]; fixed (void* p = &buf) { - var span = new ReadOnlySpan(p, len); + ReadOnlySpan span = new ReadOnlySpan(p, len); // Send it to JS - var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); + JSObject jsDump = (JSObject)Runtime.GetGlobalObject("Module"); //jsDump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); } } @@ -376,7 +376,7 @@ internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) internal static void DumpCoverageProfileData(string data, string s) { // Send it to JS - var jsDump = (JSObject)Runtime.GetGlobalObject("Module"); + JSObject jsDump = (JSObject)Runtime.GetGlobalObject("Module"); jsDump.SetObjectProperty("coverage_profile_data", data); } } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index f1725484c05df7..0ef9c06574f0b5 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -61,7 +61,7 @@ internal static partial class Runtime // / String. public static string InvokeJS(string str) { - var res = InvokeJS(str, out int exception); + string res = InvokeJS(str, out int exception); if (exception != 0) throw new JSException(res); return res; @@ -69,7 +69,7 @@ public static string InvokeJS(string str) public static Interop.JavaScript.Function? CompileFunction(string snippet) { - var res = CompileFunction(snippet, out int exception); + object res = CompileFunction(snippet, out int exception); if (exception != 0) throw new JSException((string)res); return res as Interop.JavaScript.Function; @@ -77,7 +77,7 @@ public static string InvokeJS(string str) public static int New(params object[] parms) { - var res = New(typeof(T).Name, parms, out int exception); + object res = New(typeof(T).Name, parms, out int exception); if (exception != 0) throw new JSException((string)res); return (int)res; @@ -85,7 +85,7 @@ public static int New(params object[] parms) public static int New(string hostClassName, params object[] parms) { - var res = New(hostClassName, parms, out int exception); + object res = New(hostClassName, parms, out int exception); if (exception != 0) throw new JSException((string)res); return (int)res; @@ -93,7 +93,7 @@ public static int New(string hostClassName, params object[] parms) public static JSObject? NewJSObject(JSObject? jsFuncPtr = null, params object[] parms) { - var res = NewObjectJS(jsFuncPtr?.JSHandle ?? 0, parms, out int exception); + object res = NewObjectJS(jsFuncPtr?.JSHandle ?? 0, parms, out int exception); if (exception != 0) throw new JSException((string)res); return res as JSObject; @@ -127,7 +127,7 @@ public static void FreeObject(object obj) public static object GetGlobalObject(string? str = null) { int exception; - var globalHandle = Runtime.GetGlobalObject(str, out exception); + object globalHandle = Runtime.GetGlobalObject(str, out exception); if (exception != 0) throw new JSException($"Error obtaining a handle to global {str}"); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index ebe2d90d69d03a..af74cc355170a8 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -156,11 +156,7 @@ protected internal override async Task SendAsync(HttpReques } else { - // 2.1.801 seems to have a problem with the line - // using (var uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync ())) - // so we split it up into two lines. - var byteAsync = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: true); - using (Uint8Array uint8Buffer = Uint8Array.From(byteAsync)) + using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync.ConfigureAwait(continueOnCapturedContext: true))) { requestObject.SetObjectProperty("body", uint8Buffer); } From 4818f60dfc7deb01c0ea1e452623297943ef31ad Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 09:44:54 +0200 Subject: [PATCH 64/99] Modify the underlying sockets field name. - address review comments. --- .../src/System/Net/Http/HttpClientHandler.cs | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index d46dd98a410b80..66ed7a892c2932 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -14,9 +14,9 @@ namespace System.Net.Http public partial class HttpClientHandler : HttpMessageHandler { #if TARGETS_BROWSER - private readonly BrowserHttpHandler _socketsHttpHandler; + private readonly BrowserHttpHandler _underlyingHandler; #else - private readonly SocketsHttpHandler _socketsHttpHandler; + private readonly SocketsHttpHandler _underlyingHandler; #endif private readonly DiagnosticsHandler _diagnosticsHandler; private ClientCertificateOption _clientCertificateOptions; @@ -24,11 +24,11 @@ public partial class HttpClientHandler : HttpMessageHandler public HttpClientHandler() { #if TARGETS_BROWSER - _socketsHttpHandler = new BrowserHttpHandler(); + _underlyingHandler = new BrowserHttpHandler(); #else - _socketsHttpHandler = new SocketsHttpHandler(); + _underlyingHandler = new SocketsHttpHandler(); #endif - _diagnosticsHandler = new DiagnosticsHandler(_socketsHttpHandler); + _diagnosticsHandler = new DiagnosticsHandler(_underlyingHandler); ClientCertificateOptions = ClientCertificateOption.Manual; } @@ -37,7 +37,7 @@ protected override void Dispose(bool disposing) if (disposing && !_disposed) { _disposed = true; - _socketsHttpHandler.Dispose(); + _underlyingHandler.Dispose(); } base.Dispose(disposing); @@ -49,13 +49,13 @@ protected override void Dispose(bool disposing) public bool UseCookies { - get => _socketsHttpHandler.UseCookies; - set => _socketsHttpHandler.UseCookies = value; + get => _underlyingHandler.UseCookies; + set => _underlyingHandler.UseCookies = value; } public CookieContainer CookieContainer { - get => _socketsHttpHandler.CookieContainer; + get => _underlyingHandler.CookieContainer; set { if (value == null) @@ -63,57 +63,57 @@ public CookieContainer CookieContainer throw new ArgumentNullException(nameof(value)); } - _socketsHttpHandler.CookieContainer = value; + _underlyingHandler.CookieContainer = value; } } public DecompressionMethods AutomaticDecompression { - get => _socketsHttpHandler.AutomaticDecompression; - set => _socketsHttpHandler.AutomaticDecompression = value; + get => _underlyingHandler.AutomaticDecompression; + set => _underlyingHandler.AutomaticDecompression = value; } public bool UseProxy { - get => _socketsHttpHandler.UseProxy; - set => _socketsHttpHandler.UseProxy = value; + get => _underlyingHandler.UseProxy; + set => _underlyingHandler.UseProxy = value; } public IWebProxy? Proxy { - get => _socketsHttpHandler.Proxy; - set => _socketsHttpHandler.Proxy = value; + get => _underlyingHandler.Proxy; + set => _underlyingHandler.Proxy = value; } public ICredentials? DefaultProxyCredentials { - get => _socketsHttpHandler.DefaultProxyCredentials; - set => _socketsHttpHandler.DefaultProxyCredentials = value; + get => _underlyingHandler.DefaultProxyCredentials; + set => _underlyingHandler.DefaultProxyCredentials = value; } public bool PreAuthenticate { - get => _socketsHttpHandler.PreAuthenticate; - set => _socketsHttpHandler.PreAuthenticate = value; + get => _underlyingHandler.PreAuthenticate; + set => _underlyingHandler.PreAuthenticate = value; } public bool UseDefaultCredentials { // SocketsHttpHandler doesn't have a separate UseDefaultCredentials property. There // is just a Credentials property. So, we need to map the behavior. - get => _socketsHttpHandler.Credentials == CredentialCache.DefaultCredentials; + get => _underlyingHandler.Credentials == CredentialCache.DefaultCredentials; set { if (value) { - _socketsHttpHandler.Credentials = CredentialCache.DefaultCredentials; + _underlyingHandler.Credentials = CredentialCache.DefaultCredentials; } else { - if (_socketsHttpHandler.Credentials == CredentialCache.DefaultCredentials) + if (_underlyingHandler.Credentials == CredentialCache.DefaultCredentials) { // Only clear out the Credentials property if it was a DefaultCredentials. - _socketsHttpHandler.Credentials = null; + _underlyingHandler.Credentials = null; } } } @@ -121,32 +121,32 @@ public bool UseDefaultCredentials public ICredentials? Credentials { - get => _socketsHttpHandler.Credentials; - set => _socketsHttpHandler.Credentials = value; + get => _underlyingHandler.Credentials; + set => _underlyingHandler.Credentials = value; } public bool AllowAutoRedirect { - get => _socketsHttpHandler.AllowAutoRedirect; - set => _socketsHttpHandler.AllowAutoRedirect = value; + get => _underlyingHandler.AllowAutoRedirect; + set => _underlyingHandler.AllowAutoRedirect = value; } public int MaxAutomaticRedirections { - get => _socketsHttpHandler.MaxAutomaticRedirections; - set => _socketsHttpHandler.MaxAutomaticRedirections = value; + get => _underlyingHandler.MaxAutomaticRedirections; + set => _underlyingHandler.MaxAutomaticRedirections = value; } public int MaxConnectionsPerServer { - get => _socketsHttpHandler.MaxConnectionsPerServer; - set => _socketsHttpHandler.MaxConnectionsPerServer = value; + get => _underlyingHandler.MaxConnectionsPerServer; + set => _underlyingHandler.MaxConnectionsPerServer = value; } public int MaxResponseHeadersLength { - get => _socketsHttpHandler.MaxResponseHeadersLength; - set => _socketsHttpHandler.MaxResponseHeadersLength = value; + get => _underlyingHandler.MaxResponseHeadersLength; + set => _underlyingHandler.MaxResponseHeadersLength = value; } public ClientCertificateOption ClientCertificateOptions @@ -159,13 +159,13 @@ public ClientCertificateOption ClientCertificateOptions case ClientCertificateOption.Manual: ThrowForModifiedManagedSslOptionsIfStarted(); _clientCertificateOptions = value; - _socketsHttpHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate(ClientCertificates)!; + _underlyingHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate(ClientCertificates)!; break; case ClientCertificateOption.Automatic: ThrowForModifiedManagedSslOptionsIfStarted(); _clientCertificateOptions = value; - _socketsHttpHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate()!; + _underlyingHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate()!; break; default: @@ -183,18 +183,18 @@ public X509CertificateCollection ClientCertificates throw new InvalidOperationException(SR.Format(SR.net_http_invalid_enable_first, nameof(ClientCertificateOptions), nameof(ClientCertificateOption.Manual))); } - return _socketsHttpHandler.SslOptions.ClientCertificates ?? - (_socketsHttpHandler.SslOptions.ClientCertificates = new X509CertificateCollection()); + return _underlyingHandler.SslOptions.ClientCertificates ?? + (_underlyingHandler.SslOptions.ClientCertificates = new X509CertificateCollection()); } } public Func? ServerCertificateCustomValidationCallback { - get => (_socketsHttpHandler.SslOptions.RemoteCertificateValidationCallback?.Target as ConnectHelper.CertificateCallbackMapper)?.FromHttpClientHandler; + get => (_underlyingHandler.SslOptions.RemoteCertificateValidationCallback?.Target as ConnectHelper.CertificateCallbackMapper)?.FromHttpClientHandler; set { ThrowForModifiedManagedSslOptionsIfStarted(); - _socketsHttpHandler.SslOptions.RemoteCertificateValidationCallback = value != null ? + _underlyingHandler.SslOptions.RemoteCertificateValidationCallback = value != null ? new ConnectHelper.CertificateCallbackMapper(value).ForSocketsHttpHandler : null; } @@ -202,32 +202,32 @@ public X509CertificateCollection ClientCertificates public bool CheckCertificateRevocationList { - get => _socketsHttpHandler.SslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online; + get => _underlyingHandler.SslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online; set { ThrowForModifiedManagedSslOptionsIfStarted(); - _socketsHttpHandler.SslOptions.CertificateRevocationCheckMode = value ? X509RevocationMode.Online : X509RevocationMode.NoCheck; + _underlyingHandler.SslOptions.CertificateRevocationCheckMode = value ? X509RevocationMode.Online : X509RevocationMode.NoCheck; } } public SslProtocols SslProtocols { - get => _socketsHttpHandler.SslOptions.EnabledSslProtocols; + get => _underlyingHandler.SslOptions.EnabledSslProtocols; set { ThrowForModifiedManagedSslOptionsIfStarted(); - _socketsHttpHandler.SslOptions.EnabledSslProtocols = value; + _underlyingHandler.SslOptions.EnabledSslProtocols = value; } } - public IDictionary Properties => _socketsHttpHandler.Properties; + public IDictionary Properties => _underlyingHandler.Properties; protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return DiagnosticsHandler.IsEnabled() ? _diagnosticsHandler.SendAsync(request, cancellationToken) : - _socketsHttpHandler.SendAsync(request, cancellationToken); + _underlyingHandler.SendAsync(request, cancellationToken); } public static Func DangerousAcceptAnyServerCertificateValidator { get; } = delegate { return true; }; @@ -236,7 +236,7 @@ private void ThrowForModifiedManagedSslOptionsIfStarted() { // Hack to trigger an InvalidOperationException if a property that's stored on // SslOptions is changed, since SslOptions itself does not do any such checks. - _socketsHttpHandler.SslOptions = _socketsHttpHandler.SslOptions; + _underlyingHandler.SslOptions = _underlyingHandler.SslOptions; } } } From 521e772d757e19012f5482baace05b47170f5294 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 09:45:22 +0200 Subject: [PATCH 65/99] Style and address review comments --- .../Browser/Interop.JavaScript.CoreObject.cs | 1 - .../BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- .../BrowserHttpHandler/SocketsHttpHandler.cs | 118 ------------------ 3 files changed, 1 insertion(+), 120 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs index 18aeeb3e017bd8..e87f5a238c2e4e 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs @@ -28,7 +28,6 @@ protected CoreObject(int jsHandle) : base(jsHandle) object result = Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); if (exception != 0) throw new JSException($"CoreObject Error binding: {result}"); - } internal CoreObject(IntPtr js_handle) : base(js_handle) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index af74cc355170a8..fbde0913beb907 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -156,7 +156,7 @@ protected internal override async Task SendAsync(HttpReques } else { - using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync.ConfigureAwait(continueOnCapturedContext: true))) + using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: true))) { requestObject.SetObjectProperty("body", uint8Buffer); } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index 379e250aa5df5b..3a811b06978ab4 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -12,124 +12,6 @@ namespace System.Net.Http { public sealed class SocketsHttpHandler : HttpMessageHandler { - public bool UseCookies - { - get => throw new PlatformNotSupportedException("Property UseCookies is not supported."); - set => throw new PlatformNotSupportedException("Property UseCookies is not supported."); - } - - [AllowNull] - public CookieContainer CookieContainer - { - get => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); - set => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); - } - - public DecompressionMethods AutomaticDecompression - { - get => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); - set => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); - } - - public bool UseProxy - { - get => throw new PlatformNotSupportedException("Property UseProxy is not supported."); - set => throw new PlatformNotSupportedException("Property UseProxy is not supported."); - } - - public IWebProxy? Proxy - { - get => throw new PlatformNotSupportedException("Property Proxy is not supported."); - set => throw new PlatformNotSupportedException("Property Proxy is not supported."); - } - - public ICredentials? DefaultProxyCredentials - { - get => throw new PlatformNotSupportedException("Property Credentials is not supported."); - set => throw new PlatformNotSupportedException("Property Credentials is not supported."); - } - - public bool PreAuthenticate - { - get => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); - set => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); - } - - public ICredentials? Credentials - { - get => throw new PlatformNotSupportedException("Property Credentials is not supported."); - set => throw new PlatformNotSupportedException("Property Credentials is not supported."); - } - - public bool AllowAutoRedirect - { - get => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); - set => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); - } - - public int MaxAutomaticRedirections - { - get => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); - set => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); - } - - public int MaxConnectionsPerServer - { - get => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); - set => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); - } - - public int MaxResponseDrainSize - { - get => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); - set => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); - } - - public TimeSpan ResponseDrainTimeout - { - get => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); - set => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); - } - - public int MaxResponseHeadersLength - { - get => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); - set => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); - } - - [AllowNull] - public SslClientAuthenticationOptions SslOptions - { - get => throw new PlatformNotSupportedException("Property SslOptions is not supported."); - set => throw new PlatformNotSupportedException("Property SslOptions is not supported."); - } - - public TimeSpan PooledConnectionLifetime - { - get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - } - - public TimeSpan PooledConnectionIdleTimeout - { - get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); - } - - public TimeSpan ConnectTimeout - { - get => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); - set => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); - } - - public TimeSpan Expect100ContinueTimeout - { - get => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); - set => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); - } - - public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); - protected internal override Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { From 688f5a7d270dbb8bea165236ddc884d320aa9877 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 10:51:39 +0200 Subject: [PATCH 66/99] Remove reliance on ConnectHelper.cs code as it is not supported on Browser - This removes the reliance on the QUIC support. --- src/libraries/System.Net.Http/src/System.Net.Http.csproj | 5 +---- .../Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs | 2 +- .../System.Net.Http/src/System/Net/Http/HttpClientHandler.cs | 5 +++++ .../src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs | 2 -- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 3b3a38e28dc6cb..89a4a26872a795 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -14,7 +14,7 @@ $(DefineConstants);SYSNETHTTP_NO_OPENSSL - $(DefineConstants);BROWSER_DOES_NOT_SUPPORT_QUIC;TARGETS_BROWSER + $(DefineConstants);TARGETS_BROWSER @@ -731,9 +731,6 @@ - - - diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs index f86bcf0c25de39..24fffcd463d54f 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SystemProxyInfo.Browser.cs @@ -9,7 +9,7 @@ internal static partial class SystemProxyInfo // On Browser we do not support proxy. public static IWebProxy ConstructSystemProxy() { - throw new PlatformNotSupportedException("WebProxy is not supported."); + return new HttpNoProxy(); } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index 66ed7a892c2932..3948668351375b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -190,6 +190,10 @@ public X509CertificateCollection ClientCertificates public Func? ServerCertificateCustomValidationCallback { +#if TARGETS_BROWSER + get => throw new PlatformNotSupportedException("Property ServerCertificateCustomValidationCallback is not supported."); + set => throw new PlatformNotSupportedException("Property ServerCertificateCustomValidationCallback is not supported."); +#else get => (_underlyingHandler.SslOptions.RemoteCertificateValidationCallback?.Target as ConnectHelper.CertificateCallbackMapper)?.FromHttpClientHandler; set { @@ -198,6 +202,7 @@ public X509CertificateCollection ClientCertificates new ConnectHelper.CertificateCallbackMapper(value).ForSocketsHttpHandler : null; } +#endif } public bool CheckCertificateRevocationList diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index e9c0a3bfa2a470..398f3ead983367 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -4,9 +4,7 @@ using System.Diagnostics; using System.IO; -#if !BROWSER_DOES_NOT_SUPPORT_QUIC using System.Net.Quic; -#endif using System.Net.Security; using System.Net.Sockets; using System.Runtime.CompilerServices; From ea27792df2114ee4d4906535a9ef034444a01061 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 11:07:23 +0200 Subject: [PATCH 67/99] Update src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs Co-authored-by: Stephen Toub --- .../Common/src/Interop/Browser/Interop.Runtime.Bridge.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 924aaa7b589084..b75344805c703f 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -16,8 +16,8 @@ internal static partial class Interop { internal static partial class Runtime { - private static Dictionary _boundObjects = new Dictionary(); - private static Dictionary _rawToJS = new Dictionary(); + private static readonly Dictionary _boundObjects = new Dictionary(); + private static readonly Dictionary _rawToJS = new Dictionary(); internal static int BindJSObject(int jsId, Type mappedType) { From 04c53aed7d7270139847e186cdff39f6e1d70ce8 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 11:19:24 +0200 Subject: [PATCH 68/99] Remove null check for httpresponse.Content as it is always assigned to. - Address review comments --- .../Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index fbde0913beb907..b39d9aebbd66f0 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -227,11 +227,11 @@ protected internal override async Task SendAsync(HttpReques var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); - HttpResponseMessage httpresponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); + HttpResponseMessage httpResponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); bool streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); - httpresponse.Content = StreamingSupported && streamingEnabled + httpResponse.Content = StreamingSupported && streamingEnabled ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) : (HttpContent)new BrowserHttpContent(status); @@ -259,9 +259,8 @@ protected internal override async Task SendAsync(HttpReques { var name = (string)resultValue[0]; var value = (string)resultValue[1]; - if (!httpresponse.Headers.TryAddWithoutValidation(name, value)) - if (httpresponse.Content != null) - httpresponse.Content.Headers.TryAddWithoutValidation(name, value); + if (!httpResponse.Headers.TryAddWithoutValidation(name, value)) + httpResponse.Content.Headers.TryAddWithoutValidation(name, value); } nextResult?.Dispose(); nextResult = (JSObject)entriesIterator.Invoke("next"); @@ -275,7 +274,7 @@ protected internal override async Task SendAsync(HttpReques } } - tcs.SetResult(httpresponse); + tcs.SetResult(httpResponse); } catch (JSException jsExc) { From ca9e2db69a369076e3f4eb64528965db69289a55 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 11:56:25 +0200 Subject: [PATCH 69/99] Address review comments --- .../Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index b39d9aebbd66f0..777486d2e23bb3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -168,14 +168,11 @@ protected internal override async Task SendAsync(HttpReques // https://developer.mozilla.org/en-US/docs/Web/API/Headers using (HostObject jsHeaders = new HostObject("Headers")) { - if (request.Headers != null) + foreach (KeyValuePair> header in request.Headers) { - foreach (KeyValuePair> header in request.Headers) + foreach (string value in header.Value) { - foreach (string value in header.Value) - { - jsHeaders.Invoke("append", header.Key, value); - } + jsHeaders.Invoke("append", header.Key, value); } } if (request.Content != null) @@ -332,6 +329,7 @@ protected virtual void Dispose(bool disposing) // Free any other managed objects here. // _abortCts.Cancel(); + _abortCts.Dispose(); _abortRegistration.Dispose(); } From 6e1542f0d81757d4c196eaf5117a256d079d54ca Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 8 May 2020 13:06:10 +0200 Subject: [PATCH 70/99] Remove methods that will not be implemented right now, --- .../Interop/Browser/Interop.Runtime.Bridge.cs | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index b75344805c703f..b9b7a909dbbe7e 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -349,35 +349,5 @@ internal static Uri CreateUri(string uri) { return new Uri(uri); } - - // - // Can be called by the app to stop profiling - // - [MethodImplAttribute(MethodImplOptions.NoInlining)] - public static void StopProfile() - { - } - - // Called by the AOT profiler to save profile data into Module.aot_profile_data - internal static unsafe void DumpAotProfileData(ref byte buf, int len, string s) - { - byte[] arr = new byte[len]; - fixed (void* p = &buf) - { - ReadOnlySpan span = new ReadOnlySpan(p, len); - - // Send it to JS - JSObject jsDump = (JSObject)Runtime.GetGlobalObject("Module"); - //jsDump.SetObjectProperty("aot_profile_data", WebAssembly.Core.Uint8Array.From(span)); - } - } - - // Called by the coverage profiler to save profile data into Module.coverage_profile_data - internal static void DumpCoverageProfileData(string data, string s) - { - // Send it to JS - JSObject jsDump = (JSObject)Runtime.GetGlobalObject("Module"); - jsDump.SetObjectProperty("coverage_profile_data", data); - } } } From 7e39ffb16382a4512e4d9a12529ee4ec670300bc Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 11 May 2020 07:08:38 +0200 Subject: [PATCH 71/99] Add properties back to fix build error - error : MembersMustExist : Member 'public System.Boolean System.Net.Http.SocketsHttpHandler.XXXXXXXXXXXX.get()' does not exist in the implementation but it does exist in the contract. --- .../BrowserHttpHandler/SocketsHttpHandler.cs | 121 +++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index 3a811b06978ab4..684b4fe1fb03f2 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -12,10 +12,125 @@ namespace System.Net.Http { public sealed class SocketsHttpHandler : HttpMessageHandler { - protected internal override Task SendAsync( - HttpRequestMessage request, CancellationToken cancellationToken) + public bool UseCookies { - throw new PlatformNotSupportedException("Method SendAsync is not supported."); + get => throw new PlatformNotSupportedException("Property UseCookies is not supported."); + set => throw new PlatformNotSupportedException("Property UseCookies is not supported."); } + + [AllowNull] + public CookieContainer CookieContainer + { + get => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + set => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + } + + public DecompressionMethods AutomaticDecompression + { + get => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + set => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + } + + public bool UseProxy + { + get => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + set => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + } + + public IWebProxy? Proxy + { + get => throw new PlatformNotSupportedException("Property Proxy is not supported."); + set => throw new PlatformNotSupportedException("Property Proxy is not supported."); + } + + public ICredentials? DefaultProxyCredentials + { + get => throw new PlatformNotSupportedException("Property Credentials is not supported."); + set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + } + + public bool PreAuthenticate + { + get => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + set => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + } + + public ICredentials? Credentials + { + get => throw new PlatformNotSupportedException("Property Credentials is not supported."); + set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + } + + public bool AllowAutoRedirect + { + get => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + set => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + } + + public int MaxAutomaticRedirections + { + get => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + set => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + } + + public int MaxConnectionsPerServer + { + get => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + set => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + } + + public int MaxResponseDrainSize + { + get => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); + set => throw new PlatformNotSupportedException("Property MaxResponseDrainSize is not supported."); + } + + public TimeSpan ResponseDrainTimeout + { + get => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property ResponseDrainTimeout is not supported."); + } + + public int MaxResponseHeadersLength + { + get => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + set => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + } + + [AllowNull] + public SslClientAuthenticationOptions SslOptions + { + get => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + set => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + } + + public TimeSpan PooledConnectionLifetime + { + get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + } + + public TimeSpan PooledConnectionIdleTimeout + { + get => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + set => throw new PlatformNotSupportedException("Property PooledConnectionLifetime is not supported."); + } + + public TimeSpan ConnectTimeout + { + get => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property ConnectTimeout is not supported."); + } + + public TimeSpan Expect100ContinueTimeout + { + get => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); + set => throw new PlatformNotSupportedException("Property Expect100ContinueTimeout is not supported."); + } + + public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); + + protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + => throw new PlatformNotSupportedException("Method SendAsync is not supported."); } } From 703e5044a3dac0fff230e80bbaeef4208b2989a4 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 11 May 2020 08:19:42 +0200 Subject: [PATCH 72/99] Remove tcs TaskCompletionSource to address review comments --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 210 +++++++++--------- 1 file changed, 99 insertions(+), 111 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 777486d2e23bb3..1a6c96bedbb91a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -126,163 +126,151 @@ public SslClientAuthenticationOptions SslOptions protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - // Wrap the cancellationToken in a using so that it can be disposed of whether - // we successfully fetched or failed trying. - using (cancellationToken.Register(() => tcs.TrySetCanceled())) + try { - try - { - var requestObject = new JSObject(); + var requestObject = new JSObject(); - if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out object? fetchOptionsValue) && - fetchOptionsValue is IDictionary fetchOptions) + if (request.Properties.TryGetValue("WebAssemblyFetchOptions", out object? fetchOptionsValue) && + fetchOptionsValue is IDictionary fetchOptions) + { + foreach (KeyValuePair item in fetchOptions) { - foreach (KeyValuePair item in fetchOptions) - { - requestObject.SetObjectProperty(item.Key, item.Value); - } + requestObject.SetObjectProperty(item.Key, item.Value); } + } - requestObject.SetObjectProperty("method", request.Method.Method); + requestObject.SetObjectProperty("method", request.Method.Method); - // We need to check for body content - if (request.Content != null) + // We need to check for body content + if (request.Content != null) + { + if (request.Content is StringContent) + { + requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: true)); + } + else { - if (request.Content is StringContent) + using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: true))) { - requestObject.SetObjectProperty("body", await request.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: true)); + requestObject.SetObjectProperty("body", uint8Buffer); } - else + } + } + + // Process headers + // Cors has its own restrictions on headers. + // https://developer.mozilla.org/en-US/docs/Web/API/Headers + using (HostObject jsHeaders = new HostObject("Headers")) + { + foreach (KeyValuePair> header in request.Headers) + { + foreach (string value in header.Value) { - using (Uint8Array uint8Buffer = Uint8Array.From(await request.Content.ReadAsByteArrayAsync().ConfigureAwait(continueOnCapturedContext: true))) - { - requestObject.SetObjectProperty("body", uint8Buffer); - } + jsHeaders.Invoke("append", header.Key, value); } } - - // Process headers - // Cors has its own restrictions on headers. - // https://developer.mozilla.org/en-US/docs/Web/API/Headers - using (HostObject jsHeaders = new HostObject("Headers")) + if (request.Content != null) { - foreach (KeyValuePair> header in request.Headers) + foreach (KeyValuePair> header in request.Content.Headers) { foreach (string value in header.Value) { jsHeaders.Invoke("append", header.Key, value); } } - if (request.Content != null) - { - foreach (KeyValuePair> header in request.Content.Headers) - { - foreach (string value in header.Value) - { - jsHeaders.Invoke("append", header.Key, value); - } - } - } - requestObject.SetObjectProperty("headers", jsHeaders); } + requestObject.SetObjectProperty("headers", jsHeaders); + } - WasmHttpReadStream? wasmHttpReadStream = null; - JSObject abortController = new HostObject("AbortController"); - JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); - requestObject.SetObjectProperty("signal", signal); - signal.Dispose(); + WasmHttpReadStream? wasmHttpReadStream = null; - CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => - { - if (abortController.JSHandle != -1) - { - abortController.Invoke("abort"); - abortController?.Dispose(); - } - wasmHttpReadStream?.Dispose(); - })); + JSObject abortController = new HostObject("AbortController"); + JSObject signal = (JSObject)abortController.GetObjectProperty("signal"); + requestObject.SetObjectProperty("signal", signal); + signal.Dispose(); - var args = new Interop.JavaScript.Array(); - if (request.RequestUri != null) + CancellationTokenSource abortCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + CancellationTokenRegistration abortRegistration = abortCts.Token.Register((Action)(() => + { + if (abortController.JSHandle != -1) { - args.Push(request.RequestUri.ToString()); - args.Push(requestObject); + abortController.Invoke("abort"); + abortController?.Dispose(); } + wasmHttpReadStream?.Dispose(); + })); - requestObject.Dispose(); + var args = new Interop.JavaScript.Array(); + if (request.RequestUri != null) + { + args.Push(request.RequestUri.ToString()); + args.Push(requestObject); + } - var response = s_fetch?.Invoke("apply", s_window, args) as Task; - args.Dispose(); - if (response == null) - throw new Exception("Internal error marshalling the response Promise from `fetch`."); + requestObject.Dispose(); - JSObject t = (JSObject)await response.ConfigureAwait(continueOnCapturedContext: true); + var response = s_fetch?.Invoke("apply", s_window, args) as Task; + args.Dispose(); + if (response == null) + throw new Exception("Internal error marshalling the response Promise from `fetch`."); - var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); + JSObject t = (JSObject)await response.ConfigureAwait(continueOnCapturedContext: true); - HttpResponseMessage httpResponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); + var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); - bool streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); + HttpResponseMessage httpResponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); - httpResponse.Content = StreamingSupported && streamingEnabled - ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) - : (HttpContent)new BrowserHttpContent(status); + bool streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); - // Fill the response headers - // CORS will only allow access to certain headers. - // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. - // cors and basic responses are almost identical except that a cors response restricts the headers you can view to - // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. - // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types - // - // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation - using (JSObject respHeaders = status.Headers) + httpResponse.Content = StreamingSupported && streamingEnabled + ? new StreamContent(wasmHttpReadStream = new WasmHttpReadStream(status)) + : (HttpContent)new BrowserHttpContent(status); + + // Fill the response headers + // CORS will only allow access to certain headers. + // If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. + // cors and basic responses are almost identical except that a cors response restricts the headers you can view to + // `Cache-Control`, `Content-Language`, `Content-Type`, `Expires`, `Last-Modified`, and `Pragma`. + // View more information https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types + // + // Note: Some of the headers may not even be valid header types in .NET thus we use TryAddWithoutValidation + using (JSObject respHeaders = status.Headers) + { + if (respHeaders != null) { - if (respHeaders != null) + using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) { - using (var entriesIterator = (JSObject)respHeaders.Invoke("entries")) + JSObject? nextResult = null; + try { - JSObject? nextResult = null; - try + nextResult = (JSObject)entriesIterator.Invoke("next"); + while (!(bool)nextResult.GetObjectProperty("done")) { - nextResult = (JSObject)entriesIterator.Invoke("next"); - while (!(bool)nextResult.GetObjectProperty("done")) + using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) { - using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) - { - var name = (string)resultValue[0]; - var value = (string)resultValue[1]; - if (!httpResponse.Headers.TryAddWithoutValidation(name, value)) - httpResponse.Content.Headers.TryAddWithoutValidation(name, value); - } - nextResult?.Dispose(); - nextResult = (JSObject)entriesIterator.Invoke("next"); + var name = (string)resultValue[0]; + var value = (string)resultValue[1]; + if (!httpResponse.Headers.TryAddWithoutValidation(name, value)) + httpResponse.Content.Headers.TryAddWithoutValidation(name, value); } - } - finally - { nextResult?.Dispose(); + nextResult = (JSObject)entriesIterator.Invoke("next"); } } + finally + { + nextResult?.Dispose(); + } } } - - tcs.SetResult(httpResponse); - } - catch (JSException jsExc) - { - var httpExc = new System.Net.Http.HttpRequestException(jsExc.Message); - tcs.SetException(httpExc); - } - catch (Exception exception) - { - tcs.SetException(exception); } - return await tcs.Task.ConfigureAwait(continueOnCapturedContext: true); + return httpResponse; + + } + catch (JSException jsExc) + { + throw new System.Net.Http.HttpRequestException(jsExc.Message); } } From 641452948850d8627cacc2ac54c62fc1aab84d8d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 11 May 2020 08:20:01 +0200 Subject: [PATCH 73/99] code style change --- .../System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs index 684b4fe1fb03f2..a2d41cdc2851a5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/SocketsHttpHandler.cs @@ -130,7 +130,7 @@ public TimeSpan Expect100ContinueTimeout public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); - protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - => throw new PlatformNotSupportedException("Method SendAsync is not supported."); + protected internal override Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) => throw new PlatformNotSupportedException("Method SendAsync is not supported."); } } From a74d985ec028d359494a4467ef14311f28c5f6d0 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 11 May 2020 08:37:23 +0200 Subject: [PATCH 74/99] Remove disposing of _abortCts here as it causes runtime errors as being disposed of too early --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 1a6c96bedbb91a..a4cd2733b7d47c 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -317,8 +317,6 @@ protected virtual void Dispose(bool disposing) // Free any other managed objects here. // _abortCts.Cancel(); - _abortCts.Dispose(); - _abortRegistration.Dispose(); } From 4baf4263d78810a08bc9fd2c13de02484aaac548 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 11 May 2020 09:28:39 +0200 Subject: [PATCH 75/99] Address review comment for GetType() --- .../src/Interop/Browser/Interop.JavaScript.JSObject.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs index 6d74cd97ab64f0..57e6aad47f2e14 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -103,14 +103,7 @@ protected void FreeHandle() throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"); } - public override bool Equals(object? obj) - { - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - return JSHandle == (obj as JSObject)?.JSHandle; - } + public override bool Equals(object? obj) => obj is JSObject other && JSHandle == other.JSHandle; public override int GetHashCode() { From ea61992f9fed168effef71ab6dea414e7fb1a99f Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 12 May 2020 06:51:29 +0200 Subject: [PATCH 76/99] Rename library as per discussions --- ....csproj => System.Runtime.InteropServices.JavaScript.csproj} | 0 ...Script.sln => System.Runtime.InteropServices.JavaScript.sln} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/libraries/{System.Runtime.Interop.JavaScript.csproj => System.Runtime.InteropServices.JavaScript.csproj} (100%) rename src/libraries/{System.Runtime.Interop.JavaScript.sln => System.Runtime.InteropServices.JavaScript.sln} (85%) diff --git a/src/libraries/System.Runtime.Interop.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript.csproj similarity index 100% rename from src/libraries/System.Runtime.Interop.JavaScript.csproj rename to src/libraries/System.Runtime.InteropServices.JavaScript.csproj diff --git a/src/libraries/System.Runtime.Interop.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript.sln similarity index 85% rename from src/libraries/System.Runtime.Interop.JavaScript.sln rename to src/libraries/System.Runtime.InteropServices.JavaScript.sln index e2575e6a36c6f2..f790b33d4b6894 100644 --- a/src/libraries/System.Runtime.Interop.JavaScript.sln +++ b/src/libraries/System.Runtime.InteropServices.JavaScript.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.Interop.JavaScript", "System.Runtime.Interop.JavaScript.csproj", "{8871E4FB-3682-4DB3-B5B6-C4386860CA50}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.InteropServices.JavaScript", "System.Runtime.InteropServices.JavaScript.csproj", "{8871E4FB-3682-4DB3-B5B6-C4386860CA50}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 0ae1bf72ade99c4b4ee0179eb08a18a9d8c858da Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 12 May 2020 11:55:10 +0200 Subject: [PATCH 77/99] Add lock around the _boundObjects and _rawToJS access. - Address concurrency review comments --- .../Interop/Browser/Interop.Runtime.Bridge.cs | 140 ++++++++++-------- .../src/Interop/Browser/Interop.Runtime.cs | 35 +++-- 2 files changed, 97 insertions(+), 78 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index b9b7a909dbbe7e..48d6302a73ae55 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -21,18 +21,21 @@ internal static partial class Runtime internal static int BindJSObject(int jsId, Type mappedType) { - if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + lock (_boundObjects) { - if (mappedType != null) + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) { - return BindJSType(jsId, mappedType); - } - else - { - _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); + if (mappedType != null) + { + return BindJSType(jsId, mappedType); + } + else + { + _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); + } } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; } internal static int BindCoreCLRObject(int jsId, int gcHandle) @@ -40,86 +43,95 @@ internal static int BindCoreCLRObject(int jsId, int gcHandle) GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj; - if (_boundObjects.TryGetValue(jsId, out JSObject? existingObj)) + lock (_boundObjects) { - if (existingObj?.Handle != h && h.IsAllocated) - throw new JSException($"Multiple handles pointing at js_id: {jsId}"); + if (_boundObjects.TryGetValue(jsId, out JSObject? existingObj)) + { + if (existingObj?.Handle != h && h.IsAllocated) + throw new JSException($"Multiple handles pointing at js_id: {jsId}"); - obj = existingObj; - } - else - { - obj = h.Target as JSObject; + obj = existingObj; + } + else + { + obj = h.Target as JSObject; + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - - return obj == null ? 0 : (int)(IntPtr)obj.Handle; } internal static int BindJSType(int jsId, Type mappedType) { - if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + lock (_boundObjects) { - ConstructorInfo? jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, - null, new Type[] { typeof(IntPtr) }, null); - _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + ConstructorInfo? jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, + null, new Type[] { typeof(IntPtr) }, null); + _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; } internal static int UnBindJSObject(int jsId) { - if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) + lock (_boundObjects) { - _boundObjects.Remove(jsId); - return obj == null ? 0 : (int)(IntPtr)obj.Handle; + if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + _boundObjects.Remove(jsId); + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + return 0; } - - return 0; } internal static void UnBindJSObjectAndFree(int jsId) { - if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) + lock (_boundObjects) { - if (_boundObjects[jsId] != null) + if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) { - //bound_objects[jsIs].RawObject = null; - _boundObjects.Remove(jsId); - } - if (obj != null) - { - obj.JSHandle = -1; - obj.IsDisposed = true; - obj.RawObject = null; - obj.Handle.Free(); + if (_boundObjects[jsId] != null) + { + _boundObjects.Remove(jsId); + } + if (obj != null) + { + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + obj.Handle.Free(); + } } } - } internal static void UnBindRawJSObjectAndFree(int gcHandle) { - GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj = h.Target as JSObject; - if (obj?.RawObject != null) + lock (_rawToJS) { - _rawToJS.Remove(obj.RawObject); + if (obj?.RawObject != null) + { + _rawToJS.Remove(obj.RawObject); - int exception; - ReleaseHandle(obj.JSHandle, out exception); - if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' mono '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); + int exception; + ReleaseHandle(obj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' mono '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); - // Calling Release Handle above only removes the reference from the JavaScript side but does not - // release the bridged JSObject associated with the raw object so we have to do that ourselves. - obj.JSHandle = -1; - obj.IsDisposed = true; - obj.RawObject = null; + // Calling Release Handle above only removes the reference from the JavaScript side but does not + // release the bridged JSObject associated with the raw object so we have to do that ourselves. + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; - obj.Handle.Free(); + obj.Handle.Free(); + } } - } internal static object CreateTaskSource(int jsId) @@ -145,21 +157,25 @@ internal static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) internal static int BindExistingObject(object rawObj, int jsId) { JSObject? obj = rawObj as JSObject; + lock (_rawToJS) + { + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); - if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) - _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); - - return obj == null ? 0 : (int)(IntPtr)obj.Handle; + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } } internal static int GetJSObjectId(object rawObj) { JSObject? obj = rawObj as JSObject; + lock (_rawToJS) + { + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + return -1; - if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) - return -1; - - return obj?.JSHandle ?? -1; + return obj?.JSHandle ?? -1; + } } internal static object? GetMonoObject(int gcHandle) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index 0ef9c06574f0b5..a169ffab523139 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -101,26 +101,29 @@ public static int New(string hostClassName, params object[] parms) public static void FreeObject(object obj) { - if (_rawToJS.TryGetValue(obj, out JSObject? jsobj)) + lock (_rawToJS) { - //raw_to_js [obj].RawObject = null; - _rawToJS.Remove(obj); - if (jsobj != null) + if (_rawToJS.TryGetValue(obj, out JSObject? jsobj)) { - int exception; - Runtime.ReleaseObject(jsobj.JSHandle, out exception); - if (exception != 0) - throw new JSException($"Error releasing object on (raw-obj)"); + //raw_to_js [obj].RawObject = null; + _rawToJS.Remove(obj); + if (jsobj != null) + { + int exception; + Runtime.ReleaseObject(jsobj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing object on (raw-obj)"); - jsobj.JSHandle = -1; - jsobj.RawObject = null; - jsobj.IsDisposed = true; - jsobj.Handle.Free(); + jsobj.JSHandle = -1; + jsobj.RawObject = null; + jsobj.IsDisposed = true; + jsobj.Handle.Free(); + } + } + else + { + throw new JSException($"Error releasing object on (obj)"); } - } - else - { - throw new JSException($"Error releasing object on (obj)"); } } From 63b4e1e5a3fbbb271efb65309d0cedd2a1ecabcd Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Fri, 15 May 2020 14:18:04 +0200 Subject: [PATCH 78/99] Address review comments for disposing of CancellationTokenSource --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index a4cd2733b7d47c..9f6cba989d6fd3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -199,6 +199,7 @@ protected internal override async Task SendAsync(HttpReques abortController?.Dispose(); } wasmHttpReadStream?.Dispose(); + abortCts.Dispose(); })); var args = new Interop.JavaScript.Array(); @@ -317,6 +318,7 @@ protected virtual void Dispose(bool disposing) // Free any other managed objects here. // _abortCts.Cancel(); + _abortCts.Dispose(); _abortRegistration.Dispose(); } From 648e61c9ef6e8d7b7fded0fdd1fa5b1ab7ab01df Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 18 May 2020 11:19:40 +0200 Subject: [PATCH 79/99] Address new HttpContent overloads in .NET 5 that take CancellationToken --- .../Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 9f6cba989d6fd3..35e16a74c114a2 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -365,12 +365,13 @@ protected override async Task CreateContentReadStreamAsync() return new MemoryStream(data, writable: false); } - protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context) + protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) => + SerializeToStreamAsync(stream, context, CancellationToken.None); + protected sealed override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) { byte[] data = await GetResponseData().ConfigureAwait(continueOnCapturedContext: true); - await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(continueOnCapturedContext: true); + await stream.WriteAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(continueOnCapturedContext: true); } - protected internal override bool TryComputeLength(out long length) { if (_data != null) From dd05832ac6792847e92532d7eda7a0179d0f97ae Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 19 May 2020 12:53:16 +0200 Subject: [PATCH 80/99] Add project and test structure for JavaScript InteropServices --- ...tem.Runtime.InteropServices.JavaScript.sln | 17 ------ .../Directory.Build.props | 7 +++ ...tem.Runtime.InteropServices.JavaScript.sln | 61 +++++++++++++++++++ .../src/Runtime.cs | 9 +++ ....Runtime.InteropServices.JavaScript.csproj | 35 +++++------ .../tests/AssemblyInfo.cs | 7 +++ ...me.InteropServices.JavaScript.Tests.csproj | 11 ++++ .../JavaScript/JavaScriptTests.cs | 12 ++++ 8 files changed, 122 insertions(+), 37 deletions(-) delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript.sln create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/Directory.Build.props create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs rename src/libraries/{ => System.Runtime.InteropServices.JavaScript/src}/System.Runtime.InteropServices.JavaScript.csproj (69%) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript.sln deleted file mode 100644 index f790b33d4b6894..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript.sln +++ /dev/null @@ -1,17 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.InteropServices.JavaScript", "System.Runtime.InteropServices.JavaScript.csproj", "{8871E4FB-3682-4DB3-B5B6-C4386860CA50}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8871E4FB-3682-4DB3-B5B6-C4386860CA50}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/Directory.Build.props b/src/libraries/System.Runtime.InteropServices.JavaScript/Directory.Build.props new file mode 100644 index 00000000000000..465e1110d6b07f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/Directory.Build.props @@ -0,0 +1,7 @@ + + + + Microsoft + true + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln new file mode 100644 index 00000000000000..284e65cfede9f7 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln @@ -0,0 +1,61 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27213.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript.Tests", "tests\System.Runtime.InteropServices.JavaScript.Tests.csproj", "{7A93B8A0-E663-4CB1-B6A5-850E5EC56146}" + ProjectSection(ProjectDependencies) = postProject + {4AC5343E-6E31-4BA5-A795-0493AE7E9008} = {4AC5343E-6E31-4BA5-A795-0493AE7E9008} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript", "src\System.Runtime.InteropServices.JavaScript.csproj", "{4AC5343E-6E31-4BA5-A795-0493AE7E9008}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Release|Any CPU.Build.0 = Release|Any CPU + {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.Build.0 = Release|Any CPU + {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.Build.0 = Release|Any CPU + {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Release|Any CPU.Build.0 = Release|Any CPU + {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Release|Any CPU.Build.0 = Release|Any CPU + {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {7A93B8A0-E663-4CB1-B6A5-850E5EC56146} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {96AF3242-A368-4F13-B006-A722CC3B8517} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {4AC5343E-6E31-4BA5-A795-0493AE7E9008} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {375E5A4A-9026-411E-9CD8-606874CFF7F0} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2151451C-8B2A-44DB-881E-B922A4795A30} + EndGlobalSection +EndGlobal diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs new file mode 100644 index 00000000000000..96604df6296eda --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Runtime.InteropServices.JavaScript +{ + public static partial class Runtime + { } +} \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj similarity index 69% rename from src/libraries/System.Runtime.InteropServices.JavaScript.csproj rename to src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 2cc201ddb914d4..41778e2ef3b8ac 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -1,26 +1,18 @@ - + - Library - System.Runtime.Interop.JavaScript - win - - $(NoWarn);0436;CS1573 + System.Runtime.InteropServices.JavaScript true - $(DefineConstants) - $(NetCoreAppCurrent)-Browser enable - - - $(DefineConstants) + $(NetCoreAppCurrent)-Browser - - - - - - - + + + + + + + @@ -36,5 +28,8 @@ - - \ No newline at end of file + + + + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs new file mode 100644 index 00000000000000..92a90f9cd99157 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34748", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj new file mode 100644 index 00000000000000..8fbb905857b794 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.Tests.csproj @@ -0,0 +1,11 @@ + + + true + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser + true + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs new file mode 100644 index 00000000000000..9fe5ea566419ad --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/JavaScriptTests.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Runtime.InteropServices.JavaScript.Tests +{ + public static class JavaScriptTests + { + } +} From 796692bd05ec713a8cfe2e8dc37d4f5ecc7fa72c Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 07:05:44 +0200 Subject: [PATCH 81/99] Remove previous modification as it is no longer needed. --- .../src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs index 398f3ead983367..7f88b005bfdfd6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -178,7 +178,6 @@ private static async ValueTask EstablishSslConnectionAsyncCore(Stream return sslStream; } -#if !BROWSER_DOES_NOT_SUPPORT_QUIC public static async ValueTask ConnectQuicAsync(string host, int port, SslClientAuthenticationOptions? clientAuthenticationOptions, CancellationToken cancellationToken) { IPAddress[] addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false); @@ -208,7 +207,7 @@ public static async ValueTask ConnectQuicAsync(string host, int // TODO: find correct exception to throw here. throw new HttpRequestException("No host found."); } -#endif + private static Exception CreateWrappedException(Exception error, CancellationToken cancellationToken) { return CancellationHelper.ShouldWrapInOperationCanceledException(error, cancellationToken) ? From df1be16ae6de77c7be608f3af23d6f3bdef84f84 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 07:09:46 +0200 Subject: [PATCH 82/99] Update src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs Co-authored-by: Stephen Toub --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 35e16a74c114a2..993306602369b9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -220,7 +220,7 @@ protected internal override async Task SendAsync(HttpReques var status = new WasmFetchResponse(t, abortController, abortCts, abortRegistration); - HttpResponseMessage httpResponse = new HttpResponseMessage((HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), status.Status.ToString())); + HttpResponseMessage httpResponse = new HttpResponseMessage((HttpStatusCode)status.Status); bool streamingEnabled = request.Properties.TryGetValue("WebAssemblyEnableStreamingResponse", out object? streamingEnabledValue) && (bool)(streamingEnabledValue ?? false); From e206e4b599553e2f5f0fd29da15578207c5b340b Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 07:15:56 +0200 Subject: [PATCH 83/99] Remove custom message passed to the PNSE .ctor --- .../BrowserHttpHandler/BrowserHttpHandler.cs | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 993306602369b9..23dd6de52a2874 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -46,83 +46,83 @@ static BrowserHttpHandler() public bool UseCookies { - get => throw new PlatformNotSupportedException("Property UseCookies is not supported."); - set => throw new PlatformNotSupportedException("Property UseCookies is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public CookieContainer CookieContainer { - get => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); - set => throw new PlatformNotSupportedException("Property CookieContainer is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public DecompressionMethods AutomaticDecompression { - get => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); - set => throw new PlatformNotSupportedException("Property AutomaticDecompression is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public bool UseProxy { - get => throw new PlatformNotSupportedException("Property UseProxy is not supported."); - set => throw new PlatformNotSupportedException("Property UseProxy is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public IWebProxy? Proxy { - get => throw new PlatformNotSupportedException("Property Proxy is not supported."); - set => throw new PlatformNotSupportedException("Property Proxy is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public ICredentials? DefaultProxyCredentials { - get => throw new PlatformNotSupportedException("Property Credentials is not supported."); - set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public bool PreAuthenticate { - get => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); - set => throw new PlatformNotSupportedException("Property PreAuthenticate is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public ICredentials? Credentials { - get => throw new PlatformNotSupportedException("Property Credentials is not supported."); - set => throw new PlatformNotSupportedException("Property Credentials is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public bool AllowAutoRedirect { - get => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); - set => throw new PlatformNotSupportedException("Property AllowAutoRedirect is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public int MaxAutomaticRedirections { - get => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); - set => throw new PlatformNotSupportedException("Property MaxAutomaticRedirections is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public int MaxConnectionsPerServer { - get => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); - set => throw new PlatformNotSupportedException("Property MaxConnectionsPerServer is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public int MaxResponseHeadersLength { - get => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); - set => throw new PlatformNotSupportedException("Property MaxResponseHeadersLength is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } public SslClientAuthenticationOptions SslOptions { - get => throw new PlatformNotSupportedException("Property SslOptions is not supported."); - set => throw new PlatformNotSupportedException("Property SslOptions is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); } - public IDictionary Properties => throw new PlatformNotSupportedException("Property Properties is not supported."); + public IDictionary Properties => throw new PlatformNotSupportedException(); protected internal override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { From 704bf8a7bcd56aff94181fdc7b763bf980f28b05 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 08:29:10 +0200 Subject: [PATCH 84/99] Update `mono` to dotnet or .NET --- .../src/Interop/Browser/Interop.JavaScript.JSObject.cs | 6 +++--- .../Common/src/Interop/Browser/Interop.Runtime.Bridge.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs index 57e6aad47f2e14..11990145f6ab56 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs @@ -68,7 +68,7 @@ public void SetObjectProperty(string name, object value, bool createIfNotExists object setPropResult = Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); if (exception != 0) - throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"); + throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); } @@ -100,7 +100,7 @@ protected void FreeHandle() { Runtime.ReleaseHandle(JSHandle, out int exception); if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"); + throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); } public override bool Equals(object? obj) => obj is JSObject other && JSHandle == other.JSHandle; @@ -147,7 +147,7 @@ protected virtual void Dispose(bool disposing) public override string ToString() { - return $"(js-obj js '{JSHandle}' mono '{(IntPtr)Handle} raw '{RawObject != null})"; + return $"(js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"; } } diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 48d6302a73ae55..5a9b090da1b831 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -121,7 +121,7 @@ internal static void UnBindRawJSObjectAndFree(int gcHandle) int exception; ReleaseHandle(obj.JSHandle, out exception); if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' mono '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); + throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' .NET '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); // Calling Release Handle above only removes the reference from the JavaScript side but does not // release the bridged JSObject associated with the raw object so we have to do that ourselves. @@ -178,7 +178,7 @@ internal static int GetJSObjectId(object rawObj) } } - internal static object? GetMonoObject(int gcHandle) + internal static object? GetDotNetObject(int gcHandle) { GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? o = h.Target as JSObject; From 33222ea073412e8c16b54dc7da9af2eeb31f7bd4 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 08:48:40 +0200 Subject: [PATCH 85/99] Address review comment for internal sealed class --- .../System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 23dd6de52a2874..827b19af4844fb 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -27,7 +27,7 @@ namespace System.Net.Http // remoted back to the main thread. Most APIs only work on the main browser thread. // During discussions the concensus has been that it will not matter right now which value is used for ConfigureAwait // we should put this in place now. - internal partial class BrowserHttpHandler : HttpMessageHandler + internal sealed class BrowserHttpHandler : HttpMessageHandler { // This partial implementation contains members common to Browser WebAssembly running on .NET Core. private static readonly JSObject? s_fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); From 79e32876fd99edb7d1d024d4a163967e537638d8 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 08:52:52 +0200 Subject: [PATCH 86/99] Address extra message text --- .../Common/src/Interop/Browser/Interop.Runtime.Bridge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index 5a9b090da1b831..d0f8d73351ec80 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -345,7 +345,7 @@ internal static string ObjectToString(object o) internal static double GetDateValue(object dtv) { if (dtv == null) - throw new ArgumentNullException(nameof(dtv), "Value can not be null"); + throw new ArgumentNullException(nameof(dtv)); if (!(dtv is DateTime dt)) throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); if (dt.Kind == DateTimeKind.Local) From fbb6e6f6ab88d783c1ba33fe52eaebfdeca8dc9f Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 13:01:54 +0200 Subject: [PATCH 87/99] Move these source modules to System.Runtime.InteropServices.JavaScript --- .../Browser/Interop.JavaScript.Array.cs | 104 --------- .../Browser/Interop.JavaScript.ArrayBuffer.cs | 61 ------ .../Browser/Interop.JavaScript.CoreObject.cs | 37 ---- .../Browser/Interop.JavaScript.Function.cs | 59 ------ .../Browser/Interop.JavaScript.HostObject.cs | 44 ---- .../Browser/Interop.JavaScript.JSException.cs | 19 -- .../Browser/Interop.JavaScript.JSObject.cs | 155 -------------- .../Interop.JavaScript.SharedArrayBuffer.cs | 40 ---- .../Browser/Interop.JavaScript.TypedArray.cs | 199 ------------------ .../Browser/Interop.JavaScript.Uint8Array.cs | 52 ----- .../Interop.JavaScript.Uint8ClampedArray.cs | 52 ----- .../Interop/Browser/Interop.Runtime.AnyRef.cs | 30 --- 12 files changed, 852 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs deleted file mode 100644 index 0c03508ac57afa..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Array.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - /// - /// Initializes a new instance of JavaScript Core Array class. - /// - public class Array : CoreObject - { - /// - /// Initializes a new instance of the Array class. - /// - /// Parameters. - public Array(params object[] _params) : base(Runtime.New(_params)) - { } - - /// - /// Initializes a new instance of the Array/> class. - /// - /// Js handle. - internal Array(IntPtr jsHandle) : base(jsHandle) - { } - - /// - /// Push the specified elements. - /// - /// The new length of the Array push was called on - /// Elements. - public int Push(params object[] elements) => (int)Invoke("push", elements); - - /// - /// Pop this instance. - /// - /// The element removed from the array or null if the array was empty - public object Pop() => (object)Invoke("pop"); - - /// - /// Remove the first element of the Array and return that element - /// - /// The removed element - public object Shift() => Invoke("shift"); - - /// - /// Add to the array starting at index 0 - /// - /// The length after shift. - /// Elements. - public int UnShift(params object[] elements) => (int)Invoke("unshift", elements); - - /// - /// Index of the search element. - /// - /// The index of first occurrence of searchElement in the Array or -1 if not Found - /// Search element. - /// The index to start the search from - public int IndexOf(object searchElement, int fromIndex = 0) => (int)Invoke("indexOf", searchElement, fromIndex); - - /// - /// Finds the index of the last occurrence of - /// - /// The index of the last occurrence - /// Search element. - public int LastIndexOf(object searchElement) => (int)Invoke("lastIndexOf", searchElement); - - /// - /// Finds the index of the last occurrence of between 0 and . - /// - /// The index of the last occurrence. - /// Search element. - /// End index. - public int LastIndexOf(object searchElement, int endIndex) => (int)Invoke("lastIndexOf", searchElement, endIndex); - - /// - /// Gets or sets the Array with the index specified by . - /// - /// The index. - public object this[int i] - { - get - { - object indexValue = Runtime.GetByIndex(JSHandle, i, out int exception); - - if (exception != 0) - throw new JSException((string)indexValue); - return indexValue; - } - set - { - object res = Runtime.SetByIndex(JSHandle, i, value, out int exception); - - if (exception != 0) - throw new JSException((string)res); - - } - } - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs deleted file mode 100644 index e3c75647eb511b..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.ArrayBuffer.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - public class ArrayBuffer : CoreObject - { - /// - /// Initializes a new instance of the JavaScript Core ArrayBuffer class. - /// - public ArrayBuffer() : base(Runtime.New()) - { } - - /// - /// Initializes a new instance of the JavaScript Core ArrayBuffer class. - /// - /// Length. - public ArrayBuffer(int length) : base(Runtime.New(length)) - { } - - /// - /// Initializes a new instance of the JavaScript Core ArrayBuffer class. - /// - /// Js handle. - internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle) - { } - - /// - /// The length of an ArrayBuffer in bytes. - /// - /// The length of the underlying ArrayBuffer in bytes. - public int ByteLength => (int)GetObjectProperty("byteLength"); - - /// - /// Gets a value indicating whether this ArrayBuffer is view. - /// - /// true if is view; otherwise, false. - public bool IsView => (bool)GetObjectProperty("isView"); - - /// - /// Slice the specified begin. - /// - /// The slice. - /// Begin. - public ArrayBuffer Slice(int begin) => (ArrayBuffer)Invoke("slice", begin); - - /// - /// Slice the specified begin and end. - /// - /// The slice. - /// Begin. - /// End. - public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)Invoke("slice", begin, end); - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs deleted file mode 100644 index e87f5a238c2e4e..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.CoreObject.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - /// - /// Core objects are the standard built-in objects and functions. - /// - /// - /// These objects are part of the JavaScript environment. Not to be confused - /// with objects provided by the host application or the browser context - /// such as DOM. For more information about the distinction between the - /// DOM and core JavaScript, see JavaScript technologies overview: - /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview - /// - /// Core objects are treated differently in the bridge code as they are - /// guaranteed to be there. - /// - public abstract class CoreObject : JSObject - { - protected CoreObject(int jsHandle) : base(jsHandle) - { - object result = Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); - if (exception != 0) - throw new JSException($"CoreObject Error binding: {result}"); - } - - internal CoreObject(IntPtr js_handle) : base(js_handle) - { } - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs deleted file mode 100644 index d9f8b976b34d8d..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Function.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - /// - /// The Function constructor creates a new Function object. - /// - /// - /// Calling the constructor directly can create functions dynamically, but suffers from security and similar - /// (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor - /// allows executing code in the global scope, prompting better programming habits and allowing for more efficient - /// code minification. - /// - internal static partial class JavaScript - { - public class Function : CoreObject - { - public Function(params object[] args) : base(Runtime.New(args)) - { } - - internal Function(IntPtr jHandle) : base(jHandle) - { } - - /// - /// The Apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object). - /// - /// The apply. - /// This argument. - /// Arguments. - public object Apply(object? thisArg = null, object[]? argsArray = null) => Invoke("apply", thisArg, argsArray); - - /// - /// Creates a new Function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. - /// - /// The bind. - /// This argument. - /// Arguments. - public Function Bind(object? thisArg = null, object[]? argsArray = null) => (Function)Invoke("bind", thisArg, argsArray); - - /// - /// Calls a function with a given `this` value and arguments provided individually. - /// - /// The result of calling the function with the specified `this` value and arguments. - /// Optional (null value). The value of this provided for the call to a function. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects. - /// Optional. Arguments for the function. - public object Call(object? thisArg = null, params object[] argsArray) - { - object?[] argsList = new object[argsArray.Length + 1]; - argsList[0] = thisArg; - System.Array.Copy(argsArray, 0, argsList, 1, argsArray.Length); - return Invoke("call", argsList); - } - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs deleted file mode 100644 index c38d58f48e7230..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.HostObject.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - /// - /// Host objects are object supplied by the host environment. - /// - /// - /// These objects are not part of the JavaScript environment and provided by the host application - /// or the browser context such as DOM. For more information about the distinction between the - /// DOM and core JavaScript, see JavaScript technologies overview: - /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview - /// - /// Host objects are treated differently in the bridge code as they are not guaranteed to exist. - /// - public interface IHostObject - { } - - public class HostObject : HostObjectBase - { - public HostObject(string hostName, params object[] _params) : base(Runtime.New(hostName, _params)) - { } - } - - public abstract class HostObjectBase : JSObject, IHostObject - { - protected HostObjectBase(int jHandle) : base(jHandle) - { - object result = Runtime.BindHostObject(jHandle, (int)(IntPtr)Handle, out int exception); - if (exception != 0) - throw new JSException($"HostObject Error binding: {result}"); - } - - internal HostObjectBase(IntPtr js_handle) : base(js_handle) - { } - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs deleted file mode 100644 index 48fa2d503d1afa..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSException.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - /// - /// Represents an Exception initiated from the JavaScript interop code. - /// - public class JSException : Exception - { - public JSException(string msg) : base(msg) { } - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs deleted file mode 100644 index 11990145f6ab56..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.JSObject.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - public interface IJSObject - { - int JSHandle { get; } - int Length { get; } - } - /// - /// JSObjects are wrappers for a native JavaScript object, and - /// they retain a reference to the JavaScript object for the lifetime of this C# object. - /// - public class JSObject : Interop.Runtime.AnyRef, IJSObject, IDisposable - { - internal object? RawObject; - - // to detect redundant calls - public bool IsDisposed { get; internal set; } - - public JSObject() : this(Runtime.New()) - { - object result = Runtime.BindCoreObject(JSHandle, (int)(IntPtr)Handle, out int exception); - if (exception != 0) - throw new JSException($"JSObject Error binding: {result}"); - - } - - internal JSObject(IntPtr js_handle) : base(js_handle) - { } - - internal JSObject(int js_handle) : base((IntPtr)js_handle) - { } - - internal JSObject(int js_handle, object raw_obj) : base(js_handle) - { - RawObject = raw_obj; - } - - public object Invoke(string method, params object?[] args) - { - object res = Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); - if (exception != 0) - throw new JSException((string)res); - return res; - } - - public object GetObjectProperty(string name) - { - - object propertyValue = Runtime.GetObjectProperty(JSHandle, name, out int exception); - - if (exception != 0) - throw new JSException((string)propertyValue); - - return propertyValue; - - } - - public void SetObjectProperty(string name, object value, bool createIfNotExists = true, bool hasOwnProperty = false) - { - - object setPropResult = Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); - if (exception != 0) - throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); - - } - - /// - /// Gets or sets the length. - /// - /// The length. - public int Length - { - get => Convert.ToInt32(GetObjectProperty("length")); - set => SetObjectProperty("length", value, false); - } - - /// - /// Returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it). - /// - /// true, if the object has the specified property as own property, false otherwise. - /// The String name or Symbol of the property to test. - public bool HasOwnProperty(string prop) => (bool)Invoke("hasOwnProperty", prop); - - /// - /// Returns a boolean indicating whether the specified property is enumerable. - /// - /// true, if the specified property is enumerable, false otherwise. - /// The String name or Symbol of the property to test. - public bool PropertyIsEnumerable(string prop) => (bool)Invoke("propertyIsEnumerable", prop); - - protected void FreeHandle() - { - Runtime.ReleaseHandle(JSHandle, out int exception); - if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); - } - - public override bool Equals(object? obj) => obj is JSObject other && JSHandle == other.JSHandle; - - public override int GetHashCode() - { - return JSHandle; - } - - ~JSObject() - { - Dispose(false); - } - - public void Dispose() - { - // Dispose of unmanaged resources. - Dispose(true); - // Suppress finalization. - GC.SuppressFinalize(this); - } - - // Protected implementation of Dispose pattern. - protected virtual void Dispose(bool disposing) - { - - if (!IsDisposed) - { - if (disposing) - { - - // Free any other managed objects here. - // - RawObject = null; - } - - IsDisposed = true; - - // Free any unmanaged objects here. - FreeHandle(); - - } - } - - public override string ToString() - { - return $"(js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"; - } - - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs deleted file mode 100644 index 644458d1e04118..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.SharedArrayBuffer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - public class SharedArrayBuffer : CoreObject - { - /// - /// Initializes a new instance of the JavaScript Core SharedArrayBuffer class. - /// - /// The size, in bytes, of the array buffer to create. - public SharedArrayBuffer(int length) : base(Runtime.New(length)) - { } - - internal SharedArrayBuffer(IntPtr js_handle) : base(js_handle) - { } - - /// - /// The size, in bytes, of the array. This is established when the array is constructed and cannot be changed. - /// - /// The size, in bytes, of the array. - public int ByteLength => (int)GetObjectProperty("byteLength"); - - /// - /// Returns a new JavaScript Core SharedArrayBuffer whose contents are a copy of this SharedArrayBuffer's bytes from begin, - /// inclusive, up to end, exclusive. If either begin or end is negative, it refers to an index from the end - /// of the array, as opposed to from the beginning. - /// - /// a new JavaScript Core SharedArrayBuffer - /// Beginning index of copy. - /// Ending index, exclusive. - public SharedArrayBuffer Slice(int begin, int end) => (SharedArrayBuffer)Invoke("slice", begin, end); - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs deleted file mode 100644 index aee87d4b48e7db..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.TypedArray.cs +++ /dev/null @@ -1,199 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - public interface ITypedArray - { - int BytesPerElement { get; } - string Name { get; } - int ByteLength { get; } - ArrayBuffer Buffer { get; } - - void Set(Array array); - void Set(Array array, int offset); - - void Set(ITypedArray typedArray); - void Set(ITypedArray typedArray, int offset); - } - - public interface ITypedArray where U : struct - { - T Slice(); - T Slice(int begin); - T Slice(int begin, int end); - - T SubArray(); - T SubArray(int begin); - T SubArray(int begin, int end); - } - - public enum TypedArrayTypeCode - { - Int8Array = 5, - Uint8Array = 6, - Int16Array = 7, - Uint16Array = 8, - Int32Array = 9, - Uint32Array = 10, - Float32Array = 13, - Float64Array = 14, - Uint8ClampedArray = 0xF, - } - - /// - /// Represents a JavaScript TypedArray. - /// - public abstract class TypedArray : CoreObject, ITypedArray, ITypedArray where U : struct - { - protected TypedArray() : base(Runtime.New()) - { } - - protected TypedArray(int length) : base(Runtime.New(length)) - { } - - protected TypedArray(ArrayBuffer buffer) : base(Runtime.New(buffer)) - { } - - protected TypedArray(ArrayBuffer buffer, int byteOffset) : base(Runtime.New(buffer, byteOffset)) - { } - - protected TypedArray(ArrayBuffer buffer, int byteOffset, int length) : base(Runtime.New(buffer, byteOffset, length)) - { } - - protected TypedArray(SharedArrayBuffer buffer) : base(Runtime.New(buffer)) - { } - - protected TypedArray(SharedArrayBuffer buffer, int byteOffset) : base(Runtime.New(buffer, byteOffset)) - { } - - protected TypedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(Runtime.New(buffer, byteOffset, length)) - { } - - internal TypedArray(IntPtr jsHandle) : base(jsHandle) - { } - - public int BytesPerElement => (int)GetObjectProperty("BYTES_PER_ELEMENT"); - public string Name => (string)GetObjectProperty("name"); - public int ByteLength => (int)GetObjectProperty("byteLength"); - public ArrayBuffer Buffer => (ArrayBuffer)GetObjectProperty("buffer"); - - public void Fill(U value) => Invoke("fill", value); - public void Fill(U value, int start) => Invoke("fill", value, start); - public void Fill(U value, int start, int end) => Invoke("fill", value, start, end); - - public void Set(Array array) => Invoke("set", array); - public void Set(Array array, int offset) => Invoke("set", array, offset); - public void Set(ITypedArray typedArray) => Invoke("set", typedArray); - public void Set(ITypedArray typedArray, int offset) => Invoke("set", typedArray, offset); - - public T Slice() => (T)Invoke("slice"); - public T Slice(int begin) => (T)Invoke("slice", begin); - public T Slice(int begin, int end) => (T)Invoke("slice", begin, end); - - public T SubArray() => (T)Invoke("subarray"); - public T SubArray(int begin) => (T)Invoke("subarray", begin); - public T SubArray(int begin, int end) => (T)Invoke("subarray", begin, end); - - public U? this[int i] - { - get - { - object jsValue = Runtime.GetByIndex(JSHandle, i, out int exception); - - if (exception != 0) - throw new JSException((string)jsValue); - - // The value returned from the index. - return UnBoxValue(jsValue); - } - set - { - object res = Runtime.SetByIndex(JSHandle, i, value, out int exception); - - if (exception != 0) - throw new JSException((string)res); - - } - } - - private U? UnBoxValue(object jsValue) - { - if (jsValue == null) - return null; - - Type type = jsValue.GetType(); - return (U)Convert.ChangeType(jsValue, typeof(U)); - } - - public U[] ToArray() - { - object res = Runtime.TypedArrayToArray(JSHandle, out int exception); - - if (exception != 0) - throw new JSException((string)res); - return (U[])res; - } - - public static unsafe T From(ReadOnlySpan span) - { - // source has to be instantiated. - if (span == null) - { - throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null."); - } - - TypedArrayTypeCode type = (TypedArrayTypeCode)Type.GetTypeCode(typeof(U)); - // Special case for Uint8ClampedArray, a clamped array which represents an array of 8-bit unsigned integers clamped to 0-255; - if (type == TypedArrayTypeCode.Uint8Array && typeof(T) == typeof(Uint8ClampedArray)) - type = TypedArrayTypeCode.Uint8ClampedArray; // This is only passed to the JavaScript side so it knows it will be a Uint8ClampedArray - - ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); - fixed (byte* ptr = bytes) - { - object res = Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Marshal.SizeOf(), (int)type, out int exception); - if (exception != 0) - throw new JSException((string)res); - return (T)res; - } - - } - - public unsafe int CopyTo(Span span) - { - ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); - fixed (byte* ptr = bytes) - { - object res = Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); - if (exception != 0) - throw new JSException((string)res); - return (int)res / Marshal.SizeOf(); - } - } - - public unsafe int CopyFrom(ReadOnlySpan span) - { - // source has to be instantiated. - if (span == null || span.Length == 0) - { - throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null and must have a length"); - } - - ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); - fixed (byte* ptr = bytes) - { - object res = Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); - if (exception != 0) - throw new JSException((string)res); - return (int)res / Marshal.SizeOf(); - } - } - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs deleted file mode 100644 index 8f2930593ed063..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8Array.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - public sealed class Uint8Array : TypedArray - { - /// - /// Initializes a new instance of the JavaScript Core Uint8Array class. - /// - public Uint8Array() - { } - - public Uint8Array(int length) : base(length) - { } - - - public Uint8Array(ArrayBuffer buffer) : base(buffer) - { } - - public Uint8Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) - { } - - public Uint8Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) - { } - - public Uint8Array(SharedArrayBuffer buffer) : base(buffer) - { } - - public Uint8Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) - { } - - public Uint8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) - { } - - internal Uint8Array(IntPtr js_handle) : base(js_handle) - { } - - /// - /// Defines an implicit conversion of JavaScript Core Uint8Array class to a Span<byte> - /// - public static implicit operator Span(Uint8Array typedarray) => typedarray.ToArray(); - - public static implicit operator Uint8Array(Span span) => From(span); - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs b/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs deleted file mode 100644 index c8159439849af2..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.JavaScript.Uint8ClampedArray.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -internal static partial class Interop -{ - internal static partial class JavaScript - { - public sealed class Uint8ClampedArray : TypedArray - { - /// - /// Initializes a new instance of the JavaScript Core Uint8ClampedArray class. - /// - public Uint8ClampedArray() - { } - - public Uint8ClampedArray(int length) : base(length) - { } - - - public Uint8ClampedArray(ArrayBuffer buffer) : base(buffer) - { } - - public Uint8ClampedArray(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) - { } - - public Uint8ClampedArray(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) - { } - - public Uint8ClampedArray(SharedArrayBuffer buffer) : base(buffer) - { } - - public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) - { } - - public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) - { } - - internal Uint8ClampedArray(IntPtr js_handle) : base(js_handle) - { } - - /// - /// Defines an implicit conversion of JavaScript Core Uint8ClampedArray class to a Span<byte> - /// - public static implicit operator Span(Uint8ClampedArray typedarray) => typedarray.ToArray(); - - public static implicit operator Uint8ClampedArray(Span span) => From(span); - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs deleted file mode 100644 index feb442514c9943..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.AnyRef.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Runtime - { - public class AnyRef - { - public int JSHandle { get; internal set; } - internal GCHandle Handle; - - internal AnyRef(int jsHandle) - { - JSHandle = jsHandle; - Handle = GCHandle.Alloc(this); - } - - internal AnyRef(IntPtr jsHandle) - { - JSHandle = (int)jsHandle; - Handle = GCHandle.Alloc(this); - } - } - } -} From 6614009f7591e2b778e7ec7de81e73c31821e4f6 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 13:03:02 +0200 Subject: [PATCH 88/99] Add interop source modules to System.Runtime.InteropServices.JavaScript project --- .../Interop/Browser/Interop.Runtime.Bridge.cs | 4 +- .../src/Interop/Browser/Interop.Runtime.cs | 8 +- ....Runtime.InteropServices.JavaScript.csproj | 29 +-- .../InteropServices/JavaScript/AnyRef.cs | 27 +++ .../InteropServices/JavaScript/Array.cs | 101 +++++++++ .../InteropServices/JavaScript/ArrayBuffer.cs | 58 ++++++ .../InteropServices/JavaScript/CoreObject.cs | 35 ++++ .../InteropServices/JavaScript/Function.cs | 56 +++++ .../InteropServices/JavaScript/HostObject.cs | 41 ++++ .../InteropServices/JavaScript/JSException.cs | 16 ++ .../InteropServices/JavaScript/JSObject.cs | 152 ++++++++++++++ .../InteropServices/JavaScript/Runtime.cs | 47 +++++ .../JavaScript/SharedArrayBuffer.cs | 37 ++++ .../InteropServices/JavaScript/TypedArray.cs | 196 ++++++++++++++++++ .../InteropServices/JavaScript/Uint8Array.cs | 49 +++++ .../JavaScript/Uint8ClampedArray.cs | 49 +++++ 16 files changed, 885 insertions(+), 20 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs index d0f8d73351ec80..31a495be579f5d 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; -using JSObject = Interop.JavaScript.JSObject; -using JSException = Interop.JavaScript.JSException; +using JSObject = System.Runtime.InteropServices.JavaScript.JSObject; +using JSException = System.Runtime.InteropServices.JavaScript.JSException; internal static partial class Interop { diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index a169ffab523139..f7a60d8f9bcae6 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -9,8 +9,8 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; -using JSObject = Interop.JavaScript.JSObject; -using JSException = Interop.JavaScript.JSException; +using JSObject = System.Runtime.InteropServices.JavaScript.JSObject; +using JSException = System.Runtime.InteropServices.JavaScript.JSException; internal static partial class Interop { @@ -67,12 +67,12 @@ public static string InvokeJS(string str) return res; } - public static Interop.JavaScript.Function? CompileFunction(string snippet) + public static System.Runtime.InteropServices.JavaScript.Function? CompileFunction(string snippet) { object res = CompileFunction(snippet, out int exception); if (exception != 0) throw new JSException((string)res); - return res as Interop.JavaScript.Function; + return res as System.Runtime.InteropServices.JavaScript.Function; } public static int New(params object[] parms) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 41778e2ef3b8ac..0c19e0c9dace3b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -16,20 +16,21 @@ - - - - - - - - - - - - + + + + + + + + + + + + + - + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs new file mode 100644 index 00000000000000..fa7a1675205402 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace System.Runtime.InteropServices.JavaScript +{ + public class AnyRef + { + public int JSHandle { get; internal set; } + internal GCHandle Handle; + + internal AnyRef(int jsHandle) + { + JSHandle = jsHandle; + Handle = GCHandle.Alloc(this); + } + + internal AnyRef(IntPtr jsHandle) + { + JSHandle = (int)jsHandle; + Handle = GCHandle.Alloc(this); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs new file mode 100644 index 00000000000000..6be725222fd79c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + /// + /// Initializes a new instance of JavaScript Core Array class. + /// + public class Array : CoreObject + { + /// + /// Initializes a new instance of the Array class. + /// + /// Parameters. + public Array(params object[] _params) : base(Interop.Runtime.New(_params)) + { } + + /// + /// Initializes a new instance of the Array/> class. + /// + /// Js handle. + internal Array(IntPtr jsHandle) : base(jsHandle) + { } + + /// + /// Push the specified elements. + /// + /// The new length of the Array push was called on + /// Elements. + public int Push(params object[] elements) => (int)Invoke("push", elements); + + /// + /// Pop this instance. + /// + /// The element removed from the array or null if the array was empty + public object Pop() => (object)Invoke("pop"); + + /// + /// Remove the first element of the Array and return that element + /// + /// The removed element + public object Shift() => Invoke("shift"); + + /// + /// Add to the array starting at index 0 + /// + /// The length after shift. + /// Elements. + public int UnShift(params object[] elements) => (int)Invoke("unshift", elements); + + /// + /// Index of the search element. + /// + /// The index of first occurrence of searchElement in the Array or -1 if not Found + /// Search element. + /// The index to start the search from + public int IndexOf(object searchElement, int fromIndex = 0) => (int)Invoke("indexOf", searchElement, fromIndex); + + /// + /// Finds the index of the last occurrence of + /// + /// The index of the last occurrence + /// Search element. + public int LastIndexOf(object searchElement) => (int)Invoke("lastIndexOf", searchElement); + + /// + /// Finds the index of the last occurrence of between 0 and . + /// + /// The index of the last occurrence. + /// Search element. + /// End index. + public int LastIndexOf(object searchElement, int endIndex) => (int)Invoke("lastIndexOf", searchElement, endIndex); + + /// + /// Gets or sets the Array with the index specified by . + /// + /// The index. + public object this[int i] + { + get + { + object indexValue = Interop.Runtime.GetByIndex(JSHandle, i, out int exception); + + if (exception != 0) + throw new JSException((string)indexValue); + return indexValue; + } + set + { + object res = Interop.Runtime.SetByIndex(JSHandle, i, value, out int exception); + + if (exception != 0) + throw new JSException((string)res); + + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs new file mode 100644 index 00000000000000..04e652db81cbb5 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public class ArrayBuffer : CoreObject + { + /// + /// Initializes a new instance of the JavaScript Core ArrayBuffer class. + /// + public ArrayBuffer() : base(Interop.Runtime.New()) + { } + + /// + /// Initializes a new instance of the JavaScript Core ArrayBuffer class. + /// + /// Length. + public ArrayBuffer(int length) : base(Interop.Runtime.New(length)) + { } + + /// + /// Initializes a new instance of the JavaScript Core ArrayBuffer class. + /// + /// Js handle. + internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle) + { } + + /// + /// The length of an ArrayBuffer in bytes. + /// + /// The length of the underlying ArrayBuffer in bytes. + public int ByteLength => (int)GetObjectProperty("byteLength"); + + /// + /// Gets a value indicating whether this ArrayBuffer is view. + /// + /// true if is view; otherwise, false. + public bool IsView => (bool)GetObjectProperty("isView"); + + /// + /// Slice the specified begin. + /// + /// The slice. + /// Begin. + public ArrayBuffer Slice(int begin) => (ArrayBuffer)Invoke("slice", begin); + + /// + /// Slice the specified begin and end. + /// + /// The slice. + /// Begin. + /// End. + public ArrayBuffer Slice(int begin, int end) => (ArrayBuffer)Invoke("slice", begin, end); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs new file mode 100644 index 00000000000000..24b97d16977d5a --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + + /// + /// Core objects are the standard built-in objects and functions. + /// + /// + /// These objects are part of the JavaScript environment. Not to be confused + /// with objects provided by the host application or the browser context + /// such as DOM. For more information about the distinction between the + /// DOM and core JavaScript, see JavaScript technologies overview: + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview + /// + /// Core objects are treated differently in the bridge code as they are + /// guaranteed to be there. + /// + public abstract class CoreObject : JSObject + { + protected CoreObject(int jsHandle) : base(jsHandle) + { + object result = Interop.Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); + if (exception != 0) + throw new JSException($"CoreObject Error binding: {result}"); + } + + internal CoreObject(IntPtr js_handle) : base(js_handle) + { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs new file mode 100644 index 00000000000000..e5abb9e62ec52f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + /// + /// The Function constructor creates a new Function object. + /// + /// + /// Calling the constructor directly can create functions dynamically, but suffers from security and similar + /// (but far less significant) performance issues similar to eval. However, unlike eval, the Function constructor + /// allows executing code in the global scope, prompting better programming habits and allowing for more efficient + /// code minification. + /// + public class Function : CoreObject + { + public Function(params object[] args) : base(Interop.Runtime.New(args)) + { } + + internal Function(IntPtr jHandle) : base(jHandle) + { } + + /// + /// The Apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object). + /// + /// The apply. + /// This argument. + /// Arguments. + public object Apply(object? thisArg = null, object[]? argsArray = null) => Invoke("apply", thisArg, argsArray); + + /// + /// Creates a new Function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. + /// + /// The bind. + /// This argument. + /// Arguments. + public Function Bind(object? thisArg = null, object[]? argsArray = null) => (Function)Invoke("bind", thisArg, argsArray); + + /// + /// Calls a function with a given `this` value and arguments provided individually. + /// + /// The result of calling the function with the specified `this` value and arguments. + /// Optional (null value). The value of this provided for the call to a function. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode, null and undefined will be replaced with the global object and primitive values will be converted to objects. + /// Optional. Arguments for the function. + public object Call(object? thisArg = null, params object[] argsArray) + { + object?[] argsList = new object[argsArray.Length + 1]; + argsList[0] = thisArg; + System.Array.Copy(argsArray, 0, argsList, 1, argsArray.Length); + return Invoke("call", argsList); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs new file mode 100644 index 00000000000000..cd5e41d960ac95 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + /// + /// Host objects are object supplied by the host environment. + /// + /// + /// These objects are not part of the JavaScript environment and provided by the host application + /// or the browser context such as DOM. For more information about the distinction between the + /// DOM and core JavaScript, see JavaScript technologies overview: + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview + /// + /// Host objects are treated differently in the bridge code as they are not guaranteed to exist. + /// + public interface IHostObject + { } + + public class HostObject : HostObjectBase + { + public HostObject(string hostName, params object[] _params) : base(Interop.Runtime.New(hostName, _params)) + { } + } + + public abstract class HostObjectBase : JSObject, IHostObject + { + protected HostObjectBase(int jHandle) : base(jHandle) + { + object result = Interop.Runtime.BindHostObject(jHandle, (int)(IntPtr)Handle, out int exception); + if (exception != 0) + throw new JSException($"HostObject Error binding: {result}"); + } + + internal HostObjectBase(IntPtr js_handle) : base(js_handle) + { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs new file mode 100644 index 00000000000000..dc45a674a443cc --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSException.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + /// + /// Represents an Exception initiated from the JavaScript interop code. + /// + public class JSException : Exception + { + public JSException(string msg) : base(msg) { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs new file mode 100644 index 00000000000000..ea099d4f65566d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public interface IJSObject + { + int JSHandle { get; } + int Length { get; } + } + /// + /// JSObjects are wrappers for a native JavaScript object, and + /// they retain a reference to the JavaScript object for the lifetime of this C# object. + /// + public class JSObject : AnyRef, IJSObject, IDisposable + { + internal object? RawObject; + + // to detect redundant calls + public bool IsDisposed { get; internal set; } + + public JSObject() : this(Interop.Runtime.New()) + { + object result = Interop.Runtime.BindCoreObject(JSHandle, (int)(IntPtr)Handle, out int exception); + if (exception != 0) + throw new JSException($"JSObject Error binding: {result}"); + + } + + internal JSObject(IntPtr js_handle) : base(js_handle) + { } + + internal JSObject(int js_handle) : base((IntPtr)js_handle) + { } + + internal JSObject(int js_handle, object raw_obj) : base(js_handle) + { + RawObject = raw_obj; + } + + public object Invoke(string method, params object?[] args) + { + object res = Interop.Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); + if (exception != 0) + throw new JSException((string)res); + return res; + } + + public object GetObjectProperty(string name) + { + + object propertyValue = Interop.Runtime.GetObjectProperty(JSHandle, name, out int exception); + + if (exception != 0) + throw new JSException((string)propertyValue); + + return propertyValue; + + } + + public void SetObjectProperty(string name, object value, bool createIfNotExists = true, bool hasOwnProperty = false) + { + + object setPropResult = Interop.Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); + if (exception != 0) + throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); + + } + + /// + /// Gets or sets the length. + /// + /// The length. + public int Length + { + get => Convert.ToInt32(GetObjectProperty("length")); + set => SetObjectProperty("length", value, false); + } + + /// + /// Returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it). + /// + /// true, if the object has the specified property as own property, false otherwise. + /// The String name or Symbol of the property to test. + public bool HasOwnProperty(string prop) => (bool)Invoke("hasOwnProperty", prop); + + /// + /// Returns a boolean indicating whether the specified property is enumerable. + /// + /// true, if the specified property is enumerable, false otherwise. + /// The String name or Symbol of the property to test. + public bool PropertyIsEnumerable(string prop) => (bool)Invoke("propertyIsEnumerable", prop); + + protected void FreeHandle() + { + Interop.Runtime.ReleaseHandle(JSHandle, out int exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); + } + + public override bool Equals(object? obj) => obj is JSObject other && JSHandle == other.JSHandle; + + public override int GetHashCode() + { + return JSHandle; + } + + ~JSObject() + { + Dispose(false); + } + + public void Dispose() + { + // Dispose of unmanaged resources. + Dispose(true); + // Suppress finalization. + GC.SuppressFinalize(this); + } + + // Protected implementation of Dispose pattern. + protected virtual void Dispose(bool disposing) + { + + if (!IsDisposed) + { + if (disposing) + { + + // Free any other managed objects here. + // + RawObject = null; + } + + IsDisposed = true; + + // Free any unmanaged objects here. + FreeHandle(); + + } + } + + public override string ToString() + { + return $"(js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"; + } + + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs new file mode 100644 index 00000000000000..a9432a19eb0e9b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public static class Runtime + { + + // / + // / Execute the provided string in the JavaScript context + // / + // / The js. + // / String. + public static string InvokeJS(string str) + { + return Interop.Runtime.InvokeJS(str); + } + + public static System.Runtime.InteropServices.JavaScript.Function? CompileFunction(string snippet) + { + return Interop.Runtime.CompileFunction(snippet); + } + + public static int New(params object[] parms) + { + return Interop.Runtime.New(typeof(T).Name, parms); + } + + public static int New(string hostClassName, params object[] parms) + { + return Interop.Runtime.New(hostClassName, parms); + } + + public static void FreeObject(object obj) + { + Interop.Runtime.FreeObject(obj); + } + + public static object GetGlobalObject(string? str = null) + { + return Interop.Runtime.GetGlobalObject(str); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs new file mode 100644 index 00000000000000..cc3e90ac5662e7 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public class SharedArrayBuffer : CoreObject + { + /// + /// Initializes a new instance of the JavaScript Core SharedArrayBuffer class. + /// + /// The size, in bytes, of the array buffer to create. + public SharedArrayBuffer(int length) : base(Interop.Runtime.New(length)) + { } + + internal SharedArrayBuffer(IntPtr js_handle) : base(js_handle) + { } + + /// + /// The size, in bytes, of the array. This is established when the array is constructed and cannot be changed. + /// + /// The size, in bytes, of the array. + public int ByteLength => (int)GetObjectProperty("byteLength"); + + /// + /// Returns a new JavaScript Core SharedArrayBuffer whose contents are a copy of this SharedArrayBuffer's bytes from begin, + /// inclusive, up to end, exclusive. If either begin or end is negative, it refers to an index from the end + /// of the array, as opposed to from the beginning. + /// + /// a new JavaScript Core SharedArrayBuffer + /// Beginning index of copy. + /// Ending index, exclusive. + public SharedArrayBuffer Slice(int begin, int end) => (SharedArrayBuffer)Invoke("slice", begin, end); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs new file mode 100644 index 00000000000000..205c42e1202621 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs @@ -0,0 +1,196 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; + +namespace System.Runtime.InteropServices.JavaScript +{ + public interface ITypedArray + { + int BytesPerElement { get; } + string Name { get; } + int ByteLength { get; } + ArrayBuffer Buffer { get; } + + void Set(Array array); + void Set(Array array, int offset); + + void Set(ITypedArray typedArray); + void Set(ITypedArray typedArray, int offset); + } + + public interface ITypedArray where U : struct + { + T Slice(); + T Slice(int begin); + T Slice(int begin, int end); + + T SubArray(); + T SubArray(int begin); + T SubArray(int begin, int end); + } + + public enum TypedArrayTypeCode + { + Int8Array = 5, + Uint8Array = 6, + Int16Array = 7, + Uint16Array = 8, + Int32Array = 9, + Uint32Array = 10, + Float32Array = 13, + Float64Array = 14, + Uint8ClampedArray = 0xF, + } + + /// + /// Represents a JavaScript TypedArray. + /// + public abstract class TypedArray : CoreObject, ITypedArray, ITypedArray where U : struct + { + protected TypedArray() : base(Interop.Runtime.New()) + { } + + protected TypedArray(int length) : base(Interop.Runtime.New(length)) + { } + + protected TypedArray(ArrayBuffer buffer) : base(Interop.Runtime.New(buffer)) + { } + + protected TypedArray(ArrayBuffer buffer, int byteOffset) : base(Interop.Runtime.New(buffer, byteOffset)) + { } + + protected TypedArray(ArrayBuffer buffer, int byteOffset, int length) : base(Interop.Runtime.New(buffer, byteOffset, length)) + { } + + protected TypedArray(SharedArrayBuffer buffer) : base(Interop.Runtime.New(buffer)) + { } + + protected TypedArray(SharedArrayBuffer buffer, int byteOffset) : base(Interop.Runtime.New(buffer, byteOffset)) + { } + + protected TypedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(Interop.Runtime.New(buffer, byteOffset, length)) + { } + + internal TypedArray(IntPtr jsHandle) : base(jsHandle) + { } + + public int BytesPerElement => (int)GetObjectProperty("BYTES_PER_ELEMENT"); + public string Name => (string)GetObjectProperty("name"); + public int ByteLength => (int)GetObjectProperty("byteLength"); + public ArrayBuffer Buffer => (ArrayBuffer)GetObjectProperty("buffer"); + + public void Fill(U value) => Invoke("fill", value); + public void Fill(U value, int start) => Invoke("fill", value, start); + public void Fill(U value, int start, int end) => Invoke("fill", value, start, end); + + public void Set(Array array) => Invoke("set", array); + public void Set(Array array, int offset) => Invoke("set", array, offset); + public void Set(ITypedArray typedArray) => Invoke("set", typedArray); + public void Set(ITypedArray typedArray, int offset) => Invoke("set", typedArray, offset); + + public T Slice() => (T)Invoke("slice"); + public T Slice(int begin) => (T)Invoke("slice", begin); + public T Slice(int begin, int end) => (T)Invoke("slice", begin, end); + + public T SubArray() => (T)Invoke("subarray"); + public T SubArray(int begin) => (T)Invoke("subarray", begin); + public T SubArray(int begin, int end) => (T)Invoke("subarray", begin, end); + + public U? this[int i] + { + get + { + object jsValue = Interop.Runtime.GetByIndex(JSHandle, i, out int exception); + + if (exception != 0) + throw new JSException((string)jsValue); + + // The value returned from the index. + return UnBoxValue(jsValue); + } + set + { + object res = Interop.Runtime.SetByIndex(JSHandle, i, value, out int exception); + + if (exception != 0) + throw new JSException((string)res); + + } + } + + private U? UnBoxValue(object jsValue) + { + if (jsValue == null) + return null; + + Type type = jsValue.GetType(); + return (U)Convert.ChangeType(jsValue, typeof(U)); + } + + public U[] ToArray() + { + object res = Interop.Runtime.TypedArrayToArray(JSHandle, out int exception); + + if (exception != 0) + throw new JSException((string)res); + return (U[])res; + } + + public static unsafe T From(ReadOnlySpan span) + { + // source has to be instantiated. + if (span == null) + { + throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null."); + } + + TypedArrayTypeCode type = (TypedArrayTypeCode)Type.GetTypeCode(typeof(U)); + // Special case for Uint8ClampedArray, a clamped array which represents an array of 8-bit unsigned integers clamped to 0-255; + if (type == TypedArrayTypeCode.Uint8Array && typeof(T) == typeof(Uint8ClampedArray)) + type = TypedArrayTypeCode.Uint8ClampedArray; // This is only passed to the JavaScript side so it knows it will be a Uint8ClampedArray + + ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); + fixed (byte* ptr = bytes) + { + object res = Interop.Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Marshal.SizeOf(), (int)type, out int exception); + if (exception != 0) + throw new JSException((string)res); + return (T)res; + } + + } + + public unsafe int CopyTo(Span span) + { + ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); + fixed (byte* ptr = bytes) + { + object res = Interop.Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res / Marshal.SizeOf(); + } + } + + public unsafe int CopyFrom(ReadOnlySpan span) + { + // source has to be instantiated. + if (span == null || span.Length == 0) + { + throw new System.ArgumentException($"Invalid argument: {nameof(span)} can not be null and must have a length"); + } + + ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); + fixed (byte* ptr = bytes) + { + object res = Interop.Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + if (exception != 0) + throw new JSException((string)res); + return (int)res / Marshal.SizeOf(); + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs new file mode 100644 index 00000000000000..cf3781df62924b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public sealed class Uint8Array : TypedArray + { + /// + /// Initializes a new instance of the JavaScript Core Uint8Array class. + /// + public Uint8Array() + { } + + public Uint8Array(int length) : base(length) + { } + + + public Uint8Array(ArrayBuffer buffer) : base(buffer) + { } + + public Uint8Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + public Uint8Array(SharedArrayBuffer buffer) : base(buffer) + { } + + public Uint8Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + internal Uint8Array(IntPtr js_handle) : base(js_handle) + { } + + /// + /// Defines an implicit conversion of JavaScript Core Uint8Array class to a Span<byte> + /// + public static implicit operator Span(Uint8Array typedarray) => typedarray.ToArray(); + + public static implicit operator Uint8Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs new file mode 100644 index 00000000000000..913d7a89a65f55 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public sealed class Uint8ClampedArray : TypedArray + { + /// + /// Initializes a new instance of the JavaScript Core Uint8ClampedArray class. + /// + public Uint8ClampedArray() + { } + + public Uint8ClampedArray(int length) : base(length) + { } + + + public Uint8ClampedArray(ArrayBuffer buffer) : base(buffer) + { } + + public Uint8ClampedArray(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8ClampedArray(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + public Uint8ClampedArray(SharedArrayBuffer buffer) : base(buffer) + { } + + public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + internal Uint8ClampedArray(IntPtr js_handle) : base(js_handle) + { } + + /// + /// Defines an implicit conversion of JavaScript Core Uint8ClampedArray class to a Span<byte> + /// + public static implicit operator Span(Uint8ClampedArray typedarray) => typedarray.ToArray(); + + public static implicit operator Uint8ClampedArray(Span span) => From(span); + } +} From bdc38c43ec161065dc93d55fdcaa0fd35113ad53 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 13:03:26 +0200 Subject: [PATCH 89/99] Remove placeholder source --- .../src/Runtime.cs | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs deleted file mode 100644 index 96604df6296eda..00000000000000 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/Runtime.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Runtime.InteropServices.JavaScript -{ - public static partial class Runtime - { } -} \ No newline at end of file From fcb98027a2cc5eefce231d9127aab120a5e059f2 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 13:04:11 +0200 Subject: [PATCH 90/99] Reference System.Runtime.InteropServices.JavaScript project --- .../src/System.Net.Http.csproj | 34 +++---------------- .../BrowserHttpHandler/BrowserHttpHandler.cs | 20 +++++------ 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 154dd60afb4dac..1bd8d89fc682a2 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -1,4 +1,4 @@ - + Library System.Net.Http @@ -733,34 +733,8 @@ - - - - - - - - - - - - - - - + + + \ No newline at end of file diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs index 827b19af4844fb..a046f0a1fe300e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/BrowserHttpHandler/BrowserHttpHandler.cs @@ -12,11 +12,11 @@ using System.Threading; using System.Threading.Tasks; -using JSObject = Interop.JavaScript.JSObject; -using JSException = Interop.JavaScript.JSException; -using HostObject = Interop.JavaScript.HostObject; -using Uint8Array = Interop.JavaScript.Uint8Array; -using Function = Interop.JavaScript.Function; +using JSObject = System.Runtime.InteropServices.JavaScript.JSObject; +using JSException = System.Runtime.InteropServices.JavaScript.JSException; +using HostObject = System.Runtime.InteropServices.JavaScript.HostObject; +using Uint8Array = System.Runtime.InteropServices.JavaScript.Uint8Array; +using Function = System.Runtime.InteropServices.JavaScript.Function; namespace System.Net.Http { @@ -30,8 +30,8 @@ namespace System.Net.Http internal sealed class BrowserHttpHandler : HttpMessageHandler { // This partial implementation contains members common to Browser WebAssembly running on .NET Core. - private static readonly JSObject? s_fetch = (JSObject)Interop.Runtime.GetGlobalObject("fetch"); - private static readonly JSObject? s_window = (JSObject)Interop.Runtime.GetGlobalObject("window"); + private static readonly JSObject? s_fetch = (JSObject)System.Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("fetch"); + private static readonly JSObject? s_window = (JSObject)System.Runtime.InteropServices.JavaScript.Runtime.GetGlobalObject("window"); /// /// Gets whether the current Browser supports streaming responses @@ -202,7 +202,7 @@ protected internal override async Task SendAsync(HttpReques abortCts.Dispose(); })); - var args = new Interop.JavaScript.Array(); + var args = new System.Runtime.InteropServices.JavaScript.Array(); if (request.RequestUri != null) { args.Push(request.RequestUri.ToString()); @@ -248,7 +248,7 @@ protected internal override async Task SendAsync(HttpReques nextResult = (JSObject)entriesIterator.Invoke("next"); while (!(bool)nextResult.GetObjectProperty("done")) { - using (var resultValue = (Interop.JavaScript.Array)nextResult.GetObjectProperty("value")) + using (var resultValue = (System.Runtime.InteropServices.JavaScript.Array)nextResult.GetObjectProperty("value")) { var name = (string)resultValue[0]; var value = (string)resultValue[1]; @@ -347,7 +347,7 @@ private async Task GetResponseData() return _data; } - using (Interop.JavaScript.ArrayBuffer dataBuffer = (Interop.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(continueOnCapturedContext: true)) + using (System.Runtime.InteropServices.JavaScript.ArrayBuffer dataBuffer = (System.Runtime.InteropServices.JavaScript.ArrayBuffer)await _status.ArrayBuffer().ConfigureAwait(continueOnCapturedContext: true)) { using (Uint8Array dataBinView = new Uint8Array(dataBuffer)) { From b8abce16e1c6b2a3dbf31268ba82c15628197127 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Wed, 20 May 2020 14:36:15 +0200 Subject: [PATCH 91/99] Address review comments Unsafe.SizeOf --- .../System.Runtime.InteropServices.JavaScript.csproj | 4 +--- .../Runtime/InteropServices/JavaScript/TypedArray.cs | 11 ++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 0c19e0c9dace3b..29f6bd816486e9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -12,6 +12,7 @@ + @@ -30,7 +31,4 @@ - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs index 205c42e1202621..39a71ee9579a40 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Runtime.InteropServices.JavaScript @@ -155,7 +156,7 @@ public static unsafe T From(ReadOnlySpan span) ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { - object res = Interop.Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Marshal.SizeOf(), (int)type, out int exception); + object res = Interop.Runtime.TypedArrayFrom((int)ptr, 0, span.Length, Unsafe.SizeOf(), (int)type, out int exception); if (exception != 0) throw new JSException((string)res); return (T)res; @@ -168,10 +169,10 @@ public unsafe int CopyTo(Span span) ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { - object res = Interop.Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + object res = Interop.Runtime.TypedArrayCopyTo(JSHandle, (int)ptr, 0, span.Length, Unsafe.SizeOf(), out int exception); if (exception != 0) throw new JSException((string)res); - return (int)res / Marshal.SizeOf(); + return (int)res / Unsafe.SizeOf(); } } @@ -186,10 +187,10 @@ public unsafe int CopyFrom(ReadOnlySpan span) ReadOnlySpan bytes = MemoryMarshal.AsBytes(span); fixed (byte* ptr = bytes) { - object res = Interop.Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Marshal.SizeOf(), out int exception); + object res = Interop.Runtime.TypedArrayCopyFrom(JSHandle, (int)ptr, 0, span.Length, Unsafe.SizeOf(), out int exception); if (exception != 0) throw new JSException((string)res); - return (int)res / Marshal.SizeOf(); + return (int)res / Unsafe.SizeOf(); } } } From a71211422a280eb48b576b239aaf4248cea71006 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 25 May 2020 09:11:18 +0200 Subject: [PATCH 92/99] Finish move of sources to InteropServices.JavaScript --- .../Interop/Browser/Interop.Runtime.Bridge.cs | 369 ----------------- .../src/Interop/Browser/Interop.Runtime.cs | 29 -- ....Runtime.InteropServices.JavaScript.csproj | 1 - .../InteropServices/JavaScript/Runtime.cs | 382 +++++++++++++++++- 4 files changed, 379 insertions(+), 402 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs deleted file mode 100644 index 31a495be579f5d..00000000000000 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.Bridge.cs +++ /dev/null @@ -1,369 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -using JSObject = System.Runtime.InteropServices.JavaScript.JSObject; -using JSException = System.Runtime.InteropServices.JavaScript.JSException; - -internal static partial class Interop -{ - internal static partial class Runtime - { - private static readonly Dictionary _boundObjects = new Dictionary(); - private static readonly Dictionary _rawToJS = new Dictionary(); - - internal static int BindJSObject(int jsId, Type mappedType) - { - lock (_boundObjects) - { - if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) - { - if (mappedType != null) - { - return BindJSType(jsId, mappedType); - } - else - { - _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); - } - } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - } - - internal static int BindCoreCLRObject(int jsId, int gcHandle) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? obj; - - lock (_boundObjects) - { - if (_boundObjects.TryGetValue(jsId, out JSObject? existingObj)) - { - if (existingObj?.Handle != h && h.IsAllocated) - throw new JSException($"Multiple handles pointing at js_id: {jsId}"); - - obj = existingObj; - } - else - { - obj = h.Target as JSObject; - } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - } - - internal static int BindJSType(int jsId, Type mappedType) - { - lock (_boundObjects) - { - if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) - { - ConstructorInfo? jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, - null, new Type[] { typeof(IntPtr) }, null); - _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); - } - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - } - - internal static int UnBindJSObject(int jsId) - { - lock (_boundObjects) - { - if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) - { - _boundObjects.Remove(jsId); - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - return 0; - } - } - - internal static void UnBindJSObjectAndFree(int jsId) - { - lock (_boundObjects) - { - if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) - { - if (_boundObjects[jsId] != null) - { - _boundObjects.Remove(jsId); - } - if (obj != null) - { - obj.JSHandle = -1; - obj.IsDisposed = true; - obj.RawObject = null; - obj.Handle.Free(); - } - } - } - } - - internal static void UnBindRawJSObjectAndFree(int gcHandle) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? obj = h.Target as JSObject; - lock (_rawToJS) - { - if (obj?.RawObject != null) - { - _rawToJS.Remove(obj.RawObject); - - int exception; - ReleaseHandle(obj.JSHandle, out exception); - if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' .NET '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); - - // Calling Release Handle above only removes the reference from the JavaScript side but does not - // release the bridged JSObject associated with the raw object so we have to do that ourselves. - obj.JSHandle = -1; - obj.IsDisposed = true; - obj.RawObject = null; - - obj.Handle.Free(); - } - } - } - - internal static object CreateTaskSource(int jsId) - { - return new TaskCompletionSource(); - } - - internal static void SetTaskSourceResult(TaskCompletionSource tcs, object result) - { - tcs.SetResult(result); - } - - internal static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) - { - tcs.SetException(new JSException(reason)); - } - - internal static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) - { - return BindExistingObject(tcs.Task, jsId); - } - - internal static int BindExistingObject(object rawObj, int jsId) - { - JSObject? obj = rawObj as JSObject; - lock (_rawToJS) - { - if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) - _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); - - return obj == null ? 0 : (int)(IntPtr)obj.Handle; - } - } - - internal static int GetJSObjectId(object rawObj) - { - JSObject? obj = rawObj as JSObject; - lock (_rawToJS) - { - if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) - return -1; - - return obj?.JSHandle ?? -1; - } - } - - internal static object? GetDotNetObject(int gcHandle) - { - GCHandle h = (GCHandle)(IntPtr)gcHandle; - JSObject? o = h.Target as JSObject; - return o?.RawObject ?? null; - } - - internal static object BoxInt(int i) - { - return i; - } - - internal static object BoxDouble(double d) - { - return d; - } - - internal static object BoxBool(int b) - { - return b == 0 ? false : true; - } - - internal static bool IsSimpleArray(object a) - { - return a is Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0; - } - - internal static object? GetCoreType(string coreObj) - { - Assembly asm = typeof(Runtime).Assembly; - Type? type = asm.GetType(coreObj); - return type; - - } - - [StructLayout(LayoutKind.Explicit)] - internal struct IntPtrAndHandle - { - [FieldOffset(0)] - internal IntPtr ptr; - - [FieldOffset(0)] - internal RuntimeMethodHandle handle; - } - - internal static string GetCallSignature(IntPtr methodHandle) - { - IntPtrAndHandle tmp = default(IntPtrAndHandle); - tmp.ptr = methodHandle; - - MethodBase? mb = MethodBase.GetMethodFromHandle(tmp.handle); - if (mb == null) - return string.Empty; - - ParameterInfo[] parms = mb.GetParameters(); - int parmsLength = parms.Length; - if (parmsLength == 0) - return string.Empty; - - char[] res = new char[parmsLength]; - - for (int c = 0; c < parmsLength; c++) - { - Type t = parms[c].ParameterType; - switch (Type.GetTypeCode(t)) - { - case TypeCode.Byte: - case TypeCode.SByte: - case TypeCode.Int16: - case TypeCode.UInt16: - case TypeCode.Int32: - case TypeCode.UInt32: - case TypeCode.Boolean: - // Enums types have the same code as their underlying numeric types - if (t.IsEnum) - res[c] = 'j'; - else - res[c] = 'i'; - break; - case TypeCode.Int64: - case TypeCode.UInt64: - // Enums types have the same code as their underlying numeric types - if (t.IsEnum) - res[c] = 'k'; - else - res[c] = 'l'; - break; - case TypeCode.Single: - res[c] = 'f'; - break; - case TypeCode.Double: - res[c] = 'd'; - break; - case TypeCode.String: - res[c] = 's'; - break; - default: - if (t == typeof(IntPtr)) - { - res[c] = 'i'; - } - else if (t == typeof(Uri)) - { - res[c] = 'u'; - } - else - { - if (t.IsValueType) - throw new NotSupportedException("ValueType arguments are not supported."); - res[c] = 'o'; - } - break; - } - } - return new string(res); - } - - internal static void SetupJSContinuation(Task task, JSObject continuationObj) - { - if (task.IsCompleted) - Complete(); - else - task.GetAwaiter().OnCompleted(Complete); - - void Complete() - { - try - { - if (task.Exception == null) - { - object? result; - Type task_type = task.GetType(); - if (task_type == typeof(Task)) - { - result = Array.Empty(); - } - else - { - result = task_type.GetMethod("get_Result")?.Invoke(task, Array.Empty()); - } - continuationObj.Invoke("resolve", result); - } - else - { - continuationObj.Invoke("reject", task.Exception.ToString()); - } - } - catch (Exception e) - { - continuationObj.Invoke("reject", e.ToString()); - } - finally - { - continuationObj.Dispose(); - FreeObject(task); - } - } - } - - internal static string ObjectToString(object o) - { - return o.ToString() ?? string.Empty; - } - - internal static double GetDateValue(object dtv) - { - if (dtv == null) - throw new ArgumentNullException(nameof(dtv)); - if (!(dtv is DateTime dt)) - throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); - if (dt.Kind == DateTimeKind.Local) - dt = dt.ToUniversalTime(); - else if (dt.Kind == DateTimeKind.Unspecified) - dt = new DateTime(dt.Ticks, DateTimeKind.Utc); - return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); - } - - internal static DateTime CreateDateTime(double ticks) - { - DateTimeOffset unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); - return unixTime.DateTime; - } - - internal static Uri CreateUri(string uri) - { - return new Uri(uri); - } - } -} diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index f7a60d8f9bcae6..e3b0398841fa28 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -98,35 +98,6 @@ public static int New(string hostClassName, params object[] parms) throw new JSException((string)res); return res as JSObject; } - - public static void FreeObject(object obj) - { - lock (_rawToJS) - { - if (_rawToJS.TryGetValue(obj, out JSObject? jsobj)) - { - //raw_to_js [obj].RawObject = null; - _rawToJS.Remove(obj); - if (jsobj != null) - { - int exception; - Runtime.ReleaseObject(jsobj.JSHandle, out exception); - if (exception != 0) - throw new JSException($"Error releasing object on (raw-obj)"); - - jsobj.JSHandle = -1; - jsobj.RawObject = null; - jsobj.IsDisposed = true; - jsobj.Handle.Free(); - } - } - else - { - throw new JSException($"Error releasing object on (obj)"); - } - } - } - public static object GetGlobalObject(string? str = null) { int exception; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 29f6bd816486e9..32aa138b7c801d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -16,7 +16,6 @@ - diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs index a9432a19eb0e9b..88e1592aafc5a6 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs @@ -3,12 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; namespace System.Runtime.InteropServices.JavaScript { public static class Runtime { - // / // / Execute the provided string in the JavaScript context // / @@ -36,12 +40,384 @@ public static int New(string hostClassName, params object[] parms) public static void FreeObject(object obj) { - Interop.Runtime.FreeObject(obj); - } + lock (_rawToJS) + { + if (_rawToJS.TryGetValue(obj, out JSObject? jsobj)) + { + //raw_to_js [obj].RawObject = null; + _rawToJS.Remove(obj); + if (jsobj != null) + { + int exception; + Interop.Runtime.ReleaseObject(jsobj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing object on (raw-obj)"); + jsobj.JSHandle = -1; + jsobj.RawObject = null; + jsobj.IsDisposed = true; + jsobj.Handle.Free(); + } + } + else + { + throw new JSException($"Error releasing object on (obj)"); + } + } + } public static object GetGlobalObject(string? str = null) { return Interop.Runtime.GetGlobalObject(str); } + + private static readonly Dictionary _boundObjects = new Dictionary(); + private static readonly Dictionary _rawToJS = new Dictionary(); + + public static int BindJSObject(int jsId, Type mappedType) + { + lock (_boundObjects) + { + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + if (mappedType != null) + { + return BindJSType(jsId, mappedType); + } + else + { + _boundObjects[jsId] = obj = new JSObject((IntPtr)jsId); + } + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + } + + public static int BindCoreCLRObject(int jsId, int gcHandle) + { + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? obj; + + lock (_boundObjects) + { + if (_boundObjects.TryGetValue(jsId, out JSObject? existingObj)) + { + if (existingObj?.Handle != h && h.IsAllocated) + throw new JSException($"Multiple handles pointing at js_id: {jsId}"); + + obj = existingObj; + } + else + { + obj = h.Target as JSObject; + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + } + + public static int BindJSType(int jsId, Type mappedType) + { + lock (_boundObjects) + { + if (!_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + ConstructorInfo? jsobjectnew = mappedType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.ExactBinding, + null, new Type[] { typeof(IntPtr) }, null); + _boundObjects[jsId] = obj = jsobjectnew == null ? null : (JSObject)jsobjectnew.Invoke(new object[] { (IntPtr)jsId }); + } + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + } + + public static int UnBindJSObject(int jsId) + { + lock (_boundObjects) + { + if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + _boundObjects.Remove(jsId); + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + return 0; + } + } + + public static void UnBindJSObjectAndFree(int jsId) + { + lock (_boundObjects) + { + if (_boundObjects.TryGetValue(jsId, out JSObject? obj)) + { + if (_boundObjects[jsId] != null) + { + _boundObjects.Remove(jsId); + } + if (obj != null) + { + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + obj.Handle.Free(); + } + } + } + } + + public static void UnBindRawJSObjectAndFree(int gcHandle) + { + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? obj = h.Target as JSObject; + lock (_rawToJS) + { + if (obj?.RawObject != null) + { + _rawToJS.Remove(obj.RawObject); + + int exception; + Interop.Runtime.ReleaseHandle(obj.JSHandle, out exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{obj.JSHandle}' .NET '{(IntPtr)obj.Handle} raw '{obj.RawObject != null})"); + + // Calling Release Handle above only removes the reference from the JavaScript side but does not + // release the bridged JSObject associated with the raw object so we have to do that ourselves. + obj.JSHandle = -1; + obj.IsDisposed = true; + obj.RawObject = null; + + obj.Handle.Free(); + } + } + } + + public static object CreateTaskSource(int jsId) + { + return new TaskCompletionSource(); + } + + public static void SetTaskSourceResult(TaskCompletionSource tcs, object result) + { + tcs.SetResult(result); + } + + public static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) + { + tcs.SetException(new JSException(reason)); + } + + public static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) + { + return BindExistingObject(tcs.Task, jsId); + } + + public static int BindExistingObject(object rawObj, int jsId) + { + JSObject? obj = rawObj as JSObject; + lock (_rawToJS) + { + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + _rawToJS[rawObj] = obj = new JSObject(jsId, rawObj); + + return obj == null ? 0 : (int)(IntPtr)obj.Handle; + } + } + + public static int GetJSObjectId(object rawObj) + { + JSObject? obj = rawObj as JSObject; + lock (_rawToJS) + { + if (obj == null && !_rawToJS.TryGetValue(rawObj, out obj)) + return -1; + + return obj?.JSHandle ?? -1; + } + } + + public static object? GetDotNetObject(int gcHandle) + { + GCHandle h = (GCHandle)(IntPtr)gcHandle; + JSObject? o = h.Target as JSObject; + return o?.RawObject ?? null; + } + + public static object BoxInt(int i) + { + return i; + } + + public static object BoxDouble(double d) + { + return d; + } + + public static object BoxBool(int b) + { + return b == 0 ? false : true; + } + + public static bool IsSimpleArray(object a) + { + return a is System.Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0; + } + + public static object? GetCoreType(string coreObj) + { + Assembly asm = typeof(Runtime).Assembly; + Type? type = asm.GetType(coreObj); + return type; + + } + + [StructLayout(LayoutKind.Explicit)] + public struct IntPtrAndHandle + { + [FieldOffset(0)] + internal IntPtr ptr; + + [FieldOffset(0)] + internal RuntimeMethodHandle handle; + } + + public static string GetCallSignature(IntPtr methodHandle) + { + IntPtrAndHandle tmp = default(IntPtrAndHandle); + tmp.ptr = methodHandle; + + MethodBase? mb = MethodBase.GetMethodFromHandle(tmp.handle); + if (mb == null) + return string.Empty; + + ParameterInfo[] parms = mb.GetParameters(); + int parmsLength = parms.Length; + if (parmsLength == 0) + return string.Empty; + + char[] res = new char[parmsLength]; + + for (int c = 0; c < parmsLength; c++) + { + Type t = parms[c].ParameterType; + switch (Type.GetTypeCode(t)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Boolean: + // Enums types have the same code as their underlying numeric types + if (t.IsEnum) + res[c] = 'j'; + else + res[c] = 'i'; + break; + case TypeCode.Int64: + case TypeCode.UInt64: + // Enums types have the same code as their underlying numeric types + if (t.IsEnum) + res[c] = 'k'; + else + res[c] = 'l'; + break; + case TypeCode.Single: + res[c] = 'f'; + break; + case TypeCode.Double: + res[c] = 'd'; + break; + case TypeCode.String: + res[c] = 's'; + break; + default: + if (t == typeof(IntPtr)) + { + res[c] = 'i'; + } + else if (t == typeof(Uri)) + { + res[c] = 'u'; + } + else + { + if (t.IsValueType) + throw new NotSupportedException("ValueType arguments are not supported."); + res[c] = 'o'; + } + break; + } + } + return new string(res); + } + + public static void SetupJSContinuation(Task task, JSObject continuationObj) + { + if (task.IsCompleted) + Complete(); + else + task.GetAwaiter().OnCompleted(Complete); + + void Complete() + { + try + { + if (task.Exception == null) + { + object? result; + Type task_type = task.GetType(); + if (task_type == typeof(Task)) + { + result = System.Array.Empty(); + } + else + { + result = task_type.GetMethod("get_Result")?.Invoke(task, System.Array.Empty()); + } + continuationObj.Invoke("resolve", result); + } + else + { + continuationObj.Invoke("reject", task.Exception.ToString()); + } + } + catch (Exception e) + { + continuationObj.Invoke("reject", e.ToString()); + } + finally + { + continuationObj.Dispose(); + FreeObject(task); + } + } + } + + public static string ObjectToString(object o) + { + return o.ToString() ?? string.Empty; + } + + public static double GetDateValue(object dtv) + { + if (dtv == null) + throw new ArgumentNullException(nameof(dtv)); + if (!(dtv is DateTime dt)) + throw new InvalidCastException($"Unable to cast object of type {dtv.GetType()} to type DateTime."); + if (dt.Kind == DateTimeKind.Local) + dt = dt.ToUniversalTime(); + else if (dt.Kind == DateTimeKind.Unspecified) + dt = new DateTime(dt.Ticks, DateTimeKind.Utc); + return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); + } + + public static DateTime CreateDateTime(double ticks) + { + DateTimeOffset unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); + return unixTime.DateTime; + } + + public static Uri CreateUri(string uri) + { + return new Uri(uri); + } } } From e46ab9f5fe706cd9b2f4fbf4991bb175efd3f38a Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 25 May 2020 10:01:48 +0200 Subject: [PATCH 93/99] Remove ActiveIssue to address review comments --- .../tests/AssemblyInfo.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs index 92a90f9cd99157..195efe1f21a663 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/AssemblyInfo.cs @@ -2,6 +2,4 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Xunit; - -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/34748", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] +using Xunit; \ No newline at end of file From 80b21320a2c1d18aa1564a4a212f3175a9bb8874 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 25 May 2020 10:39:23 +0200 Subject: [PATCH 94/99] Add core implementations --- .../InteropServices/JavaScript/DataView.cs | 225 ++++++++++++++++++ .../JavaScript/Float32Array.cs | 40 ++++ .../JavaScript/Float64Array.cs | 40 ++++ .../InteropServices/JavaScript/Int16Array.cs | 42 ++++ .../InteropServices/JavaScript/Int32Array.cs | 40 ++++ .../InteropServices/JavaScript/Int8Array.cs | 52 ++++ .../InteropServices/JavaScript/TypedArray.cs | 28 +++ .../InteropServices/JavaScript/Uint16Array.cs | 42 ++++ .../InteropServices/JavaScript/Uint32Array.cs | 41 ++++ 9 files changed, 550 insertions(+) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs new file mode 100644 index 00000000000000..0fd921a8e9f11a --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs @@ -0,0 +1,225 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + /// + /// The DataView view provides a low-level interface for reading and writing multiple number types in a + /// binary ArrayBuffer, without having to care about the platform's endianness. + /// + public class DataView : CoreObject + { + /// + /// Initializes a new instance of the DataView class. + /// + /// ArrayBuffer to use as the storage backing the new DataView object. + public DataView(ArrayBuffer buffer) : base(Runtime.New(buffer)) + { } + + /// + /// Initializes a new instance of the DataView class. + /// + /// ArrayBuffer to use as the storage backing the new DataView object. + /// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte. + public DataView(ArrayBuffer buffer, int byteOffset) : base(Runtime.New(buffer, byteOffset)) + { } + + /// + /// Initializes a new instance of the DataView class. + /// + /// ArrayBuffer to use as the storage backing the new DataView object. + /// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte. + /// The number of elements in the byte array. If unspecified, the view's length will match the buffer's length. + public DataView(ArrayBuffer buffer, int byteOffset, int byteLength) : base(Runtime.New(buffer, byteOffset, byteLength)) + { } + + /// + /// Initializes a new instance of the DataView class. + /// + /// SharedArrayBuffer to use as the storage backing the new DataView object. + public DataView(SharedArrayBuffer buffer) : base(Runtime.New(buffer)) + { } + + /// + /// Initializes a new instance of the DataView class. + /// + /// SharedArrayBuffer to use as the storage backing the new DataView object. + /// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte. + public DataView(SharedArrayBuffer buffer, int byteOffset) : base(Runtime.New(buffer, byteOffset)) + { } + + /// + /// Initializes a new instance of the DataView class. + /// + /// SharedArrayBuffer to use as the storage backing the new DataView object. + /// The offset, in bytes, to the first byte in the above buffer for the new view to reference. If unspecified, the buffer view starts with the first byte. + /// The number of elements in the byte array. If unspecified, the view's length will match the buffer's length. + public DataView(SharedArrayBuffer buffer, int byteOffset, int byteLength) : base(Runtime.New(buffer, byteOffset, byteLength)) + { } + + /// + /// Initializes a new instance of the DataView class. + /// + /// Js handle. + internal DataView(IntPtr js_handle) : base(js_handle) + { } + /// + /// Gets the length (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only. + /// + /// The length (in bytes) of this view. + public int ByteLength => (int)GetObjectProperty("byteLength"); + /// + /// Gets the offset (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only. + /// + /// The offset (in bytes) of this view. + public int ByteOffset => (int)GetObjectProperty("byteOffset"); + /// + /// Gets the ArrayBuffer referenced by this view. Fixed at construction time and thus read only. + /// + /// The ArrayBuffer. + public ArrayBuffer Buffer => (ArrayBuffer)GetObjectProperty("buffer"); + + /// + /// Gets the signed 32-bit float (float) at the specified byte offset from the start of the DataView. + /// + /// A signed 32-bit float number. + /// Byte offset. + /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + public float GetFloat32(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getFloat32", byteOffset, littleEndian)); + + /// + /// Gets the signed 64-bit double (double) at the specified byte offset from the start of the DataView. + /// + /// A signed 64-bit coulbe number. + /// Byte offset. + /// Indicates whether the 64-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + public double GetFloat64(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getFloat64", byteOffset, littleEndian)); + /// + /// Gets the signed 16-bit integer (short) at the specified byte offset from the start of the DataView. + /// + /// A signed 16-bit ineger (short) number. + /// Byte offset. + /// Indicates whether the 16-bit integer (short) is stored in little- or big-endian format. If false, a big-endian value is read. + public short GetInt16(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getInt16", byteOffset, littleEndian)); + /// + /// Gets the signed 32-bit integer (int) at the specified byte offset from the start of the DataView. + /// + /// A signed 32-bit ineger (int) number. + /// Byte offset. + /// Indicates whether the 32-bit integer (int) is stored in little- or big-endian format. If false, a big-endian value is read. + public int GetInt32(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getInt32", byteOffset, littleEndian)); + /// + /// Gets the signed 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. + /// + /// A signed 8-bit byte (sbyte) number. + /// Byte offset. + /// Indicates whether the 8-bit byte is stored in little- or big-endian format. If false, a big-endian value is read. + [CLSCompliant(false)] + public sbyte GetInt8(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getInt8", byteOffset, littleEndian)); + /// + /// Gets the unsigned 16-bit integer (short) at the specified byte offset from the start of the DataView. + /// + /// A unsigned 16-bit integer (ushort) number. + /// Byte offset. + /// Indicates whether the unsigned 16-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + [CLSCompliant(false)] + public ushort GetUint16(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getUint16", byteOffset, littleEndian)); + /// + /// Gets the usigned 32-bit integer (uint) at the specified byte offset from the start of the DataView. + /// + /// A usigned 32-bit ineger (uint) number. + /// Byte offset. + /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + [CLSCompliant(false)] + public uint GetUint32(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getUint32", byteOffset, littleEndian)); + /// + /// Gets the unsigned 8-bit byte (byte) at the specified byte offset from the start of the DataView. + /// + /// A unsigned 8-bit byte (byte) number. + /// Byte offset. + /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + public byte GetUint8(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getUint8", byteOffset, littleEndian)); + /// + /// Sets the signed 32-bit float (float) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// float value. + /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + public void SetFloat32(int byteOffset, float value, bool littleEndian = false) => Invoke("setFloat32", byteOffset, value, littleEndian); + + /// + /// Sets the signed 64-bit double (double) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// double value. + /// Indicates whether the 64-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + public void SetFloat64(int byteOffset, double value, bool littleEndian = false) => Invoke("setFloat64", byteOffset, value, littleEndian); + /// + /// Sets the signed 16-bit integer (short) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// short value. + /// Indicates whether the 16-bit integer (short) is stored in little- or big-endian format. If false, a big-endian value is read. + public void SetInt16(int byteOffset, short value, bool littleEndian = false) => Invoke("setInt16", byteOffset, value, littleEndian); + /// + /// Sets the signed 32-bit integer (int) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// int value. + /// Indicates whether the 32-bit integer (int) is stored in little- or big-endian format. If false, a big-endian value is read. + public void SetInt32(int byteOffset, int value, bool littleEndian = false) => Invoke("setInt32", byteOffset, value, littleEndian); + /// + /// Sets the signed 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// sbyte value. + /// Indicates whether the 8-bit byte is stored in little- or big-endian format. If false, a big-endian value is read. + [CLSCompliant(false)] + public void SetInt8(int byteOffset, sbyte value, bool littleEndian = false) => Invoke("setInt8", byteOffset, value, littleEndian); + /// + /// Sets the unsigned 16-bit integer (short) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// ushort value. + /// Indicates whether the unsigned 16-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + [CLSCompliant(false)] + public void SetUint16(int byteOffset, ushort value, bool littleEndian = false) => Invoke("setUint16", byteOffset, value, littleEndian); + /// + /// Sets the usigned 32-bit integer (uint) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// uint value. + /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + [CLSCompliant(false)] + public void SetUint32(int byteOffset, uint value, bool littleEndian = false) => Invoke("setUint32", byteOffset, value, littleEndian); + /// + /// Sets the unsigned 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. + /// + /// Byte offset. + /// byte value. + /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. + public void SetUint8(int byteOffset, byte value, bool littleEndian = false) => Invoke("setUint8", byteOffset, value, littleEndian); + + private U UnBoxValue(object jsValue) where U : struct + { + if (jsValue == null) + { + throw new InvalidCastException($"Unable to cast null to type {typeof(U)}."); + } + + var type = jsValue.GetType(); + if (type.IsPrimitive) + { + return (U)Convert.ChangeType(jsValue, typeof(U)); + } + else + { + throw new InvalidCastException($"Unable to cast object of type {type} to type {typeof(U)}."); + } + } + + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs new file mode 100644 index 00000000000000..892e35591be50e --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public sealed class Float32Array : TypedArray + { + public Float32Array() { } + + public Float32Array(int length) : base(length) { } + + + public Float32Array(ArrayBuffer buffer) : base(buffer) { } + + public Float32Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Float32Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + public Float32Array(SharedArrayBuffer buffer) : base(buffer) { } + + public Float32Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Float32Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + internal Float32Array(IntPtr js_handle) : base(js_handle) { } + + /// + /// Defines an implicit conversion of Float32Array class to a float + /// + public static implicit operator Span(Float32Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of float to a Float32Array class. + /// + public static implicit operator Float32Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs new file mode 100644 index 00000000000000..f5c96e97552489 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public sealed class Float64Array : TypedArray + { + public Float64Array() { } + + public Float64Array(int length) : base(length) { } + + + public Float64Array(ArrayBuffer buffer) : base(buffer) { } + + public Float64Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Float64Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + public Float64Array(SharedArrayBuffer buffer) : base(buffer) { } + + public Float64Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Float64Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + internal Float64Array(IntPtr js_handle) : base(js_handle) { } + + /// + /// Defines an implicit conversion of Float64Array class to a double + /// + public static implicit operator Span(Float64Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of double to a Float64Array class. + /// + public static implicit operator Float64Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs new file mode 100644 index 00000000000000..12f8936aef2eeb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public sealed class Int16Array : TypedArray + { + public Int16Array() { } + + public Int16Array(int length) : base(length) { } + + + public Int16Array(ArrayBuffer buffer) : base(buffer) { } + + public Int16Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Int16Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + public Int16Array(SharedArrayBuffer buffer) : base(buffer) { } + + public Int16Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Int16Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + internal Int16Array(IntPtr js_handle) : base(js_handle) + { } + + /// + /// Defines an implicit conversion of Int16Array class to a short + /// + [CLSCompliant(false)] + public static implicit operator Span(Int16Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of short to a Int16Array class. + /// + public static implicit operator Int16Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs new file mode 100644 index 00000000000000..f55d426787fafa --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + public sealed class Int32Array : TypedArray + { + public Int32Array() { } + + public Int32Array(int length) : base(length) { } + + + public Int32Array(ArrayBuffer buffer) : base(buffer) { } + + public Int32Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Int32Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + public Int32Array(SharedArrayBuffer buffer) : base(buffer) { } + + public Int32Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Int32Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + internal Int32Array(IntPtr js_handle) : base(js_handle) { } + + /// + /// Defines an implicit conversion of Int32Array class to a int + /// + public static implicit operator Span(Int32Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of int to a Int32Array class. + /// + public static implicit operator Int32Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs new file mode 100644 index 00000000000000..106baec6a79230 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + [CLSCompliant(false)] + public sealed class Int8Array : TypedArray + { + public Int8Array() + { } + + public Int8Array(int length) : base(length) + { } + + + public Int8Array(ArrayBuffer buffer) : base(buffer) + { } + + public Int8Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Int8Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + public Int8Array(SharedArrayBuffer buffer) : base(buffer) + { } + + public Int8Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) + { } + + public Int8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) + { } + + internal Int8Array(IntPtr js_handle) : base(js_handle) + { } + + /// + /// Defines an implicit conversion of Int8Array class to a sbyte + /// + [CLSCompliant(false)] + public static implicit operator Span(Int8Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of sbyte to a Int8Array class. + /// + [CLSCompliant(false)] + public static implicit operator Int8Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs index 39a71ee9579a40..be2fa43cb1c6c5 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs @@ -20,6 +20,7 @@ public interface ITypedArray void Set(ITypedArray typedArray); void Set(ITypedArray typedArray, int offset); + TypedArrayTypeCode GetTypedArrayType(); } public interface ITypedArray where U : struct @@ -78,6 +79,33 @@ protected TypedArray(SharedArrayBuffer buffer, int byteOffset, int length) : bas internal TypedArray(IntPtr jsHandle) : base(jsHandle) { } + public TypedArrayTypeCode GetTypedArrayType() + { + switch (this) + { + case Int8Array _: + return TypedArrayTypeCode.Int8Array; + case Uint8Array _: + return TypedArrayTypeCode.Uint8Array; + case Uint8ClampedArray _: + return TypedArrayTypeCode.Uint8ClampedArray; + case Int16Array _: + return TypedArrayTypeCode.Int16Array; + case Uint16Array _: + return TypedArrayTypeCode.Uint16Array; + case Int32Array _: + return TypedArrayTypeCode.Int32Array; + case Uint32Array _: + return TypedArrayTypeCode.Uint32Array; + case Float32Array _: + return TypedArrayTypeCode.Float32Array; + case Float64Array _: + return TypedArrayTypeCode.Float64Array; + default: + throw new ArrayTypeMismatchException("TypedArray is not of correct type."); + } + } + public int BytesPerElement => (int)GetObjectProperty("BYTES_PER_ELEMENT"); public string Name => (string)GetObjectProperty("name"); public int ByteLength => (int)GetObjectProperty("byteLength"); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs new file mode 100644 index 00000000000000..8c3a42dee25c07 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + [CLSCompliant(false)] + public sealed class Uint16Array : TypedArray + { + public Uint16Array() { } + + public Uint16Array(int length) : base(length) { } + + + public Uint16Array(ArrayBuffer buffer) : base(buffer) { } + + public Uint16Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Uint16Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + public Uint16Array(SharedArrayBuffer buffer) : base(buffer) { } + + public Uint16Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Uint16Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + internal Uint16Array(IntPtr js_handle) : base(js_handle) + { } + + /// + /// Defines an implicit conversion of Uint16Array class to a ushort + /// + public static implicit operator Span(Uint16Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of ushort to a Uint16Array class. + /// + public static implicit operator Uint16Array(Span span) => From(span); + } +} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs new file mode 100644 index 00000000000000..5e4d14e5944655 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace System.Runtime.InteropServices.JavaScript +{ + [CLSCompliant(false)] + public sealed class Uint32Array : TypedArray + { + public Uint32Array() { } + + public Uint32Array(int length) : base(length) { } + + + public Uint32Array(ArrayBuffer buffer) : base(buffer) { } + + public Uint32Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Uint32Array(ArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + public Uint32Array(SharedArrayBuffer buffer) : base(buffer) { } + + public Uint32Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } + + public Uint32Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } + + internal Uint32Array(IntPtr js_handle) : base(js_handle) { } + + /// + /// Defines an implicit conversion of Uint32Array class to a uint + /// + public static implicit operator Span(Uint32Array typedarray) => typedarray.ToArray(); + + /// + /// Defines an implicit conversion of uint to a Uint32Array class. + /// + public static implicit operator Uint32Array(Span span) => From(span); + } +} From af5d902538dcc7f6e932845554fa75134366ee5f Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Mon, 25 May 2020 11:17:31 +0200 Subject: [PATCH 95/99] Add core object implementation for Map --- ....Runtime.InteropServices.JavaScript.csproj | 11 +- .../Runtime/InteropServices/JavaScript/Map.cs | 271 ++++++++++++++++++ 2 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index 32aa138b7c801d..baa375a04360fa 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -3,7 +3,7 @@ System.Runtime.InteropServices.JavaScript true enable - $(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser @@ -19,12 +19,21 @@ + + + + + + + + + diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs new file mode 100644 index 00000000000000..95a61d2587cfa2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs @@ -0,0 +1,271 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; + +namespace System.Runtime.InteropServices.JavaScript +{ + /// + /// The Map object holds key-value pairs and remembers the original insertion order of the keys. + /// Any value (both objects and primitive values) may be used as either a key or a value. + /// + public class Map : CoreObject, IDictionary + { + + /// + /// Initializes a new instance of the Map class. + /// + /// Parameters. + public Map(params object[] _params) : base(Runtime.New(_params)) + { } + + /// + /// Initializes a new instance of the Map class. + /// + /// Js handle. + internal Map(IntPtr jsHandle) : base(jsHandle) + { } + + /// + /// Gets a value indicating whether the Map object has a fixed size. + /// + public bool IsFixedSize => false; + + /// + /// Gets a value indicating whether the Map object is read-only. + /// + public bool IsReadOnly => false; + + /// + /// Gets an System.Collections.ICollection object containing the keys of the Map object. + /// + public ICollection Keys => new MapItemCollection(this, "keys"); + + /// + /// Gets an System.Collections.ICollection object containing the values of the Map object. + /// + public ICollection Values => new MapItemCollection(this, "values"); + + public int Count => (int)GetObjectProperty("size"); + + public bool IsSynchronized => false; + + public object SyncRoot => false; + + public void Add(object key, object? value) => Invoke("set", key, value); + + public void Clear() => Invoke("clear"); + + public bool Contains(object key) => (bool)Invoke("has", key); + + public IDictionaryEnumerator GetEnumerator() => new MapEnumerator(this); + + public void Remove(object key) => Invoke("delete", key); + + public void CopyTo(System.Array array, int index) => throw new NotImplementedException(); + + // Construct and return an enumerator. + IEnumerator IEnumerable.GetEnumerator() => new MapEnumerator(this); + + /// + /// Gets or sets the Map with the key specified by . + /// + /// The key. + public object? this[object key] + { + get + { + return Invoke("get", key); + } + set + { + Invoke("set", key, value); + } + } + + private sealed class MapEnumerator : IDictionaryEnumerator, IDisposable + { + private JSObject? _mapIterator; + private readonly Map _map; + public MapEnumerator(Map map) + { + _map = map; + } + + // Return the current item. + public object Current => new DictionaryEntry(Key, Value); + + // Return the current dictionary entry. + public DictionaryEntry Entry => (DictionaryEntry)Current; + + // Return the key of the current item. + public object Key { get; private set; } = new object(); + + // Return the value of the current item. + public object? Value { get; private set; } + + // Advance to the next item. + public bool MoveNext() + { + if (_mapIterator == null) + _mapIterator = (JSObject)_map.Invoke("entries"); + + using (var result = (JSObject)_mapIterator.Invoke("next")) + { + using (var resultValue = (Array)result.GetObjectProperty("value")) + { + if (resultValue != null) + { + Key = resultValue[0]; + Value = resultValue[1]; + } + else + { + Value = null; + } + } + return !(bool)result.GetObjectProperty("done"); + } + } + + // Reset the index to restart the enumeration. + public void Reset() + { + _mapIterator?.Dispose(); + _mapIterator = null; + } + + #region IDisposable Support + private bool _disposedValue = false; // To detect redundant calls + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + _mapIterator?.Dispose(); + _mapIterator = null; + _disposedValue = true; + } + } + + ~MapEnumerator() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + } + + /// + /// Class that implements an ICollection over the "keys" or "values" + /// + private sealed class MapItemCollection : ICollection + { + private readonly Map _map; + private readonly string _iterator; // "keys" or "values" + + public MapItemCollection(Map map, string iterator) + { + _map = map; + _iterator = iterator; + + } + public int Count => _map.Count; + + public bool IsSynchronized => false; + + public object SyncRoot => this; + + public void CopyTo(System.Array array, int index) + { + throw new NotImplementedException(); + } + + // Construct and return an enumerator. + public IEnumerator GetEnumerator() => new MapItemEnumerator(this); + + /// + /// The custom enumerator used by MapItemCollection + /// + private sealed class MapItemEnumerator : IEnumerator + { + + private readonly MapItemCollection _mapItemCollection; + private JSObject? _mapItemIterator; + + public object? Current { get; private set; } + + public MapItemEnumerator(MapItemCollection mapCollection) + { + _mapItemCollection = mapCollection; + } + + public bool MoveNext() + { + if (_mapItemIterator == null) + _mapItemIterator = (JSObject)_mapItemCollection._map.Invoke(_mapItemCollection._iterator); + + var done = false; + using (var result = (JSObject)_mapItemIterator.Invoke("next")) + { + done = (bool)result.GetObjectProperty("done"); + if (!done) + Current = result.GetObjectProperty("value"); + return !done; + } + } + + public void Reset() + { + _mapItemIterator?.Dispose(); + _mapItemIterator = null; + } + + #region IDisposable Support + private bool _disposedValue = false; // To detect redundant calls + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects). + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + _mapItemIterator?.Dispose(); + _mapItemIterator = null; + _disposedValue = true; + } + } + + //TODO: override a finalizer only if Dispose (bool disposing) above has code to free unmanaged resources. + ~MapItemEnumerator() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + GC.SuppressFinalize(this); + } + #endregion + } + } + } +} From e5de0140950137657c8e840f5677f6214681d74a Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 26 May 2020 06:36:52 +0200 Subject: [PATCH 96/99] Remove unused targetframework --- .../src/System.Runtime.InteropServices.JavaScript.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj index baa375a04360fa..11b18a25fd7f4b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj @@ -3,7 +3,7 @@ System.Runtime.InteropServices.JavaScript true enable - $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent)-Browser From c72e64cf237b34b456d98bcd5bc1094838a17ab2 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 26 May 2020 08:42:44 +0200 Subject: [PATCH 97/99] Fix PNSE in HttpClientHandler .ctor - When creating an instance of the browser webassembly `System.Net.Http.BrowserHttpHandler` the following error is thrown. ``` System.PlatformNotSupportedException: Operation is not supported on this platform. at System.Net.Http.BrowserHttpHandler.get_SslOptions() at System.Net.Http.HttpClientHandler.ThrowForModifiedManagedSslOptionsIfStarted() at System.Net.Http.HttpClientHandler.set_ClientCertificateOptions(ClientCertificateOption value) at System.Net.Http.HttpClientHandler..ctor() at System.Net.Http.HttpClient..ctor() ``` --- .../src/System/Net/Http/HttpClientHandler.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index deee4695b2c056..d5987643d3887b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -157,15 +157,23 @@ public ClientCertificateOption ClientCertificateOptions switch (value) { case ClientCertificateOption.Manual: +#if TARGETS_BROWSER + _clientCertificateOptions = value; +#else ThrowForModifiedManagedSslOptionsIfStarted(); _clientCertificateOptions = value; _underlyingHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate(ClientCertificates)!; +#endif break; case ClientCertificateOption.Automatic: +#if TARGETS_BROWSER + _clientCertificateOptions = value; +#else ThrowForModifiedManagedSslOptionsIfStarted(); _clientCertificateOptions = value; _underlyingHandler.SslOptions.LocalCertificateSelectionCallback = (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => CertificateHelper.GetEligibleClientCertificate()!; +#endif break; default: From 5f17ee1c54c0adaeefceb582f2e6f29c88bc8f94 Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Tue, 26 May 2020 12:59:47 +0200 Subject: [PATCH 98/99] Remove extra parameters to `PlatformNotSupportedException` --- .../System.Net.Http/src/System/Net/Http/HttpClientHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs index d5987643d3887b..a4e5086b4c103a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.cs @@ -199,8 +199,8 @@ public X509CertificateCollection ClientCertificates public Func? ServerCertificateCustomValidationCallback { #if TARGETS_BROWSER - get => throw new PlatformNotSupportedException("Property ServerCertificateCustomValidationCallback is not supported."); - set => throw new PlatformNotSupportedException("Property ServerCertificateCustomValidationCallback is not supported."); + get => throw new PlatformNotSupportedException(); + set => throw new PlatformNotSupportedException(); #else get => (_underlyingHandler.SslOptions.RemoteCertificateValidationCallback?.Target as ConnectHelper.CertificateCallbackMapper)?.FromHttpClientHandler; set From 14681041f2becaa1006ca83e2e051112eb464f3d Mon Sep 17 00:00:00 2001 From: Kenneth Pouncey Date: Thu, 28 May 2020 17:33:13 +0200 Subject: [PATCH 99/99] Fix tests due to underlying field name changing and reflection being used to obtain the socket field --- src/libraries/Common/tests/System/Net/Http/TestHelper.cs | 2 +- .../HttpClientHandlerTestBase.SocketsHttpHandler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/TestHelper.cs b/src/libraries/Common/tests/System/Net/Http/TestHelper.cs index c3f63a84ee3c73..3c88a428908999 100644 --- a/src/libraries/Common/tests/System/Net/Http/TestHelper.cs +++ b/src/libraries/Common/tests/System/Net/Http/TestHelper.cs @@ -120,7 +120,7 @@ public static void EnableUnencryptedHttp2IfNecessary(HttpClientHandler handler) return; } - FieldInfo socketsHttpHandlerField = typeof(HttpClientHandler).GetField("_socketsHttpHandler", BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo socketsHttpHandlerField = typeof(HttpClientHandler).GetField("_underlyingHandler", BindingFlags.NonPublic | BindingFlags.Instance); if (socketsHttpHandlerField == null) { // Not using .NET Core implementation, i.e. could be .NET Framework. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs index ae7a249db060c1..5da96c960c51d9 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs @@ -43,7 +43,7 @@ protected static void SetUsePrenegotiatedHttp3(HttpClientHandler handler, bool u protected static object GetUnderlyingSocketsHttpHandler(HttpClientHandler handler) { - FieldInfo field = typeof(HttpClientHandler).GetField("_socketsHttpHandler", BindingFlags.Instance | BindingFlags.NonPublic); + FieldInfo field = typeof(HttpClientHandler).GetField("_underlyingHandler", BindingFlags.Instance | BindingFlags.NonPublic); return field?.GetValue(handler); } }