Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Fixed size array support #3458

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions crates/cli-support/src/descriptor.rs
Expand Up @@ -40,6 +40,7 @@ tys! {
RESULT
UNIT
CLAMPED
FIXED_ARRAY
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -72,6 +73,7 @@ pub enum Descriptor {
Option(Box<Descriptor>),
Result(Box<Descriptor>),
Unit,
FixedArray(Box<Descriptor>, u32),
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -161,6 +163,9 @@ impl Descriptor {
CHAR => Descriptor::Char,
UNIT => Descriptor::Unit,
CLAMPED => Descriptor::_decode(data, true),
FIXED_ARRAY => {
Descriptor::FixedArray(Box::new(Descriptor::_decode(data, clamped)), get(data))
}
other => panic!("unknown descriptor: {}", other),
}
}
Expand Down Expand Up @@ -193,6 +198,7 @@ impl Descriptor {
Descriptor::Slice(ref d) => &**d,
_ => return None,
},
Descriptor::FixedArray(ref d, _) => &**d,
_ => return None,
};
match *inner {
Expand Down
9 changes: 9 additions & 0 deletions crates/cli-support/src/descriptors.rs
Expand Up @@ -47,6 +47,12 @@ impl WasmBindgenDescriptorsSection {
interpreter: &mut Interpreter,
) -> Result<(), Error> {
let mut to_remove = Vec::new();
let to_print = module
.exports
.iter()
.map(|m| m.name.clone())
.collect::<Vec<_>>();
println!("All module exports:\n{to_print:#?}");
for export in module.exports.iter() {
let prefix = "__wbindgen_describe_";
if !export.name.starts_with(prefix) {
Expand All @@ -59,7 +65,10 @@ impl WasmBindgenDescriptorsSection {
if let Some(d) = interpreter.interpret_descriptor(id, module) {
let name = &export.name[prefix.len()..];
let descriptor = Descriptor::decode(d);
println!("Adding descriptor: {name:?} - {descriptor:#?}");
self.descriptors.insert(name.to_string(), descriptor);
} else {
eprintln!("Somehow this returned none...?, id: {:?}", id);
}
to_remove.push(export.id());
}
Expand Down
41 changes: 41 additions & 0 deletions crates/cli-support/src/js/binding.rs
Expand Up @@ -1101,6 +1101,42 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
js.push(format!("v{}", i))
}

Instruction::WasmToFixedArray { kind, length } => {
// Need to convert to JS num here
let convert_to_js_num = |val: &str| -> String {
match kind {
AdapterType::U32 => format!("{} >>> 0", val),
AdapterType::U64 => format!("BigInt.asUintN(64, {val})"),
_ => val.to_string(),
}
};
let mut to_ret = (0..*length)
.map(|_| {
let val = js.pop();
convert_to_js_num(&val)
})
.collect::<Vec<_>>();
to_ret.reverse();
js.push(format!("[{}]", to_ret.join(", ")));
}

Instruction::FixedArrayToWasm { kind, length } => {
let input = js.pop();
js.prelude(&format!("if ({input}.length !== {length}) {{\n throw new Error(`Expected an Array of length {length}, received array of length ${{{input}.length}}`);\n}}"));
// if matches!(kind, AdapterType::I64 | AdapterType::S64 | AdapterType::U64) {
// js.assert_bigint(&element);
// } else {
// js.assert_number(&element);
// }
// let elements = (0..*length)
// .map(|i| format!("{}[{}]", input, i))
// .collect::<Vec<_>>();
for i in 0..*length {
let element = format!("{}[{}]", input, i);
js.push(element);
}
}

Instruction::OptionVectorLoad { kind, mem, free } => {
let len = js.pop();
let ptr = js.pop();
Expand Down Expand Up @@ -1300,5 +1336,10 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) {
AdapterType::NamedExternref(name) => dst.push_str(name),
AdapterType::Struct(name) => dst.push_str(name),
AdapterType::Function => dst.push_str("any"),
AdapterType::Array(ty, _) => {
let mut inner = String::new();
adapter2ts(ty, &mut inner);
dst.push_str(&format!("Array<{}>", inner));
}
}
}
2 changes: 2 additions & 0 deletions crates/cli-support/src/lib.rs
Expand Up @@ -439,6 +439,8 @@ impl Bindgen {
let mut cx = js::Context::new(&mut module, self, &adapters, &aux)?;
cx.generate()?;
let (js, ts, start) = cx.finalize(stem)?;
// let (js, ts, start) = (String::default(), String::default(), None);

let generated = Generated {
snippets: aux.snippets.clone(),
local_modules: aux.local_modules.clone(),
Expand Down
55 changes: 50 additions & 5 deletions crates/cli-support/src/multivalue.rs
@@ -1,17 +1,19 @@
use crate::wit::{Adapter, NonstandardWitSection};
use crate::wit::{Adapter, AdapterType, NonstandardWitSection};
use crate::wit::{AdapterKind, Instruction, WasmBindgenAux};
use anyhow::{anyhow, Error};
use walrus::ir::Value;
use walrus::{FunctionId, InitExpr, Module};
use wasm_bindgen_multi_value_xform as multi_value_xform;
use wasm_bindgen_multi_value_xform::{ArrayLoad, ToTransform};
use wasm_bindgen_wasm_conventions as wasm_conventions;
use wasm_bindgen_wasm_conventions::get_func_name_from_id;

pub fn run(module: &mut Module) -> Result<(), Error> {
let mut adapters = module
.customs
.delete_typed::<NonstandardWitSection>()
.unwrap();
let mut to_xform = Vec::new();
let mut to_xform: Vec<ToTransform> = Vec::new();
let mut slots = Vec::new();

for (_, adapter) in adapters.adapters.iter_mut() {
Expand Down Expand Up @@ -56,12 +58,16 @@ enum Slot<'a> {
fn extract_xform<'a>(
module: &Module,
adapter: &'a mut Adapter,
to_xform: &mut Vec<(walrus::FunctionId, usize, Vec<walrus::ValType>)>,
to_xform: &mut Vec<ToTransform>,
slots: &mut Vec<Slot<'a>>,
) {
let adapter_clone = adapter.clone();
let instructions = match &mut adapter.kind {
AdapterKind::Local { instructions } => instructions,
AdapterKind::Import { .. } => return,
AdapterKind::Import { .. } => {
println!("Import adapter: {adapter_clone:#?}");
return;
}
};

// If the first instruction is a `Retptr`, then this must be an exported
Expand All @@ -77,6 +83,16 @@ fn extract_xform<'a>(
}
_ => true,
});
let array_config = instructions.iter().find_map(|i| match &i.instr {
Instruction::WasmToFixedArray { kind, length: _ } => match kind {
AdapterType::S8 => Some(ArrayLoad::I8),
AdapterType::S16 => Some(ArrayLoad::I16),
AdapterType::U8 => Some(ArrayLoad::U8),
AdapterType::U16 => Some(ArrayLoad::U16),
_ => None,
},
_ => None,
});
let slot = instructions
.iter_mut()
.find_map(|i| match &mut i.instr {
Expand All @@ -97,8 +113,37 @@ fn extract_xform<'a>(
},
Slot::TableElement(func_index) => resolve_table_entry(module, *func_index),
};
to_xform.push((id, 0, types));
println!(
"About to transform `{}`: {:#?}",
get_func_name_from_id(module, id),
adapter_clone
);
to_xform.push((id, 0, types, array_config));
slots.push(slot);
} else {
println!("Adapter that we didn't xform: {:#?}", adapter_clone);
if let Some(slot) = instructions.iter_mut().find_map(|i| match &mut i.instr {
Instruction::CallCore(f) => Some(Slot::Id(f)),
Instruction::CallExport(e) => Some(Slot::Export(*e)),
Instruction::CallTableElement(index) => Some(Slot::TableElement(*index)),
_ => None,
}) {
let id = match &slot {
Slot::Id(i) => **i,
Slot::Export(e) => match module.exports.get(*e).item {
walrus::ExportItem::Function(f) => f,
_ => panic!("found call to non-function export"),
},
Slot::TableElement(func_index) => resolve_table_entry(module, *func_index),
};

println!(
"Got name for adapter function {:?} with ID {:?}: {}",
adapter_clone.id,
id,
get_func_name_from_id(module, id)
);
}
}

// If the last instruction is a `StoreRetptr`, then this must be an adapter
Expand Down
23 changes: 23 additions & 0 deletions crates/cli-support/src/wit/incoming.rs
Expand Up @@ -11,6 +11,7 @@ use crate::descriptor::Descriptor;
use crate::wit::InstructionData;
use crate::wit::{AdapterType, Instruction, InstructionBuilder, StackChange};
use anyhow::{bail, format_err, Error};
use std::ops::Deref;
use walrus::ValType;

impl InstructionBuilder<'_, '_> {
Expand Down Expand Up @@ -146,6 +147,28 @@ impl InstructionBuilder<'_, '_> {

// Largely synthetic and can't show up
Descriptor::ClampedU8 => unreachable!(),
Descriptor::FixedArray(d,length) => {
let (input_ty, output_ty) = match d.deref() {
Descriptor::U8 => (AdapterType::U8, AdapterType::I32),
Descriptor::I8 => (AdapterType::S8, AdapterType::I32),
Descriptor::U16 => (AdapterType::U16, AdapterType::I32),
Descriptor::I16 => (AdapterType::S16, AdapterType::I32),
Descriptor::U32 => (AdapterType::U32, AdapterType::I32),
Descriptor::I32 => (AdapterType::S32, AdapterType::I32),
Descriptor::U64 => (AdapterType::U64, AdapterType::I64),
Descriptor::I64 => (AdapterType::S64, AdapterType::I64),
Descriptor::F32 => (AdapterType::F32, AdapterType::F32),
Descriptor::F64 => (AdapterType::F64, AdapterType::F64),
d => unimplemented!("unsupported type for fixed size arrays: {d:?}"),
};
let input = AdapterType::Array(Box::new(input_ty.clone()), *length as usize);
let instr = Instruction::FixedArrayToWasm {
kind: input_ty,
length: *length as usize,
};
let outputs = (0..*length).map(|_| output_ty.clone()).collect::<Vec<_>>();
self.instruction(&[input], instr, &outputs);
}
}
Ok(())
}
Expand Down
32 changes: 32 additions & 0 deletions crates/cli-support/src/wit/outgoing.rs
Expand Up @@ -2,6 +2,7 @@ use crate::descriptor::Descriptor;
use crate::wit::{AdapterType, Instruction, InstructionBuilder};
use crate::wit::{InstructionData, StackChange};
use anyhow::{bail, format_err, Error};
use std::ops::Deref;
use walrus::ValType;

impl InstructionBuilder<'_, '_> {
Expand Down Expand Up @@ -154,6 +155,32 @@ impl InstructionBuilder<'_, '_> {
// nothing to do
Descriptor::Unit => {}

Descriptor::FixedArray(d, length) => {
let (output_ty, input_ty) = match d.deref() {
Descriptor::U8 => (AdapterType::U8, AdapterType::I32),
Descriptor::I8 => (AdapterType::S8, AdapterType::I32),
Descriptor::U16 => (AdapterType::U16, AdapterType::I32),
Descriptor::I16 => (AdapterType::S16, AdapterType::I32),
Descriptor::U32 => (AdapterType::U32, AdapterType::I32),
Descriptor::I32 => (AdapterType::S32, AdapterType::I32),
Descriptor::U64 => (AdapterType::U64, AdapterType::I64),
Descriptor::I64 => (AdapterType::S64, AdapterType::I64),
Descriptor::F32 => (AdapterType::F32, AdapterType::F32),
Descriptor::F64 => (AdapterType::F64, AdapterType::F64),
d => unimplemented!("unsupported type for fixed size arrays: {d:?}",),
};
let inputs = (0..*length).map(|_| input_ty.clone()).collect::<Vec<_>>();
let instr = Instruction::WasmToFixedArray {
kind: output_ty.clone(),
length: *length as usize,
};
self.instruction(
&inputs,
instr,
&[AdapterType::Array(Box::new(output_ty), *length as usize)],
);
}

// Largely synthetic and can't show up
Descriptor::ClampedU8 => unreachable!(),
}
Expand Down Expand Up @@ -401,6 +428,11 @@ impl InstructionBuilder<'_, '_> {
self.get(AdapterType::I32);
self.get(AdapterType::I32);
}

Descriptor::FixedArray(_, _) => {
bail!("SoonTM")
}

Descriptor::String => {
// fetch the ptr/length ...
self.get(AdapterType::I32);
Expand Down
9 changes: 9 additions & 0 deletions crates/cli-support/src/wit/standard.rs
Expand Up @@ -87,6 +87,7 @@ pub enum AdapterType {
Struct(String),
NamedExternref(String),
Function,
Array(Box<AdapterType>, usize),
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -277,6 +278,14 @@ pub enum Instruction {
mem: walrus::MemoryId,
free: walrus::FunctionId,
},
FixedArrayToWasm {
kind: AdapterType,
length: usize,
},
WasmToFixedArray {
kind: AdapterType,
length: usize,
},
/// pops i32, loads externref from externref table
TableGet,
/// pops two i32 data pointers, pushes an externref closure
Expand Down