Commit 7f9bfef5 authored by Andrew Twyman's avatar Andrew Twyman

Add @autorelease pool in ObjC proxy destructors

This is fixing a memory leak found in Dropbox's iOS networking libraries. Some ObjC objects were being autoreleased into a pool which is never flushed (the top-level pool on a C++-owned thread. This was Djinni violating its contracts around language isolation. The root cause turned out to be the destructor of an ObjcProxy object. Since it's called directly from C++, and does all its work in destructors, it was manipulating ObjC objects without an @autoreleasepool.

The fix is more complex than I'd like due to the need to arrange for destructors to run inside of an @autorelease block, which forces an awkward code structure. Furthermore, I can't use optional without knowing what namespace to look in to find nullopt, which isn't easily extractable from Djinni's current usage, so I had to write my own. I'd very much like to find a simpler fix, so suggested follow-on diffs are welcome.
parent b33d05de
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from textsort.djinni
#import "TXSTextboxListener+Private.h"
#import "TXSTextboxListener.h"
#import "DJIObjcWrapperCache+Private.h"
#import "collections/generated-src/objc/TXSItemList+Private.h"
#include <stdexcept>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
namespace djinni_generated {
class TextboxListener::ObjcProxy final
: public ::textsort::TextboxListener
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::TextboxListener;
public:
using ObjcProxyBase::ObjcProxyBase;
void update(const ::collections::ItemList & c_items) override
{
@autoreleasepool {
[djinni_private_get_proxied_objc_object() update:(::djinni_generated::ItemList::fromCpp(c_items))];
}
}
};
} // namespace djinni_generated
namespace djinni_generated {
auto TextboxListener::toCpp(ObjcType objc) -> CppType
{
if (!objc) {
return nullptr;
}
return ::djinni::get_objc_proxy<ObjcProxy>(objc);
}
auto TextboxListener::fromCppOpt(const CppOptType& cpp) -> ObjcType
{
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -13,14 +13,15 @@ namespace djinni_generated {
class TextboxListener::ObjcProxy final
: public ::textsort::TextboxListener
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::TextboxListener;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
void update(const ::textsort::ItemList & c_items) override
{
@autoreleasepool {
[Handle::get() update:(::djinni_generated::ItemList::fromCpp(c_items))];
[djinni_private_get_proxied_objc_object() update:(::djinni_generated::ItemList::fromCpp(c_items))];
}
}
};
......@@ -42,7 +43,7 @@ auto TextboxListener::fromCppOpt(const CppOptType& cpp) -> ObjcType
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -208,17 +208,18 @@ class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
wrapNamespace(w, spec.objcppNamespace, w => {
w.wl(s"class $helperClass::ObjcProxy final")
w.wl(s": public $cppSelf")
w.wl(s", public ::djinni::ObjcProxyCache::Handle<ObjcType>") // Use base class to avoid name conflicts with user-defined methods having the same name as this new data member
w.wl(s", private ::djinni::ObjcProxyBase<ObjcType>")
w.bracedSemi {
w.wl(s"friend class ${objcppMarshal.helperClassWithNs(ident)};")
w.wlOutdent("public:")
w.wl("using Handle::Handle;")
w.wl("using ObjcProxyBase::ObjcProxyBase;")
for (m <- i.methods) {
val ret = cppMarshal.fqReturnType(m.ret)
val params = m.params.map(p => cppMarshal.fqParamType(p.ty) + " c_" + idCpp.local(p.ident))
w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")} override").braced {
w.w("@autoreleasepool").braced {
val ret = m.ret.fold("")(_ => "auto objcpp_result_ = ")
val call = s"[Handle::get() ${idObjc.method(m.ident)}"
val call = s"[djinni_private_get_proxied_objc_object() ${idObjc.method(m.ident)}"
writeAlignedObjcCall(w, ret + call, m.params, "]", p => (idObjc.field(p.ident), s"(${objcppMarshal.fromCpp(p.ty, "c_" + idCpp.local(p.ident))})"))
w.wl(";")
m.ret.fold()(ty => {
......@@ -284,13 +285,13 @@ class ObjcppGenerator(spec: Spec) extends BaseObjcGenerator(spec) {
if (i.ext.objc && !i.ext.cpp) {
// ObjC only. In this case we *must* unwrap a proxy object - the dynamic_cast will
// throw bad_cast if we gave it something of the wrong type.
w.wl(s"return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();")
w.wl(s"return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();")
} else if (i.ext.objc || i.ext.cpp) {
// C++ only, or C++ and ObjC.
if (i.ext.objc) {
// If it could be implemented in ObjC, we might have to unwrap a proxy object.
w.w(s"if (auto cppPtr = dynamic_cast<ObjcProxy*>(cpp.get()))").braced {
w.wl("return cppPtr->Handle::get();")
w.wl("return cppPtr->djinni_private_get_proxied_objc_object();")
}
}
w.wl(s"return ::djinni::get_cpp_proxy<$objcSelf>(cpp);")
......
......@@ -53,6 +53,8 @@ class ObjcppMarshal(spec: Spec) extends Marshal(spec) {
def helperClass(name: String) = idCpp.ty(name)
private def helperClass(tm: MExpr): String = helperName(tm) + helperTemplates(tm)
def helperClassWithNs(name: String) = withNs(Some(spec.objcppNamespace), helperClass(name))
def privateHeaderName(ident: String): String = idObjc.ty(ident) + "+Private." + spec.objcHeaderExt
def helperName(tm: MExpr): String = tm.base match {
......
......@@ -53,4 +53,72 @@ static std::shared_ptr<CppType> get_objc_proxy(ObjcType * objcRef) {
));
}
// Private implementation base class for all ObjC proxies, which manages the
// Handle, and ensures that it is created and destroyed in a safe way, inside
// of an @autoreleasepool to avoid leaks. Most of the complexity here is just
// necessary to gain explicit control of construction and destruction time of
// the member Handle, to make sure it's inside the @autoreleasepool block.
template <typename ObjcType>
class ObjcProxyBase {
public:
ObjcProxyBase(ObjcType objc) {
@autoreleasepool {
m_djinni_private_proxy_handle.emplace(objc);
}
}
ObjcProxyBase(const ObjcProxyBase&) = delete;
ObjcProxyBase& operator=(const ObjcProxyBase&) = delete;
// Not intended for polymorphic use, so dtor is not virtual
~ObjcProxyBase() {
@autoreleasepool {
m_djinni_private_proxy_handle.reset();
}
}
// Long name to minimize likelyhood of collision with interface methods.
ObjcType djinni_private_get_proxied_objc_object() const {
return m_djinni_private_proxy_handle.value().get();
}
private:
// Simple stand-in for std::experimental::optional since the
// support-lib doesn't know which optional implementation might be in use.
// We can't take OptionalType as a template arg here, because there's no
// way to implement reset() without also knowing where to find nullopt, not
// just optional itself. This class uses the same technique as at least
// one optional implementation: The union ensures proper alignment, while
// leaving construction and destruction under our direct control.
// This class is NOT safe for general use. It's safe only if emplace() and
// reset() are each called exactly once.
class OptionalObjcProxyHandle final {
public:
using HandleType = ::djinni::ObjcProxyCache::Handle<ObjcType>;
OptionalObjcProxyHandle() {};
OptionalObjcProxyHandle(const OptionalObjcProxyHandle&) = delete;
OptionalObjcProxyHandle& operator=(const OptionalObjcProxyHandle&) = delete;
~OptionalObjcProxyHandle() {};
void emplace(ObjcType objc) {
new (&m_handle) HandleType(objc);
}
void reset() {
m_handle.~HandleType();
}
const HandleType & value() const { return m_handle; }
private:
union {
char m_dummy;
HandleType m_handle;
};
};
// Long name to minimize likelyhood of collision with interface methods.
OptionalObjcProxyHandle m_djinni_private_proxy_handle;
};
} // namespace djinni
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from inner_class.djinni
#import "DBInnerClass+Private.h"
#import "DBInnerClass.h"
#import "DJIMarshal+Private.h"
#import "DJIObjcWrapperCache+Private.h"
#include <stdexcept>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
namespace djinni_generated {
class InnerClass::ObjcProxy final
: public ::package1::InnerClass
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::InnerClass;
public:
using ObjcProxyBase::ObjcProxyBase;
std::string get_name() override
{
@autoreleasepool {
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() getName];
return ::djinni::String::toCpp(objcpp_result_);
}
}
};
} // namespace djinni_generated
namespace djinni_generated {
auto InnerClass::toCpp(ObjcType objc) -> CppType
{
if (!objc) {
return nullptr;
}
return ::djinni::get_objc_proxy<ObjcProxy>(objc);
}
auto InnerClass::fromCppOpt(const CppOptType& cpp) -> ObjcType
{
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -14,14 +14,15 @@ namespace djinni_generated {
class ClientInterface::ObjcProxy final
: public ::testsuite::ClientInterface
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::ClientInterface;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
::testsuite::ClientReturnedRecord get_record(int64_t c_record_id, const std::string & c_utf8string, const std::experimental::optional<std::string> & c_misc) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() getRecord:(::djinni::I64::fromCpp(c_record_id))
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() getRecord:(::djinni::I64::fromCpp(c_record_id))
utf8string:(::djinni::String::fromCpp(c_utf8string))
misc:(::djinni::Optional<std::experimental::optional, ::djinni::String>::fromCpp(c_misc))];
return ::djinni_generated::ClientReturnedRecord::toCpp(objcpp_result_);
......@@ -30,7 +31,7 @@ public:
double identifier_check(const std::vector<uint8_t> & c_data, int32_t c_r, int64_t c_jret) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() identifierCheck:(::djinni::Binary::fromCpp(c_data))
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() identifierCheck:(::djinni::Binary::fromCpp(c_data))
r:(::djinni::I32::fromCpp(c_r))
jret:(::djinni::I64::fromCpp(c_jret))];
return ::djinni::F64::toCpp(objcpp_result_);
......@@ -39,21 +40,21 @@ public:
std::string return_str() override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() returnStr];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() returnStr];
return ::djinni::String::toCpp(objcpp_result_);
}
}
std::string meth_taking_interface(const std::shared_ptr<::testsuite::ClientInterface> & c_i) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() methTakingInterface:(::djinni_generated::ClientInterface::fromCpp(c_i))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() methTakingInterface:(::djinni_generated::ClientInterface::fromCpp(c_i))];
return ::djinni::String::toCpp(objcpp_result_);
}
}
std::string meth_taking_optional_interface(const std::shared_ptr<::testsuite::ClientInterface> & c_i) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() methTakingOptionalInterface:(::djinni::Optional<std::experimental::optional, ::djinni_generated::ClientInterface>::fromCpp(c_i))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() methTakingOptionalInterface:(::djinni::Optional<std::experimental::optional, ::djinni_generated::ClientInterface>::fromCpp(c_i))];
return ::djinni::String::toCpp(objcpp_result_);
}
}
......@@ -76,7 +77,7 @@ auto ClientInterface::fromCppOpt(const CppOptType& cpp) -> ObjcType
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -71,42 +71,43 @@ namespace djinni_generated {
class EnumUsageInterface::ObjcProxy final
: public ::testsuite::EnumUsageInterface
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::EnumUsageInterface;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
::testsuite::color e(::testsuite::color c_e) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() e:(::djinni::Enum<::testsuite::color, DBColor>::fromCpp(c_e))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() e:(::djinni::Enum<::testsuite::color, DBColor>::fromCpp(c_e))];
return ::djinni::Enum<::testsuite::color, DBColor>::toCpp(objcpp_result_);
}
}
std::experimental::optional<::testsuite::color> o(std::experimental::optional<::testsuite::color> c_o) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() o:(::djinni::Optional<std::experimental::optional, ::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_o))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() o:(::djinni::Optional<std::experimental::optional, ::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_o))];
return ::djinni::Optional<std::experimental::optional, ::djinni::Enum<::testsuite::color, DBColor>>::toCpp(objcpp_result_);
}
}
std::vector<::testsuite::color> l(const std::vector<::testsuite::color> & c_l) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() l:(::djinni::List<::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_l))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() l:(::djinni::List<::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_l))];
return ::djinni::List<::djinni::Enum<::testsuite::color, DBColor>>::toCpp(objcpp_result_);
}
}
std::unordered_set<::testsuite::color> s(const std::unordered_set<::testsuite::color> & c_s) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() s:(::djinni::Set<::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_s))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() s:(::djinni::Set<::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_s))];
return ::djinni::Set<::djinni::Enum<::testsuite::color, DBColor>>::toCpp(objcpp_result_);
}
}
std::unordered_map<::testsuite::color, ::testsuite::color> m(const std::unordered_map<::testsuite::color, ::testsuite::color> & c_m) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() m:(::djinni::Map<::djinni::Enum<::testsuite::color, DBColor>, ::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_m))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() m:(::djinni::Map<::djinni::Enum<::testsuite::color, DBColor>, ::djinni::Enum<::testsuite::color, DBColor>>::fromCpp(c_m))];
return ::djinni::Map<::djinni::Enum<::testsuite::color, DBColor>, ::djinni::Enum<::testsuite::color, DBColor>>::toCpp(objcpp_result_);
}
}
......@@ -133,7 +134,7 @@ auto EnumUsageInterface::fromCppOpt(const CppOptType& cpp) -> ObjcType
return nil;
}
if (auto cppPtr = dynamic_cast<ObjcProxy*>(cpp.get())) {
return cppPtr->Handle::get();
return cppPtr->djinni_private_get_proxied_objc_object();
}
return ::djinni::get_cpp_proxy<DBEnumUsageInterfaceCppProxy>(cpp);
}
......
......@@ -14,14 +14,15 @@ namespace djinni_generated {
class ExternInterface2::ObjcProxy final
: public ::ExternInterface2
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::ExternInterface2;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
::ExternRecordWithDerivings foo(const std::shared_ptr<::testsuite::TestHelpers> & c_i) override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() foo:(::djinni_generated::TestHelpers::fromCpp(c_i))];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() foo:(::djinni_generated::TestHelpers::fromCpp(c_i))];
return ::djinni_generated::ExternRecordWithDerivings::toCpp(objcpp_result_);
}
}
......@@ -44,7 +45,7 @@ auto ExternInterface2::fromCppOpt(const CppOptType& cpp) -> ObjcType
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -12,14 +12,15 @@ namespace djinni_generated {
class FirstListener::ObjcProxy final
: public ::testsuite::FirstListener
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::FirstListener;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
void first() override
{
@autoreleasepool {
[Handle::get() first];
[djinni_private_get_proxied_objc_object() first];
}
}
};
......@@ -41,7 +42,7 @@ auto FirstListener::fromCppOpt(const CppOptType& cpp) -> ObjcType
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -12,10 +12,11 @@ namespace djinni_generated {
class ObjcOnlyListener::ObjcProxy final
: public ::testsuite::ObjcOnlyListener
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::ObjcOnlyListener;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
};
} // namespace djinni_generated
......@@ -35,7 +36,7 @@ auto ObjcOnlyListener::fromCppOpt(const CppOptType& cpp) -> ObjcType
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -12,14 +12,15 @@ namespace djinni_generated {
class SecondListener::ObjcProxy final
: public ::testsuite::SecondListener
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::SecondListener;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
void second() override
{
@autoreleasepool {
[Handle::get() second];
[djinni_private_get_proxied_objc_object() second];
}
}
};
......@@ -41,7 +42,7 @@ auto SecondListener::fromCppOpt(const CppOptType& cpp) -> ObjcType
if (!cpp) {
return nil;
}
return dynamic_cast<ObjcProxy&>(*cpp).Handle::get();
return dynamic_cast<ObjcProxy&>(*cpp).djinni_private_get_proxied_objc_object();
}
} // namespace djinni_generated
......@@ -42,14 +42,15 @@ namespace djinni_generated {
class UserToken::ObjcProxy final
: public ::testsuite::UserToken
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::UserToken;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
std::string whoami() override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() whoami];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() whoami];
return ::djinni::String::toCpp(objcpp_result_);
}
}
......@@ -76,7 +77,7 @@ auto UserToken::fromCppOpt(const CppOptType& cpp) -> ObjcType
return nil;
}
if (auto cppPtr = dynamic_cast<ObjcProxy*>(cpp.get())) {
return cppPtr->Handle::get();
return cppPtr->djinni_private_get_proxied_objc_object();
}
return ::djinni::get_cpp_proxy<DBUserTokenCppProxy>(cpp);
}
......
......@@ -62,33 +62,34 @@ namespace djinni_generated {
class UsesSingleLanguageListeners::ObjcProxy final
: public ::testsuite::UsesSingleLanguageListeners
, public ::djinni::ObjcProxyCache::Handle<ObjcType>
, private ::djinni::ObjcProxyBase<ObjcType>
{
friend class ::djinni_generated::UsesSingleLanguageListeners;
public:
using Handle::Handle;
using ObjcProxyBase::ObjcProxyBase;
void callForObjC(const std::shared_ptr<::testsuite::ObjcOnlyListener> & c_l) override
{
@autoreleasepool {
[Handle::get() callForObjC:(::djinni_generated::ObjcOnlyListener::fromCpp(c_l))];
[djinni_private_get_proxied_objc_object() callForObjC:(::djinni_generated::ObjcOnlyListener::fromCpp(c_l))];
}
}
std::shared_ptr<::testsuite::ObjcOnlyListener> returnForObjC() override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() returnForObjC];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() returnForObjC];
return ::djinni_generated::ObjcOnlyListener::toCpp(objcpp_result_);
}
}
void callForJava(const std::shared_ptr<::testsuite::JavaOnlyListener> & c_l) override
{
@autoreleasepool {
[Handle::get() callForJava:(::djinni_generated::JavaOnlyListener::fromCpp(c_l))];
[djinni_private_get_proxied_objc_object() callForJava:(::djinni_generated::JavaOnlyListener::fromCpp(c_l))];
}
}
std::shared_ptr<::testsuite::JavaOnlyListener> returnForJava() override
{
@autoreleasepool {
auto objcpp_result_ = [Handle::get() returnForJava];
auto objcpp_result_ = [djinni_private_get_proxied_objc_object() returnForJava];
return ::djinni_generated::JavaOnlyListener::toCpp(objcpp_result_);
}
}
......@@ -115,7 +116,7 @@ auto UsesSingleLanguageListeners::fromCppOpt(const CppOptType& cpp) -> ObjcType
return nil;
}
if (auto cppPtr = dynamic_cast<ObjcProxy*>(cpp.get())) {
return cppPtr->Handle::get();
return cppPtr->djinni_private_get_proxied_objc_object();
}
return ::djinni::get_cpp_proxy<DBUsesSingleLanguageListenersCppProxy>(cpp);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment