diff --git a/Fluid.Tests/TemplateContextTests.cs b/Fluid.Tests/TemplateContextTests.cs index b4e56591..b1c7acc5 100644 --- a/Fluid.Tests/TemplateContextTests.cs +++ b/Fluid.Tests/TemplateContextTests.cs @@ -1,4 +1,5 @@ -using Fluid.Values; +using Fluid.Ast; +using Fluid.Values; using System; using System.Globalization; using System.Linq; @@ -135,6 +136,31 @@ public void ScopeSetValueAcceptsNull() Assert.Equal(NilValue.Instance, context.GetValue("text")); } + [Fact] + public async Task ShouldNotReleaseScopeAsynchronously() + { + var parser = new FluidParser(); + + parser.RegisterEmptyBlock("sleep", async (statements, writer, encoder, context) => + { + context.EnterChildScope(); + context.IncrementSteps(); + context.SetValue("id", "0"); + await Task.Delay(100); + await statements.RenderStatementsAsync(writer, encoder, context); + context.ReleaseScope(); + return Completion.Normal; + }); + + var context = new TemplateContext { }; + context.SetValue("id", "1"); + var template = parser.Parse(@"{{id}}{%sleep%}{{id}}{%endsleep%}{{id}}"); + + var output = await template.RenderAsync(context); + + Assert.Equal("101", output); + } + private class TestClass { public string Name { get; set; } diff --git a/Fluid/FluidTemplateExtensions.cs b/Fluid/FluidTemplateExtensions.cs index 00d152a7..6c05296a 100644 --- a/Fluid/FluidTemplateExtensions.cs +++ b/Fluid/FluidTemplateExtensions.cs @@ -32,7 +32,7 @@ public static void Render(this IFluidTemplate template, TemplateContext context, } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask RenderAsync(this IFluidTemplate template, TemplateContext context, TextEncoder encoder) + public static async ValueTask RenderAsync(this IFluidTemplate template, TemplateContext context, TextEncoder encoder) { if (context == null) { @@ -44,45 +44,25 @@ public static ValueTask RenderAsync(this IFluidTemplate template, Templa ExceptionHelper.ThrowArgumentNullException(nameof(template)); } - static async ValueTask Awaited( - ValueTask task, - StringWriter writer, - StringBuilderPool builder) - { - await task; - await writer.FlushAsync(); - var s = builder.ToString(); - builder.Dispose(); - writer.Dispose(); - return s; - } - var sb = StringBuilderPool.GetInstance(); var writer = new StringWriter(sb.Builder); + // A template is evaluated in a child scope such that the provided TemplateContext is immutable + context.EnterChildScope(); + try { - // A template is evaluated in a child scope such that the provided TemplateContext is immutable - context.EnterChildScope(); - - var task = template.RenderAsync(writer, encoder, context); - if (!task.IsCompletedSuccessfully) - { - return Awaited(task, writer, sb); - } + await template.RenderAsync(writer, encoder, context); writer.Flush(); + return sb.ToString(); } finally { + sb.Dispose(); + writer.Dispose(); context.ReleaseScope(); } - - var result = sb.ToString(); - sb.Dispose(); - writer.Dispose(); - - return new ValueTask(result); } [MethodImpl(MethodImplOptions.AggressiveInlining)]