diff --git a/composer/src/main/ts/imports.ts b/composer/src/main/ts/imports.ts index b70ff47b..8ec5701b 100644 --- a/composer/src/main/ts/imports.ts +++ b/composer/src/main/ts/imports.ts @@ -4,7 +4,7 @@ declare var _getImportsFunctionName: string; declare var _runtimeName: boolean; export const runtimeName = _runtimeName; -const remoteImports = require(_runtimeName + '_core/src/DeviceBridge')[_getImportsFunctionName](); +const remoteImports = require(_runtimeName + '_core/DeviceBridge')[_getImportsFunctionName](); function callRemoteFunction(method: string, ...args: any[]): any | null { return remoteImports[method](...args); diff --git a/composer/src/main/ts/main.ts b/composer/src/main/ts/main.ts index afcb27e2..66fb270a 100644 --- a/composer/src/main/ts/main.ts +++ b/composer/src/main/ts/main.ts @@ -12,7 +12,13 @@ try { if (config.composerLogs) { ["log", "error", "warn", "info", "debug"].forEach(method => { - console[method] = (...args: any) => log(method, Array.from(args).join(" ")); + console[method] = (...args: any) => log(method, Array.from(args).map(arg => { + try { + return typeof arg === 'object' && arg !== null ? JSON.stringify(arg) : String(arg); + } catch (e) { + return '[unserializable]'; + } + }).join(' ')); }) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt index 602835eb..e626b675 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/ComposerHooks.kt @@ -67,12 +67,23 @@ class ComposerHooks: Feature("ComposerHooks") { result = (context.native.composerEval(""" (() => { try { - $codeContent + const retVal = (() => { $codeContent })(); + if (typeof retVal === 'undefined') { + return 'undefined'; + } + if (retVal === null) { + return 'null'; + } + try { + return JSON.stringify(retVal, null, 2); + } catch { + return retVal.toString(); + } } catch (e) { return e.toString() } })() - """.trimIndent()) ?: "(no result)").also { + """.trimIndent()) ?: "(no result)").toString().also { context.log.verbose("result: $it", "ComposerConsole") } } @@ -174,7 +185,7 @@ class ComposerHooks: Feature("ComposerHooks") { const i = setInterval(() => { try { const _runtimeName = "${if (SnapEnhance.classCache.nativeBridge.name == "com.snapchat.client.valdi.NativeBridge") "valdi" else "composer"}"; - require(_runtimeName + '_core/src/DeviceBridge').getDisplayWidth(); + require(_runtimeName + '_core/DeviceBridge').getDisplayWidth(); clearInterval(i); (() => { const _getImportsFunctionName = "$getImportsFunctionName"; $loaderScript })(); } catch (e) {} diff --git a/native/build.gradle.kts b/native/build.gradle.kts index 114ac446..8ed45c4c 100644 --- a/native/build.gradle.kts +++ b/native/build.gradle.kts @@ -108,25 +108,26 @@ android { sourceSets["main"].jniLibs.srcDir("build/rustJniLibs/android") } -val rustupTargetTasks = cargoTargets.mapIndexed { i, target -> +// Register rustup tasks +val rustupTasks = cargoTargets.map { target -> tasks.register("rustup${target.taskSuffix}") { - group = "build" workingDir = file("rust") - // Idempotent add (only if missing) - commandLine( - "sh", "-c", - "rustup target list --installed | grep -q ${target.triple} || rustup target add ${target.triple}" - ) + commandLine("rustup", "target", "add", target.triple) + } +} - if (i > 0) { - val previousTaskName = "rustup${cargoTargets[i - 1].taskSuffix}" - mustRunAfter(tasks.named(previousTaskName)) +// Ensure rustup task ordering +rustupTasks.forEachIndexed { index, task -> + if (index > 0) { + task.configure { + mustRunAfter(rustupTasks[index - 1]) } } } -val syncTasks = cargoTargets.map { target -> - val rustupTask = tasks.named("rustup${target.taskSuffix}") +// Register sync & cargo build tasks +val syncTasks = cargoTargets.mapIndexed { index, target -> + val rustupTask = rustupTasks[index] val cargoTask = tasks.register("cargoBuild${target.taskSuffix}") { group = "build" dependsOn(rustupTask) diff --git a/native/rust/src/modules/composer_hook.rs b/native/rust/src/modules/composer_hook.rs index 8aeaf2d8..c7f3780f 100644 --- a/native/rust/src/modules/composer_hook.rs +++ b/native/rust/src/modules/composer_hook.rs @@ -185,12 +185,23 @@ pub unsafe fn composer_eval(env: JNIEnv, _: *mut c_void, script: JString) -> job { let mut env = env; - let script_str = get_jni_string(&mut env, script).expect("Failed to get script"); + let script_str = match get_jni_string(&mut env, script) { + Ok(s) => s, + Err(_) => return std::ptr::null_mut(), + }; + + if JS_EVAL_ORIGINAL2.is_none() || GLOBAL_INSTANCE.is_none() || GLOBAL_CTX.is_none() { + if let Ok(s) = env.new_string("Composer hook not initialized") { + return s.into_raw(); + } + return std::ptr::null_mut(); + } + let script_length = script_str.len(); - let js_value = JS_EVAL_ORIGINAL2.expect("No js eval found")( - GLOBAL_INSTANCE.expect("No global instance found"), - GLOBAL_CTX.expect("No global context found"), + let js_value = JS_EVAL_ORIGINAL2.unwrap()( + GLOBAL_INSTANCE.unwrap(), + GLOBAL_CTX.unwrap(), std::ptr::null_mut(), (script_str + "\0").as_ptr() as *mut u8, script_length, @@ -200,29 +211,34 @@ pub unsafe fn composer_eval(env: JNIEnv, _: *mut c_void, script: JString) -> job let result: String = if js_value.tag == JS_TAG_STRING { let string = js_value.u.ptr as *mut JsString; - CStr::from_ptr((*string).str8.as_ptr() as *const u8).to_str().unwrap().into() + match CStr::from_ptr((*string).str8.as_ptr() as *const u8).to_str() { + Ok(s) => s.to_string(), + Err(_) => "[invalid string]".to_string(), + } } else if js_value.tag == JS_TAG_INT { js_value.u.int32.to_string() } else if js_value.tag == JS_TAG_BOOL { - if js_value.u.int32 == 1 { "true" } else { "false" }.into() + if js_value.u.int32 == 1 { "true" } else { "false" }.to_string() } else if js_value.tag == JS_TAG_NULL { - "null".into() + "null".to_string() } else if js_value.tag == JS_TAG_UNDEFINED { - "undefined".into() + "undefined".to_string() } else if js_value.tag == JS_TAG_OBJECT { - "[object]".into() + "[object]".to_string() } else if js_value.tag == JS_TAG_FLOAT64 { js_value.u.float64.to_string() } else if js_value.tag == JS_TAG_EXCEPTION { - "Failed to evaluate script".into() + "Failed to evaluate script".to_string() } else { - "[unknown tag ".to_owned() + &js_value.tag.to_string() + "]".into() + format!("[unknown tag {}]", js_value.tag) }; - return env.new_string(result).unwrap().into_raw() + if let Ok(s) = env.new_string(result) { + return s.into_raw(); + } } - return env.new_string("Architecture not supported").unwrap().into_raw(); + std::ptr::null_mut() } pub fn init() { @@ -237,11 +253,20 @@ pub fn init() { #[cfg(target_arch = "aarch64")] { - if let Some(signature) = sig::find_signature( + let signature = sig::find_signature( &common::CLIENT_MODULE, "00 E4 00 6F 29 00 80 52 76 00 04 8B", -0x28, "A1 B0 07 92 81 46", -0x7 - ) { + ).or_else(|| { + warn!("primary js_eval signature failed, trying fallback"); + sig::find_signature( + &common::CLIENT_MODULE, + "00 E4 00 6F ?? ?? ?? ?? ?? ?? ?? ??", -0x28, + "?? ?? ?? ?? ?? ??", -0x7 + ) + }); + + if let Some(signature) = signature { dobby_hook!(signature as *mut c_void, js_eval); unsafe {