From 7c09e34cfb06a2a078f2e013c891e39b826efe75 Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Tue, 10 Jun 2025 16:40:50 +0100 Subject: [PATCH] [embind] Use a single invoker mechanism This is the next step in refactorings I started back in #20383 to merge all Embind generic method callers into a single mechanism. I never got around to landing this PR because I kept trying to split it into even smaller changes / steps, never found a good way to do so due to how entangled they were, got frustrated and left it alone. However, aside from being a nice-to-have optimisation or cleanup refactoring, it's also an easy way to deal with issues like #24547 which makes it a bit more pressing. Without this PR, we'd have to duplicate the `emval_as`+`emval_as_int64`+`emval_as_uint64` fix from #13889 for `emval_call` and `emval_call_method` as well, but after this PR everything goes through the same centralised implementation where we can deal with all Embind types in a consistent manner. There are some more things that I'd like to clean up here (including some of the complexity added by this very own PR), but in order to try and keep review scope smaller, I'm going to submit them separately. Fixes #24547. --- src/lib/libemval.js | 102 ++++++++------- src/lib/libsigs.js | 10 +- src/struct_info_cxx.json | 6 +- src/struct_info_generated.json | 6 +- src/struct_info_generated_wasm64.json | 6 +- system/include/emscripten/val.h | 171 ++++++++------------------ system/include/emscripten/wire.h | 10 +- test/code_size/embind_val_wasm.json | 12 +- test/embind/test_i64_val.cpp | 45 ++++--- test/embind/test_i64_val.out | 37 +++++- 10 files changed, 188 insertions(+), 217 deletions(-) diff --git a/src/lib/libemval.js b/src/lib/libemval.js index 168773e28fc04..dad70468d5a3e 100644 --- a/src/lib/libemval.js +++ b/src/lib/libemval.js @@ -122,13 +122,6 @@ var LibraryEmVal = { _emval_new_u16string__deps: ['$Emval'], _emval_new_u16string: (v) => Emval.toHandle(UTF16ToString(v)), - _emval_take_value__deps: ['$Emval', '$requireRegisteredType'], - _emval_take_value: (type, arg) => { - type = requireRegisteredType(type, '_emval_take_value'); - var v = type['readValueFromPointer'](arg); - return Emval.toHandle(v); - }, - #if SUPPORTS_GLOBALTHIS $emval_get_global: () => globalThis, #elif !DYNAMIC_EXECUTION @@ -209,27 +202,6 @@ var LibraryEmVal = { return result; }, - _emval_as__deps: ['$Emval', '$requireRegisteredType', '$emval_returnValue'], - _emval_as: (handle, returnType, destructorsRef) => { - handle = Emval.toValue(handle); - returnType = requireRegisteredType(returnType, 'emval::as'); - return emval_returnValue(returnType, destructorsRef, handle); - }, - - _emval_as_int64__deps: ['$Emval', '$requireRegisteredType'], - _emval_as_int64: (handle, returnType) => { - handle = Emval.toValue(handle); - returnType = requireRegisteredType(returnType, 'emval::as'); - return returnType['toWireType'](null, handle); - }, - - _emval_as_uint64__deps: ['$Emval', '$requireRegisteredType'], - _emval_as_uint64: (handle, returnType) => { - handle = Emval.toValue(handle); - returnType = requireRegisteredType(returnType, 'emval::as'); - return returnType['toWireType'](null, handle); - }, - _emval_equals__deps: ['$Emval'], _emval_equals: (first, second) => { first = Emval.toValue(first); @@ -264,13 +236,6 @@ var LibraryEmVal = { return !object; }, - _emval_call__deps: ['$emval_methodCallers', '$Emval'], - _emval_call: (caller, handle, destructorsRef, args) => { - caller = emval_methodCallers[caller]; - handle = Emval.toValue(handle); - return caller(null, handle, destructorsRef, args); - }, - $emval_lookupTypes__deps: ['$requireRegisteredType'], $emval_lookupTypes: (argCount, argTypes) => { var a = new Array(argCount); @@ -292,11 +257,12 @@ var LibraryEmVal = { return id; }, - _emval_get_method_caller__deps: [ + _emval_create_invoker__deps: [ '$emval_addMethodCaller', '$emval_lookupTypes', '$createNamedFunction', '$emval_returnValue', + '$Emval', '$getStringOrSymbol', ], - _emval_get_method_caller: (argCount, argTypes, kind) => { + _emval_create_invoker: (argCount, argTypes, kind) => { var GenericWireTypeSize = {{{ 2 * POINTER_SIZE }}}; var types = emval_lookupTypes(argCount, argTypes); @@ -305,26 +271,38 @@ var LibraryEmVal = { #if !DYNAMIC_EXECUTION var argN = new Array(argCount); - var invokerFunction = (obj, func, destructorsRef, args) => { + var invokerFunction = (handle, methodName, destructorsRef, args) => { var offset = 0; for (var i = 0; i < argCount; ++i) { argN[i] = types[i]['readValueFromPointer'](args + offset); offset += GenericWireTypeSize; } - var rv = kind === /* CONSTRUCTOR */ 1 ? Reflect.construct(func, argN) : func.apply(obj, argN); + var rv; + switch (kind) { + case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}: + rv = Emval.toValue(handle).apply(null, argN); + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}: + rv = Reflect.construct(Emval.toValue(handle), argN); + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}: + // no-op, just return the argument + rv = argN[0]; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}: + rv = Emval.toValue(handle)[getStringOrSymbol(methodName)](...argN); + break; + } return emval_returnValue(retType, destructorsRef, rv); }; #else var functionBody = - `return function (obj, func, destructorsRef, args) {\n`; + `return function (handle, methodName, destructorsRef, args) {\n`; var offset = 0; - var argsList = []; // 'obj?, arg0, arg1, arg2, ... , argN' - if (kind === {{{ cDefs['internal::EM_METHOD_CALLER_KIND::FUNCTION'] }}}) { - argsList.push('obj'); - } - var params = ['retType']; - var args = [retType]; + var argsList = []; // 'arg0, arg1, arg2, ... , argN' + var params = ['toValue', 'retType']; + var args = [Emval.toValue, retType]; for (var i = 0; i < argCount; ++i) { argsList.push(`arg${i}`); params.push(`argType${i}`); @@ -333,7 +311,23 @@ var LibraryEmVal = { ` var arg${i} = argType${i}.readValueFromPointer(args${offset ? '+' + offset : ''});\n`; offset += GenericWireTypeSize; } - var invoker = kind === {{{ cDefs['internal::EM_METHOD_CALLER_KIND::CONSTRUCTOR'] }}} ? 'new func' : 'func.call'; + var invoker; + switch (kind){ + case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}: + invoker = 'toValue(handle)'; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}: + invoker = 'new (toValue(handle))'; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::CAST'] }}}: + invoker = ''; + break; + case {{{ cDefs['internal::EM_INVOKER_KIND::METHOD'] }}}: + params.push('getStringOrSymbol'); + args.push(getStringOrSymbol); + invoker = 'toValue(handle)[getStringOrSymbol(methodName)]'; + break; + } functionBody += ` var rv = ${invoker}(${argsList.join(', ')});\n`; if (!retType.isVoid) { @@ -351,14 +345,16 @@ var LibraryEmVal = { return emval_addMethodCaller(createNamedFunction(functionName, invokerFunction)); }, - _emval_call_method__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'], - _emval_call_method: (caller, objHandle, methodName, destructorsRef, args) => { - caller = emval_methodCallers[caller]; - objHandle = Emval.toValue(objHandle); - methodName = getStringOrSymbol(methodName); - return caller(objHandle, objHandle[methodName], destructorsRef, args); + _emval_invoke__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'], + _emval_invoke: (caller, handle, methodName, destructorsRef, args) => { + return emval_methodCallers[caller](handle, methodName, destructorsRef, args); }, + // Same as `_emval_invoke`, just imported into Wasm under a different return type. + // TODO: remove this if/when https://github.com/emscripten-core/emscripten/issues/20478 is fixed. + _emval_invoke_i64__deps: ['_emval_invoke'], + _emval_invoke_i64: '=__emval_invoke', + _emval_typeof__deps: ['$Emval'], _emval_typeof: (handle) => { handle = Emval.toValue(handle); diff --git a/src/lib/libsigs.js b/src/lib/libsigs.js index 62aed30a90e85..4b19dec546636 100644 --- a/src/lib/libsigs.js +++ b/src/lib/libsigs.js @@ -335,26 +335,23 @@ sigs = { _emscripten_thread_mailbox_await__sig: 'vp', _emscripten_thread_set_strongref__sig: 'vp', _emscripten_throw_longjmp__sig: 'v', - _emval_as__sig: 'dppp', - _emval_as_int64__sig: 'jpp', - _emval_as_uint64__sig: 'jpp', _emval_await__sig: 'pp', - _emval_call__sig: 'dpppp', - _emval_call_method__sig: 'dppppp', _emval_coro_make_promise__sig: 'ppp', _emval_coro_suspend__sig: 'vpp', + _emval_create_invoker__sig: 'pipi', _emval_decref__sig: 'vp', _emval_delete__sig: 'ipp', _emval_equals__sig: 'ipp', _emval_from_current_cxa_exception__sig: 'p', _emval_get_global__sig: 'pp', - _emval_get_method_caller__sig: 'pipi', _emval_get_module_property__sig: 'pp', _emval_get_property__sig: 'ppp', _emval_greater_than__sig: 'ipp', _emval_in__sig: 'ipp', _emval_incref__sig: 'vp', _emval_instanceof__sig: 'ipp', + _emval_invoke__sig: 'dppppp', + _emval_invoke_i64__sig: 'jppppp', _emval_is_number__sig: 'ip', _emval_is_string__sig: 'ip', _emval_iter_begin__sig: 'pp', @@ -371,7 +368,6 @@ sigs = { _emval_run_destructors__sig: 'vp', _emval_set_property__sig: 'vppp', _emval_strictly_equals__sig: 'ipp', - _emval_take_value__sig: 'ppp', _emval_throw__sig: 'ip', _emval_typeof__sig: 'pp', _gmtime_js__sig: 'vjp', diff --git a/src/struct_info_cxx.json b/src/struct_info_cxx.json index 70a898454382c..ad5133e6912d7 100644 --- a/src/struct_info_cxx.json +++ b/src/struct_info_cxx.json @@ -28,8 +28,10 @@ { "file": "emscripten/val.h", "defines": [ - "emscripten::internal::EM_METHOD_CALLER_KIND::FUNCTION", - "emscripten::internal::EM_METHOD_CALLER_KIND::CONSTRUCTOR" + "emscripten::internal::EM_INVOKER_KIND::FUNCTION", + "emscripten::internal::EM_INVOKER_KIND::METHOD", + "emscripten::internal::EM_INVOKER_KIND::CONSTRUCTOR", + "emscripten::internal::EM_INVOKER_KIND::CAST" ] } ] diff --git a/src/struct_info_generated.json b/src/struct_info_generated.json index 9df32182481ab..3595af1b6d7a5 100644 --- a/src/struct_info_generated.json +++ b/src/struct_info_generated.json @@ -516,8 +516,10 @@ "__WASI_RIGHTS_PATH_UNLINK_FILE": 67108864, "__WASI_RIGHTS_POLL_FD_READWRITE": 134217728, "__WASI_RIGHTS_SOCK_SHUTDOWN": 268435456, - "internal::EM_METHOD_CALLER_KIND::CONSTRUCTOR": 1, - "internal::EM_METHOD_CALLER_KIND::FUNCTION": 0 + "internal::EM_INVOKER_KIND::CAST": 3, + "internal::EM_INVOKER_KIND::CONSTRUCTOR": 2, + "internal::EM_INVOKER_KIND::FUNCTION": 0, + "internal::EM_INVOKER_KIND::METHOD": 1 }, "structs": { "AudioParamFrame": { diff --git a/src/struct_info_generated_wasm64.json b/src/struct_info_generated_wasm64.json index c9fb1215b6e56..31ab797e96a84 100644 --- a/src/struct_info_generated_wasm64.json +++ b/src/struct_info_generated_wasm64.json @@ -516,8 +516,10 @@ "__WASI_RIGHTS_PATH_UNLINK_FILE": 67108864, "__WASI_RIGHTS_POLL_FD_READWRITE": 134217728, "__WASI_RIGHTS_SOCK_SHUTDOWN": 268435456, - "internal::EM_METHOD_CALLER_KIND::CONSTRUCTOR": 1, - "internal::EM_METHOD_CALLER_KIND::FUNCTION": 0 + "internal::EM_INVOKER_KIND::CAST": 3, + "internal::EM_INVOKER_KIND::CONSTRUCTOR": 2, + "internal::EM_INVOKER_KIND::FUNCTION": 0, + "internal::EM_INVOKER_KIND::METHOD": 1 }, "structs": { "AudioParamFrame": { diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h index d2b3da4d922a4..40fa48b09cd17 100644 --- a/system/include/emscripten/val.h +++ b/system/include/emscripten/val.h @@ -36,9 +36,11 @@ namespace internal { template val wrapped_extend(const std::string&, const val&); -enum class EM_METHOD_CALLER_KIND { - FUNCTION = 0, - CONSTRUCTOR = 1, +enum class EM_INVOKER_KIND { + FUNCTION, + METHOD, + CONSTRUCTOR, + CAST, }; // Implemented in JavaScript. Don't call these directly. @@ -55,7 +57,7 @@ enum { }; typedef struct _EM_DESTRUCTORS* EM_DESTRUCTORS; -typedef struct _EM_METHOD_CALLER* EM_METHOD_CALLER; +typedef struct _EM_INVOKER* EM_INVOKER; typedef double EM_GENERIC_WIRE_TYPE; typedef const void* EM_VAR_ARGS; @@ -71,15 +73,10 @@ EM_VAL _emval_new_cstring(const char*); EM_VAL _emval_new_u8string(const char*); EM_VAL _emval_new_u16string(const char16_t*); -EM_VAL _emval_take_value(TYPEID type, EM_VAR_ARGS argv); - EM_VAL _emval_get_global(const char* name); EM_VAL _emval_get_module_property(const char* name); EM_VAL _emval_get_property(EM_VAL object, EM_VAL key); void _emval_set_property(EM_VAL object, EM_VAL key, EM_VAL value); -EM_GENERIC_WIRE_TYPE _emval_as(EM_VAL value, TYPEID returnType, EM_DESTRUCTORS* destructors); -int64_t _emval_as_int64(EM_VAL value, TYPEID returnType); -uint64_t _emval_as_uint64(EM_VAL value, TYPEID returnType); bool _emval_equals(EM_VAL first, EM_VAL second); bool _emval_strictly_equals(EM_VAL first, EM_VAL second); @@ -87,20 +84,20 @@ bool _emval_greater_than(EM_VAL first, EM_VAL second); bool _emval_less_than(EM_VAL first, EM_VAL second); bool _emval_not(EM_VAL object); -EM_GENERIC_WIRE_TYPE _emval_call( - EM_METHOD_CALLER caller, - EM_VAL func, - EM_DESTRUCTORS* destructors, - EM_VAR_ARGS argv); - // DO NOT call this more than once per signature. It will // leak generated function objects! -EM_METHOD_CALLER _emval_get_method_caller( +EM_INVOKER _emval_create_invoker( unsigned argCount, // including return value const TYPEID argTypes[], - EM_METHOD_CALLER_KIND asCtor); -EM_GENERIC_WIRE_TYPE _emval_call_method( - EM_METHOD_CALLER caller, + EM_INVOKER_KIND kind); +EM_GENERIC_WIRE_TYPE _emval_invoke( + EM_INVOKER caller, + EM_VAL handle, + const char* methodName, + EM_DESTRUCTORS* destructors, + EM_VAR_ARGS argv); +int64_t _emval_invoke_i64( + EM_INVOKER caller, EM_VAL handle, const char* methodName, EM_DESTRUCTORS* destructors, @@ -131,22 +128,6 @@ struct symbol_registrar { } }; -template -struct Signature { - /* - typedef typename BindingType::WireType (*MethodCaller)( - EM_VAL object, - EM_VAL method, - EM_DESTRUCTORS* destructors, - typename BindingType::WireType...); - */ - static EM_METHOD_CALLER get_method_caller() { - static constexpr WithPolicies<>::ArgTypeList args; - thread_local EM_METHOD_CALLER mc = _emval_get_method_caller(args.getCount(), args.getTypes(), Kind); - return mc; - } -}; - struct DestructorsRunner { public: explicit DestructorsRunner(EM_DESTRUCTORS d) @@ -179,17 +160,12 @@ struct GenericWireTypeConverter { } }; -template -T fromGenericWireType(EM_GENERIC_WIRE_TYPE g) { - typedef typename BindingType::WireType WireType; - WireType wt = GenericWireTypeConverter::from(g); - return BindingType::fromWireType(wt); -} - template<> -inline void fromGenericWireType(EM_GENERIC_WIRE_TYPE g) { - (void)g; -} +struct GenericWireTypeConverter::WireType> { + static BindingType::WireType from(double) { + return {}; + } +}; template struct PackSize; @@ -368,9 +344,8 @@ class EMBIND_VISIBILITY_DEFAULT val { template explicit val(T&& value, Policies...) { using namespace internal; - typename WithPolicies::template ArgTypeList valueType; - WireTypePack argv(std::forward(value)); - new (this) val(_emval_take_value(valueType.getTypes()[0], argv)); + + new (this) val(internalCall, val>(nullptr, nullptr, std::forward(value))); } val() : val(EM_VAL(internal::_EMVAL_UNDEFINED)) {} @@ -512,86 +487,28 @@ class EMBIND_VISIBILITY_DEFAULT val { val new_(Args&&... args) const { using namespace internal; - return internalCall(_emval_call, std::forward(args)...); + return internalCall, val>(as_handle(), nullptr, std::forward(args)...); } template val operator()(Args&&... args) const { using namespace internal; - return internalCall(_emval_call, std::forward(args)...); + return internalCall, val>(as_handle(), nullptr, std::forward(args)...); } template ReturnValue call(const char* name, Args&&... args) const { using namespace internal; - return internalCall( - [name](EM_METHOD_CALLER caller, - EM_VAL handle, - EM_DESTRUCTORS* destructorsRef, - EM_VAR_ARGS argv) { - return _emval_call_method(caller, handle, name, destructorsRef, argv); - }, - std::forward(args)...); + return internalCall, ReturnValue>(as_handle(), name, std::forward(args)...); } template T as(Policies...) const { using namespace internal; - typedef BindingType BT; - typename WithPolicies::template ArgTypeList targetType; - - EM_DESTRUCTORS destructors = nullptr; - EM_GENERIC_WIRE_TYPE result = _emval_as( - as_handle(), - targetType.getTypes()[0], - &destructors); - DestructorsRunner dr(destructors); - return fromGenericWireType(result); - } - -#ifdef __wasm64__ - template<> - long as() const { - using namespace internal; - - typedef BindingType BT; - typename WithPolicies<>::template ArgTypeList targetType; - - return _emval_as_int64(as_handle(), targetType.getTypes()[0]); - } - - template<> - unsigned long as() const { - using namespace internal; - - typedef BindingType BT; - typename WithPolicies<>::template ArgTypeList targetType; - - return _emval_as_uint64(as_handle(), targetType.getTypes()[0]); - } -#endif - - template<> - int64_t as() const { - using namespace internal; - - typedef BindingType BT; - typename WithPolicies<>::template ArgTypeList targetType; - - return _emval_as_int64(as_handle(), targetType.getTypes()[0]); - } - - template<> - uint64_t as() const { - using namespace internal; - - typedef BindingType BT; - typename WithPolicies<>::template ArgTypeList targetType; - - return _emval_as_uint64(as_handle(), targetType.getTypes()[0]); + return internalCall, T>(as_handle(), nullptr, *this); } // Prefer calling val::typeOf() over val::typeof(), since this form works in both C++11 and GNU++11 build modes. "typeof" is a reserved word in GNU++11 extensions. @@ -651,19 +568,37 @@ class EMBIND_VISIBILITY_DEFAULT val { template friend val internal::wrapped_extend(const std::string& , const val& ); - template - Ret internalCall(Implementation impl, Args&&... args) const { + template + static Ret internalCall(EM_VAL handle, const char *methodName, Args&&... args) { using namespace internal; + using RetWire = BindingType::WireType; + + static constexpr typename Policy::template ArgTypeList argTypes; + thread_local EM_INVOKER mc = _emval_create_invoker(argTypes.getCount(), argTypes.getTypes(), Kind); + WireTypePack argv(std::forward(args)...); EM_DESTRUCTORS destructors = nullptr; - EM_GENERIC_WIRE_TYPE result = impl( - Signature::get_method_caller(), - as_handle(), - &destructors, - argv); + + RetWire result; + if constexpr (std::is_integral::value && sizeof(RetWire) == 8) { + // 64-bit integers can't go through "generic wire type" because double and int64 have different ABI. + result = static_cast(_emval_invoke_i64( + mc, + handle, + methodName, + &destructors, + argv)); + } else { + result = GenericWireTypeConverter::from(_emval_invoke( + mc, + handle, + methodName, + &destructors, + argv)); + } DestructorsRunner rd(destructors); - return fromGenericWireType(result); + return BindingType::fromWireType(result); } template diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index 1ef1e669c0f67..33cf72eafbbbe 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -300,7 +300,15 @@ EMSCRIPTEN_DEFINE_NATIVE_BINDING_TYPE(uint64_t); template<> struct BindingType { - typedef void WireType; + // Using empty struct instead of void is ABI-compatible, but makes it easier + // to work with wire types in a generic template context, as void can't be + // stored in local variables or passed around but empty struct can. + // TODO: switch to std::monostate when we require C++17. + struct WireType {}; + + static void fromWireType(WireType) { + // No-op, as void has no value. + } }; template<> diff --git a/test/code_size/embind_val_wasm.json b/test/code_size/embind_val_wasm.json index 8f17ee7a944e7..142d545b0c007 100644 --- a/test/code_size/embind_val_wasm.json +++ b/test/code_size/embind_val_wasm.json @@ -1,10 +1,10 @@ { "a.html": 552, "a.html.gz": 380, - "a.js": 5685, - "a.js.gz": 2538, - "a.wasm": 9097, - "a.wasm.gz": 4696, - "total": 15334, - "total_gz": 7614 + "a.js": 5748, + "a.js.gz": 2563, + "a.wasm": 9101, + "a.wasm.gz": 4698, + "total": 15401, + "total_gz": 7641 } diff --git a/test/embind/test_i64_val.cpp b/test/embind/test_i64_val.cpp index ebec5ecee6b10..0dbdf563ff423 100644 --- a/test/embind/test_i64_val.cpp +++ b/test/embind/test_i64_val.cpp @@ -33,6 +33,20 @@ string compare_a_64_js(T value) { return ss.str(); } +template +void test_value(T&& value) { + cout << " testing value " << value << endl; + cout << " setting properties preserves the expected value" << endl; + val::global().set("a", val(value)); + ensure_js(compare_a_64_js(value)); + cout << " getting properties returns the original value intact" << endl; + assert(val::global()["a"].as() == value); + cout << " function calls roundtrip the value correctly" << endl; + assert(val::global("BigInt")(value).template as() == value); + cout << " method calls roundtrip the value correctly" << endl; + assert(val::global().call("BigInt", value) == value); +} + int main() { const int64_t max_int64_t = numeric_limits::max(); const int64_t min_int64_t = numeric_limits::min(); @@ -43,33 +57,16 @@ int main() { printf("start\n"); EM_ASM({globalThis.a = null}); - test("val(int64_t v)"); - val::global().set("a", val(int64_t(1234))); - ensure_js("a === 1234n"); - - val::global().set("a", val(int64_t(-4321))); - ensure_js("a === -4321n"); - - val::global().set("a", val(int64_t(0x12345678aabbccddL))); - ensure_js("a === 1311768467732155613n"); - assert(val::global()["a"].as() == 0x12345678aabbccddL); - test("val(uint64_t v)"); - val::global().set("a", val(uint64_t(1234))); - ensure_js("a === 1234n"); - - val::global().set("a", val(max_uint64_t)); - ensure_js(compare_a_64_js(max_uint64_t)); - assert(val::global()["a"].as() == max_uint64_t); + test_value(uint64_t(1234)); + test_value(max_uint64_t); test("val(int64_t v)"); - val::global().set("a", val(max_int64_t)); - ensure_js(compare_a_64_js(max_int64_t)); - assert(val::global()["a"].as() == max_int64_t); - - val::global().set("a", val(min_int64_t)); - ensure_js(compare_a_64_js(min_int64_t)); - assert(val::global()["a"].as() == min_int64_t); + test_value(int64_t(1234)); + test_value(int64_t(-4321)); + test_value(int64_t(0x12345678aabbccddL)); + test_value(min_int64_t); + test_value(max_int64_t); test("val(typed_memory_view)"); val::global().set("a", val(typed_memory_view(uint64Array.size(), uint64Array.data()))); diff --git a/test/embind/test_i64_val.out b/test/embind/test_i64_val.out index e5b21f95d91d7..3f6bd67e36b3d 100644 --- a/test/embind/test_i64_val.out +++ b/test/embind/test_i64_val.out @@ -1,10 +1,43 @@ start test: -val(int64_t v) -test: val(uint64_t v) + testing value 1234 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly + testing value 18446744073709551615 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly test: val(int64_t v) + testing value 1234 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly + testing value -4321 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly + testing value 1311768467732155613 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly + testing value -9223372036854775808 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly + testing value 9223372036854775807 + setting properties preserves the expected value + getting properties returns the original value intact + function calls roundtrip the value correctly + method calls roundtrip the value correctly test: val(typed_memory_view) test: