From be527007df782a27b4073bef4ebc06e514e7d44f Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 9 Jul 2025 23:42:05 +0100 Subject: [PATCH 1/3] [embind] Map size_t to bigint on wasm64 This makes it consistent with other bindings like EM_ASM. As part of this, I had to change vector and map sizes and indices to explicitly take `unsigned int` instead, so that users don't have to upgrade all their code to deal with unexpected bigints in e.g. `vec.size()`. Technically this means you can't pass around a C++ vector >4GB to JS and back, but I'm sure we can live with it. --- ChangeLog.md | 2 ++ system/include/emscripten/bind.h | 54 +++++++++++++++++++------------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index d9ac84e16e487..940abbba9140b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -38,6 +38,8 @@ See docs/process.md for more on how version tagging works. wrapped with `WebAssembly.Suspending` functions. To automatically wrap library functions for use with JSPI they must now explicitly set `myLibraryFunction__async: true`. +- On wasm64 Embind will now pass `size_t` as a `bigint` instead of `number` to + match behaviour of other bindings like `EM_ASM`. 4.0.10 - 06/07/25 ----------------- diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index be8067c92655b..e537a6bb09eb3 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -573,13 +573,6 @@ struct SignatureCode : __em_asm_sig {}; template struct SignatureCode::value)> : __em_asm_sig {}; -// TODO: should we add this override to em_asm? -// Most places, including Embind, use `p` for `size_t` (aka `unsigned long`) but -// `em_asm` uses platform-specific code instead which represents `unsigned long` -// as a JavaScript `number` on wasm32 and as a `BigInt` on wasm64. -template<> -struct SignatureCode : __em_asm_sig {}; - template struct SignatureCode : SignatureCode {}; @@ -1986,7 +1979,7 @@ struct VectorAccess { #if __cplusplus >= 201703L static std::optional get( const VectorType& v, - typename VectorType::size_type index + unsigned int index ) { if (index < v.size()) { return v[index]; @@ -1997,7 +1990,7 @@ struct VectorAccess { #else static val get( const VectorType& v, - typename VectorType::size_type index + unsigned int index ) { if (index < v.size()) { return val(v[index], allow_raw_pointers()); @@ -2009,12 +2002,31 @@ struct VectorAccess { static bool set( VectorType& v, - typename VectorType::size_type index, + unsigned int index, const typename VectorType::value_type& value ) { v[index] = value; return true; } + + static unsigned int size(const VectorType& v) { + return v.size(); + } + + static void resize( + VectorType& v, + unsigned int len, + const typename VectorType::value_type& value + ) { + v.resize(len, value); + } + + static void push_back( + VectorType& v, + typename VectorType::value_type&& value + ) { + v.push_back(std::move(value)); + } }; } // end namespace internal @@ -2026,16 +2038,13 @@ class_> register_vector(const char* name) { register_optional(); #endif - void (VecType::*push_back)(const T&) = &VecType::push_back; - void (VecType::*resize)(const size_t, const T&) = &VecType::resize; - size_t (VecType::*size)() const = &VecType::size; - return class_>(name) + return class_(name) .template constructor<>() - .function("push_back", push_back, allow_raw_pointers()) - .function("resize", resize, allow_raw_pointers()) - .function("size", size) - .function("get", &internal::VectorAccess::get, allow_raw_pointers()) - .function("set", &internal::VectorAccess::set, allow_raw_pointers()) + .function("push_back", internal::VectorAccess::push_back) + .function("resize", internal::VectorAccess::resize) + .function("size", internal::VectorAccess::size) + .function("get", internal::VectorAccess::get) + .function("set", internal::VectorAccess::set) ; } @@ -2093,6 +2102,10 @@ struct MapAccess { } return keys; } + + static unsigned int size(const MapType& m) { + return m.size(); + } }; } // end namespace internal @@ -2105,10 +2118,9 @@ class_> register_map(const char* name) { register_optional(); #endif - size_t (MapType::*size)() const = &MapType::size; return class_(name) .template constructor<>() - .function("size", size) + .function("size", internal::MapAccess::size) .function("get", internal::MapAccess::get) .function("set", internal::MapAccess::set) .function("keys", internal::MapAccess::keys) From 2869fb2fee4774c68bdc03e1bca2cd5f0646a349 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Thu, 10 Jul 2025 02:16:39 +0100 Subject: [PATCH 2/3] Fixups --- system/include/emscripten/bind.h | 10 +++++----- test/embind/embind.test.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index e537a6bb09eb3..9bceabd53481e 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -2040,11 +2040,11 @@ class_> register_vector(const char* name) { return class_(name) .template constructor<>() - .function("push_back", internal::VectorAccess::push_back) - .function("resize", internal::VectorAccess::resize) - .function("size", internal::VectorAccess::size) - .function("get", internal::VectorAccess::get) - .function("set", internal::VectorAccess::set) + .function("push_back", internal::VectorAccess::push_back, allow_raw_pointers()) + .function("resize", internal::VectorAccess::resize, allow_raw_pointers()) + .function("size", internal::VectorAccess::size, allow_raw_pointers()) + .function("get", internal::VectorAccess::get, allow_raw_pointers()) + .function("set", internal::VectorAccess::set, allow_raw_pointers()) ; } diff --git a/test/embind/embind.test.js b/test/embind/embind.test.js index a6c4040139fb5..4fc2c6d0cb1cb 100644 --- a/test/embind/embind.test.js +++ b/test/embind/embind.test.js @@ -857,7 +857,7 @@ module({ assert.equal(2147483648, cm.load_unsigned_int()); cm.store_unsigned_long(2147483648); - assert.equal(2147483648, cm.load_unsigned_long()); + assert.equal(cm.getCompilerSetting('MEMORY64') ? 2147483648n : 2147483648, cm.load_unsigned_long()); }); if (cm.getCompilerSetting('ASSERTIONS')) { From b05748f7205f4fd377d8dfb4d34013fe0d08d2a3 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 11 Jul 2025 20:31:31 +0100 Subject: [PATCH 3/3] Update changelog --- ChangeLog.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 940abbba9140b..df1606ee226c6 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -38,8 +38,10 @@ See docs/process.md for more on how version tagging works. wrapped with `WebAssembly.Suspending` functions. To automatically wrap library functions for use with JSPI they must now explicitly set `myLibraryFunction__async: true`. -- On wasm64 Embind will now pass `size_t` as a `bigint` instead of `number` to - match behaviour of other bindings like `EM_ASM`. +- Removed special casing for `size_t` in Embind, since it was also inadvertently + affecting `unsigned long` on wasm64. Both will now match the behaviour of + other 64-bit integers on wasm64 and will be passed as `bigint` instead of + `number` to the JavaScript code. (#24678) 4.0.10 - 06/07/25 -----------------