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

Lilikoi 0.1 #12

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
811996f
Merge pull request #1 from Mooshua/edge
Mooshua Dec 23, 2022
9fcfaf7
Merge pull request #2 from Mooshua/edge
Mooshua Dec 23, 2022
f0138bc
Merge pull request #4 from Mooshua/main
Mooshua Feb 1, 2023
b888c96
Update nuget.yml
Mooshua Feb 1, 2023
465b4d6
Merge pull request #5 from Mooshua/main
Mooshua Feb 1, 2023
0937431
General API Improvements
Mooshua Feb 2, 2023
e33588d
Improved docs + Scanner + Factory API
Mooshua Feb 2, 2023
04fc6e9
Merge pull request #6 from Mooshua/workspace
Mooshua Feb 2, 2023
b74b198
Update nuget: Only run on pushes to main and dev
Mooshua Feb 2, 2023
10237ff
Bugfixes + Mutators + Docs + Tests
Mooshua Feb 6, 2023
23fa4e9
Reformat + Shift extension target
Mooshua Feb 6, 2023
3a8382f
Merge pull request #7 from Mooshua/workspace
Mooshua Feb 6, 2023
d8cb201
Add more verbose scan options
Mooshua Jul 29, 2023
88f585f
Fix spelling and factory namespaces
Mooshua Jul 29, 2023
dfb4767
Merge pull request #8 from Mooshua/workspace
Mooshua Jul 29, 2023
f4e1cfb
Completely disable specifying host type and implementation.
Mooshua Aug 11, 2023
558e4b9
Upgrade test frameworks
Mooshua Aug 11, 2023
e142f16
Dont have the willpower to deal with this; just drop testing support …
Mooshua Aug 11, 2023
5cc9a02
Merge pull request #9 from Mooshua/workspace
Mooshua Aug 11, 2023
c1ec902
Introduce dynamic wildcard APIs for manually specifying type
Mooshua Aug 11, 2023
7890bda
Merge pull request #10 from Mooshua/workspace
Mooshua Aug 11, 2023
f29fbef
Enable dynamic casting of lambda result within container
Mooshua Aug 12, 2023
f6c0a96
Merge pull request #11 from Mooshua/workspace
Mooshua Aug 12, 2023
66357d5
Change wrap validation behavior to use inner container return instead…
Mooshua Aug 13, 2023
dadfd4f
Expand mount API, improve docs
Mooshua Aug 13, 2023
627a808
Contexts now use an IMount interface instead of the abstract
Mooshua Aug 13, 2023
72377c5
Perform injections after wraps (safety issue), add more APIs to Lilik…
Mooshua Aug 16, 2023
b87602a
Merge pull request #13 from Mooshua/workspace
Mooshua Aug 16, 2023
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
4 changes: 3 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

name: Lilikoi.NuGet

on: [push]
on:
push:
branches:
- main
- dev

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
Binary file added Assets/LilikoiContainerOverview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.)
5 changes: 4 additions & 1 deletion Docs/containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,3 +44,4 @@ so that programmers have ways to hurl opaque data around the entry point of a co
4. Entry point
5. Wrap after execution
7. Dejection of host
8. Casting of entry return and wrap return into container result
34 changes: 34 additions & 0 deletions Docs/mounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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 environment.

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.

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.
49 changes: 49 additions & 0 deletions Docs/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 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 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.

![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, and can be assigned by the target and mutator attributes.

## 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.

- **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
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.
6 changes: 3 additions & 3 deletions Lilikoi.Benchmarks/Lilikoi.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.2" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.2"/>
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.2"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lilikoi\Lilikoi.csproj" />
<ProjectReference Include="..\Lilikoi\Lilikoi.csproj"/>
</ItemGroup>

</Project>
22 changes: 10 additions & 12 deletions Lilikoi.Benchmarks/Mahogany/Applications/InjectSimple/Simple.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
// ========================
// 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)]
public static Simple Create()
{
return new Simple()
{
Value = 0 //Random.Shared.Next()
Value = 0 //Random.Shared.Next()
};
}

[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
@@ -1,36 +1,36 @@
// ========================
// 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;
using Lilikoi.Context;

#endregion

namespace Lilikoi.Benchmarks.Mahogany.Applications.InjectSimple;

public class SimpleInjectHost
{

[SimpleInjector] public Simple Injected;

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

public static Func<SimpleInjectHost, 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<SimpleInjectHost, bool, bool>();
.Compile<object, object>();
}

}
Original file line number Diff line number Diff line change
@@ -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<Simple>
Expand All @@ -20,4 +20,4 @@ public override Simple Inject(Mount mount)
{
return Simple.Create();
}
}
}
21 changes: 11 additions & 10 deletions Lilikoi.Benchmarks/Mahogany/CompileBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -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<SimpleInjectHost, bool, bool> Simple()
public Func<object, object> Simple()
{
return SimpleInjectHost.Build();
}

}
27 changes: 14 additions & 13 deletions Lilikoi.Benchmarks/Mahogany/HeadlessInjectBenchmark.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<SimpleInjectHost> SimpleInjector;

[GlobalSetup]
Expand All @@ -51,4 +52,4 @@ public void SimpleCompiler()
{
SimpleInjector = LilikoiInjector.CreateInjector<SimpleInjectHost>();
}
}
}
Loading
Loading