Skip to content

Instantly share code, notes, and snippets.

@ochafik

ochafik/BEST.cc Secret

Last active November 3, 2022 20:34
Show Gist options
  • Select an option

  • Save ochafik/fe02564f0ce243fead9ec7d1e90ea0e0 to your computer and use it in GitHub Desktop.

Select an option

Save ochafik/fe02564f0ce243fead9ec7d1e90ea0e0 to your computer and use it in GitHub Desktop.
BridJ JNI trampolines using modern C++ (Variadic macros + lambdas) instead of hacky ASM
struct JNIEnv {};
typedef void *jobject;
#if 32BITS
#define SIGIL_PTR_VALUE1 0xFEDCBA91
#define SIGIL_PTR_VALUE2 0xFEDCBA92
#define SIGIL_PTR_VALUE3 0xFEDCBA93
#else
#define SIGIL_PTR_VALUE1 0xFEDCBA9876543211
#define SIGIL_PTR_VALUE2 0xFEDCBA9876543212
#define SIGIL_PTR_VALUE3 0xFEDCBA9876543213
#endif
// https://en.cppreference.com/w/cpp/language/parameter_pack
template <class RetType, class... ArgTypes>
RetType callTrampolineTemplate(JNIEnv* env, jobject obj, ArgTypes... args) {
typedef RetType (*FnType)(...ArgTypes);
return ((FnType)SIGIL_PTR_VALUE1)(&args...);
}
template <class RetType, class... ArgTypes>
RetType callbackTrampolineTemplate(ArgTypes... args) {
typedef RetType (*FnType)(jobject, ...ArgTypes);
return ((FnType)SIGIL_PTR_VALUE1)((jobject)SIGIL_PTR_VALUE2, &args...);
}
void* getCallTrampolineTemplate(const char* signature) {
static std::map<std::string, void*> templates;
if (templates.empty()) {
templates[")b"] = callTrampolineTemplate<bool>;
templates["i)b"] = callTrampolineTemplate<bool, int>;
templates["l)b"] = callTrampolineTemplate<bool, long>;
templates["ll)l"] = callTrampolineTemplate<long, long, long>;
//... autogenerate these!
}
return templates[signature];
}
void* getCallTrampoline(const char* signature, void *f) {
void *tmpl = getCallTrampolineTemplate(signature);
if (!tmpl) {
return nullptr;
}
return instantiateTemplateFunction1(tmpl, SIGIL_PTR_VALUE1, f);
}
void* getCallbackTrampolineTemplate(const char* signature) {
static std::map<std::string, void*> templates;
if (templates.empty()) {
templates[")b"] = callbackTrampolineTemplate<bool>;
templates["i)b"] = callbackTrampolineTemplate<bool, int>;
templates["l)b"] = callbackTrampolineTemplate<bool, long>;
templates["ll)l"] = callbackTrampolineTemplate<long, long, long>;
//... autogenerate these!
}
return templates[signature];
}
void* getCallbackTrampoline(const char* signature, JNIEnv *env, jobject obj, void *f) {
void *tmpl = getCallTrampolineTemplate(signature);
if (!tmpl) {
return nullptr;
}
void *caller = nullptr;
switch (getReturnSig(signature)) {
case 'i':
caller = env->CallIntMethod;
break;
//...
default:
return nullptr;
}
return instantiateTemplateFunction2(tmpl, SIGIL_PTR_VALUE1, caller, SIGIL_PTR_VALUE2, obj);
}
struct JNIEnv {};
typedef void *jobject;
typedef void *jobjectOrClass;
jfieldID = callbackPointerFieldId
// https://en.cppreference.com/w/cpp/language/parameter_pack
template <class RetType, class... ArgTypes>
RetType trampoline(JNIEnv* env, jobject obj, ArgTypes... args) {
jlong fptr = env->GetLongField(obj, callbackPointerFieldId);
typedef RetType (*FnType)(...ArgTypes);
FnType f = (FnType)(size_t)fptr;
return f(&args...);
}
void* org_bridj_JNI_getJNITampoline(JNIEnv *env, jclass clz, const char* signature) {
static std::map<std::string, void*> trampolines;
if (trampolines.empty()) {
trampolines[")b"] = trampoline<bool>;
trampolines["i)b"] = trampoline<bool, int>;
trampolines["l)b"] = trampoline<bool, long>;
trampolines["ll)l"] = trampoline<long, long, long>;
//... autogenerate these!
}
return trampolines[signature];
}
struct JNIEnv {};
typedef void *jobjectOrClass;
class JNITrampolineBase {
virtual ~JNITrampolineBase() {}
virtual void* getFunctionPointer() = 0;
};
template <class RetType, class... ArgTypes>
class JNITrampoline: public JNITrampolineBase {
typedef std::function<RetType (...ArgTypes)> LambdaType;
typedef RetType (*TargetFunctionType)(...ArgTypes);
typedef RetType (*TrampolineFunctionType)(JNIEnv*, jobjectOrClass, ...ArgTypes);
// FnType *fptr;
LambdaType _lambda;
JNITrampoline(LambdaType _lambda) : _lambda(lambda) {}
static JNITrampoline<RetType, ArgTypes...>* create(const TargetFunctionType* target) {
return new JNITrampoline<RetType, ArgTypes...>(
[=target](JNIEnv*, jobjectOrClass, ArgTypes... args) -> RetType {
return target(&args...);
});
}
// JNITrampoline(const LambdaType &lambda): _lambda(lambda) {}
virtual void* getFunctionPointer() override {
auto p = (size_t*)(void*)&_lambda;
// Skip 1 pointer at the beginning of the object
return p + 1;
}
virtual ~Trampoline() {}
};
// https://en.cppreference.com/w/cpp/language/parameter_pack
JNITrampolineBase* createJNITampoline(const char* signature, void *fptr) {
typedef JNITrampolineBase* (*TrampolineFactory)(void *);
static std::map<std::string, TrampolineFactory> factories;
if (factories.empty()) {
factories[")b"] = JNITrampoline<bool>::create;
factories["i)b"] = JNITrampoline<bool, int>::create;
factories["l)b"] = JNITrampoline<bool, long>::create;
factories["ll)l"] = JNITrampoline<long, long, long>::create;
//... autogenerate these!
}
auto factory = factories[signature];
if (!factory) {
return nullptr;
}
return factory(fptr);
// auto tramp = new Trampoline()
}
void deleteJNITampoline(JNITrampolineBase* p) {
delete p;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment