Commit b0e39230 authored by Jacob Potter's avatar Jacob Potter

Unify Java->C++, C++->Java, ObjC->Java, and Java->ObjC wrapper caches and handles.

parent 2895f8f2
...@@ -26,7 +26,7 @@ CJNIEXPORT void JNICALL Java_com_dropbox_textsort_SortItems_00024CppProxy_native ...@@ -26,7 +26,7 @@ CJNIEXPORT void JNICALL Java_com_dropbox_textsort_SortItems_00024CppProxy_native
{ {
try { try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::CppProxyHandle<::textsort::SortItems>::get(nativeRef); const auto& ref = ::djinni::objectFromHandleAddress<::textsort::SortItems>(nativeRef);
ref->sort(::djinni_generated::NativeSortOrder::toCpp(jniEnv, j_order), ref->sort(::djinni_generated::NativeSortOrder::toCpp(jniEnv, j_order),
::djinni_generated::NativeItemList::toCpp(jniEnv, j_items)); ::djinni_generated::NativeItemList::toCpp(jniEnv, j_items));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
......
...@@ -10,7 +10,7 @@ NativeTextboxListener::NativeTextboxListener() : ::djinni::JniInterface<::textso ...@@ -10,7 +10,7 @@ NativeTextboxListener::NativeTextboxListener() : ::djinni::JniInterface<::textso
NativeTextboxListener::~NativeTextboxListener() = default; NativeTextboxListener::~NativeTextboxListener() = default;
NativeTextboxListener::JavaProxy::JavaProxy(JniType j) : JavaProxyCacheEntry(j) { } NativeTextboxListener::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }
NativeTextboxListener::JavaProxy::~JavaProxy() = default; NativeTextboxListener::JavaProxy::~JavaProxy() = default;
...@@ -18,7 +18,7 @@ void NativeTextboxListener::JavaProxy::update(const ::textsort::ItemList & c_ite ...@@ -18,7 +18,7 @@ void NativeTextboxListener::JavaProxy::update(const ::textsort::ItemList & c_ite
auto jniEnv = ::djinni::jniGetThreadEnv(); auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10); ::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeTextboxListener>::get(); const auto& data = ::djinni::JniClass<::djinni_generated::NativeTextboxListener>::get();
jniEnv->CallVoidMethod(getGlobalRef(), data.method_update, jniEnv->CallVoidMethod(Handle::get().get(), data.method_update,
::djinni::get(::djinni_generated::NativeItemList::fromCpp(jniEnv, c_items))); ::djinni::get(::djinni_generated::NativeItemList::fromCpp(jniEnv, c_items)));
::djinni::jniExceptionCheck(jniEnv); ::djinni::jniExceptionCheck(jniEnv);
} }
......
...@@ -34,9 +34,7 @@ private: ...@@ -34,9 +34,7 @@ private:
void update(const ::textsort::ItemList & items) override; void update(const ::textsort::ItemList & items) override;
private: private:
using ::djinni::JavaProxyCacheEntry::getGlobalRef;
friend ::djinni::JniInterface<::textsort::TextboxListener, ::djinni_generated::NativeTextboxListener>; friend ::djinni::JniInterface<::textsort::TextboxListener, ::djinni_generated::NativeTextboxListener>;
friend ::djinni::JavaProxyCache<JavaProxy>;
}; };
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/textsort/TextboxListener") }; const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/textsort/TextboxListener") };
......
...@@ -21,7 +21,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -21,7 +21,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation TXSSortItems { @implementation TXSSortItems {
::djinni::DbxCppWrapperCache<::textsort::SortItems>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::textsort::SortItems>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::textsort::SortItems>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::textsort::SortItems>&)cppRef
...@@ -62,9 +62,7 @@ auto SortItems::fromCpp(const CppType& cpp) -> ObjcType ...@@ -62,9 +62,7 @@ auto SortItems::fromCpp(const CppType& cpp) -> ObjcType
if (!cpp) { if (!cpp) {
return nil; return nil;
} }
return ::djinni::DbxCppWrapperCache<::textsort::SortItems>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<TXSSortItems>(cpp);
return [[TXSSortItems alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -12,14 +12,14 @@ namespace djinni_generated { ...@@ -12,14 +12,14 @@ namespace djinni_generated {
class TextboxListener::ObjcProxy final class TextboxListener::ObjcProxy final
: public ::textsort::TextboxListener : public ::textsort::TextboxListener
, public ::djinni::DbxObjcWrapperCache<ObjcProxy>::Handle , public ::djinni::ObjcProxyCache::Handle<ObjcType>
{ {
public: public:
using Handle::Handle; using Handle::Handle;
void update(const ::textsort::ItemList & c_items) override void update(const ::textsort::ItemList & c_items) override
{ {
@autoreleasepool { @autoreleasepool {
[(ObjcType)Handle::get() update:(::djinni_generated::ItemList::fromCpp(c_items))]; [Handle::get() update:(::djinni_generated::ItemList::fromCpp(c_items))];
} }
} }
}; };
...@@ -33,7 +33,7 @@ auto TextboxListener::toCpp(ObjcType objc) -> CppType ...@@ -33,7 +33,7 @@ auto TextboxListener::toCpp(ObjcType objc) -> CppType
if (!objc) { if (!objc) {
return nullptr; return nullptr;
} }
return ::djinni::DbxObjcWrapperCache<ObjcProxy>::getInstance()->get(objc); return ::djinni::get_objc_proxy<ObjcProxy>(objc);
} }
auto TextboxListener::fromCpp(const CppType& cpp) -> ObjcType auto TextboxListener::fromCpp(const CppType& cpp) -> ObjcType
......
...@@ -214,9 +214,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { ...@@ -214,9 +214,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
} }
w.wl w.wl
w.wlOutdent(s"private:") w.wlOutdent(s"private:")
w.wl(s"using ::djinni::JavaProxyCacheEntry::getGlobalRef;")
w.wl(s"friend ::djinni::JniInterface<$cppSelf, ${withNs(Some(spec.jniNamespace), jniSelf)}>;") w.wl(s"friend ::djinni::JniInterface<$cppSelf, ${withNs(Some(spec.jniNamespace), jniSelf)}>;")
w.wl(s"friend ::djinni::JavaProxyCache<JavaProxy>;")
} }
w.wl w.wl
w.wl(s"const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass(${q(classLookup)}) };") w.wl(s"const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass(${q(classLookup)}) };")
...@@ -241,7 +239,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { ...@@ -241,7 +239,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
w.wl w.wl
if (i.ext.java) { if (i.ext.java) {
writeJniTypeParams(w, typeParams) writeJniTypeParams(w, typeParams)
w.wl(s"$jniSelfWithParams::JavaProxy::JavaProxy(JniType j) : JavaProxyCacheEntry(j) { }") w.wl(s"$jniSelfWithParams::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }")
w.wl w.wl
writeJniTypeParams(w, typeParams) writeJniTypeParams(w, typeParams)
w.wl(s"$jniSelfWithParams::JavaProxy::~JavaProxy() = default;") w.wl(s"$jniSelfWithParams::JavaProxy::~JavaProxy() = default;")
...@@ -258,7 +256,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { ...@@ -258,7 +256,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
val call = m.ret.fold("jniEnv->CallVoidMethod(")(r => "auto jret = " + toJniCall(r, (jt: String) => s"jniEnv->Call${jt}Method(")) val call = m.ret.fold("jniEnv->CallVoidMethod(")(r => "auto jret = " + toJniCall(r, (jt: String) => s"jniEnv->Call${jt}Method("))
w.w(call) w.w(call)
val javaMethodName = idJava.method(m.ident) val javaMethodName = idJava.method(m.ident)
w.w(s"getGlobalRef(), data.method_$javaMethodName") w.w(s"Handle::get().get(), data.method_$javaMethodName")
if(m.params.nonEmpty){ if(m.params.nonEmpty){
w.wl(",") w.wl(",")
writeAlignedCall(w, " " * call.length(), m.params, ")", p => { writeAlignedCall(w, " " * call.length(), m.params, ")", p => {
...@@ -321,7 +319,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) { ...@@ -321,7 +319,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
val nativeAddon = if (m.static) "" else "native_" val nativeAddon = if (m.static) "" else "native_"
nativeHook(nativeAddon + idJava.method(m.ident), m.static, m.params, m.ret, { nativeHook(nativeAddon + idJava.method(m.ident), m.static, m.params, m.ret, {
//w.wl(s"::${spec.jniNamespace}::JniLocalScope jscope(jniEnv, 10);") //w.wl(s"::${spec.jniNamespace}::JniLocalScope jscope(jniEnv, 10);")
if (!m.static) w.wl(s"const auto& ref = ::djinni::CppProxyHandle<$cppSelf>::get(nativeRef);") if (!m.static) w.wl(s"const auto& ref = ::djinni::objectFromHandleAddress<$cppSelf>(nativeRef);")
m.params.foreach(p => { m.params.foreach(p => {
if (isInterface(p.ty.resolved) && spec.cppNnCheckExpression.nonEmpty) { if (isInterface(p.ty.resolved) && spec.cppNnCheckExpression.nonEmpty) {
// We have a non-optional interface in nn mode, assert that we're getting a non-null value // We have a non-optional interface in nn mode, assert that we're getting a non-null value
......
...@@ -134,7 +134,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) { ...@@ -134,7 +134,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) {
w.wl("@end") w.wl("@end")
w.wl w.wl
w.wl(s"@implementation $objcSelf {") w.wl(s"@implementation $objcSelf {")
w.wl(s" ::djinni::DbxCppWrapperCache<$cppSelf>::Handle _cppRefHandle;") w.wl(s" ::djinni::CppProxyCache::Handle<std::shared_ptr<$cppSelf>> _cppRefHandle;")
w.wl("}") w.wl("}")
w.wl w.wl
w.wl(s"- (id)initWithCpp:(const std::shared_ptr<$cppSelf>&)cppRef") w.wl(s"- (id)initWithCpp:(const std::shared_ptr<$cppSelf>&)cppRef")
...@@ -185,7 +185,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) { ...@@ -185,7 +185,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) {
wrapNamespace(w, spec.objcppNamespace, w => { wrapNamespace(w, spec.objcppNamespace, w => {
w.wl(s"class $helperClass::ObjcProxy final") w.wl(s"class $helperClass::ObjcProxy final")
w.wl(s": public $cppSelf") w.wl(s": public $cppSelf")
w.wl(s", public ::djinni::DbxObjcWrapperCache<ObjcProxy>::Handle") // Use base class to avoid name conflicts with user-defined methods having the same name as this new data member 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.bracedSemi { w.bracedSemi {
w.wlOutdent("public:") w.wlOutdent("public:")
w.wl("using Handle::Handle;") w.wl("using Handle::Handle;")
...@@ -195,7 +195,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) { ...@@ -195,7 +195,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) {
w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")} override").braced { w.wl(s"$ret ${idCpp.method(m.ident)}${params.mkString("(", ", ", ")")} override").braced {
w.w("@autoreleasepool").braced { w.w("@autoreleasepool").braced {
val ret = m.ret.fold("")(_ => "auto r = ") val ret = m.ret.fold("")(_ => "auto r = ")
val call = s"[(ObjcType)Handle::get() ${idObjc.method(m.ident)}" val call = s"[Handle::get() ${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))})")) writeAlignedObjcCall(w, ret + call, m.params, "]", p => (idObjc.field(p.ident), s"(${objcppMarshal.fromCpp(p.ty, "c_" + idCpp.local(p.ident))})"))
w.wl(";") w.wl(";")
m.ret.fold()(ty => (spec.cppNnCheckExpression, isInterface(ty.resolved)) match { m.ret.fold()(ty => (spec.cppNnCheckExpression, isInterface(ty.resolved)) match {
...@@ -242,7 +242,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) { ...@@ -242,7 +242,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) {
w.wl(s"return (($objcSelf*)objc)->_cppRefHandle.get();") w.wl(s"return (($objcSelf*)objc)->_cppRefHandle.get();")
} }
} }
w.wl(s"return ::djinni::DbxObjcWrapperCache<$objcExtSelf>::getInstance()->get(objc);") w.wl(s"return ::djinni::get_objc_proxy<$objcExtSelf>(objc);")
} }
} }
w.wl w.wl
...@@ -265,9 +265,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) { ...@@ -265,9 +265,7 @@ class ObjcppGenerator(spec: Spec) extends Generator(spec) {
w.wl("return cppPtr->Handle::get();") w.wl("return cppPtr->Handle::get();")
} }
} }
w.w(s"return ::djinni::DbxCppWrapperCache<$cppSelf>::getInstance()->get(cpp, [] (const CppType& p)").bracedEnd(");") { w.wl(s"return ::djinni::get_cpp_proxy<$objcSelf>(cpp);")
w.wl(s"return [[$objcSelf alloc] initWithCpp:p];")
}
} }
} }
}) })
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
// //
#include "djinni_support.hpp" #include "djinni_support.hpp"
#include "../proxy_cache_impl.hpp"
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
...@@ -464,48 +465,7 @@ void jniDefaultSetPendingFromCurrent(JNIEnv * env, const char * /*ctx*/) noexcep ...@@ -464,48 +465,7 @@ void jniDefaultSetPendingFromCurrent(JNIEnv * env, const char * /*ctx*/) noexcep
// exceptions which aren't std::exception subclasses). // exceptions which aren't std::exception subclasses).
} }
struct JavaProxyCacheState { template class ProxyCache<JavaProxyCacheTraits>;
std::mutex mtx;
std::unordered_map<jobject, std::weak_ptr<void>, JavaIdentityHash, JavaIdentityEquals> m;
int counter = 0;
static JavaProxyCacheState & get() {
static JavaProxyCacheState st;
return st;
}
};
JavaProxyCacheEntry::JavaProxyCacheEntry(jobject localRef, JNIEnv * env)
: m_globalRef(env, localRef) {
DJINNI_ASSERT(m_globalRef, env);
}
JavaProxyCacheEntry::JavaProxyCacheEntry(jobject localRef)
: JavaProxyCacheEntry(localRef, jniGetThreadEnv()) {}
JavaProxyCacheEntry::~JavaProxyCacheEntry() noexcept {
JavaProxyCacheState & st = JavaProxyCacheState::get();
const std::lock_guard<std::mutex> lock(st.mtx);
st.m.erase(m_globalRef.get());
}
std::shared_ptr<void> javaProxyCacheLookup(jobject obj, std::pair<std::shared_ptr<void>, jobject>(*factory)(jobject)) {
JavaProxyCacheState & st = JavaProxyCacheState::get();
const std::lock_guard<std::mutex> lock(st.mtx);
const auto it = st.m.find(obj);
if (it != st.m.end()) {
std::shared_ptr<void> ptr = it->second.lock();
if (ptr) {
return ptr;
}
}
// Otherwise, construct a new T, save it, and return it.
std::pair<std::shared_ptr<void>, jobject> ret = factory(obj);
st.m[ret.second] = ret.first;
return ret.first;
}
CppProxyClassInfo::CppProxyClassInfo(const char * className) CppProxyClassInfo::CppProxyClassInfo(const char * className)
: clazz(jniFindClass(className)), : clazz(jniFindClass(className)),
...@@ -544,11 +504,13 @@ private: ...@@ -544,11 +504,13 @@ private:
public: public:
// Constructor // Constructor
JavaWeakRef(jobject obj) : JavaWeakRef(jniGetThreadEnv(), obj) {}
JavaWeakRef(JNIEnv * jniEnv, jobject obj) : m_weakRef(jniEnv, create(jniEnv, obj)) {} JavaWeakRef(JNIEnv * jniEnv, jobject obj) : m_weakRef(jniEnv, create(jniEnv, obj)) {}
// Get the object pointed to if it's still strongly reachable or, return null if not. // Get the object pointed to if it's still strongly reachable or, return null if not.
// (Analogous to weak_ptr::lock.) Returns a local reference. // (Analogous to weak_ptr::lock.) Returns a local reference.
jobject get(JNIEnv * jniEnv) { jobject lock() const {
const auto & jniEnv = jniGetThreadEnv();
const JniInfo & weakRefClass = JniClass<JniInfo>::get(); const JniInfo & weakRefClass = JniClass<JniInfo>::get();
jobject javaObj = jniEnv->CallObjectMethod(m_weakRef.get(), weakRefClass.method_get); jobject javaObj = jniEnv->CallObjectMethod(m_weakRef.get(), weakRefClass.method_get);
jniExceptionCheck(jniEnv); jniExceptionCheck(jniEnv);
...@@ -559,47 +521,6 @@ private: ...@@ -559,47 +521,6 @@ private:
GlobalRef<jobject> m_weakRef; GlobalRef<jobject> m_weakRef;
}; };
struct CppProxyCacheState { template class ProxyCache<JniCppProxyCacheTraits>;
std::mutex mtx;
std::unordered_map<void *, JavaWeakRef> m;
static CppProxyCacheState & get() {
static CppProxyCacheState st;
return st;
}
};
/*static*/ void JniCppProxyCache::erase(void * key) {
CppProxyCacheState & st = CppProxyCacheState::get();
const std::lock_guard<std::mutex> lock(st.mtx);
st.m.erase(key);
}
/*static*/ jobject JniCppProxyCache::get(const std::shared_ptr<void> & cppObj,
JNIEnv * jniEnv,
const CppProxyClassInfo & proxyClass,
jobject (*factory)(const std::shared_ptr<void> &,
JNIEnv *,
const CppProxyClassInfo &)) {
CppProxyCacheState & st = CppProxyCacheState::get();
const std::lock_guard<std::mutex> lock(st.mtx);
auto it = st.m.find(cppObj.get());
if (it != st.m.end()) {
// It's in the map. See if the WeakReference still points to an object.
if (jobject javaObj = it->second.get(jniEnv)) {
return javaObj;
} else {
// The WeakReference is expired, so prune it from the map eagerly.
st.m.erase(it);
}
}
jobject wrapper = factory(cppObj, jniEnv, proxyClass);
/* Make a Java WeakRef object */
st.m.emplace(cppObj.get(), JavaWeakRef(jniEnv, wrapper));
return wrapper;
}
} // namespace djinni } // namespace djinni
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include "../proxy_cache_interface.hpp"
#include <jni.h> #include <jni.h>
// work-around for missing noexcept and constexpr support in MSVC prior to 2015 // work-around for missing noexcept and constexpr support in MSVC prior to 2015
...@@ -268,10 +269,10 @@ jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig); ...@@ -268,10 +269,10 @@ jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig);
* *
* This is used for automatically wrapping a Java object that exposes some interface * This is used for automatically wrapping a Java object that exposes some interface
* with a C++ object that calls back into the JVM, such as a listener. Calling * with a C++ object that calls back into the JVM, such as a listener. Calling
* JavaProxyCache<T>::get(jobj, ...) the first time will construct a T and return a * get_java_proxy<T>(obj) the first time will construct a T and return a shared_ptr to it, and
* shared_ptr to it, and also save a weak_ptr to the new object internally. The constructed * also save a weak_ptr to the new object internally. The constructed T contains a strong
* T contains a strong GlobalRef to jobj. As long as something in C++ maintains a strong * GlobalRef to jobj. As long as something in C++ maintains a strong reference to the wrapper,
* reference to the wrapper, future calls to get(jobj) will return the *same* wrapper object. * future calls to get(jobj) will return the *same* wrapper object.
* *
* Java | C++ * Java | C++
* | ________________________ ___________ * | ________________________ ___________
...@@ -283,8 +284,7 @@ jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig); ...@@ -283,8 +284,7 @@ jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig);
* | ^ ______________________ * | ^ ______________________
* | \ | | * | \ | |
* | - - - - - - | JavaProxyCache | * | - - - - - - | JavaProxyCache |
* | weak_ptr | <JniImplFooListener> | * | weak_ptr |______________________|
* | |______________________|
* *
* As long as the C++ FooListener has references, the Java FooListener is kept alive. * As long as the C++ FooListener has references, the Java FooListener is kept alive.
* *
...@@ -293,60 +293,21 @@ jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig); ...@@ -293,60 +293,21 @@ jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig);
* we must have some other GlobalRef keeping it alive. To ensure safety, the Entry * we must have some other GlobalRef keeping it alive. To ensure safety, the Entry
* destructor removes *itself* from the map - destruction order guarantees that this * destructor removes *itself* from the map - destruction order guarantees that this
* will happen before the contained global reference becomes invalid (by destruction of * will happen before the contained global reference becomes invalid (by destruction of
* the GlobalRefGuard). * the GlobalRef).
*/ */
struct JavaIdentityHash;
/* struct JavaIdentityEquals;
* Look up an entry in the global JNI wrapper cache. If none is found, create one with factory, struct JavaProxyCacheTraits {
* save it, and return it. using UnowningImplPointer = jobject;
* using OwningImplPointer = jobject;
* The contract of `factory` is: The parameter to factory is a local ref. The factory returns using OwningProxyPointer = std::shared_ptr<void>;
* a shared_ptr to the object (JniImplFooListener, in the diagram above), as well as the using WeakProxyPointer = std::weak_ptr<void>;
* jobject *global* ref contained inside. using UnowningImplPointerHash = JavaIdentityHash;
*/ using UnowningImplPointerEqual = JavaIdentityEquals;
std::shared_ptr<void> javaProxyCacheLookup(jobject obj, std::pair<std::shared_ptr<void>,
jobject>(*factory)(jobject));
class JavaProxyCacheEntry {
public:
jobject getGlobalRef() {
return m_globalRef.get();
}
protected:
JavaProxyCacheEntry(jobject localRef, JNIEnv * env); // env used only for construction
JavaProxyCacheEntry(jobject localRef);
virtual ~JavaProxyCacheEntry() noexcept;
JavaProxyCacheEntry(const JavaProxyCacheEntry & other) = delete;
JavaProxyCacheEntry & operator=(const JavaProxyCacheEntry & other) = delete;
private:
const GlobalRef<jobject> m_globalRef;
};
template <class T>
class JavaProxyCache {
public:
using Entry = JavaProxyCacheEntry;
static std::pair<std::shared_ptr<void>, jobject> factory(jobject obj) {
std::shared_ptr<T> ret = std::make_shared<T>(obj);
return { ret, ret->getGlobalRef() };
}
/*
* Check whether a wrapped T exists for obj. If one is found, return it; if not,
* construct a new one with obj, save it, and return it.
*/
static std::shared_ptr<T> get(jobject obj) {
static_assert(std::is_base_of<JavaProxyCacheEntry, T>::value,
"JavaProxyCache can only be used with T if T derives from Entry<T>");
return std::static_pointer_cast<T>(javaProxyCacheLookup(obj, &factory));
}
}; };
extern template class ProxyCache<JavaProxyCacheTraits>;
using JavaProxyCache = ProxyCache<JavaProxyCacheTraits>;
using JavaProxyCacheEntry = JavaProxyCache::Handle<GlobalRef<jobject>>;
/* /*
* Cache for CppProxy objects. This is the inverse of the JavaProxyCache mechanism above, * Cache for CppProxy objects. This is the inverse of the JavaProxyCache mechanism above,
...@@ -371,6 +332,27 @@ public: ...@@ -371,6 +332,27 @@ public:
* WeakGlobalRef can still be upgraded to a strong reference even during finalization, which * WeakGlobalRef can still be upgraded to a strong reference even during finalization, which
* leads to use-after-free. Java WeakRefs provide the right lifetime guarantee. * leads to use-after-free. Java WeakRefs provide the right lifetime guarantee.
*/ */
class JavaWeakRef;
struct JniCppProxyCacheTraits {
using UnowningImplPointer = void *;
using OwningImplPointer = std::shared_ptr<void>;
using OwningProxyPointer = jobject;
using WeakProxyPointer = JavaWeakRef;
using UnowningImplPointerHash = std::hash<void *>;
using UnowningImplPointerEqual = std::equal_to<void *>;
};
extern template class ProxyCache<JniCppProxyCacheTraits>;
using JniCppProxyCache = ProxyCache<JniCppProxyCacheTraits>;
template <class T> using CppProxyHandle = JniCppProxyCache::Handle<std::shared_ptr<T>>;
template <class T>
static const std::shared_ptr<T> & objectFromHandleAddress(jlong handle) {
assert(handle);
assert(handle > 4096);
const auto & ret = reinterpret_cast<const CppProxyHandle<T> *>(handle)->get();
assert(ret);
return ret;
}
/* /*
* Information needed to use a CppProxy class. * Information needed to use a CppProxy class.
...@@ -392,54 +374,6 @@ struct CppProxyClassInfo { ...@@ -392,54 +374,6 @@ struct CppProxyClassInfo {
explicit operator bool() const { return bool(clazz); } explicit operator bool() const { return bool(clazz); }
}; };
/*
* Proxy cache implementation. These functions are used by CppProxyHandle::~CppProxyHandle()
* and JniInterface::_toJava, respectively. They're declared in a separate class to avoid
* having to templatize them. This way, all the map lookup code is only generated once,
* rather than once for each T, saving substantially on binary size. (We do something simiar
* in the other direction too; see javaProxyCacheLookup() above.)
*
* The data used by this class is declared only in djinni_support.cpp, since it's global and
* opaque to all other code.
*/
class JniCppProxyCache {
private:
template <class T> friend class CppProxyHandle;
static void erase(void * key);
template <class I, class Self> friend class JniInterface;
static jobject get(const std::shared_ptr<void> & cppObj,
JNIEnv * jniEnv,
const CppProxyClassInfo & proxyClass,
jobject (*factory)(const std::shared_ptr<void> &,
JNIEnv *,
const CppProxyClassInfo &));
/* This "class" is basically a namespace, to make clear that get() and erase() should only
* be used by the helper infrastructure below. */
JniCppProxyCache() = delete;
};
template <class T>
class CppProxyHandle {
public:
CppProxyHandle(std::shared_ptr<T> obj) : m_obj(move(obj)) {}
~CppProxyHandle() {
JniCppProxyCache::erase(m_obj.get());
}
static const std::shared_ptr<T> & get(jlong handle) {
assert(handle);
assert(handle > 4096);
const auto & ret = reinterpret_cast<const CppProxyHandle<T> *>(handle)->m_obj;
assert(ret);
return ret;
}
private:
const std::shared_ptr<T> m_obj;
};
/* /*
* Base class for Java <-> C++ interface adapters. * Base class for Java <-> C++ interface adapters.
* *
...@@ -471,7 +405,8 @@ public: ...@@ -471,7 +405,8 @@ public:
// Cases 3 and 4. // Cases 3 and 4.
assert(m_cppProxyClass); assert(m_cppProxyClass);
return JniCppProxyCache::get(c, jniEnv, m_cppProxyClass, &newCppProxy); return JniCppProxyCache::get(c, &newCppProxy);
} }
/* /*
...@@ -493,7 +428,7 @@ public: ...@@ -493,7 +428,7 @@ public:
&& jniEnv->IsSameObject(jniEnv->GetObjectClass(j), m_cppProxyClass.clazz.get())) { && jniEnv->IsSameObject(jniEnv->GetObjectClass(j), m_cppProxyClass.clazz.get())) {
jlong handle = jniEnv->GetLongField(j, m_cppProxyClass.idField); jlong handle = jniEnv->GetLongField(j, m_cppProxyClass.idField);
jniExceptionCheck(jniEnv); jniExceptionCheck(jniEnv);
return CppProxyHandle<I>::get(handle); return objectFromHandleAddress<I>(handle);
} }
// Cases 3 and 4 - see _getJavaProxy helper below. JavaProxyCache is responsible for // Cases 3 and 4 - see _getJavaProxy helper below. JavaProxyCache is responsible for
...@@ -515,7 +450,7 @@ private: ...@@ -515,7 +450,7 @@ private:
template <typename S, typename = typename S::JavaProxy> template <typename S, typename = typename S::JavaProxy>
jobject _unwrapJavaProxy(const std::shared_ptr<I> * c) const { jobject _unwrapJavaProxy(const std::shared_ptr<I> * c) const {
if (auto proxy = dynamic_cast<typename S::JavaProxy *>(c->get())) { if (auto proxy = dynamic_cast<typename S::JavaProxy *>(c->get())) {
return proxy->getGlobalRef(); return proxy->JavaProxyCacheEntry::get().get();
} else { } else {
return nullptr; return nullptr;
} }
...@@ -531,18 +466,18 @@ private: ...@@ -531,18 +466,18 @@ private:
* it. This is actually called by jniCppProxyCacheGet, which holds a lock on the global * it. This is actually called by jniCppProxyCacheGet, which holds a lock on the global
* C++-to-Java proxy map object. * C++-to-Java proxy map object.
*/ */
static jobject newCppProxy(const std::shared_ptr<void> & cppObj, static std::pair<jobject, void*> newCppProxy(const std::shared_ptr<void> & cppObj) {
JNIEnv * jniEnv, const auto & data = JniClass<Self>::get();
const CppProxyClassInfo & proxyClass) { const auto & jniEnv = jniGetThreadEnv();
std::unique_ptr<CppProxyHandle<I>> to_encapsulate( std::unique_ptr<CppProxyHandle<I>> to_encapsulate(
new CppProxyHandle<I>(std::static_pointer_cast<I>(cppObj))); new CppProxyHandle<I>(std::static_pointer_cast<I>(cppObj)));
jlong handle = static_cast<jlong>(reinterpret_cast<uintptr_t>(to_encapsulate.get())); jlong handle = static_cast<jlong>(reinterpret_cast<uintptr_t>(to_encapsulate.get()));
jobject cppProxy = jniEnv->NewObject(proxyClass.clazz.get(), jobject cppProxy = jniEnv->NewObject(data.m_cppProxyClass.clazz.get(),
proxyClass.constructor, data.m_cppProxyClass.constructor,
handle); handle);
jniExceptionCheck(jniEnv); jniExceptionCheck(jniEnv);
to_encapsulate.release(); to_encapsulate.release();
return cppProxy; return { cppProxy, cppObj.get() };
} }
/* /*
...@@ -551,7 +486,16 @@ private: ...@@ -551,7 +486,16 @@ private:
*/ */
template <typename S, typename = typename S::JavaProxy> template <typename S, typename = typename S::JavaProxy>
std::shared_ptr<I> _getJavaProxy(jobject j) const { std::shared_ptr<I> _getJavaProxy(jobject j) const {
return JavaProxyCache<typename S::JavaProxy>::get(j); static_assert(std::is_base_of<JavaProxyCacheEntry, typename S::JavaProxy>::value,
"JavaProxy must derive from JavaProxyCacheEntry");
return std::static_pointer_cast<typename S::JavaProxy>(JavaProxyCache::get(
j,
[] (const jobject & obj) -> std::pair<std::shared_ptr<void>, jobject> {
auto ret = std::make_shared<typename S::JavaProxy>(obj);
return { ret, ret->JavaProxyCacheEntry::get().get() };
}
));
} }
template <typename S> template <typename S>
......
...@@ -18,71 +18,36 @@ ...@@ -18,71 +18,36 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <memory> #include <memory>
#include <mutex>
#include <unordered_map>
namespace djinni { #include "../proxy_cache_interface.hpp"
template <class T>
class DbxCppWrapperCache {
public:
static const std::shared_ptr<DbxCppWrapperCache> & getInstance() {
static const std::shared_ptr<DbxCppWrapperCache> instance(new DbxCppWrapperCache);
// Return by const-ref. This is safe to call any time except during static destruction.
// Returning by reference lets us avoid touching the refcount unless needed.
return instance;
}
template <typename AllocFunc> namespace djinni {
id get(const std::shared_ptr<T> & cppRef, const AllocFunc & alloc) {
std::unique_lock<std::mutex> lock(m_mutex);
T* ptr = cppRef.get();
auto got = m_mapping.find(ptr);
id ret;
if (got != m_mapping.end()) {
ret = got->second;
if (ret == nil) {
ret = alloc(cppRef);
m_mapping[ptr] = ret;
}
} else {
ret = alloc(cppRef);
m_mapping[ptr] = ret;
}
return ret;
}
void remove(const std::shared_ptr<T> & cppRef) {
std::unique_lock<std::mutex> lock(m_mutex);
T* ptr = cppRef.get();
if (m_mapping[ptr] == nil) {
m_mapping.erase(ptr);
}
}
class Handle { struct CppProxyCacheTraits {
public: using UnowningImplPointer = void *;
Handle() = default; using OwningImplPointer = std::shared_ptr<void>;
Handle(const Handle &) = delete; using OwningProxyPointer = __strong id;
Handle & operator=(const Handle &) = delete; using WeakProxyPointer = __weak id;
~Handle() { using UnowningImplPointerHash = std::hash<void *>;
if (_ptr) { using UnowningImplPointerEqual = std::equal_to<void *>;
_cache->remove(_ptr); };
}
}
void assign(const std::shared_ptr<T>& ptr) { _ptr = ptr; }
const std::shared_ptr<T>& get() const noexcept { return _ptr; }
private: // This declares that GenericProxyCache will be instantiated separately. The actual
const std::shared_ptr<DbxCppWrapperCache> _cache = getInstance(); // explicit instantiations are in DJIProxyCaches.mm.
std::shared_ptr<T> _ptr; extern template class ProxyCache<CppProxyCacheTraits>;
using CppProxyCache = ProxyCache<CppProxyCacheTraits>;
template <typename ObjcType, typename CppType>
ObjcType * get_cpp_proxy(const std::shared_ptr<CppType> & cppRef) {
return CppProxyCache::get(
cppRef,
[] (const std::shared_ptr<void> & cppRef) -> std::pair<id, void *> {
return {
[[ObjcType alloc] initWithCpp:std::static_pointer_cast<CppType>(cppRef)],
cppRef.get()
}; };
}
private: );
std::unordered_map<T*, __weak id> m_mapping; }
std::mutex m_mutex;
DbxCppWrapperCache() {}
};
} // namespace djinni } // namespace djinni
...@@ -18,70 +18,38 @@ ...@@ -18,70 +18,38 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <memory> #include <memory>
#include <mutex>
#include <unordered_map>
namespace djinni { #include "../proxy_cache_interface.hpp"
template <class T>
class DbxObjcWrapperCache {
public:
static const std::shared_ptr<DbxObjcWrapperCache> & getInstance() {
static const std::shared_ptr<DbxObjcWrapperCache> instance(new DbxObjcWrapperCache);
// Return by const-ref. This is safe to call any time except during static destruction.
// Returning by reference lets us avoid touching the refcount unless needed.
return instance;
}
std::shared_ptr<T> get(id objcRef) { namespace djinni {
std::unique_lock<std::mutex> lock(m_mutex);
std::shared_ptr<T> ret;
auto it = m_mapping.find((__bridge void*)objcRef);
if (it != m_mapping.end()) {
ret = std::static_pointer_cast<T>(it->second.lock());
if (ret == nullptr) {
ret = new_wrapper(objcRef);
}
} else {
ret = new_wrapper(objcRef);
}
return ret;
}
void remove(id objcRef) { struct unretained_id_hash { std::size_t operator()(__unsafe_unretained id ptr) const; };
std::unique_lock<std::mutex> lock(m_mutex);
m_mapping.erase((__bridge void*)objcRef);
}
class Handle { struct ObjcProxyCacheTraits {
public: using UnowningImplPointer = __unsafe_unretained id;
Handle(id obj) : _obj(obj) { } using OwningImplPointer = __strong id;
Handle(const Handle &) = delete; using OwningProxyPointer = std::shared_ptr<void>;
Handle & operator=(const Handle &) = delete; using WeakProxyPointer = std::weak_ptr<void>;
~Handle() { using UnowningImplPointerHash = unretained_id_hash;
if (_obj) { using UnowningImplPointerEqual = std::equal_to<__unsafe_unretained id>;
_cache->remove(_obj); };
}
}
id get() const noexcept { return _obj; }
private: // This declares that GenericProxyCache will be instantiated separately. The actual
const std::shared_ptr<DbxObjcWrapperCache> _cache = getInstance(); // explicit instantiations are in DJIProxyCaches.mm.
const id _obj; extern template class ProxyCache<ObjcProxyCacheTraits>;
using ObjcProxyCache = ProxyCache<ObjcProxyCacheTraits>;
template <typename CppType, typename ObjcType>
static std::shared_ptr<CppType> get_objc_proxy(ObjcType * objcRef) {
return std::static_pointer_cast<CppType>(ObjcProxyCache::get(
objcRef,
[] (const __strong id & objcRef) -> std::pair<std::shared_ptr<void>, __unsafe_unretained id> {
return {
std::make_shared<CppType>(objcRef),
objcRef
}; };
private:
std::unordered_map<void*, std::weak_ptr<void>> m_mapping;
std::mutex m_mutex;
std::shared_ptr<T> new_wrapper(id objcRef) {
std::shared_ptr<T> ret = std::make_shared<T>(objcRef);
std::weak_ptr<void> ptr(std::static_pointer_cast<void>(ret));
m_mapping[(__bridge void*)objcRef] = ptr;
return ret;
} }
));
}; }
} // namespace djinni } // namespace djinni
// //
// Copyright 2014 Dropbox, Inc. // Copyright 2015 Dropbox, Inc.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
...@@ -14,16 +14,19 @@ ...@@ -14,16 +14,19 @@
// limitations under the License. // limitations under the License.
// //
#import "DJIWeakPtrWrapper+Private.h"
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file"); static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@implementation DBWeakPtrWrapper #include "../proxy_cache_impl.hpp"
#include "DJIObjcWrapperCache+Private.h"
#include "DJICppWrapperCache+Private.h"
- (id)initWithWeakPtr:(std::weak_ptr<void>)ptr { namespace djinni {
if (self = [super init]) {
_ptr = ptr; std::size_t unretained_id_hash::operator()(__unsafe_unretained id ptr) const {
} return std::hash<void*>()((__bridge void*)ptr);
return self;
} }
@end template class ProxyCache<ObjcProxyCacheTraits>;
template class ProxyCache<CppProxyCacheTraits>;
} // namespace djinni
//
// Copyright 2014 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
#include <memory>
/*
* Helper class for DbxObjcWrapperCache to store C++ weak_ptr in Objective-C NSMapTable.
*/
@interface DBWeakPtrWrapper : NSObject
@property (nonatomic, readonly) std::weak_ptr<void> ptr;
- (id)initWithWeakPtr:(std::weak_ptr<void>)ptr;
@end
//
// Copyright 2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#pragma once
#include "proxy_cache_interface.hpp"
#include <functional>
#include <mutex>
#include <unordered_map>
// """
// This place is not a place of honor.
// No highly esteemed deed is commemorated here.
// Nothing valued is here.
// This place is a message and part of a system of messages.
// Pay attention to it!
// Sending this message was important to us.
// We considered ourselves to be a powerful culture.
// """
//
// From "Expert Judgment on Markers to Deter Inadvertent Human Intrusion into the Waste
// Isolation Pilot Plant", Sandia National Laboratories report SAND92-1382 / UC-721, p. F-49
namespace djinni {
// See comment on `get_unowning()` in proxy_cache_interface.hpp.
template <typename T> static inline auto upgrade_weak(const T & ptr) { return ptr.lock(); }
template <typename T> static inline T * upgrade_weak(T* ptr) { return ptr; }
/*
* Generic proxy cache.
*
* This provides a general-purpose mechanism for proxies to be re-used. When we pass an object
* across the language boundary from A to B, we must create a proxy object within language B
* that passes calls back to language A. For example, if have a C++ object that is passed into
* Java, we would create a Java object that owns a `shared_ptr` and has a set of native methods
* that call in to C++.
*
* When we create such an object, we also want to cache a weak reference to it, so that if we
* later pass the *same* object across the boundary, the same proxy will be returned. This is
* necessary for correctness in some situations: for example, in the case of an `add_listener`
* and `remove_listener` pattern.
*
* To reduce code size, only one GenericProxyCache need be instantiated for each language
* boundary direction. The pointer types passed to this function can be generic, e.g. `id`,
* `shared_ptr<void>`, `jobject`, etc.
*
* In the types below, "Impl" refers to some interface that is being wrapped, and Proxy refers
* to the generated other-language object that wraps it.
*/
template <typename Traits>
class ProxyCache<Traits>::Pimpl {
public:
/*
* Look up an object in the proxy cache, and create a new one if not found.
*
* This takes a function pointer, not an arbitrary functor, because we want to minimize
* code size: this function should only be instantiated *once* per langauge direction.
*/
OwningProxyPointer get(const OwningImplPointer & impl, AllocatorFunction * alloc) {
std::unique_lock<std::mutex> lock(m_mutex);
UnowningImplPointer ptr = get_unowning(impl);
auto existing_proxy_iter = m_mapping.find(ptr);
if (existing_proxy_iter != m_mapping.end()) {
OwningProxyPointer existing_proxy = upgrade_weak(existing_proxy_iter->second);
if (existing_proxy) {
return existing_proxy;
} else {
// The weak reference is expired, so prune it from the map eagerly.
m_mapping.erase(existing_proxy_iter);
}
}
auto alloc_result = alloc(impl);
m_mapping.emplace(alloc_result.second, alloc_result.first);
return alloc_result.first;
}
/*
* Erase an object from the proxy cache.
*/
void remove(const UnowningImplPointer & impl_unowning) {
std::unique_lock<std::mutex> lock(m_mutex);
m_mapping.erase(impl_unowning);
}
private:
std::unordered_map<UnowningImplPointer,
WeakProxyPointer,
UnowningImplPointerHash,
UnowningImplPointerEqual> m_mapping;
std::mutex m_mutex;
// Only ProxyCache<Traits>::get_base() can allocate these objects.
Pimpl() = default;
friend class ProxyCache<Traits>;
};
template <typename Traits>
void ProxyCache<Traits>::cleanup(const std::shared_ptr<Pimpl> & base, UnowningImplPointer ptr) {
base->remove(ptr);
}
/*
* Magic-static singleton.
*
* It's possible for someone to hold Djinni-static objects in a global (like a shared_ptr
* at namespace scope), which can cause problems at static destruction time: if the proxy
* cache itself is destroyed before the other global, use-of-destroyed-object will result.
* To fix this, we make it possible to take a shared_ptr to the GenericProxyCache instance,
* so it will only be destroyed once all references are gone.
*/
template <typename Traits>
auto ProxyCache<Traits>::get_base() -> const std::shared_ptr<Pimpl> & {
static const std::shared_ptr<Pimpl> instance(new Pimpl);
// Return by const-ref. This is safe to call any time except during static destruction.
// Returning by reference lets us avoid touching the refcount unless needed.
return instance;
}
template <typename Traits>
auto ProxyCache<Traits>::get(const OwningImplPointer & impl, AllocatorFunction * alloc)
-> OwningProxyPointer {
return get_base()->get(impl, alloc);
}
} // namespace djinni
//
// Copyright 2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#pragma once
#include <functional>
namespace djinni {
/*
* The template parameters we receive here can be a number of different types: C++ smart
* pointers, custom wrappers, or language-specific types (like ObjC's `id` / `__weak id`).
* If custom wrapper types are used, like the `JavaWeakRef` type in the JNI library, then
* they must implement `.get()` or `.lock()` by analogy with C++'s smart pointers.
*
* We assume that built-in types are pointer-compatible. This is the case with ObjC: for
* example, __weak id pointers can be implicitly converted to __strong id, and so on.
*
* (The helper for .lock() is only used by proxy_cache_impl.hpp, so it's defined there.)
*/
template <typename T> static inline auto get_unowning(const T & ptr) { return ptr.get(); }
template <typename T> static inline T * get_unowning(T * ptr) { return ptr; }
/*
* ProxyCache provides a mechanism for re-using proxy objects generated in one language
* that wrap around implementations in a different language. This is for correctness, not
* just performance: when we pass the same object across a language boundary twice, we want
* to get the same proxy object on the other side each time, so that identity semantics
* behave as expected.
*
* ProxyCache is instantiated with a Traits class that must contain the following typedefs.
* Examples refer to the use of ProxyCache to cache C++ wrappers around ObjC objects.
* ProxyCache itself is generic (type-erased), though Handle is not, so we use e.g. `id`
* and `shared_ptr<void>` rather than any specific types.
*
* - UnowningImplPointer:
* a non-owning pointer to an object being wrapped, e.g. __unsafe_unretained id
* - OwningImplPointer:
* a strong owning pointer to an object being wrapped, e.g. __strong id
* - OwningProxyPointer:
* a strong owning pointer to a wrapper, e.g. std::shared_ptr<void>
* - WeakProxyPointer:
* a safe weak pointer to a wrapper, e.g. std::weak_ptr<void>
* - UnowningImplPointerHash:
* a hasher for UnowningImplPointer, usually std::hash<UnowningImplPointer>, unless
* std::hash doesn't work with UnowningImplPointer in which case a custom type can be
* provided.
* - UnowningImplPointerEqual:
* an equality predicate for UnowningImplPointer, like std::equal_to<UnowningImplPointer>.
* In some cases (e.g. Java) a custom equality predicate may be needed.
*
* Generally, ProxyCache will be explicitly instantiated in one source file with C++11's
* `extern template` mechanism. The WeakProxyPointer, UnowningImplPointerHash, and
* UnowningImplPointerEqual types can be incomplete except for where the explicit
* instantiation is actually defined.
*
* Here's an overview of the structure:
*
* ______________
* WeakProxyPonter | | UnowningImplPointer
* - - - - - - - -| ProxyCache:: |- - - - - - - - - -
* | | Handle<T> | |
* | |______________| |
* | |
* ____v____ ______________ ______________v__________
* | | | | | |
* | (Proxy | ===> | ProxyCache:: | =====> | (Impl object providing |
* | object) | ^ | Handle<T> | T | actual functionality) |
* |_________| . |______________| ^ |_________________________|
* . .
* ( can be member, base, ) ( T is a generally a specific )
* ( or cross-language ) ( owning type like id<Foo>, )
* ( reference like jlong ) ( shared_ptr<Foo>, or GlobalRef )
*/
template <typename Traits>
class ProxyCache {
public:
using UnowningImplPointer = typename Traits::UnowningImplPointer;
using OwningImplPointer = typename Traits::OwningImplPointer;
using OwningProxyPointer = typename Traits::OwningProxyPointer;
using WeakProxyPointer = typename Traits::WeakProxyPointer;
using UnowningImplPointerHash = typename Traits::UnowningImplPointerHash;
using UnowningImplPointerEqual = typename Traits::UnowningImplPointerEqual;
class Pimpl;
/*
* Each proxy object must own a Handle. The Handle carries a strong reference to whatever
* the proxy wraps. When `ProxyCache::get()` creates a proxy, it also adds the proxy to
* the global proxy cache; Handle::~Handle() removes the reference from the cache.
*
* The Handle can be held by the proxy in any of a number of ways: as a C++ member or
* base, as an ObjC instance variable, or across an FFI boundary (a Java object might
* contain the address of a Handle as a `long` and delete it in the destructor.)
*
* T is generally a more-specialized version of OwningImplPointer. For example, when
* managing C++ proxies for ObjC objects, OwningImplPointer would be `id`, and the C++
* proxy class `MyInterface` which wraps `@protocol DBMyInterface` would contain a
* `Handle<id<DBMyInterface>>`.
*/
template <class T>
class Handle {
public:
template <typename... Args> Handle(Args &&... args)
: m_cache(get_base()), m_obj(std::forward<Args>(args)...) {}
Handle(const Handle &) = delete;
Handle & operator=(const Handle &) = delete;
~Handle() { if (m_obj) cleanup(m_cache, get_unowning(m_obj)); }
void assign(const T & obj) { m_obj = obj; }
const T & get() const & noexcept { return m_obj; }
private:
const std::shared_ptr<Pimpl> m_cache;
T m_obj;
};
/*
* Function typedef for helpers passed in to allocate new objects.
* To reduce code size, the proxy cache type-erases the objects inside it.
*
* An allocator function takes an OwningImplPointer to the source language object,
* and returns a newly-created proxy.
*
* In Java, an OwningImplPointer does not provide the same identity semantics as the
* underlying object. (A JNI 'jobject' can be one of a few different types of reference,
* and JNI is structured to allow copying GCs, so an object's address might change over
* time.) This is why ProxyCache takes hasher and comparator paramters. In particular,
* the OwningImplPointer passed into the allocator might be a JNI local reference. The
* allocator will create a GlobalRef to the impl object and store it in the returned proxy.
*
* Because we don't constrain how Handle objects are held, there's no generic way for the
* proxy cache to get the GlobalRef out of the returned proxy object, so AllocatorFunction
* returns a pair: the first element is the newly created proxy, and the second is an
* UnowningImplPointer that will be used as a key in the map.
*/
using AllocatorFunction =
std::pair<OwningProxyPointer, UnowningImplPointer>(const OwningImplPointer &);
/*
* Return the existing proxy for `impl`, if any. If not, create one by calling `alloc`,
* store a weak reference to it in the proxy cache, and return it.
*/
static OwningProxyPointer get(const OwningImplPointer & impl, AllocatorFunction * alloc);
private:
static void cleanup(const std::shared_ptr<Pimpl> &, UnowningImplPointer);
static const std::shared_ptr<Pimpl> & get_base();
};
} // namespace djinni
...@@ -22,8 +22,14 @@ ...@@ -22,8 +22,14 @@
"CLANG_ENABLE_OBJC_ARC": "YES", "CLANG_ENABLE_OBJC_ARC": "YES",
}, },
"sources": [ "sources": [
"objc/DJIWeakPtrWrapper.mm", "objc/DJICppWrapperCache+Private.h",
"objc/DJIError.h",
"objc/DJIError.mm", "objc/DJIError.mm",
"objc/DJIMarshal+Private.h",
"objc/DJIObjcWrapperCache+Private.h",
"objc/DJIProxyCaches.mm",
"proxy_cache_impl.hpp",
"proxy_cache_interface.hpp",
], ],
"include_dirs": [ "include_dirs": [
"objc", "objc",
......
...@@ -11,7 +11,7 @@ NativeClientInterface::NativeClientInterface() : ::djinni::JniInterface<::testsu ...@@ -11,7 +11,7 @@ NativeClientInterface::NativeClientInterface() : ::djinni::JniInterface<::testsu
NativeClientInterface::~NativeClientInterface() = default; NativeClientInterface::~NativeClientInterface() = default;
NativeClientInterface::JavaProxy::JavaProxy(JniType j) : JavaProxyCacheEntry(j) { } NativeClientInterface::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }
NativeClientInterface::JavaProxy::~JavaProxy() = default; NativeClientInterface::JavaProxy::~JavaProxy() = default;
...@@ -19,7 +19,7 @@ NativeClientInterface::JavaProxy::~JavaProxy() = default; ...@@ -19,7 +19,7 @@ NativeClientInterface::JavaProxy::~JavaProxy() = default;
auto jniEnv = ::djinni::jniGetThreadEnv(); auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10); ::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeClientInterface>::get(); const auto& data = ::djinni::JniClass<::djinni_generated::NativeClientInterface>::get();
auto jret = jniEnv->CallObjectMethod(getGlobalRef(), data.method_getRecord, auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_getRecord,
::djinni::get(::djinni::I64::fromCpp(jniEnv, c_record_id)), ::djinni::get(::djinni::I64::fromCpp(jniEnv, c_record_id)),
::djinni::get(::djinni::String::fromCpp(jniEnv, c_utf8string)), ::djinni::get(::djinni::String::fromCpp(jniEnv, c_utf8string)),
::djinni::get(::djinni::Optional<std::experimental::optional, ::djinni::String>::fromCpp(jniEnv, c_misc))); ::djinni::get(::djinni::Optional<std::experimental::optional, ::djinni::String>::fromCpp(jniEnv, c_misc)));
...@@ -30,7 +30,7 @@ double NativeClientInterface::JavaProxy::identifier_check(const std::vector<uint ...@@ -30,7 +30,7 @@ double NativeClientInterface::JavaProxy::identifier_check(const std::vector<uint
auto jniEnv = ::djinni::jniGetThreadEnv(); auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10); ::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeClientInterface>::get(); const auto& data = ::djinni::JniClass<::djinni_generated::NativeClientInterface>::get();
auto jret = jniEnv->CallDoubleMethod(getGlobalRef(), data.method_identifierCheck, auto jret = jniEnv->CallDoubleMethod(Handle::get().get(), data.method_identifierCheck,
::djinni::get(::djinni::Binary::fromCpp(jniEnv, c_data)), ::djinni::get(::djinni::Binary::fromCpp(jniEnv, c_data)),
::djinni::get(::djinni::I32::fromCpp(jniEnv, c_r)), ::djinni::get(::djinni::I32::fromCpp(jniEnv, c_r)),
::djinni::get(::djinni::I64::fromCpp(jniEnv, c_jret))); ::djinni::get(::djinni::I64::fromCpp(jniEnv, c_jret)));
...@@ -41,7 +41,7 @@ std::string NativeClientInterface::JavaProxy::return_str() { ...@@ -41,7 +41,7 @@ std::string NativeClientInterface::JavaProxy::return_str() {
auto jniEnv = ::djinni::jniGetThreadEnv(); auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10); ::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeClientInterface>::get(); const auto& data = ::djinni::JniClass<::djinni_generated::NativeClientInterface>::get();
auto jret = (jstring)jniEnv->CallObjectMethod(getGlobalRef(), data.method_returnStr); auto jret = (jstring)jniEnv->CallObjectMethod(Handle::get().get(), data.method_returnStr);
::djinni::jniExceptionCheck(jniEnv); ::djinni::jniExceptionCheck(jniEnv);
return ::djinni::String::toCpp(jniEnv, jret); return ::djinni::String::toCpp(jniEnv, jret);
} }
......
...@@ -36,9 +36,7 @@ private: ...@@ -36,9 +36,7 @@ private:
std::string return_str() override; std::string return_str() override;
private: private:
using ::djinni::JavaProxyCacheEntry::getGlobalRef;
friend ::djinni::JniInterface<::testsuite::ClientInterface, ::djinni_generated::NativeClientInterface>; friend ::djinni::JniInterface<::testsuite::ClientInterface, ::djinni_generated::NativeClientInterface>;
friend ::djinni::JavaProxyCache<JavaProxy>;
}; };
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/ClientInterface") }; const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/ClientInterface") };
......
...@@ -23,7 +23,7 @@ CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_ConstantsInterface_00024Cpp ...@@ -23,7 +23,7 @@ CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_ConstantsInterface_00024Cpp
{ {
try { try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::CppProxyHandle<::testsuite::ConstantsInterface>::get(nativeRef); const auto& ref = ::djinni::objectFromHandleAddress<::testsuite::ConstantsInterface>(nativeRef);
ref->dummy(); ref->dummy();
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
} }
......
...@@ -24,7 +24,7 @@ CJNIEXPORT jint JNICALL Java_com_dropbox_djinni_test_CppException_00024CppProxy_ ...@@ -24,7 +24,7 @@ CJNIEXPORT jint JNICALL Java_com_dropbox_djinni_test_CppException_00024CppProxy_
{ {
try { try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::CppProxyHandle<::testsuite::CppException>::get(nativeRef); const auto& ref = ::djinni::objectFromHandleAddress<::testsuite::CppException>(nativeRef);
auto r = ref->throw_an_exception(); auto r = ref->throw_an_exception();
return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r)); return ::djinni::release(::djinni::I32::fromCpp(jniEnv, r));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)
......
...@@ -24,7 +24,7 @@ CJNIEXPORT ::djinni_generated::NativeClientReturnedRecord::JniType JNICALL Java_ ...@@ -24,7 +24,7 @@ CJNIEXPORT ::djinni_generated::NativeClientReturnedRecord::JniType JNICALL Java_
{ {
try { try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::CppProxyHandle<::ExternInterface1>::get(nativeRef); const auto& ref = ::djinni::objectFromHandleAddress<::ExternInterface1>(nativeRef);
auto r = ref->foo(::djinni_generated::NativeClientInterface::toCpp(jniEnv, j_i)); auto r = ref->foo(::djinni_generated::NativeClientInterface::toCpp(jniEnv, j_i));
return ::djinni::release(::djinni_generated::NativeClientReturnedRecord::fromCpp(jniEnv, r)); return ::djinni::release(::djinni_generated::NativeClientReturnedRecord::fromCpp(jniEnv, r));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)
......
...@@ -11,7 +11,7 @@ NativeExternInterface2::NativeExternInterface2() : ::djinni::JniInterface<::Exte ...@@ -11,7 +11,7 @@ NativeExternInterface2::NativeExternInterface2() : ::djinni::JniInterface<::Exte
NativeExternInterface2::~NativeExternInterface2() = default; NativeExternInterface2::~NativeExternInterface2() = default;
NativeExternInterface2::JavaProxy::JavaProxy(JniType j) : JavaProxyCacheEntry(j) { } NativeExternInterface2::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }
NativeExternInterface2::JavaProxy::~JavaProxy() = default; NativeExternInterface2::JavaProxy::~JavaProxy() = default;
...@@ -19,7 +19,7 @@ NativeExternInterface2::JavaProxy::~JavaProxy() = default; ...@@ -19,7 +19,7 @@ NativeExternInterface2::JavaProxy::~JavaProxy() = default;
auto jniEnv = ::djinni::jniGetThreadEnv(); auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10); ::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeExternInterface2>::get(); const auto& data = ::djinni::JniClass<::djinni_generated::NativeExternInterface2>::get();
auto jret = jniEnv->CallObjectMethod(getGlobalRef(), data.method_foo, auto jret = jniEnv->CallObjectMethod(Handle::get().get(), data.method_foo,
::djinni::get(::djinni_generated::NativeTestHelpers::fromCpp(jniEnv, c_i))); ::djinni::get(::djinni_generated::NativeTestHelpers::fromCpp(jniEnv, c_i)));
::djinni::jniExceptionCheck(jniEnv); ::djinni::jniExceptionCheck(jniEnv);
return ::djinni_generated::NativeExternRecordWithDerivings::toCpp(jniEnv, jret); return ::djinni_generated::NativeExternRecordWithDerivings::toCpp(jniEnv, jret);
......
...@@ -34,9 +34,7 @@ private: ...@@ -34,9 +34,7 @@ private:
::ExternRecordWithDerivings foo(const std::shared_ptr<::testsuite::TestHelpers> & i) override; ::ExternRecordWithDerivings foo(const std::shared_ptr<::testsuite::TestHelpers> & i) override;
private: private:
using ::djinni::JavaProxyCacheEntry::getGlobalRef;
friend ::djinni::JniInterface<::ExternInterface2, ::djinni_generated::NativeExternInterface2>; friend ::djinni::JniInterface<::ExternInterface2, ::djinni_generated::NativeExternInterface2>;
friend ::djinni::JavaProxyCache<JavaProxy>;
}; };
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/ExternInterface2") }; const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/ExternInterface2") };
......
...@@ -10,7 +10,7 @@ NativeUserToken::NativeUserToken() : ::djinni::JniInterface<::testsuite::UserTok ...@@ -10,7 +10,7 @@ NativeUserToken::NativeUserToken() : ::djinni::JniInterface<::testsuite::UserTok
NativeUserToken::~NativeUserToken() = default; NativeUserToken::~NativeUserToken() = default;
NativeUserToken::JavaProxy::JavaProxy(JniType j) : JavaProxyCacheEntry(j) { } NativeUserToken::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { }
NativeUserToken::JavaProxy::~JavaProxy() = default; NativeUserToken::JavaProxy::~JavaProxy() = default;
...@@ -18,7 +18,7 @@ std::string NativeUserToken::JavaProxy::whoami() { ...@@ -18,7 +18,7 @@ std::string NativeUserToken::JavaProxy::whoami() {
auto jniEnv = ::djinni::jniGetThreadEnv(); auto jniEnv = ::djinni::jniGetThreadEnv();
::djinni::JniLocalScope jscope(jniEnv, 10); ::djinni::JniLocalScope jscope(jniEnv, 10);
const auto& data = ::djinni::JniClass<::djinni_generated::NativeUserToken>::get(); const auto& data = ::djinni::JniClass<::djinni_generated::NativeUserToken>::get();
auto jret = (jstring)jniEnv->CallObjectMethod(getGlobalRef(), data.method_whoami); auto jret = (jstring)jniEnv->CallObjectMethod(Handle::get().get(), data.method_whoami);
::djinni::jniExceptionCheck(jniEnv); ::djinni::jniExceptionCheck(jniEnv);
return ::djinni::String::toCpp(jniEnv, jret); return ::djinni::String::toCpp(jniEnv, jret);
} }
...@@ -35,7 +35,7 @@ CJNIEXPORT jstring JNICALL Java_com_dropbox_djinni_test_UserToken_00024CppProxy_ ...@@ -35,7 +35,7 @@ CJNIEXPORT jstring JNICALL Java_com_dropbox_djinni_test_UserToken_00024CppProxy_
{ {
try { try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::CppProxyHandle<::testsuite::UserToken>::get(nativeRef); const auto& ref = ::djinni::objectFromHandleAddress<::testsuite::UserToken>(nativeRef);
auto r = ref->whoami(); auto r = ref->whoami();
return ::djinni::release(::djinni::String::fromCpp(jniEnv, r)); return ::djinni::release(::djinni::String::fromCpp(jniEnv, r));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */) } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */)
......
...@@ -34,9 +34,7 @@ private: ...@@ -34,9 +34,7 @@ private:
std::string whoami() override; std::string whoami() override;
private: private:
using ::djinni::JavaProxyCacheEntry::getGlobalRef;
friend ::djinni::JniInterface<::testsuite::UserToken, ::djinni_generated::NativeUserToken>; friend ::djinni::JniInterface<::testsuite::UserToken, ::djinni_generated::NativeUserToken>;
friend ::djinni::JavaProxyCache<JavaProxy>;
}; };
const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/UserToken") }; const ::djinni::GlobalRef<jclass> clazz { ::djinni::jniFindClass("com/dropbox/djinni/test/UserToken") };
......
...@@ -13,14 +13,14 @@ namespace djinni_generated { ...@@ -13,14 +13,14 @@ namespace djinni_generated {
class ClientInterface::ObjcProxy final class ClientInterface::ObjcProxy final
: public ::testsuite::ClientInterface : public ::testsuite::ClientInterface
, public ::djinni::DbxObjcWrapperCache<ObjcProxy>::Handle , public ::djinni::ObjcProxyCache::Handle<ObjcType>
{ {
public: public:
using Handle::Handle; using Handle::Handle;
::testsuite::ClientReturnedRecord get_record(int64_t c_record_id, const std::string & c_utf8string, const std::experimental::optional<std::string> & c_misc) override ::testsuite::ClientReturnedRecord get_record(int64_t c_record_id, const std::string & c_utf8string, const std::experimental::optional<std::string> & c_misc) override
{ {
@autoreleasepool { @autoreleasepool {
auto r = [(ObjcType)Handle::get() getRecord:(::djinni::I64::fromCpp(c_record_id)) auto r = [Handle::get() getRecord:(::djinni::I64::fromCpp(c_record_id))
utf8string:(::djinni::String::fromCpp(c_utf8string)) utf8string:(::djinni::String::fromCpp(c_utf8string))
misc:(::djinni::Optional<std::experimental::optional, ::djinni::String>::fromCpp(c_misc))]; misc:(::djinni::Optional<std::experimental::optional, ::djinni::String>::fromCpp(c_misc))];
return ::djinni_generated::ClientReturnedRecord::toCpp(r); return ::djinni_generated::ClientReturnedRecord::toCpp(r);
...@@ -29,7 +29,7 @@ public: ...@@ -29,7 +29,7 @@ public:
double identifier_check(const std::vector<uint8_t> & c_data, int32_t c_r, int64_t c_jret) override double identifier_check(const std::vector<uint8_t> & c_data, int32_t c_r, int64_t c_jret) override
{ {
@autoreleasepool { @autoreleasepool {
auto r = [(ObjcType)Handle::get() identifierCheck:(::djinni::Binary::fromCpp(c_data)) auto r = [Handle::get() identifierCheck:(::djinni::Binary::fromCpp(c_data))
r:(::djinni::I32::fromCpp(c_r)) r:(::djinni::I32::fromCpp(c_r))
jret:(::djinni::I64::fromCpp(c_jret))]; jret:(::djinni::I64::fromCpp(c_jret))];
return ::djinni::F64::toCpp(r); return ::djinni::F64::toCpp(r);
...@@ -38,7 +38,7 @@ public: ...@@ -38,7 +38,7 @@ public:
std::string return_str() override std::string return_str() override
{ {
@autoreleasepool { @autoreleasepool {
auto r = [(ObjcType)Handle::get() returnStr]; auto r = [Handle::get() returnStr];
return ::djinni::String::toCpp(r); return ::djinni::String::toCpp(r);
} }
} }
...@@ -53,7 +53,7 @@ auto ClientInterface::toCpp(ObjcType objc) -> CppType ...@@ -53,7 +53,7 @@ auto ClientInterface::toCpp(ObjcType objc) -> CppType
if (!objc) { if (!objc) {
return nullptr; return nullptr;
} }
return ::djinni::DbxObjcWrapperCache<ObjcProxy>::getInstance()->get(objc); return ::djinni::get_objc_proxy<ObjcProxy>(objc);
} }
auto ClientInterface::fromCpp(const CppType& cpp) -> ObjcType auto ClientInterface::fromCpp(const CppType& cpp) -> ObjcType
......
...@@ -18,7 +18,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -18,7 +18,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation DBConstantsInterface { @implementation DBConstantsInterface {
::djinni::DbxCppWrapperCache<::testsuite::ConstantsInterface>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::testsuite::ConstantsInterface>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::testsuite::ConstantsInterface>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::testsuite::ConstantsInterface>&)cppRef
...@@ -50,9 +50,7 @@ auto ConstantsInterface::fromCpp(const CppType& cpp) -> ObjcType ...@@ -50,9 +50,7 @@ auto ConstantsInterface::fromCpp(const CppType& cpp) -> ObjcType
if (!cpp) { if (!cpp) {
return nil; return nil;
} }
return ::djinni::DbxCppWrapperCache<::testsuite::ConstantsInterface>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<DBConstantsInterface>(cpp);
return [[DBConstantsInterface alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation DBCppException { @implementation DBCppException {
::djinni::DbxCppWrapperCache<::testsuite::CppException>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::testsuite::CppException>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::testsuite::CppException>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::testsuite::CppException>&)cppRef
...@@ -59,9 +59,7 @@ auto CppException::fromCpp(const CppType& cpp) -> ObjcType ...@@ -59,9 +59,7 @@ auto CppException::fromCpp(const CppType& cpp) -> ObjcType
if (!cpp) { if (!cpp) {
return nil; return nil;
} }
return ::djinni::DbxCppWrapperCache<::testsuite::CppException>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<DBCppException>(cpp);
return [[DBCppException alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation DBExternInterface1 { @implementation DBExternInterface1 {
::djinni::DbxCppWrapperCache<::ExternInterface1>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::ExternInterface1>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::ExternInterface1>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::ExternInterface1>&)cppRef
...@@ -52,9 +52,7 @@ auto ExternInterface1::fromCpp(const CppType& cpp) -> ObjcType ...@@ -52,9 +52,7 @@ auto ExternInterface1::fromCpp(const CppType& cpp) -> ObjcType
if (!cpp) { if (!cpp) {
return nil; return nil;
} }
return ::djinni::DbxCppWrapperCache<::ExternInterface1>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<DBExternInterface1>(cpp);
return [[DBExternInterface1 alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -13,14 +13,14 @@ namespace djinni_generated { ...@@ -13,14 +13,14 @@ namespace djinni_generated {
class ExternInterface2::ObjcProxy final class ExternInterface2::ObjcProxy final
: public ::ExternInterface2 : public ::ExternInterface2
, public ::djinni::DbxObjcWrapperCache<ObjcProxy>::Handle , public ::djinni::ObjcProxyCache::Handle<ObjcType>
{ {
public: public:
using Handle::Handle; using Handle::Handle;
::ExternRecordWithDerivings foo(const std::shared_ptr<::testsuite::TestHelpers> & c_i) override ::ExternRecordWithDerivings foo(const std::shared_ptr<::testsuite::TestHelpers> & c_i) override
{ {
@autoreleasepool { @autoreleasepool {
auto r = [(ObjcType)Handle::get() foo:(::djinni_generated::TestHelpers::fromCpp(c_i))]; auto r = [Handle::get() foo:(::djinni_generated::TestHelpers::fromCpp(c_i))];
return ::djinni_generated::ExternRecordWithDerivings::toCpp(r); return ::djinni_generated::ExternRecordWithDerivings::toCpp(r);
} }
} }
...@@ -35,7 +35,7 @@ auto ExternInterface2::toCpp(ObjcType objc) -> CppType ...@@ -35,7 +35,7 @@ auto ExternInterface2::toCpp(ObjcType objc) -> CppType
if (!objc) { if (!objc) {
return nullptr; return nullptr;
} }
return ::djinni::DbxObjcWrapperCache<ObjcProxy>::getInstance()->get(objc); return ::djinni::get_objc_proxy<ObjcProxy>(objc);
} }
auto ExternInterface2::fromCpp(const CppType& cpp) -> ObjcType auto ExternInterface2::fromCpp(const CppType& cpp) -> ObjcType
......
...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation DBTestDuration { @implementation DBTestDuration {
::djinni::DbxCppWrapperCache<::testsuite::TestDuration>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::testsuite::TestDuration>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::testsuite::TestDuration>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::testsuite::TestDuration>&)cppRef
...@@ -185,9 +185,7 @@ auto TestDuration::fromCpp(const CppType& cpp) -> ObjcType ...@@ -185,9 +185,7 @@ auto TestDuration::fromCpp(const CppType& cpp) -> ObjcType
if (!cpp) { if (!cpp) {
return nil; return nil;
} }
return ::djinni::DbxCppWrapperCache<::testsuite::TestDuration>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<DBTestDuration>(cpp);
return [[DBTestDuration alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -25,7 +25,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -25,7 +25,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation DBTestHelpers { @implementation DBTestHelpers {
::djinni::DbxCppWrapperCache<::testsuite::TestHelpers>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::testsuite::TestHelpers>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::testsuite::TestHelpers>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::testsuite::TestHelpers>&)cppRef
...@@ -215,9 +215,7 @@ auto TestHelpers::fromCpp(const CppType& cpp) -> ObjcType ...@@ -215,9 +215,7 @@ auto TestHelpers::fromCpp(const CppType& cpp) -> ObjcType
if (!cpp) { if (!cpp) {
return nil; return nil;
} }
return ::djinni::DbxCppWrapperCache<::testsuite::TestHelpers>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<DBTestHelpers>(cpp);
return [[DBTestHelpers alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th ...@@ -19,7 +19,7 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
@end @end
@implementation DBUserTokenCppProxy { @implementation DBUserTokenCppProxy {
::djinni::DbxCppWrapperCache<::testsuite::UserToken>::Handle _cppRefHandle; ::djinni::CppProxyCache::Handle<std::shared_ptr<::testsuite::UserToken>> _cppRefHandle;
} }
- (id)initWithCpp:(const std::shared_ptr<::testsuite::UserToken>&)cppRef - (id)initWithCpp:(const std::shared_ptr<::testsuite::UserToken>&)cppRef
...@@ -41,14 +41,14 @@ namespace djinni_generated { ...@@ -41,14 +41,14 @@ namespace djinni_generated {
class UserToken::ObjcProxy final class UserToken::ObjcProxy final
: public ::testsuite::UserToken : public ::testsuite::UserToken
, public ::djinni::DbxObjcWrapperCache<ObjcProxy>::Handle , public ::djinni::ObjcProxyCache::Handle<ObjcType>
{ {
public: public:
using Handle::Handle; using Handle::Handle;
std::string whoami() override std::string whoami() override
{ {
@autoreleasepool { @autoreleasepool {
auto r = [(ObjcType)Handle::get() whoami]; auto r = [Handle::get() whoami];
return ::djinni::String::toCpp(r); return ::djinni::String::toCpp(r);
} }
} }
...@@ -66,7 +66,7 @@ auto UserToken::toCpp(ObjcType objc) -> CppType ...@@ -66,7 +66,7 @@ auto UserToken::toCpp(ObjcType objc) -> CppType
if ([(id)objc isKindOfClass:[DBUserTokenCppProxy class]]) { if ([(id)objc isKindOfClass:[DBUserTokenCppProxy class]]) {
return ((DBUserTokenCppProxy*)objc)->_cppRefHandle.get(); return ((DBUserTokenCppProxy*)objc)->_cppRefHandle.get();
} }
return ::djinni::DbxObjcWrapperCache<ObjcProxy>::getInstance()->get(objc); return ::djinni::get_objc_proxy<ObjcProxy>(objc);
} }
auto UserToken::fromCpp(const CppType& cpp) -> ObjcType auto UserToken::fromCpp(const CppType& cpp) -> ObjcType
...@@ -77,9 +77,7 @@ auto UserToken::fromCpp(const CppType& cpp) -> ObjcType ...@@ -77,9 +77,7 @@ auto UserToken::fromCpp(const CppType& cpp) -> ObjcType
if (auto cppPtr = dynamic_cast<ObjcProxy*>(cpp.get())) { if (auto cppPtr = dynamic_cast<ObjcProxy*>(cpp.get())) {
return cppPtr->Handle::get(); return cppPtr->Handle::get();
} }
return ::djinni::DbxCppWrapperCache<::testsuite::UserToken>::getInstance()->get(cpp, [] (const CppType& p) { return ::djinni::get_cpp_proxy<DBUserTokenCppProxy>(cpp);
return [[DBUserTokenCppProxy alloc] initWithCpp:p];
});
} }
} // namespace djinni_generated } // namespace djinni_generated
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
6536CD6F19A6C82200DD7715 /* DJIWeakPtrWrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD6C19A6C82200DD7715 /* DJIWeakPtrWrapper.mm */; };
6536CD7419A6C96C00DD7715 /* DBClientInterfaceImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD7219A6C96C00DD7715 /* DBClientInterfaceImpl.mm */; }; 6536CD7419A6C96C00DD7715 /* DBClientInterfaceImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD7219A6C96C00DD7715 /* DBClientInterfaceImpl.mm */; };
6536CD7819A6C98800DD7715 /* cpp_exception_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD7619A6C98800DD7715 /* cpp_exception_impl.cpp */; }; 6536CD7819A6C98800DD7715 /* cpp_exception_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD7619A6C98800DD7715 /* cpp_exception_impl.cpp */; };
6536CD8D19A6C9A800DD7715 /* DBClientInterfaceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD7A19A6C99800DD7715 /* DBClientInterfaceTests.mm */; }; 6536CD8D19A6C9A800DD7715 /* DBClientInterfaceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6536CD7A19A6C99800DD7715 /* DBClientInterfaceTests.mm */; };
...@@ -122,8 +121,7 @@ ...@@ -122,8 +121,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
6536CD6A19A6C82200DD7715 /* DJIError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DJIError.h; sourceTree = "<group>"; }; 6536CD6A19A6C82200DD7715 /* DJIError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DJIError.h; sourceTree = "<group>"; };
6536CD6B19A6C82200DD7715 /* DJIWeakPtrWrapper+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DJIWeakPtrWrapper+Private.h"; sourceTree = "<group>"; }; 6536CD6C19A6C82200DD7715 /* DJIProxyCaches.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DJIProxyCaches.mm; sourceTree = "<group>"; };
6536CD6C19A6C82200DD7715 /* DJIWeakPtrWrapper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DJIWeakPtrWrapper.mm; sourceTree = "<group>"; };
6536CD6D19A6C82200DD7715 /* DJICppWrapperCache+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DJICppWrapperCache+Private.h"; sourceTree = "<group>"; }; 6536CD6D19A6C82200DD7715 /* DJICppWrapperCache+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DJICppWrapperCache+Private.h"; sourceTree = "<group>"; };
6536CD6E19A6C82200DD7715 /* DJIObjcWrapperCache+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DJIObjcWrapperCache+Private.h"; sourceTree = "<group>"; }; 6536CD6E19A6C82200DD7715 /* DJIObjcWrapperCache+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DJIObjcWrapperCache+Private.h"; sourceTree = "<group>"; };
6536CD7119A6C96C00DD7715 /* DBClientInterfaceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBClientInterfaceImpl.h; sourceTree = "<group>"; }; 6536CD7119A6C96C00DD7715 /* DBClientInterfaceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBClientInterfaceImpl.h; sourceTree = "<group>"; };
...@@ -305,8 +303,7 @@ ...@@ -305,8 +303,7 @@
A239F3791AF400C600DF27C8 /* DJIMarshal+Private.h */, A239F3791AF400C600DF27C8 /* DJIMarshal+Private.h */,
A2CB54B319BA6E6000A9E600 /* DJIError.mm */, A2CB54B319BA6E6000A9E600 /* DJIError.mm */,
6536CD6A19A6C82200DD7715 /* DJIError.h */, 6536CD6A19A6C82200DD7715 /* DJIError.h */,
6536CD6B19A6C82200DD7715 /* DJIWeakPtrWrapper+Private.h */, 6536CD6C19A6C82200DD7715 /* DJIProxyCaches.mm */,
6536CD6C19A6C82200DD7715 /* DJIWeakPtrWrapper.mm */,
6536CD6D19A6C82200DD7715 /* DJICppWrapperCache+Private.h */, 6536CD6D19A6C82200DD7715 /* DJICppWrapperCache+Private.h */,
6536CD6E19A6C82200DD7715 /* DJIObjcWrapperCache+Private.h */, 6536CD6E19A6C82200DD7715 /* DJIObjcWrapperCache+Private.h */,
); );
...@@ -613,7 +610,7 @@ ...@@ -613,7 +610,7 @@
CFC5D9E81B1513E800BF2DF8 /* DBExternInterface1+Private.mm in Sources */, CFC5D9E81B1513E800BF2DF8 /* DBExternInterface1+Private.mm in Sources */,
A24850311AF96EBC00AFE907 /* DBSetRecord.mm in Sources */, A24850311AF96EBC00AFE907 /* DBSetRecord.mm in Sources */,
CFAED8761B54291900E3B8A3 /* DBEmptyRecord+Private.mm in Sources */, CFAED8761B54291900E3B8A3 /* DBEmptyRecord+Private.mm in Sources */,
6536CD6F19A6C82200DD7715 /* DJIWeakPtrWrapper.mm in Sources */, 6536CD6F19A6C82200DD7715 /* DJIProxyCaches.mm in Sources */,
A24850271AF96EBC00AFE907 /* DBClientReturnedRecord.mm in Sources */, A24850271AF96EBC00AFE907 /* DBClientReturnedRecord.mm in Sources */,
CFC5D9D61B15106400BF2DF8 /* DBExternRecordWithDerivings.mm in Sources */, CFC5D9D61B15106400BF2DF8 /* DBExternRecordWithDerivings.mm in Sources */,
CFC5DA0E1B15330000BF2DF8 /* record_with_duration_and_derivings.cpp in Sources */, CFC5DA0E1B15330000BF2DF8 /* record_with_duration_and_derivings.cpp in Sources */,
......
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