Skip to content

Commit 7dae248

Browse files
committed
Implement freeze option for Pysch.load
1 parent 04f97f7 commit 7dae248

File tree

2 files changed

+27
-7
lines changed

2 files changed

+27
-7
lines changed

lib/psych/visitors/to_ruby.rb

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,18 @@ def initialize ss, class_loader, symbolize_names: false, freeze: false
3232

3333
def accept target
3434
result = super
35-
return result if @domain_types.empty? || !target.tag
3635

37-
key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
38-
key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/
36+
unless @domain_types.empty? || !target.tag
37+
key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:')
38+
key = "tag:#{key}" unless key =~ /^(?:tag:|x-private)/
3939

40-
if @domain_types.key? key
41-
value, block = @domain_types[key]
42-
return block.call value, result
40+
if @domain_types.key? key
41+
value, block = @domain_types[key]
42+
result = block.call value, result
43+
end
4344
end
4445

46+
result = deduplicate(result).freeze if @freeze
4547
result
4648
end
4749

@@ -341,7 +343,7 @@ def revive_hash hash, o
341343
key = accept(k)
342344
if @symbolize_names
343345
key = key.to_sym
344-
else
346+
elsif !@freeze
345347
key = deduplicate(key)
346348
end
347349
val = accept(v)
@@ -378,6 +380,8 @@ def revive_hash hash, o
378380
if RUBY_VERSION < '2.7'
379381
def deduplicate key
380382
if key.is_a?(String)
383+
# It is important to untaint the string, otherwise it won't
384+
# be deduplicated into an fstring, but simply frozen.
381385
-(key.untaint)
382386
else
383387
key

test/psych/test_psych.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ def test_domain_types
192192
assert_equal({ 'hello' => 'world' }, got)
193193
end
194194

195+
def test_load_freeze
196+
data = Psych.load("--- {foo: ['a']}", freeze: true)
197+
assert_predicate data, :frozen?
198+
assert_predicate data['foo'], :frozen?
199+
assert_predicate data['foo'].first, :frozen?
200+
end
201+
202+
def test_load_freeze_deduplication
203+
unless String.method_defined?(:-@) && (-("a" * 20)).equal?((-("a" * 20)))
204+
skip "This Ruby implementation doesn't support string deduplication"
205+
end
206+
207+
data = Psych.load("--- ['a']", freeze: true)
208+
assert_same 'a', data.first
209+
end
210+
195211
def test_load_default_fallback
196212
assert_equal false, Psych.load("")
197213
end

0 commit comments

Comments
 (0)