diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 32a7d90..266d8ed 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -70,33 +70,27 @@ module Liquid output = [] context.resource_limits.render_score += @nodelist.length - @nodelist.each do |token| - # Break out if we have any unhanded interrupts. - break if context.interrupt? - - begin + idx = 0 + while node = @nodelist[idx] + case node + when String + check_resources(context, node) + output << node + when Variable + render_node_to_output(node, output, context) + when Block + render_node_to_output(node, output, context, node.blank?) + break if context.interrupt? # might have happened in a for-block + when Continue, Break # If we get an Interrupt that means the block must stop processing. An # Interrupt is any command that stops block execution such as {% break %} # or {% continue %} - if token.is_a?(Continue) || token.is_a?(Break) - context.push_interrupt(token.interrupt) - break - end - - node_output = render_node(token, context) - - unless token.is_a?(Block) && token.blank? - output << node_output - end - rescue MemoryError => e - raise e - rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e - context.handle_error(e, token.line_number) - output << nil - rescue ::StandardError => e - line_number = token.is_a?(String) ? nil : token.line_number - output << context.handle_error(e, line_number) + context.push_interrupt(node.interrupt) + break + else # Other non-Block tags + render_node_to_output(node, output, context) end + idx += 1 end output.join @@ -104,15 +98,25 @@ module Liquid private - def render_node(node, context) - node_output = node.is_a?(String) ? node : node.render(context) + def render_node_to_output(node, output, context, skip_output = false) + node_output = node.render(context) node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s + check_resources(context, node_output) + output << node_output unless skip_output + rescue MemoryError => e + raise e + rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e + context.handle_error(e, node.line_number) + output << nil + rescue ::StandardError => e + line_number = node.is_a?(String) ? nil : node.line_number + output << context.handle_error(e, line_number) + end + def check_resources(context, node_output) context.resource_limits.render_length += node_output.length - if context.resource_limits.reached? - raise MemoryError.new("Memory limits exceeded".freeze) - end - node_output + return unless context.resource_limits.reached? + raise MemoryError.new("Memory limits exceeded".freeze) end def create_variable(token, parse_context) diff --git a/lib/liquid/profiler/hooks.rb b/lib/liquid/profiler/hooks.rb index 6f8d798..cb11cd7 100644 --- a/lib/liquid/profiler/hooks.rb +++ b/lib/liquid/profiler/hooks.rb @@ -1,13 +1,13 @@ module Liquid class BlockBody - def render_node_with_profiling(node, context) + def render_node_with_profiling(node, output, context, skip_output = false) Profiler.profile_node_render(node) do - render_node_without_profiling(node, context) + render_node_without_profiling(node, output, context, skip_output) end end - alias_method :render_node_without_profiling, :render_node - alias_method :render_node, :render_node_with_profiling + alias_method :render_node_without_profiling, :render_node_to_output + alias_method :render_node_to_output, :render_node_with_profiling end class Include < Tag