diff --git a/Meta/generate-libwasm-spec-test.py b/Meta/generate-libwasm-spec-test.py index 08d83c76ca4..a064b9077da 100644 --- a/Meta/generate-libwasm-spec-test.py +++ b/Meta/generate-libwasm-spec-test.py @@ -221,9 +221,11 @@ def gen_value(value: WasmValue, as_arg=False) -> str: f = struct.unpack("d", b)[0] return f - def float_to_str(f: float, preserve_nan_sign=False) -> str: + def float_to_str(bits: int, *, double=False, preserve_nan_sign=False) -> str: + f = int_to_float64_bitcast(bits) if double else int_to_float_bitcast(bits) + if math.isnan(f) and preserve_nan_sign: - f_bytes = struct.pack("d", f) + f_bytes = bits.to_bytes(8 if double else 4, byteorder="little") # -NaN does not preserve the sign bit in JavaScript land, so if # we want to preserve NaN "sign", we pass in raw bytes return f"new Uint8Array({list(f_bytes)})" @@ -251,9 +253,11 @@ def gen_value(value: WasmValue, as_arg=False) -> str: case "i64": return str(unsigned_to_signed(int(value.value), 64)) + "n" case "f32": - return float_to_str(int_to_float_bitcast(int(value.value)), as_arg) + return float_to_str( + int(value.value), double=False, preserve_nan_sign=as_arg + ) case "f64": - return float_to_str(int_to_float64_bitcast(int(value.value)), as_arg) + return float_to_str(int(value.value), double=True, preserve_nan_sign=as_arg) case "externref" | "funcref" | "v128": return value.value case _: diff --git a/Tests/LibWasm/test-wasm.cpp b/Tests/LibWasm/test-wasm.cpp index 35459a91606..bca3bce9bc9 100644 --- a/Tests/LibWasm/test-wasm.cpp +++ b/Tests/LibWasm/test-wasm.cpp @@ -259,13 +259,13 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) if (argument.is_object()) { auto object = MUST(argument.to_object(vm)); // Uint8Array allows for raw bytes to be passed into Wasm. This is - // particularly useful for preserving the sign bit of a NaN + // particularly useful for NaN bit patterns if (!is(*object)) return vm.throw_completion("Expected a Uint8Array object"sv); auto& array = static_cast(*object); - if (array.array_length().length() != 8) - return vm.throw_completion("Expected a Uint8Array of size 8"sv); - memcpy(&double_value, array.data().data(), sizeof(double)); + if (array.array_length().length() > 8) + return vm.throw_completion("Expected a Uint8Array of size <= 8"sv); + memcpy(&double_value, array.data().data(), array.array_length().length()); } else if (!argument.is_bigint()) double_value = TRY(argument.to_double(vm)); switch (param.kind()) { @@ -281,7 +281,17 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::wasm_invoke) } break; case Wasm::ValueType::Kind::F32: - arguments.append(Wasm::Value(static_cast(double_value))); + // double_value should contain up to 8 bytes of information, + // if we were passed a Uint8Array. If the expected arg is a + // float, we were probably passed a Uint8Array of size 4. So + // we copy those bytes into a float value. + if (argument.is_object()) { + float float_value = 0; + memcpy(&float_value, &double_value, sizeof(float)); + arguments.append(Wasm::Value(float_value)); + } else { + arguments.append(Wasm::Value(static_cast(double_value))); + } break; case Wasm::ValueType::Kind::F64: arguments.append(Wasm::Value(static_cast(double_value)));