2023-10-08 16:09:27 +02:00
|
|
|
#include <stdint.h>
|
2023-09-06 17:42:24 +02:00
|
|
|
#include <dlfcn.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
#include "util.h"
|
2023-10-08 16:09:27 +02:00
|
|
|
#include "src/api-impl-jni/defines.h"
|
2022-10-02 23:06:56 +02:00
|
|
|
|
|
|
|
|
struct handle_cache handle_cache = {0};
|
|
|
|
|
|
|
|
|
|
const char * attribute_set_get_string(JNIEnv *env, jobject attrs, char *attribute, char *schema)
|
|
|
|
|
{
|
2023-08-17 10:46:24 +02:00
|
|
|
if (!attrs)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
if(!schema)
|
|
|
|
|
schema = "http://schemas.android.com/apk/res/android";
|
|
|
|
|
|
2024-01-23 22:32:09 +01:00
|
|
|
jstring string = (jstring)(*env)->CallObjectMethod(env, attrs, handle_cache.attribute_set.getAttributeValue_string, _JSTRING(schema), _JSTRING(attribute));
|
|
|
|
|
return string ? _CSTRING(string) : NULL;
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int attribute_set_get_int(JNIEnv *env, jobject attrs, char *attribute, char *schema, int default_value)
|
|
|
|
|
{
|
2023-08-17 12:59:37 +02:00
|
|
|
if (!attrs)
|
|
|
|
|
return default_value;
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
if(!schema)
|
|
|
|
|
schema = "http://schemas.android.com/apk/res/android";
|
|
|
|
|
|
|
|
|
|
return (*env)->CallIntMethod(env, attrs, handle_cache.attribute_set.getAttributeValue_int, _JSTRING(schema), _JSTRING(attribute), default_value);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 17:14:30 +01:00
|
|
|
JavaVM *jvm;
|
|
|
|
|
|
|
|
|
|
// TODO: use this everywhere, not just for gdb helper functions
|
|
|
|
|
JNIEnv * get_jni_env(void)
|
|
|
|
|
{
|
|
|
|
|
JNIEnv *env;
|
|
|
|
|
(*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6);
|
|
|
|
|
return env;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JNIEnv * _gdb_get_jni_env(void)
|
|
|
|
|
{
|
|
|
|
|
return get_jni_env();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _gdb_get_java_stack_trace(void)
|
|
|
|
|
{
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 23:16:39 +02:00
|
|
|
void _gdb_force_java_stack_trace(void)
|
|
|
|
|
{
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
|
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/Exception"), "forced stack trace");
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
(*env)->ExceptionClear(env);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-08 10:16:17 +02:00
|
|
|
void set_up_handle_cache(JNIEnv *env)
|
2022-10-02 23:06:56 +02:00
|
|
|
{
|
2022-12-27 17:14:30 +01:00
|
|
|
(*env)->GetJavaVM(env, &jvm);
|
|
|
|
|
|
2023-09-25 19:53:20 +02:00
|
|
|
handle_cache.activity.class = _REF((*env)->FindClass(env, "android/app/Activity"));
|
2022-10-02 23:06:56 +02:00
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
2023-09-25 19:53:20 +02:00
|
|
|
handle_cache.activity.onCreate = _METHOD(handle_cache.activity.class, "onCreate", "(Landroid/os/Bundle;)V");
|
2024-10-05 16:52:53 +02:00
|
|
|
handle_cache.activity.onPostCreate = _METHOD(handle_cache.activity.class, "onPostCreate", "(Landroid/os/Bundle;)V");
|
2023-09-25 19:53:20 +02:00
|
|
|
handle_cache.activity.onStart = _METHOD(handle_cache.activity.class, "onStart", "()V");
|
|
|
|
|
handle_cache.activity.onWindowFocusChanged = _METHOD(handle_cache.activity.class, "onWindowFocusChanged", "(Z)V");
|
|
|
|
|
handle_cache.activity.onResume = _METHOD(handle_cache.activity.class, "onResume", "()V");
|
2024-10-05 16:52:53 +02:00
|
|
|
handle_cache.activity.onPostResume = _METHOD(handle_cache.activity.class, "onPostResume", "()V");
|
2023-09-25 19:53:20 +02:00
|
|
|
handle_cache.activity.onDestroy = _METHOD(handle_cache.activity.class, "onDestroy", "()V");
|
|
|
|
|
handle_cache.activity.onStop = _METHOD(handle_cache.activity.class, "onStop", "()V");
|
|
|
|
|
handle_cache.activity.onPause = _METHOD(handle_cache.activity.class, "onPause", "()V");
|
2024-11-15 08:15:04 +01:00
|
|
|
handle_cache.activity.onBackPressed = _METHOD(handle_cache.activity.class, "onBackPressed", "()V");
|
2022-10-02 23:06:56 +02:00
|
|
|
|
|
|
|
|
handle_cache.attribute_set.class = _REF((*env)->FindClass(env, "android/util/AttributeSet"));
|
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
handle_cache.attribute_set.getAttributeValue_string = _METHOD(handle_cache.attribute_set.class, "getAttributeValue", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
|
|
|
|
|
handle_cache.attribute_set.getAttributeValue_int = _METHOD(handle_cache.attribute_set.class, "getAttributeIntValue", "(Ljava/lang/String;Ljava/lang/String;I)I");
|
|
|
|
|
|
|
|
|
|
handle_cache.array_list.class = _REF((*env)->FindClass(env, "java/util/ArrayList"));
|
|
|
|
|
handle_cache.array_list.add = _METHOD(handle_cache.array_list.class, "add", "(Ljava/lang/Object;)Z");
|
|
|
|
|
handle_cache.array_list.remove = _METHOD(handle_cache.array_list.class, "remove", "(Ljava/lang/Object;)Z");
|
|
|
|
|
handle_cache.array_list.get = _METHOD(handle_cache.array_list.class, "get", "(I)Ljava/lang/Object;");
|
|
|
|
|
handle_cache.array_list.size = _METHOD(handle_cache.array_list.class, "size", "()I");
|
|
|
|
|
handle_cache.array_list.clear = _METHOD(handle_cache.array_list.class, "clear", "()V");
|
|
|
|
|
|
|
|
|
|
handle_cache.paint.class = _REF((*env)->FindClass(env, "android/graphics/Paint"));
|
|
|
|
|
handle_cache.paint.getColor = _METHOD(handle_cache.paint.class, "getColor", "()I");
|
|
|
|
|
|
|
|
|
|
handle_cache.motion_event.class = _REF((*env)->FindClass(env, "android/view/MotionEvent"));
|
2025-01-13 15:54:21 +01:00
|
|
|
handle_cache.motion_event.constructor = _METHOD(handle_cache.motion_event.class, "<init>", "(IIJ[I[F)V");
|
|
|
|
|
handle_cache.motion_event.constructor_single = _METHOD(handle_cache.motion_event.class, "<init>", "(IIJFFFF)V");
|
2022-10-02 23:06:56 +02:00
|
|
|
|
2024-03-16 15:04:02 +01:00
|
|
|
handle_cache.sensor_event.class = _REF((*env)->FindClass(env, "android/hardware/SensorEvent"));
|
|
|
|
|
handle_cache.sensor_event.constructor = _METHOD(handle_cache.sensor_event.class, "<init>", "([FLandroid/hardware/Sensor;)V");
|
|
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
handle_cache.audio_track_periodic_listener.class = _REF((*env)->FindClass(env, "android/media/AudioTrack$OnPlaybackPositionUpdateListener"));
|
|
|
|
|
handle_cache.audio_track_periodic_listener.onPeriodicNotification = _METHOD(handle_cache.audio_track_periodic_listener.class, "onPeriodicNotification", "(Landroid/media/AudioTrack;)V");
|
|
|
|
|
|
2022-11-11 19:18:21 +01:00
|
|
|
handle_cache.input_queue_callback.class = _REF((*env)->FindClass(env, "android/view/InputQueue$Callback"));
|
|
|
|
|
handle_cache.input_queue_callback.onInputQueueCreated = _METHOD(handle_cache.input_queue_callback.class, "onInputQueueCreated", "(Landroid/view/InputQueue;)V");
|
|
|
|
|
|
2023-10-20 20:54:37 +02:00
|
|
|
handle_cache.surface_view.class = _REF((*env)->FindClass(env, "android/view/SurfaceView"));
|
|
|
|
|
handle_cache.surface_view.surfaceCreated = _METHOD(handle_cache.surface_view.class, "surfaceCreated", "()V");
|
|
|
|
|
handle_cache.surface_view.surfaceChanged = _METHOD(handle_cache.surface_view.class, "surfaceChanged", "(III)V");
|
2022-12-27 17:14:30 +01:00
|
|
|
|
2022-10-02 23:06:56 +02:00
|
|
|
handle_cache.view.class = _REF((*env)->FindClass(env, "android/view/View"));
|
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
handle_cache.view.setLayoutParams = _METHOD(handle_cache.view.class, "setLayoutParams", "(Landroid/view/ViewGroup$LayoutParams;)V");
|
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
2023-07-14 17:53:12 +02:00
|
|
|
handle_cache.view.onDraw = _METHOD(handle_cache.view.class, "onDraw", "(Landroid/graphics/Canvas;)V");
|
2024-11-27 14:59:37 +01:00
|
|
|
handle_cache.view.dispatchDraw = _METHOD(handle_cache.view.class, "dispatchDraw", "(Landroid/graphics/Canvas;)V");
|
2024-05-19 15:04:15 +02:00
|
|
|
handle_cache.view.draw = _METHOD(handle_cache.view.class, "draw", "(Landroid/graphics/Canvas;)V");
|
2023-07-14 17:53:12 +02:00
|
|
|
handle_cache.view.onMeasure = _METHOD(handle_cache.view.class, "onMeasure", "(II)V");
|
2023-08-22 14:18:33 +02:00
|
|
|
handle_cache.view.onLayout = _METHOD(handle_cache.view.class, "onLayout", "(ZIIII)V");
|
|
|
|
|
handle_cache.view.getMeasuredWidth = _METHOD(handle_cache.view.class, "getMeasuredWidth", "()I");
|
|
|
|
|
handle_cache.view.getMeasuredHeight = _METHOD(handle_cache.view.class, "getMeasuredHeight", "()I");
|
|
|
|
|
handle_cache.view.getSuggestedMinimumWidth = _METHOD(handle_cache.view.class, "getSuggestedMinimumWidth", "()I");
|
|
|
|
|
handle_cache.view.getSuggestedMinimumHeight = _METHOD(handle_cache.view.class, "getSuggestedMinimumHeight", "()I");
|
|
|
|
|
handle_cache.view.setMeasuredDimension = _METHOD(handle_cache.view.class, "setMeasuredDimension", "(II)V");
|
2023-08-22 18:08:16 +02:00
|
|
|
handle_cache.view.onGenericMotionEvent = _METHOD(handle_cache.view.class, "onGenericMotionEvent", "(Landroid/view/MotionEvent;)Z");
|
2023-09-01 12:13:24 +02:00
|
|
|
handle_cache.view.computeScroll = _METHOD(handle_cache.view.class, "computeScroll", "()V");
|
|
|
|
|
handle_cache.view.getScrollX = _METHOD(handle_cache.view.class, "getScrollX", "()I");
|
|
|
|
|
handle_cache.view.getScrollY = _METHOD(handle_cache.view.class, "getScrollY", "()I");
|
2023-09-01 12:19:45 +02:00
|
|
|
handle_cache.view.performClick = _METHOD(handle_cache.view.class, "performClick", "()Z");
|
2023-10-17 21:33:59 +02:00
|
|
|
handle_cache.view.onTouchEvent = _METHOD(handle_cache.view.class, "onTouchEvent", "(Landroid/view/MotionEvent;)Z");
|
2024-11-27 14:59:37 +01:00
|
|
|
handle_cache.view.dispatchTouchEvent = _METHOD(handle_cache.view.class, "dispatchTouchEvent", "(Landroid/view/MotionEvent;)Z");
|
2024-02-24 18:50:03 +01:00
|
|
|
handle_cache.view.onInterceptTouchEvent = _METHOD(handle_cache.view.class, "onInterceptTouchEvent", "(Landroid/view/MotionEvent;)Z");
|
2023-10-28 22:38:43 +02:00
|
|
|
handle_cache.view.layoutInternal = _METHOD(handle_cache.view.class, "layoutInternal", "(II)V");
|
2023-10-31 22:56:22 +01:00
|
|
|
handle_cache.view.measure = _METHOD(handle_cache.view.class, "measure", "(II)V");
|
2024-07-26 21:47:08 +02:00
|
|
|
handle_cache.view.performLongClick = _METHOD(handle_cache.view.class, "performLongClick", "(FF)Z");
|
2024-11-17 15:59:53 +01:00
|
|
|
handle_cache.view.getId = _METHOD(handle_cache.view.class, "getId", "()I");
|
|
|
|
|
handle_cache.view.getIdName = _METHOD(handle_cache.view.class, "getIdName", "()Ljava/lang/String;");
|
|
|
|
|
handle_cache.view.getAllSuperClasses = _METHOD(handle_cache.view.class, "getAllSuperClasses", "()Ljava/lang/String;");
|
2024-11-30 17:46:43 +01:00
|
|
|
handle_cache.view.dispatchKeyEvent = _METHOD(handle_cache.view.class, "dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z");
|
2023-05-20 18:53:20 +02:00
|
|
|
|
|
|
|
|
handle_cache.asset_manager.class = _REF((*env)->FindClass(env, "android/content/res/AssetManager"));
|
|
|
|
|
handle_cache.asset_manager.extractFromAPK = _STATIC_METHOD(handle_cache.asset_manager.class, "extractFromAPK", "(Ljava/lang/String;Ljava/lang/String;)V");
|
2023-08-12 13:09:33 +02:00
|
|
|
|
|
|
|
|
handle_cache.context.class = _REF((*env)->FindClass(env, "android/content/Context"));
|
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
handle_cache.context.get_package_name = _METHOD(handle_cache.context.class, "getPackageName", "()Ljava/lang/String;");
|
|
|
|
|
if((*env)->ExceptionCheck(env))
|
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
|
|
|
|
|
|
handle_cache.application.class = _REF((*env)->FindClass(env, "android/app/Application"));
|
|
|
|
|
handle_cache.application.get_app_icon_path = _METHOD(handle_cache.application.class, "get_app_icon_path", "()Ljava/lang/String;");
|
|
|
|
|
|
2023-10-08 15:31:55 +02:00
|
|
|
handle_cache.looper.class = _REF((*env)->FindClass(env, "android/os/Looper"));
|
|
|
|
|
handle_cache.looper.loop = _STATIC_METHOD(handle_cache.looper.class, "loop", "()V");
|
|
|
|
|
handle_cache.looper.prepareMainLooper = _STATIC_METHOD(handle_cache.looper.class, "prepareMainLooper", "()V");
|
2023-11-08 18:00:31 +01:00
|
|
|
|
|
|
|
|
handle_cache.key_event.class = _REF((*env)->FindClass(env, "android/view/KeyEvent"));
|
|
|
|
|
handle_cache.key_event.constructor = _METHOD(handle_cache.key_event.class, "<init>", "(II)V");
|
2024-03-24 21:01:47 +01:00
|
|
|
|
|
|
|
|
handle_cache.drawable.class = _REF((*env)->FindClass(env, "android/graphics/drawable/Drawable"));
|
|
|
|
|
handle_cache.drawable.draw = _METHOD(handle_cache.drawable.class, "draw", "(Landroid/graphics/Canvas;)V");
|
|
|
|
|
handle_cache.drawable.setBounds = _METHOD(handle_cache.drawable.class, "setBounds", "(IIII)V");
|
2024-05-22 12:24:11 +02:00
|
|
|
|
|
|
|
|
handle_cache.intent.class = _REF((*env)->FindClass(env, "android/content/Intent"));
|
2024-11-30 18:57:03 +01:00
|
|
|
handle_cache.intent.constructor = _METHOD(handle_cache.intent.class, "<init>", "()V");
|
2024-05-22 12:24:11 +02:00
|
|
|
handle_cache.intent.putExtraCharSequence = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;Ljava/lang/CharSequence;)Landroid/content/Intent;");
|
2024-10-03 22:42:12 +02:00
|
|
|
|
2024-11-30 18:57:03 +01:00
|
|
|
handle_cache.instrumentation.class = _REF((*env)->FindClass(env, "android/app/Instrumentation"));
|
|
|
|
|
|
2024-10-03 22:42:12 +02:00
|
|
|
handle_cache.webview.class = _REF((*env)->FindClass(env, "android/webkit/WebView"));
|
|
|
|
|
handle_cache.webview.internalGetAssetManager = _METHOD(handle_cache.webview.class, "internalGetAssetManager", "()Landroid/content/res/AssetManager;");
|
2024-10-03 22:43:42 +02:00
|
|
|
handle_cache.webview.internalLoadChanged = _METHOD(handle_cache.webview.class, "internalLoadChanged", "(ILjava/lang/String;)V");
|
2023-05-20 18:53:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void extract_from_apk(const char *path, const char *target) {
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
2024-01-23 22:32:09 +01:00
|
|
|
(*env)->CallStaticVoidMethod(env, handle_cache.asset_manager.class, handle_cache.asset_manager.extractFromAPK, _JSTRING(path), _JSTRING(target));
|
2022-10-02 23:06:56 +02:00
|
|
|
}
|
2023-09-06 17:42:24 +02:00
|
|
|
|
|
|
|
|
/* logging with fallback to stderr */
|
|
|
|
|
|
|
|
|
|
typedef int __android_log_vprint_type(int prio, const char *tag, const char *fmt, va_list ap);
|
|
|
|
|
|
|
|
|
|
static int fallback_verbose_log(int prio, const char *tag, const char *fmt, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
|
static char buf[1024];
|
|
|
|
|
ret = vsnprintf(buf, sizeof(buf), fmt, ap);
|
2025-01-13 15:54:21 +01:00
|
|
|
fprintf(stderr, "%w64u: %s\n", (uint64_t)pthread_self(), buf);
|
2023-09-06 17:42:24 +02:00
|
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int android_log_vprintf(int prio, const char *tag, const char *fmt, va_list ap)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
static __android_log_vprint_type *_android_log_vprintf = NULL;
|
|
|
|
|
if(!_android_log_vprintf) {
|
|
|
|
|
_android_log_vprintf = dlsym(RTLD_DEFAULT, "__android_log_vprint");
|
|
|
|
|
|
|
|
|
|
if(!_android_log_vprintf) {
|
|
|
|
|
_android_log_vprintf = &fallback_verbose_log;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _android_log_vprintf(prio, tag, fmt, ap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int android_log_printf(android_LogPriority prio, const char *tag, const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
|
|
|
|
|
ret = android_log_vprintf(prio, tag, fmt, ap);
|
|
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2023-10-08 16:09:27 +02:00
|
|
|
|
2023-10-17 21:57:48 +02:00
|
|
|
void *get_nio_buffer(JNIEnv *env, jobject buffer, jarray *array_ref, jbyte **array)
|
|
|
|
|
{
|
2023-10-08 16:09:27 +02:00
|
|
|
jclass class;
|
|
|
|
|
void *pointer;
|
|
|
|
|
int elementSizeShift, position;
|
|
|
|
|
|
|
|
|
|
if (!buffer) {
|
|
|
|
|
*array_ref = NULL;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
class = _CLASS(buffer);
|
|
|
|
|
pointer = _PTR((*env)->GetLongField(env, buffer, _FIELD_ID(class, "address", "J")));
|
|
|
|
|
elementSizeShift = (*env)->GetIntField(env, buffer, _FIELD_ID(class, "_elementSizeShift", "I"));
|
|
|
|
|
position = (*env)->GetIntField(env, buffer, _FIELD_ID(class, "position", "I"));
|
|
|
|
|
if (pointer) { // buffer is direct
|
|
|
|
|
*array_ref = NULL;
|
|
|
|
|
pointer += position << elementSizeShift;
|
|
|
|
|
} else { // buffer is indirect
|
|
|
|
|
*array_ref = (*env)->CallObjectMethod(env, buffer, _METHOD(class, "array", "()Ljava/lang/Object;"));
|
|
|
|
|
jint offset = (*env)->CallIntMethod(env, buffer, _METHOD(class, "arrayOffset", "()I"));
|
2024-02-03 23:37:52 +01:00
|
|
|
pointer = *array = (*env)->GetPrimitiveArrayCritical(env, *array_ref, NULL);
|
2023-10-08 16:09:27 +02:00
|
|
|
pointer += (offset + position) << elementSizeShift;
|
|
|
|
|
}
|
|
|
|
|
return pointer;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-17 21:57:48 +02:00
|
|
|
void release_nio_buffer(JNIEnv *env, jarray array_ref, jbyte *array)
|
|
|
|
|
{
|
2023-10-08 16:09:27 +02:00
|
|
|
if (array_ref)
|
|
|
|
|
(*env)->ReleasePrimitiveArrayCritical(env, array_ref, array, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-17 21:57:48 +02:00
|
|
|
int get_nio_buffer_size(JNIEnv *env, jobject buffer)
|
|
|
|
|
{
|
2023-10-08 16:09:27 +02:00
|
|
|
jclass class = _CLASS(buffer);;
|
|
|
|
|
int limit = (*env)->GetIntField(env, buffer, _FIELD_ID(class, "limit", "I"));
|
|
|
|
|
int position = (*env)->GetIntField(env, buffer, _FIELD_ID(class, "position", "I"));
|
|
|
|
|
|
|
|
|
|
return limit - position;
|
|
|
|
|
}
|