Create an abstraction model for TypeInfo for tag helper discovery.

This commit is contained in:
Pranav K 2015-08-10 16:33:04 -07:00
parent 08c8f9f7ba
commit 231e8a9cf4
15 changed files with 958 additions and 92 deletions

View File

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
/// <summary>
/// Metadata common to types and properties.
/// </summary>
public interface IMemberInfo
{
/// <summary>
/// Gets the name.
/// </summary>
string Name { get; }
/// <summary>
/// Retrieves a collection of custom <see cref="Attribute"/>s of type <typeparamref name="TAttribute"/> applied
/// to this instance of <see cref="IMemberInfo"/>.
/// </summary>
/// <typeparam name="TAttribute">The type of <see cref="Attribute"/> to search for.</typeparam>
/// <returns>A sequence of custom <see cref="Attribute"/>s of type
/// <typeparamref name="TAttribute"/>.</returns>
/// <remarks>Result not include inherited <see cref="Attribute"/>s.</remarks>
IEnumerable<TAttribute> GetCustomAttributes<TAttribute>()
where TAttribute : Attribute;
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
/// <summary>
/// Contains property metadata.
/// </summary>
public interface IPropertyInfo : IMemberInfo
{
/// <summary>
/// Gets a value indicating whether this property has a public getter.
/// </summary>
bool HasPublicGetter { get; }
/// <summary>
/// Gets a value indicating whether this property has a public setter.
/// </summary>
bool HasPublicSetter { get; }
/// <summary>
/// Gets the <see cref="ITypeInfo"/> of the property.
/// </summary>
ITypeInfo PropertyType { get; }
}
}

View File

@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
/// <summary>
/// Contains type metadata.
/// </summary>
public interface ITypeInfo : IMemberInfo
{
/// <summary>
/// Fully qualified name of the type.
/// </summary>
string FullName { get; }
/// <summary>
/// Gets <see cref="IPropertyInfo"/>s for all properties of the current type excluding indexers.
/// </summary>
/// <remarks>
/// Indexers in this context refer to the CLR notion of an indexer (<c>this [string name]</c>
/// and does not overlap with the semantics of
/// <see cref="Razor.TagHelpers.TagHelperAttributeDescriptor.IsIndexer"/>.
/// </remarks>
IEnumerable<IPropertyInfo> Properties { get; }
/// <summary>
/// Gets a value indicating whether the type is public.
/// </summary>
bool IsPublic { get; }
/// <summary>
/// Gets a value indicating whether the type is abstract or an interface.
/// </summary>
bool IsAbstract { get; }
/// <summary>
/// Gets a value indicating whether the type is generic.
/// </summary>
bool IsGenericType { get; }
/// <summary>
/// Gets a value indicating whether the type implements the <see cref="ITagHelper"/> interface.
/// </summary>
bool IsTagHelper { get; }
/// <summary>
/// Gets the full names of the parameter types if the type implements
/// <see cref="IDictionary{TKey, TValue}"/>.
/// </summary>
/// <returns>
/// The full type names (<seealso cref="System.Type.FullName"/>) of <c>TKey</c> and <c>TValue</c>
/// parameters if the type implements <see cref="IDictionary{TKey, TValue}"/>, otherwise <c>null</c>.
/// </returns>
/// <remarks>
/// For open generic types, full type names for generic type parameters is <c>null</c>.
/// </remarks>
string[] GetGenericDictionaryParameterNames();
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime
{
/// <summary>
/// <see cref="IPropertyInfo"/> adapter for <see cref="PropertyInfo"/> instances.
/// </summary>
public class RuntimePropertyInfo : IPropertyInfo
{
/// <summary>
/// Initializes a new instance of <see cref="RuntimePropertyInfo"/>.
/// </summary>
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> instance to adapt.</param>
public RuntimePropertyInfo([NotNull] PropertyInfo propertyInfo)
{
Property = propertyInfo;
}
/// <summary>
/// The <see cref="PropertyInfo"/> instance.
/// </summary>
public PropertyInfo Property { get; }
/// <inheritdoc />
public bool HasPublicGetter => Property.GetMethod != null && Property.GetMethod.IsPublic;
/// <inheritdoc />
public bool HasPublicSetter => Property.SetMethod != null && Property.SetMethod.IsPublic;
/// <inheritdoc />
public string Name => Property.Name;
/// <inheritdoc />
public ITypeInfo PropertyType => new RuntimeTypeInfo(Property.PropertyType.GetTypeInfo());
/// <inheritdoc />
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>() where TAttribute : Attribute
=> Property.GetCustomAttributes<TAttribute>(inherit: false);
/// <inheritdoc />
public override string ToString() =>
Property.ToString();
}
}

View File

@ -0,0 +1,88 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
/// <summary>
/// <see cref="ITypeInfo"/> adapter for <see cref="System.Reflection.TypeInfo"/> instances.
/// </summary>
public class RuntimeTypeInfo : ITypeInfo
{
private static readonly TypeInfo TagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
private IEnumerable<IPropertyInfo> _properties;
/// <summary>
/// Initializes a new instance of <see cref="RuntimeTypeInfo"/>
/// </summary>
/// <param name="propertyInfo">The <see cref="System.Reflection.TypeInfo"/> instance to adapt.</param>
public RuntimeTypeInfo([NotNull] TypeInfo typeInfo)
{
TypeInfo = typeInfo;
}
/// <summary>
/// The <see cref="System.Reflection.TypeInfo"/> instance.
/// </summary>
public TypeInfo TypeInfo { get; }
/// <inheritdoc />
public string Name => TypeInfo.Name;
/// <inheritdoc />
public string FullName => TypeInfo.FullName;
/// <inheritdoc />
public bool IsAbstract => TypeInfo.IsAbstract;
/// <inheritdoc />
public bool IsGenericType => TypeInfo.IsGenericType;
/// <inheritdoc />
public bool IsPublic => TypeInfo.IsPublic;
/// <inheritdoc />
public IEnumerable<IPropertyInfo> Properties
{
get
{
if (_properties == null)
{
_properties = TypeInfo
.AsType()
.GetRuntimeProperties()
.Where(property => property.GetIndexParameters().Length == 0)
.Select(property => new RuntimePropertyInfo(property));
}
return _properties;
}
}
/// <inheritdoc />
public bool IsTagHelper => TagHelperTypeInfo.IsAssignableFrom(TypeInfo);
/// <inheritdoc />
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>() where TAttribute : Attribute =>
TypeInfo.GetCustomAttributes<TAttribute>(inherit: false);
/// <inheritdoc />
public string[] GetGenericDictionaryParameterNames()
{
return ClosedGenericMatcher.ExtractGenericInterface(
TypeInfo.AsType(),
typeof(IDictionary<,>))
?.GenericTypeArguments
.Select(type => type.FullName)
.ToArray();
}
/// <inheritdoc />
public override string ToString() => TypeInfo.ToString();
}
}

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
@ -13,7 +12,7 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
/// <summary> /// <summary>
/// Factory for <see cref="TagHelperDescriptor"/>s from <see cref="Type"/>s. /// Factory for <see cref="TagHelperDescriptor"/>s from <see cref="ITypeInfo"/>s.
/// </summary> /// </summary>
public static class TagHelperDescriptorFactory public static class TagHelperDescriptorFactory
{ {
@ -35,31 +34,30 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' }); new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
/// <summary> /// <summary>
/// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="type"/>. /// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="typeInfo"/>.
/// </summary> /// </summary>
/// <param name="assemblyName">The assembly name that contains <paramref name="type"/>.</param> /// <param name="assemblyName">The assembly name that contains <paramref name="type"/>.</param>
/// <param name="type">The type to create a <see cref="TagHelperDescriptor"/> from.</param> /// <param name="typeInfo">The <see cref="ITypeInfo"/> to create a <see cref="TagHelperDescriptor"/> from.
/// </param>
/// <param name="designTime">Indicates if the returned <see cref="TagHelperDescriptor"/>s should include /// <param name="designTime">Indicates if the returned <see cref="TagHelperDescriptor"/>s should include
/// design time specific information.</param> /// design time specific information.</param>
/// <param name="errorSink">The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered /// <param name="errorSink">The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered
/// when creating <see cref="TagHelperDescriptor"/>s for the given <paramref name="type"/>.</param> /// when creating <see cref="TagHelperDescriptor"/>s for the given <paramref name="typeInfo"/>.</param>
/// <returns> /// <returns>
/// A collection of <see cref="TagHelperDescriptor"/>s that describe the given <paramref name="type"/>. /// A collection of <see cref="TagHelperDescriptor"/>s that describe the given <paramref name="typeInfo"/>.
/// </returns> /// </returns>
public static IEnumerable<TagHelperDescriptor> CreateDescriptors( public static IEnumerable<TagHelperDescriptor> CreateDescriptors(
string assemblyName, string assemblyName,
[NotNull] Type type, [NotNull] ITypeInfo typeInfo,
bool designTime, bool designTime,
[NotNull] ErrorSink errorSink) [NotNull] ErrorSink errorSink)
{ {
var typeInfo = type.GetTypeInfo();
if (ShouldSkipDescriptorCreation(designTime, typeInfo)) if (ShouldSkipDescriptorCreation(designTime, typeInfo))
{ {
return Enumerable.Empty<TagHelperDescriptor>(); return Enumerable.Empty<TagHelperDescriptor>();
} }
var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink); var attributeDescriptors = GetAttributeDescriptors(typeInfo, designTime, errorSink);
var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink); var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink);
var allowedChildren = GetAllowedChildren(typeInfo, errorSink); var allowedChildren = GetAllowedChildren(typeInfo, errorSink);
@ -76,16 +74,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
} }
private static IEnumerable<TargetElementAttribute> GetValidTargetElementAttributes( private static IEnumerable<TargetElementAttribute> GetValidTargetElementAttributes(
TypeInfo typeInfo, ITypeInfo typeInfo,
ErrorSink errorSink) ErrorSink errorSink)
{ {
var targetElementAttributes = typeInfo.GetCustomAttributes<TargetElementAttribute>(inherit: false); var targetElementAttributes = typeInfo.GetCustomAttributes<TargetElementAttribute>();
return targetElementAttributes.Where(attribute => ValidTargetElementAttributeNames(attribute, errorSink)); return targetElementAttributes.Where(attribute => ValidTargetElementAttributeNames(attribute, errorSink));
} }
private static IEnumerable<TagHelperDescriptor> BuildTagHelperDescriptors( private static IEnumerable<TagHelperDescriptor> BuildTagHelperDescriptors(
TypeInfo typeInfo, ITypeInfo typeInfo,
string assemblyName, string assemblyName,
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors, IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
IEnumerable<TargetElementAttribute> targetElementAttributes, IEnumerable<TargetElementAttribute> targetElementAttributes,
@ -97,7 +95,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
#if !DNXCORE50 #if !DNXCORE50
if (designTime) if (designTime)
{ {
typeDesignTimeDescriptor = TagHelperDesignTimeDescriptorFactory.CreateDescriptor(typeInfo.AsType()); var runtimeTypeInfo = typeInfo as RuntimeTypeInfo;
if (runtimeTypeInfo != null)
{
typeDesignTimeDescriptor =
TagHelperDesignTimeDescriptorFactory.CreateDescriptor(runtimeTypeInfo.TypeInfo.AsType());
}
} }
#endif #endif
@ -138,9 +141,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
typeDesignTimeDescriptor)); typeDesignTimeDescriptor));
} }
private static IEnumerable<string> GetAllowedChildren(TypeInfo typeInfo, ErrorSink errorSink) private static IEnumerable<string> GetAllowedChildren(ITypeInfo typeInfo, ErrorSink errorSink)
{ {
var restrictChildrenAttribute = typeInfo.GetCustomAttribute<RestrictChildrenAttribute>(inherit: false); var restrictChildrenAttribute = typeInfo
.GetCustomAttributes<RestrictChildrenAttribute>()
.FirstOrDefault();
if (restrictChildrenAttribute == null) if (restrictChildrenAttribute == null)
{ {
return null; return null;
@ -338,7 +343,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
} }
private static IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors( private static IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors(
Type type, ITypeInfo type,
bool designTime, bool designTime,
ErrorSink errorSink) ErrorSink errorSink)
{ {
@ -347,7 +352,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Keep indexer descriptors separate to avoid sorting the combined list later. // Keep indexer descriptors separate to avoid sorting the combined list later.
var indexerDescriptors = new List<TagHelperAttributeDescriptor>(); var indexerDescriptors = new List<TagHelperAttributeDescriptor>();
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty); var accessibleProperties = type.Properties.Where(IsAccessibleProperty);
foreach (var property in accessibleProperties) foreach (var property in accessibleProperties)
{ {
if (ShouldSkipDescriptorCreation(designTime, property)) if (ShouldSkipDescriptorCreation(designTime, property))
@ -355,13 +360,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
continue; continue;
} }
var attributeNameAttribute = property.GetCustomAttribute<HtmlAttributeNameAttribute>(inherit: false); var attributeNameAttribute = property
.GetCustomAttributes<HtmlAttributeNameAttribute>()
.FirstOrDefault();
var hasExplicitName = var hasExplicitName =
attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name); attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name);
var attributeName = hasExplicitName ? attributeNameAttribute.Name : ToHtmlCase(property.Name); var attributeName = hasExplicitName ? attributeNameAttribute.Name : ToHtmlCase(property.Name);
TagHelperAttributeDescriptor mainDescriptor = null; TagHelperAttributeDescriptor mainDescriptor = null;
if (property.SetMethod?.IsPublic == true) if (property.HasPublicSetter)
{ {
mainDescriptor = ToAttributeDescriptor(property, attributeName, designTime); mainDescriptor = ToAttributeDescriptor(property, attributeName, designTime);
if (!ValidateTagHelperAttributeDescriptor(mainDescriptor, type, errorSink)) if (!ValidateTagHelperAttributeDescriptor(mainDescriptor, type, errorSink))
@ -425,7 +432,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Internal for testing. // Internal for testing.
internal static bool ValidateTagHelperAttributeDescriptor( internal static bool ValidateTagHelperAttributeDescriptor(
TagHelperAttributeDescriptor attributeDescriptor, TagHelperAttributeDescriptor attributeDescriptor,
Type parentType, ITypeInfo parentType,
ErrorSink errorSink) ErrorSink errorSink)
{ {
string nameOrPrefix; string nameOrPrefix;
@ -457,13 +464,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
nameOrPrefix); nameOrPrefix);
} }
private static bool ShouldSkipDescriptorCreation(bool designTime, MemberInfo memberInfo) private static bool ShouldSkipDescriptorCreation(bool designTime, IMemberInfo memberInfo)
{ {
if (designTime) if (designTime)
{ {
var editorBrowsableAttribute = memberInfo.GetCustomAttribute<EditorBrowsableAttribute>(inherit: false); var editorBrowsableAttribute = memberInfo
.GetCustomAttributes<EditorBrowsableAttribute>()
.FirstOrDefault();
return editorBrowsableAttribute != null && editorBrowsableAttribute.State == EditorBrowsableState.Never; return editorBrowsableAttribute != null &&
editorBrowsableAttribute.State == EditorBrowsableState.Never;
} }
return false; return false;
@ -471,7 +481,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private static bool ValidateTagHelperAttributeNameOrPrefix( private static bool ValidateTagHelperAttributeNameOrPrefix(
string attributeNameOrPrefix, string attributeNameOrPrefix,
Type parentType, ITypeInfo parentType,
string propertyName, string propertyName,
ErrorSink errorSink, ErrorSink errorSink,
string nameOrPrefix) string nameOrPrefix)
@ -540,7 +550,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
} }
private static TagHelperAttributeDescriptor ToAttributeDescriptor( private static TagHelperAttributeDescriptor ToAttributeDescriptor(
PropertyInfo property, IPropertyInfo property,
string attributeName, string attributeName,
bool designTime) bool designTime)
{ {
@ -553,21 +563,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
} }
private static TagHelperAttributeDescriptor ToIndexerAttributeDescriptor( private static TagHelperAttributeDescriptor ToIndexerAttributeDescriptor(
PropertyInfo property, IPropertyInfo property,
HtmlAttributeNameAttribute attributeNameAttribute, HtmlAttributeNameAttribute attributeNameAttribute,
Type parentType, ITypeInfo parentType,
ErrorSink errorSink, ErrorSink errorSink,
string defaultPrefix, string defaultPrefix,
bool designTime, bool designTime,
out bool isInvalid) out bool isInvalid)
{ {
isInvalid = false; isInvalid = false;
var hasPublicSetter = property.SetMethod?.IsPublic == true; var hasPublicSetter = property.HasPublicSetter;
var dictionaryTypeArguments = ClosedGenericMatcher.ExtractGenericInterface( var dictionaryTypeArguments = property.PropertyType.GetGenericDictionaryParameterNames();
property.PropertyType, if (dictionaryTypeArguments?[0] != typeof(string).FullName)
typeof(IDictionary<,>))
?.GenericTypeArguments;
if (dictionaryTypeArguments?[0] != typeof(string))
{ {
if (attributeNameAttribute?.DictionaryAttributePrefix != null) if (attributeNameAttribute?.DictionaryAttributePrefix != null)
{ {
@ -634,13 +641,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return ToAttributeDescriptor( return ToAttributeDescriptor(
property, property,
attributeName: prefix, attributeName: prefix,
typeName: dictionaryTypeArguments[1].FullName, typeName: dictionaryTypeArguments[1],
isIndexer: true, isIndexer: true,
designTime: designTime); designTime: designTime);
} }
private static TagHelperAttributeDescriptor ToAttributeDescriptor( private static TagHelperAttributeDescriptor ToAttributeDescriptor(
PropertyInfo property, IPropertyInfo property,
string attributeName, string attributeName,
string typeName, string typeName,
bool isIndexer, bool isIndexer,
@ -651,8 +658,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
#if !DNXCORE50 #if !DNXCORE50
if (designTime) if (designTime)
{ {
propertyDesignTimeDescriptor = var runtimeProperty = property as RuntimePropertyInfo;
TagHelperDesignTimeDescriptorFactory.CreateAttributeDescriptor(property); if (runtimeProperty != null)
{
propertyDesignTimeDescriptor =
TagHelperDesignTimeDescriptorFactory.CreateAttributeDescriptor(runtimeProperty.Property);
}
} }
#endif #endif
@ -666,11 +677,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
} }
private static bool IsAccessibleProperty(PropertyInfo property) private static bool IsAccessibleProperty(IPropertyInfo property)
{ {
// Accessible properties are those with public getters and without [HtmlAttributeNotBound]. // Accessible properties are those with public getters and without [HtmlAttributeNotBound].
return property.GetMethod?.IsPublic == true && return property.HasPublicGetter &&
property.GetCustomAttribute<HtmlAttributeNotBoundAttribute>(inherit: false) == null; property.GetCustomAttributes<HtmlAttributeNotBoundAttribute>().FirstOrDefault() == null;
} }
/// <summary> /// <summary>

View File

@ -17,25 +17,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
private static readonly TypeInfo ITagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo(); private static readonly TypeInfo ITagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
/// <summary> /// <summary>
/// Instantiates a new instance of the <see cref="TagHelperTypeResolver"/> class. /// Locates valid <see cref="ITagHelper"/> types from the <see cref="Assembly"/> named <paramref name="name"/>.
/// </summary>
public TagHelperTypeResolver()
{
}
/// <summary>
/// Loads an <see cref="Assembly"/> using the given <paramref name="name"/> and resolves
/// all valid <see cref="ITagHelper"/> <see cref="Type"/>s.
/// </summary> /// </summary>
/// <param name="name">The name of an <see cref="Assembly"/> to search.</param> /// <param name="name">The name of an <see cref="Assembly"/> to search.</param>
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the associated /// <param name="documentLocation">The <see cref="SourceLocation"/> of the associated
/// <see cref="Parser.SyntaxTree.SyntaxTreeNode"/> responsible for the current <see cref="Resolve"/> call. /// <see cref="Parser.SyntaxTree.SyntaxTreeNode"/> responsible for the current <see cref="Resolve"/> call.
/// </param> /// </param>
/// <param name="errorSink">The <see cref="ErrorSink"/> used to record errors found when resolving /// <param name="errorSink">The <see cref="ErrorSink"/> used to record errors found when resolving
/// <see cref="ITagHelper"/> <see cref="Type"/>s.</param> /// <see cref="ITagHelper"/> types.</param>
/// <returns>An <see cref="IEnumerable{Type}"/> of valid <see cref="ITagHelper"/> <see cref="Type"/>s. /// <returns>An <see cref="IEnumerable{ITypeInfo}"/> of valid <see cref="ITagHelper"/> types.</returns>
/// </returns> public IEnumerable<ITypeInfo> Resolve(
public IEnumerable<Type> Resolve(
string name, string name,
SourceLocation documentLocation, SourceLocation documentLocation,
[NotNull] ErrorSink errorSink) [NotNull] ErrorSink errorSink)
@ -48,15 +39,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull, Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull,
errorLength); errorLength);
return Type.EmptyTypes; return Enumerable.Empty<ITypeInfo>();
} }
var assemblyName = new AssemblyName(name); var assemblyName = new AssemblyName(name);
IEnumerable<TypeInfo> libraryTypes; IEnumerable<ITypeInfo> libraryTypes;
try try
{ {
libraryTypes = GetExportedTypes(assemblyName); libraryTypes = GetTopLevelExportedTypes(assemblyName);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -67,13 +58,26 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
ex.Message), ex.Message),
name.Length); name.Length);
return Type.EmptyTypes; return Enumerable.Empty<ITypeInfo>();
} }
var validTagHelpers = libraryTypes.Where(IsTagHelper); return libraryTypes.Where(IsTagHelper);
}
// Convert from TypeInfo[] to Type[] /// <summary>
return validTagHelpers.Select(type => type.AsType()); /// Returns all non-nested exported types from the given <paramref name="assemblyName"/>
/// </summary>
/// <param name="assemblyName">The <see cref="AssemblyName"/> to get <see cref="ITypeInfo"/>s from.</param>
/// <returns>
/// An <see cref="IEnumerable{ITypeInfo}"/> of types exported from the given <paramref name="assemblyName"/>.
/// </returns>
protected virtual IEnumerable<ITypeInfo> GetTopLevelExportedTypes([NotNull] AssemblyName assemblyName)
{
var exportedTypeInfos = GetExportedTypes(assemblyName);
return exportedTypeInfos
.Where(typeInfo => !typeInfo.IsNested)
.Select(typeInfo => new RuntimeTypeInfo(typeInfo));
} }
/// <summary> /// <summary>
@ -91,13 +95,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
} }
// Internal for testing. // Internal for testing.
internal virtual bool IsTagHelper(TypeInfo typeInfo) internal virtual bool IsTagHelper(ITypeInfo typeInfo)
{ {
return typeInfo.IsPublic && return typeInfo.IsPublic &&
!typeInfo.IsAbstract && !typeInfo.IsAbstract &&
!typeInfo.IsGenericType && !typeInfo.IsGenericType &&
!typeInfo.IsNested && typeInfo.IsTagHelper;
ITagHelperTypeInfo.IsAssignableFrom(typeInfo);
} }
} }
} }

View File

@ -0,0 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Razor.Fake
{
public class DoesNotImplementRealITagHelper : Microsoft.AspNet.Razor.Fake.ITagHelper
{
}
}

View File

@ -0,0 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace Microsoft.AspNet.Razor.Fake
{
public interface ITagHelper
{
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace Microsoft.AspNet.Razor.Fake
{
public class ImplementsRealITagHelper : Microsoft.AspNet.Razor.Runtime.TagHelpers.ITagHelper
{
public int Order
{
get
{
throw new NotImplementedException();
}
}
public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,159 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Reflection;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class RuntimePropertyInfoTest
{
[Fact]
public void PropertyInfo_ReturnsMetadataOfAdaptingProperty()
{
// Arrange
var property = GetPropertyInfo(nameof(TestType.Property));
var runtimePropertyInfo = new RuntimePropertyInfo(property);
// Act
var actual = runtimePropertyInfo.Property;
// Assert
Assert.Same(property, actual);
var runtimeTypeInfo = Assert.IsType<RuntimeTypeInfo>(runtimePropertyInfo.PropertyType);
Assert.Same(property.PropertyType, runtimeTypeInfo.TypeInfo);
}
[Theory]
[InlineData(nameof(TestType.Property))]
[InlineData(nameof(TestType.PrivateSetter))]
[InlineData(nameof(TestType.PropertyWithoutSetter))]
public void HasPublicGetter_ReturnsTrueIfGetterExistsAndIsPublic(string propertyName)
{
// Arrange
var property = GetPropertyInfo(propertyName);
var runtimePropertyInfo = new RuntimePropertyInfo(property);
// Act
var result = runtimePropertyInfo.HasPublicGetter;
// Assert
Assert.True(result);
}
[Theory]
[InlineData(nameof(TestType.PrivateGetter))]
[InlineData(nameof(TestType.PropertyWithoutGetter))]
[InlineData("ProtectedProperty")]
public void HasPublicGetter_ReturnsFalseIfGetterDoesNotExistOrIsNonPublic(string propertyName)
{
// Arrange
var property = GetPropertyInfo(propertyName);
var runtimePropertyInfo = new RuntimePropertyInfo(property);
// Act
var result = runtimePropertyInfo.HasPublicGetter;
// Assert
Assert.False(result);
}
[Theory]
[InlineData(nameof(TestType.Property))]
[InlineData(nameof(TestType.PrivateGetter))]
[InlineData(nameof(TestType.PropertyWithoutGetter))]
public void HasPublicSetter_ReturnsTrueIfSetterExistsAndIsPublic(string propertyName)
{
// Arrange
var property = GetPropertyInfo(propertyName);
var runtimePropertyInfo = new RuntimePropertyInfo(property);
// Act
var result = runtimePropertyInfo.HasPublicSetter;
// Assert
Assert.True(result);
}
[Theory]
[InlineData(nameof(TestType.PrivateSetter))]
[InlineData(nameof(TestType.PropertyWithoutSetter))]
[InlineData("ProtectedProperty")]
public void HasPublicSetter_ReturnsFalseIfGetterDoesNotExistOrIsNonPublic(string propertyName)
{
// Arrange
var property = GetPropertyInfo(propertyName);
var runtimePropertyInfo = new RuntimePropertyInfo(property);
// Act
var result = runtimePropertyInfo.HasPublicSetter;
// Assert
Assert.False(result);
}
[Fact]
public void GetAttributes_ReturnsCustomAttributesOfSpecifiedType()
{
// Arrange
var property = GetPropertyInfo(nameof(TestType.PropertyWithAttributes));
var runtimeProperty = new RuntimePropertyInfo(property);
// Act
var attributes = property.GetCustomAttributes<HtmlAttributeNameAttribute>();
// Assert
var htmlAttributeName = Assert.Single(attributes);
Assert.Equal("somename", htmlAttributeName.Name);
}
[Fact]
public void GetAttributes_DoesNotInheritAttributes()
{
// Arrange
var property = GetPropertyInfo(nameof(TestType.PropertyWithAttributes));
var runtimeProperty = new RuntimePropertyInfo(property);
// Act
var attributes = property.GetCustomAttributes<HtmlAttributeNotBoundAttribute>();
// Assert
Assert.Empty(attributes);
}
private static PropertyInfo GetPropertyInfo(string propertyName)
{
return typeof(TestType).GetRuntimeProperties()
.FirstOrDefault(p => p.Name == propertyName);
}
public class BaseType
{
[HtmlAttributeNotBound]
public virtual string PropertyWithAttributes { get; }
}
public class TestType : BaseType
{
public string Property { get; set; }
public int PrivateSetter { get; private set; }
public object PrivateGetter { private get; set; }
protected DateTimeOffset ProtectedProperty { get; set; }
public string PropertyWithoutGetter
{
set { }
}
public int PropertyWithoutSetter => 0;
[HtmlAttributeName("somename")]
public override string PropertyWithAttributes { get; }
}
}
}

View File

@ -0,0 +1,375 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Xunit;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{
public class RuntimeTypeInfoTest
{
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(string))]
[InlineData(typeof(Tuple<,>))]
[InlineData(typeof(IDictionary<string, string>))]
[InlineData(typeof(IDictionary<string, IDictionary<string, CustomType>>))]
[InlineData(typeof(AbstractType))]
[InlineData(typeof(PrivateType))]
[InlineData(typeof(KnownKeyDictionary<>))]
[InlineData(typeof(KnownKeyDictionary<string>))]
public void RuntimeTypeInfo_ReturnsMetadataOfAdaptingType(Type type)
{
// Arrange
var typeInfo = type.GetTypeInfo();
var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo);
// Act and Assert
Assert.Same(runtimeTypeInfo.TypeInfo, typeInfo);
Assert.Equal(runtimeTypeInfo.Name, typeInfo.Name);
Assert.Equal(runtimeTypeInfo.FullName, typeInfo.FullName);
Assert.Equal(runtimeTypeInfo.IsAbstract, typeInfo.IsAbstract);
Assert.Equal(runtimeTypeInfo.IsGenericType, typeInfo.IsGenericType);
Assert.Equal(runtimeTypeInfo.IsPublic, typeInfo.IsPublic);
}
[Fact]
public void Properties_ReturnsPublicPropertiesOfAdaptingType()
{
// Arrange
var typeInfo = typeof(SubType).GetTypeInfo();
var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo);
// Act and Assert
Assert.Collection(runtimeTypeInfo.Properties,
property =>
{
Assert.IsType<RuntimePropertyInfo>(property);
Assert.Equal("Property1", property.Name);
},
property =>
{
Assert.IsType<RuntimePropertyInfo>(property);
Assert.Equal("Property2", property.Name);
},
property =>
{
Assert.IsType<RuntimePropertyInfo>(property);
Assert.Equal("Property3", property.Name);
},
property =>
{
Assert.IsType<RuntimePropertyInfo>(property);
Assert.Equal("Property4", property.Name);
},
property =>
{
Assert.IsType<RuntimePropertyInfo>(property);
Assert.Equal("BaseTypeProperty", property.Name);
},
property =>
{
Assert.IsType<RuntimePropertyInfo>(property);
Assert.Equal("ProtectedProperty", property.Name);
});
}
[Fact]
public void GetCustomAttributes_ReturnsAllAttributesOfType()
{
// Arrange
var typeInfo = typeof(TypeWithAttributes).GetTypeInfo();
var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo);
var expected = typeInfo.GetCustomAttributes<TargetElementAttribute>();
// Act
var actual = runtimeTypeInfo.GetCustomAttributes<TargetElementAttribute>();
// Assert
Assert.Equal(expected, actual);
}
[Fact]
public void GetCustomAttributes_DoesNotInheritAttributesFromBaseType()
{
// Arrange
var typeInfo = typeof(SubType).GetTypeInfo();
var runtimeTypeInfo = new RuntimeTypeInfo(typeInfo);
// Act
var actual = runtimeTypeInfo.GetCustomAttributes<EditorBrowsableAttribute>();
// Assert
Assert.Empty(actual);
}
[Theory]
[InlineData(typeof(ITagHelper))]
[InlineData(typeof(TagHelper))]
[InlineData(typeof(ImplementsITagHelper))]
[InlineData(typeof(DerivesFromTagHelper))]
[InlineData(typeof(Fake.ImplementsRealITagHelper))]
public void IsTagHelper_ReturnsTrueIfTypeImplementsTagHelper(Type type)
{
// Arrange
var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo());
// Act
var result = runtimeTypeInfo.IsTagHelper;
// Assert
Assert.True(result);
}
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(SubType))]
[InlineData(typeof(Fake.DoesNotImplementRealITagHelper))]
public void IsTagHelper_ReturnsFalseIfTypeDoesNotImplementTagHelper(Type type)
{
// Arrange
var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo());
// Act
var result = runtimeTypeInfo.IsTagHelper;
// Assert
Assert.False(result);
}
[Theory]
[InlineData(typeof(Dictionary<string, string>), new[] { typeof(string), typeof(string) })]
[InlineData(typeof(DerivesFromDictionary), new[] { typeof(int), typeof(object) })]
[InlineData(typeof(ImplementsIDictionary), new[] { typeof(List<string>), typeof(string) })]
[InlineData(typeof(IDictionary<string, IDictionary<string, CustomType>>),
new[] { typeof(string), typeof(IDictionary<string, CustomType>) })]
[InlineData(typeof(Dictionary<,>), new Type[] { null, null })]
[InlineData(typeof(KnownKeyDictionary<>), new[] { typeof(string), null })]
[InlineData(typeof(KnownKeyDictionary<ImplementsIDictionary>),
new[] { typeof(string), typeof(ImplementsIDictionary) })]
public void GetGenericDictionaryParameterNames_ReturnsKeyAndValueParameterTypeNames(
Type type,
Type[] expectedTypes)
{
// Arrange
var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo());
var expected = expectedTypes.Select(t => t?.FullName);
// Act
var actual = runtimeTypeInfo.GetGenericDictionaryParameterNames();
// Assert
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(string))]
[InlineData(typeof(List<string>))]
[InlineData(typeof(IDictionary))]
[InlineData(typeof(ITagHelper))]
public void GetGenericDictionaryParameterNames_ReturnsNullIfTypeDoesNotImplementGenericDictionary(Type type)
{
// Arrange
var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo());
// Act
var actual = runtimeTypeInfo.GetGenericDictionaryParameterNames();
// Assert
Assert.Null(actual);
}
public class AbstractType
{
}
internal class InternalType
{
}
private class PrivateType
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
private class BaseType
{
public string this[string key]
{
get { return ""; }
set { }
}
public string BaseTypeProperty { get; set; }
protected int ProtectedProperty { get; set; }
}
private class SubType : BaseType
{
public string Property1 { get; set; }
public int Property2 { get; }
public object Property3 { private get; set; }
private int Property4 { get; set; }
}
[TargetElement("test1")]
[TargetElement("test2")]
private class TypeWithAttributes
{
}
private class DerivesFromTagHelper : TagHelper
{
}
private class ImplementsITagHelper : ITagHelper
{
public int Order { get; } = 0;
public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
throw new NotImplementedException();
}
}
private class DerivesFromDictionary : Dictionary<int, object>
{
}
private class ImplementsIDictionary : IDictionary<List<string>, string>, IReadOnlyList<int>
{
public int this[int index]
{
get
{
throw new NotImplementedException();
}
}
public string this[List<string> key]
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public int Count
{
get
{
throw new NotImplementedException();
}
}
public bool IsReadOnly
{
get
{
throw new NotImplementedException();
}
}
public ICollection<List<string>> Keys
{
get
{
throw new NotImplementedException();
}
}
public ICollection<string> Values
{
get
{
throw new NotImplementedException();
}
}
public void Add(KeyValuePair<List<string>, string> item)
{
throw new NotImplementedException();
}
public void Add(List<string> key, string value)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(KeyValuePair<List<string>, string> item)
{
throw new NotImplementedException();
}
public bool ContainsKey(List<string> key)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<List<string>, string>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public IEnumerator<KeyValuePair<List<string>, string>> GetEnumerator()
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<List<string>, string> item)
{
throw new NotImplementedException();
}
public bool Remove(List<string> key)
{
throw new NotImplementedException();
}
public bool TryGetValue(List<string> key, out string value)
{
throw new NotImplementedException();
}
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public class KnownKeyDictionary<TValue> : Dictionary<string, TValue>
{
}
private class CustomType
{
}
}
}

View File

@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -175,7 +175,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -256,7 +256,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime: true, designTime: true,
errorSink: errorSink); errorSink: errorSink);
@ -493,7 +493,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime, designTime,
errorSink); errorSink);
@ -695,7 +695,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -744,7 +744,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -781,7 +781,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(OverriddenAttributeTagHelper), GetTypeInfo(typeof(OverriddenAttributeTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -815,7 +815,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(InheritedOverriddenAttributeTagHelper), GetTypeInfo(typeof(InheritedOverriddenAttributeTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -849,7 +849,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(InheritedNotOverriddenAttributeTagHelper), GetTypeInfo(typeof(InheritedNotOverriddenAttributeTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -870,7 +870,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
objectAssemblyName, objectAssemblyName,
typeof(object), GetTypeInfo(typeof(object)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -902,7 +902,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(InheritedSingleAttributeTagHelper), GetTypeInfo(typeof(InheritedSingleAttributeTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -930,7 +930,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(SingleAttributeTagHelper), GetTypeInfo(typeof(SingleAttributeTagHelper)),
designTime: false, designTime: false,
errorSink: new ErrorSink()); errorSink: new ErrorSink());
@ -959,7 +959,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(MissingAccessorTagHelper), GetTypeInfo(typeof(MissingAccessorTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -988,7 +988,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(NonPublicAccessorTagHelper), GetTypeInfo(typeof(NonPublicAccessorTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1020,7 +1020,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(NotBoundAttributeTagHelper), GetTypeInfo(typeof(NotBoundAttributeTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1039,7 +1039,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(DuplicateAttributeNameTagHelper), GetTypeInfo(typeof(DuplicateAttributeNameTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1086,7 +1086,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(MultiTagTagHelper), GetTypeInfo(typeof(MultiTagTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1118,7 +1118,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(InheritedMultiTagTagHelper), GetTypeInfo(typeof(InheritedMultiTagTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1148,7 +1148,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(DuplicateTagNameTagHelper), GetTypeInfo(typeof(DuplicateTagNameTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1178,7 +1178,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
typeof(OverrideNameTagHelper), GetTypeInfo(typeof(OverrideNameTagHelper)),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1366,7 +1366,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
type, GetTypeInfo(type),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1597,7 +1597,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var descriptors = TagHelperDescriptorFactory.CreateDescriptors( var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
AssemblyName, AssemblyName,
tagHelperType, GetTypeInfo(tagHelperType),
designTime: false, designTime: false,
errorSink: errorSink); errorSink: errorSink);
@ -1651,7 +1651,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor(
descriptor, descriptor,
typeof(MultiTagTagHelper), GetTypeInfo(typeof(MultiTagTagHelper)),
errorSink); errorSink);
// Assert // Assert
@ -1693,7 +1693,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor(
descriptor, descriptor,
typeof(MultiTagTagHelper), GetTypeInfo(typeof(MultiTagTagHelper)),
errorSink); errorSink);
// Assert // Assert
@ -1739,7 +1739,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor(
descriptor, descriptor,
typeof(MultiTagTagHelper), GetTypeInfo(typeof(MultiTagTagHelper)),
errorSink); errorSink);
// Assert // Assert
@ -1794,7 +1794,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
// Act // Act
var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor(
descriptor, descriptor,
typeof(MultiTagTagHelper), GetTypeInfo(typeof(MultiTagTagHelper)),
errorSink); errorSink);
// Assert // Assert
@ -2101,6 +2101,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
}; };
} }
private static ITypeInfo GetTypeInfo(Type tagHelperType) =>
new RuntimeTypeInfo(tagHelperType.GetTypeInfo());
[RestrictChildren("p")] [RestrictChildren("p")]
private class RestrictChildrenTagHelper private class RestrictChildrenTagHelper
{ {

View File

@ -1575,7 +1575,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
return types?.Select(type => type.GetTypeInfo()) ?? Enumerable.Empty<TypeInfo>(); return types?.Select(type => type.GetTypeInfo()) ?? Enumerable.Empty<TypeInfo>();
} }
internal override bool IsTagHelper(TypeInfo typeInfo) internal override bool IsTagHelper(ITypeInfo typeInfo)
{ {
return true; return true;
} }

View File

@ -62,7 +62,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
var types = tagHelperTypeResolver.Resolve("Foo", SourceLocation.Zero, new ErrorSink()); var types = tagHelperTypeResolver.Resolve("Foo", SourceLocation.Zero, new ErrorSink());
// Assert // Assert
Assert.Equal(ValidTestableTagHelpers, types); Assert.Collection(types,
type =>
{
var typeInfo = Assert.IsType<RuntimeTypeInfo>(type);
Assert.Equal(typeof(Valid_PlainTagHelper).GetTypeInfo(), typeInfo.TypeInfo);
},
type =>
{
var typeInfo = Assert.IsType<RuntimeTypeInfo>(type);
Assert.Equal(typeof(Valid_InheritedTagHelper).GetTypeInfo(), typeInfo.TypeInfo);
});
} }
[Fact] [Fact]