yeptris 0.6.15.1-aarch64-linux → 0.6.15.2-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df5a484e47dccdf0bd9d05ff80c01220a75c5c1f34ceb453573c0be635009570
4
- data.tar.gz: 726862488f90949222bce7c0a7c4ab1da885cfe872e630ed70ebf899e2503fe6
3
+ metadata.gz: 419470a0e038bec65880a3d764da46f072d7993e5aca9de71825a8428d5b2f98
4
+ data.tar.gz: 19cf714c7f80abc07d8729420700ef2c08bf1a140de03c3e11a8bb6ff177a4f4
5
5
  SHA512:
6
- metadata.gz: 758dafc8487908e663705fb7a67fe08d2a825edb2e0a359163f5d591a1ddcb6d8d59c67f6f745ccc2dcdfc777bd3185419bb316c26d33df152a31834d80e536c
7
- data.tar.gz: 76772fdb111e3fbbd6ccd556e8fa966bb7311010c729c35d996f1399eb5e4d64a70385336c2ca8cd3d71743fbac5ca50d7e8e06fd179396d9dd29115a3c363b9
6
+ metadata.gz: 9d11c53e5305c76f780f431d73be9c62228e250ab164c4fd636429ad4fca4e3705b1d46b0e9cf7abed5d8a0662d0347a2399de0ce3a16f5eec987d05d0fb053f
7
+ data.tar.gz: 5c24e95902b4ae44595710274ede3efbd605885d8cbc0747fc0965fcb8a2addb32ea8f18648606fef823d09a6f106d777d175f4e8ed0f1c8e74810f22d6cbee6
@@ -20,7 +20,7 @@ class Yeptris::Document
20
20
  # always yields the same Ruby object (identity for aliases/eql?),
21
21
  # cleared at free — no stale entries, no GC-race weak maps.
22
22
  @wrapper_cache = {}
23
- ObjectSpace.define_finalizer(self, self.class.finalize(c_ptr, freed)) unless c_ptr.null?
23
+ ObjectSpace.define_finalizer(self, self.class.finalize(c_ptr)) unless c_ptr.null?
24
24
  end
25
25
 
26
26
  def self.parse(yaml, schema: :core_12, max_depth: 0)
@@ -88,6 +88,9 @@ class Yeptris::Document
88
88
  return if @freed.state == :freed
89
89
 
90
90
  @freed.state = :freed
91
+ # stop the GC finalizer BEFORE the C free — it captures the same
92
+ # pointer, and its firing after this free is the double free
93
+ ObjectSpace.undefine_finalizer(self)
91
94
  @wrapper_cache.clear
92
95
  Yeptris::FFI.yeptris_document_free(@c_ptr)
93
96
  end
@@ -221,12 +224,15 @@ class Yeptris::Document
221
224
  self
222
225
  end
223
226
 
224
- # GC safety net: an explicit #free already ran is fine; a miss here
225
- # frees C memory that would otherwise leak.
226
- def self.finalize(c_ptr, freed)
227
- proc do
228
- Yeptris::FFI.yeptris_document_free(c_ptr) if freed.state == :alive
229
- freed.state = :freed
230
- end
227
+ # GC safety net (#182): the proc captures ONLY the raw pointer — no
228
+ # Ruby objects, no wrapper, no freed-flag struct. A finalizer that
229
+ # closes over the owning wrapper races that wrapper's own sweep
230
+ # (the classic use-after-free: freed?/free against half-collected
231
+ # ivars, then a garbage C pointer into yeptris_document_free →
232
+ # SIGABRT). Explicit #free undefines this finalizer BEFORE the C
233
+ # free, so the double free cannot happen; finalizers run on the
234
+ # main thread, so there is no cross-thread window.
235
+ def self.finalize(c_ptr)
236
+ proc { Yeptris::FFI.yeptris_document_free(c_ptr) }
231
237
  end
232
238
  end
@@ -115,7 +115,11 @@ module Yeptris
115
115
  end
116
116
 
117
117
  def parse_timestamp(v)
118
- return Date.parse(v) unless v.match?(/[Tt ]\d/)
118
+ # stdlib scalar_scanner: date-only strings parse with
119
+ # strptime('%F', Date::GREGORIAN) — the PROLEPTIC calendar —
120
+ # not Date.parse's default ITALY reform (a 1582 cycle fails
121
+ # the ported test_julian_date otherwise)
122
+ return ::Date.strptime(v, "%F", ::Date::GREGORIAN) unless v.match?(/[Tt ]\d/)
119
123
 
120
124
  # normalize the YAML 1.1 space forms onto iso8601 for
121
125
  # xmlschema: "2001-12-14 21:59:43.10 -05:00" ->
data/lib/yeptris/node.rb CHANGED
@@ -394,6 +394,15 @@ class Yeptris::Node
394
394
  def scalar_to_ruby
395
395
  return symbolize if symbol? && tag_id == :str
396
396
 
397
+ # stdlib deserialize's tagged-scalar arms: the class tag carries
398
+ # DateTime (dumped tagged — a plain timestamp re-loads as Time)
399
+ if tag == "!ruby/object:DateTime"
400
+ ts = value.to_s.sub(/ (\d)/, 'T\\1').sub(/ ([+-]\d)/, '\\1')
401
+ t = ::Time.xmlschema(ts)
402
+ return ::DateTime.civil(t.year, t.month, t.day, t.hour, t.min, t.sec,
403
+ Rational(t.utc_offset, 86_400)) + Rational(t.subsec, 86_400)
404
+ end
405
+
397
406
  case tag_id
398
407
  when :null then nil
399
408
  when :bool then to_bool
@@ -66,11 +66,34 @@ module Yeptris
66
66
  end
67
67
  end
68
68
 
69
+ # stdlib register()s every object — scalars included — so a
70
+ # repeated scalar dumps &name/*name (test_float_references,
71
+ # test_alias_with_time). Containers already route through
72
+ # anchor_for; this is the scalar arm of the same machinery.
73
+ def anchored_scalar(obj, text: nil, tag: nil)
74
+ state, name = anchor_for(obj)
75
+ return alias_of(obj, name) if state == :alias
76
+
77
+ n = scalar(text || obj, tag: tag)
78
+ n.set_anchor(name) if name
79
+ remember(obj, n)
80
+ n
81
+ end
82
+
69
83
  def visit(obj)
70
84
  case obj
85
+ # strings/floats/ints: stdlib yaml_tree does NOT anchor them
86
+ # (its float test is load-side only) — and CBOR encode
87
+ # rejects the anchors the cbor profile feeds it
71
88
  when nil, true, false, ::Integer, ::Float, ::String then scalar(obj)
72
89
  when ::Symbol then @tree.new_scalar(":#{obj}", :plain) # psych emits symbols bare, never through visit_String's quoting rules
73
- when ::Date, ::Time then scalar(obj) # timestamp text, never ivars
90
+ # stdlib visit_DateTime: the class tag carries the type (a
91
+ # plain timestamp re-loads as Time) — "!ruby/object:DateTime
92
+ # 2017-04-13 12:00:00.500000000 +09:00", to_s text, one line
93
+ when ::DateTime
94
+ anchored_scalar(obj, text: obj.strftime("%F %H:%M:%S.%9N %:z"),
95
+ tag: "!ruby/object:DateTime")
96
+ when ::Date, ::Time then anchored_scalar(obj) # timestamp text, never ivars
74
97
  when ::Hash then visit_hash(obj)
75
98
  when ::Array then visit_array(obj)
76
99
  when ::Struct then visit_struct(obj)
@@ -101,7 +124,7 @@ module Yeptris
101
124
  if @refs[oid] < 2
102
125
  return [:none, nil]
103
126
  end
104
- name = "o#{@counter += 1}"
127
+ name = (@counter += 1).to_s # stdlib: &1, *1
105
128
  @names[oid] = name
106
129
  [:new, name]
107
130
  end
@@ -156,7 +179,7 @@ module Yeptris
156
179
  # neutral surface alone was pinned)
157
180
  ::Yeptris::YAML::BulkBuilder.time_text(obj)
158
181
  elsif obj.is_a?(::Date)
159
- obj.iso8601 # canonical calendar form (DateTime rides here too)
182
+ obj.iso8601 # canonical calendar form
160
183
  elsif obj.nil?
161
184
  "" # libyaml's null rendering rides bare (issue #290)
162
185
  elsif obj.is_a?(::Float)
@@ -165,7 +188,7 @@ module Yeptris
165
188
  obj.to_s
166
189
  end
167
190
  n =
168
- if obj.is_a?(::String)
191
+ if obj.is_a?(::String) && !tag
169
192
  ::Yeptris::YAML::Builder.build_string(@tree, obj)
170
193
  else
171
194
  @tree.new_scalar(text, :plain)
@@ -220,7 +243,7 @@ module Yeptris
220
243
  m = @tree.new_mapping
221
244
  remember(strct, m)
222
245
  m.set_anchor(name) if name
223
- m.set_tag(strct.class.name ? "!ruby/struct:#{strct.class.name}" : "!ruby/struct")
246
+ m.set_tag(strct.class.name.to_s.empty? ? "!ruby/struct" : "!ruby/struct:#{strct.class.name}")
224
247
  strct.each_pair { |k, v| m.map_add(key_text(k), visit(v)) }
225
248
  m
226
249
  end
@@ -326,6 +349,9 @@ module Yeptris
326
349
  when ::Yeptris::Psych::Nodes::Mapping then visit_mapping(node)
327
350
  else node&.to_ruby
328
351
  end
352
+ # stdlib register(): an anchored node's result is THE object
353
+ # for every alias to that anchor (scalars included)
354
+ anchors[node.anchor] = result if node.respond_to?(:anchor) && node.anchor
329
355
  # the domain-types post-pass (stdlib ToRuby#accept's tail):
330
356
  # registered blocks transform the revived result per node
331
357
  if node && !::Yeptris::Psych.domain_types.empty?
data/lib/yeptris/psych.rb CHANGED
@@ -299,10 +299,11 @@ module Yeptris
299
299
  (0...doc.document_count).each do |i|
300
300
  stream.children << Nodes::Builder.document_stream_child(doc, i)
301
301
  end
302
+ # ownership: the DOCUMENT wrapper is the sole owner — its own
303
+ # finalizer (pointer-only closure) frees the C memory; the
304
+ # stream merely references it. A tree-side finalizer closing
305
+ # over the wrapper raced the wrapper's sweep (#182's abort).
302
306
  stream.owner = doc
303
- ObjectSpace.define_finalizer(
304
- stream, proc { doc.free unless doc.freed? }
305
- )
306
307
  stream
307
308
  rescue Yeptris::ParseError => e
308
309
  raise SyntaxError.from_parse_error(e)
@@ -327,6 +328,11 @@ module Yeptris
327
328
  # data anyway
328
329
  out =
329
330
  case obj
331
+ # DateTime < Date in ruby's hierarchy — the fast scalar
332
+ # path would emit it tag-less iso8601 (re-loading as Time);
333
+ # the visitor carries stdlib's !ruby/object:DateTime tag
334
+ when ::DateTime
335
+ Visitors::YAMLTree.new.push(obj).finish
330
336
  when nil, true, false, ::String, ::Integer, ::Float, ::Symbol, ::Date, ::Time
331
337
  Yeptris::YAML.dump(obj, header: true)
332
338
  else
@@ -442,12 +448,13 @@ module Yeptris
442
448
  @tags = tags
443
449
  end
444
450
 
445
- # @api private — transfer of C ownership to this tree
451
+ # @api private — attach the document: the tree REFERENCES it
452
+ # (keeping the C memory alive while the tree is reachable);
453
+ # the wrapper's own finalizer is the sole freer (#182: a
454
+ # tree-side finalizer closing over the wrapper raced the
455
+ # wrapper's sweep — SIGABRT under document churn)
446
456
  def own(yeptris_doc)
447
457
  @owner = yeptris_doc
448
- ObjectSpace.define_finalizer(
449
- self, proc { yeptris_doc.free unless yeptris_doc.freed? }
450
- )
451
458
  self
452
459
  end
453
460
 
data/lib/yeptris.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Yeptris
4
4
  # The gem's version lives in the parent namespace's file — the last
5
5
  # internal require (yeptris/version) retired with it.
6
- VERSION = "0.6.15.1".freeze
6
+ VERSION = "0.6.15.2".freeze
7
7
  # The error hierarchy lives in THIS file (the parent namespace's
8
8
  # own file): nested constants do not trigger a parent-constant
9
9
  # autoload, and the law forbids internal requires — defining the
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yeptris
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.15.1
4
+ version: 0.6.15.2
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.