diff --git a/src/embind/embind.js b/src/embind/embind.js index 290ae88a77a7b..fc5928970eb21 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -298,7 +298,7 @@ var LibraryEmbind = { $registeredPointers: {}, $registerType__deps: [ - '$awaitingDependencies', '$registeredTypes', + '$awaitingDependencies', '$registeredTypes', '$getTypeName', '$typeDependencies', '$throwBindingError', '$whenDependentTypesAreResolved'], $registerType__docs: '/** @param {Object=} options */', @@ -319,6 +319,12 @@ var LibraryEmbind = { } } +#if MAIN_MODULE + // Store type name as well, which may or may not match the registered + // name, depending on mangling and the actual calls to register_foo. + registeredInstance.typeName = getTypeName(rawType); +#endif + registeredTypes[rawType] = registeredInstance; delete typeDependencies[rawType]; @@ -416,6 +422,20 @@ var LibraryEmbind = { $requireRegisteredType: function(rawType, humanName) { var impl = registeredTypes[rawType]; if (undefined === impl) { +#if MAIN_MODULE + // Fallback, in case of shared libraries, where the TypeID (rawType) + // for each type is not stable across library boundaries. + var typeName = getTypeName(rawType); + for (const typeId in registeredTypes) { + var type = registeredTypes[typeId]; + if (type.typeName == typeName) { + registerType(rawType, type, { + ignoreDuplicateRegistrations: true, + }); + return type; + } + } +#endif throwBindingError(humanName + " has unknown type " + getTypeName(rawType)); } return impl; diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h index e6fc0d174e0bd..9fbd7ff619e61 100644 --- a/system/include/emscripten/wire.h +++ b/system/include/emscripten/wire.h @@ -39,6 +39,10 @@ constexpr bool has_unbound_type_names = false; namespace internal { +// Note: Neither CanonicalizedID nor the typeid-based approach +// is guaranteed to produce stable type ids across library +// boundaries. See core.test_dylink_typeid for more info. + typedef const void* TYPEID; // We don't need the full std::type_info implementation. We diff --git a/test/test_core.py b/test/test_core.py index f0ba1015af975..8c072287ee305 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -5227,6 +5227,40 @@ def test_dylink_tls_export(self): self.dylink_testf(test_file('core/test_dylink_tls_export.c'), need_reverse=False) + @needs_dylink + @disabled('TypeID does not currently handle this') + def test_dylink_typeid(self): + # When the default visibility of the library's symbols is hidden, + # the type info is not merged, and we end up with different type + # ids in the shared library and the executable. + self.emcc_args.append('-fvisibility=hidden') + self.dylink_test(header=r''' + #include + + using namespace emscripten::internal; + struct SomeType {}; + __attribute__((visibility("default"))) TYPEID getSomeTypeId(); + ''', main=r''' + #include "header.h" + #include + + int main() { + if (getSomeTypeId() != TypeID::get()) { + puts("type ids are not the same"); + return 1; + } + puts("success"); + return 0; + } + ''', side=r''' + #include "header.h" + #include + + TYPEID getSomeTypeId() { + return TypeID::get(); + } + ''', expected=['success'], need_reverse=False) + def test_random(self): src = r'''#include #include @@ -7650,6 +7684,31 @@ def test_embind_no_rtti_followed_by_rtti(self): self.emcc_args += ['-lembind', '-fno-rtti', '-frtti'] self.do_run(src, '418\ndotest returned: 42\n') + @needs_dylink + def test_embind_type_registration_when_typeid_is_not_stable(self): + # See test_dylink_typeid + self.emcc_args += ['-lembind', '-fvisibility=hidden'] + self.dylink_test(header=r''' + #include + #include + #include + #include + ''', main=r''' + #include "header.h" + int main() { + emscripten::val intVal(42); + emscripten::val valVal(intVal); + puts("success"); + return 0; + } + ''', side=r''' + #include "header.h" + __attribute__((constructor)) void kaboom() { + emscripten::val intVal(42); + emscripten::val valVal(intVal); + } + ''', expected=['success'], need_reverse=False) + @no_wasm64('webidl not compatible with MEMORY64 yet') @parameterized({ '': ('DEFAULT', False),