makiri 0.7.0 → 0.8.0
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/.github/workflows/valgrind.yml +12 -1
- data/CHANGELOG.md +10 -0
- data/Rakefile +34 -2
- data/ext/makiri/dom_adapter/cross_import.c +27 -5
- data/lib/makiri/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8998ec8f7ae9b6e9f31fb3b880f50a53602e2cdea51b42faa33613638e2ceedc
|
|
4
|
+
data.tar.gz: df9c18e5917eb79e2b0311a65c70e1b648e40c62fd1b14ecec1d64b4394f3930
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5bd1a20a81195609e360ffc71108cef27a62803a387526dfdb38cf7cbf0ae6bde69e48815717ba1d236300c2aedd376589b4799c610a883ebf088d8a37e0f3ce
|
|
7
|
+
data.tar.gz: 1104a702f5348a2e52705c81153af1baaad12bcc3a649e5e863d5e8a6c98da3566ede4efa8cb0f0b8d091b0ba3ee9abef0c67dcf83ddae33d48706999df9cd74
|
|
@@ -24,11 +24,16 @@ jobs:
|
|
|
24
24
|
# (intra-arena overflows). Runs on Linux because Valgrind is x86_64/amd64
|
|
25
25
|
# Linux only.
|
|
26
26
|
valgrind-memcheck:
|
|
27
|
-
name: Valgrind memcheck (Ruby ${{ matrix.ruby }})
|
|
27
|
+
name: Valgrind memcheck (Ruby ${{ matrix.ruby }}, shard ${{ matrix.shard }}/4)
|
|
28
28
|
runs-on: ubuntu-latest
|
|
29
29
|
timeout-minutes: 360
|
|
30
30
|
env:
|
|
31
31
|
BUNDLE_WITH: valgrind
|
|
32
|
+
# Shard the suite across the matrix below: 4 parallel jobs each memcheck a
|
|
33
|
+
# quarter of the spec files, so the whole suite finishes in ~1/4 the
|
|
34
|
+
# wall-clock with no loss of coverage (each shard is balanced by file size).
|
|
35
|
+
VALGRIND_SHARDS: "4"
|
|
36
|
+
VALGRIND_SHARD_INDEX: ${{ matrix.shard }}
|
|
32
37
|
# These heavy jobs verify memory discipline (uninit values, intra-arena
|
|
33
38
|
# overflows, use-after-move), not the property space - so the full
|
|
34
39
|
# 300-iteration PBT sweep (already run by the normal CI matrix) is
|
|
@@ -37,10 +42,16 @@ jobs:
|
|
|
37
42
|
# keeping the run tractable.
|
|
38
43
|
PBT_COUNT: "15"
|
|
39
44
|
CSS_PBT_COUNT: "15"
|
|
45
|
+
# --track-origins roughly doubles memcheck's runtime and only adds an origin
|
|
46
|
+
# backtrace to a finding (the finding itself is still reported without it).
|
|
47
|
+
# Drop it on the frequent post-merge push gate to halve its runtime; keep it
|
|
48
|
+
# for the thorough nightly / manual runs, where the backtrace aids triage.
|
|
49
|
+
VALGRIND_TRACK_ORIGINS: ${{ github.event_name == 'push' && 'no' || 'yes' }}
|
|
40
50
|
strategy:
|
|
41
51
|
fail-fast: false
|
|
42
52
|
matrix:
|
|
43
53
|
ruby: ["3.4"]
|
|
54
|
+
shard: [0, 1, 2, 3] # keep in sync with VALGRIND_SHARDS above
|
|
44
55
|
|
|
45
56
|
steps:
|
|
46
57
|
- name: Checkout (with vendored Lexbor submodule)
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.0] - 2026-07-12
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
* `Document#import_node` (DOM `importNode` / `adoptNode`) now imports an HTML
|
|
8
|
+
element whose name is a valid DOM name but not a well-formed XML QName (e.g.
|
|
9
|
+
`":good:times:"`, `"x<"`, `"0:a"`) instead of raising. Such an element is not
|
|
10
|
+
XML-serializable (`#to_xml` raises). An element carrying a similarly lenient
|
|
11
|
+
attribute name is still rejected.
|
|
12
|
+
|
|
3
13
|
## [0.7.0] - 2026-07-11
|
|
4
14
|
|
|
5
15
|
### Added
|
data/Rakefile
CHANGED
|
@@ -88,7 +88,12 @@ begin
|
|
|
88
88
|
"--error-limit=no",
|
|
89
89
|
"--trace-children=yes", # spec processes may fork
|
|
90
90
|
"--undef-value-errors=yes", # the point of this job (ruby_memcheck defaults to =no)
|
|
91
|
-
|
|
91
|
+
# Origin tracking (where an uninitialised value came from) roughly DOUBLES
|
|
92
|
+
# memcheck's run time. It is a diagnostic aid, not a detection capability -
|
|
93
|
+
# memcheck still reports the uninitialised USE without it - so the frequent
|
|
94
|
+
# post-merge push gate turns it off (VALGRIND_TRACK_ORIGINS=no) to run in
|
|
95
|
+
# ~half the time, while the nightly / manual runs keep it on for the backtrace.
|
|
96
|
+
"--track-origins=#{ENV.fetch('VALGRIND_TRACK_ORIGINS', 'yes')}",
|
|
92
97
|
"--leak-check=no", # leaks are `rake leaks`' job, not this one
|
|
93
98
|
],
|
|
94
99
|
)
|
|
@@ -96,7 +101,34 @@ begin
|
|
|
96
101
|
namespace :spec do
|
|
97
102
|
desc "Run the spec suite under Valgrind memcheck (ruby_memcheck; needs the " \
|
|
98
103
|
":valgrind bundler group and the valgrind binary)"
|
|
99
|
-
RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
|
|
104
|
+
RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile) do |t|
|
|
105
|
+
# Let spec_helper skip :slow examples (fail-closed limit tests whose sheer
|
|
106
|
+
# volume dominates memcheck without adding memory-safety coverage).
|
|
107
|
+
ENV["VALGRIND"] = "1"
|
|
108
|
+
|
|
109
|
+
# Optional sharding for CI wall-clock: with VALGRIND_SHARDS=N (>1) the spec
|
|
110
|
+
# files are partitioned into N groups and this run does only group
|
|
111
|
+
# VALGRIND_SHARD_INDEX, so N parallel matrix jobs cover the whole suite in
|
|
112
|
+
# ~1/N the time WITHOUT dropping any coverage. Files are packed
|
|
113
|
+
# largest-first onto the currently-lightest shard (greedy LPT by file size,
|
|
114
|
+
# a proxy for runtime), so the slowest shard - which sets wall-clock - is
|
|
115
|
+
# balanced rather than left to name order. Deterministic, so every shard
|
|
116
|
+
# computes the same partition and takes its own index. Default N=1 = the
|
|
117
|
+
# whole suite (local `rake spec:valgrind`).
|
|
118
|
+
shards = Integer(ENV.fetch("VALGRIND_SHARDS", "1"))
|
|
119
|
+
if shards > 1
|
|
120
|
+
index = Integer(ENV.fetch("VALGRIND_SHARD_INDEX", "0"))
|
|
121
|
+
buckets = Array.new(shards) { { load: 0, files: [] } }
|
|
122
|
+
# sort by (size desc, name) so the packing is fully deterministic across
|
|
123
|
+
# shards regardless of Array#sort stability.
|
|
124
|
+
Dir["spec/**/*_spec.rb"].sort_by { |f| [-File.size(f), f] }.each do |f|
|
|
125
|
+
b = buckets.min_by { |x| x[:load] }
|
|
126
|
+
b[:files] << f
|
|
127
|
+
b[:load] += File.size(f)
|
|
128
|
+
end
|
|
129
|
+
t.pattern = buckets.fetch(index)[:files]
|
|
130
|
+
end
|
|
131
|
+
end
|
|
100
132
|
end
|
|
101
133
|
rescue LoadError
|
|
102
134
|
# ruby_memcheck not installed (optional :valgrind group absent) - skip the task.
|
|
@@ -185,15 +185,37 @@ h2x_make(mkr_xml_doc_t *xdoc, lxb_dom_node_t *s, const char *pdef, uint32_t pdef
|
|
|
185
185
|
size_t nl;
|
|
186
186
|
const lxb_char_t *nm = lxb_dom_element_qualified_name(lxb_dom_interface_element(s), &nl);
|
|
187
187
|
if (!MKR_FITS_U32(nl)) return MKR_XML_MUT_OOM;
|
|
188
|
+
|
|
189
|
+
/* The element's namespace URI (borrowed from the source doc's ns table). */
|
|
190
|
+
uint32_t eul;
|
|
191
|
+
const char *euri = mkr_html_ns_uri(s, &eul);
|
|
192
|
+
|
|
193
|
+
/* Strict first, so a valid QName stays XML-serializable. importNode/adoptNode
|
|
194
|
+
* never re-validate an existing node's name (DOM requires the source already
|
|
195
|
+
* be well-formed for its representation), so an HTML name that is a valid DOM
|
|
196
|
+
* element name but NOT a well-formed XML QName (":good:times:", "x<", "0:a")
|
|
197
|
+
* is taken VERBATIM as an unprefixed DOM-loose name (local == whole) rather
|
|
198
|
+
* than rejected - the same non-serializable escape hatch as
|
|
199
|
+
* create_loose_dom_element. HTML elements are always unprefixed. The namespace
|
|
200
|
+
* is passed DIRECTLY because ns resolution at link time skips loose names
|
|
201
|
+
* (resolve_node_ns), so a synthesized xmlns declaration would not reach it. */
|
|
188
202
|
mkr_xml_node_t *el = NULL;
|
|
189
203
|
mkr_xml_mut_status_t st = mkr_xml_new_element(xdoc, (const char *)nm, (uint32_t)nl, &el);
|
|
204
|
+
if (st == MKR_XML_MUT_BAD_NAME && nl > 0) {
|
|
205
|
+
mkr_xml_qname_t qn = {
|
|
206
|
+
(const char *)nm, (uint32_t)nl, /* qname */
|
|
207
|
+
(const char *)nm, 0, /* prefix (none) */
|
|
208
|
+
(const char *)nm, (uint32_t)nl, /* local == qname */
|
|
209
|
+
};
|
|
210
|
+
st = mkr_xml_new_loose_dom_element(xdoc, &qn, euri, eul, &el);
|
|
211
|
+
}
|
|
190
212
|
if (st != MKR_XML_MUT_OK) return st;
|
|
191
213
|
|
|
192
|
-
/* Declare the
|
|
193
|
-
*
|
|
194
|
-
* An element with no namespace under an inherited default undeclares
|
|
195
|
-
|
|
196
|
-
|
|
214
|
+
/* Declare the default namespace iff it differs from the inherited one, so
|
|
215
|
+
* this (unprefixed, like all HTML elements) element and its children resolve
|
|
216
|
+
* to it. An element with no namespace under an inherited default undeclares
|
|
217
|
+
* (xmlns=""). For a loose element this only feeds child inheritance - its own
|
|
218
|
+
* ns_uri was already set directly above. */
|
|
197
219
|
if (!mkr_uri_eq(euri, eul, pdef, pdef_len)) {
|
|
198
220
|
st = h2x_declare_ns(xdoc, el, NULL, 0, euri, eul);
|
|
199
221
|
if (st != MKR_XML_MUT_OK) return st;
|
data/lib/makiri/version.rb
CHANGED