Skip to main content

libperl_sys/
lib.rs

1//! # libperl-sys
2//!
3//! Low-level, raw FFI declarations for the Perl 5 C API (`libperl`).
4//! Generated at build time by `bindgen` (regular C declarations) plus
5//! [`libperl-macrogen`](https://docs.rs/libperl-macrogen) (the C
6//! macros and `static inline` functions that `bindgen` skips).
7//!
8//! This crate is the unsafe foundation under
9//! [`libperl-rs`](https://docs.rs/libperl-rs); most users want that
10//! safer wrapper. Reach for `libperl-sys` directly when you need an
11//! API element that hasn't been wrapped yet, or when you're writing
12//! a sibling crate at the same layer.
13//!
14//! ## What you get
15//!
16//! Re-exported at the crate root:
17//!
18//! - `Perl_*` extern functions and `PL_*` mutable statics (from
19//!   bindgen),
20//! - `Sv*` / `Av*` / `Hv*` / `PL_xxx!()` macro helpers and inline
21//!   wrappers (from libperl-macrogen) — these unify the threaded vs
22//!   non-threaded calling conventions so the same source builds
23//!   against both `MULTIPLICITY` modes,
24//! - opcode → name lookup table ([`conv_opcode`]) and per-function
25//!   signature dictionary ([`sigdb`]) for downstream codegen.
26//!
27//! ## Safety
28//!
29//! Every public item here is `unsafe` to use. Even reading a `PL_*`
30//! global requires the right interpreter context, and Perl's API
31//! uses raw `*mut` pointers ubiquitously.
32//!
33//! ## Build requirements
34//!
35//! - A working Perl 5 install with development headers
36//!   (`Perl.h`, `EXTERN.h`, ...). Typical packages: `perl-dev`,
37//!   `perl-devel`.
38//! - LLVM / libclang (for `bindgen`).
39//! - Internet access at first build (libperl-macrogen downloads a
40//!   pre-extracted apidoc snapshot from GitHub Releases).
41//!
42//! Threaded vs non-threaded Perl is auto-detected — no feature flag
43//! to set.
44
45pub mod perl_core;
46pub use perl_core::*;
47
48pub mod conv_opcode;
49
50pub mod sigdb;
51
52use std::ffi::CStr;
53
54// use std::os::raw::{c_char, c_int /*, c_void, c_schar*/};
55
56fn core_op_name(o: &op) -> Option<String> {
57    let ty = o.op_type();
58    if (ty as usize) < unsafe {PL_op_name.len()} {
59        let op_name = unsafe {CStr::from_ptr(PL_op_name[ty as usize])};
60        Some(String::from(op_name.to_str().unwrap()))
61    } else {
62        None
63    }
64}
65
66impl std::fmt::Display for op {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        write!(f, "{{ {:?}={:#?} {:?} }}"
69               , core_op_name(&self)
70               , (self as *const op)
71               , self)
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    #[test]
78    fn it_works() {
79        let perl = unsafe { super::perl_alloc() };
80        unsafe {
81            super::perl_construct(perl);
82        };
83    }
84
85    // Note: a smoke test for the PERLVAR-driven `PL_xxx!($my_perl)` macros
86    // would naturally live here, but `#[macro_export]` macros emitted via
87    // `include!()` are unreachable by absolute path within the *defining*
88    // crate (rejected by the
89    // `macro_expanded_macro_exports_accessed_by_absolute_paths` lint, which
90    // is on by default and slated to become a hard error). The smoke test
91    // is in `libperl-rs/tests/perlvar_macros.rs` instead, where cross-crate
92    // access goes through the normal path resolver and is unaffected.
93
94    #[test]
95    fn sigdb_lookup() {
96        use super::sigdb::{FN_BY_NAME, FUNCS};
97
98        // Test that FN_BY_NAME lookup works
99        if let Some(id) = FN_BY_NAME.get("Perl_sv_isbool") {
100            let sig = &FUNCS[id.0 as usize];
101            assert_eq!(sig.name, "Perl_sv_isbool");
102            assert!(!sig.ret.is_empty());
103        }
104
105        // Test perl_alloc
106        let id = FN_BY_NAME.get("perl_alloc").expect("perl_alloc should exist");
107        let sig = &FUNCS[id.0 as usize];
108        assert_eq!(sig.name, "perl_alloc");
109        assert!(sig.ret.contains("PerlInterpreter"));
110    }
111}