Skip to content

Commit

Permalink
Improved Input Object Type inference and formatter compilation (#5498)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Oct 24, 2022
1 parent df29d05 commit a3a57bc
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 121 deletions.
5 changes: 5 additions & 0 deletions src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs
Expand Up @@ -9,6 +9,7 @@
using HotChocolate.Types;
using HotChocolate.Types.Descriptors;
using HotChocolate.Types.Factories;
using HotChocolate.Types.Helpers;
using HotChocolate.Types.Interceptors;
using HotChocolate.Utilities;
using HotChocolate.Utilities.Introspection;
Expand Down Expand Up @@ -71,6 +72,10 @@ public static Schema Create(SchemaBuilder builder)
context.SchemaInterceptor.OnError(context, ex);
throw;
}
finally
{
TypeMemHelper.Clear();
}
}

public static DescriptorContext CreateContext(
Expand Down
Expand Up @@ -5,28 +5,63 @@

namespace HotChocolate.Types;

/// <summary>
/// Represents the field collection of a type.
/// </summary>
/// <typeparam name="T">
/// The field type.
/// </typeparam>
public interface IFieldCollection<out T> : IReadOnlyCollection<T> where T : class, IField
{
/// <summary>
/// Gets a field by its name.
/// </summary>
T this[string fieldName] { get; }

/// <summary>
/// Gets a field by its index.
/// </summary>
T this[int index] { get; }

/// <summary>
/// Checks if a field with the specified
/// <paramref name="fieldName"/> exists in this collection.
/// </summary>
/// <param name="fieldName">
/// The name of a field.
/// </param>
/// <returns>
/// Returns <c>true</c> if a field with the specified <paramref name="fieldName"/> exists;
/// otherwise, <c>false</c>.
/// </returns>
bool ContainsField(string fieldName);
}

/// <summary>
/// This helper class provides extensions to the <see cref="IFieldCollection{T}" /> interface
/// to allow for more efficiency when using the interface.
/// </summary>
public static class FieldCollectionExtensions
{
/// <summary>
/// Tries to get a field by its name from the field collection.
/// </summary>
/// <typeparam name="T">
/// The type of the field.
/// </typeparam>
public static bool TryGetField<T>(
this IFieldCollection<T> collection,
string fieldName,
[NotNullWhen(true)] out T? field)
where T : class, IField
{
// if we use the default implementation we will use the TryGetField method on there.
if (collection is FieldCollection<T> fc)
{
return fc.TryGetField(fieldName, out field);
}

// in any other case we simulate the behavior which is not as efficient.
if (collection.ContainsField(fieldName))
{
field = collection[fieldName];
Expand Down
@@ -1,3 +1,5 @@
#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -13,6 +15,8 @@ public class InputObjectTypeDescriptor
: DescriptorBase<InputObjectTypeDefinition>
, IInputObjectTypeDescriptor
{
private readonly List<InputFieldDescriptor> _fields = new();

protected InputObjectTypeDescriptor(IDescriptorContext context, Type runtimeType)
: base(context)
{
Expand Down Expand Up @@ -43,14 +47,14 @@ protected InputObjectTypeDescriptor(IDescriptorContext context)

foreach (var field in definition.Fields)
{
Fields.Add(InputFieldDescriptor.From(Context, field));
_fields.Add(InputFieldDescriptor.From(Context, field));
}
}

protected internal override InputObjectTypeDefinition Definition { get; protected set; } =
new();

protected List<InputFieldDescriptor> Fields { get; } = new();
protected ICollection<InputFieldDescriptor> Fields => _fields;

protected override void OnCreateDefinition(
InputObjectTypeDefinition definition)
Expand All @@ -64,28 +68,77 @@ protected InputObjectTypeDescriptor(IDescriptorContext context)
Definition.AttributesAreApplied = true;
}

var fields = new Dictionary<string, InputFieldDefinition>();
var handledProperties = new HashSet<PropertyInfo>();
var fields = TypeMemHelper.RentInputFieldDefinitionMap();
var handledMembers = TypeMemHelper.RentMemberSet();

FieldDescriptorUtilities.AddExplicitFields(
Fields.Select(t => t.CreateDefinition()),
f => f.Property,
fields,
handledProperties);
foreach (var fieldDescriptor in _fields)
{
var fieldDefinition = fieldDescriptor.CreateDefinition();

OnCompleteFields(fields, handledProperties);
if (!fieldDefinition.Ignore && !string.IsNullOrEmpty(fieldDefinition.Name))
{
fields[fieldDefinition.Name] = fieldDefinition;
}

if (fieldDefinition.Property is { } prop)
{
handledMembers.Add(prop);
}
}

OnCompleteFields(fields, handledMembers);

Definition.Fields.Clear();
Definition.Fields.AddRange(fields.Values);

TypeMemHelper.Return(fields);
TypeMemHelper.Return(handledMembers);

base.OnCreateDefinition(definition);
}

protected virtual void OnCompleteFields(
protected void InferFieldsFromFieldBindingType(
IDictionary<string, InputFieldDefinition> fields,
ISet<PropertyInfo> handledProperties)
ISet<MemberInfo> handledMembers)
{
if (Definition.Fields.IsImplicitBinding())
{
var inspector = Context.TypeInspector;
var naming = Context.Naming;
var type = Definition.RuntimeType;
var members = inspector.GetMembers(type);

foreach (var member in members)
{
if (member.MemberType is MemberTypes.Property)
{
var name = naming.GetMemberName(member, MemberKind.InputObjectField);

if (handledMembers.Add(member) &&
!fields.ContainsKey(name))
{
var descriptor = InputFieldDescriptor.New(
Context,
(PropertyInfo)member);

_fields.Add(descriptor);
handledMembers.Add(member);

// the create definition call will trigger the OnCompleteField call
// on the field description and trigger the initialization of the
// fields arguments.
fields[name] = descriptor.CreateDefinition();
}
}
}
}
}

protected virtual void OnCompleteFields(
IDictionary<string, InputFieldDefinition> fields,
ISet<MemberInfo> handledProperties)
{ }

public IInputObjectTypeDescriptor SyntaxNode(
InputObjectTypeDefinitionNode inputObjectTypeDefinition)
{
Expand All @@ -107,15 +160,15 @@ public IInputObjectTypeDescriptor Description(string value)

public IInputFieldDescriptor Field(string name)
{
var fieldDescriptor = Fields.FirstOrDefault(t => t.Definition.Name.EqualsOrdinal(name));
var fieldDescriptor = _fields.Find(t => t.Definition.Name.EqualsOrdinal(name));

if (fieldDescriptor is not null)
{
return fieldDescriptor;
}

fieldDescriptor = new InputFieldDescriptor(Context, name);
Fields.Add(fieldDescriptor);
_fields.Add(fieldDescriptor);
return fieldDescriptor;
}

Expand Down
Expand Up @@ -33,14 +33,14 @@ protected internal InputObjectTypeDescriptor(IDescriptorContext context)

protected override void OnCompleteFields(
IDictionary<string, InputFieldDefinition> fields,
ISet<PropertyInfo> handledProperties)
ISet<MemberInfo> handledProperties)
{
if (Definition.Fields.IsImplicitBinding())
{
FieldDescriptorUtilities.AddImplicitFields(
this,
p => InputFieldDescriptor
.New(Context, p)
.New(Context, (PropertyInfo)p)
.CreateDefinition(),
fields,
handledProperties);
Expand Down
@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using HotChocolate.Language;
using HotChocolate.Types.Descriptors.Definitions;
using HotChocolate.Types.Helpers;
Expand All @@ -18,11 +15,9 @@ namespace HotChocolate.Types.Descriptors;

public class ObjectTypeDescriptor
: DescriptorBase<ObjectTypeDefinition>
, IObjectTypeDescriptor
, IObjectTypeDescriptor
{
private readonly List<ObjectFieldDescriptor> _fields = new();
private static Dictionary<string, ObjectFieldDefinition>? _definitionMap = null;
private static HashSet<MemberInfo>? _memberSet = null;

protected ObjectTypeDescriptor(IDescriptorContext context, Type clrType)
: base(context)
Expand Down Expand Up @@ -52,7 +47,7 @@ protected ObjectTypeDescriptor(IDescriptorContext context)

foreach (var field in definition.Fields)
{
Fields.Add(ObjectFieldDescriptor.From(Context, field));
_fields.Add(ObjectFieldDescriptor.From(Context, field));
}
}

Expand Down Expand Up @@ -84,10 +79,8 @@ protected ObjectTypeDescriptor(IDescriptorContext context)
}
}

var fields = Interlocked.Exchange(ref _definitionMap, null) ??
new Dictionary<string, ObjectFieldDefinition>();
var handledMembers =
Interlocked.Exchange(ref _memberSet, null) ?? new HashSet<MemberInfo>();
var fields = TypeMemHelper.RentObjectFieldDefinitionMap();
var handledMembers = TypeMemHelper.RentMemberSet();

foreach (var fieldDescriptor in _fields)
{
Expand Down Expand Up @@ -116,29 +109,21 @@ protected ObjectTypeDescriptor(IDescriptorContext context)
Definition.Fields.Clear();
Definition.Fields.AddRange(fields.Values);

fields.Clear();
handledMembers.Clear();

Interlocked.CompareExchange(ref _definitionMap, fields, null);
Interlocked.CompareExchange(ref _memberSet, handledMembers, null);
TypeMemHelper.Return(fields);
TypeMemHelper.Return(handledMembers);

base.OnCreateDefinition(definition);
}

internal void InferFieldsFromFieldBindingType()
{
var fields = Interlocked.Exchange(ref _definitionMap, null) ??
new Dictionary<string, ObjectFieldDefinition>();
var handledMembers =
Interlocked.Exchange(ref _memberSet, null) ?? new HashSet<MemberInfo>();
var fields = TypeMemHelper.RentObjectFieldDefinitionMap();
var handledMembers = TypeMemHelper.RentMemberSet();

InferFieldsFromFieldBindingType(fields, handledMembers);

fields.Clear();
handledMembers.Clear();

Interlocked.CompareExchange(ref _definitionMap, fields, null);
Interlocked.CompareExchange(ref _memberSet, handledMembers, null);
TypeMemHelper.Return(fields);
TypeMemHelper.Return(handledMembers);
}

protected void InferFieldsFromFieldBindingType(
Expand Down Expand Up @@ -176,7 +161,7 @@ internal void InferFieldsFromFieldBindingType()
descriptor.Ignore();
}

Fields.Add(descriptor);
_fields.Add(descriptor);
handledMembers.Add(member);

// the create definition call will trigger the OnCompleteField call
Expand Down Expand Up @@ -305,15 +290,15 @@ public IObjectTypeDescriptor IsOfType(IsOfType? isOfType)

public IObjectFieldDescriptor Field(string name)
{
var fieldDescriptor = Fields.FirstOrDefault(t => t.Definition.Name.EqualsOrdinal(name));
var fieldDescriptor = _fields.Find(t => t.Definition.Name.EqualsOrdinal(name));

if (fieldDescriptor is not null)
{
return fieldDescriptor;
}

fieldDescriptor = ObjectFieldDescriptor.New(Context, name);
Fields.Add(fieldDescriptor);
_fields.Add(fieldDescriptor);
return fieldDescriptor;
}

Expand All @@ -331,8 +316,7 @@ public IObjectFieldDescriptor Field(string name)

if (propertyOrMethod is PropertyInfo || propertyOrMethod is MethodInfo)
{
var fieldDescriptor = Fields.FirstOrDefault(
t => t.Definition.Member == propertyOrMethod);
var fieldDescriptor = _fields.Find(t => t.Definition.Member == propertyOrMethod);

if (fieldDescriptor is not null)
{
Expand All @@ -344,7 +328,7 @@ public IObjectFieldDescriptor Field(string name)
propertyOrMethod,
Definition.RuntimeType,
propertyOrMethod.ReflectedType ?? Definition.RuntimeType);
Fields.Add(fieldDescriptor);
_fields.Add(fieldDescriptor);
return fieldDescriptor;
}

Expand All @@ -365,8 +349,7 @@ public IObjectFieldDescriptor Field(string name)

if (member is PropertyInfo or MethodInfo)
{
var fieldDescriptor = Fields.FirstOrDefault(
t => t.Definition.Member == member);
var fieldDescriptor = _fields.Find(t => t.Definition.Member == member);

if (fieldDescriptor is not null)
{
Expand All @@ -378,7 +361,7 @@ public IObjectFieldDescriptor Field(string name)
member,
Definition.RuntimeType,
typeof(TResolver));
Fields.Add(fieldDescriptor);
_fields.Add(fieldDescriptor);
return fieldDescriptor;
}

Expand All @@ -389,7 +372,7 @@ public IObjectFieldDescriptor Field(string name)
propertyOrMethod,
Definition.RuntimeType,
typeof(TResolver));
Fields.Add(fieldDescriptor);
_fields.Add(fieldDescriptor);
return fieldDescriptor;
}

Expand Down

0 comments on commit a3a57bc

Please sign in to comment.