diff --git a/Source/com/Administrator.h b/Source/com/Administrator.h index cd34302c0..5ce62e758 100644 --- a/Source/com/Administrator.h +++ b/Source/com/Administrator.h @@ -401,14 +401,51 @@ namespace RPC { } private: + // Convert a ProxyType to ProxyType using + // static_cast rather than dynamic_cast. + // + // On macOS, Apple Clang unconditionally emits typeinfo for + // implicit template instantiations as "weak private external" + // (hidden), regardless of -fvisibility settings or explicit + // visibility annotations. Apple's libc++ compares typeinfo by + // pointer only for hidden symbols (no strcmp fallback like + // libstdc++ on Linux), so dynamic_cast across dylib boundaries + // fails for template types like IPCMessageType. + // + // The label check (IIPC::Label() == InvokeMessage::Id()) provides + // the same runtime type guarantee that dynamic_cast would, and + // static_cast correctly adjusts for the multiple-inheritance + // layout (IPCMessageType inherits both IIPC and IReferenceCounted). static Core::ProxyType ExtractInvokeMessage(const Core::ProxyType& data) { - Core::ProxyType message(data); - ASSERT(message.IsValid() == true); + if (data.IsValid() == false) { + return Core::ProxyType(); + } + + Core::IIPC* raw = const_cast(data.operator->()); + ASSERT(raw->Label() == InvokeMessage::Id()); + + if (raw->Label() != InvokeMessage::Id()) { + return Core::ProxyType(); + } + + InvokeMessage* message = static_cast(raw); if (message->Parameters().IsValid() == false) { - message = Core::ProxyType(); + return Core::ProxyType(); } - return message; + return Core::ProxyType( + static_cast(*message), *message); + } + static Core::ProxyType ToInvokeMessage(const Core::ProxyType& data) + { + if ((data.IsValid() == false) || (data->Label() != InvokeMessage::Id())) { + return Core::ProxyType(); + } + + InvokeMessage* message = + static_cast(const_cast(data.operator->())); + return Core::ProxyType( + static_cast(*message), *message); } public: @@ -447,7 +484,7 @@ namespace RPC { } string Identifier() const override { string identifier; - Core::ProxyType message(_message); + Core::ProxyType message(ToInvokeMessage(_message)); if (message.IsValid() == false) { identifier = _T("{ \"type\": \"COMRPC\" }"); } diff --git a/Source/com/Communicator.cpp b/Source/com/Communicator.cpp index dca924a2f..7c6ce850d 100644 --- a/Source/com/Communicator.cpp +++ b/Source/com/Communicator.cpp @@ -598,7 +598,7 @@ namespace RPC { // Message delivered and responded on.... RPC::AnnounceMessage* announceMessage = static_cast(&element); - ASSERT(dynamic_cast(&element) != nullptr); + ASSERT(element.Label() == RPC::AnnounceMessage::Id()); if (announceMessage->Response().IsSet() == true) { string jsonMessagingCategories(announceMessage->Response().MessagingCategories()); diff --git a/Source/com/Communicator.h b/Source/com/Communicator.h index 089af57e8..1ee90e486 100644 --- a/Source/com/Communicator.h +++ b/Source/com/Communicator.h @@ -1558,7 +1558,14 @@ namespace RPC { public: void Procedure(Core::IPCChannel& channel, Core::ProxyType& data) override { - Core::ProxyType message(data); + // Use static_cast + label check instead of dynamic_cast + // to avoid cross-dylib RTTI failure on macOS (see + // Administrator.h ExtractInvokeMessage for details). + ASSERT(data.IsValid() && data->Label() == AnnounceMessage::Id()); + AnnounceMessage* rawMsg = + static_cast(const_cast(data.operator->())); + Core::ProxyType message( + static_cast(*rawMsg), *rawMsg); ASSERT(message.IsValid() == true); ASSERT(dynamic_cast(&channel) != nullptr); @@ -1842,7 +1849,14 @@ POP_WARNING() { // Oke, see if we can reference count the IPCChannel Core::ProxyType refChannel(channel); - Core::ProxyType message(data); + + // Use static_cast + label check instead of dynamic_cast + // (see Administrator.h ExtractInvokeMessage for rationale). + ASSERT(data.IsValid() && data->Label() == RPC::AnnounceMessage::Id()); + RPC::AnnounceMessage* rawMsg = + static_cast(const_cast(data.operator->())); + Core::ProxyType message( + static_cast(*rawMsg), *rawMsg); ASSERT(refChannel.IsValid());