From de0a59ac545b900ebbd84992e1fb115d045b1e7f Mon Sep 17 00:00:00 2001 From: shyguyhex Date: Thu, 13 Jan 2022 14:39:21 -0600 Subject: [PATCH] [Debugger] JS API: Add script.abort() (#2170) --- .../Debugger/ScriptAPI/ScriptAPI.h | 1 + .../ScriptAPI/ScriptAPI_AddressRange.cpp | 2 + .../Debugger/ScriptAPI/ScriptAPI_script.cpp | 14 ++ .../UserInterface/Debugger/ScriptInstance.cpp | 183 +++++++++++------- .../UserInterface/Debugger/ScriptInstance.h | 20 +- 5 files changed, 147 insertions(+), 73 deletions(-) diff --git a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.h b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.h index 9eabd53ee..7711f45cd 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.h +++ b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.h @@ -193,6 +193,7 @@ namespace ScriptAPI void Define_script(duk_context* ctx); duk_ret_t js_script_keepalive(duk_context* ctx); duk_ret_t js_script_timeout(duk_context* ctx); + duk_ret_t js_script_abort(duk_context* ctx); // ScriptAPI_fs void Define_fs(duk_context* ctx); diff --git a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_AddressRange.cpp b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_AddressRange.cpp index 854bd79c8..9e383102d 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_AddressRange.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_AddressRange.cpp @@ -1,6 +1,8 @@ #include #include "ScriptAPI.h" +#pragma warning(disable: 4702) // disable unreachable code warning + static void GetRange(duk_context* ctx, duk_idx_t idx, uint32_t* start, uint32_t* end); void ScriptAPI::Define_AddressRange(duk_context* ctx) diff --git a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_script.cpp b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_script.cpp index 5e7c1ae50..cd8c96515 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_script.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_script.cpp @@ -1,11 +1,14 @@ #include #include "ScriptAPI.h" +#pragma warning(disable: 4702) // disable unreachable code warning + void ScriptAPI::Define_script(duk_context *ctx) { const DukPropListEntry props[] = { { "timeout", DukCFunction(js_script_timeout) }, { "keepalive", DukCFunction(js_script_keepalive) }, + { "abort", DukCFunction(js_script_abort) }, { nullptr } }; @@ -46,3 +49,14 @@ duk_ret_t ScriptAPI::js_script_keepalive(duk_context *ctx) return 0; } + +duk_ret_t ScriptAPI::js_script_abort(duk_context* ctx) +{ + CheckArgs(ctx, {}); + CScriptInstance* inst = GetInstance(ctx); + if (inst->PrepareAbort()) + { + return duk_fatal(ctx, "aborted"); + } + return 0; +} diff --git a/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp b/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp index 1d193cf44..c7329314b 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp @@ -2,7 +2,6 @@ #include "ScriptTypes.h" #include "ScriptInstance.h" #include "ScriptAPI/ScriptAPI.h" - #include extern "C" { @@ -22,7 +21,8 @@ CScriptInstance::CScriptInstance(CScriptSystem* sys, const char* name) : m_ExecStartTime(0), m_SourceCode(nullptr), m_CurExecCallbackId(JS_INVALID_CALLBACK), - m_bStopping(false) + m_bStopping(false), + m_bAborting(false) { } @@ -94,18 +94,27 @@ bool CScriptInstance::Run(const char* path) ScriptAPI::InitEnvironment(m_Ctx, this); - duk_push_string(m_Ctx, m_InstanceName.c_str()); - if(duk_pcompile_string_filename(m_Ctx, DUK_COMPILE_STRICT, m_SourceCode) != 0 || - duk_pcall(m_Ctx, 0) == DUK_EXEC_ERROR) + try { - duk_get_prop_string(m_Ctx, -1, "stack"); - m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); - duk_pop_n(m_Ctx, 2); - goto error_cleanup; + duk_push_string(m_Ctx, m_InstanceName.c_str()); + if (duk_pcompile_string_filename(m_Ctx, DUK_COMPILE_STRICT, m_SourceCode) != 0 || + duk_pcall(m_Ctx, 0) == DUK_EXEC_ERROR) + { + duk_get_prop_string(m_Ctx, -1, "stack"); + m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + duk_pop_n(m_Ctx, 2); + goto error_cleanup; + } + + duk_pop(m_Ctx); + } + catch (std::runtime_error& exc) + { + FatalHandler(exc); } - duk_pop(m_Ctx); return true; + error_cleanup: Cleanup(); @@ -130,21 +139,39 @@ void CScriptInstance::SetStopping(bool bStopping) m_bStopping = bStopping; } +bool CScriptInstance::PrepareAbort() +{ + if (!m_bAborting) + { + m_bAborting = true; + m_RefCount = 0; + return true; + } + return false; +} + void CScriptInstance::RawCMethodCall(void* dukThisHeapPtr, duk_c_function func, JSDukArgSetupFunc argSetupFunc, void *argSetupParam) { - m_ExecStartTime = Timestamp(); - duk_push_c_function(m_Ctx, func, DUK_VARARGS); - duk_push_heapptr(m_Ctx, dukThisHeapPtr); - duk_idx_t nargs = argSetupFunc ? argSetupFunc(m_Ctx, argSetupParam) : 0; - - if (duk_pcall_method(m_Ctx, nargs) == DUK_EXEC_ERROR) + try { - duk_get_prop_string(m_Ctx, -1, "stack"); - m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + m_ExecStartTime = Timestamp(); + duk_push_c_function(m_Ctx, func, DUK_VARARGS); + duk_push_heapptr(m_Ctx, dukThisHeapPtr); + duk_idx_t nargs = argSetupFunc ? argSetupFunc(m_Ctx, argSetupParam) : 0; + + if (duk_pcall_method(m_Ctx, nargs) == DUK_EXEC_ERROR) + { + duk_get_prop_string(m_Ctx, -1, "stack"); + m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + duk_pop(m_Ctx); + } + duk_pop(m_Ctx); } - - duk_pop(m_Ctx); + catch (std::runtime_error& exc) + { + FatalHandler(exc); + } } void CScriptInstance::PostCMethodCall(void* dukThisHeapPtr, duk_c_function func, JSDukArgSetupFunc argSetupFunc, @@ -156,67 +183,74 @@ void CScriptInstance::PostCMethodCall(void* dukThisHeapPtr, duk_c_function func, void CScriptInstance::RawConsoleInput(const char* code) { m_System->ConsoleLog("> %s", code); - - duk_get_global_string(m_Ctx, HS_gInputListener); - - if (duk_is_function(m_Ctx, -1)) + + try { + duk_get_global_string(m_Ctx, HS_gInputListener); + + if (duk_is_function(m_Ctx, -1)) + { + m_ExecStartTime = Timestamp(); + duk_push_string(m_Ctx, code); + if (duk_pcall(m_Ctx, 1) != DUK_EXEC_SUCCESS) + { + duk_get_prop_string(m_Ctx, -1, "stack"); + m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + duk_pop_n(m_Ctx, 2); + return; + } + else + { + duk_pop(m_Ctx); + return; + } + } + duk_pop(m_Ctx); + m_ExecStartTime = Timestamp(); - duk_push_string(m_Ctx, code); - if (duk_pcall(m_Ctx, 1) != DUK_EXEC_SUCCESS) + + duk_push_string(m_Ctx, stdstr_f("", m_InstanceName.c_str()).c_str()); + if (duk_pcompile_string_filename(m_Ctx, DUK_COMPILE_STRICT, code) != 0 || + duk_pcall(m_Ctx, 0) == DUK_EXEC_ERROR) { duk_get_prop_string(m_Ctx, -1, "stack"); m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); - duk_pop_n(m_Ctx, 2); - return; + duk_pop(m_Ctx); } else { - duk_pop(m_Ctx); - return; - } - } - duk_pop(m_Ctx); + if (duk_is_string(m_Ctx, -1)) + { + m_System->ConsoleLog("\"%s\"", duk_get_string(m_Ctx, -1)); + } + else if (duk_is_object(m_Ctx, -1)) + { + duk_dup(m_Ctx, -1); + duk_get_global_string(m_Ctx, "JSON"); + duk_get_prop_string(m_Ctx, -1, "stringify"); + duk_remove(m_Ctx, -2); + duk_pull(m_Ctx, -3); + duk_push_null(m_Ctx); + duk_push_int(m_Ctx, 2); + duk_pcall(m_Ctx, 3); - m_ExecStartTime = Timestamp(); - - duk_push_string(m_Ctx, stdstr_f("", m_InstanceName.c_str()).c_str()); - if (duk_pcompile_string_filename(m_Ctx, DUK_COMPILE_STRICT, code) != 0 || - duk_pcall(m_Ctx, 0) == DUK_EXEC_ERROR) - { - duk_get_prop_string(m_Ctx, -1, "stack"); - m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + const char* str = duk_safe_to_string(m_Ctx, -2); + const char* res = duk_get_string(m_Ctx, -1); + + m_System->ConsoleLog("%s %s", str, res); + duk_pop(m_Ctx); + } + else + { + m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + } + } duk_pop(m_Ctx); } - else + catch (std::runtime_error& exc) { - if (duk_is_string(m_Ctx, -1)) - { - m_System->ConsoleLog("\"%s\"", duk_get_string(m_Ctx, -1)); - } - else if(duk_is_object(m_Ctx, -1)) - { - duk_dup(m_Ctx, -1); - duk_get_global_string(m_Ctx, "JSON"); - duk_get_prop_string(m_Ctx, -1, "stringify"); - duk_remove(m_Ctx, -2); - duk_pull(m_Ctx, -3); - duk_push_null(m_Ctx); - duk_push_int(m_Ctx, 2); - duk_pcall(m_Ctx, 3); - - const char* str = duk_safe_to_string(m_Ctx, -2); - const char* res = duk_get_string(m_Ctx, -1); - - m_System->ConsoleLog("%s %s", str, res); - duk_pop(m_Ctx); - } - else - { - m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); - } + FatalHandler(exc); } - duk_pop(m_Ctx); } void CScriptInstance::SetExecTimeout(uint64_t timeout) @@ -299,3 +333,16 @@ void CScriptInstance::StopRegisteredWorkers() worker->StopWorkerProc(); } } + +void CScriptInstance::FatalHandler(std::runtime_error& exc) +{ + if (m_bAborting) + { + m_System->ConsoleLog("[SCRIPTSYS]: '%s' aborted", m_InstanceName.c_str()); + } + else + { + m_System->ConsoleLog("%s", exc.what()); + g_Notify->BreakPoint(__FILE__, __LINE__); + } +} diff --git a/Source/Project64/UserInterface/Debugger/ScriptInstance.h b/Source/Project64/UserInterface/Debugger/ScriptInstance.h index 2bf2bf6f9..219d0d777 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptInstance.h +++ b/Source/Project64/UserInterface/Debugger/ScriptInstance.h @@ -18,6 +18,7 @@ private: JSAppCallbackID m_CurExecCallbackId; std::vector m_Workers; bool m_bStopping; + bool m_bAborting; public: CScriptInstance(CScriptSystem* sys, const char* name); @@ -37,6 +38,7 @@ public: void DecRefCount(); void SetStopping(bool bStopping); inline bool IsStopping() { return m_bStopping; } + bool PrepareAbort(); bool RegisterWorker(CScriptWorker* worker); void UnregisterWorker(CScriptWorker* worker); @@ -62,14 +64,21 @@ public: duk_push_heapptr(m_Ctx, dukFuncHeapPtr); duk_idx_t nargs = argSetupFunc ? argSetupFunc(m_Ctx, param) : 0; - if (duk_pcall(m_Ctx, nargs) == DUK_EXEC_ERROR) + try { - duk_get_prop_string(m_Ctx, -1, "stack"); - m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + if (duk_pcall(m_Ctx, nargs) == DUK_EXEC_ERROR) + { + duk_get_prop_string(m_Ctx, -1, "stack"); + m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + duk_pop(m_Ctx); + } + duk_pop(m_Ctx); } - - duk_pop(m_Ctx); + catch (std::runtime_error& exc) + { + FatalHandler(exc); + } } void RawCMethodCall(void* dukThisHeapPtr, duk_c_function func, @@ -83,6 +92,7 @@ public: void RawConsoleInput(const char* code); private: + void FatalHandler(std::runtime_error& exc); static uint64_t Timestamp(); void Cleanup(); };