Skip to content

Commit 7637d97

Browse files
committed
embind - Fix reading various bindings types from val on wasm64.
- The function simpleReadValueFromPointer was always reading pointers as i32 numbers which was incorrect on wasm64. - Decoding of std::wstring was using a right shift to read pointers. Right shift was truncating numbers larger than i32 and causing misreads. - Added tests to execute readValueFromPointer on all the binding types to ensure these don't break on wasm64.
1 parent 4aefde3 commit 7637d97

File tree

4 files changed

+102
-26
lines changed

4 files changed

+102
-26
lines changed

src/embind/embind.js

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// -- jshint doesn't understand library syntax, so we need to specifically tell it about the symbols we define
1616
/*global typeDependencies, flushPendingDeletes, getTypeName, getBasestPointer, throwBindingError, UnboundTypeError, embindRepr, registeredInstances, registeredTypes*/
1717
/*global ensureOverloadTable, embind__requireFunction, awaitingDependencies, makeLegalFunctionName, embind_charCodes:true, registerType, createNamedFunction, RegisteredPointer, throwInternalError*/
18-
/*global simpleReadValueFromPointer, floatReadValueFromPointer, integerReadValueFromPointer, enumReadValueFromPointer, replacePublicSymbol, craftInvokerFunction, tupleRegistrations*/
18+
/*global floatReadValueFromPointer, integerReadValueFromPointer, enumReadValueFromPointer, replacePublicSymbol, craftInvokerFunction, tupleRegistrations*/
1919
/*global finalizationRegistry, attachFinalizer, detachFinalizer, releaseClassHandle, runDestructor*/
2020
/*global ClassHandle, makeClassHandle, structRegistrations, whenDependentTypesAreResolved, BindingError, deletionQueue, delayFunction:true, upcastPointer*/
2121
/*global exposePublicSymbol, heap32VectorToArray, newFunc, char_0, char_9*/
@@ -41,7 +41,7 @@ var LibraryEmbind = {
4141
// If register_type is used, emval will be registered multiple times for
4242
// different type id's, but only a single type object is needed on the JS side
4343
// for all of them. Store the type for reuse.
44-
$EmValType__deps: ['_emval_decref', '$Emval', '$simpleReadValueFromPointer', '$GenericWireTypeSize'],
44+
$EmValType__deps: ['_emval_decref', '$Emval', '$readPointer', '$GenericWireTypeSize'],
4545
$EmValType: `{
4646
name: 'emscripten::val',
4747
'fromWireType': (handle) => {
@@ -51,7 +51,7 @@ var LibraryEmbind = {
5151
},
5252
'toWireType': (destructors, value) => Emval.toHandle(value),
5353
'argPackAdvance': GenericWireTypeSize,
54-
'readValueFromPointer': simpleReadValueFromPointer,
54+
'readValueFromPointer': readPointer,
5555
destructorFunction: null, // This type does not need a destructor
5656
5757
// TODO: do we need a deleteObject here? write a test where
@@ -494,12 +494,6 @@ var LibraryEmbind = {
494494
});
495495
},
496496

497-
$simpleReadValueFromPointer__docs: '/** @suppress {globalThis} */',
498-
// For types whose wire types are 32-bit pointers.
499-
$simpleReadValueFromPointer: function(pointer) {
500-
return this['fromWireType']({{{ makeGetValue('pointer', '0', 'i32') }}});
501-
},
502-
503497
$readPointer__docs: '/** @suppress {globalThis} */',
504498
$readPointer: function(pointer) {
505499
return this['fromWireType']({{{ makeGetValue('pointer', '0', '*') }}});
@@ -617,33 +611,30 @@ var LibraryEmbind = {
617611
],
618612
_embind_register_std_wstring: (rawType, charSize, name) => {
619613
name = readLatin1String(name);
620-
var decodeString, encodeString, getHeap, lengthBytesUTF, shift;
614+
var decodeString, encodeString, readCharAt, lengthBytesUTF;
621615
if (charSize === 2) {
622616
decodeString = UTF16ToString;
623617
encodeString = stringToUTF16;
624618
lengthBytesUTF = lengthBytesUTF16;
625-
getHeap = () => HEAPU16;
626-
shift = 1;
619+
readCharAt = (pointer) => {{{ makeGetValue('pointer', 0, 'u16') }}};
627620
} else if (charSize === 4) {
628621
decodeString = UTF32ToString;
629622
encodeString = stringToUTF32;
630623
lengthBytesUTF = lengthBytesUTF32;
631-
getHeap = () => HEAPU32;
632-
shift = 2;
624+
readCharAt = (pointer) => {{{ makeGetValue('pointer', 0, 'u32') }}};
633625
}
634626
registerType(rawType, {
635627
name,
636628
'fromWireType': (value) => {
637629
// Code mostly taken from _embind_register_std_string fromWireType
638630
var length = {{{ makeGetValue('value', 0, '*') }}};
639-
var HEAP = getHeap();
640631
var str;
641632

642633
var decodeStartPtr = value + {{{ POINTER_SIZE }}};
643634
// Looping here to support possible embedded '0' bytes
644635
for (var i = 0; i <= length; ++i) {
645636
var currentBytePtr = value + {{{ POINTER_SIZE }}} + i * charSize;
646-
if (i == length || HEAP[currentBytePtr >> shift] == 0) {
637+
if (i == length || readCharAt(currentBytePtr) == 0) {
647638
var maxReadBytes = currentBytePtr - decodeStartPtr;
648639
var stringSegment = decodeString(decodeStartPtr, maxReadBytes);
649640
if (str === undefined) {
@@ -668,7 +659,7 @@ var LibraryEmbind = {
668659
// assumes POINTER_SIZE alignment
669660
var length = lengthBytesUTF(value);
670661
var ptr = _malloc({{{ POINTER_SIZE }}} + length + charSize);
671-
{{{ makeSetValue('ptr', '0', 'length >> shift', SIZE_TYPE) }}};
662+
{{{ makeSetValue('ptr', '0', 'length / charSize', SIZE_TYPE) }}};
672663

673664
encodeString(value, ptr + {{{ POINTER_SIZE }}}, length + charSize);
674665

@@ -678,7 +669,7 @@ var LibraryEmbind = {
678669
return ptr;
679670
},
680671
'argPackAdvance': GenericWireTypeSize,
681-
'readValueFromPointer': simpleReadValueFromPointer,
672+
'readValueFromPointer': readPointer,
682673
destructorFunction(ptr) {
683674
_free(ptr);
684675
}
@@ -1012,7 +1003,7 @@ var LibraryEmbind = {
10121003

10131004
_embind_finalize_value_array__deps: [
10141005
'$tupleRegistrations', '$runDestructors',
1015-
'$simpleReadValueFromPointer', '$whenDependentTypesAreResolved'],
1006+
'$readPointer', '$whenDependentTypesAreResolved'],
10161007
_embind_finalize_value_array: (rawTupleType) => {
10171008
var reg = tupleRegistrations[rawTupleType];
10181009
delete tupleRegistrations[rawTupleType];
@@ -1064,7 +1055,7 @@ var LibraryEmbind = {
10641055
return ptr;
10651056
},
10661057
'argPackAdvance': GenericWireTypeSize,
1067-
'readValueFromPointer': simpleReadValueFromPointer,
1058+
'readValueFromPointer': readPointer,
10681059
destructorFunction: rawDestructor,
10691060
}];
10701061
});
@@ -1115,7 +1106,7 @@ var LibraryEmbind = {
11151106

11161107
_embind_finalize_value_object__deps: [
11171108
'$structRegistrations', '$runDestructors',
1118-
'$simpleReadValueFromPointer', '$whenDependentTypesAreResolved'],
1109+
'$readPointer', '$whenDependentTypesAreResolved'],
11191110
_embind_finalize_value_object: (structType) => {
11201111
var reg = structRegistrations[structType];
11211112
delete structRegistrations[structType];
@@ -1173,7 +1164,7 @@ var LibraryEmbind = {
11731164
return ptr;
11741165
},
11751166
'argPackAdvance': GenericWireTypeSize,
1176-
'readValueFromPointer': simpleReadValueFromPointer,
1167+
'readValueFromPointer': readPointer,
11771168
destructorFunction: rawDestructor,
11781169
}];
11791170
});

test/code_size/embind_val_wasm.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"a.html": 673,
33
"a.html.gz": 431,
4-
"a.js": 7387,
5-
"a.js.gz": 3112,
4+
"a.js": 7325,
5+
"a.js.gz": 3088,
66
"a.wasm": 11433,
77
"a.wasm.gz": 5725,
8-
"total": 19493,
9-
"total_gz": 9268
8+
"total": 19431,
9+
"total_gz": 9244
1010
}

test/embind/test_val_read_pointer.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#include <emscripten/bind.h>
2+
#include <emscripten.h>
3+
4+
using namespace emscripten;
5+
6+
struct ValueObject {
7+
ValueObject(): value(43) {};
8+
int value;
9+
};
10+
11+
struct ValueArray {
12+
ValueArray(): x(44) {};
13+
int x;
14+
};
15+
16+
struct Foo {
17+
Foo(): value(45) {};
18+
int value;
19+
};
20+
21+
enum Enum { ONE, TWO };
22+
23+
EMSCRIPTEN_BINDINGS(xxx) {
24+
value_object<ValueObject>("ValueObject")
25+
.field("value", &ValueObject::value);
26+
27+
value_array<ValueArray>("ValueArray")
28+
.element(&ValueArray::x);
29+
30+
class_<Foo>("Foo")
31+
.property("value", &Foo::value);
32+
33+
enum_<Enum>("Enum")
34+
.value("ONE", ONE)
35+
.value("TWO", TWO);
36+
}
37+
38+
39+
int main() {
40+
EM_ASM(
41+
globalThis["passthrough"] = (arg) => {
42+
return arg;
43+
};
44+
globalThis["passthroughValueObject"] = (arg) => {
45+
return arg.value;
46+
};
47+
globalThis["passthroughValueArray"] = (arg) => {
48+
return arg[0];
49+
};
50+
globalThis["passthroughClass"] = (arg) => {
51+
const value = arg.value;
52+
arg.delete();
53+
return value;
54+
};
55+
globalThis["passthroughMemoryView"] = (arg) => {
56+
return arg[2];
57+
};
58+
);
59+
60+
// These tests execute all the various readValueFromPointer functions for each
61+
// of the different binding types.
62+
assert(val::global().call<bool>("passthrough", true));
63+
assert(val::global().call<int>("passthrough", 42) == 42);
64+
assert(val::global().call<double>("passthrough", 42.2) == 42.2);
65+
ValueObject vo;
66+
assert(val::global().call<int>("passthroughValueObject", vo) == 43);
67+
ValueArray va;
68+
assert(val::global().call<int>("passthroughValueArray", va) == 44);
69+
Foo foo;
70+
assert(val::global().call<int>("passthroughClass", foo) == 45);
71+
assert(val::global().call<std::string>("passthrough", val("abc")) == "abc");
72+
std::string str = "hello";
73+
assert(val::global().call<std::string>("passthrough", str) == "hello");
74+
std::wstring wstr = L"abc";
75+
assert(val::global().call<std::wstring>("passthrough", wstr) == L"abc");
76+
static const int data[] = {0, 1, 2};
77+
assert(val::global().call<int>("passthroughMemoryView", typed_memory_view(3, data)) == 2);
78+
assert(val::global().call<Enum>("passthrough", ONE) == ONE);
79+
80+
return 0;
81+
}

test/test_core.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7471,6 +7471,10 @@ def test_embind_val(self):
74717471
self.emcc_args += ['-lembind']
74727472
self.do_run_in_out_file_test('embind/test_val.cpp')
74737473

7474+
def test_embind_val_read_pointer(self):
7475+
self.emcc_args += ['-lembind']
7476+
self.do_runf('embind/test_val_read_pointer.cpp')
7477+
74747478
def test_embind_val_assignment(self):
74757479
err = self.expect_fail([EMCC, test_file('embind/test_val_assignment.cpp'), '-lembind', '-c'])
74767480
self.assertContained('candidate function not viable: expects an lvalue for object argument', err)

0 commit comments

Comments
 (0)