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 +4 -4
- data/Gemfile +1 -1
- data/lib/moxml/element.rb +37 -4
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/children_source_retention_spec.rb +56 -0
- data/spec/moxml/cycle_rejection_spec.rb +33 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 71f2ce65d604babc4d29753cb99b353f6f9fafa4a8379f913d109aa4e3cb1aba
|
|
4
|
+
data.tar.gz: bb11508d1a28d87de8b3bcca4a6913ce40d29ac7152dfbee4311db797873ea14
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bca5457e68b0d51547a72f440097d5203fc469654f9360d6dfb6c3d0f8f8d8a6cf921e3e057e3bd9ef8c6ad86823c80057d5cb171474d27f4ca46e5de329a715
|
|
7
|
+
data.tar.gz: c382a8f7eb2e23a116021b47d785e6757e04961576b12f2f452c57d01e946ee121da96ff2b77e77c6d5539e49110dc5af32fcf0a4db208906d5852251ffa978e
|
data/Gemfile
CHANGED
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
@@ -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.
|
|
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
|