js/jsd/jsd_high.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * JavaScript Debugging support - 'High Level' functions
michael@0 9 */
michael@0 10
michael@0 11 #include "jsd.h"
michael@0 12 #include "nsCxPusher.h"
michael@0 13
michael@0 14 using mozilla::AutoSafeJSContext;
michael@0 15
michael@0 16 /***************************************************************************/
michael@0 17
michael@0 18 /* XXX not 'static' because of old Mac CodeWarrior bug */
michael@0 19 JSCList _jsd_context_list = JS_INIT_STATIC_CLIST(&_jsd_context_list);
michael@0 20
michael@0 21 /* these are used to connect JSD_SetUserCallbacks() with JSD_DebuggerOn() */
michael@0 22 static JSD_UserCallbacks _callbacks;
michael@0 23 static void* _user = nullptr;
michael@0 24 static JSRuntime* _jsrt = nullptr;
michael@0 25
michael@0 26 #ifdef JSD_HAS_DANGEROUS_THREAD
michael@0 27 static void* _dangerousThread = nullptr;
michael@0 28 #endif
michael@0 29
michael@0 30 #ifdef JSD_THREADSAFE
michael@0 31 JSDStaticLock* _jsd_global_lock = nullptr;
michael@0 32 #endif
michael@0 33
michael@0 34 #ifdef DEBUG
michael@0 35 void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc)
michael@0 36 {
michael@0 37 MOZ_ASSERT(jsdc->inited);
michael@0 38 MOZ_ASSERT(jsdc->jsrt);
michael@0 39 MOZ_ASSERT(jsdc->glob);
michael@0 40 }
michael@0 41 #endif
michael@0 42
michael@0 43 /***************************************************************************/
michael@0 44 /* xpconnect related utility functions implemented in jsd_xpc.cpp */
michael@0 45
michael@0 46 extern void
michael@0 47 global_finalize(JSFreeOp* fop, JSObject* obj);
michael@0 48
michael@0 49 extern JSObject*
michael@0 50 CreateJSDGlobal(JSContext *cx, const JSClass *clasp);
michael@0 51
michael@0 52 /***************************************************************************/
michael@0 53
michael@0 54
michael@0 55 static const JSClass global_class = {
michael@0 56 "JSDGlobal", JSCLASS_GLOBAL_FLAGS |
michael@0 57 JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
michael@0 58 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
michael@0 59 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, global_finalize,
michael@0 60 nullptr, nullptr, nullptr,
michael@0 61 JS_GlobalObjectTraceHook
michael@0 62 };
michael@0 63
michael@0 64 static bool
michael@0 65 _validateUserCallbacks(JSD_UserCallbacks* callbacks)
michael@0 66 {
michael@0 67 return !callbacks ||
michael@0 68 (callbacks->size && callbacks->size <= sizeof(JSD_UserCallbacks));
michael@0 69 }
michael@0 70
michael@0 71 static JSDContext*
michael@0 72 _newJSDContext(JSRuntime* jsrt,
michael@0 73 JSD_UserCallbacks* callbacks,
michael@0 74 void* user,
michael@0 75 JSObject* scopeobj)
michael@0 76 {
michael@0 77 JSDContext* jsdc = nullptr;
michael@0 78 bool ok = true;
michael@0 79 AutoSafeJSContext cx;
michael@0 80
michael@0 81 if( ! jsrt )
michael@0 82 return nullptr;
michael@0 83
michael@0 84 if( ! _validateUserCallbacks(callbacks) )
michael@0 85 return nullptr;
michael@0 86
michael@0 87 jsdc = (JSDContext*) calloc(1, sizeof(JSDContext));
michael@0 88 if( ! jsdc )
michael@0 89 goto label_newJSDContext_failure;
michael@0 90
michael@0 91 if( ! JSD_INIT_LOCKS(jsdc) )
michael@0 92 goto label_newJSDContext_failure;
michael@0 93
michael@0 94 JS_INIT_CLIST(&jsdc->links);
michael@0 95
michael@0 96 jsdc->jsrt = jsrt;
michael@0 97
michael@0 98 if( callbacks )
michael@0 99 memcpy(&jsdc->userCallbacks, callbacks, callbacks->size);
michael@0 100
michael@0 101 jsdc->user = user;
michael@0 102
michael@0 103 #ifdef JSD_HAS_DANGEROUS_THREAD
michael@0 104 jsdc->dangerousThread = _dangerousThread;
michael@0 105 #endif
michael@0 106
michael@0 107 JS_INIT_CLIST(&jsdc->threadsStates);
michael@0 108 JS_INIT_CLIST(&jsdc->sources);
michael@0 109 JS_INIT_CLIST(&jsdc->removedSources);
michael@0 110
michael@0 111 jsdc->sourceAlterCount = 1;
michael@0 112
michael@0 113 if( ! jsd_CreateAtomTable(jsdc) )
michael@0 114 goto label_newJSDContext_failure;
michael@0 115
michael@0 116 if( ! jsd_InitObjectManager(jsdc) )
michael@0 117 goto label_newJSDContext_failure;
michael@0 118
michael@0 119 if( ! jsd_InitScriptManager(jsdc) )
michael@0 120 goto label_newJSDContext_failure;
michael@0 121
michael@0 122 {
michael@0 123 JS::RootedObject global(cx, CreateJSDGlobal(cx, &global_class));
michael@0 124 if( ! global )
michael@0 125 goto label_newJSDContext_failure;
michael@0 126
michael@0 127 jsdc->glob = global;
michael@0 128
michael@0 129 JSAutoCompartment ac(cx, jsdc->glob);
michael@0 130 ok = JS::AddNamedObjectRoot(cx, &jsdc->glob, "JSD context global") &&
michael@0 131 JS_InitStandardClasses(cx, global);
michael@0 132 }
michael@0 133 if( ! ok )
michael@0 134 goto label_newJSDContext_failure;
michael@0 135
michael@0 136 jsdc->data = nullptr;
michael@0 137 jsdc->inited = true;
michael@0 138
michael@0 139 JSD_LOCK();
michael@0 140 JS_INSERT_LINK(&jsdc->links, &_jsd_context_list);
michael@0 141 JSD_UNLOCK();
michael@0 142
michael@0 143 return jsdc;
michael@0 144
michael@0 145 label_newJSDContext_failure:
michael@0 146 if( jsdc ) {
michael@0 147 if ( jsdc->glob )
michael@0 148 JS::RemoveObjectRootRT(JS_GetRuntime(cx), &jsdc->glob);
michael@0 149 jsd_DestroyObjectManager(jsdc);
michael@0 150 jsd_DestroyAtomTable(jsdc);
michael@0 151 free(jsdc);
michael@0 152 }
michael@0 153 return nullptr;
michael@0 154 }
michael@0 155
michael@0 156 static void
michael@0 157 _destroyJSDContext(JSDContext* jsdc)
michael@0 158 {
michael@0 159 JSD_ASSERT_VALID_CONTEXT(jsdc);
michael@0 160
michael@0 161 JSD_LOCK();
michael@0 162 JS_REMOVE_LINK(&jsdc->links);
michael@0 163 JSD_UNLOCK();
michael@0 164
michael@0 165 if ( jsdc->glob ) {
michael@0 166 JS::RemoveObjectRootRT(jsdc->jsrt, &jsdc->glob);
michael@0 167 }
michael@0 168 jsd_DestroyObjectManager(jsdc);
michael@0 169 jsd_DestroyAtomTable(jsdc);
michael@0 170
michael@0 171 jsdc->inited = false;
michael@0 172
michael@0 173 /*
michael@0 174 * We should free jsdc here, but we let it leak in case there are any
michael@0 175 * asynchronous hooks calling into the system using it as a handle
michael@0 176 *
michael@0 177 * XXX we also leak the locks
michael@0 178 */
michael@0 179 }
michael@0 180
michael@0 181 /***************************************************************************/
michael@0 182
michael@0 183 JSDContext*
michael@0 184 jsd_DebuggerOnForUser(JSRuntime* jsrt,
michael@0 185 JSD_UserCallbacks* callbacks,
michael@0 186 void* user,
michael@0 187 JSObject* scopeobj)
michael@0 188 {
michael@0 189 JSDContext* jsdc;
michael@0 190
michael@0 191 jsdc = _newJSDContext(jsrt, callbacks, user, scopeobj);
michael@0 192 if( ! jsdc )
michael@0 193 return nullptr;
michael@0 194
michael@0 195 /*
michael@0 196 * Set hooks here. The new/destroy script hooks are on even when
michael@0 197 * the debugger is paused. The destroy hook so we'll clean up
michael@0 198 * internal data structures when scripts are destroyed, and the
michael@0 199 * newscript hook for backwards compatibility for now. We'd like
michael@0 200 * to stop doing that.
michael@0 201 */
michael@0 202 JS_SetNewScriptHookProc(jsdc->jsrt, jsd_NewScriptHookProc, jsdc);
michael@0 203 JS_SetDestroyScriptHookProc(jsdc->jsrt, jsd_DestroyScriptHookProc, jsdc);
michael@0 204 jsd_DebuggerUnpause(jsdc);
michael@0 205
michael@0 206 if( jsdc->userCallbacks.setContext )
michael@0 207 jsdc->userCallbacks.setContext(jsdc, jsdc->user);
michael@0 208 return jsdc;
michael@0 209 }
michael@0 210
michael@0 211 JSDContext*
michael@0 212 jsd_DebuggerOn(void)
michael@0 213 {
michael@0 214 MOZ_ASSERT(_jsrt);
michael@0 215 MOZ_ASSERT(_validateUserCallbacks(&_callbacks));
michael@0 216 return jsd_DebuggerOnForUser(_jsrt, &_callbacks, _user, nullptr);
michael@0 217 }
michael@0 218
michael@0 219 void
michael@0 220 jsd_DebuggerOff(JSDContext* jsdc)
michael@0 221 {
michael@0 222 jsd_DebuggerPause(jsdc, true);
michael@0 223 /* clear hooks here */
michael@0 224 JS_SetNewScriptHookProc(jsdc->jsrt, nullptr, nullptr);
michael@0 225 JS_SetDestroyScriptHookProc(jsdc->jsrt, nullptr, nullptr);
michael@0 226
michael@0 227 /* clean up */
michael@0 228 JSD_LockScriptSubsystem(jsdc);
michael@0 229 jsd_DestroyScriptManager(jsdc);
michael@0 230 JSD_UnlockScriptSubsystem(jsdc);
michael@0 231 jsd_DestroyAllSources(jsdc);
michael@0 232
michael@0 233 _destroyJSDContext(jsdc);
michael@0 234
michael@0 235 if( jsdc->userCallbacks.setContext )
michael@0 236 jsdc->userCallbacks.setContext(nullptr, jsdc->user);
michael@0 237 }
michael@0 238
michael@0 239 void
michael@0 240 jsd_DebuggerPause(JSDContext* jsdc, bool forceAllHooksOff)
michael@0 241 {
michael@0 242 JS_SetDebuggerHandler(jsdc->jsrt, nullptr, nullptr);
michael@0 243 if (forceAllHooksOff || !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) {
michael@0 244 JS_SetExecuteHook(jsdc->jsrt, nullptr, nullptr);
michael@0 245 JS_SetCallHook(jsdc->jsrt, nullptr, nullptr);
michael@0 246 }
michael@0 247 JS_SetThrowHook(jsdc->jsrt, nullptr, nullptr);
michael@0 248 JS_SetDebugErrorHook(jsdc->jsrt, nullptr, nullptr);
michael@0 249 }
michael@0 250
michael@0 251 static bool
michael@0 252 jsd_DebugErrorHook(JSContext *cx, const char *message,
michael@0 253 JSErrorReport *report, void *closure);
michael@0 254
michael@0 255 void
michael@0 256 jsd_DebuggerUnpause(JSDContext* jsdc)
michael@0 257 {
michael@0 258 JS_SetDebuggerHandler(jsdc->jsrt, jsd_DebuggerHandler, jsdc);
michael@0 259 JS_SetExecuteHook(jsdc->jsrt, jsd_TopLevelCallHook, jsdc);
michael@0 260 JS_SetCallHook(jsdc->jsrt, jsd_FunctionCallHook, jsdc);
michael@0 261 JS_SetThrowHook(jsdc->jsrt, jsd_ThrowHandler, jsdc);
michael@0 262 JS_SetDebugErrorHook(jsdc->jsrt, jsd_DebugErrorHook, jsdc);
michael@0 263 }
michael@0 264
michael@0 265 void
michael@0 266 jsd_SetUserCallbacks(JSRuntime* jsrt, JSD_UserCallbacks* callbacks, void* user)
michael@0 267 {
michael@0 268 _jsrt = jsrt;
michael@0 269 _user = user;
michael@0 270
michael@0 271 #ifdef JSD_HAS_DANGEROUS_THREAD
michael@0 272 _dangerousThread = JSD_CURRENT_THREAD();
michael@0 273 #endif
michael@0 274
michael@0 275 if( callbacks )
michael@0 276 memcpy(&_callbacks, callbacks, sizeof(JSD_UserCallbacks));
michael@0 277 else
michael@0 278 memset(&_callbacks, 0 , sizeof(JSD_UserCallbacks));
michael@0 279 }
michael@0 280
michael@0 281 void*
michael@0 282 jsd_SetContextPrivate(JSDContext* jsdc, void *data)
michael@0 283 {
michael@0 284 jsdc->data = data;
michael@0 285 return data;
michael@0 286 }
michael@0 287
michael@0 288 void*
michael@0 289 jsd_GetContextPrivate(JSDContext* jsdc)
michael@0 290 {
michael@0 291 return jsdc->data;
michael@0 292 }
michael@0 293
michael@0 294 void
michael@0 295 jsd_ClearAllProfileData(JSDContext* jsdc)
michael@0 296 {
michael@0 297 JSDScript *current;
michael@0 298
michael@0 299 JSD_LOCK_SCRIPTS(jsdc);
michael@0 300 current = (JSDScript *)jsdc->scripts.next;
michael@0 301 while (current != (JSDScript *)&jsdc->scripts)
michael@0 302 {
michael@0 303 jsd_ClearScriptProfileData(jsdc, current);
michael@0 304 current = (JSDScript *)current->links.next;
michael@0 305 }
michael@0 306
michael@0 307 JSD_UNLOCK_SCRIPTS(jsdc);
michael@0 308 }
michael@0 309
michael@0 310 JSDContext*
michael@0 311 jsd_JSDContextForJSContext(JSContext* context)
michael@0 312 {
michael@0 313 JSDContext* iter;
michael@0 314 JSDContext* jsdc = nullptr;
michael@0 315 JSRuntime* runtime = JS_GetRuntime(context);
michael@0 316
michael@0 317 JSD_LOCK();
michael@0 318 for( iter = (JSDContext*)_jsd_context_list.next;
michael@0 319 iter != (JSDContext*)&_jsd_context_list;
michael@0 320 iter = (JSDContext*)iter->links.next )
michael@0 321 {
michael@0 322 if( runtime == iter->jsrt )
michael@0 323 {
michael@0 324 jsdc = iter;
michael@0 325 break;
michael@0 326 }
michael@0 327 }
michael@0 328 JSD_UNLOCK();
michael@0 329 return jsdc;
michael@0 330 }
michael@0 331
michael@0 332 static bool
michael@0 333 jsd_DebugErrorHook(JSContext *cx, const char *message,
michael@0 334 JSErrorReport *report, void *closure)
michael@0 335 {
michael@0 336 JSDContext* jsdc = (JSDContext*) closure;
michael@0 337 JSD_ErrorReporter errorReporter;
michael@0 338 void* errorReporterData;
michael@0 339
michael@0 340 if( ! jsdc )
michael@0 341 {
michael@0 342 MOZ_ASSERT(0);
michael@0 343 return true;
michael@0 344 }
michael@0 345 if( JSD_IS_DANGEROUS_THREAD(jsdc) )
michael@0 346 return true;
michael@0 347
michael@0 348 /* local in case hook gets cleared on another thread */
michael@0 349 JSD_LOCK();
michael@0 350 errorReporter = jsdc->errorReporter;
michael@0 351 errorReporterData = jsdc->errorReporterData;
michael@0 352 JSD_UNLOCK();
michael@0 353
michael@0 354 if(!errorReporter)
michael@0 355 return true;
michael@0 356
michael@0 357 switch(errorReporter(jsdc, cx, message, report, errorReporterData))
michael@0 358 {
michael@0 359 case JSD_ERROR_REPORTER_PASS_ALONG:
michael@0 360 return true;
michael@0 361 case JSD_ERROR_REPORTER_RETURN:
michael@0 362 return false;
michael@0 363 case JSD_ERROR_REPORTER_DEBUG:
michael@0 364 {
michael@0 365 JS::RootedValue rval(cx);
michael@0 366 JSD_ExecutionHookProc hook;
michael@0 367 void* hookData;
michael@0 368
michael@0 369 /* local in case hook gets cleared on another thread */
michael@0 370 JSD_LOCK();
michael@0 371 hook = jsdc->debugBreakHook;
michael@0 372 hookData = jsdc->debugBreakHookData;
michael@0 373 JSD_UNLOCK();
michael@0 374
michael@0 375 jsd_CallExecutionHook(jsdc, cx, JSD_HOOK_DEBUG_REQUESTED,
michael@0 376 hook, hookData, rval.address());
michael@0 377 /* XXX Should make this dependent on ExecutionHook retval */
michael@0 378 return true;
michael@0 379 }
michael@0 380 case JSD_ERROR_REPORTER_CLEAR_RETURN:
michael@0 381 if(report && JSREPORT_IS_EXCEPTION(report->flags))
michael@0 382 JS_ClearPendingException(cx);
michael@0 383 return false;
michael@0 384 default:
michael@0 385 MOZ_ASSERT(0);
michael@0 386 break;
michael@0 387 }
michael@0 388 return true;
michael@0 389 }
michael@0 390
michael@0 391 bool
michael@0 392 jsd_SetErrorReporter(JSDContext* jsdc,
michael@0 393 JSD_ErrorReporter reporter,
michael@0 394 void* callerdata)
michael@0 395 {
michael@0 396 JSD_LOCK();
michael@0 397 jsdc->errorReporter = reporter;
michael@0 398 jsdc->errorReporterData = callerdata;
michael@0 399 JSD_UNLOCK();
michael@0 400 return true;
michael@0 401 }
michael@0 402
michael@0 403 bool
michael@0 404 jsd_GetErrorReporter(JSDContext* jsdc,
michael@0 405 JSD_ErrorReporter* reporter,
michael@0 406 void** callerdata)
michael@0 407 {
michael@0 408 JSD_LOCK();
michael@0 409 if( reporter )
michael@0 410 *reporter = jsdc->errorReporter;
michael@0 411 if( callerdata )
michael@0 412 *callerdata = jsdc->errorReporterData;
michael@0 413 JSD_UNLOCK();
michael@0 414 return true;
michael@0 415 }

mercurial