Skip to content

Commit 1af8316

Browse files
committed
Merge remote-tracking branch 'origin/release/8.0' into mbuck/preserve-dom
2 parents b14e58a + 2772a78 commit 1af8316

File tree

120 files changed

+4670
-2507
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+4670
-2507
lines changed

eng/Version.Details.xml

Lines changed: 174 additions & 174 deletions
Large diffs are not rendered by default.

eng/Versions.props

Lines changed: 89 additions & 89 deletions
Large diffs are not rendered by default.

eng/common/cross/toolchain.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ elseif(ILLUMOS)
207207
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp")
208208
elseif(HAIKU)
209209
set(CMAKE_SYSROOT "${CROSS_ROOTFS}")
210+
set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};${CROSS_ROOTFS}/cross-tools-x86_64/bin")
210211

211212
set(TOOLSET_PREFIX ${TOOLCHAIN}-)
212213
function(locate_toolchain_exec exec var)
@@ -217,7 +218,6 @@ elseif(HAIKU)
217218
endif()
218219

219220
find_program(EXEC_LOCATION_${exec}
220-
PATHS "${CROSS_ROOTFS}/cross-tools-x86_64/bin"
221221
NAMES
222222
"${TOOLSET_PREFIX}${exec}${CLR_CMAKE_COMPILER_FILE_NAME_VERSION}"
223223
"${TOOLSET_PREFIX}${exec}")
-1.92 KB
Binary file not shown.

global.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
},
2828
"msbuild-sdks": {
2929
"Yarn.MSBuild": "1.22.10",
30-
"Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23419.1",
31-
"Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23419.1"
30+
"Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23451.1",
31+
"Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23451.1"
3232
}
3333
}

src/Components/Endpoints/src/FormMapping/FormDataResources.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
<value>The value '{0}' is not valid for '{1}'.</value>
125125
</data>
126126
<data name="MappingExceptionMessage" xml:space="preserve">
127-
<value>An error occurred while trying to map a value from form data. For more details, see the 'Error' property and the `InnerException' property.</value>
127+
<value>An error occurred while trying to map a value from form data. For more details, see the 'Error' property and the 'InnerException' property.</value>
128128
</data>
129129
<data name="MaxCollectionSizeReached" xml:space="preserve">
130130
<value>The number of elements in the {0} exceeded the maximum number of '{1}' elements allowed.</value>
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.Serialization;
9+
using Microsoft.Extensions.Internal;
10+
11+
namespace Microsoft.AspNetCore.Components.Endpoints.FormMapping.Metadata;
12+
13+
internal class FormDataMetadataFactory(List<IFormDataConverterFactory> factories)
14+
{
15+
private readonly FormMetadataContext _context = new();
16+
private readonly ParsableConverterFactory _parsableFactory = factories.OfType<ParsableConverterFactory>().Single();
17+
private readonly DictionaryConverterFactory _dictionaryFactory = factories.OfType<DictionaryConverterFactory>().Single();
18+
private readonly CollectionConverterFactory _collectionFactory = factories.OfType<CollectionConverterFactory>().Single();
19+
20+
[RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
21+
[RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
22+
public FormDataTypeMetadata GetOrCreateMetadataFor(Type type, FormDataMapperOptions options)
23+
{
24+
var shouldClearContext = !_context.ResolutionInProgress;
25+
try
26+
{
27+
// We are walking the graph in order to detect recursive types.
28+
// We evaluate whether a type is:
29+
// 1. Primitive
30+
// 2. Dictionary
31+
// 3. Collection
32+
// 4. Complex
33+
// Only complex types can be recursive.
34+
// We only compute metadata when we are dealing with objects, other classes of
35+
// types are handled directly by the appropriate converters.
36+
// We keep track of the metadata for the types because it is useful when we generate
37+
// the specific object converter for a type.
38+
// The code generation varies depending on whether there is recursion or not within
39+
// the type graph.
40+
if (shouldClearContext)
41+
{
42+
_context.BeginResolveGraph();
43+
}
44+
45+
// Try to get the metadata for the type or create and add a new instance.
46+
var result = _context.TypeMetadata.TryGetValue(type, out var value) ? value : new FormDataTypeMetadata(type);
47+
if (value == null)
48+
{
49+
_context.TypeMetadata[type] = result;
50+
}
51+
52+
// Check for cycles and mark any type as recursive if needed.
53+
DetectCyclesAndMarkMetadataTypesAsRecursive(type, result);
54+
55+
// We found the value on the existing metadata, we can return it.
56+
if (value != null)
57+
{
58+
return result;
59+
}
60+
61+
// These blocks are evaluated in a specific order.
62+
if (_parsableFactory.CanConvert(type, options) || type.IsEnum ||
63+
(Nullable.GetUnderlyingType(type) is { } underlyingType &&
64+
_parsableFactory.CanConvert(underlyingType, options)))
65+
{
66+
result.Kind = FormDataTypeKind.Primitive;
67+
return result;
68+
}
69+
70+
if (_dictionaryFactory.CanConvert(type, options))
71+
{
72+
result.Kind = FormDataTypeKind.Dictionary;
73+
var (keyType, valueType) = DictionaryConverterFactory.ResolveDictionaryTypes(type)!;
74+
result.KeyType = GetOrCreateMetadataFor(keyType, options);
75+
result.ValueType = GetOrCreateMetadataFor(valueType, options);
76+
return result;
77+
}
78+
79+
if (_collectionFactory.CanConvert(type, options))
80+
{
81+
result.Kind = FormDataTypeKind.Collection;
82+
result.ElementType = GetOrCreateMetadataFor(CollectionConverterFactory.ResolveElementType(type)!, options);
83+
return result;
84+
}
85+
86+
result.Kind = FormDataTypeKind.Object;
87+
_context.Track(type);
88+
var constructors = type.GetConstructors();
89+
90+
if (constructors.Length == 1)
91+
{
92+
result.Constructor = constructors[0];
93+
}
94+
95+
if (result.Constructor != null)
96+
{
97+
var values = result.Constructor.GetParameters();
98+
99+
foreach (var parameter in values)
100+
{
101+
var parameterTypeInfo = GetOrCreateMetadataFor(parameter.ParameterType, options);
102+
result.ConstructorParameters.Add(new FormDataParameterMetadata(parameter, parameterTypeInfo));
103+
}
104+
}
105+
106+
var candidateProperty = PropertyHelper.GetVisibleProperties(type);
107+
foreach (var propertyHelper in candidateProperty)
108+
{
109+
var property = propertyHelper.Property;
110+
var matchingConstructorParameter = result
111+
.ConstructorParameters
112+
.FirstOrDefault(p => string.Equals(p.Name, property.Name, StringComparison.OrdinalIgnoreCase));
113+
114+
if (matchingConstructorParameter != null)
115+
{
116+
var dataMember = property.GetCustomAttribute<DataMemberAttribute>();
117+
if (dataMember != null && dataMember.IsNameSetExplicitly && dataMember.Name != null)
118+
{
119+
matchingConstructorParameter.Name = dataMember.Name;
120+
}
121+
122+
// The propertyHelper is already present in the constructor, we don't need to add it again.
123+
continue;
124+
}
125+
126+
var ignoreDataMember = property.GetCustomAttribute<IgnoreDataMemberAttribute>();
127+
if (ignoreDataMember != null)
128+
{
129+
// The propertyHelper is marked as ignored, we don't need to add it.
130+
continue;
131+
}
132+
133+
if (property.SetMethod == null || !property.SetMethod.IsPublic)
134+
{
135+
// The property is readonly, we don't need to add it.
136+
continue;
137+
}
138+
139+
var propertyTypeInfo = GetOrCreateMetadataFor(property.PropertyType, options);
140+
var propertyInfo = new FormDataPropertyMetadata(property, propertyTypeInfo);
141+
142+
var dataMemberAttribute = property.GetCustomAttribute<DataMemberAttribute>();
143+
if (dataMemberAttribute != null && dataMemberAttribute.IsNameSetExplicitly && dataMemberAttribute.Name != null)
144+
{
145+
propertyInfo.Name = dataMemberAttribute.Name;
146+
propertyInfo.Required = dataMemberAttribute.IsRequired;
147+
}
148+
149+
var requiredAttribute = property.GetCustomAttribute<RequiredMemberAttribute>();
150+
if (requiredAttribute != null)
151+
{
152+
propertyInfo.Required = true;
153+
}
154+
155+
result.Properties.Add(propertyInfo);
156+
}
157+
158+
return result;
159+
}
160+
finally
161+
{
162+
_context.Untrack(type);
163+
if (shouldClearContext)
164+
{
165+
_context.EndResolveGraph();
166+
}
167+
}
168+
}
169+
170+
internal bool HasMetadataFor(Type type) => _context.TypeMetadata.ContainsKey(type);
171+
172+
private void DetectCyclesAndMarkMetadataTypesAsRecursive(Type type, FormDataTypeMetadata result)
173+
{
174+
// Mark any type as recursive if its already present in the current resolution graph or
175+
// if there is a base type for it on the resolution graph.
176+
// For example, given A and B : A. With A having a propertyHelper of type B.
177+
// when we inspect B, we can tell that A is recursive because you can have A -> B -> B -> B -> ...
178+
// The opposite scenario is not something we need to worry about because we don't support polymorphism,
179+
// meaning that we will never create an instance of B if the declared type is A.
180+
// In that scenario, A and B : A. With B having a propertyHelper of type A.
181+
// The recursion stops at A, because A doesn't define the propertyHelper and we will never bind B to A.
182+
// If in the future we support polymorphism, it's a matter or updating the logic below to account for it.
183+
for (var i = 0; i < _context.CurrentTypes.Count; i++)
184+
{
185+
if (_context.CurrentTypes[i] == type)
186+
{
187+
// We found an exact match in the current resolution graph.
188+
// This means that the type is recursive.
189+
result.IsRecursive = true;
190+
}
191+
else if (type.IsSubclassOf(_context.CurrentTypes[i]))
192+
{
193+
// We found a type that is assignable from the current type.
194+
// This means that the type is recursive.
195+
// The type must have already been registered in DI.
196+
var existingType = _context.TypeMetadata[_context.CurrentTypes[i]];
197+
existingType.IsRecursive = true;
198+
}
199+
}
200+
}
201+
202+
private class FormMetadataContext
203+
{
204+
public Dictionary<Type, FormDataTypeMetadata> TypeMetadata { get; set; } = new();
205+
206+
public List<Type> CurrentTypes { get; set; } = new();
207+
208+
public bool ResolutionInProgress { get; internal set; }
209+
210+
internal void BeginResolveGraph()
211+
{
212+
if (ResolutionInProgress)
213+
{
214+
throw new InvalidOperationException("Cannot begin a new resolution graph while one is already in progress.");
215+
}
216+
ResolutionInProgress = true;
217+
}
218+
219+
internal void EndResolveGraph()
220+
{
221+
if (!ResolutionInProgress)
222+
{
223+
throw new InvalidOperationException("Cannot end a resolution graph while one is not in progress.");
224+
}
225+
ResolutionInProgress = false;
226+
CurrentTypes.Clear();
227+
}
228+
229+
internal void Track(Type type)
230+
{
231+
CurrentTypes.Add(type);
232+
}
233+
234+
internal void Untrack(Type type)
235+
{
236+
CurrentTypes.Remove(type);
237+
}
238+
}
239+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Components.Endpoints.FormMapping.Metadata;
5+
6+
internal enum FormDataTypeKind
7+
{
8+
Primitive,
9+
Collection,
10+
Dictionary,
11+
Object,
12+
}

0 commit comments

Comments
 (0)