mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-20 03:10:39 -07:00
keep the expression parse caching per document parsing
This commit is contained in:
@@ -22,7 +22,7 @@ module Liquid
|
|||||||
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
||||||
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
||||||
|
|
||||||
def self.parse(markup, _ss = nil)
|
def self.parse(markup, _ss = nil, _cache = nil)
|
||||||
return nil unless markup
|
return nil unless markup
|
||||||
|
|
||||||
markup = markup.strip
|
markup = markup.strip
|
||||||
@@ -72,10 +72,8 @@ module Liquid
|
|||||||
INTEGER_REGEX = /\A(-?\d+)\z/
|
INTEGER_REGEX = /\A(-?\d+)\z/
|
||||||
FLOAT_REGEX = /\A(-?\d+)\.\d+\z/
|
FLOAT_REGEX = /\A(-?\d+)\.\d+\z/
|
||||||
|
|
||||||
CACHE = LruRedux::Cache.new(10_000) # most themes would have less than 2,000 unique expression
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def parse(markup, ss = StringScanner.new(""))
|
def parse(markup, ss = StringScanner.new(""), cache = nil)
|
||||||
return unless markup
|
return unless markup
|
||||||
|
|
||||||
markup = markup.strip # markup can be a frozen string
|
markup = markup.strip # markup can be a frozen string
|
||||||
@@ -87,24 +85,30 @@ module Liquid
|
|||||||
return LITERALS[markup]
|
return LITERALS[markup]
|
||||||
end
|
end
|
||||||
|
|
||||||
return CACHE[markup] if CACHE.key?(markup)
|
# Cache only exists during parsing
|
||||||
|
if cache
|
||||||
|
return cache[markup] if cache.key?(markup)
|
||||||
|
|
||||||
CACHE[markup] = inner_parse(markup, ss)
|
cache[markup] = inner_parse(markup, ss, cache)
|
||||||
|
else
|
||||||
|
inner_parse(markup, ss, nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inner_parse(markup, ss)
|
def inner_parse(markup, ss, cache)
|
||||||
if (markup.start_with?("(") && markup.end_with?(")")) && markup =~ RANGES_REGEX
|
if (markup.start_with?("(") && markup.end_with?(")")) && markup =~ RANGES_REGEX
|
||||||
return RangeLookup.parse(
|
return RangeLookup.parse(
|
||||||
Regexp.last_match(1),
|
Regexp.last_match(1),
|
||||||
Regexp.last_match(2),
|
Regexp.last_match(2),
|
||||||
ss,
|
ss,
|
||||||
|
cache,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
if (num = parse_number(markup, ss))
|
if (num = parse_number(markup, ss))
|
||||||
num
|
num
|
||||||
else
|
else
|
||||||
VariableLookup.parse(markup, ss)
|
VariableLookup.parse(markup, ss, cache)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class ParseContext
|
class ParseContext
|
||||||
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
||||||
attr_reader :partial, :warnings, :error_mode, :environment, :string_scanner
|
attr_reader :partial, :warnings, :error_mode, :environment, :string_scanner, :expression_cache
|
||||||
|
|
||||||
def initialize(options = Const::EMPTY_HASH)
|
def initialize(options = Const::EMPTY_HASH)
|
||||||
@environment = options.fetch(:environment, Environment.default)
|
@environment = options.fetch(:environment, Environment.default)
|
||||||
@@ -15,6 +15,7 @@ module Liquid
|
|||||||
# constructing new StringScanner in Lexer, Tokenizer, etc is expensive
|
# constructing new StringScanner in Lexer, Tokenizer, etc is expensive
|
||||||
# This StringScanner will be shared by all of them
|
# This StringScanner will be shared by all of them
|
||||||
@string_scanner = StringScanner.new("")
|
@string_scanner = StringScanner.new("")
|
||||||
|
@expression_cache = LruRedux::Cache.new(10_000)
|
||||||
|
|
||||||
self.depth = 0
|
self.depth = 0
|
||||||
self.partial = false
|
self.partial = false
|
||||||
@@ -42,7 +43,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_expression(markup)
|
def parse_expression(markup)
|
||||||
Expression.parse(markup, string_scanner)
|
Expression.parse(markup, string_scanner, expression_cache)
|
||||||
end
|
end
|
||||||
|
|
||||||
def partial=(value)
|
def partial=(value)
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class RangeLookup
|
class RangeLookup
|
||||||
def self.parse(start_markup, end_markup, string_scanner)
|
def self.parse(start_markup, end_markup, string_scanner, cache = nil)
|
||||||
start_obj = Expression.parse(start_markup, string_scanner)
|
start_obj = Expression.parse(start_markup, string_scanner, cache)
|
||||||
end_obj = Expression.parse(end_markup, string_scanner)
|
end_obj = Expression.parse(end_markup, string_scanner, cache)
|
||||||
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
||||||
new(start_obj, end_obj)
|
new(start_obj, end_obj)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ module Liquid
|
|||||||
|
|
||||||
attr_reader :name, :lookups
|
attr_reader :name, :lookups
|
||||||
|
|
||||||
def self.parse(markup, string_scanner)
|
def self.parse(markup, string_scanner, cache = nil)
|
||||||
new(markup, string_scanner)
|
new(markup, string_scanner, cache)
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(markup, string_scanner = StringScanner.new(""))
|
def initialize(markup, string_scanner = StringScanner.new(""), cache = nil)
|
||||||
lookups = markup.scan(VariableParser)
|
lookups = markup.scan(VariableParser)
|
||||||
|
|
||||||
name = lookups.shift
|
name = lookups.shift
|
||||||
@@ -18,6 +18,7 @@ module Liquid
|
|||||||
name = Expression.parse(
|
name = Expression.parse(
|
||||||
name[1..-2],
|
name[1..-2],
|
||||||
string_scanner,
|
string_scanner,
|
||||||
|
cache,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@name = name
|
@name = name
|
||||||
@@ -31,6 +32,7 @@ module Liquid
|
|||||||
lookups[i] = Expression.parse(
|
lookups[i] = Expression.parse(
|
||||||
lookup[1..-2],
|
lookup[1..-2],
|
||||||
string_scanner,
|
string_scanner,
|
||||||
|
cache,
|
||||||
)
|
)
|
||||||
elsif COMMAND_METHODS.include?(lookup)
|
elsif COMMAND_METHODS.include?(lookup)
|
||||||
@command_flags |= 1 << i
|
@command_flags |= 1 << i
|
||||||
|
|||||||
Reference in New Issue
Block a user