Produce ITypeInfo \ IPropertyInfo using CodeAnalysis symbol tree
This commit is contained in:
parent
231e8a9cf4
commit
9a7be69a0d
21
Razor.sln
21
Razor.sln
|
|
@ -17,6 +17,12 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Razor.Runt
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Razor.Test.Sources", "src\Microsoft.AspNet.Razor.Test.Sources\Microsoft.AspNet.Razor.Test.Sources.xproj", "{E3A2A305-634D-4BA6-95DB-AA06D6C442B0}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Razor.Runtime.Precompilation", "src\Microsoft.AspNet.Razor.Runtime.Precompilation\Microsoft.AspNet.Razor.Runtime.Precompilation.xproj", "{3B7ECD22-4C02-45CF-92E8-98C074DD9D0E}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Razor.Runtime.Precompilation.Test", "test\Microsoft.AspNet.Razor.Runtime.Precompilation.Test\Microsoft.AspNet.Razor.Runtime.Precompilation.Test.xproj", "{C626444C-5A63-42BC-ACAE-DBB2CD3EDE25}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Razor.Runtime.Precompilation.Files", "test\Microsoft.AspNet.Razor.Runtime.Precompilation.Files\Microsoft.AspNet.Razor.Runtime.Precompilation.Files.xproj", "{125D4CC1-5317-495F-88B8-472DFD9C1097}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -43,6 +49,18 @@ Global
|
|||
{E3A2A305-634D-4BA6-95DB-AA06D6C442B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3A2A305-634D-4BA6-95DB-AA06D6C442B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3A2A305-634D-4BA6-95DB-AA06D6C442B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B7ECD22-4C02-45CF-92E8-98C074DD9D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B7ECD22-4C02-45CF-92E8-98C074DD9D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B7ECD22-4C02-45CF-92E8-98C074DD9D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B7ECD22-4C02-45CF-92E8-98C074DD9D0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C626444C-5A63-42BC-ACAE-DBB2CD3EDE25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C626444C-5A63-42BC-ACAE-DBB2CD3EDE25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C626444C-5A63-42BC-ACAE-DBB2CD3EDE25}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C626444C-5A63-42BC-ACAE-DBB2CD3EDE25}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{125D4CC1-5317-495F-88B8-472DFD9C1097}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{125D4CC1-5317-495F-88B8-472DFD9C1097}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{125D4CC1-5317-495F-88B8-472DFD9C1097}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{125D4CC1-5317-495F-88B8-472DFD9C1097}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -53,5 +71,8 @@ Global
|
|||
{D0196096-1B01-4133-AACE-1A10A0F7247C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{0535998A-E32C-4D1A-80D1-0B15A513C471} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{E3A2A305-634D-4BA6-95DB-AA06D6C442B0} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{3B7ECD22-4C02-45CF-92E8-98C074DD9D0E} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
|
||||
{C626444C-5A63-42BC-ACAE-DBB2CD3EDE25} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
{125D4CC1-5317-495F-88B8-472DFD9C1097} = {92463391-81BE-462B-AC3C-78C6C760741F}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
// 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.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilities to work with creating <see cref="Attribute"/> instances from <see cref="AttributeData"/>.
|
||||
/// </summary>
|
||||
public static class CodeAnalysisAttributeUtilities
|
||||
{
|
||||
private static readonly ConcurrentDictionary<ConstructorInfo, Func<object[], Attribute>> _constructorCache =
|
||||
new ConcurrentDictionary<ConstructorInfo, Func<object[], Attribute>>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of <see cref="Attribute"/>s of type <typeparamref name="TAttribute"/>
|
||||
/// that are declared on the specified <paramref name="symbol"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">The <see cref="Attribute"/> type.</typeparam>
|
||||
/// <param name="symbol">The <see cref="ISymbol"/> to find attributes on.</param>
|
||||
/// <param name="symbolLookup">The <see cref="CodeAnalysisSymbolLookupCache"/>.</param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<TAttribute> GetCustomAttributes<TAttribute>(
|
||||
[NotNull] ISymbol symbol,
|
||||
[NotNull] CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
var attributes = symbol.GetAttributes();
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attributeSymbol = symbolLookup.GetSymbol(typeof(TAttribute).GetTypeInfo());
|
||||
return attributes
|
||||
.Where(attribute => attribute.AttributeClass == attributeSymbol)
|
||||
.Select(attribute => CreateAttribute<TAttribute>(attribute, symbolLookup))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return Enumerable.Empty<TAttribute>();
|
||||
}
|
||||
|
||||
private static TAttribute CreateAttribute<TAttribute>(
|
||||
AttributeData attributeData,
|
||||
CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
TAttribute attribute;
|
||||
var matchInfo = MatchConstructor(typeof(TAttribute), attributeData.ConstructorArguments, symbolLookup);
|
||||
Func<object[], Attribute> constructorDelegate;
|
||||
if (!_constructorCache.TryGetValue(matchInfo.Constructor, out constructorDelegate))
|
||||
{
|
||||
constructorDelegate = MakeFastConstructorInvoker(matchInfo);
|
||||
_constructorCache[matchInfo.Constructor] = constructorDelegate;
|
||||
}
|
||||
|
||||
attribute = (TAttribute)constructorDelegate(matchInfo.ArgumentValues);
|
||||
|
||||
if (attributeData.NamedArguments.Length > 0)
|
||||
{
|
||||
var helpers = PropertyHelper.GetVisibleProperties(attribute);
|
||||
foreach (var item in attributeData.NamedArguments)
|
||||
{
|
||||
var helper = helpers.FirstOrDefault(
|
||||
propertyHelper => string.Equals(propertyHelper.Name, item.Key, StringComparison.Ordinal));
|
||||
|
||||
if (helper == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatCodeAnalysis_PropertyNotFound(item.Key, attribute.GetType().FullName));
|
||||
}
|
||||
|
||||
var propertyValue = ConvertTypedConstantValue(
|
||||
helper.Property.PropertyType,
|
||||
item.Value,
|
||||
symbolLookup);
|
||||
helper.SetValue(attribute, propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
private static Func<object[], Attribute> MakeFastConstructorInvoker(ConstructorMatchInfo matchInfo)
|
||||
{
|
||||
var argsParameter = Expression.Parameter(typeof(object[]), "args");
|
||||
var parameters = new Expression[matchInfo.ArgumentValues.Length];
|
||||
|
||||
for (var index = 0; index < matchInfo.ArgumentValues.Length; index++)
|
||||
{
|
||||
parameters[index] =
|
||||
Expression.Convert(
|
||||
Expression.ArrayIndex(
|
||||
argsParameter,
|
||||
Expression.Constant(index)),
|
||||
matchInfo.ArgumentValues[index].GetType());
|
||||
}
|
||||
|
||||
// () => new TAttribute(args)
|
||||
var lambda =
|
||||
Expression.Lambda<Func<object[], Attribute>>(
|
||||
Expression.New(
|
||||
matchInfo.Constructor,
|
||||
parameters),
|
||||
argsParameter);
|
||||
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
private static ConstructorMatchInfo MatchConstructor(
|
||||
Type type,
|
||||
ImmutableArray<TypedConstant> symbolConstructorArguments,
|
||||
CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
{
|
||||
var constructor = FindConstructor(type, symbolConstructorArguments, symbolLookup);
|
||||
var constructorParmaters = constructor.GetParameters();
|
||||
|
||||
var arguments = new object[symbolConstructorArguments.Length];
|
||||
for (var i = 0; i < arguments.Length; i++)
|
||||
{
|
||||
var value = ConvertTypedConstantValue(
|
||||
constructorParmaters[i].ParameterType,
|
||||
symbolConstructorArguments[i],
|
||||
symbolLookup);
|
||||
|
||||
arguments[i] = value;
|
||||
}
|
||||
|
||||
return new ConstructorMatchInfo
|
||||
{
|
||||
Constructor = constructor,
|
||||
ArgumentValues = arguments
|
||||
};
|
||||
}
|
||||
|
||||
private static ConstructorInfo FindConstructor(
|
||||
Type type,
|
||||
ImmutableArray<TypedConstant> symbolConstructorArguments,
|
||||
CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
{
|
||||
var constructors = type.GetConstructors();
|
||||
foreach (var constructor in constructors)
|
||||
{
|
||||
var runtimeParameters = constructor.GetParameters();
|
||||
if (runtimeParameters.Length != symbolConstructorArguments.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var parametersMatched = true;
|
||||
for (var index = 0; index < runtimeParameters.Length; index++)
|
||||
{
|
||||
var runtimeParameter = runtimeParameters[index].ParameterType;
|
||||
if (symbolConstructorArguments[index].Kind == TypedConstantKind.Array &&
|
||||
runtimeParameter.IsArray)
|
||||
{
|
||||
var arrayType = (IArrayTypeSymbol)symbolConstructorArguments[index].Type;
|
||||
if (symbolLookup.GetSymbol(runtimeParameter.GetElementType().GetTypeInfo()) !=
|
||||
arrayType.ElementType)
|
||||
{
|
||||
parametersMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameterSymbol = symbolLookup.GetSymbol(runtimeParameter.GetTypeInfo());
|
||||
if (symbolConstructorArguments[index].Type != parameterSymbol)
|
||||
{
|
||||
parametersMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parametersMatched)
|
||||
{
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(Resources.FormatCodeAnalysisConstructorNotFound(type.FullName));
|
||||
}
|
||||
|
||||
private static object ConvertTypedConstantValue(
|
||||
Type type,
|
||||
TypedConstant constructorArgument,
|
||||
CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
{
|
||||
switch (constructorArgument.Kind)
|
||||
{
|
||||
case TypedConstantKind.Enum:
|
||||
return Enum.ToObject(type, constructorArgument.Value);
|
||||
case TypedConstantKind.Primitive:
|
||||
return constructorArgument.Value;
|
||||
case TypedConstantKind.Type:
|
||||
var typeSymbol = (INamedTypeSymbol)constructorArgument.Value;
|
||||
var typeName = CodeAnalysisSymbolBasedTypeInfo.GetAssemblyQualifiedName(typeSymbol);
|
||||
return Type.GetType(typeName);
|
||||
case TypedConstantKind.Array:
|
||||
Debug.Assert(type.IsArray && constructorArgument.Values != null);
|
||||
var elementType = type.GetElementType();
|
||||
var values = Array.CreateInstance(elementType, constructorArgument.Values.Length);
|
||||
for (var index = 0; index < values.Length; index++)
|
||||
{
|
||||
values.SetValue(
|
||||
ConvertTypedConstantValue(elementType, constructorArgument.Values[index], symbolLookup),
|
||||
index);
|
||||
}
|
||||
return values;
|
||||
default:
|
||||
throw new NotSupportedException(
|
||||
Resources.FormatCodeAnalysis_TypeConstantKindNotSupported(constructorArgument.Kind));
|
||||
}
|
||||
}
|
||||
|
||||
private struct ConstructorMatchInfo
|
||||
{
|
||||
public ConstructorInfo Constructor;
|
||||
|
||||
public object[] ArgumentValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// 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.Diagnostics;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IPropertyInfo"/> implementation using Code Analysis symbols.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name, PropertyType}")]
|
||||
public class CodeAnalysisSymbolBasedPropertyInfo : IPropertyInfo
|
||||
{
|
||||
private readonly IPropertySymbol _propertySymbol;
|
||||
private readonly CodeAnalysisSymbolLookupCache _symbolLookup;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CodeAnalysisSymbolBasedPropertyInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="propertySymbol">The <see cref="IPropertySymbol"/>.</param>
|
||||
/// <param name="symbolLookup">The <see cref="CodeAnalysisSymbolLookupCache"/>.</param>
|
||||
public CodeAnalysisSymbolBasedPropertyInfo(
|
||||
[NotNull] IPropertySymbol propertySymbol,
|
||||
[NotNull] CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
{
|
||||
_symbolLookup = symbolLookup;
|
||||
_propertySymbol = propertySymbol;
|
||||
PropertyType = new CodeAnalysisSymbolBasedTypeInfo(_propertySymbol.Type, _symbolLookup);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasPublicGetter
|
||||
{
|
||||
get
|
||||
{
|
||||
return _propertySymbol.GetMethod != null &&
|
||||
_propertySymbol.GetMethod.DeclaredAccessibility == Accessibility.Public;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasPublicSetter
|
||||
{
|
||||
get
|
||||
{
|
||||
return _propertySymbol.SetMethod != null &&
|
||||
_propertySymbol.SetMethod.DeclaredAccessibility == Accessibility.Public;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => _propertySymbol.MetadataName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITypeInfo PropertyType { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>()
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
return CodeAnalysisAttributeUtilities.GetCustomAttributes<TAttribute>(_propertySymbol, _symbolLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ITypeInfo"/> implementation using Code Analysis symbols.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Name}")]
|
||||
public class CodeAnalysisSymbolBasedTypeInfo : ITypeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="System.Reflection.TypeInfo"/> for <see cref="IDictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
public static readonly System.Reflection.TypeInfo OpenGenericDictionaryTypeInfo =
|
||||
typeof(IDictionary<,>).GetTypeInfo();
|
||||
private readonly CodeAnalysisSymbolLookupCache _symbolLookup;
|
||||
private readonly ITypeSymbol _type;
|
||||
private readonly ITypeSymbol _underlyingType;
|
||||
private string _fullName;
|
||||
private List<IPropertyInfo> _properties;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CodeAnalysisSymbolBasedTypeInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="propertySymbol">The <see cref="IPropertySymbol"/>.</param>
|
||||
/// <param name="symbolLookup">The <see cref="CodeAnalysisSymbolLookupCache"/>.</param>
|
||||
public CodeAnalysisSymbolBasedTypeInfo(
|
||||
[NotNull] ITypeSymbol type,
|
||||
[NotNull] CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
{
|
||||
_symbolLookup = symbolLookup;
|
||||
_type = type;
|
||||
_underlyingType = UnwrapArrayType(type);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FullName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_fullName == null)
|
||||
{
|
||||
_fullName = GetFullName(_type);
|
||||
}
|
||||
|
||||
return _fullName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ITypeSymbol"/> instance.
|
||||
/// </summary>
|
||||
public ITypeSymbol TypeSymbol => _type;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAbstract => _type.IsAbstract;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsGenericType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _type.Kind == SymbolKind.NamedType &&
|
||||
((INamedTypeSymbol)_type).IsGenericType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsNested => _underlyingType.ContainingType != null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
return _type.DeclaredAccessibility == Accessibility.Public ||
|
||||
_type.TypeKind == TypeKind.Array;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_type.TypeKind == TypeKind.Array)
|
||||
{
|
||||
return _underlyingType.MetadataName + "[]";
|
||||
}
|
||||
|
||||
return _type.MetadataName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IPropertyInfo> Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_properties == null)
|
||||
{
|
||||
_properties = GetProperties(_type, _symbolLookup);
|
||||
}
|
||||
|
||||
return _properties;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsTagHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
var interfaceSymbol = _symbolLookup.GetSymbol(typeof(ITagHelper).GetTypeInfo());
|
||||
return _type.AllInterfaces.Any(implementedInterface => implementedInterface == interfaceSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>()
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
return CodeAnalysisAttributeUtilities.GetCustomAttributes<TAttribute>(_type, _symbolLookup);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITypeInfo[] GetGenericDictionaryParameters()
|
||||
{
|
||||
var dictionarySymbol = _symbolLookup.GetSymbol(OpenGenericDictionaryTypeInfo);
|
||||
|
||||
INamedTypeSymbol dictionaryInterface;
|
||||
if (_type.Kind == SymbolKind.NamedType &&
|
||||
((INamedTypeSymbol)_type).ConstructedFrom == dictionarySymbol)
|
||||
{
|
||||
dictionaryInterface = (INamedTypeSymbol)_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
dictionaryInterface = _type
|
||||
.AllInterfaces
|
||||
.FirstOrDefault(implementedInterface => implementedInterface.ConstructedFrom == dictionarySymbol);
|
||||
}
|
||||
|
||||
if (dictionaryInterface != null)
|
||||
{
|
||||
Debug.Assert(dictionaryInterface.TypeArguments.Length == 2);
|
||||
|
||||
return new[]
|
||||
{
|
||||
new CodeAnalysisSymbolBasedTypeInfo(dictionaryInterface.TypeArguments[0], _symbolLookup),
|
||||
new CodeAnalysisSymbolBasedTypeInfo(dictionaryInterface.TypeArguments[1], _symbolLookup),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly qualified named of the specified <paramref name="symbol"/>.
|
||||
/// </summary>
|
||||
/// <param name="symbol">The <see cref="ITypeSymbol" /> to generate the name for.</param>
|
||||
/// <returns>The assembly qualified name.</returns>
|
||||
public static string GetAssemblyQualifiedName([NotNull] ITypeSymbol symbol)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
GetAssemblyQualifiedName(builder, symbol);
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ITypeInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ITypeInfo other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var otherSymbolBasedType = other as CodeAnalysisSymbolBasedTypeInfo;
|
||||
if (otherSymbolBasedType != null)
|
||||
{
|
||||
return otherSymbolBasedType.TypeSymbol == TypeSymbol;
|
||||
}
|
||||
|
||||
return string.Equals(FullName, other.FullName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => FullName.GetHashCode();
|
||||
|
||||
private static List<IPropertyInfo> GetProperties(
|
||||
ITypeSymbol typeSymbol,
|
||||
CodeAnalysisSymbolLookupCache symbolLookup)
|
||||
{
|
||||
var properties = new List<IPropertyInfo>();
|
||||
var overridenProperties = new HashSet<IPropertySymbol>();
|
||||
|
||||
do
|
||||
{
|
||||
foreach (var member in typeSymbol.GetMembers().Where(m => m.Kind == SymbolKind.Property))
|
||||
{
|
||||
var propertySymbol = (IPropertySymbol)member;
|
||||
if (!propertySymbol.IsIndexer && !overridenProperties.Contains(propertySymbol))
|
||||
{
|
||||
var propertyInfo = new CodeAnalysisSymbolBasedPropertyInfo(propertySymbol, symbolLookup);
|
||||
properties.Add(propertyInfo);
|
||||
}
|
||||
|
||||
if (propertySymbol.IsOverride)
|
||||
{
|
||||
overridenProperties.Add(propertySymbol.OverriddenProperty);
|
||||
}
|
||||
}
|
||||
|
||||
typeSymbol = typeSymbol.BaseType;
|
||||
|
||||
} while (typeSymbol != null);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static string GetFullName(ITypeSymbol typeSymbol)
|
||||
{
|
||||
var nameBuilder = new StringBuilder();
|
||||
GetFullName(nameBuilder, typeSymbol);
|
||||
|
||||
return nameBuilder.Length == 0 ? null : nameBuilder.ToString();
|
||||
}
|
||||
|
||||
private static void GetFullName(StringBuilder nameBuilder, ITypeSymbol typeSymbol)
|
||||
{
|
||||
if (typeSymbol.Kind == SymbolKind.TypeParameter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var insertIndex = nameBuilder.Length;
|
||||
if (typeSymbol.TypeKind == TypeKind.Array)
|
||||
{
|
||||
var arrayType = (IArrayTypeSymbol)typeSymbol;
|
||||
GetFullName(nameBuilder, arrayType.ElementType);
|
||||
nameBuilder.Append("[]");
|
||||
return;
|
||||
}
|
||||
|
||||
nameBuilder.Append(typeSymbol.MetadataName);
|
||||
if (typeSymbol.Kind == SymbolKind.NamedType)
|
||||
{
|
||||
var namedSymbol = (INamedTypeSymbol)typeSymbol;
|
||||
// The symbol represents a generic but not open generic type
|
||||
if (namedSymbol.IsGenericType &&
|
||||
namedSymbol.ConstructedFrom != namedSymbol)
|
||||
{
|
||||
nameBuilder.Append('[');
|
||||
foreach (var typeArgument in namedSymbol.TypeArguments)
|
||||
{
|
||||
nameBuilder.Append('[');
|
||||
GetFullName(nameBuilder, typeArgument);
|
||||
nameBuilder.Append("],");
|
||||
}
|
||||
|
||||
// Removing trailing slash
|
||||
Debug.Assert(nameBuilder.Length > 0 && nameBuilder[nameBuilder.Length - 1] == ',');
|
||||
nameBuilder.Length--;
|
||||
nameBuilder.Append("]");
|
||||
}
|
||||
}
|
||||
|
||||
var containingType = typeSymbol.ContainingType;
|
||||
while (containingType != null)
|
||||
{
|
||||
nameBuilder
|
||||
.Insert(insertIndex, '+')
|
||||
.Insert(insertIndex, containingType.MetadataName);
|
||||
|
||||
containingType = containingType.ContainingType;
|
||||
}
|
||||
|
||||
var containingNamespace = typeSymbol.ContainingNamespace;
|
||||
while (!containingNamespace.IsGlobalNamespace)
|
||||
{
|
||||
nameBuilder
|
||||
.Insert(insertIndex, '.')
|
||||
.Insert(insertIndex, containingNamespace.MetadataName);
|
||||
|
||||
containingNamespace = containingNamespace.ContainingNamespace;
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetAssemblyQualifiedName(StringBuilder builder, ITypeSymbol typeSymbol)
|
||||
{
|
||||
GetFullName(builder, typeSymbol);
|
||||
typeSymbol = UnwrapArrayType(typeSymbol);
|
||||
|
||||
builder
|
||||
.Append(", ")
|
||||
.Append(typeSymbol.ContainingAssembly.Identity);
|
||||
}
|
||||
|
||||
private static ITypeSymbol UnwrapArrayType(ITypeSymbol type)
|
||||
{
|
||||
if (type.TypeKind == TypeKind.Array)
|
||||
{
|
||||
return ((IArrayTypeSymbol)type).ElementType;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// 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;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
/// <summary>
|
||||
/// Caches mapping of <see cref="System.Reflection.TypeInfo"/> to <see cref="INamedTypeSymbol"/>.
|
||||
/// </summary>
|
||||
public class CodeAnalysisSymbolLookupCache
|
||||
{
|
||||
private readonly Dictionary<System.Reflection.TypeInfo, INamedTypeSymbol> _symbolLookup =
|
||||
new Dictionary<System.Reflection.TypeInfo, INamedTypeSymbol>();
|
||||
private readonly object _lookupLock = new object();
|
||||
private readonly Compilation _compilation;
|
||||
private INamedTypeSymbol _dictionarySymbol;
|
||||
|
||||
/// <summary>
|
||||
/// Initialzes a new instance of <see cref="CodeAnalysisSymbolLookupCache"/>.
|
||||
/// </summary>
|
||||
/// <param name="compilation">The <see cref="Compilation"/> instance.</param>
|
||||
public CodeAnalysisSymbolLookupCache([NotNull] Compilation compilation)
|
||||
{
|
||||
_compilation = compilation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="INamedTypeSymbol"/> that corresponds to <paramref name="typeInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The <see cref="System.Reflection.TypeInfo"/> to lookup.</param>
|
||||
/// <returns></returns>
|
||||
public INamedTypeSymbol GetSymbol([NotNull] System.Reflection.TypeInfo typeInfo)
|
||||
{
|
||||
if (typeInfo == CodeAnalysisSymbolBasedTypeInfo.OpenGenericDictionaryTypeInfo)
|
||||
{
|
||||
if (_dictionarySymbol == null)
|
||||
{
|
||||
// Allow safe races
|
||||
_dictionarySymbol = _compilation.GetTypeByMetadataName(
|
||||
CodeAnalysisSymbolBasedTypeInfo.OpenGenericDictionaryTypeInfo.FullName);
|
||||
}
|
||||
|
||||
return _dictionarySymbol;
|
||||
}
|
||||
|
||||
lock (_lookupLock)
|
||||
{
|
||||
INamedTypeSymbol typeSymbol;
|
||||
if (!_symbolLookup.TryGetValue(typeInfo, out typeSymbol))
|
||||
{
|
||||
typeSymbol = _compilation.GetTypeByMetadataName(typeInfo.FullName);
|
||||
_symbolLookup[typeInfo] = typeSymbol;
|
||||
}
|
||||
|
||||
return typeSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>3b7ecd22-4c02-45cf-92e8-98c074dd9d0e</ProjectGuid>
|
||||
<RootNamespace>Microsoft.AspNet.Razor.Runtime.Precompilation</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
// 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.CodeAnalysis;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="TagHelperTypeResolver"/> used during Razor precompilation.
|
||||
/// </summary>
|
||||
public class PrecompilationTagHelperTypeResolver : TagHelperTypeResolver
|
||||
{
|
||||
private readonly object _assemblyLookupLock = new object();
|
||||
private readonly Dictionary<string, IEnumerable<ITypeInfo>> _assemblyLookup
|
||||
= new Dictionary<string, IEnumerable<ITypeInfo>>(StringComparer.Ordinal);
|
||||
private readonly Compilation _compilation;
|
||||
private readonly CodeAnalysisSymbolLookupCache _symbolLookup;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="PrecompilationTagHelperTypeResolver"/>.
|
||||
/// </summary>
|
||||
/// <param name="compilation">The <see cref="Compilation"/>.</param>
|
||||
public PrecompilationTagHelperTypeResolver([NotNull] Compilation compilation)
|
||||
{
|
||||
_compilation = compilation;
|
||||
_symbolLookup = new CodeAnalysisSymbolLookupCache(compilation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<ITypeInfo> GetTopLevelExportedTypes([NotNull] AssemblyName assemblyName)
|
||||
{
|
||||
lock (_assemblyLookupLock)
|
||||
{
|
||||
IEnumerable<ITypeInfo> result;
|
||||
if (!_assemblyLookup.TryGetValue(assemblyName.Name, out result))
|
||||
{
|
||||
result = GetExportedTypes(assemblyName.Name);
|
||||
_assemblyLookup[assemblyName.Name] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal for unit testing
|
||||
internal IEnumerable<ITypeInfo> GetExportedTypes(string assemblyName)
|
||||
{
|
||||
if (string.Equals(_compilation.AssemblyName, assemblyName, StringComparison.Ordinal))
|
||||
{
|
||||
return GetExportedTypes(_compilation.Assembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var reference in _compilation.References)
|
||||
{
|
||||
var compilationReference = reference as CompilationReference;
|
||||
if (compilationReference != null &&
|
||||
string.Equals(
|
||||
compilationReference.Compilation.AssemblyName,
|
||||
assemblyName,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
return GetExportedTypes(compilationReference.Compilation.Assembly);
|
||||
}
|
||||
|
||||
var assemblySymbol = _compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol;
|
||||
if (string.Equals(
|
||||
assemblySymbol?.Identity.Name,
|
||||
assemblyName,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
return GetExportedTypes(assemblySymbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatCodeAnalysis_UnableToLoadAssemblyReference(assemblyName));
|
||||
}
|
||||
|
||||
private List<ITypeInfo> GetExportedTypes(IAssemblySymbol assembly)
|
||||
{
|
||||
var exportedTypes = new List<ITypeInfo>();
|
||||
GetExportedTypes(assembly.GlobalNamespace, exportedTypes);
|
||||
return exportedTypes;
|
||||
}
|
||||
|
||||
private void GetExportedTypes(INamespaceSymbol namespaceSymbol, List<ITypeInfo> exportedTypes)
|
||||
{
|
||||
foreach (var type in namespaceSymbol.GetTypeMembers())
|
||||
{
|
||||
if (type.TypeKind == TypeKind.Class &&
|
||||
type.DeclaredAccessibility == Accessibility.Public)
|
||||
{
|
||||
exportedTypes.Add(new CodeAnalysisSymbolBasedTypeInfo(type, _symbolLookup));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var subNamespace in namespaceSymbol.GetNamespaceMembers())
|
||||
{
|
||||
GetExportedTypes(subNamespace, exportedTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// 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.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.Razor.Runtime.Precompilation.Test")]
|
||||
94
src/Microsoft.AspNet.Razor.Runtime.Precompilation/Properties/Resources.Designer.cs
generated
Normal file
94
src/Microsoft.AspNet.Razor.Runtime.Precompilation/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class Resources
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNet.Razor.Runtime.Precompilation.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Unable to find a suitable constructor for type '{0}'.
|
||||
/// </summary>
|
||||
internal static string CodeAnalysisConstructorNotFound
|
||||
{
|
||||
get { return GetString("CodeAnalysisConstructorNotFound"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to find a suitable constructor for type '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatCodeAnalysisConstructorNotFound(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("CodeAnalysisConstructorNotFound"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to find property {0} on type {1}.
|
||||
/// </summary>
|
||||
internal static string CodeAnalysis_PropertyNotFound
|
||||
{
|
||||
get { return GetString("CodeAnalysis_PropertyNotFound"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to find property {0} on type {1}.
|
||||
/// </summary>
|
||||
internal static string FormatCodeAnalysis_PropertyNotFound(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("CodeAnalysis_PropertyNotFound"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type constant kind '{0}' is not supported.
|
||||
/// </summary>
|
||||
internal static string CodeAnalysis_TypeConstantKindNotSupported
|
||||
{
|
||||
get { return GetString("CodeAnalysis_TypeConstantKindNotSupported"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type constant kind '{0}' is not supported.
|
||||
/// </summary>
|
||||
internal static string FormatCodeAnalysis_TypeConstantKindNotSupported(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("CodeAnalysis_TypeConstantKindNotSupported"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to load assembly reference '{0}'.
|
||||
/// </summary>
|
||||
internal static string CodeAnalysis_UnableToLoadAssemblyReference
|
||||
{
|
||||
get { return GetString("CodeAnalysis_UnableToLoadAssemblyReference"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to load assembly reference '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatCodeAnalysis_UnableToLoadAssemblyReference(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("CodeAnalysis_UnableToLoadAssemblyReference"), p0);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CodeAnalysisConstructorNotFound" xml:space="preserve">
|
||||
<value>Unable to find a suitable constructor for type '{0}'.</value>
|
||||
</data>
|
||||
<data name="CodeAnalysis_PropertyNotFound" xml:space="preserve">
|
||||
<value>Unable to find property {0} on type {1}.</value>
|
||||
</data>
|
||||
<data name="CodeAnalysis_TypeConstantKindNotSupported" xml:space="preserve">
|
||||
<value>The type constant kind '{0}' is not supported.</value>
|
||||
</data>
|
||||
<data name="CodeAnalysis_UnableToLoadAssemblyReference" xml:space="preserve">
|
||||
<value>Unable to load assembly reference '{0}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"description": "Supports tag helper resolution during precompilation.",
|
||||
"version": "4.0.0-*",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/aspnet/razor"
|
||||
},
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.Framework.NotNullAttribute.Sources": {
|
||||
"version": "1.0.0-*",
|
||||
"type": "build"
|
||||
},
|
||||
"Microsoft.Framework.PropertyActivator.Sources": {
|
||||
"version": "1.0.0-*",
|
||||
"type": "build"
|
||||
},
|
||||
"Microsoft.Framework.PropertyHelper.Sources": {
|
||||
"version": "1.0.0-*",
|
||||
"type": "build"
|
||||
},
|
||||
"Microsoft.AspNet.Razor.Runtime": "4.0.0-*",
|
||||
"Microsoft.Dnx.Compilation.CSharp.Abstractions": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnx451": {
|
||||
"frameworkAssemblies": {
|
||||
"System.Runtime": "4.0.0.0"
|
||||
}
|
||||
},
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Collections.Concurrent": "4.0.11-*",
|
||||
"System.Linq.Expressions": "4.0.11-*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,16 +46,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
bool IsTagHelper { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full names of the parameter types if the type implements
|
||||
/// Gets the <see cref="ITypeInfo[]"/> for the <c>TKey</c> and <c>TValue</c> parameters of
|
||||
/// <see cref="IDictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The full type names (<seealso cref="System.Type.FullName"/>) of <c>TKey</c> and <c>TValue</c>
|
||||
/// The <see cref="ITypeInfo"/> 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>.
|
||||
/// For open generic types, <see cref="ITypeInfo" /> for generic type parameters is <c>null</c>.
|
||||
/// </remarks>
|
||||
string[] GetGenericDictionaryParameterNames();
|
||||
ITypeInfo[] GetGenericDictionaryParameters();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
|
|
@ -14,6 +15,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
public class RuntimeTypeInfo : ITypeInfo
|
||||
{
|
||||
private static readonly Regex _fullNameSanitizer = new Regex(
|
||||
@", [A-Za-z\.]+, Version=\d+\.\d+\.\d+\.\d+, Culture=neutral, PublicKeyToken=\w+",
|
||||
RegexOptions.ExplicitCapture,
|
||||
matchTimeout: TimeSpan.FromSeconds(10));
|
||||
|
||||
private static readonly TypeInfo TagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
|
||||
private IEnumerable<IPropertyInfo> _properties;
|
||||
|
||||
|
|
@ -35,7 +41,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
public string Name => TypeInfo.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string FullName => TypeInfo.FullName;
|
||||
public string FullName => SanitizeFullName(TypeInfo.FullName);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAbstract => TypeInfo.IsAbstract;
|
||||
|
|
@ -72,17 +78,53 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
TypeInfo.GetCustomAttributes<TAttribute>(inherit: false);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string[] GetGenericDictionaryParameterNames()
|
||||
public ITypeInfo[] GetGenericDictionaryParameters()
|
||||
{
|
||||
return ClosedGenericMatcher.ExtractGenericInterface(
|
||||
TypeInfo.AsType(),
|
||||
typeof(IDictionary<,>))
|
||||
?.GenericTypeArguments
|
||||
.Select(type => type.FullName)
|
||||
.Select(type => type.IsGenericParameter ? null : new RuntimeTypeInfo(type.GetTypeInfo()))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => TypeInfo.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ITypeInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(ITypeInfo other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var otherRuntimeType = other as RuntimeTypeInfo;
|
||||
if (otherRuntimeType != null)
|
||||
{
|
||||
return otherRuntimeType.TypeInfo == TypeInfo;
|
||||
}
|
||||
|
||||
return string.Equals(FullName, other.FullName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => FullName.GetHashCode();
|
||||
|
||||
// Internal for unit testing
|
||||
internal static string SanitizeFullName(string fullName)
|
||||
{
|
||||
// In CoreCLR, some types (such as System.String) are type forwarded from System.Runtime
|
||||
// to mscorlib at runtime. Type names of generic type parameters includes the assembly qualified name;
|
||||
// consequently the type name generated at precompilation differs from the one at runtime. We'll
|
||||
// avoid dealing with these inconsistencies by removing assembly information from TypeInfo.FullName.
|
||||
return _fullNameSanitizer.Replace(fullName, string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
|
@ -27,6 +28,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
private static readonly Regex HtmlCaseRegex =
|
||||
new Regex("(?<!^)((?<=[a-zA-Z0-9])[A-Z][a-z])|((?<=[a-z])[A-Z])", RegexOptions.None);
|
||||
|
||||
private static readonly ITypeInfo StringTypeInfo = new RuntimeTypeInfo(typeof(string).GetTypeInfo());
|
||||
|
||||
// TODO: Investigate if we should cache TagHelperDescriptors for types:
|
||||
// https://github.com/aspnet/Razor/issues/165
|
||||
|
||||
|
|
@ -573,8 +576,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
isInvalid = false;
|
||||
var hasPublicSetter = property.HasPublicSetter;
|
||||
var dictionaryTypeArguments = property.PropertyType.GetGenericDictionaryParameterNames();
|
||||
if (dictionaryTypeArguments?[0] != typeof(string).FullName)
|
||||
var dictionaryTypeArguments = property.PropertyType.GetGenericDictionaryParameters();
|
||||
if (!StringTypeInfo.Equals(dictionaryTypeArguments?[0]))
|
||||
{
|
||||
if (attributeNameAttribute?.DictionaryAttributePrefix != null)
|
||||
{
|
||||
|
|
@ -641,7 +644,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
return ToAttributeDescriptor(
|
||||
property,
|
||||
attributeName: prefix,
|
||||
typeName: dictionaryTypeArguments[1],
|
||||
typeName: dictionaryTypeArguments[1].FullName,
|
||||
isIndexer: true,
|
||||
designTime: designTime);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
|
||||
public class AttributesWithArrayConstructorArgumentsAttribute : Attribute
|
||||
{
|
||||
public AttributesWithArrayConstructorArgumentsAttribute(string[] stringArgs, int[] intArgs)
|
||||
{
|
||||
StringArgs = stringArgs;
|
||||
IntArgs = intArgs;
|
||||
}
|
||||
|
||||
public AttributesWithArrayConstructorArgumentsAttribute(string[] stringArgs, Type[] typeArgs, int[] intArgs)
|
||||
{
|
||||
StringArgs = stringArgs;
|
||||
TypeArgs = typeArgs;
|
||||
IntArgs = intArgs;
|
||||
}
|
||||
|
||||
public AttributesWithArrayConstructorArgumentsAttribute(int[] intArgs, Type[] typeArgs)
|
||||
{
|
||||
IntArgs = intArgs;
|
||||
TypeArgs = typeArgs;
|
||||
}
|
||||
|
||||
public string[] StringArgs { get; }
|
||||
|
||||
public Type[] TypeArgs { get; }
|
||||
|
||||
public int[] IntArgs { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public class ArrayPropertiesAttribute : Attribute
|
||||
{
|
||||
public Type[] ArrayOfTypes { get; set; }
|
||||
|
||||
public int[] ArrayOfInts { get; set; }
|
||||
|
||||
public DayOfWeek[] Days { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.Precompilation
|
||||
{
|
||||
internal class InternalType
|
||||
{
|
||||
}
|
||||
|
||||
public class PublicType
|
||||
{
|
||||
private class NestedPrivateType
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ContainerType
|
||||
{
|
||||
public class NestedType
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class GenericType<TVal>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.Precompilation
|
||||
{
|
||||
public struct MyStruct
|
||||
{
|
||||
}
|
||||
|
||||
public enum MyEnum
|
||||
{
|
||||
}
|
||||
|
||||
public interface MyInterface
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class MyAbstractClass
|
||||
{
|
||||
}
|
||||
|
||||
public partial class MyPartialClass
|
||||
{
|
||||
}
|
||||
|
||||
public partial class MyPartialClass
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class MySealedClass
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public class EnumPropertyAttribute : Attribute
|
||||
{
|
||||
public DayOfWeek DayOfWeek { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>125d4cc1-5317-495f-88b8-472dfd9c1097</ProjectGuid>
|
||||
<RootNamespace>Microsoft.AspNet.Razor.Runtime.Precompilation.Files</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class MultipleConstructorArgumentsAttribute : Attribute
|
||||
{
|
||||
public MultipleConstructorArgumentsAttribute(string firstArgument, string secondArgument, int thirdArgument)
|
||||
{
|
||||
FirstArgument = firstArgument;
|
||||
SecondArgument = secondArgument;
|
||||
ThirdArgument = thirdArgument;
|
||||
}
|
||||
|
||||
public string FirstArgument { get; }
|
||||
|
||||
public string SecondArgument { get; }
|
||||
|
||||
public int ThirdArgument { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.Precompilation
|
||||
{
|
||||
public class TypeWithArrayProperties
|
||||
{
|
||||
public MyAbstractClass[] AbstractArray { get; set; }
|
||||
|
||||
public IDisposable[] DisposableArray { get; }
|
||||
|
||||
public ContainerType.NestedType[] NestedArrayType { get; }
|
||||
|
||||
internal InternalType[] InternalArray { get; set; }
|
||||
|
||||
public ICollection<IDictionary<string, IList<object[]>>>[] GenericArray { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Resources;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
[TargetElement("img", Attributes = AppendVersionAttributeName + "," + SrcAttributeName)]
|
||||
[TargetElement("image", Attributes = SrcAttributeName)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[EnumProperty(DayOfWeek = DayOfWeek.Friday)]
|
||||
[CustomValidation(typeof(Validator), "ValidationMethod", ErrorMessageResourceType = typeof(ResourceManager))]
|
||||
[RestrictChildren("ol", "ul", "li", "dl", "dd")]
|
||||
[ArrayProperties(
|
||||
ArrayOfInts = new[] { 7, 8, 9 },
|
||||
ArrayOfTypes = new[] { typeof(ITagHelper), typeof(Guid) },
|
||||
Days = new[] { DayOfWeek.Saturday })]
|
||||
public class TypeWithAttributes
|
||||
{
|
||||
private const string AppendVersionAttributeName = "asp-append-version";
|
||||
private const string SrcAttributeName = "src";
|
||||
|
||||
[HtmlAttributeName(SrcAttributeName)]
|
||||
[Required(AllowEmptyStrings = true)]
|
||||
public string Src { get; set; }
|
||||
|
||||
[HtmlAttributeName(AppendVersionAttributeName, DictionaryAttributePrefix = "prefix")]
|
||||
[HtmlAttributeNotBound]
|
||||
public bool AppendVersion { get; set; }
|
||||
|
||||
[Required]
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
[ArrayProperties(
|
||||
ArrayOfInts = new int[0],
|
||||
ArrayOfTypes = new[] { typeof(TypeWithAttributes) },
|
||||
Days = new[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Sunday })]
|
||||
public ViewContext ViewContext { get; set; }
|
||||
|
||||
[AttributesWithArrayConstructorArguments(new[] { 1, 2 }, new[] { typeof(Uri), typeof(IList<>) })]
|
||||
[AttributesWithArrayConstructorArguments(new[] { "Hello", "world" }, new[] { typeof(List<Guid>) }, new int[0])]
|
||||
[AttributesWithArrayConstructorArguments(
|
||||
new[] { "world", "Hello" },
|
||||
new[] { typeof(IDictionary<string, object>) },
|
||||
new[] { 1 })]
|
||||
protected IEditableObject HostingEnvironment { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// 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 Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public class TypeWithComplexPropertyFullNames
|
||||
{
|
||||
public int Property1 { get; set; }
|
||||
|
||||
public int[] Property2 { get; set; }
|
||||
|
||||
public List<long> Property3 { get; set; }
|
||||
|
||||
public List<Tuple<string, DateTimeOffset>> Property4 { get; }
|
||||
|
||||
public IDictionary<ILookup<string, TagHelper>, IList<Comparer<byte[]>>> Property5 { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public class TypeWithDictionaryProperties
|
||||
{
|
||||
public IDictionary<string, string> RouteValues1 { get; set; } =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public Dictionary<int, string> RouteValues2 { get; set; } =
|
||||
new Dictionary<int, string>();
|
||||
|
||||
public Dictionary<List<string>, float> RouteValues3 { get; set; } =
|
||||
new Dictionary<List<string>, float>();
|
||||
|
||||
public IDictionary<string, ParserResults> CustomDictionary { get; set; } =
|
||||
new Dictionary<string, ParserResults>();
|
||||
|
||||
public IDictionary NonGenericDictionary { get; set; } =
|
||||
new Dictionary<string, string>();
|
||||
|
||||
public object ObjectType { get; set; } =
|
||||
new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.Precompilation
|
||||
{
|
||||
[MultipleConstructorArguments(firstArgument: "First1", secondArgument: "Second1", thirdArgument: 31)]
|
||||
[MultipleConstructorArguments(secondArgument: "Second2", firstArgument: "First2", thirdArgument: 32)]
|
||||
[MultipleConstructorArguments(thirdArgument: 33, secondArgument: "Second3", firstArgument: "First3")]
|
||||
public class TypeWithNamedAttributes
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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.Precompilation
|
||||
{
|
||||
public class Animal
|
||||
{
|
||||
public virtual string Name { get; set; }
|
||||
}
|
||||
|
||||
public class Mammal : Animal
|
||||
{
|
||||
public override string Name { get; set; }
|
||||
}
|
||||
|
||||
public class Dog : Mammal
|
||||
{
|
||||
public override string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Runtime.Precompilation
|
||||
{
|
||||
public class ViewContext
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Razor.Runtime.Precompilation": "4.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnx451": {
|
||||
"frameworkAssemblies": {
|
||||
"System.ComponentModel.DataAnnotations": "4.0.0.0"
|
||||
}
|
||||
},
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.ComponentModel.Annotations": "4.0.11-*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||
{
|
||||
[RestrictChildren(Never)]
|
||||
[CustomValidation(typeof(TypeDoesNotExist)]
|
||||
[TargetElement("img"
|
||||
public class TypeWithMalformedAttribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||
{
|
||||
public class TypeWithMalformedProperties
|
||||
{
|
||||
public DateTime DateTime { get }
|
||||
|
||||
public int DateTime2 => "Hello world";
|
||||
|
||||
public string CustomOrder { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.Mvc.Razor.Precompilation
|
||||
{
|
||||
public class TypeWithMissingReferences : ITagHelper
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// 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.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.Dnx.Compilation;
|
||||
using Microsoft.Dnx.Compilation.CSharp;
|
||||
using Microsoft.Dnx.Runtime.Infrastructure;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public static class CompilationUtility
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, AssemblyMetadata> _metadataCache =
|
||||
new ConcurrentDictionary<string, AssemblyMetadata>(StringComparer.Ordinal);
|
||||
private static readonly Assembly ExecutingAssembly = typeof(CompilationUtility).GetTypeInfo().Assembly;
|
||||
public static readonly string GeneratedAssemblyName = Path.GetRandomFileName() + "." + Path.GetRandomFileName();
|
||||
|
||||
public static Compilation GetCompilation(params string[] resourceFiles)
|
||||
{
|
||||
var assemblyVersion = ExecutingAssembly.GetName().Version;
|
||||
|
||||
var syntaxTrees = new List<SyntaxTree>
|
||||
{
|
||||
CSharpSyntaxTree.ParseText(
|
||||
$"[assembly: {typeof(AssemblyVersionAttribute).FullName}(\"{assemblyVersion}\")]")
|
||||
};
|
||||
|
||||
foreach (var resourceFile in resourceFiles)
|
||||
{
|
||||
var resourceContent = ReadManifestResource(resourceFile);
|
||||
syntaxTrees.Add(CSharpSyntaxTree.ParseText(resourceContent));
|
||||
}
|
||||
|
||||
var libraryExporter = (ILibraryExporter)CallContextServiceLocator
|
||||
.Locator
|
||||
.ServiceProvider
|
||||
.GetService(typeof(ILibraryExporter));
|
||||
var applicationName = ExecutingAssembly.GetName().Name;
|
||||
var libraryExport = libraryExporter.GetExport(applicationName);
|
||||
|
||||
var references = new List<MetadataReference>();
|
||||
var roslynReference = libraryExport.MetadataReferences[0] as IRoslynMetadataReference;
|
||||
var compilationReference = roslynReference?.MetadataReference as CompilationReference;
|
||||
if (compilationReference != null)
|
||||
{
|
||||
references.AddRange(compilationReference.Compilation.References);
|
||||
references.Add(roslynReference.MetadataReference);
|
||||
}
|
||||
else
|
||||
{
|
||||
var export = libraryExporter.GetAllExports(applicationName);
|
||||
foreach (var metadataReference in export.MetadataReferences)
|
||||
{
|
||||
var reference = metadataReference.ConvertMetadataReference(
|
||||
fileReference => _metadataCache.GetOrAdd(
|
||||
fileReference.Path,
|
||||
_ => fileReference.CreateAssemblyMetadata()));
|
||||
references.Add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
return CSharpCompilation.Create(
|
||||
GeneratedAssemblyName,
|
||||
syntaxTrees,
|
||||
references);
|
||||
}
|
||||
|
||||
private static string ReadManifestResource(string path)
|
||||
{
|
||||
path = $"{ExecutingAssembly.GetName().Name}.{path}.cs";
|
||||
using (var contentStream = ExecutingAssembly.GetManifestResourceStream(path))
|
||||
{
|
||||
using (var reader = new StreamReader(contentStream))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>c626444c-5a63-42bc-acae-dbb2cd3ede25</ProjectGuid>
|
||||
<RootNamespace>Microsoft.AspNet.Razor.Runtime.Precompilation.Test</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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 Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.Test.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public class PrecompilationTagHelperDescriptorFactoryTest : TagHelperDescriptorFactoryTest
|
||||
{
|
||||
public override ITypeInfo GetTypeInfo(Type tagHelperType)
|
||||
{
|
||||
var paths = new[]
|
||||
{
|
||||
$"TagHelperDescriptorFactoryTagHelpers",
|
||||
$"CommonTagHelpers",
|
||||
};
|
||||
|
||||
var compilation = CompilationUtility.GetCompilation(paths);
|
||||
var typeResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
return Assert.Single(typeResolver.GetExportedTypes(CompilationUtility.GeneratedAssemblyName),
|
||||
generatedType => string.Equals(generatedType.FullName, tagHelperType.FullName, StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,554 @@
|
|||
// 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.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.Precompilation
|
||||
{
|
||||
public class PrecompilationTagHelperTypeResolverTest
|
||||
{
|
||||
private static readonly TypeInfo TagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(TypeDerivingFromITagHelper))]
|
||||
[InlineData(typeof(AttributeTargetingTagHelper))]
|
||||
[InlineData(typeof(TagHelperInGlobalNamespace))]
|
||||
public void TypesReturnedFromGetTopLevelExportedTypes_ReturnsTopLevelTypeInfo(Type expected)
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("TagHelperDescriptorFactoryTagHelpers");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes, type => type.FullName == expected.FullName);
|
||||
AssertEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PropertyNamesForComplexPropertiesAreGeneratedCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var expectedType = typeof(TypeWithComplexPropertyFullNames);
|
||||
var compilation = CompilationUtility.GetCompilation(expectedType.Name);
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes);
|
||||
AssertEqual(expectedType, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PropertyNamesForArrayPropertiesAreGeneratedCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var expectedType = typeof(TypeWithArrayProperties);
|
||||
var compilation = CompilationUtility.GetCompilation(expectedType.Name);
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes);
|
||||
AssertEqual(expectedType, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTopLevelExportedTypes_DoesNotReturnNonPublicOrNestedTypes()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("AssemblyWithNonPublicTypes");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(exportedTypes,
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(PublicType), typeInfo);
|
||||
},
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(ContainerType), typeInfo);
|
||||
},
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(GenericType<>), typeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTopLevelExportedTypes_DoesNotReturnValueTypesOrInterfaces()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("AssemblyWithNonTypes");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(exportedTypes,
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(MyAbstractClass), typeInfo);
|
||||
},
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(MyPartialClass), typeInfo);
|
||||
},
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(MySealedClass), typeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_PopulatesAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var expected = typeof(TypeWithAttributes).GetTypeInfo();
|
||||
var compilation = CompilationUtility.GetCompilation(expected.Name);
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes);
|
||||
AssertEqual(expected.AsType(), actual);
|
||||
|
||||
AssertAttributes<TargetElementAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.Tag, actualAttribute.Tag);
|
||||
Assert.Equal(expectedAttribute.Attributes, actualAttribute.Attributes);
|
||||
});
|
||||
|
||||
// Verify if enum in attribute constructors works.
|
||||
AssertAttributes<EditorBrowsableAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.State, actualAttribute.State);
|
||||
});
|
||||
|
||||
// Verify if enum in attribute property works.
|
||||
AssertAttributes<EnumPropertyAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.DayOfWeek, actualAttribute.DayOfWeek);
|
||||
});
|
||||
|
||||
// Verify if Type in attribute constructor and property works.
|
||||
AssertAttributes<CustomValidationAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Same(expectedAttribute.ValidatorType, actualAttribute.ValidatorType);
|
||||
Assert.Equal(expectedAttribute.Method, actualAttribute.Method);
|
||||
Assert.Same(
|
||||
expectedAttribute.ErrorMessageResourceType,
|
||||
actualAttribute.ErrorMessageResourceType);
|
||||
});
|
||||
|
||||
// Verify if array arguments work in constructor.
|
||||
AssertAttributes<RestrictChildrenAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(
|
||||
expectedAttribute.ChildTagNames,
|
||||
actualAttribute.ChildTagNames);
|
||||
});
|
||||
|
||||
// Complex array bindings
|
||||
AssertAttributes<ArrayPropertiesAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.ArrayOfTypes, actualAttribute.ArrayOfTypes);
|
||||
Assert.Equal(expectedAttribute.ArrayOfInts, actualAttribute.ArrayOfInts);
|
||||
Assert.Equal(expectedAttribute.Days, actualAttribute.Days);
|
||||
});
|
||||
|
||||
var expectedProperties = expected.DeclaredProperties;
|
||||
Assert.Collection(actual.Properties,
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(TypeWithAttributes.Src), property.Name);
|
||||
var expectedProperty = Assert.Single(expectedProperties, p => p.Name == property.Name);
|
||||
AssertAttributes<HtmlAttributeNameAttribute>(
|
||||
expectedProperty,
|
||||
property,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.Name, actualAttribute.Name);
|
||||
Assert.Equal(expectedAttribute.DictionaryAttributePrefix, actualAttribute.DictionaryAttributePrefix);
|
||||
});
|
||||
|
||||
// Verify boolean values bind.
|
||||
AssertAttributes<RequiredAttribute>(
|
||||
expectedProperty,
|
||||
property,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.AllowEmptyStrings, actualAttribute.AllowEmptyStrings);
|
||||
});
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(TypeWithAttributes.AppendVersion), property.Name);
|
||||
var expectedProperty = Assert.Single(expectedProperties, p => p.Name == property.Name);
|
||||
AssertAttributes<HtmlAttributeNameAttribute>(
|
||||
expectedProperty,
|
||||
property,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.Name, actualAttribute.Name);
|
||||
Assert.Equal(expectedAttribute.DictionaryAttributePrefix, actualAttribute.DictionaryAttributePrefix);
|
||||
});
|
||||
|
||||
// Attribute without constructor arguments or properties.
|
||||
Assert.Single(expectedProperty.GetCustomAttributes<HtmlAttributeNotBoundAttribute>());
|
||||
Assert.Single(property.GetCustomAttributes<HtmlAttributeNotBoundAttribute>());
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(TypeWithAttributes.ViewContext), property.Name);
|
||||
var expectedProperty = Assert.Single(expectedProperties, p => p.Name == property.Name);
|
||||
|
||||
AssertAttributes<EditorBrowsableAttribute>(
|
||||
expectedProperty,
|
||||
property,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.State, actualAttribute.State);
|
||||
});
|
||||
|
||||
// Complex array bindings in properties
|
||||
AssertAttributes<ArrayPropertiesAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.ArrayOfTypes, actualAttribute.ArrayOfTypes);
|
||||
Assert.Equal(expectedAttribute.ArrayOfInts, actualAttribute.ArrayOfInts);
|
||||
Assert.Equal(expectedAttribute.Days, actualAttribute.Days);
|
||||
});
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal("HostingEnvironment", property.Name);
|
||||
Assert.Single(expectedProperties, p => p.Name == property.Name);
|
||||
|
||||
// Complex array bindings in constructor arguments
|
||||
AssertAttributes<AttributesWithArrayConstructorArgumentsAttribute>(
|
||||
expected,
|
||||
actual,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.StringArgs, actualAttribute.StringArgs);
|
||||
Assert.Equal(expectedAttribute.IntArgs, actualAttribute.IntArgs);
|
||||
Assert.Equal(expectedAttribute.TypeArgs, actualAttribute.TypeArgs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_WithDerivedAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var expected = typeof(DerivedTagHelper);
|
||||
var compilation = CompilationUtility.GetCompilation("TagHelperDescriptorFactoryTagHelpers");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes, type => type.Name == expected.Name);
|
||||
AssertEqual(expected, actual);
|
||||
|
||||
var expectedProperties = expected.GetProperties();
|
||||
AssertAttributes<BaseAttribute>(
|
||||
expectedProperties.First(p => p.Name == nameof(DerivedTagHelper.DerivedProperty)),
|
||||
actual.Properties.First(p => p.Name == nameof(DerivedTagHelper.DerivedProperty)),
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.BaseProperty, actualAttribute.BaseProperty);
|
||||
});
|
||||
|
||||
AssertAttributes<HtmlAttributeNameAttribute>(
|
||||
expectedProperties.First(p => p.Name == nameof(DerivedTagHelper.NewProperty) &&
|
||||
p.PropertyType == typeof(Type)),
|
||||
actual.Properties.First(p => p.Name == nameof(DerivedTagHelper.NewProperty) &&
|
||||
p.PropertyType.Name == typeof(Type).Name),
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.Name, actualAttribute.Name);
|
||||
Assert.Equal(
|
||||
expectedAttribute.DictionaryAttributePrefix,
|
||||
actualAttribute.DictionaryAttributePrefix);
|
||||
});
|
||||
|
||||
var expectedVirtualProperty = expectedProperties.First(
|
||||
p => p.Name == nameof(DerivedTagHelper.VirtualProperty));
|
||||
var actualVirtualProperty = actual.Properties.First(
|
||||
p => p.Name == nameof(DerivedTagHelper.VirtualProperty));
|
||||
|
||||
Assert.Empty(expectedVirtualProperty.GetCustomAttributes<HtmlAttributeNotBoundAttribute>());
|
||||
Assert.Empty(actualVirtualProperty.GetCustomAttributes<HtmlAttributeNotBoundAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_CorrectlyIdentifiesIfTypeDerivesFromDictionary()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation(nameof(TypeWithDictionaryProperties));
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var exportedType = Assert.Single(exportedTypes);
|
||||
AssertEqual(typeof(TypeWithDictionaryProperties), exportedType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_WorksCorrectlyForOverridenProperties()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("TypesWithInheritedProperties");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(exportedTypes,
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(Animal), typeInfo);
|
||||
},
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(Mammal), typeInfo);
|
||||
},
|
||||
typeInfo =>
|
||||
{
|
||||
AssertEqual(typeof(Dog), typeInfo);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_WorksIfAttributeConstructorArgumentsAreOutOfOrder()
|
||||
{
|
||||
// Arrange
|
||||
var expectedType = typeof(TypeWithNamedAttributes);
|
||||
var compilation = CompilationUtility.GetCompilation(expectedType.Name);
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var exportedType = Assert.Single(exportedTypes);
|
||||
AssertEqual(expectedType, exportedType);
|
||||
|
||||
var attributes = exportedType.GetCustomAttributes<MultipleConstructorArgumentsAttribute>();
|
||||
|
||||
AssertAttributes<MultipleConstructorArgumentsAttribute>(
|
||||
expectedType.GetTypeInfo(),
|
||||
exportedType,
|
||||
(expectedAttribute, actualAttribute) =>
|
||||
{
|
||||
Assert.Equal(expectedAttribute.FirstArgument, actualAttribute.FirstArgument);
|
||||
Assert.Equal(expectedAttribute.SecondArgument, actualAttribute.SecondArgument);
|
||||
Assert.Equal(expectedAttribute.ThirdArgument, actualAttribute.ThirdArgument);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_WorksIfAttributesAreMalformedErrors()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("BadFiles.TypeWithMalformedAttribute");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes);
|
||||
var targetElementAttribute = Assert.Single(actual.GetCustomAttributes<TargetElementAttribute>());
|
||||
Assert.Equal("img", targetElementAttribute.Tag);
|
||||
Assert.Null(targetElementAttribute.Attributes);
|
||||
|
||||
Assert.Empty(actual.GetCustomAttributes<EditorBrowsableAttribute>());
|
||||
var ex = Assert.Throws<InvalidOperationException>(
|
||||
() => actual.GetCustomAttributes<CustomValidationAttribute>());
|
||||
Assert.Equal($"Unable to find a suitable constructor for type '{typeof(CustomValidationAttribute).FullName}'.",
|
||||
ex.Message);
|
||||
|
||||
ex = Assert.Throws<InvalidOperationException>(
|
||||
() => actual.GetCustomAttributes<RestrictChildrenAttribute>());
|
||||
Assert.Equal($"Unable to find a suitable constructor for type '{typeof(RestrictChildrenAttribute).FullName}'.",
|
||||
ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_WorksForPropertiesWithErrors()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("BadFiles.TypeWithMalformedProperties");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(exportedTypes);
|
||||
Assert.Collection(actual.Properties,
|
||||
property =>
|
||||
{
|
||||
Assert.Equal("DateTime", property.Name);
|
||||
Assert.Equal(typeof(DateTime).Name, property.PropertyType.Name);
|
||||
Assert.True(property.HasPublicGetter);
|
||||
Assert.False(property.HasPublicSetter);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal("DateTime2", property.Name);
|
||||
Assert.Equal(typeof(int).Name, property.PropertyType.Name);
|
||||
Assert.True(property.HasPublicGetter);
|
||||
Assert.False(property.HasPublicSetter);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal("CustomOrder", property.Name);
|
||||
Assert.Equal(typeof(string).Name, property.PropertyType.Name);
|
||||
Assert.True(property.HasPublicGetter);
|
||||
Assert.True(property.HasPublicSetter);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExportedTypes_WorksForTypesWithErrors()
|
||||
{
|
||||
// Arrange
|
||||
var compilation = CompilationUtility.GetCompilation("BadFiles.TypeWithMissingReferences");
|
||||
var tagHelperResolver = new PrecompilationTagHelperTypeResolver(compilation);
|
||||
|
||||
// Act
|
||||
var exportedTypes = tagHelperResolver.GetExportedTypes(compilation.AssemblyName);
|
||||
|
||||
// Assert
|
||||
var type = Assert.Single(exportedTypes);
|
||||
Assert.False(type.IsTagHelper);
|
||||
}
|
||||
|
||||
private static void AssertAttributes<TAttribute>(
|
||||
MemberInfo expected,
|
||||
IMemberInfo actual,
|
||||
Action<TAttribute, TAttribute> assertItem)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
Assert.Equal(
|
||||
expected.GetCustomAttributes<TAttribute>(),
|
||||
actual.GetCustomAttributes<TAttribute>(),
|
||||
new DelegateAssertion<TAttribute>(assertItem));
|
||||
}
|
||||
|
||||
private static void AssertEqual(Type expected, ITypeInfo actual)
|
||||
{
|
||||
AssertEqual(new RuntimeTypeInfo(expected.GetTypeInfo()), actual, assertProperties: true);
|
||||
}
|
||||
|
||||
private static void AssertEqual(ITypeInfo expected, ITypeInfo actual, bool assertProperties)
|
||||
{
|
||||
var runtimeType = Assert.IsType<RuntimeTypeInfo>(expected);
|
||||
|
||||
var actualFullName = actual.FullName.Replace(
|
||||
CompilationUtility.GeneratedAssemblyName,
|
||||
runtimeType.TypeInfo.Assembly.GetName().Name);
|
||||
|
||||
Assert.Equal(expected.Name, actual.Name);
|
||||
if (expected.FullName != actualFullName)
|
||||
{
|
||||
Console.WriteLine("!!!");
|
||||
Console.WriteLine(runtimeType.TypeInfo.FullName);
|
||||
Console.WriteLine(actualFullName);
|
||||
}
|
||||
|
||||
Assert.Equal(expected.FullName, actualFullName);
|
||||
Assert.Equal(expected.IsPublic, actual.IsPublic);
|
||||
Assert.Equal(expected.IsAbstract, actual.IsAbstract);
|
||||
Assert.Equal(expected.IsGenericType, actual.IsGenericType);
|
||||
Assert.Equal(expected.IsTagHelper, actual.IsTagHelper);
|
||||
Assert.Equal(
|
||||
expected.GetGenericDictionaryParameters(),
|
||||
actual.GetGenericDictionaryParameters(),
|
||||
new DelegateAssertion<ITypeInfo>((x, y) => AssertEqual(x, y, assertProperties: false)));
|
||||
|
||||
if (assertProperties)
|
||||
{
|
||||
Assert.Equal(
|
||||
expected.Properties.OrderBy(p => p.Name),
|
||||
actual.Properties.OrderBy(p => p.Name),
|
||||
new DelegateAssertion<IPropertyInfo>((x, y) => AssertEqual(x, y)));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertEqual(IPropertyInfo expected, IPropertyInfo actual)
|
||||
{
|
||||
Assert.Equal(expected.Name, actual.Name);
|
||||
Assert.Equal(expected.HasPublicGetter, actual.HasPublicGetter);
|
||||
Assert.Equal(expected.HasPublicSetter, actual.HasPublicSetter);
|
||||
AssertEqual(expected.PropertyType, actual.PropertyType, assertProperties: false);
|
||||
}
|
||||
|
||||
private class DelegateAssertion<T> : IEqualityComparer<T>
|
||||
{
|
||||
private readonly Action<T, T> _assert;
|
||||
|
||||
public DelegateAssertion(Action<T, T> assert)
|
||||
{
|
||||
_assert = assert;
|
||||
}
|
||||
|
||||
public bool Equals(T x, T y)
|
||||
{
|
||||
_assert(x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetHashCode(T obj) => 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Razor.Runtime.Precompilation.Files": "1.0.0-*",
|
||||
"Microsoft.AspNet.Razor.Runtime.Test": "1.0.0-*",
|
||||
"Microsoft.AspNet.Razor.Test.Sources": {
|
||||
"version": "4.0.0-*",
|
||||
"type": "build"
|
||||
},
|
||||
"Microsoft.Dnx.Compilation.CSharp.Common": "1.0.0-*",
|
||||
"Microsoft.Framework.HashCodeCombiner.Sources": {
|
||||
"type": "build",
|
||||
"version": "1.0.0-*"
|
||||
}
|
||||
},
|
||||
"resource": [
|
||||
"../Microsoft.AspNet.Razor.Runtime.Precompilation.Files/*.cs",
|
||||
"../Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TestTagHelpers/*.cs",
|
||||
"BadFiles/*.cs"
|
||||
],
|
||||
"commands": {
|
||||
"test": "xunit.runner.aspnet"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnx451": {
|
||||
"dependencies": {
|
||||
"Moq": "4.2.1312.1622"
|
||||
}
|
||||
},
|
||||
"dnxcore50": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
// 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.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.AspNet.Razor.Test.Internal;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
public class RuntimeTagHelperDescriptorFactoryTest : TagHelperDescriptorFactoryTest
|
||||
{
|
||||
public override ITypeInfo GetTypeInfo(Type tagHelperType) =>
|
||||
new RuntimeTypeInfo(tagHelperType.GetTypeInfo());
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptors_BuildsDescriptorsFromSimpleTypes()
|
||||
{
|
||||
// Arrange
|
||||
var errorSink = new ErrorSink();
|
||||
var objectAssemblyName = typeof(object).GetTypeInfo().Assembly.GetName().Name;
|
||||
var expectedDescriptor =
|
||||
CreateTagHelperDescriptor("object", "System.Object", objectAssemblyName);
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||
objectAssemblyName,
|
||||
GetTypeInfo(typeof(object)),
|
||||
designTime: false,
|
||||
errorSink: errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(errorSink.Errors);
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
// TagHelperDesignTimeDescriptors are not created in CoreCLR.
|
||||
#if !DNXCORE50
|
||||
public static TheoryData OutputElementHintData
|
||||
{
|
||||
get
|
||||
{
|
||||
// tagHelperType, expectedDescriptors
|
||||
return new TheoryData<Type, TagHelperDescriptor[]>
|
||||
{
|
||||
{
|
||||
typeof(OutputElementHintTagHelper),
|
||||
new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "output-element-hint",
|
||||
TypeName = typeof(OutputElementHintTagHelper).FullName,
|
||||
AssemblyName = AssemblyName,
|
||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
OutputElementHint = "strong"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(MulitpleDescriptorTagHelperWithOutputElementHint),
|
||||
new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "a",
|
||||
TypeName = typeof(MulitpleDescriptorTagHelperWithOutputElementHint).FullName,
|
||||
AssemblyName = AssemblyName,
|
||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
OutputElementHint = "div"
|
||||
}
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = typeof(MulitpleDescriptorTagHelperWithOutputElementHint).FullName,
|
||||
AssemblyName = AssemblyName,
|
||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
OutputElementHint = "div"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(OutputElementHintData))]
|
||||
public void CreateDescriptors_CreatesDesignTimeDescriptorsWithOutputElementHint(
|
||||
Type tagHelperType,
|
||||
TagHelperDescriptor[] expectedDescriptors)
|
||||
{
|
||||
// Arrange
|
||||
var errorSink = new ErrorSink();
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||
AssemblyName,
|
||||
GetTypeInfo(tagHelperType),
|
||||
designTime: true,
|
||||
errorSink: errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(errorSink.Errors);
|
||||
|
||||
// We don't care about order. Mono returns reflected attributes differently so we need to ensure order
|
||||
// doesn't matter by sorting.
|
||||
descriptors = descriptors.OrderBy(descriptor => descriptor.TagName);
|
||||
|
||||
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -5,39 +5,64 @@ 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
|
||||
{
|
||||
private static readonly string StringFullName = typeof(string).FullName;
|
||||
private static readonly string CollectionsNamespace = typeof(IDictionary<,>).Namespace;
|
||||
|
||||
public static TheoryData RuntimeTypeInfo_ReturnsMetadataOfAdaptingTypeData =>
|
||||
new TheoryData<Type, string>
|
||||
{
|
||||
{ typeof(int), typeof(int).FullName },
|
||||
{ typeof(string), typeof(string).FullName },
|
||||
{ typeof(Tuple<>), typeof(Tuple<>).FullName },
|
||||
{ typeof(Tuple<,>), typeof(Tuple<,>).FullName },
|
||||
{
|
||||
typeof(IDictionary<string, string>),
|
||||
$"{typeof(IDictionary<,>).FullName}[[{StringFullName}],[{StringFullName}]]"
|
||||
},
|
||||
{
|
||||
typeof(IDictionary<string, IDictionary<string, CustomType>>),
|
||||
$"{typeof(IDictionary<,>).FullName}[[{StringFullName}],[{typeof(IDictionary<,>).FullName}" +
|
||||
$"[[{StringFullName}],[{typeof(CustomType).FullName}]]]]"
|
||||
},
|
||||
{
|
||||
typeof(IList<IReadOnlyList<IDictionary<List<string>, Tuple<CustomType, object[]>>>>),
|
||||
$"{typeof(IList<>).FullName}[[{typeof(IReadOnlyList<>).FullName}[[{typeof(IDictionary<,>).FullName}[[" +
|
||||
$"{typeof(List<>).FullName}[[{StringFullName}]]],[{typeof(Tuple<,>).FullName}[[{typeof(CustomType).FullName}]," +
|
||||
$"[{typeof(object).FullName}[]]]]]]]]]"
|
||||
},
|
||||
{ typeof(AbstractType), typeof(AbstractType).FullName },
|
||||
{ typeof(PrivateType), typeof(PrivateType).FullName },
|
||||
{ typeof(KnownKeyDictionary<>), typeof(KnownKeyDictionary<>).FullName },
|
||||
{
|
||||
typeof(KnownKeyDictionary<string>),
|
||||
$"{typeof(KnownKeyDictionary<>).Namespace}" +
|
||||
$".RuntimeTypeInfoTest+KnownKeyDictionary`1[[{StringFullName}]]"
|
||||
}
|
||||
};
|
||||
|
||||
[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)
|
||||
[MemberData(nameof(RuntimeTypeInfo_ReturnsMetadataOfAdaptingTypeData))]
|
||||
public void RuntimeTypeInfo_ReturnsMetadataOfAdaptingType(Type type, string expectedFullName)
|
||||
{
|
||||
// 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);
|
||||
Assert.Same(typeInfo, runtimeTypeInfo.TypeInfo);
|
||||
Assert.Equal(typeInfo.Name, runtimeTypeInfo.Name);
|
||||
Assert.Equal(expectedFullName, runtimeTypeInfo.FullName);
|
||||
Assert.Equal(typeInfo.IsAbstract, runtimeTypeInfo.IsAbstract);
|
||||
Assert.Equal(typeInfo.IsGenericType, runtimeTypeInfo.IsGenericType);
|
||||
Assert.Equal(typeInfo.IsPublic, runtimeTypeInfo.IsPublic);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -150,23 +175,62 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
[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(
|
||||
public void GetGenericDictionaryParameters_ReturnsKeyAndValueParameterTypeNames(
|
||||
Type type,
|
||||
Type[] expectedTypes)
|
||||
{
|
||||
// Arrange
|
||||
var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo());
|
||||
var expected = expectedTypes.Select(t => t?.FullName);
|
||||
|
||||
// Act
|
||||
var actual = runtimeTypeInfo.GetGenericDictionaryParameterNames();
|
||||
var actual = runtimeTypeInfo.GetGenericDictionaryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, actual);
|
||||
Assert.Collection(actual,
|
||||
keyType =>
|
||||
{
|
||||
Assert.Equal(new RuntimeTypeInfo(expectedTypes[0].GetTypeInfo()), keyType);
|
||||
},
|
||||
valueType =>
|
||||
{
|
||||
Assert.Equal(new RuntimeTypeInfo(expectedTypes[1].GetTypeInfo()), valueType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetGenericDictionaryParameters_WorksWhenValueParameterIsOpen()
|
||||
{
|
||||
// Arrange
|
||||
var runtimeTypeInfo = new RuntimeTypeInfo(typeof(KnownKeyDictionary<>).GetTypeInfo());
|
||||
|
||||
// Act
|
||||
var actual = runtimeTypeInfo.GetGenericDictionaryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.Collection(actual,
|
||||
keyType =>
|
||||
{
|
||||
Assert.Equal(new RuntimeTypeInfo(typeof(string).GetTypeInfo()), keyType);
|
||||
},
|
||||
valueType =>
|
||||
{
|
||||
Assert.Null(valueType);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetGenericDictionaryParameters_WorksWhenKeyAndValueParametersAreOpen()
|
||||
{
|
||||
// Arrange
|
||||
var runtimeTypeInfo = new RuntimeTypeInfo(typeof(Dictionary<,>).GetTypeInfo());
|
||||
|
||||
// Act
|
||||
var actual = runtimeTypeInfo.GetGenericDictionaryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new RuntimeTypeInfo[] { null, null }, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -181,12 +245,104 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
var runtimeTypeInfo = new RuntimeTypeInfo(type.GetTypeInfo());
|
||||
|
||||
// Act
|
||||
var actual = runtimeTypeInfo.GetGenericDictionaryParameterNames();
|
||||
var actual = runtimeTypeInfo.GetGenericDictionaryParameters();
|
||||
|
||||
// Assert
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(string))]
|
||||
[InlineData(typeof(IDictionary<,>))]
|
||||
[InlineData(typeof(ITagHelper))]
|
||||
[InlineData(typeof(TagHelper))]
|
||||
public void Equals_ReturnsTrueIfTypeInfosAreIdentical(Type type)
|
||||
{
|
||||
// Arrange
|
||||
var typeA = new RuntimeTypeInfo(type.GetTypeInfo());
|
||||
var typeB = new RuntimeTypeInfo(type.GetTypeInfo());
|
||||
|
||||
// Act
|
||||
var equals = typeA.Equals(typeB);
|
||||
var hashCodeA = typeA.GetHashCode();
|
||||
var hashCodeB = typeB.GetHashCode();
|
||||
|
||||
// Assert
|
||||
Assert.True(equals);
|
||||
Assert.Equal(hashCodeA, hashCodeB);
|
||||
}
|
||||
|
||||
public static TheoryData Equals_ReturnsTrueIfTypeFullNamesAreIdenticalData =>
|
||||
new TheoryData<Type, string>
|
||||
{
|
||||
{ typeof(string), typeof(string).FullName },
|
||||
{ typeof(ITagHelper), typeof(ITagHelper).FullName },
|
||||
{ typeof(TagHelper), typeof(TagHelper).FullName },
|
||||
{ typeof(TagHelper), typeof(TagHelper).FullName },
|
||||
{ typeof(IDictionary<,>), typeof(IDictionary<,>).FullName },
|
||||
{
|
||||
typeof(IDictionary<string,string>),
|
||||
RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary<string, string>).FullName)
|
||||
},
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Equals_ReturnsTrueIfTypeFullNamesAreIdenticalData))]
|
||||
public void Equals_ReturnsTrueIfTypeInfoNamesAreIdentical(Type type, string fullName)
|
||||
{
|
||||
// Arrange
|
||||
var typeA = new RuntimeTypeInfo(type.GetTypeInfo());
|
||||
var typeB = new TestTypeInfo
|
||||
{
|
||||
FullName = fullName
|
||||
};
|
||||
|
||||
// Act
|
||||
var equals = typeA.Equals(typeB);
|
||||
|
||||
// Assert
|
||||
Assert.True(equals);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(string), typeof(object))]
|
||||
[InlineData(typeof(IDictionary<,>), typeof(IDictionary<string, string>))]
|
||||
[InlineData(typeof(KnownKeyDictionary<string>), typeof(IDictionary<string, string>))]
|
||||
[InlineData(typeof(ITagHelper), typeof(TagHelper))]
|
||||
public void Equals_ReturnsFalseIfTypeInfosAreDifferent(Type typeA, Type typeB)
|
||||
{
|
||||
// Arrange
|
||||
var typeAInfo = new RuntimeTypeInfo(typeA.GetTypeInfo());
|
||||
var typeBInfo = new RuntimeTypeInfo(typeB.GetTypeInfo());
|
||||
|
||||
// Act
|
||||
var equals = typeAInfo.Equals(typeBInfo);
|
||||
var hashCodeA = typeAInfo.GetHashCode();
|
||||
var hashCodeB = typeBInfo.GetHashCode();
|
||||
|
||||
// Assert
|
||||
Assert.False(equals);
|
||||
Assert.NotEqual(hashCodeA, hashCodeB);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Equals_ReturnsTrueIfTypeFullNamesAreIdenticalData))]
|
||||
public void Equals_ReturnsFalseIfTypeInfoNamesAreDifferent(Type type, string fullName)
|
||||
{
|
||||
// Arrange
|
||||
var typeA = new RuntimeTypeInfo(type.GetTypeInfo());
|
||||
var typeB = new TestTypeInfo
|
||||
{
|
||||
FullName = "Different" + fullName
|
||||
};
|
||||
|
||||
// Act
|
||||
var equals = typeA.Equals(typeB);
|
||||
|
||||
// Assert
|
||||
Assert.False(equals);
|
||||
}
|
||||
|
||||
public class AbstractType
|
||||
{
|
||||
}
|
||||
|
|
@ -371,5 +527,68 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
private class CustomType
|
||||
{
|
||||
}
|
||||
|
||||
private class TestTypeInfo : ITypeInfo
|
||||
{
|
||||
public string FullName { get; set; }
|
||||
|
||||
public bool IsAbstract
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGenericType
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTagHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IPropertyInfo> Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TAttribute> GetCustomAttributes<TAttribute>() where TAttribute : Attribute
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ITypeInfo[] GetGenericDictionaryParameters()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
|
|
@ -12,10 +11,12 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
public class TagHelperDescriptorFactoryTest
|
||||
public abstract class TagHelperDescriptorFactoryTest
|
||||
{
|
||||
private static readonly string AssemblyName =
|
||||
typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name;
|
||||
protected static readonly AssemblyName TagHelperDescriptorFactoryTestAssembly =
|
||||
typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName();
|
||||
|
||||
protected static readonly string AssemblyName = TagHelperDescriptorFactoryTestAssembly.Name;
|
||||
|
||||
public static TheoryData RestrictChildrenData
|
||||
{
|
||||
|
|
@ -74,6 +75,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
}
|
||||
}
|
||||
|
||||
public abstract ITypeInfo GetTypeInfo(Type tagHelperType);
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RestrictChildrenData))]
|
||||
public void CreateDescriptors_CreatesDescriptorsWithAllowedChildren(
|
||||
|
|
@ -189,88 +192,6 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
// TagHelperDesignTimeDescriptors are not created in CoreCLR.
|
||||
#if !DNXCORE50
|
||||
public static TheoryData OutputElementHintData
|
||||
{
|
||||
get
|
||||
{
|
||||
// tagHelperType, expectedDescriptors
|
||||
return new TheoryData<Type, TagHelperDescriptor[]>
|
||||
{
|
||||
{
|
||||
typeof(OutputElementHintTagHelper),
|
||||
new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "output-element-hint",
|
||||
TypeName = typeof(OutputElementHintTagHelper).FullName,
|
||||
AssemblyName = AssemblyName,
|
||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
OutputElementHint = "strong"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(MulitpleDescriptorTagHelperWithOutputElementHint),
|
||||
new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "a",
|
||||
TypeName = typeof(MulitpleDescriptorTagHelperWithOutputElementHint).FullName,
|
||||
AssemblyName = AssemblyName,
|
||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
OutputElementHint = "div"
|
||||
}
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = typeof(MulitpleDescriptorTagHelperWithOutputElementHint).FullName,
|
||||
AssemblyName = AssemblyName,
|
||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
||||
{
|
||||
OutputElementHint = "div"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(OutputElementHintData))]
|
||||
public void CreateDescriptors_CreatesDesignTimeDescriptorsWithOutputElementHint(
|
||||
Type tagHelperType,
|
||||
TagHelperDescriptor[] expectedDescriptors)
|
||||
{
|
||||
// Arrange
|
||||
var errorSink = new ErrorSink();
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||
AssemblyName,
|
||||
GetTypeInfo(tagHelperType),
|
||||
designTime: true,
|
||||
errorSink: errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(errorSink.Errors);
|
||||
|
||||
// We don't care about order. Mono returns reflected attributes differently so we need to ensure order
|
||||
// doesn't matter by sorting.
|
||||
descriptors = descriptors.OrderBy(descriptor => descriptor.TagName);
|
||||
|
||||
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static TheoryData EditorBrowsableData
|
||||
{
|
||||
get
|
||||
|
|
@ -852,34 +773,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
GetTypeInfo(typeof(InheritedNotOverriddenAttributeTagHelper)),
|
||||
designTime: false,
|
||||
errorSink: errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(errorSink.Errors);
|
||||
Assert.Equal(expectedDescriptors, descriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptors_BuildsDescriptorsFromSimpleTypes()
|
||||
{
|
||||
// Arrange
|
||||
var errorSink = new ErrorSink();
|
||||
var objectAssemblyName = typeof(object).GetTypeInfo().Assembly.GetName().Name;
|
||||
var expectedDescriptor =
|
||||
CreateTagHelperDescriptor("object", "System.Object", objectAssemblyName);
|
||||
|
||||
// Act
|
||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||
objectAssemblyName,
|
||||
GetTypeInfo(typeof(object)),
|
||||
designTime: false,
|
||||
errorSink: errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(errorSink.Errors);
|
||||
var descriptor = Assert.Single(descriptors);
|
||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateDescriptors_BuildsDescriptorsWithInheritedProperties()
|
||||
{
|
||||
|
|
@ -1411,7 +1309,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
Name = "dictionary-property",
|
||||
PropertyName = nameof(DefaultValidHtmlAttributePrefix.DictionaryProperty),
|
||||
TypeName = typeof(IDictionary<string, string>).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary<string, string>).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
|
|
@ -1431,7 +1329,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
Name = "valid-name",
|
||||
PropertyName = nameof(SingleValidHtmlAttributePrefix.DictionaryProperty),
|
||||
TypeName = typeof(IDictionary<string, string>).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(IDictionary<string, string>).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
|
|
@ -1451,31 +1349,31 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
Name = "valid-name1",
|
||||
PropertyName = nameof(MultipleValidHtmlAttributePrefix.DictionaryProperty),
|
||||
TypeName = typeof(Dictionary<string, object>).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(Dictionary<string, object>).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "valid-name2",
|
||||
PropertyName = nameof(MultipleValidHtmlAttributePrefix.DictionarySubclassProperty),
|
||||
TypeName = typeof(DictionarySubclass).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(DictionarySubclass).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "valid-name3",
|
||||
PropertyName = nameof(MultipleValidHtmlAttributePrefix.DictionaryWithoutParameterlessConstructorProperty),
|
||||
TypeName = typeof(DictionaryWithoutParameterlessConstructor).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(DictionaryWithoutParameterlessConstructor).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "valid-name4",
|
||||
PropertyName = nameof(MultipleValidHtmlAttributePrefix.GenericDictionarySubclassProperty),
|
||||
TypeName = typeof(GenericDictionarySubclass<object>).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(GenericDictionarySubclass<object>).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "valid-name5",
|
||||
PropertyName = nameof(MultipleValidHtmlAttributePrefix.SortedDictionaryProperty),
|
||||
TypeName = typeof(SortedDictionary<string, int>).FullName
|
||||
TypeName = RuntimeTypeInfo.SanitizeFullName(typeof(SortedDictionary<string, int>).FullName)
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
|
|
@ -2072,7 +1970,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
return data;
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||
protected static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||
string tagName,
|
||||
string typeName,
|
||||
string assemblyName,
|
||||
|
|
@ -2100,392 +1998,5 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
TypeName = propertyInfo.PropertyType.FullName
|
||||
};
|
||||
}
|
||||
|
||||
private static ITypeInfo GetTypeInfo(Type tagHelperType) =>
|
||||
new RuntimeTypeInfo(tagHelperType.GetTypeInfo());
|
||||
|
||||
[RestrictChildren("p")]
|
||||
private class RestrictChildrenTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[RestrictChildren("p", "strong")]
|
||||
private class DoubleRestrictChildrenTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
[RestrictChildren("p", "strong")]
|
||||
private class MultiTargetRestrictChildrenTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
|
||||
private class TagStructureTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p", TagStructure = TagStructure.NormalOrSelfClosing)]
|
||||
[TargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
|
||||
private class MultiSpecifiedTagStructureTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
|
||||
private class MultiWithUnspecifiedTagStructureTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
private class DefaultEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
private class HiddenPropertyEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
private class MultiPropertyEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int Property { get; set; }
|
||||
|
||||
public virtual int Property2 { get; set; }
|
||||
}
|
||||
|
||||
private class OverriddenPropertyEditorBrowsableTagHelper : MultiPropertyEditorBrowsableTagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override int Property2 { get; set; }
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
private class EditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public virtual int Property { get; set; }
|
||||
}
|
||||
|
||||
private class InheritedEditorBrowsableTagHelper : EditorBrowsableTagHelper
|
||||
{
|
||||
public override int Property { get; set; }
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
private class OverriddenEditorBrowsableTagHelper : EditorBrowsableTagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public override int Property { get; set; }
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
private class MultiEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class*")]
|
||||
private class AttributeWildcardTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class*,style*")]
|
||||
private class MultiAttributeWildcardTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class")]
|
||||
private class AttributeTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class,style")]
|
||||
private class MultiAttributeTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "custom")]
|
||||
[TargetElement(Attributes = "class,style")]
|
||||
private class MultiAttributeAttributeTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "style")]
|
||||
private class InheritedAttributeTargetingTagHelper : AttributeTargetingTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", Attributes = "class")]
|
||||
private class RequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "class")]
|
||||
private class InheritedRequiredAttributeTagHelper : RequiredAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "class")]
|
||||
[TargetElement("input", Attributes = "class")]
|
||||
private class MultiAttributeRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", Attributes = "style")]
|
||||
[TargetElement("input", Attributes = "class")]
|
||||
private class MultiAttributeSameTagRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", Attributes = "class,style")]
|
||||
private class MultiRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "style")]
|
||||
private class InheritedMultiRequiredAttributeTagHelper : MultiRequiredAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "class,style")]
|
||||
[TargetElement("input", Attributes = "class,style")]
|
||||
private class MultiTagMultiRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
private class MultiTagTagHelper
|
||||
{
|
||||
public string ValidAttribute { get; set; }
|
||||
}
|
||||
|
||||
private class InheritedMultiTagTagHelper : MultiTagTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
[TargetElement("div")]
|
||||
private class DuplicateTagNameTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("data-condition")]
|
||||
private class OverrideNameTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
private class InheritedSingleAttributeTagHelper : SingleAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
private class DuplicateAttributeNameTagHelper
|
||||
{
|
||||
public string MyNameIsLegion { get; set; }
|
||||
|
||||
[HtmlAttributeName("my-name-is-legion")]
|
||||
public string Fred { get; set; }
|
||||
}
|
||||
|
||||
private class NotBoundAttributeTagHelper
|
||||
{
|
||||
public object BoundProperty { get; set; }
|
||||
|
||||
[HtmlAttributeNotBound]
|
||||
public string NotBoundProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("unused")]
|
||||
[HtmlAttributeNotBound]
|
||||
public string NamedNotBoundProperty { get; set; }
|
||||
}
|
||||
|
||||
private class OverriddenAttributeTagHelper
|
||||
{
|
||||
[HtmlAttributeName("SomethingElse")]
|
||||
public virtual string ValidAttribute1 { get; set; }
|
||||
|
||||
[HtmlAttributeName("Something-Else")]
|
||||
public string ValidAttribute2 { get; set; }
|
||||
}
|
||||
|
||||
private class InheritedOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
|
||||
{
|
||||
public override string ValidAttribute1 { get; set; }
|
||||
}
|
||||
|
||||
private class InheritedNotOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
private class ALLCAPSTAGHELPER : TagHelper
|
||||
{
|
||||
public int ALLCAPSATTRIBUTE { get; set; }
|
||||
}
|
||||
|
||||
private class CAPSOnOUTSIDETagHelper : TagHelper
|
||||
{
|
||||
public int CAPSOnOUTSIDEATTRIBUTE { get; set; }
|
||||
}
|
||||
|
||||
private class capsONInsideTagHelper : TagHelper
|
||||
{
|
||||
public int capsONInsideattribute { get; set; }
|
||||
}
|
||||
|
||||
private class One1Two2Three3TagHelper : TagHelper
|
||||
{
|
||||
public int One1Two2Three3Attribute { get; set; }
|
||||
}
|
||||
|
||||
private class ONE1TWO2THREE3TagHelper : TagHelper
|
||||
{
|
||||
public int ONE1TWO2THREE3Attribute { get; set; }
|
||||
}
|
||||
|
||||
private class First_Second_ThirdHiTagHelper : TagHelper
|
||||
{
|
||||
public int First_Second_ThirdAttribute { get; set; }
|
||||
}
|
||||
|
||||
private class UNSuffixedCLASS : TagHelper
|
||||
{
|
||||
public int UNSuffixedATTRIBUTE { get; set; }
|
||||
}
|
||||
|
||||
private class InvalidBoundAttribute : TagHelper
|
||||
{
|
||||
public string DataSomething { get; set; }
|
||||
}
|
||||
|
||||
private class InvalidBoundAttributeWithValid : SingleAttributeTagHelper
|
||||
{
|
||||
public string DataSomething { get; set; }
|
||||
}
|
||||
|
||||
private class OverriddenInvalidBoundAttributeWithValid : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-something")]
|
||||
public string DataSomething { get; set; }
|
||||
}
|
||||
|
||||
private class OverriddenValidBoundAttributeWithInvalid : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("data-something")]
|
||||
public string ValidSomething { get; set; }
|
||||
}
|
||||
|
||||
private class OverriddenValidBoundAttributeWithInvalidUpperCase : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("DATA-SOMETHING")]
|
||||
public string ValidSomething { get; set; }
|
||||
}
|
||||
|
||||
private class DefaultValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
public IDictionary<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
|
||||
private class SingleValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name")]
|
||||
public IDictionary<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
|
||||
private class MultipleValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name1", DictionaryAttributePrefix = "valid-prefix1-")]
|
||||
public Dictionary<string, object> DictionaryProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name2", DictionaryAttributePrefix = "valid-prefix2-")]
|
||||
public DictionarySubclass DictionarySubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name3", DictionaryAttributePrefix = "valid-prefix3-")]
|
||||
public DictionaryWithoutParameterlessConstructor DictionaryWithoutParameterlessConstructorProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name4", DictionaryAttributePrefix = "valid-prefix4-")]
|
||||
public GenericDictionarySubclass<object> GenericDictionarySubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
|
||||
public SortedDictionary<string, int> SortedDictionaryProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name6")]
|
||||
public string StringProperty { get; set; }
|
||||
|
||||
public IDictionary<string, int> GetOnlyDictionaryProperty { get; }
|
||||
|
||||
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
|
||||
public IDictionary<string, string> GetOnlyDictionaryPropertyWithAttributePrefix { get; }
|
||||
}
|
||||
|
||||
private class SingleInvalidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name", DictionaryAttributePrefix = "valid-prefix")]
|
||||
public string StringProperty { get; set; }
|
||||
}
|
||||
|
||||
private class MultipleInvalidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name1")]
|
||||
public long LongProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name2", DictionaryAttributePrefix = "valid-prefix2-")]
|
||||
public Dictionary<int, string> DictionaryOfIntProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name3", DictionaryAttributePrefix = "valid-prefix3-")]
|
||||
public IReadOnlyDictionary<string, object> ReadOnlyDictionaryProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name4", DictionaryAttributePrefix = "valid-prefix4-")]
|
||||
public int IntProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
|
||||
public DictionaryOfIntSubclass DictionaryOfIntSubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
|
||||
public IDictionary<int, string> GetOnlyDictionaryAttributePrefix { get; }
|
||||
|
||||
[HtmlAttributeName("invalid-name7")]
|
||||
public IDictionary<string, object> GetOnlyDictionaryPropertyWithAttributeName { get; }
|
||||
}
|
||||
|
||||
private class DictionarySubclass : Dictionary<string, string>
|
||||
{
|
||||
}
|
||||
|
||||
private class DictionaryWithoutParameterlessConstructor : Dictionary<string, string>
|
||||
{
|
||||
public DictionaryWithoutParameterlessConstructor(int count)
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class DictionaryOfIntSubclass : Dictionary<int, string>
|
||||
{
|
||||
}
|
||||
|
||||
private class GenericDictionarySubclass<TValue> : Dictionary<string, TValue>
|
||||
{
|
||||
}
|
||||
|
||||
[OutputElementHint("strong")]
|
||||
private class OutputElementHintTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("a")]
|
||||
[TargetElement("p")]
|
||||
[OutputElementHint("div")]
|
||||
private class MulitpleDescriptorTagHelperWithOutputElementHint : TagHelper
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,446 @@
|
|||
// 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.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
|
||||
[RestrictChildren("p")]
|
||||
public class RestrictChildrenTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[RestrictChildren("p", "strong")]
|
||||
public class DoubleRestrictChildrenTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
[RestrictChildren("p", "strong")]
|
||||
public class MultiTargetRestrictChildrenTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
|
||||
public class TagStructureTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p", TagStructure = TagStructure.NormalOrSelfClosing)]
|
||||
[TargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
|
||||
public class MultiSpecifiedTagStructureTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
|
||||
public class MultiWithUnspecifiedTagStructureTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
public class DefaultEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
public class HiddenPropertyEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int Property { get; set; }
|
||||
}
|
||||
|
||||
public class MultiPropertyEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public int Property { get; set; }
|
||||
|
||||
public virtual int Property2 { get; set; }
|
||||
}
|
||||
|
||||
public class OverriddenPropertyEditorBrowsableTagHelper : MultiPropertyEditorBrowsableTagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override int Property2 { get; set; }
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class EditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public virtual int Property { get; set; }
|
||||
}
|
||||
|
||||
public class InheritedEditorBrowsableTagHelper : EditorBrowsableTagHelper
|
||||
{
|
||||
public override int Property { get; set; }
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public class OverriddenEditorBrowsableTagHelper : EditorBrowsableTagHelper
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public override int Property { get; set; }
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class MultiEditorBrowsableTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class*")]
|
||||
public class AttributeWildcardTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class*,style*")]
|
||||
public class MultiAttributeWildcardTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class")]
|
||||
public class AttributeTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "class,style")]
|
||||
public class MultiAttributeTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "custom")]
|
||||
[TargetElement(Attributes = "class,style")]
|
||||
public class MultiAttributeAttributeTargetingTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement(Attributes = "style")]
|
||||
public class InheritedAttributeTargetingTagHelper : AttributeTargetingTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", Attributes = "class")]
|
||||
public class RequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "class")]
|
||||
public class InheritedRequiredAttributeTagHelper : RequiredAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "class")]
|
||||
[TargetElement("input", Attributes = "class")]
|
||||
public class MultiAttributeRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", Attributes = "style")]
|
||||
[TargetElement("input", Attributes = "class")]
|
||||
public class MultiAttributeSameTagRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("input", Attributes = "class,style")]
|
||||
public class MultiRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "style")]
|
||||
public class InheritedMultiRequiredAttributeTagHelper : MultiRequiredAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("div", Attributes = "class,style")]
|
||||
[TargetElement("input", Attributes = "class,style")]
|
||||
public class MultiTagMultiRequiredAttributeTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
public class MultiTagTagHelper
|
||||
{
|
||||
public string ValidAttribute { get; set; }
|
||||
}
|
||||
|
||||
public class InheritedMultiTagTagHelper : MultiTagTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("p")]
|
||||
[TargetElement("p")]
|
||||
[TargetElement("div")]
|
||||
[TargetElement("div")]
|
||||
public class DuplicateTagNameTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("data-condition")]
|
||||
public class OverrideNameTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class InheritedSingleAttributeTagHelper : SingleAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class DuplicateAttributeNameTagHelper
|
||||
{
|
||||
public string MyNameIsLegion { get; set; }
|
||||
|
||||
[HtmlAttributeName("my-name-is-legion")]
|
||||
public string Fred { get; set; }
|
||||
}
|
||||
|
||||
public class NotBoundAttributeTagHelper
|
||||
{
|
||||
public object BoundProperty { get; set; }
|
||||
|
||||
[HtmlAttributeNotBound]
|
||||
public string NotBoundProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("unused")]
|
||||
[HtmlAttributeNotBound]
|
||||
public string NamedNotBoundProperty { get; set; }
|
||||
}
|
||||
|
||||
public class OverriddenAttributeTagHelper
|
||||
{
|
||||
[HtmlAttributeName("SomethingElse")]
|
||||
public virtual string ValidAttribute1 { get; set; }
|
||||
|
||||
[HtmlAttributeName("Something-Else")]
|
||||
public string ValidAttribute2 { get; set; }
|
||||
}
|
||||
|
||||
public class InheritedOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
|
||||
{
|
||||
public override string ValidAttribute1 { get; set; }
|
||||
}
|
||||
|
||||
public class InheritedNotOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class ALLCAPSTAGHELPER : TagHelper
|
||||
{
|
||||
public int ALLCAPSATTRIBUTE { get; set; }
|
||||
}
|
||||
|
||||
public class CAPSOnOUTSIDETagHelper : TagHelper
|
||||
{
|
||||
public int CAPSOnOUTSIDEATTRIBUTE { get; set; }
|
||||
}
|
||||
|
||||
public class capsONInsideTagHelper : TagHelper
|
||||
{
|
||||
public int capsONInsideattribute { get; set; }
|
||||
}
|
||||
|
||||
public class One1Two2Three3TagHelper : TagHelper
|
||||
{
|
||||
public int One1Two2Three3Attribute { get; set; }
|
||||
}
|
||||
|
||||
public class ONE1TWO2THREE3TagHelper : TagHelper
|
||||
{
|
||||
public int ONE1TWO2THREE3Attribute { get; set; }
|
||||
}
|
||||
|
||||
public class First_Second_ThirdHiTagHelper : TagHelper
|
||||
{
|
||||
public int First_Second_ThirdAttribute { get; set; }
|
||||
}
|
||||
|
||||
public class UNSuffixedCLASS : TagHelper
|
||||
{
|
||||
public int UNSuffixedATTRIBUTE { get; set; }
|
||||
}
|
||||
|
||||
public class InvalidBoundAttribute : TagHelper
|
||||
{
|
||||
public string DataSomething { get; set; }
|
||||
}
|
||||
|
||||
public class InvalidBoundAttributeWithValid : SingleAttributeTagHelper
|
||||
{
|
||||
public string DataSomething { get; set; }
|
||||
}
|
||||
|
||||
public class OverriddenInvalidBoundAttributeWithValid : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-something")]
|
||||
public string DataSomething { get; set; }
|
||||
}
|
||||
|
||||
public class OverriddenValidBoundAttributeWithInvalid : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("data-something")]
|
||||
public string ValidSomething { get; set; }
|
||||
}
|
||||
|
||||
public class OverriddenValidBoundAttributeWithInvalidUpperCase : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("DATA-SOMETHING")]
|
||||
public string ValidSomething { get; set; }
|
||||
}
|
||||
|
||||
public class DefaultValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
public IDictionary<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
|
||||
public class SingleValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name")]
|
||||
public IDictionary<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
|
||||
public class MultipleValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name1", DictionaryAttributePrefix = "valid-prefix1-")]
|
||||
public Dictionary<string, object> DictionaryProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name2", DictionaryAttributePrefix = "valid-prefix2-")]
|
||||
public DictionarySubclass DictionarySubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name3", DictionaryAttributePrefix = "valid-prefix3-")]
|
||||
public DictionaryWithoutParameterlessConstructor DictionaryWithoutParameterlessConstructorProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name4", DictionaryAttributePrefix = "valid-prefix4-")]
|
||||
public GenericDictionarySubclass<object> GenericDictionarySubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
|
||||
public SortedDictionary<string, int> SortedDictionaryProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name6")]
|
||||
public string StringProperty { get; set; }
|
||||
|
||||
public IDictionary<string, int> GetOnlyDictionaryProperty { get; }
|
||||
|
||||
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
|
||||
public IDictionary<string, string> GetOnlyDictionaryPropertyWithAttributePrefix { get; }
|
||||
}
|
||||
|
||||
public class SingleInvalidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name", DictionaryAttributePrefix = "valid-prefix")]
|
||||
public string StringProperty { get; set; }
|
||||
}
|
||||
|
||||
public class MultipleInvalidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name1")]
|
||||
public long LongProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name2", DictionaryAttributePrefix = "valid-prefix2-")]
|
||||
public Dictionary<int, string> DictionaryOfIntProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name3", DictionaryAttributePrefix = "valid-prefix3-")]
|
||||
public IReadOnlyDictionary<string, object> ReadOnlyDictionaryProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name4", DictionaryAttributePrefix = "valid-prefix4-")]
|
||||
public int IntProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
|
||||
public DictionaryOfIntSubclass DictionaryOfIntSubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
|
||||
public IDictionary<int, string> GetOnlyDictionaryAttributePrefix { get; }
|
||||
|
||||
[HtmlAttributeName("invalid-name7")]
|
||||
public IDictionary<string, object> GetOnlyDictionaryPropertyWithAttributeName { get; }
|
||||
}
|
||||
|
||||
public class DictionarySubclass : Dictionary<string, string>
|
||||
{
|
||||
}
|
||||
|
||||
public class DictionaryWithoutParameterlessConstructor : Dictionary<string, string>
|
||||
{
|
||||
public DictionaryWithoutParameterlessConstructor(int count)
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class DictionaryOfIntSubclass : Dictionary<int, string>
|
||||
{
|
||||
}
|
||||
|
||||
public class GenericDictionarySubclass<TValue> : Dictionary<string, TValue>
|
||||
{
|
||||
}
|
||||
|
||||
[OutputElementHint("strong")]
|
||||
public class OutputElementHintTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
[TargetElement("a")]
|
||||
[TargetElement("p")]
|
||||
[OutputElementHint("div")]
|
||||
public class MulitpleDescriptorTagHelperWithOutputElementHint : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class TypeDerivingFromITagHelper : ITagHelper
|
||||
{
|
||||
public int Order { get; } = 0;
|
||||
|
||||
public Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public class BaseAttribute : Attribute
|
||||
{
|
||||
public string BaseProperty { get; set; }
|
||||
}
|
||||
|
||||
public class DerivedAttribute : BaseAttribute
|
||||
{
|
||||
public string DerivedProperty { get; set; }
|
||||
}
|
||||
|
||||
public class BaseTagHelper : TagHelper
|
||||
{
|
||||
[Derived(DerivedProperty = "DerivedPropertyValue")]
|
||||
public string BaseProperty { get; set; }
|
||||
|
||||
[HtmlAttributeNotBound]
|
||||
public virtual string VirtualProperty { get; set; }
|
||||
|
||||
public int NewProperty { get; set; }
|
||||
|
||||
public virtual new string Order { get; set; }
|
||||
}
|
||||
|
||||
public class DerivedTagHelper : BaseTagHelper
|
||||
{
|
||||
public override string VirtualProperty { get; set; }
|
||||
|
||||
[Base(BaseProperty = "BaseValue")]
|
||||
public string DerivedProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName("new-property")]
|
||||
public new Type NewProperty { get; set; }
|
||||
|
||||
public override string Order { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class TagHelperInGlobalNamespace : TagHelper
|
||||
{
|
||||
}
|
||||
Loading…
Reference in New Issue