makiri 0.12.1-aarch64-linux → 0.14.0-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 +4 -4
- data/CHANGELOG.md +98 -0
- data/README.md +29 -1
- data/lib/makiri/3.2/makiri.so +0 -0
- data/lib/makiri/3.3/makiri.so +0 -0
- data/lib/makiri/3.4/makiri.so +0 -0
- data/lib/makiri/4.0/makiri.so +0 -0
- data/lib/makiri/html/document.rb +45 -21
- data/lib/makiri/version.rb +1 -1
- data/lib/makiri/xml/document.rb +10 -16
- data/script/check_unsafe_boundaries.rb +69 -41
- data/suppressions/ruby.supp +14 -0
- 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: b3c3ed6ee3cd5fadbf71a4841bb984d84aa09b53ae9707da72ef2a4c5e6ce2c4
|
|
4
|
+
data.tar.gz: 3e92e200b9d198936e4ae65ee7f43bd0a2271827f3324c5127ea148a4b134b44
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dc3c6c844fce3e704a0560b5d07908b39082b6528ecf85fb77ea6e3d15c65cea1d7ff0cd99cd48fe52f96bf74ffed160f83cb3f233bf3e6e55de2923234124c4
|
|
7
|
+
data.tar.gz: 40b616cb7581a6b2afc5a03f6bdd3f66f7cec98eec398d19bf0121fcb011ff9e2e9f7412fb39976c0638590d9a263ef8ebdd7d285891a4e093142f94c6b097f5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,103 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.0] - 2026-10-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
* `Document#attribute_version` (HTML and XML): an Integer that increases
|
|
8
|
+
whenever an attribute is added, removed or set (to the same value too),
|
|
9
|
+
including through an Attr node's `content=` or `remove`. Child-list and
|
|
10
|
+
text edits leave it as it is, so it can key a cache of attributes.
|
|
11
|
+
* `Makiri::XML::Document#create_element_ns(namespace_uri, qualified_name)`,
|
|
12
|
+
like the HTML Document's and the DOM's `createElementNS`. The element keeps
|
|
13
|
+
its namespace wherever it is inserted; a name that is not an XML QName
|
|
14
|
+
(`f}oo`) makes an element that `to_xml` refuses.
|
|
15
|
+
* `Makiri::HTML::Document.new`: an empty HTML document in no-quirks mode.
|
|
16
|
+
* `Makiri::HTML::Document#quirks_mode?` and `#compat_mode` (`"BackCompat"` /
|
|
17
|
+
`"CSS1Compat"`, like the DOM's `compatMode`).
|
|
18
|
+
* HTML `Attr#remove` / `#unlink` remove the attribute from its element, as on
|
|
19
|
+
XML. They raised "use delete(name) to remove an attribute".
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
* `tree_version` counts child-list changes only. A Text, Comment, CDATA or
|
|
24
|
+
PI node's `content=` no longer moves it, and an HTML Attr node's
|
|
25
|
+
`content=` moves `attribute_version` instead.
|
|
26
|
+
* `tree_version` and `attribute_version` are documented as cache keys: an
|
|
27
|
+
unchanged version means nothing it covers changed, while a changed one may
|
|
28
|
+
be conservative (an edit refused part-way can still move it).
|
|
29
|
+
* `namespace_uri` and `prefix` are documented as `nil`, never `""`, when
|
|
30
|
+
there is no namespace or prefix, in HTML and XML.
|
|
31
|
+
* `Document#dup` copies the tree node by node instead of re-parsing its
|
|
32
|
+
serialization. An HTML copy keeps a tree with no `<html>` root as it is and
|
|
33
|
+
keeps the quirks mode, and `Node#line` on it is `nil`. An XML copy keeps
|
|
34
|
+
every name and namespace as written (a re-parse invented `ns1:` prefixes),
|
|
35
|
+
copies data XML cannot write, and keeps the original's `max_bytes`.
|
|
36
|
+
* XML `create_processing_instruction` accepts the target `xml` (in any case),
|
|
37
|
+
as the DOM does; `to_xml` and `canonicalize` refuse a tree holding one.
|
|
38
|
+
* HTML `create_processing_instruction` raises `ArgumentError`, as XML does,
|
|
39
|
+
for data containing `?>`. It raised `Makiri::Error`.
|
|
40
|
+
* A refused XML insertion names the DOM rule it breaks, in the same words as
|
|
41
|
+
HTML. It said "invalid placement".
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
* An Attr's `content=` and `remove` raise `FrozenError` when the element
|
|
46
|
+
that owns it is frozen, in HTML and XML.
|
|
47
|
+
* XML `Attr#content=` sets the attribute's value. It raised "operation
|
|
48
|
+
unsupported".
|
|
49
|
+
* HTML `title=` and `meta_encoding=` do nothing on a document with no
|
|
50
|
+
`<head>`, as the DOM's title setter does. They inserted the element directly
|
|
51
|
+
under the root element.
|
|
52
|
+
* HTML `Document#root` and CSS `:root` return the document element, or `nil`.
|
|
53
|
+
Without an `<html>` element they returned the first child of any kind, such
|
|
54
|
+
as a comment, and `title=` raised on such a document.
|
|
55
|
+
* An edit refused for its arguments - a frozen or non-node argument to an
|
|
56
|
+
HTML insertion, an invalid name given to XML `[]=`, `set_attribute_ns` or
|
|
57
|
+
`set_loose_dom_attribute`, a refused HTML `Attr#remove` - no longer moves a
|
|
58
|
+
version.
|
|
59
|
+
* An XML copy that runs out of the document's byte budget (`import_node`,
|
|
60
|
+
`clone_node`, an insertion from another document) gives back what it used.
|
|
61
|
+
The document could not be edited any more after one.
|
|
62
|
+
* Importing HTML into an XML document stores each namespace URI once instead
|
|
63
|
+
of once per element, which used up the byte budget faster.
|
|
64
|
+
* An HTML Attr imported on its own from another HTML document keeps its
|
|
65
|
+
namespace URI as written. It was lower-cased.
|
|
66
|
+
* `to_xml` and `canonicalize` write an element in the XML namespace as
|
|
67
|
+
`xml:local`. They wrote a declaration that does not parse. An element in
|
|
68
|
+
the XMLNS namespace is refused with `Makiri::Error`.
|
|
69
|
+
* XML XPath: a prefixed attribute on a detached element, whose namespace is
|
|
70
|
+
not decided yet, no longer matches an unprefixed name test.
|
|
71
|
+
* XML: references are read by one grammar everywhere. The internal subset
|
|
72
|
+
accepted a character reference to a character XML does not allow
|
|
73
|
+
(`<!ENTITY x "�">`), and a malformed reference next to an entity
|
|
74
|
+
reference was reported as an unsupported DTD construct instead of as
|
|
75
|
+
malformed XML.
|
|
76
|
+
|
|
77
|
+
## [0.13.0] - 2026-10-03
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
* `Document#tree_version` (HTML and XML): an Integer that increases whenever
|
|
82
|
+
a child list in the document changes - nodes added, removed or replaced,
|
|
83
|
+
`inner_html=`, `content=`, and both documents when a node moves between
|
|
84
|
+
them. Attribute changes leave it as it is, so it can key a cache of child
|
|
85
|
+
lists.
|
|
86
|
+
* `Element#attribute_value_ns(ns, local)` and `#attribute_node_ns(ns, local)`
|
|
87
|
+
(HTML and XML): the attribute's value or Attr node by namespace and local
|
|
88
|
+
name, like the DOM's `getAttributeNS` / `getAttributeNodeNS`. `nil` or `""`
|
|
89
|
+
means no namespace.
|
|
90
|
+
* `Element#set_loose_dom_attribute(name, value)` now works on HTML nodes too.
|
|
91
|
+
Like the DOM's `setAttribute`, it sets an attribute in no namespace whose
|
|
92
|
+
name may contain a colon (`v-on:click`).
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
96
|
+
* HTML documents keep namespace URIs as written. `create_element_ns`,
|
|
97
|
+
`set_attribute_ns` and `import_node` lower-cased them (`fooNamespace` read
|
|
98
|
+
back as `foonamespace`), and a differently cased XHTML namespace URI made
|
|
99
|
+
an HTML element.
|
|
100
|
+
|
|
3
101
|
## [0.12.1] - 2026-10-02
|
|
4
102
|
|
|
5
103
|
### Fixed
|
data/README.md
CHANGED
|
@@ -50,7 +50,7 @@ doc = Makiri::HTML(<<~HTML)
|
|
|
50
50
|
</body></html>
|
|
51
51
|
HTML
|
|
52
52
|
|
|
53
|
-
# CSS selectors (Lexbor's selector
|
|
53
|
+
# CSS selectors (Lexbor's selector parser, Makiri's own matcher)
|
|
54
54
|
doc.css("a").map { |a| a["href"] } # => ["/a", "/b"]
|
|
55
55
|
doc.at_css("p.lead").text # => "Hello"
|
|
56
56
|
|
|
@@ -184,6 +184,33 @@ and unusually large documents can raise it with `max_bytes:`.
|
|
|
184
184
|
Makiri::XML(huge_xml, max_bytes: 512 * 1024 * 1024) # also Makiri::XML::Document.parse(..., max_bytes:)
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
+
The limit covers the document's whole life, not only the parse: nodes and
|
|
188
|
+
values made by editing it count too, and a replaced value or a removed node
|
|
189
|
+
keeps its bytes until the document is gone. A document edited for a long time
|
|
190
|
+
can be compacted with `dup`, which copies only what is in it now, under the
|
|
191
|
+
same limit.
|
|
192
|
+
|
|
193
|
+
### Node identity
|
|
194
|
+
|
|
195
|
+
Navigating to the same node always gives the same Ruby object, for as long as
|
|
196
|
+
its document is alive. The document keeps every wrapper it has handed out, so
|
|
197
|
+
instance variables, singleton methods and `freeze` set on a node survive
|
|
198
|
+
garbage collection and are there the next time the node is reached.
|
|
199
|
+
|
|
200
|
+
That makes a node a safe place to keep per-node state, such as a wrapper
|
|
201
|
+
object of your own:
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
el = doc.at_css("p")
|
|
205
|
+
el.instance_variable_set(:@wrapper, MyElement.new(el))
|
|
206
|
+
doc.at_css("p").instance_variable_get(:@wrapper) # => the same MyElement
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
A node created by `clone_node` or `import_node` is a new node with no state.
|
|
210
|
+
`pointer_id` is unique only among live nodes: once a document is freed, a node
|
|
211
|
+
of another document may reuse its value. So key a pointer-based cache per
|
|
212
|
+
document, not across documents.
|
|
213
|
+
|
|
187
214
|
## Non-goals (v1.0)
|
|
188
215
|
|
|
189
216
|
* XSLT, DTD / Schema / RelaxNG validation, XPointer, XInclude.
|
|
@@ -224,6 +251,7 @@ See also [`spec/conformance/README.md`](spec/conformance/README.md).
|
|
|
224
251
|
| XPath 1.0 | XML | `Nokogiri::XML` — differential | `conformance:xpath_xml` |
|
|
225
252
|
| Parsed tree (property-based) | XML | `Nokogiri::XML` — differential | `conformance:xml_pbt` |
|
|
226
253
|
| CSS selectors | XML | `Nokogiri::XML` — differential | `conformance:css_xml` |
|
|
254
|
+
| XML Builder | Ruby DSL | `Nokogiri::XML::Builder` — differential | `conformance:builder` |
|
|
227
255
|
|
|
228
256
|
## Requirements
|
|
229
257
|
|
data/lib/makiri/3.2/makiri.so
CHANGED
|
Binary file
|
data/lib/makiri/3.3/makiri.so
CHANGED
|
Binary file
|
data/lib/makiri/3.4/makiri.so
CHANGED
|
Binary file
|
data/lib/makiri/4.0/makiri.so
CHANGED
|
Binary file
|
data/lib/makiri/html/document.rb
CHANGED
|
@@ -26,22 +26,37 @@ module Makiri
|
|
|
26
26
|
_parse(String(source), max_tree_depth)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# An independent copy of the whole document (like Nokogiri's Document#dup)
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
29
|
+
# An independent copy of the whole document (like Nokogiri's Document#dup):
|
|
30
|
+
# an empty document in the same quirks mode, given a deep copy of each of
|
|
31
|
+
# this one's children (+import_node+, which carries a <template>'s
|
|
32
|
+
# contents too), so the copy shares no nodes with the original and has
|
|
33
|
+
# the same tree - whatever shape it was built into, which a re-parse of
|
|
34
|
+
# +to_html+ would not keep (a Document.new without an <html> root gained
|
|
35
|
+
# the html/head/body shell and quirks mode). Node#dup's clone_node
|
|
36
|
+
# delegation is wrong for a document node, hence this override. Any
|
|
37
|
+
# level argument is ignored. #clone is this too (see {CloneViaDup}).
|
|
36
38
|
#
|
|
37
|
-
# Node#line on the copy
|
|
38
|
-
# from, not in the original source: `to_html` does not keep the original
|
|
39
|
-
# line breaks between tags, so the two differ.
|
|
40
|
-
#
|
|
41
|
-
# The re-parse has no tree-depth limit: the tree is already here, however
|
|
42
|
-
# it was built, and a copy must not fail where the original stands.
|
|
39
|
+
# Node#line is nil on the copy: its nodes were not parsed from a source.
|
|
43
40
|
def dup(*)
|
|
44
|
-
|
|
41
|
+
_empty_copy.tap do |copy|
|
|
42
|
+
children.each { |child| copy.add_child(copy.import_node(child, true)) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Whether the document is in quirks mode - the mode a missing or legacy
|
|
47
|
+
# doctype puts it in. Limited-quirks mode is not quirks mode here, as the
|
|
48
|
+
# DOM's +compatMode+ has it. (#quirks_mode is the raw value: 0 no-quirks,
|
|
49
|
+
# 1 quirks, 2 limited-quirks.)
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
def quirks_mode?
|
|
52
|
+
quirks_mode == 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The DOM's +document.compatMode+: "BackCompat" in quirks mode, else
|
|
56
|
+
# "CSS1Compat".
|
|
57
|
+
# @return [String]
|
|
58
|
+
def compat_mode
|
|
59
|
+
quirks_mode? ? "BackCompat" : "CSS1Compat"
|
|
45
60
|
end
|
|
46
61
|
|
|
47
62
|
# The document's <body> element, or nil.
|
|
@@ -56,11 +71,13 @@ module Makiri
|
|
|
56
71
|
at_css("head")
|
|
57
72
|
end
|
|
58
73
|
|
|
59
|
-
# Set the document title, creating <title> (in <head>) if absent.
|
|
74
|
+
# Set the document title, creating <title> (in <head>) if absent. A
|
|
75
|
+
# document with no <head> - a Document.new one before it has one - is
|
|
76
|
+
# left as it is, as the DOM's title setter leaves it.
|
|
60
77
|
# @param text [String]
|
|
61
78
|
# @return [String]
|
|
62
79
|
def title=(text)
|
|
63
|
-
ensure_in_head("title", "title")
|
|
80
|
+
ensure_in_head("title", "title")&.content = text
|
|
64
81
|
text
|
|
65
82
|
end
|
|
66
83
|
|
|
@@ -89,21 +106,28 @@ module Makiri
|
|
|
89
106
|
nil
|
|
90
107
|
end
|
|
91
108
|
|
|
92
|
-
# Set (or insert) a <meta charset> declaration.
|
|
109
|
+
# Set (or insert, in <head>) a <meta charset> declaration. Like #title=,
|
|
110
|
+
# it leaves a document with no <head> as it is.
|
|
93
111
|
# @param value [String]
|
|
94
112
|
# @return [String]
|
|
95
113
|
def meta_encoding=(value)
|
|
96
|
-
ensure_in_head("meta[charset]", "meta")["charset"
|
|
114
|
+
ensure_in_head("meta[charset]", "meta")&.[]=("charset", value)
|
|
97
115
|
value
|
|
98
116
|
end
|
|
99
117
|
|
|
100
118
|
private
|
|
101
119
|
|
|
102
120
|
# The first node matching +css_query+, or a freshly created <+tag+>
|
|
103
|
-
# appended to <head
|
|
104
|
-
#
|
|
121
|
+
# appended to <head>; nil when there is neither a match nor a head - never
|
|
122
|
+
# an element put straight under the root, which the DOM's title setter
|
|
123
|
+
# does not do either. Shared by #title= and #meta_encoding=, which then
|
|
124
|
+
# set content / attributes on it.
|
|
105
125
|
def ensure_in_head(css_query, tag)
|
|
106
|
-
at_css(css_query)
|
|
126
|
+
found = at_css(css_query)
|
|
127
|
+
return found if found
|
|
128
|
+
|
|
129
|
+
parent = head
|
|
130
|
+
parent && Element.new(tag, self).tap { |el| parent.add_child(el) }
|
|
107
131
|
end
|
|
108
132
|
end
|
|
109
133
|
end
|
data/lib/makiri/version.rb
CHANGED
data/lib/makiri/xml/document.rb
CHANGED
|
@@ -20,25 +20,19 @@ module Makiri
|
|
|
20
20
|
r ? r.replace(node) : add_child(node)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
# An independent copy of the whole document (like Nokogiri's
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# its Node#line numbers are lines of the serialised text, which can
|
|
33
|
-
# differ from the original source's.
|
|
23
|
+
# An independent copy of the whole document (like Nokogiri's Document#dup),
|
|
24
|
+
# made node for node rather than by serialising and re-parsing: the
|
|
25
|
+
# top-level order, every name, namespace URI and namespace state,
|
|
26
|
+
# attributes, the doctype and character data are the original's. Nothing
|
|
27
|
+
# has to be writable as XML, no namespace is resolved again (a re-parse
|
|
28
|
+
# gave an attribute set in a namespace an invented `ns1:` prefix), and the
|
|
29
|
+
# copy keeps the original's +max_bytes+ budget and whether it declared an
|
|
30
|
+
# encoding. Any level argument is ignored, and #clone is this too (see
|
|
31
|
+
# {CloneViaDup}).
|
|
34
32
|
#
|
|
35
33
|
# @return [Makiri::XML::Document]
|
|
36
34
|
def dup(*)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
self.class.new.tap do |copy|
|
|
40
|
-
children.each { |child| copy.add_child(copy.import_node(child, true)) }
|
|
41
|
-
end
|
|
35
|
+
_copy
|
|
42
36
|
end
|
|
43
37
|
end
|
|
44
38
|
end
|
|
@@ -38,14 +38,14 @@ UNSAFE_ISLANDS = {
|
|
|
38
38
|
"bridge/node_wrap.rs" => 1,
|
|
39
39
|
"bridge/ruby.rs" => 18,
|
|
40
40
|
"bridge/stack.rs" => 1,
|
|
41
|
-
"bridge/string.rs" =>
|
|
41
|
+
"bridge/string.rs" => 30,
|
|
42
42
|
"bridge/typed.rs" => 21,
|
|
43
|
-
"bridge/wrapper.rs" =>
|
|
43
|
+
"bridge/wrapper.rs" => 20,
|
|
44
44
|
"bridge/xml.rs" => 11,
|
|
45
|
-
"bridge/xml_decode.rs" =>
|
|
45
|
+
"bridge/xml_decode.rs" => 5,
|
|
46
46
|
"bridge/xpath/context_object.rs" => 3,
|
|
47
47
|
"bridge/xpath/handler.rs" => 7,
|
|
48
|
-
"bridge/xpath/mod.rs" =>
|
|
48
|
+
"bridge/xpath/mod.rs" => 7,
|
|
49
49
|
"cbuf.rs" => 16,
|
|
50
50
|
"cbuf/verify.rs" => 8,
|
|
51
51
|
"falloc/cstr.rs" => 1,
|
|
@@ -55,9 +55,9 @@ UNSAFE_ISLANDS = {
|
|
|
55
55
|
"lexbor/abi.rs" => 4,
|
|
56
56
|
"lexbor/adapter/arena_bytes.rs" => 14,
|
|
57
57
|
"lexbor/adapter/cross_import.rs" => 2,
|
|
58
|
-
"lexbor/adapter/html/attrs.rs" =>
|
|
59
|
-
"lexbor/adapter/html/build.rs" =>
|
|
60
|
-
"lexbor/adapter/html/mod.rs" =>
|
|
58
|
+
"lexbor/adapter/html/attrs.rs" => 25,
|
|
59
|
+
"lexbor/adapter/html/build.rs" => 26,
|
|
60
|
+
"lexbor/adapter/html/mod.rs" => 67,
|
|
61
61
|
"lexbor/adapter/html/mutate.rs" => 9,
|
|
62
62
|
"lexbor/adapter/html/serialize.rs" => 18,
|
|
63
63
|
"lexbor/adapter/post_parse.rs" => 13,
|
|
@@ -73,7 +73,7 @@ UNSAFE_ISLANDS = {
|
|
|
73
73
|
"lexbor/selector_cache.rs" => 15,
|
|
74
74
|
"lexbor/selectors.rs" => 9,
|
|
75
75
|
"lexbor/serialize.rs" => 2,
|
|
76
|
-
"lexbor/stylesheet.rs" =>
|
|
76
|
+
"lexbor/stylesheet.rs" => 8,
|
|
77
77
|
"lexbor/tests.rs" => 16,
|
|
78
78
|
"lexbor/xpath.rs" => 9,
|
|
79
79
|
"token.rs" => 1,
|
|
@@ -90,36 +90,37 @@ UNSAFE_ISLANDS = {
|
|
|
90
90
|
FORBID_ROOTS = %w[css/mod.rs glue/mod.rs lexbor/css_match/mod.rs xml/mod.rs xpath/mod.rs].freeze
|
|
91
91
|
|
|
92
92
|
FORBID_FILES = %w[
|
|
93
|
-
|
|
94
|
-
cutf8.rs cutf8/verify.rs
|
|
95
|
-
engine_error.rs falloc/verify.rs
|
|
96
|
-
glue/
|
|
97
|
-
glue/html_node/
|
|
98
|
-
glue/
|
|
99
|
-
glue/
|
|
100
|
-
glue/xml_node/
|
|
101
|
-
glue/xml_node/
|
|
102
|
-
lexbor/adapter/dom_index.rs lexbor/contains_guard.rs
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
xml/
|
|
107
|
-
xml/
|
|
108
|
-
xml/
|
|
109
|
-
xml/mutate/
|
|
110
|
-
xml/mutate/
|
|
111
|
-
xml/
|
|
112
|
-
xml/serialize/
|
|
113
|
-
xml/
|
|
114
|
-
xml/tree/
|
|
115
|
-
xml/
|
|
116
|
-
xpath/
|
|
117
|
-
xpath/
|
|
118
|
-
xpath/
|
|
119
|
-
xpath/
|
|
120
|
-
xpath/
|
|
121
|
-
xpath/
|
|
122
|
-
xpath/
|
|
93
|
+
bridge/dom_error.rs css/build.rs css/lower.rs
|
|
94
|
+
css/mod.rs cutf8.rs cutf8/verify.rs
|
|
95
|
+
dom_rules.rs engine_error.rs falloc/verify.rs
|
|
96
|
+
glue/css.rs glue/html_doc.rs glue/html_node/css.rs
|
|
97
|
+
glue/html_node/mutate.rs glue/html_node/read.rs glue/html_node/serialize.rs
|
|
98
|
+
glue/mod.rs glue/node.rs glue/node_set.rs
|
|
99
|
+
glue/query.rs glue/xml_doc.rs glue/xml_node/css.rs
|
|
100
|
+
glue/xml_node/mod.rs glue/xml_node/mutate.rs glue/xml_node/ns.rs
|
|
101
|
+
glue/xml_node/read.rs glue/xml_node/serialize.rs glue/xml_node/strings.rs
|
|
102
|
+
glue/xpath_context.rs lexbor/adapter/dom_index.rs lexbor/contains_guard.rs
|
|
103
|
+
lexbor/css_match/mod.rs limits.rs node_type.rs
|
|
104
|
+
ptr_table.rs rust_tests.rs stack.rs
|
|
105
|
+
text.rs utf8_input.rs xml/arena.rs
|
|
106
|
+
xml/attr_key.rs xml/chars/expand.rs xml/chars/mod.rs
|
|
107
|
+
xml/dom_name.rs xml/encoding_sniff.rs xml/index.rs
|
|
108
|
+
xml/mod.rs xml/model.rs xml/mutate/attr.rs
|
|
109
|
+
xml/mutate/copy.rs xml/mutate/edit.rs xml/mutate/factory.rs
|
|
110
|
+
xml/mutate/insert.rs xml/mutate/mod.rs xml/mutate/ns.rs
|
|
111
|
+
xml/ns_scope.rs xml/qname.rs xml/selftest.rs
|
|
112
|
+
xml/serialize/bindings.rs xml/serialize/c14n.rs xml/serialize/mod.rs
|
|
113
|
+
xml/serialize/out.rs xml/serialize/xml.rs xml/tree/cursor.rs
|
|
114
|
+
xml/tree/decl.rs xml/tree/dtd.rs xml/tree/mod.rs
|
|
115
|
+
xml/tree/scope.rs xml/verify.rs xml/xpath.rs
|
|
116
|
+
xpath/abi.rs xpath/ast.rs xpath/ast_ops.rs
|
|
117
|
+
xpath/attr_pred.rs xpath/axis.rs xpath/ctx.rs
|
|
118
|
+
xpath/dom.rs xpath/eval.rs xpath/funcs/ext.rs
|
|
119
|
+
xpath/funcs/mod.rs xpath/lex.rs xpath/limits.rs
|
|
120
|
+
xpath/mod.rs xpath/nodetest.rs xpath/number.rs
|
|
121
|
+
xpath/order.rs xpath/parse.rs xpath/step_index.rs
|
|
122
|
+
xpath/str_cache.rs xpath/tests.rs xpath/value.rs
|
|
123
|
+
xpath/verify.rs
|
|
123
124
|
].freeze
|
|
124
125
|
|
|
125
126
|
UNSAFE_USE = /\bunsafe\s*(?:\{|fn\b|impl\b|trait\b|extern\b)/
|
|
@@ -163,7 +164,9 @@ RAW_NODE_CROSSING =
|
|
|
163
164
|
RAW_NODE_CROSSING_COUNTS = {
|
|
164
165
|
"bridge/doc.rs" => 1,
|
|
165
166
|
"bridge/fragment.rs" => 3,
|
|
166
|
-
|
|
167
|
+
# 8th: an Attr's owner, read as the wrapper cache's identity key for the
|
|
168
|
+
# owner-frozen check (`check_attr_owner_frozen`); it does not escape.
|
|
169
|
+
"bridge/html.rs" => 8,
|
|
167
170
|
"bridge/wrapper.rs" => 3,
|
|
168
171
|
}.freeze
|
|
169
172
|
|
|
@@ -425,7 +428,7 @@ if lexbor_abi != LEXBOR_ABI_COUNTS
|
|
|
425
428
|
end
|
|
426
429
|
|
|
427
430
|
# A Lexbor function is DECLARED in one place, `lexbor/abi.rs` (bindgen's output
|
|
428
|
-
# plus the
|
|
431
|
+
# plus the exports no header declares). A second `extern "C"` declaration of the
|
|
429
432
|
# same symbol gives it a second Rust type, which nothing checks agree, and on
|
|
430
433
|
# macOS a declaration that matches no symbol is a NULL call rather than a link
|
|
431
434
|
# error. `post_parse.rs` re-declared four generated functions that way.
|
|
@@ -442,6 +445,31 @@ unless lexbor_decls.empty?
|
|
|
442
445
|
errors << "Lexbor functions declared outside lexbor/abi.rs: #{lexbor_decls.inspect}"
|
|
443
446
|
end
|
|
444
447
|
|
|
448
|
+
# Lexbor's CSS parsers are CALLED in one place each - the function that puts
|
|
449
|
+
# their input through `contains_guard` first (CLAUDE.md: the guard is not
|
|
450
|
+
# optional, and a new parse entry must go through it). A type alone cannot
|
|
451
|
+
# say so: the raw ABI is reachable crate-wide, so a second call site would
|
|
452
|
+
# compile and reach the parser unguarded. Pinned here instead.
|
|
453
|
+
GUARDED_PARSES = {
|
|
454
|
+
"lxb_css_selectors_parse" => "lexbor/css_engine.rs", # SelectorParser::parse
|
|
455
|
+
"lxb_css_stylesheet_parse" => "lexbor/stylesheet.rs", # guarded_stylesheet
|
|
456
|
+
}.freeze
|
|
457
|
+
guarded_calls = Hash.new { |h, k| h[k] = Hash.new(0) }
|
|
458
|
+
Dir.glob(File.join(RUST, "**", "*.rs")).sort.each do |path|
|
|
459
|
+
relative = path.delete_prefix("#{RUST}/")
|
|
460
|
+
code = comments_removed(File.binread(path))
|
|
461
|
+
GUARDED_PARSES.each_key do |name|
|
|
462
|
+
count = code.scan(/\b#{name}\s*\(/).length
|
|
463
|
+
guarded_calls[name][relative] = count unless count.zero?
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
GUARDED_PARSES.each do |name, home|
|
|
467
|
+
next if guarded_calls[name] == { home => 1 }
|
|
468
|
+
|
|
469
|
+
errors << "#{name} must be called exactly once, in #{home} (behind contains_guard): " \
|
|
470
|
+
"#{guarded_calls[name].inspect}"
|
|
471
|
+
end
|
|
472
|
+
|
|
445
473
|
# magnus's `RHash::foreach` runs its closure under magnus's own `protect`, below
|
|
446
474
|
# `rb_hash_foreach`'s C frames, so a panic in it aborts the process instead of
|
|
447
475
|
# raising. `glue::hash::hash_foreach` latches it; every Hash walk goes there.
|
|
@@ -462,7 +490,7 @@ end
|
|
|
462
490
|
# is generated, so its signature is Lexbor's. A new hand declaration of a
|
|
463
491
|
# header-declared function belongs in build.rs's allowlist instead; one with no
|
|
464
492
|
# header belongs in build.rs's UNDECLARED_EXPORTS as well as here.
|
|
465
|
-
LEXBOR_HAND_DECLS =
|
|
493
|
+
LEXBOR_HAND_DECLS = 4
|
|
466
494
|
abi_decls = comments_removed(File.binread(File.join(RUST, "lexbor/abi.rs"))).scan(LEXBOR_DECL).length
|
|
467
495
|
if abi_decls != LEXBOR_HAND_DECLS
|
|
468
496
|
errors << "lexbor/abi.rs hand-declares #{abi_decls} Lexbor functions (expected " \
|
data/suppressions/ruby.supp
CHANGED
|
@@ -161,6 +161,20 @@
|
|
|
161
161
|
...
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
# The global symbol table (`global_symbols.ids`) grows in chunks as symbols are
|
|
165
|
+
# interned, and lives for the life of the VM: RUBY_FREE_AT_EXIT reports its
|
|
166
|
+
# chunk arrays as "definitely lost" whenever the growth happened under one of
|
|
167
|
+
# our cfuncs - `parse_stylesheet` interns its hash keys with `rb_intern` on the
|
|
168
|
+
# first call, and whether that call crosses a chunk boundary depends on how many
|
|
169
|
+
# symbols the process made before it. Not ours to free.
|
|
170
|
+
{
|
|
171
|
+
ruby-global-symbol-table
|
|
172
|
+
Memcheck:Leak
|
|
173
|
+
...
|
|
174
|
+
fun:set_id_entry*
|
|
175
|
+
...
|
|
176
|
+
}
|
|
177
|
+
|
|
164
178
|
# ---- Makiri's process-lifetime CSS compiled-selector cache ----
|
|
165
179
|
# `lexbor::selector_cache` keeps compiled selector lists in a process-global
|
|
166
180
|
# `HashMap<Box<[u8]>, _>` (bounded by CACHE_CAP) that lives until exit by
|