Add test for integer limits While debugging an issue with integers in #20344, I found some cases where conversions currently fail or don't behave as expected in WASM64 mode. In the end, I decided to add a separate test that solely tests all integer limits and ensures they roundtrip between JS and C++ as expected. I don't have time to look into fixing uncovered issues, but I thought these (currently failing) tests might be a helpful start on their own.
diff --git a/test/embind/test_val_integers.cpp b/test/embind/test_val_integers.cpp new file mode 100644 index 0000000..0a871dc --- /dev/null +++ b/test/embind/test_val_integers.cpp
@@ -0,0 +1,87 @@ +// Copyright 2023 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. + +#include <emscripten.h> +#include <emscripten/val.h> +#include <iostream> +#include <limits> + +using namespace emscripten; + +bool check_num_on_js_side(std::string value_s) { + std::string code = std::string("num === ") + value_s + " || console.error(`\t\tFailed: expected " + value_s + ", got ${num}`)"; + return emscripten_run_script_int(code.c_str()) == 1; +} + +template <typename T> +bool check_num_on_cpp_side(T expected) { + T value = emscripten::val::global("num").as<T>(); + if (value != expected) { + std::cerr << "\t\tFailed: expected " << expected << ", got " << value << std::endl; + return false; + } + return true; +} + +template <typename T> +bool test_num(const char *type_name, T value) { + std::string value_s = std::to_string(value); + // 64-bit integer must be represented as a BigInt which uses `n` suffix + if (sizeof(T) > 4) { + value_s.push_back('n'); + } + bool ok = true; + std::cout << "Testing (" << type_name << ") " << value_s << std::endl; + // Test C++ to JS conversion + std::cout << "\tC++ to JS via assignment" << std::endl; + emscripten::val::global().set("num", value); + ok &= check_num_on_js_side(value_s); + // Test C++ to JS conversion via function call. + // Calls use their own argument serialization, so it's best to ensure that works too. + std::cout << "\tC++ to JS via call" << std::endl; + emscripten::val::global("setNum")(value); + ok &= check_num_on_js_side(value_s); + // Test JS to C++ conversion + std::cout << "\tJS to C++" << std::endl; + ok &= check_num_on_cpp_side(value); + return ok; +} + +template <typename T> +bool test_num_limits(const char *type_name) { + // note: using `&` instead of `&&` to ensure both tests are run + return test_num<T>(type_name, std::numeric_limits<T>::min()) & + test_num<T>(type_name, std::numeric_limits<T>::max()); +} + +#define TEST_NUM_LIMITS(T) ok &= test_num_limits<T>(#T) + +int main() { + bool ok = true; + + EM_ASM({ + globalThis.setNum = num => globalThis.num = num; + }); + + TEST_NUM_LIMITS(char); + TEST_NUM_LIMITS(unsigned char); + TEST_NUM_LIMITS(signed char); + + TEST_NUM_LIMITS(short); + TEST_NUM_LIMITS(unsigned short); + + TEST_NUM_LIMITS(int); + TEST_NUM_LIMITS(unsigned int); + + TEST_NUM_LIMITS(long); + TEST_NUM_LIMITS(unsigned long); + +#ifdef WASM_BIGINT + TEST_NUM_LIMITS(long long); + TEST_NUM_LIMITS(unsigned long long); +#endif + + return !ok; +}
diff --git a/test/test_core.py b/test/test_core.py index fbb578f..bae8025 100644 --- a/test/test_core.py +++ b/test/test_core.py
@@ -7768,6 +7768,13 @@ self.emcc_args += ['-lembind'] self.do_run_in_out_file_test('embind/test_unsigned.cpp') + @also_with_wasm_bigint + def test_embind_val_integers(self): + self.emcc_args += ['-lembind'] + if self.get_setting('WASM_BIGINT'): + self.emcc_args += ['-DWASM_BIGINT'] + self.do_runf(test_file('embind/test_val_integers.cpp')) + def test_embind_val(self): self.emcc_args += ['-lembind'] self.do_run_in_out_file_test('embind/test_val.cpp')