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')