leptris 1.9.360.0-arm-linux → 1.9.361.1-arm-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/Rakefile +50 -24
- data/ext/build_native_per_minor.rb +81 -0
- data/ext/build_windows_native.rb +6 -3
- data/ext/check_bundle_linkage.rb +61 -0
- data/ext/leptris/native/extconf.rb +41 -13
- data/ext/leptris/native/native.c +72 -28
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/builder.rb +1 -1
- data/lib/leptris/xml/declaration.rb +8 -1
- data/lib/leptris/xml/document.rb +42 -4
- data/lib/leptris/xml/element.rb +36 -5
- data/lib/leptris/xml/ffi.rb +15 -0
- data/lib/leptris/xml/native.so +0 -0
- data/lib/leptris/xml/native_3_3.so +0 -0
- data/lib/leptris/xml/native_layer.rb +21 -5
- data/lib/leptris/xml/searchable.rb +10 -3
- data/lib/leptris/xml.rb +3 -1
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +34 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/xinclude/xinclude.c +37 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +27 -3
- data/vendor-src/libleptris/test/CMakeLists.txt +1 -0
- data/vendor-src/libleptris/test/xinclude/test_xinclude_fallback_elem.cpp +81 -0
- data/vendor-src/libleptris/test/xpath/test_xpath.cpp +68 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +32 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 62b22d9381c5d319e49dad015e2ff067de8b582f0572d1a950cde8bc7cbaf84f
|
|
4
|
+
data.tar.gz: baa7eaa60f9168c4e50a0514429731cbb7b958a71f5441a8f4bfa8f4a7c9a3cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 259a2ec63050d803356e4208131c438559344714ad23012d3bb3f1b2b5a28e7eaa2f0c307fba8b761084aa500b959c9e903bf95068dcca185d0af5caffc48775
|
|
7
|
+
data.tar.gz: e004e9f4504604bad292f098eab5e111ba80d50ebfb08d4627f008b5a4aa32f5aff93b75165145fb4a745455ef924dbf894cf756a55ff4eda08308362dfc249d
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.361"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
|
@@ -292,13 +292,14 @@ task :compile do
|
|
|
292
292
|
Dir.chdir(ext_dir) do
|
|
293
293
|
sh "ruby extconf.rb"
|
|
294
294
|
if RUBY_PLATFORM =~ /darwin/
|
|
295
|
-
# Link the bundle ourselves:
|
|
296
|
-
#
|
|
297
|
-
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
# interpreter) and a
|
|
295
|
+
# Link the bundle ourselves: mkmf snapshots LIBRUBYARG from
|
|
296
|
+
# RbConfig at require time, so setup-ruby's shared-libruby
|
|
297
|
+
# rubies record their absolute hostedtoolcache path in the
|
|
298
|
+
# bundle (shipped broken through 1.9.36x, #1739) and no
|
|
299
|
+
# post-require clearing can stop it. Compile via the
|
|
300
|
+
# Makefile, link with pure -undefined dynamic_lookup (Ruby
|
|
301
|
+
# symbols resolve from the loading interpreter) and a
|
|
302
|
+
# conservative deployment target.
|
|
302
303
|
sh "make native.o"
|
|
303
304
|
sh "cc -dynamic -bundle -undefined dynamic_lookup " \
|
|
304
305
|
"-mmacosx-version-min=#{ENV['MACOSX_DEPLOYMENT_TARGET'] || '11.0'} " \
|
|
@@ -312,10 +313,11 @@ task :compile do
|
|
|
312
313
|
# their symbols via -rdynamic, like any extension .so).
|
|
313
314
|
sh "make native.o"
|
|
314
315
|
sh "#{RbConfig::CONFIG['CC']} -shared -fPIC -o native.so native.o"
|
|
315
|
-
# Guard the contract in the build log.
|
|
316
|
-
sh "if strings native.so | grep -q libruby; then " \
|
|
317
|
-
"echo 'ERROR: native.so links libruby (#207)'; exit 1; fi"
|
|
318
316
|
end
|
|
317
|
+
# Guard the linkage contract at the build chokepoint (#1739):
|
|
318
|
+
# no libruby reference, allowlisted load commands on darwin.
|
|
319
|
+
sh "#{RbConfig.ruby} ../../check_bundle_linkage.rb " \
|
|
320
|
+
"#{RUBY_PLATFORM =~ /darwin/ ? 'native.bundle' : 'native.so'}"
|
|
319
321
|
end
|
|
320
322
|
bundle = Dir.glob("#{ext_dir}/native.{bundle,so,dll}").first
|
|
321
323
|
if bundle.nil?
|
|
@@ -337,6 +339,18 @@ task :compile do
|
|
|
337
339
|
cp(bundle, "#{dest}.new")
|
|
338
340
|
mv("#{dest}.new", dest)
|
|
339
341
|
sign_darwin_binaries(dest)
|
|
342
|
+
# Per-minor twin (ruby#476): the loader prefers
|
|
343
|
+
# native_<minor>.{bundle,so}; dev builds run same-minor, so
|
|
344
|
+
# the copy is byte-identical and keeps `rake compile` +
|
|
345
|
+
# `rake spec` working without a full release matrix.
|
|
346
|
+
unless Gem.win_platform?
|
|
347
|
+
minor = RUBY_VERSION[/\A\d+\.\d+/].tr('.', '_')
|
|
348
|
+
ext = RUBY_PLATFORM.include?('darwin') ? '.bundle' : '.so'
|
|
349
|
+
twin = "lib/leptris/xml/native_#{minor}#{ext}"
|
|
350
|
+
cp(dest, "#{twin}.new")
|
|
351
|
+
mv("#{twin}.new", twin)
|
|
352
|
+
sign_darwin_binaries(twin)
|
|
353
|
+
end
|
|
340
354
|
puts "Vendored native layer (#{File.basename(bundle)}) into lib/leptris/xml/"
|
|
341
355
|
end
|
|
342
356
|
end
|
|
@@ -412,11 +426,19 @@ namespace :audit do
|
|
|
412
426
|
# leptris_version() equals the gem's engine pin (a half-bumped
|
|
413
427
|
# lockstep state can no longer package). Negative proof lives
|
|
414
428
|
# in spec/audit_codesign_spec.rb.
|
|
415
|
-
desc "Fail on unsigned/tampered macOS artifacts
|
|
429
|
+
desc "Fail on unsigned/tampered macOS artifacts, engine-version drift, or libruby-linked native layers"
|
|
416
430
|
task :artifacts do
|
|
431
|
+
# Linkage contract over EVERY shipped native layer, per-minor
|
|
432
|
+
# twins included (#1739) — every platform: the 1.9.36x trains
|
|
433
|
+
# packaged runner-absolute libruby loads in native_3_2/3_4
|
|
434
|
+
# artifacts that audit:symbols/audit:artifacts never opened.
|
|
435
|
+
bundles = Dir.glob("lib/leptris/xml/native*.{bundle,so}")
|
|
436
|
+
bundles.each do |b|
|
|
437
|
+
sh "#{RbConfig.ruby} ext/check_bundle_linkage.rb #{b}"
|
|
438
|
+
end
|
|
417
439
|
unless RUBY_PLATFORM =~ /darwin/
|
|
418
|
-
puts "audit:artifacts:
|
|
419
|
-
"
|
|
440
|
+
puts "audit:artifacts: #{bundles.size} native layers linkage-clean; " \
|
|
441
|
+
"codesign gates skip off-darwin"
|
|
420
442
|
next
|
|
421
443
|
end
|
|
422
444
|
abort "audit:artifacts: codesign unavailable" \
|
|
@@ -439,8 +461,8 @@ namespace :audit do
|
|
|
439
461
|
abort "audit:artifacts: engine-version drift — vendored " \
|
|
440
462
|
"#{vendored.inspect} != pin #{pinned.inspect}"
|
|
441
463
|
end
|
|
442
|
-
puts "audit:artifacts: #{libs.size} macOS artifacts
|
|
443
|
-
"strictly and engine-synced (#{pinned})"
|
|
464
|
+
puts "audit:artifacts: #{libs.size} macOS artifacts + #{bundles.size} native " \
|
|
465
|
+
"layers verify strictly, linkage-clean and engine-synced (#{pinned})"
|
|
444
466
|
end
|
|
445
467
|
|
|
446
468
|
# Packaging doctrine (#271): every BUILT gem carries the engine
|
|
@@ -563,14 +585,18 @@ platforms.each do |platform|
|
|
|
563
585
|
spec.files += Dir.glob("vendor-src/**/*")
|
|
564
586
|
spec.files += %w[ext/leptris/native/extconf.rb
|
|
565
587
|
ext/leptris/native/native.c]
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
588
|
+
# #207/#227 + ruby#476: per-Ruby-minor bundles on EVERY
|
|
589
|
+
# platform (the ext's compiled-in RString layout changed in
|
|
590
|
+
# 3.2 — one CI-built bundle segfaults 3.1). DOT-FREE names —
|
|
591
|
+
# MRI derives the init symbol from the basename cut at the
|
|
592
|
+
# first dot (native_3_3.so -> Init_native_3_3; the dotted form
|
|
593
|
+
# made Ruby look for "Init_native-3", which cannot exist).
|
|
594
|
+
spec.files += Dir.glob("lib/leptris/xml/native_*.so")
|
|
595
|
+
spec.files += Dir.glob("lib/leptris/xml/native_*.bundle")
|
|
596
|
+
if !platform.include?("mingw")
|
|
597
|
+
# The unversioned bundle rides for toolchains that only run
|
|
598
|
+
# rake compile's single build (native_layer prefers the
|
|
599
|
+
# per-minor variant and never loads a wrong-ABI file).
|
|
574
600
|
spec.files += Dir.glob("lib/leptris/xml/native.{bundle,so,dll}")
|
|
575
601
|
end
|
|
576
602
|
task = Gem::PackageTask.new(spec)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Builds the native read layer bundle for the CURRENT Ruby and
|
|
4
|
+
# installs it under its minor-versioned name
|
|
5
|
+
# (lib/leptris/xml/native_<major>_<minor>.bundle on darwin, .so on
|
|
6
|
+
# Linux).
|
|
7
|
+
#
|
|
8
|
+
# ruby#476: the ext's compiled-in RString layout changed in Ruby
|
|
9
|
+
# 3.2 — a bundle built by a newer minor reads garbage pointers and
|
|
10
|
+
# lengths under 3.1 (the parse segfaulted inside the engine's
|
|
11
|
+
# correctly-bounded NEON text counter). One bundle per supported
|
|
12
|
+
# minor therefore ships in the darwin/Linux platform gems — the
|
|
13
|
+
# same contract the Windows gems have had since #207/#227 — and
|
|
14
|
+
# native_layer picks by RUBY_VERSION at require, falling back to
|
|
15
|
+
# the FFI surface when the variant for the running minor is
|
|
16
|
+
# absent. A WRONG-ABI load is never attempted.
|
|
17
|
+
#
|
|
18
|
+
# The name is DOT-FREE: MRI derives a C extension's init symbol
|
|
19
|
+
# from the require feature's basename cut at the first dot, so
|
|
20
|
+
# native_3_1.bundle resolves Init_native_3_1 (exported by
|
|
21
|
+
# native.c).
|
|
22
|
+
|
|
23
|
+
require "rbconfig"
|
|
24
|
+
require "fileutils"
|
|
25
|
+
|
|
26
|
+
root = File.expand_path("..", __dir__)
|
|
27
|
+
ext_dir = File.join(root, "ext", "leptris", "native")
|
|
28
|
+
minor = RUBY_VERSION[/\A\d+\.\d+/]
|
|
29
|
+
minor_us = minor.tr(".", "_")
|
|
30
|
+
ext = RUBY_PLATFORM.include?("darwin") ? ".bundle" : ".so"
|
|
31
|
+
|
|
32
|
+
Dir.chdir(ext_dir) do
|
|
33
|
+
# Force a clean compile on every invocation (stale objects from
|
|
34
|
+
# a failed earlier attempt ship silent wrong-ABI builds).
|
|
35
|
+
FileUtils.rm_f(Dir.glob("#{ext_dir}/native.{o,so,bundle}"))
|
|
36
|
+
system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
|
|
37
|
+
# Link WITHOUT libruby (#1739): mkmf's Makefile rule records the
|
|
38
|
+
# building ruby's libruby — for setup-ruby builds by absolute
|
|
39
|
+
# hostedtoolcache path — in every LC_LOAD_DYLIB / DT_NEEDED, which
|
|
40
|
+
# dlopen-fails on any other machine. Compile via the Makefile,
|
|
41
|
+
# link exactly like rake compile's native branch: pure
|
|
42
|
+
# dynamic_lookup, rb_* resolve from the loading interpreter.
|
|
43
|
+
system("make native.o") or abort "make native.o failed under #{RUBY_VERSION}"
|
|
44
|
+
cc = RbConfig::CONFIG["CC"]
|
|
45
|
+
link =
|
|
46
|
+
if ext == ".bundle"
|
|
47
|
+
"#{cc} -dynamic -bundle -undefined dynamic_lookup " \
|
|
48
|
+
"-mmacosx-version-min=#{ENV['MACOSX_DEPLOYMENT_TARGET'] || '11.0'} " \
|
|
49
|
+
"-o native.bundle native.o"
|
|
50
|
+
else
|
|
51
|
+
"#{cc} -shared -fPIC -o native.so native.o"
|
|
52
|
+
end
|
|
53
|
+
system(link) or abort "native link failed under #{RUBY_VERSION}: #{link}"
|
|
54
|
+
built = Dir.glob("native.{so,bundle}").first
|
|
55
|
+
abort "native bundle not produced under #{RUBY_VERSION}" unless built
|
|
56
|
+
|
|
57
|
+
# Linkage gate: no libruby reference, and (darwin) only
|
|
58
|
+
# allowlisted load commands — enforced HERE so a violated bundle
|
|
59
|
+
# never reaches packaging (the 1.9.36x trains shipped exactly
|
|
60
|
+
# this shape, #1739).
|
|
61
|
+
system(RbConfig.ruby, File.join(root, "ext", "check_bundle_linkage.rb"),
|
|
62
|
+
File.expand_path(built)) or
|
|
63
|
+
abort "linkage gate failed under #{RUBY_VERSION}"
|
|
64
|
+
|
|
65
|
+
# Functional gate: dlopen the built bundle and resolve the
|
|
66
|
+
# MINOR-SUFFIXED init symbol — only a real lookup proves
|
|
67
|
+
# require "leptris/xml/native_<minor>" will resolve.
|
|
68
|
+
begin
|
|
69
|
+
require "fiddle"
|
|
70
|
+
Fiddle.dlopen(File.expand_path(built))["Init_native_#{minor_us}"]
|
|
71
|
+
rescue LoadError => e
|
|
72
|
+
warn "fiddle unavailable under #{RUBY_VERSION} (#{e.message}) — skipping"
|
|
73
|
+
rescue Fiddle::DLError => e
|
|
74
|
+
abort "#{built} does not export Init_native_#{minor_us} — refusing to " \
|
|
75
|
+
"install a bundle that would fail at require (#{e.message})"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
dest = File.join(root, "lib", "leptris", "xml", "native_#{minor_us}#{ext}")
|
|
79
|
+
FileUtils.cp(built, dest)
|
|
80
|
+
puts "Installed native layer for Ruby #{minor} -> #{dest}"
|
|
81
|
+
end
|
data/ext/build_windows_native.rb
CHANGED
|
@@ -29,14 +29,17 @@ Dir.chdir(ext_dir) do
|
|
|
29
29
|
# because make reused a stale native.obj from a failed earlier
|
|
30
30
|
# attempt (the log shows "creating Makefile -> linking" with no
|
|
31
31
|
# compile). Force a clean compile on every invocation.
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
# The globs cover both spellings of the mkmf target: the
|
|
33
|
+
# unversioned name and the per-minor name extconf now creates
|
|
34
|
+
# the Makefile for (#476 source-install parity).
|
|
35
|
+
FileUtils.rm_f(Dir.glob("#{ext_dir}/native*.{o,obj,so,dll}"))
|
|
36
|
+
FileUtils.rm_f(Dir.glob("#{ext_dir}/leptris/xml/native*.so"))
|
|
34
37
|
system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
|
|
35
38
|
# mkmf's link binds the CURRENT Ruby's runtime DLL — exactly
|
|
36
39
|
# what the versioned naming is for.
|
|
37
40
|
success = system("make")
|
|
38
41
|
abort "make failed under #{RUBY_VERSION}" unless success
|
|
39
|
-
so = Dir.glob("native
|
|
42
|
+
so = Dir.glob("native*.so").first
|
|
40
43
|
abort "native bundle not produced under #{RUBY_VERSION}" unless so
|
|
41
44
|
# Functional export gate: dlopen the built DLL and resolve the
|
|
42
45
|
# MINOR-SUFFIXED init symbol. A PE DLL can carry the symbol in its
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Linkage contract for shipped native layers (#1739 / ruby#481): a
|
|
4
|
+
# Ruby extension resolves rb_* symbols from the LOADING interpreter,
|
|
5
|
+
# so the artifact must never link libruby — mkmf records the
|
|
6
|
+
# building ruby's absolute path (setup-ruby's hostedtoolcache
|
|
7
|
+
# location), which dlopen-fails on every other machine and silently
|
|
8
|
+
# degrades the whole install to FFI. On darwin the check is
|
|
9
|
+
# stricter: every load command must stay inside the loader
|
|
10
|
+
# allowlist. Windows PE DLLs are exempt — binding the build
|
|
11
|
+
# runtime is the documented contract there (#227).
|
|
12
|
+
#
|
|
13
|
+
# Runnable as a script over any number of artifacts (exit 1 on the
|
|
14
|
+
# first violated contract batch) or required for the pure checks in
|
|
15
|
+
# spec/check_bundle_linkage_spec.rb.
|
|
16
|
+
module CheckBundleLinkage
|
|
17
|
+
ALLOWED_PREFIXES = %w[@rpath/ @loader_path/ @executable_path/ /usr/lib/ /System/].freeze
|
|
18
|
+
|
|
19
|
+
# The LC_LOAD_DYLIB paths otool -L lists that fall outside the
|
|
20
|
+
# allowlist. +otool_output+ is the raw stdout of `otool -L <file>`;
|
|
21
|
+
# entries are the tab-indented "<install name> (compatibility
|
|
22
|
+
# version X, current version Y)" lines.
|
|
23
|
+
def self.load_command_violations(otool_output)
|
|
24
|
+
otool_output.lines.grep(/^\t.*\(compatibility version/)
|
|
25
|
+
.filter_map { |l| l.split.first }
|
|
26
|
+
.reject { |path| ALLOWED_PREFIXES.any? { |p| path.start_with?(p) } }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The libruby sonames a strings(1) dump reveals — the ELF-side
|
|
30
|
+
# tripwire for DT_NEEDED entries and RUNPATH spellings alike.
|
|
31
|
+
def self.libruby_references(strings_output)
|
|
32
|
+
strings_output.scan(/libruby[\w.]*/).uniq
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if $PROGRAM_NAME == __FILE__
|
|
37
|
+
require "shellwords"
|
|
38
|
+
|
|
39
|
+
status = 0
|
|
40
|
+
ARGV.each do |path|
|
|
41
|
+
next if path.end_with?(".dll")
|
|
42
|
+
|
|
43
|
+
bad = CheckBundleLinkage.libruby_references(`strings #{Shellwords.escape(path)} 2>/dev/null`)
|
|
44
|
+
unless bad.empty?
|
|
45
|
+
warn "ERROR: #{path} references #{bad.join(', ')} — Ruby symbols must " \
|
|
46
|
+
"resolve from the loading interpreter, never a linked libruby " \
|
|
47
|
+
"(#1739); link with -undefined dynamic_lookup / plain -shared"
|
|
48
|
+
status = 1
|
|
49
|
+
end
|
|
50
|
+
next unless RUBY_PLATFORM.include?("darwin")
|
|
51
|
+
|
|
52
|
+
viol = CheckBundleLinkage.load_command_violations(`otool -L #{Shellwords.escape(path)} 2>/dev/null`)
|
|
53
|
+
next if viol.empty?
|
|
54
|
+
|
|
55
|
+
warn "ERROR: #{path} carries load commands outside the loader " \
|
|
56
|
+
"allowlist (#{CheckBundleLinkage::ALLOWED_PREFIXES.join(' ')}): " \
|
|
57
|
+
"#{viol.join(', ')} (#1739)"
|
|
58
|
+
status = 1
|
|
59
|
+
end
|
|
60
|
+
exit status
|
|
61
|
+
end
|
|
@@ -28,6 +28,19 @@ unless %w[ruby truffleruby].include?(RUBY_ENGINE)
|
|
|
28
28
|
"installing without the compiled native layer"
|
|
29
29
|
exit 0
|
|
30
30
|
end
|
|
31
|
+
# Never link libruby: a Ruby extension resolves rb_* symbols from
|
|
32
|
+
# the LOADING interpreter, and mkmf records the building ruby's
|
|
33
|
+
# libruby (for setup-ruby, an absolute hostedtoolcache path) into
|
|
34
|
+
# the bundle otherwise — dlopen-failing on every other machine
|
|
35
|
+
# (#1739). mkmf snapshots $LIBRUBYARG* from RbConfig at require
|
|
36
|
+
# time, so this MUST precede `require "mkmf"`; a later clearing is
|
|
37
|
+
# a silent no-op (exactly how the 1.9.36x trains shipped broken).
|
|
38
|
+
if RUBY_PLATFORM =~ /darwin/
|
|
39
|
+
%w[LIBRUBYARG LIBRUBYARG_SHARED LIBRUBYARG_STATIC].each do |key|
|
|
40
|
+
RbConfig::CONFIG[key] = ""
|
|
41
|
+
RbConfig::MAKEFILE_CONFIG[key] = ""
|
|
42
|
+
end
|
|
43
|
+
end
|
|
31
44
|
require "mkmf"
|
|
32
45
|
|
|
33
46
|
# A prebuilt libleptris in lib/ (dev checkout after rake compile,
|
|
@@ -106,18 +119,25 @@ if RUBY_PLATFORM =~ /darwin/
|
|
|
106
119
|
[$LDFLAGS, $DLDFLAGS].each do |var|
|
|
107
120
|
var << lookup unless var.include?("dynamic_lookup")
|
|
108
121
|
end
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
# ruby#476: newer ld64 defaults BUNDLES to two-level namespace —
|
|
123
|
+
# with -undefined dynamic_lookup a two-level bind records the
|
|
124
|
+
# symbol as expected from the MAIN EXECUTABLE ("main-executable"),
|
|
125
|
+
# and interpreters that don't re-export every ruby symbol from
|
|
126
|
+
# their binary abort at first call ("dyld missing symbol called",
|
|
127
|
+
# hit at Init_native's first import). -flat_namespace is the
|
|
128
|
+
# classic C-extension pattern: dynamic_lookup symbols search all
|
|
129
|
+
# loaded images at call time and resolve from libruby.
|
|
130
|
+
flat = " -Wl,-flat_namespace"
|
|
131
|
+
[$LDFLAGS, $DLDFLAGS].each do |var|
|
|
132
|
+
var << flat unless var.include?("flat_namespace")
|
|
133
|
+
end
|
|
134
|
+
# Belt for the require-time snapshot: RbConfig was cleared BEFORE
|
|
135
|
+
# `require "mkmf"` (top of file), and the module globals mkmf
|
|
136
|
+
# captured then are zeroed here too — a Ruby extension resolves
|
|
137
|
+
# Ruby symbols from the loading interpreter; never link libruby.
|
|
138
|
+
$LIBRUBYARG = ""
|
|
139
|
+
$LIBRUBYARG_SHARED = ""
|
|
140
|
+
$LIBRUBYARG_STATIC = ""
|
|
121
141
|
end
|
|
122
142
|
if Gem.win_platform?
|
|
123
143
|
# Windows names the artifact per Ruby minor, DOT-FREE (#227
|
|
@@ -137,4 +157,12 @@ if Gem.win_platform?
|
|
|
137
157
|
win_def = " #{def_path.tr('/', '\\\\')}"
|
|
138
158
|
$DLDFLAGS << win_def unless $DLDFLAGS.include?("native_exports.def")
|
|
139
159
|
end
|
|
140
|
-
|
|
160
|
+
# Name the artifact per Ruby minor EVERYWHERE (#476): the loader
|
|
161
|
+
# requires native_<minor> and deliberately has NO unversioned
|
|
162
|
+
# fallback (loading a bundle built by another minor is the 3.1
|
|
163
|
+
# wrong-ABI segfault class), so a source install must produce the
|
|
164
|
+
# per-minor name itself. mkmf derives Init_native_<minor> from the
|
|
165
|
+
# same basename — exported by native.c and pinned in the Windows
|
|
166
|
+
# .def above.
|
|
167
|
+
minor_us = RUBY_VERSION[/\A\d+\.\d+/].tr(".", "_")
|
|
168
|
+
create_makefile("leptris/xml/native_#{minor_us}")
|
data/ext/leptris/native/native.c
CHANGED
|
@@ -157,6 +157,19 @@ static node_xpath_fn f_node_xpath;
|
|
|
157
157
|
static elem_copy_fn f_elem_copy;
|
|
158
158
|
static parse_str_fn f_parse_str;
|
|
159
159
|
static parse_frag_fn f_parse_frag;
|
|
160
|
+
/* LeptrisParseOptions mirror (frozen layout since 1.9.0: flags,
|
|
161
|
+
* strict_mode, max_depth, recover — the FFI ParseOptionsStruct
|
|
162
|
+
* shape). Only recover is ever set: the recover-default parse
|
|
163
|
+
* (ruby#465) routes through the ex face on the C side. */
|
|
164
|
+
struct leptris_parse_opts_mirror {
|
|
165
|
+
int flags;
|
|
166
|
+
int strict_mode;
|
|
167
|
+
int max_depth;
|
|
168
|
+
int recover;
|
|
169
|
+
};
|
|
170
|
+
typedef void *(*parse_str_ex_fn)(const char *, size_t,
|
|
171
|
+
struct leptris_parse_opts_mirror *, void *);
|
|
172
|
+
static parse_str_ex_fn f_parse_str_ex;
|
|
160
173
|
static last_err_fn f_last_err;
|
|
161
174
|
static status_str_fn f_status_str;
|
|
162
175
|
static set_root_fn f_set_root;
|
|
@@ -173,6 +186,13 @@ static ID id_iv_nn_content, id_iv_nn_content_ver;
|
|
|
173
186
|
static ID id_iv_nn_attrs, id_iv_nn_attrs_ver;
|
|
174
187
|
static ID id_child_klasses;
|
|
175
188
|
static VALUE c_use_after_free_error;
|
|
189
|
+
/* ruby#476: rb_cObject / rb_e* are DATA symbols — under the newer
|
|
190
|
+
* macOS linker's chained fixups a -undefined dynamic_lookup DATA
|
|
191
|
+
* import binds at LOAD and fails ("Symbol not found: _rb_cObject")
|
|
192
|
+
* on interpreters whose main executable does not re-export them
|
|
193
|
+
* (mise builds). Function imports bind lazily and always resolve,
|
|
194
|
+
* so these four resolve at INIT via rb_path2class into statics. */
|
|
195
|
+
static VALUE c_object, c_arg_error, c_type_error, c_runtime_error;
|
|
176
196
|
|
|
177
197
|
static VALUE native_cache_of(VALUE document);
|
|
178
198
|
static void resolve_binding_classes(void);
|
|
@@ -236,7 +256,7 @@ static void resolve_symbols(const char *lib_path)
|
|
|
236
256
|
{
|
|
237
257
|
void *h = lib_open(lib_path);
|
|
238
258
|
if (!h)
|
|
239
|
-
rb_raise(
|
|
259
|
+
rb_raise(c_runtime_error, "cannot load library %s", lib_path);
|
|
240
260
|
f_elem_name = (elem_name_fn)lib_sym(h, "leptris_element_name");
|
|
241
261
|
f_text_content = (text_content_fn)lib_sym(h, "leptris_text_node_get_content");
|
|
242
262
|
f_attr = (attr_fn)lib_sym(h, "leptris_element_attribute");
|
|
@@ -304,6 +324,7 @@ static void resolve_symbols(const char *lib_path)
|
|
|
304
324
|
f_node_xpath = (node_xpath_fn)lib_sym(h, "leptris_node_get_xpath");
|
|
305
325
|
f_elem_copy = (elem_copy_fn)lib_sym(h, "leptris_element_copy");
|
|
306
326
|
f_parse_str = (parse_str_fn)lib_sym(h, "leptris_parse_string");
|
|
327
|
+
f_parse_str_ex = (parse_str_ex_fn)lib_sym(h, "leptris_parse_string_ex");
|
|
307
328
|
f_parse_frag = (parse_frag_fn)lib_sym(h, "leptris_parse_fragment");
|
|
308
329
|
f_last_err = (last_err_fn)lib_sym(h, "leptris_last_error");
|
|
309
330
|
f_status_str = (status_str_fn)lib_sym(h, "leptris_status_string");
|
|
@@ -326,7 +347,7 @@ static void resolve_symbols(const char *lib_path)
|
|
|
326
347
|
!f_text_set_content || !f_node_unlink ||
|
|
327
348
|
!f_node_traverse || !f_node_visit || !f_free_str ||
|
|
328
349
|
!f_node_xpath || !f_elem_copy || !f_parse_str ||
|
|
329
|
-
!f_parse_frag || !f_last_err || !f_status_str ||
|
|
350
|
+
!f_parse_frag || !f_parse_str_ex || !f_last_err || !f_status_str ||
|
|
330
351
|
!f_doc_free || !f_sax_parse || !f_sax_create || !f_sax_feed ||
|
|
331
352
|
!f_sax_pfree || !f_sax_set_flags ||
|
|
332
353
|
!f_elem_prefix || !f_xp_count || !f_xp_nodes_ex ||
|
|
@@ -335,7 +356,7 @@ static void resolve_symbols(const char *lib_path)
|
|
|
335
356
|
!f_elem_ns_count || !f_elem_ns ||
|
|
336
357
|
!f_attr_next || !f_attr_name || !f_attr_value ||
|
|
337
358
|
!f_node_line || !f_node_offset)
|
|
338
|
-
rb_raise(
|
|
359
|
+
rb_raise(c_runtime_error, "libleptris symbols missing");
|
|
339
360
|
}
|
|
340
361
|
|
|
341
362
|
static VALUE nn_name(VALUE self)
|
|
@@ -487,7 +508,7 @@ static VALUE nn_install_child_klasses(VALUE self, VALUE map)
|
|
|
487
508
|
Check_Type(map, T_ARRAY);
|
|
488
509
|
len = RARRAY_LEN(map);
|
|
489
510
|
if (len > 5)
|
|
490
|
-
rb_raise(
|
|
511
|
+
rb_raise(c_arg_error,
|
|
491
512
|
"child klasses: Array of classes/nil indexed by "
|
|
492
513
|
"node kind (element, text, comment, cdata, pi)");
|
|
493
514
|
for (i = 0; i < len; i++) {
|
|
@@ -495,7 +516,7 @@ static VALUE nn_install_child_klasses(VALUE self, VALUE map)
|
|
|
495
516
|
if (!NIL_P(k) &&
|
|
496
517
|
!(RB_TYPE_P(k, T_CLASS) &&
|
|
497
518
|
RTEST(rb_class_inherited_p(k, c_native_node))))
|
|
498
|
-
rb_raise(
|
|
519
|
+
rb_raise(c_type_error,
|
|
499
520
|
"child klasses entries must be nil or a "
|
|
500
521
|
"Leptris::XML::NativeNode subclass");
|
|
501
522
|
}
|
|
@@ -685,7 +706,7 @@ static void *doc_ptr_of(VALUE document)
|
|
|
685
706
|
{
|
|
686
707
|
VALUE addr = rb_ivar_get(document, id_iv_c_address);
|
|
687
708
|
if (NIL_P(addr))
|
|
688
|
-
rb_raise(
|
|
709
|
+
rb_raise(c_runtime_error, "document has been freed");
|
|
689
710
|
return (void *)(uintptr_t)NUM2ULL(addr);
|
|
690
711
|
}
|
|
691
712
|
|
|
@@ -730,7 +751,7 @@ static VALUE nf_register_doc_klasses(VALUE self, VALUE document, VALUE map)
|
|
|
730
751
|
Check_Type(map, T_ARRAY);
|
|
731
752
|
len = RARRAY_LEN(map);
|
|
732
753
|
if (len > 5)
|
|
733
|
-
rb_raise(
|
|
754
|
+
rb_raise(c_arg_error,
|
|
734
755
|
"wrapper klasses: Array of classes/nil indexed by "
|
|
735
756
|
"node kind (element, text, comment, cdata, pi)");
|
|
736
757
|
for (i = 0; i < len; i++) {
|
|
@@ -738,7 +759,7 @@ static VALUE nf_register_doc_klasses(VALUE self, VALUE document, VALUE map)
|
|
|
738
759
|
if (!NIL_P(k) &&
|
|
739
760
|
!(RB_TYPE_P(k, T_CLASS) &&
|
|
740
761
|
RTEST(rb_class_inherited_p(k, c_native_node))))
|
|
741
|
-
rb_raise(
|
|
762
|
+
rb_raise(c_type_error,
|
|
742
763
|
"wrapper klasses entries must be nil or a "
|
|
743
764
|
"Leptris::XML::NativeNode subclass");
|
|
744
765
|
}
|
|
@@ -1378,7 +1399,7 @@ static VALUE nf_insert_binding_child(VALUE self, VALUE document,
|
|
|
1378
1399
|
case 2: st = f_insert_after(anchor, child); break;
|
|
1379
1400
|
case 3: st = f_insert_before(anchor, child); break;
|
|
1380
1401
|
default:
|
|
1381
|
-
rb_raise(
|
|
1402
|
+
rb_raise(c_arg_error, "invalid insert mode %d", m);
|
|
1382
1403
|
return Qnil;
|
|
1383
1404
|
}
|
|
1384
1405
|
check_status_c(st);
|
|
@@ -1393,7 +1414,7 @@ static VALUE nn_create_element(VALUE klass, VALUE document, VALUE name)
|
|
|
1393
1414
|
(void)klass;
|
|
1394
1415
|
ptr = f_elem_create(doc_ptr_of(document), StringValueCStr(name));
|
|
1395
1416
|
if (!ptr)
|
|
1396
|
-
rb_raise(
|
|
1417
|
+
rb_raise(c_runtime_error, "leptris_element_create failed");
|
|
1397
1418
|
return wrap_new(document, ptr);
|
|
1398
1419
|
}
|
|
1399
1420
|
|
|
@@ -1403,7 +1424,7 @@ static VALUE nn_create_text(VALUE klass, VALUE document, VALUE content)
|
|
|
1403
1424
|
(void)klass;
|
|
1404
1425
|
ptr = f_text_create(doc_ptr_of(document), StringValueCStr(content));
|
|
1405
1426
|
if (!ptr)
|
|
1406
|
-
rb_raise(
|
|
1427
|
+
rb_raise(c_runtime_error, "leptris_text_node_create failed");
|
|
1407
1428
|
return wrap_new(document, ptr);
|
|
1408
1429
|
}
|
|
1409
1430
|
|
|
@@ -1416,7 +1437,7 @@ static VALUE nn_create_child(VALUE self, VALUE name)
|
|
|
1416
1437
|
document_advance_version(n->document);
|
|
1417
1438
|
ptr = f_create_child(n->ptr, StringValueCStr(name));
|
|
1418
1439
|
if (!ptr)
|
|
1419
|
-
rb_raise(
|
|
1440
|
+
rb_raise(c_runtime_error, "leptris_element_create_child failed");
|
|
1420
1441
|
out = wrap_new(n->document, ptr);
|
|
1421
1442
|
return out;
|
|
1422
1443
|
}
|
|
@@ -1430,7 +1451,7 @@ static VALUE nn_append_child(VALUE self, VALUE child)
|
|
|
1430
1451
|
document_advance_version(n->document);
|
|
1431
1452
|
rc = f_append_child(n->ptr, c->ptr);
|
|
1432
1453
|
if (rc != 0)
|
|
1433
|
-
rb_raise(
|
|
1454
|
+
rb_raise(c_runtime_error, "leptris_element_append_child failed (%d)", rc);
|
|
1434
1455
|
return child;
|
|
1435
1456
|
}
|
|
1436
1457
|
|
|
@@ -1443,7 +1464,7 @@ static VALUE nn_set_root(VALUE klass, VALUE document, VALUE element)
|
|
|
1443
1464
|
document_advance_version(document);
|
|
1444
1465
|
rc = f_set_root(doc_ptr_of(document), n->ptr);
|
|
1445
1466
|
if (rc != 0)
|
|
1446
|
-
rb_raise(
|
|
1467
|
+
rb_raise(c_runtime_error, "leptris_document_set_root failed (%d)", rc);
|
|
1447
1468
|
return element;
|
|
1448
1469
|
}
|
|
1449
1470
|
|
|
@@ -2897,14 +2918,28 @@ static void check_status_c(int st)
|
|
|
2897
2918
|
/* Document.parse default path in one dispatch (TODO.perf/35):
|
|
2898
2919
|
* leptris_parse_string + wrapper + handle. Qnil = parse failure
|
|
2899
2920
|
* (the caller raises with the same message shape). */
|
|
2900
|
-
static VALUE nf_parse_binding_document(VALUE
|
|
2921
|
+
static VALUE nf_parse_binding_document(int argc, VALUE *argv, VALUE self)
|
|
2901
2922
|
{
|
|
2902
2923
|
void *doc;
|
|
2924
|
+
VALUE xml, recover;
|
|
2903
2925
|
|
|
2904
2926
|
(void)self;
|
|
2927
|
+
rb_scan_args(argc, argv, "11", &xml, &recover);
|
|
2905
2928
|
resolve_binding_classes();
|
|
2906
|
-
|
|
2907
|
-
|
|
2929
|
+
xml = StringValue(xml);
|
|
2930
|
+
if (RTEST(recover) && f_parse_str_ex) {
|
|
2931
|
+
/* Recover lane (ruby#465): a best-effort tree plus the
|
|
2932
|
+
* engine's parse diagnostics — never NULL for malformed
|
|
2933
|
+
* input, so the caller answers a document, not a raise. */
|
|
2934
|
+
struct leptris_parse_opts_mirror opts;
|
|
2935
|
+
memset(&opts, 0, sizeof(opts));
|
|
2936
|
+
opts.recover = 1;
|
|
2937
|
+
doc = f_parse_str_ex(RSTRING_PTR(xml),
|
|
2938
|
+
(size_t)RSTRING_LEN(xml), &opts, NULL);
|
|
2939
|
+
} else {
|
|
2940
|
+
doc = f_parse_str(RSTRING_PTR(xml),
|
|
2941
|
+
(size_t)RSTRING_LEN(xml), NULL);
|
|
2942
|
+
}
|
|
2908
2943
|
return doc ? build_binding_document(doc) : Qnil;
|
|
2909
2944
|
}
|
|
2910
2945
|
|
|
@@ -3401,9 +3436,16 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3401
3436
|
* entry mutates only under the engine's document path. */
|
|
3402
3437
|
rb_ext_ractor_safe(true);
|
|
3403
3438
|
|
|
3439
|
+
/* BEFORE any class definition — NativeNode/DocHandle/SaxParser
|
|
3440
|
+
* define against c_object. */
|
|
3441
|
+
c_object = rb_path2class("Object");
|
|
3442
|
+
c_arg_error = rb_path2class("ArgumentError");
|
|
3443
|
+
c_type_error = rb_path2class("TypeError");
|
|
3444
|
+
c_runtime_error = rb_path2class("RuntimeError");
|
|
3445
|
+
|
|
3404
3446
|
m_leptris = rb_define_module("Leptris");
|
|
3405
3447
|
m_xml = rb_define_module_under(m_leptris, "XML");
|
|
3406
|
-
c_native_node = rb_define_class_under(m_xml, "NativeNode",
|
|
3448
|
+
c_native_node = rb_define_class_under(m_xml, "NativeNode", c_object);
|
|
3407
3449
|
rb_undef_alloc_func(c_native_node);
|
|
3408
3450
|
|
|
3409
3451
|
id_iv_name = rb_intern("@name");
|
|
@@ -3485,7 +3527,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3485
3527
|
id_sax_end_prefix = rb_intern("end_prefix_mapping");
|
|
3486
3528
|
id_sax_error = rb_intern("error");
|
|
3487
3529
|
c_sax_parser = rb_define_class_under(m_native, "SaxParser",
|
|
3488
|
-
|
|
3530
|
+
c_object);
|
|
3489
3531
|
rb_define_module_function(m_native, "sax_parse", nf_sax_parse, 3);
|
|
3490
3532
|
rb_define_module_function(m_native, "sax_parser_create",
|
|
3491
3533
|
nf_sax_parser_create, 2);
|
|
@@ -3545,7 +3587,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3545
3587
|
rb_define_module_function(m_native, "set_binding_root",
|
|
3546
3588
|
nf_set_binding_root, 2);
|
|
3547
3589
|
rb_define_module_function(m_native, "parse_binding_document",
|
|
3548
|
-
nf_parse_binding_document, 1);
|
|
3590
|
+
nf_parse_binding_document, -1);
|
|
3549
3591
|
rb_define_module_function(m_native, "parse_fragment_addr",
|
|
3550
3592
|
nf_parse_fragment_addr, 2);
|
|
3551
3593
|
rb_define_module_function(m_native, "append_markup",
|
|
@@ -3579,7 +3621,7 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3579
3621
|
nf_doc_handle_mark_absorbed, 1);
|
|
3580
3622
|
rb_define_module_function(m_native, "create_binding_document",
|
|
3581
3623
|
nf_create_binding_document, 0);
|
|
3582
|
-
c_doc_handle = rb_define_class_under(m_xml, "DocHandle",
|
|
3624
|
+
c_doc_handle = rb_define_class_under(m_xml, "DocHandle", c_object);
|
|
3583
3625
|
rb_undef_alloc_func(c_doc_handle);
|
|
3584
3626
|
rb_define_module_function(m_native, "bulk_attributes", nf_bulk_attributes, 1);
|
|
3585
3627
|
rb_define_module_function(m_native, "bulk_attr_faces",
|
|
@@ -3598,14 +3640,16 @@ LEPTRIS_INIT_EXPORT void Init_native(void)
|
|
|
3598
3640
|
nf_fast_element_xml, 4);
|
|
3599
3641
|
}
|
|
3600
3642
|
|
|
3601
|
-
#ifdef _WIN32
|
|
3602
3643
|
/* MRI derives a C extension's init symbol from the require
|
|
3603
|
-
* feature's basename cut at the first dot. The
|
|
3604
|
-
*
|
|
3605
|
-
* each needs its exact init name exported.
|
|
3606
|
-
* unconditionally
|
|
3607
|
-
*
|
|
3644
|
+
* feature's basename cut at the first dot. The per-minor artifacts
|
|
3645
|
+
* are therefore named dot-free (native_3_3.so / native_3_1.bundle
|
|
3646
|
+
* etc.), and each needs its exact init name exported. Defined
|
|
3647
|
+
* unconditionally on every platform since ruby#476: the darwin/
|
|
3648
|
+
* Linux per-minor bundles (native_<minor>) resolve these too —
|
|
3649
|
+
* an artifact only ever loads under the Ruby minor it was built
|
|
3650
|
+
* against. */
|
|
3651
|
+
LEPTRIS_INIT_EXPORT void Init_native_3_1(void) { Init_native(); }
|
|
3652
|
+
LEPTRIS_INIT_EXPORT void Init_native_3_2(void) { Init_native(); }
|
|
3608
3653
|
LEPTRIS_INIT_EXPORT void Init_native_3_3(void) { Init_native(); }
|
|
3609
3654
|
LEPTRIS_INIT_EXPORT void Init_native_3_4(void) { Init_native(); }
|
|
3610
3655
|
LEPTRIS_INIT_EXPORT void Init_native_4_0(void) { Init_native(); }
|
|
3611
|
-
#endif
|
data/lib/leptris/version.rb
CHANGED
data/lib/leptris/xml/builder.rb
CHANGED
|
@@ -123,7 +123,7 @@ class Leptris::XML::Builder
|
|
|
123
123
|
return @sink if markup.empty?
|
|
124
124
|
case @sink
|
|
125
125
|
when Leptris::XML::Document
|
|
126
|
-
parsed = Leptris::XML::Document.parse(markup)
|
|
126
|
+
parsed = Leptris::XML::Document.parse(markup, strict: true)
|
|
127
127
|
root = parsed.root
|
|
128
128
|
if root.nil?
|
|
129
129
|
raise Leptris::XML::Error,
|