protected override Expression VisitBlockHelperExpression(BlockHelperExpression bhex) { var fb = new FunctionBuilder(CompilationContext.Configuration); var body = fb.Compile(((BlockExpression)bhex.Body).Expressions, CompilationContext.BindingContext); var inversion = fb.Compile(((BlockExpression)bhex.Inversion).Expressions, CompilationContext.BindingContext); var helper = CompilationContext.Configuration.BlockHelpers[bhex.HelperName.Replace("#", "")]; var arguments = new Expression[] { Expression.Property( CompilationContext.BindingContext, typeof(BindingContext).GetProperty("TextWriter")), Expression.New( typeof(HelperOptions).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0], body, inversion), Expression.Property( CompilationContext.BindingContext, typeof(BindingContext).GetProperty("Value")), Expression.NewArrayInit(typeof(object), bhex.Arguments) }; if (helper.Target != null) { return Expression.Call( Expression.Constant(helper.Target), helper.Method, arguments); } else { return Expression.Call( helper.Method, arguments); } }
private Expression GetEnumerableIterator(Expression contextParameter, IteratorExpression iex) { var fb = new FunctionBuilder(CompilationContext.Configuration); return Expression.Block( Expression.Assign(contextParameter, Expression.New( #if netstandard typeof(IteratorBindingContext).GetTypeInfo().GetConstructor(new[] { typeof(BindingContext) }), #else typeof(IteratorBindingContext).GetConstructor(new[] { typeof(BindingContext) }), #endif new Expression[] { CompilationContext.BindingContext })), Expression.Call( #if netstandard new Action<IteratorBindingContext, IEnumerable, Action<TextWriter, object>, Action<TextWriter, object>>(Iterate).GetMethodInfo(), #else new Action<IteratorBindingContext, IEnumerable, Action<TextWriter, object>, Action<TextWriter, object>>(Iterate).Method, #endif new Expression[] { Expression.Convert(contextParameter, typeof(IteratorBindingContext)), Expression.Convert(iex.Sequence, typeof(IEnumerable)), fb.Compile(new [] { iex.Template }, contextParameter), fb.Compile(new [] { iex.IfEmpty }, CompilationContext.BindingContext) })); }
public HandlebarsCompiler(HandlebarsConfiguration configuration) { _configuration=configuration; _tokenizer = new Tokenizer(configuration); _expressionBuilder = new ExpressionBuilder(configuration); _functionBuilder = new FunctionBuilder(configuration); }
private Expression[] GetDeferredSectionTemplates(DeferredSectionExpression dsex) { var fb = new FunctionBuilder(CompilationContext.Configuration); var body = fb.Compile(dsex.Body.Expressions, CompilationContext.BindingContext); var inversion = fb.Compile(dsex.Inversion.Expressions, CompilationContext.BindingContext); var sectionPrefix = dsex.Path.Path.Substring(0, 1); switch (sectionPrefix) { case "#": return new[] {body, inversion}; case "^": return new[] {inversion, body}; default: throw new HandlebarsCompilerException("Tried to compile a section expression that did not begin with # or ^"); } }
private Expression GetObjectIterator(Expression contextParameter, IteratorExpression iex) { var fb = new FunctionBuilder(CompilationContext.Configuration); return Expression.Block( Expression.Assign(contextParameter, Expression.New( typeof(ObjectEnumeratorBindingContext).GetConstructor(new[] { typeof(BindingContext) }), new Expression[] { CompilationContext.BindingContext })), Expression.Call( new Action<ObjectEnumeratorBindingContext, object, Action<TextWriter, object>, Action<TextWriter, object>>(Iterate).Method, new Expression[] { Expression.Convert(contextParameter, typeof(ObjectEnumeratorBindingContext)), iex.Sequence, fb.Compile(new [] { iex.Template }, contextParameter), fb.Compile(new [] { iex.IfEmpty }, CompilationContext.BindingContext) })); }
protected override Expression VisitBlockHelperExpression(BlockHelperExpression bhex) { var isInlinePartial = bhex.HelperName == "#*inline"; var context = ExpressionShortcuts.Arg <BindingContext>(CompilationContext.BindingContext); var bindingContext = isInlinePartial ? context.Cast <object>() : context.Property(o => o.Value); var readerContext = ExpressionShortcuts.Arg(bhex.Context); var body = FunctionBuilder.CompileCore(((BlockExpression)bhex.Body).Expressions, CompilationContext.Configuration); var inverse = FunctionBuilder.CompileCore(((BlockExpression)bhex.Inversion).Expressions, CompilationContext.Configuration); var helperName = bhex.HelperName.TrimStart('#', '^'); var textWriter = context.Property(o => o.TextWriter); var arguments = ExpressionShortcuts.Array <object>(bhex.Arguments.Select(o => FunctionBuilder.Reduce(o, CompilationContext))); var configuration = ExpressionShortcuts.Arg(CompilationContext.Configuration); var reducerNew = ExpressionShortcuts.New(() => new LambdaReducer(context, body, inverse)); var reducer = ExpressionShortcuts.Var <LambdaReducer>(); var blockParamsProvider = ExpressionShortcuts.Var <BlockParamsValueProvider>(); var blockParamsExpression = ExpressionShortcuts.Call( () => BlockParamsValueProvider.Create(context, ExpressionShortcuts.Arg <BlockParam>(bhex.BlockParams)) ); var helperOptions = ExpressionShortcuts.New( () => new HelperOptions( reducer.Property(o => o.Direct), reducer.Property(o => o.Inverse), blockParamsProvider, configuration) ); var blockHelpers = CompilationContext.Configuration.BlockHelpers; if (blockHelpers.TryGetValue(helperName, out var helper)) { return(ExpressionShortcuts.Block() .Parameter(reducer, reducerNew) .Parameter(blockParamsProvider, blockParamsExpression) .Line(blockParamsProvider.Using((self, builder) => { builder .Line(context.Call(o => o.RegisterValueProvider((IValueProvider)self))) .Line(ExpressionShortcuts.Try() .Body(ExpressionShortcuts.Call( () => helper(textWriter, helperOptions, bindingContext, arguments) )) .Finally(context.Call(o => o.UnregisterValueProvider((IValueProvider)self))) ); }))); } foreach (var resolver in CompilationContext.Configuration.HelperResolvers) { if (resolver.TryResolveBlockHelper(helperName, out helper)) { return(ExpressionShortcuts.Block() .Parameter(reducer, reducerNew) .Parameter(blockParamsProvider, blockParamsExpression) .Line(blockParamsProvider.Using((self, builder) => { builder .Line(context.Call(o => o.RegisterValueProvider((IValueProvider)self))) .Line(ExpressionShortcuts.Try() .Body(ExpressionShortcuts.Call( () => helper(textWriter, helperOptions, bindingContext, arguments) )) .Finally(context.Call(o => o.UnregisterValueProvider((IValueProvider)self))) ); }))); } } var helperPrefix = bhex.HelperName[0]; return(ExpressionShortcuts.Block() .Parameter(reducer, reducerNew) .Parameter(blockParamsProvider, blockParamsExpression) .Line(blockParamsProvider.Using((self, builder) => { builder .Line(context.Call(o => o.RegisterValueProvider((IValueProvider)self))) .Line(ExpressionShortcuts.Try() .Body(ExpressionShortcuts.Call( () => LateBoundCall( helperName, helperPrefix, context, (IReaderContext)readerContext, textWriter, helperOptions, body, inverse, bindingContext, self, arguments ) )) .Finally(context.Call(o => o.UnregisterValueProvider((IValueProvider)self))) ); }))); }
protected override Expression VisitBlockHelperExpression(BlockHelperExpression bhex) { var isInlinePartial = bhex.HelperName == "#*inline"; var pathInfo = CompilationContext.Configuration.PathInfoStore.GetOrAdd(bhex.HelperName); var bindingContext = CompilationContext.Args.BindingContext; var context = isInlinePartial ? bindingContext.As <object>() : bindingContext.Property(o => o.Value); var readerContext = bhex.Context; var direct = Compile(bhex.Body); var inverse = Compile(bhex.Inversion); var args = FunctionBinderHelpers.CreateArguments(bhex.Arguments, CompilationContext); var helperName = pathInfo.TrimmedPath; var direction = bhex.IsRaw || pathInfo.IsBlockHelper ? BlockHelperDirection.Direct : BlockHelperDirection.Inverse; var blockParams = CreateBlockParams(); var blockHelpers = CompilationContext.Configuration.BlockHelpers; if (blockHelpers.TryGetValue(pathInfo, out var descriptor)) { return(BindByRef(descriptor)); } var helperResolvers = CompilationContext.Configuration.HelperResolvers; for (var index = 0; index < helperResolvers.Count; index++) { var resolver = helperResolvers[index]; if (!resolver.TryResolveBlockHelper(helperName, out var resolvedDescriptor)) { continue; } descriptor = new StrongBox <BlockHelperDescriptorBase>(resolvedDescriptor); blockHelpers.Add(pathInfo, descriptor); return(BindByRef(descriptor)); } var lateBindBlockHelperDescriptor = new LateBindBlockHelperDescriptor(pathInfo, CompilationContext.Configuration); var lateBindBlockHelperRef = new StrongBox <BlockHelperDescriptorBase>(lateBindBlockHelperDescriptor); blockHelpers.Add(pathInfo, lateBindBlockHelperRef); return(BindByRef(lateBindBlockHelperRef)); ExpressionContainer <ChainSegment[]> CreateBlockParams() { var parameters = bhex.BlockParams?.BlockParam?.Parameters; if (parameters == null) { parameters = ArrayEx.Empty <ChainSegment>(); } return(Arg(parameters)); } TemplateDelegate Compile(Expression expression) { var blockExpression = (BlockExpression)expression; return(FunctionBuilder.Compile(blockExpression.Expressions, CompilationContext.Configuration)); } Expression BindByRef(StrongBox <BlockHelperDescriptorBase> helperBox) { var writer = CompilationContext.Args.EncodedWriter; var helperOptions = direction switch { BlockHelperDirection.Direct => New(() => new HelperOptions(direct, inverse, blockParams, bindingContext)), BlockHelperDirection.Inverse => New(() => new HelperOptions(inverse, direct, blockParams, bindingContext)), _ => throw new HandlebarsCompilerException("Helper referenced with unknown prefix", readerContext) }; return(Call(() => helperBox.Value.Invoke(writer, helperOptions, context, args))); } } }
protected override Expression VisitBlockHelperExpression(BlockHelperExpression bhex) { var isInlinePartial = bhex.HelperName == "#*inline"; var pathInfo = CompilationContext.Configuration.PathInfoStore.GetOrAdd(bhex.HelperName); var bindingContext = Arg <BindingContext>(CompilationContext.BindingContext); var context = isInlinePartial ? bindingContext.As <object>() : bindingContext.Property(o => o.Value); var readerContext = bhex.Context; var direct = Compile(bhex.Body); var inverse = Compile(bhex.Inversion); var arguments = CreateArguments(); var helperName = pathInfo.TrimmedPath; var direction = bhex.IsRaw || pathInfo.IsBlockHelper ? BlockHelperDirection.Direct : BlockHelperDirection.Inverse; var blockParams = CreateBlockParams(); var blockHelpers = CompilationContext.Configuration.BlockHelpers; if (blockHelpers.TryGetValue(pathInfo, out var descriptor)) { return(BindByRef(descriptor)); } var helperResolvers = CompilationContext.Configuration.HelperResolvers; for (var index = 0; index < helperResolvers.Count; index++) { var resolver = helperResolvers[index]; if (!resolver.TryResolveBlockHelper(helperName, out var resolvedDescriptor)) { continue; } return(Bind(resolvedDescriptor)); } var lateBindBlockHelperDescriptor = new LateBindBlockHelperDescriptor(pathInfo, CompilationContext.Configuration); var lateBindBlockHelperRef = new StrongBox <BlockHelperDescriptorBase>(lateBindBlockHelperDescriptor); blockHelpers.Add(pathInfo, lateBindBlockHelperRef); return(BindByRef(lateBindBlockHelperRef)); ExpressionContainer <ChainSegment[]> CreateBlockParams() { var parameters = bhex.BlockParams?.BlockParam?.Parameters; if (parameters == null) { parameters = ArrayEx.Empty <ChainSegment>(); } return(Arg(parameters)); } ExpressionContainer <object[]> CreateArguments() { var args = bhex.Arguments .ApplyOn((PathExpression pex) => pex.Context = PathExpression.ResolutionContext.Parameter) .Select(o => FunctionBuilder.Reduce(o, CompilationContext)); return(Array <object>(args)); } Action <BindingContext, TextWriter, object> Compile(Expression expression) { var blockExpression = (BlockExpression)expression; return(FunctionBuilder.CompileCore(blockExpression.Expressions, CompilationContext.Configuration)); } Expression BindByRef(StrongBox <BlockHelperDescriptorBase> value) { return(direction switch { BlockHelperDirection.Direct => Call(() => BlockHelperCallBindingByRef(bindingContext, context, blockParams, direct, inverse, arguments, value)), BlockHelperDirection.Inverse => Call(() => BlockHelperCallBindingByRef(bindingContext, context, blockParams, inverse, direct, arguments, value)), _ => throw new HandlebarsCompilerException("Helper referenced with unknown prefix", readerContext) });