moxml 0.5.124 → 0.5.126

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: 23e5ad1aa792190fa5588d08c6869fabfb6c7984ec4959d05d21add53ebf3ab3
4
- data.tar.gz: a7bbc8695859d2b84b4e65e8abfe5a4922d52056be0d81a483cdd82be00f6c91
3
+ metadata.gz: 71f2ce65d604babc4d29753cb99b353f6f9fafa4a8379f913d109aa4e3cb1aba
4
+ data.tar.gz: bb11508d1a28d87de8b3bcca4a6913ce40d29ac7152dfbee4311db797873ea14
5
5
  SHA512:
6
- metadata.gz: 6b970f9b7d6ecc4d64d19ce08d9d84ffa438bcafa93c48935ed35b91113513488fa4b7f64b263832e6b6e33317ae1b775c12706b50961f153f0cb909144f4c02
7
- data.tar.gz: 57d5ac6080f8e5ad8a85511dfb22e4598c7d5c546c66f71c5f5493339f83f182add0f5da51164fb0ec6f25af94b0e1397de2711a234229c74c68afc23741de2b
6
+ metadata.gz: bca5457e68b0d51547a72f440097d5203fc469654f9360d6dfb6c3d0f8f8d8a6cf921e3e057e3bd9ef8c6ad86823c80057d5cb171474d27f4ca46e5de329a715
7
+ data.tar.gz: c382a8f7eb2e23a116021b47d785e6757e04961576b12f2f452c57d01e946ee121da96ff2b77e77c6d5539e49110dc5af32fcf0a4db208906d5852251ffa978e
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ gemspec
9
9
  gem "benchmark"
10
10
  gem "benchmark-ips"
11
11
  gem "get_process_mem"
12
- gem "leptris", "~> 1.9.338"
12
+ gem "leptris", "~> 1.9.341"
13
13
  gem "libxml-ruby", ">= 5.0"
14
14
  gem "nokogiri", "~> 1.18"
15
15
  gem "openssl", "~> 3.0"
data/lib/moxml/element.rb CHANGED
@@ -184,22 +184,55 @@ module Moxml
184
184
 
185
185
  # Nokogiri-compatible: children= replaces the entire child list
186
186
  def children=(node_or_nodes)
187
+ source_docs = nil
187
188
  natives = case node_or_nodes
188
189
  when String
189
190
  wrapper = "_moxml_children_#{Process.pid}_#{object_id}"
190
- context.parse("<#{wrapper}>#{node_or_nodes}</#{wrapper}>")
191
- .root.children.map(&:native)
192
- when Moxml::Node then [node_or_nodes.native]
193
- when Array, NodeSet then node_or_nodes.map(&:native)
191
+ wrapper_doc = context.parse("<#{wrapper}>#{node_or_nodes}</#{wrapper}>")
192
+ # #355: engines that move/copy across documents by
193
+ # reference (leptris absorb) leave the attached
194
+ # nodes pool-owned by the SOURCE document; the
195
+ # wrapper is otherwise unreferenced and its free
196
+ # can dangle the attached subtree (windows GC
197
+ # timing). Retain it for this element's lifetime.
198
+ source_docs = [wrapper_doc]
199
+ wrapper_doc.root.children.map(&:native)
200
+ when Moxml::Node
201
+ retain_children_source(node_or_nodes.document)
202
+ [node_or_nodes.native]
203
+ when Array, NodeSet
204
+ node_or_nodes.each { |n| retain_children_source(n.document) }
205
+ node_or_nodes.map(&:native)
194
206
  else
195
207
  raise ArgumentError, "children= accepts String, Node, or Array"
196
208
  end
197
209
  adapter.replace_children(@native, natives)
210
+ retain_children_source_docs(source_docs) if source_docs
198
211
  @text = nil
199
212
  context.bump_children_generation
200
213
  invalidate_children_cache!
201
214
  end
202
215
 
216
+ # #355: keep every distinct source document whose nodes were
217
+ # attached here reachable for as long as this element lives —
218
+ # absorbed/moved pools must not be freed out from under the
219
+ # tree. Accumulates: a pool absorbed by this document stays
220
+ # referenced even after later children= calls replace the
221
+ # nodes that came from it.
222
+ def retain_children_source(source_doc)
223
+ retain_children_source_docs([source_doc])
224
+ end
225
+
226
+ def retain_children_source_docs(docs)
227
+ docs = docs.compact.uniq
228
+ docs.delete_if { |d| d.equal?(document) }
229
+ return if docs.empty?
230
+
231
+ existing = adapter.attachments.get(@native, :children_source_docs)
232
+ merged = existing ? (existing + docs).uniq : docs
233
+ adapter.attachments.set(@native, :children_source_docs, merged)
234
+ end
235
+
203
236
  def remove_attribute(name)
204
237
  # Both remove paths end in invalidate_attribute_cache!, which
205
238
  # bumps the generation.
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.124"
4
+ VERSION = "0.5.126"
5
5
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # #355: children= attaches nodes whose engine storage may be
6
+ # pool-owned by a SOURCE document (leptris absorbs/moves across
7
+ # documents by reference). The String branch's wrapper document is
8
+ # otherwise unreferenced — collected, its free can dangle the
9
+ # attached subtree (windows GC timing, standoc#1272). The element
10
+ # retains every distinct foreign source document for its lifetime.
11
+ RSpec.describe "children= source retention (#355)" do
12
+ %i[leptris nokogiri].each do |adapter_name|
13
+ describe "on #{adapter_name}" do
14
+ let(:ctx) { Moxml.new(adapter_name) }
15
+
16
+ it "retains the String branch's wrapper document" do
17
+ el = ctx.parse("<bibitem><t>old</t></bibitem>").at("//bibitem")
18
+ el.children = "<contributor><role>author</role></contributor>"
19
+ sources = ctx.config.adapter.attachments.get(el.native, :children_source_docs)
20
+ expect(sources).not_to be_nil
21
+ expect(sources.size).to eq(1)
22
+ GC.start
23
+ expect(el.to_xml).to include("author")
24
+ end
25
+
26
+ it "retains cross-document sources from the Array branch" do
27
+ el = ctx.parse("<r/>").root
28
+ other = ctx.parse("<other><deep>v</deep></other>")
29
+ el.children = other.root.children.to_a
30
+ sources = ctx.config.adapter.attachments.get(el.native, :children_source_docs)
31
+ expect(sources).to eq([other])
32
+ GC.start
33
+ expect(el.to_xml).to include("deep")
34
+ end
35
+
36
+ it "accumulates distinct sources across repeated children= calls" do
37
+ el = ctx.parse("<r/>").root
38
+ other_a = ctx.parse("<a><x/></a>")
39
+ other_b = ctx.parse("<b><y/></b>")
40
+ el.children = other_a.root.children.to_a
41
+ el.children = other_b.root.children.to_a
42
+ sources = ctx.config.adapter.attachments.get(el.native, :children_source_docs)
43
+ expect(sources).to contain_exactly(other_a, other_b)
44
+ end
45
+
46
+ it "does not retain the element's own document" do
47
+ doc = ctx.parse("<r><x/></r>")
48
+ el = doc.root
49
+ el.children = el.children.to_a
50
+ expect(
51
+ ctx.config.adapter.attachments.get(el.native, :children_source_docs),
52
+ ).to be_nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Engine 1.9.339 (leptris#1640): reparenting a node into its own
6
+ # descendant subtree is rejected (LEPTRIS_ERROR_INVALID_ARG) —
7
+ # previously the edge built a parent cycle and the next serialize
8
+ # recursed unbounded (the windows fast_element_xml segv family,
9
+ # ASAN-reproduced). The moxml contract: the mutation RAISES and the
10
+ # tree is untouched — never a corrupt-then-crash tree.
11
+ RSpec.describe "cycle-creating reparents (engine 1.9.339+)" do
12
+ # Nokogiri raises but MOVES the argument (no tree-intact
13
+ # guarantee) — the untouched-tree contract is the engine's.
14
+ describe "on leptris" do
15
+ let(:ctx) { Moxml.new(:leptris) }
16
+
17
+ it "rejects appending an ancestor into its descendant" do
18
+ doc = ctx.parse("<r><a><b>x</b></a></r>")
19
+ a = doc.at("//a")
20
+ before = doc.to_xml
21
+ expect { a.add_child(doc.root) }.to raise_error(StandardError)
22
+ expect(doc.to_xml).to eq(before)
23
+ end
24
+
25
+ it "rejects the replace shape of the same cycle" do
26
+ doc = ctx.parse("<r><a><b><c/></b></a></r>")
27
+ b = doc.at("//b")
28
+ before = doc.to_xml
29
+ expect { b.add_child(doc.root) }.to raise_error(StandardError)
30
+ expect(doc.to_xml).to eq(before)
31
+ end
32
+ end
33
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.124
4
+ version: 0.5.126
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -483,9 +483,11 @@ files:
483
483
  - spec/moxml/c14n/w3c_examples_spec.rb
484
484
  - spec/moxml/c14n/xml_attributes_spec.rb
485
485
  - spec/moxml/cdata_spec.rb
486
+ - spec/moxml/children_source_retention_spec.rb
486
487
  - spec/moxml/comment_spec.rb
487
488
  - spec/moxml/config_spec.rb
488
489
  - spec/moxml/context_spec.rb
490
+ - spec/moxml/cycle_rejection_spec.rb
489
491
  - spec/moxml/declaration_preservation_spec.rb
490
492
  - spec/moxml/declaration_spec.rb
491
493
  - spec/moxml/doctype_spec.rb