Skip to content

Commit 66dcf64

Browse files
authored
[embind] Fix transferring size_t when memory64 is enabled. (#25896)
When passing a `size_t` to `val` in memory64 mode, the top 32 bits were being truncated. This happened since `size_t` did not match the ovlerloads for `writeGenericWireType` and instead used the generic overload that wrote the value to a 32bit unsigned number. Fixes #25859
1 parent 918cae5 commit 66dcf64

File tree

5 files changed

+99
-6
lines changed

5 files changed

+99
-6
lines changed

system/include/emscripten/val.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ inline void writeGenericWireType(GenericWireType*& cursor, uint64_t wt) {
210210
++cursor;
211211
}
212212

213+
// Explicit overload for size_t to prevent fallback to the 32-bit generic template
214+
inline void writeGenericWireType(GenericWireType*& cursor, std::size_t wt) {
215+
cursor->w[0].s = wt; // Uses the size_t member (64-bit in Memory64)
216+
++cursor;
217+
}
218+
213219
template<typename T>
214220
void writeGenericWireType(GenericWireType*& cursor, T* wt) {
215221
cursor->w[0].p = wt;
@@ -225,6 +231,7 @@ inline void writeGenericWireType(GenericWireType*& cursor, const memory_view<Ele
225231

226232
template<typename T>
227233
void writeGenericWireType(GenericWireType*& cursor, T wt) {
234+
static_assert(sizeof(T) <= sizeof(cursor->w[0].u), "Generic wire type must be smaller than unsigned.");
228235
cursor->w[0].u = static_cast<unsigned>(wt);
229236
++cursor;
230237
}

test/embind/test_i64_val.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,42 @@ void ensure_js(string js_code) {
2727
}
2828

2929
template <typename T>
30-
string compare_a_64_js(T value) {
30+
string compare_a_number_or_bigint_js(T value) {
31+
static_assert(sizeof(T) == 8 || sizeof(T) == 4);
3132
stringstream ss;
32-
ss << "a === " << value << "n";
33+
ss << "a === " << value;
34+
if constexpr (sizeof(T) == 8) {
35+
ss << "n";
36+
}
3337
return ss.str();
3438
}
3539

3640
template <typename T>
3741
void test_value(T value) {
42+
static_assert(sizeof(T) == 8 || sizeof(T) == 4);
3843
cout << " testing value " << value << endl;
3944
cout << " setting properties preserves the expected value" << endl;
4045
val::global().set("a", val(value));
41-
ensure_js(compare_a_64_js(value));
46+
ensure_js(compare_a_number_or_bigint_js(value));
4247
cout << " getting properties returns the original value intact" << endl;
4348
assert(val::global()["a"].as<T>() == value);
4449
cout << " function calls roundtrip the value correctly" << endl;
45-
assert(val::global("BigInt")(value).template as<T>() == value);
50+
const char* typeName;
51+
if constexpr (sizeof(T) == 8) {
52+
typeName = "BigInt";
53+
} else {
54+
typeName = "Number";
55+
}
56+
assert(val::global(typeName)(value).template as<T>() == value);
4657
cout << " method calls roundtrip the value correctly" << endl;
47-
assert(val::global().call<T>("BigInt", value) == value);
58+
assert(val::global().call<T>(typeName, value) == value);
4859
}
4960

5061
int main() {
5162
const int64_t max_int64_t = numeric_limits<int64_t>::max();
5263
const int64_t min_int64_t = numeric_limits<int64_t>::min();
5364
const uint64_t max_uint64_t = numeric_limits<uint64_t>::max();
65+
const size_t max_size_t = std::numeric_limits<size_t>::max();
5466
std::array<std::uint64_t, 5> uint64Array = {0, 1, 2, 3, 4};
5567
std::array<std::int64_t, 5> int64Array = {-2, -1, 0, 1, 2};
5668

@@ -68,6 +80,10 @@ int main() {
6880
test_value(min_int64_t);
6981
test_value(max_int64_t);
7082

83+
test("val(size_t v)");
84+
test_value(size_t(1234));
85+
test_value(max_size_t);
86+
7187
test("val(typed_memory_view<uint64_t>)");
7288
val::global().set("a", val(typed_memory_view(uint64Array.size(), uint64Array.data())));
7389
ensure_js("a instanceof BigUint64Array");

test/embind/test_i64_val.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ val(int64_t v)
3939
function calls roundtrip the value correctly
4040
method calls roundtrip the value correctly
4141
test:
42+
val(size_t v)
43+
testing value 1234
44+
setting properties preserves the expected value
45+
getting properties returns the original value intact
46+
function calls roundtrip the value correctly
47+
method calls roundtrip the value correctly
48+
testing value 4294967295
49+
setting properties preserves the expected value
50+
getting properties returns the original value intact
51+
function calls roundtrip the value correctly
52+
method calls roundtrip the value correctly
53+
test:
4254
val(typed_memory_view<uint64_t>)
4355
test:
4456
val(typed_memory_view<int64_t>)

test/embind/test_i64_val64.out

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
start
2+
test:
3+
val(uint64_t v)
4+
testing value 1234
5+
setting properties preserves the expected value
6+
getting properties returns the original value intact
7+
function calls roundtrip the value correctly
8+
method calls roundtrip the value correctly
9+
testing value 18446744073709551615
10+
setting properties preserves the expected value
11+
getting properties returns the original value intact
12+
function calls roundtrip the value correctly
13+
method calls roundtrip the value correctly
14+
test:
15+
val(int64_t v)
16+
testing value 1234
17+
setting properties preserves the expected value
18+
getting properties returns the original value intact
19+
function calls roundtrip the value correctly
20+
method calls roundtrip the value correctly
21+
testing value -4321
22+
setting properties preserves the expected value
23+
getting properties returns the original value intact
24+
function calls roundtrip the value correctly
25+
method calls roundtrip the value correctly
26+
testing value 1311768467732155613
27+
setting properties preserves the expected value
28+
getting properties returns the original value intact
29+
function calls roundtrip the value correctly
30+
method calls roundtrip the value correctly
31+
testing value -9223372036854775808
32+
setting properties preserves the expected value
33+
getting properties returns the original value intact
34+
function calls roundtrip the value correctly
35+
method calls roundtrip the value correctly
36+
testing value 9223372036854775807
37+
setting properties preserves the expected value
38+
getting properties returns the original value intact
39+
function calls roundtrip the value correctly
40+
method calls roundtrip the value correctly
41+
test:
42+
val(size_t v)
43+
testing value 1234
44+
setting properties preserves the expected value
45+
getting properties returns the original value intact
46+
function calls roundtrip the value correctly
47+
method calls roundtrip the value correctly
48+
testing value 18446744073709551615
49+
setting properties preserves the expected value
50+
getting properties returns the original value intact
51+
function calls roundtrip the value correctly
52+
method calls roundtrip the value correctly
53+
test:
54+
val(typed_memory_view<uint64_t>)
55+
test:
56+
val(typed_memory_view<int64_t>)
57+
end

test/test_core.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7721,7 +7721,8 @@ def test_embind_i64_val(self, safe_heap):
77217721
self.skipTest('asan does not work with SAFE_HEAP')
77227722
self.set_setting('SAFE_HEAP', safe_heap)
77237723
self.cflags += ['-lembind']
7724-
self.do_run_in_out_file_test('embind/test_i64_val.cpp', assert_identical=True)
7724+
out_suffix = '64' if self.get_setting('MEMORY64') else ''
7725+
self.do_run_in_out_file_test('embind/test_i64_val.cpp', assert_identical=True, out_suffix=out_suffix)
77257726

77267727
@no_wasm2js('wasm_bigint')
77277728
def test_embind_i64_binding(self):

0 commit comments

Comments
 (0)