Skip to content

Commit

Permalink
Added a templated implicit covnersion overload to all internal fake-j…
Browse files Browse the repository at this point in the history
…ni types to allow for conversions to JNI types
  • Loading branch information
Matthewacon committed Sep 21, 2019
1 parent 7113fe6 commit f072e5c
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 34 deletions.
2 changes: 1 addition & 1 deletion CX
Submodule CX updated 1 files
+52 −0 cx/unsafe.h
54 changes: 53 additions & 1 deletion include/fake-jni/array.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,42 @@ namespace FakeJni {\
static constexpr const auto signature = JniTypeBase<fake_object>::signature;\
static constexpr const bool hasComplexHierarchy = JniTypeBase<fake_object>::hasComplexHierarchy;\
};\
template<>\
class JniTypeBase<jni_struct> {\
public:\
static constexpr const bool isRegisteredType = JniTypeBase<fake_object>::isRegisteredType;\
static constexpr const bool isClass = JniTypeBase<fake_object>::isClass;\
static constexpr const auto signature = JniTypeBase<fake_object>::signature;\
static constexpr const bool hasComplexHierarchy = JniTypeBase<fake_object>::hasComplexHierarchy;\
};\
}\
class fake_object : public JArray<fake_object>, public jni_struct {\
class fake_object : public JArray<fake_object> {\
public:\
template<typename T>\
operator T() const;\
using JArray<fake_object>::JArray;\
inline static const JClass * getDescriptor() noexcept {\
return JArray<fake_object>::getDescriptor();\
}\
};\
template<typename T>\
fake_object::operator T() const {\
using component_t = typename CX::ComponentTypeResolver<T>::type;\
constexpr const auto \
downcast = __is_base_of(fake_object, component_t),\
upcast = __is_base_of(component_t, fake_object),\
jnicast = CX::MatchAny<component_t, _jobject, _jarray, jni_struct>::value;\
static_assert(\
downcast || upcast || jnicast,\
#fake_object " can only be upcast, downcast, or converted to _jobject, _jarray or " #jni_struct\
);\
auto ptr = const_cast<fake_object *>(this);\
if constexpr(downcast || upcast) {\
return (T&)*ptr;\
} else if constexpr(jnicast) {\
return CX::union_cast<T>(this)();\
}\
}\
}\
template<>\
const FakeJni::JClass FakeJni::JArray<FakeJni::fake_object>::descriptor;
Expand Down Expand Up @@ -127,6 +155,9 @@ namespace FakeJni {
static JInt boundsCheck(JInt len);

public:
template<typename C>
operator C() const;

JArray(std::initializer_list<component>);
explicit JArray(const JArray<T> & array);
explicit JArray(JInt size);
Expand Down Expand Up @@ -168,6 +199,27 @@ namespace FakeJni {
};

//Immutable JArray template members
template<typename T>
template<typename C>
JArray<const T>::operator C() const {
using array_t = JArray<const T>;
using component_t = typename CX::ComponentTypeResolver<T>::type;
constexpr const auto
downcast = __is_base_of(array_t, component_t),
upcast = __is_base_of(component_t, array_t),
jnicast = CX::MatchAny<component_t, _jobject, _jarray>::value;
static_assert(
downcast || upcast || jnicast,
"JString can only be upcast, downcast, or converted to _jobject or _jstring!"
);
auto ptr = const_cast<array_t *>(this);
if constexpr(upcast || downcast) {
return (T&)*ptr;
} else if constexpr (jnicast) {
return CX::union_cast<T>(this)();
}
}

template<typename T>
inline JInt JArray<const T>::boundsCheck(JInt len) {
if (len < 0) {
Expand Down
21 changes: 21 additions & 0 deletions include/fake-jni/internal/meta/meta.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "jni.h"

#include <cx/idioms.h>
#include <cx/classes.h>

Expand All @@ -26,6 +28,25 @@ namespace FakeJni::_CX {
//Stub type for generating pointer-to-member types
class AnyClass;

//Jni-class identity template
template<typename T>
struct IsJniClass : CX::false_type {};

template<> struct IsJniClass<_jobject> : CX::true_type {};
template<> struct IsJniClass<_jclass> : CX::true_type {};
template<> struct IsJniClass<_jthrowable> : CX::true_type {};
template<> struct IsJniClass<_jstring> : CX::true_type {};
template<> struct IsJniClass<_jarray> : CX::true_type {};
template<> struct IsJniClass<_jbooleanArray> : CX::true_type {};
template<> struct IsJniClass<_jbyteArray> : CX::true_type {};
template<> struct IsJniClass<_jcharArray> : CX::true_type {};
template<> struct IsJniClass<_jshortArray> : CX::true_type {};
template<> struct IsJniClass<_jintArray> : CX::true_type {};
template<> struct IsJniClass<_jlongArray> : CX::true_type {};
template<> struct IsJniClass<_jfloatArray> : CX::true_type {};
template<> struct IsJniClass<_jdoubleArray> : CX::true_type {};
template<> struct IsJniClass<_jobjectArray> : CX::true_type {};

//JNI Type metadata template
template<typename>
class JniTypeBase {
Expand Down
14 changes: 0 additions & 14 deletions include/fake-jni/internal/meta/method.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,6 @@ namespace FakeJni {
}
};

template<typename T>
class VerifyJniFunctionArguments<T> {
public:
[[gnu::always_inline]]
inline static constexpr bool verify() {
using resolver = ComponentTypeResolver<T>;
if constexpr(__is_class(typename resolver::type)) {
return __is_base_of(_jobject, typename resolver::type) && resolver::indirectionCount == 1U;
} else {
return JniTypeBase<T>::isRegisteredType && resolver::indirectionCount == 0U;
}
}
};

template<>
class VerifyJniFunctionArguments<> {
public:
Expand Down
66 changes: 59 additions & 7 deletions include/fake-jni/jvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "cx/classes.h"
#include "cx/idioms.h"
#include "cx/strings.h"
#include "cx/unsafe.h"

#include <ffi.h>

Expand All @@ -31,7 +32,7 @@
//Internal JFieldID macros
#define _ASSERT_FIELD_JNI_COMPLIANCE \
static_assert(\
__is_base_of(JObject, T) || _CX::JniTypeBase<T>::isRegisteredType,\
__is_base_of(_jobject, T) || _CX::JniTypeBase<T>::isRegisteredType,\
"Field type is not a valid JNI type!"\
);\
static_assert(\
Expand All @@ -43,11 +44,11 @@ static_assert(\
#define _ASSERT_JNI_FUNCTION_COMPLIANCE \
static_assert(\
_CX::VerifyJniFunctionArguments<R>::verify(),\
"Registered JNI functions may only return JNI types and pointers to _jobject or derived classes!"\
"Registered JNI functions may only return JNI types and pointers to JObject or derived classes!"\
);\
static_assert(\
_CX::VerifyJniFunctionArguments<Args...>::verify(),\
"Registered JNI functions may only accept JNI types and pointers to _jobject or derived classes!"\
"Registered JNI functions may only accept JNI types and pointers to JObject or derived classes!"\
);

#define _INTERNAL_INVOKE_VA_ARG(type, ffi_type) \
Expand Down Expand Up @@ -755,8 +756,8 @@ namespace FakeJni {

class JClass;

//JNI _jobject and java/lang/Object implementation
class JObject : public _jclass {
//Jni java/lang/Object implementation
class JObject {
public:
//Internal fake-jni native class metadata
//DEFINE_CLASS_NAME cant be used since this is the virtual base
Expand All @@ -767,6 +768,9 @@ namespace FakeJni {
return &descriptor;
}

template<typename T>
operator T() const;

JObject() = default;
virtual ~JObject() = default;
virtual const JClass & getClass() const noexcept;
Expand Down Expand Up @@ -999,7 +1003,10 @@ namespace FakeJni {
//Internal fake-jni native class metadata
DEFINE_CLASS_NAME("java/lang/Class")

using cast = CX::ExplicitCastGenerator<JClass, JObject, _jobject>;
// using cast = CX::ExplicitCastGenerator<JClass, JObject, _jobject>;

template<typename T>
operator T() const;

const uint32_t modifiers;
const JClass& parent;
Expand Down Expand Up @@ -1190,6 +1197,21 @@ namespace FakeJni {
//Static assertion will fail if base is not a compliant type
static constexpr const BaseTypeAssertion assert{};
};

template<typename T>
class VerifyJniFunctionArguments<T> {
public:
[[gnu::always_inline]]
inline static constexpr bool verify() {
using resolver = ComponentTypeResolver<T>;
using arg_t = typename resolver::type;
if constexpr(__is_class(arg_t)) {
return __is_base_of(JObject, arg_t) && resolver::indirectionCount == 1U;
} else {
return JniTypeBase<T>::isRegisteredType && resolver::indirectionCount == 0U;
}
}
};
}
}

Expand All @@ -1198,6 +1220,17 @@ DECLARE_NATIVE_TYPE(FakeJni::JClass)

//fake-jni API definitions
namespace FakeJni {
//JObject template members
template<typename T>
JObject::operator T() const {
using component_t = typename CX::ComponentTypeResolver<T>::type;
constexpr const auto downcast = __is_base_of(JObject, T);
static_assert(
downcast || CX::IsSame<_jobject, component_t>::value,
"JObject can only be downcasted and converted to _jobject!"
);
}

//JFieldID template members
template<typename T, typename M>
JFieldID::JFieldID(T M::* const member, const char * const name, const uint32_t modifiers) noexcept :
Expand Down Expand Up @@ -1530,7 +1563,7 @@ namespace FakeJni {

[[gnu::always_inline]]
inline static componentType* getAArg(jvalue *values) {
static_assert(__is_base_of(_jobject, componentType), "Illegal JNI function parameter type!");
static_assert(__is_base_of(JObject, componentType), "Illegal JNI function parameter type!");
if constexpr(CastDefined<componentType>::value) {
return componentType::cast::cast((JObject*)values->l);
} else {
Expand All @@ -1541,6 +1574,25 @@ namespace FakeJni {
}

//JClass template members
template<typename T>
JClass::operator T() const {
using component_t = typename CX::ComponentTypeResolver<T>::type;
constexpr const auto
downcast = __is_base_of(JClass, component_t),
upcast = __is_base_of(component_t, JClass),
jnicast = CX::MatchAny<component_t, _jobject, _jclass>::value;
static_assert(
downcast || upcast || jnicast,
"JClass can only be upcast, downcast, or converted to _jobject or _jclass!"
);
auto ptr = const_cast<JClass *>(this);
if constexpr(upcast || downcast) {
return (T&)*ptr;
} else if constexpr(jnicast) {
return CX::union_cast<T>(this)();
}
}

template<typename T>
JClass::JClass(uint32_t modifiers, _CX::JClassBreeder<T, true> breeder) noexcept :
JObject(),
Expand Down
31 changes: 28 additions & 3 deletions include/fake-jni/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@
#include "fake-jni/jvm.h"
#include "fake-jni/array.h"

//JNI _jstring and java/lang/String implementation
#include "cx/unsafe.h"

//JNI java/lang/String implementation
namespace FakeJni {
class JString : public _jstring, public JCharArray {
class JString : public JCharArray {
private:
static const JString EMPTY_STR;

JInt length;

public:
DEFINE_CLASS_NAME("java/lang/String")
using cast = typename CX::ExplicitCastGenerator<JString, JCharArray, JClass, JObject>;
// using cast = typename CX::ExplicitCastGenerator<JString, JCharArray, JClass, JObject>;

template<typename T>
operator T() const;

static constexpr JString * const EMPTY = const_cast<JString *>(&EMPTY_STR);

Expand All @@ -32,10 +37,30 @@ namespace FakeJni {
JInt getLength() const;
};

template<typename T>
JString::operator T() const {
using component_t = typename CX::ComponentTypeResolver<T>::type;
constexpr const auto
downcast = __is_base_of(JString, component_t),
upcast = __is_base_of(component_t, JString),
jnicast = CX::MatchAny<component_t, _jobject, _jstring>::value;
static_assert(
downcast || upcast || jnicast,
"JString can only be upcast, downcast, or converted to _jobject or _jstring!"
);
auto ptr = const_cast<JString *>(this);
if constexpr(upcast || downcast) {
return (T&)*ptr;
} else if constexpr (jnicast) {
return CX::union_cast<T>(this)();
}
}

template<typename T>
bool JString::operator!=(const T operand) const {
return !operator==(operand);
}
}

DEFINE_JNI_TYPE(_jstring, "java/lang/String")
DECLARE_NATIVE_TYPE(FakeJni::JString)
6 changes: 4 additions & 2 deletions include/fake-jni/throwable.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

#include "fake-jni/jvm.h"

//JNI _jthrowable and java/lang/Throwable implementation
//JNI java/lang/Throwable implementation
namespace FakeJni {
//TODO this is unfinished
using JThrowable = _jthrowable;
}
}

DEFINE_JNI_TYPE(_jthrowable, "java/lang/Throwable")
8 changes: 6 additions & 2 deletions include/fake-jni/weak.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#pragma once

#include "jni.h"

#include "fake-jni/jvm.h"

//JNI _jweak and java/lang/ref/WeakReference<T> implementation
//JNI java/lang/ref/WeakReference<T> implementation
namespace FakeJni {
//TODO this is unfinished
using JWeak = JObject;
}
}

//DEFINE_JNI_TYPE(_jweak, "java/lang/ref/WeakReference")
7 changes: 5 additions & 2 deletions src/fake-jni/jni/native/field.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include "jni.h"

#include "fake-jni/jvm.h"

#include "cx/unsafe.h"

#include <stdexcept>

namespace FakeJni {
Expand All @@ -13,7 +16,7 @@ namespace FakeJni {
}

jobject NativeInterface::getObjectField(jobject jobj, jfieldID jfid) const {
return &((JFieldID *)jfid)->get<JObject>((JObject *)jobj);
return CX::union_cast<jobject>(&((JFieldID *)jfid)->get<JObject>((JObject *)jobj))();
}

jboolean NativeInterface::getBooleanField(jobject jobj, jfieldID jfid) const {
Expand Down Expand Up @@ -94,7 +97,7 @@ namespace FakeJni {
}

jobject NativeInterface::getStaticObjectField(jclass, jfieldID jfid) const {
return &((JFieldID *)jfid)->get<JObject>(nullptr);
return CX::union_cast<jobject>(&((JFieldID *)jfid)->get<JObject>(nullptr))();
}

jboolean NativeInterface::getStaticBooleanField(jclass, jfieldID jfid) const {
Expand Down

0 comments on commit f072e5c

Please sign in to comment.