#![allow(clippy::std_instead_of_core)]
use core::ffi::c_void;
use polyplug::loader::BundleLoader;
use polyplug::logger::LoggerHandle;
use polyplug_abi::AbiError;
use polyplug_abi::AbiErrorCode;
use polyplug_abi::CallArena;
use polyplug_abi::GuestContractInstance;
use polyplug_abi::HostContractInstance;
use polyplug_abi::HostContractInterface;
use polyplug_abi::StringView;
use polyplug_abi::VmLoaderData;
use polyplug_abi::types::LogLevel;
use crate::{PythonLoader, config::PythonConfig};
#[repr(C)]
pub struct PolyplugPythonConfig {
pub min_version_ptr: *const u8,
pub min_version_len: usize,
}
#[repr(C)]
pub struct PolyplugPythonHostDispatchBridge {
pub callback: Option<unsafe extern "C" fn(u32, *const c_void, *mut c_void) -> u32>,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn polyplug_python_host_vm_dispatch(
loader_data: VmLoaderData,
_instance: GuestContractInstance,
fn_id: u32,
args: *const (),
out: *mut (),
_arena: *mut CallArena,
out_err: *mut AbiError,
) {
let result: AbiError =
unsafe { polyplug_python_host_vm_dispatch_impl(loader_data, fn_id, args, out) };
if !out_err.is_null() {
unsafe { out_err.write(result) };
}
}
unsafe fn polyplug_python_host_vm_dispatch_impl(
loader_data: VmLoaderData,
fn_id: u32,
args: *const (),
out: *mut (),
) -> AbiError {
let bridge_ptr: *const PolyplugPythonHostDispatchBridge =
loader_data.data as *const PolyplugPythonHostDispatchBridge;
if bridge_ptr.is_null() {
return AbiError {
code: AbiErrorCode::InvalidPointer as u32,
message: StringView::from_static(b"python host dispatch bridge is null"),
};
}
let callback: Option<unsafe extern "C" fn(u32, *const c_void, *mut c_void) -> u32> =
unsafe { (*bridge_ptr).callback };
match callback {
Some(cb) => {
let code: u32 = unsafe { cb(fn_id, args as *const c_void, out as *mut c_void) };
if code == AbiErrorCode::Ok as u32 {
AbiError::ok()
} else {
AbiError {
code,
message: StringView::from_static(b"python host contract returned error"),
}
}
}
None => AbiError {
code: AbiErrorCode::InvalidPointer as u32,
message: StringView::from_static(b"python host dispatch bridge has no callback"),
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn polyplug_python_host_create_instance(
this: *const HostContractInterface,
_args: *const c_void,
out_instance: *mut HostContractInstance,
) {
let instance: HostContractInstance = if this.is_null() {
HostContractInstance::null()
} else {
HostContractInstance {
data: unsafe { (*this).user_data },
}
};
if !out_instance.is_null() {
unsafe { out_instance.write(instance) };
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn polyplug_python_host_destroy_instance(
_this: *const HostContractInterface,
_instance: HostContractInstance,
) {
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn polyplug_python_loader_create(
config: *const PolyplugPythonConfig,
) -> *mut c_void {
if config.is_null() {
return std::ptr::null_mut();
}
let cfg: &PolyplugPythonConfig = unsafe { &*config };
if cfg.min_version_ptr.is_null() {
return std::ptr::null_mut();
}
let bytes: &[u8] =
unsafe { std::slice::from_raw_parts(cfg.min_version_ptr, cfg.min_version_len) };
let version_str: &str = match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
};
let min_version: (u32, u32) = match parse_version(version_str) {
Some(v) => v,
None => return std::ptr::null_mut(),
};
let loader: PythonLoader = PythonLoader::new(PythonConfig { min_version });
let trait_obj: Box<dyn BundleLoader> = Box::new(loader);
Box::into_raw(Box::new(trait_obj)) as *mut c_void
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn polyplug_python_loader_free(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe {
drop(Box::<Box<dyn BundleLoader>>::from_raw(
ptr as *mut Box<dyn BundleLoader>,
))
};
}
fn parse_version(s: &str) -> Option<(u32, u32)> {
let logger: LoggerHandle = LoggerHandle::default_stderr();
let mut parts = s.splitn(2, '.');
let major_str: &str = parts.next()?;
let major: u32 = major_str
.parse()
.map_err(|e: std::num::ParseIntError| {
logger.log(LogLevel::Error, "loader.python", || {
format!("parse_version: failed to parse major '{major_str}' as u32: {e}")
});
})
.ok()?;
let minor_str: &str = parts.next()?;
let minor: u32 = minor_str
.parse()
.map_err(|e: std::num::ParseIntError| {
logger.log(LogLevel::Error, "loader.python", || {
format!("parse_version: failed to parse minor '{minor_str}' as u32: {e}")
});
})
.ok()?;
Some((major, minor))
}