michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JavaScript Debugging support - Hook support michael@0: */ michael@0: michael@0: #include "jsd.h" michael@0: michael@0: JSTrapStatus michael@0: jsd_InterruptHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, michael@0: void *closure) michael@0: { michael@0: JSDScript* jsdscript; michael@0: JSDContext* jsdc = (JSDContext*) closure; michael@0: JSD_ExecutionHookProc hook; michael@0: void* hookData; michael@0: michael@0: if( ! jsdc || ! jsdc->inited ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: if( JSD_IS_DANGEROUS_THREAD(jsdc) ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: /* local in case jsdc->interruptHook gets cleared on another thread */ michael@0: JSD_LOCK(); michael@0: hook = jsdc->interruptHook; michael@0: hookData = jsdc->interruptHookData; michael@0: JSD_UNLOCK(); michael@0: michael@0: if (!hook) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: if( ! jsdscript ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_INTERRUPTED, michael@0: hook, hookData, rval); michael@0: } michael@0: michael@0: JSTrapStatus michael@0: jsd_DebuggerHandler(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: jsval *rval, void *closure) michael@0: { michael@0: JSDScript* jsdscript; michael@0: JSDContext* jsdc = (JSDContext*) closure; michael@0: JSD_ExecutionHookProc hook; michael@0: void* hookData; michael@0: michael@0: if( ! jsdc || ! jsdc->inited ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: if( JSD_IS_DANGEROUS_THREAD(jsdc) ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: /* local in case jsdc->debuggerHook gets cleared on another thread */ michael@0: JSD_LOCK(); michael@0: hook = jsdc->debuggerHook; michael@0: hookData = jsdc->debuggerHookData; michael@0: JSD_UNLOCK(); michael@0: if(!hook) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: if( ! jsdscript ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: return jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUGGER_KEYWORD, michael@0: hook, hookData, rval); michael@0: } michael@0: michael@0: michael@0: JSTrapStatus michael@0: jsd_ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: jsval *rvalArg, void *closure) michael@0: { michael@0: JSDScript* jsdscript; michael@0: JSDContext* jsdc = (JSDContext*) closure; michael@0: JSD_ExecutionHookProc hook; michael@0: void* hookData; michael@0: michael@0: if( ! jsdc || ! jsdc->inited ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: if( JSD_IS_DANGEROUS_THREAD(jsdc) ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: /* local in case jsdc->throwHook gets cleared on another thread */ michael@0: JSD_LOCK(); michael@0: hook = jsdc->throwHook; michael@0: hookData = jsdc->throwHookData; michael@0: JSD_UNLOCK(); michael@0: if (!hook) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, JSNullFramePtr()); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: if( ! jsdscript ) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: JS::RootedValue rval(cx); michael@0: JS_GetPendingException(cx, &rval); michael@0: michael@0: JSTrapStatus result = jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_THROW, michael@0: hook, hookData, rval.address()); michael@0: *rvalArg = rval; michael@0: return result; michael@0: } michael@0: michael@0: JSTrapStatus michael@0: jsd_CallExecutionHook(JSDContext* jsdc, michael@0: JSContext *cx, michael@0: unsigned type, michael@0: JSD_ExecutionHookProc hook, michael@0: void* hookData, michael@0: jsval* rval) michael@0: { michael@0: unsigned hookanswer = JSD_HOOK_THROW == type ? michael@0: JSD_HOOK_RETURN_CONTINUE_THROW : michael@0: JSD_HOOK_RETURN_CONTINUE; michael@0: JSDThreadState* jsdthreadstate; michael@0: michael@0: if(hook && nullptr != (jsdthreadstate = jsd_NewThreadState(jsdc, cx))) michael@0: { michael@0: if ((type != JSD_HOOK_THROW && type != JSD_HOOK_INTERRUPTED) || michael@0: jsdc->flags & JSD_MASK_TOP_FRAME_ONLY || michael@0: !(jsdthreadstate->flags & TS_HAS_DISABLED_FRAME)) michael@0: { michael@0: /* michael@0: * if it's not a throw and it's not an interrupt, michael@0: * or we're only masking the top frame, michael@0: * or there are no disabled frames in this stack, michael@0: * then call out. michael@0: */ michael@0: hookanswer = hook(jsdc, jsdthreadstate, type, hookData, rval); michael@0: jsd_DestroyThreadState(jsdc, jsdthreadstate); michael@0: } michael@0: } michael@0: michael@0: switch(hookanswer) michael@0: { michael@0: case JSD_HOOK_RETURN_ABORT: michael@0: case JSD_HOOK_RETURN_HOOK_ERROR: michael@0: return JSTRAP_ERROR; michael@0: case JSD_HOOK_RETURN_RET_WITH_VAL: michael@0: return JSTRAP_RETURN; michael@0: case JSD_HOOK_RETURN_THROW_WITH_VAL: michael@0: return JSTRAP_THROW; michael@0: case JSD_HOOK_RETURN_CONTINUE: michael@0: break; michael@0: case JSD_HOOK_RETURN_CONTINUE_THROW: michael@0: /* only makes sense for jsd_ThrowHandler (which init'd rval) */ michael@0: MOZ_ASSERT(JSD_HOOK_THROW == type); michael@0: return JSTRAP_THROW; michael@0: default: michael@0: MOZ_ASSERT(0); michael@0: break; michael@0: } michael@0: return JSTRAP_CONTINUE; michael@0: } michael@0: michael@0: bool michael@0: jsd_CallCallHook (JSDContext* jsdc, michael@0: JSContext *cx, michael@0: unsigned type, michael@0: JSD_CallHookProc hook, michael@0: void* hookData) michael@0: { michael@0: bool hookanswer; michael@0: JSDThreadState* jsdthreadstate; michael@0: michael@0: hookanswer = false; michael@0: if(hook && nullptr != (jsdthreadstate = jsd_NewThreadState(jsdc, cx))) michael@0: { michael@0: hookanswer = hook(jsdc, jsdthreadstate, type, hookData); michael@0: jsd_DestroyThreadState(jsdc, jsdthreadstate); michael@0: } michael@0: michael@0: return hookanswer; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetInterruptHook(JSDContext* jsdc, michael@0: JSD_ExecutionHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->interruptHookData = callerdata; michael@0: jsdc->interruptHook = hook; michael@0: JS_SetInterrupt(jsdc->jsrt, jsd_InterruptHandler, (void*) jsdc); michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearInterruptHook(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK(); michael@0: JS_ClearInterrupt(jsdc->jsrt, nullptr, nullptr); michael@0: jsdc->interruptHook = nullptr; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetDebugBreakHook(JSDContext* jsdc, michael@0: JSD_ExecutionHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->debugBreakHookData = callerdata; michael@0: jsdc->debugBreakHook = hook; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearDebugBreakHook(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->debugBreakHook = nullptr; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetDebuggerHook(JSDContext* jsdc, michael@0: JSD_ExecutionHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->debuggerHookData = callerdata; michael@0: jsdc->debuggerHook = hook; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearDebuggerHook(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->debuggerHook = nullptr; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetThrowHook(JSDContext* jsdc, michael@0: JSD_ExecutionHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->throwHookData = callerdata; michael@0: jsdc->throwHook = hook; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearThrowHook(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->throwHook = nullptr; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetFunctionHook(JSDContext* jsdc, michael@0: JSD_CallHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->functionHookData = callerdata; michael@0: jsdc->functionHook = hook; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearFunctionHook(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->functionHook = nullptr; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_SetTopLevelHook(JSDContext* jsdc, michael@0: JSD_CallHookProc hook, michael@0: void* callerdata) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->toplevelHookData = callerdata; michael@0: jsdc->toplevelHook = hook; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jsd_ClearTopLevelHook(JSDContext* jsdc) michael@0: { michael@0: JSD_LOCK(); michael@0: jsdc->toplevelHook = nullptr; michael@0: JSD_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: