From d8285e90f28e92f3cb657d5410020abe32dc68aa Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Sat, 2 May 2026 00:17:55 +0000 Subject: [PATCH 01/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-efcore build 20260501.6 On relative base path root dotnet-ef , Microsoft.EntityFrameworkCore , Microsoft.EntityFrameworkCore.Design , Microsoft.EntityFrameworkCore.InMemory , Microsoft.EntityFrameworkCore.Relational , Microsoft.EntityFrameworkCore.Sqlite , Microsoft.EntityFrameworkCore.SqlServer , Microsoft.EntityFrameworkCore.Tools From Version 8.0.27 -> To Version 8.0.27 --- NuGet.config | 4 ++-- eng/Version.Details.xml | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/NuGet.config b/NuGet.config index 35f6e2161360..f86e8a9b45f2 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,7 +6,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index dd945f5f6a7b..7993e1f2e5e7 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -11,35 +11,35 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - f119d39bcc0bc6c50f0a5e1965a145878b2c13bd + e41f35348b9fcd6fa3c564195b0f963ca2743670 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime From ef2a59c9f421ff12107225123bb9d555e6415768 Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Wed, 6 May 2026 00:26:24 +0000 Subject: [PATCH 02/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-efcore build 20260505.5 On relative base path root dotnet-ef , Microsoft.EntityFrameworkCore , Microsoft.EntityFrameworkCore.Design , Microsoft.EntityFrameworkCore.InMemory , Microsoft.EntityFrameworkCore.Relational , Microsoft.EntityFrameworkCore.Sqlite , Microsoft.EntityFrameworkCore.SqlServer , Microsoft.EntityFrameworkCore.Tools From Version 8.0.27 -> To Version 8.0.28 --- NuGet.config | 4 ++-- eng/Version.Details.xml | 32 ++++++++++++++++---------------- eng/Versions.props | 16 ++++++++-------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/NuGet.config b/NuGet.config index f86e8a9b45f2..c91cc9ea537e 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,7 +6,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7993e1f2e5e7..b04b0c847738 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,37 +9,37 @@ --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - e41f35348b9fcd6fa3c564195b0f963ca2743670 + de661bae8242a593a1a4c4f84ef74e25be0ecd88 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime diff --git a/eng/Versions.props b/eng/Versions.props index 1aafb34c50d7..0c11b82559fc 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -142,14 +142,14 @@ 9.0.0-preview.9.24518.1 9.0.0-preview.9.24518.1 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 4.8.0-7.24574.2 4.8.0-7.24574.2 From a2aae246a1e48f6d8aee64513c45c39a61ec7adf Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Fri, 8 May 2026 17:22:32 +0000 Subject: [PATCH 03/16] Merged PR 60175: [release/8.0] gRPC transcoding: respecting json-name aliases Fixed issue where in grpc transcoding query parameter processing overwrites route or body-bound fields. Also fixing unmatched query parameters stored in the descriptor cache --- .../CallHandlers/CallHandlerDescriptorInfo.cs | 11 +- .../Internal/JsonRequestHelpers.cs | 48 +++- .../RouteTests.cs | 29 +++ .../Proto/transcoding.proto | 1 + .../UnaryServerCallHandlerTests.cs | 226 ++++++++++++++++++ 5 files changed, 301 insertions(+), 14 deletions(-) diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs index b9c1b70c78c8..49a3b28bb18b 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs @@ -24,7 +24,14 @@ public CallHandlerDescriptorInfo( BodyFieldDescriptor = bodyFieldDescriptor; RouteParameterDescriptors = routeParameterDescriptors; RouteAdapter = routeAdapter; - PathDescriptorsCache = new ConcurrentDictionary?>(); + PathDescriptorsCache = new ConcurrentDictionary>(); + + var jsonPaths = new HashSet(StringComparer.Ordinal); + foreach (var routeParameter in routeParameterDescriptors.Values) + { + jsonPaths.Add(routeParameter.JsonPath); + } + RouteParameterJsonPaths = jsonPaths; } public FieldDescriptor? ResponseBodyDescriptor { get; } @@ -34,5 +41,5 @@ public CallHandlerDescriptorInfo( public FieldDescriptor? BodyFieldDescriptor { get; } public Dictionary RouteParameterDescriptors { get; } public JsonTranscodingRouteAdapter RouteAdapter { get; } - public ConcurrentDictionary?> PathDescriptorsCache { get; } + public ConcurrentDictionary> PathDescriptorsCache { get; } } diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs index ef0ec6d3f0cc..9e4ba1480ba4 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs @@ -341,11 +341,20 @@ private static async ValueTask ReadDataAsync(JsonTranscodingServerCallCo private static List? GetPathDescriptors(JsonTranscodingServerCallContext serverCallContext, IMessage requestMessage, string path) { - return serverCallContext.DescriptorInfo.PathDescriptorsCache.GetOrAdd(path, p => + // Must not add null values for paths that don't resolve to a descriptor + var cache = serverCallContext.DescriptorInfo.PathDescriptorsCache; + if (cache.TryGetValue(path, out var pathDescriptors)) { - ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, p.Split('.'), allowJsonName: true, out var pathDescriptors); return pathDescriptors; - }); + } + + if (ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, path.Split('.'), allowJsonName: true, out pathDescriptors)) + { + cache.TryAdd(path, pathDescriptors); + return pathDescriptors; + } + + return null; } public static async ValueTask SendMessage(JsonTranscodingServerCallContext serverCallContext, JsonSerializerOptions serializerOptions, TResponse message, CancellationToken cancellationToken) where TResponse : class @@ -388,24 +397,25 @@ private static bool CanBindQueryStringVariable(JsonTranscodingServerCallContext { if (serverCallContext.DescriptorInfo.BodyDescriptor != null) { - var bodyFieldName = serverCallContext.DescriptorInfo.BodyFieldDescriptor?.Name; + var bodyFieldDescriptor = serverCallContext.DescriptorInfo.BodyFieldDescriptor; - // Null field name indicates "*" which means the entire message is bound to the body. - if (bodyFieldName == null) + // Null field descriptor indicates "*" which means the entire message is bound to the body. + if (bodyFieldDescriptor?.Name is null) { return false; } - // Exact match - if (variable == bodyFieldName) + var bodyFieldName = bodyFieldDescriptor.Name; + var bodyFieldJsonName = bodyFieldDescriptor.JsonName; + + // Exact match (proto name or JSON name) + if (variable == bodyFieldName || variable == bodyFieldJsonName) { return false; } - // Nested field of field name. - if (bodyFieldName.Length + 1 < variable.Length && - variable.StartsWith(bodyFieldName, StringComparison.Ordinal) && - variable[bodyFieldName.Length] == '.') + // Nested field of body field (proto name prefix or JSON name prefix). + if (IsNestedBodyField(variable, bodyFieldName) || IsNestedBodyField(variable, bodyFieldJsonName)) { return false; } @@ -416,6 +426,20 @@ private static bool CanBindQueryStringVariable(JsonTranscodingServerCallContext return false; } + // Also check JSON name aliases. Route parameter keys use proto names (e.g. "user_id"), + // but query parameters can use JSON names (e.g. "userId") which resolve to the same field. + if (serverCallContext.DescriptorInfo.RouteParameterJsonPaths.Contains(variable)) + { + return false; + } + return true; } + + private static bool IsNestedBodyField(string variable, string bodyFieldName) + { + return bodyFieldName.Length + 1 < variable.Length && + variable.StartsWith(bodyFieldName, StringComparison.Ordinal) && + variable[bodyFieldName.Length] == '.'; + } } diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs index c3482d12264d..d3bef8922333 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs @@ -105,6 +105,35 @@ Task UnaryMethod(ComplextHelloRequest request, ServerCallContext con Assert.Equal("Hello complex_greeter/test2/b last_name!", result.RootElement.GetProperty("message").GetString()); } + [Fact] + public async Task ComplexParameter_NestedJsonNameQueryString_DoesNotOverwriteRouteValue() + { + // Arrange + Task UnaryMethod(ComplextHelloRequest request, ServerCallContext context) + { + return Task.FromResult(new HelloReply { Message = $"Hello {request.Name.FirstName} {request.Name.LastName}!" }); + } + var method = Fixture.DynamicGrpc.AddUnaryMethod( + UnaryMethod, + Greeter.Descriptor.FindMethodByName("SayHelloComplexCatchAll3")); + var client = new HttpClient(Fixture.Handler) { BaseAddress = new Uri("http://localhost") }; + + // route.binding sets + // request.Name.LastName = "last_name"; + // request.Name.FirstName = "complex_greeter/test2/b" + // ---- + // query binding tries to overwrite with + // name.firstName=query_first + // name.lastName=query_last + + // Act + var response = await client.GetAsync("/v1/last_name/complex_greeter/test2/b/c/d/two?name.firstName=query_first&name.lastName=query_last").DefaultTimeout(); + var responseStream = await response.Content.ReadAsStreamAsync(); + using var result = await JsonDocument.ParseAsync(responseStream); + // Assert + Assert.Equal("Hello complex_greeter/test2/b last_name!", result.RootElement.GetProperty("message").GetString()); + } + [Fact] public async Task SimpleCatchAllParameter_PrefixSuffixSlashes_MatchUrl_SuccessResult() { diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Proto/transcoding.proto b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Proto/transcoding.proto index e44535586bba..7b817554e737 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Proto/transcoding.proto +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Proto/transcoding.proto @@ -218,6 +218,7 @@ message HelloRequest { string hiding_field_name = 24 [json_name="field_name"]; repeated SubMessage repeated_messages = 25; map map_keyint_valueint = 26; + SubMessage sub_data = 27; } message HelloReply { diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs index 561a002bb8f7..23d7565502b6 100644 --- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs +++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs @@ -40,6 +40,12 @@ private static RouteParameter CreateRouteParameter(List descrip return new RouteParameter(descriptorPath, new HttpRouteVariable(), string.Empty); } + private static RouteParameter CreateRouteParameterWithJsonPath(List descriptorPath) + { + var jsonPath = string.Join(".", descriptorPath.Select(d => d.JsonName)); + return new RouteParameter(descriptorPath, new HttpRouteVariable(), jsonPath); + } + [Fact] public async Task HandleCallAsync_MatchingRouteValue_SetOnRequestMessage() { @@ -1345,6 +1351,46 @@ public async Task HandleCallAsync_MatchingRepeatedQueryStringValues_SetOnRequest Assert.Equal("TestSubfields2!", request!.Sub.Subfields[1]); } + [Fact] + public async Task HandleCallAsync_UnmatchedQueryStringValues_NotCached() + { + // Arrange + HelloRequest? request = null; + UnaryServerMethod invoker = (s, r, c) => + { + request = r; + return Task.FromResult(new HelloReply()); + }; + + var descriptorInfo = TestHelpers.CreateDescriptorInfo(); + var unaryServerCallHandler = CreateCallHandler(invoker, descriptorInfo); + var httpContext = TestHelpers.CreateHttpContext(); + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["age"] = "10", + ["sub.subfield"] = "TestSubfield!", + ["unknown"] = "value", + ["sub.unknown"] = "value", + ["name.unknown"] = "value" + }); + + // Act + await unaryServerCallHandler.HandleCallAsync(httpContext); + + // Assert + Assert.NotNull(request); + Assert.Equal(10, request!.Age); + Assert.Equal("TestSubfield!", request!.Sub.Subfield); + // matches query params + Assert.Equal(2, descriptorInfo.PathDescriptorsCache.Count); + Assert.True(descriptorInfo.PathDescriptorsCache.ContainsKey("age")); + Assert.True(descriptorInfo.PathDescriptorsCache.ContainsKey("sub.subfield")); + // not matched query params + Assert.False(descriptorInfo.PathDescriptorsCache.ContainsKey("unknown")); + Assert.False(descriptorInfo.PathDescriptorsCache.ContainsKey("sub.unknown")); + Assert.False(descriptorInfo.PathDescriptorsCache.ContainsKey("name.unknown")); + } + [Fact] public async Task HandleCallAsync_DataTypes_SetOnRequestMessage() { @@ -1644,6 +1690,186 @@ public async Task HandleCallAsync_MatchingQueryStringValues_KnownType_FieldSette Assert.Equal(fieldmask, request!.FieldMaskValue); } + [Fact] + public async Task HandleCallAsync_QueryStringJsonNameAlias_DoesNotOverwriteRouteValue() + { + // message HelloRequest { + // string name = 1; // proto name: "name", JSON name: "name" + // int32 age = 13; // proto name: "age", JSON name: "age" + // string field_name = 22 [json_name="json_customized_name"]; // proto name: "field_name", JSON name: "json_customized_name" + // ... other fields + // } + + // Arrange + // A query parameter using the JSON name should not overwrite a route-bound field. + HelloRequest? request = null; + UnaryServerMethod invoker = (s, r, c) => + { + request = r; + return Task.FromResult(new HelloReply()); + }; + + var fieldDescriptor = HelloRequest.Descriptor.FindFieldByName("field_name"); + var routeParameterDescriptors = new Dictionary + { + ["field_name"] = CreateRouteParameterWithJsonPath(new List(new[] { fieldDescriptor })) + }; + var descriptorInfo = TestHelpers.CreateDescriptorInfo(routeParameterDescriptors: routeParameterDescriptors); + var unaryServerCallHandler = CreateCallHandler(invoker, descriptorInfo: descriptorInfo); + var httpContext = TestHelpers.CreateHttpContext(); + httpContext.Request.RouteValues["field_name"] = "route_value"; + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["json_customized_name"] = "different_value" + }); + + // Act + await unaryServerCallHandler.HandleCallAsync(httpContext); + + // Assert + Assert.NotNull(request); + Assert.Equal("route_value", request!.FieldName); + } + + [Fact] + public async Task HandleCallAsync_QueryStringProtoName_DoesNotOverwriteRouteValue() + { + // Arrange + // A query parameter using the proto name is not overwritting the route-bound field. + HelloRequest? request = null; + UnaryServerMethod invoker = (s, r, c) => + { + request = r; + return Task.FromResult(new HelloReply()); + }; + + var routeParameterDescriptors = new Dictionary + { + ["name"] = CreateRouteParameterWithJsonPath(new List(new[] { HelloRequest.Descriptor.FindFieldByNumber(HelloRequest.NameFieldNumber) })) + }; + var descriptorInfo = TestHelpers.CreateDescriptorInfo(routeParameterDescriptors: routeParameterDescriptors); + var unaryServerCallHandler = CreateCallHandler(invoker, descriptorInfo: descriptorInfo); + var httpContext = TestHelpers.CreateHttpContext(); + httpContext.Request.RouteValues["name"] = "route_value"; + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["name"] = "different_value" + }); + + // Act + await unaryServerCallHandler.HandleCallAsync(httpContext); + + // Assert + Assert.NotNull(request); + Assert.Equal("route_value", request!.Name); + } + + [Fact] + public async Task HandleCallAsync_QueryStringNonRouteField_StillBindsNormally() + { + // Arrange + // Query parameters for fields NOT bound via route should work. + HelloRequest? request = null; + UnaryServerMethod invoker = (s, r, c) => + { + request = r; + return Task.FromResult(new HelloReply()); + }; + + var routeParameterDescriptors = new Dictionary + { + ["name"] = CreateRouteParameterWithJsonPath(new List(new[] { HelloRequest.Descriptor.FindFieldByNumber(HelloRequest.NameFieldNumber) })) + }; + var descriptorInfo = TestHelpers.CreateDescriptorInfo(routeParameterDescriptors: routeParameterDescriptors); + var unaryServerCallHandler = CreateCallHandler(invoker, descriptorInfo: descriptorInfo); + var httpContext = TestHelpers.CreateHttpContext(); + httpContext.Request.RouteValues["name"] = "route_value"; + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["age"] = "30" + }); + + // Act + await unaryServerCallHandler.HandleCallAsync(httpContext); + + // Assert + Assert.NotNull(request); + Assert.Equal("route_value", request!.Name); + Assert.Equal(30, request!.Age); + } + + [Fact] + public async Task HandleCallAsync_QueryStringBodyFieldJsonNameAlias_DoesNotOverwriteBodyValue() + { + // Arrange + // body: "sub" binds the sub field from JSON body. + // A query parameter using the JSON name of the body field should not overwrite it. + HelloRequest? request = null; + UnaryServerMethod invoker = (s, r, c) => + { + request = r; + return Task.FromResult(new HelloReply()); + }; + + var descriptorInfo = TestHelpers.CreateDescriptorInfo( + bodyDescriptor: HelloRequest.Types.SubMessage.Descriptor, + bodyFieldDescriptor: HelloRequest.Descriptor.FindFieldByName("sub")); + var unaryServerCallHandler = CreateCallHandler(invoker, descriptorInfo: descriptorInfo); + var httpContext = TestHelpers.CreateHttpContext(); + httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(JsonFormatter.Default.Format(new HelloRequest.Types.SubMessage + { + Subfield = "body_value" + }))); + httpContext.Request.ContentType = "application/json"; + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["sub.subfield"] = "different_value" + }); + + // Act + await unaryServerCallHandler.HandleCallAsync(httpContext); + + // Assert + Assert.NotNull(request); + Assert.Equal("body_value", request!.Sub.Subfield); + } + + [Fact] + public async Task HandleCallAsync_QueryStringBodyFieldJsonNamePrefix_DoesNotOverwriteBodyValue() + { + // Arrange + // body field: "sub_data" (proto name) has JSON name "subData". + // A query parameter using the JSON name prefix "subData.subfield" should be blocked. + HelloRequest? request = null; + UnaryServerMethod invoker = (s, r, c) => + { + request = r; + return Task.FromResult(new HelloReply()); + }; + + var descriptorInfo = TestHelpers.CreateDescriptorInfo( + bodyDescriptor: HelloRequest.Types.SubMessage.Descriptor, + bodyFieldDescriptor: HelloRequest.Descriptor.FindFieldByName("sub_data")); + var unaryServerCallHandler = CreateCallHandler(invoker, descriptorInfo: descriptorInfo); + var httpContext = TestHelpers.CreateHttpContext(); + httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(JsonFormatter.Default.Format(new HelloRequest.Types.SubMessage + { + Subfield = "body_value" + }))); + httpContext.Request.ContentType = "application/json"; + httpContext.Request.Query = new QueryCollection(new Dictionary + { + ["subData.subfield"] = "different_value" + }); + + // Act + await unaryServerCallHandler.HandleCallAsync(httpContext); + + // Assert + Assert.NotNull(request); + Assert.Equal("body_value", request!.SubData.Subfield); + } + private UnaryServerCallHandler CreateCallHandler( UnaryServerMethod invoker, CallHandlerDescriptorInfo? descriptorInfo = null, From db5294a58dfd6a1b35969556752ed7c299d9d7b2 Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Fri, 8 May 2026 17:23:10 +0000 Subject: [PATCH 04/16] Merged PR 60123: [release/8.0] fix http2/3 path header ASCII symbols validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enforce a check for non-ASCII characters in the :path header for HTTP2/HTTP3. HTTP1 is not impacted ---- #### AI description (iteration 1) #### PR Classification Bug fix to enforce ASCII validation for HTTP/2 and HTTP/3 `:path` header handling. #### PR Summary Replaces debug-only assertions with runtime validation to reject invalid ASCII characters in the request path for HTTP/2 and HTTP/3 streams, aborting the stream with protocol errors. - `src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs`: Validate each `:path` character is within ASCII (33–126); otherwise reset/abort the stream with `PROTOCOL_ERROR`. - `src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs`: Add the same ASCII validation and abort the stream with `ProtocolError` on invalid characters. --- .../Core/src/Internal/Http2/Http2Stream.cs | 7 +++-- .../Core/src/Internal/Http3/Http3Stream.cs | 7 +++-- .../Http2/Http2StreamTests.cs | 25 ++++++++++++++++ .../Http3/Http3StreamTests.cs | 30 +++++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index 5ee7fc967785..86304743cfae 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -430,8 +430,11 @@ private bool TryValidatePath(ReadOnlySpan pathSegment) for (var i = 0; i < pathSegment.Length; i++) { var ch = pathSegment[i]; - // The header parser should already be checking this - Debug.Assert(32 < ch && ch < 127); + if (ch > byte.MaxValue) + { + ResetAndAbort(new ConnectionAbortedException(CoreStrings.FormatHttp2StreamErrorPathInvalid(RawTarget)), Http2ErrorCode.PROTOCOL_ERROR); + return false; + } pathBuffer[i] = (byte)ch; } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs index 17178ffbf133..cadc301b0c76 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs @@ -1176,8 +1176,11 @@ private bool TryValidatePath(ReadOnlySpan pathSegment) for (var i = 0; i < pathSegment.Length; i++) { var ch = pathSegment[i]; - // The header parser should already be checking this - Debug.Assert(32 < ch && ch < 127); + if (ch > byte.MaxValue) + { + Abort(new ConnectionAbortedException(CoreStrings.FormatHttp3StreamErrorPathInvalid(RawTarget)), Http3ErrorCode.ProtocolError); + return false; + } pathBuffer[i] = (byte)ch; } diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2StreamTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2StreamTests.cs index 4ec15cf429f2..0e1968c5677c 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2StreamTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2StreamTests.cs @@ -798,6 +798,31 @@ public async Task HEADERS_Received_MaxRequestLineSize_Reset() await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); } + [Theory] + [InlineData("/\u0161")] + [InlineData("/a\u0161")] + [InlineData("/\u0161a")] + [InlineData("/a\u0161a")] + public async Task HEADERS_Received_CharacterLargerThanByte_Reset(string path) + { + var pathBytes = Encoding.UTF8.GetBytes(path); + var headerBlock = new byte[4 + pathBytes.Length]; + headerBlock[0] = 0x82; // Indexed: :method GET (HPACK static index 2) + headerBlock[1] = 0x86; // Indexed: :scheme http (HPACK static index 6) + headerBlock[2] = 0x44; // Literal incremental indexing, name index 4 (:path) + headerBlock[3] = (byte)pathBytes.Length; // Value length, no Huffman + pathBytes.CopyTo(headerBlock.AsSpan(4)); + + await InitializeConnectionAsync(_noopApplication); + + await StartStreamAsync(1, headerBlock, endStream: true); + + await WaitForStreamErrorAsync(expectedStreamId: 1, Http2ErrorCode.PROTOCOL_ERROR, + CoreStrings.FormatHttp2StreamErrorPathInvalid(path)); + + await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); + } + [Fact] public async Task HEADERS_Received_MaxRequestHeadersTotalSize_431() { diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3StreamTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3StreamTests.cs index c23e11328e43..a59a261e39fe 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3StreamTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3StreamTests.cs @@ -257,6 +257,36 @@ public async Task Path_DecodedAndNormalized(string input, string expected) Assert.Equal("0", responseHeaders["content-length"]); } + [Theory] + [InlineData("/\u0161dmin?x=1")] // U+0161 (353), truncates to 0x61 = 'a' → "/admin?x=1" + [InlineData("/\u0170ser")] // U+0170 (368), truncates to 0x70 = 'p' → "/pser" + [InlineData("/caf\u0165?q=1")] // U+0165 (357), truncates to 0x65 = 'e' → "/cafe?q=1" + public async Task NonAsciiPath_Reset(string path) + { + var pathBytes = Encoding.UTF8.GetBytes(path); + var qpackBlock = new byte[6 + pathBytes.Length]; + qpackBlock[0] = 0x00; // Required Insert Count = 0 + qpackBlock[1] = 0x00; // Delta Base = 0 + qpackBlock[2] = 0xD1; // Indexed: :method GET (QPACK static index 17) + qpackBlock[3] = 0xD6; // Indexed: :scheme http (QPACK static index 22) + qpackBlock[4] = 0x51; // Literal with static name ref, index 1 (:path) + qpackBlock[5] = (byte)pathBytes.Length; // Value length, no Huffman + pathBytes.CopyTo(qpackBlock.AsSpan(6)); + + await Http3Api.InitializeConnectionAsync(_noopApplication); + Http3Api.OutboundControlStream = await Http3Api.CreateControlStream(); + + var requestStream = await Http3Api.CreateRequestStream( + headers: (IEnumerable>)null, endStream: false); + + await requestStream.SendFrameAsync(Http3FrameType.Headers, qpackBlock, endStream: true); + + await requestStream.WaitForStreamErrorAsync( + Http3ErrorCode.ProtocolError, + AssertExpectedErrorMessages, + CoreStrings.FormatHttp3StreamErrorPathInvalid(path)); + } + [Theory] [InlineData(":path", "/")] [InlineData(":scheme", "http")] From 7cbee4deb7436e278d2fe60b72cf5a458f5ad0c2 Mon Sep 17 00:00:00 2001 From: Brennan Conroy Date: Tue, 12 May 2026 21:14:03 +0000 Subject: [PATCH 05/16] Merged PR 60874: Avoid caching responses for authenticated users If auth middleware was after the output caching middleware and some other auth was used besides the authorization header, then apps could end up caching a page meant for a specific user and then serve the cached page for anonymous or other users. --- .../CallHandlers/CallHandlerDescriptorInfo.cs | 1 + .../src/Policies/DefaultPolicy.cs | 6 + .../OutputCaching/test/OutputCacheTests.cs | 190 +++++++++++++++++- 3 files changed, 196 insertions(+), 1 deletion(-) diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs index 49a3b28bb18b..90a632ffed3c 100644 --- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs +++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs @@ -42,4 +42,5 @@ public CallHandlerDescriptorInfo( public Dictionary RouteParameterDescriptors { get; } public JsonTranscodingRouteAdapter RouteAdapter { get; } public ConcurrentDictionary> PathDescriptorsCache { get; } + public HashSet RouteParameterJsonPaths { get; } } diff --git a/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs b/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs index cc9ea67ec2dd..1f633dbaff83 100644 --- a/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs +++ b/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs @@ -50,6 +50,12 @@ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, Canc return ValueTask.CompletedTask; } + if (context.HttpContext.User?.Identity?.IsAuthenticated == true) + { + context.AllowCacheStorage = false; + return ValueTask.CompletedTask; + } + // Check response code if (response.StatusCode != StatusCodes.Status200OK) { diff --git a/src/Middleware/OutputCaching/test/OutputCacheTests.cs b/src/Middleware/OutputCaching/test/OutputCacheTests.cs index 85de08aadc6d..dc50e960fcf7 100644 --- a/src/Middleware/OutputCaching/test/OutputCacheTests.cs +++ b/src/Middleware/OutputCaching/test/OutputCacheTests.cs @@ -1,10 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Net; using System.Net.Http; +using System.Security.Claims; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.OutputCaching.Tests; @@ -1030,6 +1034,190 @@ static async Task RunClient(TestServer server, int id) } } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task AuthenticatedRequestsAreNotCached(bool authMiddlewareBeforeCache) + { + int finalEndpointHitCount = 0; + var builder = new HostBuilder() + .ConfigureWebHost(webHostBuilder => + { + webHostBuilder + .UseTestServer() + .ConfigureServices(services => + { + services.AddOutputCache(outputCachingOptions => + { + outputCachingOptions.BasePolicies = [new OutputCachePolicyBuilder().Build()]; + }); + }) + .Configure(app => + { + if (authMiddlewareBeforeCache) + { + AddAuth(app); + } + + app.UseOutputCache(); + + if (!authMiddlewareBeforeCache) + { + AddAuth(app); + } + + app.Run(async context => + { + finalEndpointHitCount++; + if (context.User.Identity?.IsAuthenticated == true) + { + await context.Response.WriteAsync(context.User.Identity.Name ?? "anonymous (authenticated)"); + } + else + { + await context.Response.WriteAsync(context.User.Identity?.Name ?? "anonymous"); + } + }); + }); + }); + + using var host = builder.Build(); + + await host.StartAsync(); + + using var server = host.GetTestServer(); + + var iterations = 10; + for (int i = 0; i < iterations; i++) + { + await RunClient(server, i); + } + + // RunClient sends two requests per iteration + Assert.Equal(iterations * 2, finalEndpointHitCount); + + // Unauthenticated request, check it still works + var client = server.CreateClient(); + var resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "")); + Assert.Equal("anonymous", await resp.Content.ReadAsStringAsync()); + + var resp2 = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "")); + Assert.Equal("anonymous", await resp2.Content.ReadAsStringAsync()); + // Smoke test that unauthenticated request can be served from cache. + Assert.True(resp2.Headers.Contains(HeaderNames.Age)); + + static async Task RunClient(TestServer server, int i) + { + var client = server.CreateClient(); + // Use headers for name since default vary-by uses query and we want the requests to look the same to the caching middleware + var resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "") { Headers = { { "name", $"{i}" } } }); + Assert.Equal($"{i}", await resp.Content.ReadAsStringAsync()); + + var resp2 = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "") { Headers = { { "name", $"{i}" } } }); + Assert.Equal($"{i}", await resp2.Content.ReadAsStringAsync()); + // Smoke test that authenticated request isn't served from cache. + Assert.False(resp2.Headers.Contains(HeaderNames.Age)); + } + + static void AddAuth(IApplicationBuilder app) + { + app.Use((c, n) => + { + if (c.Request.Headers.ContainsKey("name")) + { + c.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, c.Request.Headers["name"]) }, authenticationType: "custom")); + Assert.True(c.User.Identity?.IsAuthenticated); + } + + return n(c); + }); + } + } + + // This is a negative test to show that if auth middleware is after caching, the auth middleware won't run if a cache entry exists + // which is why we recommend putting auth before caching so that you don't accidentally serve anonymous cached content to an authenticated user. + [Fact] + public async Task AuthMiddlewareAfterCachingNotRunIfCacheEntryExists() + { + int finalEndpointHitCount = 0; + var builder = new HostBuilder() + .ConfigureWebHost(webHostBuilder => + { + webHostBuilder + .UseTestServer() + .ConfigureServices(services => + { + services.AddOutputCache(outputCachingOptions => + { + outputCachingOptions.BasePolicies = [new OutputCachePolicyBuilder().Build()]; + }); + }) + .Configure(app => + { + app.UseOutputCache(); + // Auth middleware + app.Use((c, n) => + { + if (c.Request.Headers.ContainsKey("name")) + { + c.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, c.Request.Headers["name"]) }, authenticationType: "custom")); + } + + return n(c); + }); + app.Run(async context => + { + finalEndpointHitCount++; + if (context.User.Identity?.IsAuthenticated == true) + { + await context.Response.WriteAsync(context.User.Identity.Name ?? "anonymous (authenticated)"); + } + else + { + await context.Response.WriteAsync(context.User.Identity?.Name ?? "anonymous"); + } + }); + }); + }); + + using var host = builder.Build(); + + await host.StartAsync(); + + using var server = host.GetTestServer(); + + // Make an unauthenticated request first to add a cache entry. + var client = server.CreateClient(); + var resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "")); + Assert.Equal("anonymous", await resp.Content.ReadAsStringAsync()); + + var resp2 = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "")); + Assert.Equal("anonymous", await resp2.Content.ReadAsStringAsync()); + // Smoke test that unauthenticated request is served from cache. + Assert.True(resp2.Headers.Contains(HeaderNames.Age)); + + var iterations = 10; + for (int i = 0; i < iterations; i++) + { + await RunClient(server, i); + } + + // Endpoint only hit once, for first request that doesn't have auth. + Assert.Equal(1, finalEndpointHitCount); + + static async Task RunClient(TestServer server, int i) + { + var client = server.CreateClient(); + // Use headers for name since default vary-by uses query and we want the requests to look the same to the caching middleware + var resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "") { Headers = { { "name", $"{i}" } } }); + + // Because the output cache middleware is before auth, these requests will serve a cached response if it exists (and we made one exist for this test) + // While this isn't ideal, we recommend app developers put caching after the auth middleware where we will skip the cache layer. + Assert.Equal("anonymous", await resp.Content.ReadAsStringAsync()); + Assert.True(resp.Headers.Contains(HeaderNames.Age)); + } + } + private static void Assert304Headers(HttpResponseMessage initialResponse, HttpResponseMessage subsequentResponse) { // https://tools.ietf.org/html/rfc7232#section-4.1 From c91f1a56428d70ab74edf3baf4cead48d2d61396 Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Wed, 13 May 2026 00:49:21 +0000 Subject: [PATCH 06/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-efcore build 20260512.6 On relative base path root dotnet-ef , Microsoft.EntityFrameworkCore , Microsoft.EntityFrameworkCore.Design , Microsoft.EntityFrameworkCore.InMemory , Microsoft.EntityFrameworkCore.Relational , Microsoft.EntityFrameworkCore.Sqlite , Microsoft.EntityFrameworkCore.SqlServer , Microsoft.EntityFrameworkCore.Tools From Version 8.0.27 -> To Version 8.0.28 --- NuGet.config | 4 ++-- eng/Version.Details.xml | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/NuGet.config b/NuGet.config index c91cc9ea537e..6f2fc9721091 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,7 +6,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b04b0c847738..27f9c72e7696 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -11,35 +11,35 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - de661bae8242a593a1a4c4f84ef74e25be0ecd88 + fb641a61a2111db563c3aca0ff496a02557b3c8e https://dev.azure.com/dnceng/internal/_git/dotnet-runtime From d569b62fbdea8e8199345e2173ee775596c0897b Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Wed, 13 May 2026 06:49:34 +0000 Subject: [PATCH 07/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-runtime build 20260512.30 On relative base path root Microsoft.Extensions.HostFactoryResolver.Sources , Microsoft.Internal.Runtime.AspNetCore.Transport , Microsoft.NETCore.BrowserDebugHost.Transport , Microsoft.NETCore.Platforms , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 8.0.27-servicing.26229.22 -> To Version 8.0.28-servicing.26262.30 Microsoft.NET.Runtime.MonoAOTCompiler.Task , Microsoft.NET.Runtime.WebAssembly.Sdk , Microsoft.NETCore.App.Ref , Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm , Microsoft.NETCore.App.Runtime.win-x64 From Version 8.0.27 -> To Version 8.0.28 System.Security.Cryptography.Xml From Version 8.0.3 -> To Version 8.0.3 --- NuGet.config | 4 ++-- eng/Version.Details.xml | 42 ++++++++++++++++++++--------------------- eng/Versions.props | 20 ++++++++++---------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/NuGet.config b/NuGet.config index 6f2fc9721091..2a6b352775c6 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,7 +9,7 @@ - + @@ -33,7 +33,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 27f9c72e7696..65fbfe67640b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -121,9 +121,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -185,9 +185,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 https://github.com/dotnet/source-build-externals @@ -241,7 +241,7 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -275,17 +275,17 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 81cabf2857a01351e5ab578947c7403a5b128ad1 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -316,22 +316,22 @@ Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime. All Runtime.$rid packages should have the same version. --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 https://github.com/dotnet/xdt @@ -368,9 +368,9 @@ - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - a6bde67c455f2ac219988c7a66171631090b6f65 + d1e47f8fba2db6bf4daf54db33c960c1a48433b3 https://github.com/dotnet/winforms diff --git a/eng/Versions.props b/eng/Versions.props index 0c11b82559fc..a1b71a5edd7f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -66,12 +66,12 @@ 8.0.2 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27 - 8.0.27-servicing.26229.22 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28 + 8.0.28-servicing.26262.30 8.0.0 8.0.1 8.0.0 @@ -92,7 +92,7 @@ 8.0.0 8.0.0 8.0.0 - 8.0.27-servicing.26229.22 + 8.0.28-servicing.26262.30 8.0.1 8.0.1 8.0.1 @@ -108,7 +108,7 @@ 8.0.0 8.0.2 8.0.0 - 8.0.27-servicing.26229.22 + 8.0.28-servicing.26262.30 8.0.1 8.0.1 8.0.2 @@ -128,9 +128,9 @@ 8.0.0 8.0.0 8.0.0 - 8.0.27-servicing.26229.22 + 8.0.28-servicing.26262.30 - 8.0.27-servicing.26229.22 + 8.0.28-servicing.26262.30 8.0.0 8.0.1 From d7acedc36b1570334337b79a946bf3ed7c9cd5ec Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Thu, 14 May 2026 18:14:03 +0000 Subject: [PATCH 08/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-runtime build 20260514.5 On relative base path root Microsoft.Extensions.HostFactoryResolver.Sources , Microsoft.Internal.Runtime.AspNetCore.Transport , Microsoft.NETCore.BrowserDebugHost.Transport , Microsoft.NETCore.Platforms , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 8.0.27-servicing.26229.22 -> To Version 8.0.28-servicing.26264.5 Microsoft.NET.Runtime.MonoAOTCompiler.Task , Microsoft.NET.Runtime.WebAssembly.Sdk , Microsoft.NETCore.App.Ref , Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm , Microsoft.NETCore.App.Runtime.win-x64 From Version 8.0.27 -> To Version 8.0.28 System.Security.Cryptography.Xml From Version 8.0.3 -> To Version 8.0.3 --- NuGet.config | 4 ++-- eng/Version.Details.xml | 32 ++++++++++++++++---------------- eng/Versions.props | 10 +++++----- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/NuGet.config b/NuGet.config index 2a6b352775c6..e8b548db4a73 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,7 +9,7 @@ - + @@ -33,7 +33,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 65fbfe67640b..432850f8b6e1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -121,9 +121,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -185,9 +185,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://github.com/dotnet/source-build-externals @@ -241,7 +241,7 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -277,15 +277,15 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -318,20 +318,20 @@ --> https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://github.com/dotnet/xdt @@ -368,9 +368,9 @@ - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - d1e47f8fba2db6bf4daf54db33c960c1a48433b3 + e77fe8485db233c832f25e1a736ea48230386fb9 https://github.com/dotnet/winforms diff --git a/eng/Versions.props b/eng/Versions.props index a1b71a5edd7f..aea92f95d94a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -71,7 +71,7 @@ 8.0.28 8.0.28 8.0.28 - 8.0.28-servicing.26262.30 + 8.0.28-servicing.26264.5 8.0.0 8.0.1 8.0.0 @@ -92,7 +92,7 @@ 8.0.0 8.0.0 8.0.0 - 8.0.28-servicing.26262.30 + 8.0.28-servicing.26264.5 8.0.1 8.0.1 8.0.1 @@ -108,7 +108,7 @@ 8.0.0 8.0.2 8.0.0 - 8.0.28-servicing.26262.30 + 8.0.28-servicing.26264.5 8.0.1 8.0.1 8.0.2 @@ -128,9 +128,9 @@ 8.0.0 8.0.0 8.0.0 - 8.0.28-servicing.26262.30 + 8.0.28-servicing.26264.5 - 8.0.28-servicing.26262.30 + 8.0.28-servicing.26264.5 8.0.0 8.0.1 From a622b28c99abe905e755fb82dcb95032dc0bc517 Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Thu, 14 May 2026 23:52:32 +0000 Subject: [PATCH 09/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-runtime build 20260514.13 On relative base path root Microsoft.Extensions.HostFactoryResolver.Sources , Microsoft.Internal.Runtime.AspNetCore.Transport , Microsoft.NETCore.BrowserDebugHost.Transport , Microsoft.NETCore.Platforms , Microsoft.SourceBuild.Intermediate.runtime.linux-x64 From Version 8.0.27-servicing.26229.22 -> To Version 8.0.28-servicing.26264.13 Microsoft.NET.Runtime.MonoAOTCompiler.Task , Microsoft.NET.Runtime.WebAssembly.Sdk , Microsoft.NETCore.App.Ref , Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm , Microsoft.NETCore.App.Runtime.win-x64 From Version 8.0.27 -> To Version 8.0.28 --- NuGet.config | 2 ++ eng/Version.Details.xml | 30 +++++++++++++++--------------- eng/Versions.props | 10 +++++----- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/NuGet.config b/NuGet.config index e8b548db4a73..5dc43b80b57b 100644 --- a/NuGet.config +++ b/NuGet.config @@ -10,6 +10,7 @@ + @@ -33,6 +34,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 432850f8b6e1..3094d56bc154 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -121,9 +121,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -185,9 +185,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://github.com/dotnet/source-build-externals @@ -277,15 +277,15 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -318,20 +318,20 @@ --> https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://github.com/dotnet/xdt @@ -368,9 +368,9 @@ - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - e77fe8485db233c832f25e1a736ea48230386fb9 + 46295af5828b062bbbf93a9cef50fd8cb9fbcb09 https://github.com/dotnet/winforms diff --git a/eng/Versions.props b/eng/Versions.props index aea92f95d94a..355b4bd13e5e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -71,7 +71,7 @@ 8.0.28 8.0.28 8.0.28 - 8.0.28-servicing.26264.5 + 8.0.28-servicing.26264.13 8.0.0 8.0.1 8.0.0 @@ -92,7 +92,7 @@ 8.0.0 8.0.0 8.0.0 - 8.0.28-servicing.26264.5 + 8.0.28-servicing.26264.13 8.0.1 8.0.1 8.0.1 @@ -108,7 +108,7 @@ 8.0.0 8.0.2 8.0.0 - 8.0.28-servicing.26264.5 + 8.0.28-servicing.26264.13 8.0.1 8.0.1 8.0.2 @@ -128,9 +128,9 @@ 8.0.0 8.0.0 8.0.0 - 8.0.28-servicing.26264.5 + 8.0.28-servicing.26264.13 - 8.0.28-servicing.26264.5 + 8.0.28-servicing.26264.13 8.0.0 8.0.1 From 09bc1a945d8fbb4038bc52b5b03c13d0dad7c1b4 Mon Sep 17 00:00:00 2001 From: DotNet-Bot Date: Sat, 16 May 2026 19:59:32 +0000 Subject: [PATCH 10/16] Update dependencies from https://dev.azure.com/dnceng/internal/_git/dotnet-efcore build 20260515.2 On relative base path root dotnet-ef , Microsoft.EntityFrameworkCore , Microsoft.EntityFrameworkCore.Design , Microsoft.EntityFrameworkCore.InMemory , Microsoft.EntityFrameworkCore.Relational , Microsoft.EntityFrameworkCore.Sqlite , Microsoft.EntityFrameworkCore.SqlServer , Microsoft.EntityFrameworkCore.Tools From Version 8.0.27 -> To Version 8.0.28 --- NuGet.config | 4 ++-- eng/Version.Details.xml | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/NuGet.config b/NuGet.config index 5dc43b80b57b..6d18aebaa59c 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,7 +6,7 @@ - + @@ -31,7 +31,7 @@ - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 3094d56bc154..170bea2c92a0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -11,35 +11,35 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - fb641a61a2111db563c3aca0ff496a02557b3c8e + 983cf3c21c0f9484cc0ed66e61b8c252d701a30b https://dev.azure.com/dnceng/internal/_git/dotnet-runtime From dc6799d73256a2e6a5463d6992e52d6d059fe83d Mon Sep 17 00:00:00 2001 From: Brennan Conroy Date: Tue, 19 May 2026 18:42:46 +0000 Subject: [PATCH 11/16] Merged PR 61221: Update MessagePack ``` git clone aspnet/messagepack git add remote upstream messagepack/messagepack git fetch upstream v2.x git merge upstream/v2.x resolve security.md file git rm nuget.config git commit git log -1 git diff --binary --no-color --output=upstream-patch.diff 94d86190bc8c30e98dae0220c0bce54cdfe48494.. ``` ---- #### AI description (iteration 5) #### PR Classification Dependency update and security fix to upgrade MessagePack library version and apply patches to address deeply nested structure parsing vulnerabilities. #### PR Summary This PR updates the MessagePack library from version 2.5.187 to 2.5.301 and applies patches to the MessagePack-CSharp submodule to handle deeply nested structures safely. - `eng/Versions.props`: Updated MessagePackVersion from 2.5.187 to 2.5.301 - `eng/build.ps1` and `eng/build.sh`: Added logic to conditionally apply two patch files (`upstream-patch.diff` and `msgpack-fix-full.diff`) to the MessagePack-CSharp submodule - `MessagePackHubProtocolTestBase.cs`: Added test `SkipResult_WithDeeplyNestedStructure` to verify parsing of 100,000 nested arrays with null values - `.gitattributes`: Enforced LF line endings for `.diff` and `.patch` files to ensure proper git apply functionality --- .gitattributes | 10 + eng/Versions.props | 2 +- eng/build.ps1 | 14 + eng/build.sh | 14 + eng/scripts/CodeCheck.ps1 | 2 +- .../MessagePackHubProtocolTestBase.cs | 23 + src/submodules/msgpack-fix-full.diff | 3737 +++ src/submodules/upstream-patch.diff | 25892 ++++++++++++++++ 8 files changed, 29692 insertions(+), 2 deletions(-) create mode 100644 src/submodules/msgpack-fix-full.diff create mode 100644 src/submodules/upstream-patch.diff diff --git a/.gitattributes b/.gitattributes index 90bf30e81370..ad5f2bc2f621 100644 --- a/.gitattributes +++ b/.gitattributes @@ -84,3 +84,13 @@ jquery*.map eol=lf bootstrap*.js eol=lf bootstrap*.map eol=lf bootstrap*.css eol=lf + +############################################################################### +# Patch/diff files must keep LF line endings so `git apply` can parse them +# correctly on machines where core.autocrlf=true (the Git-for-Windows default). +# Empty-file-creation entries in these patches have no `--- /dev/null` / `+++ b/...` +# lines, which makes the parser sensitive to a stray CR before the next +# `diff --git` header. Forcing LF avoids that. +############################################################################### +*.diff eol=lf +*.patch eol=lf diff --git a/eng/Versions.props b/eng/Versions.props index 5035ed326a3e..9e31c0d9ea9f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -290,7 +290,7 @@ 2.57.0 2.57.0 2.57.0 - 2.5.187 + 2.5.301 3.2.0 3.2.0 3.2.0 diff --git a/eng/build.ps1 b/eng/build.ps1 index b553b5cb8072..850c53401f79 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -415,6 +415,20 @@ if ($BinaryLog) { # Capture MSBuild crash logs $env:MSBUILDDEBUGPATH = $LogDir +pushd +$patch_file1 = "../upstream-patch.diff" +$patch_file2 = "../msgpack-fix-full.diff" +cd $RepoRoot/src/submodules/MessagePack-CSharp +# Only apply patches if they haven't been applied yet +$patch_check_file = "$RepoRoot/src/submodules/MessagePack-CSharp/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs" +$searchWord = "TrySkipNextArray" +$found = Select-String -Path $patch_check_file -Pattern $searchWord +if ($found) { + git apply $patch_file1 -v + git apply $patch_file2 -v +} +popd + $local:exit_code = $null try { # Set this global property so Arcade will always initialize the toolset. The error message you get when you build on a clean machine diff --git a/eng/build.sh b/eng/build.sh index 51a2cd6ecd31..a073766559af 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -355,6 +355,20 @@ fi # Capture MSBuild crash logs export MSBUILDDEBUGPATH="$log_dir" +prev_dir=$(pwd) +patch_file1="../upstream-patch.diff" +patch_file2="../msgpack-fix-full.diff" +cd $repo_root/src/submodules/MessagePack-CSharp +if command -v git > /dev/null 2>&1; then + # Only apply patches if they haven't been applied yet + patch_check_file="$repo_root/src/submodules/MessagePack-CSharp/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs" + if grep -q -- "TrySkipNextArray" "$patch_check_file"; then + git apply "$patch_file1" -v + git apply "$patch_file2" -v + fi +fi +cd "$prev_dir" + # Set this global property so Arcade will always initialize the toolset. The error message you get when you build on a clean machine # with -norestore is not obvious about what to do to fix it. As initialization takes very little time, we think always initializing # the toolset is a better default behavior. diff --git a/eng/scripts/CodeCheck.ps1 b/eng/scripts/CodeCheck.ps1 index acd1bf6429df..99b82b73d4d7 100644 --- a/eng/scripts/CodeCheck.ps1 +++ b/eng/scripts/CodeCheck.ps1 @@ -213,7 +213,7 @@ try { # Temporary: Disable check for blazor js file and nuget.config (updated automatically for # internal builds) - $changedFilesExclusions = @("src/Components/Web.JS/dist/Release/blazor.server.js", "src/Components/Web.JS/dist/Release/blazor.web.js", "NuGet.config") + $changedFilesExclusions = @("src/Components/Web.JS/dist/Release/blazor.server.js", "src/Components/Web.JS/dist/Release/blazor.web.js", "NuGet.config", "src/submodules/MessagePack-CSharp") if ($changedFiles) { foreach ($file in $changedFiles) { diff --git a/src/SignalR/common/SignalR.Common/test/Internal/Protocol/MessagePackHubProtocolTestBase.cs b/src/SignalR/common/SignalR.Common/test/Internal/Protocol/MessagePackHubProtocolTestBase.cs index 83c1f522b0b0..b748980724eb 100644 --- a/src/SignalR/common/SignalR.Common/test/Internal/Protocol/MessagePackHubProtocolTestBase.cs +++ b/src/SignalR/common/SignalR.Common/test/Internal/Protocol/MessagePackHubProtocolTestBase.cs @@ -258,6 +258,29 @@ public void ParseMessageWithExtraData() Assert.Equal(expectedMessage, message, TestHubMessageEqualityComparer.Instance); } + // Unknown invocation IDs are skipped over. We still need to parse the message + // to get to the next message, but we won't create any objects (except the outer hubmessage type) + [Fact] + public void SkipResult_WithDeeplyNestedStructure() + { + var expectedMessage = CompletionMessage.WithResult("xyz", null); + // Verify that the input binary string decodes to the expected MsgPack primitives + var bytes = new byte[] { ArrayBytes(5), + 3, // Completion message + 0x80, // Empty headers + StringBytes(3), (byte)'x', (byte)'y', (byte)'z', // invocation ID + 3, // result type; non-void + } + .Concat(Enumerable.Repeat((byte)0x91, 100_000).Append((byte)0xC0)) // 100,000 nested arrays with a null at the end + .ToArray(); + + bytes = Frame(bytes); + + var data = new ReadOnlySequence(bytes); + // Null types for TestBinder so parser will skip over the result (treats it as an unknown invocation ID) + Assert.True(HubProtocol.TryParseMessage(ref data, new TestBinder(), out var message)); + } + [Theory] [MemberData(nameof(BaseTestDataNames))] public void BaseWriteMessages(string testDataName) diff --git a/src/submodules/msgpack-fix-full.diff b/src/submodules/msgpack-fix-full.diff new file mode 100644 index 000000000000..9111c0f215c3 --- /dev/null +++ b/src/submodules/msgpack-fix-full.diff @@ -0,0 +1,3737 @@ +diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs +index c2038f5a..f84b4bf7 100644 +--- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs ++++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs +@@ -9,16 +9,17 @@ namespace MessagePack.AspNetCoreMvcFormatter + public class MessagePackInputFormatter : InputFormatter + { + private const string ContentType = "application/x-msgpack"; +- private readonly MessagePackSerializerOptions? options; ++ private static readonly MessagePackSerializerOptions DefaultOptions = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData); ++ private readonly MessagePackSerializerOptions options; + + public MessagePackInputFormatter() +- : this(null) ++ : this(DefaultOptions) + { + } + + public MessagePackInputFormatter(MessagePackSerializerOptions? options) + { +- this.options = options; ++ this.options = options ?? DefaultOptions; + + SupportedMediaTypes.Add(ContentType); + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs +index 29d8ca7d..02da01f9 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/CollectionFormatter.cs +@@ -765,7 +765,7 @@ namespace MessagePack.Formatters + + protected override Dictionary> Create(int count, MessagePackSerializerOptions options) + { +- return new Dictionary>(count); ++ return new Dictionary>(count, options.Security.GetEqualityComparer()); + } + } + +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs +index 2d932888..98932401 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/ExpandoObjectFormatter.cs +@@ -8,6 +8,8 @@ namespace MessagePack.Formatters + { + public class ExpandoObjectFormatter : IMessagePackFormatter + { ++ internal const int MaximumUntrustedDataMemberCount = 1024; ++ + public static readonly IMessagePackFormatter Instance = new ExpandoObjectFormatter(); + + private ExpandoObjectFormatter() +@@ -23,6 +25,7 @@ namespace MessagePack.Formatters + + var result = new ExpandoObject(); + int count = reader.ReadMapHeader(); ++ ThrowIfMapTooLargeForUntrustedData(count, options); + if (count > 0) + { + IFormatterResolver resolver = options.Resolver; +@@ -49,6 +52,14 @@ namespace MessagePack.Formatters + return result; + } + ++ internal static void ThrowIfMapTooLargeForUntrustedData(int count, MessagePackSerializerOptions options) ++ { ++ if (options.Security.HashCollisionResistant && count > MaximumUntrustedDataMemberCount) ++ { ++ throw new MessagePackSerializationException($"ExpandoObject map size exceeds the limit of {MaximumUntrustedDataMemberCount} entries allowed under untrusted data security mode."); ++ } ++ } ++ + public void Serialize(ref MessagePackWriter writer, ExpandoObject? value, MessagePackSerializerOptions options) + { + if (value is null) +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs +index 44812d1a..128e52e4 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/MultiDimensionalArrayFormatter.cs +@@ -2,9 +2,7 @@ + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + + using System; +-using System.Buffers; +-using System.Collections.Generic; +-using System.Text; ++using System.Diagnostics.CodeAnalysis; + + #pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name +@@ -62,6 +60,7 @@ namespace MessagePack.Formatters + var iLength = reader.ReadInt32(); + var jLength = reader.ReadInt32(); + var maxLen = reader.ReadArrayHeader(); ++ MultiDimensionalArrayFormatterHelper.ThrowIfLengthsDontMatch("T[,]", maxLen, iLength, jLength); + + var array = new T[iLength, jLength]; + +@@ -148,6 +147,7 @@ namespace MessagePack.Formatters + var jLength = reader.ReadInt32(); + var kLength = reader.ReadInt32(); + var maxLen = reader.ReadArrayHeader(); ++ MultiDimensionalArrayFormatterHelper.ThrowIfLengthsDontMatch("T[,,]", maxLen, iLength, jLength, kLength); + + var array = new T[iLength, jLength, kLength]; + +@@ -244,6 +244,8 @@ namespace MessagePack.Formatters + var kLength = reader.ReadInt32(); + var lLength = reader.ReadInt32(); + var maxLen = reader.ReadArrayHeader(); ++ MultiDimensionalArrayFormatterHelper.ThrowIfLengthsDontMatch("T[,,,]", maxLen, iLength, jLength, kLength, lLength); ++ + var array = new T[iLength, jLength, kLength, lLength]; + + var i = 0; +@@ -291,4 +293,34 @@ namespace MessagePack.Formatters + } + } + } ++ ++ internal static class MultiDimensionalArrayFormatterHelper ++ { ++ internal static void ThrowIfLengthsDontMatch(string format, int actualLength, int firstLength, int secondLength, int thirdLength = 1, int fourthLength = 1) ++ { ++ if (firstLength < 0 || secondLength < 0 || thirdLength < 0 || fourthLength < 0) ++ { ++ ThrowInvalidFormat(format); ++ } ++ ++ int expectedLength; ++ try ++ { ++ expectedLength = checked(firstLength * secondLength * thirdLength * fourthLength); ++ } ++ catch (OverflowException) ++ { ++ ThrowInvalidFormat(format); ++ return; ++ } ++ ++ if (expectedLength != actualLength) ++ { ++ ThrowInvalidFormat(format); ++ } ++ } ++ ++ [DoesNotReturn] ++ private static void ThrowInvalidFormat(string format) => throw new MessagePackSerializationException($"Invalid {format} format"); ++ } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs +index da2444b4..1cc840e5 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/StandardClassLibraryFormatter.cs +@@ -649,9 +649,14 @@ namespace MessagePack.Formatters + + public T? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { +- return reader.ReadString() is string value +- ? (T?)Type.GetType(value, throwOnError: true) +- : null; ++ if (reader.ReadString() is not string value) ++ { ++ return null; ++ } ++ ++ Type type = options.LoadType(value) ?? throw new TypeLoadException(value); ++ options.ThrowIfDeserializingTypeIsDisallowed(type); ++ return (T?)type; + } + } + +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs +index 2d082f9f..89036c12 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/TinyJsonReader.cs +@@ -134,62 +134,64 @@ namespace MessagePack + + private void ReadNextToken() + { +- this.SkipWhiteSpace(); +- +- var intChar = this.reader.Peek(); +- if (intChar == -1) ++ while (true) + { +- this.TokenType = TinyJsonToken.None; +- return; +- } ++ this.SkipWhiteSpace(); + +- var c = (char)intChar; +- switch (c) +- { +- case '{': +- this.TokenType = TinyJsonToken.StartObject; +- return; +- case '}': +- this.TokenType = TinyJsonToken.EndObject; +- return; +- case '[': +- this.TokenType = TinyJsonToken.StartArray; +- return; +- case ']': +- this.TokenType = TinyJsonToken.EndArray; +- return; +- case '"': +- this.TokenType = TinyJsonToken.String; +- return; +- case '0': +- case '1': +- case '2': +- case '3': +- case '4': +- case '5': +- case '6': +- case '7': +- case '8': +- case '9': +- case '-': +- this.TokenType = TinyJsonToken.Number; +- return; +- case 't': +- this.TokenType = TinyJsonToken.True; +- return; +- case 'f': +- this.TokenType = TinyJsonToken.False; +- return; +- case 'n': +- this.TokenType = TinyJsonToken.Null; +- return; +- case ',': +- case ':': +- this.reader.Read(); +- this.ReadNextToken(); ++ var intChar = this.reader.Peek(); ++ if (intChar == -1) ++ { ++ this.TokenType = TinyJsonToken.None; + return; +- default: +- throw new TinyJsonException("Invalid String:" + c); ++ } ++ ++ var c = (char)intChar; ++ switch (c) ++ { ++ case '{': ++ this.TokenType = TinyJsonToken.StartObject; ++ return; ++ case '}': ++ this.TokenType = TinyJsonToken.EndObject; ++ return; ++ case '[': ++ this.TokenType = TinyJsonToken.StartArray; ++ return; ++ case ']': ++ this.TokenType = TinyJsonToken.EndArray; ++ return; ++ case '"': ++ this.TokenType = TinyJsonToken.String; ++ return; ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ case '-': ++ this.TokenType = TinyJsonToken.Number; ++ return; ++ case 't': ++ this.TokenType = TinyJsonToken.True; ++ return; ++ case 'f': ++ this.TokenType = TinyJsonToken.False; ++ return; ++ case 'n': ++ this.TokenType = TinyJsonToken.Null; ++ return; ++ case ',': ++ case ':': ++ this.reader.Read(); ++ continue; ++ default: ++ throw new TinyJsonException("Invalid String:" + c); ++ } + } + } + +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs +index 1e662702..07690fd8 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/UnsafeMemory.Low.cs +@@ -17,12 +17,20 @@ namespace MessagePack.Internal + public static readonly bool Is32Bit = IntPtr.Size == 4; + } + ++ /// ++ /// Highly tuned method for writing raw bytes to a . ++ /// ++ /// ++ /// The methods on this class are not safe, in that they use pointer arithmetic ++ /// and assume that the caller has provided a with a length ++ /// of at least the number of bytes being written. The caller must ensure that this is the case. ++ /// + public static partial class UnsafeMemory32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(1); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -30,13 +38,13 @@ namespace MessagePack.Internal + *(byte*)pDst = *(byte*)pSrc; + } + +- writer.Advance(src.Length); ++ writer.Advance(1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw2(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(2); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -44,13 +52,13 @@ namespace MessagePack.Internal + *(short*)pDst = *(short*)pSrc; + } + +- writer.Advance(src.Length); ++ writer.Advance(2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw3(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(3); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -59,16 +67,24 @@ namespace MessagePack.Internal + *(short*)(pDst + 1) = *(short*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(3); + } + } + ++ /// ++ /// Highly tuned method for writing raw bytes to a . ++ /// ++ /// ++ /// The methods on this class are not safe, in that they use pointer arithmetic ++ /// and assume that the caller has provided a with a length ++ /// of at least the number of bytes being written. The caller must ensure that this is the case. ++ /// + public static partial class UnsafeMemory64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw1(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(1); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -76,13 +92,13 @@ namespace MessagePack.Internal + *(byte*)pDst = *(byte*)pSrc; + } + +- writer.Advance(src.Length); ++ writer.Advance(1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw2(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(2); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -90,13 +106,13 @@ namespace MessagePack.Internal + *(short*)pDst = *(short*)pSrc; + } + +- writer.Advance(src.Length); ++ writer.Advance(2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw3(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(3); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -105,13 +121,13 @@ namespace MessagePack.Internal + *(short*)(pDst + 1) = *(short*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(3); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw4(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(4); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -119,13 +135,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 0) = *(int*)(pSrc + 0); + } + +- writer.Advance(src.Length); ++ writer.Advance(4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw5(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(5); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -134,13 +150,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 1) = *(int*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw6(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(6); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -149,13 +165,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 2) = *(int*)(pSrc + 2); + } + +- writer.Advance(src.Length); ++ writer.Advance(6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw7(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(7); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -164,7 +180,7 @@ namespace MessagePack.Internal + *(int*)(pDst + 3) = *(int*)(pSrc + 3); + } + +- writer.Advance(src.Length); ++ writer.Advance(7); + } + } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs +index d2c630d7..5a38fccd 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackReader.cs +@@ -154,81 +154,123 @@ namespace MessagePack + /// + internal bool TrySkip() + { +- if (this.reader.Remaining == 0) ++ long remainingStructures = 1; ++ while (remainingStructures > 0) + { +- return false; +- } ++ if (this.reader.Remaining == 0) ++ { ++ return false; ++ } + +- byte code = this.NextCode; +- switch (code) +- { +- case MessagePackCode.Nil: +- case MessagePackCode.True: +- case MessagePackCode.False: +- return this.reader.TryAdvance(1); +- case MessagePackCode.Int8: +- case MessagePackCode.UInt8: +- return this.reader.TryAdvance(2); +- case MessagePackCode.Int16: +- case MessagePackCode.UInt16: +- return this.reader.TryAdvance(3); +- case MessagePackCode.Int32: +- case MessagePackCode.UInt32: +- case MessagePackCode.Float32: +- return this.reader.TryAdvance(5); +- case MessagePackCode.Int64: +- case MessagePackCode.UInt64: +- case MessagePackCode.Float64: +- return this.reader.TryAdvance(9); +- case MessagePackCode.Map16: +- case MessagePackCode.Map32: +- return this.TrySkipNextMap(); +- case MessagePackCode.Array16: +- case MessagePackCode.Array32: +- return this.TrySkipNextArray(); +- case MessagePackCode.Str8: +- case MessagePackCode.Str16: +- case MessagePackCode.Str32: +- return this.TryGetStringLengthInBytes(out int length) && this.reader.TryAdvance(length); +- case MessagePackCode.Bin8: +- case MessagePackCode.Bin16: +- case MessagePackCode.Bin32: +- return this.TryGetBytesLength(out length) && this.reader.TryAdvance(length); +- case MessagePackCode.FixExt1: +- case MessagePackCode.FixExt2: +- case MessagePackCode.FixExt4: +- case MessagePackCode.FixExt8: +- case MessagePackCode.FixExt16: +- case MessagePackCode.Ext8: +- case MessagePackCode.Ext16: +- case MessagePackCode.Ext32: +- return this.TryReadExtensionFormatHeader(out ExtensionHeader header) && this.reader.TryAdvance(header.Length); +- default: +- if ((code >= MessagePackCode.MinNegativeFixInt && code <= MessagePackCode.MaxNegativeFixInt) || +- (code >= MessagePackCode.MinFixInt && code <= MessagePackCode.MaxFixInt)) +- { +- return this.reader.TryAdvance(1); +- } ++ remainingStructures--; ++ byte code = this.NextCode; ++ switch (code) ++ { ++ case byte x when (x >= MessagePackCode.MinNegativeFixInt && x <= MessagePackCode.MaxNegativeFixInt) || (x >= MessagePackCode.MinFixInt && x <= MessagePackCode.MaxFixInt): ++ case MessagePackCode.Nil: ++ case MessagePackCode.True: ++ case MessagePackCode.False: ++ if (!this.reader.TryAdvance(1)) ++ { ++ return false; ++ } + +- if (code >= MessagePackCode.MinFixMap && code <= MessagePackCode.MaxFixMap) +- { +- return this.TrySkipNextMap(); +- } ++ break; ++ case MessagePackCode.Int8: ++ case MessagePackCode.UInt8: ++ if (!this.reader.TryAdvance(2)) ++ { ++ return false; ++ } + +- if (code >= MessagePackCode.MinFixArray && code <= MessagePackCode.MaxFixArray) +- { +- return this.TrySkipNextArray(); +- } ++ break; ++ case MessagePackCode.Int16: ++ case MessagePackCode.UInt16: ++ if (!this.reader.TryAdvance(3)) ++ { ++ return false; ++ } + +- if (code >= MessagePackCode.MinFixStr && code <= MessagePackCode.MaxFixStr) +- { +- return this.TryGetStringLengthInBytes(out length) && this.reader.TryAdvance(length); +- } ++ break; ++ case MessagePackCode.Int32: ++ case MessagePackCode.UInt32: ++ case MessagePackCode.Float32: ++ if (!this.reader.TryAdvance(5)) ++ { ++ return false; ++ } + +- // We don't actually expect to ever hit this point, since every code is supported. +- Debug.Fail("Missing handler for code: " + code); +- throw ThrowInvalidCode(code); ++ break; ++ case MessagePackCode.Int64: ++ case MessagePackCode.UInt64: ++ case MessagePackCode.Float64: ++ if (!this.reader.TryAdvance(9)) ++ { ++ return false; ++ } ++ ++ break; ++ case byte x when x >= MessagePackCode.MinFixMap && x <= MessagePackCode.MaxFixMap: ++ case MessagePackCode.Map16: ++ case MessagePackCode.Map32: ++ if (!this.TryReadMapHeader(out int count)) ++ { ++ return false; ++ } ++ ++ remainingStructures = checked(remainingStructures + ((long)count * 2)); ++ break; ++ case byte x when x >= MessagePackCode.MinFixArray && x <= MessagePackCode.MaxFixArray: ++ case MessagePackCode.Array16: ++ case MessagePackCode.Array32: ++ if (!this.TryReadArrayHeader(out count)) ++ { ++ return false; ++ } ++ ++ remainingStructures = checked(remainingStructures + count); ++ break; ++ case byte x when x >= MessagePackCode.MinFixStr && x <= MessagePackCode.MaxFixStr: ++ case MessagePackCode.Str8: ++ case MessagePackCode.Str16: ++ case MessagePackCode.Str32: ++ if (!this.TryGetStringLengthInBytes(out int length) || !this.reader.TryAdvance(length)) ++ { ++ return false; ++ } ++ ++ break; ++ case MessagePackCode.Bin8: ++ case MessagePackCode.Bin16: ++ case MessagePackCode.Bin32: ++ if (!this.TryGetBytesLength(out length) || !this.reader.TryAdvance(length)) ++ { ++ return false; ++ } ++ ++ break; ++ case MessagePackCode.FixExt1: ++ case MessagePackCode.FixExt2: ++ case MessagePackCode.FixExt4: ++ case MessagePackCode.FixExt8: ++ case MessagePackCode.FixExt16: ++ case MessagePackCode.Ext8: ++ case MessagePackCode.Ext16: ++ case MessagePackCode.Ext32: ++ if (!this.TryReadExtensionFormatHeader(out ExtensionHeader header) || !this.reader.TryAdvance(header.Length)) ++ { ++ return false; ++ } ++ ++ break; ++ default: ++ // We don't actually expect to ever hit this point, since every code is supported. ++ Debug.Fail("Missing handler for code: " + code); ++ throw ThrowInvalidCode(code); ++ } + } ++ ++ return true; + } + + /// +@@ -390,7 +432,7 @@ namespace MessagePack + // Protect against corrupted or mischievious data that may lead to allocating way too much memory. + // We allow for each primitive to be the minimal 1 byte in size, and we have a key=value map, so that's 2 bytes. + // Formatters that know each element is larger can optionally add a stronger check. +- ThrowInsufficientBufferUnless(this.reader.Remaining >= count * 2); ++ ThrowInsufficientBufferUnless(this.reader.Remaining >= (long)count * 2); + + return count; + } +@@ -1130,22 +1172,5 @@ namespace MessagePack + ArrayPool.Shared.Return(charArray); + return value; + } +- +- private bool TrySkipNextArray() => this.TryReadArrayHeader(out int count) && this.TrySkip(count); +- +- private bool TrySkipNextMap() => this.TryReadMapHeader(out int count) && this.TrySkip(count * 2); +- +- private bool TrySkip(int count) +- { +- for (int i = 0; i < count; i++) +- { +- if (!this.TrySkip()) +- { +- return false; +- } +- } +- +- return true; +- } + } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs +index 3a671529..9d8a71d5 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSecurity.cs +@@ -19,6 +19,8 @@ namespace MessagePack + /// + public class MessagePackSecurity + { ++ private const int DefaultUntrustedDataMaximumDecompressedSize = 64 * 1024 * 1024; ++ + /// + /// Gets an instance preconfigured with settings that omit all protections. Useful for deserializing fully-trusted and valid msgpack sequences. + /// +@@ -31,6 +33,7 @@ namespace MessagePack + { + HashCollisionResistant = true, + MaximumObjectGraphDepth = 500, ++ MaximumDecompressedSize = DefaultUntrustedDataMaximumDecompressedSize, + }; + + private static readonly SipHash Hash = new(); +@@ -57,6 +60,7 @@ namespace MessagePack + + this.HashCollisionResistant = copyFrom.HashCollisionResistant; + this.MaximumObjectGraphDepth = copyFrom.MaximumObjectGraphDepth; ++ this.MaximumDecompressedSize = copyFrom.MaximumDecompressedSize; + } + + /// +@@ -81,6 +85,12 @@ namespace MessagePack + /// + public int MaximumObjectGraphDepth { get; private set; } = int.MaxValue; + ++ /// ++ /// Gets the maximum decompressed size in bytes allowed when deserializing compressed payloads. ++ /// ++ /// The default value is for and 64MB for . ++ public int MaximumDecompressedSize { get; private set; } = int.MaxValue; ++ + /// + /// Gets a copy of these options with the property set to a new value. + /// +@@ -98,6 +108,28 @@ namespace MessagePack + return clone; + } + ++ /// ++ /// Gets a copy of these options with the property set to a new value. ++ /// ++ /// The new value for the property. Must not be negative. ++ /// The new instance; or the original if the value is unchanged. ++ public MessagePackSecurity WithMaximumDecompressedSize(int maximumDecompressedSize) ++ { ++ if (this.MaximumDecompressedSize == maximumDecompressedSize) ++ { ++ return this; ++ } ++ ++ if (maximumDecompressedSize < 0) ++ { ++ throw new ArgumentOutOfRangeException(nameof(maximumDecompressedSize)); ++ } ++ ++ var clone = this.Clone(); ++ clone.MaximumDecompressedSize = maximumDecompressedSize; ++ return clone; ++ } ++ + /// + /// Gets a copy of these options with the property set to a new value. + /// +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs +index b9d02ac6..baf8a4e9 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.Json.cs +@@ -89,7 +89,7 @@ namespace MessagePack + { + using (var scratchRental = options.SequencePool.Rent()) + { +- if (TryDecompress(ref reader, scratchRental.Value)) ++ if (TryDecompress(ref reader, scratchRental.Value, options)) + { + var scratchReader = new MessagePackReader(scratchRental.Value) + { +@@ -184,6 +184,11 @@ namespace MessagePack + } + + private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer, MessagePackSerializerOptions options) ++ { ++ return FromJsonCore(jr, ref writer, options, 0); ++ } ++ ++ private static uint FromJsonCore(TinyJsonReader jr, ref MessagePackWriter writer, MessagePackSerializerOptions options, int depth) + { + uint count = 0; + while (jr.Read()) +@@ -193,11 +198,13 @@ namespace MessagePack + case TinyJsonToken.None: + break; + case TinyJsonToken.StartObject: ++ VerifyJsonObjectGraphDepth(options, depth); ++ + // Set up a scratch area to serialize the collection since we don't know its length yet, which must be written first. + using (var scratchRental = options.SequencePool.Rent()) + { + MessagePackWriter scratchWriter = writer.Clone(scratchRental.Value); +- var mapCount = FromJsonCore(jr, ref scratchWriter, options); ++ var mapCount = FromJsonCore(jr, ref scratchWriter, options, depth + 1); + scratchWriter.Flush(); + + mapCount = mapCount / 2; // remove propertyname string count. +@@ -210,11 +217,13 @@ namespace MessagePack + case TinyJsonToken.EndObject: + return count; // break + case TinyJsonToken.StartArray: ++ VerifyJsonObjectGraphDepth(options, depth); ++ + // Set up a scratch area to serialize the collection since we don't know its length yet, which must be written first. + using (var scratchRental = options.SequencePool.Rent()) + { + MessagePackWriter scratchWriter = writer.Clone(scratchRental.Value); +- var arrayCount = FromJsonCore(jr, ref scratchWriter, options); ++ var arrayCount = FromJsonCore(jr, ref scratchWriter, options, depth + 1); + scratchWriter.Flush(); + + writer.WriteArrayHeader(arrayCount); +@@ -270,6 +279,14 @@ namespace MessagePack + return count; + } + ++ private static void VerifyJsonObjectGraphDepth(MessagePackSerializerOptions options, int depth) ++ { ++ if (depth >= options.Security.MaximumObjectGraphDepth) ++ { ++ throw new InsufficientExecutionStackException($"This JSON sequence has an object graph that exceeds the maximum depth allowed of {options.Security.MaximumObjectGraphDepth}."); ++ } ++ } ++ + private static void ToJsonCore(ref MessagePackReader reader, TextWriter writer, MessagePackSerializerOptions options) + { + MessagePackType type = reader.NextMessagePackType; +@@ -393,46 +410,54 @@ namespace MessagePack + #if !UNITY_2018_3_OR_NEWER + else if (extHeader.TypeCode == ThisLibraryExtensionTypeCodes.TypelessFormatter) + { +- // prepare type name token +- var privateBuilder = new StringBuilder(); +- var typeNameTokenBuilder = new StringBuilder(); +- SequencePosition positionBeforeTypeNameRead = reader.Position; +- ToJsonCore(ref reader, new StringWriter(typeNameTokenBuilder), options); +- int typeNameReadSize = (int)reader.Sequence.Slice(positionBeforeTypeNameRead, reader.Position).Length; +- if (extHeader.Length > typeNameReadSize) ++ options.Security.DepthStep(ref reader); ++ try + { +- // object map or array +- MessagePackType typeInside = reader.NextMessagePackType; +- if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) ++ // prepare type name token ++ var privateBuilder = new StringBuilder(); ++ var typeNameTokenBuilder = new StringBuilder(); ++ SequencePosition positionBeforeTypeNameRead = reader.Position; ++ ToJsonCore(ref reader, new StringWriter(typeNameTokenBuilder), options); ++ int typeNameReadSize = (int)reader.Sequence.Slice(positionBeforeTypeNameRead, reader.Position).Length; ++ if (extHeader.Length > typeNameReadSize) + { +- privateBuilder.Append("{"); +- } ++ // object map or array ++ MessagePackType typeInside = reader.NextMessagePackType; ++ if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) ++ { ++ privateBuilder.Append("{"); ++ } + +- ToJsonCore(ref reader, new StringWriter(privateBuilder), options); ++ ToJsonCore(ref reader, new StringWriter(privateBuilder), options); + +- // insert type name token to start of object map or array +- if (typeInside != MessagePackType.Array) +- { +- typeNameTokenBuilder.Insert(0, "\"$type\":"); +- } ++ // insert type name token to start of object map or array ++ if (typeInside != MessagePackType.Array) ++ { ++ typeNameTokenBuilder.Insert(0, "\"$type\":"); ++ } + +- if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) +- { +- privateBuilder.Append("}"); +- } ++ if (typeInside != MessagePackType.Array && typeInside != MessagePackType.Map) ++ { ++ privateBuilder.Append("}"); ++ } + +- if (privateBuilder.Length > 2) +- { +- typeNameTokenBuilder.Append(","); +- } ++ if (privateBuilder.Length > 2) ++ { ++ typeNameTokenBuilder.Append(","); ++ } + +- privateBuilder.Insert(1, typeNameTokenBuilder.ToString()); ++ privateBuilder.Insert(1, typeNameTokenBuilder.ToString()); + +- writer.Write(privateBuilder.ToString()); ++ writer.Write(privateBuilder.ToString()); ++ } ++ else ++ { ++ writer.Write("{\"$type\":" + typeNameTokenBuilder.ToString() + "}"); ++ } + } +- else ++ finally + { +- writer.Write("{\"$type\":" + typeNameTokenBuilder.ToString() + "}"); ++ reader.Depth--; + } + } + #endif +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs +index a75917b0..8d11b312 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializer.cs +@@ -234,7 +234,7 @@ namespace MessagePack + using (var msgPackUncompressedRental = options.SequencePool.Rent()) + { + var msgPackUncompressed = msgPackUncompressedRental.Value; +- if (TryDecompress(ref reader, msgPackUncompressed)) ++ if (TryDecompress(ref reader, msgPackUncompressed, options)) + { + MessagePackReader uncompressedReader = reader.Clone(msgPackUncompressed.AsReadOnlySequence); + return options.Resolver.GetFormatterWithVerify().Deserialize(ref uncompressedReader, options); +@@ -482,7 +482,7 @@ namespace MessagePack + } + } + +- private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter writer) ++ private static bool TryDecompress(ref MessagePackReader reader, IBufferWriter writer, MessagePackSerializerOptions options) + { + if (!reader.End) + { +@@ -504,6 +504,7 @@ namespace MessagePack + // The rest of the payload is the compressed data itself. + ReadOnlySequence compressedData = extReader.Sequence.Slice(extReader.Position); + ++ ThrowIfInvalidLz4BlockLength(uncompressedLength, options.Security.MaximumDecompressedSize); + Span uncompressedSpan = writer.GetSpan(uncompressedLength).Slice(0, uncompressedLength); + int actualUncompressedLength = LZ4Operation(compressedData, uncompressedSpan, LZ4CodecDecode); + Debug.Assert(actualUncompressedLength == uncompressedLength, "Unexpected length of uncompressed data."); +@@ -530,6 +531,7 @@ namespace MessagePack + var uncompressedLengths = ArrayPool.Shared.Rent(sequenceCount); + try + { ++ long remainingMaxDecompressedSize = options.Security.MaximumDecompressedSize; + for (int i = 0; i < sequenceCount; i++) + { + uncompressedLengths[i] = reader.ReadInt32(); +@@ -539,6 +541,8 @@ namespace MessagePack + { + var uncompressedLength = uncompressedLengths[i]; + ReadOnlySequence lz4Block = reader.ReadBytes() ?? throw MessagePackSerializationException.ThrowUnexpectedNilWhileDeserializing>(); ++ ThrowIfInvalidLz4BlockLength(uncompressedLength, remainingMaxDecompressedSize); ++ remainingMaxDecompressedSize -= uncompressedLength; + Span uncompressedSpan = writer.GetSpan(uncompressedLength).Slice(0, uncompressedLength); + var actualUncompressedLength = LZ4Operation(lz4Block, uncompressedSpan, LZ4CodecDecode); + Debug.Assert(actualUncompressedLength == uncompressedLength, "Unexpected length of uncompressed data."); +@@ -559,6 +563,14 @@ namespace MessagePack + return false; + } + ++ private static void ThrowIfInvalidLz4BlockLength(int uncompressedLength, long remainingMaxDecompressedSize) ++ { ++ if (uncompressedLength < 0 || uncompressedLength > remainingMaxDecompressedSize) ++ { ++ throw new MessagePackSerializationException("LZ4 block declares a decompressed length that exceeds the configured maximum."); ++ } ++ } ++ + private static void ToLZ4BinaryCore(in ReadOnlySequence msgpackUncompressedData, ref MessagePackWriter writer, MessagePackCompression compression, int minCompressionSize) + { + if (msgpackUncompressedData.Length < minCompressionSize) +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs +index dc459823..18c197f7 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/MessagePackSerializerOptions.cs +@@ -175,16 +175,32 @@ namespace MessagePack + /// The type to be instantiated. + /// Thrown if the is not allowed to be deserialized. + /// ++ /// + /// This method provides a means for an important security mitigation when using the Typeless formatter to prevent untrusted messagepack from + /// deserializing objects that may be harmful if instantiated, disposed or finalized. +- /// The default implementation throws for only a few known dangerous types. ++ /// The default implementation throws for only a few known dangerous types, or types that nest those dangerous types as generic type arguments or array element types. + /// Applications that deserialize from untrusted sources should override this method and throw if the type is not among the expected set. ++ /// ++ /// ++ /// This method is for backward compatibility reasons. ++ /// For better security, the preferred method to override is . ++ /// + /// + public virtual void ThrowIfDeserializingTypeIsDisallowed(Type type) + { +- if (type.FullName is string fullName && DisallowedTypes.Contains(fullName)) ++ this.ThrowIfDeserializingTypeIsDisallowedCore(type); ++ ++ if (type.HasElementType && type.GetElementType() is Type elementType) + { +- throw new MessagePackSerializationException($"Deserialization attempted to create the type {fullName} which is not allowed."); ++ this.ThrowIfDeserializingTypeIsDisallowed(elementType); ++ } ++ ++ if (type.IsConstructedGenericType) ++ { ++ foreach (Type genericTypeArgument in type.GenericTypeArguments) ++ { ++ this.ThrowIfDeserializingTypeIsDisallowed(genericTypeArgument); ++ } + } + } + +@@ -361,6 +377,31 @@ namespace MessagePack + return result; + } + ++ /// ++ /// Checks whether a specific given type may be deserialized, disregarding generic type arguments or array element types. ++ /// ++ /// The type to be instantiated. ++ /// Thrown if the is not allowed to be deserialized. ++ /// ++ /// ++ /// This method provides a means for an important security mitigation when using the Typeless formatter to prevent untrusted messagepack from ++ /// deserializing objects that may be harmful if instantiated, disposed or finalized. ++ /// The default implementation throws for only a few known dangerous types. ++ /// Applications that deserialize from untrusted sources should override this method and throw if the type is not among the expected set. ++ /// ++ /// ++ /// This method is called from the default implementation of ++ /// for the top-level type and again for each generic type argument or array element type. ++ /// ++ /// ++ protected virtual void ThrowIfDeserializingTypeIsDisallowedCore(Type type) ++ { ++ if (type.FullName is string fullName && DisallowedTypes.Contains(fullName)) ++ { ++ throw new MessagePackSerializationException($"Deserialization attempted to create the type {fullName} which is not allowed."); ++ } ++ } ++ + /// + /// Creates a clone of this instance with the same properties set. + /// +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs +index 7f1c436b..3f321b78 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/DynamicUnionResolver.cs +@@ -331,6 +331,14 @@ namespace MessagePack.Resolvers + + il.MarkLabel(falseLabel); + ++ var reader = new ArgumentField(il, 1); ++ ++ // options.Security.DepthStep(ref reader); ++ il.EmitLdarg(2); ++ il.EmitCall(getSecurityFromOptions); ++ reader.EmitLdarg(); ++ il.EmitCall(securityDepthStep); ++ + // IFormatterResolver resolver = options.Resolver; + LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver)); + il.EmitLdarg(2); +@@ -339,7 +347,6 @@ namespace MessagePack.Resolvers + + // read-array header and validate, reader.ReadArrayHeader() != 2) throw; + Label rightLabel = il.DefineLabel(); +- var reader = new ArgumentField(il, 1); + reader.EmitLdarg(); + il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader); + il.EmitLdc_I4(2); +@@ -406,6 +413,14 @@ namespace MessagePack.Resolvers + + il.MarkLabel(loopEnd); + ++ // reader.Depth--; ++ reader.EmitLdarg(); ++ il.Emit(OpCodes.Dup); ++ il.EmitCall(readerDepthGet); ++ il.Emit(OpCodes.Ldc_I4_1); ++ il.Emit(OpCodes.Sub_Ovf); ++ il.EmitCall(readerDepthSet); ++ + il.Emit(OpCodes.Ldloc, result); + il.Emit(OpCodes.Ret); + } +@@ -430,6 +445,10 @@ namespace MessagePack.Resolvers + private static readonly Type refKvp = typeof(KeyValuePair).MakeByRefType(); + private static readonly MethodInfo getFormatterWithVerify = typeof(FormatterResolverExtensions).GetRuntimeMethods().First(x => x.Name == "GetFormatterWithVerify"); + private static readonly MethodInfo getResolverFromOptions = typeof(MessagePackSerializerOptions).GetRuntimeProperty(nameof(MessagePackSerializerOptions.Resolver))!.GetMethod!; ++ private static readonly MethodInfo getSecurityFromOptions = typeof(MessagePackSerializerOptions).GetRuntimeProperty(nameof(MessagePackSerializerOptions.Security))!.GetMethod!; ++ private static readonly MethodInfo securityDepthStep = typeof(MessagePackSecurity).GetRuntimeMethod(nameof(MessagePackSecurity.DepthStep), new[] { typeof(MessagePackReader).MakeByRefType() })!; ++ private static readonly MethodInfo readerDepthGet = typeof(MessagePackReader).GetRuntimeProperty(nameof(MessagePackReader.Depth))!.GetMethod!; ++ private static readonly MethodInfo readerDepthSet = typeof(MessagePackReader).GetRuntimeProperty(nameof(MessagePackReader.Depth))!.SetMethod!; + + private static readonly Func getSerialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Serialize", new[] { typeof(MessagePackWriter).MakeByRefType(), t, typeof(MessagePackSerializerOptions) })!; + private static readonly Func getDeserialize = t => typeof(IMessagePackFormatter<>).MakeGenericType(t).GetRuntimeMethod("Deserialize", new[] { typeof(MessagePackReader).MakeByRefType(), typeof(MessagePackSerializerOptions) })!; +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs +index ffc7b149..11107efa 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/ExpandoObjectResolver.cs +@@ -40,6 +40,7 @@ namespace MessagePack.Resolvers + { + protected override object DeserializeMap(ref MessagePackReader reader, int length, MessagePackSerializerOptions options) + { ++ ExpandoObjectFormatter.ThrowIfMapTooLargeForUntrustedData(length, options); + IMessagePackFormatter keyFormatter = options.Resolver.GetFormatterWithVerify(); + IMessagePackFormatter? objectFormatter = options.Resolver.GetFormatterWithVerify(); + IDictionary dictionary = new ExpandoObject(); +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/T4/UnsafeMemory.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/T4/UnsafeMemory.cs +index 7956db01..9ebcd6e7 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/T4/UnsafeMemory.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/T4/UnsafeMemory.cs +@@ -20,7 +20,7 @@ namespace MessagePack.Internal + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw4(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(4); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -28,13 +28,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 0) = *(int*)(pSrc + 0); + } + +- writer.Advance(src.Length); ++ writer.Advance(4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw5(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(5); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -43,13 +43,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 1) = *(int*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw6(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(6); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -58,13 +58,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 2) = *(int*)(pSrc + 2); + } + +- writer.Advance(src.Length); ++ writer.Advance(6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw7(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(7); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -73,13 +73,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 3) = *(int*)(pSrc + 3); + } + +- writer.Advance(src.Length); ++ writer.Advance(7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(8); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -88,13 +88,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 4) = *(int*)(pSrc + 4); + } + +- writer.Advance(src.Length); ++ writer.Advance(8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw9(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(9); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -104,13 +104,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 5) = *(int*)(pSrc + 5); + } + +- writer.Advance(src.Length); ++ writer.Advance(9); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw10(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(10); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -120,13 +120,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 6) = *(int*)(pSrc + 6); + } + +- writer.Advance(src.Length); ++ writer.Advance(10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(11); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -136,13 +136,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 7) = *(int*)(pSrc + 7); + } + +- writer.Advance(src.Length); ++ writer.Advance(11); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(12); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -152,13 +152,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 8) = *(int*)(pSrc + 8); + } + +- writer.Advance(src.Length); ++ writer.Advance(12); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(13); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -169,13 +169,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 9) = *(int*)(pSrc + 9); + } + +- writer.Advance(src.Length); ++ writer.Advance(13); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(14); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -186,13 +186,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 10) = *(int*)(pSrc + 10); + } + +- writer.Advance(src.Length); ++ writer.Advance(14); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(15); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -203,13 +203,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 11) = *(int*)(pSrc + 11); + } + +- writer.Advance(src.Length); ++ writer.Advance(15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(16); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -220,13 +220,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 12) = *(int*)(pSrc + 12); + } + +- writer.Advance(src.Length); ++ writer.Advance(16); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(17); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -238,13 +238,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 13) = *(int*)(pSrc + 13); + } + +- writer.Advance(src.Length); ++ writer.Advance(17); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(18); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -256,13 +256,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 14) = *(int*)(pSrc + 14); + } + +- writer.Advance(src.Length); ++ writer.Advance(18); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(19); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -274,13 +274,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 15) = *(int*)(pSrc + 15); + } + +- writer.Advance(src.Length); ++ writer.Advance(19); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(20); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -292,13 +292,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 16) = *(int*)(pSrc + 16); + } + +- writer.Advance(src.Length); ++ writer.Advance(20); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(21); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -311,13 +311,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 17) = *(int*)(pSrc + 17); + } + +- writer.Advance(src.Length); ++ writer.Advance(21); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(22); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -330,13 +330,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 18) = *(int*)(pSrc + 18); + } + +- writer.Advance(src.Length); ++ writer.Advance(22); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(23); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -349,13 +349,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 19) = *(int*)(pSrc + 19); + } + +- writer.Advance(src.Length); ++ writer.Advance(23); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(24); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -368,13 +368,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 20) = *(int*)(pSrc + 20); + } + +- writer.Advance(src.Length); ++ writer.Advance(24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(25); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -388,13 +388,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 21) = *(int*)(pSrc + 21); + } + +- writer.Advance(src.Length); ++ writer.Advance(25); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(26); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -408,13 +408,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 22) = *(int*)(pSrc + 22); + } + +- writer.Advance(src.Length); ++ writer.Advance(26); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(27); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -428,13 +428,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 23) = *(int*)(pSrc + 23); + } + +- writer.Advance(src.Length); ++ writer.Advance(27); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(28); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -448,13 +448,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 24) = *(int*)(pSrc + 24); + } + +- writer.Advance(src.Length); ++ writer.Advance(28); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(29); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -469,13 +469,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 25) = *(int*)(pSrc + 25); + } + +- writer.Advance(src.Length); ++ writer.Advance(29); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(30); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -490,13 +490,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 26) = *(int*)(pSrc + 26); + } + +- writer.Advance(src.Length); ++ writer.Advance(30); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(31); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -511,7 +511,7 @@ namespace MessagePack.Internal + *(int*)(pDst + 27) = *(int*)(pSrc + 27); + } + +- writer.Advance(src.Length); ++ writer.Advance(31); + } + } + +@@ -520,7 +520,7 @@ namespace MessagePack.Internal + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(8); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -528,13 +528,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 0) = *(long*)(pSrc + 0); + } + +- writer.Advance(src.Length); ++ writer.Advance(8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw9(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(9); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -543,13 +543,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 1) = *(long*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(9); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw10(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(10); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -558,13 +558,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 2) = *(long*)(pSrc + 2); + } + +- writer.Advance(src.Length); ++ writer.Advance(10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(11); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -573,13 +573,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 3) = *(long*)(pSrc + 3); + } + +- writer.Advance(src.Length); ++ writer.Advance(11); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(12); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -588,13 +588,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 4) = *(long*)(pSrc + 4); + } + +- writer.Advance(src.Length); ++ writer.Advance(12); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(13); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -603,13 +603,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 5) = *(long*)(pSrc + 5); + } + +- writer.Advance(src.Length); ++ writer.Advance(13); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(14); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -618,13 +618,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 6) = *(long*)(pSrc + 6); + } + +- writer.Advance(src.Length); ++ writer.Advance(14); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(15); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -633,13 +633,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 7) = *(long*)(pSrc + 7); + } + +- writer.Advance(src.Length); ++ writer.Advance(15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(16); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -648,13 +648,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 8) = *(long*)(pSrc + 8); + } + +- writer.Advance(src.Length); ++ writer.Advance(16); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(17); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -664,13 +664,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 9) = *(long*)(pSrc + 9); + } + +- writer.Advance(src.Length); ++ writer.Advance(17); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(18); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -680,13 +680,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 10) = *(long*)(pSrc + 10); + } + +- writer.Advance(src.Length); ++ writer.Advance(18); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(19); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -696,13 +696,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 11) = *(long*)(pSrc + 11); + } + +- writer.Advance(src.Length); ++ writer.Advance(19); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(20); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -712,13 +712,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 12) = *(long*)(pSrc + 12); + } + +- writer.Advance(src.Length); ++ writer.Advance(20); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(21); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -728,13 +728,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 13) = *(long*)(pSrc + 13); + } + +- writer.Advance(src.Length); ++ writer.Advance(21); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(22); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -744,13 +744,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 14) = *(long*)(pSrc + 14); + } + +- writer.Advance(src.Length); ++ writer.Advance(22); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(23); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -760,13 +760,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 15) = *(long*)(pSrc + 15); + } + +- writer.Advance(src.Length); ++ writer.Advance(23); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(24); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -776,13 +776,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 16) = *(long*)(pSrc + 16); + } + +- writer.Advance(src.Length); ++ writer.Advance(24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(25); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -793,13 +793,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 17) = *(long*)(pSrc + 17); + } + +- writer.Advance(src.Length); ++ writer.Advance(25); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(26); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -810,13 +810,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 18) = *(long*)(pSrc + 18); + } + +- writer.Advance(src.Length); ++ writer.Advance(26); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(27); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -827,13 +827,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 19) = *(long*)(pSrc + 19); + } + +- writer.Advance(src.Length); ++ writer.Advance(27); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(28); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -844,13 +844,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 20) = *(long*)(pSrc + 20); + } + +- writer.Advance(src.Length); ++ writer.Advance(28); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(29); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -861,13 +861,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 21) = *(long*)(pSrc + 21); + } + +- writer.Advance(src.Length); ++ writer.Advance(29); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(30); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -878,13 +878,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 22) = *(long*)(pSrc + 22); + } + +- writer.Advance(src.Length); ++ writer.Advance(30); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(31); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -895,7 +895,7 @@ namespace MessagePack.Internal + *(long*)(pDst + 23) = *(long*)(pSrc + 23); + } + +- writer.Advance(src.Length); ++ writer.Advance(31); + } + } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Extension/UnsafeBlitFormatter.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Extension/UnsafeBlitFormatter.cs +index 14efd4fa..011b9199 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Extension/UnsafeBlitFormatter.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Unity/Extension/UnsafeBlitFormatter.cs +@@ -48,19 +48,26 @@ namespace MessagePack.Unity.Extension + return null; + } + +- ExtensionHeader header = reader.ReadExtensionFormatHeader(); +- if (header.TypeCode != this.TypeCode) ++ ExtensionResult extension = reader.ReadExtensionFormat(); ++ if (extension.TypeCode != this.TypeCode) + { + throw new InvalidOperationException("Invalid typeCode."); + } + +- var byteLength = reader.ReadInt32(); +- var isLittleEndian = reader.ReadBoolean(); ++ MessagePackReader extensionReader = reader.Clone(extension.Data); ++ var byteLength = extensionReader.ReadInt32(); ++ var isLittleEndian = extensionReader.ReadBoolean(); ++ int elementSize = Marshal.SizeOf(); ++ long remainingBytes = extensionReader.Sequence.Length - extensionReader.Consumed; ++ if (byteLength < 0 || byteLength % elementSize != 0 || byteLength != remainingBytes) ++ { ++ throw new MessagePackSerializationException("Invalid Unity blit extension length."); ++ } + + // Allocate a T[] that we will return. We'll then cast the T[] as byte[] so we can copy the byte sequence directly into it. +- var result = new T[byteLength / Marshal.SizeOf()]; ++ var result = new T[byteLength / elementSize]; + Span resultAsBytes = MemoryMarshal.Cast(result); +- reader.ReadRaw(byteLength).CopyTo(resultAsBytes); ++ extensionReader.ReadRaw(byteLength).CopyTo(resultAsBytes); + + // Reverse the byte order if necessary. + if (isLittleEndian != BitConverter.IsLittleEndian) +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ExpandoObjectTests.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ExpandoObjectTests.cs +index 99f3b808..9cbf63bb 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ExpandoObjectTests.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ExpandoObjectTests.cs +@@ -3,6 +3,7 @@ + + #if !UNITY_2018_3_OR_NEWER + ++using System.Collections.Generic; + using System.Dynamic; + using System.Runtime.Serialization; + using MessagePack.Resolvers; +@@ -85,6 +86,24 @@ namespace MessagePack.Tests + Assert.Equal(expando.Other.OtherProperty, expando2.Other.OtherProperty); + } + ++ [Fact] ++ [Trait("CWE", "407")] ++ public void ExpandoObject_UntrustedDataRejectsLargeMaps() ++ { ++ byte[] msgpack = CreateMapWithNilValues(1025); ++ ++ Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, ExpandoObjectResolver.Options)); ++ } ++ ++ [Fact] ++ [Trait("CWE", "407")] ++ public void ExpandoObjectNestedMap_UntrustedDataRejectsLargeMaps() ++ { ++ byte[] msgpack = CreateMapWithNilValues(1025); ++ ++ Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, ExpandoObjectResolver.Options)); ++ } ++ + #if !UNITY_2018_3_OR_NEWER + + [Fact] +@@ -115,6 +134,17 @@ namespace MessagePack.Tests + [DataMember] + public string OtherProperty { get; set; } + } ++ ++ private static byte[] CreateMapWithNilValues(int count) ++ { ++ var dictionary = new Dictionary(); ++ for (int index = 0; index < count; index++) ++ { ++ dictionary.Add("k" + index.ToString(System.Globalization.CultureInfo.InvariantCulture), null); ++ } ++ ++ return MessagePackSerializer.Serialize(dictionary, MessagePackSerializerOptions.Standard); ++ } + } + } + +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/LZ4Test.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/LZ4Test.cs +index 062576fc..3a75c5ac 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/LZ4Test.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/LZ4Test.cs +@@ -1,6 +1,7 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++using System.Buffers; + using System.Linq; + using Xunit; + using Xunit.Abstractions; +@@ -22,6 +23,64 @@ namespace MessagePack.Tests + + #endif + ++ [Theory] ++ [InlineData(MessagePackCompression.Lz4Block)] ++ [InlineData(MessagePackCompression.Lz4BlockArray)] ++ [Trait("CWE", "409")] ++ public void Lz4RejectsDeclaredOutputOverMaximumBeforeAllocating(MessagePackCompression compression) ++ { ++ byte[] payload = compression == MessagePackCompression.Lz4Block ++ ? new byte[] { 0xC7, 0x06, 0x63, 0xD2, 0x7F, 0xFF, 0xFF, 0xFF, 0x00 } ++ : new byte[] { 0x92, 0xC7, 0x05, 0x62, 0xD2, 0x7F, 0xFF, 0xFF, 0xFF, 0xC4, 0x01, 0x00 }; ++ var arrayPool = new ThrowingArrayPool(1024); ++ var options = MessagePackSerializerOptions.Standard ++ .WithCompression(compression) ++ .WithSecurity(MessagePackSecurity.UntrustedData) ++ .WithPool(new SequencePool(1, arrayPool)); ++ ++ MessagePackSerializationException ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); ++ ++ Assert.Contains("exceeds the configured maximum", FlattenMessages(ex)); ++ Assert.Null(arrayPool.LargestRequestedLength); ++ } ++ ++ [Fact] ++ [Trait("CWE", "409")] ++ public void Lz4BlockArrayRejectsTotalDeclaredOutputOverMaximum() ++ { ++ byte[] payload = ++ { ++ 0x93, ++ 0xC7, 0x02, 0x62, 0x04, 0x04, ++ 0xC4, 0x05, 0x40, 0x20, 0x20, 0x20, 0x20, ++ 0xC4, 0x05, 0x40, 0x20, 0x20, 0x20, 0x20, ++ }; ++ var options = MessagePackSerializerOptions.Standard ++ .WithCompression(MessagePackCompression.Lz4BlockArray) ++ .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(7)); ++ ++ MessagePackSerializationException ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); ++ ++ Assert.Contains("exceeds the configured maximum", FlattenMessages(ex)); ++ } ++ ++ [Theory] ++ [InlineData(MessagePackCompression.Lz4Block)] ++ [InlineData(MessagePackCompression.Lz4BlockArray)] ++ [Trait("CWE", "409")] ++ public void Lz4AllowsHighlyCompressiblePayloadWithinMaximum(MessagePackCompression compression) ++ { ++ string data = new string(' ', 100_000); ++ var options = MessagePackSerializerOptions.Standard ++ .WithCompression(compression) ++ .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(200_000)); ++ ++ byte[] payload = MessagePackSerializer.Serialize(data, options); ++ string actual = MessagePackSerializer.Deserialize(payload, options); ++ ++ Assert.Equal(data, actual); ++ } ++ + [Fact] + public void Lz4Compress() + { +@@ -123,5 +182,37 @@ namespace MessagePack.Tests + actual[i].Prop3.Is(expected[i].Prop3); + } + } ++ ++ private static string FlattenMessages(System.Exception ex) ++ { ++ return ex.InnerException is null ? ex.Message : ex.Message + " " + FlattenMessages(ex.InnerException); ++ } ++ ++ private class ThrowingArrayPool : ArrayPool ++ { ++ private readonly int maximumLength; ++ ++ internal ThrowingArrayPool(int maximumLength) ++ { ++ this.maximumLength = maximumLength; ++ } ++ ++ internal int? LargestRequestedLength { get; private set; } ++ ++ public override byte[] Rent(int minimumLength) ++ { ++ this.LargestRequestedLength = this.LargestRequestedLength.HasValue ? System.Math.Max(this.LargestRequestedLength.Value, minimumLength) : minimumLength; ++ if (minimumLength > this.maximumLength) ++ { ++ throw new System.InvalidOperationException("Unexpected decompression allocation request: " + minimumLength); ++ } ++ ++ return new byte[minimumLength]; ++ } ++ ++ public override void Return(byte[] array, bool clearArray = false) ++ { ++ } ++ } + } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackReaderTests.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackReaderTests.cs +index 774927d1..33c36343 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackReaderTests.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackReaderTests.cs +@@ -101,6 +101,33 @@ namespace MessagePack.Tests + }); + } + ++ [Fact] ++ [Trait("CWE", "190")] ++ public void ReadMapHeader_MitigatesLargeAllocations_WhenMinimumPayloadLengthOverflowsInt32() ++ { ++ byte[] msgpack = { MessagePackCode.Map32, 0x40, 0, 0, 0 }; ++ ++ Assert.Throws(() => ++ { ++ var reader = new MessagePackReader(msgpack); ++ reader.ReadMapHeader(); ++ }); ++ } ++ ++ [Fact] ++ [Trait("CWE", "190")] ++ public void SkipMap_MitigatesLargeAllocations_WhenMinimumPayloadLengthOverflowsInt32() ++ { ++ byte[] msgpack = { MessagePackCode.Map32, 0x40, 0, 0, 0 }; ++ ++ var ex = Assert.ThrowsAny(() => ++ { ++ var reader = new MessagePackReader(msgpack); ++ reader.Skip(); ++ }); ++ Assert.True(ex is EndOfStreamException or MessagePackSerializationException or OverflowException); ++ } ++ + [Fact] + public void TryReadMapHeader() + { +@@ -252,6 +279,49 @@ namespace MessagePack.Tests + Assert.True(reader.End); + } + ++ [Fact] ++ [Trait("CWE", "674")] ++ public void Skip_DeeplyNestedArrays_DoesNotOverflowStack() ++ { ++ const int depth = 100_000; ++ byte[] msgpack = new byte[depth + 1]; ++ ++ for (int i = 0; i < depth; i++) ++ { ++ msgpack[i] = MessagePackCode.MinFixArray + 1; ++ } ++ ++ msgpack[msgpack.Length - 1] = MessagePackCode.Nil; ++ ++ MessagePackReader reader = new(msgpack); ++ ++ reader.Skip(); ++ ++ Assert.True(reader.End); ++ } ++ ++ [Fact] ++ [Trait("CWE", "674")] ++ public void Skip_DeeplyNestedMaps_DoesNotOverflowStack() ++ { ++ const int depth = 100_000; ++ byte[] msgpack = new byte[(depth * 2) + 1]; ++ ++ for (int i = 0; i < depth; i++) ++ { ++ msgpack[i * 2] = MessagePackCode.MinFixMap + 1; ++ msgpack[(i * 2) + 1] = MessagePackCode.Nil; ++ } ++ ++ msgpack[msgpack.Length - 1] = MessagePackCode.Nil; ++ ++ MessagePackReader reader = new(msgpack); ++ ++ reader.Skip(); ++ ++ Assert.True(reader.End); ++ } ++ + [Fact] + public void Depth() + { +@@ -322,6 +392,15 @@ namespace MessagePack.Tests + AssertIncomplete((ref MessagePackWriter writer) => writer.Write(0xff), (ref MessagePackReader reader) => reader.ReadUInt64()); + } + ++ [Fact] ++ [Trait("CWE", "789")] ++ public void ReadDateTime_RejectsInvalidExtensionLengths() ++ { ++ byte[] payload = new byte[] { MessagePackCode.Ext32, 0x00, 0x10, 0x00, 0x00, 0xff }; ++ ++ Assert.Throws(() => new MessagePackReader(new ReadOnlySequence(payload)).ReadDateTime()); ++ } ++ + [Fact] + public void CreatePeekReader() + { +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSecurityTests.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSecurityTests.cs +index 74121e5d..ef1cd3f4 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSecurityTests.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSecurityTests.cs +@@ -31,12 +31,14 @@ public class MessagePackSecurityTests + public void Untrusted() + { + Assert.True(MessagePackSecurity.UntrustedData.HashCollisionResistant); ++ Assert.Equal(64 * 1024 * 1024, MessagePackSecurity.UntrustedData.MaximumDecompressedSize); + } + + [Fact] + public void Trusted() + { + Assert.False(MessagePackSecurity.TrustedData.HashCollisionResistant); ++ Assert.Equal(int.MaxValue, MessagePackSecurity.TrustedData.MaximumDecompressedSize); + } + + [Fact] +@@ -46,6 +48,15 @@ public class MessagePackSecurityTests + Assert.True(MessagePackSecurity.TrustedData.WithHashCollisionResistant(true).HashCollisionResistant); + } + ++ [Fact] ++ [Trait("CWE", "409")] ++ public void WithMaximumDecompressedSize() ++ { ++ Assert.Same(MessagePackSecurity.UntrustedData, MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(64 * 1024 * 1024)); ++ Assert.Throws(() => MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(-1)); ++ Assert.Equal(1024, MessagePackSecurity.UntrustedData.WithMaximumDecompressedSize(1024).MaximumDecompressedSize); ++ } ++ + [Fact] + public void EqualityComparer_CollisionResistance_Int64() + { +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTest.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTest.cs +index 211a3514..5157b4fa 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTest.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTest.cs +@@ -209,6 +209,18 @@ namespace MessagePack.Tests + Assert.Equal(3, await MessagePackSerializer.DeserializeAsync(stream)); + } + ++ [Fact] ++ [Trait("CWE", "674")] ++ public void StackDepthCheck_ConvertToJsonTypelessExtension() ++ { ++ const int maxDepth = 3; ++ byte[] msgpack = BuildNestedTypelessExtension(maxDepth + 1); ++ var options = MessagePackSerializerOptions.Standard ++ .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumObjectGraphDepth(maxDepth)); ++ ++ AssertConvertToJsonRecursionCheckThrows(new ReadOnlySequence(msgpack), options); ++ } ++ + [Theory] + [InlineData(true)] + [InlineData(false)] +@@ -275,6 +287,18 @@ namespace MessagePack.Tests + Assert.IsType(ex.InnerException); + } + ++ [Fact] ++ [Trait("CWE", "674")] ++ public void StackDepthCheck_DynamicUnionResolver() ++ { ++ byte[] msgpack = MessagePackSerializer.Serialize(new DepthCheckedUnionBranch()); ++ var options = MessagePackSerializerOptions.Standard ++ .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumObjectGraphDepth(1)); ++ ++ var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, options)); ++ Assert.IsType(ex.InnerException); ++ } ++ + #endif + + private delegate void WriterHelper(ref MessagePackWriter writer); +@@ -318,6 +342,28 @@ namespace MessagePack.Tests + Assert.IsType(ex.InnerException); + } + ++ private static byte[] BuildNestedTypelessExtension(int levels) ++ { ++ byte[] msgpack = new byte[(levels * 6) + 2]; ++ int offset = msgpack.Length; ++ msgpack[--offset] = (byte)'x'; ++ msgpack[--offset] = 0xa1; ++ int innerLength = 2; ++ ++ for (int level = 0; level < levels; level++) ++ { ++ msgpack[--offset] = unchecked((byte)ThisLibraryExtensionTypeCodes.TypelessFormatter); ++ msgpack[--offset] = (byte)innerLength; ++ msgpack[--offset] = (byte)(innerLength >> 8); ++ msgpack[--offset] = (byte)(innerLength >> 16); ++ msgpack[--offset] = (byte)(innerLength >> 24); ++ msgpack[--offset] = 0xc9; ++ innerLength += 6; ++ } ++ ++ return msgpack; ++ } ++ + [DataContract] + public class RecursiveObjectGraph + { +@@ -414,4 +460,30 @@ namespace MessagePack.Tests + base.Dispose(disposing); + } + } ++ ++ [MessagePackObject(keyAsPropertyName: true)] ++ public class SkipUnknownMemberTarget ++ { ++ public int Known { get; set; } ++ } ++ ++ [Union(0, typeof(DepthCheckedUnionLeaf))] ++ [Union(999, typeof(DepthCheckedUnionBranch))] ++ public interface IDepthCheckedUnionNode ++ { ++ } ++ ++ [MessagePackObject] ++ public class DepthCheckedUnionLeaf : IDepthCheckedUnionNode ++ { ++ [Key(0)] ++ public int Value { get; set; } ++ } ++ ++ [MessagePackObject] ++ public class DepthCheckedUnionBranch : IDepthCheckedUnionNode ++ { ++ [Key(0)] ++ public IDepthCheckedUnionNode Child { get; set; } ++ } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTypelessTests.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTypelessTests.cs +index 995aaa6c..9243854f 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTypelessTests.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MessagePackSerializerTypelessTests.cs +@@ -4,6 +4,7 @@ + #if !UNITY_2018_3_OR_NEWER + + using System; ++using System.Collections.Generic; + using System.Runtime.Serialization; + using MessagePack; + using MessagePack.Formatters; +@@ -47,6 +48,24 @@ public class MessagePackSerializerTypelessTests + Assert.IsType(ex.InnerException); + } + ++ [Theory] ++ [MemberData(nameof(DisallowedNestedTypeData))] ++ [Trait("CWE", "502")] ++ public void SerializationOfDisallowedNestedType(object value) ++ { ++ var myOptions = new MyTypelessOptions(); ++ byte[] msgpack = MessagePackSerializer.Typeless.Serialize(value, myOptions); ++ this.logger.WriteLine(MessagePackSerializer.ConvertToJson(msgpack, myOptions)); ++ var ex = Assert.Throws(() => MessagePackSerializer.Typeless.Deserialize(msgpack, myOptions)); ++ Assert.IsType(ex.InnerException); ++ } ++ ++ public static IEnumerable DisallowedNestedTypeData() ++ { ++ yield return new object[] { new MyObject[] { new() { SomeValue = 5 } } }; ++ yield return new object[] { new List { new() { SomeValue = 5 } } }; ++ } ++ + [Fact(Skip = "Known bug https://github.com/neuecc/MessagePack-CSharp/issues/651")] + public void DecimalShouldBeDeserializedAsDecimal() + { +@@ -137,7 +156,7 @@ public class MessagePackSerializerTypelessTests + { + } + +- public override void ThrowIfDeserializingTypeIsDisallowed(Type type) ++ protected override void ThrowIfDeserializingTypeIsDisallowedCore(Type type) + { + if (type == typeof(MyObject)) + { +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MultiDimensionalArrayTest.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MultiDimensionalArrayTest.cs +index f4a0a261..59daa7fa 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MultiDimensionalArrayTest.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/MultiDimensionalArrayTest.cs +@@ -67,5 +67,60 @@ namespace MessagePack.Tests + } + } + } ++ ++ [Fact] ++ [Trait("CWE", "789")] ++ public void RejectsTwoDimensionalArrayWithMismatchedElementCount() ++ { ++ byte[] payload = ++ { ++ 0x93, ++ 0xCE, 0x00, 0x00, 0x07, 0xD0, ++ 0xCE, 0x00, 0x00, 0x07, 0xD0, ++ 0x90, ++ }; ++ ++ AssertRejects(payload); ++ } ++ ++ [Fact] ++ [Trait("CWE", "789")] ++ public void RejectsThreeDimensionalArrayWithMismatchedElementCount() ++ { ++ byte[] payload = ++ { ++ 0x94, ++ 0xCC, 0x80, ++ 0xCC, 0x80, ++ 0xCC, 0x80, ++ 0x90, ++ }; ++ ++ AssertRejects(payload); ++ } ++ ++ [Fact] ++ [Trait("CWE", "789")] ++ public void RejectsFourDimensionalArrayWithMismatchedElementCount() ++ { ++ byte[] payload = ++ { ++ 0x95, ++ 0x20, ++ 0x20, ++ 0x20, ++ 0x20, ++ 0x90, ++ }; ++ ++ AssertRejects(payload); ++ } ++ ++ private void AssertRejects(byte[] payload) ++ { ++ var options = MessagePackSerializerOptions.Standard.WithSecurity(MessagePackSecurity.UntrustedData); ++ ++ Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); ++ } + } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs +index 35123476..c3c60cef 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs +@@ -35,6 +35,27 @@ namespace MessagePack.Tests + Assert.Equal(type, type2); + } + ++ [Fact] ++ public void SystemType_DeserializeUsesLoadType() ++ { ++ byte[] msgpack = MessagePackSerializer.Serialize(new TypeHolder { Type = typeof(Uri) }, MessagePackSerializerOptions.Standard); ++ var options = new RejectingTypeLoadOptions(typeof(Uri)); ++ ++ var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, options)); ++ Assert.IsType(ex.InnerException); ++ Assert.Equal(1, options.LoadTypeCalls); ++ } ++ ++ [Fact] ++ public void SystemType_DeserializeRejectsDisallowedType() ++ { ++ byte[] msgpack = MessagePackSerializer.Serialize(new TypeHolder { Type = typeof(Uri) }, MessagePackSerializerOptions.Standard); ++ var options = new DisallowingTypeOptions(typeof(Uri)); ++ ++ var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(msgpack, options)); ++ Assert.IsType(ex.InnerException); ++ } ++ + [Fact] + public void DeserializeByteArrayFromFixArray() + { +@@ -132,5 +153,68 @@ namespace MessagePack.Tests + return MessagePackSerializer.Deserialize(msgpack, MessagePackSerializerOptions.Standard); + } + } ++ ++ [MessagePackObject] ++ public class TypeHolder ++ { ++ [Key(0)] ++ public Type Type { get; set; } ++ } ++ ++ private class RejectingTypeLoadOptions : MessagePackSerializerOptions ++ { ++ private readonly Type rejectedType; ++ ++ internal RejectingTypeLoadOptions(Type rejectedType) ++ : base(MessagePackSerializerOptions.Standard) ++ { ++ this.rejectedType = rejectedType; ++ } ++ ++ private RejectingTypeLoadOptions(RejectingTypeLoadOptions copyFrom) ++ : base(copyFrom) ++ { ++ this.rejectedType = copyFrom.rejectedType; ++ this.LoadTypeCalls = copyFrom.LoadTypeCalls; ++ } ++ ++ public int LoadTypeCalls { get; private set; } ++ ++ public override Type LoadType(string typeName) ++ { ++ Type type = base.LoadType(typeName); ++ this.LoadTypeCalls++; ++ return type == this.rejectedType ? null : type; ++ } ++ ++ protected override MessagePackSerializerOptions Clone() => new RejectingTypeLoadOptions(this); ++ } ++ ++ private class DisallowingTypeOptions : MessagePackSerializerOptions ++ { ++ private readonly Type rejectedType; ++ ++ internal DisallowingTypeOptions(Type rejectedType) ++ : base(MessagePackSerializerOptions.Standard) ++ { ++ this.rejectedType = rejectedType; ++ } ++ ++ private DisallowingTypeOptions(DisallowingTypeOptions copyFrom) ++ : base(copyFrom) ++ { ++ this.rejectedType = copyFrom.rejectedType; ++ } ++ ++ public override void ThrowIfDeserializingTypeIsDisallowed(Type type) ++ { ++ if (type == this.rejectedType) ++ { ++ throw new TypeAccessException(); ++ } ++ } ++ ++ protected override MessagePackSerializerOptions Clone() => new DisallowingTypeOptions(this); ++ } + } + } +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ToJsonTest.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ToJsonTest.cs +index 8069ea26..99fceee4 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ToJsonTest.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/ToJsonTest.cs +@@ -47,6 +47,29 @@ namespace MessagePack.Tests + this.JsonConvert(json, LZ4Standard).Is(json); + } + ++ [Theory] ++ [InlineData(false)] ++ [InlineData(true)] ++ [Trait("CWE", "674")] ++ public void ConvertFromJsonRejectsExcessiveNesting(bool compression) ++ { ++ var options = MessagePackSerializerOptions.Standard ++ .WithCompression(compression ? MessagePackCompression.Lz4Block : MessagePackCompression.None) ++ .WithSecurity(MessagePackSecurity.UntrustedData.WithMaximumObjectGraphDepth(3)); ++ ++ Assert.Throws(() => MessagePackSerializer.ConvertFromJson("[[[[1]]]]", options)); ++ } ++ ++ [Fact] ++ [Trait("CWE", "674")] ++ public void ConvertFromJsonSkipsLongSeparatorRunIteratively() ++ { ++ var json = new string(',', 200_000) + "null"; ++ var msgpack = MessagePackSerializer.ConvertFromJson(json); ++ ++ MessagePackSerializer.ConvertToJson(msgpack).Is("null"); ++ } ++ + [Fact] + public void FloatJson() + { +diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/UnsafeMemoryTest.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/UnsafeMemoryTest.cs +index c9b5f023..14002930 100644 +--- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/UnsafeMemoryTest.cs ++++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/UnsafeMemoryTest.cs +@@ -3,12 +3,7 @@ + + using System; + using System.Buffers; +-using System.Collections.Generic; + using System.Linq; +-using System.Reflection; +-using System.Text; +-using System.Threading.Tasks; +-using MessagePack.Formatters; + using MessagePack.Internal; + using Nerdbank.Streams; + using Xunit; +@@ -70,6 +65,35 @@ namespace MessagePack.Tests + } + } + ++ [Fact] ++ public void WriteRaw_LargerSpan() ++ { ++ ReadOnlySpan src = new byte[MessagePackRange.MaxFixStringLength + 15]; ++ Sequence dst = new(); ++ ++ // x86 ++ for (int i = 1; i <= MessagePackRange.MaxFixStringLength; i++) ++ { ++ dst.Reset(); ++ MessagePackWriter dstWriter = new(dst); ++ (typeof(UnsafeMemory32).GetMethod("WriteRaw" + i).CreateDelegate(typeof(WriteDelegate)) as WriteDelegate).Invoke(ref dstWriter, src); ++ dstWriter.Flush(); ++ dst.Length.Is(i); ++ src.Slice(0, i).SequenceEqual(CodeGenHelpers.GetSpanFromSequence(dst.AsReadOnlySequence)).IsTrue(); ++ } ++ ++ // x64 ++ for (int i = 1; i <= MessagePackRange.MaxFixStringLength; i++) ++ { ++ dst.Reset(); ++ var dstWriter = new MessagePackWriter(dst); ++ (typeof(UnsafeMemory64).GetMethod("WriteRaw" + i).CreateDelegate(typeof(WriteDelegate)) as WriteDelegate).Invoke(ref dstWriter, src); ++ dstWriter.Flush(); ++ dst.Length.Is(i); ++ src.Slice(0, i).SequenceEqual(CodeGenHelpers.GetSpanFromSequence(dst.AsReadOnlySequence)).IsTrue(); ++ } ++ } ++ + #endif + } + } +diff --git a/src/MessagePack/Internal/UnsafeMemory.cs b/src/MessagePack/Internal/UnsafeMemory.cs +index 7956db01..9ebcd6e7 100644 +--- a/src/MessagePack/Internal/UnsafeMemory.cs ++++ b/src/MessagePack/Internal/UnsafeMemory.cs +@@ -20,7 +20,7 @@ namespace MessagePack.Internal + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw4(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(4); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -28,13 +28,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 0) = *(int*)(pSrc + 0); + } + +- writer.Advance(src.Length); ++ writer.Advance(4); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw5(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(5); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -43,13 +43,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 1) = *(int*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw6(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(6); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -58,13 +58,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 2) = *(int*)(pSrc + 2); + } + +- writer.Advance(src.Length); ++ writer.Advance(6); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw7(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(7); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -73,13 +73,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 3) = *(int*)(pSrc + 3); + } + +- writer.Advance(src.Length); ++ writer.Advance(7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(8); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -88,13 +88,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 4) = *(int*)(pSrc + 4); + } + +- writer.Advance(src.Length); ++ writer.Advance(8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw9(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(9); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -104,13 +104,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 5) = *(int*)(pSrc + 5); + } + +- writer.Advance(src.Length); ++ writer.Advance(9); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw10(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(10); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -120,13 +120,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 6) = *(int*)(pSrc + 6); + } + +- writer.Advance(src.Length); ++ writer.Advance(10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(11); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -136,13 +136,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 7) = *(int*)(pSrc + 7); + } + +- writer.Advance(src.Length); ++ writer.Advance(11); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(12); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -152,13 +152,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 8) = *(int*)(pSrc + 8); + } + +- writer.Advance(src.Length); ++ writer.Advance(12); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(13); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -169,13 +169,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 9) = *(int*)(pSrc + 9); + } + +- writer.Advance(src.Length); ++ writer.Advance(13); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(14); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -186,13 +186,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 10) = *(int*)(pSrc + 10); + } + +- writer.Advance(src.Length); ++ writer.Advance(14); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(15); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -203,13 +203,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 11) = *(int*)(pSrc + 11); + } + +- writer.Advance(src.Length); ++ writer.Advance(15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(16); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -220,13 +220,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 12) = *(int*)(pSrc + 12); + } + +- writer.Advance(src.Length); ++ writer.Advance(16); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(17); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -238,13 +238,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 13) = *(int*)(pSrc + 13); + } + +- writer.Advance(src.Length); ++ writer.Advance(17); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(18); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -256,13 +256,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 14) = *(int*)(pSrc + 14); + } + +- writer.Advance(src.Length); ++ writer.Advance(18); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(19); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -274,13 +274,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 15) = *(int*)(pSrc + 15); + } + +- writer.Advance(src.Length); ++ writer.Advance(19); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(20); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -292,13 +292,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 16) = *(int*)(pSrc + 16); + } + +- writer.Advance(src.Length); ++ writer.Advance(20); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(21); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -311,13 +311,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 17) = *(int*)(pSrc + 17); + } + +- writer.Advance(src.Length); ++ writer.Advance(21); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(22); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -330,13 +330,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 18) = *(int*)(pSrc + 18); + } + +- writer.Advance(src.Length); ++ writer.Advance(22); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(23); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -349,13 +349,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 19) = *(int*)(pSrc + 19); + } + +- writer.Advance(src.Length); ++ writer.Advance(23); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(24); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -368,13 +368,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 20) = *(int*)(pSrc + 20); + } + +- writer.Advance(src.Length); ++ writer.Advance(24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(25); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -388,13 +388,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 21) = *(int*)(pSrc + 21); + } + +- writer.Advance(src.Length); ++ writer.Advance(25); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(26); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -408,13 +408,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 22) = *(int*)(pSrc + 22); + } + +- writer.Advance(src.Length); ++ writer.Advance(26); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(27); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -428,13 +428,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 23) = *(int*)(pSrc + 23); + } + +- writer.Advance(src.Length); ++ writer.Advance(27); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(28); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -448,13 +448,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 24) = *(int*)(pSrc + 24); + } + +- writer.Advance(src.Length); ++ writer.Advance(28); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(29); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -469,13 +469,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 25) = *(int*)(pSrc + 25); + } + +- writer.Advance(src.Length); ++ writer.Advance(29); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(30); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -490,13 +490,13 @@ namespace MessagePack.Internal + *(int*)(pDst + 26) = *(int*)(pSrc + 26); + } + +- writer.Advance(src.Length); ++ writer.Advance(30); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(31); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -511,7 +511,7 @@ namespace MessagePack.Internal + *(int*)(pDst + 27) = *(int*)(pSrc + 27); + } + +- writer.Advance(src.Length); ++ writer.Advance(31); + } + } + +@@ -520,7 +520,7 @@ namespace MessagePack.Internal + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw8(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(8); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -528,13 +528,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 0) = *(long*)(pSrc + 0); + } + +- writer.Advance(src.Length); ++ writer.Advance(8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw9(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(9); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -543,13 +543,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 1) = *(long*)(pSrc + 1); + } + +- writer.Advance(src.Length); ++ writer.Advance(9); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw10(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(10); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -558,13 +558,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 2) = *(long*)(pSrc + 2); + } + +- writer.Advance(src.Length); ++ writer.Advance(10); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw11(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(11); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -573,13 +573,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 3) = *(long*)(pSrc + 3); + } + +- writer.Advance(src.Length); ++ writer.Advance(11); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw12(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(12); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -588,13 +588,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 4) = *(long*)(pSrc + 4); + } + +- writer.Advance(src.Length); ++ writer.Advance(12); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw13(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(13); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -603,13 +603,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 5) = *(long*)(pSrc + 5); + } + +- writer.Advance(src.Length); ++ writer.Advance(13); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw14(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(14); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -618,13 +618,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 6) = *(long*)(pSrc + 6); + } + +- writer.Advance(src.Length); ++ writer.Advance(14); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw15(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(15); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -633,13 +633,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 7) = *(long*)(pSrc + 7); + } + +- writer.Advance(src.Length); ++ writer.Advance(15); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw16(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(16); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -648,13 +648,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 8) = *(long*)(pSrc + 8); + } + +- writer.Advance(src.Length); ++ writer.Advance(16); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw17(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(17); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -664,13 +664,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 9) = *(long*)(pSrc + 9); + } + +- writer.Advance(src.Length); ++ writer.Advance(17); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw18(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(18); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -680,13 +680,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 10) = *(long*)(pSrc + 10); + } + +- writer.Advance(src.Length); ++ writer.Advance(18); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw19(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(19); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -696,13 +696,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 11) = *(long*)(pSrc + 11); + } + +- writer.Advance(src.Length); ++ writer.Advance(19); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw20(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(20); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -712,13 +712,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 12) = *(long*)(pSrc + 12); + } + +- writer.Advance(src.Length); ++ writer.Advance(20); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw21(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(21); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -728,13 +728,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 13) = *(long*)(pSrc + 13); + } + +- writer.Advance(src.Length); ++ writer.Advance(21); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw22(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(22); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -744,13 +744,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 14) = *(long*)(pSrc + 14); + } + +- writer.Advance(src.Length); ++ writer.Advance(22); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw23(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(23); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -760,13 +760,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 15) = *(long*)(pSrc + 15); + } + +- writer.Advance(src.Length); ++ writer.Advance(23); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw24(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(24); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -776,13 +776,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 16) = *(long*)(pSrc + 16); + } + +- writer.Advance(src.Length); ++ writer.Advance(24); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw25(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(25); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -793,13 +793,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 17) = *(long*)(pSrc + 17); + } + +- writer.Advance(src.Length); ++ writer.Advance(25); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw26(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(26); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -810,13 +810,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 18) = *(long*)(pSrc + 18); + } + +- writer.Advance(src.Length); ++ writer.Advance(26); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw27(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(27); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -827,13 +827,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 19) = *(long*)(pSrc + 19); + } + +- writer.Advance(src.Length); ++ writer.Advance(27); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw28(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(28); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -844,13 +844,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 20) = *(long*)(pSrc + 20); + } + +- writer.Advance(src.Length); ++ writer.Advance(28); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw29(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(29); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -861,13 +861,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 21) = *(long*)(pSrc + 21); + } + +- writer.Advance(src.Length); ++ writer.Advance(29); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw30(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(30); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -878,13 +878,13 @@ namespace MessagePack.Internal + *(long*)(pDst + 22) = *(long*)(pSrc + 22); + } + +- writer.Advance(src.Length); ++ writer.Advance(30); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw31(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(31); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -895,7 +895,7 @@ namespace MessagePack.Internal + *(long*)(pDst + 23) = *(long*)(pSrc + 23); + } + +- writer.Advance(src.Length); ++ writer.Advance(31); + } + } + } +diff --git a/src/MessagePack/Internal/UnsafeMemory.tt b/src/MessagePack/Internal/UnsafeMemory.tt +index cace009d..6d189170 100644 +--- a/src/MessagePack/Internal/UnsafeMemory.tt ++++ b/src/MessagePack/Internal/UnsafeMemory.tt +@@ -28,7 +28,7 @@ namespace MessagePack.Internal + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw<#= i #>(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(<#= i #>); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -41,7 +41,7 @@ namespace MessagePack.Internal + <# } #> + } + +- writer.Advance(src.Length); ++ writer.Advance(<#= i #>); + } + <# } #> + } +@@ -52,7 +52,7 @@ namespace MessagePack.Internal + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteRaw<#= i #>(ref MessagePackWriter writer, ReadOnlySpan src) + { +- Span dst = writer.GetSpan(src.Length); ++ Span dst = writer.GetSpan(<#= i #>); + + fixed (byte* pSrc = &src[0]) + fixed (byte* pDst = &dst[0]) +@@ -65,7 +65,7 @@ namespace MessagePack.Internal + <# } #> + } + +- writer.Advance(src.Length); ++ writer.Advance(<#= i #>); + } + <# } #> + } +diff --git a/src/MessagePack/net472/PublicAPI.Shipped.txt b/src/MessagePack/net472/PublicAPI.Shipped.txt +index 2893efd8..ad5a0623 100644 +--- a/src/MessagePack/net472/PublicAPI.Shipped.txt ++++ b/src/MessagePack/net472/PublicAPI.Shipped.txt +@@ -1177,9 +1177,12 @@ MessagePack.Formatters.StringInterningFormatter + MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> string? + MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string? value, MessagePack.MessagePackSerializerOptions! options) -> void + MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void ++MessagePack.MessagePackSecurity.MaximumDecompressedSize.get -> int ++MessagePack.MessagePackSecurity.WithMaximumDecompressedSize(int maximumDecompressedSize) -> MessagePack.MessagePackSecurity! + MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int + MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int + MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions! + MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions! + static MessagePack.MessagePackWriter.GetEncodedLength(long value) -> int + static MessagePack.MessagePackWriter.GetEncodedLength(ulong value) -> int ++virtual MessagePack.MessagePackSerializerOptions.ThrowIfDeserializingTypeIsDisallowedCore(System.Type! type) -> void +diff --git a/src/MessagePack/net6.0/PublicAPI.Shipped.txt b/src/MessagePack/net6.0/PublicAPI.Shipped.txt +index 2c67e946..cc4cfc41 100644 +--- a/src/MessagePack/net6.0/PublicAPI.Shipped.txt ++++ b/src/MessagePack/net6.0/PublicAPI.Shipped.txt +@@ -1189,6 +1189,8 @@ MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> vo + MessagePack.Formatters.TimeOnlyFormatter + MessagePack.Formatters.TimeOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.TimeOnly + MessagePack.Formatters.TimeOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.TimeOnly value, MessagePack.MessagePackSerializerOptions! options) -> void ++MessagePack.MessagePackSecurity.MaximumDecompressedSize.get -> int ++MessagePack.MessagePackSecurity.WithMaximumDecompressedSize(int maximumDecompressedSize) -> MessagePack.MessagePackSecurity! + MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int + MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int + MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions! +@@ -1197,3 +1199,4 @@ static MessagePack.MessagePackWriter.GetEncodedLength(long value) -> int + static MessagePack.MessagePackWriter.GetEncodedLength(ulong value) -> int + static readonly MessagePack.Formatters.DateOnlyFormatter.Instance -> MessagePack.Formatters.DateOnlyFormatter! + static readonly MessagePack.Formatters.TimeOnlyFormatter.Instance -> MessagePack.Formatters.TimeOnlyFormatter! ++virtual MessagePack.MessagePackSerializerOptions.ThrowIfDeserializingTypeIsDisallowedCore(System.Type! type) -> void +diff --git a/src/MessagePack/netstandard2.0/PublicAPI.Shipped.txt b/src/MessagePack/netstandard2.0/PublicAPI.Shipped.txt +index 2893efd8..ad5a0623 100644 +--- a/src/MessagePack/netstandard2.0/PublicAPI.Shipped.txt ++++ b/src/MessagePack/netstandard2.0/PublicAPI.Shipped.txt +@@ -1177,9 +1177,12 @@ MessagePack.Formatters.StringInterningFormatter + MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> string? + MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string? value, MessagePack.MessagePackSerializerOptions! options) -> void + MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void ++MessagePack.MessagePackSecurity.MaximumDecompressedSize.get -> int ++MessagePack.MessagePackSecurity.WithMaximumDecompressedSize(int maximumDecompressedSize) -> MessagePack.MessagePackSecurity! + MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int + MessagePack.MessagePackSerializerOptions.SuggestedContiguousMemorySize.get -> int + MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions! + MessagePack.MessagePackSerializerOptions.WithSuggestedContiguousMemorySize(int suggestedContiguousMemorySize) -> MessagePack.MessagePackSerializerOptions! + static MessagePack.MessagePackWriter.GetEncodedLength(long value) -> int + static MessagePack.MessagePackWriter.GetEncodedLength(ulong value) -> int ++virtual MessagePack.MessagePackSerializerOptions.ThrowIfDeserializingTypeIsDisallowedCore(System.Type! type) -> void +diff --git a/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs b/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs +index 96b54b31..ed2219b1 100644 +--- a/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs ++++ b/tests/MessagePack.AspNetCoreMvcFormatter.Tests/AspNetCoreMvcFormatterTest.cs +@@ -2,6 +2,7 @@ + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + + using System; ++using System.Collections.Generic; + using System.IO; + using System.Text; + using System.Threading; +@@ -195,6 +196,34 @@ namespace MessagePack.Tests.ExtensionTests + inputFormatter.SupportedMediaTypes.Is(MsgPackContentType); + } + ++ [Fact] ++ public async Task MessagePackInputFormatterDefaultsToUntrustedData() ++ { ++ const long value1 = 0x100000001; ++ const long value2 = 0x200000002; ++ ++ var messagePackBinary = MessagePackSerializer.Serialize(new Dictionary ++ { ++ [value1] = 1, ++ [value2] = 2, ++ }); ++ ++ var httpContext = new DefaultHttpContext(); ++ httpContext.Features.Set(new TestResponseFeature()); ++ httpContext.Request.Body = new NonSeekableReadStream(messagePackBinary); ++ httpContext.Request.ContentType = MsgPackContentType; ++ ++ InputFormatterContext inputFormatterContext = this.CreateInputFormatterContext(typeof(Dictionary), httpContext); ++ var inputFormatter = new MessagePackInputFormatter(); ++ ++ InputFormatterResult result = await inputFormatter.ReadAsync(inputFormatterContext); ++ ++ Assert.False(result.HasError); ++ var dictionary = Assert.IsType>(result.Model); ++ Assert.Equal(EqualityComparer.Default.GetHashCode(value1), EqualityComparer.Default.GetHashCode(value2)); ++ Assert.NotEqual(dictionary.Comparer.GetHashCode(value1), dictionary.Comparer.GetHashCode(value2)); ++ } ++ + /// + /// JsonOutputFormatterTests.cs#L453. + /// +diff --git a/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs b/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs +index 7e35b244..df5b1abc 100644 +--- a/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs ++++ b/tests/MessagePack.Tests/ExtensionTests/UnityShimTest.cs +@@ -94,6 +94,18 @@ namespace MessagePack.Tests.ExtensionTests + EnsureSpecCompatibility(data.Array); + } + ++ [Fact] ++ [Trait("CWE", "789")] ++ public void BlitRejectsByteLengthThatExceedsExtensionBody() ++ { ++ MessagePackSerializerOptions options = MessagePackSerializerOptions.Standard.WithResolver(new WithUnityBlitResolver()); ++ byte[] payload = { 0xC7, 0x06, unchecked((byte)ThisLibraryExtensionTypeCodes.UnityInt), 0xCE, 0x00, 0x00, 0x00, 0x08, 0xC3 }; ++ ++ var ex = Assert.Throws(() => MessagePackSerializer.Deserialize(payload, options)); ++ var inner = Assert.IsType(ex.InnerException); ++ Assert.Contains("Invalid Unity blit extension length", inner.Message); ++ } ++ + public class WithUnityBlitResolver : IFormatterResolver + { + public IMessagePackFormatter GetFormatter() +diff --git a/version.json b/version.json +index d711c9c7..40602d3e 100644 +--- a/version.json ++++ b/version.json +@@ -1,6 +1,7 @@ + { + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "2.5", ++ "versionHeightOffset": "95", + "publicReleaseRefSpec": [ + "^refs/heads/master$", + "^refs/heads/v1\\.x$", diff --git a/src/submodules/upstream-patch.diff b/src/submodules/upstream-patch.diff new file mode 100644 index 000000000000..74ff861ca64a --- /dev/null +++ b/src/submodules/upstream-patch.diff @@ -0,0 +1,25892 @@ +diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile +new file mode 100644 +index 00000000..6ed7f6e7 +--- /dev/null ++++ b/.devcontainer/Dockerfile +@@ -0,0 +1,14 @@ ++# Refer to https://hub.docker.com/_/microsoft-dotnet-sdk for available versions ++FROM mcr.microsoft.com/dotnet/sdk:8.0.300-jammy ++ ++# Installing mono makes `dotnet test` work without errors even for net472. ++# But installing it takes a long time, so it's excluded by default. ++#RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF ++#RUN echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list ++#RUN apt-get update ++#RUN DEBIAN_FRONTEND=noninteractive apt-get install -y mono-devel ++ ++# Clear the NUGET_XMLDOC_MODE env var so xml api doc files get unpacked, allowing a rich experience in Intellisense. ++# See https://github.com/dotnet/dotnet-docker/issues/2790 for a discussion on this, where the prioritized use case ++# was *not* devcontainers, sadly. ++ENV NUGET_XMLDOC_MODE= +diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json +new file mode 100644 +index 00000000..f4e3b31a +--- /dev/null ++++ b/.devcontainer/devcontainer.json +@@ -0,0 +1,20 @@ ++{ ++ "name": "Dev space", ++ "dockerFile": "Dockerfile", ++ "settings": { ++ "terminal.integrated.shell.linux": "/usr/bin/pwsh" ++ }, ++ "postCreateCommand": "./init.ps1 -InstallLocality machine", ++ "extensions": [ ++ "ms-azure-devops.azure-pipelines", ++ "ms-dotnettools.csharp", ++ "k--kato.docomment", ++ "editorconfig.editorconfig", ++ "pflannery.vscode-versionlens", ++ "davidanson.vscode-markdownlint", ++ "dotjoshjohnson.xml", ++ "ms-vscode-remote.remote-containers", ++ "ms-azuretools.vscode-docker", ++ "ms-vscode.powershell" ++ ] ++} +diff --git a/.editorconfig b/.editorconfig +index c1f1e313..c99b5fb7 100644 +--- a/.editorconfig ++++ b/.editorconfig +@@ -1,4 +1,4 @@ +-# EditorConfig is awesome:http://EditorConfig.org ++# EditorConfig is awesome:http://EditorConfig.org + + # top-most EditorConfig file + root = true +@@ -6,8 +6,6 @@ root = true + # Don't use tabs for indentation. + [*] + indent_style = space +-insert_final_newline = true +-trim_trailing_whitespace = true + + # (Please don't specify an indent_size here; that has too many unintended consequences.) + +@@ -18,18 +16,21 @@ indent_style = space + # Code files + [*.{cs,csx,vb,vbx,h,cpp,idl}] + indent_size = 4 ++insert_final_newline = true ++trim_trailing_whitespace = true + +-# Xml project files +-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] ++# MSBuild project files ++[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,msbuildproj,props,targets}] + indent_size = 2 + + # Xml config files +-[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] ++[*.{ruleset,config,nuspec,resx,vsixmanifest,vsct,runsettings}] + indent_size = 2 + + # JSON files + [*.json] + indent_size = 2 ++indent_style = space + + # Dotnet code style settings: + [*.{cs,vb}] +@@ -167,3 +168,71 @@ csharp_new_line_before_members_in_anonymous_types = true + + # Blocks are allowed + csharp_prefer_braces = true:silent ++ ++dotnet_diagnostic.RS0041.severity = suggestion ++ ++# SA1130: Use lambda syntax ++dotnet_diagnostic.SA1130.severity = silent ++ ++# IDE1006: Naming Styles - StyleCop handles these for us ++dotnet_diagnostic.IDE1006.severity = none ++ ++dotnet_diagnostic.DOC100.severity = silent ++dotnet_diagnostic.DOC104.severity = warning ++dotnet_diagnostic.DOC105.severity = warning ++dotnet_diagnostic.DOC106.severity = warning ++dotnet_diagnostic.DOC107.severity = warning ++dotnet_diagnostic.DOC108.severity = warning ++dotnet_diagnostic.DOC200.severity = warning ++dotnet_diagnostic.DOC202.severity = warning ++ ++# CS1591: Missing XML comment for publicly visible type or member ++dotnet_diagnostic.CS1591.severity = suggestion ++ ++# SA1101: Prefix local calls with this ++dotnet_diagnostic.SA1101.severity = suggestion ++ ++# SA120x: Element ordering ++dotnet_diagnostic.SA1201.severity = silent ++dotnet_diagnostic.SA1202.severity = silent ++dotnet_diagnostic.SA1204.severity = silent ++dotnet_diagnostic.SA1214.severity = silent ++ ++# SA1121: Use built-in type alias ++dotnet_diagnostic.SA1121.severity = suggestion ++ ++# SA1124: Do not use regions ++dotnet_diagnostic.SA1124.severity = suggestion ++ ++# SA1133: Each attribute should be placed in its own set of square brackets ++dotnet_diagnostic.SA1133.severity = silent ++ ++# SA1134: Each attribute should be placed on its own line of code ++dotnet_diagnostic.SA1134.severity = silent ++ ++# SA1139: Use literal syntax instead of casting ++dotnet_diagnostic.SA1139.severity = suggestion ++ ++# SA1600: Elements should be documented ++dotnet_diagnostic.SA1600.severity = silent ++ ++# SA1601: Partial elements should be documented ++dotnet_diagnostic.SA1601.severity = silent ++ ++# SA1602: Enumeration items should be documented ++dotnet_diagnostic.SA1602.severity = silent ++ ++# SA1601: Element documentation should have summary ++dotnet_diagnostic.SA1604.severity = suggestion ++ ++# SA1615: The documentation for parameter is missing ++dotnet_diagnostic.SA1611.severity = suggestion ++ ++# SA1615: Element return value should be documented ++dotnet_diagnostic.SA1615.severity = suggestion ++ ++# SA1618: Document type parameters ++dotnet_diagnostic.SA1618.severity = suggestion ++ ++[*.sln] ++indent_style = tab +diff --git a/.gitattributes b/.gitattributes +index f84f11f2..1f35e683 100644 +--- a/.gitattributes ++++ b/.gitattributes +@@ -2,7 +2,13 @@ + # Set default behavior to automatically normalize line endings. + ############################################################################### + * text=auto +-*.sh text eol=lf ++ ++# Ensure shell scripts use LF line endings (linux only accepts LF) ++*.sh eol=lf ++*.ps1 eol=lf ++ ++# The macOS codesign tool is extremely picky, and requires LF line endings. ++*.plist eol=lf + + ############################################################################### + # Set default behavior for command prompt diff. +@@ -18,7 +24,7 @@ + # + # Merging from the command prompt will add diff markers to the files if there + # are conflicts (Merging from VS is not affected by the settings below, in VS +-# the diff markers are never inserted). Diff markers may cause the following ++# the diff markers are never inserted). Diff markers may cause the following + # file extensions to fail to load in VS. An alternative would be to treat + # these files as binary and thus will always conflict and require user + # intervention with every merge. To do so, just uncomment the entries below +@@ -47,9 +53,9 @@ + + ############################################################################### + # diff behavior for common document formats +-# ++# + # Convert binary document formats to text before diffing them. This feature +-# is only available from the command line. Turn it on by uncommenting the ++# is only available from the command line. Turn it on by uncommenting the + # entries below. + ############################################################################### + #*.doc diff=astextplain +@@ -62,4 +68,3 @@ + #*.PDF diff=astextplain + #*.rtf diff=astextplain + #*.RTF diff=astextplain +- +diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml +new file mode 100644 +index 00000000..74a8d180 +--- /dev/null ++++ b/.github/FUNDING.yml +@@ -0,0 +1,5 @@ ++# These are supported funding model platforms ++ ++github: ++- neuecc ++- aarnott +diff --git a/.github/dependabot.yml b/.github/dependabot.yml +new file mode 100644 +index 00000000..7150f8e0 +--- /dev/null ++++ b/.github/dependabot.yml +@@ -0,0 +1,12 @@ ++# Please see the documentation for all configuration options: ++# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates ++ ++version: 2 ++updates: ++- package-ecosystem: nuget ++ target-branch: develop ++ directory: / ++ schedule: ++ interval: monthly ++ ignore: ++ - dependency-name: MessagePack +diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml +index e625d116..27e7f9cf 100644 +--- a/.github/workflows/stale.yml ++++ b/.github/workflows/stale.yml +@@ -3,6 +3,7 @@ name: Mark stale issues and pull requests + on: + schedule: + - cron: "0 0 * * *" ++ workflow_dispatch: + + jobs: + stale: +@@ -10,14 +11,16 @@ jobs: + runs-on: ubuntu-latest + + steps: +- - uses: actions/stale@v1 ++ - uses: actions/stale@v5.2.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + days-before-stale: 90 + days-before-close: 5 + stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.' + stale-issue-label: 'no-issue-activity' +- exempt-issue-label: 'awaiting-approval' ++ exempt-issue-labels: awaiting-approval + stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.' + stale-pr-label: 'no-pr-activity' +- exempt-pr-label: 'awaiting-approval' ++ exempt-pr-labels: awaiting-approval ++ close-issue-reason: not_planned ++ +diff --git a/.github/workflows/unity.yml b/.github/workflows/unity.yml +new file mode 100644 +index 00000000..2bf7bcba +--- /dev/null ++++ b/.github/workflows/unity.yml +@@ -0,0 +1,48 @@ ++name: unity ++ ++on: ++ workflow_dispatch: ++ push: ++ branches: ++ - master ++ - develop ++ pull_request: ++ branches: ++ - master ++ - develop ++ ++jobs: ++ unity: ++ runs-on: ubuntu-latest ++ timeout-minutes: 15 ++ steps: ++ - uses: actions/checkout@v3 ++ with: ++ fetch-depth: 0 # avoid shallow clone so nbgv can do its work. ++ - uses: actions/setup-dotnet@v3 ++ with: ++ global-json-file: global.json ++ - name: copy assets ++ run: src/MessagePack.UnityClient/copy_assets.sh ++ - uses: actions/cache@v3 ++ with: ++ path: src/MessagePack.UnityClient/Library ++ key: MessagePack-ubuntu ++ - name: build ++ uses: game-ci/unity-builder@v2 ++ env: ++ UNITY_LICENSE: ${{ secrets.UNITY_LICENSE_2021 }} ++ with: ++ projectPath: src/MessagePack.UnityClient ++ unityVersion: 2021.3.11f1 ++ targetPlatform: StandaloneLinux64 ++ buildMethod: PackageExporter.Export ++ customParameters: /headless /ScriptBackend mono ++ versioning: None ++ - uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files ++ with: ++ directory: src/MessagePack.UnityClient ++ - uses: actions/upload-artifact@v3 ++ with: ++ name: MessagePack.unitypackage ++ path: bin/*.unitypackage +diff --git a/.gitignore b/.gitignore +index e2bdc90a..7209c6cb 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,16 +1,23 @@ + ## Ignore Visual Studio temporary files, build results, and + ## files generated by popular Visual Studio add-ons. ++## ++## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + + # User-specific files ++*.rsuser + *.suo + *.user + *.userosscache + *.sln.docstates ++*.lutconfig + launchSettings.json + + # User-specific files (MonoDevelop/Xamarin Studio) + *.userprefs + ++# Mono auto generated files ++mono_crash.* ++ + # Build results + [Dd]ebug/ + [Dd]ebugPublic/ +@@ -18,46 +25,66 @@ launchSettings.json + [Rr]eleases/ + x64/ + x86/ ++[Aa][Rr][Mm]/ ++[Aa][Rr][Mm]64/ + bld/ + [Bb]in/ + [Oo]bj/ + [Ll]og/ + +-# Visual Studio 2015 cache/options directory ++# Visual Studio 2015/2017 cache/options directory + .vs/ + # Uncomment if you have tasks that create the project's static files in wwwroot + #wwwroot/ + ++# Jetbrains Rider cache directory ++.idea/ ++ ++# Visual Studio 2017 auto generated files ++Generated\ Files/ ++ + # MSTest test Results + [Tt]est[Rr]esult*/ + [Bb]uild[Ll]og.* + +-# NUNIT ++# NUnit + *.VisualState.xml + TestResult.xml ++nunit-*.xml + + # Build Results of an ATL Project + [Dd]ebugPS/ + [Rr]eleasePS/ + dlldata.c + ++# Benchmark Results ++BenchmarkDotNet.Artifacts/ ++ ++# StyleCop ++StyleCopReport.xml ++ ++# Files built by Visual Studio + *_i.c + *_p.c +-*_i.h ++*_h.h + *.ilk + *.obj ++*.iobj + *.pch + *.pdb ++*.ipdb + *.pgc + *.pgd ++*.rsp ++!Directory.Build.rsp + *.sbr + *.tlb + *.tli + *.tlh + *.tmp + *.tmp_proj ++*_wpftmp.csproj + *.log +-*.binlog + *.vspscc + *.vssscc + .builds +@@ -85,6 +112,9 @@ ipch/ + *.vspx + *.sap + ++# Visual Studio Trace Files ++*.e2e ++ + # TFS 2012 Local Workspace + $tf/ + +@@ -105,6 +135,15 @@ _TeamCity* + # DotCover is a Code Coverage Tool + *.dotCover + ++# AxoCover is a Code Coverage Tool ++.axoCover/* ++!.axoCover/settings.json ++ ++# Visual Studio code coverage results ++*.coverage ++*.coveragexml ++/coveragereport/ ++ + # NCrunch + _NCrunch_* + .*crunch*.local.xml +@@ -136,9 +175,9 @@ publish/ + # Publish Web Output + *.[Pp]ublish.xml + *.azurePubxml +-# TODO: Comment the next line if you want to checkin your web deploy settings ++# Note: Comment the next line if you want to checkin your web deploy settings, + # but database connection strings (with potential passwords) will be unencrypted +-#*.pubxml ++*.pubxml + *.publishproj + + # Microsoft Azure Web App publish settings. Comment the next line if you want to +@@ -148,13 +187,15 @@ PublishScripts/ + + # NuGet Packages + *.nupkg ++# NuGet Symbol Packages ++*.snupkg + # The packages folder can be ignored because of Package Restore +-**/packages/* ++**/[Pp]ackages/* + # except build/, which is used as an MSBuild target. +-!**/packages/build/ ++!**/[Pp]ackages/build/ + # Uncomment if necessary however generally it will be regenerated when needed +-#!**/packages/repositories.config +-# NuGet v3's project.json files produces more ignoreable files ++#!**/[Pp]ackages/repositories.config ++# NuGet v3's project.json files produces more ignorable files + *.nuget.props + *.nuget.targets + +@@ -171,12 +212,15 @@ AppPackages/ + BundleArtifacts/ + Package.StoreAssociation.xml + _pkginfo.txt ++*.appx ++*.appxbundle ++*.appxupload + + # Visual Studio cache files + # files ending in .cache can be ignored + *.[Cc]ache + # but keep track of directories ending in .cache +-!*.[Cc]ache/ ++!?*.[Cc]ache/ + + # Others + ClientBin/ +@@ -187,9 +231,12 @@ ClientBin/ + *.jfm + *.pfx + *.publishsettings +-node_modules/ + orleans.codegen.cs + ++# Including strong name files can present a security risk ++# (https://github.com/github/gitignore/pull/2483#issue-259490424) ++#*.snk ++ + # Since there are multiple workflows, uncomment next line to ignore bower_components + # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) + #bower_components/ +@@ -204,15 +251,22 @@ _UpgradeReport_Files/ + Backup*/ + UpgradeLog*.XML + UpgradeLog*.htm ++ServiceFabricBackup/ ++*.rptproj.bak + + # SQL Server files + *.mdf + *.ldf ++*.ndf + + # Business Intelligence projects + *.rdl.data + *.bim.layout + *.bim_*.settings ++*.rptproj.rsuser ++*- [Bb]ackup.rdl ++*- [Bb]ackup ([0-9]).rdl ++*- [Bb]ackup ([0-9][0-9]).rdl + + # Microsoft Fakes + FakesAssemblies/ +@@ -222,6 +276,7 @@ FakesAssemblies/ + + # Node.js Tools for Visual Studio + .ntvs_analysis.dat ++node_modules/ + + # Visual Studio 6 build log + *.plg +@@ -229,6 +284,9 @@ FakesAssemblies/ + # Visual Studio 6 workspace options file + *.opt + ++# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) ++*.vbw ++ + # Visual Studio LightSwitch build output + **/*.HTMLClient/GeneratedArtifacts + **/*.DesktopClient/GeneratedArtifacts +@@ -244,40 +302,74 @@ paket-files/ + # FAKE - F# Make + .fake/ + +-# JetBrains Rider +-.idea/ +-*.sln.iml +- +-# CodeRush +-.cr/ ++# CodeRush personal settings ++.cr/personal + + # Python Tools for Visual Studio (PTVS) + __pycache__/ + *.pyc + ++# Cake - Uncomment if you are using it ++# tools/** ++# !tools/packages.config ++ ++# Tabs Studio ++*.tss ++ ++# Telerik's JustMock configuration file ++*.jmconfig ++ ++# BizTalk build output ++*.btp.cs ++*.btm.cs ++*.odx.cs ++*.xsd.cs ++ ++# OpenCover UI analysis results ++OpenCover/ ++ ++# Azure Stream Analytics local run output ++ASALocalRun/ ++ ++# MSBuild Binary and Structured Log ++*.binlog ++ ++# NVidia Nsight GPU debugger configuration file ++*.nvuser ++ ++# MFractors (Xamarin productivity tool) working folder ++.mfractor/ ++ ++# Local History for Visual Studio ++.localhistory/ ++ ++# BeatPulse healthcheck temp database ++healthchecksdb ++ ++# Backup folder for Package Reference Convert tool in Visual Studio 2017 ++MigrationBackup/ ++ ++# dotnet tool local install directory ++.store/ ++ ++# mac-created file to track user view preferences for a directory ++.DS_Store ++ ++# Analysis results ++*.sarif ++ + # Unity + + src/MessagePack.UnityClient/bin/* + src/MessagePack.UnityClient/Library/* + src/MessagePack.UnityClient/obj/* + src/MessagePack.UnityClient/Temp/* +- +-# Project Specified +- +-nuget/mpc.exe +-nuget/mpc.exe.config +- +-nuget/tools/* +-nuget/unity/tools/* +-nuget/unity* +-/nuget/*.unitypackage +- +-# VSCode +-.vscode/* +-!.vscode/settings.json +-!.vscode/tasks.json +-!.vscode/launch.json +-!.vscode/extensions.json ++src/MessagePack.UnityClient/UserSettings/* ++src/MessagePack.UnityClient/Assets/Packages/ + + # BenchmarkDotNet results + BenchmarkDotNet.Artifacts/ ++ ++src/MessagePack.UnityClient/.vsconfig ++ ++*.lscache +diff --git a/.vscode/extensions.json b/.vscode/extensions.json +new file mode 100644 +index 00000000..ca3a2aa9 +--- /dev/null ++++ b/.vscode/extensions.json +@@ -0,0 +1,19 @@ ++{ ++ // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. ++ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp ++ // List of extensions which should be recommended for users of this workspace. ++ "recommendations": [ ++ "ms-azure-devops.azure-pipelines", ++ "ms-dotnettools.csharp", ++ "k--kato.docomment", ++ "editorconfig.editorconfig", ++ "pflannery.vscode-versionlens", ++ "davidanson.vscode-markdownlint", ++ "dotjoshjohnson.xml", ++ "ms-vscode-remote.remote-containers", ++ "ms-azuretools.vscode-docker", ++ "tintoy.msbuild-project-tools" ++ ], ++ // List of extensions recommended by VS Code that should not be recommended for users of this workspace. ++ "unwantedRecommendations": [] ++} +diff --git a/.vscode/launch.json b/.vscode/launch.json +new file mode 100644 +index 00000000..af4bc6a3 +--- /dev/null ++++ b/.vscode/launch.json +@@ -0,0 +1,21 @@ ++{ ++ // Use IntelliSense to find out which attributes exist for C# debugging ++ // Use hover for the description of the existing attributes ++ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md ++ "version": "0.2.0", ++ "configurations": [ ++ { ++ "name": "PowerShell Launch Current File", ++ "type": "PowerShell", ++ "request": "launch", ++ "script": "${file}", ++ "cwd": "${cwd}" ++ }, ++ { ++ "name": ".NET Core Attach", ++ "type": "coreclr", ++ "request": "attach", ++ "processId": "${command:pickProcess}" ++ } ++ ] ++} +diff --git a/.vscode/settings.json b/.vscode/settings.json +new file mode 100644 +index 00000000..3ae1371c +--- /dev/null ++++ b/.vscode/settings.json +@@ -0,0 +1,8 @@ ++{ ++ "files.trimTrailingWhitespace": true, ++ "files.insertFinalNewline": true, ++ "files.trimFinalNewlines": true, ++ "omnisharp.enableEditorConfigSupport": true, ++ "omnisharp.enableImportCompletion": true, ++ "omnisharp.enableRoslynAnalyzers": true ++} +diff --git a/.vscode/tasks.json b/.vscode/tasks.json +new file mode 100644 +index 00000000..67b06180 +--- /dev/null ++++ b/.vscode/tasks.json +@@ -0,0 +1,17 @@ ++{ ++ "version": "2.0.0", ++ "tasks": [ ++ { ++ "label": "build", ++ "command": "dotnet", ++ "type": "process", ++ "args": [ ++ "build", ++ "${workspaceFolder}", ++ "/property:GenerateFullPaths=true", ++ "/consoleloggerparameters:NoSummary" ++ ], ++ "problemMatcher": "$msCompile" ++ } ++ ] ++} +diff --git a/CONTRIBUTORS b/CONTRIBUTORS +index 23975945..016f0566 100644 +--- a/CONTRIBUTORS ++++ b/CONTRIBUTORS +@@ -1,2 +1,2 @@ + Yoshifumi Kawai +-Andrew Arnott ++Andrew Arnott +diff --git a/Directory.Build.props b/Directory.Build.props +index fc7c5f0f..5c788b00 100644 +--- a/Directory.Build.props ++++ b/Directory.Build.props +@@ -1,43 +1,70 @@ + + + Debug +- $(MSBuildThisFileDirectory)obj\$(MSBuildProjectName)\ +- $(MSBuildThisFileDirectory)bin\$(MSBuildProjectName)\ +- $(MSBuildThisFileDirectory)bin\Packages\$(Configuration)\NuGet\ +- ++ $(MSBuildThisFileDirectory) ++ $(RepoRootPath)obj\$([MSBuild]::MakeRelative($(RepoRootPath), $(MSBuildProjectDirectory)))\ ++ $(RepoRootPath)bin\$(MSBuildProjectName)\ ++ $(RepoRootPath)bin\Packages\$(Configuration)\ ++ 10 ++ latest ++ true + true +- 7.3 +- $(NoWarn);CS1591 +- $(MSBuildThisFileDirectory)MessagePack.ruleset ++ true + +- false +- false +- MIT ++ ++ true ++ ++ ++ ++ false ++ ++ ++ $(MSBuildThisFileDirectory) ++ ++ ++ embedded ++ ++ https://github.com/MessagePack-CSharp/MessagePack-CSharp + neuecc,aarnott +- neuecc,aarnott + © Yoshifumi Kawai and contributors. All rights reserved. +- https://github.com/neuecc/MessagePack-CSharp +- ++ MIT + true + true +- true ++ true + snupkg + ++ + +- +- +- +- +- +- +- +- +- ++ + + ++ ++ ++ ++ ++ + + +- https://github.com/neuecc/MessagePack-CSharp/releases/tag/v$(Version) ++ $(PackageProjectUrl)/releases/tag/v$(Version) + + ++ ++ ++ false ++ true ++ ++ ++ ++ ++ false ++ false ++ false ++ false ++ + +diff --git a/Directory.Build.targets b/Directory.Build.targets +new file mode 100644 +index 00000000..ea7b6e6f +--- /dev/null ++++ b/Directory.Build.targets +@@ -0,0 +1,11 @@ ++ ++ ++ ++ false ++ ++ ++ ++ ++ ++ ++ +diff --git a/Directory.Packages.props b/Directory.Packages.props +new file mode 100644 +index 00000000..ecd5e9eb +--- /dev/null ++++ b/Directory.Packages.props +@@ -0,0 +1,75 @@ ++ ++ ++ ++ true ++ true ++ 0.13.5 ++ 3.11.0 ++ 4.4.0 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/LICENSE b/LICENSE +index 8519f42f..eebeb87e 100644 +--- a/LICENSE ++++ b/LICENSE +@@ -26,6 +26,8 @@ SOFTWARE. + + lz4net + ++BSD 2-Clause "Simplified" License ++ + Copyright (c) 2013-2017, Milosz Krajewski + + All rights reserved. +diff --git a/MessagePack.ruleset b/MessagePack.ruleset +deleted file mode 100644 +index 48d2d2be..00000000 +--- a/MessagePack.ruleset ++++ /dev/null +@@ -1,98 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +\ No newline at end of file +diff --git a/MessagePack.sln b/MessagePack.sln +index 77d762b8..2b743e8e 100644 +--- a/MessagePack.sln ++++ b/MessagePack.sln +@@ -1,9 +1,13 @@ +  + Microsoft Visual Studio Solution File, Format Version 12.00 +-# Visual Studio Version 16 +-VisualStudioVersion = 16.0.29108.181 ++# Visual Studio Version 17 ++VisualStudioVersion = 17.5.33201.384 + MinimumVisualStudioVersion = 10.0.40219.1 + Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{86309CF6-0054-4CE3-BFD3-CA0AA7DB17BC}" ++ ProjectSection(SolutionItems) = preProject ++ src\Directory.Build.props = src\Directory.Build.props ++ src\Directory.Build.targets = src\Directory.Build.targets ++ EndProjectSection + EndProject + Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MessagePack", "src\MessagePack\MessagePack.csproj", "{7ABB33EE-A2F1-492B-8DAF-5DF89F0F0B79}" + EndProject +@@ -44,9 +48,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestData2", "sandbox\TestDa + EndProject + Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{301F812B-8AEE-4DC2-8009-4510F02294AD}" + ProjectSection(SolutionItems) = preProject ++ .editorconfig = .editorconfig + azure-pipelines.yml = azure-pipelines.yml + azure-pipelines\build.yml = azure-pipelines\build.yml + Directory.Build.props = Directory.Build.props ++ Directory.Packages.props = Directory.Packages.props + global.json = global.json + nuget.config = nuget.config + stylecop.json = stylecop.json +diff --git a/README.md b/README.md +index 27af11f5..b15a0df3 100644 +--- a/README.md ++++ b/README.md +@@ -1,8 +1,8 @@ +-# MessagePack for C# (.NET, .NET Core, Unity, Xamarin) ++# MessagePack for C# (.NET Framework, .NET 6, Unity, Xamarin) + + [![NuGet](https://img.shields.io/nuget/v/MessagePack.svg)](https://www.nuget.org/packages/messagepack) + [![NuGet](https://img.shields.io/nuget/vpre/MessagePack.svg)](https://www.nuget.org/packages/messagepack) +-[![Releases](https://img.shields.io/github/release/neuecc/MessagePack-CSharp.svg)][Releases] ++[![Releases](https://img.shields.io/github/release/neuecc/MessagePack-CSharp.svg)](https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases) + + [![Join the chat at https://gitter.im/MessagePack-CSharp/Lobby](https://badges.gitter.im/MessagePack-CSharp/Lobby.svg)](https://gitter.im/MessagePack-CSharp/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Build Status](https://dev.azure.com/ils0086/MessagePack-CSharp/_apis/build/status/MessagePack-CSharp-CI?branchName=master)](https://dev.azure.com/ils0086/MessagePack-CSharp/_build/latest?definitionId=2&branchName=master) +@@ -63,12 +63,13 @@ MessagePack has a compact binary size and a full set of general purpose expressi + - [StreamJsonRpc](#streamjsonrpc) + - [How to build](#how-to-build) + - [Author Info](#author-info) ++- [Code of Conduct & .NET Foundation notice](#coc) + + ## Installation + + This library is distributed via NuGet. Special [Unity support](#unity) is available, too. + +-We target .NET Standard 2.0 with special optimizations for .NET Core 2.1+, making it compatible with most reasonably recent .NET runtimes such as Core 2.0 and later, Framework 4.6.1 and later, Mono 5.4 and later and Unity 2018.3 and later. ++We target .NET Standard 2.0 with special optimizations for .NET 6+, making it compatible with most reasonably recent .NET runtimes such as Core 2.0 and later, Framework 4.6.1 and later, Mono 5.4 and later and Unity 2018.3 and later. + The library code is pure C# (with Just-In-Time IL code generation on some platforms). + + ### NuGet packages +@@ -95,7 +96,7 @@ Install-Package MessagePack.AspNetCoreMvcFormatter + + ### Unity + +-For Unity projects, the [releases][Releases] page provides downloadable `.unitypackage` files. When using in Unity IL2CPP or Xamarin AOT environments, please carefully read the [pre-code generation section](#aot). ++For Unity projects, the [Releases](https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases) page provides downloadable `.unitypackage` files. When using in Unity IL2CPP or Xamarin AOT environments, please carefully read the [pre-code generation section](#aot). + + ### Migration notes from v1.x + +@@ -1531,7 +1532,7 @@ Within the *reserved* ranges, this library defines or implements extensions that + + Unity lowest supported version is `2018.3`, API Compatibility Level supports both `.NET 4.x` and `.NET Standard 2.0`. + +-You can install the `unitypackage` from the [releases][Releases] page. ++You can install the `unitypackage` from the [Releases](https://github.com/MessagePack-CSharp/MessagePack-CSharp/releases) page. + If your build targets .NET Framework 4.x and runs on mono, you can use it as is. + But if your build targets IL2CPP, you can not use `Dynamic***Resolver`, so it is required to use pre-code generation. Please see [pre-code generation section](#aot). + +@@ -1566,7 +1567,7 @@ The `MessagePack.UnityShims` NuGet package is for .NET server-side serialization + + If you want to share a class between Unity and a server, you can use `SharedProject` or `Reference as Link` or a glob reference (with `LinkBase`), etc. Anyway, you need to share at source-code level. This is a sample project structure using a glob reference (recommended). + +-- ServerProject(.NET 4.6/.NET Core/.NET Standard) ++- ServerProject(.NET Framework 4.6/.NET/.NET Standard) + - \[``\] + - \[MessagePack\] + - \[MessagePack.UnityShims\] +@@ -1584,7 +1585,7 @@ For all other Unity targets, AOT is required. + + If you want to avoid the upfront dynamic generation cost or you need to run on Xamarin or Unity, you need AOT code generation. `mpc` (MessagePackCompiler) is the code generator of MessagePack for C#. mpc uses [Roslyn](https://github.com/dotnet/roslyn) to analyze source code. + +-First of all, mpc requires [.NET Core 3 Runtime](https://dotnet.microsoft.com/download). The easiest way to acquire and run mpc is as a dotnet tool. ++First of all, mpc requires [.NET 6+ Runtime](https://dotnet.microsoft.com/download). The easiest way to acquire and run mpc is as a dotnet tool. + + ``` + dotnet tool install --global MessagePack.Generator +@@ -1603,11 +1604,7 @@ Once you have the tool installed, simply invoke using `dotnet mpc` within your r + + ``` + dotnet mpc --help +-``` +- +-Alternatively, you can download mpc from the [releases][Releases] page, that includes platform native binaries (that don't require a separate dotnet runtime). + +-``` + Usage: mpc [options...] + + Options: +@@ -1677,7 +1674,7 @@ In Unity, you can use MessagePack CodeGen windows at `Windows -> MessagePack -> + + ![](https://user-images.githubusercontent.com/46207/69414381-f14da400-0d55-11ea-9f8d-9af448d347dc.png) + +-Install the .NET Core runtime, install mpc (as a .NET Core Tool as described above), and execute `dotnet mpc`. Currently this tool is experimental so please tell me your opinion. ++Install the .NET runtime, install mpc (as a .NET Tool as described above), and execute `dotnet mpc`. Currently this tool is experimental so please tell me your opinion. + + In Xamarin, you can install the [the `MessagePack.MSBuild.Tasks` NuGet package](doc/msbuildtask.md) into your projects to pre-compile fast serialization code and run in environments where JIT compilation is not allowed. + +@@ -1697,16 +1694,11 @@ The StreamJsonRpc library is based on [JSON-RPC](https://www.jsonrpc.org/) and i + + See our [contributor's guide](CONTRIBUTING.md). + +-## Author Info ++## Code of Conduct + +-Yoshifumi Kawai (a.k.a. neuecc) is a software developer in Japan. +-He is the Director/CTO at Grani, Inc. +-Grani is a mobile game developer company in Japan and well known for using C#. +-He is awarding Microsoft MVP for Visual C# since 2011. +-He is known as the creator of [UniRx](https://github.com/neuecc/UniRx/) (Reactive Extensions for Unity) ++This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. ++For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). + +-* Blog: [https://medium.com/@neuecc](https://medium.com/@neuecc) (English) +-* Blog: [http://neue.cc/](http://neue.cc/) (Japanese) +-* Twitter: [https://twitter.com/neuecc](https://twitter.com/neuecc) (Japanese) ++## .NET Foundation + +-[Releases]: https://github.com/neuecc/MessagePack-CSharp/releases ++This project is supported by the [.NET Foundation](https://dotnetfoundation.org). +diff --git a/azure-pipelines.yml b/azure-pipelines.yml +index d18997c3..98cea23d 100644 +--- a/azure-pipelines.yml ++++ b/azure-pipelines.yml +@@ -1,4 +1,5 @@ + trigger: ++ batch: true + branches: + include: + - master +@@ -6,96 +7,32 @@ trigger: + - 'v?.*' + - 'validate/*' + paths: +- exclude: ["doc", "*.md"] ++ exclude: ++ - doc/ ++ - '*.md' ++ - .vscode/ ++ - .github/ ++ - azure-pipelines/release.yml ++ ++parameters: ++- name: includeMacOS ++ displayName: Build on macOS ++ type: boolean ++ default: false # macOS is often bogged down in Azure Pipelines ++- name: RunTests ++ displayName: Run tests ++ type: boolean ++ default: true + + variables: +- TreatWarningsAsErrors: true ++ MSBuildTreatWarningsAsErrors: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + BuildConfiguration: Release +- BuildPlatform: Any CPU +- +-stages: +-- stage: Build +- jobs: +- - job: Windows +- pool: +- vmImage: windows-2022 +- steps: +- - checkout: self +- clean: true +- - template: azure-pipelines/install-dependencies.yml +- +- - powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud' +- displayName: Set build number +- +- - template: azure-pipelines/build.yml +- +- - job: Unity +- pool: +- name: CustomAgents +- demands: +- - UNITYHUB_EDITORS_FOLDER_LOCATION +- - UNITYVERSION -equals 2019.1 +- steps: +- - checkout: self +- clean: true +- - template: azure-pipelines/install-dependencies.yml +- - template: azure-pipelines/build_unity.yml +- +- - job: Linux +- pool: +- vmImage: ubuntu-20.04 +- steps: +- - checkout: self +- clean: true +- - template: azure-pipelines/install-dependencies.yml +- - template: azure-pipelines/build_nonWindows.yml +- +- - job: macOS +- pool: +- vmImage: macOS-11 +- steps: +- - checkout: self +- clean: true +- - template: azure-pipelines/install-dependencies.yml +- - template: azure-pipelines/build_nonWindows.yml +- +-# This job ensures that we're running mpc regularly on the generated code that we check in. +-# It also helps exercise mpc so bugs don't go unnoticed. +- - job: codegen_diff +- pool: +- vmImage: ubuntu-20.04 +- steps: +- - checkout: self +- clean: true +- - template: azure-pipelines/install-dependencies.yml +- - pwsh: sandbox/codegen.ps1 +- displayName: Regenerate checked-in code +- - bash: | +- git add -u . # compare after applying git EOL normalization +- git diff --cached --exit-code --stat \ +- || (echo "##[error] found changed files after build. please 'azure-pipelines/codegen.ps1'" \ +- "and check in all changes" \ +- && git diff --cached \ +- && exit 1) +- displayName: Check for uncommitted changes +-- stage: Publish +- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) +- jobs: +- - job: push +- pool: +- vmImage: ubuntu-20.04 +- steps: +- - download: current +- artifact: nuget +- - task: NuGetToolInstaller@1 +- displayName: Use NuGet 5.x +- inputs: +- versionSpec: 5.x +- - task: NuGetCommand@2 +- displayName: NuGet push +- inputs: +- command: push +- packagesToPush: $(Pipeline.Workspace)/nuget/*.nupkg +- publishVstsFeed: MessagePack-CSharp/MessagePack-CI +- continueOnError: true ++ ci_feed: https://pkgs.dev.azure.com/ils0086/MessagePack-CSharp/_packaging/MessagePack-CI/nuget/v3/index.json ++ NUGET_PACKAGES: $(Agent.TempDirectory)/.nuget/packages/ ++ ++jobs: ++- template: azure-pipelines/build.yml ++ parameters: ++ includeMacOS: ${{ parameters.includeMacOS }} ++ RunTests: ${{ parameters.RunTests }} +diff --git a/azure-pipelines/Darwin.runsettings b/azure-pipelines/Darwin.runsettings +deleted file mode 100644 +index b444e819..00000000 +--- a/azure-pipelines/Darwin.runsettings ++++ /dev/null +@@ -1 +0,0 @@ +- +diff --git a/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 b/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 +new file mode 100644 +index 00000000..391e5713 +--- /dev/null ++++ b/azure-pipelines/Get-ArtifactsStagingDirectory.ps1 +@@ -0,0 +1,15 @@ ++Param( ++ [switch]$CleanIfLocal ++) ++if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { ++ $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY ++} elseif ($env:RUNNER_TEMP) { ++ $ArtifactStagingFolder = "$env:RUNNER_TEMP\_artifacts" ++} else { ++ $ArtifactStagingFolder = [System.IO.Path]::GetFullPath("$PSScriptRoot/../obj/_artifacts") ++ if ($CleanIfLocal -and (Test-Path $ArtifactStagingFolder)) { ++ Remove-Item $ArtifactStagingFolder -Recurse -Force ++ } ++} ++ ++$ArtifactStagingFolder +diff --git a/azure-pipelines/Get-CodeCovTool.ps1 b/azure-pipelines/Get-CodeCovTool.ps1 +new file mode 100644 +index 00000000..ca580b4d +--- /dev/null ++++ b/azure-pipelines/Get-CodeCovTool.ps1 +@@ -0,0 +1,86 @@ ++<# ++.SYNOPSIS ++ Downloads the CodeCov.io uploader tool and returns the path to it. ++.PARAMETER AllowSkipVerify ++ Allows skipping signature verification of the downloaded tool if gpg is not installed. ++#> ++[CmdletBinding()] ++Param( ++ [switch]$AllowSkipVerify ++) ++ ++if ($IsMacOS) { ++ $codeCovUrl = "https://uploader.codecov.io/latest/macos/codecov" ++ $toolName = 'codecov' ++} ++elseif ($IsLinux) { ++ $codeCovUrl = "https://uploader.codecov.io/latest/linux/codecov" ++ $toolName = 'codecov' ++} ++else { ++ $codeCovUrl = "https://uploader.codecov.io/latest/windows/codecov.exe" ++ $toolName = 'codecov.exe' ++} ++ ++$shaSuffix = ".SHA256SUM" ++$sigSuffix = $shaSuffix + ".sig" ++ ++Function Get-FileFromWeb([Uri]$Uri, $OutDir) { ++ $OutFile = Join-Path $OutDir $Uri.Segments[-1] ++ if (!(Test-Path $OutFile)) { ++ Write-Verbose "Downloading $Uri..." ++ if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null } ++ try { ++ (New-Object System.Net.WebClient).DownloadFile($Uri, $OutFile) ++ } finally { ++ # This try/finally causes the script to abort ++ } ++ } ++ ++ $OutFile ++} ++ ++$toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" ++$binaryToolsPath = Join-Path $toolsPath codecov ++$testingPath = Join-Path $binaryToolsPath unverified ++$finalToolPath = Join-Path $binaryToolsPath $toolName ++ ++if (!(Test-Path $finalToolPath)) { ++ if (Test-Path $testingPath) { ++ Remove-Item -Recurse -Force $testingPath # ensure we download all matching files ++ } ++ $tool = Get-FileFromWeb $codeCovUrl $testingPath ++ $sha = Get-FileFromWeb "$codeCovUrl$shaSuffix" $testingPath ++ $sig = Get-FileFromWeb "$codeCovUrl$sigSuffix" $testingPath ++ $key = Get-FileFromWeb https://keybase.io/codecovsecurity/pgp_keys.asc $testingPath ++ ++ if ((Get-Command gpg -ErrorAction SilentlyContinue)) { ++ Write-Host "Importing codecov key" -ForegroundColor Yellow ++ gpg --import $key ++ Write-Host "Verifying signature on codecov hash" -ForegroundColor Yellow ++ gpg --verify $sig $sha ++ } else { ++ if ($AllowSkipVerify) { ++ Write-Warning "gpg not found. Unable to verify hash signature." ++ } else { ++ throw "gpg not found. Unable to verify hash signature. Install gpg or add -AllowSkipVerify to override." ++ } ++ } ++ ++ Write-Host "Verifying hash on downloaded tool" -ForegroundColor Yellow ++ $actualHash = (Get-FileHash -Path $tool -Algorithm SHA256).Hash ++ $expectedHash = (Get-Content $sha).Split()[0] ++ if ($actualHash -ne $expectedHash) { ++ # Validation failed. Delete the tool so we can't execute it. ++ #Remove-Item $codeCovPath ++ throw "codecov uploader tool failed signature validation." ++ } ++ ++ Copy-Item $tool $finalToolPath ++ ++ if ($IsMacOS -or $IsLinux) { ++ chmod u+x $finalToolPath ++ } ++} ++ ++return $finalToolPath +diff --git a/azure-pipelines/Get-NuGetTool.ps1 b/azure-pipelines/Get-NuGetTool.ps1 +index 4431adb9..3097c873 100644 +--- a/azure-pipelines/Get-NuGetTool.ps1 ++++ b/azure-pipelines/Get-NuGetTool.ps1 +@@ -6,7 +6,7 @@ + #> + Param( + [Parameter()] +- [string]$NuGetVersion='5.2.0' ++ [string]$NuGetVersion='6.4.0' + ) + + $toolsPath = & "$PSScriptRoot\Get-TempToolsPath.ps1" +diff --git a/azure-pipelines/Get-SymbolFiles.ps1 b/azure-pipelines/Get-SymbolFiles.ps1 +new file mode 100644 +index 00000000..0ce229fc +--- /dev/null ++++ b/azure-pipelines/Get-SymbolFiles.ps1 +@@ -0,0 +1,61 @@ ++<# ++.SYNOPSIS ++ Collect the list of PDBs built in this repo. ++.PARAMETER Path ++ The directory to recursively search for PDBs. ++.PARAMETER Tests ++ A switch indicating to find PDBs only for test binaries instead of only for shipping shipping binaries. ++#> ++[CmdletBinding()] ++param ( ++ [parameter(Mandatory=$true)] ++ [string]$Path, ++ [switch]$Tests ++) ++ ++$ActivityName = "Collecting symbols from $Path" ++Write-Progress -Activity $ActivityName -CurrentOperation "Discovery PDB files" ++$PDBs = Get-ChildItem -rec "$Path/*.pdb" ++ ++# Filter PDBs to product OR test related. ++$testregex = "unittest|tests|\.test\." ++ ++Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" ++$PDBsByHash = @{} ++$i = 0 ++$PDBs |% { ++ Write-Progress -Activity $ActivityName -CurrentOperation "De-duplicating symbols" -PercentComplete (100 * $i / $PDBs.Length) ++ $hash = Get-FileHash $_ ++ $i++ ++ Add-Member -InputObject $_ -MemberType NoteProperty -Name Hash -Value $hash.Hash ++ Write-Output $_ ++} | Sort-Object CreationTime |% { ++ # De-dupe based on hash. Prefer the first match so we take the first built copy. ++ if (-not $PDBsByHash.ContainsKey($_.Hash)) { ++ $PDBsByHash.Add($_.Hash, $_.FullName) ++ Write-Output $_ ++ } ++} |? { ++ if ($Tests) { ++ $_.FullName -match $testregex ++ } else { ++ $_.FullName -notmatch $testregex ++ } ++} |% { ++ # Collect the DLLs/EXEs as well. ++ $dllPath = "$($_.Directory)/$($_.BaseName).dll" ++ $exePath = "$($_.Directory)/$($_.BaseName).exe" ++ if (Test-Path $dllPath) { ++ $BinaryImagePath = $dllPath ++ } elseif (Test-Path $exePath) { ++ $BinaryImagePath = $exePath ++ } else { ++ Write-Warning "`"$_`" found with no matching binary file." ++ $BinaryImagePath = $null ++ } ++ ++ if ($BinaryImagePath) { ++ Write-Output $BinaryImagePath ++ Write-Output $_.FullName ++ } ++} +diff --git a/azure-pipelines/Linux.runsettings b/azure-pipelines/Linux.runsettings +deleted file mode 100644 +index b444e819..00000000 +--- a/azure-pipelines/Linux.runsettings ++++ /dev/null +@@ -1 +0,0 @@ +- +diff --git a/azure-pipelines/Merge-CodeCoverage.ps1 b/azure-pipelines/Merge-CodeCoverage.ps1 +new file mode 100644 +index 00000000..02ff12b0 +--- /dev/null ++++ b/azure-pipelines/Merge-CodeCoverage.ps1 +@@ -0,0 +1,51 @@ ++#!/usr/bin/env pwsh ++ ++<# ++.SYNOPSIS ++ Merges code coverage reports. ++.PARAMETER Path ++ The path(s) to search for Cobertura code coverage reports. ++.PARAMETER Format ++ The format for the merged result. The default is Cobertura ++.PARAMETER OutputDir ++ The directory the merged result will be written to. The default is `coveragereport` in the root of this repo. ++#> ++[CmdletBinding()] ++Param( ++ [Parameter(Mandatory=$true)] ++ [string[]]$Path, ++ [ValidateSet('Badges', 'Clover', 'Cobertura', 'CsvSummary', 'Html', 'Html_Dark', 'Html_Light', 'HtmlChart', 'HtmlInline', 'HtmlInline_AzurePipelines', 'HtmlInline_AzurePipelines_Dark', 'HtmlInline_AzurePipelines_Light', 'HtmlSummary', 'JsonSummary', 'Latex', 'LatexSummary', 'lcov', 'MarkdownSummary', 'MHtml', 'PngChart', 'SonarQube', 'TeamCitySummary', 'TextSummary', 'Xml', 'XmlSummary')] ++ [string]$Format='Cobertura', ++ [string]$OutputFile=("$PSScriptRoot/../coveragereport/merged.cobertura.xml") ++) ++ ++$RepoRoot = [string](Resolve-Path $PSScriptRoot/..) ++ ++if (!(Test-Path $RepoRoot/obj/dotnet-coverage*)) { ++ dotnet tool install --tool-path $RepoRoot/obj dotnet-coverage --version 17.4.3 --configfile $PSScriptRoot/justnugetorg.nuget.config ++} ++ ++Write-Verbose "Searching $Path for *.cobertura.xml files" ++$reports = Get-ChildItem -Recurse $Path -Filter *.cobertura.xml ++ ++if ($reports) { ++ $reports |% { $_.FullName } |% { ++ # In addition to replacing {reporoot}, we also normalize on one kind of slash so that the report aggregates data for a file whether data was collected on Windows or not. ++ $xml = [xml](Get-Content -Path $_) ++ $xml.coverage.packages.package.classes.class |? { $_.filename} |% { ++ $_.filename = $_.filename.Replace('{reporoot}', $RepoRoot).Replace([IO.Path]::AltDirectorySeparatorChar, [IO.Path]::DirectorySeparatorChar) ++ } ++ ++ $xml.Save($_) ++ } ++ ++ $Inputs = $reports |% { Resolve-Path -relative $_.FullName } ++ ++ if ((Split-Path $OutputFile) -and -not (Test-Path (Split-Path $OutputFile))) { ++ New-Item -Type Directory -Path (Split-Path $OutputFile) | Out-Null ++ } ++ ++ & "$RepoRoot/obj/dotnet-coverage" merge $Inputs -o $OutputFile -f cobertura ++} else { ++ Write-Error "No reports found to merge." ++} +diff --git a/azure-pipelines/Windows_NT.runsettings b/azure-pipelines/Windows_NT.runsettings +deleted file mode 100644 +index 0b43ecb0..00000000 +--- a/azure-pipelines/Windows_NT.runsettings ++++ /dev/null +@@ -1,14 +0,0 @@ +- +- +- +- +- +- +- +- +- %BUILD_ARTIFACTSTAGINGDIRECTORY% +- +- +- +- +- +diff --git a/azure-pipelines/artifacts/Variables.ps1 b/azure-pipelines/artifacts/Variables.ps1 +index cfcb7df5..4bc6d216 100644 +--- a/azure-pipelines/artifacts/Variables.ps1 ++++ b/azure-pipelines/artifacts/Variables.ps1 +@@ -2,18 +2,21 @@ + # It "snaps" the values of these variables where we can compute them during the build, + # and otherwise captures the scripts to run later during an Azure Pipelines environment release. + +-$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +-$ArtifactBasePath = "$RepoRoot\obj\_artifacts" +-$VariablesArtifactPath = "$ArtifactBasePath\variables" ++$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot/../..") ++$ArtifactBasePath = "$RepoRoot/obj/_artifacts" ++$VariablesArtifactPath = Join-Path $ArtifactBasePath variables + if (-not (Test-Path $VariablesArtifactPath)) { New-Item -ItemType Directory -Path $VariablesArtifactPath | Out-Null } + + # Copy variables, either by value if the value is calculable now, or by script +-Get-ChildItem -Path "$PSScriptRoot\..\variables" |% { ++Get-ChildItem "$PSScriptRoot/../variables" |% { + $value = $null + if (-not $_.BaseName.StartsWith('_')) { # Skip trying to interpret special scripts + # First check the environment variables in case the variable was set in a queued build +- if (Test-Path env:$($_.BaseName)) { +- $value = Get-Content "env:$($_.BaseName)" ++ # Always use all caps for env var access because Azure Pipelines converts variables to upper-case for env vars, ++ # and on non-Windows env vars are case sensitive. ++ $envVarName = $_.BaseName.ToUpper() ++ if (Test-Path env:$envVarName) { ++ $value = Get-Content "env:$envVarName" + } + + # If that didn't give us anything, try executing the script right now from its original position +@@ -32,7 +35,7 @@ Get-ChildItem -Path "$PSScriptRoot\..\variables" |% { + $value = Get-Content -Path $_.FullName + } + +- Set-Content -Path "$VariablesArtifactPath\$($_.Name)" -Value $value ++ Set-Content -Path "$VariablesArtifactPath/$($_.Name)" -Value $value + } + + @{ +diff --git a/azure-pipelines/artifacts/_all.ps1 b/azure-pipelines/artifacts/_all.ps1 +index efd6bdb3..9a22a1d0 100755 +--- a/azure-pipelines/artifacts/_all.ps1 ++++ b/azure-pipelines/artifacts/_all.ps1 +@@ -1,18 +1,25 @@ + #!/usr/bin/env pwsh + +-# This script returns all the artifacts that should be collected after a build. +-# +-# Each powershell artifact is expressed as an object with these properties: +-# Source - the full path to the source file +-# ArtifactName - the name of the artifact to upload to +-# ContainerFolder - the relative path within the artifact in which the file should appear +-# +-# Each artifact aggregating .ps1 script should return a hashtable: +-# Key = path to the directory from which relative paths within the artifact should be calculated +-# Value = an array of paths (absolute or relative to the BaseDirectory) to files to include in the artifact. +-# FileInfo objects are also allowed. +- +-$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") ++<# ++.SYNOPSIS ++ This script returns all the artifacts that should be collected after a build. ++ Each powershell artifact is expressed as an object with these properties: ++ Source - the full path to the source file ++ ArtifactName - the name of the artifact to upload to ++ ContainerFolder - the relative path within the artifact in which the file should appear ++ Each artifact aggregating .ps1 script should return a hashtable: ++ Key = path to the directory from which relative paths within the artifact should be calculated ++ Value = an array of paths (absolute or relative to the BaseDirectory) to files to include in the artifact. ++ FileInfo objects are also allowed. ++.PARAMETER Force ++ Executes artifact scripts even if they have already been staged. ++#> ++ ++[CmdletBinding(SupportsShouldProcess = $true)] ++param ( ++ [string]$ArtifactNameSuffix, ++ [switch]$Force ++) + + Function EnsureTrailingSlash($path) { + if ($path.length -gt 0 -and !$path.EndsWith('\') -and !$path.EndsWith('/')) { +@@ -22,31 +29,44 @@ Function EnsureTrailingSlash($path) { + $path.Replace('\', [IO.Path]::DirectorySeparatorChar) + } + +-Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse |% { +- $ArtifactName = $_.BaseName ++Function Test-ArtifactStaged($artifactName) { ++ $varName = "ARTIFACTSTAGED_$($artifactName.ToUpper())" ++ Test-Path "env:$varName" ++} + +- $fileGroups = & $_ +- if (!$fileGroups -or $fileGroups.Count -eq 0) { +- Write-Warning "No files found for the `"$ArtifactName`" artifact." +- } else { +- $fileGroups.GetEnumerator() | % { +- $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) +- $_.Value | % { +- if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { +- $_ = $_.FullName +- } ++Get-ChildItem "$PSScriptRoot\*.ps1" -Exclude "_*" -Recurse | % { ++ $ArtifactName = $_.BaseName ++ if ($Force -or !(Test-ArtifactStaged($ArtifactName + $ArtifactNameSuffix))) { ++ $totalFileCount = 0 ++ Write-Verbose "Collecting file list for artifact $($_.BaseName)" ++ $fileGroups = & $_ ++ if ($fileGroups) { ++ $fileGroups.GetEnumerator() | % { ++ $BaseDirectory = New-Object Uri ((EnsureTrailingSlash $_.Key.ToString()), [UriKind]::Absolute) ++ $_.Value | ? { $_ } | % { ++ if ($_.GetType() -eq [IO.FileInfo] -or $_.GetType() -eq [IO.DirectoryInfo]) { ++ $_ = $_.FullName ++ } + +- $artifact = New-Object -TypeName PSObject +- Add-Member -InputObject $artifact -MemberType NoteProperty -Name ArtifactName -Value $ArtifactName ++ $artifact = New-Object -TypeName PSObject ++ Add-Member -InputObject $artifact -MemberType NoteProperty -Name ArtifactName -Value $ArtifactName + +- $SourceFullPath = New-Object Uri ($BaseDirectory, $_) +- Add-Member -InputObject $artifact -MemberType NoteProperty -Name Source -Value $SourceFullPath.LocalPath ++ $SourceFullPath = New-Object Uri ($BaseDirectory, $_) ++ Add-Member -InputObject $artifact -MemberType NoteProperty -Name Source -Value $SourceFullPath.LocalPath + +- $RelativePath = [Uri]::UnescapeDataString($BaseDirectory.MakeRelative($SourceFullPath)) +- Add-Member -InputObject $artifact -MemberType NoteProperty -Name ContainerFolder -Value (Split-Path $RelativePath) ++ $RelativePath = [Uri]::UnescapeDataString($BaseDirectory.MakeRelative($SourceFullPath)) ++ Add-Member -InputObject $artifact -MemberType NoteProperty -Name ContainerFolder -Value (Split-Path $RelativePath) + +- Write-Output $artifact ++ Write-Output $artifact ++ $totalFileCount += 1 ++ } + } + } ++ ++ if ($totalFileCount -eq 0) { ++ Write-Warning "No files found for the `"$ArtifactName`" artifact." ++ } ++ } else { ++ Write-Host "Skipping $ArtifactName because it has already been staged." -ForegroundColor DarkGray + } + } +diff --git a/azure-pipelines/artifacts/_pipelines.ps1 b/azure-pipelines/artifacts/_pipelines.ps1 +index 5bca7c6c..2d3338b2 100644 +--- a/azure-pipelines/artifacts/_pipelines.ps1 ++++ b/azure-pipelines/artifacts/_pipelines.ps1 +@@ -1,10 +1,44 @@ +-# This script translates all the artifacts described by _all.ps1 +-# into commands that instruct Azure Pipelines to actually collect those artifacts. ++<# ++.SYNOPSIS ++ This script translates all the artifacts described by _all.ps1 ++ into commands that instruct Azure Pipelines to actually collect those artifacts. ++#> + ++[CmdletBinding()] + param ( +- [string]$ArtifactNameSuffix ++ [string]$ArtifactNameSuffix, ++ [switch]$StageOnly + ) + ++Function Set-PipelineVariable($name, $value) { ++ if ((Test-Path "Env:\$name") -and (Get-Item "Env:\$name").Value -eq $value) { ++ return # already set ++ } ++ ++ #New-Item -Path "Env:\$name".ToUpper() -Value $value -Force | Out-Null ++ Write-Host "##vso[task.setvariable variable=$name]$value" ++} ++ ++Function Test-ArtifactUploaded($artifactName) { ++ $varName = "ARTIFACTUPLOADED_$($artifactName.ToUpper())" ++ Test-Path "env:$varName" ++} ++ + & "$PSScriptRoot/_stage_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix |% { +- Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" ++ # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts ++ # will skip this one from a check in the _all.ps1 script. ++ Set-PipelineVariable "ARTIFACTSTAGED_$($_.Name.ToUpper())" 'true' ++ Write-Host "Staged artifact $($_.Name) to $($_.Path)" ++ ++ if (!$StageOnly) { ++ if (Test-ArtifactUploaded $_.Name) { ++ Write-Host "Skipping $($_.Name) because it has already been uploaded." -ForegroundColor DarkGray ++ } else { ++ Write-Host "##vso[artifact.upload containerfolder=$($_.Name);artifactname=$($_.Name);]$($_.Path)" ++ ++ # Set a variable which will out-live this script so that a subsequent attempt to collect and upload artifacts ++ # will skip this one from a check in the _all.ps1 script. ++ Set-PipelineVariable "ARTIFACTUPLOADED_$($_.Name.ToUpper())" 'true' ++ } ++ } + } +diff --git a/azure-pipelines/artifacts/_stage_all.ps1 b/azure-pipelines/artifacts/_stage_all.ps1 +index 4e6a6dbe..d81d16d4 100644 +--- a/azure-pipelines/artifacts/_stage_all.ps1 ++++ b/azure-pipelines/artifacts/_stage_all.ps1 +@@ -1,20 +1,16 @@ +-# This script links all the artifacts described by _all.ps1 +-# into a staging directory, reading for uploading to a cloud build artifact store. +-# It returns a sequence of objects with Name and Path properties. +- ++<# ++.SYNOPSIS ++ This script links all the artifacts described by _all.ps1 ++ into a staging directory, reading for uploading to a cloud build artifact store. ++ It returns a sequence of objects with Name and Path properties. ++#> ++ ++[CmdletBinding()] + param ( + [string]$ArtifactNameSuffix + ) + +-$RepoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot (Join-Path .. ..))) +-if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { +- $ArtifactStagingFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY +-} else { +- $ArtifactStagingFolder = Join-Path $RepoRoot (Join-Path obj _artifacts) +- if (Test-Path $ArtifactStagingFolder) { +- Remove-Item $ArtifactStagingFolder -Recurse -Force +- } +-} ++$ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" -CleanIfLocal + + function Create-SymbolicLink { + param ( +@@ -29,7 +25,6 @@ function Create-SymbolicLink { + if (Test-Path $Link) { Remove-Item $Link } + $LinkContainer = Split-Path $Link -Parent + if (!(Test-Path $LinkContainer)) { mkdir $LinkContainer } +- Write-Verbose "Linking $Link to $Target" + if ($IsMacOS -or $IsLinux) { + ln $Target $Link | Out-Null + } else { +@@ -38,9 +33,9 @@ function Create-SymbolicLink { + } + + # Stage all artifacts +-$Artifacts = & "$PSScriptRoot\_all.ps1" ++$Artifacts = & "$PSScriptRoot\_all.ps1" -ArtifactNameSuffix $ArtifactNameSuffix + $Artifacts |% { +- $DestinationFolder = (Join-Path (Join-Path $ArtifactStagingFolder "$($_.ArtifactName)$ArtifactNameSuffix") $_.ContainerFolder).TrimEnd('\') ++ $DestinationFolder = [System.IO.Path]::GetFullPath("$ArtifactStagingFolder/$($_.ArtifactName)$ArtifactNameSuffix/$($_.ContainerFolder)").TrimEnd('\') + $Name = "$(Split-Path $_.Source -Leaf)" + + #Write-Host "$($_.Source) -> $($_.ArtifactName)\$($_.ContainerFolder)" -ForegroundColor Yellow +@@ -51,7 +46,13 @@ $Artifacts |% { + } + } + +-$Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } | Get-Unique |% { ++$ArtifactNames = $Artifacts |% { "$($_.ArtifactName)$ArtifactNameSuffix" } ++$ArtifactNames += Get-ChildItem env:ARTIFACTSTAGED_* |% { ++ # Return from ALLCAPS to the actual capitalization used for the artifact. ++ $artifactNameAllCaps = "$($_.Name.Substring('ARTIFACTSTAGED_'.Length))" ++ (Get-ChildItem $ArtifactStagingFolder\$artifactNameAllCaps* -Filter $artifactNameAllCaps).Name ++} ++$ArtifactNames | Get-Unique |% { + $artifact = New-Object -TypeName PSObject + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Name -Value $_ + Add-Member -InputObject $artifact -MemberType NoteProperty -Name Path -Value (Join-Path $ArtifactStagingFolder $_) +diff --git a/azure-pipelines/artifacts/build_logs.ps1 b/azure-pipelines/artifacts/build_logs.ps1 +index b55ba48f..f05358e0 100644 +--- a/azure-pipelines/artifacts/build_logs.ps1 ++++ b/azure-pipelines/artifacts/build_logs.ps1 +@@ -1,12 +1,7 @@ +-if ($env:BUILD_ARTIFACTSTAGINGDIRECTORY) { +- $artifactsRoot = $env:BUILD_ARTIFACTSTAGINGDIRECTORY +-} else { +- $RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +- $artifactsRoot = "$RepoRoot\bin" +-} ++$ArtifactStagingFolder = & "$PSScriptRoot/../Get-ArtifactsStagingDirectory.ps1" + +-if (!(Test-Path $artifactsRoot/build_logs)) { return } ++if (!(Test-Path $ArtifactStagingFolder/build_logs)) { return } + + @{ +- "$artifactsRoot/build_logs" = (Get-ChildItem -Recurse "$artifactsRoot/build_logs") ++ "$ArtifactStagingFolder/build_logs" = (Get-ChildItem -Recurse "$ArtifactStagingFolder/build_logs") + } +diff --git a/azure-pipelines/artifacts/coverageResults.ps1 b/azure-pipelines/artifacts/coverageResults.ps1 +new file mode 100644 +index 00000000..d2fee501 +--- /dev/null ++++ b/azure-pipelines/artifacts/coverageResults.ps1 +@@ -0,0 +1,23 @@ ++$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") ++ ++$coverageFiles = @(Get-ChildItem "$RepoRoot/tests/*.cobertura.xml" -Recurse | Where {$_.FullName -notlike "*/In/*" -and $_.FullName -notlike "*\In\*" }) ++ ++# Prepare code coverage reports for merging on another machine ++if ($env:SYSTEM_DEFAULTWORKINGDIRECTORY) { ++ Write-Host "Substituting $env:SYSTEM_DEFAULTWORKINGDIRECTORY with `"{reporoot}`"" ++ $coverageFiles |% { ++ $content = Get-Content -Path $_ |% { $_ -Replace [regex]::Escape($env:SYSTEM_DEFAULTWORKINGDIRECTORY), "{reporoot}" } ++ Set-Content -Path $_ -Value $content -Encoding UTF8 ++ } ++} else { ++ Write-Warning "coverageResults: Azure Pipelines not detected. Machine-neutral token replacement skipped." ++} ++ ++if (!((Test-Path $RepoRoot\bin) -and (Test-Path $RepoRoot\obj))) { return } ++ ++@{ ++ $RepoRoot = ( ++ $coverageFiles + ++ (Get-ChildItem "$RepoRoot\obj\*.cs" -Recurse) ++ ); ++} +diff --git a/azure-pipelines/artifacts/vsix.ps1 b/azure-pipelines/artifacts/deployables.ps1 +similarity index 56% +rename from azure-pipelines/artifacts/vsix.ps1 +rename to azure-pipelines/artifacts/deployables.ps1 +index 963404f9..94c48cdd 100644 +--- a/azure-pipelines/artifacts/vsix.ps1 ++++ b/azure-pipelines/artifacts/deployables.ps1 +@@ -4,11 +4,10 @@ if (!$BuildConfiguration) { + $BuildConfiguration = 'Debug' + } + +-$result = @{} ++$PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration" + +-$VsixRoot = "$RepoRoot/bin/MessagePackAnalyzer.Vsix/$BuildConfiguration" +-if (Test-Path $VsixRoot) { +- $result[$VsixRoot] = (Get-ChildItem "$VsixRoot/*vsix") +-} ++if (!(Test-Path $PackagesRoot)) { return } + +-return $result ++@{ ++ "$PackagesRoot" = (Get-ChildItem $PackagesRoot -Recurse) ++} +diff --git a/azure-pipelines/artifacts/mpc.ps1 b/azure-pipelines/artifacts/mpc.ps1 +deleted file mode 100644 +index fa22d29d..00000000 +--- a/azure-pipelines/artifacts/mpc.ps1 ++++ /dev/null +@@ -1,11 +0,0 @@ +-$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +-$BuildConfiguration = $env:BUILDCONFIGURATION +-if (!$BuildConfiguration) { +- $BuildConfiguration = 'Debug' +-} +- +-$mpcArtifactPath = "$RepoRoot/bin/MessagePack.Generator/$BuildConfiguration/netcoreapp3.0" +- +-@{ +- $mpcArtifactPath = (Get-ChildItem $mpcArtifactPath -Recurse); +-} +diff --git a/azure-pipelines/artifacts/nuget.ps1 b/azure-pipelines/artifacts/nuget.ps1 +deleted file mode 100644 +index ea4a55a0..00000000 +--- a/azure-pipelines/artifacts/nuget.ps1 ++++ /dev/null +@@ -1,14 +0,0 @@ +-$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +-$BuildConfiguration = $env:BUILDCONFIGURATION +-if (!$BuildConfiguration) { +- $BuildConfiguration = 'Debug' +-} +- +-$result = @{} +- +-$PackagesRoot = "$RepoRoot/bin/Packages/$BuildConfiguration/NuGet" +-if (Test-Path $PackagesRoot) { +- $result[$PackagesRoot] = (Get-ChildItem $PackagesRoot -Recurse) +-} +- +-return $result +diff --git a/azure-pipelines/artifacts/symbols.ps1 b/azure-pipelines/artifacts/symbols.ps1 +new file mode 100644 +index 00000000..9e2c7bd5 +--- /dev/null ++++ b/azure-pipelines/artifacts/symbols.ps1 +@@ -0,0 +1,7 @@ ++$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") ++if (!(Test-Path $BinPath)) { return } ++$symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath | Get-Unique ++ ++@{ ++ "$BinPath" = $SymbolFiles; ++} +diff --git a/azure-pipelines/artifacts/testResults.ps1 b/azure-pipelines/artifacts/testResults.ps1 +new file mode 100644 +index 00000000..6c042043 +--- /dev/null ++++ b/azure-pipelines/artifacts/testResults.ps1 +@@ -0,0 +1,15 @@ ++[CmdletBinding()] ++Param( ++) ++ ++$result = @{} ++ ++$testRoot = Resolve-Path "$PSScriptRoot\..\..\tests" ++$result[$testRoot] = (Get-ChildItem "$testRoot\TestResults" -Recurse -Directory | Get-ChildItem -Recurse -File) ++ ++$testlogsPath = "$env:BUILD_ARTIFACTSTAGINGDIRECTORY\test_logs" ++if (Test-Path $testlogsPath) { ++ $result[$testlogsPath] = Get-ChildItem "$testlogsPath\*"; ++} ++ ++$result +diff --git a/azure-pipelines/artifacts/test_symbols.ps1 b/azure-pipelines/artifacts/test_symbols.ps1 +new file mode 100644 +index 00000000..ce2b6481 +--- /dev/null ++++ b/azure-pipelines/artifacts/test_symbols.ps1 +@@ -0,0 +1,7 @@ ++$BinPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../bin") ++if (!(Test-Path $BinPath)) { return } ++$symbolfiles = & "$PSScriptRoot/../Get-SymbolFiles.ps1" -Path $BinPath -Tests | Get-Unique ++ ++@{ ++ "$BinPath" = $SymbolFiles; ++} +diff --git a/azure-pipelines/artifacts/unity.ps1 b/azure-pipelines/artifacts/unity.ps1 +deleted file mode 100644 +index b5059b30..00000000 +--- a/azure-pipelines/artifacts/unity.ps1 ++++ /dev/null +@@ -1,14 +0,0 @@ +-$RepoRoot = [System.IO.Path]::GetFullPath("$PSScriptRoot\..\..") +-$BuildConfiguration = $env:BUILDCONFIGURATION +-if (!$BuildConfiguration) { +- $BuildConfiguration = 'Debug' +-} +- +-$result = @{} +- +-$UnityPackRoot = "$RepoRoot/bin" +-if (Test-Path $UnityPackRoot) { +- $result[$UnityPackRoot] = (Get-ChildItem $UnityPackRoot/*.unitypackage) +-} +- +-return $result +diff --git a/azure-pipelines/build.yml b/azure-pipelines/build.yml +index 99f5524d..c9f5fc17 100644 +--- a/azure-pipelines/build.yml ++++ b/azure-pipelines/build.yml +@@ -1,30 +1,93 @@ +-steps: +-- task: DotNetCoreCLI@2 +- inputs: +- command: build +- arguments: --configuration $(BuildConfiguration) /t:build,pack /m /v:m /bl:"$(Build.ArtifactStagingDirectory)/build_logs/msbuild.binlog" +- displayName: Build MessagePack.sln ++parameters: ++- name: windowsPool ++ type: object ++ default: ++ vmImage: windows-2022 ++- name: includeMacOS ++- name: RunTests ++ type: boolean ++ default: true + +-- task: DotNetCoreCLI@2 +- displayName: Run tests +- inputs: +- command: test +- projects: tests/**/*.Tests.csproj +- arguments: --configuration $(BuildConfiguration) --no-build -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" +- testRunTitle: $(Agent.JobName) ++jobs: ++- job: Windows ++ pool: ${{ parameters.windowsPool }} ++ steps: ++ - checkout: self ++ fetchDepth: 0 # avoid shallow clone so nbgv can do its work. ++ clean: true ++ - template: install-dependencies.yml + +-## The rest of these steps are for deployment and skipped for PR builds ++ - powershell: '& (./azure-pipelines/Get-nbgv.ps1) cloud -c' ++ displayName: ⚙ Set build number + +-- task: PowerShell@2 +- inputs: +- filePath: azure-pipelines/variables/_pipelines.ps1 +- failOnStderr: true +- displayName: Update pipeline variables based on build outputs +- condition: succeededOrFailed() ++ - template: dotnet.yml ++ parameters: ++ RunTests: ${{ parameters.RunTests }} ++ ++- job: Linux ++ pool: ++ vmImage: ubuntu-22.04 ++ steps: ++ - checkout: self ++ fetchDepth: 0 # avoid shallow clone so nbgv can do its work. ++ clean: true ++ - template: install-dependencies.yml ++ - template: dotnet.yml ++ parameters: ++ RunTests: ${{ parameters.RunTests }} ++ ++- job: macOS ++ condition: ${{ parameters.includeMacOS }} ++ pool: ++ vmImage: macOS-12 ++ steps: ++ - checkout: self ++ fetchDepth: 0 # avoid shallow clone so nbgv can do its work. ++ clean: true ++ - template: install-dependencies.yml ++ - template: dotnet.yml ++ parameters: ++ RunTests: ${{ parameters.RunTests }} ++ ++# This job ensures that we're running mpc regularly on the generated code that we check in. ++# It also helps exercise mpc so bugs don't go unnoticed. ++- job: codegen_diff ++ pool: ++ vmImage: ubuntu-22.04 ++ steps: ++ - checkout: self ++ clean: true ++ - template: install-dependencies.yml ++ - pwsh: sandbox/codegen.ps1 ++ displayName: 🏭 Regenerate checked-in code ++ - bash: | ++ git add -u . # compare after applying git EOL normalization ++ git diff --cached --exit-code --stat \ ++ || (echo "##[error] found changed files after build. Please run 'sandbox/codegen.ps1'" \ ++ "and check in all changes" \ ++ && git diff --cached \ ++ && exit 1) ++ displayName: 🔍 Check for uncommitted changes + +-- task: PowerShell@2 +- inputs: +- filePath: azure-pipelines/artifacts/_pipelines.ps1 +- # arguments: -ArtifactNameSuffix "-$(Agent.JobName)" +- displayName: Publish artifacts ++- job: WrapUp ++ dependsOn: ++ - Windows ++ - Linux ++ - macOS ++ pool: ${{ parameters.windowsPool }} # Use Windows agent because PublishSymbols task requires it (https://github.com/microsoft/azure-pipelines-tasks/issues/13821). + condition: succeededOrFailed() ++ steps: ++ - checkout: self ++ fetchDepth: 0 # avoid shallow clone so nbgv can do its work. ++ clean: true ++ - template: install-dependencies.yml ++ parameters: ++ initArgs: -NoRestore ++ - template: publish-symbols.yml ++ parameters: ++ includeMacOS: ${{ parameters.includeMacOS }} ++ - ${{ if parameters.RunTests }}: ++ - template: publish-codecoverage.yml ++ parameters: ++ includeMacOS: ${{ parameters.includeMacOS }} ++ - template: publish-deployables.yml +diff --git a/azure-pipelines/build_nonWindows.yml b/azure-pipelines/build_nonWindows.yml +deleted file mode 100644 +index cf3f51d7..00000000 +--- a/azure-pipelines/build_nonWindows.yml ++++ /dev/null +@@ -1,41 +0,0 @@ +-steps: +-- task: DotNetCoreCLI@2 +- displayName: Build MessagePack.sln +- inputs: +- command: build +- arguments: --no-restore -c $(BuildConfiguration) +- +-- task: DotNetCoreCLI@2 +- displayName: Run MessagePack.Tests (netcoreapp3.1) +- inputs: +- command: test +- projects: tests/MessagePack.Tests/MessagePack.Tests.csproj +- arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" +- testRunTitle: netcoreapp3.1-$(Agent.JobName) +- +-- task: DotNetCoreCLI@2 +- displayName: Run MessagePack.Tests (net6.0) +- inputs: +- command: test +- projects: tests/MessagePack.Tests/MessagePack.Tests.csproj +- arguments: --no-build -c $(BuildConfiguration) -f net6.0 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" +- testRunTitle: net6.0-$(Agent.JobName) +- +-- bash: mono ~/.nuget/packages/xunit.runner.console/2.4.1/tools/net472/xunit.console.exe bin/MessagePack.Tests/$(BuildConfiguration)/net472/MessagePack.Tests.dll -html $(BUILD.ARTIFACTSTAGINGDIRECTORY)/build_logs/mono_testrun.html +- displayName: Run MessagePack.Tests (mono) +- +-- task: DotNetCoreCLI@2 +- displayName: Run MessagePackAnalyzer.Tests +- inputs: +- command: test +- projects: tests/MessagePackAnalyzer.Tests/MessagePackAnalyzer.Tests.csproj +- arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" +- testRunTitle: netcoreapp3.1-$(Agent.JobName)-Analyzers +- +-- task: DotNetCoreCLI@2 +- displayName: Run MessagePack.Generator.Tests +- inputs: +- command: test +- projects: tests/MessagePack.Generator.Tests/MessagePack.Generator.Tests.csproj +- arguments: --no-build -c $(BuildConfiguration) -f netcoreapp3.1 -v n --settings "$(Build.Repository.LocalPath)/azure-pipelines/$(Agent.OS).runsettings" +- testRunTitle: netcoreapp3.1-$(Agent.JobName)-Generator +diff --git a/azure-pipelines/build_unity.yml b/azure-pipelines/build_unity.yml +deleted file mode 100644 +index 6338d449..00000000 +--- a/azure-pipelines/build_unity.yml ++++ /dev/null +@@ -1,34 +0,0 @@ +-steps: +-- powershell: Write-Host "##vso[task.setvariable variable=unity_package_version;]$((& (./azure-pipelines/Get-nbgv.ps1) get-version -f json | ConvertFrom-Json).NuGetPackageVersion)" +- displayName: Get unity package version +- +-- script: dotnet publish src/MessagePack -c $(BuildConfiguration) -f netstandard2.0 +- displayName: Build MessagePack +- +-- script: src/MessagePack.UnityClient/build.sh -l $(Build.ArtifactStagingDirectory)/build_logs/unitypackage.log +- displayName: Build Unity package +- +-- task: PublishBuildArtifacts@1 +- inputs: +- PathtoPublish: $(Build.ArtifactStagingDirectory)/build_logs +- ArtifactName: build_logs +- ArtifactType: Container +- displayName: Publish build_logs artifacts +- condition: succeededOrFailed() +- +-- task: CopyFiles@1 +- inputs: +- Contents: | +- bin/*.unitypackage +- TargetFolder: $(Build.ArtifactStagingDirectory)/unity +- flattenFolders: true +- displayName: Collecting deployables +- condition: succeededOrFailed() +- +-- task: PublishBuildArtifacts@1 +- inputs: +- PathtoPublish: $(Build.ArtifactStagingDirectory)/unity +- ArtifactName: unity +- ArtifactType: Container +- displayName: Publish deployables artifacts +- condition: succeededOrFailed() +diff --git a/azure-pipelines/dotnet-test-cloud.ps1 b/azure-pipelines/dotnet-test-cloud.ps1 +new file mode 100644 +index 00000000..13f973cd +--- /dev/null ++++ b/azure-pipelines/dotnet-test-cloud.ps1 +@@ -0,0 +1,83 @@ ++#!/usr/bin/env pwsh ++ ++<# ++.SYNOPSIS ++ Runs tests as they are run in cloud test runs. ++.PARAMETER Configuration ++ The configuration within which to run tests ++.PARAMETER Agent ++ The name of the agent. This is used in preparing test run titles. ++.PARAMETER PublishResults ++ A switch to publish results to Azure Pipelines. ++.PARAMETER x86 ++ A switch to run the tests in an x86 process. ++.PARAMETER dotnet32 ++ The path to a 32-bit dotnet executable to use. ++#> ++[CmdletBinding()] ++Param( ++ [string]$Configuration='Debug', ++ [string]$Agent='Local', ++ [switch]$PublishResults, ++ [switch]$x86, ++ [string]$dotnet32 ++) ++ ++$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path ++$ArtifactStagingFolder = & "$PSScriptRoot/Get-ArtifactsStagingDirectory.ps1" ++ ++$dotnet = 'dotnet' ++if ($x86) { ++ $x86RunTitleSuffix = ", x86" ++ if ($dotnet32) { ++ $dotnet = $dotnet32 ++ } else { ++ $dotnet32Possibilities = "$PSScriptRoot\../obj/tools/x86/.dotnet/dotnet.exe", "$env:AGENT_TOOLSDIRECTORY/x86/dotnet/dotnet.exe", "${env:ProgramFiles(x86)}\dotnet\dotnet.exe" ++ $dotnet32Matches = $dotnet32Possibilities |? { Test-Path $_ } ++ if ($dotnet32Matches) { ++ $dotnet = Resolve-Path @($dotnet32Matches)[0] ++ Write-Host "Running tests using `"$dotnet`"" -ForegroundColor DarkGray ++ } else { ++ Write-Error "Unable to find 32-bit dotnet.exe" ++ return 1 ++ } ++ } ++} ++ ++& $dotnet test $RepoRoot ` ++ --no-build ` ++ -c $Configuration ` ++ --filter "TestCategory!=FailsInCloudTest" ` ++ --collect "Code Coverage;Format=cobertura" ` ++ --settings "$PSScriptRoot/test.runsettings" ` ++ --blame-hang-timeout 60s ` ++ --blame-crash ` ++ -bl:"$ArtifactStagingFolder/build_logs/test.binlog" ` ++ --diag "$ArtifactStagingFolder/test_logs/diag.log;TraceLevel=info" ` ++ --logger trx ` ++ ++$unknownCounter = 0 ++Get-ChildItem -Recurse -Path $RepoRoot\tests\*.trx |% { ++ Copy-Item $_ -Destination $ArtifactStagingFolder/test_logs/ ++ ++ if ($PublishResults) { ++ $x = [xml](Get-Content -Path $_) ++ $runTitle = $null ++ if ($x.TestRun.TestDefinitions -and $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')) { ++ $storage = $x.TestRun.TestDefinitions.GetElementsByTagName('UnitTest')[0].storage -replace '\\','/' ++ if ($storage -match '/(?net[^/]+)/(?:(?[^/]+)/)?(?[^/]+)\.dll$') { ++ if ($matches.rid) { ++ $runTitle = "$($matches.lib) ($($matches.tfm), $($matches.rid), $Agent)" ++ } else { ++ $runTitle = "$($matches.lib) ($($matches.tfm)$x86RunTitleSuffix, $Agent)" ++ } ++ } ++ } ++ if (!$runTitle) { ++ $unknownCounter += 1; ++ $runTitle = "unknown$unknownCounter ($Agent$x86RunTitleSuffix)"; ++ } ++ ++ Write-Host "##vso[results.publish type=VSTest;runTitle=$runTitle;publishRunAttachments=true;resultFiles=$_;failTaskOnFailedTests=true;testRunSystem=VSTS - PTR;]" ++ } ++} +diff --git a/azure-pipelines/dotnet.yml b/azure-pipelines/dotnet.yml +new file mode 100644 +index 00000000..79babd4d +--- /dev/null ++++ b/azure-pipelines/dotnet.yml +@@ -0,0 +1,30 @@ ++parameters: ++ RunTests: ++ ++steps: ++ ++- script: dotnet build -t:build,pack --no-restore -c $(BuildConfiguration) /bl:"$(Build.ArtifactStagingDirectory)/build_logs/build.binlog" ++ displayName: 🛠 dotnet build ++ ++- powershell: azure-pipelines/dotnet-test-cloud.ps1 -Configuration $(BuildConfiguration) -Agent $(Agent.JobName) -PublishResults ++ displayName: 🧪 dotnet test ++ condition: and(succeeded(), ${{ parameters.RunTests }}) ++ ++- powershell: azure-pipelines/variables/_pipelines.ps1 ++ failOnStderr: true ++ displayName: ⚙ Update pipeline variables based on build outputs ++ condition: succeededOrFailed() ++ ++- powershell: azure-pipelines/artifacts/_pipelines.ps1 -ArtifactNameSuffix "-$(Agent.JobName)" -Verbose ++ failOnStderr: true ++ displayName: 📢 Publish artifacts ++ condition: succeededOrFailed() ++ ++- ${{ if and(ne(variables['codecov_token'], ''), parameters.RunTests) }}: ++ - powershell: | ++ $ArtifactStagingFolder = & "azure-pipelines/Get-ArtifactsStagingDirectory.ps1" ++ $CoverageResultsFolder = Join-Path $ArtifactStagingFolder "coverageResults-$(Agent.JobName)" ++ azure-pipelines/publish-CodeCov.ps1 -CodeCovToken "$(codecov_token)" -PathToCodeCoverage "$CoverageResultsFolder" -Name "$(Agent.JobName) Coverage Results" -Flags "$(Agent.JobName)Host,$(BuildConfiguration)" ++ displayName: 📢 Publish code coverage results to codecov.io ++ timeoutInMinutes: 3 ++ continueOnError: true +diff --git a/azure-pipelines/install-dependencies.yml b/azure-pipelines/install-dependencies.yml +index 7563bc1f..81782668 100644 +--- a/azure-pipelines/install-dependencies.yml ++++ b/azure-pipelines/install-dependencies.yml +@@ -3,18 +3,23 @@ parameters: + + steps: + ++- task: NuGetAuthenticate@1 ++ displayName: 🔏 Authenticate NuGet feeds ++ inputs: ++ forceReinstallCredentialProvider: true ++ + - powershell: | + $AccessToken = '$(System.AccessToken)' # Avoid specifying the access token directly on the init.ps1 command line to avoid it showing up in errors +- .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites ++ .\init.ps1 -AccessToken $AccessToken ${{ parameters['initArgs'] }} -UpgradePrerequisites -NoNuGetCredProvider + dotnet --info + + # Print mono version if it is present. + if (Get-Command mono -ErrorAction SilentlyContinue) { + mono --version + } +- displayName: Install prerequisites ++ displayName: ⚙ Install prerequisites + + - powershell: azure-pipelines/variables/_pipelines.ps1 + failOnStderr: true +- displayName: Set pipeline variables based on source ++ displayName: ⚙ Set pipeline variables based on source + name: SetPipelineVariables +diff --git a/azure-pipelines/publish-CodeCov.ps1 b/azure-pipelines/publish-CodeCov.ps1 +new file mode 100644 +index 00000000..9926f018 +--- /dev/null ++++ b/azure-pipelines/publish-CodeCov.ps1 +@@ -0,0 +1,30 @@ ++<# ++.SYNOPSIS ++ Uploads code coverage to codecov.io ++.PARAMETER CodeCovToken ++ Code coverage token to use ++.PARAMETER PathToCodeCoverage ++ Path to root of code coverage files ++.PARAMETER Name ++ Name to upload with codecoverge ++.PARAMETER Flags ++ Flags to upload with codecoverge ++#> ++[CmdletBinding()] ++Param ( ++ [Parameter(Mandatory=$true)] ++ [string]$CodeCovToken, ++ [Parameter(Mandatory=$true)] ++ [string]$PathToCodeCoverage, ++ [string]$Name, ++ [string]$Flags ++) ++ ++$RepoRoot = (Resolve-Path "$PSScriptRoot/..").Path ++ ++Get-ChildItem -Recurse -Path $PathToCodeCoverage -Filter "*.cobertura.xml" | % { ++ $relativeFilePath = Resolve-Path -relative $_.FullName ++ ++ Write-Host "Uploading: $relativeFilePath" -ForegroundColor Yellow ++ & (& "$PSScriptRoot/Get-CodeCovTool.ps1") -t $CodeCovToken -f $relativeFilePath -R $RepoRoot -F $Flags -n $Name ++} +diff --git a/azure-pipelines/publish-codecoverage.yml b/azure-pipelines/publish-codecoverage.yml +new file mode 100644 +index 00000000..fbb6a39a +--- /dev/null ++++ b/azure-pipelines/publish-codecoverage.yml +@@ -0,0 +1,25 @@ ++parameters: ++ includeMacOS: ++ ++steps: ++- download: current ++ artifact: coverageResults-Windows ++ displayName: 🔻 Download Windows code coverage results ++ continueOnError: true ++- download: current ++ artifact: coverageResults-Linux ++ displayName: 🔻 Download Linux code coverage results ++ continueOnError: true ++- download: current ++ artifact: coverageResults-macOS ++ displayName: 🔻 Download macOS code coverage results ++ continueOnError: true ++ condition: and(succeeded(), ${{ parameters.includeMacOS }}) ++- powershell: azure-pipelines/Merge-CodeCoverage.ps1 -Path '$(Pipeline.Workspace)' -OutputFile coveragereport/merged.cobertura.xml -Format Cobertura -Verbose ++ displayName: ⚙ Merge coverage ++- task: PublishCodeCoverageResults@1 ++ displayName: 📢 Publish code coverage results to Azure DevOps ++ inputs: ++ codeCoverageTool: cobertura ++ summaryFileLocation: coveragereport/merged.cobertura.xml ++ failIfCoverageEmpty: true +diff --git a/azure-pipelines/publish-deployables.yml b/azure-pipelines/publish-deployables.yml +new file mode 100644 +index 00000000..31e80a43 +--- /dev/null ++++ b/azure-pipelines/publish-deployables.yml +@@ -0,0 +1,8 @@ ++steps: ++- download: current ++ displayName: 🔻 Download deployables ++ artifact: deployables-Windows ++ ++- powershell: dotnet nuget push "$(Resolve-Path '$(Pipeline.Workspace)\deployables-Windows\')*.nupkg" -s $(ci_feed) -k azdo --skip-duplicate ++ displayName: 📦 Push packages to CI feed ++ condition: and(succeeded(), ne(variables['ci_feed'], ''), ne(variables['Build.Reason'], 'PullRequest')) +diff --git a/azure-pipelines/publish-symbols.yml b/azure-pipelines/publish-symbols.yml +new file mode 100644 +index 00000000..00c188fc +--- /dev/null ++++ b/azure-pipelines/publish-symbols.yml +@@ -0,0 +1,59 @@ ++parameters: ++ includeMacOS: ++ ++steps: ++- task: DownloadPipelineArtifact@2 ++ inputs: ++ artifact: symbols-Windows ++ path: $(Pipeline.Workspace)/symbols/Windows ++ displayName: 🔻 Download Windows symbols ++ continueOnError: true ++- task: DownloadPipelineArtifact@2 ++ inputs: ++ artifact: symbols-Linux ++ path: $(Pipeline.Workspace)/symbols/Linux ++ displayName: 🔻 Download Linux symbols ++ continueOnError: true ++- task: DownloadPipelineArtifact@2 ++ inputs: ++ artifact: symbols-macOS ++ path: $(Pipeline.Workspace)/symbols/macOS ++ displayName: 🔻 Download macOS symbols ++ continueOnError: true ++ condition: ${{ parameters.includeMacOS }} ++ ++- task: DownloadPipelineArtifact@2 ++ inputs: ++ artifact: test_symbols-Windows ++ path: $(Pipeline.Workspace)/test_symbols/Windows ++ displayName: 🔻 Download Windows test symbols ++ continueOnError: true ++- task: DownloadPipelineArtifact@2 ++ inputs: ++ artifact: test_symbols-Linux ++ path: $(Pipeline.Workspace)/test_symbols/Linux ++ displayName: 🔻 Download Linux test symbols ++ continueOnError: true ++- task: DownloadPipelineArtifact@2 ++ inputs: ++ artifact: test_symbols-macOS ++ path: $(Pipeline.Workspace)/test_symbols/macOS ++ displayName: 🔻 Download macOS test symbols ++ continueOnError: true ++ condition: ${{ parameters.includeMacOS }} ++ ++- task: PublishSymbols@2 ++ inputs: ++ SymbolsFolder: $(Pipeline.Workspace)/symbols ++ SearchPattern: '**/*.pdb' ++ IndexSources: false ++ SymbolServerType: TeamServices ++ displayName: 📢 Publish symbols ++ ++- task: PublishSymbols@2 ++ inputs: ++ SymbolsFolder: $(Pipeline.Workspace)/test_symbols ++ SearchPattern: '**/*.pdb' ++ IndexSources: false ++ SymbolServerType: TeamServices ++ displayName: 📢 Publish test symbols +diff --git a/azure-pipelines/release.yml b/azure-pipelines/release.yml +index 83a6ab1a..be5c4f61 100644 +--- a/azure-pipelines/release.yml ++++ b/azure-pipelines/release.yml +@@ -9,67 +9,54 @@ resources: + tags: + - auto-release + +-stages: +-- stage: GitHubRelease +- displayName: GitHub Release +- jobs: +- - deployment: create +- pool: +- vmImage: ubuntu-latest +- environment: No-Approval +- strategy: +- runOnce: +- deploy: +- steps: +- - download: none +- - powershell: | +- Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" +- displayName: Set pipeline name +- - download: CI +- artifact: unity +- displayName: 'Downloading artifact: unity' +- - task: GitHubRelease@1 +- displayName: GitHub release (create) +- inputs: +- gitHubConnection: AArnott github +- repositoryName: $(Build.Repository.Name) +- target: $(resources.pipeline.CI.sourceCommit) +- tagSource: userSpecifiedTag +- tag: v$(resources.pipeline.CI.runName) +- title: v$(resources.pipeline.CI.runName) +- assets: | +- $(Pipeline.Workspace)/CI/unity/*.unitypackage +- isDraft: true # After running this step, visit the new draft release, edit, and publish. +- changeLogCompareToRelease: lastNonDraftRelease +- changeLogType: issueBased +- changeLogLabels: | +- [ +- { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, +- { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } +- ] ++variables: ++- group: Publishing secrets + +-- stage: nuget_org +- displayName: nuget.org +- dependsOn: GitHubRelease +- jobs: +- - deployment: push +- pool: +- vmImage: ubuntu-latest +- environment: No-Approval +- strategy: +- runOnce: +- deploy: +- steps: +- - download: CI +- artifact: nuget +- - task: NuGetToolInstaller@1 +- displayName: Use NuGet 5.x +- inputs: +- versionSpec: 5.x +- - task: NuGetCommand@2 +- displayName: NuGet push +- inputs: +- command: push +- packagesToPush: $(Pipeline.Workspace)/CI/nuget/*.nupkg +- nuGetFeedType: external +- publishFeedCredentials: nuget.org (aarnott) ++jobs: ++- job: release ++ pool: ++ vmImage: ubuntu-latest ++ steps: ++ - checkout: none ++ - powershell: | ++ Write-Host "##vso[build.updatebuildnumber]$(resources.pipeline.CI.runName)" ++ if ('$(resources.pipeline.CI.runName)'.Contains('-')) { ++ Write-Host "##vso[task.setvariable variable=IsPrerelease]true" ++ } else { ++ Write-Host "##vso[task.setvariable variable=IsPrerelease]false" ++ } ++ displayName: ⚙ Set up pipeline ++ - task: UseDotNet@2 ++ displayName: ⚙ Install .NET SDK ++ inputs: ++ packageType: sdk ++ version: 6.x ++ - download: CI ++ artifact: deployables-Windows ++ displayName: 🔻 Download deployables-Windows artifact ++ patterns: 'deployables-Windows/*' ++ - task: GitHubRelease@1 ++ displayName: 📢 GitHub release (create) ++ inputs: ++ gitHubConnection: AArnott github ++ repositoryName: $(Build.Repository.Name) ++ target: $(resources.pipeline.CI.sourceCommit) ++ tagSource: userSpecifiedTag ++ tag: v$(resources.pipeline.CI.runName) ++ title: v$(resources.pipeline.CI.runName) ++ isDraft: true # After running this step, visit the new draft release, edit, and publish. ++ isPreRelease: $(IsPrerelease) ++ assets: | ++ $(Pipeline.Workspace)/CI/deployables-Windows/*.nupkg ++ $(Pipeline.Workspace)/CI/unity/*.unitypackage ++ changeLogCompareToRelease: lastNonDraftRelease ++ changeLogType: issueBased ++ changeLogLabels: | ++ [ ++ { "label" : "breaking change", "displayName" : "Breaking changes", "state" : "closed" }, ++ { "label" : "bug", "displayName" : "Fixes", "state" : "closed" }, ++ { "label" : "enhancement", "displayName": "Enhancements", "state" : "closed" } ++ ] ++ - script: dotnet nuget push $(Pipeline.Workspace)/CI/deployables-Windows/*.nupkg -s https://api.nuget.org/v3/index.json --api-key $(NuGetOrgApiKey) --skip-duplicate ++ displayName: 📦 Push packages to nuget.org ++ condition: and(succeeded(), ne(variables['NuGetOrgApiKey'], '')) +diff --git a/azure-pipelines/test.runsettings b/azure-pipelines/test.runsettings +new file mode 100644 +index 00000000..4e24a0a6 +--- /dev/null ++++ b/azure-pipelines/test.runsettings +@@ -0,0 +1,44 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ \.dll$ ++ \.exe$ ++ ++ ++ xunit\..* ++ ++ ++ ++ ++ ^System\.Diagnostics\.DebuggerHiddenAttribute$ ++ ^System\.Diagnostics\.DebuggerNonUserCodeAttribute$ ++ ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ ++ ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ ++ ++ ++ ++ ++ True ++ ++ True ++ ++ True ++ ++ False ++ ++ False ++ ++ False ++ ++ True ++ ++ ++ ++ ++ ++ +diff --git a/azure-pipelines/variables/_all.ps1 b/azure-pipelines/variables/_all.ps1 +index 0407d307..cc6e8810 100755 +--- a/azure-pipelines/variables/_all.ps1 ++++ b/azure-pipelines/variables/_all.ps1 +@@ -1,7 +1,14 @@ + #!/usr/bin/env pwsh + +-# This script returns a hashtable of build variables that should be set +-# at the start of a build or release definition's execution. ++<# ++.SYNOPSIS ++ This script returns a hashtable of build variables that should be set ++ at the start of a build or release definition's execution. ++#> ++ ++[CmdletBinding(SupportsShouldProcess = $true)] ++param ( ++) + + $vars = @{} + +diff --git a/azure-pipelines/variables/_pipelines.ps1 b/azure-pipelines/variables/_pipelines.ps1 +index 867b7fc8..11748b81 100644 +--- a/azure-pipelines/variables/_pipelines.ps1 ++++ b/azure-pipelines/variables/_pipelines.ps1 +@@ -1,13 +1,20 @@ +-# This script translates the variables returned by the _all.ps1 script +-# into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. ++<# ++.SYNOPSIS ++ This script translates the variables returned by the _all.ps1 script ++ into commands that instruct Azure Pipelines to actually set those variables for other pipeline tasks to consume. + +-# The build or release definition may have set these variables to override +-# what the build would do. So only set them if they have not already been set. ++ The build or release definition may have set these variables to override ++ what the build would do. So only set them if they have not already been set. ++#> ++ ++[CmdletBinding()] ++param ( ++) + + (& "$PSScriptRoot\_all.ps1").GetEnumerator() |% { + # Always use ALL CAPS for env var names since Azure Pipelines converts variable names to all caps and on non-Windows OS, env vars are case sensitive. + $keyCaps = $_.Key.ToUpper() +- if (Test-Path -Path "env:$keyCaps") { ++ if ((Test-Path "env:$keyCaps") -and (Get-Content "env:$keyCaps")) { + Write-Host "Skipping setting $keyCaps because variable is already set to '$(Get-Content env:$keyCaps)'." -ForegroundColor Cyan + } else { + Write-Host "$keyCaps=$($_.Value)" -ForegroundColor Yellow +diff --git a/benchmark/.editorconfig b/benchmark/.editorconfig +new file mode 100644 +index 00000000..c43f8d7e +--- /dev/null ++++ b/benchmark/.editorconfig +@@ -0,0 +1,2 @@ ++# SA1300: Element should begin with an uppercase letter ++dotnet_diagnostic.SA1300.severity = silent +diff --git a/benchmark/Directory.Build.props b/benchmark/Directory.Build.props +new file mode 100644 +index 00000000..68555510 +--- /dev/null ++++ b/benchmark/Directory.Build.props +@@ -0,0 +1,6 @@ ++ ++ ++ ++ false ++ ++ +diff --git a/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj b/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj +index dbefafaf..08cce028 100644 +--- a/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj ++++ b/benchmark/ExperimentalBenchmark/ExperimentalBenchmark.csproj +@@ -2,16 +2,15 @@ + + + Exe +- netcoreapp3.1 ++ net6.0 + Benchmark + true +- True + $(NoWarn);MSB3243 + + + +- +- ++ ++ + + + +diff --git a/benchmark/ExperimentalBenchmark/Tests.cs b/benchmark/ExperimentalBenchmark/Tests.cs +index ffbfe7d6..e51837b1 100644 +--- a/benchmark/ExperimentalBenchmark/Tests.cs ++++ b/benchmark/ExperimentalBenchmark/Tests.cs +@@ -9,6 +9,7 @@ using System.Linq; + using System.Runtime.InteropServices; + using BenchmarkDotNet.Attributes; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace Benchmark +diff --git a/benchmark/SerializerBenchmark/BenchmarkConfig.cs b/benchmark/SerializerBenchmark/BenchmarkConfig.cs +index e6e7066c..750d4f2d 100644 +--- a/benchmark/SerializerBenchmark/BenchmarkConfig.cs ++++ b/benchmark/SerializerBenchmark/BenchmarkConfig.cs +@@ -42,7 +42,7 @@ namespace Benchmark + { + public bool SeparateLogicalGroups => false; + +- public IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase) ++ public IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase, IEnumerable order) + { + return benchmarksCase; + } +@@ -57,7 +57,7 @@ namespace Benchmark + return null; + } + +- public IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups) ++ public IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups, IEnumerable order) + { + return logicalGroups; + } +diff --git a/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs b/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs +index b73b10e8..0fb2a100 100644 +--- a/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs ++++ b/benchmark/SerializerBenchmark/Fixture/OtherPrimitiveFixtures.cs +@@ -4,6 +4,7 @@ + using System; + using System.Runtime.CompilerServices; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace Benchmark.Fixture +diff --git a/benchmark/SerializerBenchmark/Models/Answer.cs b/benchmark/SerializerBenchmark/Models/Answer.cs +index cf421348..442fe852 100644 +--- a/benchmark/SerializerBenchmark/Models/Answer.cs ++++ b/benchmark/SerializerBenchmark/Models/Answer.cs +@@ -1,6 +1,7 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable IDE1006 + #pragma warning disable SA1516 + +diff --git a/benchmark/SerializerBenchmark/Models/Comment.cs b/benchmark/SerializerBenchmark/Models/Comment.cs +index 1142d728..e923671d 100644 +--- a/benchmark/SerializerBenchmark/Models/Comment.cs ++++ b/benchmark/SerializerBenchmark/Models/Comment.cs +@@ -1,6 +1,7 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable IDE1006 + #pragma warning disable SA1516 + +diff --git a/benchmark/SerializerBenchmark/Models/Feed.cs b/benchmark/SerializerBenchmark/Models/Feed.cs +index d18af036..196b2a4c 100644 +--- a/benchmark/SerializerBenchmark/Models/Feed.cs ++++ b/benchmark/SerializerBenchmark/Models/Feed.cs +@@ -7,6 +7,7 @@ extern alias newmsgpack; + using System.Collections.Generic; + using ProtoBuf; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace Benchmark.Models +diff --git a/benchmark/SerializerBenchmark/Models/ShallowUser.cs b/benchmark/SerializerBenchmark/Models/ShallowUser.cs +index d3faeb43..1c0b0705 100644 +--- a/benchmark/SerializerBenchmark/Models/ShallowUser.cs ++++ b/benchmark/SerializerBenchmark/Models/ShallowUser.cs +@@ -1,6 +1,7 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable IDE1006 + #pragma warning disable SA1516 + +diff --git a/benchmark/SerializerBenchmark/Program.cs b/benchmark/SerializerBenchmark/Program.cs +index c0d23073..880af229 100644 +--- a/benchmark/SerializerBenchmark/Program.cs ++++ b/benchmark/SerializerBenchmark/Program.cs +@@ -10,7 +10,7 @@ namespace Benchmark + private static void Main(string[] args) + { + #if !DEBUG +- //BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); ++ ////BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + BenchmarkRunner.Run(); + #else + BenchmarkRunner.Run(new BenchmarkDotNet.Configs.DebugInProcessConfig()); +diff --git a/benchmark/SerializerBenchmark/SerializerBenchmark.cs b/benchmark/SerializerBenchmark/SerializerBenchmark.cs +index 71a95e20..cd58a164 100644 +--- a/benchmark/SerializerBenchmark/SerializerBenchmark.cs ++++ b/benchmark/SerializerBenchmark/SerializerBenchmark.cs +@@ -11,6 +11,7 @@ using BenchmarkDotNet.Attributes; + #pragma warning disable SA1300 // Element should begin with upper-case letter + #pragma warning disable SA1306 // Field names should begin with lower-case letter + #pragma warning disable SA1401 // Fields should be private ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace Benchmark +@@ -29,8 +30,8 @@ namespace Benchmark + new MessagePackLz4_v1(), + new MessagePackLz4_v2(), + new MsgPack_v2_opt(), +- //new MsgPack_v2_string(), +- //new MsgPack_v2_str_lz4(), ++ ////new MsgPack_v2_string(), ++ ////new MsgPack_v2_str_lz4(), + new ProtobufNetSerializer(), + new JsonNetSerializer(), + new BsonNetSerializer(), +@@ -1135,6 +1136,7 @@ namespace Benchmark + + // models + protected static readonly Benchmark.Models.Answer AnswerInput = ExpressionTreeFixture.Create(); ++ + // not same data so does not gurantee correctly. + protected static readonly Benchmark.Models.Answer2 Answer2Input = ExpressionTreeFixture.Create(); + +diff --git a/benchmark/SerializerBenchmark/SerializerBenchmark.csproj b/benchmark/SerializerBenchmark/SerializerBenchmark.csproj +index b6be2ea0..a6ea965e 100644 +--- a/benchmark/SerializerBenchmark/SerializerBenchmark.csproj ++++ b/benchmark/SerializerBenchmark/SerializerBenchmark.csproj +@@ -2,26 +2,25 @@ + + + Exe +- netcoreapp3.1 ++ net7.0 + SerializerBenchmark + Benchmark + true +- SerializerBenchmark.ruleset ++ $(NoWarn);SA1300 + + + +- +- +- +- +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -41,6 +40,6 @@ + false + + +- ++ + + +diff --git a/benchmark/SerializerBenchmark/SerializerBenchmark.ruleset b/benchmark/SerializerBenchmark/SerializerBenchmark.ruleset +deleted file mode 100644 +index 46e72a17..00000000 +--- a/benchmark/SerializerBenchmark/SerializerBenchmark.ruleset ++++ /dev/null +@@ -1,7 +0,0 @@ +- +- +- +- +- +- +- +diff --git a/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs b/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs +index c54b5487..ec389117 100644 +--- a/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs ++++ b/benchmark/SerializerBenchmark/Serializers/BinaryFormatterSerializer.cs +@@ -6,6 +6,8 @@ using System.Runtime.Serialization.Formatters.Binary; + + namespace Benchmark.Serializers + { ++#pragma warning disable SYSLIB0011 // Type or member is obsolete ++ + public class BinaryFormatterSerializer : SerializerBase + { + public override T Deserialize(object input) +diff --git a/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs b/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs +index 7583e036..f9438c98 100644 +--- a/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs ++++ b/benchmark/SerializerBenchmark/Serializers/MessagePackSerializer.cs +@@ -4,6 +4,7 @@ + extern alias oldmsgpack; + extern alias newmsgpack; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace Benchmark.Serializers +diff --git a/global.json b/global.json +index 954a92e7..c83e939e 100644 +--- a/global.json ++++ b/global.json +@@ -1,6 +1,6 @@ + { + "sdk": { +- "version": "6.0.300", ++ "version": "8.0.300", + "rollForward": "patch", + "allowPrerelease": false + } +diff --git a/init.ps1 b/init.ps1 +index e5aad5bf..ad3b4145 100755 +--- a/init.ps1 ++++ b/init.ps1 +@@ -20,6 +20,9 @@ + Per-machine requires elevation and will download and install all SDKs and runtimes to machine-wide locations so all applications can find it. + .PARAMETER NoPrerequisites + Skips the installation of prerequisite software (e.g. SDKs, tools). ++.PARAMETER NoNuGetCredProvider ++ Skips the installation of the NuGet credential provider. Useful in pipelines with the `NuGetAuthenticate` task, as a workaround for https://github.com/microsoft/artifacts-credprovider/issues/244. ++ This switch is ignored and installation is skipped when -NoPrerequisites is specified. + .PARAMETER UpgradePrerequisites + Takes time to install prerequisites even if they are already present in case they need to be upgraded. + No effect if -NoPrerequisites is specified. +@@ -27,25 +30,35 @@ + Skips the package restore step. + .PARAMETER AccessToken + An optional access token for authenticating to Azure Artifacts authenticated feeds. ++.PARAMETER Interactive ++ Runs NuGet restore in interactive mode. This can turn authentication failures into authentication challenges. + #> +-[CmdletBinding(SupportsShouldProcess=$true)] ++[CmdletBinding(SupportsShouldProcess = $true)] + Param ( +- [ValidateSet('repo','user','machine')] +- [string]$InstallLocality='user', ++ [ValidateSet('repo', 'user', 'machine')] ++ [string]$InstallLocality = 'user', + [Parameter()] + [switch]$NoPrerequisites, + [Parameter()] ++ [switch]$NoNuGetCredProvider, ++ [Parameter()] + [switch]$UpgradePrerequisites, + [Parameter()] + [switch]$NoRestore, + [Parameter()] +- [string]$AccessToken ++ [string]$AccessToken, ++ [Parameter()] ++ [switch]$Interactive + ) + + $EnvVars = @{} ++$PrependPath = @() + + if (!$NoPrerequisites) { +- & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites ++ if (!$NoNuGetCredProvider) { ++ & "$PSScriptRoot\tools\Install-NuGetCredProvider.ps1" -AccessToken $AccessToken -Force:$UpgradePrerequisites ++ } ++ + & "$PSScriptRoot\tools\Install-DotNetSdk.ps1" -InstallLocality $InstallLocality + if ($LASTEXITCODE -eq 3010) { + Exit 3010 +@@ -59,22 +72,28 @@ if (!$NoPrerequisites) { + } + + # Workaround nuget credential provider bug that causes very unreliable package restores on Azure Pipelines +-$env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS=20 +-$env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS=20 ++$env:NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS = 20 ++$env:NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS = 20 + + Push-Location $PSScriptRoot + try { + $HeaderColor = 'Green' + + if (!$NoRestore -and $PSCmdlet.ShouldProcess("NuGet packages", "Restore")) { ++ $RestoreArguments = @() ++ if ($Interactive) ++ { ++ $RestoreArguments += '--interactive' ++ } ++ + Write-Host "Restoring NuGet packages" -ForegroundColor $HeaderColor +- dotnet restore ++ dotnet restore @RestoreArguments + if ($lastexitcode -ne 0) { + throw "Failure while restoring packages." + } + } + +- & "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars | Out-Null ++ & "$PSScriptRoot/tools/Set-EnvVars.ps1" -Variables $EnvVars -PrependPath $PrependPath | Out-Null + } + catch { + Write-Error $error[0] +diff --git a/sandbox/Directory.Build.props b/sandbox/Directory.Build.props +new file mode 100644 +index 00000000..68555510 +--- /dev/null ++++ b/sandbox/Directory.Build.props +@@ -0,0 +1,6 @@ ++ ++ ++ ++ false ++ ++ +diff --git a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj +index 9a41bc14..67607404 100644 +--- a/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj ++++ b/sandbox/DynamicCodeDumper/DynamicCodeDumper.csproj +@@ -2,6 +2,7 @@ + + net472 + true ++ enable + exe + $(DefineConstants);DYNAMICCODEDUMPER + +@@ -45,6 +46,9 @@ + + Code\DynamicAssembly.cs + ++ ++ Code\DynamicAssemblyFactory.cs ++ + + Code\ExpressionUtility.cs + +@@ -69,6 +73,9 @@ + + Code\ExtensionResult.cs + ++ ++ Code\MessagePackEventSource.cs ++ + + Code\MessagePackSerializerOptions.cs + +@@ -146,7 +153,7 @@ + + + +- +- ++ ++ + + +diff --git a/sandbox/DynamicCodeDumper/Program.cs b/sandbox/DynamicCodeDumper/Program.cs +index 0d74db48..d9ec30cb 100644 +--- a/sandbox/DynamicCodeDumper/Program.cs ++++ b/sandbox/DynamicCodeDumper/Program.cs +@@ -1,6 +1,8 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type ++ + using System; + using System.Buffers; + using System.Collections.Generic; +@@ -43,7 +45,7 @@ namespace DynamicCodeDumper + ////DynamicObjectResolver.Instance.GetFormatter(); + ////DynamicObjectResolver.Instance.GetFormatter(); + ////DynamicObjectResolver.Instance.GetFormatter(); +- IMessagePackFormatter f = DynamicObjectResolverAllowPrivate.Instance.GetFormatter(); ++ IMessagePackFormatter f = DynamicObjectResolverAllowPrivate.Instance.GetFormatterWithVerify(); + ////IMessagePackFormatter f = DynamicObjectResolver.Instance.GetFormatter(); + ////DynamicObjectResolver.Instance.GetFormatter(); + ////DynamicObjectResolver.Instance.GetFormatter(); +@@ -114,7 +116,7 @@ namespace DynamicCodeDumper + { + internal static readonly MessagePackSerializerOptions Options = new MessagePackSerializerOptions(new EmptyResolver()); + +- public IMessagePackFormatter GetFormatter() => null; ++ public IMessagePackFormatter? GetFormatter() => null; + } + } + +@@ -127,7 +129,7 @@ namespace DynamicCodeDumper + + [Key(1)] + [MessagePackFormatter(typeof(String_x2Formatter))] +- public string MyProperty2 { get; set; } ++ public string? MyProperty2 { get; set; } + + #pragma warning disable SA1306 // Field names should begin with lower-case letter + [Key(2)] +@@ -334,7 +336,7 @@ namespace DynamicCodeDumper + { + public int MyProperty { get; set; } + +- public string MyProperty2 { get; set; } ++ public string? MyProperty2 { get; set; } + } + + public interface IEntity +@@ -364,7 +366,7 @@ namespace DynamicCodeDumper + + public abstract class EntityBase + { +- public string Name { get; } ++ public string? Name { get; } + + public EntityBase() + { +diff --git a/sandbox/MessagePack.Internal/MessagePack.Internal.csproj b/sandbox/MessagePack.Internal/MessagePack.Internal.csproj +index 28352480..bc7d5d8d 100644 +--- a/sandbox/MessagePack.Internal/MessagePack.Internal.csproj ++++ b/sandbox/MessagePack.Internal/MessagePack.Internal.csproj +@@ -1,7 +1,8 @@ +  + + +- netcoreapp3.1 ++ net6.0 ++ enable + $(DefineConstants);SPAN_BUILTIN;MESSAGEPACK_INTERNAL + true + True +@@ -14,10 +15,6 @@ + + + +- +- +- +- + + + +diff --git a/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs b/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs +index 9f29282d..d07283a1 100644 +--- a/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs ++++ b/sandbox/PerfBenchmarkDotNet/BenchmarkConfig.cs +@@ -18,10 +18,10 @@ namespace PerfBenchmarkDotNet + { + public BenchmarkConfig() + { +- this.Add(MarkdownExporter.GitHub); +- this.Add(MemoryDiagnoser.Default); ++ this.AddExporter(MarkdownExporter.GitHub); ++ this.AddDiagnoser(MemoryDiagnoser.Default); + +- this.Add(Job.ShortRun.With(BenchmarkDotNet.Environments.Platform.X64).WithWarmupCount(1).WithIterationCount(1)); ++ this.AddJob(Job.ShortRun.WithPlatform(BenchmarkDotNet.Environments.Platform.X64).WithWarmupCount(1).WithIterationCount(1)); + + ////Add(Job.ShortRun.With(BenchmarkDotNet.Environments.Platform.X64).WithWarmupCount(1).WithIterationCount(1), + ////Job.ShortRun.With(BenchmarkDotNet.Environments.Platform.X86).WithWarmupCount(1).WithIterationCount(1)); +diff --git a/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs b/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs +index 81f10784..95b69bd5 100644 +--- a/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs ++++ b/sandbox/PerfBenchmarkDotNet/GeneratedFormatter.cs +@@ -6,6 +6,7 @@ extern alias newmsgpack; + #pragma warning disable SA1200 // Using directives should be placed correctly + #pragma warning disable SA1306 // Field names should begin with lower-case letter + #pragma warning disable SA1312 // Variable names should begin with lower-case letter ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1403 // File may only contain a single namespace + #pragma warning disable SA1649 // File name should match first type name + +@@ -596,4 +597,3 @@ namespace GeneratedFormatter + + #pragma warning restore SA1200 // Using directives should be placed correctly + #pragma warning restore SA1403 // File may only contain a single namespace +- +diff --git a/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj b/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj +index 6804253d..4dbab7b2 100644 +--- a/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj ++++ b/sandbox/PerfBenchmarkDotNet/PerfBenchmarkDotNet.csproj +@@ -1,7 +1,7 @@ +  + + Exe +- net472;net6.0 ++ net472;net7.0 + true + true + +@@ -13,18 +13,18 @@ + + + +- +- +- +- +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +diff --git a/sandbox/PerfNetFramework/PerfNetFramework.csproj b/sandbox/PerfNetFramework/PerfNetFramework.csproj +index 57cbfd36..e97936e7 100644 +--- a/sandbox/PerfNetFramework/PerfNetFramework.csproj ++++ b/sandbox/PerfNetFramework/PerfNetFramework.csproj +@@ -1,15 +1,15 @@ +  + + Exe +- net472;net6.0 ++ net472;net7.0 + true + + +- +- +- +- +- ++ ++ ++ ++ ++ + + + +diff --git a/sandbox/PerfNetFramework/README.md b/sandbox/PerfNetFramework/README.md +index 3c202c2b..f91fa842 100644 +--- a/sandbox/PerfNetFramework/README.md ++++ b/sandbox/PerfNetFramework/README.md +@@ -10,7 +10,7 @@ When collecting ETL traces, use these settings in the Collect->Run dialog: + + | Setting | Value | + |-------------|-------| +-| Command | `dotnet run -c release -p .\sandbox\PerfNetFramework\ -f net461 --no-build` ++| Command | `dotnet run -c release -p .\sandbox\PerfNetFramework\ -f net472 --no-build` + | Current Dir | `d:\git\messagepack-csharp` (or wherever your enlistment is) + | Additional Providers | `*MessagePack-Benchmark` + | No V3.X NGen | Checked +diff --git a/sandbox/Sandbox/Generated.cs b/sandbox/Sandbox/Generated.cs +index bc3bcd7a..16b34988 100644 +--- a/sandbox/Sandbox/Generated.cs ++++ b/sandbox/Sandbox/Generated.cs +@@ -344,13 +344,13 @@ namespace MessagePack.Formatters + switch (keyValuePair.Value) + { + case 0: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::TextMessageBody)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::TextMessageBody)value, options); + break; + case 1: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::StampMessageBody)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::StampMessageBody)value, options); + break; + case 2: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::QuestMessageBody)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::QuestMessageBody)value, options); + break; + default: + break; +@@ -386,13 +386,13 @@ namespace MessagePack.Formatters + switch (key) + { + case 0: +- result = (global::IMessageBody)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::IMessageBody)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 1: +- result = (global::IMessageBody)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::IMessageBody)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 2: +- result = (global::IMessageBody)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::IMessageBody)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); +@@ -457,7 +457,7 @@ namespace MessagePack.Formatters.SharedData + switch (keyValuePair.Value) + { + case 0: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); + break; + default: + break; +@@ -493,7 +493,7 @@ namespace MessagePack.Formatters.SharedData + switch (key) + { + case 0: +- result = (global::SharedData.IIVersioningUnion)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IIVersioningUnion)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); +@@ -538,16 +538,16 @@ namespace MessagePack.Formatters.SharedData + switch (keyValuePair.Value) + { + case 0: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); + break; + case 1: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); + break; + case 2: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); + break; + case 3: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); + break; + default: + break; +@@ -583,16 +583,16 @@ namespace MessagePack.Formatters.SharedData + switch (key) + { + case 0: +- result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 1: +- result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 2: +- result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 3: +- result = (global::SharedData.IUnionChecker)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); +@@ -637,16 +637,16 @@ namespace MessagePack.Formatters.SharedData + switch (keyValuePair.Value) + { + case 0: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion2)value, options); + break; + case 1: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion3)value, options); + break; + case 2: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion4)value, options); + break; + case 3: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.MySubUnion1)value, options); + break; + default: + break; +@@ -682,16 +682,16 @@ namespace MessagePack.Formatters.SharedData + switch (key) + { + case 0: +- result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 1: +- result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 2: +- result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 3: +- result = (global::SharedData.IUnionChecker2)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionChecker2)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); +@@ -732,10 +732,10 @@ namespace MessagePack.Formatters.SharedData + switch (keyValuePair.Value) + { + case 0: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.FooClass)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.FooClass)value, options); + break; + case 1: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.BarClass)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.BarClass)value, options); + break; + default: + break; +@@ -771,10 +771,10 @@ namespace MessagePack.Formatters.SharedData + switch (key) + { + case 0: +- result = (global::SharedData.IUnionSample)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionSample)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 1: +- result = (global::SharedData.IUnionSample)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.IUnionSample)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); +@@ -815,10 +815,10 @@ namespace MessagePack.Formatters.SharedData + switch (keyValuePair.Value) + { + case 0: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.SubUnionType1)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.SubUnionType1)value, options); + break; + case 1: +- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, (global::SharedData.SubUnionType2)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Serialize(ref writer, (global::SharedData.SubUnionType2)value, options); + break; + default: + break; +@@ -854,10 +854,10 @@ namespace MessagePack.Formatters.SharedData + switch (key) + { + case 0: +- result = (global::SharedData.RootUnionType)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.RootUnionType)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + case 1: +- result = (global::SharedData.RootUnionType)options.Resolver.GetFormatterWithVerify().Deserialize(ref reader, options); ++ result = (global::SharedData.RootUnionType)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(options.Resolver).Deserialize(ref reader, options); + break; + default: + reader.Skip(); +@@ -3093,6 +3093,53 @@ namespace MessagePack.Formatters.SharedData + } + } + ++ public sealed class SimpleGenericDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter> ++ { ++ ++ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::SharedData.SimpleGenericData value, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (value == null) ++ { ++ writer.WriteNil(); ++ return; ++ } ++ ++ global::MessagePack.IFormatterResolver formatterResolver = options.Resolver; ++ writer.WriteArrayHeader(1); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Value, options); ++ } ++ ++ public global::SharedData.SimpleGenericData Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (reader.TryReadNil()) ++ { ++ return null; ++ } ++ ++ options.Security.DepthStep(ref reader); ++ global::MessagePack.IFormatterResolver formatterResolver = options.Resolver; ++ var length = reader.ReadArrayHeader(); ++ var __Value__ = default(T); ++ ++ for (int i = 0; i < length; i++) ++ { ++ switch (i) ++ { ++ case 0: ++ __Value__ = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options); ++ break; ++ default: ++ reader.Skip(); ++ break; ++ } ++ } ++ ++ var ____result = new global::SharedData.SimpleGenericData(__Value__); ++ reader.Depth--; ++ return ____result; ++ } ++ } ++ + public sealed class SimpleIntKeyDataFormatter : global::MessagePack.Formatters.IMessagePackFormatter + { + +diff --git a/sandbox/Sandbox/Program.cs b/sandbox/Sandbox/Program.cs +index d920a907..9d9ce7e0 100644 +--- a/sandbox/Sandbox/Program.cs ++++ b/sandbox/Sandbox/Program.cs +@@ -24,6 +24,7 @@ using ZeroFormatter; + + #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter + #pragma warning disable SA1401 // Fields should be private ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace Sandbox +@@ -206,7 +207,7 @@ namespace Sandbox + [Key(1)] + public readonly int Y; + +- // can't find matched constructor parameter, parameterType mismatch. type:Point parameterIndex:0 paramterType:ValueTuple`2 ++ // can't find matched constructor parameter, parameterType mismatch. type:Point parameterIndex:0 parameterType:ValueTuple`2 + public Point((int, int) p) + { + this.X = p.Item1; +diff --git a/sandbox/Sandbox/Sandbox.csproj b/sandbox/Sandbox/Sandbox.csproj +index 17da2db3..5ab57a0c 100644 +--- a/sandbox/Sandbox/Sandbox.csproj ++++ b/sandbox/Sandbox/Sandbox.csproj +@@ -2,16 +2,16 @@ + + + Exe +- netcoreapp3.1 ++ net6.0 + True + + + +- +- +- +- +- ++ ++ ++ ++ ++ + + + +diff --git a/sandbox/Sandbox/codegen.ps1 b/sandbox/Sandbox/codegen.ps1 +index 279718f6..d5220129 100644 +--- a/sandbox/Sandbox/codegen.ps1 ++++ b/sandbox/Sandbox/codegen.ps1 +@@ -1 +1 @@ +-dotnet run -f "net6.0" --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/../SharedData/SharedData.csproj" -o "$PSScriptRoot/Generated.cs" ++dotnet run -f net8.0 --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/../SharedData/SharedData.csproj" -o "$PSScriptRoot/Generated.cs" +diff --git a/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj b/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj +index 3743f78a..3b149c7b 100644 +--- a/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj ++++ b/sandbox/TestData.InvalidProject/TestData.InvalidProject.csproj +@@ -1,11 +1,11 @@ +  + +- net461 ++ net462 + + +- +- +- ++ ++ ++ + + + +diff --git a/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj b/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj +index d992280d..26e2e25d 100644 +--- a/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj ++++ b/sandbox/TestData.InvalidSyntax/TestData.InvalidSyntax.csproj +@@ -1,10 +1,10 @@ +  + +- net461 ++ net462 + + +- +- +- ++ ++ ++ + + +diff --git a/sandbox/TestData2/Generated.cs b/sandbox/TestData2/Generated.cs +index 1e65486d..ac86ae02 100644 +--- a/sandbox/TestData2/Generated.cs ++++ b/sandbox/TestData2/Generated.cs +@@ -47,22 +47,28 @@ namespace MessagePack.Resolvers + + static GeneratedResolverGetFormatterHelper() + { +- lookup = new global::System.Collections.Generic.Dictionary(14) +- { +- { typeof(global::System.Collections.Generic.List), 0 }, +- { typeof(global::System.Collections.Generic.List), 1 }, +- { typeof(global::TestData2.Nest1.Id), 2 }, +- { typeof(global::TestData2.Nest2.Id), 3 }, +- { typeof(global::TestData2.A), 4 }, +- { typeof(global::TestData2.B), 5 }, +- { typeof(global::TestData2.C), 6 }, +- { typeof(global::TestData2.Nest1), 7 }, +- { typeof(global::TestData2.Nest1.IdType), 8 }, +- { typeof(global::TestData2.Nest2), 9 }, +- { typeof(global::TestData2.Nest2.IdType), 10 }, +- { typeof(global::TestData2.PropNameCheck1), 11 }, +- { typeof(global::TestData2.PropNameCheck2), 12 }, +- { typeof(global::TestData2.Record), 13 }, ++ lookup = new global::System.Collections.Generic.Dictionary(20) ++ { ++ { typeof(global::System.Collections.Generic.List), 0 }, ++ { typeof(global::System.Collections.Generic.List), 1 }, ++ { typeof(global::System.Collections.Generic.List), 2 }, ++ { typeof(global::System.Collections.Generic.List>), 3 }, ++ { typeof(global::TestData2.NestedGenericTestA), 4 }, ++ { typeof(global::TestData2.NestedGenericTestB), 5 }, ++ { typeof(global::TestData2.Nest1.Id), 6 }, ++ { typeof(global::TestData2.Nest2.Id), 7 }, ++ { typeof(global::TestData2.A), 8 }, ++ { typeof(global::TestData2.B), 9 }, ++ { typeof(global::TestData2.C), 10 }, ++ { typeof(global::TestData2.Nest1), 11 }, ++ { typeof(global::TestData2.Nest1.IdType), 12 }, ++ { typeof(global::TestData2.Nest2), 13 }, ++ { typeof(global::TestData2.Nest2.IdType), 14 }, ++ { typeof(global::TestData2.NestedGenericTestC), 15 }, ++ { typeof(global::TestData2.NullableTest), 16 }, ++ { typeof(global::TestData2.PropNameCheck1), 17 }, ++ { typeof(global::TestData2.PropNameCheck2), 18 }, ++ { typeof(global::TestData2.Record), 19 }, + }; + } + +@@ -76,20 +82,26 @@ namespace MessagePack.Resolvers + + switch (key) + { +- case 0: return new global::MessagePack.Formatters.ListFormatter(); +- case 1: return new global::MessagePack.Formatters.ListFormatter(); +- case 2: return new MessagePack.Formatters.TestData2.Nest1_IdFormatter(); +- case 3: return new MessagePack.Formatters.TestData2.Nest2_IdFormatter(); +- case 4: return new MessagePack.Formatters.TestData2.AFormatter(); +- case 5: return new MessagePack.Formatters.TestData2.BFormatter(); +- case 6: return new MessagePack.Formatters.TestData2.CFormatter(); +- case 7: return new MessagePack.Formatters.TestData2.Nest1Formatter(); +- case 8: return new MessagePack.Formatters.TestData2.Nest1_IdTypeFormatter(); +- case 9: return new MessagePack.Formatters.TestData2.Nest2Formatter(); +- case 10: return new MessagePack.Formatters.TestData2.Nest2_IdTypeFormatter(); +- case 11: return new MessagePack.Formatters.TestData2.PropNameCheck1Formatter(); +- case 12: return new MessagePack.Formatters.TestData2.PropNameCheck2Formatter(); +- case 13: return new MessagePack.Formatters.TestData2.RecordFormatter(); ++ case 0: return new global::MessagePack.Formatters.ListFormatter(); ++ case 1: return new global::MessagePack.Formatters.ListFormatter(); ++ case 2: return new global::MessagePack.Formatters.ListFormatter(); ++ case 3: return new global::MessagePack.Formatters.ListFormatter>(); ++ case 4: return new MessagePack.Formatters.TestData2.NestedGenericTestAFormatter(); ++ case 5: return new MessagePack.Formatters.TestData2.NestedGenericTestBFormatter(); ++ case 6: return new MessagePack.Formatters.TestData2.Nest1_IdFormatter(); ++ case 7: return new MessagePack.Formatters.TestData2.Nest2_IdFormatter(); ++ case 8: return new MessagePack.Formatters.TestData2.AFormatter(); ++ case 9: return new MessagePack.Formatters.TestData2.BFormatter(); ++ case 10: return new MessagePack.Formatters.TestData2.CFormatter(); ++ case 11: return new MessagePack.Formatters.TestData2.Nest1Formatter(); ++ case 12: return new MessagePack.Formatters.TestData2.Nest1_IdTypeFormatter(); ++ case 13: return new MessagePack.Formatters.TestData2.Nest2Formatter(); ++ case 14: return new MessagePack.Formatters.TestData2.Nest2_IdTypeFormatter(); ++ case 15: return new MessagePack.Formatters.TestData2.NestedGenericTestCFormatter(); ++ case 16: return new MessagePack.Formatters.TestData2.NullableTestFormatter(); ++ case 17: return new MessagePack.Formatters.TestData2.PropNameCheck1Formatter(); ++ case 18: return new MessagePack.Formatters.TestData2.PropNameCheck2Formatter(); ++ case 19: return new MessagePack.Formatters.TestData2.RecordFormatter(); + default: return null; + } + } +@@ -564,6 +576,232 @@ namespace MessagePack.Formatters.TestData2 + } + } + ++ public sealed class NestedGenericTestAFormatter : global::MessagePack.Formatters.IMessagePackFormatter> ++ { ++ // Field ++ private static global::System.ReadOnlySpan GetSpan_Field() => new byte[1 + 5] { 165, 70, 105, 101, 108, 100 }; ++ ++ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NestedGenericTestA value, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (value is null) ++ { ++ writer.WriteNil(); ++ return; ++ } ++ ++ var formatterResolver = options.Resolver; ++ writer.WriteMapHeader(1); ++ writer.WriteRaw(GetSpan_Field()); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.Field, options); ++ } ++ ++ public global::TestData2.NestedGenericTestA Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (reader.TryReadNil()) ++ { ++ return null; ++ } ++ ++ options.Security.DepthStep(ref reader); ++ var formatterResolver = options.Resolver; ++ var length = reader.ReadMapHeader(); ++ var ____result = new global::TestData2.NestedGenericTestA(); ++ ++ for (int i = 0; i < length; i++) ++ { ++ var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); ++ switch (stringKey.Length) ++ { ++ default: ++ FAIL: ++ reader.Skip(); ++ continue; ++ case 5: ++ if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != 431315315014UL) { goto FAIL; } ++ ++ ____result.Field = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options); ++ continue; ++ ++ } ++ } ++ ++ reader.Depth--; ++ return ____result; ++ } ++ } ++ ++ public sealed class NestedGenericTestBFormatter : global::MessagePack.Formatters.IMessagePackFormatter> ++ { ++ // Field ++ private static global::System.ReadOnlySpan GetSpan_Field() => new byte[1 + 5] { 165, 70, 105, 101, 108, 100 }; ++ ++ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NestedGenericTestB value, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (value is null) ++ { ++ writer.WriteNil(); ++ return; ++ } ++ ++ var formatterResolver = options.Resolver; ++ writer.WriteMapHeader(1); ++ writer.WriteRaw(GetSpan_Field()); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>>(formatterResolver).Serialize(ref writer, value.Field, options); ++ } ++ ++ public global::TestData2.NestedGenericTestB Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (reader.TryReadNil()) ++ { ++ return null; ++ } ++ ++ options.Security.DepthStep(ref reader); ++ var formatterResolver = options.Resolver; ++ var length = reader.ReadMapHeader(); ++ var ____result = new global::TestData2.NestedGenericTestB(); ++ ++ for (int i = 0; i < length; i++) ++ { ++ var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); ++ switch (stringKey.Length) ++ { ++ default: ++ FAIL: ++ reader.Skip(); ++ continue; ++ case 5: ++ if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != 431315315014UL) { goto FAIL; } ++ ++ ____result.Field = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>>(formatterResolver).Deserialize(ref reader, options); ++ continue; ++ ++ } ++ } ++ ++ reader.Depth--; ++ return ____result; ++ } ++ } ++ ++ public sealed class NestedGenericTestCFormatter : global::MessagePack.Formatters.IMessagePackFormatter ++ { ++ // Field ++ private static global::System.ReadOnlySpan GetSpan_Field() => new byte[1 + 5] { 165, 70, 105, 101, 108, 100 }; ++ ++ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NestedGenericTestC value, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (value is null) ++ { ++ writer.WriteNil(); ++ return; ++ } ++ ++ var formatterResolver = options.Resolver; ++ writer.WriteMapHeader(1); ++ writer.WriteRaw(GetSpan_Field()); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Serialize(ref writer, value.Field, options); ++ } ++ ++ public global::TestData2.NestedGenericTestC Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (reader.TryReadNil()) ++ { ++ return null; ++ } ++ ++ options.Security.DepthStep(ref reader); ++ var formatterResolver = options.Resolver; ++ var length = reader.ReadMapHeader(); ++ var ____result = new global::TestData2.NestedGenericTestC(); ++ ++ for (int i = 0; i < length; i++) ++ { ++ var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); ++ switch (stringKey.Length) ++ { ++ default: ++ FAIL: ++ reader.Skip(); ++ continue; ++ case 5: ++ if (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey) != 431315315014UL) { goto FAIL; } ++ ++ ____result.Field = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Deserialize(ref reader, options); ++ continue; ++ ++ } ++ } ++ ++ reader.Depth--; ++ return ____result; ++ } ++ } ++ ++ public sealed class NullableTestFormatter : global::MessagePack.Formatters.IMessagePackFormatter ++ { ++ // a ++ private static global::System.ReadOnlySpan GetSpan_a() => new byte[1 + 1] { 161, 97 }; ++ // b ++ private static global::System.ReadOnlySpan GetSpan_b() => new byte[1 + 1] { 161, 98 }; ++ ++ public void Serialize(ref global::MessagePack.MessagePackWriter writer, global::TestData2.NullableTest value, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (value is null) ++ { ++ writer.WriteNil(); ++ return; ++ } ++ ++ var formatterResolver = options.Resolver; ++ writer.WriteMapHeader(2); ++ writer.WriteRaw(GetSpan_a()); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Serialize(ref writer, value.a, options); ++ writer.WriteRaw(GetSpan_b()); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Serialize(ref writer, value.b, options); ++ } ++ ++ public global::TestData2.NullableTest Deserialize(ref global::MessagePack.MessagePackReader reader, global::MessagePack.MessagePackSerializerOptions options) ++ { ++ if (reader.TryReadNil()) ++ { ++ return null; ++ } ++ ++ options.Security.DepthStep(ref reader); ++ var formatterResolver = options.Resolver; ++ var length = reader.ReadMapHeader(); ++ var ____result = new global::TestData2.NullableTest(); ++ ++ for (int i = 0; i < length; i++) ++ { ++ var stringKey = global::MessagePack.Internal.CodeGenHelpers.ReadStringSpan(ref reader); ++ switch (stringKey.Length) ++ { ++ default: ++ FAIL: ++ reader.Skip(); ++ continue; ++ case 1: ++ switch (global::MessagePack.Internal.AutomataKeyGen.GetKey(ref stringKey)) ++ { ++ default: goto FAIL; ++ case 97UL: ++ ____result.a = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify(formatterResolver).Deserialize(ref reader, options); ++ continue; ++ case 98UL: ++ ____result.b = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify>(formatterResolver).Deserialize(ref reader, options); ++ continue; ++ } ++ ++ } ++ } ++ ++ reader.Depth--; ++ return ____result; ++ } ++ } ++ + public sealed class PropNameCheck1Formatter : global::MessagePack.Formatters.IMessagePackFormatter + { + // MyProperty1 +diff --git a/sandbox/TestData2/NestedGenerics.cs b/sandbox/TestData2/NestedGenerics.cs +new file mode 100644 +index 00000000..a3e9f549 +--- /dev/null ++++ b/sandbox/TestData2/NestedGenerics.cs +@@ -0,0 +1,28 @@ ++// Copyright (c) All contributors. All rights reserved. ++// Licensed under the MIT license. See LICENSE file in the project root for full license information. ++ ++#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter ++#pragma warning disable SA1401 // Fields should be private ++#pragma warning disable SA1402 // File may only contain a single type ++#pragma warning disable SA1649 // File name should match first type name ++ ++namespace TestData2 ++{ ++ [MessagePackObject(true)] ++ public class NestedGenericTestA ++ { ++ public T Field { get; set; } ++ } ++ ++ [MessagePackObject(true)] ++ public class NestedGenericTestB ++ { ++ public List> Field { get; set; } ++ } ++ ++ [MessagePackObject(true)] ++ public class NestedGenericTestC ++ { ++ public NestedGenericTestB Field { get; set; } ++ } ++} +diff --git a/sandbox/TestData2/NestedTypeCheck.cs b/sandbox/TestData2/NestedTypeCheck.cs +index 3573fb33..59a6f474 100644 +--- a/sandbox/TestData2/NestedTypeCheck.cs ++++ b/sandbox/TestData2/NestedTypeCheck.cs +@@ -5,6 +5,7 @@ using MessagePack; + + #pragma warning disable SA1307 // Accessible fields should begin with upper-case letter + #pragma warning disable SA1401 // Fields should be private ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace TestData2 +diff --git a/sandbox/TestData2/NullableTest.cs b/sandbox/TestData2/NullableTest.cs +new file mode 100644 +index 00000000..64edfbf2 +--- /dev/null ++++ b/sandbox/TestData2/NullableTest.cs +@@ -0,0 +1,17 @@ ++// Copyright (c) All contributors. All rights reserved. ++// Licensed under the MIT license. See LICENSE file in the project root for full license information. ++ ++#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter ++#pragma warning disable SA1401 // Fields should be private ++#pragma warning disable SA1649 // File name should match first type name ++ ++#nullable enable ++ ++namespace TestData2; ++ ++[MessagePackObject(true)] ++public class NullableTest ++{ ++ public int[]? a; ++ public List b = null!; ++} +diff --git a/sandbox/TestData2/TestData2.csproj b/sandbox/TestData2/TestData2.csproj +index a7313049..3d013422 100644 +--- a/sandbox/TestData2/TestData2.csproj ++++ b/sandbox/TestData2/TestData2.csproj +@@ -1,13 +1,13 @@ +  + +- net461 +- 10 ++ net462 + + +- +- +- +- ++ ++ ++ ++ ++ + + + +diff --git a/sandbox/TestData2/codegen.ps1 b/sandbox/TestData2/codegen.ps1 +index 6781688e..b964af89 100644 +--- a/sandbox/TestData2/codegen.ps1 ++++ b/sandbox/TestData2/codegen.ps1 +@@ -1 +1 @@ +-dotnet run -f "net6.0" --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/TestData2.csproj" -o "$PSScriptRoot/Generated.cs" ++dotnet run -f net8.0 --project "$PSScriptRoot/../../src/MessagePack.Generator/MessagePack.Generator.csproj" -- -i "$PSScriptRoot/TestData2.csproj" -o "$PSScriptRoot/Generated.cs" +diff --git a/src/.editorconfig b/src/.editorconfig +new file mode 100644 +index 00000000..e69de29b +diff --git a/src/AssemblyInfo.cs b/src/AssemblyInfo.cs +new file mode 100644 +index 00000000..e083c6a9 +--- /dev/null ++++ b/src/AssemblyInfo.cs +@@ -0,0 +1,6 @@ ++// Copyright (c) All contributors. All rights reserved. ++// Licensed under the MIT license. See LICENSE file in the project root for full license information. ++ ++using System.Runtime.InteropServices; ++ ++[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] +diff --git a/src/Directory.Build.props b/src/Directory.Build.props +new file mode 100644 +index 00000000..199d8a01 +--- /dev/null ++++ b/src/Directory.Build.props +@@ -0,0 +1,8 @@ ++ ++ ++ enable ++ True ++ $(MSBuildThisFileDirectory)..\opensource.snk ++ ++ ++ +diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets +index c983d082..9af35cb0 100644 +--- a/src/Directory.Build.targets ++++ b/src/Directory.Build.targets +@@ -1,12 +1,11 @@ + +- +- +- True +- $(MSBuildThisFileDirectory)..\opensource.snk +- + + + + +- ++ ++ ++ ++ ++ + +diff --git a/src/MessagePack.Annotations/MessagePack.Annotations.csproj b/src/MessagePack.Annotations/MessagePack.Annotations.csproj +index 7561506d..48e95271 100644 +--- a/src/MessagePack.Annotations/MessagePack.Annotations.csproj ++++ b/src/MessagePack.Annotations/MessagePack.Annotations.csproj +@@ -5,7 +5,6 @@ + $(NoWarn);CS0649 + MessagePack + +- true + MessagePack annotations for C# + Attributes and interfaces for .NET types serializable with MessagePack. + MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin +diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj b/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj +index d68ddacd..6203d8a9 100644 +--- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj ++++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePack.AspNetCoreMvcFormatter.csproj +@@ -1,21 +1,20 @@ +  + +- netstandard2.0;netcoreapp3.1 +- true ++ netstandard2.0;net6.0 + ASP.NET Core MVC Input/Output MessagePack formatter + ASP.NET Core MVC Input/Output MessagePack formatter. + MsgPack;MessagePack;Serialization;Formatter;Serializer;aspnetcore;aspnetcoremvc + + + +- ++ + + + + + + +- ++ + + + +@@ -23,4 +22,4 @@ + + + +- +\ No newline at end of file ++ +diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs +index 59c49a99..c2038f5a 100644 +--- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs ++++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackInputFormatter.cs +@@ -9,14 +9,14 @@ namespace MessagePack.AspNetCoreMvcFormatter + public class MessagePackInputFormatter : InputFormatter + { + private const string ContentType = "application/x-msgpack"; +- private readonly MessagePackSerializerOptions options; ++ private readonly MessagePackSerializerOptions? options; + + public MessagePackInputFormatter() + : this(null) + { + } + +- public MessagePackInputFormatter(MessagePackSerializerOptions options) ++ public MessagePackInputFormatter(MessagePackSerializerOptions? options) + { + this.options = options; + +diff --git a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs +index 40998114..fa0f6567 100644 +--- a/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs ++++ b/src/MessagePack.AspNetCoreMvcFormatter/MessagePackOutputFormatter.cs +@@ -9,14 +9,14 @@ namespace MessagePack.AspNetCoreMvcFormatter + public class MessagePackOutputFormatter : OutputFormatter + { + private const string ContentType = "application/x-msgpack"; +- private readonly MessagePackSerializerOptions options; ++ private readonly MessagePackSerializerOptions? options; + + public MessagePackOutputFormatter() + : this(null) + { + } + +- public MessagePackOutputFormatter(MessagePackSerializerOptions options) ++ public MessagePackOutputFormatter(MessagePackSerializerOptions? options) + { + this.options = options; + +@@ -25,55 +25,38 @@ namespace MessagePack.AspNetCoreMvcFormatter + + public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context) + { +- if (context.ObjectType == typeof(object)) ++ if (context.Object == null) + { +- if (context.Object == null) +- { + #if NETSTANDARD2_0 ++ context.HttpContext.Response.Body.WriteByte(MessagePackCode.Nil); ++ return Task.CompletedTask; ++#else ++ var writer = context.HttpContext.Response.BodyWriter; ++ if (writer == null) ++ { + context.HttpContext.Response.Body.WriteByte(MessagePackCode.Nil); + return Task.CompletedTask; +-#else +- var writer = context.HttpContext.Response.BodyWriter; +- if (writer == null) +- { +- context.HttpContext.Response.Body.WriteByte(MessagePackCode.Nil); +- return Task.CompletedTask; +- } +- +- var span = writer.GetSpan(1); +- span[0] = MessagePackCode.Nil; +- writer.Advance(1); +- return writer.FlushAsync().AsTask(); +-#endif + } +- else +- { +-#if NETSTANDARD2_0 +- return MessagePackSerializer.SerializeAsync(context.Object.GetType(), context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); +-#else +- var writer = context.HttpContext.Response.BodyWriter; +- if (writer == null) +- { +- return MessagePackSerializer.SerializeAsync(context.Object.GetType(), context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); +- } + +- MessagePackSerializer.Serialize(context.Object.GetType(), writer, context.Object, this.options, context.HttpContext.RequestAborted); +- return writer.FlushAsync().AsTask(); ++ var span = writer.GetSpan(1); ++ span[0] = MessagePackCode.Nil; ++ writer.Advance(1); ++ return writer.FlushAsync().AsTask(); + #endif +- } + } + else + { ++ var objectType = context.ObjectType == null || context.ObjectType == typeof(object) ? context.Object.GetType() : context.ObjectType; + #if NETSTANDARD2_0 +- return MessagePackSerializer.SerializeAsync(context.ObjectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); ++ return MessagePackSerializer.SerializeAsync(objectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); + #else + var writer = context.HttpContext.Response.BodyWriter; + if (writer == null) + { +- return MessagePackSerializer.SerializeAsync(context.ObjectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); ++ return MessagePackSerializer.SerializeAsync(objectType, context.HttpContext.Response.Body, context.Object, this.options, context.HttpContext.RequestAborted); + } + +- MessagePackSerializer.Serialize(context.ObjectType, writer, context.Object, this.options, context.HttpContext.RequestAborted); ++ MessagePackSerializer.Serialize(objectType, writer, context.Object, this.options, context.HttpContext.RequestAborted); + return writer.FlushAsync().AsTask(); + #endif + } +diff --git a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs +index ab013605..cfa117d3 100644 +--- a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs ++++ b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.cs +@@ -109,6 +109,7 @@ + + using System; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + #pragma warning disable CS0436 // The same name of the primary package + +diff --git a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt +index f7767c9d..3ef9693b 100644 +--- a/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt ++++ b/src/MessagePack.Experimental/HardwareIntrinsics/FormatterHelpers.tt +@@ -127,6 +127,7 @@ int range;#> + + using System; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + #pragma warning disable CS0436 // The same name of the primary package + +diff --git a/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs b/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs +index ce7342f4..34b7a143 100644 +--- a/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs ++++ b/src/MessagePack.Experimental/HardwareIntrinsics/Formatters.cs +@@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; + using System.Runtime.Intrinsics; + using System.Runtime.Intrinsics.X86; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace MessagePack.Formatters +@@ -36,6 +37,7 @@ namespace MessagePack.Formatters + { + const int ShiftCount = 4; + const int Stride = 1 << ShiftCount; ++ + // We enter the SIMD mode when there are more than the Stride after alignment adjustment. + if (inputLength < Stride << 1) + { +@@ -148,6 +150,7 @@ namespace MessagePack.Formatters + { + // Make InputIterator Aligned + var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator); ++ + // When offset is times of 2, you can adjust memory address. + if ((offset & 1) == 0) + { +@@ -275,6 +278,7 @@ namespace MessagePack.Formatters + { + // Make InputIterator Aligned + var offset = UnsafeMemoryAlignmentUtility.CalculateDifferenceAlign16(inputIterator); ++ + // When offset is times of 4, you can adjust memory address. + if ((offset & 3) == 0) + { +@@ -412,6 +416,7 @@ namespace MessagePack.Formatters + { + // new float[] { 1.0, -2.0, 3.5, } is byte[12] { 00, 00, 80, 3f, 00, 00, 00, c0, 00, 00, 60, 40 } in binary expression; + var current = Sse2.LoadVector128((byte*)inputIterator); ++ + // Output binary should be byte[15] { ca, 3f, 80, 00, 00, ca, c0, 00, 00, 00, ca, 40, 60, 00, 00 }; + Sse2.Store(outputIterator, Sse2.Or(Ssse3.Shuffle(current, vectorShuffle), vectorConstant)); + } +@@ -477,8 +482,10 @@ namespace MessagePack.Formatters + { + // Fetch 4 doubles. + var current = Avx.LoadVector256((byte*)inputIterator); ++ + // Reorder Little Endian bytes to Big Endian. + var answer = Avx2.Shuffle(current, vectorShuffle).AsUInt64(); ++ + // Write 4 Big-Endian doubles. + *outputIterator++ = MessagePackCode.Float64; + *(ulong*)outputIterator = answer.GetElement(0); +@@ -595,6 +602,7 @@ namespace MessagePack.Formatters + + // A value of false for the type bool is 0 for the sbyte representation. + var isTrue = Avx2.CompareEqual(current, Vector256.Zero); ++ + // A value of true in the SIMD context is -1 for the sbyte representation. + // True is 0xc3 as MessagePackCode and false is 0xc2. + // Reinterpreted as sbyte values, they are -61 and -62, respectively. +@@ -633,6 +641,7 @@ namespace MessagePack.Formatters + + // A value of false for the type bool is 0 for the sbyte representation. + var isTrue = Sse2.CompareEqual(current, Vector128.Zero); ++ + // A value of true in the SIMD context is -1 for the sbyte representation. + // True is 0xc3 as MessagePackCode and false is 0xc2. + // Reinterpreted as sbyte values, they are -61 and -62, respectively. +diff --git a/src/MessagePack.Experimental/MessagePack.Experimental.csproj b/src/MessagePack.Experimental/MessagePack.Experimental.csproj +index 5053878d..58d6fb68 100644 +--- a/src/MessagePack.Experimental/MessagePack.Experimental.csproj ++++ b/src/MessagePack.Experimental/MessagePack.Experimental.csproj +@@ -1,13 +1,9 @@ + + + +- netcoreapp3.1 ++ net6.0 + true +- 8.0 +- enable +- True + +- true + MessagePack for C#, Experimental Plugins + Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). Experimental implementations. + MsgPack;MessagePack;Serialization;Formatter;Serializer +diff --git a/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs b/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs +index eaf867b5..c65220f2 100644 +--- a/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs ++++ b/src/MessagePack.Experimental/UnsafeUnmanagedStructFormatter/UnsafeUnmanagedStructReadOnlySequenceFormatter.cs +@@ -42,12 +42,18 @@ namespace MessagePack.Formatters + } + else + { ++ var destinationIterator = (byte*)destination; ++ var destinationCapacity = byteCount; + foreach (var sourceMemory in value) + { +- fixed (void* source = &sourceMemory.Span[0]) ++ var sourceSpan = sourceMemory.Span; ++ fixed (void* source = &sourceSpan[0]) + { +- Buffer.MemoryCopy(source, destination, byteCount, byteCount); ++ Buffer.MemoryCopy(source, destinationIterator, destinationCapacity, sourceSpan.Length); + } ++ ++ destinationCapacity -= sourceSpan.Length; ++ destinationIterator += sourceSpan.Length; + } + } + } +diff --git a/src/MessagePack.Generator/MessagePack.Generator.csproj b/src/MessagePack.Generator/MessagePack.Generator.csproj +index 42b02f30..dda450aa 100644 +--- a/src/MessagePack.Generator/MessagePack.Generator.csproj ++++ b/src/MessagePack.Generator/MessagePack.Generator.csproj +@@ -3,10 +3,7 @@ + + mpc + Exe +- netcoreapp3.1;net6.0 +- 10 +- enable +- true ++ net6.0;net8.0 + true + mpc + Major +@@ -19,11 +16,12 @@ + + + +- +- +- +- +- ++ ++ ++ ++ ++ ++ + + + +diff --git a/src/MessagePack.Generator/MessagepackCompiler.cs b/src/MessagePack.Generator/MessagepackCompiler.cs +index dfea8196..17e18f10 100644 +--- a/src/MessagePack.Generator/MessagepackCompiler.cs ++++ b/src/MessagePack.Generator/MessagepackCompiler.cs +@@ -4,6 +4,7 @@ + using System; + using System.Collections.Generic; + using System.IO; ++using System.Linq; + using System.Runtime.Loader; + using System.Threading; + using System.Threading.Tasks; +@@ -90,6 +91,12 @@ namespace MessagePack.Generator + { + var logger = new ConsoleLogger(Microsoft.Build.Framework.LoggerVerbosity.Quiet); + var project = await workspace.OpenProjectAsync(projectPath, logger, null, cancellationToken); ++ if (workspace.Diagnostics.Any(x => x.Kind is WorkspaceDiagnosticKind.Failure)) ++ { ++ throw new InvalidOperationException("One or more errors occured while opening the project:" + Environment.NewLine + ++ string.Join(Environment.NewLine, workspace.Diagnostics.Select(x => $"========================================{Environment.NewLine}{x.Message}{Environment.NewLine}========================================"))); ++ } ++ + var compilation = await project.GetCompilationAsync(cancellationToken); + if (compilation is null) + { +diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs +index 30aaedb9..c7282896 100644 +--- a/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs ++++ b/src/MessagePack.GeneratorCore/CodeAnalysis/Definitions.cs +@@ -5,6 +5,7 @@ using System; + using System.Collections.Generic; + using System.Linq; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace MessagePackCompiler.CodeAnalysis +diff --git a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs +index eeedbdad..5f991d51 100644 +--- a/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs ++++ b/src/MessagePack.GeneratorCore/CodeAnalysis/TypeCollector.cs +@@ -1,6 +1,7 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + using System; +@@ -252,12 +253,15 @@ namespace MessagePackCompiler.CodeAnalysis + private readonly List collectedGenericInfo = new(); + private readonly List collectedUnionInfo = new(); + ++ private readonly Compilation compilation; ++ + public TypeCollector(Compilation compilation, bool disallowInternal, bool isForceUseMap, string[]? ignoreTypeNames, Action logger) + { + this.typeReferences = new ReferenceSymbols(compilation, logger); + this.disallowInternal = disallowInternal; + this.isForceUseMap = isForceUseMap; + this.externalIgnoreTypeNames = new HashSet(ignoreTypeNames ?? Array.Empty()); ++ this.compilation = compilation; + + targetTypes = compilation.GetNamedTypeSymbols() + .Where(x => +@@ -316,7 +320,7 @@ namespace MessagePackCompiler.CodeAnalysis + return; + } + +- var typeSymbolString = typeSymbol.ToString() ?? throw new InvalidOperationException(); ++ var typeSymbolString = typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToString() ?? throw new InvalidOperationException(); + if (this.embeddedTypes.Contains(typeSymbolString)) + { + return; +@@ -329,7 +333,7 @@ namespace MessagePackCompiler.CodeAnalysis + + if (typeSymbol is IArrayTypeSymbol arrayTypeSymbol) + { +- this.CollectArray(arrayTypeSymbol); ++ this.CollectArray((IArrayTypeSymbol)ToTupleUnderlyingType(arrayTypeSymbol)); + return; + } + +@@ -357,13 +361,7 @@ namespace MessagePackCompiler.CodeAnalysis + + if (type.IsGenericType) + { +- this.CollectGeneric(type); +- return; +- } +- +- if (type.TupleUnderlyingType != null) +- { +- CollectGeneric(type.TupleUnderlyingType); ++ this.CollectGeneric((INamedTypeSymbol)ToTupleUnderlyingType(type)); + return; + } + +@@ -459,12 +457,34 @@ namespace MessagePackCompiler.CodeAnalysis + this.collectedGenericInfo.Add(info); + } + ++ private ITypeSymbol ToTupleUnderlyingType(ITypeSymbol typeSymbol) ++ { ++ if (typeSymbol is IArrayTypeSymbol array) ++ { ++ return compilation.CreateArrayTypeSymbol(ToTupleUnderlyingType(array.ElementType), array.Rank); ++ } ++ ++ if (typeSymbol is not INamedTypeSymbol namedType || !namedType.IsGenericType) ++ { ++ return typeSymbol; ++ } ++ ++ namedType = namedType.TupleUnderlyingType ?? namedType; ++ var newTypeArguments = namedType.TypeArguments.Select(ToTupleUnderlyingType).ToArray(); ++ if (!namedType.TypeArguments.SequenceEqual(newTypeArguments)) ++ { ++ return namedType.ConstructedFrom.Construct(newTypeArguments); ++ } ++ ++ return namedType; ++ } ++ + private void CollectGeneric(INamedTypeSymbol type) + { + INamedTypeSymbol genericType = type.ConstructUnboundGenericType(); + var genericTypeString = genericType.ToDisplayString(); + var fullName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); +- var isOpenGenericType = type.TypeArguments.Any(x => x is ITypeParameterSymbol); ++ var isOpenGenericType = IsOpenGenericTypeRecursively(type); + + // special case + if (fullName == "global::System.ArraySegment" || fullName == "global::System.ArraySegment?") +@@ -615,7 +635,14 @@ namespace MessagePackCompiler.CodeAnalysis + } + + var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol; +- var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); ++ var keyAttribute = item.GetAttributes() ++ .FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute)); ++ ++ var stringKey = keyAttribute?.ConstructorArguments.Length > 0 ++ ? keyAttribute.ConstructorArguments[0].Value as string ?? item.Name ++ : item.Name; ++ ++ var member = new MemberSerializationInfo(true, isWritable, isReadable, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + stringMembers.Add(member.StringKey, member); + + this.CollectCore(item.Type); // recursive collect +@@ -641,7 +668,14 @@ namespace MessagePackCompiler.CodeAnalysis + } + + var customFormatterAttr = item.GetAttributes().FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.MessagePackFormatterAttribute))?.ConstructorArguments[0].Value as INamedTypeSymbol; +- var member = new MemberSerializationInfo(false, isWritable, isReadable, hiddenIntKey++, item.Name, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); ++ var keyAttribute = item.GetAttributes() ++ .FirstOrDefault(x => x.AttributeClass.ApproximatelyEqual(this.typeReferences.KeyAttribute)); ++ ++ var stringKey = keyAttribute?.ConstructorArguments.Length > 0 ++ ? keyAttribute.ConstructorArguments[0].Value as string ?? item.Name ++ : item.Name; ++ ++ var member = new MemberSerializationInfo(false, isWritable, isReadable, hiddenIntKey++, stringKey, item.Name, item.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), item.Type.ToDisplayString(BinaryWriteFormat), customFormatterAttr?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + stringMembers.Add(member.StringKey, member); + this.CollectCore(item.Type); // recursive collect + } +@@ -843,7 +877,7 @@ namespace MessagePackCompiler.CodeAnalysis + } + else + { +- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex + " paramterType:" + item.Type.Name); ++ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterIndex:" + ctorParamIndex + " parameterType:" + item.Type.Name); + } + } + } +@@ -864,6 +898,7 @@ namespace MessagePackCompiler.CodeAnalysis + { + IEnumerable> hasKey = constructorLookupDictionary[item.Name]; + using var enumerator = hasKey.GetEnumerator(); ++ + // hasKey.Count() == 0 + if (!enumerator.MoveNext()) + { +@@ -877,12 +912,13 @@ namespace MessagePackCompiler.CodeAnalysis + } + + var first = enumerator.Current.Value; ++ + // hasKey.Count() != 1 + if (enumerator.MoveNext()) + { + if (ctorEnumerator == null) + { +- throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " paramterType:" + item.Type.Name); ++ throw new MessagePackGeneratorResolveFailedException("duplicate matched constructor parameter name:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name); + } + + ctor = null; +@@ -898,7 +934,7 @@ namespace MessagePackCompiler.CodeAnalysis + { + if (ctorEnumerator == null) + { +- throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " paramterType:" + item.Type.Name); ++ throw new MessagePackGeneratorResolveFailedException("can't find matched constructor parameter, parameterType mismatch. type:" + type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + " parameterName:" + item.Name + " parameterType:" + item.Type.Name); + } + + ctor = null; +@@ -1026,5 +1062,10 @@ namespace MessagePackCompiler.CodeAnalysis + + return true; + } ++ ++ private bool IsOpenGenericTypeRecursively(INamedTypeSymbol type) ++ { ++ return type.IsGenericType && type.TypeArguments.Any(x => x is ITypeParameterSymbol || (x is INamedTypeSymbol symbol && IsOpenGenericTypeRecursively(symbol))); ++ } + } + } +diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs +index abc89f78..083c6903 100644 +--- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs ++++ b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.cs +@@ -66,6 +66,17 @@ foreach (var objInfo in ObjectSerializationInfos) { + this.Write("\r\n"); + } + this.Write(" {\r\n"); ++ foreach (var item in objInfo.Members) { ++ if (item.CustomFormatterTypeName != null) { ++ this.Write(" private readonly "); ++ this.Write(this.ToStringHelper.ToStringWithCulture(item.CustomFormatterTypeName)); ++ this.Write(" __"); ++ this.Write(this.ToStringHelper.ToStringWithCulture(item.Name)); ++ this.Write("CustomFormatter__ = new "); ++ this.Write(this.ToStringHelper.ToStringWithCulture(item.CustomFormatterTypeName)); ++ this.Write("();\r\n"); ++ } ++ } + for (var i = 0; i < list.Count; i++) { + var member = list[i].Item1; + var binary = list[i].Item2; +diff --git a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt +index 9f0dac87..6a6c3a8e 100644 +--- a/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt ++++ b/src/MessagePack.GeneratorCore/Generator/StringKey/StringKeyFormatterTemplate.tt +@@ -36,6 +36,11 @@ foreach (var objInfo in ObjectSerializationInfos) { + where <#= typeArg.Name #> : <#= typeArg.Constraints #> + <# }#> + { ++<# foreach (var item in objInfo.Members) { #> ++<# if (item.CustomFormatterTypeName != null) { #> ++ private readonly <#= item.CustomFormatterTypeName #> __<#= item.Name #>CustomFormatter__ = new <#= item.CustomFormatterTypeName #>(); ++<# } #> ++<# } #> + <# for (var i = 0; i < list.Count; i++) { + var member = list[i].Item1; + var binary = list[i].Item2; #> +diff --git a/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs b/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs +index bca5c587..9e04131b 100644 +--- a/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs ++++ b/src/MessagePack.GeneratorCore/Generator/TemplatePartials.cs +@@ -1,6 +1,8 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type ++ + using MessagePackCompiler.CodeAnalysis; + + namespace MessagePackCompiler.Generator +diff --git a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs +index b7a639bf..12e2abb8 100644 +--- a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs ++++ b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.cs +@@ -95,9 +95,10 @@ namespace "); + for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; + this.Write(" case "); + this.Write(this.ToStringHelper.ToStringWithCulture(i)); +- this.Write(":\r\n options.Resolver.GetFormatterWithVerify<"); ++ this.Write(":\r\n global::MessagePack.FormatterResolverExtensions.GetFor" + ++ "matterWithVerify<"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Type)); +- this.Write(">().Serialize(ref writer, ("); ++ this.Write(">(options.Resolver).Serialize(ref writer, ("); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Type)); + this.Write(")value, options);\r\n break;\r\n"); + } +@@ -127,9 +128,10 @@ namespace "); + this.Write(this.ToStringHelper.ToStringWithCulture(i)); + this.Write(":\r\n result = ("); + this.Write(this.ToStringHelper.ToStringWithCulture(info.FullName)); +- this.Write(")options.Resolver.GetFormatterWithVerify<"); ++ this.Write(")global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<"); + this.Write(this.ToStringHelper.ToStringWithCulture(item.Type)); +- this.Write(">().Deserialize(ref reader, options);\r\n break;\r\n"); ++ this.Write(">(options.Resolver).Deserialize(ref reader, options);\r\n break;" + ++ "\r\n"); + } + this.Write(" default:\r\n reader.Skip();\r\n " + + " break;\r\n }\r\n\r\n reader.Depth--;\r\n return result" + +diff --git a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt +index eb9272d0..a3ecb671 100644 +--- a/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt ++++ b/src/MessagePack.GeneratorCore/Generator/UnionTemplate.tt +@@ -51,7 +51,7 @@ namespace <#= Namespace #> + { + <# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #> + case <#= i #>: +- options.Resolver.GetFormatterWithVerify<<#= item.Type #>>().Serialize(ref writer, (<#= item.Type #>)value, options); ++ global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<<#= item.Type #>>(options.Resolver).Serialize(ref writer, (<#= item.Type #>)value, options); + break; + <# } #> + default: +@@ -89,7 +89,7 @@ namespace <#= Namespace #> + { + <# for(var i = 0; i < info.SubTypes.Length; i++) { var item = info.SubTypes[i]; #> + case <#= i #>: +- result = (<#= info.FullName #>)options.Resolver.GetFormatterWithVerify<<#= item.Type #>>().Deserialize(ref reader, options); ++ result = (<#= info.FullName #>)global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<<#= item.Type #>>(options.Resolver).Deserialize(ref reader, options); + break; + <# } #> + default: +diff --git a/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj b/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj +index df4755e2..2714c70f 100644 +--- a/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj ++++ b/src/MessagePack.GeneratorCore/MessagePack.GeneratorCore.csproj +@@ -3,16 +3,12 @@ + + netstandard2.0 + MessagePackCompiler +- +- True +- ..\..\opensource.snk +- 9 +- enable ++ false + + + +- +- ++ ++ + + + +diff --git a/src/MessagePack.GeneratorCore/MessagePack.UniversalCodeGenerator.ruleset b/src/MessagePack.GeneratorCore/MessagePack.UniversalCodeGenerator.ruleset +deleted file mode 100644 +index 75c353d7..00000000 +--- a/src/MessagePack.GeneratorCore/MessagePack.UniversalCodeGenerator.ruleset ++++ /dev/null +@@ -1,31 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj b/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj +index 3a53f9e2..5e579b21 100644 +--- a/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj ++++ b/src/MessagePack.ImmutableCollection/MessagePack.ImmutableCollection.csproj +@@ -3,7 +3,6 @@ + + netstandard2.0 + +- true + MessagePack for C# Extension Support for ImmutableCollection + Deprecated package to add System.Collections.Immutable support to MessagePack. This is now built into the main package. + MsgPack;MessagePack;Serialization;Formatter;Serializer +diff --git a/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj b/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj +index 4eb0b7fc..6f2e11c8 100644 +--- a/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj ++++ b/src/MessagePack.MSBuild.Tasks/MessagePack.MSBuild.Tasks.csproj +@@ -2,8 +2,6 @@ + + + netstandard2.0 +- 8 +- enable + + true + true +@@ -11,7 +9,6 @@ + $(TargetsForTfmSpecificContentInPackage);AddBuildOutputAndDependencies + true + true +- true + MessagePack CodeGenerator Tasks + MSBuild Tasks of MessagePack for C#. + MsgPack;MessagePack;Serialization;Formatter;Serializer;Unity;Xamarin +@@ -26,7 +23,7 @@ + + + +- ++ + + + +diff --git a/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs b/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs +index a3766024..69d8f23e 100644 +--- a/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs ++++ b/src/MessagePack.MSBuild.Tasks/MessagePackGenerator.cs +@@ -20,15 +20,13 @@ namespace MessagePack.MSBuild.Tasks + { + public class MessagePackGenerator : Microsoft.Build.Utilities.Task, ICancelableTask + { +- private const string GeneratedFileName = "mpc_generated.cs"; +- + private readonly CancellationTokenSource cts = new CancellationTokenSource(); + + [Required] + public ITaskItem[] Compile { get; set; } = null!; + + [Required] +- public string IntermediateOutputPath { get; set; } = null!; ++ public string GeneratedOutputPath { get; set; } = null!; + + [Required] + public ITaskItem[] ReferencePath { get; set; } = null!; +@@ -44,9 +42,6 @@ namespace MessagePack.MSBuild.Tasks + + public string[]? ExternalIgnoreTypeNames { get; set; } + +- [Output] +- public string? GeneratedOutputPath { get; set; } +- + internal CancellationToken CancellationToken => this.cts.Token; + + public void Cancel() => this.cts.Cancel(); +@@ -59,8 +54,6 @@ namespace MessagePack.MSBuild.Tasks + return false; + } + +- this.GeneratedOutputPath = Path.Combine(this.IntermediateOutputPath, GeneratedFileName); +- + try + { + var compilation = this.CreateCompilation(); +diff --git a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets b/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets +index 554bc7f4..e95cf9e9 100644 +--- a/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets ++++ b/src/MessagePack.MSBuild.Tasks/build/MessagePack.MSBuild.Tasks.targets +@@ -1,21 +1,28 @@ +  ++ ++ $(IntermediateOutputPath)mpc_generated.cs ++ ++ ++ ++ ++ ++ + + + ++ DependsOnTargets="ResolveReferences" ++ Inputs="@(Compile)" ++ Outputs="$(GeneratedMessagePackFile)"> + + +- +- +- ++ /> + + +diff --git a/src/MessagePack.ReactiveProperty/Formatters.cs b/src/MessagePack.ReactiveProperty/Formatters.cs +index 6374a63b..6a3d4ec2 100644 +--- a/src/MessagePack.ReactiveProperty/Formatters.cs ++++ b/src/MessagePack.ReactiveProperty/Formatters.cs +@@ -8,6 +8,7 @@ using System.Reactive.Concurrency; + using MessagePack.Formatters; + using Reactive.Bindings; + ++#pragma warning disable SA1402 // File may only contain a single type + #pragma warning disable SA1649 // File name should match first type name + + namespace MessagePack.ReactivePropertyExtension +@@ -109,9 +110,9 @@ namespace MessagePack.ReactivePropertyExtension + } + + // [Mode, SchedulerId, Value] : length should be three. +- public class ReactivePropertyFormatter : IMessagePackFormatter> ++ public class ReactivePropertyFormatter : IMessagePackFormatter?> + { +- public void Serialize(ref MessagePackWriter writer, ReactiveProperty value, MessagePackSerializerOptions options) ++ public void Serialize(ref MessagePackWriter writer, ReactiveProperty? value, MessagePackSerializerOptions options) + { + if (value == null) + { +@@ -127,7 +128,7 @@ namespace MessagePack.ReactivePropertyExtension + } + } + +- public ReactiveProperty Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) ++ public ReactiveProperty? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { +@@ -332,9 +333,9 @@ namespace MessagePack.ReactivePropertyExtension + } + + // [Mode, Value] +- public class ReactivePropertySlimFormatter : IMessagePackFormatter> ++ public class ReactivePropertySlimFormatter : IMessagePackFormatter?> + { +- public void Serialize(ref MessagePackWriter writer, ReactivePropertySlim value, MessagePackSerializerOptions options) ++ public void Serialize(ref MessagePackWriter writer, ReactivePropertySlim? value, MessagePackSerializerOptions options) + { + if (value == null) + { +@@ -349,7 +350,7 @@ namespace MessagePack.ReactivePropertyExtension + } + } + +- public ReactivePropertySlim Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) ++ public ReactivePropertySlim? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { +diff --git a/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj b/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj +index f6b54590..c0ffb1ae 100644 +--- a/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj ++++ b/src/MessagePack.ReactiveProperty/MessagePack.ReactiveProperty.csproj +@@ -3,7 +3,6 @@ + + netstandard2.0 + +- true + MessagePack for C# Extension Support for ReactiveProperty + Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). Extension support for ReactiveProperty. + MsgPack;MessagePack;Serialization;Formatter;Serializer;ReactiveProperty +@@ -15,8 +14,7 @@ + + + +- +- ++ + + + +diff --git a/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs b/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs +index 51e30e74..7dd40cf2 100644 +--- a/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs ++++ b/src/MessagePack.ReactiveProperty/ReactivePropertyResolver.cs +@@ -1,6 +1,8 @@ + // Copyright (c) All contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE file in the project root for full license information. + ++#pragma warning disable SA1402 // File may only contain a single type ++ + using System; + using System.Collections.Generic; + using System.Reactive; +@@ -18,18 +20,18 @@ namespace MessagePack.ReactivePropertyExtension + { + } + +- public IMessagePackFormatter GetFormatter() ++ public IMessagePackFormatter? GetFormatter() + { + return FormatterCache.Formatter; + } + + private static class FormatterCache + { +- internal static readonly IMessagePackFormatter Formatter; ++ internal static readonly IMessagePackFormatter? Formatter; + + static FormatterCache() + { +- Formatter = (IMessagePackFormatter)ReactivePropertyResolverGetFormatterHelper.GetFormatter(typeof(T)); ++ Formatter = (IMessagePackFormatter?)ReactivePropertyResolverGetFormatterHelper.GetFormatter(typeof(T)); + } + } + } +@@ -45,7 +47,7 @@ namespace MessagePack.ReactivePropertyExtension + { typeof(ReactivePropertySlim<>), typeof(ReactivePropertySlimFormatter<>) }, + }; + +- internal static object GetFormatter(Type t) ++ internal static object? GetFormatter(Type t) + { + if (t == typeof(Unit)) + { +@@ -71,7 +73,7 @@ namespace MessagePack.ReactivePropertyExtension + return null; + } + +- private static object CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) ++ private static object? CreateInstance(Type genericType, Type[] genericTypeArguments, params object[] arguments) + { + return Activator.CreateInstance(genericType.MakeGenericType(genericTypeArguments), arguments); + } +diff --git a/src/MessagePack.UnityClient/.gitignore b/src/MessagePack.UnityClient/.gitignore +index 7de09ed8..357eef0a 100644 +--- a/src/MessagePack.UnityClient/.gitignore ++++ b/src/MessagePack.UnityClient/.gitignore +@@ -5,3 +5,6 @@ + # Unity wants to see binaries inside its Assets directory. + # We link them in with make_unity_symlink.bat, but we don't want to actually commit them into git. + Assets/*.dll ++ ++# Unity requires .meta files to be committed - unignore the setting from above ++!*.meta +\ No newline at end of file +diff --git a/src/MessagePack.UnityClient/.vsconfig b/src/MessagePack.UnityClient/.vsconfig +new file mode 100644 +index 00000000..d70cd98b +--- /dev/null ++++ b/src/MessagePack.UnityClient/.vsconfig +@@ -0,0 +1,6 @@ ++{ ++ "version": "1.0", ++ "components": [ ++ "Microsoft.VisualStudio.Workload.ManagedGame" ++ ] ++} +diff --git a/src/MessagePack.UnityClient/Assets/Resources.meta b/src/MessagePack.UnityClient/Assets/Resources.meta +new file mode 100644 +index 00000000..73088aba +--- /dev/null ++++ b/src/MessagePack.UnityClient/Assets/Resources.meta +@@ -0,0 +1,8 @@ ++fileFormatVersion: 2 ++guid: b1de8635c42f5144f9c77b537e3a9616 ++folderAsset: yes ++DefaultImporter: ++ externalObjects: {} ++ userData: ++ assetBundleName: ++ assetBundleVariant: +diff --git a/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json +new file mode 100644 +index 00000000..6f4bfb71 +--- /dev/null ++++ b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json +@@ -0,0 +1 @@ ++{"androidStore":"GooglePlay"} +\ No newline at end of file +diff --git a/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json.meta b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json.meta +new file mode 100644 +index 00000000..7a010803 +--- /dev/null ++++ b/src/MessagePack.UnityClient/Assets/Resources/BillingMode.json.meta +@@ -0,0 +1,7 @@ ++fileFormatVersion: 2 ++guid: ae6ce7e0a0dd7ca45b060f370d748bd3 ++TextScriptImporter: ++ externalObjects: {} ++ userData: ++ assetBundleName: ++ assetBundleVariant: +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta +index 049ee197..2e4c0c54 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor.meta +@@ -1,5 +1,5 @@ + fileFormatVersion: 2 +-guid: e64ff5fb973b7104f9c76c166c46f872 ++guid: bc6ad6aaa548c204f9866a24b01d4507 + folderAsset: yes + DefaultImporter: + externalObjects: {} +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta +index 102152e9..82c8e9bb 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/HierarchyTreeBuilder.cs.meta +@@ -1,5 +1,5 @@ + fileFormatVersion: 2 +-guid: 50688156d1ed48e4d8e835a295e75699 ++guid: 8760bbbab905a534eb6fb7b61b736926 + MonoImporter: + externalObjects: {} + serializedVersion: 2 +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs +index 9ec1c545..94508b51 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs +@@ -169,6 +169,8 @@ Switch, + SaveSettings(settings); + } + ++#if !UNITY_2019_2_OR_NEWER ++ + [MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = true, priority = 3)] + static bool ValidateBuildTargetStandaloneLinux() + { +@@ -185,6 +187,8 @@ Switch, + SaveSettings(settings); + } + ++#endif ++ + [MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = true, priority = 4)] + static bool ValidateBuildTargetStandaloneLinux64() + { +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta +index 5aef7c9c..7c991729 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.MenuItems.cs.meta +@@ -1,5 +1,5 @@ + fileFormatVersion: 2 +-guid: 8b7df7a947db56a4389c26cf3022840f ++guid: 12bdad0556e999f4aa82da29415d361f + MonoImporter: + externalObjects: {} + serializedVersion: 2 +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs +index 46da861f..9d7ddeb1 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs +@@ -4,6 +4,7 @@ using RuntimeUnitTestToolkit; + using RuntimeUnitTestToolkit.Editor; + using System; + using UnityEditor; ++using UnityEditor.Build; + using UnityEditor.Build.Reporting; + using UnityEditor.SceneManagement; + using UnityEngine; +@@ -118,7 +119,7 @@ public static partial class UnitTestBuilder + + if (buildPath == null) + { +- buildPath = $"bin/UnitTest/{settings.BuildTarget}_{settings.ScriptBackend}/test" + (IsWindows(settings.BuildTarget) ? ".exe" : ""); ++ buildPath = $"bin/UnitTest/{settings.BuildTarget}_{settings.ScriptBackend}/test" + GetExtensionForBuildTarget(settings.BuildTarget); + } + + var originalScene = SceneManager.GetActiveScene().path; +@@ -397,7 +398,9 @@ public static partial class UnitTestBuilder + } + if (settings.Headless) + { ++#pragma warning disable CS0618 + options |= BuildOptions.EnableHeadlessMode; ++#pragma warning restore CS0618 + } + + var targetGroup = ToBuildTargetGroup(settings.BuildTarget); +@@ -408,6 +411,9 @@ public static partial class UnitTestBuilder + PlayerSettings.SetScriptingBackend(targetGroup, settings.ScriptBackend); + } + ++ // MemoryPack changed: ++ EditorUserBuildSettings.il2CppCodeGeneration = Il2CppCodeGeneration.OptimizeSpeed; //runtime ++ + var buildOptions = new BuildPlayerOptions + { + target = settings.BuildTarget, +@@ -457,20 +463,42 @@ public static partial class UnitTestBuilder + } + } + ++ static string GetExtensionForBuildTarget(BuildTarget buildTarget) ++ { ++ switch (buildTarget) ++ { ++ case BuildTarget.StandaloneWindows: ++ case BuildTarget.StandaloneWindows64: ++ case BuildTarget.WSAPlayer: ++ return ".exe"; ++ case BuildTarget.StandaloneOSX: ++ return ".app"; ++ case BuildTarget.Android: ++ return ".apk"; ++ default: ++ return ""; ++ } ++ } ++ + static BuildTargetGroup ToBuildTargetGroup(BuildTarget buildTarget) + { + #pragma warning disable CS0618 + switch (buildTarget) + { ++#if UNITY_2017_3_OR_NEWER + case BuildTarget.StandaloneOSX: +- case (BuildTarget)3: ++#else + case BuildTarget.StandaloneOSXIntel: + case BuildTarget.StandaloneOSXIntel64: ++ case BuildTarget.StandaloneOSXUniversal: ++#endif // UNITY_2017_3_OR_NEWER + case BuildTarget.StandaloneWindows: + case BuildTarget.StandaloneWindows64: +- case BuildTarget.StandaloneLinux: + case BuildTarget.StandaloneLinux64: ++#if !UNITY_2019_2_OR_NEWER ++ case BuildTarget.StandaloneLinux: + case BuildTarget.StandaloneLinuxUniversal: ++#endif // !UNITY_2019_2_OR_NEWER + return BuildTargetGroup.Standalone; + case (BuildTarget)6: + case (BuildTarget)7: +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta +index 7a1cd450..bf66c58a 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/Editor/UnitTestBuilder.cs.meta +@@ -1,7 +1,8 @@ + fileFormatVersion: 2 +-guid: 4a1edf0e809243a4b95687c2fc05134c ++guid: 3518da33b6245d341a0ef3670ee9268b ++timeCreated: 1488689723 ++licenseType: Pro + MonoImporter: +- externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta +index d9659311..5ad198be 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/RuntimeUnitTestToolkit.asmdef.meta +@@ -1,5 +1,5 @@ + fileFormatVersion: 2 +-guid: e1ad5407f3f9cec45a06a114f01e5d25 ++guid: 14c4fea4b238088479114ba2ffe195f9 + AssemblyDefinitionImporter: + externalObjects: {} + userData: +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs +new file mode 100644 +index 00000000..2e31d3af +--- /dev/null ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs +@@ -0,0 +1,391 @@ ++using NUnit.Framework; ++using System; ++using System.Collections; ++using System.Collections.Generic; ++using System.Linq; ++using System.Reflection; ++using UnityEngine; ++using UnityEngine.TestTools; ++ ++namespace RuntimeUnitTestToolkit ++{ ++ public class UnitTestData ++ { ++ // [SetupFixture][OneTimeSetup] ++ readonly List globalSetups = new List(); ++ // [SetupFixture][OneTimeTearDown] ++ readonly List globalTearDowns = new List(); ++ ++ // Key:Type.FullName ++ readonly Dictionary testGroups = new Dictionary(); ++ ++ public IEnumerable GlobalSetups => globalSetups; ++ public IEnumerable GlobalTearDowns => globalTearDowns; ++ public IEnumerable> TestGroups => testGroups; ++ ++ static IEnumerable GetTestTargetTypes() ++ { ++ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) ++ { ++ var n = assembly.FullName; ++ if (n.StartsWith("UnityEngine")) continue; ++ if (n.StartsWith("mscorlib")) continue; ++ if (n.StartsWith("System")) continue; ++ ++ foreach (var item in assembly.GetTypes()) ++ { ++ SetUpFixtureAttribute setupFixture; ++ try ++ { ++ setupFixture = item.GetCustomAttribute(true); ++ } ++ catch (Exception ex) ++ { ++ Debug.Log("TestAttribute Load Fail, Assembly:" + assembly.FullName); ++ Debug.LogException(ex); ++ goto NEXT_ASSEMBLY; ++ } ++ if (setupFixture != null) ++ { ++ yield return item; ++ continue; ++ } ++ ++ foreach (var method in item.GetMethods()) ++ { ++ TestAttribute t1 = null; ++ try ++ { ++ t1 = method.GetCustomAttribute(true); ++ } ++ catch (Exception ex) ++ { ++ Debug.Log("TestAttribute Load Fail, Assembly:" + assembly.FullName); ++ Debug.LogException(ex); ++ goto NEXT_ASSEMBLY; ++ } ++ if (t1 != null) ++ { ++ yield return item; ++ break; ++ } ++ ++ UnityTestAttribute t2 = null; ++ try ++ { ++ t2 = method.GetCustomAttribute(true); ++ } ++ catch (Exception ex) ++ { ++ Debug.Log("UnityTestAttribute Load Fail, Assembly:" + assembly.FullName); ++ Debug.LogException(ex); ++ goto NEXT_ASSEMBLY; ++ } ++ if (t2 != null) ++ { ++ yield return item; ++ break; ++ } ++ } ++ } ++ ++ NEXT_ASSEMBLY: ++ continue; ++ } ++ } ++ ++ static void RegisterAttributeAction(MethodInfo info, object instance, List list) ++ where T : Attribute ++ { ++ var attr = info.GetCustomAttribute(true); ++ if (attr != null) ++ { ++ list.Add((Action)Delegate.CreateDelegate(typeof(Action), instance, info)); ++ } ++ } ++ ++ public static UnitTestData CreateFromAllAssemblies() ++ { ++ return new UnitTestData(); ++ } ++ ++ UnitTestData() ++ { ++ foreach (var testType in GetTestTargetTypes()) ++ { ++ var setupFixture = testType.GetCustomAttribute(true); ++ if (setupFixture != null) ++ { ++ var instance = Activator.CreateInstance(testType); ++ ++ var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); ++ foreach (var item in methods) ++ { ++ RegisterAttributeAction(item, instance, globalSetups); ++ RegisterAttributeAction(item, instance, globalTearDowns); ++ } ++ continue; ++ } ++ else ++ { ++ var group = new TestGroup(testType); ++ testGroups[testType.FullName] = group; ++ } ++ } ++ } ++ } ++ ++ public class TestGroup ++ { ++ // [Test] ++ List<(string name, Action test)> syncTests = new List<(string name, Action test)>(); ++ ++ // [UnityTest] ++ List<(string name, Func test)> asyncTests = new List<(string name, Func test)>(); ++ ++ // [SetUp] ++ List setups = new List(); ++ ++ // [OneTimeSetUp] ++ List onetimeSetups = new List(); ++ ++ // [TearDown] ++ List tearDowns = new List(); ++ ++ // [OneTimeTearDown] ++ List oneTimeTearDowns = new List(); ++ ++ // [UnitySetUp] ++ List> unitySetUps = new List>(); ++ ++ // [UnityTearDown] ++ List> unityTearDowns = new List>(); ++ ++ public string Name { get; private set; } ++ public IEnumerable<(string name, Action test)> SyncTests => syncTests; ++ public IEnumerable<(string name, Func test)> AsyncTests => asyncTests; ++ public IEnumerable Setups => setups; ++ public IEnumerable OnetimeSetups => onetimeSetups; ++ public IEnumerable TearDowns => tearDowns; ++ public IEnumerable OneTimeTearDowns => oneTimeTearDowns; ++ public IEnumerable> UnitySetUps => unitySetUps; ++ public IEnumerable> UnityTearDowns => unityTearDowns; ++ ++ public IEnumerable<(string name, object test)> Tests ++ { ++ get ++ { ++ foreach (var item in syncTests) yield return (item.name, item.test); ++ foreach (var item in asyncTests) yield return (item.name, item.test); ++ } ++ } ++ ++ static void RegisterAttributeAction(MethodInfo info, object instance, List list) ++ where T : Attribute ++ { ++ var attr = info.GetCustomAttribute(true); ++ if (attr != null) ++ { ++ list.Add((Action)Delegate.CreateDelegate(typeof(Action), instance, info)); ++ } ++ } ++ ++ static void RegisterAttributeAction(MethodInfo info, object instance, List> list) ++ where T : Attribute ++ { ++ var attr = info.GetCustomAttribute(true); ++ if (attr != null) ++ { ++ list.Add((Func)Delegate.CreateDelegate(typeof(Func), instance, info)); ++ } ++ } ++ ++ public TestGroup(Type testType) ++ { ++ this.Name = testType.Name; // not FullName ++ ++ var test = Activator.CreateInstance(testType); ++ var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); ++ ++ foreach (var item in methods) ++ { ++ try ++ { ++ RegisterAttributeAction(item, test, setups); ++ RegisterAttributeAction(item, test, tearDowns); ++ RegisterAttributeAction(item, test, onetimeSetups); ++ RegisterAttributeAction(item, test, oneTimeTearDowns); ++ RegisterAttributeAction(item, test, unitySetUps); ++ RegisterAttributeAction(item, test, unityTearDowns); ++ } ++ catch (Exception e) ++ { ++ UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register setup/teardown method, exception: " + e.ToString()); ++ } ++ ++ try ++ { ++ var iteratorTest = item.GetCustomAttribute(true); ++ if (iteratorTest != null) ++ { ++ if (item.GetParameters().Length == 0 && item.ReturnType == typeof(IEnumerator)) ++ { ++ var factory = (Func)Delegate.CreateDelegate(typeof(Func), test, item); ++ asyncTests.Add((factory.Method.Name, factory)); ++ } ++ else ++ { ++ var testData = GetTestData(item); ++ if (testData.Count != 0) ++ { ++ foreach (var item2 in testData) ++ { ++ Func factory; ++ if (item.IsGenericMethod) ++ { ++ var method2 = InferGenericType(item, item2); ++ factory = () => (IEnumerator)method2.Invoke(test, item2); ++ } ++ else ++ { ++ factory = () => (IEnumerator)item.Invoke(test, item2); ++ } ++ var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; ++ name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); ++ asyncTests.Add((name, factory)); ++ } ++ } ++ else ++ { ++ UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); ++ } ++ } ++ } ++ ++ var standardTest = item.GetCustomAttribute(true); ++ if (standardTest != null) ++ { ++ if (item.GetParameters().Length == 0 && item.ReturnType == typeof(void)) ++ { ++ var invoke = (Action)Delegate.CreateDelegate(typeof(Action), test, item); ++ syncTests.Add((invoke.Method.Name, invoke)); ++ } ++ else ++ { ++ var testData = GetTestData(item); ++ if (testData.Count != 0) ++ { ++ foreach (var item2 in testData) ++ { ++ Action invoke = null; ++ if (item.IsGenericMethod) ++ { ++ var method2 = InferGenericType(item, item2); ++ invoke = () => method2.Invoke(test, item2); ++ } ++ else ++ { ++ invoke = () => item.Invoke(test, item2); ++ } ++ var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; ++ name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); ++ syncTests.Add((name, invoke)); ++ } ++ } ++ else ++ { ++ UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); ++ } ++ } ++ } ++ } ++ catch (Exception e) ++ { ++ UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register method, exception: " + e.ToString()); ++ } ++ } ++ } ++ ++ List GetTestData(MethodInfo methodInfo) ++ { ++ List testCases = new List(); ++ ++ var inlineData = methodInfo.GetCustomAttributes(true); ++ foreach (var item in inlineData) ++ { ++ testCases.Add(item.Arguments); ++ } ++ ++ var sourceData = methodInfo.GetCustomAttributes(true); ++ foreach (var item in sourceData) ++ { ++ var enumerator = GetTestCaseSource(methodInfo, item.SourceType, item.SourceName, item.MethodParams); ++ foreach (var item2 in enumerator) ++ { ++ var item3 = item2 as IEnumerable; // object[][] ++ if (item3 != null) ++ { ++ var l = new List(); ++ foreach (var item4 in item3) ++ { ++ l.Add(item4); ++ } ++ testCases.Add(l.ToArray()); ++ } ++ } ++ } ++ ++ return testCases; ++ } ++ ++ IEnumerable GetTestCaseSource(MethodInfo method, Type sourceType, string sourceName, object[] methodParams) ++ { ++ Type type = sourceType ?? method.DeclaringType; ++ ++ MemberInfo[] member = type.GetMember(sourceName, BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); ++ if (member.Length == 1) ++ { ++ MemberInfo memberInfo = member[0]; ++ FieldInfo fieldInfo = memberInfo as FieldInfo; ++ if ((object)fieldInfo != null) ++ { ++ return (!fieldInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)fieldInfo.GetValue(null)) : ReturnErrorAsParameter("You have specified a data source field but also given a set of parameters. Fields cannot take parameters, please revise the 3rd parameter passed to the TestCaseSourceAttribute and either remove it or specify a method.")); ++ } ++ PropertyInfo propertyInfo = memberInfo as PropertyInfo; ++ if ((object)propertyInfo != null) ++ { ++ return (!propertyInfo.GetGetMethod(nonPublic: true).IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)propertyInfo.GetValue(null, null)) : ReturnErrorAsParameter("You have specified a data source property but also given a set of parameters. Properties cannot take parameters, please revise the 3rd parameter passed to the TestCaseSource attribute and either remove it or specify a method.")); ++ } ++ MethodInfo methodInfo = memberInfo as MethodInfo; ++ if ((object)methodInfo != null) ++ { ++ return (!methodInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null || methodInfo.GetParameters().Length == methodParams.Length) ? ((IEnumerable)methodInfo.Invoke(null, methodParams)) : ReturnErrorAsParameter("You have given the wrong number of arguments to the method in the TestCaseSourceAttribute, please check the number of parameters passed in the object is correct in the 3rd parameter for the TestCaseSourceAttribute and this matches the number of parameters in the target method and try again.")); ++ } ++ } ++ return null; ++ } ++ ++ MethodInfo InferGenericType(MethodInfo methodInfo, object[] parameters) ++ { ++ var set = new HashSet(); ++ List genericParameters = new List(); ++ foreach (var item in methodInfo.GetParameters() ++ .Select((x, i) => new { x.ParameterType, i }) ++ .Where(x => x.ParameterType.IsGenericParameter) ++ .OrderBy(x => x.ParameterType.GenericParameterPosition)) ++ { ++ if (set.Add(item.ParameterType)) // DistinctBy ++ { ++ genericParameters.Add(parameters[item.i].GetType()); ++ } ++ } ++ ++ return methodInfo.MakeGenericMethod(genericParameters.ToArray()); ++ } ++ ++ IEnumerable ReturnErrorAsParameter(string name) ++ { ++ throw new Exception(name); ++ } ++ } ++} +\ No newline at end of file +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs.meta b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs.meta +new file mode 100644 +index 00000000..dc968a32 +--- /dev/null ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestData.cs.meta +@@ -0,0 +1,11 @@ ++fileFormatVersion: 2 ++guid: 9658eff528f61734a84e348d7954e502 ++MonoImporter: ++ externalObjects: {} ++ serializedVersion: 2 ++ defaultReferences: [] ++ executionOrder: 0 ++ icon: {instanceID: 0} ++ userData: ++ assetBundleName: ++ assetBundleVariant: +diff --git a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs +index 8637876c..e05b45c0 100644 +--- a/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs ++++ b/src/MessagePack.UnityClient/Assets/RuntimeUnitTestToolkit/UnitTestRunner.cs +@@ -13,9 +13,7 @@ namespace RuntimeUnitTestToolkit + { + public class UnitTestRunner : MonoBehaviour + { +- // object is IEnumerator or Func +- Dictionary> tests = new Dictionary>(); +- ++ UnitTestData testData; + List additionalActionsOnFirst = new List(); + + public Button clearButton; +@@ -50,21 +48,18 @@ namespace RuntimeUnitTestToolkit + }; + + // register all test types +- foreach (var item in GetTestTargetTypes()) +- { +- RegisterAllMethods(item); +- } ++ testData = UnitTestData.CreateFromAllAssemblies(); + + var executeAll = new List>(); +- foreach (var ___item in tests) ++ foreach (var ___item in testData.TestGroups) + { + var actionList = ___item; // be careful, capture in lambda + +- executeAll.Add(() => StartCoroutine(RunTestInCoroutine(actionList))); +- Add(actionList.Key, () => StartCoroutine(RunTestInCoroutine(actionList))); ++ executeAll.Add(() => StartCoroutine(RunTestInCoroutine(actionList.Key, actionList.Value, true))); ++ Add(actionList.Key, actionList.Value.Name, () => StartCoroutine(RunTestInCoroutine(actionList.Key, actionList.Value, false))); + } + +- var executeAllButton = Add("Run All Tests", () => StartCoroutine(ExecuteAllInCoroutine(executeAll))); ++ var executeAllButton = Add("$<>__AllTest", "Run All Tests", () => StartCoroutine(ExecuteAllInCoroutine(executeAll))); + + clearButton.gameObject.GetComponent().color = new Color(170 / 255f, 170 / 255f, 170 / 255f, 1); + executeAllButton.gameObject.GetComponent().color = new Color(250 / 255f, 150 / 255f, 150 / 255f, 1); +@@ -117,10 +112,10 @@ namespace RuntimeUnitTestToolkit + } + } + +- Button Add(string title, UnityAction test) ++ Button Add(string key, string title, UnityAction test) + { + var newButton = GameObject.Instantiate(clearButton); +- newButton.name = title; ++ newButton.name = key; + newButton.onClick.RemoveAllListeners(); + newButton.GetComponentInChildren().text = title; + newButton.onClick.AddListener(test); +@@ -178,249 +173,16 @@ namespace RuntimeUnitTestToolkit + } + } + +-NEXT_ASSEMBLY: ++ NEXT_ASSEMBLY: + continue; + } + } + +- public void AddTest(string group, string title, Action test, List setups, List teardowns) +- { +- List list; +- if (!tests.TryGetValue(group, out list)) +- { +- list = new List(); +- tests[group] = list; +- } +- +- list.Add(new TestKeyValuePair(title, test, setups, teardowns)); +- } +- +- public void AddAsyncTest(string group, string title, Func asyncTestCoroutine, List setups, List teardowns) +- { +- List list; +- if (!tests.TryGetValue(group, out list)) +- { +- list = new List(); +- tests[group] = list; +- } +- +- list.Add(new TestKeyValuePair(title, asyncTestCoroutine, setups, teardowns)); +- } +- + public void AddCutomAction(string name, UnityAction action) + { + additionalActionsOnFirst.Add(new Pair { Name = name, Action = action }); + } + +- +- public void RegisterAllMethods() +- where T : new() +- { +- RegisterAllMethods(typeof(T)); +- } +- +- public void RegisterAllMethods(Type testType) +- { +- try +- { +- var test = Activator.CreateInstance(testType); +- +- var methods = testType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); +- List setups = new List(); +- List teardowns = new List(); +- foreach (var item in methods) +- { +- try +- { +- var setup = item.GetCustomAttribute(true); +- if (setup != null) +- { +- setups.Add((Action)Delegate.CreateDelegate(typeof(Action), test, item)); +- } +- var teardown = item.GetCustomAttribute(true); +- if (teardown != null) +- { +- teardowns.Add((Action)Delegate.CreateDelegate(typeof(Action), test, item)); +- } +- } +- catch (Exception e) +- { +- UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register setup/teardown method, exception: " + e.ToString()); +- } +- } +- +- foreach (var item in methods) +- { +- try +- { +- var iteratorTest = item.GetCustomAttribute(true); +- if (iteratorTest != null) +- { +- if (item.GetParameters().Length == 0 && item.ReturnType == typeof(IEnumerator)) +- { +- var factory = (Func)Delegate.CreateDelegate(typeof(Func), test, item); +- AddAsyncTest(factory.Target.GetType().Name, factory.Method.Name, factory, setups, teardowns); +- } +- else +- { +- var testData = GetTestData(item); +- if (testData.Count != 0) +- { +- foreach (var item2 in testData) +- { +- Func factory; +- if (item.IsGenericMethod) +- { +- var method2 = InferGenericType(item, item2); +- factory = () => (IEnumerator)method2.Invoke(test, item2); +- } +- else +- { +- factory = () => (IEnumerator)item.Invoke(test, item2); +- } +- var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; +- name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); +- AddAsyncTest(test.GetType().Name, name, factory, setups, teardowns); +- } +- } +- else +- { +- UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); +- } +- } +- } +- +- var standardTest = item.GetCustomAttribute(true); +- if (standardTest != null) +- { +- if (item.GetParameters().Length == 0 && item.ReturnType == typeof(void)) +- { +- var invoke = (Action)Delegate.CreateDelegate(typeof(Action), test, item); +- AddTest(invoke.Target.GetType().Name, invoke.Method.Name, invoke, setups, teardowns); +- } +- else +- { +- var testData = GetTestData(item); +- if (testData.Count != 0) +- { +- foreach (var item2 in testData) +- { +- Action invoke = null; +- if (item.IsGenericMethod) +- { +- var method2 = InferGenericType(item, item2); +- invoke = () => method2.Invoke(test, item2); +- } +- else +- { +- invoke = () => item.Invoke(test, item2); +- } +- var name = item.Name + "(" + string.Join(", ", item2.Select(x => x?.ToString() ?? "null")) + ")"; +- name = name.Replace(Char.MinValue, ' ').Replace(Char.MaxValue, ' ').Replace("<", "[").Replace(">", "]"); +- AddTest(test.GetType().Name, name, invoke, setups, teardowns); +- } +- } +- else +- { +- UnityEngine.Debug.Log(testType.Name + "." + item.Name + " currently does not supported in RuntumeUnitTestToolkit(multiple parameter without TestCase or return type is invalid)."); +- } +- } +- } +- } +- catch (Exception e) +- { +- UnityEngine.Debug.LogError(testType.Name + "." + item.Name + " failed to register method, exception: " + e.ToString()); +- } +- } +- } +- catch (Exception ex) +- { +- Debug.LogException(ex); +- } +- } +- +- List GetTestData(MethodInfo methodInfo) +- { +- List testCases = new List(); +- +- var inlineData = methodInfo.GetCustomAttributes(true); +- foreach (var item in inlineData) +- { +- testCases.Add(item.Arguments); +- } +- +- var sourceData = methodInfo.GetCustomAttributes(true); +- foreach (var item in sourceData) +- { +- var enumerator = GetTestCaseSource(methodInfo, item.SourceType, item.SourceName, item.MethodParams); +- foreach (var item2 in enumerator) +- { +- var item3 = item2 as IEnumerable; // object[][] +- if (item3 != null) +- { +- var l = new List(); +- foreach (var item4 in item3) +- { +- l.Add(item4); +- } +- testCases.Add(l.ToArray()); +- } +- } +- } +- +- return testCases; +- } +- +- IEnumerable GetTestCaseSource(MethodInfo method, Type sourceType, string sourceName, object[] methodParams) +- { +- Type type = sourceType ?? method.DeclaringType; +- +- MemberInfo[] member = type.GetMember(sourceName, BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); +- if (member.Length == 1) +- { +- MemberInfo memberInfo = member[0]; +- FieldInfo fieldInfo = memberInfo as FieldInfo; +- if ((object)fieldInfo != null) +- { +- return (!fieldInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)fieldInfo.GetValue(null)) : ReturnErrorAsParameter("You have specified a data source field but also given a set of parameters. Fields cannot take parameters, please revise the 3rd parameter passed to the TestCaseSourceAttribute and either remove it or specify a method.")); +- } +- PropertyInfo propertyInfo = memberInfo as PropertyInfo; +- if ((object)propertyInfo != null) +- { +- return (!propertyInfo.GetGetMethod(nonPublic: true).IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null) ? ((IEnumerable)propertyInfo.GetValue(null, null)) : ReturnErrorAsParameter("You have specified a data source property but also given a set of parameters. Properties cannot take parameters, please revise the 3rd parameter passed to the TestCaseSource attribute and either remove it or specify a method.")); +- } +- MethodInfo methodInfo = memberInfo as MethodInfo; +- if ((object)methodInfo != null) +- { +- return (!methodInfo.IsStatic) ? ReturnErrorAsParameter("The sourceName specified on a TestCaseSourceAttribute must refer to a static field, property or method.") : ((methodParams == null || methodInfo.GetParameters().Length == methodParams.Length) ? ((IEnumerable)methodInfo.Invoke(null, methodParams)) : ReturnErrorAsParameter("You have given the wrong number of arguments to the method in the TestCaseSourceAttribute, please check the number of parameters passed in the object is correct in the 3rd parameter for the TestCaseSourceAttribute and this matches the number of parameters in the target method and try again.")); +- } +- } +- return null; +- } +- +- MethodInfo InferGenericType(MethodInfo methodInfo, object[] parameters) +- { +- var set = new HashSet(); +- List genericParameters = new List(); +- foreach (var item in methodInfo.GetParameters() +- .Select((x, i) => new { x.ParameterType, i }) +- .Where(x => x.ParameterType.IsGenericParameter) +- .OrderBy(x => x.ParameterType.GenericParameterPosition)) +- { +- if (set.Add(item.ParameterType)) // DistinctBy +- { +- genericParameters.Add(parameters[item.i].GetType()); +- } +- } +- +- return methodInfo.MakeGenericMethod(genericParameters.ToArray()); +- } +- +- IEnumerable ReturnErrorAsParameter(string name) +- { +- throw new Exception(name); +- } +- + System.Collections.IEnumerator ScrollLogToEndNextFrame() + { + yield return null; +@@ -428,13 +190,13 @@ NEXT_ASSEMBLY: + logScrollBar.value = 0; + } + +- IEnumerator RunTestInCoroutine(KeyValuePair> actionList) ++ IEnumerator RunTestInCoroutine(string key, TestGroup testGroup, bool withoutGlobalSetup) + { + Button self = null; + foreach (var btn in list.GetComponentsInChildren