|
7 | 7 | using System.Diagnostics.CodeAnalysis; |
8 | 8 | using System.Reflection; |
9 | 9 | using System.Reflection.Metadata; |
| 10 | +using System.Runtime.CompilerServices; |
10 | 11 | using System.Runtime.ExceptionServices; |
11 | 12 | using System.Text; |
12 | 13 | using System.Text.Json; |
@@ -55,7 +56,7 @@ public static class DotNetDispatcher |
55 | 56 | targetInstance = jsRuntime.GetObjectReference(invocationInfo.DotNetObjectId); |
56 | 57 | } |
57 | 58 |
|
58 | | - var syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson); |
| 59 | + var syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson, isAsyncContext: false); |
59 | 60 | if (syncResult == null) |
60 | 61 | { |
61 | 62 | return null; |
@@ -94,7 +95,7 @@ public static void BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo i |
94 | 95 | targetInstance = jsRuntime.GetObjectReference(invocationInfo.DotNetObjectId); |
95 | 96 | } |
96 | 97 |
|
97 | | - syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson); |
| 98 | + syncResult = InvokeSynchronously(jsRuntime, invocationInfo, targetInstance, argsJson, isAsyncContext: true); |
98 | 99 | } |
99 | 100 | catch (Exception ex) |
100 | 101 | { |
@@ -153,7 +154,7 @@ private static void EndInvokeDotNetAfterTask(Task task, JSRuntime jsRuntime, in |
153 | 154 | jsRuntime.EndInvokeDotNet(invocationInfo, new DotNetInvocationResult(resultJson)); |
154 | 155 | } |
155 | 156 |
|
156 | | - private static object? InvokeSynchronously(JSRuntime jsRuntime, in DotNetInvocationInfo callInfo, IDotNetObjectReference? objectReference, string argsJson) |
| 157 | + private static object? InvokeSynchronously(JSRuntime jsRuntime, in DotNetInvocationInfo callInfo, IDotNetObjectReference? objectReference, string argsJson, bool isAsyncContext) |
157 | 158 | { |
158 | 159 | var assemblyName = callInfo.AssemblyName; |
159 | 160 | var methodIdentifier = callInfo.MethodIdentifier; |
@@ -183,6 +184,13 @@ private static void EndInvokeDotNetAfterTask(Task task, JSRuntime jsRuntime, in |
183 | 184 | (methodInfo, parameterTypes) = GetCachedMethodInfo(objectReference, methodIdentifier); |
184 | 185 | } |
185 | 186 |
|
| 187 | + // If the method is async but is not called asynchronously, throw to indicate the misuse |
| 188 | + // We need to check the asyncContext flag since this method is used for both sync and async calls |
| 189 | + if (!isAsyncContext && IsAsyncMethod(methodInfo)) |
| 190 | + { |
| 191 | + throw new InvalidOperationException($"The method '{methodIdentifier}' cannot be invoked synchronously because it is asynchronous. Use '{nameof(BeginInvokeDotNet)}' instead."); |
| 192 | + } |
| 193 | + |
186 | 194 | var suppliedArgs = ParseArguments(jsRuntime, methodIdentifier, argsJson, parameterTypes); |
187 | 195 |
|
188 | 196 | try |
@@ -211,6 +219,8 @@ private static void EndInvokeDotNetAfterTask(Task task, JSRuntime jsRuntime, in |
211 | 219 | } |
212 | 220 | } |
213 | 221 |
|
| 222 | + private static bool IsAsyncMethod(MethodInfo methodInfo) => methodInfo.GetCustomAttribute<AsyncStateMachineAttribute>() != null; |
| 223 | + |
214 | 224 | [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "We expect application code is configured to ensure return types of JSInvokable methods are retained.")] |
215 | 225 | internal static object?[] ParseArguments(JSRuntime jsRuntime, string methodIdentifier, string arguments, Type[] parameterTypes) |
216 | 226 | { |
|
0 commit comments