Skip to content

Commit 9ed3613

Browse files
author
Guy Bedford
authored
JS host formatting (#400)
* gen-host-js: whitespace & formatting * collect utf8EncodedLen into Utf8Encode * single line without braces * pr feedback * ws fixup
1 parent 9e19a7d commit 9ed3613

1 file changed

Lines changed: 103 additions & 61 deletions

File tree

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

Lines changed: 103 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,43 @@ use wit_bindgen_core::{
1717
};
1818
use wit_component::ComponentInterfaces;
1919

20+
// https://tc39.es/ecma262/#prod-IdentifierStartChar
21+
// Unicode ID_Start | "$" | "_"
22+
fn is_js_identifier_start(code: char) -> bool {
23+
return match code {
24+
'A'..='Z' | 'a'..='z' | '$' | '_' => true,
25+
// leaving out non-ascii for now...
26+
_ => false,
27+
};
28+
}
29+
30+
// https://tc39.es/ecma262/#prod-IdentifierPartChar
31+
// Unicode ID_Continue | "$" | U+200C | U+200D
32+
fn is_js_identifier_char(code: char) -> bool {
33+
return match code {
34+
'0'..='9' | 'A'..='Z' | 'a'..='z' | '$' | '_' => true,
35+
// leaving out non-ascii for now...
36+
_ => false,
37+
};
38+
}
39+
40+
fn is_js_identifier(s: &str) -> bool {
41+
let mut chars = s.chars();
42+
if let Some(char) = chars.next() {
43+
if !is_js_identifier_start(char) {
44+
return false;
45+
}
46+
} else {
47+
return false;
48+
}
49+
while let Some(char) = chars.next() {
50+
if !is_js_identifier_char(char) {
51+
return false;
52+
}
53+
}
54+
return true;
55+
}
56+
2057
#[derive(Default)]
2158
struct Js {
2259
/// The source code for the "main" file that's going to be created for the
@@ -191,7 +228,12 @@ impl ComponentGenerator for Js {
191228
}
192229

193230
fn finish_component(&mut self, name: &str, files: &mut Files) {
194-
files.push(&format!("{name}.js"), self.src.js.as_bytes());
231+
let mut bytes = self.src.js.as_bytes();
232+
// strip leading newline
233+
if bytes[0] == b'\n' {
234+
bytes = &bytes[1..];
235+
}
236+
files.push(&format!("{name}.js"), bytes);
195237
if !self.opts.no_typescript {
196238
files.push(&format!("{name}.d.ts"), self.src.ts.as_bytes());
197239
}
@@ -327,7 +369,7 @@ impl Js {
327369
"),
328370

329371
Intrinsic::IsLE => self.src.js("
330-
const isLE = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
372+
const isLE = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
331373
"),
332374

333375
Intrinsic::ValidateGuestChar => self.src.js("
@@ -397,77 +439,61 @@ impl Js {
397439
"),
398440

399441
Intrinsic::ToBigInt64 => self.src.js("
400-
function toInt64(val) {
401-
return BigInt.asIntN(64, val);
402-
}
442+
const toInt64 = val => BigInt.asIntN(64, val);
403443
"),
404444
Intrinsic::ToBigUint64 => self.src.js("
405-
function toUint64(val) {
406-
return BigInt.asUintN(64, val);
407-
}
445+
const toUint64 = val => BigInt.asUintN(64, val);
408446
"),
409447

448+
// Calling `String` almost directly calls `ToString`, except that it also allows symbols,
449+
// which is why we have the symbol-rejecting branch above.
450+
//
451+
// Definition of `String`: https://tc39.es/ecma262/#sec-string-constructor-string-value
410452
Intrinsic::ToString => self.src.js("
411453
function toString(val) {
412-
if (typeof val === 'symbol') {
413-
throw new TypeError('symbols cannot be converted to strings');
414-
} else {
415-
// Calling `String` almost directly calls `ToString`, except that it also allows symbols,
416-
// which is why we have the symbol-rejecting branch above.
417-
//
418-
// Definition of `String`: https://tc39.es/ecma262/#sec-string-constructor-string-value
419-
return String(val);
420-
}
454+
if (typeof val === 'symbol') throw new TypeError('symbols cannot be converted to strings');
455+
return String(val);
421456
}
422457
"),
423458

424459
Intrinsic::I32ToF32 => self.src.js("
425-
function i32ToF32(i) {
426-
i32ToF32I[0] = i;
427-
return i32ToF32F[0];
428-
}
460+
const i32ToF32 = i => (i32ToF32I[0] = i, i32ToF32F[0]);
429461
"),
430462
Intrinsic::F32ToI32 => self.src.js("
431-
function f32ToI32(f) {
432-
i32ToF32F[0] = f;
433-
return i32ToF32I[0];
434-
}
463+
const f32ToI32 = f => (i32ToF32F[0] = f, i32ToF32I[0]);
435464
"),
436465
Intrinsic::I64ToF64 => self.src.js("
437-
function i64ToF64(i) {
438-
i64ToF64I[0] = i;
439-
return i64ToF64F[0];
440-
}
466+
const i64ToF64 = i => (i64ToF64I[0] = i, i64ToF64F[0]);
441467
"),
442468
Intrinsic::F64ToI64 => self.src.js("
443-
function f64ToI64(f) {
444-
i64ToF64F[0] = f;
445-
return i64ToF64I[0];
446-
}
469+
const f64ToI64 = f => (i64ToF64F[0] = f, i64ToF64I[0]);
447470
"),
448471

449472
Intrinsic::Utf8Decoder => self
450473
.src
451-
.js("const utf8Decoder = new TextDecoder();\n"),
474+
.js("
475+
const utf8Decoder = new TextDecoder();
476+
"),
452477

453478
Intrinsic::Utf16Decoder => self
454479
.src
455-
.js("const utf16Decoder = new TextDecoder('utf-16');\n"),
480+
.js("
481+
const utf16Decoder = new TextDecoder('utf-16');
482+
"),
456483

457-
Intrinsic::Utf8EncodedLen => self.src.js("let utf8EncodedLen = 0;\n"),
484+
Intrinsic::Utf8EncodedLen => {},
458485

459486
Intrinsic::Utf8Encode => self.src.js("
460487
const utf8Encoder = new TextEncoder();
461488
489+
let utf8EncodedLen = 0;
462490
function utf8Encode(s, realloc, memory) {
463491
if (typeof s !== 'string') \
464492
throw new TypeError('expected a string');
465-
466493
if (s.length === 0) {
467494
utf8EncodedLen = 0;
468495
return 1;
469496
}
470-
471497
let allocLen = 0;
472498
let ptr = 0;
473499
let writtenTotal = 0;
@@ -595,7 +621,9 @@ impl Instantiator<'_> {
595621
fn instantiate(&mut self) {
596622
uwriteln!(
597623
self.src.js,
598-
"export async function instantiate(instantiateCore, imports) {{"
624+
"
625+
export async function instantiate(instantiateCore, imports) {{\
626+
"
599627
);
600628

601629
for init in self.component.initializers.iter() {
@@ -676,9 +704,19 @@ impl Instantiator<'_> {
676704
} else {
677705
imports.push_str("{\n");
678706
for (module, names) in import_obj {
679-
uwrite!(imports, "\"{module}\": {{\n");
707+
if is_js_identifier(module) {
708+
imports.push_str(module);
709+
} else {
710+
uwrite!(imports, "'{module}'");
711+
}
712+
imports.push_str(": {\n");
680713
for (name, val) in names {
681-
uwriteln!(imports, "\"{name}\": {val},");
714+
if is_js_identifier(name) {
715+
imports.push_str(name);
716+
} else {
717+
uwrite!(imports, "'{name}'");
718+
}
719+
uwriteln!(imports, ": {val},");
682720
}
683721
imports.push_str("},\n");
684722
}
@@ -823,7 +861,11 @@ impl Instantiator<'_> {
823861
ExportItem::Name(s) => s,
824862
};
825863
let i = export.instance.as_u32() as usize;
826-
format!("instance{i}.exports[\"{name}\"]")
864+
if is_js_identifier(name) {
865+
format!("instance{i}.exports.{name}")
866+
} else {
867+
format!("instance{i}.exports[\"{name}\"]")
868+
}
827869
}
828870

829871
fn exports(
@@ -1743,7 +1785,7 @@ impl Bindgen for FunctionBindgen<'_> {
17431785
let block_result = &block_results[0];
17441786
self.src.js(&format!(
17451787
"case {i}: {{
1746-
{block}
1788+
{block}\
17471789
union{tmp} = {{
17481790
tag: {i},
17491791
val: {block_result},
@@ -1788,12 +1830,12 @@ impl Bindgen for FunctionBindgen<'_> {
17881830
"
17891831
switch (variant{tmp}.tag) {{
17901832
case \"none\": {{
1791-
{none}
1833+
{none}\
17921834
break;
17931835
}}
17941836
case \"some\": {{
17951837
const e = variant{tmp}.val;
1796-
{some}
1838+
{some}\
17971839
break;
17981840
}}
17991841
default: {{
@@ -1804,15 +1846,15 @@ impl Bindgen for FunctionBindgen<'_> {
18041846
));
18051847
} else {
18061848
self.src.js(&format!(
1807-
"
1849+
"\
18081850
switch (variant{tmp}) {{
18091851
case null: {{
1810-
{none}
1852+
{none}\
18111853
break;
18121854
}}
18131855
default: {{
18141856
const e = variant{tmp};
1815-
{some}
1857+
{some}\
18161858
break;
18171859
}}
18181860
}}
@@ -1835,36 +1877,36 @@ impl Bindgen for FunctionBindgen<'_> {
18351877

18361878
if self.gen.maybe_null(iface, payload) {
18371879
self.src.js(&format!(
1838-
"
1880+
"\
18391881
case 0: {{
1840-
{none}
1882+
{none}\
18411883
variant{tmp} = {{ tag: \"none\" }};
18421884
break;
18431885
}}
18441886
case 1: {{
1845-
{some}
1887+
{some}\
18461888
variant{tmp} = {{ tag: \"some\", val: {some_result} }};
18471889
break;
18481890
}}
18491891
",
18501892
));
18511893
} else {
18521894
self.src.js(&format!(
1853-
"
1895+
"\
18541896
case 0: {{
1855-
{none}
1897+
{none}\
18561898
variant{tmp} = null;
18571899
break;
18581900
}}
18591901
case 1: {{
1860-
{some}
1902+
{some}\
18611903
variant{tmp} = {some_result};
18621904
break;
18631905
}}
18641906
",
18651907
));
18661908
}
1867-
self.src.js("
1909+
self.src.js("\
18681910
default:
18691911
throw new RangeError(\"invalid variant discriminant for option\");
18701912
");
@@ -1894,16 +1936,16 @@ impl Bindgen for FunctionBindgen<'_> {
18941936
}
18951937

18961938
self.src.js(&format!(
1897-
"
1939+
"\
18981940
switch (variant{tmp}.tag) {{
18991941
case \"ok\": {{
19001942
const e = variant{tmp}.val;
1901-
{ok}
1943+
{ok}\
19021944
break;
19031945
}}
19041946
case \"err\": {{
19051947
const e = variant{tmp}.val;
1906-
{err}
1948+
{err}\
19071949
break;
19081950
}}
19091951
default: {{
@@ -1938,12 +1980,12 @@ impl Bindgen for FunctionBindgen<'_> {
19381980
let variant{tmp};
19391981
switch ({op0}) {{
19401982
case 0: {{
1941-
{ok}
1983+
{ok}\
19421984
variant{tmp} = {{ tag: \"ok\", val: {ok_result} }};
19431985
break;
19441986
}}
19451987
case 1: {{
1946-
{err}
1988+
{err}\
19471989
variant{tmp} = {{ tag: \"err\", val: {err_result} }};
19481990
break;
19491991
}}
@@ -2139,7 +2181,7 @@ impl Bindgen for FunctionBindgen<'_> {
21392181
// result.
21402182
uwriteln!(self.src.js, "for (let i = 0; i < {vec}.length; i++) {{");
21412183
uwriteln!(self.src.js, "const e = {vec}[i];");
2142-
uwriteln!(self.src.js, "const base = {result} + i * {size};");
2184+
uwrite!(self.src.js, "const base = {result} + i * {size};");
21432185
self.src.js(&body);
21442186
self.src.js("}\n");
21452187

0 commit comments

Comments
 (0)