Skip to content

Commit 83e41df

Browse files
authored
Make Liquid::C::Tokenizer#shift private so it is only used for testing (#140)
Since it won't be supported for liquid template (de)serialization.
1 parent bd5ef13 commit 83e41df

File tree

3 files changed

+43
-40
lines changed

3 files changed

+43
-40
lines changed

ext/liquid_c/tokenizer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,12 @@ void liquid_define_tokenizer()
287287

288288
rb_define_alloc_func(cLiquidTokenizer, tokenizer_allocate);
289289
rb_define_method(cLiquidTokenizer, "initialize", tokenizer_initialize_method, 3);
290-
rb_define_method(cLiquidTokenizer, "shift", tokenizer_shift_method, 0);
291290
rb_define_method(cLiquidTokenizer, "line_number", tokenizer_line_number_method, 0);
292291
rb_define_method(cLiquidTokenizer, "for_liquid_tag", tokenizer_for_liquid_tag_method, 0);
293292
rb_define_method(cLiquidTokenizer, "bug_compatible_whitespace_trimming!", tokenizer_bug_compatible_whitespace_trimming, 0);
294293

295294
// For testing the internal token representation.
295+
rb_define_private_method(cLiquidTokenizer, "shift", tokenizer_shift_method, 0);
296296
rb_define_private_method(cLiquidTokenizer, "shift_trimmed", tokenizer_shift_trimmed_method, 0);
297297
}
298298

lib/liquid/c.rb

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,18 @@ class Tokenizer
4141
end
4242
end
4343

44-
Liquid::Tokenizer.class_eval do
45-
def self.new(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
46-
source = source.to_s
47-
if Liquid::C.enabled && source.bytesize <= Liquid::C::Tokenizer::MAX_SOURCE_BYTE_SIZE
48-
source = source.encode(Encoding::UTF_8)
49-
Liquid::C::Tokenizer.new(source, line_number || (line_numbers ? 1 : 0), for_liquid_tag)
44+
Liquid::Raw.class_eval do
45+
alias_method :ruby_parse, :parse
46+
47+
def parse(tokenizer)
48+
if parse_context.liquid_c_nodes_disabled?
49+
ruby_parse(tokenizer)
5050
else
51-
super
51+
c_parse(tokenizer)
5252
end
5353
end
5454
end
5555

56-
Liquid::Raw.class_eval do
57-
alias_method :ruby_parse, :parse
58-
end
59-
6056
Liquid::ParseContext.class_eval do
6157
class << self
6258
attr_accessor :liquid_c_nodes_disabled
@@ -73,34 +69,42 @@ def new_block_body
7369
end
7470
end
7571

72+
alias_method :ruby_new_tokenizer, :new_tokenizer
73+
74+
def new_tokenizer(source, start_line_number: nil, for_liquid_tag: false)
75+
unless liquid_c_nodes_disabled?
76+
source = source.to_s
77+
if source.bytesize <= Liquid::C::Tokenizer::MAX_SOURCE_BYTE_SIZE
78+
source = source.encode(Encoding::UTF_8)
79+
return Liquid::C::Tokenizer.new(source, start_line_number || 0, for_liquid_tag)
80+
else
81+
@liquid_c_nodes_disabled = true
82+
end
83+
end
84+
85+
ruby_new_tokenizer(source, start_line_number: start_line_number, for_liquid_tag: for_liquid_tag)
86+
end
87+
7688
# @api private
7789
def liquid_c_nodes_disabled?
7890
# Liquid::Profiler exposes the internal parse tree that we don't want to build when
7991
# parsing with liquid-c, so consider liquid-c to be disabled when using it.
8092
# Also, some templates are parsed before the profiler is running, on which case we
8193
# provide the `disable_liquid_c_nodes` option to enable the Ruby AST to be produced
8294
# so the profiler can use it on future runs.
83-
@liquid_c_nodes_disabled ||= !Liquid::C.enabled || @template_options[:profile] ||
95+
return @liquid_c_nodes_disabled if defined?(@liquid_c_nodes_disabled)
96+
@liquid_c_nodes_disabled = !Liquid::C.enabled || @template_options[:profile] ||
8497
@template_options[:disable_liquid_c_nodes] || self.class.liquid_c_nodes_disabled
8598
end
86-
87-
# @api private
88-
attr_writer :liquid_c_nodes_disabled
8999
end
90100

91101
module Liquid
92102
module C
93103
module DocumentClassPatch
94104
def parse(tokenizer, parse_context)
95-
if tokenizer.is_a?(Liquid::C::Tokenizer)
105+
if tokenizer.is_a?(Liquid::C::Tokenizer) && parse_context[:bug_compatible_whitespace_trimming]
96106
# Temporary to test rollout of the fix for this bug
97-
if parse_context[:bug_compatible_whitespace_trimming]
98-
tokenizer.bug_compatible_whitespace_trimming!
99-
end
100-
else
101-
# Liquid::Tokenizer.new may return a Liquid::Tokenizer if the source is too large
102-
# to be supported, so indicate in the parse context that the liquid VM won't be used
103-
parse_context.liquid_c_nodes_disabled = true
107+
tokenizer.bug_compatible_whitespace_trimming!
104108
end
105109
super
106110
end
@@ -237,12 +241,10 @@ def enabled=(value)
237241
Liquid::Context.send(:alias_method, :evaluate, :c_evaluate)
238242
Liquid::Context.send(:alias_method, :find_variable, :c_find_variable_kwarg)
239243
Liquid::Context.send(:alias_method, :strict_variables=, :c_strict_variables=)
240-
Liquid::Raw.send(:alias_method, :parse, :c_parse)
241244
else
242245
Liquid::Context.send(:alias_method, :evaluate, :ruby_evaluate)
243246
Liquid::Context.send(:alias_method, :find_variable, :ruby_find_variable)
244247
Liquid::Context.send(:alias_method, :strict_variables=, :ruby_strict_variables=)
245-
Liquid::Raw.send(:alias_method, :parse, :ruby_parse)
246248
end
247249
end
248250
end

test/unit/tokenizer_test.rb

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
class TokenizerTest < Minitest::Test
66
def test_tokenizer_nil
7-
tokenizer = Liquid::Tokenizer.new(nil)
8-
assert_nil(tokenizer.shift)
7+
tokenizer = new_tokenizer(nil)
8+
assert_nil(tokenizer.send(:shift))
99
end
1010

1111
def test_tokenize_strings
@@ -58,11 +58,11 @@ def test_utf8_encoded_source
5858

5959
def test_utf8_compatible_source
6060
source = String.new('ascii', encoding: Encoding::ASCII)
61-
tokenizer = Liquid::Tokenizer.new(source)
62-
output = tokenizer.shift
61+
tokenizer = new_tokenizer(source)
62+
output = tokenizer.send(:shift)
6363
assert_equal(Encoding::UTF_8, output.encoding)
6464
assert_equal(source, output)
65-
assert_nil(tokenizer.shift)
65+
assert_nil(tokenizer.send(:shift))
6666
end
6767

6868
def test_non_utf8_compatible_source
@@ -84,26 +84,27 @@ def test_source_too_large
8484
assert_match(/Source too large, max \d+ bytes/, err.message)
8585

8686
# ruby patch fallback
87-
liquid_c_tokenizer = Liquid::Tokenizer.new(max_length_source)
87+
parse_context = Liquid::ParseContext.new
88+
liquid_c_tokenizer = parse_context.new_tokenizer(max_length_source)
8889
assert_instance_of(Liquid::C::Tokenizer, liquid_c_tokenizer)
89-
fallback_tokenizer = Liquid::Tokenizer.new(too_large_source)
90-
assert_instance_of(Liquid::Tokenizer, fallback_tokenizer)
90+
refute(parse_context.liquid_c_nodes_disabled?)
9191

92-
# Document.parse patch parse context update
9392
parse_context = Liquid::ParseContext.new
94-
refute(parse_context.liquid_c_nodes_disabled?)
95-
Liquid::Document.parse(liquid_c_tokenizer, parse_context)
96-
refute(parse_context.liquid_c_nodes_disabled?)
97-
Liquid::Document.parse(fallback_tokenizer, parse_context)
93+
fallback_tokenizer = parse_context.new_tokenizer(too_large_source)
94+
assert_instance_of(Liquid::Tokenizer, fallback_tokenizer)
9895
assert_equal(true, parse_context.liquid_c_nodes_disabled?)
9996
end
10097

10198
private
10299

100+
def new_tokenizer(source, parse_context: Liquid::ParseContext.new)
101+
parse_context.new_tokenizer(source)
102+
end
103+
103104
def tokenize(source, for_liquid_tag: false, trimmed: false)
104105
tokenizer = Liquid::C::Tokenizer.new(source, 1, for_liquid_tag)
105106
tokens = []
106-
while (t = trimmed ? tokenizer.send(:shift_trimmed) : tokenizer.shift)
107+
while (t = trimmed ? tokenizer.send(:shift_trimmed) : tokenizer.send(:shift))
107108
tokens << t
108109
end
109110
tokens

0 commit comments

Comments
 (0)