|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using Implab.Diagnostics;
|
|
|
|
|
|
namespace Implab.ServiceHost.Unity {
|
|
|
using static Trace<TypeResolver>;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Позволяет обойти вложенные типы и собрать цеочку из типов и параметров генериков, которые они предлагают
|
|
|
/// </summary>
|
|
|
internal class TypeResolutionContext {
|
|
|
readonly TypeResolver m_resolver;
|
|
|
|
|
|
|
|
|
readonly List<Type> m_genericParameters = new List<Type>();
|
|
|
|
|
|
public IEnumerable<Type> GenericParameters { get { return m_genericParameters; } }
|
|
|
|
|
|
public Type ResolvedType { get; private set; }
|
|
|
|
|
|
|
|
|
public int ArrayRank { get; private set; }
|
|
|
|
|
|
|
|
|
public bool ThrowOnFail { get; private set; }
|
|
|
|
|
|
public TypeResolutionContext(TypeResolver resolver, bool throwOnFail) {
|
|
|
m_resolver = resolver;
|
|
|
ThrowOnFail = throwOnFail;
|
|
|
}
|
|
|
|
|
|
public Type MakeType() {
|
|
|
return m_genericParameters.Count > 0 ?
|
|
|
ResolvedType?.MakeGenericType(m_genericParameters.ToArray()) :
|
|
|
ResolvedType;
|
|
|
}
|
|
|
|
|
|
public void Visit(SpecializedTypeReference typeReference) {
|
|
|
typeReference.GenericType.Visit(this);
|
|
|
|
|
|
if (ResolvedType != null) {
|
|
|
foreach (var genericParamRef in typeReference.GenericParameters) {
|
|
|
var context = new TypeResolutionContext(m_resolver, ThrowOnFail);
|
|
|
genericParamRef.Visit(context);
|
|
|
m_genericParameters.Add(context.MakeType());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void Visit(NestedTypeReference typeReference) {
|
|
|
typeReference.DeclaringType.Visit(this);
|
|
|
if (ResolvedType != null)
|
|
|
ResolvedType = ResolvedType?.GetNestedType(typeReference.ClrName) ?? Failed(typeReference);
|
|
|
}
|
|
|
|
|
|
public void Visit(RootTypeReference typeReference) {
|
|
|
ResolvedType = m_resolver.Resolve(typeReference.Namespace, typeReference.ClrName) ?? Failed(typeReference);
|
|
|
}
|
|
|
|
|
|
public void Visit(ArrayTypeReference typeReference) {
|
|
|
var context = new TypeResolutionContext(m_resolver, ThrowOnFail);
|
|
|
typeReference.ItemsType.Visit(context);
|
|
|
ResolvedType = typeReference.Rank == 1 ?
|
|
|
context.MakeType()?.MakeArrayType() :
|
|
|
context.MakeType()?.MakeArrayType(typeReference.Rank);
|
|
|
}
|
|
|
|
|
|
Type Failed(TypeReference reference) {
|
|
|
Log($"Falied to resolve {reference}");
|
|
|
if (ThrowOnFail)
|
|
|
throw new Exception($"Failed to resolve {reference}");
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
}
|