Skip to content

Commit 18aaa44

Browse files
authored
Update JS integer lowering (#256)
* Update JS integer lowering Updates JS integer lowering to match the component model explainer (assuming that WebAssembly/component-model#52 is merged). The main differences are that out-of-range numbers now wrap instead of throwing, and that non-integers are now rounded down instead of throwing.
1 parent a4a138b commit 18aaa44

File tree

1 file changed

+112
-47
lines changed

1 file changed

+112
-47
lines changed

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

Lines changed: 112 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,27 @@ impl Opts {
5454
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5555
enum Intrinsic {
5656
ClampGuest,
57-
ClampHost,
58-
ClampHost64,
5957
DataView,
6058
ValidateGuestChar,
6159
ValidateHostChar,
6260
ValidateFlags,
6361
ValidateFlags64,
62+
/// Implementation of https://tc39.es/ecma262/#sec-toint32.
63+
ToInt32,
64+
/// Implementation of https://tc39.es/ecma262/#sec-touint32.
65+
ToUint32,
66+
/// Implementation of https://tc39.es/ecma262/#sec-toint16.
67+
ToInt16,
68+
/// Implementation of https://tc39.es/ecma262/#sec-touint16.
69+
ToUint16,
70+
/// Implementation of https://tc39.es/ecma262/#sec-toint8.
71+
ToInt8,
72+
/// Implementation of https://tc39.es/ecma262/#sec-touint8.
73+
ToUint8,
74+
/// Implementation of https://tc39.es/ecma262/#sec-tobigint64.
75+
ToBigInt64,
76+
/// Implementation of https://tc39.es/ecma262/#sec-tobiguint64.
77+
ToBigUint64,
6478
/// Implementation of https://tc39.es/ecma262/#sec-tostring.
6579
ToString,
6680
I32ToF32,
@@ -80,13 +94,19 @@ impl Intrinsic {
8094
fn name(&self) -> &'static str {
8195
match self {
8296
Intrinsic::ClampGuest => "clamp_guest",
83-
Intrinsic::ClampHost => "clamp_host",
84-
Intrinsic::ClampHost64 => "clamp_host64",
8597
Intrinsic::DataView => "data_view",
8698
Intrinsic::ValidateGuestChar => "validate_guest_char",
8799
Intrinsic::ValidateHostChar => "validate_host_char",
88100
Intrinsic::ValidateFlags => "validate_flags",
89101
Intrinsic::ValidateFlags64 => "validate_flags64",
102+
Intrinsic::ToInt32 => "to_int32",
103+
Intrinsic::ToUint32 => "to_uint32",
104+
Intrinsic::ToInt16 => "to_int16",
105+
Intrinsic::ToUint16 => "to_uint16",
106+
Intrinsic::ToInt8 => "to_int8",
107+
Intrinsic::ToUint8 => "to_uint8",
108+
Intrinsic::ToBigInt64 => "to_int64",
109+
Intrinsic::ToBigUint64 => "to_uint64",
90110
Intrinsic::ToString => "to_string",
91111
Intrinsic::F32ToI32 => "f32ToI32",
92112
Intrinsic::I32ToF32 => "i32ToF32",
@@ -1207,22 +1227,6 @@ impl FunctionBindgen<'_> {
12071227
results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
12081228
}
12091229

1210-
fn clamp_host<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
1211-
where
1212-
T: std::fmt::Display,
1213-
{
1214-
let clamp = self.gen.intrinsic(Intrinsic::ClampHost);
1215-
results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
1216-
}
1217-
1218-
fn clamp_host64<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
1219-
where
1220-
T: std::fmt::Display,
1221-
{
1222-
let clamp = self.gen.intrinsic(Intrinsic::ClampHost64);
1223-
results.push(format!("{}({}, {}n, {}n)", clamp, operands[0], min, max));
1224-
}
1225-
12261230
fn load(&mut self, method: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
12271231
self.needs_memory = true;
12281232
let view = self.gen.intrinsic(Intrinsic::DataView);
@@ -1334,16 +1338,38 @@ impl Bindgen for FunctionBindgen<'_> {
13341338

13351339
// All values coming from the host and going to wasm need to have
13361340
// their ranges validated, since the host could give us any value.
1337-
Instruction::I32FromU8 => self.clamp_host(results, operands, u8::MIN, u8::MAX),
1338-
Instruction::I32FromS8 => self.clamp_host(results, operands, i8::MIN, i8::MAX),
1339-
Instruction::I32FromU16 => self.clamp_host(results, operands, u16::MIN, u16::MAX),
1340-
Instruction::I32FromS16 => self.clamp_host(results, operands, i16::MIN, i16::MAX),
1341+
Instruction::I32FromU8 => {
1342+
let conv = self.gen.intrinsic(Intrinsic::ToUint8);
1343+
results.push(format!("{conv}({op})", op = operands[0]))
1344+
}
1345+
Instruction::I32FromS8 => {
1346+
let conv = self.gen.intrinsic(Intrinsic::ToInt8);
1347+
results.push(format!("{conv}({op})", op = operands[0]))
1348+
}
1349+
Instruction::I32FromU16 => {
1350+
let conv = self.gen.intrinsic(Intrinsic::ToUint16);
1351+
results.push(format!("{conv}({op})", op = operands[0]))
1352+
}
1353+
Instruction::I32FromS16 => {
1354+
let conv = self.gen.intrinsic(Intrinsic::ToInt16);
1355+
results.push(format!("{conv}({op})", op = operands[0]))
1356+
}
13411357
Instruction::I32FromU32 => {
1342-
self.clamp_host(results, operands, u32::MIN, u32::MAX);
1358+
let conv = self.gen.intrinsic(Intrinsic::ToUint32);
1359+
results.push(format!("{conv}({op})", op = operands[0]))
1360+
}
1361+
Instruction::I32FromS32 => {
1362+
let conv = self.gen.intrinsic(Intrinsic::ToInt32);
1363+
results.push(format!("{conv}({op})", op = operands[0]))
1364+
}
1365+
Instruction::I64FromU64 => {
1366+
let conv = self.gen.intrinsic(Intrinsic::ToBigUint64);
1367+
results.push(format!("{conv}({op})", op = operands[0]))
1368+
}
1369+
Instruction::I64FromS64 => {
1370+
let conv = self.gen.intrinsic(Intrinsic::ToBigInt64);
1371+
results.push(format!("{conv}({op})", op = operands[0]))
13431372
}
1344-
Instruction::I32FromS32 => self.clamp_host(results, operands, i32::MIN, i32::MAX),
1345-
Instruction::I64FromU64 => self.clamp_host64(results, operands, u64::MIN, u64::MAX),
1346-
Instruction::I64FromS64 => self.clamp_host64(results, operands, i64::MIN, i64::MAX),
13471373

13481374
// The native representation in JS of f32 and f64 is just a number,
13491375
// so there's nothing to do here. Everything wasm gives us is
@@ -2388,15 +2414,6 @@ impl Js {
23882414
return i;
23892415
}
23902416
"),
2391-
Intrinsic::ClampHost => self.src.js("
2392-
export function clamp_host(i, min, max) {
2393-
if (!Number.isInteger(i)) \
2394-
throw new TypeError(`must be an integer`);
2395-
if (i < min || i > max) \
2396-
throw new RangeError(`must be between ${min} and ${max}`);
2397-
return i;
2398-
}
2399-
"),
24002417

24012418
Intrinsic::DataView => self.src.js("
24022419
let DATA_VIEW = new DataView(new ArrayBuffer());
@@ -2408,16 +2425,6 @@ impl Js {
24082425
}
24092426
"),
24102427

2411-
Intrinsic::ClampHost64 => self.src.js("
2412-
export function clamp_host64(i, min, max) {
2413-
if (typeof i !== 'bigint') \
2414-
throw new TypeError(`must be a bigint`);
2415-
if (i < min || i > max) \
2416-
throw new RangeError(`must be between ${min} and ${max}`);
2417-
return i;
2418-
}
2419-
"),
2420-
24212428
Intrinsic::ValidateGuestChar => self.src.js("
24222429
export function validate_guest_char(i) {
24232430
if ((i > 0x10ffff) || (i >= 0xd800 && i <= 0xdfff)) \
@@ -2457,6 +2464,64 @@ impl Js {
24572464
}
24582465
"),
24592466

2467+
2468+
Intrinsic::ToInt32 => self.src.js("
2469+
export function to_int32(val) {
2470+
return val >> 0;
2471+
}
2472+
"),
2473+
Intrinsic::ToUint32 => self.src.js("
2474+
export function to_uint32(val) {
2475+
return val >>> 0;
2476+
}
2477+
"),
2478+
2479+
Intrinsic::ToInt16 => self.src.js("
2480+
export function to_int16(val) {
2481+
val >>>= 0;
2482+
val %= 2 ** 16;
2483+
if (val >= 2 ** 15) {
2484+
val -= 2 ** 16;
2485+
}
2486+
return val;
2487+
}
2488+
"),
2489+
Intrinsic::ToUint16 => self.src.js("
2490+
export function to_uint16(val) {
2491+
val >>>= 0;
2492+
val %= 2 ** 16;
2493+
return val;
2494+
}
2495+
"),
2496+
Intrinsic::ToInt8 => self.src.js("
2497+
export function to_int8(val) {
2498+
val >>>= 0;
2499+
val %= 2 ** 8;
2500+
if (val >= 2 ** 7) {
2501+
val -= 2 ** 8;
2502+
}
2503+
return val;
2504+
}
2505+
"),
2506+
Intrinsic::ToUint8 => self.src.js("
2507+
export function to_uint8(val) {
2508+
val >>>= 0;
2509+
val %= 2 ** 8;
2510+
return val;
2511+
}
2512+
"),
2513+
2514+
Intrinsic::ToBigInt64 => self.src.js("
2515+
export function to_int64(val) {
2516+
return BigInt.asIntN(64, val);
2517+
}
2518+
"),
2519+
Intrinsic::ToBigUint64 => self.src.js("
2520+
export function to_uint64(val) {
2521+
return BigInt.asUintN(64, val);
2522+
}
2523+
"),
2524+
24602525
Intrinsic::ToString => self.src.js("
24612526
export function to_string(val) {
24622527
if (typeof val === 'symbol') {

0 commit comments

Comments
 (0)