diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index 73b024d47..9e8f939b0 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -1278,6 +1278,38 @@ public: } }; + /** + * Helper function to handle a JS exception by extracting its message + * and freeing any additional JSValues + */ + template + static std::string + handleException( + JSContext* ctx, + const char* defaultMsg, + JSValues... valuesToFree) + { + // Get the actual exception object + JSValue exception = JS_GetException(ctx); + + // Extract the error message property + JSValue msgProp = JS_GetPropertyStr(ctx, exception, "message"); + const char* str = JS_ToCString(ctx, msgProp); + + std::string result = (str != nullptr) ? str : defaultMsg; + + if (str != nullptr) + JS_FreeCString(ctx, str); + + JS_FreeValue(ctx, msgProp); + JS_FreeValue(ctx, exception); + + // Free all additional values passed + (JS_FreeValue(ctx, valuesToFree), ...); + + return result; + } + /** * Validate that a js blob can be loaded by quickjs */ @@ -1299,38 +1331,15 @@ public: JSValue obj = JS_ReadObject( ctx, (uint8_t const*)buf, buf_len, JS_READ_OBJ_BYTECODE); - if (JS_IsException(obj)) + if (JS_IsException(obj) || JS_IsUndefined(obj)) { - if (const char* str = JS_ToCString(ctx, obj); str) - { - retval.emplace(str); - JS_FreeCString(ctx, str); - } - else - { - retval.emplace("invalid bytecode"); - } - - JS_FreeValue(ctx, obj); - return retval; + return handleException(ctx, "invalid bytecode"); } JSValue val = JS_EvalFunction(ctx, obj); if (JS_IsException(val)) { - if (const char* str = JS_ToCString(ctx, val); str) - { - retval.emplace(str); - JS_FreeCString(ctx, str); - } - else - { - retval.emplace("bytecode eval failure"); - } - JS_FreeValue(ctx, val); - // JS_FreeValue(ctx, obj); - - return retval; + return handleException(ctx, "bytecode eval failure", obj); } JS_FreeValue(ctx, val); @@ -1341,18 +1350,19 @@ public: "\"undefined\")) " "throw Error(\"Hook/Callback function required\")"; - val = JS_Eval(vm.ctx, testCalls, sizeof(testCalls) - 1, "", 0); + val = JS_Eval(vm.ctx, testCalls, strlen(testCalls), "", 0); if (JS_IsException(val)) { - if (const char* str = JS_ToCString(ctx, val); str) - { - retval.emplace(str); - JS_FreeCString(ctx, str); - } + std::string errMsg = + handleException(ctx, "Hook/Callback validation failure", obj); + return errMsg; } JS_FreeValue(ctx, val); + // We don't manually free the bytecode object (obj) here because + // JS_EvalFunction internally transforms it into a closure and takes + // ownership of its internal structures. // JS_FreeValue(ctx, obj); return retval;