From b4e6f3fe781670340b7f5b51354dee8b19842c68 Mon Sep 17 00:00:00 2001 From: hoodles <207470673+hoo-dles@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:25:35 -0800 Subject: [PATCH 1/3] android: Ensure class initialization with call of valid call to getName #376 --- lib/android.js | 11 ++++++++--- lib/class-factory.js | 2 +- lib/env.js | 4 ++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/android.js b/lib/android.js index 7340d2b..85440fa 100644 --- a/lib/android.js +++ b/lib/android.js @@ -580,14 +580,19 @@ function tryGetEnvJvmti (vm, runtime) { return env; } -export function ensureClassInitialized (env, classRef) { +export function ensureClassInitialized(env, className) { const api = getApi(); if (api.flavor !== 'art') { return; } - env.getFieldId(classRef, 'x', 'Z'); - env.exceptionClear(); + // Ensure class is loaded by prematurely calling Class::getName() + const jniClassName = className.replace(/\./g, '/'); + const clazz = env.findClass(jniClassName); + const javaLangClass = env.findClass("java/lang/Class"); + const getNameId = env.getMethodId(javaLangClass, "getName", "()Ljava/lang/String;"); + + env.callObjectMethodA(clazz, getNameId, ptr(0)); } function getArtVMSpec (api) { diff --git a/lib/class-factory.js b/lib/class-factory.js index 68a1a3b..1a96606 100644 --- a/lib/class-factory.js +++ b/lib/class-factory.js @@ -282,7 +282,7 @@ export default class ClassFactory { try { const classHandle = h.value; - ensureClassInitialized(env, classHandle); + ensureClassInitialized(env, name); proto.$l = ClassModel.build(classHandle, env); } finally { diff --git a/lib/env.js b/lib/env.js index b8a5a7d..508ff1a 100644 --- a/lib/env.js +++ b/lib/env.js @@ -326,6 +326,10 @@ Env.prototype.getMethodId = proxy(33, 'pointer', ['pointer', 'pointer', 'pointer return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); }); +Env.prototype.callObjectMethodA = proxy(36, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, obj, methodId, args) { + return impl(this.handle, obj, methodId, args); +}); + Env.prototype.getFieldId = proxy(94, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, klass, name, sig) { return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); }); From 4a4970bc8172cf999dbbce51fcb2d36839f025f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Sat, 10 Jan 2026 14:16:56 +0100 Subject: [PATCH 2/3] android: Detect new GetOatQuickMethodHeader() --- lib/android.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/android.js b/lib/android.js index 7340d2b..d5ee3b0 100644 --- a/lib/android.js +++ b/lib/android.js @@ -2292,6 +2292,21 @@ const artGetOatQuickMethodHeaderInlinedCopyHandler = { offset: 1, validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm64 }, + { + pattern: [ + /* e8 */ '0a 40 b9', // ldr w8, [x?, #0x8] + '1f 05 00 31', // cmn w8, #0x1 + '40 01 00 54', // b.eq + '00 0e 40 f9', // ldr x?, [x?, #0x18] + ':', + /* 00 */ 'fc ff ff', + '1f fc ff ff', + '1f 00 00 ff', + '00 fc ff ff' + ], + offset: 1, + validateMatch: validateGetOatQuickMethodHeaderInlinedMatchArm64 + }, { pattern: [ /* e8 */ '0a 40 b9', // ldr w8, [x23, #0x8] From f1c957e9857070df04334a77734cc6c131b93c0d Mon Sep 17 00:00:00 2001 From: hoodles <207470673+hoo-dles@users.noreply.github.com> Date: Sat, 10 Jan 2026 17:02:09 -0800 Subject: [PATCH 3/3] Simplifying with existing helper method --- lib/android.js | 10 ++-------- lib/class-factory.js | 2 +- lib/env.js | 4 ---- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/android.js b/lib/android.js index 1cccac8..11daebe 100644 --- a/lib/android.js +++ b/lib/android.js @@ -580,19 +580,13 @@ function tryGetEnvJvmti (vm, runtime) { return env; } -export function ensureClassInitialized(env, className) { +export function ensureClassInitialized (env, classRef) { const api = getApi(); if (api.flavor !== 'art') { return; } - // Ensure class is loaded by prematurely calling Class::getName() - const jniClassName = className.replace(/\./g, '/'); - const clazz = env.findClass(jniClassName); - const javaLangClass = env.findClass("java/lang/Class"); - const getNameId = env.getMethodId(javaLangClass, "getName", "()Ljava/lang/String;"); - - env.callObjectMethodA(clazz, getNameId, ptr(0)); + env.getClassName(classRef); } function getArtVMSpec (api) { diff --git a/lib/class-factory.js b/lib/class-factory.js index 1a96606..68a1a3b 100644 --- a/lib/class-factory.js +++ b/lib/class-factory.js @@ -282,7 +282,7 @@ export default class ClassFactory { try { const classHandle = h.value; - ensureClassInitialized(env, name); + ensureClassInitialized(env, classHandle); proto.$l = ClassModel.build(classHandle, env); } finally { diff --git a/lib/env.js b/lib/env.js index 508ff1a..b8a5a7d 100644 --- a/lib/env.js +++ b/lib/env.js @@ -326,10 +326,6 @@ Env.prototype.getMethodId = proxy(33, 'pointer', ['pointer', 'pointer', 'pointer return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); }); -Env.prototype.callObjectMethodA = proxy(36, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, obj, methodId, args) { - return impl(this.handle, obj, methodId, args); -}); - Env.prototype.getFieldId = proxy(94, 'pointer', ['pointer', 'pointer', 'pointer', 'pointer'], function (impl, klass, name, sig) { return impl(this.handle, klass, Memory.allocUtf8String(name), Memory.allocUtf8String(sig)); });