Skip to content

Commit

Permalink
Reduce state machine creation (#156)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma authored and sebastienros committed Sep 20, 2019
1 parent a10f6bd commit 05979cc
Show file tree
Hide file tree
Showing 32 changed files with 330 additions and 146 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,7 @@ __pycache__/
*.btm.cs
*.odx.cs
*.xsd.cs
Fluid.Benchmarks/BenchmarkDotNet.Artifacts
*.orig
results

BenchmarkDotNet.Artifacts
2 changes: 1 addition & 1 deletion Fluid.Benchmarks/Fluid.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.4" />
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Include="DotLiquid" Version="2.0.298" />
<PackageReference Include="Liquid.NET" Version="0.10.0" />
<PackageReference Include="Scriban" Version="2.0.0" />
Expand Down
7 changes: 7 additions & 0 deletions Fluid.MvcViewEngine/Fluid.MvcViewEngine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFrameworks>netstandard1.6;netstandard2.0</TargetFrameworks>
<LangVersion>8</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand All @@ -11,5 +12,11 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.3" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\Fluid\ExceptionHelper.cs">
<Link>ExceptionHelper.cs</Link>
</Compile>
</ItemGroup>

</Project>
9 changes: 5 additions & 4 deletions Fluid.MvcViewEngine/FluidMvcViewOptionsSetup.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace Fluid.MvcViewEngine
Expand All @@ -14,7 +13,8 @@ public class FluidMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
/// <param name="fluidViewEngine">The <see cref="IFluidViewEngine"/>.</param>
public FluidMvcViewOptionsSetup(IFluidViewEngine fluidViewEngine)
{
_fluidViewEngine = fluidViewEngine ?? throw new ArgumentNullException(nameof(fluidViewEngine));
_fluidViewEngine = fluidViewEngine
?? ExceptionHelper.ThrowArgumentNullException<IFluidViewEngine>(nameof(fluidViewEngine));
}

/// <summary>
Expand All @@ -25,7 +25,8 @@ public void Configure(MvcViewOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
ExceptionHelper.ThrowArgumentNullException(nameof(options));
return;
}

options.ViewEngines.Add(_fluidViewEngine);
Expand Down
6 changes: 3 additions & 3 deletions Fluid.MvcViewEngine/FluidRendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public FluidRendering(
private readonly IHostingEnvironment _hostingEnvironment;
private FluidViewEngineOptions _options;

public async Task<string> RenderAsync(string path, object model, ViewDataDictionary viewData, ModelStateDictionary modelState)
public async ValueTask<string> RenderAsync(string path, object model, ViewDataDictionary viewData, ModelStateDictionary modelState)
{
// Check for a custom file provider
var fileProvider = _options.FileProvider ?? _hostingEnvironment.ContentRootFileProvider;
Expand Down Expand Up @@ -78,7 +78,7 @@ public async Task<string> RenderAsync(string path, object model, ViewDataDiction
return body;
}

public IEnumerable<string> FindViewStarts(string viewPath, IFileProvider fileProvider)
public List<string> FindViewStarts(string viewPath, IFileProvider fileProvider)
{
var viewStarts = new List<string>();
int index = viewPath.Length - 1;
Expand Down Expand Up @@ -127,7 +127,7 @@ public FluidViewTemplate ParseLiquidFile(string path, IFileProvider fileProvider
statements.Add(new CallbackStatement((writer, encoder, context) =>
{
context.AmbientValues[ViewPath] = viewStartPath;
return Task.FromResult(Completion.Normal);
return new ValueTask<Completion>(Completion.Normal);
}));
var viewStartTemplate = ParseLiquidFile(viewStartPath, fileProvider, false);
Expand Down
21 changes: 16 additions & 5 deletions Fluid.MvcViewEngine/FluidTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,23 @@ public FluidTagHelper(IFluidRendering fluidRendering)
[HtmlAttributeName("view")]
public string View { get; set; }

public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var result = await _fluidRendering.RenderAsync(View, Model, null, null);
output.TagName = null;
output.Content.AppendHtml(result);
}
static async Task Awaited(TagHelperOutput o, ValueTask<string> t)
{
o.TagName = null;
o.Content.AppendHtml(await t);
}

var task = _fluidRendering.RenderAsync(View, Model, null, null);
if (task.IsCompletedSuccessfully)
{
output.TagName = null;
output.Content.AppendHtml(task.Result);
return Task.FromResult(output);
}

return Awaited(output, task);
}
}
}
4 changes: 2 additions & 2 deletions Fluid.MvcViewEngine/FluidViewEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,12 @@ public static string GetNormalizedRouteValue(ActionContext context, string key)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
ExceptionHelper.ThrowArgumentNullException(nameof(context));
}

if (key == null)
{
throw new ArgumentNullException(nameof(key));
ExceptionHelper.ThrowArgumentNullException(nameof(key));
}

if (!context.RouteData.Values.TryGetValue(key, out object routeValue))
Expand Down
2 changes: 1 addition & 1 deletion Fluid.MvcViewEngine/IFluidRendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ namespace Fluid.MvcViewEngine
{
public interface IFluidRendering
{
Task<string> RenderAsync(string path, object model, ViewDataDictionary viewData, ModelStateDictionary modelState);
ValueTask<string> RenderAsync(string path, object model, ViewDataDictionary viewData, ModelStateDictionary modelState);
}
}
2 changes: 1 addition & 1 deletion Fluid.MvcViewEngine/MvcViewFeaturesMvcBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static IMvcBuilder AddFluid(this IMvcBuilder builder, Action<FluidViewEng
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
ExceptionHelper.ThrowArgumentNullException(nameof(builder));
}

builder.Services.AddOptions();
Expand Down
6 changes: 3 additions & 3 deletions Fluid.Tests/Fluid.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
<TargetFrameworks>netcoreapp2.2;netcoreapp3.0;net461</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions Fluid/Ast/CallbackStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ namespace Fluid.Ast
/// </summary>
public class CallbackStatement : Statement
{
public CallbackStatement(Func<TextWriter, TextEncoder, TemplateContext, Task<Completion>> action)
public CallbackStatement(Func<TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> action)
{
Action = action;
}

public Func<TextWriter, TextEncoder, TemplateContext, Task<Completion>> Action { get; }
public Func<TextWriter, TextEncoder, TemplateContext, ValueTask<Completion>> Action { get; }

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
context.IncrementSteps();

return new ValueTask<Completion>(Action?.Invoke(writer, encoder, context));
return Action?.Invoke(writer, encoder, context) ?? new ValueTask<Completion>(Completion.Normal);
}
}
}
3 changes: 1 addition & 2 deletions Fluid/Ast/DecrementStatement.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Fluid.Values;
Expand Down
26 changes: 16 additions & 10 deletions Fluid/Ast/IdentifierSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,39 @@ public override ValueTask<FluidValue> ResolveAsync(FluidValue value, TemplateCon
return value.GetValueAsync(Identifier, context);
}

public override async ValueTask<FluidValue> ResolveAsync(Scope value, TemplateContext context)
public override ValueTask<FluidValue> ResolveAsync(Scope value, TemplateContext context)
{
static async ValueTask<FluidValue> Awaited(
IAsyncMemberAccessor asyncAccessor,
TemplateContext ctx,
string identifier)
{
var o = await asyncAccessor.GetAsync(ctx.Model, identifier, ctx);
return FluidValue.Create(o);
}

var result = value.GetValue(Identifier);

if (result.IsNil() && context.Model != null)
{
// Look into the Model if defined
_accessor = _accessor ?? context.MemberAccessStrategy.GetAccessor(context.Model.GetType(), Identifier);
_accessor ??= context.MemberAccessStrategy.GetAccessor(context.Model.GetType(), Identifier);

if (_accessor != null)
{

if (_accessor is IAsyncMemberAccessor asyncAccessor)
{
var o = await asyncAccessor.GetAsync(context.Model, Identifier, context);
return FluidValue.Create(o);
return Awaited(asyncAccessor, context, Identifier);
}

return FluidValue.Create(_accessor.Get(context.Model, Identifier, context));
}
else
{
return NilValue.Instance;
return new ValueTask<FluidValue>(FluidValue.Create(_accessor.Get(context.Model, Identifier, context)));
}

return new ValueTask<FluidValue>(NilValue.Instance);
}

return result;
return new ValueTask<FluidValue>(result);
}
}
}
17 changes: 14 additions & 3 deletions Fluid/Ast/IndexerSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,21 @@ public override async ValueTask<FluidValue> ResolveAsync(FluidValue value, Templ
return await value.GetIndexAsync(index, context);
}

public override async ValueTask<FluidValue> ResolveAsync(Scope value, TemplateContext context)
public override ValueTask<FluidValue> ResolveAsync(Scope value, TemplateContext context)
{
var index = await Expression.EvaluateAsync(context);
return value.GetIndex(index);
static async ValueTask<FluidValue> Awaited(ValueTask<FluidValue> valueTask, Scope s)
{
var index = await valueTask;
return s.GetIndex(index);
}

var task = Expression.EvaluateAsync(context);
if (task.IsCompletedSuccessfully)
{
return new ValueTask<FluidValue>(value.GetIndex(task.Result));
}

return Awaited(task, value);
}
}
}
41 changes: 32 additions & 9 deletions Fluid/Ast/MemberExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,46 @@ public MemberExpression(params MemberSegment[] segments)

public MemberSegment[] Segments { get; }

public override async ValueTask<FluidValue> EvaluateAsync(TemplateContext context)
public override ValueTask<FluidValue> EvaluateAsync(TemplateContext context)
{
FluidValue value = null;

var length = Segments.Length;

for (var i = 0; i< length; i++)
for (var i = 0; i < Segments.Length; i++)
{
if (value == null)
var s = Segments[i];
var task = value == null
? s.ResolveAsync(context.LocalScope, context) // root
: s.ResolveAsync(value, context);

if (!task.IsCompletedSuccessfully)
{
// Root property
value = await Segments[i].ResolveAsync(context.LocalScope, context);
return Awaited(task, context, Segments, i + 1);
}
else

value = task.Result;
// Stop processing as soon as a member returns nothing
if (value.IsNil())
{
value = await Segments[i].ResolveAsync(value, context);
return new ValueTask<FluidValue>(value);
}
}

return new ValueTask<FluidValue>(value);
}

private static async ValueTask<FluidValue> Awaited(
ValueTask<FluidValue> task,
TemplateContext context,
MemberSegment[] segments,
int startIndex)
{
var value = await task;
for (var i = startIndex; i < segments.Length; i++)
{
var s = segments[i];
value = await (value == null
? s.ResolveAsync(context.LocalScope, context) // root
: s.ResolveAsync(value, context));

// Stop processing as soon as a member returns nothing
if (value.IsNil())
Expand Down
25 changes: 20 additions & 5 deletions Fluid/Ast/OutputStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Fluid.Values;

namespace Fluid.Ast
{
Expand All @@ -16,15 +17,29 @@ public OutputStatement(Expression expression)

public IList<FilterExpression> Filters { get ; }

public override async ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
context.IncrementSteps();
static async ValueTask<Completion> Awaited(
ValueTask<FluidValue> t,
TextWriter w,
TextEncoder enc,
TemplateContext ctx)
{
var value = await t;
value.WriteTo(w, enc, ctx.CultureInfo);
return Completion.Normal;
}

var value = await Expression.EvaluateAsync(context);
context.IncrementSteps();

value.WriteTo(writer, encoder, context.CultureInfo);
var task = Expression.EvaluateAsync(context);
if (task.IsCompletedSuccessfully)
{
task.Result.WriteTo(writer, encoder, context.CultureInfo);
return new ValueTask<Completion>(Completion.Normal);
}

return Completion.Normal;
return Awaited(task, writer, encoder, context);
}
}
}
Loading

0 comments on commit 05979cc

Please sign in to comment.