From b888c96754a84c4b1e6484f07839d441de952eea Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Tue, 31 Jan 2023 19:27:44 -0800 Subject: [PATCH 01/17] Update nuget.yml Fix on conditions --- .github/workflows/nuget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 2c40bd4..21c7b3d 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -3,7 +3,7 @@ name: Lilikoi.NuGet -on: [push, pull_request] +on: [push] jobs: build: From 0937431003aaeddbad58d7b2cb01ab3b7991d7f9 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Wed, 1 Feb 2023 17:46:40 -0800 Subject: [PATCH 02/17] General API Improvements * Added Impostors to make merges easier * Added support for merging multiple injection attributes * LilikoiMutators are now mounts, and will forward all mounted types to the LilikoiContainer. * Implicit wildcards are now in the LilikoiCompiler. --- Docs/containers.md | 4 +- Lilikoi.Benchmarks/Micro/Mount.cs | 90 +++++++++++++++ .../Builders/LkTargetBuilderAttribute.cs | 2 +- .../Impostor/LkInjectionImpostor.cs | 29 +++++ Lilikoi/Attributes/LkTargetAttribute.cs | 5 + Lilikoi/Compiler/Public/LilikoiCompiler.cs | 21 ++-- Lilikoi/Compiler/Public/LilikoiContainer.cs | 29 ++--- Lilikoi/Compiler/Public/LilikoiMutator.cs | 31 +++++- Lilikoi/Context/Mount.cs | 10 ++ Lilikoi/Lilikoi.csproj | 4 +- Lilikoi/Merge/Injection/InjectionMerger.cs | 103 ++++++++++++++++++ .../Injection/MergedInjectionImpostor.cs | 39 +++++++ Lilikoi/Scan/Scanner.cs | 14 +-- 13 files changed, 337 insertions(+), 44 deletions(-) create mode 100644 Lilikoi.Benchmarks/Micro/Mount.cs create mode 100644 Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs create mode 100644 Lilikoi/Merge/Injection/InjectionMerger.cs create mode 100644 Lilikoi/Merge/Injection/MergedInjectionImpostor.cs diff --git a/Docs/containers.md b/Docs/containers.md index 4fc69ac..a72062e 100644 --- a/Docs/containers.md +++ b/Docs/containers.md @@ -29,11 +29,13 @@ A "mount" is a type-keyed dictionary which is passed to all invocations of the s A mount can be used for configuration, injecting values, or for storing factories to be used by attributes. It is **highly recommended** (and may soon become a requirement) that all input types derive from the `Mount` type -so that programmers have ways to hurl opaque data around the entry point of a container. +so that users and libraries have ways to hurl opaque data around the entry point of a container. > **Idea**: If you use parameter injection, you really don't need any other input type than `Mount`. > By using parameter attributes, you can bundle all of your input into a mount and retrieve it for the entry point invocation. + + ## Steps 1. Injection attributes present on host diff --git a/Lilikoi.Benchmarks/Micro/Mount.cs b/Lilikoi.Benchmarks/Micro/Mount.cs new file mode 100644 index 0000000..7157275 --- /dev/null +++ b/Lilikoi.Benchmarks/Micro/Mount.cs @@ -0,0 +1,90 @@ +// ======================== +// Lilikoi.Benchmarks::Mount.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; + +using Lilikoi.Attributes; +using Lilikoi.Attributes.Builders; +using Lilikoi.Attributes.Typed; +using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; +using Lilikoi.Compiler.Public; +using Lilikoi.Compiler.Public.Utilities; +using Lilikoi.Context; + +namespace Lilikoi.Benchmarks.Micro; + + +[SimpleJob(RuntimeMoniker.Net47)] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net60)] +[SimpleJob(RuntimeMoniker.Net70)] +[Q1Column, MeanColumn, MedianColumn, Q3Column, StdDevColumn, StdErrorColumn] +[MemoryDiagnoser(true)] +//[EventPipeProfiler(EventPipeProfile.CpuSampling)] +public class MountBenchmark +{ + public Mount Mount = new Mount(); + + [Params(5, 15)] + public int Fodder; + + [GlobalSetup] + public void Setup() + { + Mount.Store(new MountBenchmark()); + + Type[] fodder = new[] + { + + typeof(LilikoiMutator), + typeof(LilikoiCompiler), + typeof(LilikoiMethod), + typeof(LilikoiContainer), + + typeof(Dictionary), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + typeof(List), + + }; + + for (int i = 0; i < Fodder; i++) + { + Console.WriteLine(fodder[i].FullName); + Mount.Store(Activator.CreateInstance(fodder[i])); + } + + } + + [Benchmark] + public MountBenchmark Read() + { + return Mount.Get(); + } + + [Benchmark] + public bool Has() + { + return Mount.Has(); + } + + [Benchmark] + public void Store() + { + Mount.Store(this); + } +} diff --git a/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs b/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs index 5b6d4c7..8b377ad 100644 --- a/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs +++ b/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs @@ -20,7 +20,7 @@ public abstract class LkTargetBuilderAttribute : Attribute /// Create an instance of LkTargetAttribute that will be consumed by Lilikoi. /// /// - public abstract LkTargetAttribute Build(); + public abstract LkTargetAttribute Build(Mount mount); /// /// Declare if the built TargetAttribute will be able to accept a context with the type "TUserContext". diff --git a/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs b/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs new file mode 100644 index 0000000..ec5df5e --- /dev/null +++ b/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs @@ -0,0 +1,29 @@ +// ======================== +// Lilikoi::LkInjectionImpostor.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +using Lilikoi.Attributes.Builders; +using Lilikoi.Context; + +namespace Lilikoi.Attributes.Impostor; + +public class LkInjectionImpostor : LkInjectionBuilderAttribute +{ + public LkInjectionAttribute Impostor { get; } + + public sealed override LkInjectionAttribute Build(Mount mount) + { + return Impostor; + } + + public sealed override bool IsInjectable(Mount mount) + { + return Impostor.IsInjectable(mount); + } +} diff --git a/Lilikoi/Attributes/LkTargetAttribute.cs b/Lilikoi/Attributes/LkTargetAttribute.cs index 5c49a2b..f9c6187 100644 --- a/Lilikoi/Attributes/LkTargetAttribute.cs +++ b/Lilikoi/Attributes/LkTargetAttribute.cs @@ -17,6 +17,11 @@ namespace Lilikoi.Attributes; public abstract class LkTargetAttribute : LkTargetBuilderAttribute { + public sealed override LkTargetAttribute Build(Mount mount) + { + return this.MemberwiseClone() as LkTargetAttribute; + } + /// /// Determine if this method should be included in the UserContext's scan. /// diff --git a/Lilikoi/Compiler/Public/LilikoiCompiler.cs b/Lilikoi/Compiler/Public/LilikoiCompiler.cs index ff86177..5195a88 100644 --- a/Lilikoi/Compiler/Public/LilikoiCompiler.cs +++ b/Lilikoi/Compiler/Public/LilikoiCompiler.cs @@ -13,6 +13,7 @@ using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Mahogany; +using Lilikoi.Context; #endregion @@ -22,15 +23,14 @@ public class LilikoiCompiler { internal MahoganyCompiler Internal { get; set; } + internal Mount Smuggler { get; } = new Mount(); + internal List ImplicitWraps = new List(); - internal List ImplicitWildcards = new List(); + internal List<(LkParameterBuilderAttribute, Type)> ImplicitWildcards = new List<(LkParameterBuilderAttribute, Type)>(); public LilikoiMutator Mutator() { - return new LilikoiMutator() - { - Compiler = this - }; + return new LilikoiMutator(Smuggler, this); } public LilikoiContainer Finish() @@ -43,13 +43,16 @@ public LilikoiContainer Finish() Internal.ImplicitWrap(implicitWrap); } Internal.WrapsFor(); + + foreach (var (implicitWildcard, type) in ImplicitWildcards) + { + Internal.ImplicitWildcard(implicitWildcard, type); + } + Internal.ParametersFor(); Internal.Apex(); - return new LilikoiContainer() - { - Body = Internal.Method.Lambda() - }; + return new LilikoiContainer(Smuggler, Internal.Method.Lambda()); } } diff --git a/Lilikoi/Compiler/Public/LilikoiContainer.cs b/Lilikoi/Compiler/Public/LilikoiContainer.cs index 6306da6..535d295 100644 --- a/Lilikoi/Compiler/Public/LilikoiContainer.cs +++ b/Lilikoi/Compiler/Public/LilikoiContainer.cs @@ -9,39 +9,30 @@ // // // ======================== -#region - using System.Linq.Expressions; -#endregion +using Lilikoi.Context; namespace Lilikoi.Compiler.Public; -public class LilikoiContainer +public class LilikoiContainer : Mount { - internal LambdaExpression Body { get; set; } - -/*#if DEBUG - - public TOut Run(THost host, TIn input) + internal LilikoiContainer(Mount self, LambdaExpression body) : base(self) { - return (Body.Compile(true) as Func)(host, input); + Body = body; } - public Func Compile() => Body.Compile(true) as Func; + private LambdaExpression Body { get; set; } - public override string ToString() - { - return ((Expression)Body).ToReadableString(opt => { return opt; }); - } -#endif*/ + private Delegate Memoized { get; set; } -#if !DEBUG public TOut Run(THost host, TIn input) { - return (Body.Compile(false) as Func)(host, input); + if (Memoized is null) + Memoized = Compile(); + + return (Memoized as Func)(host, input); } public Func Compile() => Body.Compile(false) as Func; -#endif } diff --git a/Lilikoi/Compiler/Public/LilikoiMutator.cs b/Lilikoi/Compiler/Public/LilikoiMutator.cs index b7d9c88..29565dd 100644 --- a/Lilikoi/Compiler/Public/LilikoiMutator.cs +++ b/Lilikoi/Compiler/Public/LilikoiMutator.cs @@ -10,11 +10,16 @@ // ======================== using Lilikoi.Attributes.Builders; +using Lilikoi.Context; namespace Lilikoi.Compiler.Public; -public class LilikoiMutator +public class LilikoiMutator : Mount { + internal LilikoiMutator(Mount self, LilikoiCompiler compiler) : base(self) + { + Compiler = compiler; + } internal LilikoiCompiler Compiler { get; set; } @@ -51,9 +56,9 @@ public LilikoiMutator Implicit(TWrap value = null) /// /// /// - public LilikoiMutator Wildcard(LkParameterBuilderAttribute value ) + public LilikoiMutator Wildcard(LkParameterBuilderAttribute value) { - Compiler.ImplicitWildcards.Add(value); + Compiler.ImplicitWildcards.Add( (value, typeof(TType)) ); return this; } @@ -64,13 +69,29 @@ public LilikoiMutator Wildcard(LkParameterBuilderAttribute value ) /// /// /// - public LilikoiMutator Wildcard(TParameter value = null) + public LilikoiMutator Wildcard(TParameter value = null) where TParameter: LkParameterBuilderAttribute, new() { if (value is null) value = new TParameter(); - Compiler.ImplicitWildcards.Add(value); + Compiler.ImplicitWildcards.Add( (value, typeof(TType)) ); return this; } + + /// + /// Get the parameter type of a function by the parameter number + /// Used for type routing + /// + /// + /// + public Type? Parameter(int paramNum = 0) + { + var parameters = Compiler.Internal.Method.Parameters; + + if (parameters.Count <= paramNum) + return null; + + return parameters[paramNum]; + } } diff --git a/Lilikoi/Context/Mount.cs b/Lilikoi/Context/Mount.cs index ae68b1c..a71bd2f 100644 --- a/Lilikoi/Context/Mount.cs +++ b/Lilikoi/Context/Mount.cs @@ -17,6 +17,16 @@ public class Mount { private TypeDictionary dictionary = new TypeDictionary(); + public Mount() + { + + } + + public Mount(Mount other) + { + dictionary = other.dictionary; + } + public virtual void Store(T value) where T : class { diff --git a/Lilikoi/Lilikoi.csproj b/Lilikoi/Lilikoi.csproj index c6af31e..637e321 100644 --- a/Lilikoi/Lilikoi.csproj +++ b/Lilikoi/Lilikoi.csproj @@ -44,8 +44,8 @@ - - + + diff --git a/Lilikoi/Merge/Injection/InjectionMerger.cs b/Lilikoi/Merge/Injection/InjectionMerger.cs new file mode 100644 index 0000000..9ce64c4 --- /dev/null +++ b/Lilikoi/Merge/Injection/InjectionMerger.cs @@ -0,0 +1,103 @@ +// ======================== +// Lilikoi::InjectionMerger.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +using System.Runtime.CompilerServices; + +using Lilikoi.Attributes; +using Lilikoi.Context; + +namespace Lilikoi.Merge.Injection; + +public class InjectionMerger +{ + private List attributes = new(); + private Dictionary> merged = new(); + + /// + /// Add this initializer to this merged injection + /// + /// + /// + /// + /// + public InjectionMerger And(Func initializer) + where TAs : class + { + if (merged.ContainsKey(typeof(TAs))) + throw new InvalidOperationException($"Already contains injection handler for {typeof(TAs).Name}"); + + merged.Add(typeof(TAs), initializer as Func ); + + return this; + } + + /// + /// Add this class to this merged injection + /// + /// + /// + public InjectionMerger And() + where TAs : class, new() + => And(mount => new TAs()); + + public InjectionMerger And(LkInjectionAttribute attribute) + { + attributes.Add(attribute); + + return this; + } + + public MergedInjectionImpostor Impostor() + { + return new MergedInjectionImpostor(this); + } + + internal bool Has(Mount mount) + where TAs: class + { + foreach (var attribute in attributes) + { + if (attribute.IsInjectable(mount)) + return true; + } + + return merged.ContainsKey(typeof(TAs)); + } + + internal TAs? Inject(Mount mount) + where TAs: class + { + foreach (var attribute in attributes) + { + if (attribute.IsInjectable(mount)) + return attribute.Inject(mount); + } + + if (merged.TryGetValue(typeof(TAs), out var func)) + { + return func(mount) as TAs; + } + + return null; + } + + internal void Deject(Mount mount, TAs value) + where TAs: class + { + foreach (var attribute in attributes) + { + if (attribute.IsInjectable(mount)) + { + attribute.Deject(mount, value); + return; + } + } + } +} diff --git a/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs b/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs new file mode 100644 index 0000000..789c1d5 --- /dev/null +++ b/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs @@ -0,0 +1,39 @@ +// ======================== +// Lilikoi::MergedInjectionActor.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +using Lilikoi.Attributes; +using Lilikoi.Context; + +namespace Lilikoi.Merge.Injection; + +public class MergedInjectionImpostor : LkInjectionAttribute +{ + protected InjectionMerger Merged { get; } + + public MergedInjectionImpostor(InjectionMerger merged) + { + Merged = merged; + } + + public override bool IsInjectable(Mount mount) + { + return Merged.Has(mount); + } + + public override TInjectable Inject(Mount context) + { + return Merged.Inject(context); + } + + public override void Deject(Mount context, TInjectable injected) + { + Merged.Deject(context, injected); + } +} diff --git a/Lilikoi/Scan/Scanner.cs b/Lilikoi/Scan/Scanner.cs index 4e16872..c210d06 100644 --- a/Lilikoi/Scan/Scanner.cs +++ b/Lilikoi/Scan/Scanner.cs @@ -1,16 +1,16 @@ // ======================== // Lilikoi.Core::Scanner.cs -// +// // -> Created: 31.01.2023 // -> Bumped: 31.01.2023 -// +// // -> Purpose: -// -// +// +// // ======================== namespace Lilikoi.Scan; -public class Scanner +public static class Scanner { - -} \ No newline at end of file + +} From e33588d6d2ef6bd7ffb76aa083d1b327bc0ce268 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Wed, 1 Feb 2023 22:00:20 -0800 Subject: [PATCH 03/17] Improved docs + Scanner + Factory API * Added basic document on mounts * Added basic scanner functionality * Added basic factory API (hopefully extend with parameters?) * Mount extensions for more familiar singleton/factory interfaces. --- Docs/mounts.md | 17 +++++ Lilikoi/Scan/Scanner.cs | 72 +++++++++++++++++++ .../Standard/Extensions/MountExtensions.cs | 34 +++++++++ Lilikoi/Standard/Factory/FactoryAttribute.cs | 27 +++++++ Lilikoi/Standard/Factory/IFactory.cs | 16 +++++ Lilikoi/Standard/NewAttribute.cs | 4 +- ...ountAttribute.cs => SingletonAttribute.cs} | 2 +- 7 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 Docs/mounts.md create mode 100644 Lilikoi/Standard/Extensions/MountExtensions.cs create mode 100644 Lilikoi/Standard/Factory/FactoryAttribute.cs create mode 100644 Lilikoi/Standard/Factory/IFactory.cs rename Lilikoi/Standard/{MountAttribute.cs => SingletonAttribute.cs} (90%) diff --git a/Docs/mounts.md b/Docs/mounts.md new file mode 100644 index 0000000..84a7c23 --- /dev/null +++ b/Docs/mounts.md @@ -0,0 +1,17 @@ +# Mounts + +Mounts are a powerful type dictionary that is designed to allow for a wide array +of software to store and read their own opaque states without colliding with +other software in the same environments. + +Mounts are present throughout many stages in the lifecycle of a Lilikoi container: + - Creation/Compilation + - Injecting Variables + - Wrapping Entry Points + - Injecting Parameters and Wildcards + +Mounts can be used to store configuration, factories, and even other Lilikoi containers. +The only requirement is that each entry into the mount have a unique type. + +Additionally, several Lilikoi Types expose a mount interface. You can add entries to a `LilikoiMutator`'s +mount and see those entries in the finalized `LilikoiContainer`'s mount. diff --git a/Lilikoi/Scan/Scanner.cs b/Lilikoi/Scan/Scanner.cs index c210d06..e58e1c5 100644 --- a/Lilikoi/Scan/Scanner.cs +++ b/Lilikoi/Scan/Scanner.cs @@ -8,9 +8,81 @@ // // // ======================== +using System.Collections.Concurrent; +using System.Reflection; + +using Lilikoi.Attributes.Builders; +using Lilikoi.Compiler.Public; +using Lilikoi.Context; + namespace Lilikoi.Scan; public static class Scanner { + /// + /// Scan the provided assembly for all Lilikoi Containers matching the user context and return them + /// Use the provided mount as the global mount + /// + /// A user-supplied context potentially consumed by a target + /// + /// + /// + /// + /// + /// + public static List Scan(TUserContext context, Assembly assembly, Mount mount) + where TUserContext : Mount + { + return Scan(context, assembly, () => mount); + } + + /// + /// Scan the provided assembly for all Lilikoi Containers matching the user context and return them + /// Use a provided function to supply the Mount for each container. + /// + /// A user-supplied context potentially consumed by a target + /// + /// + /// + /// + /// + /// + public static List Scan(TUserContext context, Assembly assembly, Func sourceMount) + where TUserContext : Mount + { + List containers = new List(); + + foreach (Type type in assembly.GetTypes()) + { + foreach (MethodInfo methodInfo in type.GetMethods()) + { + foreach (LkTargetBuilderAttribute attribute in methodInfo.GetCustomAttributes() + .Where(attribute => attribute + .GetType() + .IsSubclassOf(typeof(LkTargetBuilderAttribute)))) + { + if (attribute.IsTargetable()) + { + var mount = sourceMount(); + + // This method is targeted! + // Begin container creation + var compiler = LilikoiMethod.FromMethodInfo(methodInfo) + .Mount(mount) + .Input() + .Output() + .Build(); + + var built = attribute.Build(mount); + built.Target(context, compiler.Mutator()); + + containers.Add( compiler.Finish() ); + } + } + } + } + + return containers; + } } diff --git a/Lilikoi/Standard/Extensions/MountExtensions.cs b/Lilikoi/Standard/Extensions/MountExtensions.cs new file mode 100644 index 0000000..ea68689 --- /dev/null +++ b/Lilikoi/Standard/Extensions/MountExtensions.cs @@ -0,0 +1,34 @@ +// ======================== +// Lilikoi::MountExtensions.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +using System.Runtime.CompilerServices; + +using Lilikoi.Context; + +namespace Lilikoi.Standard.Extensions; + +public static class MountExtensions +{ + public static Mount RegisterFactory(this Mount self, TFactory factory) + where TFactory: IFactory + { + self.Store>(factory); + + return self; + } + + public static Mount RegisterSingleton(this Mount self, TSingleton value) + where TSingleton : class + { + self.Store(value); + + return self; + } +} diff --git a/Lilikoi/Standard/Factory/FactoryAttribute.cs b/Lilikoi/Standard/Factory/FactoryAttribute.cs new file mode 100644 index 0000000..2357387 --- /dev/null +++ b/Lilikoi/Standard/Factory/FactoryAttribute.cs @@ -0,0 +1,27 @@ +// ======================== +// Lilikoi::FactoryAttribute.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +using Lilikoi.Attributes; +using Lilikoi.Context; + +namespace Lilikoi.Standard; + +public class FactoryAttribute : LkInjectionAttribute +{ + public override bool IsInjectable(Mount mount) + { + return mount.Has>(); + } + + public override TInjectable Inject(Mount context) + { + return context.Get>().Create(); + } +} diff --git a/Lilikoi/Standard/Factory/IFactory.cs b/Lilikoi/Standard/Factory/IFactory.cs new file mode 100644 index 0000000..eb9e605 --- /dev/null +++ b/Lilikoi/Standard/Factory/IFactory.cs @@ -0,0 +1,16 @@ +// ======================== +// Lilikoi::IFactory.cs +// +// -> Created: 01.02.2023 +// -> Bumped: 01.02.2023 +// +// -> Purpose: +// +// +// ======================== +namespace Lilikoi.Standard; + +public interface IFactory +{ + public TProduct Create(); +} diff --git a/Lilikoi/Standard/NewAttribute.cs b/Lilikoi/Standard/NewAttribute.cs index 3c49595..00137d3 100644 --- a/Lilikoi/Standard/NewAttribute.cs +++ b/Lilikoi/Standard/NewAttribute.cs @@ -23,12 +23,12 @@ public NewAttribute(params object[] constructor) ConstructorParameters = constructor; } - public override bool IsInjectable(Mount mount) + public override bool IsInjectable(Context.Mount mount) { return Activator.CreateInstance(typeof(TInjectable), ConstructorParameters) as TInjectable is not null; } - public override TInjectable Inject(Mount context) + public override TInjectable Inject(Context.Mount context) { var instance = Activator.CreateInstance(typeof(TInjectable), ConstructorParameters) as TInjectable; diff --git a/Lilikoi/Standard/MountAttribute.cs b/Lilikoi/Standard/SingletonAttribute.cs similarity index 90% rename from Lilikoi/Standard/MountAttribute.cs rename to Lilikoi/Standard/SingletonAttribute.cs index 92c407e..7235a7a 100644 --- a/Lilikoi/Standard/MountAttribute.cs +++ b/Lilikoi/Standard/SingletonAttribute.cs @@ -14,7 +14,7 @@ namespace Lilikoi.Standard; -public class MountAttribute : LkInjectionAttribute +public class SingletonAttribute : LkInjectionAttribute { public override bool IsInjectable(Mount mount) { From b74b198e62812e327740b2b6941ceac24e907d62 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Wed, 1 Feb 2023 22:14:44 -0800 Subject: [PATCH 04/17] Update nuget: Only run on pushes to main and dev --- .github/workflows/nuget.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 21c7b3d..1ce2661 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -3,7 +3,11 @@ name: Lilikoi.NuGet -on: [push] +on: + push: + branches: + - main + - dev jobs: build: From 10237ffeec3d9750ff4e1f88f24db531f3cd9b28 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:29:37 -0800 Subject: [PATCH 05/17] Bugfixes + Mutators + Docs + Tests * Fixed the "Continue" wrap test + added some more tests * Adjusted CodeQL pipeline * Fixed a bug where the return value for the entry point was being discarded due to improper scoping * More documentation +semver:minor --- .github/workflows/codeql.yml | 4 +- Assets/LilikoiContainerOverview.png | Bin 0 -> 80609 bytes Docs/mounts.md | 2 +- Docs/overview.md | 45 ++++++++++++ Lilikoi.Benchmarks/Micro/Mount.cs | 18 ++--- Lilikoi.Tests/Lilikoi.Tests.csproj | 5 ++ .../Mutator/Smuggling/SmuggleTest.cs | 55 ++++++++++++++ .../Mutator/Wildcards/WildcardTest.cs | 67 ++++++++++++++++++ .../Invocations/ContinueWrapAttribute.cs | 2 +- Lilikoi.Tests/Wraps/WrapTests.cs | 12 ++-- .../Mahogany/Generator/WrapGenerator.cs | 3 +- Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs | 27 +++---- Lilikoi/Compiler/Mahogany/MahoganyMethod.cs | 34 +++++---- Lilikoi/Compiler/Public/LilikoiCompiler.cs | 13 ++++ Lilikoi/Compiler/Public/LilikoiMethod.cs | 3 +- README.md | 14 +++- 16 files changed, 256 insertions(+), 48 deletions(-) create mode 100644 Assets/LilikoiContainerOverview.png create mode 100644 Docs/overview.md create mode 100644 Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs create mode 100644 Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b1b8378..5ebb678 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,9 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - + with: + fetch-depth: 0 + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/Assets/LilikoiContainerOverview.png b/Assets/LilikoiContainerOverview.png new file mode 100644 index 0000000000000000000000000000000000000000..7811878484d0286d9e8d1d505a4fa06f3d116675 GIT binary patch literal 80609 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
    8+vQb=HJr!TJjI?I9zH`${4$ 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 G_WuB_Z0qI# literal 0 HcmV?d00001 diff --git a/Docs/mounts.md b/Docs/mounts.md index 84a7c23..f5b312a 100644 --- a/Docs/mounts.md +++ b/Docs/mounts.md @@ -2,7 +2,7 @@ Mounts are a powerful type dictionary that is designed to allow for a wide array of software to store and read their own opaque states without colliding with -other software in the same environments. +other software in the same environment. Mounts are present throughout many stages in the lifecycle of a Lilikoi container: - Creation/Compilation diff --git a/Docs/overview.md b/Docs/overview.md new file mode 100644 index 0000000..a9673fd --- /dev/null +++ b/Docs/overview.md @@ -0,0 +1,45 @@ +# High Level Overview + +Lilikoi is designed to replace expensive reflection logic and deliver a consistent API surface +from the perspective of both framework and framework consumer. + +Lilikoi provides APIs for frameworks to define declarative helpers, such as parameter injection +and before/after hooks (called wraps). Additionally, users can specify arbitrary types in the +parameters of their entry point, and Lilikoi will resolve those parameters to a wildcard injection +if one is available. + +![A diagram of Lilikoi's exposed interfaces](../Assets/LilikoiContainerOverview.png) + +The **Framework** sees a small API surface where it can request Lilikoi to execute a container +with an arbitrary input and receive an output from that container. + +The **User** sees a powerful suite of tools to declaratively describe the resources required +to fulfill an action. + +**Both** will see an attribute system where various wraps and injectors can quickly be +scaffolded to replace repetitive imperative logic. + +## Types of Injections + +- **Property** injection is for values that are *not* expected to change *behavior* between invocations. + These should primarily be used for service providers such as database connections. +- **Parameter** injection is for values that are *expected* to change both value and behavior between invocations, + depending on the input value for the container. +- **Wildcards** are similar to parameter injection, but are detected by the parameter type and not an attribute. + They have no explicit usage + +## Other Attributes + +- **Wraps** (also known as "Hooks") allow users to specify additional code to run before and after the entry point using an attribute. + This can be used to include third-party code that translates the input, mutates the output, or prevents the entry point from executing altogether. + For example, wraps can be used to add authentication to an API endpoint, or decode a string into it's JSON payload. + +- **Mutators** allow you to hook into the Lilikoi container compiler and add your own wraps and wildcards. + Mutators can be used to add advanced functionality to containers, and even smuggle metadata from the compilation process + to the final container object using the provided mount interface. + +## Headless +Lilikoi has some APIs, called "Headless" APIs, which are designed to be used without the full framework. +These headless APIs can be used to piecemeal implement Lilikoi behavior (or extend lilikoi injection behavior +to parts of the framework that don't already have it). So far only property injection headless APIs are provided, +but headless APIs for all types of injections and attributes is planned. diff --git a/Lilikoi.Benchmarks/Micro/Mount.cs b/Lilikoi.Benchmarks/Micro/Mount.cs index 7157275..46d8be0 100644 --- a/Lilikoi.Benchmarks/Micro/Mount.cs +++ b/Lilikoi.Benchmarks/Micro/Mount.cs @@ -22,9 +22,9 @@ namespace Lilikoi.Benchmarks.Micro; -[SimpleJob(RuntimeMoniker.Net47)] +//[SimpleJob(RuntimeMoniker.Net47)] [SimpleJob(RuntimeMoniker.Net48)] -[SimpleJob(RuntimeMoniker.Net60)] +//[SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net70)] [Q1Column, MeanColumn, MedianColumn, Q3Column, StdDevColumn, StdErrorColumn] [MemoryDiagnoser(true)] @@ -33,7 +33,7 @@ public class MountBenchmark { public Mount Mount = new Mount(); - [Params(5, 15)] + [Params(0, 5, 15)] public int Fodder; [GlobalSetup] @@ -43,13 +43,15 @@ public void Setup() Type[] fodder = new[] { + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), - typeof(LilikoiMutator), - typeof(LilikoiCompiler), - typeof(LilikoiMethod), - typeof(LilikoiContainer), - typeof(Dictionary), typeof(List), typeof(List), typeof(List), diff --git a/Lilikoi.Tests/Lilikoi.Tests.csproj b/Lilikoi.Tests/Lilikoi.Tests.csproj index 75235b8..024d7f5 100644 --- a/Lilikoi.Tests/Lilikoi.Tests.csproj +++ b/Lilikoi.Tests/Lilikoi.Tests.csproj @@ -25,4 +25,9 @@ + + + + + diff --git a/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs b/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs new file mode 100644 index 0000000..af38037 --- /dev/null +++ b/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs @@ -0,0 +1,55 @@ +// ======================== +// Lilikoi.Tests::SmuggleHost.cs +// +// -> Created: 02.02.2023 +// -> Bumped: 02.02.2023 +// +// -> Purpose: +// +// +// ======================== +using Lilikoi.Attributes.Static; +using Lilikoi.Compiler.Public; +using Lilikoi.Context; + +namespace Lilikoi.Tests.Mutator.Smuggling; + +public class SmuggleTest +{ + public const string SMUGGLED_VALUE = "Smuggled"; + + [Test] + public void CanSmuggle() + { + var mount = new Mount(); + + var container = LilikoiMethod.FromMethodInfo(typeof(SmuggleHost).GetMethod("Entry")) + .Input() + .Output() + .Mount(mount) + .Build() + .Finish(); + + Assert.AreEqual(SMUGGLED_VALUE, container.Get()); + Assert.AreNotEqual(SMUGGLED_VALUE, mount.Get()); + } + + public class SmuggleAttribute : LkMutatorAttribute + { + public override void Mutate(LilikoiMutator mutator) + { + mutator.Store(SMUGGLED_VALUE); + } + } + + public class SmuggleHost + { + + [Smuggle] + public object Entry() + { + return this; + } + + } +} diff --git a/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs b/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs new file mode 100644 index 0000000..55ec987 --- /dev/null +++ b/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs @@ -0,0 +1,67 @@ +// ======================== +// Lilikoi.Tests::WildcardTest.cs +// +// -> Created: 02.02.2023 +// -> Bumped: 02.02.2023 +// +// -> Purpose: +// +// +// ======================== +using Lilikoi.Attributes; +using Lilikoi.Attributes.Static; +using Lilikoi.Attributes.Typed; +using Lilikoi.Compiler.Public; +using Lilikoi.Context; + +namespace Lilikoi.Tests.Mutator.Wildcards; + +public class WildcardTest +{ + public const string WILDCARD_VALUE = "Uno!"; + + [Test] + public void CanInjectWildcard() + { + var mount = new Mount(); + + var container = LilikoiMethod.FromMethodInfo(typeof(WildcardHost).GetMethod("Entry")) + .Input() + .Output() + .Mount(mount) + .Build() + .Finish(); + + var host = new WildcardHost(); + var value = container.Run(host, host); + + Assert.AreEqual(WILDCARD_VALUE, value); + } + + public class WildcardAttribute : LkMutatorAttribute + { + public override void Mutate(LilikoiMutator mutator) + { + mutator.Wildcard(new WildcardInjector()); + } + } + + public class WildcardInjector : LkTypedParameterAttribute + { + public override string Inject(Mount context, object input) + { + return WILDCARD_VALUE; + } + } + + public class WildcardHost + { + + [Wildcard] + public string Entry(string thing) + { + return thing; + } + + } +} diff --git a/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs b/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs index ac76de6..5537cea 100644 --- a/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs +++ b/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs @@ -28,6 +28,6 @@ public override WrapResult Before(Mount mount, ref TIn public override void After(Mount mount, ref TOutput output) { - Assert.Pass(); + Assert.Pass("Reached after"); } } diff --git a/Lilikoi.Tests/Wraps/WrapTests.cs b/Lilikoi.Tests/Wraps/WrapTests.cs index 38afc11..9a1b57b 100644 --- a/Lilikoi.Tests/Wraps/WrapTests.cs +++ b/Lilikoi.Tests/Wraps/WrapTests.cs @@ -54,7 +54,7 @@ public string ShouldModifyInput(string input) [Test] public void Halts() { - var method = typeof(DummyHost).GetMethod("ShouldContinue"); + var method = typeof(DummyHost).GetMethod("ShouldHalt"); var build = LilikoiMethod.FromMethodInfo(method) .Input() @@ -67,13 +67,13 @@ public void Halts() var output = build.Run(new DummyHost(), new object()); - Assert.AreEqual(output, "Entry"); + Assert.AreEqual("Before", output); } [Test] public void Continues() { - var method = typeof(DummyHost).GetMethod("ShouldHalt"); + var method = typeof(DummyHost).GetMethod("ShouldContinue"); var build = LilikoiMethod.FromMethodInfo(method) .Input() @@ -86,7 +86,7 @@ public void Continues() var output = build.Run(new DummyHost(), new object()); - Assert.AreEqual(output, "Before"); + Assert.Fail("Reached exit point without passing"); } [Test] @@ -105,7 +105,7 @@ public void Modifies() var output = build.Run(new DummyHost(), new object()); - Assert.AreEqual(output, "After"); + Assert.AreEqual("After", output); } [Test] @@ -123,6 +123,8 @@ public void ModifiesInput() Console.WriteLine(build.ToString()); var output = build.Run(new DummyHost(), "Input"); + + Assert.Fail("Reached exit point without passing"); } } diff --git a/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs index f093bed..cde95dc 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs @@ -71,9 +71,8 @@ internal static Expression Before(MahoganyMethod method, Expression attribute) )); return Expression.Block( - invocation, guard, - invocation); + result); } /// diff --git a/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs b/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs index 2fd998f..7a332ec 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs @@ -15,8 +15,10 @@ using System.Reflection; using Lilikoi.Attributes.Builders; +using Lilikoi.Attributes.Static; using Lilikoi.Compiler.Mahogany.Generator; using Lilikoi.Compiler.Mahogany.Steps; +using Lilikoi.Compiler.Public; #endregion @@ -116,14 +118,15 @@ public static List InjectStepBuilder(Type host, MahoganyMeth /// /// /// - public static List ParameterFillingForMethod( + public static Expression[] ParameterFillingForMethod( Dictionary param, Dictionary wildcards, MethodInfo runtimeMethod) { - var fill = new Dictionary(); + var parameters = runtimeMethod.GetParameters(); + var fill = new Expression[parameters.Length]; - foreach (var parameterInfo in runtimeMethod.GetParameters()) + foreach (var parameterInfo in parameters) { if (param.ContainsKey(parameterInfo)) { @@ -139,12 +142,8 @@ public static List ParameterFillingForMethod( throw new Exception($"No wildcard or parameter injection available for parameter at position {parameterInfo.Position} of method {runtimeMethod.Name} with type {parameterInfo.ParameterType.FullName}"); } - var expressions = fill.Select(kv => (kv.Key, kv.Value)) - .ToList(); - expressions.Sort((a, b) => a.Key.CompareTo(b.Key)); - - return expressions.Select(obj => obj.Value).ToList(); + return fill; } /* public static List WrapStepBuilder(MethodInfo method) @@ -181,6 +180,11 @@ public static List WrapStepBuilder(MethodInfo method) } */ + public static List MutatorsForMethod(MethodInfo methodInfo) + { + return methodInfo.GetCustomAttributes().OfType().ToList(); + } + #endregion #region Compiler @@ -243,18 +247,15 @@ public void ParameterSafety() public void Apex() { - var filled = ParameterFillingForMethod(Method.MethodInjects, new Dictionary() - { - { Method.Input, Method.Named(MahoganyConstants.INPUT_VAR) } - }, Method.Entry); + var filled = ParameterFillingForMethod(Method.MethodInjects, Method.Wildcards, Method.Entry); Stack.Apex( Expression.Block( - new[] { Method.Named(MahoganyConstants.OUTPUT_VAR) }, Expression.Assign(Method.Named(MahoganyConstants.OUTPUT_VAR), Expression.Call(Method.Named(MahoganyConstants.HOST_VAR), Method.Entry, filled)) ) ); + } #endregion diff --git a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs index e005f63..968c270 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs @@ -77,20 +77,28 @@ public void Append(Expression block) public LambdaExpression Lambda() { var func = typeof(Func<,,>).MakeGenericType(Host, Input, Result); - - return Expression.Lambda( - func, - Expression.Block(Temporaries.ToArray(), Expression.Block( - new[] - { - //Named(MahoganyConstants.HOST_VAR), Named(MahoganyConstants.INPUT_VAR), - Named(MahoganyConstants.OUTPUT_VAR) - }, - Expression.Block(Expression.Block(Unordered), Expression.Block(Body)), Expression.Label(HaltTarget, Named(MahoganyConstants.OUTPUT_VAR)) - //, Named(MahoganyConstants.OUTPUT_VAR) - )), + var internalVariables = new[] + { + //Named(MahoganyConstants.HOST_VAR), Named(MahoganyConstants.INPUT_VAR), + Named(MahoganyConstants.OUTPUT_VAR) + }; + + var parameters = new[] + { Named(MahoganyConstants.HOST_VAR), - Named(MahoganyConstants.INPUT_VAR)); + Named(MahoganyConstants.INPUT_VAR) + }; + + var internalBody = Expression.Block(Expression.Block(Unordered), Expression.Block(Body)); + + var lambdaBody = Expression.Block( + internalVariables, + Expression.Block(Temporaries.ToArray(), internalBody), + Expression.Return(HaltTarget, Named(MahoganyConstants.OUTPUT_VAR)), + Expression.Label(HaltTarget, Named(MahoganyConstants.OUTPUT_VAR)) + ); + + return Expression.Lambda(lambdaBody, "LilikoiContainer", parameters); } #region Containerized diff --git a/Lilikoi/Compiler/Public/LilikoiCompiler.cs b/Lilikoi/Compiler/Public/LilikoiCompiler.cs index 5195a88..fbbf016 100644 --- a/Lilikoi/Compiler/Public/LilikoiCompiler.cs +++ b/Lilikoi/Compiler/Public/LilikoiCompiler.cs @@ -12,6 +12,7 @@ #region using Lilikoi.Attributes.Builders; +using Lilikoi.Attributes.Static; using Lilikoi.Compiler.Mahogany; using Lilikoi.Context; @@ -33,8 +34,20 @@ public LilikoiMutator Mutator() return new LilikoiMutator(Smuggler, this); } + private void Mutators() + { + var mutators = MahoganyCompiler.MutatorsForMethod(Internal.Method.Entry); + + foreach (LkMutatorAttribute lilikoiMutator in mutators) + { + lilikoiMutator.Mutate(Mutator()); + } + } + public LilikoiContainer Finish() { + Mutators(); + Internal.ParameterSafety(); Internal.InjectionsFor(Internal.Method.Host); diff --git a/Lilikoi/Compiler/Public/LilikoiMethod.cs b/Lilikoi/Compiler/Public/LilikoiMethod.cs index a3b5726..2852857 100644 --- a/Lilikoi/Compiler/Public/LilikoiMethod.cs +++ b/Lilikoi/Compiler/Public/LilikoiMethod.cs @@ -33,7 +33,7 @@ public static LilikoiMethod FromMethodInfo(MethodInfo method) { Parameters = method.GetParameters().Select(x => x.ParameterType).ToList(), Return = method.ReturnType, - HaltTarget = Expression.Label(method.ReturnType), + HaltTarget = Expression.Label(method.ReturnType, "Halt"), Entry = method, Host = method.DeclaringType, NamedVariables = new Dictionary() @@ -53,6 +53,7 @@ public LilikoiMethod Input() { Implementation.Input = typeof(TInput); Implementation.NamedVariables.Add(MahoganyConstants.INPUT_VAR, Expression.Parameter(typeof(TInput), MahoganyConstants.INPUT_VAR)); + Implementation.Wildcards.Add(typeof(TInput), Implementation.Named(MahoganyConstants.INPUT_VAR)); return this; } diff --git a/README.md b/README.md index a15d019..390d72b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ -
    +

    Lilikoi

    -
    - + | Main | [![main](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml) | @@ -30,6 +29,15 @@ | Debugging | ⏳ | | NuGet Release | 🏗️ | +### Documentation + +Documentation is a work in progress, but here's what we have so far: + +- **[Overview](./Docs/overview.md)** -- *Read me first!* +- **[Mounts](./Docs/mounts.md)** +- **[Containers](./Docs/containers.md)** +- **[Attributes](./Docs/attributes.md)** + ### What is it? Lilikoi is a *framework for frameworks*. From 23fa4e9bbff5db6a00001b659ab3799b6b764457 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:42:18 -0800 Subject: [PATCH 06/17] Reformat + Shift extension target * Created new MountBuilder class to be the subject of extensions so they don't clutter other mount-api classes. * Reformatted solution --- Lilikoi.Benchmarks/Lilikoi.Benchmarks.csproj | 6 +- .../Applications/InjectSimple/Simple.cs | 24 +++--- .../InjectSimple/SimpleInjectHost.cs | 20 ++--- .../InjectSimple/SimpleInjectorAttribute.cs | 18 ++-- .../Mahogany/CompileBenchmarks.cs | 23 +++--- .../Mahogany/HeadlessInjectBenchmark.cs | 27 +++--- Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs | 27 +++--- Lilikoi.Benchmarks/Micro/Mount.cs | 55 ++++++------- Lilikoi.Benchmarks/Program.cs | 19 ++++- Lilikoi.Tests/HelloWorld/ConsoleAttribute.cs | 12 +-- Lilikoi.Tests/HelloWorld/ConsoleInj.cs | 8 +- Lilikoi.Tests/HelloWorld/HelloWorldHost.cs | 12 +-- Lilikoi.Tests/HelloWorld/HelloWorldTest.cs | 12 +-- .../AllMethodsCalledAttribute.cs | 13 +-- .../AllMethodsCalled/AllMethodsCalledHost.cs | 13 +-- .../AllMethodsCalledInject.cs | 8 +- .../AllMethodsCalledParameterAttribute.cs | 16 ++-- .../AllMethodsCalled/AllMethodsCalledTest.cs | 30 +++---- .../Injections/Headless/HeadlessInjectTest.cs | 18 ++-- .../Injections/UnaccessibleProperties.cs | 55 ++++++------- Lilikoi.Tests/Lilikoi.Tests.csproj | 16 ++-- .../Mutator/Smuggling/SmuggleTest.cs | 19 ++--- .../Mutator/Wildcards/WildcardTest.cs | 18 ++-- Lilikoi.Tests/UnitTest1.cs | 8 +- Lilikoi.Tests/Usings.cs | 8 +- .../Invocations/ContinueWrapAttribute.cs | 12 +-- .../Wraps/Invocations/HaltWrapAttribute.cs | 16 ++-- .../Wraps/Invocations/ModifyWrapAttribute.cs | 18 ++-- .../Invocations/MutateInputWrapAttribute.cs | 16 ++-- Lilikoi.Tests/Wraps/SelfInject.cs | 57 +++++++------ Lilikoi.Tests/Wraps/WrapTests.cs | 82 +++++++++---------- .../Builders/LkInjectionBuilderAttribute.cs | 14 ++-- .../Builders/LkParameterBuilderAttribute.cs | 24 +++--- .../Builders/LkTargetBuilderAttribute.cs | 23 +++--- .../Builders/LkWrapBuilderAttribute.cs | 15 ++-- .../Impostor/LkInjectionImpostor.cs | 15 ++-- Lilikoi/Attributes/LkInjectionAttribute.cs | 16 ++-- Lilikoi/Attributes/LkParameterAttribute.cs | 24 +++--- Lilikoi/Attributes/LkTargetAttribute.cs | 35 ++++---- Lilikoi/Attributes/LkWrapAttribute.cs | 22 ++--- .../Attributes/Static/LkMutatorAttribute.cs | 16 ++-- .../Typed/LkTypedInjectionAttribute.cs | 14 ++-- .../Typed/LkTypedParameterAttribute.cs | 17 ++-- .../Attributes/Typed/LkTypedWrapAttribute.cs | 17 ++-- Lilikoi/Collection/Padlock.cs | 14 ++-- Lilikoi/Collection/TypeDictionary.cs | 14 ++-- .../Mahogany/Generator/CommonGenerator.cs | 14 ++-- .../Mahogany/Generator/DebugGenerator.cs | 19 +++-- .../Mahogany/Generator/InjectionGenerator.cs | 21 +++-- .../Mahogany/Generator/ParameterGenerator.cs | 18 ++-- .../Mahogany/Generator/WrapGenerator.cs | 54 ++++++------ .../Compiler/Mahogany/MahoganyBlockStack.cs | 15 ++-- Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs | 30 +++---- .../Compiler/Mahogany/MahoganyConstants.cs | 14 ++-- Lilikoi/Compiler/Mahogany/MahoganyMethod.cs | 17 ++-- .../Compiler/Mahogany/MahoganyValidator.cs | 25 +++--- .../Mahogany/Steps/MahoganyInjectStep.cs | 14 ++-- .../Mahogany/Steps/MahoganyParameterStep.cs | 17 ++-- .../Steps/MahoganyParameterWildcardStep.cs | 17 ++-- .../Mahogany/Steps/MahoganyWrapStep.cs | 21 +++-- Lilikoi/Compiler/Public/LilikoiCompiler.cs | 32 +++----- Lilikoi/Compiler/Public/LilikoiContainer.cs | 23 +++--- Lilikoi/Compiler/Public/LilikoiMethod.cs | 12 +-- Lilikoi/Compiler/Public/LilikoiMutator.cs | 38 ++++----- .../Public/Utilities/LilikoiInjector.cs | 60 +++++++------- Lilikoi/Context/Mount.cs | 26 +++--- Lilikoi/Lilikoi.csproj | 26 +++--- Lilikoi/Merge/Injection/InjectionMerger.cs | 39 ++++----- .../Injection/MergedInjectionImpostor.cs | 21 ++--- Lilikoi/Scan/Scanner.cs | 71 ++++++++-------- Lilikoi/Standard/Extensions/MountBuilder.cs | 25 ++++++ .../Extensions/MountConverterExtensions.cs | 22 +++++ .../Standard/Extensions/MountExtensions.cs | 34 -------- .../Extensions/RegistrationExtensions.cs | 27 ++++++ Lilikoi/Standard/Factory/FactoryAttribute.cs | 11 +-- Lilikoi/Standard/Factory/IFactory.cs | 11 +-- Lilikoi/Standard/NewAttribute.cs | 20 ++--- Lilikoi/Standard/SingletonAttribute.cs | 14 ++-- README.md | 62 +++++++------- 79 files changed, 870 insertions(+), 966 deletions(-) create mode 100644 Lilikoi/Standard/Extensions/MountBuilder.cs create mode 100644 Lilikoi/Standard/Extensions/MountConverterExtensions.cs delete mode 100644 Lilikoi/Standard/Extensions/MountExtensions.cs create mode 100644 Lilikoi/Standard/Extensions/RegistrationExtensions.cs diff --git a/Lilikoi.Benchmarks/Lilikoi.Benchmarks.csproj b/Lilikoi.Benchmarks/Lilikoi.Benchmarks.csproj index c826ab6..d9ed3a7 100644 --- a/Lilikoi.Benchmarks/Lilikoi.Benchmarks.csproj +++ b/Lilikoi.Benchmarks/Lilikoi.Benchmarks.csproj @@ -10,12 +10,12 @@ - - + + - + diff --git a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs index 0db97c5..6a00414 100644 --- a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs +++ b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs @@ -1,21 +1,20 @@ // ======================== -// Lilikoi.Benchmarks::Hell.cs -// Distributed under the MIT License. -// +// Lilikoi.Benchmarks::Simple.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using System.Runtime.CompilerServices; +#endregion + namespace Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; public class Simple { - public int Value { get; set; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -23,14 +22,13 @@ public static Simple Create() { return new Simple() { - Value = 0 //Random.Shared.Next() + Value = 0 //Random.Shared.Next() }; } [MethodImpl(MethodImplOptions.NoInlining)] public bool Execute() { - return 1 == 0; // Random.Shared.Next() == Value; + return 1 == 0; // Random.Shared.Next() == Value; } - -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs index b7936e4..0bdbe09 100644 --- a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs +++ b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs @@ -1,21 +1,20 @@ // ======================== -// Lilikoi.Benchmarks::InjectHellHost.cs -// Distributed under the MIT License. -// +// Lilikoi.Benchmarks::SimpleInjectHost.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Compiler.Public; +#endregion + namespace Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; public class SimpleInjectHost { - [SimpleInjector] public Simple Injected; public bool Execute() @@ -32,5 +31,4 @@ public static Func Build() .Finish() .Compile(); } - -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectorAttribute.cs b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectorAttribute.cs index 781780f..16fdeec 100644 --- a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectorAttribute.cs +++ b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectorAttribute.cs @@ -1,17 +1,17 @@ // ======================== -// Lilikoi.Benchmarks::HellInjectorAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi.Benchmarks::SimpleInjectorAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes.Typed; using Lilikoi.Context; +#endregion + namespace Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; public class SimpleInjectorAttribute : LkTypedInjectionAttribute @@ -20,4 +20,4 @@ public override Simple Inject(Mount mount) { return Simple.Create(); } -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs b/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs index 05a2786..3651564 100644 --- a/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs +++ b/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs @@ -1,29 +1,30 @@ // ======================== // Lilikoi.Benchmarks::CompileBenchmarks.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using BenchmarkDotNet.Attributes; using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; +#endregion + namespace Lilikoi.Benchmarks.Mahogany; [SimpleJob()] -[Q1Column, MeanColumn, MedianColumn, Q3Column] +[Q1Column] +[MeanColumn] +[MedianColumn] +[Q3Column] public class CompileBenchmarks { - [Benchmark()] public Func Simple() { return SimpleInjectHost.Build(); } - -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Mahogany/HeadlessInjectBenchmark.cs b/Lilikoi.Benchmarks/Mahogany/HeadlessInjectBenchmark.cs index cb1c715..5935bec 100644 --- a/Lilikoi.Benchmarks/Mahogany/HeadlessInjectBenchmark.cs +++ b/Lilikoi.Benchmarks/Mahogany/HeadlessInjectBenchmark.cs @@ -1,23 +1,19 @@ // ======================== // Lilikoi.Benchmarks::HeadlessInjectBenchmark.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 23.12.2022 -// -> Bumped: 23.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Diagnostics.Windows.Configs; using BenchmarkDotNet.Jobs; using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; using Lilikoi.Compiler.Public.Utilities; -using Perfolizer.Mathematics.QuantileEstimators; +#endregion namespace Lilikoi.Benchmarks.Mahogany; @@ -26,12 +22,17 @@ namespace Lilikoi.Benchmarks.Mahogany; [SimpleJob(RuntimeMoniker.Net48)] [SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net70)] -[Q1Column, MeanColumn, MedianColumn, Q3Column, StdDevColumn, StdErrorColumn] +[Q1Column] +[MeanColumn] +[MedianColumn] +[Q3Column] +[StdDevColumn] +[StdErrorColumn] [MemoryDiagnoser(true)] //[EventPipeProfiler(EventPipeProfile.CpuSampling)] public class HeadlessInjectBenchmark { - public SimpleInjectHost SimpleHost = new SimpleInjectHost(); + public SimpleInjectHost SimpleHost = new(); public LilikoiInjector.Injector SimpleInjector; [GlobalSetup] @@ -51,4 +52,4 @@ public void SimpleCompiler() { SimpleInjector = LilikoiInjector.CreateInjector(); } -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs b/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs index 16fdc14..7268a78 100644 --- a/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs +++ b/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs @@ -1,28 +1,32 @@ // ======================== // Lilikoi.Benchmarks::RunBenchmarks.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using BenchmarkDotNet.Attributes; using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; +#endregion + namespace Lilikoi.Benchmarks.Mahogany; [SimpleJob()] -[Q1Column, MeanColumn, MedianColumn, Q3Column, StdDevColumn, StdErrorColumn] +[Q1Column] +[MeanColumn] +[MedianColumn] +[Q3Column] +[StdDevColumn] +[StdErrorColumn] [MemoryDiagnoser(true)] public class RunBenchmarks { - public Func SimpleContainer; - public SimpleInjectHost SimpleHost = new SimpleInjectHost(); + public SimpleInjectHost SimpleHost = new(); [GlobalSetup] public void Setup() @@ -35,5 +39,4 @@ public bool Simple() { return SimpleContainer(SimpleHost, true); } - -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Micro/Mount.cs b/Lilikoi.Benchmarks/Micro/Mount.cs index 46d8be0..3916b2d 100644 --- a/Lilikoi.Benchmarks/Micro/Mount.cs +++ b/Lilikoi.Benchmarks/Micro/Mount.cs @@ -1,40 +1,37 @@ // ======================== // Lilikoi.Benchmarks::Mount.cs -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; -using Lilikoi.Attributes; -using Lilikoi.Attributes.Builders; -using Lilikoi.Attributes.Typed; -using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; -using Lilikoi.Compiler.Public; -using Lilikoi.Compiler.Public.Utilities; using Lilikoi.Context; -namespace Lilikoi.Benchmarks.Micro; +#endregion +namespace Lilikoi.Benchmarks.Micro; //[SimpleJob(RuntimeMoniker.Net47)] [SimpleJob(RuntimeMoniker.Net48)] //[SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net70)] -[Q1Column, MeanColumn, MedianColumn, Q3Column, StdDevColumn, StdErrorColumn] +[Q1Column] +[MeanColumn] +[MedianColumn] +[Q3Column] +[StdDevColumn] +[StdErrorColumn] [MemoryDiagnoser(true)] //[EventPipeProfiler(EventPipeProfile.CpuSampling)] public class MountBenchmark { - public Mount Mount = new Mount(); - - [Params(0, 5, 15)] - public int Fodder; + [Params(0, 5, 15)] public int Fodder; + public Mount Mount = new(); [GlobalSetup] public void Setup() @@ -43,13 +40,13 @@ public void Setup() Type[] fodder = new[] { - typeof(Dictionary), - typeof(Dictionary), - typeof(Dictionary), - typeof(Dictionary), - typeof(Dictionary), - typeof(Dictionary), - typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), + typeof(Dictionary), typeof(List), @@ -60,16 +57,14 @@ public void Setup() typeof(List), typeof(List), typeof(List), - typeof(List), - + typeof(List) }; - for (int i = 0; i < Fodder; i++) + for (var i = 0; i < Fodder; i++) { Console.WriteLine(fodder[i].FullName); Mount.Store(Activator.CreateInstance(fodder[i])); } - } [Benchmark] @@ -89,4 +84,4 @@ public void Store() { Mount.Store(this); } -} +} \ No newline at end of file diff --git a/Lilikoi.Benchmarks/Program.cs b/Lilikoi.Benchmarks/Program.cs index 0d20291..1dd2dee 100644 --- a/Lilikoi.Benchmarks/Program.cs +++ b/Lilikoi.Benchmarks/Program.cs @@ -1,9 +1,22 @@ -// See https://aka.ms/new-console-template for more information +// ======================== +// Lilikoi.Benchmarks::Program.cs +// (c) 2023. Distributed under the MIT License +// +// -> Created: 22.12.2022 +// -> Bumped: 06.02.2023 +// ======================== +#region + using BenchmarkDotNet.Running; +#endregion + namespace Lilikoi.Benchmarks; public static class Program { - static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); -} + private static void Main(string[] args) + { + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + } +} \ No newline at end of file diff --git a/Lilikoi.Tests/HelloWorld/ConsoleAttribute.cs b/Lilikoi.Tests/HelloWorld/ConsoleAttribute.cs index 493f3d0..5027829 100644 --- a/Lilikoi.Tests/HelloWorld/ConsoleAttribute.cs +++ b/Lilikoi.Tests/HelloWorld/ConsoleAttribute.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::ConsoleAttribute.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -31,4 +27,4 @@ public override void Deject(Mount context, ConsoleInj injected) Console.WriteLine("Dejecting console inj!"); return; } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/HelloWorld/ConsoleInj.cs b/Lilikoi.Tests/HelloWorld/ConsoleInj.cs index e96bceb..3616de9 100644 --- a/Lilikoi.Tests/HelloWorld/ConsoleInj.cs +++ b/Lilikoi.Tests/HelloWorld/ConsoleInj.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::ConsoleInj.cs -// Distributed under the MIT License. +// (c) 2023. Distributed under the MIT License // // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== namespace Lilikoi.Tests.HelloWorld; diff --git a/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs b/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs index 31156d2..0f056d8 100644 --- a/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs +++ b/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::HelloWorldHost.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -25,4 +21,4 @@ public object HelloWorld() return ConsoleImpl; } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs b/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs index f9970c8..0074baa 100644 --- a/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs +++ b/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::HelloWorldTest.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -40,4 +36,4 @@ public async Task HelloWorld() build.Run(new HelloWorldHost(), new object()); } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledAttribute.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledAttribute.cs index cb014b7..2ee00ae 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledAttribute.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledAttribute.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledAttribute.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -20,7 +16,6 @@ namespace Lilikoi.Tests.Injections.AllMethodsCalled; public class AllMethodsCalledAttribute : LkTypedInjectionAttribute { - public override AllMethodsCalledInject Inject(Mount context) { AllMethodsCalledTest.Instance.InjectCalled = true; @@ -32,4 +27,4 @@ public override void Deject(Mount context, AllMethodsCalledInject injected) { AllMethodsCalledTest.Instance.DejectCalled = true; } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs index b566c66..c6e0a96 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs @@ -1,19 +1,14 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledHost.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== namespace Lilikoi.Tests.Injections.AllMethodsCalled; public class AllMethodsCalledHost { - [AllMethodsCalled] public AllMethodsCalledInject Inject; public object Entry(AllMethodsCalledTest.AllMethodsCalledCounter test, [AllMethodsCalledParameter] object param) @@ -27,4 +22,4 @@ public object Entry(AllMethodsCalledTest.AllMethodsCalledCounter test, [AllMetho return new object(); } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledInject.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledInject.cs index 5cc1e39..2ebf40a 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledInject.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledInject.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledInject.cs -// Distributed under the MIT License. +// (c) 2023. Distributed under the MIT License // // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledParameterAttribute.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledParameterAttribute.cs index e386802..cacfc1c 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledParameterAttribute.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledParameterAttribute.cs @@ -1,17 +1,17 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledParameterAttribute.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes.Typed; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Injections.AllMethodsCalled; public class AllMethodsCalledParameterAttribute : LkTypedParameterAttribute @@ -21,4 +21,4 @@ public override object Inject(Mount context, AllMethodsCalledTest.AllMethodsCall input.ParameterCalled = true; return new object(); } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs index 259e14f..01c62d6 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledTest.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -24,15 +20,6 @@ public class AllMethodsCalledTest { public static AllMethodsCalledCounter Instance; - public class AllMethodsCalledCounter - { - public bool DejectCalled = false; - public bool EntryCalled = false; - - public bool InjectCalled = false; - public bool ParameterCalled = false; - } - [Test] public void AllMethodsCalled() { @@ -57,4 +44,13 @@ public void AllMethodsCalled() Assert.IsTrue(Instance.DejectCalled, "Deject was not invoked"); Assert.IsTrue(Instance.ParameterCalled, "Parameter was not invoked"); } -} + + public class AllMethodsCalledCounter + { + public bool DejectCalled = false; + public bool EntryCalled = false; + + public bool InjectCalled = false; + public bool ParameterCalled = false; + } +} \ No newline at end of file diff --git a/Lilikoi.Tests/Injections/Headless/HeadlessInjectTest.cs b/Lilikoi.Tests/Injections/Headless/HeadlessInjectTest.cs index c48556c..0013732 100644 --- a/Lilikoi.Tests/Injections/Headless/HeadlessInjectTest.cs +++ b/Lilikoi.Tests/Injections/Headless/HeadlessInjectTest.cs @@ -1,17 +1,17 @@ // ======================== // Lilikoi.Tests::HeadlessInjectTest.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 23.12.2022 -// -> Bumped: 23.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Compiler.Public.Utilities; using Lilikoi.Tests.Injections.AllMethodsCalled; +#endregion + namespace Lilikoi.Tests.Injections.Headless; public class HeadlessInjectTest @@ -19,7 +19,6 @@ public class HeadlessInjectTest [Test] public void HeadlessAllMethodsCalled() { - AllMethodsCalledTest.Instance = new AllMethodsCalledTest.AllMethodsCalledCounter(); var counter = AllMethodsCalledTest.Instance; @@ -35,6 +34,5 @@ public void HeadlessAllMethodsCalled() Assert.IsTrue(counter.InjectCalled); Assert.IsNotNull(host.Inject); - } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Injections/UnaccessibleProperties.cs b/Lilikoi.Tests/Injections/UnaccessibleProperties.cs index 9d00f08..e583ec2 100644 --- a/Lilikoi.Tests/Injections/UnaccessibleProperties.cs +++ b/Lilikoi.Tests/Injections/UnaccessibleProperties.cs @@ -1,25 +1,43 @@ // ======================== // Lilikoi.Tests::UnaccessibleProperties.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using System.Reflection; using Lilikoi.Compiler.Public; using Lilikoi.Context; using Lilikoi.Tests.HelloWorld; -using Lilikoi.Tests.Injections.AllMethodsCalled; + +#endregion namespace Lilikoi.Tests.Injections; public class UnaccessibleProperties { + [Test] + public void UnaccessablePropertiesStillInject() + { + var method = (MethodInfo)typeof(Host).GetMethod("Entry")!; + + var build = LilikoiMethod.FromMethodInfo(method) + .Input() + .Output() + .Mount(new Mount()) + .Build() + .Finish(); + + Console.WriteLine(build.ToString()); + + build.Run(new Host(), "Input"); + + Assert.Fail("Entry point not executed"); + } + public class Host { [Console] private ConsoleInj Private; @@ -40,23 +58,4 @@ public string Entry() return "Entry"; } } - - [Test] - public void UnaccessablePropertiesStillInject() - { - var method = (MethodInfo)typeof(Host).GetMethod("Entry")!; - - var build = LilikoiMethod.FromMethodInfo(method) - .Input() - .Output() - .Mount(new Mount()) - .Build() - .Finish(); - - Console.WriteLine(build.ToString()); - - build.Run(new Host(), "Input"); - - Assert.Fail("Entry point not executed"); - } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Lilikoi.Tests.csproj b/Lilikoi.Tests/Lilikoi.Tests.csproj index 024d7f5..125f6a1 100644 --- a/Lilikoi.Tests/Lilikoi.Tests.csproj +++ b/Lilikoi.Tests/Lilikoi.Tests.csproj @@ -12,22 +12,22 @@ - - - - - - + + + + + + - + - + diff --git a/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs b/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs index af38037..9dd604a 100644 --- a/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs +++ b/Lilikoi.Tests/Mutator/Smuggling/SmuggleTest.cs @@ -1,17 +1,18 @@ // ======================== -// Lilikoi.Tests::SmuggleHost.cs -// +// Lilikoi.Tests::SmuggleTest.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 02.02.2023 -// -> Bumped: 02.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes.Static; using Lilikoi.Compiler.Public; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Mutator.Smuggling; public class SmuggleTest @@ -44,12 +45,10 @@ public override void Mutate(LilikoiMutator mutator) public class SmuggleHost { - [Smuggle] public object Entry() { return this; } - } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs b/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs index 55ec987..60805c2 100644 --- a/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs +++ b/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs @@ -1,19 +1,19 @@ // ======================== // Lilikoi.Tests::WildcardTest.cs -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 02.02.2023 -// -> Bumped: 02.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== -using Lilikoi.Attributes; +#region + using Lilikoi.Attributes.Static; using Lilikoi.Attributes.Typed; using Lilikoi.Compiler.Public; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Mutator.Wildcards; public class WildcardTest @@ -56,12 +56,10 @@ public override string Inject(Mount context, object input) public class WildcardHost { - [Wildcard] public string Entry(string thing) { return thing; } - } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/UnitTest1.cs b/Lilikoi.Tests/UnitTest1.cs index ac61595..ad3422a 100644 --- a/Lilikoi.Tests/UnitTest1.cs +++ b/Lilikoi.Tests/UnitTest1.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::UnitTest1.cs -// Distributed under the MIT License. +// (c) 2023. Distributed under the MIT License // // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== namespace Lilikoi.Tests; diff --git a/Lilikoi.Tests/Usings.cs b/Lilikoi.Tests/Usings.cs index bc8fdc3..5dcf678 100644 --- a/Lilikoi.Tests/Usings.cs +++ b/Lilikoi.Tests/Usings.cs @@ -1,13 +1,9 @@ // ======================== // Lilikoi.Tests::Usings.cs -// Distributed under the MIT License. +// (c) 2023. Distributed under the MIT License // // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region diff --git a/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs b/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs index 5537cea..35db555 100644 --- a/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs +++ b/Lilikoi.Tests/Wraps/Invocations/ContinueWrapAttribute.cs @@ -1,17 +1,17 @@ // ======================== // Lilikoi.Tests::ContinueWrapAttribute.cs -// Distributed under the MIT License. +// (c) 2023. Distributed under the MIT License // // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Wraps.Invocations; public class ContinueWrapAttribute : LkWrapAttribute diff --git a/Lilikoi.Tests/Wraps/Invocations/HaltWrapAttribute.cs b/Lilikoi.Tests/Wraps/Invocations/HaltWrapAttribute.cs index 78360e9..8e79806 100644 --- a/Lilikoi.Tests/Wraps/Invocations/HaltWrapAttribute.cs +++ b/Lilikoi.Tests/Wraps/Invocations/HaltWrapAttribute.cs @@ -1,17 +1,17 @@ // ======================== // Lilikoi.Tests::HaltWrapAttribute.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Wraps.Invocations; public class HaltWrapAttribute : LkWrapAttribute @@ -30,4 +30,4 @@ public override void After(Mount mount, ref TOutput output) { Assert.Fail("After method invoked after Stop call"); } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Wraps/Invocations/ModifyWrapAttribute.cs b/Lilikoi.Tests/Wraps/Invocations/ModifyWrapAttribute.cs index 9ccd4e3..c138248 100644 --- a/Lilikoi.Tests/Wraps/Invocations/ModifyWrapAttribute.cs +++ b/Lilikoi.Tests/Wraps/Invocations/ModifyWrapAttribute.cs @@ -1,20 +1,20 @@ // ======================== // Lilikoi.Tests::ModifyWrapAttribute.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Wraps.Invocations; -public class ModifyWrapAttribute: LkWrapAttribute +public class ModifyWrapAttribute : LkWrapAttribute { public override bool IsWrappable(Mount mount) { @@ -30,4 +30,4 @@ public override void After(Mount mount, ref TOutput output) { output = "After" as TOutput; } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Wraps/Invocations/MutateInputWrapAttribute.cs b/Lilikoi.Tests/Wraps/Invocations/MutateInputWrapAttribute.cs index a1ed4d0..002d7af 100644 --- a/Lilikoi.Tests/Wraps/Invocations/MutateInputWrapAttribute.cs +++ b/Lilikoi.Tests/Wraps/Invocations/MutateInputWrapAttribute.cs @@ -1,17 +1,17 @@ // ======================== // Lilikoi.Tests::MutateInputWrapAttribute.cs -// Distributed under the MIT License. -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Tests.Wraps.Invocations; public class MutateInputWrapAttribute : LkWrapAttribute @@ -32,4 +32,4 @@ public override void After(Mount mount, ref TOutput output) { Assert.Fail("Pass block in Entry point not hit."); } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Wraps/SelfInject.cs b/Lilikoi.Tests/Wraps/SelfInject.cs index 0fcae6a..aba383b 100644 --- a/Lilikoi.Tests/Wraps/SelfInject.cs +++ b/Lilikoi.Tests/Wraps/SelfInject.cs @@ -1,14 +1,12 @@ // ======================== -// Lilikoi.Tests::RespectsNonStandardReturns.cs -// Distributed under the MIT License. -// +// Lilikoi.Tests::SelfInject.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Public; @@ -16,10 +14,31 @@ using Lilikoi.Context; using Lilikoi.Tests.HelloWorld; +#endregion + namespace Lilikoi.Tests.Wraps; public class SelfInject { + [Test] + public void BasicSelfInject() + { + var method = typeof(Host).GetMethod("Entry"); + + var build = LilikoiMethod.FromMethodInfo(method) + .Input() + .Output() + .Mount(new Mount()) + .Build() + .Finish(); + + Console.WriteLine(build.ToString()); + + var output = build.Run(new Host(), new object()); + + Assert.Fail("Did not evaluate Assert.Pass() in WrapWithInjectionAttribute."); + } + public class NonStandardReturn : LkWrapBuilderAttribute { public override LkWrapAttribute Build(Mount mount) @@ -60,30 +79,10 @@ public override void After(Mount mount, ref TOutput output) public class Host { - [NonStandardReturn] public string Entry() { return "Entry"; } } - - [Test] - public void BasicSelfInject() - { - var method = typeof(Host).GetMethod("Entry"); - - var build = LilikoiMethod.FromMethodInfo(method) - .Input() - .Output() - .Mount(new Mount()) - .Build() - .Finish(); - - Console.WriteLine(build.ToString()); - - var output = build.Run(new Host(), new object()); - - Assert.Fail("Did not evaluate Assert.Pass() in WrapWithInjectionAttribute."); - } -} +} \ No newline at end of file diff --git a/Lilikoi.Tests/Wraps/WrapTests.cs b/Lilikoi.Tests/Wraps/WrapTests.cs index 9a1b57b..d50a2b1 100644 --- a/Lilikoi.Tests/Wraps/WrapTests.cs +++ b/Lilikoi.Tests/Wraps/WrapTests.cs @@ -1,56 +1,22 @@ // ======================== -// Lilikoi.Tests::RespectsWrapResultTest.cs -// Distributed under the MIT License. -// +// Lilikoi.Tests::WrapTests.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Compiler.Public; using Lilikoi.Context; -using Lilikoi.Tests.Injections.AllMethodsCalled; using Lilikoi.Tests.Wraps.Invocations; +#endregion + namespace Lilikoi.Tests.Wraps; public class WrapTests { - public class DummyHost - { - [HaltWrap] - public string ShouldHalt() - { - Assert.Fail("Entry point invoked"); - - return "Entry"; - } - - [ContinueWrap] - public string ShouldContinue() - { - - return "Entry"; - } - - [ModifyWrap] - public string ShouldModify() - { - - return "Entry"; - } - - [MutateInputWrap] - public string ShouldModifyInput(string input) - { - Assert.AreEqual(input, "Modified"); - Assert.Pass("Modified entry"); - return "Entry"; - } - } - [Test] public void Halts() { @@ -127,4 +93,34 @@ public void ModifiesInput() Assert.Fail("Reached exit point without passing"); } -} + public class DummyHost + { + [HaltWrap] + public string ShouldHalt() + { + Assert.Fail("Entry point invoked"); + + return "Entry"; + } + + [ContinueWrap] + public string ShouldContinue() + { + return "Entry"; + } + + [ModifyWrap] + public string ShouldModify() + { + return "Entry"; + } + + [MutateInputWrap] + public string ShouldModifyInput(string input) + { + Assert.AreEqual(input, "Modified"); + Assert.Pass("Modified entry"); + return "Entry"; + } + } +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Builders/LkInjectionBuilderAttribute.cs b/Lilikoi/Attributes/Builders/LkInjectionBuilderAttribute.cs index ccc8658..662b7d2 100644 --- a/Lilikoi/Attributes/Builders/LkInjectionBuilderAttribute.cs +++ b/Lilikoi/Attributes/Builders/LkInjectionBuilderAttribute.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MkInjectionBuilderAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkInjectionBuilderAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -33,4 +29,4 @@ public abstract class LkInjectionBuilderAttribute : Attribute /// public abstract bool IsInjectable(Mount mount) where TInjectable : class; -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Builders/LkParameterBuilderAttribute.cs b/Lilikoi/Attributes/Builders/LkParameterBuilderAttribute.cs index d39bb02..2f29f53 100644 --- a/Lilikoi/Attributes/Builders/LkParameterBuilderAttribute.cs +++ b/Lilikoi/Attributes/Builders/LkParameterBuilderAttribute.cs @@ -1,31 +1,30 @@ // ======================== -// Lilikoi.Core::LkParameterBuilderAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkParameterBuilderAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using Lilikoi.Context; +#endregion + namespace Lilikoi.Attributes.Builders; [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] public abstract class LkParameterBuilderAttribute : Attribute { - /// - /// A function which returns the parameter injector to use for this parameter. + /// A function which returns the parameter injector to use for this parameter. /// /// public abstract LkParameterAttribute Build(Mount mount); /// - /// Whether or not this parameter can handle an input of and provide an output of . + /// Whether or not this parameter can handle an input of and provide an + /// output of . /// /// /// @@ -33,5 +32,4 @@ public abstract class LkParameterBuilderAttribute : Attribute public abstract bool IsInjectable(Mount mount) where TParameter : class where TInput : class; - -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs b/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs index 8b377ad..2c6e23e 100644 --- a/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs +++ b/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs @@ -1,32 +1,33 @@ // ======================== -// Lilikoi.Core::LkTargetBuilderAttribute.cs -// +// Lilikoi::LkTargetBuilderAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 31.01.2023 -// -> Bumped: 31.01.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using Lilikoi.Context; +#endregion + namespace Lilikoi.Attributes.Builders; [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public abstract class LkTargetBuilderAttribute : Attribute { /// - /// Create an instance of LkTargetAttribute that will be consumed by Lilikoi. + /// Create an instance of LkTargetAttribute that will be consumed by Lilikoi. /// /// public abstract LkTargetAttribute Build(Mount mount); /// - /// Declare if the built TargetAttribute will be able to accept a context with the type "TUserContext". + /// Declare if the built TargetAttribute will be able to accept a context with the type + /// "TUserContext". /// /// /// public abstract bool IsTargetable() - where TUserContext: Mount; -} + where TUserContext : Mount; +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Builders/LkWrapBuilderAttribute.cs b/Lilikoi/Attributes/Builders/LkWrapBuilderAttribute.cs index ea391a2..9b8e72f 100644 --- a/Lilikoi/Attributes/Builders/LkWrapBuilderAttribute.cs +++ b/Lilikoi/Attributes/Builders/LkWrapBuilderAttribute.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MkWrapBuilderAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkWrapBuilderAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -29,5 +25,4 @@ public abstract class LkWrapBuilderAttribute : Attribute /// /// public abstract bool IsWrappable(Mount mount); - -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs b/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs index ec5df5e..b7f8695 100644 --- a/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs +++ b/Lilikoi/Attributes/Impostor/LkInjectionImpostor.cs @@ -1,16 +1,17 @@ // ======================== // Lilikoi::LkInjectionImpostor.cs -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes.Builders; using Lilikoi.Context; +#endregion + namespace Lilikoi.Attributes.Impostor; public class LkInjectionImpostor : LkInjectionBuilderAttribute @@ -26,4 +27,4 @@ public sealed override bool IsInjectable(Mount mount) { return Impostor.IsInjectable(mount); } -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/LkInjectionAttribute.cs b/Lilikoi/Attributes/LkInjectionAttribute.cs index 67b1e2a..f36f629 100644 --- a/Lilikoi/Attributes/LkInjectionAttribute.cs +++ b/Lilikoi/Attributes/LkInjectionAttribute.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MkInjectionAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkInjectionAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -32,7 +28,7 @@ public abstract class LkInjectionAttribute : LkInjectionBuilderAttribute /// public sealed override LkInjectionAttribute Build(Mount mount) { - return this.MemberwiseClone() as LkInjectionAttribute; + return MemberwiseClone() as LkInjectionAttribute; } /// @@ -52,4 +48,4 @@ public virtual void Deject(Mount context, TInjectable injected) where TInjectable : class { } -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/LkParameterAttribute.cs b/Lilikoi/Attributes/LkParameterAttribute.cs index aeea8c0..208ec11 100644 --- a/Lilikoi/Attributes/LkParameterAttribute.cs +++ b/Lilikoi/Attributes/LkParameterAttribute.cs @@ -1,29 +1,28 @@ // ======================== -// Lilikoi.Core::LkParameterAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkParameterAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes.Builders; using Lilikoi.Context; +#endregion + namespace Lilikoi.Attributes; public abstract class LkParameterAttribute : LkParameterBuilderAttribute { - public sealed override LkParameterAttribute Build(Mount mount) { - return this.MemberwiseClone() as LkParameterAttribute; + return MemberwiseClone() as LkParameterAttribute; } /// - /// A function which accepts a context and the input and returns a type + /// A function which accepts a context and the input and returns a type /// /// /// @@ -33,5 +32,4 @@ public sealed override LkParameterAttribute Build(Mount mount) public abstract TParameter Inject(Mount context, TInput input) where TParameter : class where TInput : class; - -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/LkTargetAttribute.cs b/Lilikoi/Attributes/LkTargetAttribute.cs index f9c6187..d90c795 100644 --- a/Lilikoi/Attributes/LkTargetAttribute.cs +++ b/Lilikoi/Attributes/LkTargetAttribute.cs @@ -1,46 +1,47 @@ // ======================== -// Lilikoi.Core::LkTargetAttribute.cs -// +// Lilikoi::LkTargetAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 31.01.2023 -// -> Bumped: 31.01.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Public; using Lilikoi.Context; +#endregion + namespace Lilikoi.Attributes; public abstract class LkTargetAttribute : LkTargetBuilderAttribute { - public sealed override LkTargetAttribute Build(Mount mount) { - return this.MemberwiseClone() as LkTargetAttribute; + return MemberwiseClone() as LkTargetAttribute; } /// - /// Determine if this method should be included in the UserContext's scan. + /// Determine if this method should be included in the UserContext's scan. /// /// /// /// /// public virtual bool IsTargetedBy(TUserContext context, LilikoiMutator mutator) - where TUserContext: Mount - => true; + where TUserContext : Mount + { + return true; + } /// - /// Mutate the usercontext and lilikoi container. - /// Only called if IsTargetedBy returns true. + /// Mutate the usercontext and lilikoi container. + /// Only called if IsTargetedBy returns true. /// /// /// /// public abstract void Target(TUserContext context, LilikoiMutator mutator) - where TUserContext: Mount; - -} + where TUserContext : Mount; +} \ No newline at end of file diff --git a/Lilikoi/Attributes/LkWrapAttribute.cs b/Lilikoi/Attributes/LkWrapAttribute.cs index f184205..cab3385 100644 --- a/Lilikoi/Attributes/LkWrapAttribute.cs +++ b/Lilikoi/Attributes/LkWrapAttribute.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MkWrapAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkWrapAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -29,7 +25,7 @@ public abstract class LkWrapAttribute : LkWrapBuilderAttribute /// public sealed override LkWrapAttribute Build(Mount mount) { - return this.MemberwiseClone() as LkWrapAttribute; + return MemberwiseClone() as LkWrapAttribute; } public abstract WrapResult Before(Mount mount, ref TInput input) @@ -58,12 +54,12 @@ public WrapResult(bool shouldStop, TUnderlying? value = null) public static WrapResult Continue() { - return new(); + return new WrapResult(); } public static WrapResult Stop(TUnderlying value) { - return new() + return new WrapResult { stop = true, stopWithValue = value @@ -73,11 +69,11 @@ public static WrapResult Stop(TUnderlying value) public WrapResult Cast() where TNew : class { - return new() + return new WrapResult { stop = stop, stopWithValue = stopWithValue as TNew }; } } -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Static/LkMutatorAttribute.cs b/Lilikoi/Attributes/Static/LkMutatorAttribute.cs index 89e9a15..3e0c004 100644 --- a/Lilikoi/Attributes/Static/LkMutatorAttribute.cs +++ b/Lilikoi/Attributes/Static/LkMutatorAttribute.cs @@ -1,20 +1,20 @@ // ======================== -// Lilikoi.Core::LkMutatorAttribute.cs -// +// Lilikoi::LkMutatorAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 31.01.2023 -// -> Bumped: 31.01.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using Lilikoi.Compiler.Public; +#endregion + namespace Lilikoi.Attributes.Static; [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public abstract class LkMutatorAttribute : Attribute { public abstract void Mutate(LilikoiMutator mutator); -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Typed/LkTypedInjectionAttribute.cs b/Lilikoi/Attributes/Typed/LkTypedInjectionAttribute.cs index 412b1d7..dbf0884 100644 --- a/Lilikoi/Attributes/Typed/LkTypedInjectionAttribute.cs +++ b/Lilikoi/Attributes/Typed/LkTypedInjectionAttribute.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MkTypedInjectionAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkTypedInjectionAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -66,4 +62,4 @@ public virtual void Deject(Mount context, TInject injected) } #endregion -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Typed/LkTypedParameterAttribute.cs b/Lilikoi/Attributes/Typed/LkTypedParameterAttribute.cs index 2661464..84be76c 100644 --- a/Lilikoi/Attributes/Typed/LkTypedParameterAttribute.cs +++ b/Lilikoi/Attributes/Typed/LkTypedParameterAttribute.cs @@ -1,17 +1,16 @@ // ======================== -// Lilikoi.Core::LkTypedParameterAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkTypedParameterAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using Lilikoi.Context; +#endregion + namespace Lilikoi.Attributes.Typed; public abstract class LkTypedParameterAttribute : LkParameterAttribute @@ -42,4 +41,4 @@ public override TParameter Inject(Mount context, TIn input) } public abstract TOutput Inject(Mount context, TInput input); -} +} \ No newline at end of file diff --git a/Lilikoi/Attributes/Typed/LkTypedWrapAttribute.cs b/Lilikoi/Attributes/Typed/LkTypedWrapAttribute.cs index 953f847..36498a2 100644 --- a/Lilikoi/Attributes/Typed/LkTypedWrapAttribute.cs +++ b/Lilikoi/Attributes/Typed/LkTypedWrapAttribute.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MkTypedWrapAttribute.cs -// Distributed under the MIT License. -// +// Lilikoi::LkTypedWrapAttribute.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -30,7 +26,6 @@ public abstract class LkTypedWrapAttribute : LkWrapAttribute { public sealed override WrapResult Before(Mount mount, ref TInput input) { - var casted = input as TIn; if (casted is null) @@ -52,7 +47,7 @@ public sealed override void After(Mount mount, ref TOutput output) public sealed override bool IsWrappable(Mount mount) { return typeof(TIn).IsAssignableFrom(typeof(TInput)) && - typeof(TOut).IsAssignableFrom(typeof(TOutput)); + typeof(TOut).IsAssignableFrom(typeof(TOutput)); } #region Abstract @@ -62,4 +57,4 @@ public sealed override bool IsWrappable(Mount mount) public abstract void After(Mount mount, ref TOut output); #endregion -} +} \ No newline at end of file diff --git a/Lilikoi/Collection/Padlock.cs b/Lilikoi/Collection/Padlock.cs index ef9ea02..fb6f3c9 100644 --- a/Lilikoi/Collection/Padlock.cs +++ b/Lilikoi/Collection/Padlock.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::Padlock.cs -// Distributed under the MIT License. -// +// Lilikoi::Padlock.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -63,4 +59,4 @@ public bool Equals(Key other) return value.Equals(other.value); } } -} +} \ No newline at end of file diff --git a/Lilikoi/Collection/TypeDictionary.cs b/Lilikoi/Collection/TypeDictionary.cs index 6502eec..a9daac9 100644 --- a/Lilikoi/Collection/TypeDictionary.cs +++ b/Lilikoi/Collection/TypeDictionary.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::TypeDictionary.cs -// Distributed under the MIT License. -// +// Lilikoi::TypeDictionary.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -64,4 +60,4 @@ public void Unlock(Padlock.Key key) { mutable.Unlock(key); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Generator/CommonGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/CommonGenerator.cs index 036b99c..543f095 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/CommonGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/CommonGenerator.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::CommonGenerator.cs -// Distributed under the MIT License. -// +// Lilikoi::CommonGenerator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -45,4 +41,4 @@ public static Expression ToVariable(Expression input, out ParameterExpression va return block; } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Generator/DebugGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/DebugGenerator.cs index f953f44..edb9cd1 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/DebugGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/DebugGenerator.cs @@ -1,16 +1,16 @@ // ======================== -// Lilikoi.Core::DebugGenerator.cs -// Distributed under the MIT License. -// +// Lilikoi::DebugGenerator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using System.Linq.Expressions; +#endregion + namespace Lilikoi.Compiler.Mahogany.Generator; public static class DebugGenerator @@ -19,8 +19,9 @@ public static Expression Quick(int startline, int endline, string file = "Liliko { return Expression.DebugInfo(Expression.SymbolDocument(file), startline, 0, endline, 1); } + public static Expression Quick(int line, string file = "LilikoiMahogany.cs") { return Expression.DebugInfo(Expression.SymbolDocument(file), line, 0, line, 1); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Generator/InjectionGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/InjectionGenerator.cs index ea7e6c3..4d174e6 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/InjectionGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/InjectionGenerator.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::InjectionGenerator.cs -// Distributed under the MIT License. -// +// Lilikoi::InjectionGenerator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 26.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -62,13 +58,16 @@ public static Expression InjectValue(MahoganyMethod method, Expression attribute } /// - /// Returns a binary expression that sets the property on the source to the injected value + /// Returns a binary expression that sets the property on the source to the injected value /// /// An expression that evaluates to the built attribute object. /// An expression that resolves to the object the result will be assigned to. /// The property that will be assigned. /// The compiler context, used for mount named variable. - /// An expression that injects the value from and assigns it to + /// + /// An expression that injects the value from and assigns it to + /// + /// public static Expression InjectValueAsProperty( MahoganyMethod method, Expression attribute, @@ -237,4 +236,4 @@ public static Expression DejectValueAsFieldHeadless(ParameterExpression mountVar #endregion #endregion -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Generator/ParameterGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/ParameterGenerator.cs index 4432bf4..3a1143b 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/ParameterGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/ParameterGenerator.cs @@ -1,14 +1,11 @@ // ======================== -// Lilikoi.Core::ParameterGenerator.cs -// Distributed under the MIT License. -// +// Lilikoi::ParameterGenerator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Linq.Expressions; using System.Reflection; @@ -17,6 +14,8 @@ using Lilikoi.Attributes.Builders; using Lilikoi.Context; +#endregion + namespace Lilikoi.Compiler.Mahogany.Generator; internal static class ParameterGenerator @@ -78,5 +77,4 @@ public static Expression InjectWildcard(MahoganyMethod method, Expression source } #endregion - -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs index cde95dc..c241051 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs @@ -1,14 +1,11 @@ // ======================== -// Lilikoi.Core::WrapGenerator.cs -// Distributed under the MIT License. -// +// Lilikoi::WrapGenerator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Linq.Expressions; using System.Reflection; @@ -17,21 +14,22 @@ using Lilikoi.Attributes.Builders; using Lilikoi.Context; +#endregion + namespace Lilikoi.Compiler.Mahogany.Generator; internal static class WrapGenerator { + public const string WRAPRESULT_STOP = nameof(LkWrapAttribute.WrapResult.stop); + public const string WRAPRESULT_VALUE = nameof(LkWrapAttribute.WrapResult.stopWithValue); internal static MethodInfo MkWrapBuilderAttribute_Build = typeof(LkWrapBuilderAttribute).GetMethod("Build"); public static MethodInfo MkWrapAttribute_Before = typeof(LkWrapAttribute).GetMethod("Before"); public static MethodInfo MkWrapAttribute_After = typeof(LkWrapAttribute).GetMethod("After"); - public const string WRAPRESULT_STOP = nameof(LkWrapAttribute.WrapResult.stop); - public const string WRAPRESULT_VALUE = nameof(LkWrapAttribute.WrapResult.stopWithValue); - /// - /// Create an expression which creates an MkWrapAttribute. + /// Create an expression which creates an MkWrapAttribute. /// /// /// @@ -41,11 +39,14 @@ internal static Expression Builder(LkWrapBuilderAttribute builderAttribute, Moun } /// - /// Create a redirectable "before" wrap which can modify the input or halt execution altogether. - /// This wrap can jump to the "end" label provided in the builder. + /// Create a redirectable "before" wrap which can modify the input or halt execution altogether. + /// This wrap can jump to the "end" label provided in the builder. /// /// - /// An expression which represents the MkWrapAttribute in use for this ececution. + /// + /// An expression which represents the MkWrapAttribute in use for this + /// ececution. + /// /// /// /// @@ -65,10 +66,10 @@ internal static Expression Before(MahoganyMethod method, Expression attribute) var guard = Expression.Block( setter, - Expression.IfThen( - Expression.IsTrue(Expression.Field(result, WRAPRESULT_STOP)), - Expression.Return(method.HaltTarget, Expression.Field(result, WRAPRESULT_VALUE)) - )); + Expression.IfThen( + Expression.IsTrue(Expression.Field(result, WRAPRESULT_STOP)), + Expression.Return(method.HaltTarget, Expression.Field(result, WRAPRESULT_VALUE)) + )); return Expression.Block( guard, @@ -76,9 +77,12 @@ internal static Expression Before(MahoganyMethod method, Expression attribute) } /// - /// Create an "after" filter which can modify the output. + /// Create an "after" filter which can modify the output. /// - /// An expression which represents the MkWrapAttribute in use for this ececution. + /// + /// An expression which represents the MkWrapAttribute in use for this + /// ececution. + /// /// /// /// @@ -88,13 +92,11 @@ internal static Expression After(Expression attribute, Expression mountSource, E var method = MkWrapAttribute_After.MakeGenericMethod(output); - return Expression.Call(attribute, method, mountSource, outputSource); + return Expression.Call(attribute, method, mountSource, outputSource); } internal static Expression After(MahoganyMethod method, Expression attribute) { - return After(attribute, method.Named(MahoganyConstants.MOUNT_VAR), method.Named(MahoganyConstants.OUTPUT_VAR), method.Result); + return After(attribute, method.Named(MahoganyConstants.MOUNT_VAR), method.Named(MahoganyConstants.OUTPUT_VAR), method.Result); } - - -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/MahoganyBlockStack.cs b/Lilikoi/Compiler/Mahogany/MahoganyBlockStack.cs index 1c224c3..21cf7cd 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyBlockStack.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyBlockStack.cs @@ -1,14 +1,9 @@ // ======================== -// Lilikoi.Core::MahoganyBlockStack.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyBlockStack.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// Block stack to ensure the pyramid is executed in the same order climbing both up and down. -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -37,4 +32,4 @@ public void Apex(Expression apex) while (Blocks.Count != 0) Method.Append(Blocks.Pop()); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs b/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs index 7a332ec..b14908d 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MahoganyCompiler.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyCompiler.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -18,7 +14,6 @@ using Lilikoi.Attributes.Static; using Lilikoi.Compiler.Mahogany.Generator; using Lilikoi.Compiler.Mahogany.Steps; -using Lilikoi.Compiler.Public; #endregion @@ -38,10 +33,9 @@ public static List ParameterStepBuilder(MahoganyMethod me { var steps = new List(); - foreach (ParameterInfo parameterInfo in method.Entry.GetParameters()) + foreach (var parameterInfo in method.Entry.GetParameters()) foreach (var attribute in parameterInfo.GetCustomAttributes() .Where(obj => obj.GetType().IsSubclassOf(typeof(LkParameterBuilderAttribute)))) - { try { var builders = (LkParameterBuilderAttribute)attribute; @@ -56,7 +50,6 @@ public static List ParameterStepBuilder(MahoganyMethod me { throw new AggregateException($"Unable to process '{attribute.GetType().FullName}'s injection of parameter '{parameterInfo.Name}' for type '{method.Host.FullName}':", e); } - } return steps; } @@ -195,8 +188,8 @@ public void WrapsFor() foreach (var mahoganyWrapStep in steps) { - (var enter, var exit) = mahoganyWrapStep.Generate(); - Stack.Push(enter,exit); + var (enter, exit) = mahoganyWrapStep.Generate(); + Stack.Push(enter, exit); } } @@ -204,7 +197,7 @@ public void ImplicitWrap(LkWrapBuilderAttribute builder) { var step = new MahoganyWrapStep(Method, builder); - (var enter, var exit) = step.Generate(); + var (enter, exit) = step.Generate(); Stack.Push(enter, exit); } @@ -222,7 +215,7 @@ public void InjectionsFor(Type host) foreach (var mahoganyInjectStep in steps) { - (var enter, var exit) = mahoganyInjectStep.Generate(); + var (enter, exit) = mahoganyInjectStep.Generate(); Stack.Push(enter, exit); } } @@ -231,7 +224,7 @@ public void ParametersFor() { var steps = ParameterStepBuilder(Method); - foreach (MahoganyParameterStep mahoganyParameterStep in steps) + foreach (var mahoganyParameterStep in steps) { var expression = mahoganyParameterStep.Generate(); Stack.Push(expression, Expression.Empty()); @@ -255,8 +248,7 @@ public void Apex() Expression.Call(Method.Named(MahoganyConstants.HOST_VAR), Method.Entry, filled)) ) ); - } #endregion -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/MahoganyConstants.cs b/Lilikoi/Compiler/Mahogany/MahoganyConstants.cs index fffa11b..478f3a8 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyConstants.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyConstants.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MahoganyConstants.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyConstants.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== namespace Lilikoi.Compiler.Mahogany; @@ -17,4 +13,4 @@ public class MahoganyConstants public const string MOUNT_VAR = "__mount"; public const string HOST_VAR = "__host"; public const string OUTPUT_VAR = "__output"; -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs index 968c270..6f1d661 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MahoganyMethod.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyMethod.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -23,11 +19,10 @@ namespace Lilikoi.Compiler.Mahogany; public class MahoganyMethod { + public List Temporaries = new(); public LabelTarget HaltTarget { get; set; } - public List Temporaries = new(); - /// /// A list of parameters that are to be injected into the method, indexed by the parameter they /// will be filling. @@ -137,4 +132,4 @@ public LambdaExpression Lambda() public Type Result { get; set; } #endregion -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs b/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs index 2de2517..51fac0e 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs @@ -1,20 +1,19 @@ // ======================== -// Lilikoi.Core::MahoganyValidator.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyValidator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 23.12.2022 -// -> Bumped: 23.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Reflection; using Lilikoi.Attributes.Builders; using Lilikoi.Context; +#endregion + namespace Lilikoi.Compiler.Mahogany; internal static class MahoganyValidator @@ -25,7 +24,7 @@ internal static bool ValidInjectable(LkInjectionBuilderAttribute attribute, Type // Look at what they have been doing behind our backs!!! return (bool)(attribute.GetType().GetMethod("IsInjectable") ?.MakeGenericMethod(test) - ?.Invoke(attribute, new []{mount}) ?? false); + ?.Invoke(attribute, new[] { mount }) ?? false); } internal static void ValidateInjection(LkInjectionBuilderAttribute attribute, FieldInfo fieldInfo, Mount mount) @@ -41,7 +40,7 @@ internal static bool ValidParameter(LkParameterBuilderAttribute attribute, Type { return (bool)(attribute.GetType().GetMethod("IsInjectable") ?.MakeGenericMethod(output, input) - ?.Invoke(attribute, new [] {mount}) ?? false); + ?.Invoke(attribute, new[] { mount }) ?? false); } internal static void ValidateParameter(LkParameterBuilderAttribute attribute, MethodInfo method, Type input, ParameterInfo parameterInfo, Mount mount) @@ -58,7 +57,7 @@ internal static bool ValidWrap(LkWrapBuilderAttribute attribute, Type input, Typ { return (bool)(attribute.GetType().GetMethod("IsWrappable") ?.MakeGenericMethod(input, output) - ?.Invoke(attribute, new [] {mount}) ?? false); + ?.Invoke(attribute, new[] { mount }) ?? false); } internal static void ValidateWrap(LkWrapBuilderAttribute attribute, MahoganyMethod method) @@ -66,6 +65,4 @@ internal static void ValidateWrap(LkWrapBuilderAttribute attribute, MahoganyMeth if (!ValidWrap(attribute, method.Input, method.Result, method.Mount)) throw new InvalidCastException($"Wrap '{attribute.GetType().FullName}'" + $"rejected input-output pair on method '{method.Entry.Name}'"); } - - -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Steps/MahoganyInjectStep.cs b/Lilikoi/Compiler/Mahogany/Steps/MahoganyInjectStep.cs index c1bc4ba..54ffb8f 100644 --- a/Lilikoi/Compiler/Mahogany/Steps/MahoganyInjectStep.cs +++ b/Lilikoi/Compiler/Mahogany/Steps/MahoganyInjectStep.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MahoganyInjectStep.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyInjectStep.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -63,4 +59,4 @@ public MahoganyInjectStep(MahoganyMethod method, FieldInfo fieldInfo, LkInjectio return (entry, exit); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterStep.cs b/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterStep.cs index be49de1..ff2098c 100644 --- a/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterStep.cs +++ b/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterStep.cs @@ -1,14 +1,11 @@ // ======================== -// Lilikoi.Core::MahoganyParameterStep.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyParameterStep.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Linq.Expressions; using System.Reflection; @@ -16,6 +13,8 @@ using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Mahogany.Generator; +#endregion + namespace Lilikoi.Compiler.Mahogany.Steps; public class MahoganyParameterStep @@ -40,4 +39,4 @@ public Expression Generate() return ParameterGenerator.InjectParameter(Method, instance, ParameterInfo); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterWildcardStep.cs b/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterWildcardStep.cs index ac0334e..6734a6d 100644 --- a/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterWildcardStep.cs +++ b/Lilikoi/Compiler/Mahogany/Steps/MahoganyParameterWildcardStep.cs @@ -1,20 +1,19 @@ // ======================== -// Lilikoi.Core::MahoganyParameterWildcardStep.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyParameterWildcardStep.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 03.01.2023 -// -> Bumped: 03.01.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Linq.Expressions; using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Mahogany.Generator; +#endregion + namespace Lilikoi.Compiler.Mahogany.Steps; public class MahoganyParameterWildcardStep @@ -39,4 +38,4 @@ public Expression Generate() return ParameterGenerator.InjectWildcard(Method, instance, Wildcard); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Mahogany/Steps/MahoganyWrapStep.cs b/Lilikoi/Compiler/Mahogany/Steps/MahoganyWrapStep.cs index dde72b6..a762380 100644 --- a/Lilikoi/Compiler/Mahogany/Steps/MahoganyWrapStep.cs +++ b/Lilikoi/Compiler/Mahogany/Steps/MahoganyWrapStep.cs @@ -1,20 +1,19 @@ // ======================== -// Lilikoi.Core::MahoganyWrapStep.cs -// Distributed under the MIT License. -// +// Lilikoi::MahoganyWrapStep.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 24.12.2022 -// -> Bumped: 24.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Linq.Expressions; using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Mahogany.Generator; +#endregion + namespace Lilikoi.Compiler.Mahogany.Steps; public class MahoganyWrapStep @@ -33,7 +32,7 @@ public MahoganyWrapStep(MahoganyMethod method, LkWrapBuilderAttribute builder) { var setter = Method.AsVariable( - WrapGenerator.Builder(Builder, Method.Mount), out var instance); + WrapGenerator.Builder(Builder, Method.Mount), out var instance); //var injects = MahoganyCompiler.InjectStepBuilder(Actual, Method); @@ -48,7 +47,7 @@ public MahoganyWrapStep(MahoganyMethod method, LkWrapBuilderAttribute builder) setter, //enterInj, WrapGenerator.Before(Method, instance) - ); + ); var exit = Expression.Block( //exitInj, @@ -57,4 +56,4 @@ public MahoganyWrapStep(MahoganyMethod method, LkWrapBuilderAttribute builder) return (entry, exit); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Public/LilikoiCompiler.cs b/Lilikoi/Compiler/Public/LilikoiCompiler.cs index fbbf016..0fbd6f8 100644 --- a/Lilikoi/Compiler/Public/LilikoiCompiler.cs +++ b/Lilikoi/Compiler/Public/LilikoiCompiler.cs @@ -1,18 +1,13 @@ // ======================== -// Lilikoi.Core::MilikoCompiler.cs -// Distributed under the MIT License. -// +// Lilikoi::LilikoiCompiler.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region using Lilikoi.Attributes.Builders; -using Lilikoi.Attributes.Static; using Lilikoi.Compiler.Mahogany; using Lilikoi.Context; @@ -22,12 +17,13 @@ namespace Lilikoi.Compiler.Public; public class LilikoiCompiler { - internal MahoganyCompiler Internal { get; set; } + internal List<(LkParameterBuilderAttribute, Type)> ImplicitWildcards = new(); + + internal List ImplicitWraps = new(); - internal Mount Smuggler { get; } = new Mount(); + internal MahoganyCompiler Internal { get; set; } - internal List ImplicitWraps = new List(); - internal List<(LkParameterBuilderAttribute, Type)> ImplicitWildcards = new List<(LkParameterBuilderAttribute, Type)>(); + internal Mount Smuggler { get; } = new(); public LilikoiMutator Mutator() { @@ -38,10 +34,8 @@ private void Mutators() { var mutators = MahoganyCompiler.MutatorsForMethod(Internal.Method.Entry); - foreach (LkMutatorAttribute lilikoiMutator in mutators) - { + foreach (var lilikoiMutator in mutators) lilikoiMutator.Mutate(Mutator()); - } } public LilikoiContainer Finish() @@ -52,15 +46,11 @@ public LilikoiContainer Finish() Internal.InjectionsFor(Internal.Method.Host); foreach (var implicitWrap in ImplicitWraps) - { Internal.ImplicitWrap(implicitWrap); - } Internal.WrapsFor(); foreach (var (implicitWildcard, type) in ImplicitWildcards) - { Internal.ImplicitWildcard(implicitWildcard, type); - } Internal.ParametersFor(); @@ -68,4 +58,4 @@ public LilikoiContainer Finish() return new LilikoiContainer(Smuggler, Internal.Method.Lambda()); } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Public/LilikoiContainer.cs b/Lilikoi/Compiler/Public/LilikoiContainer.cs index 535d295..4b39a83 100644 --- a/Lilikoi/Compiler/Public/LilikoiContainer.cs +++ b/Lilikoi/Compiler/Public/LilikoiContainer.cs @@ -1,18 +1,18 @@ // ======================== -// Lilikoi.Core::MilikoContainer.cs -// Distributed under the MIT License. -// +// Lilikoi::LilikoiContainer.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using System.Linq.Expressions; using Lilikoi.Context; +#endregion + namespace Lilikoi.Compiler.Public; public class LilikoiContainer : Mount @@ -34,5 +34,8 @@ public TOut Run(THost host, TIn input) return (Memoized as Func)(host, input); } - public Func Compile() => Body.Compile(false) as Func; -} + public Func Compile() + { + return Body.Compile(false) as Func; + } +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Public/LilikoiMethod.cs b/Lilikoi/Compiler/Public/LilikoiMethod.cs index 2852857..60e5531 100644 --- a/Lilikoi/Compiler/Public/LilikoiMethod.cs +++ b/Lilikoi/Compiler/Public/LilikoiMethod.cs @@ -1,13 +1,9 @@ // ======================== -// Lilikoi.Core::MilikoMethod.cs -// Distributed under the MIT License. +// Lilikoi::LilikoiMethod.cs +// (c) 2023. Distributed under the MIT License // // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== #region @@ -38,7 +34,7 @@ public static LilikoiMethod FromMethodInfo(MethodInfo method) Host = method.DeclaringType, NamedVariables = new Dictionary() { - { MahoganyConstants.HOST_VAR, Expression.Parameter(method.DeclaringType, MahoganyConstants.HOST_VAR) }, + { MahoganyConstants.HOST_VAR, Expression.Parameter(method.DeclaringType, MahoganyConstants.HOST_VAR) } } } }; diff --git a/Lilikoi/Compiler/Public/LilikoiMutator.cs b/Lilikoi/Compiler/Public/LilikoiMutator.cs index 29565dd..512e2b2 100644 --- a/Lilikoi/Compiler/Public/LilikoiMutator.cs +++ b/Lilikoi/Compiler/Public/LilikoiMutator.cs @@ -1,17 +1,17 @@ // ======================== -// Lilikoi.Core::LilikoiMutator.cs -// +// Lilikoi::LilikoiMutator.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 31.01.2023 -// -> Bumped: 31.01.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using Lilikoi.Attributes.Builders; using Lilikoi.Context; +#endregion + namespace Lilikoi.Compiler.Public; public class LilikoiMutator : Mount @@ -24,7 +24,7 @@ internal LilikoiMutator(Mount self, LilikoiCompiler compiler) : base(self) internal LilikoiCompiler Compiler { get; set; } /// - /// Add an implicit wrap to this container + /// Add an implicit wrap to this container /// /// /// @@ -35,8 +35,8 @@ public LilikoiMutator Implicit(LkWrapBuilderAttribute value) } /// - /// Add an implicit wrap to this container. - /// If a null value is provided, the default constructor is used. + /// Add an implicit wrap to this container. + /// If a null value is provided, the default constructor is used. /// /// /// @@ -52,36 +52,36 @@ public LilikoiMutator Implicit(TWrap value = null) } /// - /// Add a parameter wildcard to this container. + /// Add a parameter wildcard to this container. /// /// /// public LilikoiMutator Wildcard(LkParameterBuilderAttribute value) { - Compiler.ImplicitWildcards.Add( (value, typeof(TType)) ); + Compiler.ImplicitWildcards.Add((value, typeof(TType))); return this; } /// - /// Add a parameter wildcard to this container. - /// If a null value is provided, the default constructor is used. + /// Add a parameter wildcard to this container. + /// If a null value is provided, the default constructor is used. /// /// /// /// public LilikoiMutator Wildcard(TParameter value = null) - where TParameter: LkParameterBuilderAttribute, new() + where TParameter : LkParameterBuilderAttribute, new() { if (value is null) value = new TParameter(); - Compiler.ImplicitWildcards.Add( (value, typeof(TType)) ); + Compiler.ImplicitWildcards.Add((value, typeof(TType))); return this; } /// - /// Get the parameter type of a function by the parameter number - /// Used for type routing + /// Get the parameter type of a function by the parameter number + /// Used for type routing /// /// /// @@ -94,4 +94,4 @@ public LilikoiMutator Wildcard(TParameter value = null) return parameters[paramNum]; } -} +} \ No newline at end of file diff --git a/Lilikoi/Compiler/Public/Utilities/LilikoiInjector.cs b/Lilikoi/Compiler/Public/Utilities/LilikoiInjector.cs index 654ac98..f67c67e 100644 --- a/Lilikoi/Compiler/Public/Utilities/LilikoiInjector.cs +++ b/Lilikoi/Compiler/Public/Utilities/LilikoiInjector.cs @@ -1,14 +1,11 @@ // ======================== -// Lilikoi.Core::LilikoiInjector.cs -// Distributed under the MIT License. -// +// Lilikoi::LilikoiInjector.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 23.12.2022 -// -> Bumped: 23.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region using System.Linq.Expressions; using System.Reflection; @@ -18,18 +15,23 @@ using Lilikoi.Compiler.Mahogany.Generator; using Lilikoi.Context; +#endregion + namespace Lilikoi.Compiler.Public.Utilities; public static class LilikoiInjector { + public delegate void Dejector(T obj); + + public delegate void Injector(T obj); + internal static void InjectsForClass(List injects, List dejects, List variables, ParameterExpression host, ParameterExpression mount, Mount mountVal, Type type) { - foreach (FieldInfo fieldInfo in type.GetFields()) - foreach (LkInjectionBuilderAttribute attribute in + foreach (var fieldInfo in type.GetFields()) + foreach (var attribute in fieldInfo.GetCustomAttributes() .OfType()) { - MahoganyValidator.ValidateInjection(attribute, fieldInfo, mountVal); var setter = CommonGenerator.ToVariable( @@ -49,15 +51,15 @@ out var instance } } - internal static Injector InjectorForClass( Mount mount ) + internal static Injector InjectorForClass(Mount mount) { var host = typeof(THost); - List body = new List(); - List dejectBody = new List(); + List body = new(); + List dejectBody = new(); - List variables = new List(); - ParameterExpression hostVar = Expression.Parameter(host, "__host"); - ParameterExpression mountVar = Expression.Parameter(typeof(Mount), "__mount"); + List variables = new(); + var hostVar = Expression.Parameter(host, "__host"); + var mountVar = Expression.Parameter(typeof(Mount), "__mount"); body.Add(Expression.Assign(mountVar, Expression.Constant(mount))); @@ -80,12 +82,12 @@ internal static Injector InjectorForClass( Mount mount ) internal static Dejector DejectorForClass(Mount mount) { var host = typeof(THost); - List injectBody = new List(); - List body = new List(); + List injectBody = new(); + List body = new(); - List variables = new List(); - ParameterExpression hostVar = Expression.Parameter(host, "__host"); - ParameterExpression mountVar = Expression.Parameter(typeof(Mount), "__mount"); + List variables = new(); + var hostVar = Expression.Parameter(host, "__host"); + var mountVar = Expression.Parameter(typeof(Mount), "__mount"); body.Add(Expression.Assign(mountVar, Expression.Constant(mount))); @@ -105,31 +107,27 @@ internal static Dejector DejectorForClass(Mount mount) #endif } - public delegate void Injector(T obj); - - public delegate void Dejector(T obj); - public static Injector CreateInjector(Mount mount) - where ToInject: class + where ToInject : class { return InjectorForClass(mount); } public static Injector CreateInjector() - where ToInject: class + where ToInject : class { return CreateInjector(new Mount()); } public static Dejector CreateDejector(Mount mount) - where ToInject: class + where ToInject : class { return DejectorForClass(mount); } public static Dejector CreateDejector() - where ToInject: class + where ToInject : class { return DejectorForClass(new Mount()); } @@ -151,4 +149,4 @@ public static void Deject(Mount cache, T self) cache.Get>()!(self); } -} +} \ No newline at end of file diff --git a/Lilikoi/Context/Mount.cs b/Lilikoi/Context/Mount.cs index a71bd2f..21dcd8b 100644 --- a/Lilikoi/Context/Mount.cs +++ b/Lilikoi/Context/Mount.cs @@ -1,25 +1,24 @@ // ======================== -// Lilikoi.Core::InvocationContext.cs -// Distributed under the MIT License. -// +// Lilikoi::Mount.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 22.12.2022 -// -> Bumped: 22.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Collection; +#endregion + namespace Lilikoi.Context; public class Mount { - private TypeDictionary dictionary = new TypeDictionary(); + private TypeDictionary dictionary = new(); public Mount() { - } public Mount(Mount other) @@ -40,5 +39,8 @@ public virtual void Store(T value) return dictionary.Get(); } - public virtual bool Has() => dictionary.Has(); -} + public virtual bool Has() + { + return dictionary.Has(); + } +} \ No newline at end of file diff --git a/Lilikoi/Lilikoi.csproj b/Lilikoi/Lilikoi.csproj index 637e321..4f06aed 100644 --- a/Lilikoi/Lilikoi.csproj +++ b/Lilikoi/Lilikoi.csproj @@ -6,7 +6,7 @@ enable 11 Lilikoi - + Lilikoi @@ -18,7 +18,7 @@ LICENSE.md README.md - + https://github.com/Mooshua/Lilikoi git @@ -27,7 +27,7 @@ false true - + false true @@ -37,23 +37,23 @@ false false $(SolutionDir)artifacts/ - + true true - + - - - + + + - + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Lilikoi/Merge/Injection/InjectionMerger.cs b/Lilikoi/Merge/Injection/InjectionMerger.cs index 9ce64c4..384ee54 100644 --- a/Lilikoi/Merge/Injection/InjectionMerger.cs +++ b/Lilikoi/Merge/Injection/InjectionMerger.cs @@ -1,18 +1,17 @@ // ======================== // Lilikoi::InjectionMerger.cs -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== -using System.Runtime.CompilerServices; +#region using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Merge.Injection; public class InjectionMerger @@ -21,7 +20,7 @@ public class InjectionMerger private Dictionary> merged = new(); /// - /// Add this initializer to this merged injection + /// Add this initializer to this merged injection /// /// /// @@ -33,19 +32,21 @@ public InjectionMerger And(Func initializer) if (merged.ContainsKey(typeof(TAs))) throw new InvalidOperationException($"Already contains injection handler for {typeof(TAs).Name}"); - merged.Add(typeof(TAs), initializer as Func ); + merged.Add(typeof(TAs), initializer as Func); return this; } /// - /// Add this class to this merged injection + /// Add this class to this merged injection /// /// /// public InjectionMerger And() where TAs : class, new() - => And(mount => new TAs()); + { + return And(mount => new TAs()); + } public InjectionMerger And(LkInjectionAttribute attribute) { @@ -60,44 +61,36 @@ public MergedInjectionImpostor Impostor() } internal bool Has(Mount mount) - where TAs: class + where TAs : class { foreach (var attribute in attributes) - { if (attribute.IsInjectable(mount)) return true; - } return merged.ContainsKey(typeof(TAs)); } internal TAs? Inject(Mount mount) - where TAs: class + where TAs : class { foreach (var attribute in attributes) - { if (attribute.IsInjectable(mount)) return attribute.Inject(mount); - } if (merged.TryGetValue(typeof(TAs), out var func)) - { return func(mount) as TAs; - } return null; } internal void Deject(Mount mount, TAs value) - where TAs: class + where TAs : class { foreach (var attribute in attributes) - { if (attribute.IsInjectable(mount)) { attribute.Deject(mount, value); return; } - } } -} +} \ No newline at end of file diff --git a/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs b/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs index 789c1d5..06a089d 100644 --- a/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs +++ b/Lilikoi/Merge/Injection/MergedInjectionImpostor.cs @@ -1,27 +1,28 @@ // ======================== -// Lilikoi::MergedInjectionActor.cs -// +// Lilikoi::MergedInjectionImpostor.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Merge.Injection; public class MergedInjectionImpostor : LkInjectionAttribute { - protected InjectionMerger Merged { get; } - public MergedInjectionImpostor(InjectionMerger merged) { Merged = merged; } + protected InjectionMerger Merged { get; } + public override bool IsInjectable(Mount mount) { return Merged.Has(mount); @@ -36,4 +37,4 @@ public override void Deject(Mount context, TInjectable injected) { Merged.Deject(context, injected); } -} +} \ No newline at end of file diff --git a/Lilikoi/Scan/Scanner.cs b/Lilikoi/Scan/Scanner.cs index e58e1c5..f29ee52 100644 --- a/Lilikoi/Scan/Scanner.cs +++ b/Lilikoi/Scan/Scanner.cs @@ -1,28 +1,27 @@ // ======================== -// Lilikoi.Core::Scanner.cs -// +// Lilikoi::Scanner.cs +// (c) 2023. Distributed under the MIT License +// // -> Created: 31.01.2023 -// -> Bumped: 31.01.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== -using System.Collections.Concurrent; +#region + using System.Reflection; using Lilikoi.Attributes.Builders; using Lilikoi.Compiler.Public; using Lilikoi.Context; +#endregion + namespace Lilikoi.Scan; public static class Scanner { - /// - /// Scan the provided assembly for all Lilikoi Containers matching the user context and return them - /// Use the provided mount as the global mount + /// Scan the provided assembly for all Lilikoi Containers matching the user context and return them + /// Use the provided mount as the global mount /// /// A user-supplied context potentially consumed by a target /// @@ -38,8 +37,8 @@ public static List Scan(TUserCo } /// - /// Scan the provided assembly for all Lilikoi Containers matching the user context and return them - /// Use a provided function to supply the Mount for each container. + /// Scan the provided assembly for all Lilikoi Containers matching the user context and return them + /// Use a provided function to supply the Mount for each container. /// /// A user-supplied context potentially consumed by a target /// @@ -51,38 +50,32 @@ public static List Scan(TUserCo public static List Scan(TUserContext context, Assembly assembly, Func sourceMount) where TUserContext : Mount { - List containers = new List(); + List containers = new(); - foreach (Type type in assembly.GetTypes()) - { - foreach (MethodInfo methodInfo in type.GetMethods()) + foreach (var type in assembly.GetTypes()) + foreach (var methodInfo in type.GetMethods()) + foreach (LkTargetBuilderAttribute attribute in methodInfo.GetCustomAttributes() + .Where(attribute => attribute + .GetType() + .IsSubclassOf(typeof(LkTargetBuilderAttribute)))) + if (attribute.IsTargetable()) { - foreach (LkTargetBuilderAttribute attribute in methodInfo.GetCustomAttributes() - .Where(attribute => attribute - .GetType() - .IsSubclassOf(typeof(LkTargetBuilderAttribute)))) - { - if (attribute.IsTargetable()) - { - var mount = sourceMount(); + var mount = sourceMount(); - // This method is targeted! - // Begin container creation - var compiler = LilikoiMethod.FromMethodInfo(methodInfo) - .Mount(mount) - .Input() - .Output() - .Build(); + // This method is targeted! + // Begin container creation + var compiler = LilikoiMethod.FromMethodInfo(methodInfo) + .Mount(mount) + .Input() + .Output() + .Build(); - var built = attribute.Build(mount); - built.Target(context, compiler.Mutator()); + var built = attribute.Build(mount); + built.Target(context, compiler.Mutator()); - containers.Add( compiler.Finish() ); - } - } + containers.Add(compiler.Finish()); } - } return containers; } -} +} \ No newline at end of file diff --git a/Lilikoi/Standard/Extensions/MountBuilder.cs b/Lilikoi/Standard/Extensions/MountBuilder.cs new file mode 100644 index 0000000..5f0e36c --- /dev/null +++ b/Lilikoi/Standard/Extensions/MountBuilder.cs @@ -0,0 +1,25 @@ +// ======================== +// Lilikoi::MountBuilder.cs +// (c) 2023. Distributed under the MIT License +// +// -> Created: 06.02.2023 +// -> Bumped: 06.02.2023 +// ======================== +#region + +using Lilikoi.Context; + +#endregion + +namespace Lilikoi.Standard.Extensions; + +public class MountBuilder : Mount +{ + public MountBuilder() : base() + { + } + + public MountBuilder(Mount self) : base(self) + { + } +} diff --git a/Lilikoi/Standard/Extensions/MountConverterExtensions.cs b/Lilikoi/Standard/Extensions/MountConverterExtensions.cs new file mode 100644 index 0000000..e8c89f9 --- /dev/null +++ b/Lilikoi/Standard/Extensions/MountConverterExtensions.cs @@ -0,0 +1,22 @@ +// ======================== +// Lilikoi::MountConverterExtensions.cs +// (c) 2023. Distributed under the MIT License +// +// -> Created: 06.02.2023 +// -> Bumped: 06.02.2023 +// ======================== +#region + +using Lilikoi.Context; + +#endregion + +namespace Lilikoi.Standard.Extensions; + +public static class MountConverterExtensions +{ + public static MountBuilder AsBuilder(this Mount self) + { + return new MountBuilder(self); + } +} diff --git a/Lilikoi/Standard/Extensions/MountExtensions.cs b/Lilikoi/Standard/Extensions/MountExtensions.cs deleted file mode 100644 index ea68689..0000000 --- a/Lilikoi/Standard/Extensions/MountExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -// ======================== -// Lilikoi::MountExtensions.cs -// -// -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// -// ======================== -using System.Runtime.CompilerServices; - -using Lilikoi.Context; - -namespace Lilikoi.Standard.Extensions; - -public static class MountExtensions -{ - public static Mount RegisterFactory(this Mount self, TFactory factory) - where TFactory: IFactory - { - self.Store>(factory); - - return self; - } - - public static Mount RegisterSingleton(this Mount self, TSingleton value) - where TSingleton : class - { - self.Store(value); - - return self; - } -} diff --git a/Lilikoi/Standard/Extensions/RegistrationExtensions.cs b/Lilikoi/Standard/Extensions/RegistrationExtensions.cs new file mode 100644 index 0000000..9af61d6 --- /dev/null +++ b/Lilikoi/Standard/Extensions/RegistrationExtensions.cs @@ -0,0 +1,27 @@ +// ======================== +// Lilikoi::RegistrationExtensions.cs +// (c) 2023. Distributed under the MIT License +// +// -> Created: 01.02.2023 +// -> Bumped: 06.02.2023 +// ======================== +namespace Lilikoi.Standard.Extensions; + +public static class RegistrationExtensions +{ + public static MountBuilder RegisterFactory(this MountBuilder self, TFactory factory) + where TFactory : IFactory + { + self.Store>(factory); + + return self; + } + + public static MountBuilder RegisterSingleton(this MountBuilder self, TSingleton value) + where TSingleton : class + { + self.Store(value); + + return self; + } +} diff --git a/Lilikoi/Standard/Factory/FactoryAttribute.cs b/Lilikoi/Standard/Factory/FactoryAttribute.cs index 2357387..a599864 100644 --- a/Lilikoi/Standard/Factory/FactoryAttribute.cs +++ b/Lilikoi/Standard/Factory/FactoryAttribute.cs @@ -1,16 +1,17 @@ // ======================== // Lilikoi::FactoryAttribute.cs +// (c) 2023. Distributed under the MIT License // // -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Standard; public class FactoryAttribute : LkInjectionAttribute diff --git a/Lilikoi/Standard/Factory/IFactory.cs b/Lilikoi/Standard/Factory/IFactory.cs index eb9e605..3d1ee44 100644 --- a/Lilikoi/Standard/Factory/IFactory.cs +++ b/Lilikoi/Standard/Factory/IFactory.cs @@ -1,16 +1,13 @@ // ======================== // Lilikoi::IFactory.cs -// +// (c) 2023. Distributed under the MIT License +// // -> Created: 01.02.2023 -// -> Bumped: 01.02.2023 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== namespace Lilikoi.Standard; public interface IFactory { public TProduct Create(); -} +} \ No newline at end of file diff --git a/Lilikoi/Standard/NewAttribute.cs b/Lilikoi/Standard/NewAttribute.cs index 00137d3..212c589 100644 --- a/Lilikoi/Standard/NewAttribute.cs +++ b/Lilikoi/Standard/NewAttribute.cs @@ -1,34 +1,34 @@ // ======================== // Lilikoi::NewAttribute.cs -// Distributed under the MIT License. +// (c) 2023. Distributed under the MIT License // // -> Created: 23.12.2022 -// -> Bumped: 23.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Standard; public class NewAttribute : LkInjectionAttribute { - protected object[] ConstructorParameters { get; set; } - public NewAttribute(params object[] constructor) { ConstructorParameters = constructor; } - public override bool IsInjectable(Context.Mount mount) + protected object[] ConstructorParameters { get; set; } + + public override bool IsInjectable(Mount mount) { return Activator.CreateInstance(typeof(TInjectable), ConstructorParameters) as TInjectable is not null; } - public override TInjectable Inject(Context.Mount context) + public override TInjectable Inject(Mount context) { var instance = Activator.CreateInstance(typeof(TInjectable), ConstructorParameters) as TInjectable; diff --git a/Lilikoi/Standard/SingletonAttribute.cs b/Lilikoi/Standard/SingletonAttribute.cs index 7235a7a..0dcd6b9 100644 --- a/Lilikoi/Standard/SingletonAttribute.cs +++ b/Lilikoi/Standard/SingletonAttribute.cs @@ -1,17 +1,17 @@ // ======================== -// Lilikoi::MountAttribute.cs -// Distributed under the MIT License. +// Lilikoi::SingletonAttribute.cs +// (c) 2023. Distributed under the MIT License // // -> Created: 23.12.2022 -// -> Bumped: 23.12.2022 -// -// -> Purpose: -// -// +// -> Bumped: 06.02.2023 // ======================== +#region + using Lilikoi.Attributes; using Lilikoi.Context; +#endregion + namespace Lilikoi.Standard; public class SingletonAttribute : LkInjectionAttribute diff --git a/README.md b/README.md index 390d72b..9a55500 100644 --- a/README.md +++ b/README.md @@ -3,31 +3,32 @@

    Lilikoi

    +| Main | [![main](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml) | +|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Dev | [![dev](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml/badge.svg?branch=dev)](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml) | -| Main | [![main](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml) | -|--------| --- | -| Dev | [![dev](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml/badge.svg?branch=dev)](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml) | +> **Warning**: Lilikoi is in active, early development and should not be used unless you accept the +> risk that everything may change or break. -> **Warning**: Lilikoi is in active, early development and should not be used unless you accept the risk that everything may change or break. - -> **Note**: Lilikoi requires the .NET 7 SDK in order to be compiled, but can be used (and is tested on) on any .NET Standard 2.0 platform. +> **Note**: Lilikoi requires the .NET 7 SDK in order to be compiled, but can be used (and is tested +> on) on any .NET Standard 2.0 platform. ### Status | Feature | Status | -|---------------------|----| -| Field Injection | ✔️ | -| Parameter Injection | ️️✔️ | -| Hooks ("Wraps") | ✔️ | -| Contexts ("Mounts") | ✔️ | -| Builder Attributes | 🏗️ | -| Configuration | 🏗️ | -| Headless/Portable | 🏗️ | -| Wildcards | ✔️ | -| Ecosystem | ⏳ | -| Async/Await | ⏳ | -| Debugging | ⏳ | -| NuGet Release | 🏗️ | +|---------------------|--------| +| Field Injection | ✔️ | +| Parameter Injection | ️️✔️ | +| Hooks ("Wraps") | ✔️ | +| Contexts ("Mounts") | ✔️ | +| Builder Attributes | 🏗️ | +| Configuration | 🏗️ | +| Headless/Portable | 🏗️ | +| Wildcards | ✔️ | +| Ecosystem | ⏳ | +| Async/Await | ⏳ | +| Debugging | ⏳ | +| NuGet Release | 🏗️ | ### Documentation @@ -40,12 +41,14 @@ Documentation is a work in progress, but here's what we have so far: ### What is it? -Lilikoi is a *framework for frameworks*. +Lilikoi is a *framework for frameworks*. It defines a common ground for dependency injection and pre/post behavior. This allows programmers to write framework agnostic code that runs anywhere Lilikoi runs. -Lilikoi consists of "Containers", which contain a class ("Host") and a method defined on that class (the "Entry Point"). -Using C# reflection APIs, a method is created which injects values into an instance of the host class and then executes the entry point. +Lilikoi consists of "Containers", which contain a class ("Host") and a method defined on that +class (the "Entry Point"). +Using C# reflection APIs, a method is created which injects values into an instance of the host +class and then executes the entry point. ```cs public class SampleInjectionAttribute : MkTypedInjectionAttribute @@ -56,6 +59,7 @@ public class SampleInjectionAttribute : MkTypedInjectionAttribute } } ``` + ```cs class Program { @@ -72,18 +76,20 @@ class Program ### Headless -Like injecting things but don't want a full framework? +Like injecting things but don't want a full framework? Lilikoi's headless injectors build minimal `Action`s which behave similarly to the full framework, giving you full control over the when and where. ### Performance -Lilikoi is designed with performance in mind, so that projects of any scale can benefit from it's paradigms. +Lilikoi is designed with performance in mind, so that projects of any scale can benefit from it's +paradigms. -In order to maximize performance and prevent diving into .NET reflection, Lilikoi uses **compiled expression trees**, +In order to maximize performance and prevent diving into .NET reflection, Lilikoi uses **compiled +expression trees**, which behave just like a normal method. -Expression trees are no golden ticket to performanceville, +Expression trees are no golden ticket to performanceville, but proper runtime code generation makes Lilikoi's overhead as low as **40ns** per injection | Framework | Task | Speed | @@ -98,8 +104,8 @@ but proper runtime code generation makes Lilikoi's overhead as low as **40ns** p ### What could finished Lilikoi look like? Lilikoi could be used in any framework which heavily uses event-based programming, -such as games (or game mods!), discord bots, HTTP servers (ASP.NET), -RPC servers, automation tools, and more. +such as games (or game mods!), discord bots, HTTP servers (ASP.NET), +RPC servers, automation tools, and more. A finished version of Lilikoi could look like this from the standpoint of an application developer: From d8cb201e9ff0bad8a6fe595ad52c5c9d747576ae Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Sat, 29 Jul 2023 14:13:11 -0700 Subject: [PATCH 07/17] Add more verbose scan options * Minor refactors * Add parameter count and return type to the mutator's context --- Lilikoi/Compiler/Mahogany/MahoganyMethod.cs | 20 +++++++- Lilikoi/Compiler/Public/LilikoiMethod.cs | 13 +----- Lilikoi/Compiler/Public/LilikoiMutator.cs | 14 +++++- Lilikoi/Scan/Scanner.cs | 51 +++++++++++++++++++-- 4 files changed, 79 insertions(+), 19 deletions(-) diff --git a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs index 6f1d661..3d011eb 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::MahoganyMethod.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -19,6 +19,22 @@ namespace Lilikoi.Compiler.Mahogany; public class MahoganyMethod { + public MahoganyMethod(MethodInfo method) + { + Parameters = method.GetParameters().Select(x => x.ParameterType).ToList(); + Return = method.ReturnType; + HaltTarget = Expression.Label(method.ReturnType, "Halt"); + Entry = method; + Host = method.DeclaringType; + NamedVariables = new Dictionary() + { + { MahoganyConstants.HOST_VAR, Expression.Parameter(method.DeclaringType, MahoganyConstants.HOST_VAR) } + }; + SymbolDocument = Expression.SymbolDocument("Lk__Autogenerated.cs"); + } + + public SymbolDocumentInfo SymbolDocument { get; } + public List Temporaries = new(); public LabelTarget HaltTarget { get; set; } @@ -132,4 +148,4 @@ public LambdaExpression Lambda() public Type Result { get; set; } #endregion -} \ No newline at end of file +} diff --git a/Lilikoi/Compiler/Public/LilikoiMethod.cs b/Lilikoi/Compiler/Public/LilikoiMethod.cs index 60e5531..e5ffd7d 100644 --- a/Lilikoi/Compiler/Public/LilikoiMethod.cs +++ b/Lilikoi/Compiler/Public/LilikoiMethod.cs @@ -25,18 +25,7 @@ public static LilikoiMethod FromMethodInfo(MethodInfo method) { return new LilikoiMethod() { - Implementation = new MahoganyMethod() - { - Parameters = method.GetParameters().Select(x => x.ParameterType).ToList(), - Return = method.ReturnType, - HaltTarget = Expression.Label(method.ReturnType, "Halt"), - Entry = method, - Host = method.DeclaringType, - NamedVariables = new Dictionary() - { - { MahoganyConstants.HOST_VAR, Expression.Parameter(method.DeclaringType, MahoganyConstants.HOST_VAR) } - } - } + Implementation = new MahoganyMethod(method) }; } diff --git a/Lilikoi/Compiler/Public/LilikoiMutator.cs b/Lilikoi/Compiler/Public/LilikoiMutator.cs index 512e2b2..a81cbd8 100644 --- a/Lilikoi/Compiler/Public/LilikoiMutator.cs +++ b/Lilikoi/Compiler/Public/LilikoiMutator.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::LilikoiMutator.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 31.01.2023 // -> Bumped: 06.02.2023 // ======================== @@ -94,4 +94,14 @@ public LilikoiMutator Wildcard(TParameter value = null) return parameters[paramNum]; } -} \ No newline at end of file + + /// + /// Get the number of parameters on the host function + /// + public int Paramters => Compiler.Internal.Method.Parameters.Count; + + /// + /// The return type of the underlying function + /// + public Type Result => Compiler.Internal.Method.Return; +} diff --git a/Lilikoi/Scan/Scanner.cs b/Lilikoi/Scan/Scanner.cs index f29ee52..ce2efe9 100644 --- a/Lilikoi/Scan/Scanner.cs +++ b/Lilikoi/Scan/Scanner.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::Scanner.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 31.01.2023 // -> Bumped: 06.02.2023 // ======================== @@ -53,7 +53,53 @@ public static List Scan(TUserCo List containers = new(); foreach (var type in assembly.GetTypes()) + containers.AddRange(Scan(context, type, sourceMount)); + + return containers; + } + + /// + /// Scan the provided generic type for Lilikoi Containers. + /// Use the last parameter to supply a mount for each container. + /// + /// + /// + /// + /// + /// + /// + /// + public static List Scan(TUserContext context, Func sourceMount) + where TUserContext : Mount + => Scan(context, typeof(TType), sourceMount); + + public static List Scan(TUserContext context, Type type, Func sourceMount) + where TUserContext : Mount + { + List containers = new(); + foreach (var methodInfo in type.GetMethods()) + containers.AddRange(Scan(context, methodInfo, sourceMount)); + + return containers; + } + + /// + /// Scan an individual method for lilikoi containers. + /// Note that if there are multiple target attributes then there can be multiple containers returned. + /// + /// + /// + /// + /// + /// + /// + /// + public static List Scan(TUserContext context, MethodInfo methodInfo, Func sourceMount) + where TUserContext : Mount + { + List containers = new(); + foreach (LkTargetBuilderAttribute attribute in methodInfo.GetCustomAttributes() .Where(attribute => attribute .GetType() @@ -75,7 +121,6 @@ public static List Scan(TUserCo containers.Add(compiler.Finish()); } - return containers; } -} \ No newline at end of file +} From 88f585fb7e601454a5d9c009f7b99bb1e5304a93 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Sat, 29 Jul 2023 14:54:09 -0700 Subject: [PATCH 08/17] Fix spelling and factory namespaces --- Lilikoi/Compiler/Public/LilikoiMutator.cs | 9 ++++----- Lilikoi/Standard/Extensions/RegistrationExtensions.cs | 2 ++ Lilikoi/Standard/Factory/FactoryAttribute.cs | 9 +++++++-- Lilikoi/Standard/Factory/IFactory.cs | 6 +++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Lilikoi/Compiler/Public/LilikoiMutator.cs b/Lilikoi/Compiler/Public/LilikoiMutator.cs index a81cbd8..cd6b593 100644 --- a/Lilikoi/Compiler/Public/LilikoiMutator.cs +++ b/Lilikoi/Compiler/Public/LilikoiMutator.cs @@ -44,8 +44,7 @@ public LilikoiMutator Implicit(LkWrapBuilderAttribute value) public LilikoiMutator Implicit(TWrap value = null) where TWrap : LkWrapBuilderAttribute, new() { - if (value is null) - value = new TWrap(); + value ??= new TWrap(); Compiler.ImplicitWraps.Add(value); return this; @@ -68,12 +67,12 @@ public LilikoiMutator Wildcard(LkParameterBuilderAttribute value) ///
    /// /// + /// /// public LilikoiMutator Wildcard(TParameter value = null) where TParameter : LkParameterBuilderAttribute, new() { - if (value is null) - value = new TParameter(); + value ??= new TParameter(); Compiler.ImplicitWildcards.Add((value, typeof(TType))); return this; @@ -98,7 +97,7 @@ public LilikoiMutator Wildcard(TParameter value = null) /// /// Get the number of parameters on the host function /// - public int Paramters => Compiler.Internal.Method.Parameters.Count; + public int Parameters => Compiler.Internal.Method.Parameters.Count; /// /// The return type of the underlying function diff --git a/Lilikoi/Standard/Extensions/RegistrationExtensions.cs b/Lilikoi/Standard/Extensions/RegistrationExtensions.cs index 9af61d6..e8ad524 100644 --- a/Lilikoi/Standard/Extensions/RegistrationExtensions.cs +++ b/Lilikoi/Standard/Extensions/RegistrationExtensions.cs @@ -5,6 +5,8 @@ // -> Created: 01.02.2023 // -> Bumped: 06.02.2023 // ======================== +using Lilikoi.Standard.Factory; + namespace Lilikoi.Standard.Extensions; public static class RegistrationExtensions diff --git a/Lilikoi/Standard/Factory/FactoryAttribute.cs b/Lilikoi/Standard/Factory/FactoryAttribute.cs index a599864..8c9ef51 100644 --- a/Lilikoi/Standard/Factory/FactoryAttribute.cs +++ b/Lilikoi/Standard/Factory/FactoryAttribute.cs @@ -12,7 +12,7 @@ #endregion -namespace Lilikoi.Standard; +namespace Lilikoi.Standard.Factory; public class FactoryAttribute : LkInjectionAttribute { @@ -23,6 +23,11 @@ public override bool IsInjectable(Mount mount) public override TInjectable Inject(Mount context) { - return context.Get>().Create(); + var factory = context.Get>(); + + if (factory is null) + throw new InvalidOperationException($"No factory for type {typeof(TInjectable).FullName} exists."); + + return factory.Create(); } } diff --git a/Lilikoi/Standard/Factory/IFactory.cs b/Lilikoi/Standard/Factory/IFactory.cs index 3d1ee44..fa0fa65 100644 --- a/Lilikoi/Standard/Factory/IFactory.cs +++ b/Lilikoi/Standard/Factory/IFactory.cs @@ -1,13 +1,13 @@ // ======================== // Lilikoi::IFactory.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 01.02.2023 // -> Bumped: 06.02.2023 // ======================== -namespace Lilikoi.Standard; +namespace Lilikoi.Standard.Factory; public interface IFactory { public TProduct Create(); -} \ No newline at end of file +} From f4e1cfbdc69a187b7853ab83d1d1eaf214212aa4 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Thu, 10 Aug 2023 18:57:44 -0700 Subject: [PATCH 09/17] Completely disable specifying host type and implementation. Hosts should be in a completely undefined state after being used by the container, so having this extra functionality really doesn't make sense. This is a pretty big breaking change throughout the API. --- .../InjectSimple/SimpleInjectHost.cs | 8 ++--- .../Mahogany/CompileBenchmarks.cs | 6 ++-- Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs | 9 +++-- Lilikoi.Tests/HelloWorld/HelloWorldHost.cs | 10 ++++-- Lilikoi.Tests/HelloWorld/HelloWorldTest.cs | 6 ++-- .../AllMethodsCalled/AllMethodsCalledTest.cs | 6 ++-- .../Injections/UnaccessibleProperties.cs | 6 ++-- .../Mutator/Wildcards/WildcardTest.cs | 6 ++-- Lilikoi.Tests/Wraps/SelfInject.cs | 6 ++-- Lilikoi.Tests/Wraps/WrapTests.cs | 12 +++---- Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs | 10 ++++-- Lilikoi/Compiler/Mahogany/MahoganyMethod.cs | 4 +-- .../Mahogany/Steps/MahoganyCreateHostStep.cs | 33 +++++++++++++++++++ Lilikoi/Compiler/Public/LilikoiCompiler.cs | 6 ++-- Lilikoi/Compiler/Public/LilikoiContainer.cs | 14 ++++---- Lilikoi/Lilikoi.csproj | 6 ++-- 16 files changed, 96 insertions(+), 52 deletions(-) create mode 100644 Lilikoi/Compiler/Mahogany/Steps/MahoganyCreateHostStep.cs diff --git a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs index 0bdbe09..fcec056 100644 --- a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs +++ b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Benchmarks::SimpleInjectHost.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -22,13 +22,13 @@ public bool Execute() return Injected.Execute(); } - public static Func Build() + public static Func Build() { return LilikoiMethod.FromMethodInfo(typeof(SimpleInjectHost).GetMethod(nameof(Execute))) .Input() .Output() .Build() .Finish() - .Compile(); + .Compile(); } -} \ No newline at end of file +} diff --git a/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs b/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs index 3651564..e28b069 100644 --- a/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs +++ b/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Benchmarks::CompileBenchmarks.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -23,8 +23,8 @@ namespace Lilikoi.Benchmarks.Mahogany; public class CompileBenchmarks { [Benchmark()] - public Func Simple() + public Func Simple() { return SimpleInjectHost.Build(); } -} \ No newline at end of file +} diff --git a/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs b/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs index 7268a78..ef15487 100644 --- a/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs +++ b/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Benchmarks::RunBenchmarks.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -25,8 +25,7 @@ namespace Lilikoi.Benchmarks.Mahogany; [MemoryDiagnoser(true)] public class RunBenchmarks { - public Func SimpleContainer; - public SimpleInjectHost SimpleHost = new(); + public Func SimpleContainer; [GlobalSetup] public void Setup() @@ -37,6 +36,6 @@ public void Setup() [Benchmark()] public bool Simple() { - return SimpleContainer(SimpleHost, true); + return SimpleContainer(true); } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs b/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs index 0f056d8..36b9da7 100644 --- a/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs +++ b/Lilikoi.Tests/HelloWorld/HelloWorldHost.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::HelloWorldHost.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -11,7 +11,7 @@ namespace Lilikoi.Tests.HelloWorld; -public class HelloWorldHost +public class HelloWorldHost : IDisposable { [Console] public ConsoleInj ConsoleImpl; @@ -21,4 +21,8 @@ public object HelloWorld() return ConsoleImpl; } -} \ No newline at end of file + + public void Dispose() + { + } +} diff --git a/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs b/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs index 0074baa..e9a8039 100644 --- a/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs +++ b/Lilikoi.Tests/HelloWorld/HelloWorldTest.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::HelloWorldTest.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -34,6 +34,6 @@ public async Task HelloWorld() Console.WriteLine(build.ToString()); - build.Run(new HelloWorldHost(), new object()); + build.Run(new object()); } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs index 01c62d6..3d5720e 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledTest.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -36,7 +36,7 @@ public void AllMethodsCalled() Console.WriteLine(build.ToString()); - build.Run(new AllMethodsCalledHost(), Instance); + build.Run(Instance); Assert.IsTrue(Instance.InjectCalled, "Injection was not invoked"); @@ -53,4 +53,4 @@ public class AllMethodsCalledCounter public bool InjectCalled = false; public bool ParameterCalled = false; } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/Injections/UnaccessibleProperties.cs b/Lilikoi.Tests/Injections/UnaccessibleProperties.cs index e583ec2..60d642f 100644 --- a/Lilikoi.Tests/Injections/UnaccessibleProperties.cs +++ b/Lilikoi.Tests/Injections/UnaccessibleProperties.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::UnaccessibleProperties.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 24.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -33,7 +33,7 @@ public void UnaccessablePropertiesStillInject() Console.WriteLine(build.ToString()); - build.Run(new Host(), "Input"); + build.Run( "Input"); Assert.Fail("Entry point not executed"); } @@ -58,4 +58,4 @@ public string Entry() return "Entry"; } } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs b/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs index 60805c2..003962e 100644 --- a/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs +++ b/Lilikoi.Tests/Mutator/Wildcards/WildcardTest.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::WildcardTest.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 02.02.2023 // -> Bumped: 06.02.2023 // ======================== @@ -33,7 +33,7 @@ public void CanInjectWildcard() .Finish(); var host = new WildcardHost(); - var value = container.Run(host, host); + var value = container.Run(host); Assert.AreEqual(WILDCARD_VALUE, value); } @@ -62,4 +62,4 @@ public string Entry(string thing) return thing; } } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/Wraps/SelfInject.cs b/Lilikoi.Tests/Wraps/SelfInject.cs index aba383b..e83bca6 100644 --- a/Lilikoi.Tests/Wraps/SelfInject.cs +++ b/Lilikoi.Tests/Wraps/SelfInject.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::SelfInject.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 24.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -34,7 +34,7 @@ public void BasicSelfInject() Console.WriteLine(build.ToString()); - var output = build.Run(new Host(), new object()); + var output = build.Run( new object()); Assert.Fail("Did not evaluate Assert.Pass() in WrapWithInjectionAttribute."); } @@ -85,4 +85,4 @@ public string Entry() return "Entry"; } } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/Wraps/WrapTests.cs b/Lilikoi.Tests/Wraps/WrapTests.cs index d50a2b1..6b8c03f 100644 --- a/Lilikoi.Tests/Wraps/WrapTests.cs +++ b/Lilikoi.Tests/Wraps/WrapTests.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::WrapTests.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 24.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -31,7 +31,7 @@ public void Halts() Console.WriteLine(build.ToString()); - var output = build.Run(new DummyHost(), new object()); + var output = build.Run(new object()); Assert.AreEqual("Before", output); } @@ -50,7 +50,7 @@ public void Continues() Console.WriteLine(build.ToString()); - var output = build.Run(new DummyHost(), new object()); + var output = build.Run( new object()); Assert.Fail("Reached exit point without passing"); } @@ -69,7 +69,7 @@ public void Modifies() Console.WriteLine(build.ToString()); - var output = build.Run(new DummyHost(), new object()); + var output = build.Run< object, string>(new object()); Assert.AreEqual("After", output); } @@ -88,7 +88,7 @@ public void ModifiesInput() Console.WriteLine(build.ToString()); - var output = build.Run(new DummyHost(), "Input"); + var output = build.Run("Input"); Assert.Fail("Reached exit point without passing"); } @@ -123,4 +123,4 @@ public string ShouldModifyInput(string input) return "Entry"; } } -} \ No newline at end of file +} diff --git a/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs b/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs index b14908d..fcaed3c 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyCompiler.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::MahoganyCompiler.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -231,6 +231,12 @@ public void ParametersFor() } } + public void HostFor() + { + var step = new MahoganyCreateHostStep(Method); + Stack.Push(step.Generate(), Expression.Empty()); + } + public void ParameterSafety() { Method.Append( @@ -251,4 +257,4 @@ public void Apex() } #endregion -} \ No newline at end of file +} diff --git a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs index 3d011eb..1403932 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs @@ -90,13 +90,13 @@ public LambdaExpression Lambda() var func = typeof(Func<,,>).MakeGenericType(Host, Input, Result); var internalVariables = new[] { - //Named(MahoganyConstants.HOST_VAR), Named(MahoganyConstants.INPUT_VAR), + // Named(MahoganyConstants.INPUT_VAR), + Named(MahoganyConstants.HOST_VAR), Named(MahoganyConstants.OUTPUT_VAR) }; var parameters = new[] { - Named(MahoganyConstants.HOST_VAR), Named(MahoganyConstants.INPUT_VAR) }; diff --git a/Lilikoi/Compiler/Mahogany/Steps/MahoganyCreateHostStep.cs b/Lilikoi/Compiler/Mahogany/Steps/MahoganyCreateHostStep.cs new file mode 100644 index 0000000..c8fb8a2 --- /dev/null +++ b/Lilikoi/Compiler/Mahogany/Steps/MahoganyCreateHostStep.cs @@ -0,0 +1,33 @@ +// ======================== +// Lilikoi::MahoganyCreateHostStep.cs +// (c) 2023. Distributed under the MIT License +// +// -> Created: 10.08.2023 +// -> Bumped: 10.08.2023 +// ======================== +using System.Linq.Expressions; +using System.Reflection; + +using Lilikoi.Attributes.Builders; + +namespace Lilikoi.Compiler.Mahogany.Steps; + +public class MahoganyCreateHostStep +{ + public MahoganyCreateHostStep(MahoganyMethod method) + { + Method = method; + } + + public MahoganyMethod Method { get; set; } + + public Expression Generate() + { + var host = Method.Named(MahoganyConstants.HOST_VAR); + + var creation = Expression.New(Method.Host); + var assignment = Expression.Assign(host, creation); + + return assignment; + } +} diff --git a/Lilikoi/Compiler/Public/LilikoiCompiler.cs b/Lilikoi/Compiler/Public/LilikoiCompiler.cs index 0fbd6f8..64de61c 100644 --- a/Lilikoi/Compiler/Public/LilikoiCompiler.cs +++ b/Lilikoi/Compiler/Public/LilikoiCompiler.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::LilikoiCompiler.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -42,7 +42,9 @@ public LilikoiContainer Finish() { Mutators(); + Internal.HostFor(); Internal.ParameterSafety(); + Internal.InjectionsFor(Internal.Method.Host); foreach (var implicitWrap in ImplicitWraps) @@ -58,4 +60,4 @@ public LilikoiContainer Finish() return new LilikoiContainer(Smuggler, Internal.Method.Lambda()); } -} \ No newline at end of file +} diff --git a/Lilikoi/Compiler/Public/LilikoiContainer.cs b/Lilikoi/Compiler/Public/LilikoiContainer.cs index 4b39a83..5deb99a 100644 --- a/Lilikoi/Compiler/Public/LilikoiContainer.cs +++ b/Lilikoi/Compiler/Public/LilikoiContainer.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::LilikoiContainer.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -26,16 +26,16 @@ internal LilikoiContainer(Mount self, LambdaExpression body) : base(self) private Delegate Memoized { get; set; } - public TOut Run(THost host, TIn input) + public TOut Run(TIn input) { if (Memoized is null) - Memoized = Compile(); + Memoized = Compile(); - return (Memoized as Func)(host, input); + return (Memoized as Func)(input); } - public Func Compile() + public Func Compile() { - return Body.Compile(false) as Func; + return Body.Compile(false) as Func; } -} \ No newline at end of file +} diff --git a/Lilikoi/Lilikoi.csproj b/Lilikoi/Lilikoi.csproj index 4f06aed..3bb4cc6 100644 --- a/Lilikoi/Lilikoi.csproj +++ b/Lilikoi/Lilikoi.csproj @@ -43,9 +43,9 @@ - - - + + + From 558e4b9282952d1b4171f4f6d71f88a199f20a58 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Thu, 10 Aug 2023 19:02:41 -0700 Subject: [PATCH 10/17] Upgrade test frameworks --- Lilikoi.Tests/Lilikoi.Tests.csproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lilikoi.Tests/Lilikoi.Tests.csproj b/Lilikoi.Tests/Lilikoi.Tests.csproj index 125f6a1..87cb834 100644 --- a/Lilikoi.Tests/Lilikoi.Tests.csproj +++ b/Lilikoi.Tests/Lilikoi.Tests.csproj @@ -12,22 +12,22 @@ - - - - - - + + + + + + - + - + From e142f1688cdd8a0a5370015369bb4fcbbd22216b Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Thu, 10 Aug 2023 19:07:56 -0700 Subject: [PATCH 11/17] Dont have the willpower to deal with this; just drop testing support for .NET framework altogether. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8a8144a..c3193d2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - framework-version: [ 'net47', 'net48', 'net6.0', 'net7.0' ] + framework-version: [ 'net6.0', 'net7.0' ] steps: From c1ec902d8a40a1a9c61bfb36bd1714741110743f Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Thu, 10 Aug 2023 21:11:45 -0700 Subject: [PATCH 12/17] Introduce dynamic wildcard APIs for manually specifying type --- Lilikoi/Compiler/Public/LilikoiMutator.cs | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Lilikoi/Compiler/Public/LilikoiMutator.cs b/Lilikoi/Compiler/Public/LilikoiMutator.cs index cd6b593..1da856f 100644 --- a/Lilikoi/Compiler/Public/LilikoiMutator.cs +++ b/Lilikoi/Compiler/Public/LilikoiMutator.cs @@ -61,6 +61,18 @@ public LilikoiMutator Wildcard(LkParameterBuilderAttribute value) return this; } + /// + /// Add a parameter wildcard to this container for the provided type + /// + /// + /// + /// + public LilikoiMutator Wildcard(LkParameterBuilderAttribute value, Type type) + { + Compiler.ImplicitWildcards.Add((value, type)); + return this; + } + /// /// Add a parameter wildcard to this container. /// If a null value is provided, the default constructor is used. @@ -78,6 +90,23 @@ public LilikoiMutator Wildcard(TParameter value = null) return this; } + /// + /// Add a parameter wildcard to this container for the specified type. + /// If a null value is provided, the default constructor is used. + /// + /// + /// + /// + /// + public LilikoiMutator Wildcard(Type type, TParameter value = null) + where TParameter : LkParameterBuilderAttribute, new() + { + value ??= new TParameter(); + + Compiler.ImplicitWildcards.Add((value, type)); + return this; + } + /// /// Get the parameter type of a function by the parameter number /// Used for type routing From f29fbefb9d5533abce3186681f0d3488478fb89f Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Sat, 12 Aug 2023 15:00:19 -0700 Subject: [PATCH 13/17] Enable dynamic casting of lambda result within container --- .../Injections/AllMethodsCalled/AllMethodsCalledHost.cs | 8 ++++---- .../Injections/AllMethodsCalled/AllMethodsCalledTest.cs | 3 ++- Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs | 7 ++++--- Lilikoi/Compiler/Mahogany/MahoganyMethod.cs | 4 ++-- Lilikoi/Compiler/Public/LilikoiMethod.cs | 2 ++ 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs index c6e0a96..3d25494 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledHost.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Tests::AllMethodsCalledHost.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -11,7 +11,7 @@ public class AllMethodsCalledHost { [AllMethodsCalled] public AllMethodsCalledInject Inject; - public object Entry(AllMethodsCalledTest.AllMethodsCalledCounter test, [AllMethodsCalledParameter] object param) + public string Entry(AllMethodsCalledTest.AllMethodsCalledCounter test, [AllMethodsCalledParameter] object param) { test.EntryCalled = true; @@ -20,6 +20,6 @@ public object Entry(AllMethodsCalledTest.AllMethodsCalledCounter test, [AllMetho Assert.IsNotNull(Inject); Assert.IsTrue(Inject.IsNotNull()); - return new object(); + return "Okay!"; } -} \ No newline at end of file +} diff --git a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs index 3d5720e..c60c790 100644 --- a/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs +++ b/Lilikoi.Tests/Injections/AllMethodsCalled/AllMethodsCalledTest.cs @@ -36,8 +36,9 @@ public void AllMethodsCalled() Console.WriteLine(build.ToString()); - build.Run(Instance); + var value = build.Run(Instance); + Assert.NotNull(value); Assert.IsTrue(Instance.InjectCalled, "Injection was not invoked"); Assert.IsTrue(Instance.EntryCalled, "Entry was not invoked"); diff --git a/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs b/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs index c241051..ae8905a 100644 --- a/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs +++ b/Lilikoi/Compiler/Mahogany/Generator/WrapGenerator.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::WrapGenerator.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -68,7 +68,8 @@ internal static Expression Before(MahoganyMethod method, Expression attribute) setter, Expression.IfThen( Expression.IsTrue(Expression.Field(result, WRAPRESULT_STOP)), - Expression.Return(method.HaltTarget, Expression.Field(result, WRAPRESULT_VALUE)) + Expression.Return(method.HaltTarget, + Expression.TypeAs( Expression.Field(result, WRAPRESULT_VALUE), method.Return) ) )); return Expression.Block( @@ -99,4 +100,4 @@ internal static Expression After(MahoganyMethod method, Expression attribute) { return After(attribute, method.Named(MahoganyConstants.MOUNT_VAR), method.Named(MahoganyConstants.OUTPUT_VAR), method.Result); } -} \ No newline at end of file +} diff --git a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs index 1403932..1096840 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyMethod.cs @@ -105,8 +105,8 @@ public LambdaExpression Lambda() var lambdaBody = Expression.Block( internalVariables, Expression.Block(Temporaries.ToArray(), internalBody), - Expression.Return(HaltTarget, Named(MahoganyConstants.OUTPUT_VAR)), - Expression.Label(HaltTarget, Named(MahoganyConstants.OUTPUT_VAR)) + Expression.Return(HaltTarget, Expression.TypeAs( Named(MahoganyConstants.OUTPUT_VAR), Return ) ), + Expression.Label(HaltTarget, Expression.TypeAs( Named(MahoganyConstants.OUTPUT_VAR), Return ) ) ); return Expression.Lambda(lambdaBody, "LilikoiContainer", parameters); diff --git a/Lilikoi/Compiler/Public/LilikoiMethod.cs b/Lilikoi/Compiler/Public/LilikoiMethod.cs index e5ffd7d..ea690f4 100644 --- a/Lilikoi/Compiler/Public/LilikoiMethod.cs +++ b/Lilikoi/Compiler/Public/LilikoiMethod.cs @@ -48,6 +48,8 @@ public LilikoiMethod Output() Implementation.Result = typeof(TOutput); Implementation.NamedVariables.Add(MahoganyConstants.OUTPUT_VAR, Expression.Parameter(typeof(TOutput), MahoganyConstants.OUTPUT_VAR)); + if (!Implementation.Result.IsAssignableFrom(Implementation.Return)) + throw new InvalidCastException($"Cannot cast to .Output() result of {typeof(TOutput).FullName} from container host return of {Implementation.Return.FullName}"); return this; } From 66357d546ad59042427252db0fe826e3feeec0b2 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Sun, 13 Aug 2023 13:29:19 -0700 Subject: [PATCH 14/17] Change wrap validation behavior to use inner container return instead of container result --- Lilikoi/Compiler/Mahogany/MahoganyValidator.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs b/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs index 51fac0e..51c6a7f 100644 --- a/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs +++ b/Lilikoi/Compiler/Mahogany/MahoganyValidator.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::MahoganyValidator.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 23.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -62,7 +62,10 @@ internal static bool ValidWrap(LkWrapBuilderAttribute attribute, Type input, Typ internal static void ValidateWrap(LkWrapBuilderAttribute attribute, MahoganyMethod method) { - if (!ValidWrap(attribute, method.Input, method.Result, method.Mount)) - throw new InvalidCastException($"Wrap '{attribute.GetType().FullName}'" + $"rejected input-output pair on method '{method.Entry.Name}'"); + if (!ValidWrap(attribute, method.Input, method.Return, method.Mount)) + throw new InvalidCastException($"Wrap '{attribute.GetType().FullName}' " + + $"rejected input-output pair on method '{method.Entry.Name}' " + + $"with input of {method.Input.FullName} " + + $"and inner container output of {method.Return.FullName} "); } -} \ No newline at end of file +} From dadfd4f667a2e548867400ad7a81497cabca5404 Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Sun, 13 Aug 2023 14:05:49 -0700 Subject: [PATCH 15/17] Expand mount API, improve docs --- Docs/attributes.md | 5 +- Docs/containers.md | 1 + Docs/mounts.md | 17 +++ Docs/overview.md | 8 +- .../Applications/InjectSimple/Simple.cs | 8 +- .../InjectSimple/SimpleInjectHost.cs | 12 +- .../Mahogany/CompileBenchmarks.cs | 2 +- Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs | 11 +- Lilikoi/Collection/TypeDictionary.cs | 24 +++- Lilikoi/Context/Mount.cs | 54 ++++++-- README.md | 120 +++++++++--------- 11 files changed, 169 insertions(+), 93 deletions(-) diff --git a/Docs/attributes.md b/Docs/attributes.md index eb13072..f058614 100644 --- a/Docs/attributes.md +++ b/Docs/attributes.md @@ -152,8 +152,9 @@ The presence of a builder attribute should do two things: Wraps allow you to execute before or after the entry point which modifies the input or output, or prevents the entry point from running altogether. -> **Warning**: Wraps *do not* support property injection by default. -> +> [!IMPORTANT] +> Wraps *do not* support property injection by default. +> > You can use `LilikoiInjector.Inject(mount, this)` and `LilikoiInjector.Deject(mount, this)` to emulate standard injection in the before and after methods, respectively. > > (If you do this, it is **highly** recommended to call `Deject` at the end of your `After` function as the injections may rely on this to prevent leaks.) diff --git a/Docs/containers.md b/Docs/containers.md index a72062e..9aa2eac 100644 --- a/Docs/containers.md +++ b/Docs/containers.md @@ -44,3 +44,4 @@ so that users and libraries have ways to hurl opaque data around the entry point 4. Entry point 5. Wrap after execution 7. Dejection of host +8. Casting of entry return and wrap return into container result diff --git a/Docs/mounts.md b/Docs/mounts.md index f5b312a..71e122f 100644 --- a/Docs/mounts.md +++ b/Docs/mounts.md @@ -15,3 +15,20 @@ The only requirement is that each entry into the mount have a unique type. Additionally, several Lilikoi Types expose a mount interface. You can add entries to a `LilikoiMutator`'s mount and see those entries in the finalized `LilikoiContainer`'s mount. + +For example, a target attribute can place metadata (or even itself!) into the LilikoiMutator +for the creator of the Container to consume for metadata about the target. This functionality +is used frequently in [BitMod](https://github.com/Mooshua/BitMod) + +## Performance + +Mounts are slower than normal dictionaries, but not by much. +A mount typically takes 30-50ns to look up a single object, regardless of +how many objects are in the mount. + +Writing and checking for existance is typically +in the 20ns range as it does not need to unbox anything. + +Performance for mounts is not really a big deal, as they are usually read only once +or twice by a consuming program, compared to normal dictionaries which are constantly +enumerated and updated. diff --git a/Docs/overview.md b/Docs/overview.md index a9673fd..bd70d61 100644 --- a/Docs/overview.md +++ b/Docs/overview.md @@ -3,7 +3,7 @@ Lilikoi is designed to replace expensive reflection logic and deliver a consistent API surface from the perspective of both framework and framework consumer. -Lilikoi provides APIs for frameworks to define declarative helpers, such as parameter injection +Lilikoi provides APIs for event producers to define declarative helpers, such as parameter injection and before/after hooks (called wraps). Additionally, users can specify arbitrary types in the parameters of their entry point, and Lilikoi will resolve those parameters to a wildcard injection if one is available. @@ -26,7 +26,7 @@ scaffolded to replace repetitive imperative logic. - **Parameter** injection is for values that are *expected* to change both value and behavior between invocations, depending on the input value for the container. - **Wildcards** are similar to parameter injection, but are detected by the parameter type and not an attribute. - They have no explicit usage + They have no explicit usage, and can be assigned by the target and mutator attributes. ## Other Attributes @@ -38,6 +38,10 @@ scaffolded to replace repetitive imperative logic. Mutators can be used to add advanced functionality to containers, and even smuggle metadata from the compilation process to the final container object using the provided mount interface. +- **Targets** are used by the Scanner APIs to find containers within assemblies and types. + Targets are not required to make containers, but make the process of creating them + significantly easier and more consistent. + ## Headless Lilikoi has some APIs, called "Headless" APIs, which are designed to be used without the full framework. These headless APIs can be used to piecemeal implement Lilikoi behavior (or extend lilikoi injection behavior diff --git a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs index 6a00414..6b393e2 100644 --- a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs +++ b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi.Benchmarks::Simple.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -27,8 +27,8 @@ public static Simple Create() } [MethodImpl(MethodImplOptions.NoInlining)] - public bool Execute() + public object Execute() { - return 1 == 0; // Random.Shared.Next() == Value; + return "Hello, World!"; } -} \ No newline at end of file +} diff --git a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs index fcec056..8ca2b96 100644 --- a/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs +++ b/Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/SimpleInjectHost.cs @@ -8,6 +8,7 @@ #region using Lilikoi.Compiler.Public; +using Lilikoi.Context; #endregion @@ -17,18 +18,19 @@ public class SimpleInjectHost { [SimpleInjector] public Simple Injected; - public bool Execute() + public object Execute() { return Injected.Execute(); } - public static Func Build() + public static Func Build() { return LilikoiMethod.FromMethodInfo(typeof(SimpleInjectHost).GetMethod(nameof(Execute))) - .Input() - .Output() + .Mount(new Mount()) + .Input() + .Output() .Build() .Finish() - .Compile(); + .Compile(); } } diff --git a/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs b/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs index e28b069..23de187 100644 --- a/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs +++ b/Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs @@ -23,7 +23,7 @@ namespace Lilikoi.Benchmarks.Mahogany; public class CompileBenchmarks { [Benchmark()] - public Func Simple() + public Func Simple() { return SimpleInjectHost.Build(); } diff --git a/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs b/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs index ef15487..1288b43 100644 --- a/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs +++ b/Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs @@ -8,6 +8,7 @@ #region using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple; @@ -15,7 +16,9 @@ namespace Lilikoi.Benchmarks.Mahogany; -[SimpleJob()] +[SimpleJob(RuntimeMoniker.Net48)] +[SimpleJob(RuntimeMoniker.Net60)] +[SimpleJob(RuntimeMoniker.Net70)] [Q1Column] [MeanColumn] [MedianColumn] @@ -25,7 +28,7 @@ namespace Lilikoi.Benchmarks.Mahogany; [MemoryDiagnoser(true)] public class RunBenchmarks { - public Func SimpleContainer; + public Func SimpleContainer; [GlobalSetup] public void Setup() @@ -34,8 +37,8 @@ public void Setup() } [Benchmark()] - public bool Simple() + public object Simple() { - return SimpleContainer(true); + return SimpleContainer("Hello?"); } } diff --git a/Lilikoi/Collection/TypeDictionary.cs b/Lilikoi/Collection/TypeDictionary.cs index a9daac9..c889a6c 100644 --- a/Lilikoi/Collection/TypeDictionary.cs +++ b/Lilikoi/Collection/TypeDictionary.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::TypeDictionary.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -39,9 +39,10 @@ public TypeDictionary() } public bool Has() - { - return _underlying.ContainsKey(typeof(TValue)); - } + => Has(typeof(TValue)); + + public bool Has(Type t) + => _underlying.ContainsKey(t); public void Set(TValue obj) { @@ -51,6 +52,19 @@ public void Set(TValue obj) _underlying[typeof(TValue)] = obj; } + public TBase? Super(Type super) + where TBase: class + { + if (!typeof(TBase).IsAssignableFrom(super)) + return null; + + if (!_underlying.ContainsKey(super)) + return null; + + return _underlying[super] as TBase; + } + + public void Lock(out Padlock.Key key) { mutable.Lock(out key); @@ -60,4 +74,4 @@ public void Unlock(Padlock.Key key) { mutable.Unlock(key); } -} \ No newline at end of file +} diff --git a/Lilikoi/Context/Mount.cs b/Lilikoi/Context/Mount.cs index 21dcd8b..37a452b 100644 --- a/Lilikoi/Context/Mount.cs +++ b/Lilikoi/Context/Mount.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::Mount.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 22.12.2022 // -> Bumped: 06.02.2023 // ======================== @@ -26,21 +26,53 @@ public Mount(Mount other) dictionary = other.dictionary; } + /// + /// Store a single object in this mount. + /// The location where it is stored depends on the + /// generic parameter passed + /// + /// + /// public virtual void Store(T value) where T : class - { - dictionary.Set(value); - } + => dictionary.Set(value); + + /// + /// Get a single object, based on the generic parameter + /// provided. + /// + /// + /// public virtual T? Get() where T : class + => dictionary.Get(); - { - return dictionary.Get(); - } + /// + /// Get a subclass of a generic specified + /// by the passed type. + /// + /// + /// + /// + public virtual T? Super(Type super) where T : class + => dictionary.Super(super); + + /// + /// Check if an object exists in the mount + /// + /// + /// public virtual bool Has() - { - return dictionary.Has(); - } -} \ No newline at end of file + => dictionary.Has(); + + /// + /// Check if an object exists in the mount + /// + /// + /// + public virtual bool Has(Type t) + => dictionary.Has(t); + +} diff --git a/README.md b/README.md index 9a55500..9e0962e 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,12 @@ |------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| | Dev | [![dev](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml/badge.svg?branch=dev)](https://github.com/Mooshua/Lilikoi/actions/workflows/tests.yml) | -> **Warning**: Lilikoi is in active, early development and should not be used unless you accept the +> [!WARNING] +> Lilikoi is in active, early development and should not be used unless you accept the > risk that everything may change or break. -> **Note**: Lilikoi requires the .NET 7 SDK in order to be compiled, but can be used (and is tested +> [!NOTE] +> Lilikoi requires the .NET 7 SDK in order to be compiled, but can be used (and is tested > on) on any .NET Standard 2.0 platform. ### Status @@ -21,14 +23,14 @@ | Parameter Injection | ️️✔️ | | Hooks ("Wraps") | ✔️ | | Contexts ("Mounts") | ✔️ | -| Builder Attributes | 🏗️ | +| Builder Attributes | ✔️ | | Configuration | 🏗️ | | Headless/Portable | 🏗️ | | Wildcards | ✔️ | | Ecosystem | ⏳ | | Async/Await | ⏳ | | Debugging | ⏳ | -| NuGet Release | 🏗️ | +| NuGet Release | ✔️ | ### Documentation @@ -41,7 +43,7 @@ Documentation is a work in progress, but here's what we have so far: ### What is it? -Lilikoi is a *framework for frameworks*. +Lilikoi is a *framework for event handlers*. It defines a common ground for dependency injection and pre/post behavior. This allows programmers to write framework agnostic code that runs anywhere Lilikoi runs. @@ -51,7 +53,8 @@ Using C# reflection APIs, a method is created which injects values into an insta class and then executes the entry point. ```cs -public class SampleInjectionAttribute : MkTypedInjectionAttribute +// An attribute which injects a new SampleClass into the container +public class SampleInjectionAttribute : LkTypedInjectionAttribute { public override SampleClass Inject() { @@ -61,19 +64,64 @@ public class SampleInjectionAttribute : MkTypedInjectionAttribute ``` ```cs -class Program +// The host of the container +class Host { [SampleInjection] - public SampleClass InjectedClass; + private SampleClass _injectedClass; - // Entry point + // The entry point of the container public Task Entry() { - InjectedClass.DoSomething(); + _injectedClass.DoSomething(); } } ``` +### What is it good for? + +Lilikoi acts as a glue layer between event producers and event handlers. + +Lilikoi automates discovery of event handlers and creating an API surface +that an event producer can easily consume. Lilikoi also generates the API surface +expected by the handler at runtime--so both sides of the event handler can use the contracts +they expect. + +Lilikoi is used in my project [BitMod](https://github.com/Mooshua/BitMod), where it +glues plugins together with event handlers: + +```cs +public class WhitelistHooks +{ + [Config("whitelist")] + private WhitelistFile _whitelist; + + [BitHook(Priority.LOW)] + private async Task ServerConnectionRequest(GameServerConnectingEventArgs ev) + { + foreach (IPAddress allowedConnection in _whitelist.Parse(_logger)) + { + if (allowedConnection.GetAddressBytes().SequenceEqual(ev.IPAddress.GetAddressBytes())) + return Directive.Allow; + } + return Directive.Neutral; + } +} +``` + +Other plugins and BitMod can then invoke these handlers with a gloriously simple API. +The `BitHook` type places a `RouterAssignments` object in the container during compilation, +which is consumed by BitMod to find containers that handle the same event and group them together. +All in all, it looks like this: + +```cs +public override async Task OnGameServerConnecting(IPAddress arg) + => _invoker.Hook(new GameServerConnectingEventArgs(arg), defaultValue: false); +``` + +Overall, Lilikoi can be used in almost all event-handler use cases to provide +consistent and understandable APIs to both the event producer and the consumer. + ### Headless Like injecting things but don't want a full framework? @@ -90,59 +138,13 @@ expression trees**, which behave just like a normal method. Expression trees are no golden ticket to performanceville, -but proper runtime code generation makes Lilikoi's overhead as low as **40ns** per injection +but proper runtime code generation makes Lilikoi's overhead as low as **60ns** per injection | Framework | Task | Speed | |:---------------|:------------------|----------| -| .NET CLR | Inject | 45 ns | +| .NET CLR | Inject | 60 ns | | .NET CLR | Inject *(Debug)* | 325 ns | -| .NET Framework | Inject | 65 ns | +| .NET Framework | Inject | 100 ns | | .NET CLR | Compile | 0.330 ms | | .NET CLR | Compile *(Debug)* | 0.015 ms | | .NET Framework | Compile | 0.460 ms | - -### What could finished Lilikoi look like? - -Lilikoi could be used in any framework which heavily uses event-based programming, -such as games (or game mods!), discord bots, HTTP servers (ASP.NET), -RPC servers, automation tools, and more. - -A finished version of Lilikoi could look like this from the standpoint of an application developer: - -```cs -[Controller("/users")] -public class ApiController -{ - /// DbContext is instantiated by a generic new() injector attribute - /// EF Core will take care of the rest. - [New] - public ApiContext Db; - - /// You can imagine anything to go here--Because anything can! - /// Lilikoi should be customizable to your heart's content. - /// No more arbitrary framework restrictions! - [ApiService] - public UserService Users; - - /// POSTAttribute is a "builder" attribute - /// with a method (similar to Inject()) that describes how to build the container to Lilikoi. - /// A HTTP server could use this to gather a list of routes and Lilikoi containers associated with them! - [POST("/message")] - /// BodyAttribute is a "wrap" attribute - /// which defines methods to be executed before or after the entry point (in this case, parsing the body) - [Body(BodyType.Json)] - /// You can define your own application code here to authenticate and authorize the users. - [Authenticate(UserType.Commenter)] - /// FromBodyAttribute is a "parameter inject" attribute - /// which accepts a context ("mount") from the wraps and builder attributes to provide additional parameters and abstractions. - public Response PostMessage([JsonBody] PostMessageRequest request) - { - var success = Users.NewMessage(request.ToMessage()); - - if (success) - return Response.Success(); - - return Response.Error(); - } -} -``` From 627a8086726580b1c3db9e29187fe72e77a738ed Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Sun, 13 Aug 2023 14:14:22 -0700 Subject: [PATCH 16/17] Contexts now use an IMount interface instead of the abstract --- .../Builders/LkTargetBuilderAttribute.cs | 6 +-- Lilikoi/Attributes/LkTargetAttribute.cs | 8 +-- Lilikoi/Context/IMount.cs | 54 +++++++++++++++++++ Lilikoi/Context/Mount.cs | 37 +++---------- Lilikoi/Scan/Scanner.cs | 10 ++-- 5 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 Lilikoi/Context/IMount.cs diff --git a/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs b/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs index 2c6e23e..ab3e918 100644 --- a/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs +++ b/Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::LkTargetBuilderAttribute.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 31.01.2023 // -> Bumped: 06.02.2023 // ======================== @@ -29,5 +29,5 @@ public abstract class LkTargetBuilderAttribute : Attribute /// /// public abstract bool IsTargetable() - where TUserContext : Mount; -} \ No newline at end of file + where TUserContext : IMount; +} diff --git a/Lilikoi/Attributes/LkTargetAttribute.cs b/Lilikoi/Attributes/LkTargetAttribute.cs index d90c795..575d000 100644 --- a/Lilikoi/Attributes/LkTargetAttribute.cs +++ b/Lilikoi/Attributes/LkTargetAttribute.cs @@ -1,7 +1,7 @@ // ======================== // Lilikoi::LkTargetAttribute.cs // (c) 2023. Distributed under the MIT License -// +// // -> Created: 31.01.2023 // -> Bumped: 06.02.2023 // ======================== @@ -30,7 +30,7 @@ public sealed override LkTargetAttribute Build(Mount mount) /// /// public virtual bool IsTargetedBy(TUserContext context, LilikoiMutator mutator) - where TUserContext : Mount + where TUserContext : IMount { return true; } @@ -43,5 +43,5 @@ public virtual bool IsTargetedBy(TUserContext context, LilikoiMuta /// /// public abstract void Target(TUserContext context, LilikoiMutator mutator) - where TUserContext : Mount; -} \ No newline at end of file + where TUserContext : IMount; +} diff --git a/Lilikoi/Context/IMount.cs b/Lilikoi/Context/IMount.cs new file mode 100644 index 0000000..ce05a07 --- /dev/null +++ b/Lilikoi/Context/IMount.cs @@ -0,0 +1,54 @@ +// ======================== +// Lilikoi::IMount.cs +// (c) 2023. Distributed under the MIT License +// +// -> Created: 13.08.2023 +// -> Bumped: 13.08.2023 +// ======================== +namespace Lilikoi.Context; + +public interface IMount +{ + /// + /// Store a single object in this mount. + /// The location where it is stored depends on the + /// generic parameter passed + /// + /// + /// + void Store(T value) + where T : class; + + /// + /// Get a single object, based on the generic parameter + /// provided. + /// + /// + /// + T? Get() + where T : class; + + /// + /// Get a subclass of a generic specified + /// by the passed type. + /// + /// + /// + /// + T? Super(Type super) + where T : class; + + /// + /// Check if an object exists in the mount + /// + /// + /// + bool Has(); + + /// + /// Check if an object exists in the mount + /// + /// + /// + bool Has(Type t); +} diff --git a/Lilikoi/Context/Mount.cs b/Lilikoi/Context/Mount.cs index 37a452b..aeb0123 100644 --- a/Lilikoi/Context/Mount.cs +++ b/Lilikoi/Context/Mount.cs @@ -13,7 +13,7 @@ namespace Lilikoi.Context; -public class Mount +public class Mount : IMount { private TypeDictionary dictionary = new(); @@ -26,52 +26,27 @@ public Mount(Mount other) dictionary = other.dictionary; } - /// - /// Store a single object in this mount. - /// The location where it is stored depends on the - /// generic parameter passed - /// - /// - /// + /// public virtual void Store(T value) where T : class => dictionary.Set(value); - /// - /// Get a single object, based on the generic parameter - /// provided. - /// - /// - /// + /// public virtual T? Get() where T : class => dictionary.Get(); - /// - /// Get a subclass of a generic specified - /// by the passed type. - /// - /// - /// - /// + /// public virtual T? Super(Type super) where T : class => dictionary.Super(super); - /// - /// Check if an object exists in the mount - /// - /// - /// + /// public virtual bool Has() => dictionary.Has(); - /// - /// Check if an object exists in the mount - /// - /// - /// + /// public virtual bool Has(Type t) => dictionary.Has(t); diff --git a/Lilikoi/Scan/Scanner.cs b/Lilikoi/Scan/Scanner.cs index ce2efe9..f98ca22 100644 --- a/Lilikoi/Scan/Scanner.cs +++ b/Lilikoi/Scan/Scanner.cs @@ -31,7 +31,7 @@ public static class Scanner /// /// public static List Scan(TUserContext context, Assembly assembly, Mount mount) - where TUserContext : Mount + where TUserContext : IMount { return Scan(context, assembly, () => mount); } @@ -48,7 +48,7 @@ public static List Scan(TUserCo /// /// public static List Scan(TUserContext context, Assembly assembly, Func sourceMount) - where TUserContext : Mount + where TUserContext : IMount { List containers = new(); @@ -70,11 +70,11 @@ public static List Scan(TUserCo /// /// public static List Scan(TUserContext context, Func sourceMount) - where TUserContext : Mount + where TUserContext : IMount => Scan(context, typeof(TType), sourceMount); public static List Scan(TUserContext context, Type type, Func sourceMount) - where TUserContext : Mount + where TUserContext : IMount { List containers = new(); @@ -96,7 +96,7 @@ public static List Scan(TUserCo /// /// public static List Scan(TUserContext context, MethodInfo methodInfo, Func sourceMount) - where TUserContext : Mount + where TUserContext : IMount { List containers = new(); From 72377c54b3501f929e3604598f65f105dd7e270f Mon Sep 17 00:00:00 2001 From: Mooshua <43320783+Mooshua@users.noreply.github.com> Date: Wed, 16 Aug 2023 00:07:28 -0700 Subject: [PATCH 17/17] Perform injections after wraps (safety issue), add more APIs to LilikoiMutator.cs Adds type and entry point data to the mutator. --- Lilikoi/Compiler/Public/LilikoiCompiler.cs | 4 ++-- Lilikoi/Compiler/Public/LilikoiMutator.cs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Lilikoi/Compiler/Public/LilikoiCompiler.cs b/Lilikoi/Compiler/Public/LilikoiCompiler.cs index 64de61c..a50e220 100644 --- a/Lilikoi/Compiler/Public/LilikoiCompiler.cs +++ b/Lilikoi/Compiler/Public/LilikoiCompiler.cs @@ -45,12 +45,12 @@ public LilikoiContainer Finish() Internal.HostFor(); Internal.ParameterSafety(); - Internal.InjectionsFor(Internal.Method.Host); - foreach (var implicitWrap in ImplicitWraps) Internal.ImplicitWrap(implicitWrap); Internal.WrapsFor(); + Internal.InjectionsFor(Internal.Method.Host); + foreach (var (implicitWildcard, type) in ImplicitWildcards) Internal.ImplicitWildcard(implicitWildcard, type); diff --git a/Lilikoi/Compiler/Public/LilikoiMutator.cs b/Lilikoi/Compiler/Public/LilikoiMutator.cs index 1da856f..d7372e1 100644 --- a/Lilikoi/Compiler/Public/LilikoiMutator.cs +++ b/Lilikoi/Compiler/Public/LilikoiMutator.cs @@ -7,6 +7,8 @@ // ======================== #region +using System.Reflection; + using Lilikoi.Attributes.Builders; using Lilikoi.Context; @@ -132,4 +134,15 @@ public LilikoiMutator Wildcard(Type type, TParameter value = null) /// The return type of the underlying function /// public Type Result => Compiler.Internal.Method.Return; + + /// + /// The entry point that the mutator is applying to + /// + public MethodInfo Method => Compiler.Internal.Method.Entry; + + /// + /// The host that the entry point belongs to. + /// Note that this is the same as Method.DeclaringType + /// + public Type Host => Compiler.Internal.Method.Host; }