Skip to main content

libperl_sys/
lib.rs

1//! # libperl-sys
2//!
3#![doc = concat!(
4    "**Built against Perl ", env!("LIBPERL_SYS_PERL_VERSION"),
5    " (", env!("LIBPERL_SYS_PERL_THREADED"),
6    ", `", env!("LIBPERL_SYS_PERL_ARCHNAME"), "`).**",
7)]
8//!
9//! The function signatures, `PL_*` globals, and `Sv*` / `Av*` / `Hv*`
10//! helpers documented below reflect this specific Perl. Different
11//! Perl versions may have minor signature differences (added /
12//! removed functions, changed integer widths, threading-mode
13//! variations). Use the [`PERL_VERSION`] / [`PERL_THREADED`] /
14//! [`PERL_ARCHNAME`] constants for runtime identification.
15//!
16//! Low-level, raw FFI declarations for the Perl 5 C API (`libperl`).
17//! Generated at build time by `bindgen` (regular C declarations) plus
18//! [`libperl-macrogen`](https://docs.rs/libperl-macrogen) (the C
19//! macros and `static inline` functions that `bindgen` skips).
20//!
21//! This crate is the unsafe foundation under
22//! [`libperl-rs`](https://docs.rs/libperl-rs); most users want that
23//! safer wrapper. Reach for `libperl-sys` directly when you need an
24//! API element that hasn't been wrapped yet, or when you're writing
25//! a sibling crate at the same layer.
26//!
27//! ## What you get
28//!
29//! Re-exported at the crate root:
30//!
31//! - `Perl_*` extern functions and `PL_*` mutable statics (from
32//!   bindgen),
33//! - `Sv*` / `Av*` / `Hv*` / `PL_xxx!()` macro helpers and inline
34//!   wrappers (from libperl-macrogen) — these unify the threaded vs
35//!   non-threaded calling conventions so the same source builds
36//!   against both `MULTIPLICITY` modes,
37//! - `PL_xxx_ptr!()` pointer accessors (read *and write* the
38//!   interpreter variables through one primitive) and the [`thx`]
39//!   calling-convention shim module — together they let downstream
40//!   crates touch raw interpreter state without `cfg`-forking on the
41//!   threading mode (GH-20),
42//! - opcode → name lookup table ([`conv_opcode`]) and per-function
43//!   signature dictionary ([`sigdb`]) for downstream codegen.
44//!
45//! ## Safety
46//!
47//! Every public item here is `unsafe` to use. Even reading a `PL_*`
48//! global requires the right interpreter context, and Perl's API
49//! uses raw `*mut` pointers ubiquitously.
50//!
51//! ## Build requirements
52//!
53//! - A working Perl 5 install with development headers
54//!   (`Perl.h`, `EXTERN.h`, ...). Typical packages: `perl-dev`,
55//!   `perl-devel`.
56//! - LLVM / libclang (for `bindgen`).
57//! - Internet access at first build (libperl-macrogen downloads a
58//!   pre-extracted apidoc snapshot from GitHub Releases).
59//!
60//! Threaded vs non-threaded Perl is auto-detected — no feature flag
61//! to set.
62
63pub mod perl_core;
64pub use perl_core::*;
65
66pub mod conv_opcode;
67
68pub mod sigdb;
69
70/// Threaded-style calling-convention shims (GH-20).
71///
72/// Every function here takes `my_perl: *mut PerlInterpreter` first.
73/// The argument is forwarded when the wrapped function wants a context
74/// and silently dropped when it does not — which covers both
75/// non-threaded builds (no function takes a context) and the handful of
76/// context-free functions on threaded builds. Downstream code can
77/// therefore call `sys::thx::Perl_foo(my_perl, ...)` uniformly and
78/// compile against both `MULTIPLICITY` modes unchanged.
79///
80/// Wraps both the bindgen externs (bindings.rs) and the
81/// libperl-macrogen-generated inline functions (macro_bindings.rs) —
82/// the latter also change signature with the threading mode. C variadic
83/// functions (e.g. `Perl_croak`) cannot be wrapped in stable Rust and
84/// are omitted; call them through the crate root with an explicit
85/// `#[cfg(perl_useithreads)]` branch if you need them.
86#[allow(
87    non_snake_case,
88    unused_imports,
89    unused_unsafe,
90    clippy::missing_safety_doc,
91    clippy::too_many_arguments
92)]
93pub mod thx {
94    include!(concat!(env!("OUT_DIR"), "/thx_bindings.rs"));
95
96    // perl_core.rs と同じ <5.32 互換 (5.31 で S_SvREFCNT_dec →
97    // Perl_SvREFCNT_dec 改名): shim は S_SvREFCNT_dec としてしか生成
98    // されないので、thx 名前空間にも Perl_ 名の alias を張る。shim が
99    // 既に呼び出し規約を正規化済みのため alias だけで足りる。
100    // 5.20〜5.26 でも S_SvREFCNT_dec が生成されることは macrogen 0.1.12
101    // (apidoc data 1.15) の multi-perl 成果物で確認済み。
102    #[cfg(not(perlapi_ver32))]
103    pub use self::S_SvREFCNT_dec as Perl_SvREFCNT_dec;
104
105    // <5.26 互換: newAV / newHV / hv_store の関数形 (`Perl_` 名の
106    // extern) は perl 5.26 で生まれた。5.24 以前はマクロのみだが、
107    // macrogen がそのマクロを同シグネチャの inline fn として生成する
108    // (5.20〜5.24 x 両モードの multi-perl 成果物で確認済み) ので、
109    // その thx shim を `Perl_` 名でも使えるようにする。
110    #[cfg(not(perlapi_ver26))]
111    pub use self::newAV as Perl_newAV;
112    #[cfg(not(perlapi_ver26))]
113    pub use self::newHV as Perl_newHV;
114    #[cfg(not(perlapi_ver26))]
115    pub use self::hv_store as Perl_hv_store;
116    // `Perl_sv_2iv` の extern も 5.26 生まれ (それ以前は sv_2iv_flags
117    // のみ)。マクロ生成体 sv_2iv (= sv_2iv_flags(sv, SV_GMAGIC)) の
118    // shim を同名で使えるようにする。
119    #[cfg(not(perlapi_ver26))]
120    pub use self::sv_2iv as Perl_sv_2iv;
121}
122
123/// Perl version this binding was generated against (e.g. `"5.38.4"`).
124pub const PERL_VERSION:  &str = env!("LIBPERL_SYS_PERL_VERSION");
125
126/// `"threaded"` if the target Perl was built with `useithreads`,
127/// `"non-threaded"` otherwise. Threading mode determines whether
128/// most Perl C API functions take a leading `my_perl: *mut PerlInterpreter`
129/// parameter.
130pub const PERL_THREADED: &str = env!("LIBPERL_SYS_PERL_THREADED");
131
132/// Perl `archname` (e.g. `"x86_64-linux-thread-multi"` or
133/// `"x86_64-linux-gnu"`). Mostly informational; the more useful
134/// invariants are in [`PERL_VERSION`] and [`PERL_THREADED`].
135pub const PERL_ARCHNAME: &str = env!("LIBPERL_SYS_PERL_ARCHNAME");
136
137use std::ffi::CStr;
138
139// use std::os::raw::{c_char, c_int /*, c_void, c_schar*/};
140
141fn core_op_name(o: &op) -> Option<String> {
142    let ty = o.op_type();
143    if (ty as usize) < unsafe {PL_op_name.len()} {
144        let op_name = unsafe {CStr::from_ptr(PL_op_name[ty as usize])};
145        Some(String::from(op_name.to_str().unwrap()))
146    } else {
147        None
148    }
149}
150
151impl std::fmt::Display for op {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        write!(f, "{{ {:?}={:#?} {:?} }}"
154               , core_op_name(&self)
155               , (self as *const op)
156               , self)
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    #[test]
163    fn it_works() {
164        let perl = unsafe { super::perl_alloc() };
165        unsafe {
166            super::perl_construct(perl);
167        };
168    }
169
170    // Note: a smoke test for the PERLVAR-driven `PL_xxx!($my_perl)` macros
171    // would naturally live here, but `#[macro_export]` macros emitted via
172    // `include!()` are unreachable by absolute path within the *defining*
173    // crate (rejected by the
174    // `macro_expanded_macro_exports_accessed_by_absolute_paths` lint, which
175    // is on by default and slated to become a hard error). The smoke test
176    // is in `libperl-rs/tests/perlvar_macros.rs` instead, where cross-crate
177    // access goes through the normal path resolver and is unaffected.
178
179    #[test]
180    fn sigdb_lookup() {
181        use super::sigdb::{FN_BY_NAME, FUNCS};
182
183        // Test that FN_BY_NAME lookup works
184        if let Some(id) = FN_BY_NAME.get("Perl_sv_isbool") {
185            let sig = &FUNCS[id.0 as usize];
186            assert_eq!(sig.name, "Perl_sv_isbool");
187            assert!(!sig.ret.is_empty());
188        }
189
190        // Test perl_alloc
191        let id = FN_BY_NAME.get("perl_alloc").expect("perl_alloc should exist");
192        let sig = &FUNCS[id.0 as usize];
193        assert_eq!(sig.name, "perl_alloc");
194        assert!(sig.ret.contains("PerlInterpreter"));
195    }
196}