Commit b08c54a0 authored by Jacob Potter's avatar Jacob Potter

Fix JNI proxy infrastructure

We previously had weak-maps for passing objects from Java to C++ (necessary for listeners etc),
but each C++->Java pass would previously create a new proxy. This change:

- Unifies the +c and +j wrapper types, allowing an interface to be +c +j
- Fixes C++->Java object passing to use the existing wrapper if one exists
- Fixes both C++->Java and Java->C++ to "unwrap" an object if need be (allowing a Java object to be passed to C++ and then back properly)
- Adds tests for all of the above
parent a0997b01
......@@ -10,12 +10,12 @@ public abstract class SortItems {
public static native SortItems createWithListener(TextboxListener listener);
public static final class NativeProxy extends SortItems
public static final class CppProxy extends SortItems
{
private final long nativeRef;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private NativeProxy(long nativeRef)
private CppProxy(long nativeRef)
{
if (nativeRef == 0) throw new RuntimeException("nativeRef is zero");
this.nativeRef = nativeRef;
......
......@@ -8,23 +8,23 @@
namespace djinni_generated {
NativeSortItems::NativeSortItems() : djinni::JniInterfaceCppExt<::textsort::SortItems>("com/dropbox/textsort/SortItems$NativeProxy") {}
NativeSortItems::NativeSortItems() : djinni::JniInterface<::textsort::SortItems, NativeSortItems>("com/dropbox/textsort/SortItems$CppProxy") {}
using namespace ::djinni_generated;
CJNIEXPORT void JNICALL Java_com_dropbox_textsort_SortItems_00024NativeProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
CJNIEXPORT void JNICALL Java_com_dropbox_textsort_SortItems_00024CppProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
delete reinterpret_cast<std::shared_ptr<::textsort::SortItems>*>(nativeRef);
delete reinterpret_cast<djinni::CppProxyHandle<::textsort::SortItems>*>(nativeRef);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
CJNIEXPORT void JNICALL Java_com_dropbox_textsort_SortItems_00024NativeProxy_native_1sort(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_items)
CJNIEXPORT void JNICALL Java_com_dropbox_textsort_SortItems_00024CppProxy_native_1sort(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_items)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const std::shared_ptr<::textsort::SortItems> & ref = *reinterpret_cast<const std::shared_ptr<::textsort::SortItems>*>(nativeRef);
const std::shared_ptr<::textsort::SortItems> & ref = djinni::CppProxyHandle<::textsort::SortItems>::get(nativeRef);
::textsort::ItemList c_items = NativeItemList::fromJava(jniEnv, j_items);
ref->sort(c_items);
......
......@@ -8,7 +8,7 @@
namespace djinni_generated {
class NativeSortItems final : djinni::JniInterfaceCppExt<::textsort::SortItems> {
class NativeSortItems final : djinni::JniInterface<::textsort::SortItems, NativeSortItems> {
public:
using CppType = std::shared_ptr<::textsort::SortItems>;
using JniType = jobject;
......
......@@ -6,7 +6,7 @@
namespace djinni_generated {
NativeTextboxListener::NativeTextboxListener() : djinni::JniInterfaceJavaExt<::textsort::TextboxListener, NativeTextboxListener>() {}
NativeTextboxListener::NativeTextboxListener() : djinni::JniInterface<::textsort::TextboxListener, NativeTextboxListener>() {}
NativeTextboxListener::JavaProxy::JavaProxy(jobject obj) : JavaProxyCacheEntry(obj) {}
......
......@@ -8,11 +8,12 @@
namespace djinni_generated {
class NativeTextboxListener final : djinni::JniInterfaceJavaExt<::textsort::TextboxListener, NativeTextboxListener> {
class NativeTextboxListener final : djinni::JniInterface<::textsort::TextboxListener, NativeTextboxListener> {
public:
using CppType = std::shared_ptr<::textsort::TextboxListener>;
using JniType = jobject;
static jobject toJava(JNIEnv* jniEnv, std::shared_ptr<::textsort::TextboxListener> c) { return djinni::JniClass<::djinni_generated::NativeTextboxListener>::get()._toJava(jniEnv, c); }
static std::shared_ptr<::textsort::TextboxListener> fromJava(JNIEnv* jniEnv, jobject j) { return djinni::JniClass<::djinni_generated::NativeTextboxListener>::get()._fromJava(jniEnv, j); }
const djinni::GlobalRef<jclass> clazz { djinni::jniFindClass("com/dropbox/textsort/TextboxListener") };
......@@ -25,7 +26,7 @@ public:
private:
using djinni::JavaProxyCacheEntry::getGlobalRef;
friend class djinni::JniInterfaceJavaExt<::textsort::TextboxListener, ::djinni_generated::NativeTextboxListener>;
friend class djinni::JniInterface<::textsort::TextboxListener, ::djinni_generated::NativeTextboxListener>;
friend class djinni::JavaProxyCache<JavaProxy>;
};
......
......@@ -183,13 +183,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
val jniClassName = spec.jniClassIdentStyle(ident)
val fqJniClassName = withNs(Some(spec.jniNamespace), jniClassName)
val classLookup = toJavaClassLookup(ident, spec.javaPackage)
val (baseClassName, baseClassArgs) = (i.ext.java, i.ext.cpp) match {
case (true, true) => throw new AssertionError("an interface cannot be both +c and +j")
case (false, true) => ("JniInterfaceCppExt", s"<$selfQ>")
case (true, false) => ("JniInterfaceJavaExt", s"<$selfQ, $jniClassName>")
case (false, false) => throw new AssertionError("interface isn't implementable on either side?")
}
val baseType = s"djinni::"+baseClassName+baseClassArgs
val baseType = s"djinni::JniInterface<$selfQ, $jniClassName>"
def writeJniPrototype(w: IndentWriter) {
writeJniTypeParams(w, typeParams)
......@@ -198,9 +192,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
w.wl(s"using CppType = std::shared_ptr<$selfQ>;")
w.wl(s"using JniType = jobject;")
w.wl
if (!i.ext.java) {
w.wl(s"static jobject toJava(JNIEnv* jniEnv, std::shared_ptr<$selfQ> c) { return djinni::JniClass<$fqJniClassName>::get()._toJava(jniEnv, c); }")
}
w.wl(s"static std::shared_ptr<$selfQ> fromJava(JNIEnv* jniEnv, jobject j) { return djinni::JniClass<$fqJniClassName>::get()._fromJava(jniEnv, j); }")
w.wl
if (i.ext.java) {
......@@ -224,7 +216,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
w.wl
w.wlOutdent(s"private:")
w.wl(s"using djinni::JavaProxyCacheEntry::getGlobalRef;")
w.wl(s"friend class djinni::JniInterfaceJavaExt<$selfQ, $fqJniClassName>;")
w.wl(s"friend class djinni::JniInterface<$selfQ, $fqJniClassName>;")
w.wl(s"friend class djinni::JavaProxyCache<JavaProxy>;")
}
}
......@@ -236,7 +228,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
}
def writeJniBody(w: IndentWriter) {
val baseClassParam = if (i.ext.cpp) q(classLookup+"$NativeProxy") else ""
val baseClassParam = if (i.ext.cpp) q(classLookup+"$CppProxy") else ""
writeJniTypeParams(w, typeParams)
w.wl(s"$jniClassName::$jniClassName() : $baseType($baseClassParam) {}")
w.wl
......@@ -300,7 +292,7 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
}
}
else {
w.wl(s"CJNIEXPORT $jniRetType JNICALL ${prefix}_00024NativeProxy_$methodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef${preComma(paramList)})").braced {
w.wl(s"CJNIEXPORT $jniRetType JNICALL ${prefix}_00024CppProxy_$methodNameMunged(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef${preComma(paramList)})").braced {
val zero = "0 /* value doesn't matter*/"
w.w("try").bracedEnd(s" JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ${ret.fold("")(s => zero)})") {
w.wl(s"DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);")
......@@ -310,13 +302,13 @@ class JNIGenerator(spec: Spec) extends Generator(spec) {
}
}
nativeHook("nativeDestroy", false, Seq.empty, None, {
w.wl(s"delete reinterpret_cast<std::shared_ptr<$selfQ>*>(nativeRef);")
w.wl(s"delete reinterpret_cast<djinni::CppProxyHandle<$selfQ>*>(nativeRef);")
})
for (m <- i.methods) {
val nativeAddon = if (m.static) "" else "native_"
nativeHook(nativeAddon + idJava.method(m.ident), m.static, m.params, m.ret, {
//w.wl(s"::${spec.jniNamespace}::JniLocalScope jscope(jniEnv, 10);")
if (!m.static) w.wl(s"const std::shared_ptr<$selfQ> & ref = *reinterpret_cast<const std::shared_ptr<$selfQ>*>(nativeRef);")
if (!m.static) w.wl(s"const std::shared_ptr<$selfQ> & ref = djinni::CppProxyHandle<$selfQ>::get(nativeRef);")
for (p <- m.params) {
val jniHelperClass = toJniHelperClass(p.ty)
val cppType = toCppType(p.ty, spec.cppNamespace)
......
......@@ -165,11 +165,11 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
if (i.ext.cpp) {
w.wl
javaAnnotationHeader.foreach(w.wl)
w.wl(s"public static final class NativeProxy$typeParamList extends $javaClass$typeParamList").braced {
w.wl(s"public static final class CppProxy$typeParamList extends $javaClass$typeParamList").braced {
w.wl("private final long nativeRef;")
w.wl("private final AtomicBoolean destroyed = new AtomicBoolean(false);")
w.wl
w.wl(s"private NativeProxy(long nativeRef)").braced {
w.wl(s"private CppProxy(long nativeRef)").braced {
w.wl("if (nativeRef == 0) throw new RuntimeException(\"nativeRef is zero\");")
w.wl(s"this.nativeRef = nativeRef;")
}
......@@ -183,7 +183,7 @@ class JavaGenerator(spec: Spec) extends Generator(spec) {
w.wl("destroy();")
w.wl("super.finalize();")
}
for (m <- i.methods if !m.static) { // Static methods not in NativeProxy
for (m <- i.methods if !m.static) { // Static methods not in CppProxy
val ret = m.ret.fold("void")(toJavaType(_))
val returnStmt = m.ret.fold("")(_ => "return ")
val params = m.params.map(p => toJavaType(p.ty)+" "+idJava.local(p.ident)).mkString(", ")
......
......@@ -51,19 +51,19 @@ JNIEnv * jniGetThreadEnv() {
return env;
}
void GlobalRefDeleter::operator() (jobject globalRef) noexcept {
if (!globalRef || !g_cachedJVM) {
return;
static JNIEnv * getOptThreadEnv() {
if (!g_cachedJVM) {
return nullptr;
}
// Special case: ignore GlobalRef deletions that happen after this thread has been
// detached. (This can happen during process shutdown, so there's no need to release the
// ref anyway.)
// Special case: this allows us to ignore GlobalRef deletions that happen after this
// thread has been detached. (This is known to happen during process shutdown, when
// there's no need to release the ref anyway.)
JNIEnv * env = nullptr;
const jint get_res = g_cachedJVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (get_res == JNI_EDETACHED) {
return;
return nullptr;
}
// Still bail on any other error.
......@@ -72,11 +72,29 @@ void GlobalRefDeleter::operator() (jobject globalRef) noexcept {
std::abort();
}
return env;
}
void WeakGlobalRefDeleter::operator() (jobject wgr) noexcept {
if (wgr) {
if (JNIEnv * env = getOptThreadEnv()) {
env->DeleteWeakGlobalRef(wgr);
}
}
}
void GlobalRefDeleter::operator() (jobject globalRef) noexcept {
if (globalRef) {
if (JNIEnv * env = getOptThreadEnv()) {
env->DeleteGlobalRef(globalRef);
}
}
}
void LocalRefDeleter::operator() (jobject localRef) noexcept {
if (localRef) {
jniGetThreadEnv()->DeleteLocalRef(localRef);
}
}
void jniExceptionCheck(JNIEnv * env) {
......@@ -442,4 +460,54 @@ std::shared_ptr<void> javaProxyCacheLookup(jobject obj, std::pair<std::shared_pt
return ret.first;
}
CppProxyClassInfo::CppProxyClassInfo(const char * className)
: clazz(jniFindClass(className)),
constructor(jniGetMethodID(clazz.get(), "<init>", "(J)V")),
idField(jniGetFieldID(clazz.get(), "nativeRef", "J")) {
}
CppProxyClassInfo::CppProxyClassInfo() : constructor{}, idField{} {
}
CppProxyClassInfo::~CppProxyClassInfo() {
}
struct CppProxyCacheState {
std::mutex mtx;
std::unordered_map<void *, WeakGlobalRef<jobject>> 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()) {
jobject localRef = jniEnv->NewLocalRef(it->second.get());
if (localRef) {
return localRef;
}
}
jobject wrapper = factory(cppObj, jniEnv, proxyClass);
st.m.emplace(cppObj.get(), WeakGlobalRef<jobject>(jniEnv, wrapper));
return wrapper;
}
} // namespace djinni
......@@ -107,6 +107,20 @@ public:
localRef) {}
};
struct WeakGlobalRefDeleter { void operator() (jobject wgr) noexcept; };
template <typename PointerType>
class WeakGlobalRef : public std::unique_ptr<typename std::remove_pointer<PointerType>::type,
WeakGlobalRefDeleter> {
public:
WeakGlobalRef() {}
WeakGlobalRef(JNIEnv * env, PointerType localRef)
: std::unique_ptr<typename std::remove_pointer<PointerType>::type, WeakGlobalRefDeleter>(
static_cast<PointerType>(env->NewWeakGlobalRef(localRef)),
WeakGlobalRefDeleter{}
) {}
};
/*
* Helper for JniClassInitializer. Copied from Oxygen.
*/
......@@ -282,50 +296,210 @@ public:
}
};
template <class I>
class JniInterfaceCppExt {
/*
* Cache for CppProxy objects. This is the inverse of the JavaProxyCache mechanism above,
* ensuring that each time we pass an interface from Java to C++, we get the *same* CppProxy
* object on the Java side:
*
* Java | C++
* |
* ______________ | ________________ ___________
* | | | | | | |
* | Foo.CppProxy | ------------> | CppProxyHandle | =============> | Foo |
* |______________| (jlong) | <Foo> | (shared_ptr) |___________|
* ^ | |________________|
* \ |
* \ | __________________
* -----------------------| |
* (WeakGlobalRef) | jniCppProxyCache |
* | |__________________|
*/
/*
* Information needed to use a CppProxy class.
*
* In an ideal world, this object would be properly always-valid RAII, and we'd use an
* optional<CppProxyClassInfo> where needed. Unfortunately we don't want to depend on optional
* here, so this object has an invalid state and default constructor.
*/
struct CppProxyClassInfo {
const GlobalRef<jclass> clazz;
const jmethodID constructor;
const jfieldID idField;
CppProxyClassInfo(const char * className);
CppProxyClassInfo();
~CppProxyClassInfo();
// Validity check
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) {
return reinterpret_cast<const CppProxyHandle<T> *>(handle)->m_obj;
}
private:
const std::shared_ptr<T> m_obj;
};
/*
* Base class for Java <-> C++ interface adapters.
*
* I is the C++ base class (interface) being adapted; Self is the interface adapter class
* derived from JniInterface (using CRTP). For example:
*
* class NativeToken final : djinni::JniInterface<Token, NativeToken> { ... }
*/
template <class I, class Self>
class JniInterface {
public:
/*
* Given a C++ object, find or create a Java version. The cases here are:
* 1. Null
* 2. The provided C++ object is actually a JavaProxy (C++-side proxy for Java impl)
* 3. The provided C++ object has an existing CppProxy (Java-side proxy for C++ impl)
* 4. The provided C++ object needs a new CppProxy allocated
*/
jobject _toJava(JNIEnv* jniEnv, const std::shared_ptr<I> & c) const {
if (c == nullptr) {
return 0;
// Case 1 - null
if (!c) {
return nullptr;
}
std::unique_ptr<std::shared_ptr<I>> to_encapsulate(new std::shared_ptr<I>(c));
jlong shared_ptr_val = static_cast<jlong>(reinterpret_cast<uintptr_t>(to_encapsulate.get()));
jobject nativeProxy = jniEnv->NewObject(nativeProxyClass.get(), nativeProxyConstructor, shared_ptr_val);
jniExceptionCheck(jniEnv);
to_encapsulate.release();
return nativeProxy;
// Case 2 - already a JavaProxy. Only possible if Self::JavaProxy exists.
if (jobject impl = _unwrapJavaProxy<Self>(&c)) {
return jniEnv->NewLocalRef(impl);
}
// Cases 3 and 4.
assert(m_cppProxyClass);
return JniCppProxyCache::get(c, jniEnv, m_cppProxyClass, &newCppProxy);
}
/*
* Given a Java object, find or create a C++ version. The cases here are:
* 1. Null
* 2. The provided Java object is actually a CppProxy (Java-side proxy for a C++ impl)
* 3. The provided Java object has an existing JavaProxy (C++-side proxy for a Java impl)
* 4. The provided Java object needs a new JavaProxy allocated
*/
std::shared_ptr<I> _fromJava(JNIEnv* jniEnv, jobject j) const {
if (j == 0) {
// Case 1 - null
if (!j) {
return nullptr;
}
jlong packed_raw_shared_ptr = jniEnv->GetLongField(j, nativeProxyField);
// Case 2 - already a Java proxy; we just need to pull the C++ impl out. (This case
// is only possible if we were constructed with a cppProxyClassName parameter.)
if (m_cppProxyClass
&& jniEnv->IsSameObject(jniEnv->GetObjectClass(j), m_cppProxyClass.clazz.get())) {
jlong handle = jniEnv->GetLongField(j, m_cppProxyClass.idField);
jniExceptionCheck(jniEnv);
return *reinterpret_cast<const std::shared_ptr<I> *>(packed_raw_shared_ptr);
return CppProxyHandle<I>::get(handle);
}
JniInterfaceCppExt(const char* nativeProxyClassName) :
nativeProxyClass(jniFindClass(nativeProxyClassName)),
nativeProxyConstructor(jniGetMethodID(nativeProxyClass.get(), "<init>", "(J)V")),
nativeProxyField(jniGetFieldID(nativeProxyClass.get(), "nativeRef", "J"))
{}
// Cases 3 and 4 - see _getJavaProxy helper below. JavaProxyCache is responsible for
// distinguishing between the two cases. Only possible if Self::JavaProxy exists.
return _getJavaProxy<Self>(j);
}
const GlobalRef<jclass> nativeProxyClass;
const jmethodID nativeProxyConstructor;
const jfieldID nativeProxyField;
};
// Constructor for interfaces for which a Java-side CppProxy class exists
JniInterface(const char * cppProxyClassName) : m_cppProxyClass(cppProxyClassName) {}
template <class I, class Self>
class JniInterfaceJavaExt {
public:
std::shared_ptr<I> _fromJava(JNIEnv* /*jniEnv*/, jobject j) const {
if (j == 0) {
// Constructor for interfaces without a Java proxy class
JniInterface() : m_cppProxyClass{} {}
private:
/*
* Helpers for _toJava above. The possibility that an object is already a C++-side proxy
* only exists if the code generator emitted one (if Self::JavaProxy exists).
*/
template <typename S, typename = typename S::JavaProxy>
jobject _unwrapJavaProxy(const std::shared_ptr<I> * c) const {
if (auto proxy = dynamic_cast<typename S::JavaProxy *>(c->get())) {
return proxy->getGlobalRef();
} else {
return nullptr;
}
}
template <typename S>
jobject _unwrapJavaProxy(...) const {
return nullptr;
}
return JavaProxyCache<typename Self::JavaProxy>::get(j);
/*
* Helper for _toJava above: given a C++ object, allocate a CppProxy on the Java side for
* it. This is actually called by jniCppProxyCacheGet, which holds a lock on the global
* C++-to-Java proxy map object.
*/
static jobject newCppProxy(const std::shared_ptr<void> & cppObj,
JNIEnv * jniEnv,
const CppProxyClassInfo & proxyClass) {
std::unique_ptr<CppProxyHandle<I>> to_encapsulate(
new CppProxyHandle<I>(std::static_pointer_cast<I>(cppObj)));
jlong handle = static_cast<jlong>(reinterpret_cast<uintptr_t>(to_encapsulate.get()));
jobject cppProxy = jniEnv->NewObject(proxyClass.clazz.get(),
proxyClass.constructor,
handle);
jniExceptionCheck(jniEnv);
to_encapsulate.release();
return cppProxy;
}
/*
* Helpers for _fromJava above. We can only produce a C++-side proxy if the code generator
* emitted one (if Self::JavaProxy exists).
*/
template <typename S, typename = typename S::JavaProxy>
std::shared_ptr<I> _getJavaProxy(jobject j) const {
return JavaProxyCache<typename S::JavaProxy>::get(j);
}
template <typename S>
std::shared_ptr<I> _getJavaProxy(...) const {
assert(false);
return nullptr;
}
const CppProxyClassInfo m_cppProxyClass;
};
/*
......
......@@ -6,5 +6,6 @@
@import "exception.djinni"
@import "client_interface.djinni"
@import "enum.djinni"
@import "token.djinni"
@import "test.djinni"
@import "inttypes.djinni"
......@@ -20,6 +20,11 @@ test_helpers = interface +c {
static check_enum_map(m: map<color, string>);
static token_id(t: token): token;
static create_cpp_token(): token;
static check_cpp_token(t: token);
static cpp_token_id(t: token): i64;
static return_none(): optional<i32>;
# Ensures that we generate integer translation code
......
token = interface +c +j {
}
......@@ -16,6 +16,7 @@
#include <unordered_map>
class ClientInterface;
class Token;
class TestHelpers {
public:
......@@ -51,6 +52,14 @@ public:
static void check_enum_map(const std::unordered_map<color, std::string> & m);
static std::shared_ptr<Token> token_id(const std::shared_ptr<Token> & t);
static std::shared_ptr<Token> create_cpp_token();
static void check_cpp_token(const std::shared_ptr<Token> & t);
static int64_t cpp_token_id(const std::shared_ptr<Token> & t);
static std::experimental::optional<int32_t> return_none();
/** Ensures that we generate integer translation code */
......
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
#pragma once
class Token {
public:
virtual ~Token() {}
};
......@@ -10,12 +10,12 @@ public abstract class CppException {
public static native CppException get();
public static final class NativeProxy extends CppException
public static final class CppProxy extends CppException
{
private final long nativeRef;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private NativeProxy(long nativeRef)
private CppProxy(long nativeRef)
{
if (nativeRef == 0) throw new RuntimeException("nativeRef is zero");
this.nativeRef = nativeRef;
......
......@@ -37,17 +37,25 @@ public abstract class TestHelpers {
public static native void checkEnumMap(HashMap<Color, String> m);
public static native Token tokenId(Token t);
public static native Token createCppToken();
public static native void checkCppToken(Token t);
public static native long cppTokenId(Token t);
public static native Integer returnNone();
/** Ensures that we generate integer translation code */
public static native AssortedIntegers assortedIntegersId(AssortedIntegers i);
public static final class NativeProxy extends TestHelpers
public static final class CppProxy extends TestHelpers
{
private final long nativeRef;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private NativeProxy(long nativeRef)
private CppProxy(long nativeRef)
{
if (nativeRef == 0) throw new RuntimeException("nativeRef is zero");
this.nativeRef = nativeRef;
......
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
package com.dropbox.djinni.test;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class Token {
public static final class CppProxy extends Token
{
private final long nativeRef;
private final AtomicBoolean destroyed = new AtomicBoolean(false);
private CppProxy(long nativeRef)
{
if (nativeRef == 0) throw new RuntimeException("nativeRef is zero");
this.nativeRef = nativeRef;
}
private native void nativeDestroy(long nativeRef);
public void destroy()
{
boolean destroyed = this.destroyed.getAndSet(true);
if (!destroyed) nativeDestroy(this.nativeRef);
}
protected void finalize() throws java.lang.Throwable
{
destroy();
super.finalize();
}
}
}
......@@ -7,7 +7,7 @@
namespace djinni_generated {
NativeClientInterface::NativeClientInterface() : djinni::JniInterfaceJavaExt<ClientInterface, NativeClientInterface>() {}
NativeClientInterface::NativeClientInterface() : djinni::JniInterface<ClientInterface, NativeClientInterface>() {}
NativeClientInterface::JavaProxy::JavaProxy(jobject obj) : JavaProxyCacheEntry(obj) {}
......
......@@ -8,11 +8,12 @@
namespace djinni_generated {
class NativeClientInterface final : djinni::JniInterfaceJavaExt<ClientInterface, NativeClientInterface> {
class NativeClientInterface final : djinni::JniInterface<ClientInterface, NativeClientInterface> {
public:
using CppType = std::shared_ptr<ClientInterface>;
using JniType = jobject;
static jobject toJava(JNIEnv* jniEnv, std::shared_ptr<ClientInterface> c) { return djinni::JniClass<::djinni_generated::NativeClientInterface>::get()._toJava(jniEnv, c); }
static std::shared_ptr<ClientInterface> fromJava(JNIEnv* jniEnv, jobject j) { return djinni::JniClass<::djinni_generated::NativeClientInterface>::get()._fromJava(jniEnv, j); }
const djinni::GlobalRef<jclass> clazz { djinni::jniFindClass("com/dropbox/djinni/test/ClientInterface") };
......@@ -25,7 +26,7 @@ public:
private:
using djinni::JavaProxyCacheEntry::getGlobalRef;
friend class djinni::JniInterfaceJavaExt<ClientInterface, ::djinni_generated::NativeClientInterface>;
friend class djinni::JniInterface<ClientInterface, ::djinni_generated::NativeClientInterface>;
friend class djinni::JavaProxyCache<JavaProxy>;
};
......
......@@ -7,23 +7,23 @@
namespace djinni_generated {
NativeCppException::NativeCppException() : djinni::JniInterfaceCppExt<CppException>("com/dropbox/djinni/test/CppException$NativeProxy") {}
NativeCppException::NativeCppException() : djinni::JniInterface<CppException, NativeCppException>("com/dropbox/djinni/test/CppException$CppProxy") {}
using namespace ::djinni_generated;
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_CppException_00024NativeProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_CppException_00024CppProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
delete reinterpret_cast<std::shared_ptr<CppException>*>(nativeRef);
delete reinterpret_cast<djinni::CppProxyHandle<CppException>*>(nativeRef);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
CJNIEXPORT jint JNICALL Java_com_dropbox_djinni_test_CppException_00024NativeProxy_native_1throwAnException(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
CJNIEXPORT jint JNICALL Java_com_dropbox_djinni_test_CppException_00024CppProxy_native_1throwAnException(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const std::shared_ptr<CppException> & ref = *reinterpret_cast<const std::shared_ptr<CppException>*>(nativeRef);
const std::shared_ptr<CppException> & ref = djinni::CppProxyHandle<CppException>::get(nativeRef);
int32_t cr = ref->throw_an_exception();
......
......@@ -8,7 +8,7 @@
namespace djinni_generated {
class NativeCppException final : djinni::JniInterfaceCppExt<CppException> {
class NativeCppException final : djinni::JniInterface<CppException, NativeCppException> {
public:
using CppType = std::shared_ptr<CppException>;
using JniType = jobject;
......
......@@ -15,18 +15,19 @@
#include "NativeNestedCollection.hpp"
#include "NativePrimitiveList.hpp"
#include "NativeSetRecord.hpp"
#include "NativeToken.hpp"
namespace djinni_generated {
NativeTestHelpers::NativeTestHelpers() : djinni::JniInterfaceCppExt<TestHelpers>("com/dropbox/djinni/test/TestHelpers$NativeProxy") {}
NativeTestHelpers::NativeTestHelpers() : djinni::JniInterface<TestHelpers, NativeTestHelpers>("com/dropbox/djinni/test/TestHelpers$CppProxy") {}
using namespace ::djinni_generated;
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_TestHelpers_00024NativeProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_TestHelpers_00024CppProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
delete reinterpret_cast<std::shared_ptr<TestHelpers>*>(nativeRef);
delete reinterpret_cast<djinni::CppProxyHandle<TestHelpers>*>(nativeRef);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
......@@ -198,6 +199,51 @@ CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_TestHelpers_checkEnumMap(JN
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
CJNIEXPORT jobject JNICALL Java_com_dropbox_djinni_test_TestHelpers_tokenId(JNIEnv* jniEnv, jobject /*this*/, jobject j_t)
{
try {
DJINNI_FUNCTION_PROLOGUE0(jniEnv);
std::shared_ptr<Token> c_t = NativeToken::fromJava(jniEnv, j_t);
std::shared_ptr<Token> cr = TestHelpers::token_id(c_t);
return NativeToken::toJava(jniEnv, cr);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */ )
}
CJNIEXPORT jobject JNICALL Java_com_dropbox_djinni_test_TestHelpers_createCppToken(JNIEnv* jniEnv, jobject /*this*/)
{
try {
DJINNI_FUNCTION_PROLOGUE0(jniEnv);
std::shared_ptr<Token> cr = TestHelpers::create_cpp_token();
return NativeToken::toJava(jniEnv, cr);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */ )
}
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_TestHelpers_checkCppToken(JNIEnv* jniEnv, jobject /*this*/, jobject j_t)
{
try {
DJINNI_FUNCTION_PROLOGUE0(jniEnv);
std::shared_ptr<Token> c_t = NativeToken::fromJava(jniEnv, j_t);
TestHelpers::check_cpp_token(c_t);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
CJNIEXPORT jlong JNICALL Java_com_dropbox_djinni_test_TestHelpers_cppTokenId(JNIEnv* jniEnv, jobject /*this*/, jobject j_t)
{
try {
DJINNI_FUNCTION_PROLOGUE0(jniEnv);
std::shared_ptr<Token> c_t = NativeToken::fromJava(jniEnv, j_t);
int64_t cr = TestHelpers::cpp_token_id(c_t);
return ::djinni::HI64::Unboxed::toJava(jniEnv, cr);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, 0 /* value doesn't matter */ )
}
CJNIEXPORT jobject JNICALL Java_com_dropbox_djinni_test_TestHelpers_returnNone(JNIEnv* jniEnv, jobject /*this*/)
{
try {
......
......@@ -8,7 +8,7 @@
namespace djinni_generated {
class NativeTestHelpers final : djinni::JniInterfaceCppExt<TestHelpers> {
class NativeTestHelpers final : djinni::JniInterface<TestHelpers, NativeTestHelpers> {
public:
using CppType = std::shared_ptr<TestHelpers>;
using JniType = jobject;
......
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
#include "NativeToken.hpp" // my header
namespace djinni_generated {
NativeToken::NativeToken() : djinni::JniInterface<Token, NativeToken>("com/dropbox/djinni/test/Token$CppProxy") {}
NativeToken::JavaProxy::JavaProxy(jobject obj) : JavaProxyCacheEntry(obj) {}
using namespace ::djinni_generated;
CJNIEXPORT void JNICALL Java_com_dropbox_djinni_test_Token_00024CppProxy_nativeDestroy(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
delete reinterpret_cast<djinni::CppProxyHandle<Token>*>(nativeRef);
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}
} // namespace djinni_generated
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
#pragma once
#include "djinni_support.hpp"
#include "token.hpp"
namespace djinni_generated {
class NativeToken final : djinni::JniInterface<Token, NativeToken> {
public:
using CppType = std::shared_ptr<Token>;
using JniType = jobject;
static jobject toJava(JNIEnv* jniEnv, std::shared_ptr<Token> c) { return djinni::JniClass<::djinni_generated::NativeToken>::get()._toJava(jniEnv, c); }
static std::shared_ptr<Token> fromJava(JNIEnv* jniEnv, jobject j) { return djinni::JniClass<::djinni_generated::NativeToken>::get()._fromJava(jniEnv, j); }
const djinni::GlobalRef<jclass> clazz { djinni::jniFindClass("com/dropbox/djinni/test/Token") };
class JavaProxy final : djinni::JavaProxyCacheEntry, public Token {
public:
JavaProxy(jobject obj);
private:
using djinni::JavaProxyCacheEntry::getGlobalRef;
friend class djinni::JniInterface<Token, ::djinni_generated::NativeToken>;
friend class djinni::JavaProxyCache<JavaProxy>;
};
private:
NativeToken();
friend class djinni::JniClass<::djinni_generated::NativeToken>;
};
} // namespace djinni_generated
......@@ -3,6 +3,7 @@
#import "DBClientInterface.h"
#import "DBColor.h"
#import "DBToken.h"
#import <Foundation/Foundation.h>
@class DBAssortedIntegers;
@class DBMapListRecord;
......@@ -43,6 +44,14 @@
+ (void)checkEnumMap:(NSMutableDictionary *)m;
+ (id <DBToken>)tokenId:(id <DBToken>)t;
+ (id <DBToken>)createCppToken;
+ (void)checkCppToken:(id <DBToken>)t;
+ (int64_t)cppTokenId:(id <DBToken>)t;
+ (NSNumber *)returnNone;
/** Ensures that we generate integer translation code */
......
......@@ -11,6 +11,7 @@
#import "DBPrimitiveList+Private.h"
#import "DBSetRecord+Private.h"
#import "DBTestHelpers.h"
#import "DBTokenCppProxy+Private.h"
#import "DJIError.h"
#include <exception>
#include <utility>
......@@ -191,6 +192,39 @@ static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for th
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (id <DBToken>)tokenId:(id <DBToken>)t {
try {
std::shared_ptr<Token> cppT = [(DBTokenCppProxy *)t cppRef];
std::shared_ptr<Token> cppRet = TestHelpers::token_id(std::move(cppT));
id <DBToken> objcRet = [DBTokenCppProxy tokenWithCpp:cppRet];
return objcRet;
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (id <DBToken>)createCppToken {
try {
std::shared_ptr<Token> cppRet = TestHelpers::create_cpp_token();
id <DBToken> objcRet = [DBTokenCppProxy tokenWithCpp:cppRet];
return objcRet;
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (void)checkCppToken:(id <DBToken>)t {
try {
std::shared_ptr<Token> cppT = [(DBTokenCppProxy *)t cppRef];
TestHelpers::check_cpp_token(std::move(cppT));
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (int64_t)cppTokenId:(id <DBToken>)t {
try {
std::shared_ptr<Token> cppT = [(DBTokenCppProxy *)t cppRef];
int64_t cppRet = TestHelpers::cpp_token_id(std::move(cppT));
int64_t objcRet = cppRet;
return objcRet;
} DJINNI_TRANSLATE_EXCEPTIONS()
}
+ (NSNumber *)returnNone {
try {
std::experimental::optional<int32_t> cppRet = TestHelpers::return_none();
......
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
@protocol DBToken
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
#import "DBToken.h"
#import "DBTokenCppProxy.h"
#include "token.hpp"
#import "DJICppWrapperCache+Private.h"
#import <Foundation/Foundation.h>
#include <memory>
@interface DBTokenCppProxy ()
@property (nonatomic, readonly) std::shared_ptr<Token> cppRef;
+ (id)tokenWithCpp:(const std::shared_ptr<Token> &)cppRef;
- (id)initWithCpp:(const std::shared_ptr<Token> &)cppRef;
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
#import "DBToken.h"
#import <Foundation/Foundation.h>
@interface DBTokenCppProxy : NSObject <DBToken>
@end
// AUTOGENERATED FILE - DO NOT MODIFY!
// This file generated by Djinni from token.djinni
#import "DBTokenCppProxy+Private.h"
#import "DBToken.h"
#import "DJIError.h"
#include <exception>
#include <utility>
static_assert(__has_feature(objc_arc), "Djinni requires ARC to be enabled for this file");
@implementation DBTokenCppProxy
- (id)initWithCpp:(const std::shared_ptr<Token> &)cppRef
{
if (self = [super init]) {
_cppRef = cppRef;
}
return self;
}
- (void)dealloc
{
djinni::DbxCppWrapperCache<Token> & cache = djinni::DbxCppWrapperCache<Token>::getInstance();
cache.remove(_cppRef);
}
+ (id)tokenWithCpp:(const std::shared_ptr<Token> &)cppRef
{
djinni::DbxCppWrapperCache<Token> & cache = djinni::DbxCppWrapperCache<Token>::getInstance();
return cache.get(cppRef, [] (const std::shared_ptr<Token> & p) { return [[DBTokenCppProxy alloc] initWithCpp:p]; });
}
@end
#include "test_helpers.hpp"
#include "client_returned_record.hpp"
#include "client_interface.hpp"
#include "token.hpp"
#include <exception>
SetRecord TestHelpers::get_set_record() {
......@@ -85,6 +86,25 @@ void TestHelpers::check_client_interface_nonascii(const std::shared_ptr<ClientIn
}
}
std::shared_ptr<Token> TestHelpers::token_id(const std::shared_ptr<Token> & in) {
return in;
}
class CppToken : public Token {};
std::shared_ptr<Token> TestHelpers::create_cpp_token() {
return std::make_shared<CppToken>();
}
void TestHelpers::check_cpp_token(const std::shared_ptr<Token> & in) {
// Throws bad_cast if type is wrong
(void)dynamic_cast<CppToken &>(*in);
}
int64_t TestHelpers::cpp_token_id(const std::shared_ptr<Token> & in) {
return reinterpret_cast<int64_t>(in.get());
}
std::experimental::optional<int32_t> TestHelpers::return_none() {
return {};
}
......
......@@ -16,6 +16,7 @@ public class AllTests extends TestSuite {
mySuite.addTestSuite(ClientInterfaceTest.class);
mySuite.addTestSuite(EnumTest.class);
mySuite.addTestSuite(IntegerTest.class);
mySuite.addTestSuite(TokenTest.class);
return mySuite;
}
......
package com.dropbox.djinni.test;
import junit.framework.TestCase;
public class TokenTest extends TestCase {
private class JavaToken extends Token {
}
@Override
protected void setUp() {
}
public void testTokens() {
Token jt = new JavaToken();
assertSame(TestHelpers.tokenId(jt), jt);
}
public void testNullToken() {
assertSame(TestHelpers.tokenId(null), null);
}
public void testCppToken() {
Token ct = TestHelpers.createCppToken();
assertSame(TestHelpers.tokenId(ct), ct);
TestHelpers.checkCppToken(ct);
ct = null;
System.gc();
System.runFinalization();
}
public void testNotCppToken() {
boolean threw = false;
try {
TestHelpers.checkCppToken(new JavaToken());
} catch (RuntimeException e) {
threw = true;
}
assertTrue(threw);
System.gc();
}
}
......@@ -38,6 +38,7 @@
A2059B6219DF7DDE006A2896 /* DBColorTranslator.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2059B6019DF7DDE006A2896 /* DBColorTranslator.mm */; };
A227B6A91A0D79F10003BBAC /* assorted_integers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A227B6A71A0D79F10003BBAC /* assorted_integers.cpp */; };
A227B6AD1A0D7A290003BBAC /* DBAssortedIntegers.mm in Sources */ = {isa = PBXBuildFile; fileRef = A227B6AB1A0D7A290003BBAC /* DBAssortedIntegers.mm */; };
A21557A91A16DC5000084E93 /* DBTokenCppProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = A21557A71A16DC5000084E93 /* DBTokenCppProxy.mm */; };
A278D45019BA33CB006FD937 /* DBTestHelpersCppProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = A278D44E19BA33CB006FD937 /* DBTestHelpersCppProxy.mm */; };
A278D45319BA3601006FD937 /* test_helpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A278D45219BA3601006FD937 /* test_helpers.cpp */; };
A2CB54B419BA6E6000A9E600 /* DJIError.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2CB54B319BA6E6000A9E600 /* DJIError.mm */; };
......@@ -142,12 +143,17 @@
A227B6AA1A0D7A290003BBAC /* DBAssortedIntegers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBAssortedIntegers.h; sourceTree = "<group>"; };
A227B6AB1A0D7A290003BBAC /* DBAssortedIntegers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DBAssortedIntegers.mm; sourceTree = "<group>"; };
A227B6AC1A0D7A290003BBAC /* DBAssortedIntegers+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DBAssortedIntegers+Private.h"; sourceTree = "<group>"; };
A21557A51A16DC5000084E93 /* DBToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBToken.h; sourceTree = "<group>"; };
A21557A61A16DC5000084E93 /* DBTokenCppProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBTokenCppProxy.h; sourceTree = "<group>"; };
A21557A71A16DC5000084E93 /* DBTokenCppProxy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DBTokenCppProxy.mm; sourceTree = "<group>"; };
A21557A81A16DC5000084E93 /* DBTokenCppProxy+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DBTokenCppProxy+Private.h"; sourceTree = "<group>"; };
A278D44C19BA33CB006FD937 /* DBTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBTestHelpers.h; sourceTree = "<group>"; };
A278D44D19BA33CB006FD937 /* DBTestHelpersCppProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DBTestHelpersCppProxy.h; sourceTree = "<group>"; };
A278D44E19BA33CB006FD937 /* DBTestHelpersCppProxy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DBTestHelpersCppProxy.mm; sourceTree = "<group>"; };
A278D44F19BA33CB006FD937 /* DBTestHelpersCppProxy+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "DBTestHelpersCppProxy+Private.h"; sourceTree = "<group>"; };
A278D45119BA3428006FD937 /* test_helpers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = test_helpers.hpp; sourceTree = "<group>"; };
A278D45219BA3601006FD937 /* test_helpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_helpers.cpp; sourceTree = "<group>"; };
A2CAD0871A16CA1800080DF0 /* token.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = token.hpp; sourceTree = "<group>"; };
A2CB54B319BA6E6000A9E600 /* DJIError.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DJIError.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
......@@ -265,6 +271,7 @@
children = (
A227B6A71A0D79F10003BBAC /* assorted_integers.cpp */,
A227B6A81A0D79F10003BBAC /* assorted_integers.hpp */,
A2CAD0871A16CA1800080DF0 /* token.hpp */,
A2059B5E19DF7DBA006A2896 /* color.hpp */,
A278D45119BA3428006FD937 /* test_helpers.hpp */,
65BAE6D719A6C7100035F775 /* client_interface.hpp */,
......@@ -290,6 +297,10 @@
A227B6AA1A0D7A290003BBAC /* DBAssortedIntegers.h */,
A227B6AB1A0D7A290003BBAC /* DBAssortedIntegers.mm */,
A227B6AC1A0D7A290003BBAC /* DBAssortedIntegers+Private.h */,
A21557A51A16DC5000084E93 /* DBToken.h */,
A21557A61A16DC5000084E93 /* DBTokenCppProxy.h */,
A21557A71A16DC5000084E93 /* DBTokenCppProxy.mm */,
A21557A81A16DC5000084E93 /* DBTokenCppProxy+Private.h */,
A2059B5F19DF7DDE006A2896 /* DBColor.h */,
A2059B6019DF7DDE006A2896 /* DBColorTranslator.mm */,
A2059B6119DF7DDE006A2896 /* DBColorTranslator+Private.h */,
......@@ -416,6 +427,7 @@
files = (
65BAE70B19A6C7100035F775 /* DBPrimitiveList.mm in Sources */,
6536CD6F19A6C82200DD7715 /* DJIWeakPtrWrapper.mm in Sources */,
A21557A91A16DC5000084E93 /* DBTokenCppProxy.mm in Sources */,
65BAE70819A6C7100035F775 /* DBMapListRecord.mm in Sources */,
65BAE70919A6C7100035F775 /* DBMapRecord.mm in Sources */,
A278D45319BA3601006FD937 /* test_helpers.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