#![allow(improper_ctypes)] use crate::function::{wrap_into_pyfunction, Function}; use crate::instance::Instance; use crate::memory::Memory; use crate::module::Module; use pyo3::exceptions::Exception; use pyo3::prelude::*; use pyo3::types::{PyAny, PyBytes, PyDict, PySet}; use pyo3::wrap_pyfunction; use std::rc::Rc; use wasmtime_interface_types::ModuleData; use wasmtime_jit::Features; mod function; mod instance; mod memory; mod module; mod value; fn err2py(err: anyhow::Error) -> PyErr { PyErr::new::(format!("{:?}", err)) } #[pyclass] pub struct InstantiateResultObject { instance: Py, module: Py, } #[pymethods] impl InstantiateResultObject { #[getter(instance)] fn get_instance(&self) -> PyResult> { let gil = Python::acquire_gil(); let py = gil.python(); Ok(self.instance.clone_ref(py)) } #[getter(module)] fn get_module(&self) -> PyResult> { let gil = Python::acquire_gil(); let py = gil.python(); Ok(self.module.clone_ref(py)) } } fn find_export_in( obj: &PyAny, store: &wasmtime::HostRef, name: &str, ) -> PyResult { let obj = obj.cast_as::()?; Ok(if let Some(item) = obj.get_item(name) { if item.is_callable() { if item.get_type().is_subclass::()? { let wasm_fn = item.cast_as::()?; wasm_fn.func().into() } else { wrap_into_pyfunction(store, item)?.into() } } else if item.get_type().is_subclass::()? { let wasm_mem = item.cast_as::()?; wasm_mem.memory.clone().into() } else { return Err(PyErr::new::(format!( "unsupported import type {}", name ))); } } else { return Err(PyErr::new::(format!( "import {} is not found", name ))); }) } /// WebAssembly instantiate API method. #[pyfunction] pub fn instantiate( py: Python, buffer_source: &PyBytes, import_obj: &PyDict, ) -> PyResult> { let wasm_data = buffer_source.as_bytes(); let mut config = wasmtime::Config::new(); config.features(Features { multi_value: true, ..Default::default() }); let engine = wasmtime::HostRef::new(wasmtime::Engine::new(&config)); let store = wasmtime::HostRef::new(wasmtime::Store::new(&engine)); let module = wasmtime::HostRef::new(wasmtime::Module::new(&store, wasm_data).map_err(err2py)?); let data = Rc::new(ModuleData::new(wasm_data).map_err(err2py)?); // If this module expects to be able to use wasi then go ahead and hook // that up into the imported crates. let wasi = if let Some(module_name) = data.find_wasi_module_name() { let instance = wasmtime_wasi::create_wasi_instance(&store, &[], &[], &[]) .map_err(|e| err2py(e.into()))?; Some((module_name, instance)) } else { None }; let mut imports: Vec = Vec::new(); for i in module.borrow().imports() { let module_name = i.module(); if let Some(m) = import_obj.get_item(module_name) { let e = find_export_in(m, &store, i.name())?; imports.push(e); } else if wasi.is_some() && module_name == wasi.as_ref().unwrap().0 { let e = wasi .as_ref() .unwrap() .1 .find_export_by_name(i.name()) .ok_or_else(|| { PyErr::new::(format!("wasi export {} is not found", i.name(),)) })?; imports.push(e.clone()); } else { return Err(PyErr::new::(format!( "imported module {} is not found", module_name ))); } } let instance = wasmtime::HostRef::new( wasmtime::Instance::new(&store, &module, &imports) .map_err(|t| PyErr::new::(format!("instantiated with trap {:?}", t)))?, ); let module = Py::new(py, Module { module })?; let instance = Py::new(py, Instance { instance, data })?; Py::new(py, InstantiateResultObject { instance, module }) } #[pyfunction] pub fn imported_modules<'p>(py: Python<'p>, buffer_source: &PyBytes) -> PyResult<&'p PyDict> { let wasm_data = buffer_source.as_bytes(); let dict = PyDict::new(py); // TODO: error handling let mut parser = wasmparser::ModuleReader::new(wasm_data).unwrap(); while !parser.eof() { let section = parser.read().unwrap(); match section.code { wasmparser::SectionCode::Import => {} _ => continue, }; let reader = section.get_import_section_reader().unwrap(); for import in reader { let import = import.unwrap(); // Skip over wasi-looking imports since those aren't imported from // Python but rather they're implemented natively. if wasmtime_wasi::is_wasi_module(import.module) { continue; } let set = match dict.get_item(import.module) { Some(set) => set.downcast_ref::().unwrap(), None => { let set = PySet::new::(py, &[])?; dict.set_item(import.module, set)?; set } }; set.add(import.field)?; } } Ok(dict) } #[pymodule] fn lib_wasmtime(_: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_wrapped(wrap_pyfunction!(instantiate))?; m.add_wrapped(wrap_pyfunction!(imported_modules))?; Ok(()) }