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: