Skip to content

Commit e7fd381

Browse files
author
Guy Bedford
authored
gen-host-js: --load flag support (#418)
1 parent 47037af commit e7fd381

3 files changed

Lines changed: 70 additions & 41 deletions

File tree

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

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ pub struct Opts {
6565
)
6666
)]
6767
pub instantiation: bool,
68-
/// Inline core WebAssembly modules as base64 strings.
69-
#[cfg_attr(feature = "clap", arg(long, conflicts_with = "instantiation"))]
70-
pub base64: bool,
68+
/// Specify how generated WebAssembly core modules are loaded
69+
#[cfg_attr(feature = "clap", arg(long, conflicts_with = "instantiation", value_enum, default_value_t = Load::Fetch))]
70+
pub load: Load,
7171
/// Comma-separated list of "from-specifier=./to-specifier.js" mappings of
7272
/// component import specifiers to JS import specifiers.
7373
#[cfg_attr(feature = "clap", arg(long), clap(value_parser = maps_str_to_map))]
@@ -91,6 +91,18 @@ pub struct Opts {
9191
pub valid_lifting_optimization: bool,
9292
}
9393

94+
#[derive(Debug, Clone, Default)]
95+
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
96+
pub enum Load {
97+
/// Fetch core WebAssembly modules as separate files.
98+
#[default]
99+
Fetch,
100+
/// Inline core WebAssembly modules as base64 strings.
101+
Base64,
102+
/// Define custom scoped loadWasm & instantiateWasm functions.
103+
Custom,
104+
}
105+
94106
impl Opts {
95107
pub fn build(self) -> Result<Box<dyn ComponentGenerator>> {
96108
let mut gen = Js::default();
@@ -113,8 +125,9 @@ enum Intrinsic {
113125
GetErrorPayload,
114126
HasOwnProperty,
115127
I32ToF32,
116-
IsLE,
117128
I64ToF64,
129+
InstantiateWasm,
130+
IsLE,
118131
LoadWasm,
119132
ThrowInvalidBool,
120133
ThrowUninitialized,
@@ -156,8 +169,9 @@ impl Intrinsic {
156169
Intrinsic::GetErrorPayload => "getErrorPayload",
157170
Intrinsic::HasOwnProperty => "hasOwnProperty",
158171
Intrinsic::I32ToF32 => "i32ToF32",
159-
Intrinsic::IsLE => "isLE",
160172
Intrinsic::I64ToF64 => "i64ToF64",
173+
Intrinsic::InstantiateWasm => "instantiateWasm",
174+
Intrinsic::IsLE => "isLE",
161175
Intrinsic::LoadWasm => "loadWasm",
162176
Intrinsic::ThrowInvalidBool => "throwInvalidBool",
163177
Intrinsic::ThrowUninitialized => "throwUninitialized",
@@ -279,19 +293,31 @@ impl ComponentGenerator for Js {
279293
}
280294
}
281295

282-
if self.opts.base64 && self.core_module_cnt > 0 {
296+
if !self.opts.instantiation
297+
&& !matches!(self.opts.load, Load::Custom)
298+
&& self.core_module_cnt > 0
299+
{
283300
let mut first = true;
284-
output.push_str("const ");
301+
uwrite!(output, "const ");
285302
for i in 0..self.core_module_cnt {
286303
if first {
287304
first = false;
288305
} else {
289-
output.push_str(", ");
306+
uwriteln!(output, ",");
307+
}
308+
let name = self.core_file_name(name, i as u32);
309+
match self.opts.load {
310+
Load::Fetch => {
311+
uwrite!(output, "CORE{i} = new URL('./{name}', import.meta.url)")
312+
}
313+
Load::Base64 => {
314+
let data = files.remove(&name).unwrap();
315+
uwrite!(output, "CORE{i} = '{}'", base64::encode(&data));
316+
}
317+
Load::Custom => {}
290318
}
291-
let data = files.remove(&self.core_file_name(name, i as u32)).unwrap();
292-
uwrite!(output, "BINARY{i} = '{}'", base64::encode(&data));
293319
}
294-
output.push_str(";");
320+
uwriteln!(output, ";");
295321
}
296322

297323
let (maybe_init_export, maybe_init) = if self.opts.tla_compat {
@@ -526,31 +552,39 @@ impl Js {
526552
const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer);
527553
"),
528554

529-
Intrinsic::LoadWasm => if self.opts.base64 {
530-
if self.opts.nodejs_compat {
555+
Intrinsic::LoadWasm => match self.opts.load {
556+
Load::Fetch => if self.opts.nodejs_compat {
557+
self.src.js_intrinsics("
558+
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
559+
let _fs;
560+
async function loadWasm (url) {
561+
if (isNode) {
562+
_fs = _fs || await import('fs/promises');
563+
return WebAssembly.compile(await _fs.readFile(url));
564+
}
565+
return fetch(url).then(WebAssembly.compileStreaming);
566+
}
567+
")
568+
} else {
569+
self.src.js_intrinsics("
570+
const loadWasm = url => fetch(url).then(WebAssembly.compileStreaming);
571+
")
572+
},
573+
Load::Base64 => if self.opts.nodejs_compat {
531574
self.src.js_intrinsics("
532575
const loadWasm = str => WebAssembly.compile(typeof Buffer !== 'undefined' ? Buffer.from(str, 'base64') : Uint8Array.from(atob(str), b => b.charCodeAt(0)));
533576
")
534577
} else {
535578
self.src.js_intrinsics("
536579
const loadWasm = str => WebAssembly.compile(Uint8Array.from(atob(str), b => b.charCodeAt(0)));
537580
")
538-
}
539-
} else if self.opts.nodejs_compat {
540-
self.src.js_intrinsics("
541-
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
542-
let _fs;
543-
async function loadWasm (url) {
544-
if (isNode) {
545-
_fs = _fs || await import('fs/promises');
546-
return WebAssembly.compile(await _fs.readFile(url));
547-
}
548-
return fetch(url).then(WebAssembly.compileStreaming);
549-
}
550-
")
551-
} else {
581+
},
582+
Load::Custom => {},
583+
},
584+
585+
Intrinsic::InstantiateWasm => if !matches!(self.opts.load, Load::Custom) {
552586
self.src.js_intrinsics("
553-
const loadWasm = url => fetch(url).then(WebAssembly.compileStreaming);
587+
const instantiateWasm = WebAssembly.instantiate;
554588
")
555589
},
556590

@@ -826,22 +860,16 @@ impl Instantiator<'_> {
826860
for init in self.component.initializers.iter() {
827861
if let GlobalInitializer::InstantiateModule(InstantiateModule::Static(idx, _)) = init {
828862
// Get the compiled WebAssembly.Module objects in parallel
829-
let local_name = format!("module{}", idx.as_u32());
830-
let name = self.gen.core_file_name(&self.name, idx.as_u32());
863+
let idx_num = idx.as_u32();
864+
let local_name = format!("module{}", idx_num);
865+
let name = self.gen.core_file_name(&self.name, idx_num);
831866
if self.gen.opts.instantiation {
832867
uwriteln!(self.src.js, "const {local_name} = compileCore('{name}');");
833-
} else if self.gen.opts.base64 {
834-
let load_wasm = self.gen.intrinsic(Intrinsic::LoadWasm);
835-
let idx_num = idx.as_u32();
836-
uwriteln!(
837-
self.src.js_init,
838-
"const {local_name} = {load_wasm}(BINARY{idx_num});"
839-
);
840868
} else {
841869
let load_wasm = self.gen.intrinsic(Intrinsic::LoadWasm);
842870
uwriteln!(
843871
self.src.js_init,
844-
"const {local_name} = {load_wasm}(new URL('./{name}', import.meta.url));"
872+
"const {local_name} = {load_wasm}(CORE{idx_num});"
845873
);
846874
}
847875
}
@@ -979,11 +1007,12 @@ impl Instantiator<'_> {
9791007

9801008
let i = self.instances.push(idx);
9811009
let iu32 = i.as_u32();
1010+
let instantiate = self.gen.intrinsic(Intrinsic::InstantiateWasm);
9821011
uwriteln!(self.src.js, "let exports{iu32};");
9831012
uwriteln!(
9841013
self.src.js_init,
9851014
"
986-
({{ exports: exports{iu32} }} = await WebAssembly.instantiate(await module{}{imports}));\
1015+
({{ exports: exports{iu32} }} = await {instantiate}(await module{}{imports}));\
9871016
",
9881017
idx.as_u32()
9891018
);

tests/runtime/exports_only/host.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ strictEqual(result, 'test');
1313
// Verify the inlined file size does not regress
1414
const url = new URL('./exports_only.js', import.meta.url);
1515
const jsSource = await readFile(url);
16-
const max_size = 1105;
16+
const max_size = 1200;
1717
ok(jsSource.byteLength <= max_size, `JS inlined bytelength ${jsSource.byteLength} is greater than ${max_size} bytes, at ${fileURLToPath(url)}`);
1818

tests/runtime/smoke/host.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --base64 --compat --map testwasi=./helpers.js,imports=./host.js
1+
// Flags: --load=base64 --compat --map testwasi=./helpers.js,imports=./host.js
22
function assert(x: boolean, msg: string) {
33
if (!x)
44
throw new Error(msg);

0 commit comments

Comments
 (0)