moxml 0.5.98 → 0.5.100
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/lib/moxml/adapter/leptris.rb +32 -0
- data/lib/moxml/version.rb +1 -1
- data/spec/moxml/adapter/leptris_append_gc_spec.rb +66 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a421da0e82158a69de2d25328346153be526d74c6d2bc2bad732427763b175c1
|
|
4
|
+
data.tar.gz: c9d982a98b2ce4984b3ee8659e89f2a4d86484d7e39fec4c04221bd2767473eb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a3c9a536530f058a8c0a7721a3f192d89be195df277e959d746f2b6360db2da3587c2467fc21222aff85b96c8c3483a6a59406554804e073b25336c6d5dfe17
|
|
7
|
+
data.tar.gz: 750f9751134a3c077bd103edb9b18b0de7ea179d0c27839f1dff22654d372d7eb989a55c154c09ee155ec64d386547885013320eefa4044cb7dccc994d309806
|
|
@@ -1774,6 +1774,37 @@ module Moxml
|
|
|
1774
1774
|
child_native
|
|
1775
1775
|
end
|
|
1776
1776
|
|
|
1777
|
+
# moxml#304: a cross-document attach makes the target's tree
|
|
1778
|
+
# point into the child's owning document; when that document is
|
|
1779
|
+
# collected its arena dies and the subtree vanishes (empty
|
|
1780
|
+
# output, nameless element pairs — silent, GC-dependent). Pin
|
|
1781
|
+
# the owning document on the target's document so the arena
|
|
1782
|
+
# outlives every attached subtree.
|
|
1783
|
+
def pin_foreign_owner_document(parent, child)
|
|
1784
|
+
return unless child.is_a?(NN) || child.is_a?(::Leptris::XML::Node)
|
|
1785
|
+
|
|
1786
|
+
# Two document fetches + identity compare on the same-document
|
|
1787
|
+
# hot path (every builder add_child); the WeakMap bridge and
|
|
1788
|
+
# the pin only pay when the child actually comes from another
|
|
1789
|
+
# document.
|
|
1790
|
+
child_doc = child.is_a?(NN) ? NN_DOCUMENT.bind_call(child) : child.document
|
|
1791
|
+
return if child_doc.nil?
|
|
1792
|
+
|
|
1793
|
+
parent_doc = parent.is_a?(NN) ? NN_DOCUMENT.bind_call(parent) : parent.document
|
|
1794
|
+
return if parent_doc.nil? || child_doc.equal?(parent_doc)
|
|
1795
|
+
|
|
1796
|
+
# moxml#308: one source document's children attach
|
|
1797
|
+
# node-by-node — dedup by identity or the pin list grows
|
|
1798
|
+
# linearly with append count (GB-scale malloc on
|
|
1799
|
+
# large-document hydration). Distinct owners stay few.
|
|
1800
|
+
owner = child_doc.is_a?(NN) ? to_binding(child_doc) : child_doc
|
|
1801
|
+
docs = attachments.get(parent_doc, :adopted_docs) || []
|
|
1802
|
+
return if docs.include?(owner)
|
|
1803
|
+
|
|
1804
|
+
docs << owner
|
|
1805
|
+
attachments.set(parent_doc, :adopted_docs, docs)
|
|
1806
|
+
end
|
|
1807
|
+
|
|
1777
1808
|
def add_child(parent, child)
|
|
1778
1809
|
if NATIVE_READ_LAYER && !(NATIVE_MUTATIONS_COHERENT &&
|
|
1779
1810
|
parent.is_a?(::Leptris::XML::NativeNode) &&
|
|
@@ -1781,6 +1812,7 @@ module Moxml
|
|
|
1781
1812
|
parent = to_binding(parent)
|
|
1782
1813
|
child = to_binding(child)
|
|
1783
1814
|
end
|
|
1815
|
+
pin_foreign_owner_document(parent, child)
|
|
1784
1816
|
case parent
|
|
1785
1817
|
when ::Leptris::XML::Document then add_document_child(parent, child)
|
|
1786
1818
|
else
|
data/lib/moxml/version.rb
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
# moxml#304: cross-document append_xml / add_child silently lost
|
|
6
|
+
# appended subtrees under GC pressure on MRI < 3.4 — the wrapper
|
|
7
|
+
# registry's WeakMap loses/mangles entries during incremental GC
|
|
8
|
+
# marking (moxml#303's root cause) while the appended fragment's
|
|
9
|
+
# natives are only weakly held; the references then read freed
|
|
10
|
+
# natives ("<sections></sections>", nameless element pairs).
|
|
11
|
+
# The strong-registry fallback (WEAK_WRAPPERS=false on MRI < 3.4)
|
|
12
|
+
# resolves it; this pins the shape.
|
|
13
|
+
RSpec.describe "leptris cross-document append under GC pressure" do
|
|
14
|
+
let(:ctx) { Moxml.new(:leptris) }
|
|
15
|
+
let(:frags) do
|
|
16
|
+
[
|
|
17
|
+
'<clause id="a"><p>one</p></clause>',
|
|
18
|
+
'<annex id="b"><p>two & more</p><fn ref="1"/><!-- c --></annex>',
|
|
19
|
+
'<p standalone="x" y="1"/>',
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "keeps appended subtrees across collections" do
|
|
24
|
+
8.times do
|
|
25
|
+
root = ctx.parse("<sections/>").root
|
|
26
|
+
frags.each do |frag|
|
|
27
|
+
root.append_xml(frag)
|
|
28
|
+
GC.start(full_mark: true, immediate_sweep: true)
|
|
29
|
+
end
|
|
30
|
+
out = root.to_xml(indent: 0)
|
|
31
|
+
expect(out).to include("<clause")
|
|
32
|
+
expect(out).to include("<annex")
|
|
33
|
+
expect(out).to include("standalone")
|
|
34
|
+
expect(out).not_to include("<></>")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "keeps parse_fragment nodes across collections" do
|
|
39
|
+
built = ctx.parse("<sections/>")
|
|
40
|
+
4.times do
|
|
41
|
+
frags.each do |frag|
|
|
42
|
+
ctx.parse_fragment(frag).each do |node|
|
|
43
|
+
built.root.add_child(node)
|
|
44
|
+
GC.start(full_mark: true, immediate_sweep: true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
out = built.root.to_xml(indent: 0)
|
|
48
|
+
expect(out).to include("<clause")
|
|
49
|
+
expect(out).to include("<annex")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# moxml#308: the pin list must dedup by identity — one source
|
|
54
|
+
# document's children attach node-by-node, and without dedup the
|
|
55
|
+
# list grows linearly with append count (GB-scale malloc on
|
|
56
|
+
# large-document hydration; OOM at 43-75M slots).
|
|
57
|
+
it "pins each adopted document once regardless of append count" do
|
|
58
|
+
target = ctx.parse("<r/>")
|
|
59
|
+
frag = ctx.parse("<m>#{Array.new(300) { |i| "<c#{i}>x</c#{i}>" }.join}</m>")
|
|
60
|
+
frag.root.children.each { |c| target.root.add_child(c) }
|
|
61
|
+
|
|
62
|
+
docs = ctx.config.adapter.attachments.get(target.native.document, :adopted_docs)
|
|
63
|
+
expect(docs.size).to eq(1)
|
|
64
|
+
expect(target.root.to_xml).to include("<c299")
|
|
65
|
+
end
|
|
66
|
+
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.100
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -440,6 +440,7 @@ files:
|
|
|
440
440
|
- spec/moxml/adapter/base_spec.rb
|
|
441
441
|
- spec/moxml/adapter/entity_restoration_spec.rb
|
|
442
442
|
- spec/moxml/adapter/headed_ox_spec.rb
|
|
443
|
+
- spec/moxml/adapter/leptris_append_gc_spec.rb
|
|
443
444
|
- spec/moxml/adapter/leptris_doc_parts_spec.rb
|
|
444
445
|
- spec/moxml/adapter/leptris_parse_skips_spec.rb
|
|
445
446
|
- spec/moxml/adapter/leptris_record_driver_spec.rb
|