diff --git a/Cargo.lock b/Cargo.lock index dd296d7..adc585a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,14 +474,14 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "component-init-transform" version = "0.2.0" -source = "git+https://github.com/dicej/component-init?rev=3800ab6e#3800ab6ef88c4a9817119ccfe5fa9de8ff2afff1" +source = "git+https://github.com/dicej/component-init?rev=3b9680fe#3b9680fe652421d4d80e3d00c93d8b5eecb8acdc" dependencies = [ "anyhow", "async-trait", "futures", - "wasm-encoder 0.238.1", - "wasm-metadata 0.238.1", - "wasmparser 0.238.1", + "wasm-encoder 0.240.0", + "wasm-metadata 0.240.0", + "wasmparser 0.240.0", ] [[package]] @@ -496,7 +496,6 @@ dependencies = [ "cap-std", "clap", "component-init-transform", - "componentize-py-shared", "flate2", "fs_extra", "futures", @@ -517,12 +516,13 @@ dependencies = [ "test-generator", "tokio", "toml", - "wasm-encoder 0.239.0", - "wasmparser 0.239.0", + "wasm-encoder 0.240.0", + "wasmparser 0.240.0", "wasmtime", "wasmtime-wasi", - "wit-component 0.239.0", - "wit-parser 0.239.0", + "wit-component 0.240.0", + "wit-dylib", + "wit-parser 0.240.0", "zstd", ] @@ -531,18 +531,14 @@ name = "componentize-py-runtime" version = "0.1.0" dependencies = [ "anyhow", - "componentize-py-shared", "num-bigint", "once_cell", "pyo3", "wit-bindgen 0.40.0", "wit-bindgen-rt", + "wit-dylib-ffi", ] -[[package]] -name = "componentize-py-shared" -version = "0.1.0" - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2911,22 +2907,21 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.238.1" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50d48c31c615f77679b61c607b8151378a5d03159616bf3d17e8e2005afdaf5" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" dependencies = [ "leb128fmt", - "wasmparser 0.238.1", + "wasmparser 0.239.0", ] [[package]] name = "wasm-encoder" -version = "0.239.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +version = "0.240.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" dependencies = [ "leb128fmt", - "wasmparser 0.239.0", + "wasmparser 0.240.0", ] [[package]] @@ -2950,9 +2945,8 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.238.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00094573b000c92134f2ef0f8afa4f6f892de37e78442988c946243a8c44364e" +version = "0.240.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" dependencies = [ "anyhow", "auditable-serde", @@ -2963,20 +2957,8 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder 0.238.1", - "wasmparser 0.238.1", -] - -[[package]] -name = "wasm-metadata" -version = "0.239.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder 0.239.0", - "wasmparser 0.239.0", + "wasm-encoder 0.240.0", + "wasmparser 0.240.0", ] [[package]] @@ -2993,9 +2975,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.238.1" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa99c8328024423875ae4a55345cfde8f0371327fb2d0f33b0f52a06fc44408" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ "bitflags", "hashbrown", @@ -3006,9 +2988,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.239.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +version = "0.240.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" dependencies = [ "bitflags", "hashbrown", @@ -3042,6 +3023,7 @@ dependencies = [ "cc", "cfg-if", "encoding_rs", + "futures", "fxprof-processed-profile", "gimli 0.32.2", "hashbrown", @@ -3810,9 +3792,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.239.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" +version = "0.240.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" dependencies = [ "anyhow", "bitflags", @@ -3821,12 +3802,28 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.239.0", - "wasm-metadata 0.239.0", - "wasmparser 0.239.0", - "wit-parser 0.239.0", + "wasm-encoder 0.240.0", + "wasm-metadata 0.240.0", + "wasmparser 0.240.0", + "wit-parser 0.240.0", ] +[[package]] +name = "wit-dylib" +version = "0.240.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.240.0", + "wit-parser 0.240.0", +] + +[[package]] +name = "wit-dylib-ffi" +version = "0.1.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" + [[package]] name = "wit-parser" version = "0.227.1" @@ -3863,6 +3860,23 @@ dependencies = [ "wasmparser 0.239.0", ] +[[package]] +name = "wit-parser" +version = "0.240.0" +source = "git+https://github.com/bytecodealliance/wasm-tools?rev=b1d8ff59#b1d8ff591bcb8c052b54c3c17495bbbef401897c" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.240.0", +] + [[package]] name = "witx" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index 6e5a122..9ff0364 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,12 @@ clap = { version = "4.5.20", features = ["derive"] } tar = "0.4.42" tempfile = "3.13.0" zstd = "0.13.2" -componentize-py-shared = { path = "shared" } -wasm-encoder = "0.239.0" -wit-parser = "0.239.0" -wit-component = "0.239.0" -wasmparser = "0.239.0" +# TODO: switch to wasm-tools 1.241.0 when available +wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } +wit-dylib = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } +wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } +wit-component = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } +wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59" } indexmap = "2.6.0" bincode = "1.3.3" heck = "0.5.0" @@ -26,10 +27,10 @@ pyo3 = { version = "0.26.0", features = [ "abi3-py39", "extension-module", ], optional = true } -wasmtime = "37.0.2" +wasmtime = { version = "37.0.2", features = [ "component-model-async" ] } wasmtime-wasi = "37.0.2" once_cell = "1.20.2" -component-init-transform = { version = "0.2", git = "https://github.com/dicej/component-init", rev = "3800ab6e" } +component-init-transform = { git = "https://github.com/dicej/component-init", rev = "3b9680fe" } async-trait = "0.1.83" futures = "0.3.31" tokio = { version = "1.41.0", features = [ @@ -80,5 +81,4 @@ test-generator = { path = "test-generator" } flate2 = "1.1.1" [workspace] -members = ["runtime", "shared", "test-generator"] - +members = ["runtime", "test-generator"] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b2920b6..75f6464 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -10,7 +10,8 @@ crate-type = ["staticlib"] anyhow = "1.0.91" once_cell = "1.20.2" pyo3 = { version="0.26.0", features = ["abi3-py312", "num-bigint"] } -componentize-py-shared = { path = "../shared" } num-bigint = "0.4.6" wit-bindgen = { version = "0.40.0", default-features = false, features = ["macros", "realloc"] } wit-bindgen-rt = { version = "0.40.0" } +# TODO: switch to a release when available: +wit-dylib-ffi = { git = "https://github.com/bytecodealliance/wasm-tools", rev = "b1d8ff59", default-features = false } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2d1a721..d0d6186 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -3,42 +3,50 @@ clippy::useless_conversion, reason = "some pyo3 macros produce code that does this" )] -#![allow(static_mut_refs, reason = "wit-bindgen produces code that does this")] +#![allow( + static_mut_refs, + reason = "wit-bindgen::generate produces code that does this" +)] #![allow(unknown_lints)] #![allow( unnecessary_transmutes, reason = "nightly warning but not supported on stable" )] +#![allow( + clippy::not_unsafe_ptr_arg_deref, + reason = "wit_dylib_ffi::export produces code that does this" +)] use { anyhow::{Error, Result}, - componentize_py_shared::ReturnStyle, exports::exports::{ - self as exp, Bundled, Constructor, Function, FunctionExport, Guest, LocalResource, - OwnedKind, OwnedType, RemoteResource, Resource, Static, Symbols, + self as exp, Constructor, FunctionExportKind, Guest, OptionKind, ResultRecord, ReturnStyle, + Static, Symbols, }, num_bigint::BigUint, once_cell::sync::OnceCell, pyo3::{ + Bound, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, exceptions::PyAssertionError, - ffi, intern, + intern, types::{ - PyAnyMethods, PyBool, PyBytes, PyBytesMethods, PyDict, PyFloat, PyInt, PyList, - PyListMethods, PyMapping, PyMappingMethods, PyModule, PyModuleMethods, PyString, - PyTuple, + PyAnyMethods, PyBool, PyBytes, PyBytesMethods, PyDict, PyList, PyListMethods, + PyMapping, PyMappingMethods, PyModule, PyModuleMethods, PyString, PyTuple, }, - Borrowed, Bound, IntoPyObject, Py, PyAny, PyErr, PyResult, Python, }, std::{ alloc::{self, Layout}, - ffi::c_void, iter, - mem::{self, MaybeUninit}, + marker::PhantomData, + mem, ops::DerefMut, - ptr, slice, str, + slice, str, sync::{Mutex, Once}, }, wasi::cli::environment, + wit_dylib_ffi::{ + self as wit, Call, ExportFunction, Interpreter, List, Type, Wit, WitOption, WitResult, + }, }; wit_bindgen::generate!({ @@ -49,9 +57,17 @@ wit_bindgen::generate!({ export!(MyExports); +static WIT: OnceCell = OnceCell::new(); static STUB_WASI: OnceCell = OnceCell::new(); static EXPORTS: OnceCell> = OnceCell::new(); -static TYPES: OnceCell> = OnceCell::new(); +static RESOURCES: OnceCell> = OnceCell::new(); +static RECORDS: OnceCell> = OnceCell::new(); +static FLAGS: OnceCell> = OnceCell::new(); +static TUPLES: OnceCell> = OnceCell::new(); +static VARIANTS: OnceCell> = OnceCell::new(); +static ENUMS: OnceCell> = OnceCell::new(); +static OPTIONS: OnceCell> = OnceCell::new(); +static RESULTS: OnceCell> = OnceCell::new(); static ENVIRON: OnceCell> = OnceCell::new(); static SOME_CONSTRUCTOR: OnceCell> = OnceCell::new(); static OK_CONSTRUCTOR: OnceCell> = OnceCell::new(); @@ -62,15 +78,13 @@ static SEED: OnceCell> = OnceCell::new(); static ARGV: OnceCell> = OnceCell::new(); struct Borrow { - handle: i32, - drop: u32, + value: Py, + handle: u32, + drop: unsafe extern "C" fn(u32), } static BORROWS: Mutex> = Mutex::new(Vec::new()); -const DISCRIMINANT_FIELD_INDEX: i32 = 0; -const PAYLOAD_FIELD_INDEX: i32 = 1; - #[derive(Debug)] struct Case { constructor: Py, @@ -78,38 +92,47 @@ struct Case { } #[derive(Debug)] -enum Type { - Record { - constructor: Py, - fields: Vec, - }, - Variant { - types_to_discriminants: Py, - cases: Vec, - }, - Enum { - constructor: Py, - count: usize, - }, - Flags { - constructor: Py, - u32_count: usize, - }, - Option, - NestingOption, - Result, - Tuple(usize), - Handle, - Resource { - constructor: Py, - local: Option, - #[allow(dead_code)] - remote: Option, - }, +struct Resource { + constructor: Py, +} + +#[derive(Debug)] +struct Record { + constructor: Py, + fields: Vec, +} + +#[derive(Debug)] +struct Flags { + constructor: Py, + u32_count: usize, +} + +#[derive(Debug)] +struct Tuple { + count: usize, +} + +#[derive(Debug)] +struct Variant { + types_to_discriminants: Py, + cases: Vec, +} + +#[derive(Debug)] +struct Enum { + constructor: Py, + count: usize, } #[derive(Debug)] -enum Export { +struct Export { + kind: ExportKind, + return_style: ReturnStyle, +} + +#[derive(Debug)] +enum ExportKind { Freestanding { instance: Py, name: Py, @@ -136,55 +159,36 @@ impl From for Anyhow { } } -#[link(wasm_import_module = "env")] -extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "componentize-py#CallIndirect")] - fn componentize_py_call_indirect( - context: *const c_void, - input: *const c_void, - output: *mut c_void, - index: u32, - ); -} - #[pyo3::pyfunction] #[pyo3(pass_module)] fn call_import<'a>( module: Bound<'a, PyModule>, index: u32, params: Vec>, - result_count: usize, + _result_count: usize, ) -> PyResult>> { - let mut results = iter::repeat_with(MaybeUninit::>::uninit) - .take(result_count) - .collect::>(); - unsafe { - componentize_py_call_indirect( - &module.py() as *const _ as _, - params.as_ptr() as _, - results.as_mut_ptr() as _, - index, - ); - - // todo: is this sound, or do we need to `.into_iter().map(MaybeUninit::assume_init).collect()` instead? - Ok(mem::transmute::< - Vec>>, - Vec>, - >(results)) + let func = WIT + .get() + .unwrap() + .import_func(usize::try_from(index).unwrap()); + + if func.is_async() { + todo!() + } else { + let mut call = MyCall::new(params.into_iter().rev().map(|v| v.unbind()).collect()); + func.call_import_sync(&mut call); + Ok(mem::take(&mut call.stack) + .into_iter() + .map(|v| v.into_bound(module.py())) + .collect()) } } #[pyo3::pyfunction] #[pyo3(pass_module)] -fn drop_resource(module: &Bound, index: u32, handle: usize) -> PyResult<()> { - let params = [handle]; +fn drop_resource(_module: &Bound, index: usize, handle: u32) -> PyResult<()> { unsafe { - componentize_py_call_indirect( - &module.py() as *const _ as _, - params.as_ptr() as _, - ptr::null_mut(), - index, - ); + mem::transmute::(index)(handle); } Ok(()) } @@ -218,131 +222,167 @@ fn do_init(app_name: String, symbols: Symbols, stub_wasi: bool) -> Result<(), St .exports .iter() .map(|export| { - Ok(match export { - FunctionExport::Bundled(Bundled { - module, - protocol, - name, - }) => Export::Freestanding { - name: PyString::intern(py, name).into(), - instance: py - .import(module.as_str())? - .getattr(protocol.as_str())? - .call0()? - .into(), - }, - FunctionExport::Freestanding(Function { protocol, name }) => { - Export::Freestanding { + Ok(Export { + kind: match &export.kind { + FunctionExportKind::Freestanding(exp::Function { + protocol, + name, + }) => ExportKind::Freestanding { name: PyString::intern(py, name).into(), instance: app.getattr(protocol.as_str())?.call0()?.into(), - } - } - FunctionExport::Constructor(Constructor { module, protocol }) => { - Export::Constructor( + }, + FunctionExportKind::Constructor(Constructor { + module, + protocol, + }) => ExportKind::Constructor( py.import(module.as_str())? .getattr(protocol.as_str())? .into(), - ) - } - FunctionExport::Method(name) => { - Export::Method(PyString::intern(py, name).into()) - } - FunctionExport::Static(Static { - module, - protocol, - name, - }) => Export::Static { - name: PyString::intern(py, name).into(), - class: py - .import(module.as_str())? - .getattr(protocol.as_str())? - .into(), + ), + FunctionExportKind::Method(name) => { + ExportKind::Method(PyString::intern(py, name).into()) + } + FunctionExportKind::Static(Static { + module, + protocol, + name, + }) => ExportKind::Static { + name: PyString::intern(py, name).into(), + class: py + .import(module.as_str())? + .getattr(protocol.as_str())? + .into(), + }, }, + return_style: export.return_style, }) }) .collect::>()?, ) .unwrap(); - TYPES + RESOURCES .set( symbols - .types + .resources .into_iter() .map(|ty| { - Ok(match ty { - exp::Type::Owned(OwnedType { - kind, - package, - name, - }) => match kind { - OwnedKind::Record(fields) => Type::Record { - constructor: py - .import(package.as_str())? - .getattr(name.as_str())? - .into(), - fields, - }, - OwnedKind::Variant(cases) => { - let package = py.import(package.as_str())?; - - let cases = cases - .iter() - .map(|case| { - Ok(Case { - constructor: package - .getattr(case.name.as_str())? - .into(), - has_payload: case.has_payload, - }) - }) - .collect::>>()?; - - let types_to_discriminants = PyDict::new(py); - for (index, case) in cases.iter().enumerate() { - types_to_discriminants - .set_item(&case.constructor, index)?; - } - - Type::Variant { - cases, - types_to_discriminants: types_to_discriminants.into(), - } - } - OwnedKind::Enum(count) => Type::Enum { - constructor: py - .import(package.as_str())? - .getattr(name.as_str())? - .into(), - count: count.try_into().unwrap(), - }, - OwnedKind::Flags(u32_count) => Type::Flags { - constructor: py - .import(package.as_str())? - .getattr(name.as_str())? - .into(), - u32_count: u32_count.try_into().unwrap(), - }, - OwnedKind::Resource(Resource { local, remote }) => Type::Resource { - constructor: py - .import(package.as_str())? - .getattr(name.as_str())? - .into(), - local, - remote, - }, - }, - exp::Type::Option => Type::Option, - exp::Type::NestingOption => Type::NestingOption, - exp::Type::Result => Type::Result, - exp::Type::Tuple(length) => Type::Tuple(length.try_into().unwrap()), - exp::Type::Handle => Type::Handle, + Ok(Resource { + constructor: py + .import(ty.package.as_str())? + .getattr(ty.name.as_str())? + .into(), }) }) .collect::>()?, ) .unwrap(); + RECORDS + .set( + symbols + .records + .into_iter() + .map(|ty| { + Ok(Record { + constructor: py + .import(ty.package.as_str())? + .getattr(ty.name.as_str())? + .into(), + fields: ty.fields, + }) + }) + .collect::>()?, + ) + .unwrap(); + + FLAGS + .set( + symbols + .flags + .into_iter() + .map(|ty| { + Ok(Flags { + constructor: py + .import(ty.package.as_str())? + .getattr(ty.name.as_str())? + .into(), + u32_count: ty.u32_count.try_into().unwrap(), + }) + }) + .collect::>()?, + ) + .unwrap(); + + TUPLES + .set( + symbols + .tuples + .into_iter() + .map(|ty| { + Ok(Tuple { + count: ty.count.try_into().unwrap(), + }) + }) + .collect::>()?, + ) + .unwrap(); + + VARIANTS + .set( + symbols + .variants + .into_iter() + .map(|ty| { + let package = py.import(ty.package.as_str())?; + + let cases = ty + .cases + .iter() + .map(|case| { + Ok(Case { + constructor: package.getattr(case.name.as_str())?.into(), + has_payload: case.has_payload, + }) + }) + .collect::>>()?; + + let types_to_discriminants = PyDict::new(py); + for (index, case) in cases.iter().enumerate() { + types_to_discriminants.set_item(&case.constructor, index)?; + } + + Ok(Variant { + cases, + types_to_discriminants: types_to_discriminants.into(), + }) + }) + .collect::>()?, + ) + .unwrap(); + + ENUMS + .set( + symbols + .enums + .into_iter() + .map(|ty| { + Ok(Enum { + constructor: py + .import(ty.package.as_str())? + .getattr(ty.name.as_str())? + .into(), + count: ty.count.try_into().unwrap(), + }) + }) + .collect::>()?, + ) + .unwrap(); + + OPTIONS.set(symbols.options).unwrap(); + + RESULTS.set(symbols.results).unwrap(); + let types = py.import(symbols.types_package.as_str())?; SOME_CONSTRUCTOR.set(types.getattr("Some")?.into()).unwrap(); @@ -427,797 +467,857 @@ impl Guest for MyExports { } } -/// # Safety -/// TODO -#[export_name = "componentize-py#Dispatch"] -pub unsafe extern "C" fn componentize_py_dispatch( - export: usize, - from_canon: u32, - to_canon: u32, - param_count: u32, - return_style: ReturnStyle, - params_canon: *const c_void, - results_canon: *mut c_void, -) { - Python::attach(|py| { - let mut params_py = vec![ptr::null_mut::(); param_count.try_into().unwrap()]; - - componentize_py_call_indirect( - &py as *const _ as _, - params_canon, - params_py.as_mut_ptr() as _, - from_canon, - ); - - let mut params_py = params_py - .into_iter() - .map(|p| Bound::from_borrowed_ptr(py, p)); - - if !*STUB_WASI.get().unwrap() { - static ONCE: Once = Once::new(); - ONCE.call_once(|| { - // We must call directly into the host to get the runtime environment since libc's version will only - // contain the build-time pre-init snapshot. - let environ = ENVIRON.get().unwrap().bind(py); - for (k, v) in environment::get_environment() { - environ.set_item(k, v).unwrap(); - } +struct MyInterpreter; - // Likewise for CLI arguments. - for arg in environment::get_arguments() { - ARGV.get().unwrap().bind(py).append(arg).unwrap(); - } +impl Interpreter for MyInterpreter { + type CallCx<'a> = MyCall<'a>; - // Call `random.seed()` to ensure we get a fresh seed rather than the one that got baked in during - // pre-init. - SEED.get().unwrap().call0(py).unwrap(); - }); - } + fn initialize(wit: Wit) { + WIT.set(wit).map_err(drop).unwrap(); + } - let export = &EXPORTS.get().unwrap()[export]; - let result = match export { - Export::Freestanding { instance, name } => { - instance.call_method1(py, name, PyTuple::new(py, params_py).unwrap()) + fn export_start<'a>(_: Wit, _: ExportFunction) -> Box> { + Box::new(MyCall::new(Vec::new())) + } + + fn export_call(_: Wit, func: ExportFunction, cx: &mut MyCall<'_>) { + Python::attach(|py| { + if !*STUB_WASI.get().unwrap() { + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + // We must call directly into the host to get the runtime + // environment since libc's version will only contain the + // build-time pre-init snapshot. + let environ = ENVIRON.get().unwrap().bind(py); + for (k, v) in environment::get_environment() { + environ.set_item(k, v).unwrap(); + } + + // Likewise for CLI arguments. + for arg in environment::get_arguments() { + ARGV.get().unwrap().bind(py).append(arg).unwrap(); + } + + // Call `random.seed()` to ensure we get a fresh seed rather + // than the one that got baked in during pre-init. + SEED.get().unwrap().call0(py).unwrap(); + }); } - Export::Constructor(class) => class.call1(py, PyTuple::new(py, params_py).unwrap()), - Export::Method(name) => params_py - // Call method on self with remaining iterator elements - .next() - .unwrap() - .call_method1(name, PyTuple::new(py, params_py).unwrap()) - .map(|r| r.into()), - Export::Static { class, name } => class - .getattr(py, name) - .and_then(|function| function.call1(py, PyTuple::new(py, params_py).unwrap())), - }; - let result = match return_style { - ReturnStyle::Normal => match result { - Ok(result) => result, - Err(error) => { + let mut params_py = mem::take(&mut cx.stack).into_iter(); + let export = &EXPORTS.get().unwrap()[func.index()]; + let result = match &export.kind { + ExportKind::Freestanding { instance, name } => { + instance.call_method1(py, name, PyTuple::new(py, params_py).unwrap()) + } + ExportKind::Constructor(class) => { + class.call1(py, PyTuple::new(py, params_py).unwrap()) + } + ExportKind::Method(name) => params_py + // Call method on self with remaining iterator elements + .next() + .unwrap() + .call_method1(py, name, PyTuple::new(py, params_py).unwrap()) + .map(|r| r.into()), + ExportKind::Static { class, name } => class + .getattr(py, name) + .and_then(|function| function.call1(py, PyTuple::new(py, params_py).unwrap())), + }; + + let result = match (result, export.return_style) { + (Ok(_), ReturnStyle::None) => None, + (Ok(result), ReturnStyle::Normal) => Some(result), + (Ok(result), ReturnStyle::Result) => { + Some(OK_CONSTRUCTOR.get().unwrap().call1(py, (result,)).unwrap()) + } + (Err(error), ReturnStyle::None | ReturnStyle::Normal) => { error.print(py); panic!("Python function threw an unexpected exception") } - }, - ReturnStyle::Result => match result { - Ok(result) => OK_CONSTRUCTOR.get().unwrap().call1(py, (result,)).unwrap(), - Err(result) => { + (Err(error), ReturnStyle::Result) => { if ERR_CONSTRUCTOR .get() .unwrap() .bind(py) - .eq(result.get_type(py)) + .eq(error.get_type(py)) .unwrap() { - result.into_value(py).into_any() + Some(error.into_value(py).into_any()) } else { - result.print(py); + error.print(py); panic!("Python function threw an unexpected exception") } } - }, - }; + }; - let result_array = [result]; + if let Some(result) = result { + cx.stack.push(result); + } - componentize_py_call_indirect( - &py as *const _ as _, - result_array.as_ptr() as *const _ as _, - results_canon, - to_canon, - ); + let borrows = mem::take(BORROWS.lock().unwrap().deref_mut()); + for Borrow { + value, + handle, + drop, + } in borrows + { + let value = value.bind(py); - let borrows = mem::take(BORROWS.lock().unwrap().deref_mut()); - for Borrow { handle, drop } in borrows { - let params = [handle]; - unsafe { - componentize_py_call_indirect( - &py as *const _ as _, - params.as_ptr() as _, - ptr::null_mut(), - drop, - ); + value.delattr(intern!(py, "handle")).unwrap(); + + value + .getattr(intern!(py, "finalizer")) + .unwrap() + .call_method0(intern!(py, "detach")) + .unwrap(); + + unsafe { + drop(handle); + } } - } - }); -} + }); + } -/// # Safety -/// TODO -#[export_name = "componentize-py#Allocate"] -pub unsafe extern "C" fn componentize_py_allocate(size: usize, align: usize) -> *mut u8 { - alloc::alloc(Layout::from_size_align(size, align).unwrap()) + async fn export_call_async(_: Wit, func: ExportFunction, cx: Box>) { + _ = (func, cx); + todo!() + } + + fn resource_dtor(ty: wit::Resource, handle: usize) { + _ = (ty, handle); + todo!() + } } -/// # Safety -/// TODO -#[export_name = "componentize-py#Free"] -pub unsafe extern "C" fn componentize_py_free(ptr: *mut u8, size: usize, align: usize) { - alloc::dealloc(ptr, Layout::from_size_align(size, align).unwrap()) +struct MyCall<'a> { + _phantom: PhantomData<&'a ()>, + iter_stack: Vec, + deferred_deallocations: Vec<(*mut u8, Layout)>, + strings: Vec, + stack: Vec>, } -#[export_name = "componentize-py#ToCanonBool"] -pub extern "C" fn componentize_py_to_canon_bool(_py: &Python, value: Borrowed) -> u32 { - if value.is_truthy().unwrap() { - 1 - } else { - 0 +impl MyCall<'_> { + fn new(stack: Vec>) -> Self { + // TODO: tell py03 to attach (and detach on drop) this thread to the + // interpreter. + Self { + _phantom: PhantomData, + iter_stack: Vec::new(), + deferred_deallocations: Vec::new(), + strings: Vec::new(), + stack, + } } } -#[export_name = "componentize-py#ToCanonI32"] -pub extern "C" fn componentize_py_to_canon_i32(_py: &Python, value: Borrowed) -> i32 { - value.extract().unwrap() +impl Drop for MyCall<'_> { + fn drop(&mut self) { + for &(ptr, layout) in &self.deferred_deallocations { + unsafe { + alloc::dealloc(ptr, layout); + } + } + } } -#[export_name = "componentize-py#ToCanonU32"] -pub extern "C" fn componentize_py_to_canon_u32(_py: &Python, value: Borrowed) -> u32 { - value.extract().unwrap() -} +impl Call for MyCall<'_> { + unsafe fn defer_deallocate(&mut self, ptr: *mut u8, layout: Layout) { + self.deferred_deallocations.push((ptr, layout)); + } -#[export_name = "componentize-py#ToCanonI64"] -pub extern "C" fn componentize_py_to_canon_i64(_py: &Python, value: Borrowed) -> i64 { - value.extract().unwrap() -} + fn pop_u8(&mut self) -> u8 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } -#[export_name = "componentize-py#ToCanonU64"] -pub extern "C" fn componentize_py_to_canon_u64(_py: &Python, value: Borrowed) -> u64 { - value.extract().unwrap() -} + fn pop_u16(&mut self) -> u16 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } -#[export_name = "componentize-py#ToCanonF32"] -pub extern "C" fn componentize_py_to_canon_f32(_py: &Python, value: Borrowed) -> f32 { - value.extract().unwrap() -} + fn pop_u32(&mut self) -> u32 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } -#[export_name = "componentize-py#ToCanonF64"] -pub extern "C" fn componentize_py_to_canon_f64(_py: &Python, value: Borrowed) -> f64 { - value.extract().unwrap() -} + fn pop_u64(&mut self) -> u64 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } -#[export_name = "componentize-py#ToCanonChar"] -pub extern "C" fn componentize_py_to_canon_char(_py: &Python, value: Borrowed) -> u32 { - let value = value.extract::().unwrap(); - assert!(value.chars().count() == 1); - value.chars().next().unwrap() as u32 -} + fn pop_s8(&mut self) -> i8 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } -/// # Safety -/// TODO -#[export_name = "componentize-py#ToCanonString"] -pub unsafe extern "C" fn componentize_py_to_canon_string( - _py: &Python, - value: Borrowed, - destination: *mut (*const u8, usize), -) { - let value = value.extract::().unwrap().into_bytes(); - unsafe { - let result = alloc::alloc(Layout::from_size_align(value.len(), 1).unwrap()); - ptr::copy_nonoverlapping(value.as_ptr(), result, value.len()); - destination.write((result, value.len())); + fn pop_s16(&mut self) -> i16 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) } -} -#[export_name = "componentize-py#GetField"] -pub extern "C" fn componentize_py_get_field<'a>( - py: &'a Python, - value: Borrowed<'_, 'a, PyAny>, - ty: usize, - field: usize, -) -> Bound<'a, PyAny> { - match &TYPES.get().unwrap()[ty] { - Type::Record { fields, .. } => value.getattr(fields[field].as_str()).unwrap(), - Type::Variant { - types_to_discriminants, - cases, - } => { - let discriminant = types_to_discriminants - .bind(*py) - .get_item(value.get_type()) - .unwrap(); + fn pop_s32(&mut self) -> i32 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } - match i32::try_from(field).unwrap() { - DISCRIMINANT_FIELD_INDEX => discriminant, - PAYLOAD_FIELD_INDEX => { - if cases[discriminant.extract::().unwrap()].has_payload { - value.getattr("value").unwrap() - } else { - py.None().into_bound(*py) - } - } - _ => unreachable!(), + fn pop_s64(&mut self) -> i64 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } + + fn pop_bool(&mut self) -> bool { + Python::attach(|py| self.stack.pop().unwrap().is_truthy(py).unwrap()) + } + + fn pop_char(&mut self) -> char { + let value = Python::attach(|py| self.stack.pop().unwrap().extract::(py).unwrap()); + assert!(value.chars().count() == 1); + value.chars().next().unwrap() + } + + fn pop_f32(&mut self) -> f32 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } + + fn pop_f64(&mut self) -> f64 { + Python::attach(|py| self.stack.pop().unwrap().extract(py).unwrap()) + } + + fn pop_string(&mut self) -> &str { + let value = Python::attach(|py| self.stack.pop().unwrap().extract::(py).unwrap()); + self.strings.push(value); + self.strings.last().unwrap() + } + + fn pop_borrow(&mut self, ty: wit::Resource) -> u32 { + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + if let Some(new) = ty.new() { + // exported resource type + exported_resource_to_canon(py, ty, new, value) + } else { + // imported resource type + imported_resource_to_canon(py, value) } - } - Type::Enum { .. } => match i32::try_from(field).unwrap() { - DISCRIMINANT_FIELD_INDEX => value.getattr("value").unwrap(), - PAYLOAD_FIELD_INDEX => py.None().into_bound(*py), - _ => unreachable!(), - }, - Type::Flags { u32_count, .. } => { - assert!(field < *u32_count); - let value = value - .getattr("value") + }) + } + + fn pop_own(&mut self, ty: wit::Resource) -> u32 { + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + if let Some(new) = ty.new() { + // exported resource type + exported_resource_to_canon(py, ty, new, value) + } else { + // imported resource type + value + .bind(py) + .getattr(intern!(py, "finalizer")) + .unwrap() + .call_method0(intern!(py, "detach")) + .unwrap(); + + imported_resource_to_canon(py, value) + } + }) + } + + fn pop_enum(&mut self, _ty: wit::Enum) -> u32 { + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + let value = value.bind(py); + value + .getattr(intern!(py, "value")) .unwrap() - .extract::() + .extract() .unwrap() - .iter_u32_digits() - .nth(field) - .unwrap_or(0); + }) + } - u32::cast_signed(value) - .into_pyobject(*py) - .unwrap() - .into_any() - } - Type::Option => match i32::try_from(field).unwrap() { - DISCRIMINANT_FIELD_INDEX => if value.is_none() { 0u32 } else { 1 } - .into_pyobject(*py) + fn pop_flags(&mut self, _ty: wit::Flags) -> u32 { + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + let value = value.bind(py); + // See the comment in `Self::push_flags` about using `num-bigint` + // here to represent arbitrary bit lengths. + value + .getattr(intern!(py, "value")) .unwrap() - .into_any(), - PAYLOAD_FIELD_INDEX => unsafe { - // Avoid incrementing `value`'s refcount while returning it: - Bound::from_owned_ptr(*py, value.as_ptr()) - }, - _ => unreachable!(), - }, - Type::NestingOption => match i32::try_from(field).unwrap() { - DISCRIMINANT_FIELD_INDEX => if value.is_none() { 0u32 } else { 1 } - .into_pyobject(*py) + .extract::() .unwrap() - .into_any(), - PAYLOAD_FIELD_INDEX => { - if value.is_none() { - unsafe { - // Avoid incrementing `value`'s refcount while returning - // it: - Bound::from_owned_ptr(*py, value.as_ptr()) + .iter_u32_digits() + .next() + .unwrap_or(0) + }) + } + + fn pop_future(&mut self, ty: wit::Future) -> u32 { + _ = ty; + todo!() + } + + fn pop_stream(&mut self, ty: wit::Stream) -> u32 { + _ = ty; + todo!() + } + + fn pop_option(&mut self, ty: WitOption) -> u32 { + Python::attach(|py| { + if self.stack.last().unwrap().is_none(py) { + self.stack.pop().unwrap(); + 0 + } else { + match &OPTIONS.get().unwrap()[ty.index()] { + OptionKind::NonNesting => { + // Leave value on the stack as-is + } + OptionKind::Nesting => { + let value = self.stack.pop().unwrap(); + self.stack + .push(value.getattr(py, intern!(py, "value")).unwrap()); } - } else { - value.getattr("value").unwrap() } + 1 } - _ => unreachable!(), - }, - Type::Result => match i32::try_from(field).unwrap() { - DISCRIMINANT_FIELD_INDEX => if OK_CONSTRUCTOR + }) + } + + fn pop_result(&mut self, ty: WitResult) -> u32 { + let &ResultRecord { has_ok, has_err } = &RESULTS.get().unwrap()[ty.index()]; + + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + let value = value.bind(py); + + let (discriminant, has_payload) = if OK_CONSTRUCTOR .get() .unwrap() - .bind(*py) + .bind(py) .eq(value.get_type()) .unwrap() { - 0_i32 + (0, has_ok) } else if ERR_CONSTRUCTOR .get() .unwrap() - .bind(*py) + .bind(py) .eq(value.get_type()) .unwrap() { - 1 + (1, has_err) } else { unreachable!() + }; + + if has_payload { + self.stack + .push(value.getattr(intern!(py, "value")).unwrap().unbind()); } - .into_pyobject(*py) - .unwrap() - .into_any(), - PAYLOAD_FIELD_INDEX => value.getattr("value").unwrap(), - _ => unreachable!(), - }, - Type::Tuple(length) => { - assert!(field < *length); - value - .downcast::() - .unwrap() - .get_item(field) + + discriminant + }) + } + + fn pop_variant(&mut self, ty: wit::Variant) -> u32 { + let Variant { + types_to_discriminants, + cases, + } = &VARIANTS.get().unwrap()[ty.index()]; + + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + let value = value.bind(py); + + let discriminant = types_to_discriminants + .bind(py) + .get_item(value.get_type()) .unwrap() + .extract::() + .unwrap(); + + if cases[discriminant].has_payload { + self.stack + .push(value.getattr(intern!(py, "value")).unwrap().unbind()) + } + + u32::try_from(discriminant).unwrap() + }) + } + + fn pop_record(&mut self, ty: wit::Record) { + let Record { fields, .. } = &RECORDS.get().unwrap()[ty.index()]; + + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + self.stack.extend( + fields + .iter() + .rev() + .map(|name| value.getattr(py, name.as_str()).unwrap()), + ); + }); + } + + fn pop_tuple(&mut self, ty: wit::Tuple) { + let &Tuple { count } = &TUPLES.get().unwrap()[ty.index()]; + + Python::attach(|py| { + let value = self.stack.pop().unwrap(); + let value = value.cast_bound::(py).unwrap(); + + self.stack.extend( + (0..count) + .rev() + .map(|index| value.get_item(index).unwrap().unbind()), + ); + }); + } + + unsafe fn maybe_pop_list(&mut self, ty: List) -> Option<(*const u8, usize)> { + if let Type::U8 = ty.ty() { + Python::attach(|py| { + let src = self.stack.pop().unwrap(); + let src = src.cast_bound::(py).unwrap(); + let len = src.len().unwrap(); + let dst = alloc::alloc(Layout::from_size_align(len, 1).unwrap()); + slice::from_raw_parts_mut(dst, len).copy_from_slice(src.as_bytes()); + Some((dst as _, len)) + }) + } else { + None } - Type::Handle | Type::Resource { .. } => unreachable!(), } -} -#[export_name = "componentize-py#GetListLength"] -pub extern "C" fn componentize_py_get_list_length(_py: &Python, value: Borrowed) -> usize { - if let Ok(bytes) = value.downcast::() { - bytes.len().unwrap() - } else { - value.downcast::().unwrap().len() + fn pop_list(&mut self, _ty: List) -> usize { + Python::attach(|py| { + self.iter_stack.push(0); + let value = self.stack.last().unwrap(); + value.cast_bound::(py).unwrap().len() + }) } -} -#[export_name = "componentize-py#GetListElement"] -pub extern "C" fn componentize_py_get_list_element<'a>( - _py: &Python<'a>, - value: Borrowed<'_, 'a, PyAny>, - index: usize, -) -> Bound<'a, PyAny> { - value.downcast::().unwrap().get_item(index).unwrap() -} + fn pop_iter_next(&mut self, _ty: List) { + Python::attach(|py| { + let index = *self.iter_stack.last().unwrap(); + let element = self + .stack + .last() + .unwrap() + .cast_bound::(py) + .unwrap() + .get_item(index) + .unwrap(); + *self.iter_stack.last_mut().unwrap() = index + 1; + self.stack.push(element.into_any().unbind()); + }) + } -#[export_name = "componentize-py#FromCanonBool"] -pub extern "C" fn componentize_py_from_canon_bool<'a>( - py: &Python<'a>, - value: u32, -) -> Bound<'a, PyBool> { - PyBool::new(*py, value != 0).to_owned() -} + fn pop_iter(&mut self, _ty: List) { + self.iter_stack.pop().unwrap(); + Python::attach(|py| { + self.stack.pop().unwrap().drop_ref(py); + }) + } -#[export_name = "componentize-py#FromCanonI32"] -pub extern "C" fn componentize_py_from_canon_i32<'a>( - py: &Python<'a>, - value: i32, -) -> Bound<'a, PyInt> { - value.into_pyobject(*py).unwrap() -} + fn push_bool(&mut self, val: bool) { + self.stack.push(Python::attach(|py| { + PyBool::new(py, val).to_owned().into_any().unbind() + })) + } -#[export_name = "componentize-py#FromCanonU32"] -pub extern "C" fn componentize_py_from_canon_u32<'a>( - py: &Python<'a>, - value: u32, -) -> Bound<'a, PyInt> { - value.into_pyobject(*py).unwrap() -} + fn push_char(&mut self, val: char) { + self.stack.push(Python::attach(|py| { + val.to_string() + .into_pyobject(py) + .unwrap() + .into_any() + .unbind() + })) + } -#[export_name = "componentize-py#FromCanonI64"] -pub extern "C" fn componentize_py_from_canon_i64<'a>( - py: &Python<'a>, - value: i64, -) -> Bound<'a, PyInt> { - value.into_pyobject(*py).unwrap() -} + fn push_u8(&mut self, val: u8) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } -#[export_name = "componentize-py#FromCanonU64"] -pub extern "C" fn componentize_py_from_canon_u64<'a>( - py: &Python<'a>, - value: u64, -) -> Bound<'a, PyInt> { - value.into_pyobject(*py).unwrap() -} + fn push_s8(&mut self, val: i8) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } -#[export_name = "componentize-py#FromCanonF32"] -pub extern "C" fn componentize_py_from_canon_f32<'a>( - py: &Python<'a>, - value: f32, -) -> Bound<'a, PyFloat> { - value.into_pyobject(*py).unwrap() -} + fn push_u16(&mut self, val: u16) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } -#[export_name = "componentize-py#FromCanonF64"] -pub extern "C" fn componentize_py_from_canon_f64<'a>( - py: &Python<'a>, - value: f64, -) -> Bound<'a, PyFloat> { - value.into_pyobject(*py).unwrap() -} + fn push_s16(&mut self, val: i16) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } -#[export_name = "componentize-py#FromCanonChar"] -pub extern "C" fn componentize_py_from_canon_char<'a>( - py: &Python<'a>, - value: u32, -) -> Bound<'a, PyString> { - char::from_u32(value) - .unwrap() - .to_string() - .into_pyobject(*py) - .unwrap() -} + fn push_u32(&mut self, val: u32) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })); + } -/// # Safety -/// TODO -#[export_name = "componentize-py#FromCanonString"] -pub unsafe extern "C" fn componentize_py_from_canon_string<'a>( - py: &Python<'a>, - data: *const u8, - len: usize, -) -> Bound<'a, PyString> { - PyString::new(*py, unsafe { - str::from_utf8_unchecked(slice::from_raw_parts(data, len)) - }) -} + fn push_s32(&mut self, val: i32) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } -/// # Safety -/// TODO -#[export_name = "componentize-py#Init"] -pub unsafe extern "C" fn componentize_py_init<'a>( - py: &Python<'a>, - ty: usize, - data: *const *mut ffi::PyObject, - len: usize, -) -> Bound<'a, PyAny> { - match &TYPES.get().unwrap()[ty] { - Type::Record { constructor, .. } => { - let elements = slice::from_raw_parts(data, len) - .iter() - .map(|e| Bound::from_owned_ptr(*py, *e)); + fn push_u64(&mut self, val: u64) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } + + fn push_s64(&mut self, val: i64) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } + + fn push_f32(&mut self, val: f32) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } + + fn push_f64(&mut self, val: f64) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } + + fn push_string(&mut self, val: String) { + self.stack.push(Python::attach(|py| { + val.into_pyobject(py).unwrap().into_any().unbind() + })) + } + + fn push_record(&mut self, ty: wit::Record) { + let Record { + constructor, + fields, + } = &RECORDS.get().unwrap()[ty.index()]; + + let elements = self + .stack + .drain(self.stack.len().checked_sub(fields.len()).unwrap()..); + + let result = Python::attach(|py| { constructor - .call1(*py, PyTuple::new(*py, elements).unwrap()) + .call1(py, PyTuple::new(py, elements).unwrap()) .unwrap() - .into_bound(*py) - } - Type::Variant { cases, .. } => { - assert!(len == 2); - let discriminant = Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(DISCRIMINANT_FIELD_INDEX).unwrap())), - ) - .extract::() - .unwrap(); - let case = &cases[usize::try_from(discriminant).unwrap()]; - if case.has_payload { - case.constructor.call1( - *py, - (Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(PAYLOAD_FIELD_INDEX).unwrap())), - ),), - ) - } else { - case.constructor.call1(*py, ()) - } - .unwrap() - .into_bound(*py) - } - Type::Enum { constructor, count } => { - assert!(len == 2); - let discriminant = Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(DISCRIMINANT_FIELD_INDEX).unwrap())), - ) - .extract::() - .unwrap(); - assert!(discriminant < *count); - constructor - .call1(*py, (discriminant,)) + }); + + self.stack.push(result); + } + + fn push_tuple(&mut self, ty: wit::Tuple) { + let &Tuple { count } = &TUPLES.get().unwrap()[ty.index()]; + + let elements = self + .stack + .drain(self.stack.len().checked_sub(count).unwrap()..); + + let result = Python::attach(|py| { + PyTuple::new(py, elements) .unwrap() - .into_bound(*py) - } - Type::Flags { + .to_owned() + .into_any() + .unbind() + }); + + self.stack.push(result); + } + + fn push_flags(&mut self, ty: wit::Flags, bits: u32) { + let Flags { constructor, u32_count, - } => { - assert!(len == *u32_count); + } = &FLAGS.get().unwrap()[ty.index()]; + + // Note that `componentize-py` was originally written when a component + // model `flags` type could have an arbitrary number of bits. Since + // then, the spec was updated to only allow up to 32 bits, but we still + // support arbitrary sizes here. See + // https://github.com/WebAssembly/component-model/issues/370 for + // details. + // + // TODO: If it's unlikely that the spec will ever support more than 32 + // bits, remove the `num-bigint` dependency and simplify the code here + // and in `summary.rs`. + let result = Python::attach(|py| { constructor .call1( - *py, + py, (BigUint::new( - slice::from_raw_parts(data, len) - .iter() - .map(|v| { - i32::cast_unsigned( - Bound::from_owned_ptr(*py, *v).extract().unwrap(), - ) - }) + iter::once(bits) + .chain(iter::repeat(0)) + .take(*u32_count) .collect(), ),), ) .unwrap() - .into_bound(*py) - } - Type::Option => { - assert!(len == 2); - let discriminant = Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(DISCRIMINANT_FIELD_INDEX).unwrap())), - ) - .extract::() - .unwrap(); - match discriminant { - 0 => py.None().into_bound(*py), - 1 => Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(PAYLOAD_FIELD_INDEX).unwrap())), - ), - _ => unreachable!(), - } - } - Type::NestingOption => { - assert!(len == 2); - let discriminant = Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(DISCRIMINANT_FIELD_INDEX).unwrap())), - ) - .extract::() - .unwrap(); + }); - match discriminant { - 0 => py.None().into_bound(*py), + self.stack.push(result); + } - 1 => SOME_CONSTRUCTOR - .get() - .unwrap() - .call1( - *py, - (Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(PAYLOAD_FIELD_INDEX).unwrap())), - ),), - ) + fn push_enum(&mut self, ty: wit::Enum, discriminant: u32) { + let &Enum { + ref constructor, + count, + } = &ENUMS.get().unwrap()[ty.index()]; + + assert!(usize::try_from(discriminant).unwrap() < count); + + let result = Python::attach(|py| constructor.call1(py, (discriminant,)).unwrap()); + + self.stack.push(result); + } + + fn push_borrow(&mut self, ty: wit::Resource, handle: u32) { + Python::attach(|py| { + self.stack.push(if ty.rep().is_some() { + // exported resource type + unsafe { Py::::from_borrowed_ptr(py, handle as usize as _) } + } else { + // imported resource type + let value = imported_resource_from_canon(py, ty, handle); + + BORROWS.lock().unwrap().push(Borrow { + value: value.clone_ref(py), + handle, + drop: ty.drop(), + }); + + value + }) + }) + } + + fn push_own(&mut self, ty: wit::Resource, handle: u32) { + Python::attach(|py| { + self.stack.push(if let Some(rep) = ty.rep() { + // exported resource type + let rep = unsafe { rep(handle) }; + let value = unsafe { Py::::from_borrowed_ptr(py, rep as _) }.into_bound(py); + + value + .delattr(intern!(py, "__componentize_py_handle")) + .unwrap(); + + value + .getattr(intern!(py, "finalizer")) .unwrap() - .into_bound(*py), + .call_method0(intern!(py, "detach")) + .unwrap(); - _ => unreachable!(), - } - } - Type::Result => { - assert!(len == 2); - let discriminant = Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(DISCRIMINANT_FIELD_INDEX).unwrap())), - ) - .extract::() - .unwrap(); + value.unbind() + } else { + // imported resource type + imported_resource_from_canon(py, ty, handle) + }); + }) + } - match discriminant { - 0 => OK_CONSTRUCTOR.get().unwrap(), - 1 => ERR_CONSTRUCTOR.get().unwrap(), - _ => unreachable!(), - } - .call1( - *py, - (Bound::from_owned_ptr( - *py, - ptr::read(data.offset(isize::try_from(PAYLOAD_FIELD_INDEX).unwrap())), - ),), - ) - .unwrap() - .into_bound(*py) - } - Type::Tuple(length) => { - assert!(*length == len); - let elements = slice::from_raw_parts(data, len) - .iter() - .map(|e| Bound::from_owned_ptr(*py, *e)); - PyTuple::new(*py, elements).unwrap().into_any() - } - Type::Handle | Type::Resource { .. } => unreachable!(), + fn push_future(&mut self, ty: wit::Future, handle: u32) { + _ = (ty, handle); + todo!() } -} -#[export_name = "componentize-py#MakeList"] -pub extern "C" fn componentize_py_make_list<'a>(py: &Python<'a>) -> Bound<'a, PyList> { - PyList::empty(*py) -} + fn push_stream(&mut self, ty: wit::Stream, handle: u32) { + _ = (ty, handle); + todo!() + } -#[export_name = "componentize-py#ListAppend"] -pub extern "C" fn componentize_py_list_append( - _py: &Python, - list: Borrowed, - element: Borrowed, -) { - list.append(element).unwrap(); -} + fn push_variant(&mut self, ty: wit::Variant, discriminant: u32) { + let Variant { cases, .. } = &VARIANTS.get().unwrap()[ty.index()]; -#[export_name = "componentize-py#None"] -pub extern "C" fn componentize_py_none(py: &Python) -> Py { - py.None() -} + let result = Python::attach(|py| { + let case = &cases[usize::try_from(discriminant).unwrap()]; + if case.has_payload { + let payload = self.stack.pop().unwrap(); + case.constructor.call1(py, (payload,)) + } else { + case.constructor.call1(py, ()) + } + .unwrap() + }); -/// # Safety -/// TODO -#[export_name = "componentize-py#GetBytes"] -pub unsafe extern "C" fn componentize_py_get_bytes( - _py: &Python, - src: Borrowed, - dst: *mut u8, - len: usize, -) { - assert_eq!(len, src.len().unwrap()); - slice::from_raw_parts_mut(dst, len).copy_from_slice(src.as_bytes()) -} + self.stack.push(result); + } -/// # Safety -/// TODO -#[export_name = "componentize-py#MakeBytes"] -pub unsafe extern "C" fn componentize_py_make_bytes<'a>( - py: &Python<'a>, - src: *const u8, - len: usize, -) -> Bound<'a, PyBytes> { - PyBytes::new(*py, slice::from_raw_parts(src, len)) -} + fn push_option(&mut self, ty: WitOption, is_some: bool) { + Python::attach(|py| { + if is_some { + match &OPTIONS.get().unwrap()[ty.index()] { + OptionKind::NonNesting => { + // Leave payload on the stack as-is. + } + OptionKind::Nesting => { + let payload = self.stack.pop().unwrap(); + self.stack.push( + SOME_CONSTRUCTOR + .get() + .unwrap() + .call1(py, (payload,)) + .unwrap(), + ); + } + } + } else { + self.stack.push(py.None()); + } + }); + } -#[export_name = "componentize-py#FromCanonHandle"] -pub extern "C" fn componentize_py_from_canon_handle<'a>( - py: &Python<'a>, - value: i32, - borrow: i32, - local: i32, - resource: i32, -) -> Bound<'a, PyAny> { - let ty = &TYPES.get().unwrap()[usize::try_from(resource).unwrap()]; - let Type::Resource { - constructor, - local: resource_local, - remote: resource_remote, - } = ty - else { - panic!("expected resource, found {ty:?}"); - }; + fn push_result(&mut self, ty: WitResult, is_err: bool) { + let &ResultRecord { has_ok, has_err } = &RESULTS.get().unwrap()[ty.index()]; - if local != 0 { - if borrow != 0 { - unsafe { Py::::from_borrowed_ptr(*py, value as usize as _) }.into_bound(*py) - } else { - let Some(LocalResource { rep, .. }) = resource_local else { - panic!("expected local resource, found {ty:?}"); + Python::attach(|py| { + let (constructor, has_payload) = if is_err { + (ERR_CONSTRUCTOR.get().unwrap(), has_err) + } else { + (OK_CONSTRUCTOR.get().unwrap(), has_ok) }; - let rep = { - let params = [value]; - let mut results = [MaybeUninit::::uninit()]; - unsafe { - componentize_py_call_indirect( - py as *const _ as _, - params.as_ptr() as _, - results.as_mut_ptr() as _, - *rep, - ); - results[0].assume_init() - } + let payload = if has_payload { + self.stack.pop().unwrap() + } else { + py.None() }; - let value = unsafe { Py::::from_borrowed_ptr(*py, rep as _) }.into_bound(*py); + self.stack.push(constructor.call1(py, (payload,)).unwrap()) + }); + } - value - .delattr(intern!(*py, "__componentize_py_handle")) - .unwrap(); + unsafe fn push_raw_list(&mut self, ty: List, src: *mut u8, len: usize) -> bool { + if let Type::U8 = ty.ty() { + self.stack.push(Python::attach(|py| { + let value = PyBytes::new(py, slice::from_raw_parts(src, len)) + .to_owned() + .into_any() + .unbind(); + alloc::dealloc(src, Layout::from_size_align(len, 1).unwrap()); + value + })); + true + } else { + false + } + } - value - .getattr(intern!(*py, "finalizer")) + fn push_list(&mut self, _ty: List, _capacity: usize) { + self.stack.push(Python::attach(|py| { + PyList::empty(py).to_owned().into_any().unbind() + })); + } + + fn list_append(&mut self, _ty: List) { + Python::attach(|py| { + let element = self.stack.pop().unwrap(); + self.stack + .last() .unwrap() - .call_method0(intern!(*py, "detach")) - .unwrap(); + .cast_bound::(py) + .unwrap() + .append(element) + .unwrap() + }); + } +} - value - } - } else { - let Some(RemoteResource { drop }) = resource_remote else { - panic!("expected remote resource, found {ty:?}"); - }; +fn imported_resource_from_canon(py: Python<'_>, ty: wit::Resource, handle: u32) -> Py { + let Resource { constructor } = &RESOURCES.get().unwrap()[ty.index()]; - if borrow != 0 { - BORROWS.lock().unwrap().push(Borrow { - handle: value, - drop: *drop, - }); - } + let instance = constructor + .call_method1( + py, + intern!(py, "__new__"), + PyTuple::new(py, [constructor]).unwrap(), + ) + .unwrap(); - let instance = constructor - .call_method1( - *py, - intern!(*py, "__new__"), - PyTuple::new(*py, [constructor]).unwrap(), - ) - .unwrap(); + let handle = handle.into_pyobject(py).unwrap(); + + instance + .setattr(py, intern!(py, "handle"), handle.as_borrowed()) + .unwrap(); + + let finalizer = FINALIZE + .get() + .unwrap() + .call1( + py, + ( + instance.clone_ref(py), + DROP_RESOURCE.get().unwrap(), + (ty.drop() as usize).into_pyobject(py).unwrap(), + handle, + ), + ) + .unwrap(); + + instance + .setattr(py, intern!(py, "finalizer"), finalizer) + .unwrap(); + + instance +} - let handle = value.into_pyobject(*py).unwrap(); +fn exported_resource_to_canon( + py: Python<'_>, + ty: wit::Resource, + new: unsafe extern "C" fn(usize) -> u32, + value: Py, +) -> u32 { + let name = intern!(py, "__componentize_py_handle"); + if value.bind(py).hasattr(name).unwrap() { + value.bind(py).getattr(name).unwrap().extract().unwrap() + } else { + let rep = value.into_ptr(); + let handle = unsafe { new(rep as usize) }; + let instance = unsafe { Py::::from_borrowed_ptr(py, rep) }; instance - .setattr(*py, intern!(*py, "handle"), handle.as_borrowed()) + .setattr(py, name, handle.into_pyobject(py).unwrap()) .unwrap(); let finalizer = FINALIZE .get() .unwrap() .call1( - *py, + py, ( - instance.clone_ref(*py), + instance.clone_ref(py), DROP_RESOURCE.get().unwrap(), - drop.into_pyobject(*py).unwrap(), + (ty.drop() as usize).into_pyobject(py).unwrap(), handle, ), ) .unwrap(); instance - .setattr(*py, intern!(*py, "finalizer"), finalizer) + .setattr(py, intern!(py, "finalizer"), finalizer) .unwrap(); - instance.into_bound(*py) + handle } } -#[export_name = "componentize-py#ToCanonHandle"] -pub extern "C" fn componentize_py_to_canon_handle( - py: &Python, - value: Borrowed, - borrow: i32, - local: i32, - resource: i32, -) -> u32 { - if local != 0 { - let ty = &TYPES.get().unwrap()[usize::try_from(resource).unwrap()]; - let Type::Resource { - local: Some(LocalResource { new, drop, .. }), - .. - } = ty - else { - panic!("expected local resource, found {ty:?}"); - }; - - let name = intern!(*py, "__componentize_py_handle"); - if value.hasattr(name).unwrap() { - value.getattr(name).unwrap().extract().unwrap() - } else { - let rep = Py::::from(value.to_owned()).into_ptr(); - let handle = { - let params = [rep as usize]; - let mut results = [MaybeUninit::::uninit()]; - unsafe { - componentize_py_call_indirect( - py as *const _ as _, - params.as_ptr() as _, - results.as_mut_ptr() as _, - *new, - ); - results[0].assume_init() - } - }; - - let instance = unsafe { Py::::from_borrowed_ptr(*py, rep) }; - - instance - .setattr(*py, name, handle.into_pyobject(*py).unwrap()) - .unwrap(); - - let finalizer = FINALIZE - .get() - .unwrap() - .call1( - *py, - ( - instance.clone_ref(*py), - DROP_RESOURCE.get().unwrap(), - drop.into_pyobject(*py).unwrap(), - handle, - ), - ) - .unwrap(); - - instance - .setattr(*py, intern!(*py, "finalizer"), finalizer) - .unwrap(); - - handle - } - } else { - if borrow == 0 { - value - .getattr(intern!(*py, "finalizer")) - .unwrap() - .call_method0(intern!(*py, "detach")) - .unwrap(); - } - - value - .getattr(intern!(*py, "handle")) - .unwrap() - .extract() - .unwrap() - } +fn imported_resource_to_canon(py: Python<'_>, value: Py) -> u32 { + value + .bind(py) + .getattr(intern!(py, "handle")) + .unwrap() + .extract() + .unwrap() } +wit_dylib_ffi::export!(MyInterpreter); + // As of this writing, recent Rust `nightly` builds include a version of the `libc` crate that expects `wasi-libc` // to define the following global variables, but `wasi-libc` defines them as preprocessor constants which aren't // visible at link time, so we need to define them somewhere. Ideally, we should fix this upstream, but for now we diff --git a/shared/Cargo.toml b/shared/Cargo.toml deleted file mode 100644 index 037f3d0..0000000 --- a/shared/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "componentize-py-shared" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/shared/src/lib.rs b/shared/src/lib.rs deleted file mode 100644 index f4ef1c2..0000000 --- a/shared/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[repr(u8)] -pub enum ReturnStyle { - Normal, - Result, -} diff --git a/src/abi.rs b/src/abi.rs deleted file mode 100644 index 3fb7053..0000000 --- a/src/abi.rs +++ /dev/null @@ -1,247 +0,0 @@ -use { - std::iter, - wasm_encoder::ValType, - wit_parser::{FlagsRepr, Resolve, Type, TypeDefKind}, -}; - -pub const MAX_FLAT_PARAMS: usize = 16; -pub const MAX_FLAT_RESULTS: usize = 1; - -pub fn align(a: usize, b: usize) -> usize { - assert!(b.is_power_of_two()); - (a + (b - 1)) & !(b - 1) -} - -pub struct Abi { - pub size: usize, - pub align: usize, - pub flattened: Vec, -} - -pub fn record_abi(resolve: &Resolve, types: impl IntoIterator) -> Abi { - let mut size = 0_usize; - let mut align_ = 1; - let mut flattened = Vec::new(); - for ty in types { - let abi = abi(resolve, ty); - size = align(size, abi.align); - size += abi.size; - if abi.align > align_ { - align_ = abi.align; - } - flattened.extend(abi.flattened); - } - - Abi { - size: align(size, align_), - align: align_, - flattened, - } -} - -pub fn record_abi_limit( - resolve: &Resolve, - types: impl IntoIterator, - limit: usize, -) -> Abi { - let mut abi = record_abi(resolve, types); - if abi.flattened.len() > limit { - abi.flattened = vec![ValType::I32]; - } - abi -} - -fn join(a: ValType, b: ValType) -> ValType { - if a == b { - a - } else if let (ValType::I32, ValType::F32) | (ValType::F32, ValType::I32) = (a, b) { - ValType::I32 - } else { - ValType::I64 - } -} - -pub fn discriminant_size(count: usize) -> usize { - match count { - 1..=0xFF => 1, - 0x100..=0xFFFF => 2, - 0x1_0000..=0xFFFF_FFFF => 4, - _ => unreachable!(), - } -} - -fn variant_abi(resolve: &Resolve, types: impl IntoIterator>) -> Abi { - let mut size = 0_usize; - let mut align_ = 1; - let mut flattened = Vec::new(); - let mut count = 0; - for ty in types { - count += 1; - if let Some(ty) = ty { - let abi = abi(resolve, ty); - if abi.size > size { - size = abi.size; - } - if abi.align > align_ { - align_ = abi.align; - } - for (index, ty) in abi.flattened.iter().enumerate() { - if index == flattened.len() { - flattened.push(*ty); - } else { - flattened[index] = join(flattened[index], *ty); - } - } - } - } - - let discriminant_size = discriminant_size(count); - let align_ = align_.max(discriminant_size); - let size = align(size + align(discriminant_size, align_), align_); - let flattened = iter::once(ValType::I32).chain(flattened).collect(); - - Abi { - size, - align: align_, - flattened, - } -} - -pub fn has_pointer(resolve: &Resolve, ty: Type) -> bool { - match ty { - Type::Bool - | Type::U8 - | Type::S8 - | Type::U16 - | Type::S16 - | Type::U32 - | Type::S32 - | Type::Char - | Type::U64 - | Type::S64 - | Type::F32 - | Type::F64 - | Type::ErrorContext => false, - Type::String => true, - Type::Id(id) => match &resolve.types[id].kind { - TypeDefKind::Record(record) => record - .fields - .iter() - .any(|field| has_pointer(resolve, field.ty)), - TypeDefKind::Variant(variant) => variant - .cases - .iter() - .any(|case| case.ty.map(|ty| has_pointer(resolve, ty)).unwrap_or(false)), - // TODO: should we be calling destructors for handles in post-return functions? - TypeDefKind::Handle(_) | TypeDefKind::Enum(_) | TypeDefKind::Flags(_) => false, - TypeDefKind::Option(ty) => has_pointer(resolve, *ty), - TypeDefKind::Result(result) => { - result - .ok - .map(|ty| has_pointer(resolve, ty)) - .unwrap_or(false) - || result - .err - .map(|ty| has_pointer(resolve, ty)) - .unwrap_or(false) - } - TypeDefKind::Tuple(tuple) => tuple.types.iter().any(|ty| has_pointer(resolve, *ty)), - TypeDefKind::List(_) => true, - TypeDefKind::Type(ty) => has_pointer(resolve, *ty), - kind => todo!("{kind:?}"), - }, - } -} - -pub fn abi(resolve: &Resolve, ty: Type) -> Abi { - match ty { - Type::Bool | Type::U8 | Type::S8 => Abi { - size: 1, - align: 1, - flattened: vec![ValType::I32], - }, - Type::U16 | Type::S16 => Abi { - size: 2, - align: 2, - flattened: vec![ValType::I32], - }, - Type::U32 | Type::S32 | Type::Char | Type::ErrorContext => Abi { - size: 4, - align: 4, - flattened: vec![ValType::I32], - }, - Type::U64 | Type::S64 => Abi { - size: 8, - align: 8, - flattened: vec![ValType::I64], - }, - Type::F32 => Abi { - size: 4, - align: 4, - flattened: vec![ValType::F32], - }, - Type::F64 => Abi { - size: 8, - align: 8, - flattened: vec![ValType::F64], - }, - Type::String => Abi { - size: 8, - align: 4, - flattened: vec![ValType::I32; 2], - }, - Type::Id(id) => match &resolve.types[id].kind { - TypeDefKind::Record(record) => { - record_abi(resolve, record.fields.iter().map(|field| field.ty)) - } - TypeDefKind::Variant(variant) => { - variant_abi(resolve, variant.cases.iter().map(|case| case.ty)) - } - TypeDefKind::Enum(en) => variant_abi(resolve, en.cases.iter().map(|_| None)), - TypeDefKind::Option(ty) => variant_abi(resolve, [None, Some(*ty)]), - TypeDefKind::Result(result) => variant_abi(resolve, [result.ok, result.err]), - TypeDefKind::Flags(flags) => { - let repr = flags.repr(); - - Abi { - size: match &repr { - FlagsRepr::U8 => 1, - FlagsRepr::U16 => 2, - FlagsRepr::U32(count) => 4 * *count, - }, - align: match &repr { - FlagsRepr::U8 | FlagsRepr::U32(0) => 1, - FlagsRepr::U16 => 2, - FlagsRepr::U32(_) => 4, - }, - flattened: vec![ValType::I32; repr.count()], - } - } - TypeDefKind::Tuple(tuple) => record_abi(resolve, tuple.types.iter().copied()), - TypeDefKind::List(_) => Abi { - size: 8, - align: 4, - flattened: vec![ValType::I32; 2], - }, - TypeDefKind::Type(ty) => abi(resolve, *ty), - TypeDefKind::Handle(_) => Abi { - size: 4, - align: 4, - flattened: vec![ValType::I32], - }, - kind => todo!("{kind:?}"), - }, - } -} - -pub fn is_option(resolve: &Resolve, ty: Type) -> bool { - if let Type::Id(id) = ty { - match &resolve.types[id].kind { - TypeDefKind::Option(_) => true, - TypeDefKind::Type(ty) => is_option(resolve, *ty), - _ => false, - } - } else { - false - } -} diff --git a/src/bindgen.rs b/src/bindgen.rs deleted file mode 100644 index c5c0156..0000000 --- a/src/bindgen.rs +++ /dev/null @@ -1,2691 +0,0 @@ -use { - crate::{ - abi::{self, Abi, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS}, - summary::{Direction, MyFunction, Summary}, - util::Types as _, - }, - componentize_py_shared::ReturnStyle, - indexmap::IndexSet, - once_cell::sync::Lazy, - std::collections::HashMap, - wasm_encoder::{BlockType, Instruction as Ins, MemArg, ValType}, - wit_parser::{Handle, Resolve, Type, TypeDefKind, TypeId}, -}; - -// Assume Wasm32 -// TODO: Wasm64 support -const WORD_SIZE: usize = 4; -const WORD_ALIGN: usize = 2; // as a power of two - -const STACK_ALIGNMENT: usize = 8; - -pub const DISPATCHABLE_CORE_PARAM_COUNT: usize = 3; -pub const DISPATCH_CORE_PARAM_COUNT: usize = DISPATCHABLE_CORE_PARAM_COUNT + 1; - -const DISCRIMINANT_FIELD_INDEX: i32 = 0; -const PAYLOAD_FIELD_INDEX: i32 = 1; - -pub static IMPORT_SIGNATURES: &[(&str, &[ValType], &[ValType])] = &[ - ( - "componentize-py#Dispatch", - &[ValType::I32; 7] as &[_], - &[] as &[_], - ), - ( - "componentize-py#Allocate", - &[ValType::I32; 2], - &[ValType::I32], - ), - ("componentize-py#Free", &[ValType::I32; 3], &[]), - ( - "componentize-py#ToCanonBool", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#ToCanonI32", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#ToCanonU32", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#ToCanonI64", - &[ValType::I32; 2], - &[ValType::I64], - ), - ( - "componentize-py#ToCanonU64", - &[ValType::I32; 2], - &[ValType::I64], - ), - ( - "componentize-py#ToCanonF32", - &[ValType::I32; 2], - &[ValType::F32], - ), - ( - "componentize-py#ToCanonF64", - &[ValType::I32; 2], - &[ValType::F64], - ), - ( - "componentize-py#ToCanonChar", - &[ValType::I32; 2], - &[ValType::I32], - ), - ("componentize-py#ToCanonString", &[ValType::I32; 3], &[]), - ( - "componentize-py#GetField", - &[ValType::I32; 4], - &[ValType::I32], - ), - ( - "componentize-py#GetListLength", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#GetListElement", - &[ValType::I32; 3], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonBool", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonI32", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonU32", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonI64", - &[ValType::I32, ValType::I64], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonU64", - &[ValType::I32, ValType::I64], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonF32", - &[ValType::I32, ValType::F32], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonF64", - &[ValType::I32, ValType::F64], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonChar", - &[ValType::I32; 2], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonString", - &[ValType::I32; 3], - &[ValType::I32], - ), - ("componentize-py#MakeList", &[ValType::I32], &[ValType::I32]), - ("componentize-py#ListAppend", &[ValType::I32; 3], &[]), - ("componentize-py#None", &[ValType::I32], &[ValType::I32]), - ("componentize-py#Init", &[ValType::I32; 4], &[ValType::I32]), - ("componentize-py#GetBytes", &[ValType::I32; 4], &[]), - ( - "componentize-py#MakeBytes", - &[ValType::I32; 3], - &[ValType::I32], - ), - ( - "componentize-py#FromCanonHandle", - &[ValType::I32; 5], - &[ValType::I32], - ), - ( - "componentize-py#ToCanonHandle", - &[ValType::I32; 5], - &[ValType::I32], - ), - ("cabi_realloc", &[ValType::I32; 4], &[ValType::I32]), -]; - -pub static IMPORTS: Lazy> = Lazy::new(|| { - IMPORT_SIGNATURES - .iter() - .enumerate() - .map(|(index, (name, ..))| (*name, index.try_into().unwrap())) - .collect() -}); - -pub fn mem_arg(offset: u64, align: u32) -> MemArg { - MemArg { - offset, - align, - memory_index: 0, - } -} - -pub struct FunctionBindgen<'a> { - pub local_types: Vec, - pub instructions: Vec>, - resolve: &'a Resolve, - stack_pointer: u32, - types: &'a IndexSet, - params: &'a [(String, Type)], - result: &'a Option, - params_abi: Abi, - results_abi: Abi, - local_stack: Vec, - param_count: usize, - tuple_types: &'a HashMap, - option_type: Option, - nesting_option_type: Option, - result_type: Option, - resource_directions: Option<&'a im_rc::HashMap>, -} - -#[allow(clippy::wrong_self_convention)] -impl<'a> FunctionBindgen<'a> { - pub fn new(summary: &'a Summary, function: &'a MyFunction, stack_pointer: u32) -> Self { - Self { - resolve: summary.resolve, - stack_pointer, - types: &summary.types, - params: function.params, - result: function.result, - params_abi: abi::record_abi(summary.resolve, function.params.types()), - results_abi: abi::record_abi(summary.resolve, function.result.types()), - local_types: Vec::new(), - local_stack: Vec::new(), - instructions: Vec::new(), - param_count: function.core_export_type(summary.resolve).0.len(), - tuple_types: &summary.tuple_types, - option_type: summary.option_type, - nesting_option_type: summary.nesting_option_type, - result_type: summary.result_type, - resource_directions: function - .interface - .as_ref() - .map(|interface| &interface.resource_directions), - } - } - - pub fn compile_import(&mut self, index: u32) { - // Arg 0: *const Python - let context = 0; - // Arg 1: *const &PyAny - let input = 1; - // Arg 2: *mut &PyAny - let output = 2; - - let locals = if self.params_abi.flattened.len() <= MAX_FLAT_PARAMS { - let locals = self - .params_abi - .flattened - .clone() - .iter() - .map(|ty| self.push_local(*ty)) - .collect::>(); - - let mut from_canon_index = 0; - let mut load_offset = 0; - for ty in self.params.types() { - let abi = abi::abi(self.resolve, ty); - - let value = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(input)); - self.push(Ins::I32Load(mem_arg( - load_offset, - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::LocalSet(value)); - - self.to_canon(ty, context, value); - - for local in locals[from_canon_index..][..abi.flattened.len()] - .iter() - .rev() - { - self.push(Ins::LocalSet(*local)); - } - - for local in &locals[from_canon_index..][..abi.flattened.len()] { - self.push(Ins::LocalGet(*local)); - } - - from_canon_index += abi.flattened.len(); - load_offset += u64::try_from(WORD_SIZE).unwrap(); - - self.pop_local(value, ValType::I32); - } - - Some(locals) - } else { - self.push_stack(self.params_abi.size); - - let mut store_offset = 0; - let mut load_offset = 0; - for ty in self.params.types() { - let value = self.push_local(ValType::I32); - let destination = self.push_local(ValType::I32); - - let abi = abi::abi(self.resolve, ty); - store_offset = abi::align(store_offset, abi.align); - - self.get_stack(); - self.push(Ins::I32Const(store_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(destination)); - - self.push(Ins::LocalGet(input)); - self.push(Ins::I32Load(mem_arg( - load_offset, - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::LocalSet(value)); - - self.store(ty, context, value, destination); - - store_offset += abi.size; - load_offset += u64::try_from(WORD_SIZE).unwrap(); - - self.pop_local(destination, ValType::I32); - self.pop_local(value, ValType::I32); - } - - self.get_stack(); - - None - }; - - if self.results_abi.flattened.len() > MAX_FLAT_RESULTS { - self.push_stack(self.results_abi.size); - - self.get_stack(); - } - - self.push(Ins::Call(index)); - - if self.results_abi.flattened.len() <= MAX_FLAT_RESULTS { - let locals = self - .results_abi - .flattened - .clone() - .iter() - .map(|ty| { - let local = self.push_local(*ty); - self.push(Ins::LocalSet(local)); - local - }) - .collect::>(); - - self.from_canon_record(self.result.types(), context, &locals, output); - self.free_canon_record(self.result.types(), &locals); - - for (local, ty) in locals.iter().zip(&self.results_abi.flattened.clone()).rev() { - self.pop_local(*local, *ty); - } - } else { - let source = self.push_local(ValType::I32); - - self.get_stack(); - self.push(Ins::LocalSet(source)); - - self.load_record(self.result.types(), context, source, output); - self.free_stored_record(self.result.types(), source); - - self.pop_local(source, ValType::I32); - self.pop_stack(self.results_abi.size); - } - - if let Some(locals) = locals { - self.free_canon_record(self.params.types(), &locals); - - for (local, ty) in locals.iter().zip(&self.params_abi.flattened.clone()).rev() { - self.pop_local(*local, *ty); - } - } else { - let value = self.push_local(ValType::I32); - - self.get_stack(); - self.push(Ins::LocalSet(value)); - - self.free_stored_record(self.params.types(), value); - - self.pop_local(value, ValType::I32); - self.pop_stack(self.params_abi.size); - } - } - - pub fn compile_export(&mut self, index: i32, from_canon: i32, to_canon: i32) { - let return_style = match self.result.types().collect::>().as_slice() { - [Type::Id(id)] if matches!(&self.resolve.types[*id].kind, TypeDefKind::Result(_)) => { - ReturnStyle::Result - } - _ => ReturnStyle::Normal, - }; - - self.push(Ins::I32Const(index)); - self.push(Ins::I32Const(from_canon)); - self.push(Ins::I32Const(to_canon)); - self.push(Ins::I32Const( - self.params.types().count().try_into().unwrap(), - )); - self.push(Ins::I32Const(return_style as _)); - - if self.params_abi.flattened.len() <= MAX_FLAT_PARAMS { - self.push_stack(self.params_abi.size); - - let destination = self.push_local(ValType::I32); - self.get_stack(); - self.push(Ins::LocalSet(destination)); - - self.store_copy_record( - self.params.types(), - &(0..self.params_abi.flattened.len().try_into().unwrap()).collect::>(), - destination, - ); - - self.pop_local(destination, ValType::I32); - - self.get_stack(); - } else { - self.push(Ins::LocalGet(0)); - }; - - let result = if self.results_abi.flattened.len() <= MAX_FLAT_RESULTS { - self.push_stack(self.results_abi.size); - self.get_stack(); - - None - } else { - let result = self.push_local(ValType::I32); - self.push(Ins::I32Const(self.results_abi.size.try_into().unwrap())); - self.push(Ins::I32Const(self.results_abi.align.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Allocate").unwrap())); - self.push(Ins::LocalTee(result)); - - Some(result) - }; - - self.push(Ins::Call(*IMPORTS.get("componentize-py#Dispatch").unwrap())); - - if let Some(result) = result { - self.push(Ins::LocalGet(result)); - self.pop_local(result, ValType::I32); - } else { - let source = self.push_local(ValType::I32); - self.get_stack(); - self.push(Ins::LocalSet(source)); - - self.load_copy_record(self.result.types(), source); - - self.pop_local(source, ValType::I32); - - self.pop_stack(self.results_abi.size); - } - - if self.params_abi.flattened.len() <= MAX_FLAT_PARAMS { - self.pop_stack(self.params_abi.size); - } - } - - pub fn compile_export_from_canon(&mut self) { - // Arg 0: *const Python - let context = 0; - // Arg 1: *const MyParams - let source = 1; - // Arg 2: *mut &PyAny - let destination = 2; - - self.load_record(self.params.types(), context, source, destination); - } - - pub fn compile_export_to_canon(&mut self) { - // Arg 0: *const Python - let context = 0; - // Arg 1: *const &PyAny - let source = 1; - // Arg 2: *mut MyResults - let destination = 2; - - let mut store_offset = 0; - let mut load_offset = 0; - for ty in self.result.types() { - let abi = abi::abi(self.resolve, ty); - store_offset = abi::align(store_offset, abi.align); - - let field_value = self.push_local(ValType::I32); - let field_destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg( - load_offset, - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::LocalSet(field_value)); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::I32Const(store_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(field_destination)); - - self.store(ty, context, field_value, field_destination); - - store_offset += abi.size; - load_offset += u64::try_from(WORD_SIZE).unwrap(); - - self.pop_local(field_destination, ValType::I32); - self.pop_local(field_value, ValType::I32); - } - } - - pub fn compile_export_post_return(&mut self) { - if self.results_abi.flattened.len() > MAX_FLAT_RESULTS { - // Arg 0: *mut MyResults - let value = 0; - - self.free_stored_record(self.result.types(), value); - - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(self.results_abi.size.try_into().unwrap())); - self.push(Ins::I32Const(self.results_abi.align.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Free").unwrap())); - } else { - unreachable!() - } - } - - pub fn compile_resource_new(&mut self, index: u32) { - // Arg 0: *const Python - let _ = 0; - // Arg 1: *const usize - let input = 1; - // Arg 2: *mut u32 - let output = 2; - - self.push(Ins::LocalGet(output)); - self.push(Ins::LocalGet(input)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::Call(index)); - self.push(Ins::I32Store(mem_arg(0, 2))); - } - - pub fn compile_resource_rep(&mut self, index: u32) { - // Arg 0: *const Python - let _ = 0; - // Arg 1: *const u32 - let input = 1; - // Arg 2: *mut usize - let output = 2; - - self.push(Ins::LocalGet(output)); - self.push(Ins::LocalGet(input)); - self.push(Ins::I32Load(mem_arg(0, 2))); - self.push(Ins::Call(index)); - self.push(Ins::I32Store(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - } - - pub fn compile_resource_drop(&mut self, index: u32) { - // Arg 0: *const Python - let _ = 0; - // Arg 1: *const u32 - let input = 1; - // Arg 2: *mut c_void - let _ = 2; - - self.push(Ins::LocalGet(input)); - self.push(Ins::I32Load(mem_arg(0, 2))); - self.push(Ins::Call(index)); - } - - fn push_stack(&mut self, size: usize) { - self.push(Ins::GlobalGet(self.stack_pointer)); - self.push(Ins::I32Const( - abi::align(size, STACK_ALIGNMENT).try_into().unwrap(), - )); - self.push(Ins::I32Sub); - self.push(Ins::GlobalSet(self.stack_pointer)); - } - - fn pop_stack(&mut self, size: usize) { - self.push(Ins::GlobalGet(self.stack_pointer)); - self.push(Ins::I32Const( - abi::align(size, STACK_ALIGNMENT).try_into().unwrap(), - )); - self.push(Ins::I32Add); - self.push(Ins::GlobalSet(self.stack_pointer)); - } - - fn push(&mut self, instruction: Ins<'static>) { - self.instructions.push(instruction) - } - - fn get_stack(&mut self) { - self.push(Ins::GlobalGet(self.stack_pointer)); - } - - fn push_local(&mut self, ty: ValType) -> u32 { - while self.local_types.len() > self.local_stack.len() - && self.local_types[self.local_stack.len()] != ty - { - self.local_stack.push(false); - } - - self.local_stack.push(true); - if self.local_types.len() < self.local_stack.len() { - self.local_types.push(ty); - } - - (self.param_count + self.local_stack.len() - 1) - .try_into() - .unwrap() - } - - fn pop_local(&mut self, index: u32, ty: ValType) { - assert!(index == u32::try_from(self.param_count + self.local_stack.len() - 1).unwrap()); - assert!(ty == self.local_types[self.local_stack.len() - 1]); - - self.local_stack.pop(); - while let Some(false) = self.local_stack.last() { - self.local_stack.pop(); - } - } - - fn to_canon(&mut self, ty: Type, context: u32, value: u32) { - match ty { - Type::Bool => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonBool").unwrap(), - )); - } - Type::S8 | Type::S16 | Type::S32 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonI32").unwrap(), - )); - } - Type::U8 | Type::U16 | Type::U32 | Type::ErrorContext => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonU32").unwrap(), - )); - } - Type::S64 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonI64").unwrap(), - )); - } - Type::U64 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonU64").unwrap(), - )); - } - Type::F32 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonF32").unwrap(), - )); - } - Type::F64 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonF64").unwrap(), - )); - } - Type::Char => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonChar").unwrap(), - )); - } - Type::String => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push_stack(WORD_SIZE * 2); - self.get_stack(); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonString").unwrap(), - )); - self.get_stack(); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.get_stack(); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - self.pop_stack(WORD_SIZE * 2); - } - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.to_canon_record(id, record.fields.iter().map(|f| f.ty), context, value); - } - TypeDefKind::Variant(variant) => { - self.to_canon_variant( - id, - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - context, - value, - ); - } - TypeDefKind::Enum(en) => { - self.to_canon_variant( - id, - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - context, - value, - ); - } - TypeDefKind::Option(some) => { - self.to_canon_variant( - self.get_option_type(*some), - &abi::abi(self.resolve, ty), - [None, Some(*some)], - context, - value, - ); - } - TypeDefKind::Result(result) => { - self.to_canon_variant( - self.result_type.unwrap(), - &abi::abi(self.resolve, ty), - [result.ok, result.err], - context, - value, - ); - } - TypeDefKind::Flags(flags) => { - self.to_canon_record(id, flags.types(), context, value); - } - TypeDefKind::Tuple(tuple) => { - self.to_canon_record( - *self.tuple_types.get(&tuple.types.len()).unwrap(), - tuple.types.iter().copied(), - context, - value, - ); - } - TypeDefKind::List(ty) => { - let abi = abi::abi(self.resolve, *ty); - - let length = self.push_local(ValType::I32); - let destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#GetListLength").unwrap(), - )); - self.push(Ins::LocalSet(length)); - - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Const(abi.align.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Allocate").unwrap())); - self.push(Ins::LocalSet(destination)); - - if let Type::U8 | Type::S8 = ty { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(length)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#GetBytes").unwrap())); - } else { - let index = self.push_local(ValType::I32); - let element_value = self.push_local(ValType::I32); - let element_destination = self.push_local(ValType::I32); - - self.push(Ins::I32Const(0)); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Loop(BlockType::Empty)); - - self.push(Ins::LocalGet(index)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Ne); - - self.push(Ins::If(BlockType::Empty)); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::LocalGet(index)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#GetListElement").unwrap(), - )); - self.push(Ins::LocalSet(element_value)); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(element_destination)); - - self.store(*ty, context, element_value, element_destination); - - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(1)); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Br(1)); - - self.push(Ins::End); - - self.push(Ins::End); - - self.pop_local(element_destination, ValType::I32); - self.pop_local(element_value, ValType::I32); - self.pop_local(index, ValType::I32); - } - - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(length)); - - self.pop_local(destination, ValType::I32); - self.pop_local(length, ValType::I32); - } - TypeDefKind::Handle(handle) => { - self.marshal_handle(handle, context, value); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonHandle").unwrap(), - )); - } - TypeDefKind::Type(ty) => self.to_canon(*ty, context, value), - kind => todo!("{kind:?}"), - }, - } - } - - fn marshal_handle(&mut self, handle: &Handle, context: u32, value: u32) { - let (borrow, resource) = match handle { - Handle::Own(resource) => (0, resource), - Handle::Borrow(resource) => (1, resource), - }; - let resource = dealias(self.resolve, *resource); - let local = if let Some(Direction::Export) = self - .resource_directions - .and_then(|directions| directions.get(&resource)) - { - 1 - } else { - 0 - }; - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(borrow)); - self.push(Ins::I32Const(local)); - self.push(Ins::I32Const( - self.types - .get_index_of(&resource) - .unwrap() - .try_into() - .unwrap(), - )); - } - - fn to_canon_record( - &mut self, - id: TypeId, - types: impl IntoIterator, - context: u32, - value: u32, - ) { - let type_index = self.types.get_index_of(&id).unwrap(); - for (field_index, ty) in types.into_iter().enumerate() { - let field_value = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(type_index.try_into().unwrap())); - self.push(Ins::I32Const(field_index.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#GetField").unwrap())); - self.push(Ins::LocalSet(field_value)); - - self.to_canon(ty, context, field_value); - - self.pop_local(field_value, ValType::I32); - } - } - - fn to_canon_variant( - &mut self, - id: TypeId, - abi: &Abi, - types: impl IntoIterator>, - context: u32, - value: u32, - ) { - // TODO: instead of storing to and then loading from memory, write directly to the primary stack (and/or - // locals) - - let destination = self.push_local(ValType::I32); - self.push_stack(abi.size); - self.get_stack(); - self.push(Ins::LocalSet(destination)); - - let types = types.into_iter().collect::>(); - - self.store_variant(id, abi, types.clone(), context, value, destination); - - self.load_copy_variant(abi, types, destination); - - self.pop_stack(abi.size); - self.pop_local(destination, ValType::I32); - } - - fn store(&mut self, ty: Type, context: u32, value: u32, destination: u32) { - match ty { - Type::Bool => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonBool").unwrap(), - )); - self.push(Ins::I32Store8(mem_arg(0, 0))); - } - Type::U8 | Type::S8 => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::I32Store8(mem_arg(0, 0))); - } - Type::U16 | Type::S16 => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::I32Store16(mem_arg(0, 1))); - } - Type::U32 | Type::S32 | Type::ErrorContext => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::I32Store(mem_arg(0, 2))); - } - Type::U64 | Type::S64 => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::I64Store(mem_arg(0, 3))); - } - Type::F32 => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::F32Store(mem_arg(0, 2))); - } - Type::F64 => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::F64Store(mem_arg(0, 3))); - } - Type::Char => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonChar").unwrap(), - )); - self.push(Ins::I32Store(mem_arg(0, 2))); - } - Type::String => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::LocalGet(destination)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonString").unwrap(), - )); - } - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.store_record( - id, - record.fields.iter().map(|f| f.ty), - context, - value, - destination, - ); - } - TypeDefKind::Variant(variant) => { - self.store_variant( - id, - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - context, - value, - destination, - ); - } - TypeDefKind::Enum(en) => { - self.store_variant( - id, - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - context, - value, - destination, - ); - } - TypeDefKind::Option(some) => { - self.store_variant( - self.get_option_type(*some), - &abi::abi(self.resolve, ty), - [None, Some(*some)], - context, - value, - destination, - ); - } - TypeDefKind::Result(result) => { - self.store_variant( - self.result_type.unwrap(), - &abi::abi(self.resolve, ty), - [result.ok, result.err], - context, - value, - destination, - ); - } - TypeDefKind::Flags(flags) => { - self.store_record(id, flags.types(), context, value, destination); - } - TypeDefKind::Tuple(tuple) => { - self.store_record( - *self.tuple_types.get(&tuple.types.len()).unwrap(), - tuple.types.iter().copied(), - context, - value, - destination, - ); - } - TypeDefKind::List(_) => { - let length = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::LocalSet(length)); - self.push(Ins::I32Store(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Store(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - - self.pop_local(length, ValType::I32); - } - TypeDefKind::Handle(_) => { - self.push(Ins::LocalGet(destination)); - self.to_canon(ty, context, value); - self.push(Ins::I32Store(mem_arg(0, 2))); - } - TypeDefKind::Type(ty) => self.store(*ty, context, value, destination), - kind => todo!("{kind:?}"), - }, - } - } - - fn store_record( - &mut self, - id: TypeId, - types: impl IntoIterator, - context: u32, - value: u32, - destination: u32, - ) { - let type_index = self.types.get_index_of(&id).unwrap(); - let mut store_offset = 0; - for (field_index, ty) in types.into_iter().enumerate() { - let abi = abi::abi(self.resolve, ty); - store_offset = abi::align(store_offset, abi.align); - - let field_value = self.push_local(ValType::I32); - let field_destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(type_index.try_into().unwrap())); - self.push(Ins::I32Const(field_index.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#GetField").unwrap())); - self.push(Ins::LocalSet(field_value)); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::I32Const(store_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(field_destination)); - - self.store(ty, context, field_value, field_destination); - - store_offset += abi.size; - - self.pop_local(field_destination, ValType::I32); - self.pop_local(field_value, ValType::I32); - } - } - - fn search_variant( - &mut self, - block_type: BlockType, - base: usize, - types: &[Option], - discriminant: u32, - predicate: impl (Fn(&Self, Option) -> bool) + Copy, - fun: impl Fn(&mut Self, Option) + Copy, - ) { - match types { - [] => unreachable!(), - [ty] => fun(self, *ty), - types => { - if types.iter().any(|ty| predicate(self, *ty)) { - let middle = types.len() / 2; - self.push(Ins::LocalGet(discriminant)); - self.push(Ins::I32Const((base + middle).try_into().unwrap())); - self.push(Ins::I32LtU); - self.push(Ins::If(block_type)); - self.search_variant( - block_type, - base, - &types[..middle], - discriminant, - predicate, - fun, - ); - self.push(Ins::Else); - self.search_variant( - block_type, - base + middle, - &types[middle..], - discriminant, - predicate, - fun, - ); - self.push(Ins::End); - } else { - fun(self, None); - } - } - } - } - - fn store_variant( - &mut self, - id: TypeId, - abi: &Abi, - types: impl IntoIterator>, - context: u32, - value: u32, - destination: u32, - ) { - let type_index = self.types.get_index_of(&id).unwrap(); - let types = types.into_iter().collect::>(); - let discriminant_size = abi::discriminant_size(types.len()); - let discriminant = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(type_index.try_into().unwrap())); - self.push(Ins::I32Const(DISCRIMINANT_FIELD_INDEX)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#GetField").unwrap())); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ToCanonI32").unwrap(), - )); - self.push(Ins::LocalSet(discriminant)); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(discriminant)); - match discriminant_size { - 1 => self.push(Ins::I32Store8(mem_arg(0, 0))), - 2 => self.push(Ins::I32Store16(mem_arg(0, 1))), - 4 => self.push(Ins::I32Store(mem_arg(0, 2))), - _ => unreachable!(), - } - - if types.iter().any(Option::is_some) { - let payload = self.push_local(ValType::I32); - let payload_destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(type_index.try_into().unwrap())); - self.push(Ins::I32Const(PAYLOAD_FIELD_INDEX)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#GetField").unwrap())); - self.push(Ins::LocalSet(payload)); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::I32Const( - abi::align(discriminant_size, abi.align).try_into().unwrap(), - )); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(payload_destination)); - - self.search_variant( - BlockType::Empty, - 0, - &types, - discriminant, - |_, ty| ty.is_some(), - |this, ty| { - if let Some(ty) = ty { - this.store(ty, context, payload, payload_destination); - } - }, - ); - - self.pop_local(payload_destination, ValType::I32); - self.pop_local(payload, ValType::I32); - } - - self.pop_local(discriminant, ValType::I32); - } - - fn store_copy(&mut self, ty: Type, source: &[u32], destination: u32) { - match ty { - Type::Bool | Type::U8 | Type::S8 => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I32Store8(mem_arg(0, 0))); - } - Type::U16 | Type::S16 => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I32Store16(mem_arg(0, 1))); - } - Type::U32 | Type::S32 | Type::Char | Type::ErrorContext => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I32Store(mem_arg(0, 2))); - } - Type::U64 | Type::S64 => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I64Store(mem_arg(0, 3))); - } - Type::F32 => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::F32Store(mem_arg(0, 2))); - } - Type::F64 => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::F64Store(mem_arg(0, 3))); - } - Type::String => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I32Store(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[1])); - self.push(Ins::I32Store(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - } - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.store_copy_record(record.fields.iter().map(|f| f.ty), source, destination); - } - TypeDefKind::Variant(variant) => { - self.store_copy_variant( - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - source, - destination, - ); - } - TypeDefKind::Enum(en) => { - self.store_copy_variant( - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - source, - destination, - ); - } - TypeDefKind::Option(some) => { - self.store_copy_variant( - &abi::abi(self.resolve, ty), - [None, Some(*some)], - source, - destination, - ); - } - TypeDefKind::Result(result) => { - self.store_copy_variant( - &abi::abi(self.resolve, ty), - [result.ok, result.err], - source, - destination, - ); - } - TypeDefKind::Flags(flags) => { - self.store_copy_record(flags.types(), source, destination); - } - TypeDefKind::Tuple(tuple) => { - self.store_copy_record(tuple.types.iter().copied(), source, destination); - } - TypeDefKind::List(_) => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I32Store(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[1])); - self.push(Ins::I32Store(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - } - TypeDefKind::Handle(_) => { - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::I32Store(mem_arg(0, 2))); - } - TypeDefKind::Type(ty) => self.store_copy(*ty, source, destination), - kind => todo!("{kind:?}"), - }, - } - } - - fn store_copy_record( - &mut self, - types: impl IntoIterator, - source: &[u32], - destination: u32, - ) { - let mut local_index = 0; - let mut store_offset = 0; - for ty in types { - let abi = abi::abi(self.resolve, ty); - store_offset = abi::align(store_offset, abi.align); - - let field_destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::I32Const(store_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(field_destination)); - - self.store_copy( - ty, - &source[local_index..][..abi.flattened.len()], - field_destination, - ); - - local_index += abi.flattened.len(); - store_offset += abi.size; - - self.pop_local(field_destination, ValType::I32); - } - } - - fn convert(&mut self, source_type: ValType, destination_type: ValType) { - match (source_type, destination_type) { - (ValType::I32, ValType::I64) => self.push(Ins::I64ExtendI32U), - (ValType::I64, ValType::I32) => self.push(Ins::I32WrapI64), - (ValType::I32, ValType::F32) => self.push(Ins::F32ReinterpretI32), - (ValType::F32, ValType::I32) => self.push(Ins::I32ReinterpretF32), - (ValType::I64, ValType::F64) => self.push(Ins::F64ReinterpretI64), - (ValType::F64, ValType::I64) => self.push(Ins::I64ReinterpretF64), - (ValType::F32, ValType::I64) => { - self.push(Ins::I32ReinterpretF32); - self.push(Ins::I64ExtendI32U); - } - (ValType::I64, ValType::F32) => { - self.push(Ins::I32WrapI64); - self.push(Ins::F32ReinterpretI32); - } - _ => unreachable!("can't convert {source_type:?} to {destination_type:?}"), - } - } - - fn convert_all( - &mut self, - abi: &Abi, - payload_type: Type, - value: &[u32], - ) -> (Vec, Vec<(u32, ValType)>) { - let payload_abi = abi::abi(self.resolve, payload_type); - let mut my_value = Vec::new(); - let locals = payload_abi - .flattened - .iter() - .zip(abi.flattened.iter().skip(1)) - .zip(value) - .filter_map(|((payload_type, joined_type), value)| { - if payload_type == joined_type { - my_value.push(*value); - None - } else { - let local = self.push_local(*payload_type); - self.push(Ins::LocalGet(*value)); - self.convert(*joined_type, *payload_type); - self.push(Ins::LocalSet(local)); - my_value.push(local); - Some((local, *payload_type)) - } - }) - .collect::>(); - - (my_value, locals) - } - - fn store_copy_variant( - &mut self, - abi: &Abi, - types: impl IntoIterator>, - source: &[u32], - destination: u32, - ) { - let types = types.into_iter().collect::>(); - let discriminant_size = abi::discriminant_size(types.len()); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::LocalGet(source[0])); - match discriminant_size { - 1 => self.push(Ins::I32Store8(mem_arg(0, 0))), - 2 => self.push(Ins::I32Store16(mem_arg(0, 1))), - 4 => self.push(Ins::I32Store(mem_arg(0, 2))), - _ => unreachable!(), - } - - if types.iter().any(Option::is_some) { - let payload_destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(destination)); - self.push(Ins::I32Const( - abi::align(discriminant_size, abi.align).try_into().unwrap(), - )); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(payload_destination)); - - self.search_variant( - BlockType::Empty, - 0, - &types, - source[0], - |_, ty| ty.is_some(), - |this, ty| { - if let Some(ty) = ty { - let (source, locals) = this.convert_all(abi, ty, &source[1..]); - - this.store_copy(ty, &source, payload_destination); - - for (local, ty) in locals.into_iter().rev() { - this.pop_local(local, ty); - } - } - }, - ); - - self.pop_local(payload_destination, ValType::I32); - } - } - - fn from_canon(&mut self, ty: Type, context: u32, value: &[u32]) { - match ty { - Type::Bool => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonBool").unwrap(), - )); - } - Type::S8 | Type::S16 | Type::S32 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonI32").unwrap(), - )); - } - Type::U8 | Type::U16 | Type::U32 | Type::ErrorContext => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonU32").unwrap(), - )); - } - Type::S64 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonI64").unwrap(), - )); - } - Type::U64 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonU64").unwrap(), - )); - } - Type::F32 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonF32").unwrap(), - )); - } - Type::F64 => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonF64").unwrap(), - )); - } - Type::Char => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonChar").unwrap(), - )); - } - Type::String => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(value[0])); - self.push(Ins::LocalGet(value[1])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonString").unwrap(), - )); - } - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.from_canon_record_onto_stack( - id, - record.fields.iter().map(|f| f.ty), - context, - value, - ); - } - TypeDefKind::Variant(variant) => { - self.from_canon_variant( - id, - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - context, - value, - ); - } - TypeDefKind::Enum(en) => { - self.from_canon_variant( - id, - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - context, - value, - ); - } - TypeDefKind::Option(some) => { - self.from_canon_variant( - self.get_option_type(*some), - &abi::abi(self.resolve, ty), - [None, Some(*some)], - context, - value, - ); - } - TypeDefKind::Result(result) => { - self.from_canon_variant( - self.result_type.unwrap(), - &abi::abi(self.resolve, ty), - [result.ok, result.err], - context, - value, - ); - } - TypeDefKind::Flags(flags) => { - self.from_canon_record_onto_stack( - id, - flags.types().collect::>().into_iter(), - context, - value, - ); - } - TypeDefKind::Tuple(tuple) => { - self.from_canon_record_onto_stack( - *self.tuple_types.get(&tuple.types.len()).unwrap(), - tuple.types.iter().copied(), - context, - value, - ); - } - TypeDefKind::List(ty) => { - let source = value[0]; - let length = value[1]; - - let abi = abi::abi(self.resolve, *ty); - - if let Type::U8 | Type::S8 = ty { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(source)); - self.push(Ins::LocalGet(length)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#MakeBytes").unwrap(), - )); - } else { - let index = self.push_local(ValType::I32); - let element_source = self.push_local(ValType::I32); - let destination = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#MakeList").unwrap())); - self.push(Ins::LocalSet(destination)); - - self.push(Ins::I32Const(0)); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Loop(BlockType::Empty)); - - self.push(Ins::LocalGet(index)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Ne); - - self.push(Ins::If(BlockType::Empty)); - - self.push(Ins::LocalGet(source)); - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(element_source)); - - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(destination)); - - self.load(*ty, context, element_source); - - self.push(Ins::Call( - *IMPORTS.get("componentize-py#ListAppend").unwrap(), - )); - - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(1)); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Br(1)); - - self.push(Ins::End); - - self.push(Ins::End); - - self.push(Ins::LocalGet(destination)); - - self.pop_local(destination, ValType::I32); - self.pop_local(element_source, ValType::I32); - self.pop_local(index, ValType::I32); - } - } - TypeDefKind::Handle(handle) => { - self.marshal_handle(handle, context, value[0]); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonHandle").unwrap(), - )); - } - TypeDefKind::Type(ty) => self.from_canon(*ty, context, value), - kind => todo!("{kind:?}"), - }, - } - } - - fn from_canon_record( - &mut self, - types: impl IntoIterator, - context: u32, - source: &[u32], - destination: u32, - ) { - let mut from_canon_index = 0; - let mut store_offset = 0; - for ty in types { - let flat_count = abi::abi(self.resolve, ty).flattened.len(); - - self.push(Ins::LocalGet(destination)); - self.from_canon(ty, context, &source[from_canon_index..][..flat_count]); - self.push(Ins::I32Store(mem_arg( - store_offset, - WORD_ALIGN.try_into().unwrap(), - ))); - - from_canon_index += flat_count; - store_offset += u64::try_from(WORD_SIZE).unwrap(); - } - } - - fn from_canon_record_onto_stack( - &mut self, - id: TypeId, - types: impl ExactSizeIterator, - context: u32, - source: &[u32], - ) { - let len = types.len(); - self.push_stack(len * WORD_SIZE); - let destination = self.push_local(ValType::I32); - - self.get_stack(); - self.push(Ins::LocalSet(destination)); - - self.from_canon_record(types, context, source, destination); - - self.push(Ins::LocalGet(context)); - self.push(Ins::I32Const( - self.types.get_index_of(&id).unwrap().try_into().unwrap(), - )); - self.get_stack(); - self.push(Ins::I32Const(len.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Init").unwrap())); - - self.pop_local(destination, ValType::I32); - self.pop_stack(len * WORD_SIZE); - } - - fn from_canon_variant( - &mut self, - id: TypeId, - abi: &Abi, - types: impl IntoIterator>, - context: u32, - source: &[u32], - ) { - self.push_stack(WORD_SIZE * 2); - - let types = types.into_iter().collect::>(); - - self.push(Ins::LocalGet(context)); - self.push(Ins::I32Const( - self.types.get_index_of(&id).unwrap().try_into().unwrap(), - )); - self.get_stack(); - self.push(Ins::I32Const(2)); - - self.get_stack(); - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(source[0])); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonI32").unwrap(), - )); - self.push(Ins::I32Store(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - - self.get_stack(); - self.search_variant( - BlockType::Result(ValType::I32), - 0, - &types, - source[0], - |_, ty| ty.is_some(), - |this, ty| { - if let Some(ty) = ty { - let (source, locals) = this.convert_all(abi, ty, &source[1..]); - - this.from_canon(ty, context, &source); - - for (local, ty) in locals.into_iter().rev() { - this.pop_local(local, ty); - } - } else { - this.push(Ins::LocalGet(context)); - this.push(Ins::Call(*IMPORTS.get("componentize-py#None").unwrap())); - } - }, - ); - self.push(Ins::I32Store(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - - self.push(Ins::Call(*IMPORTS.get("componentize-py#Init").unwrap())); - - self.pop_stack(WORD_SIZE * 2); - } - - fn load(&mut self, ty: Type, context: u32, source: u32) { - match ty { - Type::Bool | Type::U8 => { - let value = self.push_local(ValType::I32); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load8U(mem_arg(0, 0))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I32); - } - Type::S8 => { - let value = self.push_local(ValType::I32); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load8S(mem_arg(0, 0))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I32); - } - Type::U16 => { - let value = self.push_local(ValType::I32); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load16U(mem_arg(0, 1))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I32); - } - Type::S16 => { - let value = self.push_local(ValType::I32); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load16S(mem_arg(0, 1))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I32); - } - Type::U32 | Type::S32 | Type::Char | Type::ErrorContext => { - let value = self.push_local(ValType::I32); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, 2))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I32); - } - Type::U64 | Type::S64 => { - let value = self.push_local(ValType::I64); - self.push(Ins::LocalGet(source)); - self.push(Ins::I64Load(mem_arg(0, 3))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I64); - } - Type::F32 => { - let value = self.push_local(ValType::F32); - self.push(Ins::LocalGet(source)); - self.push(Ins::F32Load(mem_arg(0, 2))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::F32); - } - Type::F64 => { - let value = self.push_local(ValType::F64); - self.push(Ins::LocalGet(source)); - self.push(Ins::F64Load(mem_arg(0, 3))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::F64); - } - Type::String => { - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonString").unwrap(), - )); - } - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.load_record_onto_stack( - id, - record.fields.iter().map(|f| f.ty), - context, - source, - ); - } - TypeDefKind::Variant(variant) => { - self.load_variant( - id, - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - context, - source, - ); - } - TypeDefKind::Enum(en) => { - self.load_variant( - id, - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - context, - source, - ); - } - TypeDefKind::Option(some) => { - self.load_variant( - self.get_option_type(*some), - &abi::abi(self.resolve, ty), - [None, Some(*some)], - context, - source, - ); - } - TypeDefKind::Result(result) => { - self.load_variant( - self.result_type.unwrap(), - &abi::abi(self.resolve, ty), - [result.ok, result.err], - context, - source, - ); - } - TypeDefKind::Flags(flags) => { - self.load_record_onto_stack( - id, - flags.types().collect::>().into_iter(), - context, - source, - ); - } - TypeDefKind::Tuple(tuple) => { - self.load_record_onto_stack( - *self.tuple_types.get(&tuple.types.len()).unwrap(), - tuple.types.iter().copied(), - context, - source, - ); - } - TypeDefKind::List(_) => { - let body = self.push_local(ValType::I32); - let length = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalSet(body)); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::LocalSet(length)); - - self.from_canon(ty, context, &[body, length]); - - self.pop_local(length, ValType::I32); - self.pop_local(body, ValType::I32); - } - TypeDefKind::Handle(_) => { - let value = self.push_local(ValType::I32); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, 2))); - self.push(Ins::LocalSet(value)); - self.from_canon(ty, context, &[value]); - self.pop_local(value, ValType::I32); - } - TypeDefKind::Type(ty) => self.load(*ty, context, source), - kind => todo!("{kind:?}"), - }, - } - } - - fn load_record( - &mut self, - types: impl IntoIterator, - context: u32, - source: u32, - destination: u32, - ) { - let mut load_offset = 0; - let mut store_offset = 0; - for ty in types { - let field_source = self.push_local(ValType::I32); - - let abi = abi::abi(self.resolve, ty); - load_offset = abi::align(load_offset, abi.align); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Const(load_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(field_source)); - self.push(Ins::LocalGet(destination)); - self.load(ty, context, field_source); - self.push(Ins::I32Store(mem_arg( - store_offset, - WORD_ALIGN.try_into().unwrap(), - ))); - - load_offset += abi.size; - store_offset += u64::try_from(WORD_SIZE).unwrap(); - - self.pop_local(field_source, ValType::I32); - } - } - - fn load_record_onto_stack( - &mut self, - id: TypeId, - types: impl ExactSizeIterator, - context: u32, - source: u32, - ) { - let len = types.len(); - self.push_stack(len * WORD_SIZE); - let destination = self.push_local(ValType::I32); - - self.get_stack(); - self.push(Ins::LocalSet(destination)); - - self.load_record(types, context, source, destination); - - self.push(Ins::LocalGet(context)); - self.push(Ins::I32Const( - self.types.get_index_of(&id).unwrap().try_into().unwrap(), - )); - self.get_stack(); - self.push(Ins::I32Const(len.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Init").unwrap())); - - self.pop_local(destination, ValType::I32); - self.pop_stack(len * WORD_SIZE); - } - - fn load_variant( - &mut self, - id: TypeId, - abi: &Abi, - types: impl IntoIterator>, - context: u32, - source: u32, - ) { - self.push_stack(WORD_SIZE * 2); - - let types = types.into_iter().collect::>(); - let discriminant_size = abi::discriminant_size(types.len()); - let discriminant = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(context)); - self.push(Ins::I32Const( - self.types.get_index_of(&id).unwrap().try_into().unwrap(), - )); - self.get_stack(); - self.push(Ins::I32Const(2)); - - self.get_stack(); - self.push(Ins::LocalGet(context)); - self.push(Ins::LocalGet(source)); - match discriminant_size { - 1 => self.push(Ins::I32Load8U(mem_arg(0, 0))), - 2 => self.push(Ins::I32Load16U(mem_arg(0, 1))), - 4 => self.push(Ins::I32Load(mem_arg(0, 2))), - _ => unreachable!(), - } - self.push(Ins::LocalTee(discriminant)); - self.push(Ins::Call( - *IMPORTS.get("componentize-py#FromCanonI32").unwrap(), - )); - self.push(Ins::I32Store(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - - self.get_stack(); - if types.iter().any(Option::is_some) { - let payload_source = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Const( - abi::align(discriminant_size, abi.align).try_into().unwrap(), - )); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(payload_source)); - - self.search_variant( - BlockType::Result(ValType::I32), - 0, - &types, - discriminant, - |_, ty| ty.is_some(), - |this, ty| { - if let Some(ty) = ty { - this.load(ty, context, payload_source); - } else { - this.push(Ins::LocalGet(context)); - this.push(Ins::Call(*IMPORTS.get("componentize-py#None").unwrap())); - } - }, - ); - - self.pop_local(payload_source, ValType::I32); - } else { - self.push(Ins::LocalGet(context)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#None").unwrap())); - } - self.push(Ins::I32Store(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - - self.push(Ins::Call(*IMPORTS.get("componentize-py#Init").unwrap())); - - self.pop_stack(WORD_SIZE * 2); - self.pop_local(discriminant, ValType::I32); - } - - fn load_copy(&mut self, ty: Type, source: u32) { - match ty { - Type::Bool | Type::U8 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load8U(mem_arg(0, 0))); - } - Type::S8 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load8S(mem_arg(0, 0))); - } - Type::U16 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load16U(mem_arg(0, 1))); - } - Type::S16 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load16S(mem_arg(0, 1))); - } - Type::U32 | Type::S32 | Type::Char | Type::ErrorContext => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, 2))); - } - Type::U64 | Type::S64 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I64Load(mem_arg(0, 3))); - } - Type::F32 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::F32Load(mem_arg(0, 2))); - } - Type::F64 => { - self.push(Ins::LocalGet(source)); - self.push(Ins::F64Load(mem_arg(0, 3))); - } - Type::String => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - } - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.load_copy_record(record.fields.iter().map(|f| f.ty), source); - } - TypeDefKind::Variant(variant) => { - self.load_copy_variant( - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - source, - ); - } - TypeDefKind::Enum(en) => { - self.load_copy_variant( - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - source, - ); - } - TypeDefKind::Option(some) => { - self.load_copy_variant( - &abi::abi(self.resolve, ty), - [None, Some(*some)], - source, - ); - } - TypeDefKind::Result(result) => { - self.load_copy_variant( - &abi::abi(self.resolve, ty), - [result.ok, result.err], - source, - ); - } - TypeDefKind::Flags(flags) => { - self.load_copy_record(flags.types(), source); - } - TypeDefKind::Tuple(tuple) => { - self.load_copy_record(tuple.types.iter().copied(), source); - } - TypeDefKind::List(_) => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - } - TypeDefKind::Handle(_) => { - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Load(mem_arg(0, 2))); - } - TypeDefKind::Type(ty) => self.load_copy(*ty, source), - kind => todo!("{kind:?}"), - }, - } - } - - fn load_copy_record(&mut self, types: impl IntoIterator, source: u32) { - let mut load_offset = 0; - for ty in types { - let field_source = self.push_local(ValType::I32); - - let abi = abi::abi(self.resolve, ty); - load_offset = abi::align(load_offset, abi.align); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Const(load_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(field_source)); - - self.load_copy(ty, field_source); - - load_offset += abi.size; - - self.pop_local(field_source, ValType::I32); - } - } - - fn zero(&mut self, ty: ValType) { - self.push(match ty { - ValType::I32 => Ins::I32Const(0), - ValType::I64 => Ins::I64Const(0), - ValType::F32 => Ins::F32Const(0.0.into()), - ValType::F64 => Ins::F64Const(0.0.into()), - _ => unreachable!(), - }) - } - - fn load_copy_variant( - &mut self, - abi: &Abi, - types: impl IntoIterator>, - source: u32, - ) { - let types = types.into_iter().collect::>(); - let discriminant_size = abi::discriminant_size(types.len()); - - self.push(Ins::LocalGet(source)); - match discriminant_size { - 1 => self.push(Ins::I32Load8U(mem_arg(0, 0))), - 2 => self.push(Ins::I32Load16U(mem_arg(0, 1))), - 4 => self.push(Ins::I32Load(mem_arg(0, 2))), - _ => unreachable!(), - } - - if types.iter().any(Option::is_some) { - let discriminant = self.push_local(ValType::I32); - let payload_source = self.push_local(ValType::I32); - let destination = abi - .flattened - .iter() - .skip(1) - .map(|&ty| self.push_local(ty)) - .collect::>(); - - self.push(Ins::LocalTee(discriminant)); - - self.push(Ins::LocalGet(source)); - self.push(Ins::I32Const( - abi::align(discriminant_size, abi.align).try_into().unwrap(), - )); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(payload_source)); - - self.search_variant( - BlockType::Empty, - 0, - &types, - discriminant, - |_, ty| ty.is_some(), - |this, ty| { - if let Some(ty) = ty { - this.load_copy(ty, payload_source); - - let payload_abi = abi::abi(this.resolve, ty); - for ((payload_type, joined_type), local) in payload_abi - .flattened - .iter() - .zip(abi.flattened.iter().skip(1)) - .zip(&destination) - .rev() - { - if payload_type != joined_type { - this.convert(*payload_type, *joined_type); - } - this.push(Ins::LocalSet(*local)); - } - - for (joined_type, local) in abi - .flattened - .iter() - .skip(1) - .zip(&destination) - .skip(payload_abi.flattened.len()) - { - this.zero(*joined_type); - this.push(Ins::LocalSet(*local)); - } - } - }, - ); - - for &local in &destination { - self.push(Ins::LocalGet(local)); - } - - for (local, ty) in destination - .into_iter() - .zip(abi.flattened.iter().skip(1)) - .rev() - { - self.pop_local(local, *ty); - } - self.pop_local(payload_source, ValType::I32); - self.pop_local(discriminant, ValType::I32); - } - } - - fn free_canon(&mut self, ty: Type, value: &[u32]) { - match ty { - Type::Bool - | Type::U8 - | Type::U16 - | Type::U32 - | Type::S8 - | Type::S16 - | Type::S32 - | Type::Char - | Type::U64 - | Type::S64 - | Type::F32 - | Type::F64 - | Type::ErrorContext => {} - - Type::String => { - self.push(Ins::LocalGet(value[0])); - self.push(Ins::LocalGet(value[1])); - self.push(Ins::I32Const(1)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Free").unwrap())); - } - - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.free_canon_record(record.fields.iter().map(|f| f.ty), value); - } - TypeDefKind::Variant(variant) => { - self.free_canon_variant( - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - value, - ); - } - TypeDefKind::Enum(en) => { - self.free_canon_variant( - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - value, - ); - } - TypeDefKind::Option(some) => { - self.free_canon_variant( - &abi::abi(self.resolve, ty), - [None, Some(*some)], - value, - ); - } - TypeDefKind::Result(result) => { - self.free_canon_variant( - &abi::abi(self.resolve, ty), - [result.ok, result.err], - value, - ); - } - TypeDefKind::Flags(flags) => { - self.free_canon_record(flags.types(), value); - } - TypeDefKind::Tuple(tuple) => { - self.free_canon_record(tuple.types.iter().copied(), value); - } - TypeDefKind::List(ty) => { - let pointer = value[0]; - let length = value[1]; - - let abi = abi::abi(self.resolve, *ty); - - if abi::has_pointer(self.resolve, *ty) { - let index = self.push_local(ValType::I32); - let element_pointer = self.push_local(ValType::I32); - - self.push(Ins::I32Const(0)); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Loop(BlockType::Empty)); - - self.push(Ins::LocalGet(index)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Ne); - - self.push(Ins::If(BlockType::Empty)); - - self.push(Ins::LocalGet(pointer)); - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(element_pointer)); - - self.free_stored(*ty, element_pointer); - - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(1)); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Br(1)); - - self.push(Ins::End); - - self.push(Ins::End); - - self.pop_local(element_pointer, ValType::I32); - self.pop_local(index, ValType::I32); - } - - self.push(Ins::LocalGet(pointer)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Const(abi.align.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Free").unwrap())); - } - TypeDefKind::Handle(_) => {} - TypeDefKind::Type(ty) => self.free_canon(*ty, value), - kind => todo!("{kind:?}"), - }, - } - } - - fn free_canon_record(&mut self, types: impl IntoIterator, value: &[u32]) { - let mut from_canon_index = 0; - for ty in types { - let flat_count = abi::abi(self.resolve, ty).flattened.len(); - - self.free_canon(ty, &value[from_canon_index..][..flat_count]); - - from_canon_index += flat_count; - } - } - - fn free_canon_variant( - &mut self, - abi: &Abi, - types: impl IntoIterator>, - value: &[u32], - ) { - self.search_variant( - BlockType::Empty, - 0, - &types.into_iter().collect::>(), - value[0], - |this, ty| { - ty.map(|ty| abi::has_pointer(this.resolve, ty)) - .unwrap_or(false) - }, - |this, ty| { - if let Some(ty) = ty { - let (value, locals) = this.convert_all(abi, ty, &value[1..]); - - this.free_canon(ty, &value); - - for (local, ty) in locals.into_iter().rev() { - this.pop_local(local, ty); - } - } - }, - ) - } - - fn free_stored(&mut self, ty: Type, value: u32) { - match ty { - Type::Bool - | Type::U8 - | Type::U16 - | Type::U32 - | Type::S8 - | Type::S16 - | Type::S32 - | Type::Char - | Type::U64 - | Type::S64 - | Type::F32 - | Type::F64 - | Type::ErrorContext => {} - - Type::String => { - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::I32Const(1)); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Free").unwrap())); - } - - Type::Id(id) => match &self.resolve.types[id].kind { - TypeDefKind::Record(record) => { - self.free_stored_record(record.fields.iter().map(|f| f.ty), value); - } - TypeDefKind::Variant(variant) => { - self.free_stored_variant( - &abi::abi(self.resolve, ty), - variant.cases.iter().map(|c| c.ty), - value, - ); - } - TypeDefKind::Enum(en) => { - self.free_stored_variant( - &abi::abi(self.resolve, ty), - en.cases.iter().map(|_| None), - value, - ); - } - TypeDefKind::Option(some) => { - self.free_stored_variant( - &abi::abi(self.resolve, ty), - [None, Some(*some)], - value, - ); - } - TypeDefKind::Result(result) => { - self.free_stored_variant( - &abi::abi(self.resolve, ty), - [result.ok, result.err], - value, - ); - } - TypeDefKind::Flags(flags) => { - self.free_stored_record(flags.types(), value); - } - TypeDefKind::Tuple(tuple) => { - self.free_stored_record(tuple.types.iter().copied(), value); - } - TypeDefKind::List(ty) => { - let abi = abi::abi(self.resolve, *ty); - - let body = self.push_local(ValType::I32); - let length = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Load(mem_arg(0, WORD_ALIGN.try_into().unwrap()))); - self.push(Ins::LocalSet(body)); - - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Load(mem_arg( - WORD_SIZE.try_into().unwrap(), - WORD_ALIGN.try_into().unwrap(), - ))); - self.push(Ins::LocalSet(length)); - - if abi::has_pointer(self.resolve, *ty) { - let index = self.push_local(ValType::I32); - let element_value = self.push_local(ValType::I32); - - self.push(Ins::I32Const(0)); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Loop(BlockType::Empty)); - - self.push(Ins::LocalGet(index)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Ne); - - self.push(Ins::If(BlockType::Empty)); - - self.push(Ins::LocalGet(body)); - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(element_value)); - - self.free_stored(*ty, element_value); - - self.push(Ins::LocalGet(index)); - self.push(Ins::I32Const(1)); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(index)); - - self.push(Ins::Br(1)); - - self.push(Ins::End); - - self.push(Ins::End); - - self.pop_local(element_value, ValType::I32); - self.pop_local(index, ValType::I32); - } - - self.push(Ins::LocalGet(body)); - self.push(Ins::LocalGet(length)); - self.push(Ins::I32Const(abi.size.try_into().unwrap())); - self.push(Ins::I32Mul); - self.push(Ins::I32Const(abi.align.try_into().unwrap())); - self.push(Ins::Call(*IMPORTS.get("componentize-py#Free").unwrap())); - - self.pop_local(length, ValType::I32); - self.pop_local(body, ValType::I32); - } - TypeDefKind::Handle(_) => {} - TypeDefKind::Type(ty) => self.free_stored(*ty, value), - kind => todo!("{kind:?}"), - }, - } - } - - fn free_stored_record(&mut self, types: impl IntoIterator, value: u32) { - let types = types.into_iter().collect::>(); - - let mut load_offset = 0; - for ty in types { - let abi = abi::abi(self.resolve, ty); - load_offset = abi::align(load_offset, abi.align); - - if abi::has_pointer(self.resolve, ty) { - let field_value = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const(load_offset.try_into().unwrap())); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(field_value)); - - self.free_stored(ty, field_value); - - self.pop_local(field_value, ValType::I32); - } - - load_offset += abi.size; - } - } - - fn free_stored_variant( - &mut self, - abi: &Abi, - types: impl IntoIterator>, - value: u32, - ) { - let types = types.into_iter().collect::>(); - let discriminant_size = abi::discriminant_size(types.len()); - let predicate = |this: &Self, ty: Option| { - ty.map(|ty| abi::has_pointer(this.resolve, ty)) - .unwrap_or(false) - }; - - if types.iter().any(|ty| predicate(self, *ty)) { - let discriminant = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(value)); - match discriminant_size { - 1 => self.push(Ins::I32Load8U(mem_arg(0, 0))), - 2 => self.push(Ins::I32Load16U(mem_arg(0, 1))), - 4 => self.push(Ins::I32Load(mem_arg(0, 2))), - _ => unreachable!(), - } - self.push(Ins::LocalSet(discriminant)); - - let payload_value = self.push_local(ValType::I32); - - self.push(Ins::LocalGet(value)); - self.push(Ins::I32Const( - abi::align(discriminant_size, abi.align).try_into().unwrap(), - )); - self.push(Ins::I32Add); - self.push(Ins::LocalSet(payload_value)); - - self.search_variant( - BlockType::Empty, - 0, - &types, - discriminant, - predicate, - |this, ty| { - if let Some(ty) = ty { - this.free_stored(ty, payload_value); - } - }, - ); - - self.pop_local(payload_value, ValType::I32); - self.pop_local(discriminant, ValType::I32); - } - } - - fn get_option_type(&self, some: Type) -> TypeId { - if abi::is_option(self.resolve, some) { - self.nesting_option_type.unwrap() - } else { - self.option_type.unwrap() - } - } -} - -pub fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId { - loop { - match &resolve.types[id].kind { - TypeDefKind::Type(Type::Id(that_id)) => id = *that_id, - _ => break id, - } - } -} diff --git a/src/bindings.rs b/src/bindings.rs deleted file mode 100644 index 0fc0918..0000000 --- a/src/bindings.rs +++ /dev/null @@ -1,396 +0,0 @@ -use { - crate::{ - bindgen::{ - DISPATCH_CORE_PARAM_COUNT, DISPATCHABLE_CORE_PARAM_COUNT, FunctionBindgen, - IMPORT_SIGNATURES, IMPORTS, - }, - summary::{FunctionKind, Summary}, - }, - anyhow::Result, - indexmap::IndexSet, - std::borrow::Cow, - wasm_encoder::{ - CodeSection, ConstExpr, CustomSection, ElementSection, Elements, Encode, EntityType, - ExportKind, ExportSection, Function, FunctionSection, GlobalType, ImportSection, - Instruction as Ins, MemoryType, Module, RefType, TableType, TypeSection, ValType, - }, - wit_component::metadata, - wit_parser::{Resolve, WorldId}, -}; - -const WASM_DYLINK_MEM_INFO: u8 = 1; -const WASM_DYLINK_NEEDED: u8 = 2; - -struct MemInfo { - memory_size: u32, - memory_alignment: u32, - table_size: u32, - table_alignment: u32, -} - -pub fn make_bindings( - resolve: &Resolve, - worlds: &IndexSet, - summary: &Summary, -) -> Result> { - // TODO: deduplicate types - let mut types = TypeSection::new(); - let mut imports = ImportSection::new(); - let mut functions = FunctionSection::new(); - let mut exports = ExportSection::new(); - let mut code = CodeSection::new(); - let mut function_names = Vec::new(); - let mut global_names = Vec::new(); - - for (name, params, results) in IMPORT_SIGNATURES { - let offset = types.len(); - types - .ty() - .function(params.iter().copied(), results.iter().copied()); - imports.import("env", name, EntityType::Function(offset)); - function_names.push((offset, (*name).to_owned())); - } - - for function in summary.functions.iter().filter(|f| { - matches!( - f.kind, - FunctionKind::Import - | FunctionKind::ResourceNew - | FunctionKind::ResourceRep - | FunctionKind::ResourceDropLocal - | FunctionKind::ResourceDropRemote - ) - }) { - let module = &function - .interface - .as_ref() - .map(|interface| { - format!( - "{}{}", - if matches!( - function.kind, - FunctionKind::Import | FunctionKind::ResourceDropRemote - ) { - "" - } else { - "[export]" - }, - if let Some(name) = resolve.id_of(interface.id) { - name - } else { - interface.name.to_owned() - } - ) - }) - .unwrap_or_else(|| "$root".to_owned()); - - let name = function.name; - let name = &match function.kind { - FunctionKind::ResourceNew => format!("[resource-new]{name}"), - FunctionKind::ResourceRep => format!("[resource-rep]{name}"), - FunctionKind::ResourceDropLocal | FunctionKind::ResourceDropRemote => { - format!("[resource-drop]{name}") - } - _ => name.to_owned(), - }; - - let (params, results) = function.core_import_type(resolve); - let offset = types.len(); - - types.ty().function(params, results); - imports.import(module, name, EntityType::Function(offset)); - function_names.push(( - offset, - format!("{}-imported", function.internal_name(resolve)), - )); - } - - let import_function_count = imports.len(); - - let table_base = 0; - imports.import( - "env", - "__table_base", - EntityType::Global(GlobalType { - val_type: ValType::I32, - mutable: false, - shared: false, - }), - ); - global_names.push((table_base, "__table_base".to_owned())); - - let stack_pointer = 1; - imports.import( - "env", - "__stack_pointer", - EntityType::Global(GlobalType { - val_type: ValType::I32, - mutable: true, - shared: false, - }), - ); - global_names.push((stack_pointer, "__stack_pointer".to_owned())); - - imports.import( - "env", - "memory", - EntityType::Memory(MemoryType { - minimum: 0, - maximum: None, - memory64: false, - shared: false, - page_size_log2: None, - }), - ); - - imports.import( - "env", - "__indirect_function_table", - EntityType::Table(TableType { - element_type: RefType::FUNCREF, - minimum: summary - .functions - .iter() - .filter(|function| function.is_dispatchable()) - .count() - .try_into() - .unwrap(), - maximum: None, - table64: false, - shared: false, - }), - ); - - let export_set = summary - .functions - .iter() - .filter_map(|f| { - if let FunctionKind::Export = f.kind { - Some((f.interface.as_ref().map(|i| i.name), f.name)) - } else { - None - } - }) - .collect::>(); - - let mut import_index = IMPORT_SIGNATURES.len(); - let mut dispatch_index = 0; - for (index, function) in summary.functions.iter().enumerate() { - let offset = types.len(); - let (params, results) = function.core_export_type(resolve); - types.ty().function(params, results); - functions.function(offset); - function_names.push((offset, function.internal_name(resolve))); - let mut r#gen = FunctionBindgen::new(summary, function, stack_pointer); - - match function.kind { - FunctionKind::Import => { - r#gen.compile_import(import_index.try_into().unwrap()); - import_index += 1; - } - FunctionKind::Export => r#gen.compile_export( - export_set - .get_index_of(&(function.interface.as_ref().map(|i| i.name), function.name)) - .unwrap() - .try_into()?, - // The next two `dispatch_index`es should be the from_canon and to_canon functions (see ordering in - // `Summary::visit_function`): - dispatch_index, - dispatch_index + 1, - ), - FunctionKind::ExportFromCanon => r#gen.compile_export_from_canon(), - FunctionKind::ExportToCanon => r#gen.compile_export_to_canon(), - FunctionKind::ExportPostReturn => r#gen.compile_export_post_return(), - FunctionKind::ResourceNew => { - r#gen.compile_resource_new(import_index.try_into().unwrap()); - import_index += 1; - } - FunctionKind::ResourceRep => { - r#gen.compile_resource_rep(import_index.try_into().unwrap()); - import_index += 1; - } - FunctionKind::ResourceDropLocal | FunctionKind::ResourceDropRemote => { - r#gen.compile_resource_drop(import_index.try_into().unwrap()); - import_index += 1; - } - }; - - let mut func = Function::new_with_locals_types(r#gen.local_types); - for instruction in &r#gen.instructions { - func.instruction(instruction); - } - func.instruction(&Ins::End); - code.function(&func); - - if function.is_dispatchable() { - dispatch_index += 1; - } - - match function.kind { - FunctionKind::Export | FunctionKind::ExportPostReturn => { - exports.export( - &format!( - "{}{}", - if let FunctionKind::ExportPostReturn = function.kind { - "cabi_post_" - } else { - "" - }, - if let Some(interface) = &function.interface { - format!( - "{}#{}", - if let Some(name) = resolve.id_of(interface.id) { - name - } else { - interface.name.to_owned() - }, - function.name - ) - } else { - function.name.to_owned() - } - ), - ExportKind::Func, - import_function_count + u32::try_from(index).unwrap(), - ); - } - - _ => (), - } - } - - { - let dispatch_offset = types.len(); - types - .ty() - .function([ValType::I32; DISPATCH_CORE_PARAM_COUNT], []); - let dispatchable_offset = types.len(); - types - .ty() - .function([ValType::I32; DISPATCHABLE_CORE_PARAM_COUNT], []); - functions.function(dispatch_offset); - let name = "componentize-py#CallIndirect"; - function_names.push((dispatch_offset, name.to_owned())); - let mut dispatch = Function::new([]); - - for local in 0..DISPATCH_CORE_PARAM_COUNT { - dispatch.instruction(&Ins::LocalGet(u32::try_from(local).unwrap())); - } - dispatch.instruction(&Ins::GlobalGet(table_base)); - dispatch.instruction(&Ins::I32Add); - dispatch.instruction(&Ins::CallIndirect { - type_index: dispatchable_offset, - table_index: 0, - }); - dispatch.instruction(&Ins::End); - - code.function(&dispatch); - - exports.export(name, ExportKind::Func, dispatch_offset); - } - - exports.export( - "cabi_import_realloc", - ExportKind::Func, - *IMPORTS.get("cabi_realloc").unwrap(), - ); - - exports.export( - "cabi_export_realloc", - ExportKind::Func, - *IMPORTS.get("cabi_realloc").unwrap(), - ); - - let mut elements = ElementSection::new(); - elements.active( - Some(0), - &ConstExpr::global_get(table_base), - Elements::Functions( - summary - .functions - .iter() - .enumerate() - .filter_map(|(index, function)| { - function - .is_dispatchable() - .then_some(import_function_count + u32::try_from(index).unwrap()) - }) - .collect::>() - .into(), - ), - ); - - let mut names_data = Vec::new(); - for (code, names) in [(0x01_u8, &function_names), (0x07_u8, &global_names)] { - let mut subsection = Vec::new(); - names.len().encode(&mut subsection); - for (index, name) in names { - index.encode(&mut subsection); - name.encode(&mut subsection); - } - names_data.push(code); - subsection.encode(&mut names_data); - } - - let mem_info = MemInfo { - memory_size: 0, - memory_alignment: 0, - table_size: summary - .functions - .iter() - .filter(|function| function.is_dispatchable()) - .count() - .try_into() - .unwrap(), - table_alignment: 0, - }; - - let mut mem_info_subsection = Vec::new(); - mem_info.memory_size.encode(&mut mem_info_subsection); - mem_info.memory_alignment.encode(&mut mem_info_subsection); - mem_info.table_size.encode(&mut mem_info_subsection); - mem_info.table_alignment.encode(&mut mem_info_subsection); - - let mut needed_subsection = Vec::new(); - 1_u32.encode(&mut needed_subsection); - "libcomponentize_py_runtime.so".encode(&mut needed_subsection); - - let mut dylink0 = Vec::new(); - dylink0.push(WASM_DYLINK_MEM_INFO); - mem_info_subsection.encode(&mut dylink0); - dylink0.push(WASM_DYLINK_NEEDED); - needed_subsection.encode(&mut dylink0); - - let mut result = Module::new(); - result.section(&CustomSection { - name: Cow::Borrowed("dylink.0"), - data: Cow::Borrowed(&dylink0), - }); - result.section(&types); - result.section(&imports); - result.section(&functions); - result.section(&exports); - result.section(&elements); - result.section(&code); - result.section(&CustomSection { - name: Cow::Borrowed("name"), - data: Cow::Borrowed(&names_data), - }); - for &world in worlds { - result.section(&CustomSection { - name: Cow::Owned(format!("component-type:{}", resolve.worlds[world].name)), - data: Cow::Owned(metadata::encode( - resolve, - world, - wit_component::StringEncoding::UTF8, - None, - )?), - }); - } - - let result = result.finish(); - - wasmparser::validate(&result)?; - - Ok(result) -} diff --git a/src/lib.rs b/src/lib.rs index 46f97a2..babbf8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ use { indexmap::{IndexMap, IndexSet}, serde::Deserialize, std::{ + borrow::Cow, collections::HashMap, fs, iter, ops::Deref, @@ -17,6 +18,7 @@ use { str, }, summary::{Escape, Locations, Summary}, + wasm_encoder::{CustomSection, Section as _}, wasmtime::{ Config, Engine, Store, component::{Component, Instance, Linker, ResourceTable, ResourceType}, @@ -25,12 +27,14 @@ use { DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView, p2::pipe::{MemoryInputPipe, MemoryOutputPipe}, }, - wit_parser::{Resolve, TypeDefKind, UnresolvedPackageGroup, WorldId, WorldItem, WorldKey}, + wit_component::metadata, + wit_dylib::DylibOpts, + wit_parser::{ + CloneMaps, Package, PackageName, Resolve, Stability, TypeDefKind, UnresolvedPackageGroup, + World, WorldId, WorldItem, WorldKey, + }, }; -mod abi; -mod bindgen; -mod bindings; pub mod command; mod link; mod prelink; @@ -50,7 +54,7 @@ static DEFAULT_WORLD_MODULE: &str = "wit_world"; wasmtime::component::bindgen!({ path: "wit", world: "init", - exports: { default: async } + exports: { default: async }, }); pub struct Ctx { @@ -189,11 +193,13 @@ pub fn generate_bindings( // `componentize` function below, since that can affect the bindings we should be generating. let (resolve, world) = parse_wit(wit_path, world, features, all_features)?; + let import_function_indexes = &HashMap::new(); let summary = Summary::try_new( &resolve, &iter::once(world).collect(), import_interface_names, export_interface_names, + import_function_indexes, )?; let world_module = world_module.unwrap_or(DEFAULT_WORLD_MODULE); let world_dir = output_dir.join(world_module.replace('.', "/")); @@ -296,7 +302,7 @@ pub async fn componentize( }) .collect::>>()?; - let resolve = if let Some(resolve) = resolve { + let mut resolve = if let Some(resolve) = resolve { resolve } else { // If no WIT directory was provided as a parameter and none were referenced by Python packages, use @@ -304,7 +310,7 @@ pub async fn componentize( let paths: &[&Path] = &[]; let (my_resolve, world) = parse_wit(paths, world, features, all_features).context( "no WIT files found; please specify the directory or file \ - containing the WIT world you wish to target", + containing the WIT world you wish to target", )?; main_world = Some(world); my_resolve @@ -328,16 +334,75 @@ pub async fn componentize( ); } + let union_package = resolve.packages.alloc(Package { + name: PackageName { + namespace: "componentize-py".into(), + name: "union".into(), + version: None, + }, + docs: Default::default(), + interfaces: Default::default(), + worlds: Default::default(), + }); + + let union_world = resolve.worlds.alloc(World { + name: "union".into(), + imports: Default::default(), + exports: Default::default(), + package: Some(union_package), + docs: Default::default(), + stability: Stability::Unknown, + includes: Default::default(), + include_names: Default::default(), + }); + + resolve.packages[union_package] + .worlds + .insert("union".into(), union_world); + + let mut clone_maps = CloneMaps::default(); + for &world in &worlds { + resolve.merge_worlds(world, union_world, &mut clone_maps)?; + } + + let (mut bindings, metadata) = wit_dylib::create_with_metadata( + &resolve, + union_world, + Some(&mut DylibOpts { + interpreter: Some("libcomponentize_py_runtime.so".into()), + async_: Default::default(), + }), + ); + + CustomSection { + name: Cow::Borrowed("component-type:componentize-py-union"), + data: Cow::Owned(metadata::encode( + &resolve, + union_world, + wit_component::StringEncoding::UTF8, + None, + )?), + } + .append_to(&mut bindings); + + let imported_function_indexes = metadata + .import_funcs + .iter() + .enumerate() + .map(|(index, func)| ((func.interface.as_deref(), func.name.as_str()), index)) + .collect::>(); + let summary = Summary::try_new( &resolve, &worlds, &import_interface_names, &export_interface_names, + &imported_function_indexes, )?; libraries.push(Library { name: "libcomponentize_py_bindings.so".into(), - module: bindings::make_bindings(&resolve, &worlds, &summary)?, + module: bindings, dl_openable: false, }); @@ -362,7 +427,6 @@ pub async fn componentize( .stdout(stdout.clone()) .stderr(stderr.clone()) .env("PYTHONUNBUFFERED", "1") - .env("COMPONENTIZE_PY_APP_NAME", app_name) .env("PYTHONHOME", "/python") .preopened_dir( embedded_python_standard_lib.path(), @@ -479,7 +543,7 @@ pub async fn componentize( // Generate a `Symbols` object containing metadata to be passed to the pre-init function. The runtime library // will use this to look up types and functions that will later be referenced by the generated Wasm code. - let symbols = summary.collect_symbols(&locations); + let symbols = summary.collect_symbols(&locations, &metadata, &clone_maps); // Finally, pre-initialize the component, writing the result to `output_path`. diff --git a/src/summary.rs b/src/summary.rs index dcf6ff8..9aba42b 100644 --- a/src/summary.rs +++ b/src/summary.rs @@ -1,17 +1,14 @@ use { crate::{ - abi::{self, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS}, - bindgen::{self, DISPATCHABLE_CORE_PARAM_COUNT}, exports::exports::{ - self, Case, Constructor, Function, FunctionExport, LocalResource, OwnedKind, OwnedType, - RemoteResource, Resource, Static, Symbols, + self, Case, Constructor, Function, FunctionExport, FunctionExportKind, ReturnStyle, + Static, Symbols, }, util::Types as _, }, anyhow::{Result, bail}, heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}, indexmap::{IndexMap, IndexSet}, - once_cell::sync, semver::Version, std::{ collections::{HashMap, HashSet, hash_map::Entry}, @@ -19,14 +16,13 @@ use { fs::{self, File}, io::Write as _, iter, - ops::Deref, path::Path, str, }, - wasm_encoder::ValType, + wit_dylib::Metadata, wit_parser::{ - Handle, InterfaceId, Resolve, Result_, Type, TypeDefKind, TypeId, TypeOwner, WorldId, - WorldItem, WorldKey, + CloneMaps, Handle, InterfaceId, Resolve, Result_, Type, TypeDef, TypeDefKind, TypeId, + TypeOwner, WorldId, WorldItem, WorldKey, }, }; @@ -40,27 +36,19 @@ pub enum Direction { #[derive(Default, Copy, Clone)] struct ResourceInfo { - local_dispatch_index: Option, - remote_dispatch_index: Option, + local: bool, + remote: bool, } #[derive(Clone)] -struct ResourceState<'a> { +struct ResourceState { direction: Direction, - interface: Option>, } #[derive(Copy, Clone)] pub enum FunctionKind { Import, - ResourceNew, - ResourceRep, - ResourceDropLocal, - ResourceDropRemote, Export, - ExportFromCanon, - ExportToCanon, - ExportPostReturn, } #[derive(Copy, Clone)] @@ -73,9 +61,8 @@ pub struct PackageName<'a> { #[derive(Clone)] pub struct MyInterface<'a> { pub id: InterfaceId, - pub name: &'a str, + pub key: &'a WorldKey, pub docs: Option<&'a str>, - pub resource_directions: im_rc::HashMap, } pub struct MyFunction<'a> { @@ -96,80 +83,6 @@ impl MyFunction<'_> { WorldKey::Name(self.name.into()) } } - - pub fn internal_name(&self, resolve: &Resolve) -> String { - if let Some(interface) = &self.interface { - format!( - "{}#{}{}", - if let Some(name) = resolve.id_of(interface.id) { - name - } else { - interface.name.to_owned() - }, - self.name, - match self.kind { - FunctionKind::Import => "-import", - FunctionKind::ResourceNew => "-resource-new", - FunctionKind::ResourceRep => "-resource-rep", - FunctionKind::ResourceDropLocal => "-resource-drop-local", - FunctionKind::ResourceDropRemote => "-resource-drop-remote", - FunctionKind::Export => "-export", - FunctionKind::ExportFromCanon => "-from-canon", - FunctionKind::ExportToCanon => "-to-canon", - FunctionKind::ExportPostReturn => "-post-return", - } - ) - } else { - self.name.to_owned() - } - } - - pub fn core_import_type(&self, resolve: &Resolve) -> (Vec, Vec) { - let mut params = - abi::record_abi_limit(resolve, self.params.types(), MAX_FLAT_PARAMS).flattened; - - let mut results = abi::record_abi(resolve, self.result.types()).flattened; - - if results.len() > MAX_FLAT_RESULTS { - params.push(ValType::I32); - results = Vec::new(); - }; - - (params, results) - } - - pub fn core_export_type(&self, resolve: &Resolve) -> (Vec, Vec) { - match self.kind { - FunctionKind::Export => ( - abi::record_abi_limit(resolve, self.params.types(), MAX_FLAT_PARAMS).flattened, - abi::record_abi_limit(resolve, self.result.types(), MAX_FLAT_RESULTS).flattened, - ), - FunctionKind::Import - | FunctionKind::ResourceNew - | FunctionKind::ResourceRep - | FunctionKind::ResourceDropLocal - | FunctionKind::ResourceDropRemote - | FunctionKind::ExportFromCanon - | FunctionKind::ExportToCanon => ( - vec![ValType::I32; DISPATCHABLE_CORE_PARAM_COUNT], - Vec::new(), - ), - FunctionKind::ExportPostReturn => (vec![ValType::I32], Vec::new()), - } - } - - pub fn is_dispatchable(&self) -> bool { - match self.kind { - FunctionKind::Import - | FunctionKind::ResourceNew - | FunctionKind::ResourceRep - | FunctionKind::ResourceDropLocal - | FunctionKind::ResourceDropRemote - | FunctionKind::ExportFromCanon - | FunctionKind::ExportToCanon => true, - FunctionKind::Export | FunctionKind::ExportPostReturn => false, - } - } } #[derive(Copy, Clone)] @@ -221,14 +134,14 @@ pub struct Summary<'a> { pub option_type: Option, pub nesting_option_type: Option, pub result_type: Option, - resource_state: Option>, + resource_state: Option, resource_directions: im_rc::HashMap, resource_info: HashMap, - dispatch_count: usize, world_types: HashMap>, world_keys: HashMap>, imported_interface_names: HashMap, exported_interface_names: HashMap, + imported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, } impl<'a> Summary<'a> { @@ -237,6 +150,7 @@ impl<'a> Summary<'a> { worlds: &IndexSet, import_interface_names: &HashMap<&str, &str>, export_interface_names: &HashMap<&str, &str>, + imported_function_indexes: &'a HashMap<(Option<&'a str>, &'a str), usize>, ) -> Result { let mut me = Self { resolve, @@ -251,11 +165,11 @@ impl<'a> Summary<'a> { resource_state: None, resource_directions: im_rc::HashMap::new(), resource_info: HashMap::new(), - dispatch_count: 0, world_types: HashMap::new(), world_keys: HashMap::new(), imported_interface_names: HashMap::new(), exported_interface_names: HashMap::new(), + imported_function_indexes, }; let mut import_keys_seen = HashSet::new(); @@ -290,9 +204,6 @@ impl<'a> Summary<'a> { } fn push_function(&mut self, function: MyFunction<'a>) { - if function.is_dispatchable() { - self.dispatch_count += 1; - } self.functions.push(function); } @@ -335,7 +246,7 @@ impl<'a> Summary<'a> { self.types.insert(id); } TypeDefKind::Option(some) => { - if abi::is_option(self.resolve, *some) { + if is_option(self.resolve, *some) { if self.nesting_option_type.is_none() { self.nesting_option_type = Some(id); } @@ -381,73 +292,13 @@ impl<'a> Summary<'a> { self.resource_directions.insert(id, state.direction); let info = self.resource_info.entry(id).or_default(); - let make = |kind, params, result| MyFunction { - kind, - interface: state.interface.clone(), - name: ty.name.as_deref().unwrap(), - docs: None, - params, - result, - wit_kind: wit_parser::FunctionKind::Freestanding, - }; - match state.direction { Direction::Import => { - info.remote_dispatch_index = Some(self.dispatch_count); - - static DROP_PARAMS: sync::Lazy<[(String, Type); 1]> = - sync::Lazy::new(|| [("handle".to_string(), Type::U32)]); - - static DROP_RESULTS: sync::Lazy> = - sync::Lazy::new(|| None); - - self.push_function(make( - FunctionKind::ResourceDropRemote, - DROP_PARAMS.deref(), - &DROP_RESULTS, - )); + info.remote = true; } Direction::Export => { - info.local_dispatch_index = Some(self.dispatch_count); - - // The order these functions are added must match the `LocalResource` field - // initialization order in `summarize_type`. - // TODO: make this less fragile. - - static NEW_PARAMS: sync::Lazy<[(String, Type); 1]> = - sync::Lazy::new(|| [("rep".to_string(), Type::U32)]); - - static NEW_RESULTS: Option = Some(Type::U32); - - self.push_function(make( - FunctionKind::ResourceNew, - NEW_PARAMS.deref(), - &NEW_RESULTS, - )); - - static REP_PARAMS: sync::Lazy<[(String, Type); 1]> = - sync::Lazy::new(|| [("handle".to_string(), Type::U32)]); - - static REP_RESULTS: Option = Some(Type::U32); - - self.push_function(make( - FunctionKind::ResourceRep, - REP_PARAMS.deref(), - &REP_RESULTS, - )); - - static DROP_PARAMS: sync::Lazy<[(String, Type); 1]> = - sync::Lazy::new(|| [("handle".to_string(), Type::U32)]); - - static DROP_RESULTS: sync::Lazy> = - sync::Lazy::new(|| None); - - self.push_function(make( - FunctionKind::ResourceDropLocal, - DROP_PARAMS.deref(), - DROP_RESULTS.deref(), - )); + info.local = true; } } } @@ -494,22 +345,7 @@ impl<'a> Summary<'a> { self.push_function(make(FunctionKind::Import)); } Direction::Export => { - // NB: We rely on this order when compiling, so please don't change it: - // todo: make this less fragile self.push_function(make(FunctionKind::Export)); - self.push_function(make(FunctionKind::ExportFromCanon)); - self.push_function(make(FunctionKind::ExportToCanon)); - if abi::record_abi(self.resolve, result.types()) - .flattened - .len() - > MAX_FLAT_RESULTS - { - self.push_function(make(FunctionKind::ExportPostReturn)); - } else { - // As of this writing, no type involving heap allocation can fit into `MAX_FLAT_RESULTS`, so - // nothing to do. We'll need to revisit this if `MAX_FLAT_RESULTS` changes or if new types are - // added. - } } } } @@ -567,15 +403,7 @@ impl<'a> Summary<'a> { docs: interface.docs.contents.as_deref(), }; - self.resource_state = Some(ResourceState { - direction, - interface: Some(MyInterface { - name: item_name, - id: *id, - docs: interface.docs.contents.as_deref(), - resource_directions: Default::default(), - }), - }); + self.resource_state = Some(ResourceState { direction }); for id in interface.types.values() { self.visit_type(Type::Id(*id), world); } @@ -588,10 +416,9 @@ impl<'a> Summary<'a> { for (func_name, func) in &interface.functions { self.visit_function( Some(MyInterface { - name: item_name, id: *id, + key, docs: interface.docs.contents.as_deref(), - resource_directions: self.resource_directions.clone(), }), func_name, func.docs.contents.as_deref(), @@ -623,91 +450,178 @@ impl<'a> Summary<'a> { Ok(()) } - fn summarize_type(&self, id: TypeId, world_module: &str) -> exports::Type { - let ty = &self.resolve.types[id]; - if let Some(package) = self.package(ty.owner, world_module) { + fn package_and_name( + &self, + id: TypeId, + ty: &TypeDef, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> Option<(String, String)> { + if let Some(package) = self.package(ty.owner, world_module, reverse_cloned_interfaces) { let name = if let Some(name) = &ty.name { name.to_upper_camel_case().escape() } else { format!("AnonymousType{}", self.types.get_index_of(&id).unwrap()) }; - let kind = match &ty.kind { - TypeDefKind::Record(record) => OwnedKind::Record( - record - .fields - .iter() - .map(|f| f.name.to_snake_case().escape()) - .collect(), - ), - TypeDefKind::Variant(variant) => OwnedKind::Variant( - variant - .cases - .iter() - .map(|c| Case { - name: format!("{name}_{}", c.name.to_upper_camel_case().escape()), - has_payload: c.ty.is_some(), - }) - .collect(), - ), - TypeDefKind::Enum(en) => OwnedKind::Enum(en.cases.len().try_into().unwrap()), - TypeDefKind::Flags(flags) => { - OwnedKind::Flags(flags.repr().count().try_into().unwrap()) - } - TypeDefKind::Tuple(_) | TypeDefKind::Option(_) | TypeDefKind::Result(_) => { - return self.summarize_unowned_type(id); - } - TypeDefKind::Resource => { - let info = &self.resource_info[&id]; - OwnedKind::Resource(Resource { - local: info - .local_dispatch_index - .map(|dispatch_index| LocalResource { - // This must match the order the functions are added in `visit_type`: - new: u32::try_from(dispatch_index).unwrap(), - rep: u32::try_from(dispatch_index + 1).unwrap(), - drop: u32::try_from(dispatch_index + 2).unwrap(), - }), - remote: info - .remote_dispatch_index - .map(|dispatch_index| RemoteResource { - drop: u32::try_from(dispatch_index).unwrap(), - }), - }) - } - kind => todo!("{kind:?}"), - }; - exports::Type::Owned(OwnedType { - package, - name, - kind, - }) + Some((package, name)) } else { - self.summarize_unowned_type(id) + None } } - fn summarize_unowned_type(&self, id: TypeId) -> exports::Type { + fn summarize_resource( + &self, + id: TypeId, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> exports::Resource { let ty = &self.resolve.types[id]; - match &ty.kind { - TypeDefKind::Tuple(tuple) => { - exports::Type::Tuple(tuple.types.len().try_into().unwrap()) - } - TypeDefKind::Option(some) => { - if abi::is_option(self.resolve, *some) { - exports::Type::NestingOption - } else { - exports::Type::Option - } - } - TypeDefKind::Result(_) => exports::Type::Result, - TypeDefKind::Handle(_) => exports::Type::Handle, - kind => todo!("{kind:?}"), + assert!(matches!(ty.kind, TypeDefKind::Resource)); + let (package, name) = self + .package_and_name(id, ty, world_module, reverse_cloned_interfaces) + .unwrap(); + + exports::Resource { package, name } + } + + fn summarize_record( + &self, + id: TypeId, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> exports::Record { + let ty = &self.resolve.types[id]; + let TypeDefKind::Record(record) = &ty.kind else { + unreachable!() + }; + let (package, name) = self + .package_and_name(id, ty, world_module, reverse_cloned_interfaces) + .unwrap(); + + exports::Record { + package, + name, + fields: record + .fields + .iter() + .map(|f| f.name.to_snake_case().escape()) + .collect(), + } + } + + fn summarize_flags( + &self, + id: TypeId, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> exports::Flags { + let ty = &self.resolve.types[id]; + let TypeDefKind::Flags(flags) = &ty.kind else { + unreachable!() + }; + let (package, name) = self + .package_and_name(id, ty, world_module, reverse_cloned_interfaces) + .unwrap(); + + exports::Flags { + package, + name, + u32_count: flags.repr().count().try_into().unwrap(), + } + } + + fn summarize_variant( + &self, + id: TypeId, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> exports::Variant { + let ty = &self.resolve.types[id]; + let TypeDefKind::Variant(variant) = &ty.kind else { + unreachable!() + }; + let (package, name) = self + .package_and_name(id, ty, world_module, reverse_cloned_interfaces) + .unwrap(); + + let cases = variant + .cases + .iter() + .map(|c| Case { + name: format!("{name}_{}", c.name.to_upper_camel_case().escape()), + has_payload: c.ty.is_some(), + }) + .collect(); + + exports::Variant { + package, + name, + cases, + } + } + + fn summarize_enum( + &self, + id: TypeId, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> exports::Enum { + let ty = &self.resolve.types[id]; + let TypeDefKind::Enum(enum_) = &ty.kind else { + unreachable!() + }; + let (package, name) = self + .package_and_name(id, ty, world_module, reverse_cloned_interfaces) + .unwrap(); + + exports::Enum { + package, + name, + count: enum_.cases.len().try_into().unwrap(), + } + } + + fn summarize_tuple(&self, id: TypeId) -> exports::Tuple { + let TypeDefKind::Tuple(tuple) = &self.resolve.types[id].kind else { + unreachable!() + }; + + exports::Tuple { + count: tuple.types.len().try_into().unwrap(), + } + } + + fn summarize_option(&self, id: TypeId) -> exports::OptionKind { + let TypeDefKind::Option(some) = &self.resolve.types[id].kind else { + unreachable!() + }; + + if is_option(self.resolve, *some) { + exports::OptionKind::Nesting + } else { + exports::OptionKind::NonNesting } } - pub fn collect_symbols(&self, locations: &Locations) -> Symbols { - let mut exports = Vec::new(); + fn summarize_result(&self, id: TypeId) -> exports::ResultRecord { + let TypeDefKind::Result(result) = &self.resolve.types[id].kind else { + unreachable!() + }; + + exports::ResultRecord { + has_ok: result.ok.is_some(), + has_err: result.err.is_some(), + } + } + + pub fn collect_symbols( + &self, + locations: &Locations, + metadata: &Metadata, + clone_maps: &CloneMaps, + ) -> Symbols { + let mut map = HashMap::new(); for function in &self.functions { if let FunctionKind::Export = function.kind { let scope = if let Some(interface) = &function.interface { @@ -716,54 +630,182 @@ impl<'a> Summary<'a> { locations.keys.get(&function.key()).unwrap() }; - exports.push(match function.wit_kind { - wit_parser::FunctionKind::Freestanding => { - FunctionExport::Freestanding(Function { - protocol: scope.to_upper_camel_case().escape(), - name: self.function_name(function), - }) - } - wit_parser::FunctionKind::Constructor(id) => { - FunctionExport::Constructor(Constructor { - module: scope.to_snake_case().escape(), - protocol: self.resolve.types[id] - .name - .as_deref() - .unwrap() - .to_upper_camel_case() - .escape(), - }) - } - wit_parser::FunctionKind::Method(_) => { - FunctionExport::Method(self.function_name(function)) - } - wit_parser::FunctionKind::Static(id) => FunctionExport::Static(Static { - module: scope.to_snake_case().escape(), - protocol: self.resolve.types[id] - .name - .as_deref() - .unwrap() - .to_upper_camel_case() - .escape(), - name: self.function_name(function), - }), - _ => todo!("handle async functions"), - }); + map.insert( + ( + function + .interface + .as_ref() + .map(|v| self.resolve.name_world_key(v.key)), + function.name, + ), + FunctionExport { + kind: match function.wit_kind { + wit_parser::FunctionKind::Freestanding => { + FunctionExportKind::Freestanding(Function { + protocol: scope.to_upper_camel_case().escape(), + name: self.function_name(function), + }) + } + wit_parser::FunctionKind::Constructor(id) => { + FunctionExportKind::Constructor(Constructor { + module: scope.to_snake_case().escape(), + protocol: self.resolve.types[id] + .name + .as_deref() + .unwrap() + .to_upper_camel_case() + .escape(), + }) + } + wit_parser::FunctionKind::Method(_) => { + FunctionExportKind::Method(self.function_name(function)) + } + wit_parser::FunctionKind::Static(id) => { + FunctionExportKind::Static(Static { + module: scope.to_snake_case().escape(), + protocol: self.resolve.types[id] + .name + .as_deref() + .unwrap() + .to_upper_camel_case() + .escape(), + name: self.function_name(function), + }) + } + _ => todo!("handle async functions"), + }, + return_style: match function.result { + None => ReturnStyle::None, + &Some(Type::Id(id)) + if matches!( + &self.resolve.types[id].kind, + TypeDefKind::Result(_) + ) => + { + ReturnStyle::Result + } + _ => ReturnStyle::Normal, + }, + }, + ); } } - let mut types = Vec::new(); - for ty in &self.types { - types.push(self.summarize_type(*ty, &locations.types.get(ty).unwrap().module)); + let exports = metadata + .export_funcs + .iter() + .map(|function| { + map.remove(&(function.interface.clone(), &function.name)) + .unwrap() + }) + .collect(); + + assert!(map.is_empty()); + + let mut reverse_cloned_types = HashMap::new(); + for (&original, &clone) in clone_maps.types() { + assert!(reverse_cloned_types.insert(clone, original).is_none()); + } + + let original = |ty| { + if let Some(&original) = reverse_cloned_types.get(&ty) { + original + } else { + ty + } + }; + + let module = |ty| &locations.types.get(&ty).unwrap().module; + + let mut reverse_cloned_interfaces = HashMap::new(); + for (&original, &clone) in clone_maps.interfaces() { + assert!(reverse_cloned_interfaces.insert(clone, original).is_none()); } + let resources = metadata + .resources + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_resource(ty, module(ty), &reverse_cloned_interfaces)) + .collect(); + + let records = metadata + .records + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_record(ty, module(ty), &reverse_cloned_interfaces)) + .collect(); + + let flags = metadata + .flags + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_flags(ty, module(ty), &reverse_cloned_interfaces)) + .collect(); + + let tuples = metadata + .tuples + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_tuple(ty)) + .collect(); + + let variants = metadata + .variants + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_variant(ty, module(ty), &reverse_cloned_interfaces)) + .collect(); + + let enums = metadata + .enums + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_enum(ty, module(ty), &reverse_cloned_interfaces)) + .collect(); + + let options = metadata + .options + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_option(ty)) + .collect(); + + let results = metadata + .results + .iter() + .map(|ty| original(ty.id)) + .map(|ty| self.summarize_result(ty)) + .collect(); + Symbols { types_package: format!("{}.types", locations.types_module.as_ref().unwrap()), exports, - types, + resources, + records, + flags, + tuples, + variants, + enums, + options, + results, } } + fn imported_function_index(&self, function: &MyFunction) -> usize { + *self + .imported_function_indexes + .get(&( + function + .interface + .as_ref() + .map(|v| self.resolve.name_world_key(v.key)) + .as_deref(), + function.name, + )) + .unwrap() + } + fn function_name(&self, function: &MyFunction) -> String { self.function_name_with(&function.wit_kind, function.name) } @@ -840,7 +882,7 @@ impl<'a> Summary<'a> { TypeNames::new(self, TypeOwner::None).type_name( ty, &if let Type::Id(id) = ty { - Some(bindgen::dealias(self.resolve, id)) + Some(dealias(self.resolve, id)) } else { None } @@ -1021,13 +1063,7 @@ impl<'a> Summary<'a> { let empty = &ResourceInfo::default(); - if self - .resource_info - .get(&id) - .unwrap_or(empty) - .remote_dispatch_index - .is_some() - { + if self.resource_info.get(&id).unwrap_or(empty).remote { for function in &self.functions { if matches_resource(function, id, Direction::Import) { sort(function, sorted, visited); @@ -1035,13 +1071,7 @@ impl<'a> Summary<'a> { } } - if self - .resource_info - .get(&id) - .unwrap_or(empty) - .local_dispatch_index - .is_some() - { + if self.resource_info.get(&id).unwrap_or(empty).local { for function in &self.functions { if matches_resource(function, id, Direction::Export) { sort(function, sorted, visited); @@ -1353,14 +1383,8 @@ class {camel}(Flag): let empty = &ResourceInfo::default(); - let import = if self - .resource_info - .get(&id) - .unwrap_or(empty) - .remote_dispatch_index - .is_some() - { - let method = |(index, function)| { + let import = if self.resource_info.get(&id).unwrap_or(empty).remote { + let method = |function| { let FunctionCode { snake, params, @@ -1392,6 +1416,7 @@ class {camel}(Flag): " ) } else { + let index = self.imported_function_index(function); format!( " def {snake}({params}){return_type}: @@ -1409,6 +1434,7 @@ class {camel}(Flag): {docs}{NOT_IMPLEMENTED}" ) } else { + let index = self.imported_function_index(function); format!( "{class_method} def {snake}({params}){return_type}: @@ -1419,53 +1445,41 @@ class {camel}(Flag): }; let methods = self - .functions - .iter() - .filter_map({ - let mut index = 0; - move |function| { - let result = matches_resource(function, id, Direction::Import) - .then_some((index, function)); - - if function.is_dispatchable() { - index += 1; - } - - result - } - }) - .map(method) - .chain(iter::once({ - let newline = '\n'; - let indent = " "; - let doc = "Release this resource."; - let docs = - format!(r#""""{newline}{indent}{doc}{newline}{indent}"""{newline}{indent}"#); - let enter = r#" + .functions + .iter() + .filter(move |function| matches_resource(function, id, Direction::Import)) + .map(method) + .chain(iter::once({ + let newline = '\n'; + let indent = " "; + let doc = "Release this resource."; + let docs = + format!(r#""""{newline}{indent}{doc}{newline}{indent}"""{newline}{indent}"#); + let enter = r#" def __enter__(self) -> Self: """Returns self""" return self "#; - if stub_runtime_calls { - format!( - "{enter} + if stub_runtime_calls { + format!( + "{enter} def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None) -> bool | None: {docs}{NOT_IMPLEMENTED} " - ) - } else { - format!( - "{enter} + ) + } else { + format!( + "{enter} def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None) -> bool | None: {docs}(_, func, args, _) = self.finalizer.detach() self.handle = None func(args[0], args[1]) " - ) - } - })) - .collect::>() - .concat(); + ) + } + })) + .collect::>() + .concat(); Some(format!( " @@ -1477,13 +1491,7 @@ class {camel}: None }; - let export = if self - .resource_info - .get(&id) - .unwrap_or(empty) - .local_dispatch_index - .is_some() - { + let export = if self.resource_info.get(&id).unwrap_or(empty).local { let method = |function| { let FunctionCode { snake, @@ -1640,7 +1648,6 @@ class {camel}(Protocol): seen.insert(id); } - let mut index = 0; for function in &self.functions { let key = function.key(); let direction = if let FunctionKind::Import = &function.kind { @@ -1704,6 +1711,7 @@ def {snake}({params}){return_type}: " ) } else { + let index = self.imported_function_index(function); format!( " def {snake}({params}){return_type}: @@ -1772,15 +1780,10 @@ def {snake}({params}){return_type}: definitions.alias_module = Some(module.clone()); } } - _ => unreachable!(), } } _ => (), } - - if function.is_dispatchable() { - index += 1; - } } let python_imports = @@ -2019,9 +2022,17 @@ from .types import Result, Ok, Err, Some } } - fn package(&self, owner: TypeOwner, world_module: &str) -> Option { + fn package( + &self, + owner: TypeOwner, + world_module: &str, + reverse_cloned_interfaces: &HashMap, + ) -> Option { match owner { - TypeOwner::Interface(interface) => { + TypeOwner::Interface(mut interface) => { + if let Some(&original) = reverse_cloned_interfaces.get(&interface) { + interface = original; + } let (module, package) = self.interface_package(interface); Some(format!("{world_module}.{module}.{package}")) } @@ -2080,7 +2091,7 @@ from .types import Result, Ok, Err, Some TypeDefKind::Resource => { let empty = &ResourceInfo::default(); let info = self.resource_info.get(&id).unwrap_or(empty); - info.local_dispatch_index.is_some() && info.remote_dispatch_index.is_some() + info.local && info.remote } kind => todo!("{kind:?}"), }, @@ -2156,7 +2167,7 @@ impl<'a> TypeNames<'a> { } } TypeDefKind::Option(some) => { - if abi::is_option(self.summary.resolve, *some) { + if is_option(self.summary.resolve, *some) { format!("Optional[Some[{}]]", self.type_name(*some, seen, resource)) } else { format!("Optional[{}]", self.type_name(*some, seen, resource)) @@ -2280,3 +2291,24 @@ fn docstring( String::new() } } + +fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId { + loop { + match &resolve.types[id].kind { + TypeDefKind::Type(Type::Id(that_id)) => id = *that_id, + _ => break id, + } + } +} + +fn is_option(resolve: &Resolve, ty: Type) -> bool { + if let Type::Id(id) = ty { + match &resolve.types[id].kind { + TypeDefKind::Option(_) => true, + TypeDefKind::Type(ty) => is_option(resolve, *ty), + _ => false, + } + } else { + false + } +} diff --git a/wit/init.wit b/wit/init.wit index ef3ec33..35f8826 100644 --- a/wit/init.wit +++ b/wit/init.wit @@ -2,14 +2,8 @@ package componentize-py:init; world init { import wasi:cli/environment@0.2.0; - + export exports: interface { - record bundled { - module: string, - protocol: string, - name: string - } - record function { protocol: string, name: string @@ -26,61 +20,83 @@ world init { name: string } - variant function-export { - bundled(bundled), + variant function-export-kind { freestanding(function), %constructor(%constructor), %method(string), %static(%static) } - record case { + variant return-style { + none, + normal, + %result, + } + + record function-export { + kind: function-export-kind, + return-style: return-style + } + + record %resource { + %package: string, + name: string + } + + record %record { + %package: string, name: string, - has-payload: bool, + fields: list } - record local-resource { - new: u32, - rep: u32, - drop: u32 + record %flags { + %package: string, + name: string, + u32-count: u32 } - record remote-resource { - drop: u32 + record %tuple { + count: u32 } - - record %resource { - local: option, - remote: option + + record case { + name: string, + has-payload: bool } - variant owned-kind { - %record(list), - %variant(list), - %enum(u32), - %flags(u32), - %resource(%resource), + record %variant { + %package: string, + name: string, + cases: list } - record owned-type { - kind: owned-kind, + record %enum { %package: string, - name: string + name: string, + count: u32 } - variant %type { - owned(owned-type), - %option, - nesting-option, - %result, - %tuple(u32), - handle + enum option-kind { + non-nesting, + nesting + } + + record result-record { + has-ok: bool, + has-err: bool, } record symbols { types-package: string, exports: list, - types: list<%type> + resources: list<%resource>, + records: list<%record>, + %flags: list<%flags>, + tuples: list<%tuple>, + variants: list<%variant>, + enums: list<%enum>, + options: list, + results: list, } init: func(app-name: string, symbols: symbols, stub-wasi: bool) -> result<_, string>;