diff options
Diffstat (limited to 'ext/psych/lib/psych/visitors')
-rw-r--r-- | ext/psych/lib/psych/visitors/json_tree.rb | 2 | ||||
-rw-r--r-- | ext/psych/lib/psych/visitors/to_ruby.rb | 36 | ||||
-rw-r--r-- | ext/psych/lib/psych/visitors/visitor.rb | 2 | ||||
-rw-r--r-- | ext/psych/lib/psych/visitors/yaml_tree.rb | 96 |
4 files changed, 92 insertions, 44 deletions
diff --git a/ext/psych/lib/psych/visitors/json_tree.rb b/ext/psych/lib/psych/visitors/json_tree.rb index 9912cb1362..979fc100bd 100644 --- a/ext/psych/lib/psych/visitors/json_tree.rb +++ b/ext/psych/lib/psych/visitors/json_tree.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -require 'psych/json/ruby_events' +require_relative '../json/ruby_events' module Psych module Visitors diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index ec80701917..f0fda9bdbc 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'psych/scalar_scanner' -require 'psych/class_loader' -require 'psych/exception' +require_relative '../scalar_scanner' +require_relative '../class_loader' +require_relative '../exception' unless defined?(Regexp::NOENCODING) Regexp::NOENCODING = 32 @@ -12,9 +12,9 @@ module Psych ### # This class walks a YAML AST, converting each node to Ruby class ToRuby < Psych::Visitors::Visitor - def self.create(symbolize_names: false, freeze: false) + def self.create(symbolize_names: false, freeze: false, strict_integer: false) class_loader = ClassLoader.new - scanner = ScalarScanner.new class_loader + scanner = ScalarScanner.new class_loader, strict_integer: strict_integer new(scanner, class_loader, symbolize_names: symbolize_names, freeze: freeze) end @@ -24,6 +24,7 @@ module Psych super() @st = {} @ss = ss + @load_tags = Psych.load_tags @domain_types = Psych.domain_types @class_loader = class_loader @symbolize_names = symbolize_names @@ -48,7 +49,7 @@ module Psych end def deserialize o - if klass = resolve_class(Psych.load_tags[o.tag]) + if klass = resolve_class(@load_tags[o.tag]) instance = klass.allocate if instance.respond_to?(:init_with) @@ -79,7 +80,9 @@ module Psych when "!ruby/object:DateTime" class_loader.date_time require 'date' unless defined? DateTime - @ss.parse_time(o.value).to_datetime + t = @ss.parse_time(o.value) + DateTime.civil(*t.to_a[0, 6].reverse, Rational(t.utc_offset, 86400)) + + (t.subsec/86400) when '!ruby/encoding' ::Encoding.find o.value when "!ruby/object:Complex" @@ -98,7 +101,7 @@ module Psych source = $1 options = 0 lang = nil - ($2 || '').split('').each do |option| + $2&.each_char do |option| case option when 'x' then options |= Regexp::EXTENDED when 'i' then options |= Regexp::IGNORECASE @@ -128,7 +131,7 @@ module Psych end def visit_Psych_Nodes_Sequence o - if klass = resolve_class(Psych.load_tags[o.tag]) + if klass = resolve_class(@load_tags[o.tag]) instance = klass.allocate if instance.respond_to?(:init_with) @@ -160,8 +163,8 @@ module Psych end def visit_Psych_Nodes_Mapping o - if Psych.load_tags[o.tag] - return revive(resolve_class(Psych.load_tags[o.tag]), o) + if @load_tags[o.tag] + return revive(resolve_class(@load_tags[o.tag]), o) end return revive_hash(register(o, {}), o) unless o.tag @@ -322,10 +325,11 @@ module Psych end def visit_Psych_Nodes_Alias o - @st.fetch(o.anchor) { raise BadAlias, "Unknown alias: #{o.anchor}" } + @st.fetch(o.anchor) { raise AnchorNotDefined, o.anchor } end private + def register node, object @st[node.anchor] = object if node.anchor object @@ -337,7 +341,7 @@ module Psych list end - def revive_hash hash, o + def revive_hash hash, o, tagged= false o.children.each_slice(2) { |k,v| key = accept(k) val = accept(v) @@ -364,7 +368,7 @@ module Psych hash[key] = val end else - if @symbolize_names + if !tagged && @symbolize_names && key.is_a?(String) key = key.to_sym elsif !@freeze key = deduplicate(key) @@ -402,7 +406,7 @@ module Psych def revive klass, node s = register(node, klass.allocate) - init_with(s, revive_hash({}, node), node) + init_with(s, revive_hash({}, node, true), node) end def init_with o, h, node @@ -425,7 +429,7 @@ module Psych class NoAliasRuby < ToRuby def visit_Psych_Nodes_Alias o - raise BadAlias, "Unknown alias: #{o.anchor}" + raise AliasesNotEnabled end end end diff --git a/ext/psych/lib/psych/visitors/visitor.rb b/ext/psych/lib/psych/visitors/visitor.rb index e2585c0c77..21052aa66f 100644 --- a/ext/psych/lib/psych/visitors/visitor.rb +++ b/ext/psych/lib/psych/visitors/visitor.rb @@ -17,7 +17,7 @@ module Psych if defined?(Ractor) def dispatch - Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache + @dispatch_cache ||= (Ractor.current[:Psych_Visitors_Visitor] ||= Visitor.dispatch_cache) end else DISPATCH = dispatch_cache diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb index ac6777aeb5..a2ebc4d781 100644 --- a/ext/psych/lib/psych/visitors/yaml_tree.rb +++ b/ext/psych/lib/psych/visitors/yaml_tree.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'psych/tree_builder' -require 'psych/scalar_scanner' -require 'psych/class_loader' +require_relative '../tree_builder' +require_relative '../scalar_scanner' +require_relative '../class_loader' module Psych module Visitors @@ -15,30 +15,25 @@ module Psych class YAMLTree < Psych::Visitors::Visitor class Registrar # :nodoc: def initialize - @obj_to_id = {} - @obj_to_node = {} - @targets = [] + @obj_to_id = {}.compare_by_identity + @obj_to_node = {}.compare_by_identity @counter = 0 end def register target, node - return unless target.respond_to? :object_id - @targets << target - @obj_to_node[target.object_id] = node + @obj_to_node[target] = node end def key? target - @obj_to_node.key? target.object_id - rescue NoMethodError - false + @obj_to_node.key? target end def id_for target - @obj_to_id[target.object_id] ||= (@counter += 1) + @obj_to_id[target] ||= (@counter += 1) end def node_for target - @obj_to_node[target.object_id] + @obj_to_node[target] end end @@ -70,6 +65,7 @@ module Psych fail(ArgumentError, "Invalid line_width #{@line_width}, must be non-negative or -1 for unlimited.") end end + @stringify_names = options[:stringify_names] @coders = [] @dispatch_cache = Hash.new do |h,klass| @@ -192,12 +188,13 @@ module Psych register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY) end + def visit_Date o + register o, visit_Integer(o.gregorian) + end + def visit_DateTime o - formatted = if o.offset.zero? - o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze) - else - o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze) - end + t = o.italy + formatted = format_time t, t.offset.zero? tag = '!ruby/object:DateTime' register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY) end @@ -235,7 +232,6 @@ module Psych end alias :visit_TrueClass :visit_Integer alias :visit_FalseClass :visit_Integer - alias :visit_Date :visit_Integer def visit_Float o if o.nan? @@ -272,6 +268,8 @@ module Psych tag = 'tag:yaml.org,2002:str' plain = false quote = false + elsif o == 'y' || o == 'Y' || o == 'n' || o == 'N' + style = Nodes::Scalar::DOUBLE_QUOTED elsif @line_width && o.length > @line_width style = Nodes::Scalar::FOLDED elsif o =~ /^[^[:word:]][^"]*$/ @@ -326,7 +324,7 @@ module Psych if o.class == ::Hash register(o, @emitter.start_mapping(nil, nil, true, Psych::Nodes::Mapping::BLOCK)) o.each do |k,v| - accept k + accept(@stringify_names && Symbol === k ? k.to_s : k) accept v end @emitter.end_mapping @@ -339,7 +337,7 @@ module Psych register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK)) o.each do |k,v| - accept k + accept(@stringify_names && Symbol === k ? k.to_s : k) accept v end @@ -480,8 +478,8 @@ module Psych @emitter.end_mapping end - def format_time time - if time.utc? + def format_time time, utc = time.utc? + if utc time.strftime("%Y-%m-%d %H:%M:%S.%9N Z") else time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z") @@ -509,9 +507,9 @@ module Psych def emit_coder c, o case c.type when :scalar - @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY + @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, c.style when :seq - @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK + @emitter.start_sequence nil, c.tag, c.tag.nil?, c.style c.seq.each do |thing| accept thing end @@ -535,5 +533,51 @@ module Psych end end end + + class RestrictedYAMLTree < YAMLTree + DEFAULT_PERMITTED_CLASSES = { + TrueClass => true, + FalseClass => true, + NilClass => true, + Integer => true, + Float => true, + String => true, + Array => true, + Hash => true, + }.compare_by_identity.freeze + + def initialize emitter, ss, options + super + @permitted_classes = DEFAULT_PERMITTED_CLASSES.dup + Array(options[:permitted_classes]).each do |klass| + @permitted_classes[klass] = true + end + @permitted_symbols = {}.compare_by_identity + Array(options[:permitted_symbols]).each do |symbol| + @permitted_symbols[symbol] = true + end + @aliases = options.fetch(:aliases, false) + end + + def accept target + if !@aliases && @st.key?(target) + raise BadAlias, "Tried to dump an aliased object" + end + + unless Symbol === target || @permitted_classes[target.class] + raise DisallowedClass.new('dump', target.class.name || target.class.inspect) + end + + super + end + + def visit_Symbol sym + unless @permitted_classes[Symbol] || @permitted_symbols[sym] + raise DisallowedClass.new('dump', "Symbol(#{sym.inspect})") + end + + super + end + end end end |