|
2 | 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
3 | 3 |
|
4 | 4 | using System;
|
| 5 | +using System.Buffers; |
5 | 6 | using System.Collections.Concurrent;
|
6 | 7 | using System.Collections.Generic;
|
| 8 | +using System.Diagnostics; |
7 | 9 | using System.Diagnostics.CodeAnalysis;
|
8 | 10 | using System.Reflection;
|
9 | 11 | using System.Reflection.Metadata;
|
@@ -188,62 +190,73 @@ private static void EndInvokeDotNetAfterTask(Task task, JSRuntime jsRuntime, in
|
188 | 190 | return Array.Empty<object>();
|
189 | 191 | }
|
190 | 192 |
|
191 |
| - var utf8JsonBytes = Encoding.UTF8.GetBytes(arguments); |
192 |
| - var reader = new Utf8JsonReader(utf8JsonBytes); |
193 |
| - if (!reader.Read() || reader.TokenType != JsonTokenType.StartArray) |
| 193 | + var count = Encoding.UTF8.GetByteCount(arguments); |
| 194 | + var buffer = ArrayPool<byte>.Shared.Rent(count); |
| 195 | + try |
194 | 196 | {
|
195 |
| - throw new JsonException("Invalid JSON"); |
196 |
| - } |
| 197 | + var receivedBytes = Encoding.UTF8.GetBytes(arguments, buffer); |
| 198 | + Debug.Assert(count == receivedBytes); |
197 | 199 |
|
198 |
| - var suppliedArgs = new object?[parameterTypes.Length]; |
199 |
| - |
200 |
| - var index = 0; |
201 |
| - while (index < parameterTypes.Length && reader.Read() && reader.TokenType != JsonTokenType.EndArray) |
202 |
| - { |
203 |
| - var parameterType = parameterTypes[index]; |
204 |
| - if (reader.TokenType == JsonTokenType.StartObject && IsIncorrectDotNetObjectRefUse(parameterType, reader)) |
| 200 | + var reader = new Utf8JsonReader(buffer.AsSpan(0, count)); |
| 201 | + if (!reader.Read() || reader.TokenType != JsonTokenType.StartArray) |
205 | 202 | {
|
206 |
| - throw new InvalidOperationException($"In call to '{methodIdentifier}', parameter of type '{parameterType.Name}' at index {(index + 1)} must be declared as type 'DotNetObjectRef<{parameterType.Name}>' to receive the incoming value."); |
| 203 | + throw new JsonException("Invalid JSON"); |
207 | 204 | }
|
208 | 205 |
|
209 |
| - suppliedArgs[index] = JsonSerializer.Deserialize(ref reader, parameterType, jsRuntime.JsonSerializerOptions); |
210 |
| - index++; |
211 |
| - } |
| 206 | + var suppliedArgs = new object?[parameterTypes.Length]; |
212 | 207 |
|
213 |
| - // Note it's possible not all ByteArraysToBeRevived were actually revived |
214 |
| - // due to potential differences between the JS & .NET data models for a |
215 |
| - // particular type. |
216 |
| - jsRuntime.ByteArraysToBeRevived.Clear(); |
| 208 | + var index = 0; |
| 209 | + while (index < parameterTypes.Length && reader.Read() && reader.TokenType != JsonTokenType.EndArray) |
| 210 | + { |
| 211 | + var parameterType = parameterTypes[index]; |
| 212 | + if (reader.TokenType == JsonTokenType.StartObject && IsIncorrectDotNetObjectRefUse(parameterType, reader)) |
| 213 | + { |
| 214 | + throw new InvalidOperationException($"In call to '{methodIdentifier}', parameter of type '{parameterType.Name}' at index {(index + 1)} must be declared as type 'DotNetObjectRef<{parameterType.Name}>' to receive the incoming value."); |
| 215 | + } |
217 | 216 |
|
218 |
| - if (index < parameterTypes.Length) |
219 |
| - { |
220 |
| - // If we parsed fewer parameters, we can always make a definitive claim about how many parameters were received. |
221 |
| - throw new ArgumentException($"The call to '{methodIdentifier}' expects '{parameterTypes.Length}' parameters, but received '{index}'."); |
222 |
| - } |
| 217 | + suppliedArgs[index] = JsonSerializer.Deserialize(ref reader, parameterType, jsRuntime.JsonSerializerOptions); |
| 218 | + index++; |
| 219 | + } |
223 | 220 |
|
224 |
| - if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray) |
225 |
| - { |
226 |
| - // Either we received more parameters than we expected or the JSON is malformed. |
227 |
| - throw new JsonException($"Unexpected JSON token {reader.TokenType}. Ensure that the call to `{methodIdentifier}' is supplied with exactly '{parameterTypes.Length}' parameters."); |
228 |
| - } |
| 221 | + // Note it's possible not all ByteArraysToBeRevived were actually revived |
| 222 | + // due to potential differences between the JS & .NET data models for a |
| 223 | + // particular type. |
| 224 | + jsRuntime.ByteArraysToBeRevived.Clear(); |
229 | 225 |
|
230 |
| - return suppliedArgs; |
| 226 | + if (index < parameterTypes.Length) |
| 227 | + { |
| 228 | + // If we parsed fewer parameters, we can always make a definitive claim about how many parameters were received. |
| 229 | + throw new ArgumentException($"The call to '{methodIdentifier}' expects '{parameterTypes.Length}' parameters, but received '{index}'."); |
| 230 | + } |
231 | 231 |
|
232 |
| - // Note that the JsonReader instance is intentionally not passed by ref (or an in parameter) since we want a copy of the original reader. |
233 |
| - static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jsonReader) |
234 |
| - { |
235 |
| - // Check for incorrect use of DotNetObjectRef<T> at the top level. We know it's |
236 |
| - // an incorrect use if there's a object that looks like { '__dotNetObject': <some number> }, |
237 |
| - // but we aren't assigning to DotNetObjectRef{T}. |
238 |
| - if (jsonReader.Read() && |
239 |
| - jsonReader.TokenType == JsonTokenType.PropertyName && |
240 |
| - jsonReader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) |
| 232 | + if (!reader.Read() || reader.TokenType != JsonTokenType.EndArray) |
241 | 233 | {
|
242 |
| - // The JSON payload has the shape we expect from a DotNetObjectRef instance. |
243 |
| - return !parameterType.IsGenericType || parameterType.GetGenericTypeDefinition() != typeof(DotNetObjectReference<>); |
| 234 | + // Either we received more parameters than we expected or the JSON is malformed. |
| 235 | + throw new JsonException($"Unexpected JSON token {reader.TokenType}. Ensure that the call to `{methodIdentifier}' is supplied with exactly '{parameterTypes.Length}' parameters."); |
244 | 236 | }
|
245 | 237 |
|
246 |
| - return false; |
| 238 | + return suppliedArgs; |
| 239 | + |
| 240 | + // Note that the JsonReader instance is intentionally not passed by ref (or an in parameter) since we want a copy of the original reader. |
| 241 | + static bool IsIncorrectDotNetObjectRefUse(Type parameterType, Utf8JsonReader jsonReader) |
| 242 | + { |
| 243 | + // Check for incorrect use of DotNetObjectRef<T> at the top level. We know it's |
| 244 | + // an incorrect use if there's a object that looks like { '__dotNetObject': <some number> }, |
| 245 | + // but we aren't assigning to DotNetObjectRef{T}. |
| 246 | + if (jsonReader.Read() && |
| 247 | + jsonReader.TokenType == JsonTokenType.PropertyName && |
| 248 | + jsonReader.ValueTextEquals(DotNetObjectRefKey.EncodedUtf8Bytes)) |
| 249 | + { |
| 250 | + // The JSON payload has the shape we expect from a DotNetObjectRef instance. |
| 251 | + return !parameterType.IsGenericType || parameterType.GetGenericTypeDefinition() != typeof(DotNetObjectReference<>); |
| 252 | + } |
| 253 | + |
| 254 | + return false; |
| 255 | + } |
| 256 | + } |
| 257 | + finally |
| 258 | + { |
| 259 | + ArrayPool<byte>.Shared.Return(buffer); |
247 | 260 | }
|
248 | 261 | }
|
249 | 262 |
|
|
0 commit comments