#include "MockJniEnv.h"
#include <string>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
namespace falcon_test {
namespace {
thread_local MockJniEnv* current_mock_env = nullptr;
}
MockJniEnv::MockJniEnv() {
functions_.NewByteArray = &MockJniEnv::newByteArray;
functions_.GetArrayLength = &MockJniEnv::getArrayLength;
functions_.GetByteArrayRegion = &MockJniEnv::getByteArrayRegion;
functions_.SetByteArrayRegion = &MockJniEnv::setByteArrayRegion;
functions_.GetByteArrayElements = &MockJniEnv::getByteArrayElements;
functions_.ReleaseByteArrayElements = &MockJniEnv::releaseByteArrayElements;
functions_.ExceptionCheck = &MockJniEnv::exceptionCheck;
functions_.ExceptionClear = &MockJniEnv::exceptionClear;
functions_.FindClass = &MockJniEnv::findClass;
functions_.ThrowNew = &MockJniEnv::throwNew;
functions_.GetMethodID = &MockJniEnv::getMethodID;
functions_.NewStringUTF = &MockJniEnv::newStringUTF;
functions_.NewObjectV = &MockJniEnv::newObjectV;
functions_.Throw = &MockJniEnv::throwObject;
functions_.DeleteLocalRef = &MockJniEnv::deleteLocalRef;
functions_.GetStringUTFChars = &MockJniEnv::getStringUTFChars;
functions_.ReleaseStringUTFChars = &MockJniEnv::releaseStringUTFChars;
functions_.GetStringUTFLength = &MockJniEnv::getStringUTFLength;
functions_.IsInstanceOf = &MockJniEnv::isInstanceOf;
functions_.GetStaticMethodID = &MockJniEnv::getStaticMethodID;
functions_.CallObjectMethodV = &MockJniEnv::callObjectMethodV;
functions_.CallByteMethodV = &MockJniEnv::callByteMethodV;
functions_.CallStaticObjectMethodV = &MockJniEnv::callStaticObjectMethodV;
functions_.NewObjectArray = &MockJniEnv::newObjectArray;
functions_.GetObjectArrayElement = &MockJniEnv::getObjectArrayElement;
functions_.SetObjectArrayElement = &MockJniEnv::setObjectArrayElement;
functions_.NewLongArray = &MockJniEnv::newLongArray;
functions_.GetLongArrayElements = &MockJniEnv::getLongArrayElements;
functions_.ReleaseLongArrayElements = &MockJniEnv::releaseLongArrayElements;
functions_.SetLongArrayRegion = &MockJniEnv::setLongArrayRegion;
functions_.GetDirectBufferAddress = &MockJniEnv::getDirectBufferAddress;
functions_.GetDirectBufferCapacity = &MockJniEnv::getDirectBufferCapacity;
env_.functions = &functions_;
current_mock_env = this;
}
bool MockJniEnv::maybeInjectByteArrayFailure() {
++byte_array_call_num_;
if (inject_byte_array_failure_at_ > 0 &&
byte_array_call_num_ == inject_byte_array_failure_at_) {
pending_exception_ = true;
inject_byte_array_failure_at_ = 0;
return true;
}
return false;
}
bool MockJniEnv::incrementLocalRef() {
++live_local_refs_;
if (local_ref_cap_ > 0 && live_local_refs_ > local_ref_cap_) {
pending_exception_ = true;
--live_local_refs_;
return false;
}
return true;
}
MockJniEnv::~MockJniEnv() {
if (current_mock_env == this) {
current_mock_env = nullptr;
}
}
MockJniEnv* MockJniEnv::fromEnv(JNIEnv* ) {
return current_mock_env;
}
std::vector<jbyte> MockJniEnv::arrayBytes(jbyteArray array) const {
auto it = arrays_.find(array);
if (it == arrays_.end()) {
return {};
}
return it->second->data;
}
jbyteArray MockJniEnv::newByteArray(JNIEnv* env, jsize len) {
auto* self = fromEnv(env);
if (self->maybeInjectByteArrayFailure()) {
return nullptr;
}
self->last_new_byte_array_len_ = len;
auto handle = reinterpret_cast<jbyteArray>(self->next_handle_++);
bool use_phantom = false;
if (self->skip_backing_store_above_ != 0) {
if (len < 0 || len >= self->skip_backing_store_above_) {
use_phantom = true;
}
}
if (use_phantom) {
self->phantom_arrays_[handle] = std::make_unique<PhantomArray>(
PhantomArray{len});
} else {
self->arrays_[handle] = std::make_unique<ByteArray>(
ByteArray{std::vector<jbyte>(static_cast<size_t>(len), 0)});
}
return handle;
}
jsize MockJniEnv::getArrayLength(JNIEnv* env, jarray array) {
auto* self = fromEnv(env);
auto it = self->arrays_.find(reinterpret_cast<jbyteArray>(array));
if (it != self->arrays_.end()) {
return static_cast<jsize>(it->second->data.size());
}
auto pit = self->phantom_arrays_.find(reinterpret_cast<jbyteArray>(array));
if (pit != self->phantom_arrays_.end()) {
return pit->second->recorded_len;
}
auto oit = self->object_arrays_.find(reinterpret_cast<jobjectArray>(array));
if (oit != self->object_arrays_.end()) {
return static_cast<jsize>(oit->second->elements.size());
}
auto lit = self->long_arrays_.find(reinterpret_cast<jlongArray>(array));
if (lit != self->long_arrays_.end()) {
return static_cast<jsize>(lit->second->data.size());
}
return 0;
}
void MockJniEnv::getByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize len, jbyte* buf) {
auto* self = fromEnv(env);
if (self->maybeInjectByteArrayFailure()) {
return;
}
if (self->phantom_arrays_.count(array)) {
self->pending_exception_ = true;
return;
}
auto it = self->arrays_.find(array);
if (it == self->arrays_.end()) {
self->pending_exception_ = true;
return;
}
if (start < 0 || len < 0 || static_cast<size_t>(start + len) > it->second->data.size()) {
self->pending_exception_ = true;
return;
}
std::memcpy(buf, it->second->data.data() + start, len);
}
void MockJniEnv::setByteArrayRegion(JNIEnv* env, jbyteArray array, jsize start, jsize len, const jbyte* buf) {
auto* self = fromEnv(env);
if (self->maybeInjectByteArrayFailure()) {
return;
}
if (self->phantom_arrays_.count(array)) {
self->pending_exception_ = true;
return;
}
auto it = self->arrays_.find(array);
if (it == self->arrays_.end()) {
self->pending_exception_ = true;
return;
}
if (start < 0 || len < 0 || static_cast<size_t>(start + len) > it->second->data.size()) {
self->pending_exception_ = true;
return;
}
std::memcpy(it->second->data.data() + start, buf, len);
}
jbyte* MockJniEnv::getByteArrayElements(JNIEnv* env, jbyteArray array, jboolean* is_copy) {
auto* self = fromEnv(env);
if (self->maybeInjectByteArrayFailure()) {
return nullptr;
}
if (self->phantom_arrays_.count(array)) {
self->pending_exception_ = true;
return nullptr;
}
auto it = self->arrays_.find(array);
if (it == self->arrays_.end()) {
self->pending_exception_ = true;
return nullptr;
}
if (is_copy != nullptr) {
*is_copy = JNI_FALSE;
}
return it->second->data.data();
}
void MockJniEnv::releaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte* , jint ) {
auto* self = fromEnv(env);
if (self->arrays_.find(array) == self->arrays_.end()) {
self->pending_exception_ = true;
}
}
jboolean MockJniEnv::exceptionCheck(JNIEnv* env) {
return fromEnv(env)->pending_exception_ ? JNI_TRUE : JNI_FALSE;
}
void MockJniEnv::exceptionClear(JNIEnv* env) {
fromEnv(env)->pending_exception_ = false;
}
jclass MockJniEnv::findClass(JNIEnv* , const char* ) {
static int sentinel = 0;
return reinterpret_cast<jclass>(&sentinel);
}
jint MockJniEnv::throwNew(JNIEnv* env, jclass , const char* ) {
fromEnv(env)->pending_exception_ = true;
return JNI_OK;
}
jmethodID MockJniEnv::getMethodID(JNIEnv* , jclass ,
const char* , const char* ) {
static int sentinel = 0;
return reinterpret_cast<jmethodID>(&sentinel);
}
jstring MockJniEnv::newStringUTF(JNIEnv* env, const char* utf) {
auto* self = fromEnv(env);
if (!self->incrementLocalRef()) {
return nullptr;
}
auto handle = reinterpret_cast<jstring>(self->next_handle_++);
self->strings_[handle] = std::make_unique<StringObj>(
StringObj{utf == nullptr ? std::string{} : std::string{utf}});
return handle;
}
jobject MockJniEnv::newObjectV(JNIEnv* env, jclass ,
jmethodID , va_list ) {
auto* self = fromEnv(env);
if (!self->incrementLocalRef()) {
return nullptr;
}
static int sentinel = 0;
return reinterpret_cast<jobject>(&sentinel);
}
jint MockJniEnv::throwObject(JNIEnv* env, jthrowable ) {
fromEnv(env)->pending_exception_ = true;
return JNI_OK;
}
void MockJniEnv::deleteLocalRef(JNIEnv* env, jobject ) {
auto* self = fromEnv(env);
if (self->live_local_refs_ > 0) {
--self->live_local_refs_;
}
}
const char* MockJniEnv::getStringUTFChars(JNIEnv* env, jstring str, jboolean* is_copy) {
auto* self = fromEnv(env);
auto it = self->strings_.find(str);
if (it == self->strings_.end()) {
self->pending_exception_ = true;
return nullptr;
}
if (is_copy != nullptr) *is_copy = JNI_FALSE;
return it->second->utf.c_str();
}
void MockJniEnv::releaseStringUTFChars(JNIEnv* , jstring , const char* ) {
}
jsize MockJniEnv::getStringUTFLength(JNIEnv* env, jstring str) {
auto* self = fromEnv(env);
auto it = self->strings_.find(str);
if (it == self->strings_.end()) {
self->pending_exception_ = true;
return 0;
}
return static_cast<jsize>(it->second->utf.size());
}
jboolean MockJniEnv::isInstanceOf(JNIEnv* env, jobject , jclass ) {
return fromEnv(env)->is_instance_of_result_;
}
jmethodID MockJniEnv::getStaticMethodID(JNIEnv* , jclass ,
const char* , const char* ) {
static int sentinel = 0;
return reinterpret_cast<jmethodID>(&sentinel);
}
jobject MockJniEnv::callObjectMethodV(JNIEnv* , jobject ,
jmethodID , va_list ) {
static int sentinel = 0;
return reinterpret_cast<jobject>(&sentinel);
}
jbyte MockJniEnv::callByteMethodV(JNIEnv* , jobject ,
jmethodID , va_list ) {
return 0;
}
jobject MockJniEnv::callStaticObjectMethodV(JNIEnv* , jclass ,
jmethodID , va_list ) {
static int sentinel = 0;
return reinterpret_cast<jobject>(&sentinel);
}
jobjectArray MockJniEnv::newObjectArray(JNIEnv* env, jsize len,
jclass , jobject ) {
auto* self = fromEnv(env);
if (!self->incrementLocalRef()) {
return nullptr;
}
auto handle = reinterpret_cast<jobjectArray>(self->next_handle_++);
self->object_arrays_[handle] = std::make_unique<ObjectArray>(
ObjectArray{std::vector<jobject>(static_cast<size_t>(len), nullptr)});
return handle;
}
jobject MockJniEnv::getObjectArrayElement(JNIEnv* env, jobjectArray arr, jsize idx) {
auto* self = fromEnv(env);
auto it = self->object_arrays_.find(arr);
if (it == self->object_arrays_.end() || idx < 0 ||
static_cast<size_t>(idx) >= it->second->elements.size()) {
self->pending_exception_ = true;
return nullptr;
}
return it->second->elements[static_cast<size_t>(idx)];
}
void MockJniEnv::setObjectArrayElement(JNIEnv* env, jobjectArray arr, jsize idx, jobject val) {
auto* self = fromEnv(env);
auto it = self->object_arrays_.find(arr);
if (it == self->object_arrays_.end() || idx < 0 ||
static_cast<size_t>(idx) >= it->second->elements.size()) {
self->pending_exception_ = true;
return;
}
it->second->elements[static_cast<size_t>(idx)] = val;
}
jobjectArray MockJniEnv::makeObjectArray(jsize len) {
auto handle = reinterpret_cast<jobjectArray>(next_handle_++);
object_arrays_[handle] = std::make_unique<ObjectArray>(
ObjectArray{std::vector<jobject>(static_cast<size_t>(len), nullptr)});
return handle;
}
jlongArray MockJniEnv::newLongArray(JNIEnv* env, jsize len) {
auto* self = fromEnv(env);
auto handle = reinterpret_cast<jlongArray>(self->next_handle_++);
self->long_arrays_[handle] = std::make_unique<LongArray>(
LongArray{std::vector<jlong>(static_cast<size_t>(len), 0)});
return handle;
}
jlong* MockJniEnv::getLongArrayElements(JNIEnv* env, jlongArray arr, jboolean* is_copy) {
auto* self = fromEnv(env);
auto it = self->long_arrays_.find(arr);
if (it == self->long_arrays_.end()) {
self->pending_exception_ = true;
return nullptr;
}
if (is_copy != nullptr) *is_copy = JNI_FALSE;
return it->second->data.data();
}
void MockJniEnv::releaseLongArrayElements(JNIEnv* env, jlongArray arr,
jlong* , jint ) {
auto* self = fromEnv(env);
if (self->long_arrays_.find(arr) == self->long_arrays_.end()) {
self->pending_exception_ = true;
}
}
void MockJniEnv::setLongArrayRegion(JNIEnv* env, jlongArray arr,
jsize start, jsize len, const jlong* buf) {
auto* self = fromEnv(env);
auto it = self->long_arrays_.find(arr);
if (it == self->long_arrays_.end()) {
self->pending_exception_ = true;
return;
}
if (start < 0 || len < 0 ||
static_cast<size_t>(start + len) > it->second->data.size()) {
self->pending_exception_ = true;
return;
}
std::memcpy(it->second->data.data() + start, buf,
static_cast<size_t>(len) * sizeof(jlong));
}
std::vector<jlong> MockJniEnv::longArrayValues(jlongArray arr) const {
auto it = long_arrays_.find(arr);
if (it == long_arrays_.end()) return {};
return it->second->data;
}
void* MockJniEnv::getDirectBufferAddress(JNIEnv* env, jobject buf) {
auto* self = fromEnv(env);
auto it = self->direct_buffers_.find(buf);
if (it == self->direct_buffers_.end()) {
return nullptr;
}
return it->second->mem.get();
}
jlong MockJniEnv::getDirectBufferCapacity(JNIEnv* env, jobject buf) {
auto* self = fromEnv(env);
auto it = self->direct_buffers_.find(buf);
if (it == self->direct_buffers_.end()) {
return -1;
}
return it->second->capacity;
}
jobject MockJniEnv::makeDirectBuffer(jlong capacity) {
auto handle = reinterpret_cast<jobject>(next_handle_++);
auto buf = std::make_unique<DirectBuffer>();
buf->capacity = capacity;
buf->mem = std::unique_ptr<char[]>(new char[static_cast<size_t>(capacity)]());
direct_buffers_[handle] = std::move(buf);
return handle;
}
char* MockJniEnv::directBufferPtr(jobject buf) const {
auto it = direct_buffers_.find(buf);
if (it == direct_buffers_.end()) return nullptr;
return it->second->mem.get();
}
jlong MockJniEnv::directBufferCapacity(jobject buf) const {
auto it = direct_buffers_.find(buf);
if (it == direct_buffers_.end()) return -1;
return it->second->capacity;
}
}