Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Directory.build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<Authors>Ian Johnson</Authors>
<TargetFrameworks>net462;net6.0;net7.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<DebugType>full</DebugType>
</PropertyGroup>
</Project>
5 changes: 1 addition & 4 deletions src/Grace.Dynamic/Grace.Dynamic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

<PropertyGroup>
<Description>IL Generation library for Grace dependency injection container</Description>
<Authors>Ian Johnson</Authors>
<TargetFrameworks>net462;net6.0;net7.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>Grace.Dynamic</AssemblyName>
<AssemblyOriginatorKeyFile>..\Grace.snk</AssemblyOriginatorKeyFile>
Expand All @@ -18,8 +16,7 @@
<RepositoryUrl>https://github.com/ipjohnson/Grace</RepositoryUrl>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<DebugType>full</DebugType>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>

<PropertyGroup Condition="'$(OS)' == 'Windows_NT'">
Expand Down
3 changes: 2 additions & 1 deletion src/Grace.Factory/Impl/DynamicFactoryStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ protected override IActivationExpressionResult CreateExpression(IInjectionScope
{
request.ScopeParameter,
request.DisposalScopeExpression,
request.InjectionContextParameter
request.InjectionContextParameter,
Expression.Constant(request.LocateKey, typeof(object)),
};

var uniqueId = UniqueStringId.Generate();
Expand Down
75 changes: 55 additions & 20 deletions src/Grace.Factory/Impl/DynamicTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@ public class DynamicTypeBuilder
private static readonly object BuilderLock = new object();
private static ModuleBuilder _moduleBuilder;
private static int _proxyCount = 0;
private static MethodInfo CloneMethod = typeof(IInjectionContext).GetRuntimeMethod(nameof(IInjectionContext.Clone),Type.EmptyTypes);
private static MethodInfo DelegateInvoke = typeof(ActivationStrategyDelegate).GetRuntimeMethod(nameof(ActivationStrategyDelegate.Invoke),
new[] {typeof(IExportLocatorScope), typeof(IDisposalScope), typeof(IInjectionContext)});
private static MethodInfo SetExtraDataMethod =
typeof(IExtraDataContainer).GetRuntimeMethod(nameof(IExtraDataContainer.SetExtraData), new[] { typeof(object), typeof(object), typeof(bool) });
private static MethodInfo CloneMethod = typeof(IInjectionContext).GetRuntimeMethod(nameof(IInjectionContext.Clone), Type.EmptyTypes);
private static MethodInfo DelegateInvoke = typeof(ActivationStrategyDelegate)
.GetRuntimeMethod(
nameof(ActivationStrategyDelegate.Invoke),
new[] { typeof(IExportLocatorScope), typeof(IDisposalScope), typeof(IInjectionContext), typeof(object) });
private static MethodInfo SetExtraDataMethod = typeof(IExtraDataContainer)
.GetRuntimeMethod(
nameof(IExtraDataContainer.SetExtraData),
new[] { typeof(object), typeof(object), typeof(bool) });

/// <summary>
/// Default constructor
/// </summary>
public DynamicTypeBuilder()
{
SetupAssembly();

}

public class DelegateInfo
Expand Down Expand Up @@ -86,26 +89,41 @@ public Type CreateType(Type interfaceType, out List<DelegateInfo> methods)
var contextField = proxyBuilder.DefineField("_context", typeof(IInjectionContext),
FieldAttributes.Private);

var keyField = proxyBuilder.DefineField("_key", typeof(object),
FieldAttributes.Private);

int methodCount = 1;

methods = new List<DelegateInfo>();

foreach (var method in interfaceType.GetRuntimeMethods())
{
methods.Add(CreateMethod(method, proxyBuilder, methodCount, scopeField, disposalScopeField, contextField));
methods.Add(CreateMethod(method, proxyBuilder, methodCount, scopeField, disposalScopeField, contextField, keyField));

methodCount++;
}

CreateConstructor(proxyBuilder, methods, scopeField, disposalScopeField, contextField);
CreateConstructor(proxyBuilder, methods, scopeField, disposalScopeField, contextField, keyField);

return proxyBuilder.CreateTypeInfo().AsType();
}
}

private void CreateConstructor(TypeBuilder proxyBuilder, List<DelegateInfo> methods, FieldBuilder scopeField, FieldBuilder disposalScopeField, FieldBuilder contextField)
private void CreateConstructor(
TypeBuilder proxyBuilder,
List<DelegateInfo> methods,
FieldBuilder scopeField,
FieldBuilder disposalScopeField,
FieldBuilder contextField,
FieldBuilder keyField)
{
var constructorParameterTypes = new List<Type> { typeof(IExportLocatorScope), typeof(IDisposalScope), typeof(IInjectionContext) };
var constructorParameterTypes = new List<Type>
{
typeof(IExportLocatorScope),
typeof(IDisposalScope),
typeof(IInjectionContext),
typeof(object),
};

foreach (var _ in methods)
{
Expand All @@ -115,9 +133,9 @@ private void CreateConstructor(TypeBuilder proxyBuilder, List<DelegateInfo> meth
var proxyConstructor = proxyBuilder.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard, constructorParameterTypes.ToArray());

for (int i = 0; i < constructorParameterTypes.Count; i++)
for (int i = 1; i <= constructorParameterTypes.Count; i++)
{
proxyConstructor.DefineParameter(i + 1, ParameterAttributes.None, "arg" + (i + 1));
proxyConstructor.DefineParameter(i, ParameterAttributes.None, "arg" + i);
}

var proxyConstructorILGenerator = proxyConstructor.GetILGenerator();
Expand All @@ -134,20 +152,29 @@ private void CreateConstructor(TypeBuilder proxyBuilder, List<DelegateInfo> meth
proxyConstructorILGenerator.Emit(OpCodes.Ldarg_3);
proxyConstructorILGenerator.Emit(OpCodes.Stfld, contextField);

int argIndex = 4;
proxyConstructorILGenerator.Emit(OpCodes.Ldarg_0);
proxyConstructorILGenerator.Emit(OpCodes.Ldarg_S, (byte)4);
proxyConstructorILGenerator.Emit(OpCodes.Stfld, keyField);

byte argIndex = 5;
foreach (var method in methods)
{
proxyConstructorILGenerator.Emit(OpCodes.Ldarg_0);
proxyConstructorILGenerator.Emit(OpCodes.Ldarg_S, (byte)argIndex);
proxyConstructorILGenerator.Emit(OpCodes.Ldarg_S, argIndex++);
proxyConstructorILGenerator.Emit(OpCodes.Stfld, method.Activation);

argIndex++;
}

proxyConstructorILGenerator.Emit(OpCodes.Ret);
}

private DelegateInfo CreateMethod(MethodInfo method, TypeBuilder proxyBuilder, int methodCount, FieldBuilder scopeField, FieldBuilder disposalScopeField, FieldBuilder contextField)
private DelegateInfo CreateMethod(
MethodInfo method,
TypeBuilder proxyBuilder,
int methodCount,
FieldBuilder scopeField,
FieldBuilder disposalScopeField,
FieldBuilder contextField,
FieldBuilder keyField)
{
var parameters = method.GetParameters();

Expand Down Expand Up @@ -209,12 +236,20 @@ private DelegateInfo CreateMethod(MethodInfo method, TypeBuilder proxyBuilder, i
ilGenerator.Emit(OpCodes.Pop);
}

ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, keyField);

ilGenerator.Emit(OpCodes.Callvirt, DelegateInvoke);

if (method.ReturnType != null || method.ReturnType != typeof(void))
if (method.ReturnType != null && method.ReturnType != typeof(void))
{
ilGenerator.Emit(method.ReturnType.GetTypeInfo().IsValueType ?
OpCodes.Unbox_Any : OpCodes.Castclass, method.ReturnType);
ilGenerator.Emit(
method.ReturnType.GetTypeInfo().IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass,
method.ReturnType);
}
else
{
ilGenerator.Emit(OpCodes.Pop);
}

ilGenerator.Emit(OpCodes.Ret);
Expand Down
7 changes: 7 additions & 0 deletions src/Grace/DependencyInjection/Arg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,12 @@ public static IInjectionContext Context()
return null;
}

/// <summary>
/// Get the currently injected key
/// </summary>
public static T ImportKey<T>()
{
return default;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using Grace.DependencyInjection.Attributes.Interfaces;

namespace Grace.DependencyInjection.Attributes
{
/// <summary>
/// Adds the special ImportKey.Any key to an exported class
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ExportAnyKeyedTypeAttribute : Attribute, IExportKeyedTypeAttribute
{
private readonly Type _type;

/// <summary>
/// Default constructor
/// </summary>
/// <param name="type">export type</param>
public ExportAnyKeyedTypeAttribute(Type type)
{
_type = type;
}

/// <summary>
/// Provide type and key for export
/// </summary>
/// <param name="attributedType"></param>
public Tuple<Type, object> ProvideKey(Type attributedType)
{
return new Tuple<Type, object>(_type, ImportKey.Any);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public ExportKeyedTypeAttribute(Type type, object key)
}

/// <summary>
/// provide type and key for export
/// Provide type and key for export
/// </summary>
/// <param name="attributedType"></param>
public Tuple<Type, object> ProvideKey(Type attributedType)
Expand Down
8 changes: 4 additions & 4 deletions src/Grace/DependencyInjection/Attributes/ImportAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public ImportAttribute()
public ImportAttributeInfo ProvideImportInfo(Type attributedType, string attributedName)
{
return new ImportAttributeInfo
{
ImportKey = Key,
IsRequired = Required
};
{
ImportKey = Key,
IsRequired = Required
};
}
}
}
22 changes: 22 additions & 0 deletions src/Grace/DependencyInjection/Attributes/ImportKeyAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Grace.DependencyInjection.Attributes.Interfaces;

namespace Grace.DependencyInjection.Attributes
{
/// <summary>
/// Imports the currently located key.
/// Usually used on keyed services registered as ImportKey.Any.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
public class ImportKeyAttribute : Attribute, IImportAttribute
{
ImportAttributeInfo IImportAttribute.ProvideImportInfo(Type attributedType, string attributedName)
{
return new ImportAttributeInfo
{
ImportKey = ImportKey.Key,
IsRequired = true,
};
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,57 @@
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Grace.DependencyInjection.Attributes.Interfaces
{
using Adapter = Func<object, Type, string, ImportAttributeInfo>;
/// <summary>
/// Information about how the import should be performed
/// </summary>
public class ImportAttributeInfo
{
/// <summary>
/// Default value
/// </summary>
public object DefaultValue { get; set; }
private static readonly Dictionary<Type, Adapter> adapters = new Dictionary<Type, Adapter>(16);

/// <summary>
/// Register an adapter for an attribute that cannot implement IImportAttribute
/// </summary>
public static void RegisterImportAttributeAdapter<T>(Adapter adapter) where T : Attribute
=> adapters.Add(typeof(T), adapter);

/// <summary>
/// Factory method to get ImportAttributeInfo for a specific reflection member
/// </summary>
public static ImportAttributeInfo For(
ICustomAttributeProvider attributeProvider,
Type activationType,
string name)
{
foreach (var attr in attributeProvider.GetCustomAttributes(true))
{
ImportAttributeInfo info = null;

if (attr is IImportAttribute iia)
{
info = iia.ProvideImportInfo(activationType, name);
}
else if (adapters.TryGetValue(attr.GetType(), out var adapter))
{
info = adapter(attr, activationType, name);
}

if (info != null)
{
return info;
}
}

return null;
}

/// <summary>
/// Default value
/// </summary>
public object DefaultValue { get; set; }

/// <summary>
/// Is the import required
Expand All @@ -30,7 +71,7 @@ public class ImportAttributeInfo
/// <summary>
/// Comparer object for import
/// </summary>
public object Comparer { get; set; }
public object Comparer { get; set; }
}

/// <summary>
Expand Down
9 changes: 6 additions & 3 deletions src/Grace/DependencyInjection/Delegates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
/// <param name="scope"></param>
/// <param name="disposalScope"></param>
/// <param name="injectionContext"></param>
/// <param name="key"></param>
/// <returns></returns>
public delegate object ActivationStrategyDelegate(IExportLocatorScope scope, IDisposalScope disposalScope, IInjectionContext injectionContext);
public delegate object ActivationStrategyDelegate(IExportLocatorScope scope, IDisposalScope disposalScope, IInjectionContext injectionContext, object key);

/// <summary>
/// Delegate for activating a strongly typed strategy
Expand All @@ -16,17 +17,19 @@
/// <param name="scope"></param>
/// <param name="disposalScope"></param>
/// <param name="injectionContext"></param>
/// <param name="key"></param>
/// <returns></returns>
public delegate T TypedActivationStrategyDelegate<out T>(IExportLocatorScope scope, IDisposalScope disposalScope, IInjectionContext injectionContext);
public delegate T TypedActivationStrategyDelegate<out T>(IExportLocatorScope scope, IDisposalScope disposalScope, IInjectionContext injectionContext, object key);

/// <summary>
/// Delegate for injecting value
/// </summary>
/// <param name="scope">injection scope</param>
/// <param name="disposalScope">disposal scope</param>
/// <param name="injectionContext">injection context</param>
/// <param name="key">located key</param>
/// <param name="injectedInstance">instance to inject</param>
public delegate void InjectionStrategyDelegate(IExportLocatorScope scope, IDisposalScope disposalScope, IInjectionContext injectionContext, object injectedInstance);
public delegate void InjectionStrategyDelegate(IExportLocatorScope scope, IDisposalScope disposalScope, IInjectionContext injectionContext, object key, object injectedInstance);

/// <summary>
/// Used to filter out exports at container configuration time
Expand Down
Loading