From 19bdbe1f15b72e0797186d5382fd83c4852ae058 Mon Sep 17 00:00:00 2001 From: Peter Z Date: Tue, 22 Apr 2025 12:36:25 +0300 Subject: [PATCH] Throw exceptions from Panda loader --- arkoala-arkts/arkui/src/Application.ts | 12 ++-- arkoala-arkts/arkui/src/ets/Application.ets | 12 ++-- .../ets-harness/src/EtsHarnessApplication.ts | 16 +++-- arkoala-arkts/ets-harness/src/loader.ts | 6 +- arkoala-arkts/loader/src/loader.ts | 8 ++- .../ets-harness/src/EtsHarnessApplication.ts | 16 +++-- interop/src/arkts/InteropNativeModule.ts | 2 +- interop/src/cangjie/InteropNativeModule.cj | 6 +- interop/src/cpp/common-interop.cc | 11 ++-- interop/src/cpp/vmloader.cc | 59 ++++++++++++------- interop/src/interop/InteropNativeModule.ts | 2 +- 11 files changed, 92 insertions(+), 58 deletions(-) diff --git a/arkoala-arkts/arkui/src/Application.ts b/arkoala-arkts/arkui/src/Application.ts index f620d975a..f3c26fe79 100644 --- a/arkoala-arkts/arkui/src/Application.ts +++ b/arkoala-arkts/arkui/src/Application.ts @@ -152,7 +152,7 @@ export class Application { private timer: MutableState | undefined = undefined private currentCrash: Object | undefined = undefined private enableDumpTree = false - private exitApp: boolean = false + private appResult: string = "" private userView: UserView | undefined = undefined private withLog = false @@ -284,7 +284,7 @@ export class Application { private render() { if (this.withLog) InteropNativeModule._NativeLog("ARKTS: render") } - enter(arg0: int32, arg1: int32, foreignContext: pointer): boolean { + enter(arg0: int32, arg1: int32, foreignContext: pointer): string { enterForeignContext(foreignContext) try { if (this.withLog) UserView.startNativeLog(1) @@ -303,7 +303,7 @@ export class Application { if (stack) { leaveForeignContext() InteropNativeModule._NativeLog("Application.enter: " + stack) - return true + return error.message } } this.currentCrash = error as Object @@ -326,11 +326,11 @@ export class Application { if (stack) { console.log("Application.enter stack trace: " + stack) } - this.exitApp = true + this.appResult = e.message } } leaveForeignContext() - return this.exitApp + return this.appResult } loopIteration(arg0: int32, arg1: int32) { @@ -368,7 +368,7 @@ export class Application { return NativeLog.Default.getNativeLog(0) } case EventType.ExitApp: { - this.exitApp = true + this.appResult = "0" break } case EventType.SyncNeeded: { diff --git a/arkoala-arkts/arkui/src/ets/Application.ets b/arkoala-arkts/arkui/src/ets/Application.ets index 8ee0c7757..1e512d614 100644 --- a/arkoala-arkts/arkui/src/ets/Application.ets +++ b/arkoala-arkts/arkui/src/ets/Application.ets @@ -153,7 +153,7 @@ export class Application { private timer: MutableState | undefined = undefined private currentCrash: Object | undefined = undefined private enableDumpTree = false - private exitApp: boolean = false + private appResult: string = "" private userView: UserView | undefined = undefined private withLog = false @@ -285,7 +285,7 @@ export class Application { private render() { if (this.withLog) InteropNativeModule._NativeLog("ARKTS: render") } - enter(arg0: int32, arg1: int32, foreignContext: pointer): boolean { + enter(arg0: int32, arg1: int32, foreignContext: pointer): string { enterForeignContext(foreignContext) try { if (this.withLog) UserView.startNativeLog(1) @@ -304,7 +304,7 @@ export class Application { if (stack) { leaveForeignContext() InteropNativeModule._NativeLog("Application.enter: " + stack) - return true + return error.message } } this.currentCrash = error as Object @@ -327,11 +327,11 @@ export class Application { if (stack) { console.log("Application.enter stack trace: " + stack) } - this.exitApp = true + this.appResult = e.message } } leaveForeignContext() - return this.exitApp + return this.appResult } loopIteration(arg0: int32, arg1: int32) { @@ -369,7 +369,7 @@ export class Application { return NativeLog.Default.getNativeLog(0) } case EventType.ExitApp: { - this.exitApp = true + this.appResult = "0" break } case EventType.SyncNeeded: { diff --git a/arkoala-arkts/ets-harness/src/EtsHarnessApplication.ts b/arkoala-arkts/ets-harness/src/EtsHarnessApplication.ts index 49a3da6d1..1127398db 100644 --- a/arkoala-arkts/ets-harness/src/EtsHarnessApplication.ts +++ b/arkoala-arkts/ets-harness/src/EtsHarnessApplication.ts @@ -135,12 +135,16 @@ export class EtsHarnessApplication { return this.root!.value.peer.ptr } - enter(arg0: int32, arg1: int32, foreignContext: pointer): boolean { - this.timer!.value = Date.now() as int64 - checkEvents() - this.updateStates(this.manager!, this.root!) - callScheduledCallbacks() - return true + enter(arg0: int32, arg1: int32, foreignContext: pointer): string { + try { + this.timer!.value = Date.now() as int64 + checkEvents() + this.updateStates(this.manager!, this.root!) + callScheduledCallbacks() + return "0" + } catch (err) { + return (err as Error).message + } } emitEvent(type: int32, target: int32, arg0: int32, arg1: int32): string { diff --git a/arkoala-arkts/ets-harness/src/loader.ts b/arkoala-arkts/ets-harness/src/loader.ts index a7fd38912..a98eac9b5 100644 --- a/arkoala-arkts/ets-harness/src/loader.ts +++ b/arkoala-arkts/ets-harness/src/loader.ts @@ -36,7 +36,7 @@ export type KNativePointer = KPointer export interface LoaderOps { _LoadVirtualMachine(vmKind: int32, appClassPath: string, appLibPath: string): int32 _StartApplication(appUrl: string, appParams: string): KPointer - _RunApplication(arg0: int32, arg1: int32): boolean + _RunApplication(arg0: int32, arg1: int32): string } export interface NativeControl extends LoaderOps { @@ -77,7 +77,9 @@ export class AppControlImpl implements AppControl { return this } nextFrame(): AppControl { - nativeModule()._RunApplication(0, 0) + const result = nativeModule()._RunApplication(0, 0) + if (result !== "0") + throw new Error(result) return this } skipFrames(count: int32): AppControl { diff --git a/arkoala-arkts/loader/src/loader.ts b/arkoala-arkts/loader/src/loader.ts index 8561d7c78..2e7038675 100644 --- a/arkoala-arkts/loader/src/loader.ts +++ b/arkoala-arkts/loader/src/loader.ts @@ -45,7 +45,7 @@ const nullptr = BigInt(0) export interface LoaderOps { _LoadVirtualMachine(vmKind: int32, appClassPath: string, appLibPath: string): int32 _StartApplication(appUrl: string, appParams: string): KPointer - _RunApplication(arg0: int32, arg1: int32): boolean + _RunApplication(arg0: int32, arg1: int32): string _SetCallbackDispatcher(dispather: Object): void _CallCallbackSync(kind: int32, data: Uint8Array, length: int32): void } @@ -180,7 +180,8 @@ class Application { nativeModule()._UnblockVsyncWait(pipelineContext) }, 64) } - while (!nativeModule()._RunApplication(0, 0)) { + let result = nativeModule()._RunApplication(0, 0) + while (result === "") { await nativeModule()._VSyncAwait(pipelineContext) if (loopIterations != undefined) { loopIterations-- @@ -188,7 +189,10 @@ class Application { break } } + result = nativeModule()._RunApplication(0, 0) } + if (result !== "0") + throw new Error(result) console.log("EXIT_APP"); process?.exit(0) } diff --git a/arkoala/ets-harness/src/EtsHarnessApplication.ts b/arkoala/ets-harness/src/EtsHarnessApplication.ts index 17338ef53..5413cf6d5 100644 --- a/arkoala/ets-harness/src/EtsHarnessApplication.ts +++ b/arkoala/ets-harness/src/EtsHarnessApplication.ts @@ -117,12 +117,16 @@ export class EtsHarnessApplication { return (this.root!.value as PeerNode).peer.ptr } - enter(arg0: int32, arg1: int32, foreignContext: pointer): boolean { - this.timer!.value = Date.now() as int64 - checkEvents() - this.updateStates(this.manager!, this.root!) - callScheduledCallbacks() - return true + enter(arg0: int32, arg1: int32, foreignContext: pointer): string { + try { + this.timer!.value = Date.now() as int64 + checkEvents() + this.updateStates(this.manager!, this.root!) + callScheduledCallbacks() + return "0" + } catch (err) { + return (err as Error).message + } } emitEvent(type: int32, target: int32, arg0: int32, arg1: int32): string { diff --git a/interop/src/arkts/InteropNativeModule.ts b/interop/src/arkts/InteropNativeModule.ts index 619641974..604f95eb7 100644 --- a/interop/src/arkts/InteropNativeModule.ts +++ b/interop/src/arkts/InteropNativeModule.ts @@ -38,7 +38,7 @@ export class InteropNativeModule { native static _CallCallbackResourceHolder(holder: KPointer, resourceId: int32): void native static _CallCallbackResourceReleaser(releaser: KPointer, resourceId: int32): void native static _LoadVirtualMachine(arg0: int32, arg1: string, arg2: string): int32 - native static _RunApplication(arg0: int32, arg1: int32): boolean + native static _RunApplication(arg0: int32, arg1: int32): string native static _StartApplication(appUrl: string, appParams: string): KPointer native static _EmitEvent(eventType: int32, target: int32, arg0: int32, arg1: int32): void native static _CallForeignVM(context:KPointer, callback: int32, data: KSerializerBuffer, dataLength: int32): int32 diff --git a/interop/src/cangjie/InteropNativeModule.cj b/interop/src/cangjie/InteropNativeModule.cj index d2ce6919b..2e78d316c 100644 --- a/interop/src/cangjie/InteropNativeModule.cj +++ b/interop/src/cangjie/InteropNativeModule.cj @@ -47,7 +47,7 @@ foreign { func CallCallbackResourceReleaser(releaser: UInt64, resourceId: Int32): Unit func CallForeignVM(foreignContext: UInt64, kind: Int32, data: CPointer, length: Int32): Int32 func LoadVirtualMachine(arg0: Int32, arg1: CString, arg2: CString): Int32 - func RunApplication(arg0: Int32, arg1: Int32): Bool + func RunApplication(arg0: Int32, arg1: Int32): CString func StartApplication(appUrl: CString, appParams: CString): UInt64 func EmitEvent(eventType: Int32, target: Int32, arg0: Int32, arg1: Int32): CString func RestartWith(page: CString): Unit @@ -222,10 +222,10 @@ public open class InteropNativeModule { return result } } - public static func _RunApplication(arg0: Int32, arg1: Int32): Bool { + public static func _RunApplication(arg0: Int32, arg1: Int32): String { unsafe { let result = RunApplication(arg0, arg1) - return result + return result.toString() } } public static func _StartApplication(appUrl: String, appParams: String): UInt64 { diff --git a/interop/src/cpp/common-interop.cc b/interop/src/cpp/common-interop.cc index 4686a8314..8595fbe20 100644 --- a/interop/src/cpp/common-interop.cc +++ b/interop/src/cpp/common-interop.cc @@ -248,7 +248,7 @@ struct ForeignVMContext { }; typedef KInt (*LoadVirtualMachine_t)(KInt vmKind, const char* classPath, const char* libraryPath, const struct ForeignVMContext* foreignVM); typedef KNativePointer (*StartApplication_t)(const char* appUrl, const char* appParams); -typedef KBoolean (*RunApplication_t)(const KInt arg0, const KInt arg1); +typedef const char* (*RunApplication_t)(const KInt arg0, const KInt arg1); typedef const char* (*EmitEvent_t)(const KInt type, const KInt target, const KInt arg0, const KInt arg1); typedef void (*RestartWith_t)(const char* page); @@ -293,12 +293,15 @@ KNativePointer impl_StartApplication(const KStringPtr& appUrl, const KStringPtr& } KOALA_INTEROP_2(StartApplication, KNativePointer, KStringPtr, KStringPtr) -KBoolean impl_RunApplication(const KInt arg0, const KInt arg1) { +KStringPtr impl_RunApplication(const KInt arg0, const KInt arg1) { static RunApplication_t impl = nullptr; if (!impl) impl = reinterpret_cast(getImpl(nullptr, "RunApplication")); - return impl(arg0, arg1); + const char *out = impl(arg0, arg1); + auto size = std::string(out).size(); + KStringPtr result(out, size, true); + return result; } -KOALA_INTEROP_2(RunApplication, KBoolean, KInt, KInt) +KOALA_INTEROP_2(RunApplication, KStringPtr, KInt, KInt) KStringPtr impl_EmitEvent(KVMContext vmContext, KInt type, KInt target, KInt arg0, KInt arg1) { static EmitEvent_t impl = nullptr; diff --git a/interop/src/cpp/vmloader.cc b/interop/src/cpp/vmloader.cc index d5fc15262..578441be7 100644 --- a/interop/src/cpp/vmloader.cc +++ b/interop/src/cpp/vmloader.cc @@ -491,7 +491,7 @@ const AppInfo javaAppInfo = { "start", "()J", "enter", - "(IIJ)Z", + "(IIJ)Ljava/lang/String;", "emitEvent", "(IIII)Ljava/lang/String;", }; @@ -505,7 +505,7 @@ const AppInfo pandaAppInfo = { "start", "J:J", "enter", - "IIJ:Z", + "IIJ:Lstd/core/String;", "emitEvent", "IIII:Lstd/core/String;", }; @@ -516,7 +516,7 @@ const AppInfo harnessAppInfo = { "start", "J:J", "enter", - "IIJ:Z", + "IIJ:Lstd/core/String;", "emitEvent", "IIII:Lstd/core/String;", "restartWith", @@ -531,7 +531,7 @@ const AppInfo harnessAniAppInfo = { "start", "J:J", "enter", - "IIJ:Z", + "IIJ:Lstd/core/String;", "emitEvent", "IIII:Lstd/core/String;", "restartWith", @@ -544,7 +544,7 @@ const AppInfo aniAppInfo = { "start", "J:J", "enter", - "IIJ:Z", + "IIJ:Lstd/core/String;", "emitEvent", "IIII:Lstd/core/String;", }; @@ -787,22 +787,31 @@ extern "C" DLL_EXPORT KNativePointer StartApplication(const char* appUrl, const return nullptr; } -extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, const KInt arg1) { +/** + * Return values: + * "" : continue running + * "0" : app exited normally + * any other string : an error was thrown + */ +extern "C" DLL_EXPORT const char* RunApplication(const KInt arg0, const KInt arg1) { #ifdef KOALA_JNI if (g_vmEntry.vmKind == JAVA_VM_KIND) { JNIEnv* jEnv = (JNIEnv*)(g_vmEntry.env); - auto result = jEnv->CallBooleanMethod( + auto result = jEnv->CallObjectMethod( (jobject)(g_vmEntry.app), (jmethodID)(g_vmEntry.enter), (jint)arg0, (jint)arg1, (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext) ); - if (jEnv->ExceptionCheck()) { - jEnv->ExceptionDescribe(); + jthrowable error = jEnv->ExceptionOccurred(); + if (error) { jEnv->ExceptionClear(); + jclass errorClass = jEnv->GetObjectClass(error); + jmethodID getMessage = jEnv->GetMethodID(errorClass, "getMessage", "()Ljava/lang/String;"); + result = jEnv->CallObjectMethod(error, getMessage); } - return result; + return jEnv->GetStringUTFChars(result, 0); } #endif #if defined(KOALA_ETS_NAPI) @@ -810,21 +819,24 @@ extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, const KInt arg1) EtsEnv* etsEnv = (EtsEnv*)(g_vmEntry.env); if (!g_vmEntry.enter) { LOGE("Cannot find enter method"); - return -1; + return "Cannot find enter method"; } - auto result = etsEnv->CallBooleanMethod( + ets_object result = etsEnv->CallObjectMethod( (ets_object)(g_vmEntry.app), (ets_method)(g_vmEntry.enter), (ets_int)arg0, (ets_int)arg1, (int64_t)(intptr_t)(&g_vmEntry.foreignVMContext) ); - if (etsEnv->ErrorCheck()) { + ets_error error = etsEnv->ErrorOccurred(); + if (error) { LOGE("Calling enter() method gave an error"); - etsEnv->ErrorDescribe(); etsEnv->ErrorClear(); + ets_class errorClass = etsEnv->GetObjectClass(error); + ets_method getMessage = etsEnv->Getp_method(errorClass, "getMessage", ":Lstd/core/String;"); + result = etsEnv->CallObjectMethod(error, getMessage); } - return result; + return etsEnv->GetStringUTFChars((ets_string)result, 0); } #endif #if defined(KOALA_ANI) @@ -832,22 +844,27 @@ extern "C" DLL_EXPORT KBoolean RunApplication(const KInt arg0, const KInt arg1) ani_env* env = reinterpret_cast(g_vmEntry.env); if (g_vmEntry.enter == nullptr) { LOGE("Cannot find enter method"); - return -1; + return "Cannot find enter method"; } - ani_boolean result = ANI_FALSE; - auto status = env->Object_CallMethod_Boolean(reinterpret_cast(g_vmEntry.app), + ani_ref result {}; + auto status = env->Object_CallMethod_Ref(reinterpret_cast(g_vmEntry.app), reinterpret_cast(g_vmEntry.enter), &result, static_cast(arg0), static_cast(arg1), reinterpret_cast(&g_vmEntry.foreignVMContext)); if (status != ANI_OK) { ResetErrorIfExists(env); - return ANI_FALSE; + return "Some error occurred"; } - return result; + auto str = static_cast(result); + ani_size sz = 0; + env->String_GetUTF8Size(str, &sz); + auto buffer = new char[sz + 1]; + env->String_GetUTF8(str, buffer, sz + 1, &sz); + return buffer; } #endif - return 1; + return "Unknown VM kind"; } extern "C" DLL_EXPORT const char* EmitEvent(const KInt type, const KInt target, const KInt arg0, const KInt arg1) { diff --git a/interop/src/interop/InteropNativeModule.ts b/interop/src/interop/InteropNativeModule.ts index ca82c8cc5..21ac2de6a 100644 --- a/interop/src/interop/InteropNativeModule.ts +++ b/interop/src/interop/InteropNativeModule.ts @@ -34,7 +34,7 @@ export class InteropNativeModule { public static _GetNativeBufferPointer(data: ArrayBuffer): KPointer { throw "method not loaded" } public static _LoadVirtualMachine(arg0: int32, arg1: string, arg2: string): int32 { throw "method not loaded" } - public static _RunApplication(arg0: int32, arg1: int32): number { throw "method not loaded" } + public static _RunApplication(arg0: int32, arg1: int32): string { throw "method not loaded" } public static _StartApplication(appUrl: string, appParams: string): KPointer { throw "method not loaded" } public static _EmitEvent(eventType: int32, target: int32, arg0: int32, arg1: int32): void { throw "method not loaded" } public static _CallForeignVM(foreignContext: KPointer, kind: int32, args: KSerializerBuffer, argsSize: int32): int32 { throw "method not loaded" } -- Gitee