Skip to content

Commit ad62c5a

Browse files
author
Guy Bedford
authored
gen-host-js: support direct ArrayBuffer for Uint8Array lists (#423)
1 parent 217b866 commit ad62c5a

3 files changed

Lines changed: 87 additions & 41 deletions

File tree

crates/gen-host-js/src/lib.rs

Lines changed: 84 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,14 @@ impl ComponentGenerator for Js {
371371

372372
impl WorldGenerator for Js {
373373
fn import(&mut self, name: &str, iface: &Interface, files: &mut Files) {
374-
self.generate_interface(name, iface, "imports", "Imports", files);
374+
self.generate_interface(
375+
name,
376+
iface,
377+
"imports",
378+
"Imports",
379+
files,
380+
AbiVariant::GuestImport,
381+
);
375382
let camel = name.to_upper_camel_case();
376383
uwriteln!(
377384
self.import_object,
@@ -380,7 +387,14 @@ impl WorldGenerator for Js {
380387
}
381388

382389
fn export(&mut self, name: &str, iface: &Interface, files: &mut Files) {
383-
self.generate_interface(name, iface, "exports", "Exports", files);
390+
self.generate_interface(
391+
name,
392+
iface,
393+
"exports",
394+
"Exports",
395+
files,
396+
AbiVariant::GuestExport,
397+
);
384398
let camel = name.to_upper_camel_case();
385399
uwriteln!(self.src.ts, "export const {name}: typeof {camel}Exports;");
386400
}
@@ -389,7 +403,7 @@ impl WorldGenerator for Js {
389403
let instantiation = self.opts.instantiation;
390404
let mut gen = self.js_interface(iface);
391405
for func in iface.functions.iter() {
392-
gen.ts_func(func);
406+
gen.ts_func(func, AbiVariant::GuestExport);
393407
}
394408
if instantiation {
395409
gen.gen.export_object.push_str(&mem::take(&mut gen.src.ts));
@@ -435,6 +449,7 @@ impl Js {
435449
dir: &str,
436450
extra: &str,
437451
files: &mut Files,
452+
abi: AbiVariant,
438453
) {
439454
let camel = name.to_upper_camel_case();
440455
let mut gen = self.js_interface(iface);
@@ -443,7 +458,7 @@ impl Js {
443458

444459
uwriteln!(gen.src.ts, "export namespace {camel} {{");
445460
for func in iface.functions.iter() {
446-
gen.ts_func(func);
461+
gen.ts_func(func, abi);
447462
}
448463
uwriteln!(gen.src.ts, "}}");
449464

@@ -1265,6 +1280,12 @@ impl Instantiator<'_> {
12651280
}
12661281
}
12671282

1283+
#[derive(Copy, Clone)]
1284+
enum Mode {
1285+
Lift,
1286+
Lower,
1287+
}
1288+
12681289
impl<'a> JsInterface<'a> {
12691290
fn docs_raw(&mut self, docs: &str) {
12701291
self.src.ts("/**\n");
@@ -1285,7 +1306,7 @@ impl<'a> JsInterface<'a> {
12851306
self.gen.array_ty(self.iface, ty)
12861307
}
12871308

1288-
fn print_ty(&mut self, ty: &Type) {
1309+
fn print_ty(&mut self, ty: &Type, mode: Mode) {
12891310
match ty {
12901311
Type::Bool => self.src.ts("boolean"),
12911312
Type::U8
@@ -1305,8 +1326,8 @@ impl<'a> JsInterface<'a> {
13051326
return self.src.ts(&name.to_upper_camel_case());
13061327
}
13071328
match &ty.kind {
1308-
TypeDefKind::Type(t) => self.print_ty(t),
1309-
TypeDefKind::Tuple(t) => self.print_tuple(t),
1329+
TypeDefKind::Type(t) => self.print_ty(t, mode),
1330+
TypeDefKind::Tuple(t) => self.print_tuple(t, mode),
13101331
TypeDefKind::Record(_) => panic!("anonymous record"),
13111332
TypeDefKind::Flags(_) => panic!("anonymous flags"),
13121333
TypeDefKind::Enum(_) => panic!("anonymous enum"),
@@ -1315,59 +1336,63 @@ impl<'a> JsInterface<'a> {
13151336
if self.maybe_null(t) {
13161337
self.needs_ty_option = true;
13171338
self.src.ts("Option<");
1318-
self.print_ty(t);
1339+
self.print_ty(t, mode);
13191340
self.src.ts(">");
13201341
} else {
1321-
self.print_ty(t);
1342+
self.print_ty(t, mode);
13221343
self.src.ts(" | null");
13231344
}
13241345
}
13251346
TypeDefKind::Result(r) => {
13261347
self.needs_ty_result = true;
13271348
self.src.ts("Result<");
1328-
self.print_optional_ty(r.ok.as_ref());
1349+
self.print_optional_ty(r.ok.as_ref(), mode);
13291350
self.src.ts(", ");
1330-
self.print_optional_ty(r.err.as_ref());
1351+
self.print_optional_ty(r.err.as_ref(), mode);
13311352
self.src.ts(">");
13321353
}
13331354
TypeDefKind::Variant(_) => panic!("anonymous variant"),
1334-
TypeDefKind::List(v) => self.print_list(v),
1355+
TypeDefKind::List(v) => self.print_list(v, mode),
13351356
TypeDefKind::Future(_) => todo!("anonymous future"),
13361357
TypeDefKind::Stream(_) => todo!("anonymous stream"),
13371358
}
13381359
}
13391360
}
13401361
}
13411362

1342-
fn print_optional_ty(&mut self, ty: Option<&Type>) {
1363+
fn print_optional_ty(&mut self, ty: Option<&Type>, mode: Mode) {
13431364
match ty {
1344-
Some(ty) => self.print_ty(ty),
1365+
Some(ty) => self.print_ty(ty, mode),
13451366
None => self.src.ts("void"),
13461367
}
13471368
}
13481369

1349-
fn print_list(&mut self, ty: &Type) {
1370+
fn print_list(&mut self, ty: &Type, mode: Mode) {
13501371
match self.array_ty(ty) {
1372+
Some("Uint8Array") => match mode {
1373+
Mode::Lift => self.src.ts("Uint8Array"),
1374+
Mode::Lower => self.src.ts("Uint8Array | ArrayBuffer"),
1375+
},
13511376
Some(ty) => self.src.ts(ty),
13521377
None => {
1353-
self.print_ty(ty);
1378+
self.print_ty(ty, mode);
13541379
self.src.ts("[]");
13551380
}
13561381
}
13571382
}
13581383

1359-
fn print_tuple(&mut self, tuple: &Tuple) {
1384+
fn print_tuple(&mut self, tuple: &Tuple, mode: Mode) {
13601385
self.src.ts("[");
13611386
for (i, ty) in tuple.types.iter().enumerate() {
13621387
if i > 0 {
13631388
self.src.ts(", ");
13641389
}
1365-
self.print_ty(ty);
1390+
self.print_ty(ty, mode);
13661391
}
13671392
self.src.ts("]");
13681393
}
13691394

1370-
fn ts_func(&mut self, func: &Function) {
1395+
fn ts_func(&mut self, func: &Function, abi: AbiVariant) {
13711396
self.docs(&func.docs);
13721397

13731398
self.src.ts("export function ");
@@ -1384,22 +1409,32 @@ impl<'a> JsInterface<'a> {
13841409
}
13851410
self.src.ts(to_js_ident(&name.to_lower_camel_case()));
13861411
self.src.ts(": ");
1387-
self.print_ty(ty);
1412+
self.print_ty(
1413+
ty,
1414+
match abi {
1415+
AbiVariant::GuestExport => Mode::Lower,
1416+
AbiVariant::GuestImport => Mode::Lift,
1417+
},
1418+
);
13881419
}
13891420
self.src.ts("): ");
1421+
let result_mode = match abi {
1422+
AbiVariant::GuestExport => Mode::Lift,
1423+
AbiVariant::GuestImport => Mode::Lower,
1424+
};
13901425
if let Some((ok_ty, _)) = func.results.throws(self.iface) {
1391-
self.print_optional_ty(ok_ty);
1426+
self.print_optional_ty(ok_ty, result_mode);
13921427
} else {
13931428
match func.results.len() {
13941429
0 => self.src.ts("void"),
1395-
1 => self.print_ty(func.results.iter_types().next().unwrap()),
1430+
1 => self.print_ty(func.results.iter_types().next().unwrap(), result_mode),
13961431
_ => {
13971432
self.src.ts("[");
13981433
for (i, ty) in func.results.iter_types().enumerate() {
13991434
if i != 0 {
14001435
self.src.ts(", ");
14011436
}
1402-
self.print_ty(ty);
1437+
self.print_ty(ty, result_mode);
14031438
}
14041439
self.src.ts("]");
14051440
}
@@ -1452,7 +1487,7 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
14521487
field.name.to_lower_camel_case(),
14531488
option_str
14541489
));
1455-
self.print_ty(ty);
1490+
self.print_ty(ty, Mode::Lift);
14561491
self.src.ts(",\n");
14571492
}
14581493
self.src.ts("}\n");
@@ -1462,7 +1497,7 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
14621497
self.docs(docs);
14631498
self.src
14641499
.ts(&format!("export type {} = ", name.to_upper_camel_case()));
1465-
self.print_tuple(tuple);
1500+
self.print_tuple(tuple, Mode::Lift);
14661501
self.src.ts(";\n");
14671502
}
14681503

@@ -1503,7 +1538,7 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
15031538
self.src.ts("',\n");
15041539
if let Some(ty) = case.ty {
15051540
self.src.ts("val: ");
1506-
self.print_ty(&ty);
1541+
self.print_ty(&ty, Mode::Lift);
15071542
self.src.ts(",\n");
15081543
}
15091544
self.src.ts("}\n");
@@ -1526,7 +1561,7 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
15261561
self.src.ts(&format!("export interface {name}{i} {{\n"));
15271562
self.src.ts(&format!("tag: {i},\n"));
15281563
self.src.ts("val: ");
1529-
self.print_ty(&case.ty);
1564+
self.print_ty(&case.ty, Mode::Lift);
15301565
self.src.ts(",\n");
15311566
self.src.ts("}\n");
15321567
}
@@ -1539,10 +1574,10 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
15391574
if self.maybe_null(payload) {
15401575
self.needs_ty_option = true;
15411576
self.src.ts("Option<");
1542-
self.print_ty(payload);
1577+
self.print_ty(payload, Mode::Lift);
15431578
self.src.ts(">");
15441579
} else {
1545-
self.print_ty(payload);
1580+
self.print_ty(payload, Mode::Lift);
15461581
self.src.ts(" | null");
15471582
}
15481583
self.src.ts(";\n");
@@ -1553,9 +1588,9 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
15531588
let name = name.to_upper_camel_case();
15541589
self.needs_ty_result = true;
15551590
self.src.ts(&format!("export type {name} = Result<"));
1556-
self.print_optional_ty(result.ok.as_ref());
1591+
self.print_optional_ty(result.ok.as_ref(), Mode::Lift);
15571592
self.src.ts(", ");
1558-
self.print_optional_ty(result.err.as_ref());
1593+
self.print_optional_ty(result.err.as_ref(), Mode::Lift);
15591594
self.src.ts(">;\n");
15601595
}
15611596

@@ -1598,15 +1633,15 @@ impl<'a> InterfaceGenerator<'a> for JsInterface<'a> {
15981633
self.docs(docs);
15991634
self.src
16001635
.ts(&format!("export type {} = ", name.to_upper_camel_case()));
1601-
self.print_ty(ty);
1636+
self.print_ty(ty, Mode::Lift);
16021637
self.src.ts(";\n");
16031638
}
16041639

16051640
fn type_list(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
16061641
self.docs(docs);
16071642
self.src
16081643
.ts(&format!("export type {} = ", name.to_upper_camel_case()));
1609-
self.print_list(ty);
1644+
self.print_list(ty, Mode::Lift);
16101645
self.src.ts(";\n");
16111646
}
16121647

@@ -2497,16 +2532,27 @@ impl Bindgen for FunctionBindgen<'_> {
24972532
let size = self.sizes.size(element);
24982533
let align = self.sizes.align(element);
24992534
uwriteln!(self.src.js, "const val{tmp} = {};", operands[0]);
2500-
uwriteln!(self.src.js, "const len{tmp} = val{tmp}.length;");
2535+
if matches!(element, Type::U8) {
2536+
uwriteln!(self.src.js, "const len{tmp} = val{tmp}.byteLength;");
2537+
} else {
2538+
uwriteln!(self.src.js, "const len{tmp} = val{tmp}.length;");
2539+
}
25012540
uwriteln!(
25022541
self.src.js,
25032542
"const ptr{tmp} = {realloc}(0, 0, {align}, len{tmp} * {size});"
25042543
);
25052544
// TODO: this is the wrong endianness
2506-
uwriteln!(
2507-
self.src.js,
2508-
"const src{tmp} = new Uint8Array(val{tmp}.buffer, val{tmp}.byteOffset, len{tmp} * {size});",
2509-
);
2545+
if matches!(element, Type::U8) {
2546+
uwriteln!(
2547+
self.src.js,
2548+
"const src{tmp} = new Uint8Array(val{tmp}.buffer || val{tmp}, val{tmp}.byteOffset, len{tmp} * {size});",
2549+
);
2550+
} else {
2551+
uwriteln!(
2552+
self.src.js,
2553+
"const src{tmp} = new Uint8Array(val{tmp}.buffer, val{tmp}.byteOffset, len{tmp} * {size});",
2554+
);
2555+
}
25102556
uwriteln!(
25112557
self.src.js,
25122558
"(new Uint8Array({memory}.buffer, ptr{tmp}, len{tmp} * {size})).set(src{tmp});",

crates/gen-host-js/tests/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ export async function loadWasm(path: string) {
1616
}
1717

1818
// Export a WASI interface directly for instance imports
19-
export function log (bytes: Uint8Array) {
19+
export function log (bytes: Uint8Array | ArrayBuffer) {
2020
stdout.write(bytes);
2121
}
22-
export function logErr (bytes: Uint8Array) {
22+
export function logErr (bytes: Uint8Array | ArrayBuffer) {
2323
stderr.write(bytes);
2424
}

tests/runtime/lists/host.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ async function run() {
106106
wasm.testImports();
107107
wasm.emptyListParam(new Uint8Array([]));
108108
wasm.emptyStringParam('');
109-
wasm.listParam(new Uint8Array([1, 2, 3, 4]));
109+
wasm.listParam(new Uint8Array([1, 2, 3, 4]).buffer);
110110
wasm.listParam2("foo");
111111
wasm.listParam3(["foo", "bar", "baz"]);
112112
wasm.listParam4([["foo", "bar"], ["baz"]]);

0 commit comments

Comments
 (0)