From fef053d89e0bf6f554ef9196621849c3b18a031a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Wed, 7 Jan 2026 12:59:31 +0100 Subject: [PATCH] android: Support ART w/o copied_methods_offset_ --- lib/class-model.js | 87 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/lib/class-model.js b/lib/class-model.js index 4419d1f..bfe843c 100644 --- a/lib/class-model.js +++ b/lib/class-model.js @@ -85,6 +85,7 @@ struct _JavaFieldApi struct _JavaApi { + jvmtiEnv * jvmti; JavaClassApi clazz; JavaMethodApi method; JavaFieldApi field; @@ -235,6 +236,7 @@ model_new (jclass class_handle, { Model * model; GHashTable * members; + jvmtiEnv * jvmti = java_api.jvmti; gpointer * funcs = env->functions; jmethodID (* from_reflected_method) (JNIEnv *, jobject) = funcs[7]; jfieldID (* from_reflected_field) (JNIEnv *, jobject) = funcs[8]; @@ -254,7 +256,52 @@ model_new (jclass class_handle, members = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); model->members = members; - if (art_api.available) + if (jvmti != NULL) + { + gpointer * jf = jvmti->functions - 1; + jvmtiError (* deallocate) (jvmtiEnv *, void * mem) = jf[47]; + jvmtiError (* get_class_methods) (jvmtiEnv *, jclass, jint *, jmethodID **) = jf[52]; + jvmtiError (* get_class_fields) (jvmtiEnv *, jclass, jint *, jfieldID **) = jf[53]; + jvmtiError (* get_field_name) (jvmtiEnv *, jclass, jfieldID, char **, char **, char **) = jf[60]; + jvmtiError (* get_field_modifiers) (jvmtiEnv *, jclass, jfieldID, jint *) = jf[62]; + jvmtiError (* get_method_name) (jvmtiEnv *, jmethodID, char **, char **, char **) = jf[64]; + jvmtiError (* get_method_modifiers) (jvmtiEnv *, jmethodID, jint *) = jf[66]; + jint method_count; + jmethodID * methods; + jint field_count; + jfieldID * fields; + char * name; + jint modifiers; + + get_class_methods (jvmti, class_handle, &method_count, &methods); + for (i = 0; i != method_count; i++) + { + jmethodID method = methods[i]; + + get_method_name (jvmti, method, &name, NULL, NULL); + get_method_modifiers (jvmti, method, &modifiers); + + model_add_method (model, name, method, modifiers); + + deallocate (jvmti, name); + } + deallocate (jvmti, methods); + + get_class_fields (jvmti, class_handle, &field_count, &fields); + for (i = 0; i != field_count; i++) + { + jfieldID field = fields[i]; + + get_field_name (jvmti, class_handle, field, &name, NULL, NULL); + get_field_modifiers (jvmti, class_handle, field, &modifiers); + + model_add_field (model, name, field, modifiers); + + deallocate (jvmti, name); + } + deallocate (jvmti, fields); + } + else if (art_api.available) { gpointer elements; guint n, i; @@ -702,8 +749,7 @@ enumerate_methods_jvm (const gchar * class_query, jboolean include_signature, jboolean ignore_case, jboolean skip_system_classes, - JNIEnv * env, - jvmtiEnv * jvmti) + JNIEnv * env) { gchar * result; GPatternSpec * class_pattern, * method_pattern; @@ -712,6 +758,7 @@ enumerate_methods_jvm (const gchar * class_query, jobject (* new_global_ref) (JNIEnv *, jobject) = ef[21]; void (* delete_local_ref) (JNIEnv *, jobject) = ef[23]; jboolean (* is_same_object) (JNIEnv *, jobject, jobject) = ef[24]; + jvmtiEnv * jvmti = java_api.jvmti; gpointer * jf = jvmti->functions - 1; jvmtiError (* deallocate) (jvmtiEnv *, void * mem) = jf[47]; jvmtiError (* get_class_signature) (jvmtiEnv *, jclass, char **, char **) = jf[48]; @@ -1207,10 +1254,10 @@ export default class Model { } let result; - if (api.flavor === 'jvm') { + if (api.jvmti !== null) { const json = cm.enumerateMethodsJvm(classQuery, methodQuery, boolToNative(includeSignature), boolToNative(ignoreCase), boolToNative(skipSystemClasses), - env, api.jvmti); + env); try { result = JSON.parse(json.readUtf8String()) .map(group => { @@ -1273,11 +1320,14 @@ function ensureInitialized (env) { } function compileModule (env) { + const api = getApi(); + const { jvmti } = api; + const { pointerSize } = Process; const lockSize = 8; const modelsSize = pointerSize; - const javaApiSize = 6 * pointerSize; + const javaApiSize = 7 * pointerSize; const artApiSize = (10 * 4) + (5 * pointerSize); const dataSize = lockSize + modelsSize + javaApiSize + artApiSize; @@ -1293,6 +1343,7 @@ function compileModule (env) { const field = env.javaLangReflectField(); let j = javaApi; [ + jvmti, getDeclaredMethods, getDeclaredFields, method.getName, method.getModifiers, field.getName, field.getModifiers @@ -1303,16 +1354,22 @@ function compileModule (env) { const artApi = javaApi.add(javaApiSize); const { vm } = env; - const artClass = getArtClassSpec(vm); - if (artClass !== null) { - const c = artClass.offset; + if (api.flavor === 'art') { + let artClassOffsets; + if (jvmti !== null) { + artClassOffsets = [0, 0, 0, 0]; + } else { + const c = getArtClassSpec(vm).offset; + artClassOffsets = [c.ifields, c.methods, c.sfields, c.copiedMethodsOffset]; + } + const m = getArtMethodSpec(vm); const f = getArtFieldSpec(vm); let s = artApi; [ 1, - c.ifields, c.methods, c.sfields, c.copiedMethodsOffset, + ...artClassOffsets, m.size, m.offset.accessFlags, f.size, f.offset.accessFlags, 0xffffffff @@ -1321,7 +1378,6 @@ function compileModule (env) { s = s.writeUInt(value).add(4); }); - const api = getApi(); [ api.artClassLinker.address, api['art::ClassLinker::VisitClasses'], @@ -1349,7 +1405,6 @@ function compileModule (env) { return { handle: cm, - mode: (artClass !== null) ? 'full' : 'basic', new: new NativeFunction(cm.model_new, 'pointer', ['pointer', 'pointer', 'pointer'], reentrantOptions), has: new NativeFunction(cm.model_has, 'bool', ['pointer', 'pointer'], fastOptions), find: new NativeFunction(cm.model_find, 'pointer', ['pointer', 'pointer'], fastOptions), @@ -1357,17 +1412,19 @@ function compileModule (env) { enumerateMethodsArt: new NativeFunction(cm.enumerate_methods_art, 'pointer', ['pointer', 'pointer', 'bool', 'bool', 'bool'], reentrantOptions), enumerateMethodsJvm: new NativeFunction(cm.enumerate_methods_jvm, 'pointer', ['pointer', 'pointer', 'bool', 'bool', 'bool', - 'pointer', 'pointer'], reentrantOptions), + 'pointer'], reentrantOptions), dealloc: new NativeFunction(cm.dealloc, 'void', ['pointer'], fastOptions) }; } function makeHandleUnwrapper (cm, vm) { - if (cm.mode === 'basic') { + const api = getApi(); + + if (api.flavor !== 'art') { return nullUnwrap; } - const decodeGlobal = getApi()['art::JavaVMExt::DecodeGlobal']; + const decodeGlobal = api['art::JavaVMExt::DecodeGlobal']; return function (handle, env, fn) { let result;