Просмотр исходного кода

IJSUnmarshalledObjectReference for unmarshalled JS interop calls on JavaScript objects. (#25548)

* Added IJSUnmarshalledObjectReference

* Working support for IJSUnmarshalledObjectReference

* CR feedback

* Removed IVT and made JSObjectReference public

* Updated JSON converter.

* Update JSObjectReferenceJsonConverterTest.cs

* Removed whitespace :sweat:
Mackinnon Buck 5 лет назад
Родитель
Сommit
bbc7fd8192
23 измененных файлов с 336 добавлено и 70 удалено
  1. 0 0
      src/Components/Web.JS/dist/Release/blazor.webassembly.js
  2. 10 1
      src/Components/Web.JS/src/Boot.WebAssembly.ts
  3. 45 0
      src/Components/WebAssembly/JSInterop/src/WebAssemblyJSObjectReference.cs
  4. 38 19
      src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs
  5. 1 1
      src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs
  6. 1 1
      src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyConsoleLogger.cs
  7. 1 1
      src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyJSRuntimeInvoker.cs
  8. 1 0
      src/Components/test/E2ETest/Tests/InteropTest.cs
  9. 8 0
      src/Components/test/testassets/BasicTestApp/InteropComponent.razor
  10. 14 0
      src/Components/test/testassets/BasicTestApp/InteropTest/InteropStruct.cs
  11. 5 0
      src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js
  12. 3 1
      src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs
  13. 55 0
      src/JSInterop/Microsoft.JSInterop/src/IJSUnmarshalledObjectReference.cs
  14. 45 0
      src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs
  15. 23 11
      src/JSInterop/Microsoft.JSInterop/src/Implementation/JSObjectReference.cs
  16. 6 3
      src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs
  17. 0 25
      src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs
  18. 4 2
      src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs
  19. 3 1
      src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs
  20. 1 1
      src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj
  21. 68 2
      src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs
  22. 1 0
      src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs
  23. 3 1
      src/Shared/JSInterop/JSCallResultTypeHelper.cs

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.webassembly.js


+ 10 - 1
src/Components/Web.JS/src/Boot.WebAssembly.ts

@@ -130,7 +130,16 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any):
     }
   } else {
     const func = DotNet.jsCallDispatcher.findJSFunction(functionIdentifier, targetInstanceId);
-    return func.call(null, arg0, arg1, arg2);
+    const result = func.call(null, arg0, arg1, arg2);
+
+    switch (resultType) {
+      case DotNet.JSCallResultType.Default:
+        return result;
+      case DotNet.JSCallResultType.JSObjectReference:
+        return DotNet.createJSObjectReference(result).__jsObjectId;
+      default:
+        throw new Error(`Invalid JS call result type '${resultType}'.`);
+    }
   }
 }
 

+ 45 - 0
src/Components/WebAssembly/JSInterop/src/WebAssemblyJSObjectReference.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.JSInterop.Implementation;
+
+namespace Microsoft.JSInterop.WebAssembly
+{
+    internal class WebAssemblyJSObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
+    {
+        private readonly WebAssemblyJSRuntime _jsRuntime;
+
+        public WebAssemblyJSObjectReference(WebAssemblyJSRuntime jsRuntime, long id) : base(jsRuntime, id)
+        {
+            _jsRuntime = jsRuntime;
+        }
+
+        public TResult InvokeUnmarshalled<TResult>(string identifier)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, Id);
+        }
+
+        public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, Id);
+        }
+
+        public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, Id);
+        }
+
+        public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, Id);
+        }
+    }
+}

+ 38 - 19
src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
 using System.Text.Json;
 using Microsoft.JSInterop.Infrastructure;
 using WebAssembly.JSInterop;
@@ -60,32 +61,50 @@ namespace Microsoft.JSInterop.WebAssembly
             BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0);
         }
 
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<TResult>(string identifier)
-            => ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null);
-
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
-            => ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null);
-
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
-            => ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null);
-
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+        internal TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2, long targetInstanceId)
         {
+            var resultType = JSCallResultTypeHelper.FromGeneric<TResult>();
+
             var callInfo = new JSCallInfo
             {
                 FunctionIdentifier = identifier,
-                ResultType = JSCallResultTypeHelper.FromGeneric<TResult>()
+                TargetInstanceId = targetInstanceId,
+                ResultType = resultType,
             };
 
-            var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out var exception, ref callInfo, arg0, arg1, arg2);
+            string exception;
 
-            return exception != null
-                ? throw new JSException(exception)
-                : result;
+            switch (resultType)
+            {
+                case JSCallResultType.Default:
+                    var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out exception, ref callInfo, arg0, arg1, arg2);
+                    return exception != null
+                        ? throw new JSException(exception)
+                        : result;
+                case JSCallResultType.JSObjectReference:
+                    var id = InternalCalls.InvokeJS<T0, T1, T2, int>(out exception, ref callInfo, arg0, arg1, arg2);
+                    return exception != null
+                        ? throw new JSException(exception)
+                        : (TResult)(object)new WebAssemblyJSObjectReference(this, id);
+                default:
+                    throw new InvalidOperationException($"Invalid result type '{resultType}'.");
+            }
         }
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<TResult>(string identifier)
+            => InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, 0);
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
+            => InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, 0);
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
+            => InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, 0);
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+            => InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, 0);
     }
 }

+ 1 - 1
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs

@@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
         /// <inheritdoc />
         protected override Task UpdateDisplayAsync(in RenderBatch batch)
         {
-            ((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<int, RenderBatch, object>(
+            DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<int, RenderBatch, object>(
                 "Blazor._internal.renderBatch",
                 _webAssemblyRendererId,
                 batch);

+ 1 - 1
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyConsoleLogger.cs

@@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
                             _jsRuntime.InvokeVoid("console.error", formattedMessage);
                             break;
                         case LogLevel.Critical:
-                            ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
+                            _jsRuntime.InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
                             break;
                         default: // LogLevel.None or invalid enum values
                             Console.WriteLine(formattedMessage);

+ 1 - 1
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyJSRuntimeInvoker.cs

@@ -22,6 +22,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
         public static WebAssemblyJSRuntimeInvoker Instance = new WebAssemblyJSRuntimeInvoker();
 
         public virtual TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
-            => ((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
+            => DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
     }
 }

+ 1 - 0
src/Components/test/E2ETest/Tests/InteropTest.cs

@@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
                 ["instanceMethodIncomingByRef"] = "123",
                 ["instanceMethodOutgoingByRef"] = "1234",
                 ["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
+                ["jsUnmarshalledObjectReference.unmarshalledFunction"] = "True",
                 ["stringValueUpperSync"] = "MY STRING",
                 ["testDtoNonSerializedValueSync"] = "99999",
                 ["testDtoSync"] = "Same",

+ 8 - 0
src/Components/test/testassets/BasicTestApp/InteropComponent.razor

@@ -194,6 +194,14 @@
 
         var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
         ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");
+
+        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JSRuntime;
+        var jsUnmarshalledReference = unmarshalledRuntime.InvokeUnmarshalled<IJSUnmarshalledObjectReference>("returnJSObjectReference");
+        ReturnValues["jsUnmarshalledObjectReference.unmarshalledFunction"] = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>("unmarshalledFunction", new InteropStruct
+        {
+            Message = "Sent from .NET",
+            NumberField = 42,
+        }).ToString();
     }
 
     public class PassDotNetObjectByRefArgs

+ 14 - 0
src/Components/test/testassets/BasicTestApp/InteropTest/InteropStruct.cs

@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace BasicTestApp.InteropTest
+{
+    [StructLayout(LayoutKind.Explicit)]
+    public struct InteropStruct
+    {
+        [FieldOffset(0)]
+        public string Message;
+
+        [FieldOffset(8)]
+        public int NumberField;
+    }
+}

+ 5 - 0
src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js

@@ -233,6 +233,11 @@ function returnJSObjectReference() {
     dispose: function () {
       DotNet.disposeJSObjectReference(this);
     },
+    unmarshalledFunction: function (fields) {
+      const message = Blazor.platform.readStringField(fields, 0);
+      const numberField = Blazor.platform.readInt32Field(fields, 8);
+      return message === "Sent from .NET" && numberField === 42;
+    }
   };
 }
 

+ 3 - 1
src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs

@@ -1,12 +1,14 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
+
 namespace Microsoft.JSInterop
 {
     /// <summary>
     /// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
     /// </summary>
-    public interface IJSInProcessObjectReference : IJSObjectReference
+    public interface IJSInProcessObjectReference : IJSObjectReference, IDisposable
     {
         /// <summary>
         /// Invokes the specified JavaScript function synchronously.

+ 55 - 0
src/JSInterop/Microsoft.JSInterop/src/IJSUnmarshalledObjectReference.cs

@@ -0,0 +1,55 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.JSInterop
+{
+    /// <summary>
+    /// Represents a reference to a JavaScript object whose functions can be invoked synchronously without JSON marshalling.
+    /// </summary>
+    public interface IJSUnmarshalledObjectReference : IJSInProcessObjectReference
+    {
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<TResult>(string identifier);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="T1">The type of the second argument.</typeparam>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <param name="arg1">The second argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="T1">The type of the second argument.</typeparam>
+        /// <typeparam name="T2">The type of the third argument.</typeparam>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <param name="arg1">The second argument.</param>
+        /// <param name="arg2">The third argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2);
+    }
+}

+ 45 - 0
src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.JSInterop.Implementation
+{
+    /// <summary>
+    /// Implements functionality for <see cref="IJSInProcessObjectReference"/>.
+    /// </summary>
+    public class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
+    {
+        private readonly JSInProcessRuntime _jsRuntime;
+
+        /// <summary>
+        /// Inititializes a new <see cref="JSInProcessObjectReference"/> instance.
+        /// </summary>
+        /// <param name="jsRuntime">The <see cref="JSInProcessRuntime"/> used for invoking JS interop calls.</param>
+        /// <param name="id">The unique identifier.</param>
+        protected internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id)
+        {
+            _jsRuntime = jsRuntime;
+        }
+
+        /// <inheritdoc />
+        [return: MaybeNull]
+        public TValue Invoke<TValue>(string identifier, params object?[]? args)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.Invoke<TValue>(identifier, Id, args);
+        }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            if (!Disposed)
+            {
+                Disposed = true;
+
+                _jsRuntime.InvokeVoid("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
+            }
+        }
+    }
+}

+ 23 - 11
src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs → src/JSInterop/Microsoft.JSInterop/src/Implementation/JSObjectReference.cs

@@ -2,29 +2,38 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
-using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace Microsoft.JSInterop
+namespace Microsoft.JSInterop.Implementation
 {
-    internal class JSObjectReference : IJSObjectReference
+    /// <summary>
+    /// Implements functionality for <see cref="IJSObjectReference"/>.
+    /// </summary>
+    public class JSObjectReference : IJSObjectReference
     {
-        public static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId");
-
         private readonly JSRuntime _jsRuntime;
 
-        private bool _disposed;
+        internal bool Disposed { get; set; }
 
-        public long Id { get; }
+        /// <summary>
+        /// The unique identifier assigned to this instance.
+        /// </summary>
+        protected internal long Id { get; }
 
-        public JSObjectReference(JSRuntime jsRuntime, long id)
+        /// <summary>
+        /// Inititializes a new <see cref="JSObjectReference"/> instance.
+        /// </summary>
+        /// <param name="jsRuntime">The <see cref="JSRuntime"/> used for invoking JS interop calls.</param>
+        /// <param name="id">The unique identifier.</param>
+        protected internal JSObjectReference(JSRuntime jsRuntime, long id)
         {
             _jsRuntime = jsRuntime;
 
             Id = id;
         }
 
+        /// <inheritdoc />
         public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
         {
             ThrowIfDisposed();
@@ -32,6 +41,7 @@ namespace Microsoft.JSInterop
             return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
         }
 
+        /// <inheritdoc />
         public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
         {
             ThrowIfDisposed();
@@ -39,19 +49,21 @@ namespace Microsoft.JSInterop
             return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
         }
 
+        /// <inheritdoc />
         public async ValueTask DisposeAsync()
         {
-            if (!_disposed)
+            if (!Disposed)
             {
-                _disposed = true;
+                Disposed = true;
 
                 await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
             }
         }
 
+        /// <inheritdoc />
         protected void ThrowIfDisposed()
         {
-            if (_disposed)
+            if (Disposed)
             {
                 throw new ObjectDisposedException(GetType().Name);
             }

+ 6 - 3
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Text.Json;
 using System.Text.Json.Serialization;
+using Microsoft.JSInterop.Implementation;
 
 namespace Microsoft.JSInterop.Infrastructure
 {
@@ -11,6 +12,8 @@ namespace Microsoft.JSInterop.Infrastructure
         where TInterface : class, IJSObjectReference
         where TImplementation : JSObjectReference, TInterface
     {
+        private static readonly JsonEncodedText _idKey = JsonEncodedText.Encode("__jsObjectId");
+
         private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
 
         public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
@@ -29,7 +32,7 @@ namespace Microsoft.JSInterop.Infrastructure
             {
                 if (reader.TokenType == JsonTokenType.PropertyName)
                 {
-                    if (id == -1 && reader.ValueTextEquals(JSObjectReference.IdKey.EncodedUtf8Bytes))
+                    if (id == -1 && reader.ValueTextEquals(_idKey.EncodedUtf8Bytes))
                     {
                         reader.Read();
                         id = reader.GetInt64();
@@ -47,7 +50,7 @@ namespace Microsoft.JSInterop.Infrastructure
 
             if (id == -1)
             {
-                throw new JsonException($"Required property {JSObjectReference.IdKey} not found.");
+                throw new JsonException($"Required property {_idKey} not found.");
             }
 
             return _jsObjectReferenceFactory(id);
@@ -56,7 +59,7 @@ namespace Microsoft.JSInterop.Infrastructure
         public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
         {
             writer.WriteStartObject();
-            writer.WriteNumber(JSObjectReference.IdKey, ((TImplementation)value).Id);
+            writer.WriteNumber(_idKey, ((TImplementation)value).Id);
             writer.WriteEndObject();
         }
     }

+ 0 - 25
src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs

@@ -1,25 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Diagnostics.CodeAnalysis;
-
-namespace Microsoft.JSInterop
-{
-    internal class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
-    {
-        private readonly JSInProcessRuntime _jsRuntime;
-
-        internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id)
-        {
-            _jsRuntime = jsRuntime;
-        }
-
-        [return: MaybeNull]
-        public TValue Invoke<TValue>(string identifier, params object?[]? args)
-        {
-            ThrowIfDisposed();
-
-            return _jsRuntime.Invoke<TValue>(identifier, Id, args);
-        }
-    }
-}

+ 4 - 2
src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs

@@ -3,6 +3,7 @@
 
 using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
+using Microsoft.JSInterop.Implementation;
 using Microsoft.JSInterop.Infrastructure;
 
 namespace Microsoft.JSInterop
@@ -17,8 +18,9 @@ namespace Microsoft.JSInterop
         /// </summary>
         protected JSInProcessRuntime()
         {
-            JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
-                id => new JSInProcessObjectReference(this, id)));
+            JsonSerializerOptions.Converters.Add(
+                new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
+                    id => new JSInProcessObjectReference(this, id)));
         }
 
         [return: MaybeNull]

+ 3 - 1
src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs

@@ -8,6 +8,7 @@ using System.Linq;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
+using Microsoft.JSInterop.Implementation;
 using Microsoft.JSInterop.Infrastructure;
 
 namespace Microsoft.JSInterop
@@ -37,7 +38,8 @@ namespace Microsoft.JSInterop
                 Converters =
                 {
                     new DotNetObjectReferenceJsonConverterFactory(this),
-                    new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(id => new JSObjectReference(this, id)),
+                    new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(
+                        id => new JSObjectReference(this, id)),
                 }
             };
         }

+ 1 - 1
src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>

+ 68 - 2
src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs

@@ -1,7 +1,9 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
 using System.Text.Json;
+using Microsoft.JSInterop.Implementation;
 using Xunit;
 
 namespace Microsoft.JSInterop.Infrastructure
@@ -9,7 +11,16 @@ namespace Microsoft.JSInterop.Infrastructure
     public class JSObjectReferenceJsonConverterTest
     {
         private readonly JSRuntime JSRuntime = new TestJSRuntime();
-        private JsonSerializerOptions JsonSerializerOptions => JSRuntime.JsonSerializerOptions;
+        private readonly JsonSerializerOptions JsonSerializerOptions;
+
+        public JSObjectReferenceJsonConverterTest()
+        {
+            JsonSerializerOptions = JSRuntime.JsonSerializerOptions;
+            JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
+                id => new JSInProcessObjectReference(default!, id)));
+            JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSUnmarshalledObjectReference, TestJSUnmarshalledObjectReference>(
+                id => new TestJSUnmarshalledObjectReference(id)));
+        }
 
         [Fact]
         public void Read_Throws_IfJsonIsMissingJSObjectIdProperty()
@@ -56,7 +67,7 @@ namespace Microsoft.JSInterop.Infrastructure
         }
 
         [Fact]
-        public void Read_ReadsJson()
+        public void Read_ReadsJson_IJSObjectReference()
         {
             // Arrange
             var expectedId = 3;
@@ -69,6 +80,34 @@ namespace Microsoft.JSInterop.Infrastructure
             Assert.Equal(expectedId, deserialized?.Id);
         }
 
+        [Fact]
+        public void Read_ReadsJson_IJSInProcessObjectReference()
+        {
+            // Arrange
+            var expectedId = 3;
+            var json = $"{{\"__jsObjectId\":{expectedId}}}";
+
+            // Act
+            var deserialized = (JSInProcessObjectReference)JsonSerializer.Deserialize<IJSInProcessObjectReference>(json, JsonSerializerOptions)!;
+
+            // Assert
+            Assert.Equal(expectedId, deserialized?.Id);
+        }
+
+        [Fact]
+        public void Read_ReadsJson_IJSUnmarshalledObjectReference()
+        {
+            // Arrange
+            var expectedId = 3;
+            var json = $"{{\"__jsObjectId\":{expectedId}}}";
+
+            // Act
+            var deserialized = (TestJSUnmarshalledObjectReference)JsonSerializer.Deserialize<IJSUnmarshalledObjectReference>(json, JsonSerializerOptions)!;
+
+            // Assert
+            Assert.Equal(expectedId, deserialized?.Id);
+        }
+
         [Fact]
         public void Write_WritesValidJson()
         {
@@ -81,5 +120,32 @@ namespace Microsoft.JSInterop.Infrastructure
             // Assert
             Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json);
         }
+
+        private class TestJSUnmarshalledObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
+        {
+            public TestJSUnmarshalledObjectReference(long id) : base(default!, id)
+            {
+            }
+
+            public TResult InvokeUnmarshalled<TResult>(string identifier)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+            {
+                throw new NotImplementedException();
+            }
+        }
     }
 }

+ 1 - 0
src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Threading;
 using System.Threading.Tasks;
+using Microsoft.JSInterop.Implementation;
 using Microsoft.JSInterop.Infrastructure;
 using Xunit;
 

+ 3 - 1
src/Shared/JSInterop/JSCallResultTypeHelper.cs

@@ -6,7 +6,9 @@ namespace Microsoft.JSInterop
     internal static class JSCallResultTypeHelper
     {
         public static JSCallResultType FromGeneric<TResult>()
-            => typeof(TResult) == typeof(IJSObjectReference) || typeof(TResult) == typeof(IJSInProcessObjectReference) ?
+            => typeof(TResult) == typeof(IJSObjectReference)
+            || typeof(TResult) == typeof(IJSInProcessObjectReference)
+            || typeof(TResult) == typeof(IJSUnmarshalledObjectReference) ?
                 JSCallResultType.JSObjectReference :
                 JSCallResultType.Default;
     }

Некоторые файлы не были показаны из-за большого количества измененных файлов