use heck::*;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::mem;
use wai_bindgen_gen_core::wai_parser::abi::{
AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType,
};
use wai_bindgen_gen_core::{wai_parser::*, Direction, Files, Generator, Ns};
pub mod dependencies;
pub mod source;
use dependencies::Dependencies;
use source::Source;
#[derive(Default)]
pub struct WasmerPy {
src: Source,
in_import: bool,
opts: Opts,
guest_imports: HashMap<String, Imports>,
guest_exports: HashMap<String, Exports>,
sizes: SizeAlign,
deps: Dependencies,
union_representation: HashMap<String, PyUnionRepresentation>,
}
#[derive(Debug, Clone, Copy)]
enum PyUnionRepresentation {
Raw,
Wrapped,
}
#[derive(Default)]
struct Imports {
freestanding_funcs: Vec<Import>,
resource_funcs: BTreeMap<ResourceId, Vec<Import>>,
}
struct Import {
name: String,
src: Source,
wasm_ty: String,
pysig: String,
}
#[derive(Default)]
struct Exports {
freestanding_funcs: Vec<Source>,
resource_funcs: BTreeMap<ResourceId, Vec<Source>>,
fields: BTreeMap<String, &'static str>,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "structopt", derive(structopt::StructOpt))]
pub struct Opts {
#[cfg_attr(feature = "structopt", structopt(long = "no-typescript"))]
pub no_typescript: bool,
}
impl Opts {
pub fn build(self) -> WasmerPy {
let mut r = WasmerPy::new();
r.opts = self;
r
}
}
impl WasmerPy {
pub fn new() -> WasmerPy {
WasmerPy::default()
}
fn abi_variant(dir: Direction) -> AbiVariant {
match dir {
Direction::Import => AbiVariant::GuestExport,
Direction::Export => AbiVariant::GuestImport,
}
}
fn intrinsics(&mut self, iface: &Interface) -> Source {
if iface.resources.len() > 0 {
self.deps.needs_resources = true;
self.deps.pyimport("typing", "runtime_checkable");
}
self.deps.intrinsics()
}
}
fn array_ty(iface: &Interface, ty: &Type) -> Option<&'static str> {
match ty {
Type::Unit | Type::Bool => None,
Type::U8 => Some("uint8"),
Type::S8 => Some("int8"),
Type::U16 => Some("uint16"),
Type::S16 => Some("int16"),
Type::U32 => Some("uint32"),
Type::S32 => Some("int32"),
Type::U64 => Some("uint64"),
Type::S64 => Some("int64"),
Type::Float32 => Some("float32"),
Type::Float64 => Some("float64"),
Type::Char => None,
Type::Handle(_) => None,
Type::String => None,
Type::Id(id) => match &iface.types[*id].kind {
TypeDefKind::Type(t) => array_ty(iface, t),
_ => None,
},
}
}
impl Generator for WasmerPy {
fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
let variant = Self::abi_variant(dir);
self.sizes.fill(iface);
self.in_import = variant == AbiVariant::GuestImport;
}
fn type_record(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
record: &Record,
docs: &Docs,
) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.pyimport("dataclasses", "dataclass");
builder.push_str("@dataclass\n");
builder.push_str(&format!("class {}:\n", name.to_camel_case()));
builder.indent();
builder.docstring(docs);
for field in record.fields.iter() {
builder.comment(&field.docs);
let field_name = field.name.to_snake_case();
builder.push_str(&format!("{field_name}: "));
builder.print_ty(&field.ty, true);
builder.push_str("\n");
}
if record.fields.is_empty() {
builder.push_str("pass\n");
}
builder.dedent();
builder.push_str("\n");
}
fn type_tuple(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
tuple: &Tuple,
docs: &Docs,
) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.comment(docs);
builder.push_str(&format!("{} = ", name.to_camel_case()));
builder.print_tuple(tuple);
builder.push_str("\n");
}
fn type_flags(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
flags: &Flags,
docs: &Docs,
) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.pyimport("enum", "Flag");
builder.pyimport("enum", "auto");
builder.push_str(&format!("class {}(Flag):\n", name.to_camel_case()));
builder.indent();
builder.docstring(docs);
for flag in flags.flags.iter() {
let flag_name = flag.name.to_shouty_snake_case();
builder.comment(&flag.docs);
builder.push_str(&format!("{flag_name} = auto()\n"));
}
if flags.flags.is_empty() {
builder.push_str("pass\n");
}
builder.dedent();
builder.push_str("\n");
}
fn type_variant(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
variant: &Variant,
docs: &Docs,
) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.pyimport("dataclasses", "dataclass");
let mut cases = Vec::new();
for case in variant.cases.iter() {
builder.docstring(&case.docs);
builder.push_str("@dataclass\n");
let case_name = format!("{}{}", name.to_camel_case(), case.name.to_camel_case());
builder.push_str(&format!("class {case_name}:\n"));
builder.indent();
builder.push_str("value: ");
builder.print_ty(&case.ty, true);
builder.push_str("\n");
builder.dedent();
builder.push_str("\n");
cases.push(case_name);
}
builder.deps.pyimport("typing", "Union");
builder.comment(docs);
builder.push_str(&format!(
"{} = Union[{}]\n",
name.to_camel_case(),
cases.join(", "),
));
builder.push_str("\n");
}
fn type_union(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
union: &Union,
docs: &Docs,
) {
let mut py_type_classes = BTreeSet::new();
for case in union.cases.iter() {
py_type_classes.insert(py_type_class_of(&case.ty));
}
let mut builder = self.src.builder(&mut self.deps, iface);
if py_type_classes.len() != union.cases.len() {
self.union_representation
.insert(name.to_string(), PyUnionRepresentation::Wrapped);
builder.print_union_wrapped(name, union, docs);
} else {
self.union_representation
.insert(name.to_string(), PyUnionRepresentation::Raw);
builder.print_union_raw(name, union, docs);
}
}
fn type_option(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
payload: &Type,
docs: &Docs,
) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.pyimport("typing", "Optional");
builder.comment(docs);
builder.push_str(&name.to_camel_case());
builder.push_str(" = Optional[");
builder.print_ty(payload, true);
builder.push_str("]\n\n");
}
fn type_expected(
&mut self,
iface: &Interface,
_id: TypeId,
name: &str,
expected: &Expected,
docs: &Docs,
) {
self.deps.needs_expected = true;
let mut builder = self.src.builder(&mut self.deps, iface);
builder.comment(docs);
builder.push_str(&format!("{} = Expected[", name.to_camel_case()));
builder.print_ty(&expected.ok, true);
builder.push_str(", ");
builder.print_ty(&expected.err, true);
builder.push_str("]\n\n");
}
fn type_enum(&mut self, iface: &Interface, _id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.pyimport("enum", "Enum");
builder.push_str(&format!("class {}(Enum):\n", name.to_camel_case()));
builder.indent();
builder.docstring(docs);
for (i, case) in enum_.cases.iter().enumerate() {
builder.comment(&case.docs);
let mut name = case.name.to_shouty_snake_case();
if name.chars().next().unwrap().is_ascii_digit() {
name = format!("_{}", name);
}
builder.push_str(&format!("{} = {}\n", name, i));
}
builder.dedent();
builder.push_str("\n");
}
fn type_resource(&mut self, _iface: &Interface, _ty: ResourceId) {
}
fn type_alias(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.comment(docs);
builder.push_str(&format!("{} = ", name.to_camel_case()));
builder.print_ty(ty, false);
builder.push_str("\n");
}
fn type_list(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
let mut builder = self.src.builder(&mut self.deps, iface);
builder.comment(docs);
builder.push_str(&format!("{} = ", name.to_camel_case()));
builder.print_list(ty);
builder.push_str("\n");
}
fn type_builtin(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
self.type_alias(iface, id, name, ty, docs);
}
fn export(&mut self, iface: &Interface, func: &Function) {
assert!(!func.is_async, "async not supported yet");
let mut pysig = Source::default();
let mut builder = pysig.builder(&mut self.deps, iface);
builder.print_sig(func, self.in_import);
let pysig = pysig.to_string();
let mut func_body = Source::default();
let mut builder = func_body.builder(&mut self.deps, iface);
let sig = iface.wasm_signature(AbiVariant::GuestImport, func);
builder.push_str(&format!("def {}(", func.name.to_snake_case(),));
let mut params = Vec::new();
for (i, param) in sig.params.iter().enumerate() {
if i != 0 {
builder.push_str(", ");
}
let name = format!("arg{}", i);
builder.push_str(&name);
builder.push_str(": ");
builder.push_str(wasm_ty_typing(*param));
params.push(name);
}
builder.push_str(") -> ");
match sig.results.len() {
0 => builder.push_str("None"),
1 => builder.push_str(wasm_ty_typing(sig.results[0])),
_ => unimplemented!(),
}
builder.push_str(":\n");
builder.indent();
drop(builder);
let mut f = FunctionBindgen::new(self, params);
iface.call(
AbiVariant::GuestImport,
LiftLower::LiftArgsLowerResults,
func,
&mut f,
);
let FunctionBindgen {
src,
needs_memory,
needs_realloc,
needs_free,
mut locals,
..
} = f;
let mut builder = func_body.builder(&mut self.deps, iface);
if needs_memory {
builder.push_str("m = get_export(\"memory\")\n");
builder.push_str("assert(isinstance(m, wasmer.Memory))\n");
builder.deps.pyimport("typing", "cast");
builder.push_str("memory = cast(wasmer.Memory, m)\n");
locals.insert("memory").unwrap();
}
if let Some(name) = needs_realloc {
builder.push_str(&format!("realloc = get_export(\"{}\")\n", name));
builder.push_str("assert(isinstance(realloc, wasmer.Function))\n");
locals.insert("realloc").unwrap();
}
if let Some(name) = needs_free {
builder.push_str(&format!("free = get_export(\"{}\")\n", name));
builder.push_str("assert(isinstance(free, wasmer.Function))\n");
locals.insert("free").unwrap();
}
builder.push_str(&src);
builder.dedent();
let mut wasm_ty = String::from("wasmer.FunctionType([");
wasm_ty.push_str(
&sig.params
.iter()
.map(|t| wasm_ty_ctor(*t))
.collect::<Vec<_>>()
.join(", "),
);
wasm_ty.push_str("], [");
wasm_ty.push_str(
&sig.results
.iter()
.map(|t| wasm_ty_ctor(*t))
.collect::<Vec<_>>()
.join(", "),
);
wasm_ty.push_str("])");
let import = Import {
name: func.name.clone(),
src: func_body,
wasm_ty,
pysig,
};
let imports = self
.guest_imports
.entry(iface.name.to_string())
.or_default();
let dst = match &func.kind {
FunctionKind::Freestanding | FunctionKind::Static { .. } => {
&mut imports.freestanding_funcs
}
FunctionKind::Method { resource, .. } => {
imports.resource_funcs.entry(*resource).or_default()
}
};
dst.push(import);
}
fn import(&mut self, iface: &Interface, func: &Function) {
assert!(!func.is_async, "async not supported yet");
let mut func_body = Source::default();
let mut builder = func_body.builder(&mut self.deps, iface);
let params = builder.print_sig(func, self.in_import);
builder.push_str(":\n");
builder.indent();
drop(builder);
let src_object = match &func.kind {
FunctionKind::Freestanding => "self".to_string(),
FunctionKind::Static { .. } => "obj".to_string(),
FunctionKind::Method { .. } => "self._obj".to_string(),
};
let mut f = FunctionBindgen::new(self, params);
f.src_object = src_object;
iface.call(
AbiVariant::GuestExport,
LiftLower::LowerArgsLiftResults,
func,
&mut f,
);
let FunctionBindgen {
src,
needs_memory,
needs_realloc,
needs_free,
src_object,
..
} = f;
let mut builder = func_body.builder(&mut self.deps, iface);
if needs_memory {
builder.push_str(&format!("memory = {}._memory;\n", src_object));
}
if let Some(name) = &needs_realloc {
builder.push_str(&format!(
"realloc = {}._{}\n",
src_object,
name.to_snake_case(),
));
}
if let Some(name) = &needs_free {
builder.push_str(&format!(
"free = {}._{}\n",
src_object,
name.to_snake_case(),
));
}
builder.push_str(&src);
builder.dedent();
let exports = self
.guest_exports
.entry(iface.name.to_string())
.or_insert_with(Exports::default);
if needs_memory {
exports.fields.insert("memory".to_string(), "wasmer.Memory");
}
if let Some(name) = &needs_realloc {
exports.fields.insert(name.clone(), "wasmer.Function");
}
if let Some(name) = &needs_free {
exports.fields.insert(name.clone(), "wasmer.Function");
}
exports.fields.insert(func.name.clone(), "wasmer.Function");
let dst = match &func.kind {
FunctionKind::Freestanding => &mut exports.freestanding_funcs,
FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => {
exports.resource_funcs.entry(*resource).or_default()
}
};
dst.push(func_body);
}
fn finish_one(&mut self, iface: &Interface, files: &mut Files) {
self.deps.pyimport("typing", "Any");
self.deps.pyimport("abc", "abstractmethod");
self.deps.pyimport("typing", "Callable");
let types = mem::take(&mut self.src);
let intrinsics = self.intrinsics(iface);
for (k, v) in self.deps.pyimports.iter() {
match v {
Some(list) => {
let list = list.iter().cloned().collect::<Vec<_>>().join(", ");
self.src.push_str(&format!("from {} import {}\n", k, list));
}
None => {
self.src.push_str(&format!("import {}\n", k));
}
}
}
self.src.push_str("import wasmer # type: ignore\n");
self.src.push_str(
"
try:
from typing import Protocol
except ImportError:
class Protocol: # type: ignore
pass
",
);
self.src.push_str("\n");
if self.deps.needs_t_typevar {
self.src.push_str("T = TypeVar('T')\n");
}
self.src.push_str(&intrinsics);
for (id, r) in iface.resources.iter() {
let name = r.name.to_camel_case();
if self.in_import {
self.src.push_str("@runtime_checkable\n");
self.src.push_str(&format!("class {}(Protocol):\n", name));
self.src.indent();
self.src.push_str("def drop(self) -> None:\n");
self.src.indent();
self.src.push_str("pass\n");
self.src.dedent();
for (_, funcs) in self.guest_imports.iter() {
if let Some(funcs) = funcs.resource_funcs.get(&id) {
for func in funcs {
self.src.push_str("@abstractmethod\n");
self.src.push_str(&func.pysig);
self.src.push_str(":\n");
self.src.indent();
self.src.push_str("raise NotImplementedError\n");
self.src.dedent();
}
}
}
self.src.dedent();
} else {
self.src.push_str(&format!("class {}:\n", name));
self.src.indent();
self.src.push_str(&format!(
"
_wasm_val: int
_refcnt: int
_obj: '{iface}'
_destroyed: bool
def __init__(self, val: int, obj: '{iface}') -> None:
self._wasm_val = val
self._refcnt = 1
self._obj = obj
self._destroyed = False
def clone(self) -> '{name}':
self._refcnt += 1
return self
def drop(self) -> None:
self._refcnt -= 1;
if self._refcnt != 0:
return
assert(not self._destroyed)
self._destroyed = True
self._obj._canonical_abi_drop_{drop}(self._wasm_val)
def __del__(self) -> None:
if not self._destroyed:
raise RuntimeError('wasm object not dropped')
",
name = name,
iface = iface.name.to_camel_case(),
drop = name.to_snake_case(),
));
for (_, exports) in self.guest_exports.iter() {
if let Some(funcs) = exports.resource_funcs.get(&id) {
for func in funcs {
self.src.push_str(func);
}
}
}
self.src.dedent();
}
}
self.src.push_str(&types);
for (module, funcs) in mem::take(&mut self.guest_imports) {
self.src
.push_str(&format!("class {}(Protocol):\n", module.to_camel_case()));
self.src.indent();
for func in funcs.freestanding_funcs.iter() {
self.src.push_str("@abstractmethod\n");
self.src.push_str(&func.pysig);
self.src.push_str(":\n");
self.src.indent();
self.src.push_str("raise NotImplementedError\n");
self.src.dedent();
}
self.src.dedent();
self.src.push_str("\n");
self.src.push_str(&format!(
"def add_{}_to_imports(store: wasmer.Store, imports: dict[str, dict[str, Any]], host: {}, get_export: Callable[[str], Any]) -> None:\n",
module.to_snake_case(),
module.to_camel_case(),
));
self.src.indent();
for (id, r) in iface.resources.iter() {
self.src.push_str(&format!(
"_resources{}: Slab[{}] = Slab()\n",
id.index(),
r.name.to_camel_case()
));
}
for func in funcs
.freestanding_funcs
.iter()
.chain(funcs.resource_funcs.values().flatten())
{
self.src.push_str(&format!("ty = {}\n", func.wasm_ty));
self.src.push_str(&func.src);
self.src.push_str(&format!(
"imports.setdefault('{}', {{}})['{}'] = wasmer.Function(store, {}, ty)\n",
iface.name,
func.name,
func.name.to_snake_case(),
));
}
for (id, resource) in iface.resources.iter() {
let snake = resource.name.to_snake_case();
self.src.push_str(&format!(
"def resource_drop_{}(i: int) -> None:\n _resources{}.remove(i).drop()\n",
snake,
id.index(),
));
self.src
.push_str("ty = wasmer.FunctionType([wasmer.Type.I32], [])\n");
self.src.push_str(&format!(
"imports.setdefault('canonical_abi', {{}})['resource_drop_{}'] = \
wasmer.Function(store, resource_drop_{}, ty)\n",
resource.name, snake,
));
}
self.src.dedent();
}
if !self.in_import && self.guest_exports.is_empty() {
self.src
.push_str(&format!("class {}:\n", iface.name.to_camel_case()));
self.src.indent();
if iface.resources.len() == 0 {
self.src.push_str("pass\n");
} else {
for (_, r) in iface.resources.iter() {
self.src.push_str(&format!(
"_canonical_abi_drop_{}: wasmer.Function\n",
r.name.to_snake_case(),
));
}
}
self.src.dedent();
}
for (module, exports) in mem::take(&mut self.guest_exports) {
let module = module.to_camel_case();
self.src.push_str(&format!("class {}:\n", module));
self.src.indent();
self.src.push_str("instance: wasmer.Instance\n");
for (name, ty) in exports.fields.iter() {
self.src
.push_str(&format!("_{}: {}\n", name.to_snake_case(), ty));
}
for (id, r) in iface.resources.iter() {
self.src.push_str(&format!(
"_resource{}_slab: Slab[{}]\n",
id.index(),
r.name.to_camel_case(),
));
self.src.push_str(&format!(
"_canonical_abi_drop_{}: wasmer.Function\n",
r.name.to_snake_case(),
));
}
self.src.push_str("def __init__(self, store: wasmer.Store, imports: dict[str, dict[str, Any]], module: wasmer.Module):\n");
self.src.indent();
for (id, r) in iface.resources.iter() {
self.src.push_str(&format!(
"
ty1 = wasmer.FunctionType([wasmer.Type.I32], [])
ty2 = wasmer.FunctionType([wasmer.Type.I32], [wasmer.Type.I32])
def drop_{snake}(idx: int) -> None:
self._resource{idx}_slab.remove(idx).drop();
imports.setdefault('canonical_abi', {{}})['resource_drop_{name}'] = wasmer.Function(store, drop_{snake}, ty1)
def clone_{snake}(idx: int) -> int:
obj = self._resource{idx}_slab.get(idx)
return self._resource{idx}_slab.insert(obj.clone())
imports.setdefault('canonical_abi', {{}})['resource_clone_{name}'] = wasmer.Function(store, clone_{snake}, ty2)
def get_{snake}(idx: int) -> int:
obj = self._resource{idx}_slab.get(idx)
return obj._wasm_val
imports.setdefault('canonical_abi', {{}})['resource_get_{name}'] = wasmer.Function(store, get_{snake}, ty2)
def new_{snake}(val: int) -> int:
return self._resource{idx}_slab.insert({camel}(val, self))
imports.setdefault('canonical_abi', {{}})['resource_new_{name}'] = wasmer.Function(store, new_{snake}, ty2)
",
name = r.name,
camel = r.name.to_camel_case(),
snake = r.name.to_snake_case(),
idx = id.index(),
));
}
self.src
.push_str("self.instance = wasmer.Instance(module, imports)\n");
for (name, ty) in exports.fields.iter() {
self.src.push_str(&format!(
"
{snake} = self.instance.exports.__getattribute__('{name}')
assert(isinstance({snake}, {ty}))
self._{snake} = {snake}
",
name = name,
snake = name.to_snake_case(),
ty = ty,
));
}
for (id, r) in iface.resources.iter() {
self.src.push_str(&format!(
"
self._resource{idx}_slab = Slab()
canon_drop_{snake} = self.instance.exports.__getattribute__('canonical_abi_drop_{name}')
assert(isinstance(canon_drop_{snake}, wasmer.Function))
self._canonical_abi_drop_{snake} = canon_drop_{snake}
",
idx = id.index(),
name = r.name,
snake = r.name.to_snake_case(),
));
}
self.src.dedent();
for func in exports.freestanding_funcs.iter() {
self.src.push_str(func);
}
self.src.dedent();
}
files.push("bindings.py", self.src.as_bytes());
}
}
struct FunctionBindgen<'a> {
gen: &'a mut WasmerPy,
locals: Ns,
src: Source,
block_storage: Vec<Source>,
blocks: Vec<(String, Vec<String>)>,
needs_memory: bool,
needs_realloc: Option<String>,
needs_free: Option<String>,
params: Vec<String>,
payloads: Vec<String>,
src_object: String,
}
impl FunctionBindgen<'_> {
fn new(gen: &mut WasmerPy, params: Vec<String>) -> FunctionBindgen<'_> {
let mut locals = Ns::default();
locals.insert("len").unwrap(); locals.insert("base").unwrap(); locals.insert("i").unwrap(); for param in params.iter() {
locals.insert(param).unwrap();
}
FunctionBindgen {
gen,
locals,
src: Source::default(),
block_storage: Vec::new(),
blocks: Vec::new(),
needs_memory: false,
needs_realloc: None,
needs_free: None,
params,
payloads: Vec::new(),
src_object: "self".to_string(),
}
}
fn clamp<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
where
T: std::fmt::Display,
{
self.gen.deps.needs_clamp = true;
results.push(format!("_clamp({}, {}, {})", operands[0], min, max));
}
fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
self.needs_memory = true;
self.gen.deps.needs_load = true;
let tmp = self.locals.tmp("load");
self.src.push_str(&format!(
"{} = _load(memory.{}_view, memory, {}, {})\n",
tmp, ty, operands[0], offset,
));
results.push(tmp);
}
fn store(&mut self, ty: &str, offset: i32, operands: &[String]) {
self.needs_memory = true;
self.gen.deps.needs_store = true;
self.src.push_str(&format!(
"_store(memory.{}_view, memory, {}, {}, {})\n",
ty, operands[1], offset, operands[0]
));
}
}
impl Bindgen for FunctionBindgen<'_> {
type Operand = String;
fn sizes(&self) -> &SizeAlign {
&self.gen.sizes
}
fn push_block(&mut self) {
let prev = mem::take(&mut self.src);
self.block_storage.push(prev);
}
fn finish_block(&mut self, operands: &mut Vec<String>) {
let to_restore = self.block_storage.pop().unwrap();
let src = mem::replace(&mut self.src, to_restore);
self.blocks.push((src.into(), mem::take(operands)));
}
fn return_pointer(&mut self, _iface: &Interface, _size: usize, _align: usize) -> String {
unimplemented!()
}
fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool {
array_ty(iface, ty).is_some()
}
fn emit(
&mut self,
iface: &Interface,
inst: &Instruction<'_>,
operands: &mut Vec<String>,
results: &mut Vec<String>,
) {
let mut builder = self.src.builder(&mut self.gen.deps, iface);
match inst {
Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
Instruction::I32Const { val } => results.push(val.to_string()),
Instruction::ConstZero { tys } => {
for t in tys.iter() {
match t {
WasmType::I32 | WasmType::I64 => results.push("0".to_string()),
WasmType::F32 | WasmType::F64 => results.push("0.0".to_string()),
}
}
}
Instruction::U8FromI32 => self.clamp(results, operands, u8::MIN, u8::MAX),
Instruction::S8FromI32 => self.clamp(results, operands, i8::MIN, i8::MAX),
Instruction::U16FromI32 => self.clamp(results, operands, u16::MIN, u16::MAX),
Instruction::S16FromI32 => self.clamp(results, operands, i16::MIN, i16::MAX),
Instruction::U32FromI32 => {
results.push(format!("{} & 0xffffffff", operands[0]));
}
Instruction::U64FromI64 => {
results.push(format!("{} & 0xffffffffffffffff", operands[0]));
}
Instruction::S32FromI32 | Instruction::S64FromI64 => {
results.push(operands.pop().unwrap())
}
Instruction::I32FromU8 => self.clamp(results, operands, u8::MIN, u8::MAX),
Instruction::I32FromS8 => self.clamp(results, operands, i8::MIN, i8::MAX),
Instruction::I32FromU16 => self.clamp(results, operands, u16::MIN, u16::MAX),
Instruction::I32FromS16 => self.clamp(results, operands, i16::MIN, i16::MAX),
Instruction::I32FromU32 => {
self.clamp(results, operands, u32::MIN, u32::MAX);
}
Instruction::I32FromS32 => self.clamp(results, operands, i32::MIN, i32::MAX),
Instruction::I64FromU64 => self.clamp(results, operands, u64::MIN, u64::MAX),
Instruction::I64FromS64 => self.clamp(results, operands, i64::MIN, i64::MAX),
Instruction::Float32FromF32
| Instruction::Float64FromF64
| Instruction::F32FromFloat32
| Instruction::F64FromFloat64 => results.push(operands.pop().unwrap()),
Instruction::CharFromI32 => {
builder.deps.needs_validate_guest_char = true;
results.push(format!("_validate_guest_char({})", operands[0]));
}
Instruction::I32FromChar => {
results.push(format!("ord({})", operands[0]));
}
Instruction::Bitcasts { casts } => {
for (cast, op) in casts.iter().zip(operands) {
match cast {
Bitcast::I32ToF32 => {
builder.deps.needs_i32_to_f32 = true;
results.push(format!("_i32_to_f32({})", op));
}
Bitcast::F32ToI32 => {
builder.deps.needs_f32_to_i32 = true;
results.push(format!("_f32_to_i32({})", op));
}
Bitcast::I64ToF64 => {
builder.deps.needs_i64_to_f64 = true;
results.push(format!("_i64_to_f64({})", op));
}
Bitcast::F64ToI64 => {
builder.deps.needs_f64_to_i64 = true;
results.push(format!("_f64_to_i64({})", op));
}
Bitcast::I64ToF32 => {
builder.deps.needs_i32_to_f32 = true;
results.push(format!("_i32_to_f32(({}) & 0xffffffff)", op));
}
Bitcast::F32ToI64 => {
builder.deps.needs_f32_to_i32 = true;
results.push(format!("_f32_to_i32({})", op));
}
Bitcast::I32ToI64 | Bitcast::I64ToI32 | Bitcast::None => {
results.push(op.clone())
}
}
}
}
Instruction::UnitLower => {}
Instruction::UnitLift => {
results.push("None".to_string());
}
Instruction::BoolFromI32 => {
let op = self.locals.tmp("operand");
let ret = self.locals.tmp("boolean");
builder.push_str(&format!(
"
{op} = {}
if {op} == 0:
{ret} = False
elif {op} == 1:
{ret} = True
else:
raise TypeError(\"invalid variant discriminant for bool\")
",
operands[0]
));
results.push(ret);
}
Instruction::I32FromBool => {
results.push(format!("int({})", operands[0]));
}
Instruction::I32FromOwnedHandle { ty } => {
results.push(format!("_resources{}.insert({})", ty.index(), operands[0]));
}
Instruction::HandleBorrowedFromI32 { ty } => {
results.push(format!("_resources{}.get({})", ty.index(), operands[0]));
}
Instruction::I32FromBorrowedHandle { ty } => {
let obj = self.locals.tmp("obj");
builder.push_str(&format!("{} = {}\n", obj, operands[0]));
results.push(format!(
"{}._resource{}_slab.insert({}.clone())",
self.src_object,
ty.index(),
obj,
));
}
Instruction::HandleOwnedFromI32 { ty } => {
results.push(format!(
"{}._resource{}_slab.remove({})",
self.src_object,
ty.index(),
operands[0],
));
}
Instruction::RecordLower { record, .. } => {
if record.fields.is_empty() {
return;
}
let tmp = self.locals.tmp("record");
builder.push_str(&format!("{} = {}\n", tmp, operands[0]));
for field in record.fields.iter() {
let name = self.locals.tmp("field");
builder.push_str(&format!(
"{} = {}.{}\n",
name,
tmp,
field.name.to_snake_case(),
));
results.push(name);
}
}
Instruction::RecordLift { name, .. } => {
results.push(format!("{}({})", name.to_camel_case(), operands.join(", ")));
}
Instruction::TupleLower { tuple, .. } => {
if tuple.types.is_empty() {
return;
}
builder.push_str("(");
for _ in 0..tuple.types.len() {
let name = self.locals.tmp("tuplei");
builder.push_str(&name);
builder.push_str(",");
results.push(name);
}
builder.push_str(") = ");
builder.push_str(&operands[0]);
builder.push_str("\n");
}
Instruction::TupleLift { .. } => {
if operands.is_empty() {
results.push("None".to_string());
} else {
results.push(format!("({},)", operands.join(", ")));
}
}
Instruction::FlagsLift { name, .. } => {
let operand = match operands.len() {
1 => operands[0].clone(),
_ => {
let tmp = self.locals.tmp("flags");
builder.push_str(&format!("{tmp} = 0\n"));
for (i, op) in operands.iter().enumerate() {
let i = 32 * i;
builder.push_str(&format!("{tmp} |= {op} << {i}\n"));
}
tmp
}
};
results.push(format!("{}({})", name.to_camel_case(), operand));
}
Instruction::FlagsLower { flags, .. } => match flags.repr().count() {
1 => results.push(format!("({}).value", operands[0])),
n => {
let tmp = self.locals.tmp("flags");
self.src
.push_str(&format!("{tmp} = ({}).value\n", operands[0]));
for i in 0..n {
let i = 32 * i;
results.push(format!("({tmp} >> {i}) & 0xffffffff"));
}
}
},
Instruction::VariantPayloadName => {
let name = self.locals.tmp("payload");
results.push(name.clone());
self.payloads.push(name);
}
Instruction::VariantLower {
variant,
results: result_types,
name,
..
} => {
let blocks = self
.blocks
.drain(self.blocks.len() - variant.cases.len()..)
.collect::<Vec<_>>();
let payloads = self
.payloads
.drain(self.payloads.len() - variant.cases.len()..)
.collect::<Vec<_>>();
for _ in 0..result_types.len() {
results.push(self.locals.tmp("variant"));
}
for (i, ((case, (block, block_results)), payload)) in
variant.cases.iter().zip(blocks).zip(payloads).enumerate()
{
if i == 0 {
builder.push_str("if ");
} else {
builder.push_str("elif ");
}
builder.push_str(&format!(
"isinstance({}, {}{}):\n",
operands[0],
name.to_camel_case(),
case.name.to_camel_case()
));
builder.indent();
builder.push_str(&format!("{} = {}.value\n", payload, operands[0]));
builder.push_str(&block);
for (i, result) in block_results.iter().enumerate() {
builder.push_str(&format!("{} = {}\n", results[i], result));
}
builder.dedent();
}
let variant_name = name.to_camel_case();
builder.push_str("else:\n");
builder.indent();
builder.push_str(&format!(
"raise TypeError(\"invalid variant specified for {}\")\n",
variant_name
));
builder.dedent();
}
Instruction::VariantLift {
variant, name, ty, ..
} => {
let blocks = self
.blocks
.drain(self.blocks.len() - variant.cases.len()..)
.collect::<Vec<_>>();
let result = self.locals.tmp("variant");
builder.print_var_declaration(&result, &Type::Id(*ty));
for (i, (case, (block, block_results))) in
variant.cases.iter().zip(blocks).enumerate()
{
if i == 0 {
builder.push_str("if ");
} else {
builder.push_str("elif ");
}
builder.push_str(&format!("{} == {}:\n", operands[0], i));
builder.indent();
builder.push_str(&block);
builder.push_str(&format!(
"{} = {}{}(",
result,
name.to_camel_case(),
case.name.to_camel_case()
));
assert!(block_results.len() == 1);
builder.push_str(&block_results[0]);
builder.push_str(")\n");
builder.dedent();
}
builder.push_str("else:\n");
builder.indent();
let variant_name = name.to_camel_case();
builder.push_str(&format!(
"raise TypeError(\"invalid variant discriminant for {}\")\n",
variant_name
));
builder.dedent();
results.push(result);
}
Instruction::UnionLower {
union,
results: result_types,
name,
..
} => {
let blocks = self
.blocks
.drain(self.blocks.len() - union.cases.len()..)
.collect::<Vec<_>>();
let payloads = self
.payloads
.drain(self.payloads.len() - union.cases.len()..)
.collect::<Vec<_>>();
for _ in 0..result_types.len() {
results.push(self.locals.tmp("variant"));
}
let union_representation = *self
.gen
.union_representation
.get(&name.to_string())
.unwrap();
let name = name.to_camel_case();
let op0 = &operands[0];
for (i, ((case, (block, block_results)), payload)) in
union.cases.iter().zip(blocks).zip(payloads).enumerate()
{
builder.push_str(if i == 0 { "if " } else { "elif " });
builder.push_str(&format!("isinstance({op0}, "));
match union_representation {
PyUnionRepresentation::Raw => {
builder.print_ty(&case.ty, false);
}
PyUnionRepresentation::Wrapped => {
builder.push_str(&format!("{name}{i}"));
}
}
builder.push_str("):\n");
builder.indent();
match union_representation {
PyUnionRepresentation::Raw => {
builder.push_str(&format!("{payload} = {op0}\n"))
}
PyUnionRepresentation::Wrapped => {
builder.push_str(&format!("{payload} = {op0}.value\n"))
}
}
builder.push_str(&block);
for (i, result) in block_results.iter().enumerate() {
builder.push_str(&format!("{} = {result}\n", results[i]));
}
builder.dedent();
}
builder.push_str("else:\n");
builder.indent();
builder.push_str(&format!(
"raise TypeError(\"invalid variant specified for {name}\")\n"
));
builder.dedent();
}
Instruction::UnionLift {
union, name, ty, ..
} => {
let blocks = self
.blocks
.drain(self.blocks.len() - union.cases.len()..)
.collect::<Vec<_>>();
let result = self.locals.tmp("variant");
builder.print_var_declaration(&result, &Type::Id(*ty));
let union_representation = *self
.gen
.union_representation
.get(&name.to_string())
.unwrap();
let name = name.to_camel_case();
let op0 = &operands[0];
for (i, (_case, (block, block_results))) in
union.cases.iter().zip(blocks).enumerate()
{
builder.push_str(if i == 0 { "if " } else { "elif " });
builder.push_str(&format!("{op0} == {i}:\n"));
builder.indent();
builder.push_str(&block);
assert!(block_results.len() == 1);
let block_result = &block_results[0];
builder.push_str(&format!("{result} = "));
match union_representation {
PyUnionRepresentation::Raw => builder.push_str(block_result),
PyUnionRepresentation::Wrapped => {
builder.push_str(&format!("{name}{i}({block_result})"))
}
}
builder.newline();
builder.dedent();
}
builder.push_str("else:\n");
builder.indent();
builder.push_str(&format!(
"raise TypeError(\"invalid variant discriminant for {name}\")\n",
));
builder.dedent();
results.push(result);
}
Instruction::OptionLower {
results: result_types,
..
} => {
let (some, some_results) = self.blocks.pop().unwrap();
let (none, none_results) = self.blocks.pop().unwrap();
let some_payload = self.payloads.pop().unwrap();
let _none_payload = self.payloads.pop().unwrap();
for _ in 0..result_types.len() {
results.push(self.locals.tmp("variant"));
}
let op0 = &operands[0];
builder.push_str(&format!("if {op0} is None:\n"));
builder.indent();
builder.push_str(&none);
for (dst, result) in results.iter().zip(&none_results) {
builder.push_str(&format!("{dst} = {result}\n"));
}
builder.dedent();
builder.push_str("else:\n");
builder.indent();
builder.push_str(&format!("{some_payload} = {op0}\n"));
builder.push_str(&some);
for (dst, result) in results.iter().zip(&some_results) {
builder.push_str(&format!("{dst} = {result}\n"));
}
builder.dedent();
}
Instruction::OptionLift { ty, .. } => {
let (some, some_results) = self.blocks.pop().unwrap();
let (none, none_results) = self.blocks.pop().unwrap();
assert!(none_results.len() == 1);
assert!(some_results.len() == 1);
let some_result = &some_results[0];
assert_eq!(none_results[0], "None");
let result = self.locals.tmp("option");
builder.print_var_declaration(&result, &Type::Id(*ty));
let op0 = &operands[0];
builder.push_str(&format!("if {op0} == 0:\n"));
builder.indent();
builder.push_str(&none);
builder.push_str(&format!("{result} = None\n"));
builder.dedent();
builder.push_str(&format!("elif {op0} == 1:\n"));
builder.indent();
builder.push_str(&some);
builder.push_str(&format!("{result} = {some_result}\n"));
builder.dedent();
builder.push_str("else:\n");
builder.indent();
builder.push_str("raise TypeError(\"invalid variant discriminant for option\")\n");
builder.dedent();
results.push(result);
}
Instruction::ExpectedLower {
results: result_types,
..
} => {
let (err, err_results) = self.blocks.pop().unwrap();
let (ok, ok_results) = self.blocks.pop().unwrap();
let err_payload = self.payloads.pop().unwrap();
let ok_payload = self.payloads.pop().unwrap();
for _ in 0..result_types.len() {
results.push(self.locals.tmp("variant"));
}
let op0 = &operands[0];
builder.push_str(&format!("if isinstance({op0}, Ok):\n"));
builder.indent();
builder.push_str(&format!("{ok_payload} = {op0}.value\n"));
builder.push_str(&ok);
for (dst, result) in results.iter().zip(&ok_results) {
builder.push_str(&format!("{dst} = {result}\n"));
}
builder.dedent();
builder.push_str(&format!("elif isinstance({op0}, Err):\n"));
builder.indent();
builder.push_str(&format!("{err_payload} = {op0}.value\n"));
builder.push_str(&err);
for (dst, result) in results.iter().zip(&err_results) {
builder.push_str(&format!("{dst} = {result}\n"));
}
builder.dedent();
builder.push_str("else:\n");
builder.indent();
builder.push_str("raise TypeError(\"invalid variant specified for expected\")\n");
builder.dedent();
}
Instruction::ExpectedLift { ty, .. } => {
let (err, err_results) = self.blocks.pop().unwrap();
let (ok, ok_results) = self.blocks.pop().unwrap();
assert!(err_results.len() == 1);
let err_result = &err_results[0];
assert!(ok_results.len() == 1);
let ok_result = &ok_results[0];
let result = self.locals.tmp("expected");
builder.print_var_declaration(&result, &Type::Id(*ty));
let op0 = &operands[0];
builder.push_str(&format!("if {op0} == 0:\n"));
builder.indent();
builder.push_str(&ok);
builder.push_str(&format!("{result} = Ok({ok_result})\n"));
builder.dedent();
builder.push_str(&format!("elif {op0} == 1:\n"));
builder.indent();
builder.push_str(&err);
builder.push_str(&format!("{result} = Err({err_result})\n"));
builder.dedent();
builder.push_str("else:\n");
builder.indent();
builder
.push_str("raise TypeError(\"invalid variant discriminant for expected\")\n");
builder.dedent();
results.push(result);
}
Instruction::EnumLower { .. } => results.push(format!("({}).value", operands[0])),
Instruction::EnumLift { name, .. } => {
results.push(format!("{}({})", name.to_camel_case(), operands[0]));
}
Instruction::ListCanonLower { element, realloc } => {
let realloc = realloc.unwrap();
self.needs_memory = true;
self.needs_realloc = Some(realloc.to_string());
let ptr = self.locals.tmp("ptr");
let len = self.locals.tmp("len");
let array_ty = array_ty(iface, element).unwrap();
builder.deps.needs_list_canon_lower = true;
let size = self.gen.sizes.size(element);
let align = self.gen.sizes.align(element);
builder.push_str(&format!(
"{}, {} = _list_canon_lower({}, memory.{}_view, {}, {}, realloc, memory)\n",
ptr, len, operands[0], array_ty, size, align,
));
results.push(ptr);
results.push(len);
}
Instruction::ListCanonLift { element, free, .. } => {
self.needs_memory = true;
let ptr = self.locals.tmp("ptr");
let len = self.locals.tmp("len");
builder.push_str(&format!("{} = {}\n", ptr, operands[0]));
builder.push_str(&format!("{} = {}\n", len, operands[1]));
let array_ty = array_ty(iface, element).unwrap();
builder.deps.needs_list_canon_lift = true;
let lift = format!(
"_list_canon_lift({}, {}, {}, memory.{}_view, memory)",
ptr,
len,
self.gen.sizes.size(element),
array_ty,
);
builder.deps.pyimport("typing", "cast");
let align = self.gen.sizes.align(element);
match free {
Some(free) => {
self.needs_free = Some(free.to_string());
let list = self.locals.tmp("list");
builder.push_str(&list);
builder.push_str(" = cast(");
builder.print_list(element);
builder.push_str(", ");
builder.push_str(&lift);
builder.push_str(")\n");
builder.push_str(&format!("free({}, {}, {})\n", ptr, len, align));
results.push(list);
}
None => {
let mut result_src = Source::default();
drop(builder);
let mut builder = result_src.builder(&mut self.gen.deps, iface);
builder.push_str("cast(");
builder.print_list(element);
builder.push_str(", ");
builder.push_str(&lift);
builder.push_str(")");
results.push(result_src.to_string());
}
}
}
Instruction::StringLower { realloc } => {
let realloc = realloc.unwrap();
self.needs_memory = true;
self.needs_realloc = Some(realloc.to_string());
let ptr = self.locals.tmp("ptr");
let len = self.locals.tmp("len");
builder.deps.needs_encode_utf8 = true;
builder.push_str(&format!(
"{}, {} = _encode_utf8({}, realloc, memory)\n",
ptr, len, operands[0],
));
results.push(ptr);
results.push(len);
}
Instruction::StringLift { free, .. } => {
self.needs_memory = true;
let ptr = self.locals.tmp("ptr");
let len = self.locals.tmp("len");
builder.push_str(&format!("{} = {}\n", ptr, operands[0]));
builder.push_str(&format!("{} = {}\n", len, operands[1]));
builder.deps.needs_decode_utf8 = true;
let result = format!("_decode_utf8(memory, {}, {})", ptr, len);
match free {
Some(free) => {
self.needs_free = Some(free.to_string());
let list = self.locals.tmp("list");
builder.push_str(&format!("{} = {}\n", list, result));
self.src.push_str(&format!("free({}, {}, 1)\n", ptr, len));
results.push(list);
}
None => results.push(result),
}
}
Instruction::ListLower { element, realloc } => {
let base = self.payloads.pop().unwrap();
let e = self.payloads.pop().unwrap();
let realloc = realloc.unwrap();
let (body, body_results) = self.blocks.pop().unwrap();
assert!(body_results.is_empty());
let vec = self.locals.tmp("vec");
let result = self.locals.tmp("result");
let len = self.locals.tmp("len");
self.needs_realloc = Some(realloc.to_string());
let size = self.gen.sizes.size(element);
let align = self.gen.sizes.align(element);
builder.push_str(&format!("{} = {}\n", vec, operands[0]));
builder.push_str(&format!("{} = len({})\n", len, vec));
builder.push_str(&format!(
"{} = realloc(0, 0, {}, {} * {})\n",
result, align, len, size,
));
builder.push_str(&format!("assert(isinstance({}, int))\n", result));
let i = self.locals.tmp("i");
builder.push_str(&format!("for {} in range(0, {}):\n", i, len));
builder.indent();
builder.push_str(&format!("{} = {}[{}]\n", e, vec, i));
builder.push_str(&format!("{} = {} + {} * {}\n", base, result, i, size));
builder.push_str(&body);
builder.dedent();
results.push(result);
results.push(len);
}
Instruction::ListLift { element, free, .. } => {
let (body, body_results) = self.blocks.pop().unwrap();
let base = self.payloads.pop().unwrap();
let size = self.gen.sizes.size(element);
let align = self.gen.sizes.align(element);
let ptr = self.locals.tmp("ptr");
let len = self.locals.tmp("len");
builder.push_str(&format!("{} = {}\n", ptr, operands[0]));
builder.push_str(&format!("{} = {}\n", len, operands[1]));
let result = self.locals.tmp("result");
builder.push_str(&format!("{}: List[", result));
builder.print_ty(element, true);
builder.push_str("] = []\n");
let i = self.locals.tmp("i");
builder.push_str(&format!("for {} in range(0, {}):\n", i, len));
builder.indent();
builder.push_str(&format!("{} = {} + {} * {}\n", base, ptr, i, size));
builder.push_str(&body);
assert_eq!(body_results.len(), 1);
builder.push_str(&format!("{}.append({})\n", result, body_results[0]));
builder.dedent();
if let Some(free) = free {
self.needs_free = Some(free.to_string());
builder.push_str(&format!("free({}, {} * {}, {})\n", ptr, len, size, align,));
}
results.push(result);
}
Instruction::IterElem { .. } => {
let name = self.locals.tmp("e");
results.push(name.clone());
self.payloads.push(name);
}
Instruction::IterBasePointer => {
let name = self.locals.tmp("base");
results.push(name.clone());
self.payloads.push(name);
}
Instruction::CallWasm {
iface: _,
name,
sig,
} => {
if !sig.results.is_empty() {
for i in 0..sig.results.len() {
if i > 0 {
builder.push_str(", ");
}
let ret = self.locals.tmp("ret");
builder.push_str(&ret);
results.push(ret);
}
builder.push_str(" = ");
}
builder.push_str(&self.src_object);
builder.push_str("._");
builder.push_str(&name.to_snake_case());
builder.push_str("(");
builder.push_str(&operands.join(", "));
builder.push_str(")\n");
for (ty, name) in sig.results.iter().zip(results.iter()) {
let ty = match ty {
WasmType::I32 | WasmType::I64 => "int",
WasmType::F32 | WasmType::F64 => "float",
};
self.src
.push_str(&format!("assert(isinstance({}, {}))\n", name, ty));
}
}
Instruction::CallInterface { module: _, func } => {
match &func.result {
Type::Unit => {
results.push("".to_string());
}
_ => {
let result = self.locals.tmp("ret");
builder.push_str(&result);
results.push(result);
builder.push_str(" = ");
}
}
match &func.kind {
FunctionKind::Freestanding | FunctionKind::Static { .. } => {
builder.push_str(&format!(
"host.{}({})",
func.name.to_snake_case(),
operands.join(", "),
));
}
FunctionKind::Method { name, .. } => {
builder.push_str(&format!(
"{}.{}({})",
operands[0],
name.to_snake_case(),
operands[1..].join(", "),
));
}
}
builder.push_str("\n");
}
Instruction::Return { amt, .. } => match amt {
0 => {}
1 => builder.push_str(&format!("return {}\n", operands[0])),
_ => {
self.src
.push_str(&format!("return ({})\n", operands.join(", ")));
}
},
Instruction::I32Load { offset } => self.load("int32", *offset, operands, results),
Instruction::I64Load { offset } => self.load("int64", *offset, operands, results),
Instruction::F32Load { offset } => self.load("float32", *offset, operands, results),
Instruction::F64Load { offset } => self.load("float64", *offset, operands, results),
Instruction::I32Load8U { offset } => self.load("uint8", *offset, operands, results),
Instruction::I32Load8S { offset } => self.load("int8", *offset, operands, results),
Instruction::I32Load16U { offset } => self.load("uint16", *offset, operands, results),
Instruction::I32Load16S { offset } => self.load("int16", *offset, operands, results),
Instruction::I32Store { offset } => self.store("uint32", *offset, operands),
Instruction::I64Store { offset } => self.store("uint64", *offset, operands),
Instruction::F32Store { offset } => self.store("float32", *offset, operands),
Instruction::F64Store { offset } => self.store("float64", *offset, operands),
Instruction::I32Store8 { offset } => self.store("uint8", *offset, operands),
Instruction::I32Store16 { offset } => self.store("uint16", *offset, operands),
Instruction::Malloc {
realloc,
size,
align,
} => {
self.needs_realloc = Some(realloc.to_string());
let ptr = self.locals.tmp("ptr");
builder.push_str(&format!(
"
{ptr} = realloc(0, 0, {align}, {size})
assert(isinstance({ptr}, int))
",
));
results.push(ptr);
}
i => unimplemented!("{:?}", i),
}
}
}
fn py_type_class_of(ty: &Type) -> PyTypeClass {
match ty {
Type::Unit => PyTypeClass::None,
Type::Bool
| Type::U8
| Type::U16
| Type::U32
| Type::U64
| Type::S8
| Type::S16
| Type::S32
| Type::S64 => PyTypeClass::Int,
Type::Float32 | Type::Float64 => PyTypeClass::Float,
Type::Char | Type::String => PyTypeClass::Str,
Type::Handle(_) | Type::Id(_) => PyTypeClass::Custom,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum PyTypeClass {
None,
Int,
Str,
Float,
Custom,
}
fn wasm_ty_ctor(ty: WasmType) -> &'static str {
match ty {
WasmType::I32 => "wasmer.Type.I32",
WasmType::I64 => "wasmer.Type.I64",
WasmType::F32 => "wasmer.Type.F32",
WasmType::F64 => "wasmer.Type.F64",
}
}
fn wasm_ty_typing(ty: WasmType) -> &'static str {
match ty {
WasmType::I32 => "int",
WasmType::I64 => "int",
WasmType::F32 => "float",
WasmType::F64 => "float",
}
}