summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qcocoahelpers.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoahelpers.h')
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h129
1 files changed, 129 insertions, 0 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 4478895538..1f4f9cd276 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -55,6 +55,9 @@
#include <QtGui/qpalette.h>
#include <QtGui/qscreen.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView));
QT_BEGIN_NAMESPACE
@@ -188,5 +191,131 @@ QT_END_NAMESPACE
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
+// -------------------------------------------------------------------------
+
+// Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret:
+// - http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
+// - https://lists.apple.com/archives/cocoa-dev/2008/Feb/msg02338.html
+template <typename T>
+struct objc_msgsend_requires_stret
+{ static const bool value =
+#if defined(Q_PROCESSOR_X86)
+ // Any return value larger than two registers on i386/x86_64
+ sizeof(T) > sizeof(void*) * 2;
+#elif defined(Q_PROCESSOR_ARM_32)
+ // Any return value larger than a single register on arm
+ sizeof(T) > sizeof(void*);
+#elif defined(Q_PROCESSOR_ARM_64)
+ // Stret not used on arm64
+ false;
+#endif
+};
+
+template <>
+struct objc_msgsend_requires_stret<void>
+{ static const bool value = false; };
+
+template <typename ReturnType, typename... Args>
+ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args)
+{
+ static_assert(!objc_msgsend_requires_stret<ReturnType>::value,
+ "The given return type requires stret on this platform");
+
+ typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...);
+ SuperFn superFn = reinterpret_cast<SuperFn>(objc_msgSendSuper);
+ objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) };
+ return superFn(&sup, selector, args...);
+}
+
+template <typename ReturnType, typename... Args>
+ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args)
+{
+ static_assert(objc_msgsend_requires_stret<ReturnType>::value,
+ "The given return type does not use stret on this platform");
+
+ typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...);
+ SuperStretFn superStretFn = reinterpret_cast<SuperStretFn>(objc_msgSendSuper_stret);
+
+ objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) };
+ ReturnType ret;
+ superStretFn(&ret, &sup, selector, args...);
+ return ret;
+}
+
+template<typename... Args>
+class QSendSuperHelper {
+public:
+ QSendSuperHelper(id receiver, SEL sel, Args... args)
+ : m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...)), m_sent(false)
+ {
+ }
+
+ ~QSendSuperHelper()
+ {
+ if (!m_sent)
+ msgSendSuper<void>(m_args);
+ }
+
+ template <typename ReturnType>
+ operator ReturnType()
+ {
+#if defined(QT_DEBUG)
+ Method method = class_getInstanceMethod(object_getClass(m_receiver), m_selector);
+ char returnTypeEncoding[256];
+ method_getReturnType(method, returnTypeEncoding, sizeof(returnTypeEncoding));
+ NSUInteger alignedReturnTypeSize = 0;
+ NSGetSizeAndAlignment(returnTypeEncoding, nullptr, &alignedReturnTypeSize);
+ Q_ASSERT(alignedReturnTypeSize == sizeof(ReturnType));
+#endif
+ m_sent = true;
+ return msgSendSuper<ReturnType>(m_args);
+ }
+
+private:
+ template <std::size_t... Ts>
+ struct index {};
+
+ template <std::size_t N, std::size_t... Ts>
+ struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};
+
+ template <std::size_t... Ts>
+ struct gen_seq<0, Ts...> : index<Ts...> {};
+
+ template <typename ReturnType, bool V>
+ using if_requires_stret = typename std::enable_if<objc_msgsend_requires_stret<ReturnType>::value == V, ReturnType>::type;
+
+ template <typename ReturnType, std::size_t... Is>
+ if_requires_stret<ReturnType, false> msgSendSuper(std::tuple<Args...>& args, index<Is...>)
+ {
+ return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
+ }
+
+ template <typename ReturnType, std::size_t... Is>
+ if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, index<Is...>)
+ {
+ return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
+ }
+
+ template <typename ReturnType>
+ ReturnType msgSendSuper(std::tuple<Args...>& args)
+ {
+ return msgSendSuper<ReturnType>(args, gen_seq<sizeof...(Args)>{});
+ }
+
+ id m_receiver;
+ SEL m_selector;
+ std::tuple<Args...> m_args;
+ bool m_sent;
+};
+
+template<typename... Args>
+QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, Args... args)
+{
+ return QSendSuperHelper<Args...>(receiver, selector, args...);
+}
+
+// Same as calling super, but the super_class field resolved at runtime instead of compile time
+#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__)
+
#endif //QCOCOAHELPERS_H