1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/jsd/jsd_high.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,415 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JavaScript Debugging support - 'High Level' functions 1.12 + */ 1.13 + 1.14 +#include "jsd.h" 1.15 +#include "nsCxPusher.h" 1.16 + 1.17 +using mozilla::AutoSafeJSContext; 1.18 + 1.19 +/***************************************************************************/ 1.20 + 1.21 +/* XXX not 'static' because of old Mac CodeWarrior bug */ 1.22 +JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list); 1.23 + 1.24 +/* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */ 1.25 +static JSD_UserCallbacks _callbacks; 1.26 +static void* _user = nullptr; 1.27 +static JSRuntime* _jsrt = nullptr; 1.28 + 1.29 +#ifdef JSD_HAS_DANGEROUS_THREAD 1.30 +static void* _dangerousThread = nullptr; 1.31 +#endif 1.32 + 1.33 +#ifdef JSD_THREADSAFE 1.34 +JSDStaticLock* _jsd_global_lock = nullptr; 1.35 +#endif 1.36 + 1.37 +#ifdef DEBUG 1.38 +void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc) 1.39 +{ 1.40 + MOZ_ASSERT(jsdc->inited); 1.41 + MOZ_ASSERT(jsdc->jsrt); 1.42 + MOZ_ASSERT(jsdc->glob); 1.43 +} 1.44 +#endif 1.45 + 1.46 +/***************************************************************************/ 1.47 +/* xpconnect related utility functions implemented in jsd_xpc.cpp */ 1.48 + 1.49 +extern void 1.50 +global_finalize(JSFreeOp* fop, JSObject* obj); 1.51 + 1.52 +extern JSObject* 1.53 +CreateJSDGlobal(JSContext *cx, const JSClass *clasp); 1.54 + 1.55 +/***************************************************************************/ 1.56 + 1.57 + 1.58 +static const JSClass global_class = { 1.59 + "JSDGlobal", JSCLASS_GLOBAL_FLAGS | 1.60 + JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS, 1.61 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.62 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, global_finalize, 1.63 + nullptr, nullptr, nullptr, 1.64 + JS_GlobalObjectTraceHook 1.65 +}; 1.66 + 1.67 +static bool 1.68 +_validateUserCallbacks(JSD_UserCallbacks* callbacks) 1.69 +{ 1.70 + return !callbacks || 1.71 + (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks)); 1.72 +} 1.73 + 1.74 +static JSDContext* 1.75 +_newJSDContext(JSRuntime* jsrt, 1.76 + JSD_UserCallbacks* callbacks, 1.77 + void* user, 1.78 + JSObject* scopeobj) 1.79 +{ 1.80 + JSDContext* jsdc = nullptr; 1.81 + bool ok = true; 1.82 + AutoSafeJSContext cx; 1.83 + 1.84 + if( ! jsrt ) 1.85 + return nullptr; 1.86 + 1.87 + if( ! _validateUserCallbacks(callbacks) ) 1.88 + return nullptr; 1.89 + 1.90 + jsdc = (JSDContext*) calloc(1, sizeof(JSDContext)); 1.91 + if( ! jsdc ) 1.92 + goto label_newJSDContext_failure; 1.93 + 1.94 + if( ! JSD_INIT_LOCKS(jsdc) ) 1.95 + goto label_newJSDContext_failure; 1.96 + 1.97 + JS_INIT_CLIST(&jsdc->links); 1.98 + 1.99 + jsdc->jsrt = jsrt; 1.100 + 1.101 + if( callbacks ) 1.102 + memcpy(&jsdc->userCallbacks, callbacks, callbacks->size); 1.103 + 1.104 + jsdc->user = user; 1.105 + 1.106 +#ifdef JSD_HAS_DANGEROUS_THREAD 1.107 + jsdc->dangerousThread = _dangerousThread; 1.108 +#endif 1.109 + 1.110 + JS_INIT_CLIST(&jsdc->threadsStates); 1.111 + JS_INIT_CLIST(&jsdc->sources); 1.112 + JS_INIT_CLIST(&jsdc->removedSources); 1.113 + 1.114 + jsdc->sourceAlterCount = 1; 1.115 + 1.116 + if( ! jsd_CreateAtomTable(jsdc) ) 1.117 + goto label_newJSDContext_failure; 1.118 + 1.119 + if( ! jsd_InitObjectManager(jsdc) ) 1.120 + goto label_newJSDContext_failure; 1.121 + 1.122 + if( ! jsd_InitScriptManager(jsdc) ) 1.123 + goto label_newJSDContext_failure; 1.124 + 1.125 + { 1.126 + JS::RootedObject global(cx, CreateJSDGlobal(cx, &global_class)); 1.127 + if( ! global ) 1.128 + goto label_newJSDContext_failure; 1.129 + 1.130 + jsdc->glob = global; 1.131 + 1.132 + JSAutoCompartment ac(cx, jsdc->glob); 1.133 + ok = JS::AddNamedObjectRoot(cx, &jsdc->glob, "JSD context global") && 1.134 + JS_InitStandardClasses(cx, global); 1.135 + } 1.136 + if( ! ok ) 1.137 + goto label_newJSDContext_failure; 1.138 + 1.139 + jsdc->data = nullptr; 1.140 + jsdc->inited = true; 1.141 + 1.142 + JSD_LOCK(); 1.143 + JS_INSERT_LINK(&jsdc->links, &_jsd_context_list); 1.144 + JSD_UNLOCK(); 1.145 + 1.146 + return jsdc; 1.147 + 1.148 +label_newJSDContext_failure: 1.149 + if( jsdc ) { 1.150 + if ( jsdc->glob ) 1.151 + JS::RemoveObjectRootRT(JS_GetRuntime(cx), &jsdc->glob); 1.152 + jsd_DestroyObjectManager(jsdc); 1.153 + jsd_DestroyAtomTable(jsdc); 1.154 + free(jsdc); 1.155 + } 1.156 + return nullptr; 1.157 +} 1.158 + 1.159 +static void 1.160 +_destroyJSDContext(JSDContext* jsdc) 1.161 +{ 1.162 + JSD_ASSERT_VALID_CONTEXT(jsdc); 1.163 + 1.164 + JSD_LOCK(); 1.165 + JS_REMOVE_LINK(&jsdc->links); 1.166 + JSD_UNLOCK(); 1.167 + 1.168 + if ( jsdc->glob ) { 1.169 + JS::RemoveObjectRootRT(jsdc->jsrt, &jsdc->glob); 1.170 + } 1.171 + jsd_DestroyObjectManager(jsdc); 1.172 + jsd_DestroyAtomTable(jsdc); 1.173 + 1.174 + jsdc->inited = false; 1.175 + 1.176 + /* 1.177 + * We should free jsdc here, but we let it leak in case there are any 1.178 + * asynchronous hooks calling into the system using it as a handle 1.179 + * 1.180 + * XXX we also leak the locks 1.181 + */ 1.182 +} 1.183 + 1.184 +/***************************************************************************/ 1.185 + 1.186 +JSDContext* 1.187 +jsd_DebuggerOnForUser(JSRuntime* jsrt, 1.188 + JSD_UserCallbacks* callbacks, 1.189 + void* user, 1.190 + JSObject* scopeobj) 1.191 +{ 1.192 + JSDContext* jsdc; 1.193 + 1.194 + jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj); 1.195 + if( ! jsdc ) 1.196 + return nullptr; 1.197 + 1.198 + /* 1.199 + * Set hooks here. The new/destroy script hooks are on even when 1.200 + * the debugger is paused. The destroy hook so we'll clean up 1.201 + * internal data structures when scripts are destroyed, and the 1.202 + * newscript hook for backwards compatibility for now. We'd like 1.203 + * to stop doing that. 1.204 + */ 1.205 + JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc); 1.206 + JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc); 1.207 + jsd_DebuggerUnpause(jsdc); 1.208 + 1.209 + if( jsdc->userCallbacks.setContext ) 1.210 + jsdc->userCallbacks.setContext(jsdc, jsdc->user); 1.211 + return jsdc; 1.212 +} 1.213 + 1.214 +JSDContext* 1.215 +jsd_DebuggerOn(void) 1.216 +{ 1.217 + MOZ_ASSERT(_jsrt); 1.218 + MOZ_ASSERT(_validateUserCallbacks(&_callbacks)); 1.219 + return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, nullptr); 1.220 +} 1.221 + 1.222 +void 1.223 +jsd_DebuggerOff(JSDContext* jsdc) 1.224 +{ 1.225 + jsd_DebuggerPause(jsdc, true); 1.226 + /* clear hooks here */ 1.227 + JS_SetNewScriptHookProc(jsdc->jsrt, nullptr, nullptr); 1.228 + JS_SetDestroyScriptHookProc(jsdc->jsrt, nullptr, nullptr); 1.229 + 1.230 + /* clean up */ 1.231 + JSD_LockScriptSubsystem(jsdc); 1.232 + jsd_DestroyScriptManager(jsdc); 1.233 + JSD_UnlockScriptSubsystem(jsdc); 1.234 + jsd_DestroyAllSources(jsdc); 1.235 + 1.236 + _destroyJSDContext(jsdc); 1.237 + 1.238 + if( jsdc->userCallbacks.setContext ) 1.239 + jsdc->userCallbacks.setContext(nullptr, jsdc->user); 1.240 +} 1.241 + 1.242 +void 1.243 +jsd_DebuggerPause(JSDContext* jsdc, bool forceAllHooksOff) 1.244 +{ 1.245 + JS_SetDebuggerHandler(jsdc->jsrt, nullptr, nullptr); 1.246 + if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) { 1.247 + JS_SetExecuteHook(jsdc->jsrt, nullptr, nullptr); 1.248 + JS_SetCallHook(jsdc->jsrt, nullptr, nullptr); 1.249 + } 1.250 + JS_SetThrowHook(jsdc->jsrt, nullptr, nullptr); 1.251 + JS_SetDebugErrorHook(jsdc->jsrt, nullptr, nullptr); 1.252 +} 1.253 + 1.254 +static bool 1.255 +jsd_DebugErrorHook(JSContext *cx, const char *message, 1.256 + JSErrorReport *report, void *closure); 1.257 + 1.258 +void 1.259 +jsd_DebuggerUnpause(JSDContext* jsdc) 1.260 +{ 1.261 + JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc); 1.262 + JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc); 1.263 + JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc); 1.264 + JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc); 1.265 + JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc); 1.266 +} 1.267 + 1.268 +void 1.269 +jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user) 1.270 +{ 1.271 + _jsrt = jsrt; 1.272 + _user = user; 1.273 + 1.274 +#ifdef JSD_HAS_DANGEROUS_THREAD 1.275 + _dangerousThread = JSD_CURRENT_THREAD(); 1.276 +#endif 1.277 + 1.278 + if( callbacks ) 1.279 + memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks)); 1.280 + else 1.281 + memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks)); 1.282 +} 1.283 + 1.284 +void* 1.285 +jsd_SetContextPrivate(JSDContext* jsdc, void *data) 1.286 +{ 1.287 + jsdc->data = data; 1.288 + return data; 1.289 +} 1.290 + 1.291 +void* 1.292 +jsd_GetContextPrivate(JSDContext* jsdc) 1.293 +{ 1.294 + return jsdc->data; 1.295 +} 1.296 + 1.297 +void 1.298 +jsd_ClearAllProfileData(JSDContext* jsdc) 1.299 +{ 1.300 + JSDScript *current; 1.301 + 1.302 + JSD_LOCK_SCRIPTS(jsdc); 1.303 + current = (JSDScript *)jsdc->scripts.next; 1.304 + while (current != (JSDScript *)&jsdc->scripts) 1.305 + { 1.306 + jsd_ClearScriptProfileData(jsdc, current); 1.307 + current = (JSDScript *)current->links.next; 1.308 + } 1.309 + 1.310 + JSD_UNLOCK_SCRIPTS(jsdc); 1.311 +} 1.312 + 1.313 +JSDContext* 1.314 +jsd_JSDContextForJSContext(JSContext* context) 1.315 +{ 1.316 + JSDContext* iter; 1.317 + JSDContext* jsdc = nullptr; 1.318 + JSRuntime* runtime = JS_GetRuntime(context); 1.319 + 1.320 + JSD_LOCK(); 1.321 + for( iter = (JSDContext*)_jsd_context_list.next; 1.322 + iter != (JSDContext*)&_jsd_context_list; 1.323 + iter = (JSDContext*)iter->links.next ) 1.324 + { 1.325 + if( runtime == iter->jsrt ) 1.326 + { 1.327 + jsdc = iter; 1.328 + break; 1.329 + } 1.330 + } 1.331 + JSD_UNLOCK(); 1.332 + return jsdc; 1.333 +} 1.334 + 1.335 +static bool 1.336 +jsd_DebugErrorHook(JSContext *cx, const char *message, 1.337 + JSErrorReport *report, void *closure) 1.338 +{ 1.339 + JSDContext* jsdc = (JSDContext*) closure; 1.340 + JSD_ErrorReporter errorReporter; 1.341 + void* errorReporterData; 1.342 + 1.343 + if( ! jsdc ) 1.344 + { 1.345 + MOZ_ASSERT(0); 1.346 + return true; 1.347 + } 1.348 + if( JSD_IS_DANGEROUS_THREAD(jsdc) ) 1.349 + return true; 1.350 + 1.351 + /* local in case hook gets cleared on another thread */ 1.352 + JSD_LOCK(); 1.353 + errorReporter = jsdc->errorReporter; 1.354 + errorReporterData = jsdc->errorReporterData; 1.355 + JSD_UNLOCK(); 1.356 + 1.357 + if(!errorReporter) 1.358 + return true; 1.359 + 1.360 + switch(errorReporter(jsdc, cx, message, report, errorReporterData)) 1.361 + { 1.362 + case JSD_ERROR_REPORTER_PASS_ALONG: 1.363 + return true; 1.364 + case JSD_ERROR_REPORTER_RETURN: 1.365 + return false; 1.366 + case JSD_ERROR_REPORTER_DEBUG: 1.367 + { 1.368 + JS::RootedValue rval(cx); 1.369 + JSD_ExecutionHookProc hook; 1.370 + void* hookData; 1.371 + 1.372 + /* local in case hook gets cleared on another thread */ 1.373 + JSD_LOCK(); 1.374 + hook = jsdc->debugBreakHook; 1.375 + hookData = jsdc->debugBreakHookData; 1.376 + JSD_UNLOCK(); 1.377 + 1.378 + jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED, 1.379 + hook, hookData, rval.address()); 1.380 + /* XXX Should make this dependent on ExecutionHook retval */ 1.381 + return true; 1.382 + } 1.383 + case JSD_ERROR_REPORTER_CLEAR_RETURN: 1.384 + if(report && JSREPORT_IS_EXCEPTION(report->flags)) 1.385 + JS_ClearPendingException(cx); 1.386 + return false; 1.387 + default: 1.388 + MOZ_ASSERT(0); 1.389 + break; 1.390 + } 1.391 + return true; 1.392 +} 1.393 + 1.394 +bool 1.395 +jsd_SetErrorReporter(JSDContext* jsdc, 1.396 + JSD_ErrorReporter reporter, 1.397 + void* callerdata) 1.398 +{ 1.399 + JSD_LOCK(); 1.400 + jsdc->errorReporter = reporter; 1.401 + jsdc->errorReporterData = callerdata; 1.402 + JSD_UNLOCK(); 1.403 + return true; 1.404 +} 1.405 + 1.406 +bool 1.407 +jsd_GetErrorReporter(JSDContext* jsdc, 1.408 + JSD_ErrorReporter* reporter, 1.409 + void** callerdata) 1.410 +{ 1.411 + JSD_LOCK(); 1.412 + if( reporter ) 1.413 + *reporter = jsdc->errorReporter; 1.414 + if( callerdata ) 1.415 + *callerdata = jsdc->errorReporterData; 1.416 + JSD_UNLOCK(); 1.417 + return true; 1.418 +}