From d1b0e2bfd03488d6f8b6b709607d3fbc2587a3c1 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 11 Oct 2018 18:12:53 -0700 Subject: [PATCH] Teach property helper to ignore ref-struct Fixes: https://github.com/aspnet/Mvc/issues/8552 --- .../PropertyHelper.cs | 29 +++++++++++++++-- .../PropertyHelperTest.cs | 32 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/shared/Microsoft.Extensions.PropertyHelper.Sources/PropertyHelper.cs b/shared/Microsoft.Extensions.PropertyHelper.Sources/PropertyHelper.cs index 27ba5661a42..f6aad151e52 100644 --- a/shared/Microsoft.Extensions.PropertyHelper.Sources/PropertyHelper.cs +++ b/shared/Microsoft.Extensions.PropertyHelper.Sources/PropertyHelper.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace Microsoft.Extensions.Internal { @@ -37,6 +38,12 @@ internal class PropertyHelper private static readonly ConcurrentDictionary VisiblePropertiesCache = new ConcurrentDictionary(); + // We need to be able to check if a type is a 'ref struct' - but we need to be able to compile + // for platforms where the attribute is not defined, like net46. So we can fetch the attribute + // by late binding. If the attribute isn't defined, then we assume we won't encounter any + // 'ref struct' types. + private static readonly Type IsByRefLikeAttribute = Type.GetType("System.Runtime.CompilerServices.IsByRefLikeAttribute", throwOnError: false); + private Action _valueSetter; private Func _valueGetter; @@ -511,16 +518,34 @@ protected static PropertyHelper[] GetProperties( return helpers; } - // Indexed properties are not useful (or valid) for grabbing properties off an object. + private static bool IsInterestingProperty(PropertyInfo property) { // For improving application startup time, do not use GetIndexParameters() api early in this check as it // creates a copy of parameter array and also we would like to check for the presence of a get method // and short circuit asap. - return property.GetMethod != null && + return + property.GetMethod != null && property.GetMethod.IsPublic && !property.GetMethod.IsStatic && + + // PropertyHelper can't work with ref structs. + !IsRefStructProperty(property) && + + // Indexed properties are not useful (or valid) for grabbing properties off an object. property.GetMethod.GetParameters().Length == 0; } + + // PropertyHelper can't really interact with ref-struct properties since they can't be + // boxed and can't be used as generic types. We just ignore them. + // + // see: https://github.com/aspnet/Mvc/issues/8545 + private static bool IsRefStructProperty(PropertyInfo property) + { + return + IsByRefLikeAttribute != null && + property.PropertyType.IsValueType && + property.PropertyType.IsDefined(IsByRefLikeAttribute); + } } } diff --git a/test/Microsoft.Extensions.Internal.Test/PropertyHelperTest.cs b/test/Microsoft.Extensions.Internal.Test/PropertyHelperTest.cs index 19cf08b3705..1c43dc880b3 100644 --- a/test/Microsoft.Extensions.Internal.Test/PropertyHelperTest.cs +++ b/test/Microsoft.Extensions.Internal.Test/PropertyHelperTest.cs @@ -153,6 +153,22 @@ public void PropertyHelper_DoesNotFindStaticProperties() Assert.Equal("Prop5", helper.Name); } +#if NETSTANDARD || NETCOREAPP + [Fact] + public void PropertyHelper_RefStructProperties() + { + // Arrange + var obj = new RefStructProperties(); + + // Act + Assert + var helper = Assert.Single(PropertyHelper.GetProperties(obj.GetType().GetTypeInfo())); + Assert.Equal("Prop5", helper.Name); + } +#elif NET46 || NET461 +#else +#error Unknown TFM - update the set of TFMs where we test for ref structs +#endif + [Fact] public void PropertyHelper_DoesNotFindSetOnlyProperties() { @@ -718,6 +734,22 @@ private class Static public int Prop5 { get; set; } } +#if NETSTANDARD || NETCOREAPP + private class RefStructProperties + { + public Span Span => throw new NotImplementedException(); + public MyRefStruct UserDefined => throw new NotImplementedException(); + + public int Prop5 { get; set; } + } + + private readonly ref struct MyRefStruct + { + } +#elif NET46 || NET461 +#else +#error Unknown TFM - update the set of TFMs where we test for ref structs +#endif private struct MyProperties { public int IntProp { get; set; }