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:
Selfhosting Dev
2025-09-18 03:57:25 +09:00
parent 1b12b1eb7d
commit 5d51086530
27 changed files with 2802 additions and 2484 deletions

View File

@ -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,
}

View File

@ -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)

View File

@ -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,

View 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

View 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)
}

View 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))
}

View File

@ -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>,

View File

@ -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
},

View File

@ -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 {

View 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(())
}

View 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

View 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
}

View File

@ -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,
}

View File

@ -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,
}