protected override Expression VisitSubExpression(SubExpressionExpression subex) { switch (subex.Expression) { case MethodCallExpression callExpression: return(HandleMethodCallExpression(callExpression)); default: var expression = FunctionBuilder.Reduce(subex.Expression, CompilationContext); if (expression is MethodCallExpression lateBoundCall) { return(HandleMethodCallExpression(lateBoundCall)); } throw new HandlebarsCompilerException("Sub-expression does not contain a converted MethodCall expression"); } }
protected override Expression VisitHelperExpression(HelperExpression hex) { var pathInfo = CompilationContext.Configuration.PathInfoStore.GetOrAdd(hex.HelperName); if (!pathInfo.IsValidHelperLiteral && !CompilationContext.Configuration.Compatibility.RelaxedHelperNaming) { return(Expression.Empty()); } var helperName = pathInfo.TrimmedPath; var bindingContext = Arg <BindingContext>(CompilationContext.BindingContext); var contextValue = bindingContext.Property(o => o.Value); var textWriter = bindingContext.Property(o => o.TextWriter); var arguments = hex.Arguments .ApplyOn <Expression, PathExpression>(path => path.Context = PathExpression.ResolutionContext.Parameter) .Select(o => FunctionBuilder.Reduce(o, CompilationContext)); var args = Array <object>(arguments); var configuration = CompilationContext.Configuration; if (configuration.Helpers.TryGetValue(pathInfo, out var helper)) { return(Call(() => helper.Value.WriteInvoke(bindingContext, textWriter, contextValue, args))); } for (var index = 0; index < configuration.HelperResolvers.Count; index++) { var resolver = configuration.HelperResolvers[index]; if (resolver.TryResolveHelper(helperName, typeof(object), out var resolvedHelper)) { return(Call(() => resolvedHelper.WriteInvoke(bindingContext, textWriter, contextValue, args))); } } var lateBindDescriptor = new StrongBox <HelperDescriptorBase>(new LateBindHelperDescriptor(pathInfo, configuration)); configuration.Helpers.Add(pathInfo, lateBindDescriptor); return(Call(() => lateBindDescriptor.Value.WriteInvoke(bindingContext, textWriter, contextValue, args))); }
protected override Expression VisitPartialExpression(PartialExpression pex) { var bindingContext = ExpressionShortcuts.Arg <BindingContext>(CompilationContext.BindingContext); var partialBlockTemplate = pex.Fallback != null ? FunctionBuilder.CompileCore(new[] { pex.Fallback }, CompilationContext.Configuration) : null; if (pex.Argument != null || partialBlockTemplate != null) { var value = ExpressionShortcuts.Arg <object>(FunctionBuilder.Reduce(pex.Argument, CompilationContext)); var partialTemplate = ExpressionShortcuts.Arg(partialBlockTemplate); bindingContext = bindingContext.Call(o => o.CreateChildContext(value, partialTemplate)); } var partialName = ExpressionShortcuts.Cast <string>(pex.PartialName); var configuration = ExpressionShortcuts.Arg(CompilationContext.Configuration); return(ExpressionShortcuts.Call(() => InvokePartialWithFallback(partialName, bindingContext, (ICompiledHandlebarsConfiguration)configuration) )); }
protected override Expression VisitHelperExpression(HelperExpression hex) { var helperName = hex.HelperName; var bindingContext = ExpressionShortcuts.Arg <BindingContext>(CompilationContext.BindingContext); var textWriter = bindingContext.Property(o => o.TextWriter); var arguments = hex.Arguments.Select(o => FunctionBuilder.Reduce(o, CompilationContext)); var args = ExpressionShortcuts.Array <object>(arguments); var configuration = CompilationContext.Configuration; if (configuration.Helpers.TryGetValue(helperName, out var helper)) { return(ExpressionShortcuts.Call(() => helper(textWriter, bindingContext, args))); } if (configuration.ReturnHelpers.TryGetValue(helperName, out var returnHelper)) { return(ExpressionShortcuts.Call(() => CaptureResult(textWriter, ExpressionShortcuts.Call(() => returnHelper(bindingContext, args))) )); } var pureHelperName = helperName.Substring(1); foreach (var resolver in configuration.HelperResolvers) { if (resolver.TryResolveReturnHelper(pureHelperName, typeof(object), out var resolvedHelper)) { return(ExpressionShortcuts.Call(() => CaptureResult(textWriter, ExpressionShortcuts.Call(() => resolvedHelper(bindingContext, args))) )); } } return(ExpressionShortcuts.Call(() => CaptureResult(textWriter, ExpressionShortcuts.Call(() => LateBindHelperExpression(bindingContext, helperName, args) )) )); }
protected override Expression VisitIteratorExpression(IteratorExpression iex) { var context = CompilationContext.Args.BindingContext; var writer = CompilationContext.Args.EncodedWriter; var template = FunctionBuilder.Compile(new[] { iex.Template }, new CompilationContext(CompilationContext)); var ifEmpty = FunctionBuilder.Compile(new[] { iex.IfEmpty }, new CompilationContext(CompilationContext)); if (iex.Sequence is PathExpression pathExpression) { pathExpression.Context = PathExpression.ResolutionContext.Parameter; } var compiledSequence = Arg <object>(FunctionBuilder.Reduce(iex.Sequence, CompilationContext)); var blockParamsValues = CreateBlockParams(); return(iex.HelperName[0] switch { '#' => Call(() => Iterator.Iterate(context, writer, blockParamsValues, compiledSequence, template, ifEmpty)), '^' => Call(() => Iterator.Iterate(context, writer, blockParamsValues, compiledSequence, ifEmpty, template)), _ => throw new HandlebarsCompilerException($"Tried to convert {iex.HelperName} expression to iterator block", iex.Context) });
public static ExpressionContainer <Arguments> CreateArguments(IEnumerable <Expression> expressions, CompilationContext compilationContext) { var arguments = expressions .ApplyOn <Expression, PathExpression>(path => path.Context = PathExpression.ResolutionContext.Parameter) .Select(o => FunctionBuilder.Reduce(o, compilationContext)) .ToArray(); if (arguments.Length == 0) { return(New(() => new Arguments(0))); } var constructor = ArgumentsConstructorsMap.GetOrAdd(arguments.Length, CtorFactory).Value; if (!ReferenceEquals(constructor, null)) { var newExpression = Expression.New(constructor, arguments); return(Arg <Arguments>(newExpression)); } var arr = Array <object>(arguments); return(New(() => new Arguments(arr))); }
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 = 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) });