Open sebastienros opened 4 months ago
@lahma I hope to get your attention, very challenging work
Thanks for the heads up, a very interesting feature. I'm just afraid that I might have too much on my plate already...
The main branch has been updated to include the Visitor pattern from this PR.
Motivation This is a PR showing the current progress on Liquid compilation support. It doesn't work but this needs to be saved to I salvaged it from my laptop before it's too late. I wrote this a year ago and I don't remember much of the details of the implementation, I will ty to write it down as I re-understand how everything works.
The idea is that currently Fluid parses a template to generate an AST (
IFluidTemplate
). This AST is processed by evaluating all its statements and "writing" them on aTextWriter
to render it. Many things have to be checked or computed every time the AST is evaluated, boolean expressions, math operations, for loops, ... It also has some overhead because of stack depths and extra method calls. Compilation helps with rendering performance as some conditions can be evaluated once at compiled time to generate the code that needs to be executed. It can also remove the need of reflection to access properties. Finally it allows to generate compiled templates statically if the template is known (strongly typed model) at the project compile time, even though we can still compile dynamically a random template.Part of this PR is the creation of a visitor pattern resembling the one in Roslyn (and Esprima-dotnet) that allows to alter the AST. This could be something that is extracted before the PR is finalized as it could be useful even today. This was done to be able to move the compilation code out of each AST element.
This PR contains a source code generator project that allows to create compiled templates statically. I don't remember if it works, it might since the code is there.
The remaining work consists of implementing the
Visit
for each AST node type in theAstCompiler
class. This means generating the source code that will render the template once compiled.Once the work is done, Fluid will be the fastest Liquid templates parser, will be able to interpret template quickly, detect syntax errors and optimizations (visitor) and compile templates for the fastest rendering experience possible (even compared to Razor).
How to work with this branch:
FluidTemplate.RenderAsync
is patched to support automatic compilation. There is a propertyTemplateOptions.TemplateCompilationThreshold
that will trigger the compilation automatically when a template is rendered more than that number of times. It's set to 1 such that templates are always compiled automatically on the first use. This way running the tests will run the compiled version always. I suggest trying theTemplateTests
class, a nice test beingShouldNotEncodeBlocks
. The generated code is overwriting%TEMP%\fluid.cs
every time.How do use visitors to detect specific patterns in a template: Check how
IdentifierIsAccessedVisitor
can visit eachMemberExpression
to detect if the first segment matches a specific identifier. If one does then this identifier is used somewhere in the template. Another example of such visitor isContinueOffsetVisitor
which will tell if aForStatement
has a continue offset behavior. These visitors let us know if we can omit specific construct once and for all. In the case offor
loops knowing that theforloop
property is not used anywhere allows the final compiled code to omit the creation and tracking of theforloop
object (Index
,First
,Last
... properties).NB: this optimization could also be added to the interpreter when visitors are available.
TODO:
main
. NB: It's not, but shouldn't be hard to get it back. We might want to extract some of the things directly tomain
too, for instance visitors and the model changes (public types, public properties).sebros/member_refactor
branch which has the next commit I was working on is supposed to do. Probably found some issues with strongly typed member access.