@ -3,10 +3,8 @@ using System;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using IPA.Config ;
using IPA.Logging ;
using IPA.Utilities ;
using System.Linq.Expressions ;
using IPA.AntiMalware ;
#if NET4
using Expression = System . Linq . Expressions . Expression ;
@ -61,12 +59,53 @@ namespace IPA.Loader
/// <returns>the value to inject into that parameter.</returns>
public delegate object? InjectParameter ( object? previous , ParameterInfo param , PluginMetadata meta ) ;
/// <summary>
/// A provider for parameter injectors to request injected values themselves.
/// </summary>
/// <remarks>
/// Some injectors may look at attributes on the parameter to gain additional information about what it should provide.
/// If an injector wants to allow end users to affect the things it requests, it may pass the parameter it is currently
/// injecting for to this delegate along with a type override to select some other type.
/// </remarks>
/// <param name="forParam">the parameter that this is providing for.</param>
/// <param name="typeOverride">an optional override for the parameter type.</param>
/// <returns>the value that would otherwise be injected.</returns>
public delegate object? InjectedValueProvider ( ParameterInfo forParam , Type ? typeOverride = null ) ;
/// <summary>
/// A typed injector for a plugin's Init method. When registered, called for all associated types. If it returns null, the default for the type will be used.
/// </summary>
/// <param name="previous">the previous return value of the function, or <see langword="null"/> if never called for plugin.</param>
/// <param name="param">the <see cref="ParameterInfo"/> of the parameter being injected.</param>
/// <param name="meta">the <see cref="PluginMetadata"/> for the plugin being loaded.</param>
/// <param name="provider">an <see cref="InjectedValueProvider"/> to allow the injector to request injected values.</param>
/// <returns>the value to inject into that parameter.</returns>
public delegate object? InjectParameterNested ( object? previous , ParameterInfo param , PluginMetadata meta , InjectedValueProvider provider ) ;
/// <summary>
/// Invokes the provider with <paramref name="param"/> and <typeparamref name="T"/> and casts the result to <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">the type of object to be injected</typeparam>
/// <param name="provider">the provider to invoke.</param>
/// <param name="param">the parameter to provide for</param>
/// <returns>the value requested, or <see langword="null"/>.</returns>
public static T ? Inject < T > ( this InjectedValueProvider provider , ParameterInfo param )
= > ( T ? ) provider ? . Invoke ( param , typeof ( T ) ) ;
/// <summary>
/// Adds an injector to be used when calling future plugins' Init methods.
/// </summary>
/// <param name="type">the type of the parameter.</param>
/// <param name="injector">the function to call for injection.</param>
public static void AddInjector ( Type type , InjectParameter injector )
= > AddInjector ( type , ( pre , par , met , pro ) = > injector ( pre , par , met ) ) ;
/// <summary>
/// Adds an injector to be used when calling future plugins' Init methods.
/// </summary>
/// <param name="type">the type of the parameter.</param>
/// <param name="injector">the function to call for injection.</param>
public static void AddInjector ( Type type , InjectParameterNested injector )
{
injectors . Add ( new TypedInjector ( type , injector ) ) ;
}
@ -74,13 +113,13 @@ namespace IPA.Loader
private struct TypedInjector : IEquatable < TypedInjector >
{
public Type Type ;
public InjectParameter Injector ;
public InjectParameterNested Injector ;
public TypedInjector ( Type t , InjectParameter i )
public TypedInjector ( Type t , InjectParameterNested i )
{ Type = t ; Injector = i ; }
public object? Inject ( object? prev , ParameterInfo info , PluginMetadata meta )
= > Injector ( prev , info , meta ) ;
public object? Inject ( object? prev , ParameterInfo info , PluginMetadata meta , InjectedValueProvider provider )
= > Injector ( prev , info , meta , provider ) ;
public bool Equals ( TypedInjector other )
= > Type = = other . Type & & Injector = = other . Injector ;
@ -98,10 +137,10 @@ namespace IPA.Loader
private static readonly List < TypedInjector > injectors = new ( )
{
new TypedInjector ( typeof ( Logger ) , ( prev , param , meta ) = > prev ? ? new StandardLogger ( meta . Name ) ) ,
new TypedInjector ( typeof ( PluginMetadata ) , ( prev , param , meta ) = > prev ? ? meta ) ,
new TypedInjector ( typeof ( Config . Config ) , ( prev , param , meta ) = > prev ? ? Config . Config . GetConfigFor ( meta . Name , param ) ) ,
new TypedInjector ( typeof ( IAntiMalware ) , ( prev , param , meta ) = > prev ? ? AntiMalwareEngine . Engine )
new TypedInjector ( typeof ( Logger ) , ( prev , param , meta , _ ) = > prev ? ? new StandardLogger ( meta . Name ) ) ,
new TypedInjector ( typeof ( PluginMetadata ) , ( prev , param , meta , _ ) = > prev ? ? meta ) ,
new TypedInjector ( typeof ( Config . Config ) , ( prev , param , meta , _ ) = > prev ? ? Config . Config . GetConfigFor ( meta . Name , param ) ) ,
new TypedInjector ( typeof ( IAntiMalware ) , ( prev , param , meta , _ ) = > prev ? ? AntiMalwareEngine . Engine )
} ;
private static int? MatchPriority ( Type target , Type source )
@ -138,52 +177,83 @@ namespace IPA.Loader
Expression . ArrayIndex ( arr , Expression . Constant ( i ) ) , t ) ) ) ) ;
}
internal static object? [ ] Inject ( ParameterInfo [ ] initParams , PluginMetadata meta , ref object? persist )
private static object? InjectForParameter (
Dictionary < TypedInjector , object? > previousValues ,
PluginMetadata meta ,
ParameterInfo param ,
Type paramType ,
InjectedValueProvider provider )
{
var initArgs = new List < object? > ( ) ;
var value = paramType . GetDefault ( ) ;
var toUse = injectors
. Select ( i = > ( inject : i , priority : MatchPriority ( paramType , i . Type ) ) ) // check match priority, combine it
. NonNull ( t = > t . priority ) // filter null priorities
. Select ( t = > ( t . inject , priority : t . priority ! . Value ) ) // remove nullable
. OrderByDescending ( t = > t . priority ) // sort by value
. Select ( t = > t . inject ) ; // remove priority value
var previousValues = persist as Dictionary < TypedInjector , object? > ;
if ( previousValues = = null )
// this tries injectors in order of closest match by type provided
foreach ( var pair in toUse )
{
previousValues = new ( injectors . Count ) ;
persist = previousValues ;
object? prev = null ;
if ( previousValues . ContainsKey ( pair ) )
prev = previousValues [ pair ] ;
var val = pair . Inject ( prev , param , meta , provider ) ;
if ( previousValues . ContainsKey ( pair ) )
previousValues [ pair ] = val ;
else
previousValues . Add ( pair , val ) ;
if ( val = = null ) continue ;
value = val ;
break ;
}
foreach ( var param in initParams )
{
var paramType = param . ParameterType ;
return value ;
}
var value = paramType . GetDefault ( ) ;
private class InjectedValueProviderWrapperImplementation
{
public Dictionary < TypedInjector , object? > PreviousValues { get ; }
var toUse = injectors . Select ( i = > ( inject : i , priority : MatchPriority ( paramType , i . Type ) ) ) // check match priority, combine it
. NonNull ( t = > t . priority ) // filter null priorities
. Select ( t = > ( t . inject , priority : t . priority ! . Value ) ) // remove nullable
. OrderByDescending ( t = > t . priority ) // sort by value
. Select ( t = > t . inject ) ; // remove priority value
public PluginMetadata Meta { get ; }
// this tries injectors in order of closest match by type provided
foreach ( var pair in toUse )
{
object? prev = null ;
if ( previousValues . ContainsKey ( pair ) )
prev = previousValues [ pair ] ;
public InjectedValueProvider Provider { get ; }
public InjectedValueProviderWrapperImplementation ( PluginMetadata meta )
{
Meta = meta ;
PreviousValues = new ( ) ;
Provider = Inject ;
}
var val = pair . Inject ( prev , param , meta ) ;
private object? Inject ( ParameterInfo param , Type ? typeOverride = null )
= > InjectForParameter ( PreviousValues , Meta , param , typeOverride ? ? param . ParameterType , Provider ) ;
}
if ( previousValues . ContainsKey ( pair ) )
previousValues [ pair ] = val ;
else
previousValues . Add ( pair , val ) ;
internal static object? [ ] Inject ( ParameterInfo [ ] initParams , PluginMetadata meta , ref object? persist )
{
var initArgs = new List < object? > ( ) ;
if ( val = = null ) continue ;
value = val ;
break ;
}
var impl = persist as InjectedValueProviderWrapperImplementation ;
if ( impl = = null | | impl . Meta ! = meta )
{
impl = new ( meta ) ;
persist = impl ;
}
foreach ( var param in initParams )
{
var paramType = param . ParameterType ;
var value = InjectForParameter ( impl . PreviousValues , meta , param , paramType , impl . Provider ) ;
initArgs . Add ( value ) ;
}
//init.Invoke(instance, initArgs.ToArray());
return initArgs . ToArray ( ) ;
}
}