Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change wrap validation behavior + mount API #13

Merged
merged 4 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Docs/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
1 change: 1 addition & 0 deletions Docs/containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 17 additions & 0 deletions Docs/mounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 6 additions & 2 deletions Docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ========================
// Lilikoi.Benchmarks::Simple.cs
// (c) 2023. Distributed under the MIT License
//
//
// -> Created: 22.12.2022
// -> Bumped: 06.02.2023
// ========================
Expand All @@ -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!";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#region

using Lilikoi.Compiler.Public;
using Lilikoi.Context;

#endregion

Expand All @@ -17,18 +18,19 @@ public class SimpleInjectHost
{
[SimpleInjector] public Simple Injected;

public bool Execute()
public object Execute()
{
return Injected.Execute();
}

public static Func<bool, bool> Build()
public static Func<object, object> Build()
{
return LilikoiMethod.FromMethodInfo(typeof(SimpleInjectHost).GetMethod(nameof(Execute)))
.Input<bool>()
.Output<bool>()
.Mount(new Mount())
.Input<object>()
.Output<object>()
.Build()
.Finish()
.Compile<bool, bool>();
.Compile<object, object>();
}
}
2 changes: 1 addition & 1 deletion Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace Lilikoi.Benchmarks.Mahogany;
public class CompileBenchmarks
{
[Benchmark()]
public Func<bool, bool> Simple()
public Func<object, object> Simple()
{
return SimpleInjectHost.Build();
}
Expand Down
11 changes: 7 additions & 4 deletions Lilikoi.Benchmarks/Mahogany/RunBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
#region

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

using Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple;

#endregion

namespace Lilikoi.Benchmarks.Mahogany;

[SimpleJob()]
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net70)]
[Q1Column]
[MeanColumn]
[MedianColumn]
Expand All @@ -25,7 +28,7 @@ namespace Lilikoi.Benchmarks.Mahogany;
[MemoryDiagnoser(true)]
public class RunBenchmarks
{
public Func<bool, bool> SimpleContainer;
public Func<object, object> SimpleContainer;

[GlobalSetup]
public void Setup()
Expand All @@ -34,8 +37,8 @@ public void Setup()
}

[Benchmark()]
public bool Simple()
public object Simple()
{
return SimpleContainer(true);
return SimpleContainer("Hello?");
}
}
6 changes: 3 additions & 3 deletions Lilikoi/Attributes/Builders/LkTargetBuilderAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ========================
// Lilikoi::LkTargetBuilderAttribute.cs
// (c) 2023. Distributed under the MIT License
//
//
// -> Created: 31.01.2023
// -> Bumped: 06.02.2023
// ========================
Expand Down Expand Up @@ -29,5 +29,5 @@ public abstract class LkTargetBuilderAttribute : Attribute
/// <typeparam name="TUserContext"></typeparam>
/// <returns></returns>
public abstract bool IsTargetable<TUserContext>()
where TUserContext : Mount;
}
where TUserContext : IMount;
}
8 changes: 4 additions & 4 deletions Lilikoi/Attributes/LkTargetAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ========================
// Lilikoi::LkTargetAttribute.cs
// (c) 2023. Distributed under the MIT License
//
//
// -> Created: 31.01.2023
// -> Bumped: 06.02.2023
// ========================
Expand All @@ -19,7 +19,7 @@
{
public sealed override LkTargetAttribute Build(Mount mount)
{
return MemberwiseClone() as LkTargetAttribute;

Check warning on line 22 in Lilikoi/Attributes/LkTargetAttribute.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Possible null reference return.

Check warning on line 22 in Lilikoi/Attributes/LkTargetAttribute.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Possible null reference return.

Check warning on line 22 in Lilikoi/Attributes/LkTargetAttribute.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Possible null reference return.

Check warning on line 22 in Lilikoi/Attributes/LkTargetAttribute.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Possible null reference return.
}

/// <summary>
Expand All @@ -30,7 +30,7 @@
/// <typeparam name="TUserContext"></typeparam>
/// <returns></returns>
public virtual bool IsTargetedBy<TUserContext>(TUserContext context, LilikoiMutator mutator)
where TUserContext : Mount
where TUserContext : IMount
{
return true;
}
Expand All @@ -43,5 +43,5 @@
/// <param name="mutator"></param>
/// <typeparam name="TUserContext"></typeparam>
public abstract void Target<TUserContext>(TUserContext context, LilikoiMutator mutator)
where TUserContext : Mount;
}
where TUserContext : IMount;
}
24 changes: 19 additions & 5 deletions Lilikoi/Collection/TypeDictionary.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ========================
// Lilikoi::TypeDictionary.cs
// (c) 2023. Distributed under the MIT License
//
//
// -> Created: 22.12.2022
// -> Bumped: 06.02.2023
// ========================
Expand Down Expand Up @@ -39,18 +39,32 @@
}

public bool Has<TValue>()
{
return _underlying.ContainsKey(typeof(TValue));
}
=> Has(typeof(TValue));

public bool Has(Type t)
=> _underlying.ContainsKey(t);

public void Set<TValue>(TValue obj)
{
if (mutable.IsLocked())
throw new Exception("Locked.");

_underlying[typeof(TValue)] = obj;

Check warning on line 52 in Lilikoi/Collection/TypeDictionary.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Possible null reference assignment.

Check warning on line 52 in Lilikoi/Collection/TypeDictionary.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Possible null reference assignment.

Check warning on line 52 in Lilikoi/Collection/TypeDictionary.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Possible null reference assignment.

Check warning on line 52 in Lilikoi/Collection/TypeDictionary.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Possible null reference assignment.
}

public TBase? Super<TBase>(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);
Expand All @@ -60,4 +74,4 @@
{
mutable.Unlock(key);
}
}
}
11 changes: 7 additions & 4 deletions Lilikoi/Compiler/Mahogany/MahoganyValidator.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ========================
// Lilikoi::MahoganyValidator.cs
// (c) 2023. Distributed under the MIT License
//
//
// -> Created: 23.12.2022
// -> Bumped: 06.02.2023
// ========================
Expand Down Expand Up @@ -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} ");
}
}
}
4 changes: 2 additions & 2 deletions Lilikoi/Compiler/Public/LilikoiCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
13 changes: 13 additions & 0 deletions Lilikoi/Compiler/Public/LilikoiMutator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// ========================
#region

using System.Reflection;

using Lilikoi.Attributes.Builders;
using Lilikoi.Context;

Expand Down Expand Up @@ -41,7 +43,7 @@
/// <param name="value"></param>
/// <typeparam name="TWrap"></typeparam>
/// <returns></returns>
public LilikoiMutator Implicit<TWrap>(TWrap value = null)

Check warning on line 46 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 46 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 46 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 46 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Cannot convert null literal to non-nullable reference type.
where TWrap : LkWrapBuilderAttribute, new()
{
value ??= new TWrap();
Expand Down Expand Up @@ -81,7 +83,7 @@
/// <typeparam name="TParameter"></typeparam>
/// <typeparam name="TType"></typeparam>
/// <returns></returns>
public LilikoiMutator Wildcard<TType, TParameter>(TParameter value = null)

Check warning on line 86 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 86 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 86 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 86 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Cannot convert null literal to non-nullable reference type.
where TParameter : LkParameterBuilderAttribute, new()
{
value ??= new TParameter();
Expand All @@ -98,7 +100,7 @@
/// <typeparam name="TParameter"></typeparam>
/// <typeparam name="TType"></typeparam>
/// <returns></returns>
public LilikoiMutator Wildcard<TParameter>(Type type, TParameter value = null)

Check warning on line 103 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 103 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 103 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net6.0)

Cannot convert null literal to non-nullable reference type.

Check warning on line 103 in Lilikoi/Compiler/Public/LilikoiMutator.cs

View workflow job for this annotation

GitHub Actions / test (net7.0)

Cannot convert null literal to non-nullable reference type.
where TParameter : LkParameterBuilderAttribute, new()
{
value ??= new TParameter();
Expand Down Expand Up @@ -132,4 +134,15 @@
/// The return type of the underlying function
/// </summary>
public Type Result => Compiler.Internal.Method.Return;

/// <summary>
/// The entry point that the mutator is applying to
/// </summary>
public MethodInfo Method => Compiler.Internal.Method.Entry;

/// <summary>
/// The host that the entry point belongs to.
/// Note that this is the same as Method.DeclaringType
/// </summary>
public Type Host => Compiler.Internal.Method.Host;
}
54 changes: 54 additions & 0 deletions Lilikoi/Context/IMount.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Store a single object in this mount.
/// The location where it is stored depends on the
/// generic parameter passed
/// </summary>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
void Store<T>(T value)
where T : class;

/// <summary>
/// Get a single object, based on the generic parameter
/// provided.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T? Get<T>()
where T : class;

/// <summary>
/// Get a subclass of a generic specified
/// by the passed type.
/// </summary>
/// <param name="super"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T? Super<T>(Type super)
where T : class;

/// <summary>
/// Check if an object exists in the mount
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool Has<T>();

/// <summary>
/// Check if an object exists in the mount
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
bool Has(Type t);
}
Loading
Loading