Skip to content

Commit

Permalink
Expand mount API, improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Mooshua committed Aug 13, 2023
1 parent 66357d5 commit dadfd4f
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 93 deletions.
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?");
}
}
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,9 +39,10 @@ public TypeDictionary()
}

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)
{
Expand All @@ -51,6 +52,19 @@ public void Set<TValue>(TValue obj)
_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 (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.
}

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 @@ public void Unlock(Padlock.Key key)
{
mutable.Unlock(key);
}
}
}
54 changes: 43 additions & 11 deletions Lilikoi/Context/Mount.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ========================
// Lilikoi::Mount.cs
// (c) 2023. Distributed under the MIT License
//
//
// -> Created: 22.12.2022
// -> Bumped: 06.02.2023
// ========================
Expand All @@ -26,21 +26,53 @@ public Mount(Mount other)
dictionary = other.dictionary;
}

/// <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>
public virtual void Store<T>(T value)
where T : class
{
dictionary.Set(value);
}
=> dictionary.Set(value);


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

{
return dictionary.Get<T>();
}

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

/// <summary>
/// Check if an object exists in the mount
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public virtual bool Has<T>()
{
return dictionary.Has<T>();
}
}
=> dictionary.Has<T>();

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

}

0 comments on commit dadfd4f

Please sign in to comment.