python-plugin: RAII (PyOwned/PyBorrowed) + autodecode enum; crate: ny-llvmc --emit exe with NyRT link; tools: build_llvm.sh crate-exe path + crate_exe_smoke; CURRENT_TASK update
This commit is contained in:
@ -146,9 +146,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// ===== TypeBox ABI v2 (resolve/invoke_id) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char,
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
@ -158,7 +158,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
use std::ffi::CStr;
|
||||
extern "C" fn encoding_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"toUtf8Bytes" => M_TO_UTF8_BYTES,
|
||||
@ -184,42 +186,91 @@ extern "C" fn encoding_invoke_id(
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, EncInstance); } else { return E_PLUGIN; }
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, EncInstance);
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4; OK
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_TO_UTF8_BYTES => {
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
write_tlv_bytes(s.as_bytes(), result, result_len)
|
||||
}
|
||||
M_FROM_UTF8_BYTES => {
|
||||
let bytes = match read_arg_bytes(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
match String::from_utf8(bytes) { Ok(s) => write_tlv_string(&s, result, result_len), Err(_) => write_tlv_string("", result, result_len) }
|
||||
let bytes = match read_arg_bytes(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
match String::from_utf8(bytes) {
|
||||
Ok(s) => write_tlv_string(&s, result, result_len),
|
||||
Err(_) => write_tlv_string("", result, result_len),
|
||||
}
|
||||
}
|
||||
M_BASE64_ENC => {
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) { let s = base64::encode(b); return write_tlv_string(&s, result, result_len); }
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) {
|
||||
let s = base64::encode(b);
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
}
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let enc = base64::encode(s.as_bytes());
|
||||
write_tlv_string(&enc, result, result_len)
|
||||
}
|
||||
M_BASE64_DEC => {
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
match base64::decode(s.as_bytes()) { Ok(b) => write_tlv_bytes(&b, result, result_len), Err(_) => write_tlv_bytes(&[], result, result_len) }
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
match base64::decode(s.as_bytes()) {
|
||||
Ok(b) => write_tlv_bytes(&b, result, result_len),
|
||||
Err(_) => write_tlv_bytes(&[], result, result_len),
|
||||
}
|
||||
}
|
||||
M_HEX_ENC => {
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) { let s = hex::encode(b); return write_tlv_string(&s, result, result_len); }
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
if let Some(b) = read_arg_bytes(args, args_len, 0) {
|
||||
let s = hex::encode(b);
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
}
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let enc = hex::encode(s.as_bytes());
|
||||
write_tlv_string(&enc, result, result_len)
|
||||
}
|
||||
M_HEX_DEC => {
|
||||
let s = match read_arg_string(args, args_len, 0) { Some(v) => v, None => return E_ARGS };
|
||||
match hex::decode(s.as_bytes()) { Ok(b) => write_tlv_bytes(&b, result, result_len), Err(_) => write_tlv_bytes(&[], result, result_len) }
|
||||
let s = match read_arg_string(args, args_len, 0) {
|
||||
Some(v) => v,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
match hex::decode(s.as_bytes()) {
|
||||
Ok(b) => write_tlv_bytes(&b, result, result_len),
|
||||
Err(_) => write_tlv_bytes(&[], result, result_len),
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
|
||||
@ -681,7 +681,11 @@ extern "C" fn filebox_invoke_id(
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(
|
||||
id,
|
||||
FileBoxInstance { file: None, path: String::new(), buffer: None },
|
||||
FileBoxInstance {
|
||||
file: None,
|
||||
path: String::new(),
|
||||
buffer: None,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return NYB_E_PLUGIN_ERROR;
|
||||
@ -900,7 +904,11 @@ extern "C" fn filebox_invoke_id(
|
||||
if let Ok(mut map) = INSTANCES.lock() {
|
||||
map.insert(
|
||||
new_id,
|
||||
FileBoxInstance { file: None, path: String::new(), buffer: None },
|
||||
FileBoxInstance {
|
||||
file: None,
|
||||
path: String::new(),
|
||||
buffer: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
// Return Handle TLV (type_id from config resolves host-side; we encode (6,new_id) here if needed)
|
||||
|
||||
@ -115,9 +115,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// ===== TypeBox ABI v2 (resolve/invoke_id per box) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char,
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
@ -127,7 +127,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
use std::ffi::CStr;
|
||||
extern "C" fn mathbox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"sqrt" => M_SQRT,
|
||||
@ -140,7 +142,9 @@ extern "C" fn mathbox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
}
|
||||
}
|
||||
extern "C" fn timebox_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"now" => T_NOW,
|
||||
|
||||
19
plugins/nyash-net-plugin/src/ffi.rs
Normal file
19
plugins/nyash-net-plugin/src/ffi.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
// Safe wrapper: convert C string pointer to owned String.
|
||||
// Safety details are contained within; caller gets a safe String.
|
||||
pub fn cstr_to_string(ptr: *const c_char) -> String {
|
||||
if ptr.is_null() {
|
||||
return String::new();
|
||||
}
|
||||
unsafe { CStr::from_ptr(ptr) }
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
}
|
||||
|
||||
// Re-export a safe view over a raw byte slice pointer.
|
||||
// This function is unsafe since it trusts the pointer/length.
|
||||
pub unsafe fn slice<'a>(p: *const u8, len: usize) -> &'a [u8] {
|
||||
std::slice::from_raw_parts(p, len)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
78
plugins/nyash-net-plugin/src/state.rs
Normal file
78
plugins/nyash-net-plugin/src/state.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
use super::{
|
||||
ClientState, RequestState, ResponseState, ServerState, SockClientState, SockConnState,
|
||||
SockServerState,
|
||||
};
|
||||
|
||||
pub(crate) static SERVER_INSTANCES: Lazy<Mutex<HashMap<u32, ServerState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
pub(crate) static SERVER_START_SEQ: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static ACTIVE_SERVER_ID: Lazy<Mutex<Option<u32>>> = Lazy::new(|| Mutex::new(None));
|
||||
pub(crate) static LAST_ACCEPTED_REQ: Lazy<Mutex<Option<u32>>> = Lazy::new(|| Mutex::new(None));
|
||||
pub(crate) static REQUESTS: Lazy<Mutex<HashMap<u32, RequestState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
pub(crate) static RESPONSES: Lazy<Mutex<HashMap<u32, ResponseState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
pub(crate) static CLIENTS: Lazy<Mutex<HashMap<u32, ClientState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
pub(crate) static SERVER_ID: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static REQUEST_ID: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static RESPONSE_ID: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static CLIENT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static SOCK_SERVER_ID: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static SOCK_CONN_ID: AtomicU32 = AtomicU32::new(1);
|
||||
pub(crate) static SOCK_CLIENT_ID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
pub(crate) static SOCK_SERVERS: Lazy<Mutex<HashMap<u32, SockServerState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
pub(crate) static SOCK_CONNS: Lazy<Mutex<HashMap<u32, SockConnState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
pub(crate) static SOCK_CLIENTS: Lazy<Mutex<HashMap<u32, SockClientState>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_server_id() -> u32 {
|
||||
SERVER_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_server_start_seq() -> u32 {
|
||||
SERVER_START_SEQ.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_request_id() -> u32 {
|
||||
REQUEST_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_response_id() -> u32 {
|
||||
RESPONSE_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_client_id() -> u32 {
|
||||
CLIENT_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_sock_server_id() -> u32 {
|
||||
SOCK_SERVER_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_sock_conn_id() -> u32 {
|
||||
SOCK_CONN_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn next_sock_client_id() -> u32 {
|
||||
SOCK_CLIENT_ID.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
181
plugins/nyash-net-plugin/src/tlv.rs
Normal file
181
plugins/nyash-net-plugin/src/tlv.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use super::{E_INV_ARGS, E_SHORT, OK};
|
||||
|
||||
#[inline]
|
||||
fn tlv_result_size(payloads: &[(u8, &[u8])]) -> usize {
|
||||
4 + payloads.iter().map(|(_, p)| 4 + p.len()).sum::<usize>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ensure_result_capacity(res: *mut u8, res_len: *mut usize, need: usize) -> Result<(), i32> {
|
||||
if res_len.is_null() {
|
||||
return Err(E_INV_ARGS);
|
||||
}
|
||||
unsafe {
|
||||
if res.is_null() || *res_len < need {
|
||||
*res_len = need;
|
||||
return Err(E_SHORT);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn write_bytes_unchecked(bytes: &[u8], res: *mut u8, res_len: *mut usize) {
|
||||
std::ptr::copy_nonoverlapping(bytes.as_ptr(), res, bytes.len());
|
||||
*res_len = bytes.len();
|
||||
}
|
||||
|
||||
pub fn write_u32(v: u32, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
let bytes = v.to_le_bytes();
|
||||
if let Err(err) = ensure_result_capacity(res, res_len, bytes.len()) {
|
||||
return err;
|
||||
}
|
||||
unsafe {
|
||||
write_bytes_unchecked(&bytes, res, res_len);
|
||||
}
|
||||
OK
|
||||
}
|
||||
|
||||
pub fn write_tlv_result(payloads: &[(u8, &[u8])], res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
let need = tlv_result_size(payloads);
|
||||
if let Err(err) = ensure_result_capacity(res, res_len, need) {
|
||||
return err;
|
||||
}
|
||||
let mut buf = Vec::with_capacity(need);
|
||||
buf.extend_from_slice(&1u16.to_le_bytes());
|
||||
buf.extend_from_slice(&(payloads.len() as u16).to_le_bytes());
|
||||
for (tag, p) in payloads {
|
||||
buf.push(*tag);
|
||||
buf.push(0);
|
||||
buf.extend_from_slice(&(p.len() as u16).to_le_bytes());
|
||||
buf.extend_from_slice(p);
|
||||
}
|
||||
unsafe {
|
||||
write_bytes_unchecked(&buf, res, res_len);
|
||||
}
|
||||
OK
|
||||
}
|
||||
|
||||
pub fn write_tlv_void(res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(9u8, &[])], res, res_len)
|
||||
}
|
||||
pub fn write_tlv_string(s: &str, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(6u8, s.as_bytes())], res, res_len)
|
||||
}
|
||||
pub fn write_tlv_bytes(b: &[u8], res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(7u8, b)], res, res_len)
|
||||
}
|
||||
pub fn write_tlv_i32(v: i32, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
write_tlv_result(&[(2u8, &v.to_le_bytes())], res, res_len)
|
||||
}
|
||||
pub fn write_tlv_handle(t: u32, id: u32, res: *mut u8, res_len: *mut usize) -> i32 {
|
||||
let mut payload = [0u8; 8];
|
||||
payload[0..4].copy_from_slice(&t.to_le_bytes());
|
||||
payload[4..8].copy_from_slice(&id.to_le_bytes());
|
||||
write_tlv_result(&[(8u8, &payload)], res, res_len)
|
||||
}
|
||||
|
||||
pub fn tlv_parse_header(data: &[u8]) -> Result<(u16, u16, usize), ()> {
|
||||
if data.len() < 4 {
|
||||
return Err(());
|
||||
}
|
||||
let ver = u16::from_le_bytes([data[0], data[1]]);
|
||||
let argc = u16::from_le_bytes([data[2], data[3]]);
|
||||
if ver != 1 {
|
||||
return Err(());
|
||||
}
|
||||
Ok((ver, argc, 4))
|
||||
}
|
||||
pub fn tlv_parse_string(data: &[u8]) -> Result<String, ()> {
|
||||
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||
if tag != 6 {
|
||||
return Err(());
|
||||
}
|
||||
Ok(std::str::from_utf8(&data[p..p + size])
|
||||
.map_err(|_| ())?
|
||||
.to_string())
|
||||
}
|
||||
pub fn tlv_parse_two_strings(data: &[u8]) -> Result<(String, String), ()> {
|
||||
let (_, argc, mut pos) = tlv_parse_header(data)?;
|
||||
if argc < 2 {
|
||||
return Err(());
|
||||
}
|
||||
let (tag1, size1, p1) = tlv_parse_entry_hdr(data, pos)?;
|
||||
if tag1 != 6 {
|
||||
return Err(());
|
||||
}
|
||||
let s1 = std::str::from_utf8(&data[p1..p1 + size1])
|
||||
.map_err(|_| ())?
|
||||
.to_string();
|
||||
pos = p1 + size1;
|
||||
let (tag2, size2, p2) = tlv_parse_entry_hdr(data, pos)?;
|
||||
if tag2 != 6 {
|
||||
return Err(());
|
||||
}
|
||||
let s2 = std::str::from_utf8(&data[p2..p2 + size2])
|
||||
.map_err(|_| ())?
|
||||
.to_string();
|
||||
Ok((s1, s2))
|
||||
}
|
||||
pub fn tlv_parse_bytes(data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||
if tag != 6 && tag != 7 {
|
||||
return Err(());
|
||||
}
|
||||
Ok(data[p..p + size].to_vec())
|
||||
}
|
||||
pub fn tlv_parse_i32(data: &[u8]) -> Result<i32, ()> {
|
||||
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||
match (tag, size) {
|
||||
(2, 4) => {
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(&data[p..p + 4]);
|
||||
Ok(i32::from_le_bytes(b))
|
||||
}
|
||||
(5, 8) => {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(&data[p..p + 8]);
|
||||
Ok(i64::from_le_bytes(b) as i32)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
pub fn tlv_parse_handle(data: &[u8]) -> Result<(u32, u32), ()> {
|
||||
let (_, argc, pos) = tlv_parse_header(data)?;
|
||||
if argc < 1 {
|
||||
return Err(());
|
||||
}
|
||||
let (tag, size, p) = tlv_parse_entry_hdr(data, pos)?;
|
||||
if tag != 8 || size != 8 {
|
||||
return Err(());
|
||||
}
|
||||
let mut t = [0u8; 4];
|
||||
let mut i = [0u8; 4];
|
||||
t.copy_from_slice(&data[p..p + 4]);
|
||||
i.copy_from_slice(&data[p + 4..p + 8]);
|
||||
Ok((u32::from_le_bytes(t), u32::from_le_bytes(i)))
|
||||
}
|
||||
pub fn tlv_parse_entry_hdr(data: &[u8], pos: usize) -> Result<(u8, usize, usize), ()> {
|
||||
if pos + 4 > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
let tag = data[pos];
|
||||
let size = u16::from_le_bytes([data[pos + 2], data[pos + 3]]) as usize;
|
||||
let p = pos + 4;
|
||||
if p + size > data.len() {
|
||||
return Err(());
|
||||
}
|
||||
Ok((tag, size, p))
|
||||
}
|
||||
@ -150,9 +150,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// ===== TypeBox ABI (resolve/invoke_id) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char, // C string
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
|
||||
@ -123,9 +123,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// ===== TypeBox ABI v2 (resolve/invoke_id) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char,
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
@ -135,7 +135,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
use std::ffi::CStr;
|
||||
extern "C" fn pycompiler_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"birth" => METHOD_BIRTH,
|
||||
@ -156,52 +158,81 @@ extern "C" fn pycompiler_invoke_id(
|
||||
match method_id {
|
||||
METHOD_BIRTH => unsafe {
|
||||
let mut id_g = NEXT_ID.lock().unwrap();
|
||||
let id = *id_g; *id_g += 1;
|
||||
if result_len.is_null() { return NYB_E_SHORT_BUFFER; }
|
||||
let id = *id_g;
|
||||
*id_g += 1;
|
||||
if result_len.is_null() {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let need = 4usize;
|
||||
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
|
||||
if *result_len < need {
|
||||
*result_len = need;
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
||||
out[0..4].copy_from_slice(&(id as u32).to_le_bytes());
|
||||
*result_len = need;
|
||||
NYB_SUCCESS
|
||||
},
|
||||
METHOD_COMPILE => unsafe {
|
||||
let ir = if args.is_null() || args_len < 8 { None } else {
|
||||
let ir = if args.is_null() || args_len < 8 {
|
||||
None
|
||||
} else {
|
||||
let buf = std::slice::from_raw_parts(args, args_len);
|
||||
let tag = u16::from_le_bytes([buf[4], buf[5]]);
|
||||
let len = u16::from_le_bytes([buf[6], buf[7]]) as usize;
|
||||
if tag == 6 && 8 + len <= buf.len() {
|
||||
std::str::from_utf8(&buf[8..8+len]).ok().map(|s| s.to_string())
|
||||
} else { None }
|
||||
std::str::from_utf8(&buf[8..8 + len])
|
||||
.ok()
|
||||
.map(|s| s.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let nyash_source = if let Some(s) = ir.or_else(|| std::env::var("NYASH_PY_IR").ok()) {
|
||||
match serde_json::from_str::<Json>(&s).ok() {
|
||||
Some(Json::Object(map)) => {
|
||||
if let Some(Json::String(src)) = map.get("nyash_source") { src.clone() }
|
||||
else if let Some(module) = map.get("module") {
|
||||
if let Some(Json::String(src)) = map.get("nyash_source") {
|
||||
src.clone()
|
||||
} else if let Some(module) = map.get("module") {
|
||||
let mut ret_expr = "0".to_string();
|
||||
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array()) {
|
||||
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array())
|
||||
{
|
||||
if let Some(fun0) = funcs.get(0) {
|
||||
if let Some(retv) = fun0.get("return_value") {
|
||||
if retv.is_number() { ret_expr = retv.to_string(); }
|
||||
else if let Some(s) = retv.as_str() { ret_expr = s.to_string(); }
|
||||
if retv.is_number() {
|
||||
ret_expr = retv.to_string();
|
||||
} else if let Some(s) = retv.as_str() {
|
||||
ret_expr = s.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
format!("static box Generated {\n main() {\n return {}\n }\n}}", ret_expr)
|
||||
} else { "static box Generated { main() { return 0 } }".to_string() }
|
||||
format!(
|
||||
"static box Generated {\n main() {\n return {}\n }\n}}",
|
||||
ret_expr
|
||||
)
|
||||
} else {
|
||||
"static box Generated { main() { return 0 } }".to_string()
|
||||
}
|
||||
}
|
||||
_ => "static box Generated { main() { return 0 } }".to_string(),
|
||||
}
|
||||
} else { "static box Generated { main() { return 0 } }".to_string() };
|
||||
} else {
|
||||
"static box Generated { main() { return 0 } }".to_string()
|
||||
};
|
||||
let bytes = nyash_source.as_bytes();
|
||||
if result_len.is_null() { return NYB_E_SHORT_BUFFER; }
|
||||
if result_len.is_null() {
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let need = 4 + bytes.len();
|
||||
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
|
||||
if *result_len < need {
|
||||
*result_len = need;
|
||||
return NYB_E_SHORT_BUFFER;
|
||||
}
|
||||
let out = std::slice::from_raw_parts_mut(result, *result_len);
|
||||
out[0..2].copy_from_slice(&6u16.to_le_bytes());
|
||||
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
|
||||
out[4..4+bytes.len()].copy_from_slice(bytes);
|
||||
out[4..4 + bytes.len()].copy_from_slice(bytes);
|
||||
*result_len = need;
|
||||
NYB_SUCCESS
|
||||
},
|
||||
|
||||
@ -105,9 +105,9 @@ fn parse_python_code(py: Python, code: &str) -> ParseResult {
|
||||
// ===== TypeBox ABI v2 (resolve/invoke_id) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char,
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
@ -122,7 +122,9 @@ const METHOD_FINI: u32 = u32::MAX;
|
||||
|
||||
use std::ffi::CStr;
|
||||
extern "C" fn pyparser_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"birth" => METHOD_BIRTH,
|
||||
@ -143,7 +145,9 @@ extern "C" fn pyparser_invoke_id(
|
||||
match method_id {
|
||||
METHOD_BIRTH => unsafe {
|
||||
let instance_id = 1u32; // simple singleton
|
||||
if result_len.is_null() { return -1; }
|
||||
if result_len.is_null() {
|
||||
return -1;
|
||||
}
|
||||
if *result_len < 4 {
|
||||
*result_len = 4;
|
||||
return -1;
|
||||
@ -157,26 +161,35 @@ extern "C" fn pyparser_invoke_id(
|
||||
// Decode TLV string from args if present, else env fallback
|
||||
let code = unsafe {
|
||||
if args.is_null() || args_len < 4 {
|
||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
} else {
|
||||
let buf = std::slice::from_raw_parts(args, args_len);
|
||||
if args_len >= 8 {
|
||||
let tag = u16::from_le_bytes([buf[0], buf[1]]);
|
||||
let len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
|
||||
if tag == 6 && 4 + len <= args_len {
|
||||
match std::str::from_utf8(&buf[4..4 + len]) { Ok(s) => s.to_string(), Err(_) => std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string()) }
|
||||
match std::str::from_utf8(&buf[4..4 + len]) {
|
||||
Ok(s) => s.to_string(),
|
||||
Err(_) => std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string()),
|
||||
}
|
||||
} else {
|
||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
}
|
||||
} else {
|
||||
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
std::env::var("NYASH_PY_CODE")
|
||||
.unwrap_or_else(|_| "def main():\n return 0".to_string())
|
||||
}
|
||||
}
|
||||
};
|
||||
let parse_result = Python::with_gil(|py| parse_python_code(py, &code));
|
||||
match serde_json::to_string(&parse_result) {
|
||||
Ok(json) => unsafe {
|
||||
if result_len.is_null() { return -1; }
|
||||
if result_len.is_null() {
|
||||
return -1;
|
||||
}
|
||||
let bytes = json.as_bytes();
|
||||
let need = 4 + bytes.len();
|
||||
if *result_len < need {
|
||||
|
||||
284
plugins/nyash-python-plugin/src/ffi.rs
Normal file
284
plugins/nyash-python-plugin/src/ffi.rs
Normal file
@ -0,0 +1,284 @@
|
||||
#![allow(non_snake_case, non_camel_case_types, dead_code)]
|
||||
use libloading::Library;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::os::raw::{c_char, c_int, c_long, c_void};
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub type PyObject = c_void;
|
||||
pub type PyGILState_STATE = c_int;
|
||||
|
||||
pub struct CPython {
|
||||
pub(crate) _lib: Library,
|
||||
pub(crate) Py_Initialize: unsafe extern "C" fn(),
|
||||
pub(crate) Py_Finalize: unsafe extern "C" fn(),
|
||||
pub(crate) Py_IsInitialized: unsafe extern "C" fn() -> c_int,
|
||||
pub(crate) PyGILState_Ensure: unsafe extern "C" fn() -> PyGILState_STATE,
|
||||
pub(crate) PyGILState_Release: unsafe extern "C" fn(PyGILState_STATE),
|
||||
pub(crate) PyRun_StringFlags: unsafe extern "C" fn(
|
||||
*const c_char,
|
||||
c_int,
|
||||
*mut PyObject,
|
||||
*mut PyObject,
|
||||
*mut c_void,
|
||||
) -> *mut PyObject,
|
||||
pub(crate) PyImport_AddModule: unsafe extern "C" fn(*const c_char) -> *mut PyObject,
|
||||
pub(crate) PyModule_GetDict: unsafe extern "C" fn(*mut PyObject) -> *mut PyObject,
|
||||
pub(crate) PyImport_ImportModule: unsafe extern "C" fn(*const c_char) -> *mut PyObject,
|
||||
pub(crate) PyObject_Str: unsafe extern "C" fn(*mut PyObject) -> *mut PyObject,
|
||||
pub(crate) PyUnicode_AsUTF8: unsafe extern "C" fn(*mut PyObject) -> *const c_char,
|
||||
pub(crate) Py_DecRef: unsafe extern "C" fn(*mut PyObject),
|
||||
pub(crate) Py_IncRef: unsafe extern "C" fn(*mut PyObject),
|
||||
pub(crate) PyObject_GetAttrString:
|
||||
unsafe extern "C" fn(*mut PyObject, *const c_char) -> *mut PyObject,
|
||||
pub(crate) PyObject_CallObject:
|
||||
unsafe extern "C" fn(*mut PyObject, *mut PyObject) -> *mut PyObject,
|
||||
pub(crate) PyObject_Call:
|
||||
unsafe extern "C" fn(*mut PyObject, *mut PyObject, *mut PyObject) -> *mut PyObject,
|
||||
pub(crate) PyTuple_New: unsafe extern "C" fn(isize) -> *mut PyObject,
|
||||
pub(crate) PyTuple_SetItem: unsafe extern "C" fn(*mut PyObject, isize, *mut PyObject) -> c_int,
|
||||
pub(crate) PyLong_FromLongLong: unsafe extern "C" fn(i64) -> *mut PyObject,
|
||||
pub(crate) PyUnicode_FromString: unsafe extern "C" fn(*const c_char) -> *mut PyObject,
|
||||
pub(crate) PyBool_FromLong: unsafe extern "C" fn(c_long: c_long) -> *mut PyObject,
|
||||
pub(crate) PyFloat_FromDouble: unsafe extern "C" fn(f64) -> *mut PyObject,
|
||||
pub(crate) PyFloat_AsDouble: unsafe extern "C" fn(*mut PyObject) -> f64,
|
||||
pub(crate) PyLong_AsLongLong: unsafe extern "C" fn(*mut PyObject) -> i64,
|
||||
pub(crate) PyBytes_FromStringAndSize:
|
||||
unsafe extern "C" fn(*const c_char, isize) -> *mut PyObject,
|
||||
pub(crate) PyBytes_AsStringAndSize:
|
||||
unsafe extern "C" fn(*mut PyObject, *mut *mut c_char, *mut isize) -> c_int,
|
||||
pub(crate) PyDict_New: unsafe extern "C" fn() -> *mut PyObject,
|
||||
pub(crate) PyDict_SetItemString:
|
||||
unsafe extern "C" fn(*mut PyObject, *const c_char, *mut PyObject) -> c_int,
|
||||
pub(crate) PyErr_Occurred: unsafe extern "C" fn() -> *mut PyObject,
|
||||
pub(crate) PyErr_Fetch:
|
||||
unsafe extern "C" fn(*mut *mut PyObject, *mut *mut PyObject, *mut *mut PyObject),
|
||||
pub(crate) PyErr_Clear: unsafe extern "C" fn(),
|
||||
}
|
||||
|
||||
pub static CPY: Lazy<Mutex<Option<CPython>>> = Lazy::new(|| Mutex::new(None));
|
||||
|
||||
pub fn try_load_cpython() -> Result<(), ()> {
|
||||
let mut candidates: Vec<String> = vec![
|
||||
// Linux
|
||||
"libpython3.12.so".into(),
|
||||
"libpython3.12.so.1.0".into(),
|
||||
"libpython3.11.so".into(),
|
||||
"libpython3.11.so.1.0".into(),
|
||||
"libpython3.10.so".into(),
|
||||
"libpython3.10.so.1.0".into(),
|
||||
"libpython3.9.so".into(),
|
||||
"libpython3.9.so.1.0".into(),
|
||||
// macOS
|
||||
"libpython3.12.dylib".into(),
|
||||
"libpython3.11.dylib".into(),
|
||||
"libpython3.10.dylib".into(),
|
||||
"libpython3.9.dylib".into(),
|
||||
];
|
||||
if cfg!(target_os = "windows") {
|
||||
let dlls = [
|
||||
"python312.dll",
|
||||
"python311.dll",
|
||||
"python310.dll",
|
||||
"python39.dll",
|
||||
];
|
||||
for d in dlls {
|
||||
candidates.push(d.into());
|
||||
}
|
||||
if let Ok(pyhome) = std::env::var("PYTHONHOME") {
|
||||
for d in [
|
||||
"python312.dll",
|
||||
"python311.dll",
|
||||
"python310.dll",
|
||||
"python39.dll",
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
let p = std::path::Path::new(&pyhome).join(d);
|
||||
if p.exists() {
|
||||
candidates.push(p.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for name in candidates.into_iter() {
|
||||
if let Ok(lib) = unsafe { Library::new(&name) } {
|
||||
unsafe {
|
||||
let Py_Initialize = *lib
|
||||
.get::<unsafe extern "C" fn()>(b"Py_Initialize\0")
|
||||
.map_err(|_| ())?;
|
||||
let Py_Finalize = *lib
|
||||
.get::<unsafe extern "C" fn()>(b"Py_Finalize\0")
|
||||
.map_err(|_| ())?;
|
||||
let Py_IsInitialized = *lib
|
||||
.get::<unsafe extern "C" fn() -> c_int>(b"Py_IsInitialized\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyGILState_Ensure = *lib
|
||||
.get::<unsafe extern "C" fn() -> PyGILState_STATE>(b"PyGILState_Ensure\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyGILState_Release = *lib
|
||||
.get::<unsafe extern "C" fn(PyGILState_STATE)>(b"PyGILState_Release\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyRun_StringFlags = *lib
|
||||
.get::<unsafe extern "C" fn(
|
||||
*const c_char,
|
||||
c_int,
|
||||
*mut PyObject,
|
||||
*mut PyObject,
|
||||
*mut c_void,
|
||||
) -> *mut PyObject>(b"PyRun_StringFlags\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyImport_AddModule = *lib
|
||||
.get::<unsafe extern "C" fn(*const c_char) -> *mut PyObject>(
|
||||
b"PyImport_AddModule\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyModule_GetDict = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject) -> *mut PyObject>(
|
||||
b"PyModule_GetDict\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyImport_ImportModule = *lib
|
||||
.get::<unsafe extern "C" fn(*const c_char) -> *mut PyObject>(
|
||||
b"PyImport_ImportModule\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyObject_Str = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject) -> *mut PyObject>(b"PyObject_Str\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyUnicode_AsUTF8 = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject) -> *const c_char>(
|
||||
b"PyUnicode_AsUTF8\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let Py_DecRef = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject)>(b"Py_DecRef\0")
|
||||
.map_err(|_| ())?;
|
||||
let Py_IncRef = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject)>(b"Py_IncRef\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyObject_GetAttrString = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject, *const c_char) -> *mut PyObject>(
|
||||
b"PyObject_GetAttrString\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyObject_CallObject = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject, *mut PyObject) -> *mut PyObject>(
|
||||
b"PyObject_CallObject\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyObject_Call = *lib
|
||||
.get::<unsafe extern "C" fn(
|
||||
*mut PyObject,
|
||||
*mut PyObject,
|
||||
*mut PyObject,
|
||||
) -> *mut PyObject>(b"PyObject_Call\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyTuple_New = *lib
|
||||
.get::<unsafe extern "C" fn(isize) -> *mut PyObject>(b"PyTuple_New\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyTuple_SetItem = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject, isize, *mut PyObject) -> c_int>(
|
||||
b"PyTuple_SetItem\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyLong_FromLongLong = *lib
|
||||
.get::<unsafe extern "C" fn(i64) -> *mut PyObject>(b"PyLong_FromLongLong\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyUnicode_FromString = *lib
|
||||
.get::<unsafe extern "C" fn(*const c_char) -> *mut PyObject>(
|
||||
b"PyUnicode_FromString\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyBool_FromLong = *lib
|
||||
.get::<unsafe extern "C" fn(c_long: c_long) -> *mut PyObject>(
|
||||
b"PyBool_FromLong\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyFloat_FromDouble = *lib
|
||||
.get::<unsafe extern "C" fn(f64) -> *mut PyObject>(b"PyFloat_FromDouble\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyFloat_AsDouble = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject) -> f64>(b"PyFloat_AsDouble\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyLong_AsLongLong = *lib
|
||||
.get::<unsafe extern "C" fn(*mut PyObject) -> i64>(b"PyLong_AsLongLong\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyBytes_FromStringAndSize = *lib
|
||||
.get::<unsafe extern "C" fn(*const c_char, isize) -> *mut PyObject>(
|
||||
b"PyBytes_FromStringAndSize\0",
|
||||
)
|
||||
.map_err(|_| ())?;
|
||||
let PyBytes_AsStringAndSize = *lib.get::<unsafe extern "C" fn(*mut PyObject, *mut *mut c_char, *mut isize) -> c_int>(b"PyBytes_AsStringAndSize\0").map_err(|_| ())?;
|
||||
let PyDict_New = *lib
|
||||
.get::<unsafe extern "C" fn() -> *mut PyObject>(b"PyDict_New\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyDict_SetItemString = *lib.get::<unsafe extern "C" fn(*mut PyObject, *const c_char, *mut PyObject) -> c_int>(b"PyDict_SetItemString\0").map_err(|_| ())?;
|
||||
let PyErr_Occurred = *lib
|
||||
.get::<unsafe extern "C" fn() -> *mut PyObject>(b"PyErr_Occurred\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyErr_Fetch =
|
||||
*lib.get::<unsafe extern "C" fn(
|
||||
*mut *mut PyObject,
|
||||
*mut *mut PyObject,
|
||||
*mut *mut PyObject,
|
||||
)>(b"PyErr_Fetch\0")
|
||||
.map_err(|_| ())?;
|
||||
let PyErr_Clear = *lib
|
||||
.get::<unsafe extern "C" fn()>(b"PyErr_Clear\0")
|
||||
.map_err(|_| ())?;
|
||||
|
||||
let cpy = CPython {
|
||||
_lib: lib,
|
||||
Py_Initialize,
|
||||
Py_Finalize,
|
||||
Py_IsInitialized,
|
||||
PyGILState_Ensure,
|
||||
PyGILState_Release,
|
||||
PyRun_StringFlags,
|
||||
PyImport_AddModule,
|
||||
PyModule_GetDict,
|
||||
PyImport_ImportModule,
|
||||
PyObject_Str,
|
||||
PyUnicode_AsUTF8,
|
||||
Py_DecRef,
|
||||
Py_IncRef,
|
||||
PyObject_GetAttrString,
|
||||
PyObject_CallObject,
|
||||
PyObject_Call,
|
||||
PyTuple_New,
|
||||
PyTuple_SetItem,
|
||||
PyLong_FromLongLong,
|
||||
PyUnicode_FromString,
|
||||
PyBool_FromLong,
|
||||
PyFloat_FromDouble,
|
||||
PyFloat_AsDouble,
|
||||
PyLong_AsLongLong,
|
||||
PyBytes_FromStringAndSize,
|
||||
PyBytes_AsStringAndSize,
|
||||
PyDict_New,
|
||||
PyDict_SetItemString,
|
||||
PyErr_Occurred,
|
||||
PyErr_Fetch,
|
||||
PyErr_Clear,
|
||||
};
|
||||
*CPY.lock().unwrap() = Some(cpy);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub fn ensure_cpython() -> Result<(), ()> {
|
||||
if CPY.lock().unwrap().is_none() {
|
||||
try_load_cpython()?;
|
||||
unsafe {
|
||||
if let Some(cpy) = &*CPY.lock().unwrap() {
|
||||
if (cpy.Py_IsInitialized)() == 0 {
|
||||
(cpy.Py_Initialize)();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
19
plugins/nyash-python-plugin/src/gil.rs
Normal file
19
plugins/nyash-python-plugin/src/gil.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::ffi::{CPython, PyGILState_STATE};
|
||||
|
||||
pub struct GILGuard<'a> {
|
||||
cpy: &'a CPython,
|
||||
state: PyGILState_STATE,
|
||||
}
|
||||
|
||||
impl<'a> GILGuard<'a> {
|
||||
pub fn acquire(cpy: &'a CPython) -> Self {
|
||||
let state = unsafe { (cpy.PyGILState_Ensure)() };
|
||||
GILGuard { cpy, state }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for GILGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { (self.cpy.PyGILState_Release)(self.state) };
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
461
plugins/nyash-python-plugin/src/pytypes.rs
Normal file
461
plugins/nyash-python-plugin/src/pytypes.rs
Normal file
@ -0,0 +1,461 @@
|
||||
use crate::ffi::{self, CPython, PyObject};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
pub enum DecodedValue {
|
||||
Float(f64),
|
||||
Int(i64),
|
||||
Str(String),
|
||||
Bytes(Vec<u8>),
|
||||
}
|
||||
|
||||
pub struct PyOwned {
|
||||
ptr: NonNull<PyObject>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PyBorrowed<'a> {
|
||||
ptr: NonNull<PyObject>,
|
||||
_marker: PhantomData<&'a PyObject>,
|
||||
}
|
||||
|
||||
impl PyOwned {
|
||||
pub unsafe fn from_new(ptr: *mut PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|ptr| PyOwned { ptr })
|
||||
}
|
||||
|
||||
pub unsafe fn from_raw(ptr: *mut PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|ptr| PyOwned { ptr })
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn from_borrowed(cpy: &CPython, borrowed: PyBorrowed<'_>) -> Self {
|
||||
(cpy.Py_IncRef)(borrowed.ptr.as_ptr());
|
||||
PyOwned { ptr: borrowed.ptr }
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut PyObject {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn borrow(&self) -> PyBorrowed<'_> {
|
||||
PyBorrowed {
|
||||
ptr: self.ptr,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clone_ref(&self, cpy: &CPython) -> Self {
|
||||
unsafe {
|
||||
(cpy.Py_IncRef)(self.ptr.as_ptr());
|
||||
}
|
||||
PyOwned { ptr: self.ptr }
|
||||
}
|
||||
|
||||
pub fn into_raw(self) -> *mut PyObject {
|
||||
let ptr = self.ptr.as_ptr();
|
||||
std::mem::forget(self);
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PyOwned {
|
||||
fn drop(&mut self) {
|
||||
if let Ok(guard) = ffi::CPY.lock() {
|
||||
if let Some(cpy) = guard.as_ref() {
|
||||
unsafe {
|
||||
(cpy.Py_DecRef)(self.ptr.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for PyOwned {
|
||||
fn clone(&self) -> Self {
|
||||
let guard = ffi::CPY.lock().expect("CPython state poisoned");
|
||||
let cpy = guard.as_ref().expect("CPython not initialized");
|
||||
unsafe {
|
||||
(cpy.Py_IncRef)(self.ptr.as_ptr());
|
||||
}
|
||||
PyOwned { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for PyOwned {}
|
||||
unsafe impl Sync for PyOwned {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<'a> PyBorrowed<'a> {
|
||||
pub unsafe fn new(ptr: *mut PyObject) -> Option<Self> {
|
||||
NonNull::new(ptr).map(|ptr| PyBorrowed {
|
||||
ptr,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut PyObject {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Copy for PyBorrowed<'a> {}
|
||||
|
||||
impl<'a> Clone for PyBorrowed<'a> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn autodecode(cpy: &CPython, obj: *mut PyObject) -> Option<DecodedValue> {
|
||||
unsafe {
|
||||
let f = (cpy.PyFloat_AsDouble)(obj);
|
||||
if (cpy.PyErr_Occurred)().is_null() {
|
||||
return Some(DecodedValue::Float(f));
|
||||
}
|
||||
(cpy.PyErr_Clear)();
|
||||
|
||||
let i = (cpy.PyLong_AsLongLong)(obj);
|
||||
if (cpy.PyErr_Occurred)().is_null() {
|
||||
return Some(DecodedValue::Int(i));
|
||||
}
|
||||
(cpy.PyErr_Clear)();
|
||||
|
||||
let u = (cpy.PyUnicode_AsUTF8)(obj);
|
||||
if (cpy.PyErr_Occurred)().is_null() && !u.is_null() {
|
||||
let s = CStr::from_ptr(u).to_string_lossy().to_string();
|
||||
return Some(DecodedValue::Str(s));
|
||||
}
|
||||
(cpy.PyErr_Clear)();
|
||||
|
||||
let mut ptr: *mut c_char = std::ptr::null_mut();
|
||||
let mut sz: isize = 0;
|
||||
if (cpy.PyBytes_AsStringAndSize)(obj, &mut ptr, &mut sz) == 0 {
|
||||
let slice = std::slice::from_raw_parts(ptr as *const u8, sz as usize);
|
||||
return Some(DecodedValue::Bytes(slice.to_vec()));
|
||||
}
|
||||
if !(cpy.PyErr_Occurred)().is_null() {
|
||||
(cpy.PyErr_Clear)();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn cstring_from_str(s: &str) -> Result<CString, ()> {
|
||||
CString::new(s).map_err(|_| ())
|
||||
}
|
||||
|
||||
pub unsafe fn cstr_to_string(ptr: *const c_char) -> Option<String> {
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(CStr::from_ptr(ptr).to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn incref(cpy: &CPython, obj: *mut PyObject) {
|
||||
(cpy.Py_IncRef)(obj);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn decref(cpy: &CPython, obj: *mut PyObject) {
|
||||
(cpy.Py_DecRef)(obj);
|
||||
}
|
||||
|
||||
pub fn take_py_error_string(cpy: &CPython) -> Option<String> {
|
||||
unsafe {
|
||||
if (cpy.PyErr_Occurred)().is_null() {
|
||||
return None;
|
||||
}
|
||||
let mut ptype: *mut PyObject = std::ptr::null_mut();
|
||||
let mut pvalue: *mut PyObject = std::ptr::null_mut();
|
||||
let mut ptrace: *mut PyObject = std::ptr::null_mut();
|
||||
(cpy.PyErr_Fetch)(&mut ptype, &mut pvalue, &mut ptrace);
|
||||
let s = if !pvalue.is_null() {
|
||||
let sobj = (cpy.PyObject_Str)(pvalue);
|
||||
if sobj.is_null() {
|
||||
(cpy.PyErr_Clear)();
|
||||
return Some("Python error".to_string());
|
||||
}
|
||||
let cstr = (cpy.PyUnicode_AsUTF8)(sobj);
|
||||
let msg = if cstr.is_null() {
|
||||
"Python error".to_string()
|
||||
} else {
|
||||
CStr::from_ptr(cstr).to_string_lossy().to_string()
|
||||
};
|
||||
(cpy.Py_DecRef)(sobj);
|
||||
msg
|
||||
} else {
|
||||
"Python error".to_string()
|
||||
};
|
||||
(cpy.PyErr_Clear)();
|
||||
Some(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn count_tlv_args(args: *const u8, args_len: usize) -> usize {
|
||||
if args.is_null() || args_len < 4 {
|
||||
return 0;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
if buf.len() < 4 {
|
||||
return 0;
|
||||
}
|
||||
u16::from_le_bytes([buf[2], buf[3]]) as usize
|
||||
}
|
||||
|
||||
pub fn tuple_from_tlv(
|
||||
cpy: &CPython,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
) -> Result<*mut PyObject, ()> {
|
||||
let argc = count_tlv_args(args, args_len) as isize;
|
||||
let tuple = unsafe { (cpy.PyTuple_New)(argc) };
|
||||
if tuple.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
if argc == 0 {
|
||||
return Ok(tuple);
|
||||
}
|
||||
if !fill_tuple_from_tlv(cpy, tuple, args, args_len) {
|
||||
unsafe {
|
||||
(cpy.Py_DecRef)(tuple);
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
Ok(tuple)
|
||||
}
|
||||
|
||||
pub fn kwargs_from_tlv(
|
||||
cpy: &CPython,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
) -> Result<*mut PyObject, ()> {
|
||||
let dict = unsafe { (cpy.PyDict_New)() };
|
||||
if dict.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
if !fill_kwargs_from_tlv(cpy, dict, args, args_len) {
|
||||
unsafe {
|
||||
(cpy.Py_DecRef)(dict);
|
||||
}
|
||||
return Err(());
|
||||
}
|
||||
Ok(dict)
|
||||
}
|
||||
|
||||
pub fn fill_tuple_from_tlv(
|
||||
cpy: &CPython,
|
||||
tuple: *mut PyObject,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
) -> bool {
|
||||
if args.is_null() || args_len < 4 {
|
||||
return true;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize;
|
||||
let mut idx: isize = 0;
|
||||
while off + 4 <= buf.len() {
|
||||
let tag = buf[off];
|
||||
let _rsv = buf[off + 1];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if off + 4 + size > buf.len() {
|
||||
return false;
|
||||
}
|
||||
let payload = &buf[off + 4..off + 4 + size];
|
||||
let mut obj: *mut PyObject = std::ptr::null_mut();
|
||||
unsafe {
|
||||
let mut owned_transfer: Option<PyOwned> = None;
|
||||
match tag {
|
||||
1 => {
|
||||
let v = if size >= 1 && payload[0] != 0 { 1 } else { 0 };
|
||||
obj = (cpy.PyBool_FromLong)(v);
|
||||
}
|
||||
2 => {
|
||||
if size != 4 {
|
||||
return false;
|
||||
}
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(payload);
|
||||
obj = (cpy.PyLong_FromLongLong)(i32::from_le_bytes(b) as i64);
|
||||
}
|
||||
3 => {
|
||||
if size != 8 {
|
||||
return false;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
obj = (cpy.PyLong_FromLongLong)(i64::from_le_bytes(b));
|
||||
}
|
||||
5 => {
|
||||
if size != 8 {
|
||||
return false;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
obj = (cpy.PyFloat_FromDouble)(f64::from_le_bytes(b));
|
||||
}
|
||||
6 => {
|
||||
let c = match CString::new(payload) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return false,
|
||||
};
|
||||
obj = (cpy.PyUnicode_FromString)(c.as_ptr());
|
||||
}
|
||||
7 => {
|
||||
let ptr = if size > 0 {
|
||||
payload.as_ptr() as *const c_char
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
obj = (cpy.PyBytes_FromStringAndSize)(ptr, size as isize);
|
||||
}
|
||||
8 => {
|
||||
if size != 8 {
|
||||
return false;
|
||||
}
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&payload[4..8]);
|
||||
let inst_id = u32::from_le_bytes(i);
|
||||
let Some(handle) = super::PY_HANDLES.lock().unwrap().get(&inst_id).cloned()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
owned_transfer = Some(handle);
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
let raw = if let Some(handle) = owned_transfer.take() {
|
||||
handle.into_raw()
|
||||
} else {
|
||||
obj
|
||||
};
|
||||
if (cpy.PyTuple_SetItem)(tuple, idx, raw) != 0 {
|
||||
if let Some(rewrap) = PyOwned::from_raw(raw) {
|
||||
drop(rewrap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn fill_kwargs_from_tlv(
|
||||
cpy: &CPython,
|
||||
dict: *mut PyObject,
|
||||
args: *const u8,
|
||||
args_len: usize,
|
||||
) -> bool {
|
||||
if args.is_null() || args_len < 4 {
|
||||
return true;
|
||||
}
|
||||
let buf = unsafe { std::slice::from_raw_parts(args, args_len) };
|
||||
let mut off = 4usize;
|
||||
while off + 4 <= buf.len() {
|
||||
// key (string)
|
||||
if buf[off] != 6 {
|
||||
return false;
|
||||
}
|
||||
let key_size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if off + 4 + key_size > buf.len() {
|
||||
return false;
|
||||
}
|
||||
let key_slice = &buf[off + 4..off + 4 + key_size];
|
||||
let key_c = match CString::new(key_slice) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return false,
|
||||
};
|
||||
off += 4 + key_size;
|
||||
if off + 4 > buf.len() {
|
||||
return false;
|
||||
}
|
||||
let tag_v = buf[off];
|
||||
let _rsv = buf[off + 1];
|
||||
let size_v = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if off + 4 + size_v > buf.len() {
|
||||
return false;
|
||||
}
|
||||
let val_payload = &buf[off + 4..off + 4 + size_v];
|
||||
let obj: *mut PyObject;
|
||||
unsafe {
|
||||
match tag_v {
|
||||
1 => {
|
||||
let v = if size_v >= 1 && val_payload[0] != 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
obj = (cpy.PyBool_FromLong)(v);
|
||||
}
|
||||
2 => {
|
||||
if size_v != 4 {
|
||||
return false;
|
||||
}
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(val_payload);
|
||||
obj = (cpy.PyLong_FromLongLong)(i32::from_le_bytes(b) as i64);
|
||||
}
|
||||
3 => {
|
||||
if size_v != 8 {
|
||||
return false;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(val_payload);
|
||||
obj = (cpy.PyLong_FromLongLong)(i64::from_le_bytes(b));
|
||||
}
|
||||
5 => {
|
||||
if size_v != 8 {
|
||||
return false;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(val_payload);
|
||||
obj = (cpy.PyFloat_FromDouble)(f64::from_le_bytes(b));
|
||||
}
|
||||
6 => {
|
||||
let c = match CString::new(val_payload) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return false,
|
||||
};
|
||||
obj = (cpy.PyUnicode_FromString)(c.as_ptr());
|
||||
}
|
||||
7 => {
|
||||
let ptr = if size_v > 0 {
|
||||
val_payload.as_ptr() as *const c_char
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
obj = (cpy.PyBytes_FromStringAndSize)(ptr, size_v as isize);
|
||||
}
|
||||
8 => {
|
||||
if size_v != 8 {
|
||||
return false;
|
||||
}
|
||||
let mut i = [0u8; 4];
|
||||
i.copy_from_slice(&val_payload[4..8]);
|
||||
let inst_id = u32::from_le_bytes(i);
|
||||
let Some(handle) = super::PY_HANDLES.lock().unwrap().get(&inst_id).cloned()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
obj = handle.into_raw();
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
let rc = (cpy.PyDict_SetItemString)(dict, key_c.as_ptr(), obj);
|
||||
(cpy.Py_DecRef)(obj);
|
||||
if rc != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
off += 4 + size_v;
|
||||
}
|
||||
true
|
||||
}
|
||||
@ -208,9 +208,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// ===== TypeBox ABI v2 (resolve/invoke_id) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char,
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
@ -220,7 +220,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
use std::ffi::CStr;
|
||||
extern "C" fn regex_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"compile" => M_COMPILE,
|
||||
@ -246,66 +248,148 @@ extern "C" fn regex_invoke_id(
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
// mirror v1: birth may take optional pattern
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
let inst = if let Some(pat) = read_arg_string(args, args_len, 0) {
|
||||
match Regex::new(&pat) { Ok(re) => RegexInstance { re: Some(re) }, Err(_) => RegexInstance { re: None } }
|
||||
} else { RegexInstance { re: None } };
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, inst); } else { return E_PLUGIN; }
|
||||
match Regex::new(&pat) {
|
||||
Ok(re) => RegexInstance { re: Some(re) },
|
||||
Err(_) => RegexInstance { re: None },
|
||||
}
|
||||
} else {
|
||||
RegexInstance { re: None }
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, inst);
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4; OK
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN }
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_COMPILE => {
|
||||
let pat = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let pat = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) { inst.re = Regex::new(&pat).ok(); OK } else { E_HANDLE }
|
||||
} else { E_PLUGIN }
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.re = Regex::new(&pat).ok();
|
||||
OK
|
||||
} else {
|
||||
E_HANDLE
|
||||
}
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_IS_MATCH => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re { return write_tlv_bool(re.is_match(&text), result, result_len); } else { return write_tlv_bool(false, result, result_len); }
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
}
|
||||
M_FIND => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
let s = re.find(&text).map(|m| m.as_str().to_string()).unwrap_or_else(|| "".to_string());
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
} else { return write_tlv_string("", result, result_len); }
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
return write_tlv_bool(re.is_match(&text), result, result_len);
|
||||
} else {
|
||||
return write_tlv_bool(false, result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_REPLACE_ALL => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let repl = match read_arg_string(args, args_len, 1) { Some(s) => s, None => return E_ARGS };
|
||||
M_FIND => {
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re { let out = re.replace_all(&text, repl.as_str()).to_string(); return write_tlv_string(&out, result, result_len); } else { return write_tlv_string(&text, result, result_len); }
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
if let Some(re) = &inst.re {
|
||||
let s = re
|
||||
.find(&text)
|
||||
.map(|m| m.as_str().to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
} else {
|
||||
return write_tlv_string("", result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_REPLACE_ALL => {
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let repl = match read_arg_string(args, args_len, 1) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
let out = re.replace_all(&text, repl.as_str()).to_string();
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else {
|
||||
return write_tlv_string(&text, result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_SPLIT => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
let limit = read_arg_i64(args, args_len, 1).unwrap_or(0);
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(re) = &inst.re {
|
||||
let parts: Vec<String> = if limit > 0 { re.splitn(&text, limit as usize).map(|s| s.to_string()).collect() } else { re.split(&text).map(|s| s.to_string()).collect() };
|
||||
let parts: Vec<String> = if limit > 0 {
|
||||
re.splitn(&text, limit as usize)
|
||||
.map(|s| s.to_string())
|
||||
.collect()
|
||||
} else {
|
||||
re.split(&text).map(|s| s.to_string()).collect()
|
||||
};
|
||||
let out = parts.join("\n");
|
||||
return write_tlv_string(&out, result, result_len);
|
||||
} else { return write_tlv_string(&text, result, result_len); }
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return write_tlv_string(&text, result, result_len);
|
||||
}
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
|
||||
@ -151,9 +151,9 @@ pub extern "C" fn nyash_plugin_invoke(
|
||||
// ===== TypeBox ABI v2 (resolve/invoke_id) =====
|
||||
#[repr(C)]
|
||||
pub struct NyashTypeBoxFfi {
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub abi_tag: u32, // 'TYBX'
|
||||
pub version: u16, // 1
|
||||
pub struct_size: u16, // sizeof(NyashTypeBoxFfi)
|
||||
pub name: *const std::os::raw::c_char,
|
||||
pub resolve: Option<extern "C" fn(*const std::os::raw::c_char) -> u32>,
|
||||
pub invoke_id: Option<extern "C" fn(u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32>,
|
||||
@ -163,7 +163,9 @@ unsafe impl Sync for NyashTypeBoxFfi {}
|
||||
|
||||
use std::ffi::CStr;
|
||||
extern "C" fn toml_resolve(name: *const std::os::raw::c_char) -> u32 {
|
||||
if name.is_null() { return 0; }
|
||||
if name.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let s = unsafe { CStr::from_ptr(name) }.to_string_lossy();
|
||||
match s.as_ref() {
|
||||
"parse" => M_PARSE,
|
||||
@ -186,47 +188,93 @@ extern "C" fn toml_invoke_id(
|
||||
unsafe {
|
||||
match method_id {
|
||||
M_BIRTH => {
|
||||
if result_len.is_null() { return E_ARGS; }
|
||||
if preflight(result, result_len, 4) { return E_SHORT; }
|
||||
if result_len.is_null() {
|
||||
return E_ARGS;
|
||||
}
|
||||
if preflight(result, result_len, 4) {
|
||||
return E_SHORT;
|
||||
}
|
||||
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = INST.lock() { m.insert(id, TomlInstance { value: None }); } else { return E_PLUGIN; }
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.insert(id, TomlInstance { value: None });
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
let b = id.to_le_bytes();
|
||||
std::ptr::copy_nonoverlapping(b.as_ptr(), result, 4);
|
||||
*result_len = 4; OK
|
||||
*result_len = 4;
|
||||
OK
|
||||
}
|
||||
M_FINI => {
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
m.remove(&instance_id);
|
||||
OK
|
||||
} else {
|
||||
E_PLUGIN
|
||||
}
|
||||
}
|
||||
M_FINI => { if let Ok(mut m) = INST.lock() { m.remove(&instance_id); OK } else { E_PLUGIN } }
|
||||
M_PARSE => {
|
||||
let text = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let text = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(mut m) = INST.lock() {
|
||||
if let Some(inst) = m.get_mut(&instance_id) {
|
||||
inst.value = toml::from_str::<toml::Value>(&text).ok();
|
||||
return write_tlv_bool(inst.value.is_some(), result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_GET => {
|
||||
let path = match read_arg_string(args, args_len, 0) { Some(s) => s, None => return E_ARGS };
|
||||
let path = match read_arg_string(args, args_len, 0) {
|
||||
Some(s) => s,
|
||||
None => return E_ARGS,
|
||||
};
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
let mut cur = match &inst.value { Some(v) => v, None => { return write_tlv_string("", result, result_len); } };
|
||||
let mut cur = match &inst.value {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return write_tlv_string("", result, result_len);
|
||||
}
|
||||
};
|
||||
if !path.is_empty() {
|
||||
for seg in path.split('.') {
|
||||
match cur.get(seg) { Some(v) => cur = v, None => { return write_tlv_string("", result, result_len); } }
|
||||
match cur.get(seg) {
|
||||
Some(v) => cur = v,
|
||||
None => {
|
||||
return write_tlv_string("", result, result_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return write_tlv_string(&cur.to_string(), result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
M_TO_JSON => {
|
||||
if let Ok(m) = INST.lock() {
|
||||
if let Some(inst) = m.get(&instance_id) {
|
||||
if let Some(v) = &inst.value {
|
||||
if let Ok(s) = serde_json::to_string(v) { return write_tlv_string(&s, result, result_len); }
|
||||
if let Ok(s) = serde_json::to_string(v) {
|
||||
return write_tlv_string(&s, result, result_len);
|
||||
}
|
||||
}
|
||||
return write_tlv_string("{}", result, result_len);
|
||||
} else { return E_HANDLE; }
|
||||
} else { return E_PLUGIN; }
|
||||
} else {
|
||||
return E_HANDLE;
|
||||
}
|
||||
} else {
|
||||
return E_PLUGIN;
|
||||
}
|
||||
}
|
||||
_ => E_METHOD,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user