makiri 0.14.0-aarch64-linux → 0.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3c3ed6ee3cd5fadbf71a4841bb984d84aa09b53ae9707da72ef2a4c5e6ce2c4
4
- data.tar.gz: 3e92e200b9d198936e4ae65ee7f43bd0a2271827f3324c5127ea148a4b134b44
3
+ metadata.gz: 651fa28c960d1fd0099870394fc7f19bef09e328ac4851d7102ebfbf9136a6a7
4
+ data.tar.gz: e84db4b38c9fbe3184b90cee36976b005521dfdca1165d10adf1c70970a36918
5
5
  SHA512:
6
- metadata.gz: dc3c6c844fce3e704a0560b5d07908b39082b6528ecf85fb77ea6e3d15c65cea1d7ff0cd99cd48fe52f96bf74ffed160f83cb3f233bf3e6e55de2923234124c4
7
- data.tar.gz: 40b616cb7581a6b2afc5a03f6bdd3f66f7cec98eec398d19bf0121fcb011ff9e2e9f7412fb39976c0638590d9a263ef8ebdd7d285891a4e093142f94c6b097f5
6
+ metadata.gz: 9d9e73501a9744ecf0b16610f52cdf0f8ed17473914654b5bf719cdf3e84fd333cbe77f12305b0d0be9d52da6a9652a659c7a970e0a6e317a98edbe8b4c84b6c
7
+ data.tar.gz: 11627962979c3a7649f3ce09fc4bd5b7bc019626c675263ae73bae9434ebd003b565d9726bb0e8d0d497c80c01bbebfd79c0b677e40f894dcbad1135749b7e53
@@ -5,6 +5,13 @@ on:
5
5
  branches: [main, master]
6
6
  pull_request:
7
7
 
8
+ # A new push to a pull request supersedes the run on its previous head. The
9
+ # event is in the group so a push to main never queues behind (or replaces) a
10
+ # nightly run of the same workflow.
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
13
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
14
+
8
15
  jobs:
9
16
  test:
10
17
  name: Ruby ${{ matrix.ruby }} on ${{ matrix.os }}
@@ -15,6 +15,13 @@ on:
15
15
  - cron: "37 5 * * *" # nightly, offset from the other workflows
16
16
  workflow_dispatch:
17
17
 
18
+ # A new push to a pull request supersedes the run on its previous head. The
19
+ # event is in the group so a push to main never queues behind (or replaces) a
20
+ # nightly run of the same workflow.
21
+ concurrency:
22
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
23
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
24
+
18
25
  jobs:
19
26
  # PR + push: HTML5 parsing conformance, plus the XPath and CSS differentials
20
27
  # over the curated corpus and a moderate batch of generated XPath expressions.
@@ -37,6 +37,13 @@ on:
37
37
  - cron: "41 4 * * *"
38
38
  workflow_dispatch:
39
39
 
40
+ # A new push to a pull request supersedes the run on its previous head. The
41
+ # event is in the group so a push to main never queues behind (or replaces) a
42
+ # nightly run of the same workflow.
43
+ concurrency:
44
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
45
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
46
+
40
47
  env:
41
48
  # Short on a push or PR, long on the nightly schedule.
42
49
  LONG: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
@@ -139,8 +146,12 @@ jobs:
139
146
  # also starts from the other's corpus (read-only: new units go to the
140
147
  # first directory). The cache prefix is libfuzzer.yml's, so the corpus
141
148
  # accumulated there carries over.
149
+ # Restored here, saved after the minimization below - and not on a pull
150
+ # request: a cache a PR saves is scoped to that PR's ref, so main and the
151
+ # nightly never read it, and minimizing it (~5 min) only bought a cache
152
+ # nobody restores.
142
153
  - name: Restore the corpus
143
- uses: actions/cache@v6
154
+ uses: actions/cache/restore@v6
144
155
  with:
145
156
  path: ext/makiri/rust/fuzz/corpus/${{ matrix.target }}
146
157
  key: cargo-fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
@@ -171,6 +182,32 @@ jobs:
171
182
  -dict=css.dict \
172
183
  -print_final_stats=1
173
184
 
185
+ # The corpus is saved after every main/nightly run, so without this it
186
+ # only grows: every nightly hour adds units and nothing ever drops one.
187
+ # It reached ~58k files (with the sibling, ~107k units replayed before
188
+ # the first mutation) of which libFuzzer kept ~2.8k, and a short run
189
+ # spent its whole 180 s replaying - 0 new units, no fuzzing at all.
190
+ # Minimizing before the save keeps only what adds coverage. The first
191
+ # run after a long gap pays one replay of the backlog; later ones a
192
+ # few thousand units.
193
+ - name: Minimize the corpus before it is cached
194
+ if: github.event_name != 'pull_request'
195
+ working-directory: ext/makiri/rust/fuzz
196
+ run: |
197
+ before=$(ls corpus/${{ matrix.target }} | wc -l)
198
+ cargo fuzz cmin ${{ matrix.target }} corpus/${{ matrix.target }} -- \
199
+ -max_len=4096 \
200
+ -timeout=30
201
+ after=$(ls corpus/${{ matrix.target }} | wc -l)
202
+ echo "corpus/${{ matrix.target }}: $before -> $after files"
203
+
204
+ - name: Save the corpus
205
+ if: github.event_name != 'pull_request'
206
+ uses: actions/cache/save@v6
207
+ with:
208
+ path: ext/makiri/rust/fuzz/corpus/${{ matrix.target }}
209
+ key: cargo-fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
210
+
174
211
  - name: Upload reproducers
175
212
  if: failure()
176
213
  uses: actions/upload-artifact@v4
@@ -8,6 +8,13 @@ on:
8
8
  - cron: "17 19 * * *"
9
9
  workflow_dispatch:
10
10
 
11
+ # A new push to a pull request supersedes the run on its previous head. The
12
+ # event is in the group so a push to main never queues behind (or replaces) a
13
+ # nightly run of the same workflow.
14
+ concurrency:
15
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
16
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
17
+
11
18
  jobs:
12
19
  # A "C safety lint" job stood here (`rake security:clint`, over
13
20
  # script/check_c_safety.rb). It enforced rules about C that no longer exists -
@@ -23,6 +23,13 @@ on:
23
23
  - cron: "0 2 * * *"
24
24
  workflow_dispatch:
25
25
 
26
+ # A new push to a pull request supersedes the run on its previous head. The
27
+ # event is in the group so a push to main never queues behind (or replaces) a
28
+ # nightly run of the same workflow.
29
+ concurrency:
30
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
31
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
32
+
26
33
  jobs:
27
34
  # Valgrind memcheck on the full spec suite. Complements ASan by catching
28
35
  # layout-independent errors ASan misses: use of uninitialised values, and
@@ -111,8 +118,8 @@ jobs:
111
118
  # (parse/drop cycles) scale their iteration count down under stress
112
119
  # (`gc_churn_iters` / `GC_COMPACT_ITERS`) because each stressed iteration is
113
120
  # orders of magnitude heavier; `GC_COMPACT_ITERS` below tunes the total runtime
114
- # (~6-9 min on CI at 200). An earlier version forced GC.stress onto the
115
- # *entire* suite (~800 examples): it ran 1h40m+ and never finished, while
121
+ # (~14.5 min on CI at 200 in one job, hence the three shards below). An
122
+ # earlier version forced GC.stress onto the *entire* suite (~800 examples): it ran 1h40m+ and never finished, while
116
123
  # testing borrowed-pointer discipline on hundreds of examples that have none.
117
124
  # The rest of the suite still runs in ci.yml.
118
125
  #
@@ -123,7 +130,7 @@ jobs:
123
130
  # Pull requests, nightly and on demand - not on push (the valgrind job is the
124
131
  # post-merge gate). See the note under `on:` for why pull requests.
125
132
  if: github.event_name != 'push'
126
- name: GC.auto_compact + GC.stress (Ruby ${{ matrix.ruby }})
133
+ name: GC.auto_compact + GC.stress (Ruby ${{ matrix.ruby }}, ${{ matrix.shard.name }})
127
134
  runs-on: ubuntu-latest
128
135
  timeout-minutes: 30
129
136
  env:
@@ -137,6 +144,17 @@ jobs:
137
144
  fail-fast: false
138
145
  matrix:
139
146
  ruby: ["3.4"]
147
+ # Split by spec file into three ~equal parts (measured per file: the
148
+ # handler spec alone is a third, attribute + source_location another).
149
+ # The last shard is everything ELSE, so a newly tagged file lands
150
+ # there rather than in no shard; rebalance if it grows past the others.
151
+ shard:
152
+ - name: xpath_handler
153
+ args: spec/xpath_handler_spec.rb
154
+ - name: attribute
155
+ args: spec/attribute_spec.rb spec/source_location_spec.rb
156
+ - name: rest
157
+ args: --exclude-pattern "spec/{xpath_handler,attribute,source_location}_spec.rb" spec
140
158
 
141
159
  steps:
142
160
  - name: Checkout (with vendored Lexbor submodule)
@@ -166,4 +184,4 @@ jobs:
166
184
  # auto_compact globally and wrap each example in GC.stress; --tag gc_compact
167
185
  # limits the run to the borrowed-pointer examples.
168
186
  - name: Run GC-sensitive examples under GC.auto_compact + GC.stress
169
- run: bundle exec rspec --tag gc_compact spec
187
+ run: bundle exec rspec --tag gc_compact ${{ matrix.shard.args }}
@@ -1,11 +1,37 @@
1
1
  name: Verify
2
2
 
3
3
  on:
4
+ # Ten minutes of proofs, over the Ruby-free engine only - so they run when
5
+ # that engine (or how it is built) changes, not on every push.
4
6
  push:
5
7
  branches: [main, master]
8
+ paths: &kani-paths
9
+ # The proofs build `--no-default-features`, so the Ruby/Lexbor layers
10
+ # below are compiled out of them; a change confined to those cannot
11
+ # change a proof's outcome. Everything else under src/ stays in, since
12
+ # the harnesses reach the engine's shared modules (limits, text, ...).
13
+ - "ext/makiri/rust/src/**"
14
+ - "!ext/makiri/rust/src/bridge/**"
15
+ - "!ext/makiri/rust/src/glue/**"
16
+ - "!ext/makiri/rust/src/lexbor/**"
17
+ - "!ext/makiri/rust/src/css/**"
18
+ - "!ext/makiri/rust/src/init.rs"
19
+ - "ext/makiri/rust/build.rs"
20
+ - "ext/makiri/rust/Cargo.toml"
21
+ - "ext/makiri/rust/Cargo.lock"
22
+ - "Rakefile"
23
+ - ".github/workflows/verify.yml"
6
24
  pull_request:
25
+ paths: *kani-paths
7
26
  workflow_dispatch:
8
27
 
28
+ # A new push to a pull request supersedes the run on its previous head. The
29
+ # event is in the group so a push to main never queues behind (or replaces) a
30
+ # nightly run of the same workflow.
31
+ concurrency:
32
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
33
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
34
+
9
35
  jobs:
10
36
  # A CBMC job stood here and proved ext/makiri/{core,xml,xpath}/*.c. Those
11
37
  # files are gone, and `rake verify` with them: every one of its fifteen
data/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.16.0] - 2026-10-10
4
+
5
+ ### Added
6
+
7
+ * `Makiri::NATIVE_VERSION` and `Makiri::NATIVE_RUBY_API_VERSION`: the gem
8
+ version and Ruby `MAJOR.MINOR` the native extension was built as.
9
+ `require "makiri"` raises `LoadError` for an extension built for another
10
+ Ruby (refused by the extension itself, before it defines anything) or as
11
+ another makiri version (e.g. a stale `lib/makiri/makiri.bundle` in a
12
+ checkout).
13
+ * `Node#child_count`, `#child_at(i)`, `#element_child_count` and
14
+ `#element_child_at(i)` (HTML and XML): count and index a child list without
15
+ building `#children`. A loop over the indexes in order costs O(1) a step,
16
+ also while other lists are read in between. Any child-list edit in the
17
+ document resets that, so a loop that edits as it indexes walks from the
18
+ nearer end after each edit. `child_at` returns nil for a negative index or
19
+ one past the end.
20
+ * `Node#first_child` (an alias of `#child`) and `#last_child`.
21
+ * `Node#root_node`, DOM `getRootNode()`: the Document, a DocumentFragment, or
22
+ the top of a detached subtree. An Attr is its own root.
23
+
24
+ ### Changed
25
+
26
+ * `#local_name`, `#prefix`, `#namespace_uri` and `#tag_name` return frozen,
27
+ interned Strings: every read of one name returns the same object. `#name`
28
+ still returns a new, mutable String.
29
+
30
+ ## [0.15.0] - 2026-10-09
31
+
32
+ ### Added
33
+
34
+ * `Makiri::HTML::Document#create_loose_dom_element(qualified_name, prefix,
35
+ local_name, namespace_uri)`, like the XML Document's: the local name is
36
+ taken as given (`"Foo:Bar"` keeps its colon and case). Names the DOM
37
+ refuses raise `ArgumentError`.
38
+ * `Makiri::XML::Document#quirks_mode?` (always `false`) and `#compat_mode`
39
+ (always `"CSS1Compat"`), as on the HTML Document.
40
+
41
+ ### Changed
42
+
43
+ * `:lexbor-contains()` is no longer supported. `#css` / `#at_css` /
44
+ `#matches?` raise `Makiri::CSS::SyntaxError` for it (HTML and XML), and
45
+ `parse_stylesheet` reports its rule as `:bad_style`. Use XPath `contains()`
46
+ instead.
47
+ * HTML `#css` / `#at_css` / `#matches?` raise `Makiri::Error` ("could not be
48
+ run") for `:current(S)` instead of matching nothing.
49
+
50
+ ### Fixed
51
+
52
+ * `parse_stylesheet` keeps escapes in a selector's `:text`: `.md\:block` came
53
+ back as `.md:block`. `:current(S)` keeps its argument, and a rule
54
+ with `[|a]` is no longer reported as `:bad_style`.
55
+ * `parse_stylesheet` trims only CSS whitespace and never an escaped one:
56
+ `screen\ ` came back as `screen\`. A `:bad_style`'s `selector_text` no
57
+ longer keeps the whitespace before its `{`.
58
+ * HTML `:has()` with a descendant combinator is no longer slow on deep
59
+ documents: `:has(* * x)` exceeded the work budget at 200 levels.
60
+
3
61
  ## [0.14.0] - 2026-10-04
4
62
 
5
63
  ### Added
@@ -964,14 +1022,3 @@ Tooling
964
1022
  benchmark harness (`rake bench`); conformance harnesses (html5lib-tests, WPT
965
1023
  domxpath, CSS differential vs `Nokogiri::HTML5`). GitHub Actions CI across
966
1024
  Ruby 3.2–4.0 × Ubuntu/macOS plus a sanitizer job.
967
-
968
- [0.9.0]: https://github.com/takahashim/makiri/compare/v0.8.0...v0.9.0
969
- [0.8.0]: https://github.com/takahashim/makiri/compare/v0.7.0...v0.8.0
970
- [0.7.0]: https://github.com/takahashim/makiri/compare/v0.6.0...v0.7.0
971
- [0.6.0]: https://github.com/takahashim/makiri/compare/v0.5.1...v0.6.0
972
- [0.5.1]: https://github.com/takahashim/makiri/compare/v0.5.0...v0.5.1
973
- [0.5.0]: https://github.com/takahashim/makiri/compare/v0.4.0...v0.5.0
974
- [0.4.0]: https://github.com/takahashim/makiri/compare/v0.3.0...v0.4.0
975
- [0.3.0]: https://github.com/takahashim/makiri/compare/v0.2.0...v0.3.0
976
- [0.2.0]: https://github.com/takahashim/makiri/compare/v0.1.0...v0.2.0
977
- [0.1.0]: https://github.com/takahashim/makiri/releases/tag/v0.1.0
@@ -245,18 +245,12 @@ what browsers do - rather than libxml2. Detailed, test-backed notes live in
245
245
  text-containment extension. Use XPath (`xpath("//p[contains(., 'x')]")`) or
246
246
  Enumerable (`css('li')[1]`) for the rest.
247
247
  Standard Level-4 selectors (`:is` / `:where` / `:has`) are supported; some of which Nokogiri rejects.
248
- * `:lexbor-contains("text")` - Lexbor's spelling of the jQuery `:contains()`
249
- substring filter, matching an element whose text contains the string;
250
- append ` i` (`:lexbor-contains("text" i)`) for an ASCII case-insensitive
251
- match. (Nokogiri's name `:contains` is not an alias.) **XML only**: it
252
- lowers to XPath `child::text()[contains(., "text")]`, testing the
253
- element's immediate child text nodes, not the deep string-value. HTML no
254
- longer supports it - `lexbor::css_match`, the safe-Rust port that
255
- replaced Lexbor's own matcher for HTML, deliberately does not reimplement
256
- it, so a well-formed `:lexbor-contains()` now raises `Makiri::Error`
257
- ("could not be run") on HTML rather than ever matching (it still PARSES,
258
- and an actually malformed one still raises `Makiri::CSS::SyntaxError`, as
259
- before).
248
+ * There is no text-containment pseudo-class. Nokogiri's `:contains()` is
249
+ not supported, and neither is Lexbor's `:lexbor-contains()`: it raises
250
+ `Makiri::CSS::SyntaxError` on HTML and XML alike, like any unknown
251
+ pseudo-class (Lexbor's parser for it had memory-safety bugs, so Makiri
252
+ never lets it run). Use XPath instead:
253
+ `xpath('//p[contains(., "text")]')`.
260
254
  * Untyped `:*-of-type` (`:first-of-type`, `:nth-of-type(an+b)`, ... with no type
261
255
  selector) is supported and correct on both HTML and XML - the "type" is the
262
256
  element's own expanded name.
data/Rakefile CHANGED
@@ -94,10 +94,12 @@ end
94
94
  # the report down to errors whose stack touches our extension, so we no longer
95
95
  # have to fetch ruby.supp from ruby/ruby (that path was removed upstream).
96
96
  #
97
- # We keep this job's historical contract: catch *use of uninitialised values*
98
- # and *invalid reads/writes* (incl. intra-arena overflows) - NOT leaks (leak
99
- # detection stays with `rake leaks`). So we override ruby_memcheck's defaults,
100
- # which disable undef-value errors and turn on full leak-check.
97
+ # The job catches *use of uninitialised values* and *invalid reads/writes*
98
+ # (incl. intra-arena overflows), and also any "definitely lost" block whose
99
+ # stack touches our extension - the one leak gate with no frequency threshold
100
+ # (`rake leaks`, macOS, flags only stacks repeated >= 30x). It once meant to
101
+ # leave leaks out and could not: see `--leak-check` below. ruby_memcheck's
102
+ # defaults disable undef-value errors, so we override them.
101
103
  #
102
104
  # `filter_all_errors: true` is essential: by default ruby_memcheck only applies
103
105
  # its "stack must touch the makiri binary" filter to *leak*-kind errors
@@ -146,7 +148,19 @@ begin
146
148
  # post-merge push gate turns it off (VALGRIND_TRACK_ORIGINS=no) to run in
147
149
  # ~half the time, while the nightly / manual runs keep it on for the backtrace.
148
150
  "--track-origins=#{ENV.fetch('VALGRIND_TRACK_ORIGINS', 'yes')}",
149
- "--leak-check=no", # leaks are `rake leaks`' job, not this one
151
+ # Leaks ARE checked here, whatever this says: ruby_memcheck always runs
152
+ # valgrind with --xml=yes, and in XML mode memcheck writes its leak
153
+ # records even under --leak-check=no (a "definitely lost" in a spec's
154
+ # child process failed run 37864917248 that way). So say what happens,
155
+ # and keep ruby_memcheck's own leak kinds - these options REPLACE its
156
+ # defaults rather than adding to them, and without the line below
157
+ # valgrind's default (definite,possible) fails the job on a "possibly
158
+ # lost" too. A spec that runs Ruby in a child must leave
159
+ # RUBY_FREE_AT_EXIT set there (it is inherited): without it the child's
160
+ # live strings at exit are reported as definitely lost through our
161
+ # frames (PR #55).
162
+ "--leak-check=full",
163
+ "--show-leak-kinds=definite",
150
164
  ],
151
165
  )
152
166
 
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Makiri
4
- VERSION = "0.14.0"
4
+ VERSION = "0.16.0"
5
5
  end
@@ -34,6 +34,21 @@ module Makiri
34
34
  def dup(*)
35
35
  _copy
36
36
  end
37
+
38
+ # Always false: an XML document is in no-quirks mode, as the DOM has it.
39
+ # The same name as {Makiri::HTML::Document#quirks_mode?}, so a caller
40
+ # need not ask which kind of document it holds.
41
+ # @return [false]
42
+ def quirks_mode?
43
+ false
44
+ end
45
+
46
+ # The DOM's +document.compatMode+, which is always "CSS1Compat" for an
47
+ # XML document.
48
+ # @return [String]
49
+ def compat_mode
50
+ "CSS1Compat"
51
+ end
37
52
  end
38
53
  end
39
54
  end
data/lib/makiri.rb CHANGED
@@ -2,17 +2,36 @@
2
2
 
3
3
  require_relative "makiri/version"
4
4
 
5
- # Native extension (a Rust crate). Located at lib/makiri/<ruby_abi>/makiri.{so,bundle}
6
- # (created by rake-compiler). Loading is gated so the gem can be required
7
- # in environments where the binary is not yet built (the require error
8
- # is then surfaced clearly).
9
- begin
10
- RUBY_VERSION =~ /(\d+\.\d+)/
11
- require_relative "makiri/#{Regexp.last_match(1)}/makiri"
12
- rescue LoadError
5
+ # Native extension (a Rust crate): lib/makiri/<major.minor>/makiri.<dlext> in a
6
+ # precompiled gem (one per Ruby), else lib/makiri/makiri.<dlext> (a source
7
+ # install, or a checkout's `rake compile`). The versioned one is required only
8
+ # if it is there: rescuing LoadError instead would also swallow the extension's
9
+ # own refusal (a binary built for another Ruby raises LoadError from its init)
10
+ # and load the fallback over it.
11
+ versioned = "makiri/#{RUBY_VERSION[/\A\d+\.\d+/]}/makiri"
12
+ if File.exist?(File.join(__dir__, "#{versioned}.#{RbConfig::CONFIG["DLEXT"]}"))
13
+ require_relative versioned
14
+ else
13
15
  require_relative "makiri/makiri"
14
16
  end
15
17
 
18
+ # The fallback loads lib/makiri/makiri.<dlext> whichever version of this gem's
19
+ # Ruby code is asking: a checkout (or a Bundler `path:` gem) keeps one binary
20
+ # from its last compile, and one from another gem version lacks or misnames
21
+ # methods. The extension states what it was built as, and a mismatch stops
22
+ # here; a binary older than the constant states nothing, a mismatch too. (A
23
+ # binary for another Ruby never gets this far: the extension refuses it
24
+ # before it defines anything.)
25
+ module Makiri
26
+ native_version = const_defined?(:NATIVE_VERSION, false) ? NATIVE_VERSION : "(unknown)"
27
+ if native_version != VERSION
28
+ raise LoadError,
29
+ "makiri's native extension was built as makiri #{native_version}, but was " \
30
+ "loaded by makiri #{VERSION}; rebuild it (`bundle exec rake clean compile` in a " \
31
+ "checkout, or reinstall the gem)"
32
+ end
33
+ end
34
+
16
35
  require_relative "makiri/error"
17
36
  require_relative "makiri/clone_via_dup"
18
37
  require_relative "makiri/xpath_syntax"
@@ -345,8 +345,8 @@ SCENARIOS = {
345
345
  # CSS on XML: a different engine from the HTML one above - the selector is
346
346
  # lowered (css/) into an XPath AST, every node of which is a falloc site. One
347
347
  # selector per lowering shape: combinators, attribute operators, the nth and
348
- # of-type arithmetic, the selector-list pseudo-classes, :lexbor-contains and
349
- # namespaced type/universal selectors.
348
+ # of-type arithmetic, the selector-list pseudo-classes and namespaced
349
+ # type/universal selectors.
350
350
  "xml_css" => lambda do
351
351
  doc = Makiri::XML::Document.parse(<<~XML)
352
352
  <root xmlns:p="urn:p">
@@ -362,7 +362,7 @@ SCENARIOS = {
362
362
  ":nth-child(2n+1):not(:last-child)",
363
363
  "a:nth-last-of-type(1), :first-of-type:only-of-type",
364
364
  ":is(root > a, p|*):where(a) :empty",
365
- "root:has(> b + a) a:lexbor-contains(\"BETA\" i)",
365
+ "root:has(> b + a) a",
366
366
  "*|a, |b, p|*",
367
367
  ].map { |s| doc.css(s, ns).map { |n| n.name }.join(",") }.join("\n")
368
368
  end,
@@ -428,6 +428,7 @@ SCENARIOS = {
428
428
  "css_stylesheet" => lambda do
429
429
  css = <<~CSS
430
430
  div.a, p#b > span { color: red; margin: 0 !important }
431
+ .md\\:block, :is(.a\\,b, :not(#x\\.y)) > [a="q\\\\"] { color: red }
431
432
  p::before { content: "x" }
432
433
  @media (min-width: 600px) { .x { color: blue } }
433
434
  @font-face { font-family: F; src: url(f.woff) }
@@ -770,7 +771,7 @@ guard_docs = {
770
771
  "html" => Makiri::HTML::Document.parse("<p>hello</p>"),
771
772
  "xml" => Makiri::XML::Document.parse("<r><p>hello</p></r>")
772
773
  }
773
- [":lexbor-contains(#x)", "p:LEXBOR-CONTAINS(*)"].each do |sel|
774
+ [":lexbor-contains(#x)", "p:LEXBOR-CONTAINS(*)", %(p:lexbor-contains("x"))].each do |sel|
774
775
  guard_docs.each do |kind, doc|
775
776
  disarm
776
777
  3.times do
@@ -33,12 +33,12 @@ UNSAFE_ISLANDS = {
33
33
  "bridge/doc.rs" => 3,
34
34
  "bridge/fragment.rs" => 6,
35
35
  "bridge/gvl.rs" => 5,
36
- "bridge/html.rs" => 17,
36
+ "bridge/html.rs" => 18,
37
37
  "bridge/node_set.rs" => 8,
38
38
  "bridge/node_wrap.rs" => 1,
39
- "bridge/ruby.rs" => 18,
39
+ "bridge/ruby.rs" => 19,
40
40
  "bridge/stack.rs" => 1,
41
- "bridge/string.rs" => 30,
41
+ "bridge/string.rs" => 31,
42
42
  "bridge/typed.rs" => 21,
43
43
  "bridge/wrapper.rs" => 20,
44
44
  "bridge/xml.rs" => 11,
@@ -66,14 +66,14 @@ UNSAFE_ISLANDS = {
66
66
  "lexbor/adapter/tree_guard.rs" => 9,
67
67
  "lexbor/chunks.rs" => 2,
68
68
  "lexbor/css_engine.rs" => 14,
69
- "lexbor/css_parser.rs" => 21,
69
+ "lexbor/css_parser.rs" => 19,
70
70
  "lexbor/css_tokens.rs" => 10,
71
71
  "lexbor/fragment.rs" => 7,
72
72
  "lexbor/memory.rs" => 9,
73
73
  "lexbor/selector_cache.rs" => 15,
74
74
  "lexbor/selectors.rs" => 9,
75
75
  "lexbor/serialize.rs" => 2,
76
- "lexbor/stylesheet.rs" => 8,
76
+ "lexbor/stylesheet.rs" => 9,
77
77
  "lexbor/tests.rs" => 16,
78
78
  "lexbor/xpath.rs" => 9,
79
79
  "token.rs" => 1,
@@ -90,37 +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
- 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
93
+ bridge/dom_error.rs child_index.rs css/build.rs
94
+ css/lower.rs css/mod.rs cutf8.rs
95
+ cutf8/verify.rs dom_rules.rs engine_error.rs
96
+ falloc/verify.rs glue/css.rs glue/html_doc.rs
97
+ glue/html_node/css.rs glue/html_node/mutate.rs glue/html_node/read.rs
98
+ glue/html_node/serialize.rs glue/mod.rs glue/node.rs
99
+ glue/node_set.rs glue/query.rs glue/xml_doc.rs
100
+ glue/xml_node/css.rs glue/xml_node/mod.rs glue/xml_node/mutate.rs
101
+ glue/xml_node/ns.rs glue/xml_node/read.rs glue/xml_node/serialize.rs
102
+ glue/xml_node/strings.rs glue/xpath_context.rs lexbor/adapter/dom_index.rs
103
+ lexbor/contains_guard.rs lexbor/css_match/mod.rs lexbor/selector_text.rs
104
+ limits.rs node_type.rs ptr_table.rs
105
+ rust_tests.rs stack.rs text.rs
106
+ utf8_input.rs xml/arena.rs xml/attr_key.rs
107
+ xml/chars/expand.rs xml/chars/mod.rs xml/dom_name.rs
108
+ xml/encoding_sniff.rs xml/index.rs xml/mod.rs
109
+ xml/model.rs xml/mutate/attr.rs xml/mutate/copy.rs
110
+ xml/mutate/edit.rs xml/mutate/factory.rs xml/mutate/insert.rs
111
+ xml/mutate/mod.rs xml/mutate/ns.rs xml/ns_scope.rs
112
+ xml/qname.rs xml/selftest.rs xml/serialize/bindings.rs
113
+ xml/serialize/c14n.rs xml/serialize/mod.rs xml/serialize/out.rs
114
+ xml/serialize/xml.rs xml/tree/cursor.rs xml/tree/decl.rs
115
+ xml/tree/dtd.rs xml/tree/mod.rs xml/tree/scope.rs
116
+ xml/verify.rs xml/xpath.rs xpath/abi.rs
117
+ xpath/ast.rs xpath/ast_ops.rs xpath/attr_pred.rs
118
+ xpath/axis.rs xpath/ctx.rs xpath/dom.rs
119
+ xpath/eval.rs xpath/funcs/ext.rs xpath/funcs/mod.rs
120
+ xpath/lex.rs xpath/limits.rs xpath/mod.rs
121
+ xpath/nodetest.rs xpath/number.rs xpath/order.rs
122
+ xpath/parse.rs xpath/step_index.rs xpath/str_cache.rs
123
+ xpath/tests.rs xpath/value.rs xpath/verify.rs
124
124
  ].freeze
125
125
 
126
126
  UNSAFE_USE = /\bunsafe\s*(?:\{|fn\b|impl\b|trait\b|extern\b)/
@@ -166,7 +166,11 @@ RAW_NODE_CROSSING_COUNTS = {
166
166
  "bridge/fragment.rs" => 3,
167
167
  # 8th: an Attr's owner, read as the wrapper cache's identity key for the
168
168
  # owner-frozen check (`check_attr_owner_frozen`); it does not escape.
169
- "bridge/html.rs" => 8,
169
+ # 9th/10th: `TokenTree for HtmlTree` (`child_count` / `child_at`), a node's pointer as
170
+ # the child-position memo's token and back. `child_index::ChildPositionMemo`
171
+ # answers only for the `tree_version` it was filled under, so a token read
172
+ # back is still that child; the node is never handed out raw, only wrapped.
173
+ "bridge/html.rs" => 10,
170
174
  "bridge/wrapper.rs" => 3,
171
175
  }.freeze
172
176
 
@@ -718,6 +722,10 @@ def unwrapped_entries(rust)
718
722
  code.to_enum(:scan, REGISTRATION).each do
719
723
  m = Regexp.last_match
720
724
  fpath = first_argument(code, m.end(0) - 1)
725
+ # A generic function registered per representation
726
+ # (`child_count::<HtmlSelf>`) is one definition: resolve it without its
727
+ # type arguments.
728
+ fpath = fpath&.sub(/::<[\w:, ]+>\z/, "")
721
729
  unless fpath&.match?(/\A[\w:]+\z/)
722
730
  missing << "#{rel}: registration of `#{fpath}` is not a function path"
723
731
  next
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: makiri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.16.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - takahashim