Skip to content

Commit 4182f94

Browse files
authored
[embind] Don't truncate unsigned long under wasm64 (#24678)
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.
1 parent cfafd50 commit 4182f94

File tree

3 files changed

+38
-22
lines changed

3 files changed

+38
-22
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ See docs/process.md for more on how version tagging works.
4040
wrapped with `WebAssembly.Suspending` functions. To automatically wrap library
4141
functions for use with JSPI they must now explicitly set
4242
`myLibraryFunction__async: true`.
43+
- Removed special casing for `size_t` in Embind, since it was also inadvertently
44+
affecting `unsigned long` on wasm64. Both will now match the behaviour of
45+
other 64-bit integers on wasm64 and will be passed as `bigint` instead of
46+
`number` to the JavaScript code. (#24678)
4347

4448
4.0.10 - 06/07/25
4549
-----------------

system/include/emscripten/bind.h

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,6 @@ struct SignatureCode : __em_asm_sig<int> {};
573573
template<typename T>
574574
struct SignatureCode<T, decltype(__em_asm_sig<T>::value)> : __em_asm_sig<T> {};
575575

576-
// TODO: should we add this override to em_asm?
577-
// Most places, including Embind, use `p` for `size_t` (aka `unsigned long`) but
578-
// `em_asm` uses platform-specific code instead which represents `unsigned long`
579-
// as a JavaScript `number` on wasm32 and as a `BigInt` on wasm64.
580-
template<>
581-
struct SignatureCode<size_t> : __em_asm_sig<void*> {};
582-
583576
template<typename T>
584577
struct SignatureCode<T&> : SignatureCode<T*> {};
585578

@@ -1986,7 +1979,7 @@ struct VectorAccess {
19861979
#if __cplusplus >= 201703L
19871980
static std::optional<typename VectorType::value_type> get(
19881981
const VectorType& v,
1989-
typename VectorType::size_type index
1982+
unsigned int index
19901983
) {
19911984
if (index < v.size()) {
19921985
return v[index];
@@ -1997,7 +1990,7 @@ struct VectorAccess {
19971990
#else
19981991
static val get(
19991992
const VectorType& v,
2000-
typename VectorType::size_type index
1993+
unsigned int index
20011994
) {
20021995
if (index < v.size()) {
20031996
return val(v[index], allow_raw_pointers());
@@ -2009,12 +2002,31 @@ struct VectorAccess {
20092002

20102003
static bool set(
20112004
VectorType& v,
2012-
typename VectorType::size_type index,
2005+
unsigned int index,
20132006
const typename VectorType::value_type& value
20142007
) {
20152008
v[index] = value;
20162009
return true;
20172010
}
2011+
2012+
static unsigned int size(const VectorType& v) {
2013+
return v.size();
2014+
}
2015+
2016+
static void resize(
2017+
VectorType& v,
2018+
unsigned int len,
2019+
const typename VectorType::value_type& value
2020+
) {
2021+
v.resize(len, value);
2022+
}
2023+
2024+
static void push_back(
2025+
VectorType& v,
2026+
typename VectorType::value_type&& value
2027+
) {
2028+
v.push_back(std::move(value));
2029+
}
20182030
};
20192031

20202032
} // end namespace internal
@@ -2026,16 +2038,13 @@ class_<std::vector<T, Allocator>> register_vector(const char* name) {
20262038
register_optional<T>();
20272039
#endif
20282040

2029-
void (VecType::*push_back)(const T&) = &VecType::push_back;
2030-
void (VecType::*resize)(const size_t, const T&) = &VecType::resize;
2031-
size_t (VecType::*size)() const = &VecType::size;
2032-
return class_<std::vector<T>>(name)
2041+
return class_<VecType>(name)
20332042
.template constructor<>()
2034-
.function("push_back", push_back, allow_raw_pointers())
2035-
.function("resize", resize, allow_raw_pointers())
2036-
.function("size", size)
2037-
.function("get", &internal::VectorAccess<VecType>::get, allow_raw_pointers())
2038-
.function("set", &internal::VectorAccess<VecType>::set, allow_raw_pointers())
2043+
.function("push_back", internal::VectorAccess<VecType>::push_back, allow_raw_pointers())
2044+
.function("resize", internal::VectorAccess<VecType>::resize, allow_raw_pointers())
2045+
.function("size", internal::VectorAccess<VecType>::size, allow_raw_pointers())
2046+
.function("get", internal::VectorAccess<VecType>::get, allow_raw_pointers())
2047+
.function("set", internal::VectorAccess<VecType>::set, allow_raw_pointers())
20392048
;
20402049
}
20412050

@@ -2093,6 +2102,10 @@ struct MapAccess {
20932102
}
20942103
return keys;
20952104
}
2105+
2106+
static unsigned int size(const MapType& m) {
2107+
return m.size();
2108+
}
20962109
};
20972110

20982111
} // end namespace internal
@@ -2105,10 +2118,9 @@ class_<std::map<K, V, Compare, Allocator>> register_map(const char* name) {
21052118
register_optional<V>();
21062119
#endif
21072120

2108-
size_t (MapType::*size)() const = &MapType::size;
21092121
return class_<MapType>(name)
21102122
.template constructor<>()
2111-
.function("size", size)
2123+
.function("size", internal::MapAccess<MapType>::size)
21122124
.function("get", internal::MapAccess<MapType>::get)
21132125
.function("set", internal::MapAccess<MapType>::set)
21142126
.function("keys", internal::MapAccess<MapType>::keys)

test/embind/embind.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ module({
857857
assert.equal(2147483648, cm.load_unsigned_int());
858858

859859
cm.store_unsigned_long(2147483648);
860-
assert.equal(2147483648, cm.load_unsigned_long());
860+
assert.equal(cm.getCompilerSetting('MEMORY64') ? 2147483648n : 2147483648, cm.load_unsigned_long());
861861
});
862862

863863
if (cm.getCompilerSetting('ASSERTIONS')) {

0 commit comments

Comments
 (0)