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: #include "jsfriendapi.h" michael@0: #include "jsd_xpc.h" michael@0: #include "xpcpublic.h" michael@0: michael@0: #include "js/GCAPI.h" michael@0: #include "js/OldDebugAPI.h" michael@0: michael@0: #include "nsIXPConnect.h" michael@0: #include "mozilla/ModuleUtils.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "nsIJSRuntimeService.h" michael@0: #include "nsIThreadInternal.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsTArray.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsMemory.h" michael@0: #include "jsdebug.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsCRT.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: /* XXX DOM dependency */ michael@0: #include "nsIScriptContext.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsDOMJSUtils.h" michael@0: #include "SandboxPrivate.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "nsContentUtils.h" michael@0: #include "mozilla/dom/ScriptSettings.h" michael@0: michael@0: using mozilla::AutoSafeJSContext; michael@0: using mozilla::AutoPushJSContext; michael@0: using mozilla::dom::AutoNoJSAPI; michael@0: michael@0: /* michael@0: * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the michael@0: * script hook. This was a hack to avoid some js engine problems that should michael@0: * be fixed now (see Mozilla bug 77636). michael@0: */ michael@0: #undef CAUTIOUS_SCRIPTHOOK michael@0: michael@0: #ifdef DEBUG_verbose michael@0: # define DEBUG_COUNT(name, count) \ michael@0: { if ((count % 10) == 0) printf (name ": %i\n", count); } michael@0: # define DEBUG_CREATE(name, count) {count++; DEBUG_COUNT ("+++++ " name,count)} michael@0: # define DEBUG_DESTROY(name, count) {count--; DEBUG_COUNT ("----- " name,count)} michael@0: #else michael@0: # define DEBUG_CREATE(name, count) michael@0: # define DEBUG_DESTROY(name, count) michael@0: #endif michael@0: michael@0: #define ASSERT_VALID_CONTEXT { if (!mCx) return NS_ERROR_NOT_AVAILABLE; } michael@0: #define ASSERT_VALID_EPHEMERAL { if (!mValid) return NS_ERROR_NOT_AVAILABLE; } michael@0: michael@0: #define JSDSERVICE_CID \ michael@0: { /* f1299dc2-1dd1-11b2-a347-ee6b7660e048 */ \ michael@0: 0xf1299dc2, \ michael@0: 0x1dd1, \ michael@0: 0x11b2, \ michael@0: {0xa3, 0x47, 0xee, 0x6b, 0x76, 0x60, 0xe0, 0x48} \ michael@0: } michael@0: michael@0: #define JSDASO_CID \ michael@0: { /* 2fd6b7f6-eb8c-4f32-ad26-113f2c02d0fe */ \ michael@0: 0x2fd6b7f6, \ michael@0: 0xeb8c, \ michael@0: 0x4f32, \ michael@0: {0xad, 0x26, 0x11, 0x3f, 0x2c, 0x02, 0xd0, 0xfe} \ michael@0: } michael@0: michael@0: #define JSDS_MAJOR_VERSION 1 michael@0: #define JSDS_MINOR_VERSION 2 michael@0: michael@0: #define NS_CATMAN_CTRID "@mozilla.org/categorymanager;1" michael@0: #define NS_JSRT_CTRID "@mozilla.org/js/xpc/RuntimeService;1" michael@0: michael@0: #define AUTOREG_CATEGORY "xpcom-autoregistration" michael@0: #define APPSTART_CATEGORY "app-startup" michael@0: #define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer" michael@0: #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer" michael@0: michael@0: static void michael@0: jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc); michael@0: michael@0: /******************************************************************************* michael@0: * global vars michael@0: ******************************************************************************/ michael@0: michael@0: const char implementationString[] = "Mozilla JavaScript Debugger Service"; michael@0: michael@0: const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1"; michael@0: const char jsdARObserverCtrID[] = "@mozilla.org/js/jsd/app-start-observer;2"; michael@0: const char jsdASObserverCtrID[] = "service,@mozilla.org/js/jsd/app-start-observer;2"; michael@0: michael@0: #ifdef DEBUG_verbose michael@0: uint32_t gScriptCount = 0; michael@0: uint32_t gValueCount = 0; michael@0: uint32_t gPropertyCount = 0; michael@0: uint32_t gContextCount = 0; michael@0: uint32_t gFrameCount = 0; michael@0: #endif michael@0: michael@0: static jsdService *gJsds = 0; michael@0: static JS::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc; michael@0: static bool gGCRunning = false; michael@0: michael@0: static struct DeadScript { michael@0: PRCList links; michael@0: JSDContext *jsdc; michael@0: jsdIScript *script; michael@0: } *gDeadScripts = nullptr; michael@0: michael@0: enum PatternType { michael@0: ptIgnore = 0U, michael@0: ptStartsWith = 1U, michael@0: ptEndsWith = 2U, michael@0: ptContains = 3U, michael@0: ptEquals = 4U michael@0: }; michael@0: michael@0: static struct FilterRecord { michael@0: PRCList links; michael@0: jsdIFilter *filterObject; michael@0: nsCString urlPattern; michael@0: PatternType patternType; michael@0: uint32_t startLine; michael@0: uint32_t endLine; michael@0: } *gFilters = nullptr; michael@0: michael@0: static struct LiveEphemeral *gLiveValues = nullptr; michael@0: static struct LiveEphemeral *gLiveProperties = nullptr; michael@0: static struct LiveEphemeral *gLiveContexts = nullptr; michael@0: static struct LiveEphemeral *gLiveStackFrames = nullptr; michael@0: michael@0: /******************************************************************************* michael@0: * utility functions for ephemeral lists michael@0: *******************************************************************************/ michael@0: already_AddRefed michael@0: jsds_FindEphemeral (LiveEphemeral **listHead, void *key) michael@0: { michael@0: if (!*listHead) michael@0: return nullptr; michael@0: michael@0: LiveEphemeral *lv_record = michael@0: reinterpret_cast michael@0: (PR_NEXT_LINK(&(*listHead)->links)); michael@0: do michael@0: { michael@0: if (lv_record->key == key) michael@0: { michael@0: nsCOMPtr ret = lv_record->value; michael@0: return ret.forget(); michael@0: } michael@0: lv_record = reinterpret_cast michael@0: (PR_NEXT_LINK(&lv_record->links)); michael@0: } michael@0: while (lv_record != *listHead); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: jsds_InvalidateAllEphemerals (LiveEphemeral **listHead) michael@0: { michael@0: LiveEphemeral *lv_record = michael@0: reinterpret_cast michael@0: (PR_NEXT_LINK(&(*listHead)->links)); michael@0: do michael@0: { michael@0: LiveEphemeral *next = michael@0: reinterpret_cast michael@0: (PR_NEXT_LINK(&lv_record->links)); michael@0: lv_record->value->Invalidate(); michael@0: lv_record = next; michael@0: } michael@0: while (*listHead); michael@0: } michael@0: michael@0: void michael@0: jsds_InsertEphemeral (LiveEphemeral **listHead, LiveEphemeral *item) michael@0: { michael@0: if (*listHead) { michael@0: /* if the list exists, add to it */ michael@0: PR_APPEND_LINK(&item->links, &(*listHead)->links); michael@0: } else { michael@0: /* otherwise create the list */ michael@0: PR_INIT_CLIST(&item->links); michael@0: *listHead = item; michael@0: } michael@0: } michael@0: michael@0: void michael@0: jsds_RemoveEphemeral (LiveEphemeral **listHead, LiveEphemeral *item) michael@0: { michael@0: LiveEphemeral *next = reinterpret_cast michael@0: (PR_NEXT_LINK(&item->links)); michael@0: michael@0: if (next == item) michael@0: { michael@0: /* if the current item is also the next item, we're the only element, michael@0: * null out the list head */ michael@0: NS_ASSERTION (*listHead == item, michael@0: "How could we not be the head of a one item list?"); michael@0: *listHead = nullptr; michael@0: } michael@0: else if (item == *listHead) michael@0: { michael@0: /* otherwise, if we're currently the list head, change it */ michael@0: *listHead = next; michael@0: } michael@0: michael@0: PR_REMOVE_AND_INIT_LINK(&item->links); michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: * utility functions for filters michael@0: *******************************************************************************/ michael@0: void michael@0: jsds_FreeFilter (FilterRecord *rec) michael@0: { michael@0: NS_IF_RELEASE (rec->filterObject); michael@0: PR_Free (rec); michael@0: } michael@0: michael@0: /* copies appropriate |filter| attributes into |rec|. michael@0: * False return indicates failure, the contents of |rec| will not be changed. michael@0: */ michael@0: bool michael@0: jsds_SyncFilter (FilterRecord *rec, jsdIFilter *filter) michael@0: { michael@0: NS_ASSERTION (rec, "jsds_SyncFilter without rec"); michael@0: NS_ASSERTION (filter, "jsds_SyncFilter without filter"); michael@0: michael@0: uint32_t startLine; michael@0: nsresult rv = filter->GetStartLine(&startLine); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: uint32_t endLine; michael@0: rv = filter->GetStartLine(&endLine); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoCString urlPattern; michael@0: rv = filter->GetUrlPattern (urlPattern); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: uint32_t len = urlPattern.Length(); michael@0: if (len) { michael@0: if (urlPattern[0] == '*') { michael@0: /* pattern starts with a *, shift all chars once to the left, michael@0: * including the trailing null. */ michael@0: urlPattern = Substring(urlPattern, 1, len); michael@0: michael@0: if (urlPattern[len - 2] == '*') { michael@0: /* pattern is in the format "*foo*", overwrite the final * with michael@0: * a null. */ michael@0: urlPattern.Truncate(len - 2); michael@0: rec->patternType = ptContains; michael@0: } else { michael@0: /* pattern is in the format "*foo", just make a note of the michael@0: * new length. */ michael@0: rec->patternType = ptEndsWith; michael@0: } michael@0: } else if (urlPattern[len - 1] == '*') { michael@0: /* pattern is in the format "foo*", overwrite the final * with a michael@0: * null. */ michael@0: urlPattern.Truncate(len - 1); michael@0: rec->patternType = ptStartsWith; michael@0: } else { michael@0: /* pattern is in the format "foo". */ michael@0: rec->patternType = ptEquals; michael@0: } michael@0: } else { michael@0: rec->patternType = ptIgnore; michael@0: } michael@0: michael@0: /* we got everything we need without failing, now copy it into rec. */ michael@0: michael@0: if (rec->filterObject != filter) { michael@0: NS_IF_RELEASE(rec->filterObject); michael@0: NS_ADDREF(filter); michael@0: rec->filterObject = filter; michael@0: } michael@0: michael@0: rec->startLine = startLine; michael@0: rec->endLine = endLine; michael@0: michael@0: rec->urlPattern = urlPattern; michael@0: michael@0: return true; michael@0: michael@0: } michael@0: michael@0: FilterRecord * michael@0: jsds_FindFilter (jsdIFilter *filter) michael@0: { michael@0: if (!gFilters) michael@0: return nullptr; michael@0: michael@0: FilterRecord *current = gFilters; michael@0: michael@0: do { michael@0: if (current->filterObject == filter) michael@0: return current; michael@0: current = reinterpret_cast michael@0: (PR_NEXT_LINK(¤t->links)); michael@0: } while (current != gFilters); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* returns true if the hook should be executed. */ michael@0: bool michael@0: jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state) michael@0: { michael@0: JSDStackFrameInfo *frame = JSD_GetStackFrame (jsdc, state); michael@0: michael@0: if (!frame) { michael@0: NS_WARNING("No frame in threadstate"); michael@0: return false; michael@0: } michael@0: michael@0: JSDScript *script = JSD_GetScriptForStackFrame (jsdc, state, frame); michael@0: if (!script) michael@0: return true; michael@0: michael@0: uintptr_t pc = JSD_GetPCForStackFrame (jsdc, state, frame); michael@0: michael@0: nsCString url(JSD_GetScriptFilename (jsdc, script)); michael@0: if (url.IsEmpty()) { michael@0: NS_WARNING ("Script with no filename"); michael@0: return false; michael@0: } michael@0: michael@0: if (!gFilters) michael@0: return true; michael@0: michael@0: uint32_t currentLine = JSD_GetClosestLine (jsdc, script, pc); michael@0: uint32_t len = 0; michael@0: FilterRecord *currentFilter = gFilters; michael@0: do { michael@0: uint32_t flags = 0; michael@0: michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: currentFilter->filterObject->GetFlags(&flags); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Error getting flags for filter"); michael@0: michael@0: if (flags & jsdIFilter::FLAG_ENABLED) { michael@0: /* If there is no start line, or the start line is before michael@0: * or equal to the current */ michael@0: if ((!currentFilter->startLine || michael@0: currentFilter->startLine <= currentLine) && michael@0: /* and there is no end line, or the end line is after michael@0: * or equal to the current */ michael@0: (!currentFilter->endLine || michael@0: currentFilter->endLine >= currentLine)) { michael@0: /* then we're going to have to compare the url. */ michael@0: if (currentFilter->patternType == ptIgnore) michael@0: return !!(flags & jsdIFilter::FLAG_PASS); michael@0: michael@0: if (!len) michael@0: len = url.Length(); michael@0: nsCString urlPattern = currentFilter->urlPattern; michael@0: uint32_t patternLength = urlPattern.Length(); michael@0: if (len >= patternLength) { michael@0: switch (currentFilter->patternType) { michael@0: case ptEquals: michael@0: if (urlPattern.Equals(url)) michael@0: return !!(flags & jsdIFilter::FLAG_PASS); michael@0: break; michael@0: case ptStartsWith: michael@0: if (urlPattern.Equals(Substring(url, 0, patternLength))) michael@0: return !!(flags & jsdIFilter::FLAG_PASS); michael@0: break; michael@0: case ptEndsWith: michael@0: if (urlPattern.Equals(Substring(url, len - patternLength))) michael@0: return !!(flags & jsdIFilter::FLAG_PASS); michael@0: break; michael@0: case ptContains: michael@0: { michael@0: nsACString::const_iterator start, end; michael@0: url.BeginReading(start); michael@0: url.EndReading(end); michael@0: if (FindInReadable(currentFilter->urlPattern, start, end)) michael@0: return !!(flags & jsdIFilter::FLAG_PASS); michael@0: } michael@0: break; michael@0: default: michael@0: NS_ERROR("Invalid pattern type"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: currentFilter = reinterpret_cast michael@0: (PR_NEXT_LINK(¤tFilter->links)); michael@0: } while (currentFilter != gFilters); michael@0: michael@0: return true; michael@0: michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: * c callbacks michael@0: *******************************************************************************/ michael@0: michael@0: static void michael@0: jsds_NotifyPendingDeadScripts (JSRuntime *rt) michael@0: { michael@0: jsdService *jsds = gJsds; michael@0: michael@0: nsCOMPtr hook; michael@0: if (jsds) { michael@0: NS_ADDREF(jsds); michael@0: jsds->GetScriptHook (getter_AddRefs(hook)); michael@0: jsds->DoPause(nullptr, true); michael@0: } michael@0: michael@0: DeadScript *deadScripts = gDeadScripts; michael@0: gDeadScripts = nullptr; michael@0: while (deadScripts) { michael@0: DeadScript *ds = deadScripts; michael@0: /* get next deleted script */ michael@0: deadScripts = reinterpret_cast michael@0: (PR_NEXT_LINK(&ds->links)); michael@0: if (deadScripts == ds) michael@0: deadScripts = nullptr; michael@0: michael@0: if (hook) michael@0: { michael@0: /* tell the user this script has been destroyed */ michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JS_UNKEEP_ATOMS(rt); michael@0: #endif michael@0: hook->OnScriptDestroyed (ds->script); michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JS_KEEP_ATOMS(rt); michael@0: #endif michael@0: } michael@0: michael@0: /* take it out of the circular list */ michael@0: PR_REMOVE_LINK(&ds->links); michael@0: michael@0: /* addref came from the FromPtr call in jsds_ScriptHookProc */ michael@0: NS_RELEASE(ds->script); michael@0: /* free the struct! */ michael@0: PR_Free(ds); michael@0: } michael@0: michael@0: if (jsds) { michael@0: jsds->DoUnPause(nullptr, true); michael@0: NS_RELEASE(jsds); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: jsds_GCSliceCallbackProc (JSRuntime *rt, JS::GCProgress progress, const JS::GCDescription &desc) michael@0: { michael@0: if (progress == JS::GC_CYCLE_END || progress == JS::GC_SLICE_END) { michael@0: NS_ASSERTION(gGCRunning, "GC slice callback was missed"); michael@0: michael@0: while (gDeadScripts) michael@0: jsds_NotifyPendingDeadScripts (rt); michael@0: michael@0: gGCRunning = false; michael@0: } else { michael@0: NS_ASSERTION(!gGCRunning, "should not re-enter GC"); michael@0: gGCRunning = true; michael@0: } michael@0: michael@0: if (gPrevGCSliceCallback) michael@0: (*gPrevGCSliceCallback)(rt, progress, desc); michael@0: } michael@0: michael@0: static unsigned michael@0: jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message, michael@0: JSErrorReport *report, void *callerdata) michael@0: { michael@0: static bool running = false; michael@0: michael@0: nsCOMPtr hook; michael@0: gJsds->GetErrorHook(getter_AddRefs(hook)); michael@0: if (!hook) michael@0: return JSD_ERROR_REPORTER_PASS_ALONG; michael@0: michael@0: if (running) michael@0: return JSD_ERROR_REPORTER_PASS_ALONG; michael@0: michael@0: running = true; michael@0: michael@0: nsCOMPtr val; michael@0: if (JS_IsExceptionPending(cx)) { michael@0: JS::RootedValue jv(cx); michael@0: JS_GetPendingException(cx, &jv); michael@0: JSDValue *jsdv = JSD_NewValue (jsdc, jv); michael@0: val = dont_AddRef(jsdValue::FromPtr(jsdc, jsdv)); michael@0: } michael@0: michael@0: nsAutoCString fileName; michael@0: uint32_t line; michael@0: uint32_t pos; michael@0: uint32_t flags; michael@0: uint32_t errnum; michael@0: bool rval; michael@0: if (report) { michael@0: fileName.Assign(report->filename); michael@0: line = report->lineno; michael@0: pos = report->tokenptr - report->linebuf; michael@0: flags = report->flags; michael@0: errnum = report->errorNumber; michael@0: } michael@0: else michael@0: { michael@0: line = 0; michael@0: pos = 0; michael@0: flags = 0; michael@0: errnum = 0; michael@0: } michael@0: michael@0: gJsds->DoPause(nullptr, true); michael@0: hook->OnError (nsDependentCString(message), fileName, line, pos, flags, errnum, val, &rval); michael@0: gJsds->DoUnPause(nullptr, true); michael@0: michael@0: running = false; michael@0: if (!rval) michael@0: return JSD_ERROR_REPORTER_DEBUG; michael@0: michael@0: return JSD_ERROR_REPORTER_PASS_ALONG; michael@0: } michael@0: michael@0: static bool michael@0: jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, michael@0: unsigned type, void* callerdata) michael@0: { michael@0: nsCOMPtr hook; michael@0: michael@0: switch (type) michael@0: { michael@0: case JSD_HOOK_TOPLEVEL_START: michael@0: case JSD_HOOK_TOPLEVEL_END: michael@0: gJsds->GetTopLevelHook(getter_AddRefs(hook)); michael@0: break; michael@0: michael@0: case JSD_HOOK_FUNCTION_CALL: michael@0: case JSD_HOOK_FUNCTION_RETURN: michael@0: gJsds->GetFunctionHook(getter_AddRefs(hook)); michael@0: break; michael@0: michael@0: default: michael@0: NS_ASSERTION (0, "Unknown hook type."); michael@0: } michael@0: michael@0: if (!hook) michael@0: return true; michael@0: michael@0: if (!jsds_FilterHook (jsdc, jsdthreadstate)) michael@0: return false; michael@0: michael@0: JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); michael@0: nsCOMPtr frame = michael@0: dont_AddRef(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame)); michael@0: gJsds->DoPause(nullptr, true); michael@0: hook->OnCall(frame, type); michael@0: gJsds->DoUnPause(nullptr, true); michael@0: jsdStackFrame::InvalidateAll(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static uint32_t michael@0: jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate, michael@0: unsigned type, void* callerdata, jsval* rval) michael@0: { michael@0: nsCOMPtr hook(0); michael@0: uint32_t hook_rv = JSD_HOOK_RETURN_CONTINUE; michael@0: nsCOMPtr js_rv; michael@0: michael@0: switch (type) michael@0: { michael@0: case JSD_HOOK_INTERRUPTED: michael@0: gJsds->GetInterruptHook(getter_AddRefs(hook)); michael@0: break; michael@0: case JSD_HOOK_DEBUG_REQUESTED: michael@0: gJsds->GetDebugHook(getter_AddRefs(hook)); michael@0: break; michael@0: case JSD_HOOK_DEBUGGER_KEYWORD: michael@0: gJsds->GetDebuggerHook(getter_AddRefs(hook)); michael@0: break; michael@0: case JSD_HOOK_BREAKPOINT: michael@0: { michael@0: /* we can't pause breakpoints the way we pause the other michael@0: * execution hooks (at least, not easily.) Instead we bail michael@0: * here if the service is paused. */ michael@0: uint32_t level; michael@0: gJsds->GetPauseDepth(&level); michael@0: if (!level) michael@0: gJsds->GetBreakpointHook(getter_AddRefs(hook)); michael@0: } michael@0: break; michael@0: case JSD_HOOK_THROW: michael@0: { michael@0: hook_rv = JSD_HOOK_RETURN_CONTINUE_THROW; michael@0: gJsds->GetThrowHook(getter_AddRefs(hook)); michael@0: if (hook) { michael@0: JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate); michael@0: js_rv = dont_AddRef(jsdValue::FromPtr (jsdc, jsdv)); michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: NS_ASSERTION (0, "Unknown hook type."); michael@0: } michael@0: michael@0: if (!hook) michael@0: return hook_rv; michael@0: michael@0: if (!jsds_FilterHook (jsdc, jsdthreadstate)) michael@0: return JSD_HOOK_RETURN_CONTINUE; michael@0: michael@0: JSDStackFrameInfo *native_frame = JSD_GetStackFrame (jsdc, jsdthreadstate); michael@0: nsCOMPtr frame = michael@0: dont_AddRef(jsdStackFrame::FromPtr(jsdc, jsdthreadstate, native_frame)); michael@0: gJsds->DoPause(nullptr, true); michael@0: jsdIValue *inout_rv = js_rv; michael@0: NS_IF_ADDREF(inout_rv); michael@0: hook->OnExecute (frame, type, &inout_rv, &hook_rv); michael@0: js_rv = inout_rv; michael@0: NS_IF_RELEASE(inout_rv); michael@0: gJsds->DoUnPause(nullptr, true); michael@0: jsdStackFrame::InvalidateAll(); michael@0: michael@0: if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL || michael@0: hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) { michael@0: *rval = JSVAL_VOID; michael@0: if (js_rv) { michael@0: JSDValue *jsdv; michael@0: if (NS_SUCCEEDED(js_rv->GetJSDValue (&jsdv))) michael@0: *rval = JSD_GetValueWrappedJSVal(jsdc, jsdv); michael@0: } michael@0: } michael@0: michael@0: return hook_rv; michael@0: } michael@0: michael@0: static void michael@0: jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, bool creating, michael@0: void* callerdata) michael@0: { michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JSRuntime *rt = JS_GetRuntime(nsContentUtils::GetSafeJSContext()); michael@0: #endif michael@0: michael@0: if (creating) { michael@0: nsCOMPtr hook; michael@0: gJsds->GetScriptHook(getter_AddRefs(hook)); michael@0: michael@0: /* a script is being created */ michael@0: if (!hook) { michael@0: /* nobody cares, just exit */ michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr script = michael@0: dont_AddRef(jsdScript::FromPtr(jsdc, jsdscript)); michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JS_UNKEEP_ATOMS(rt); michael@0: #endif michael@0: gJsds->DoPause(nullptr, true); michael@0: hook->OnScriptCreated (script); michael@0: gJsds->DoUnPause(nullptr, true); michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JS_KEEP_ATOMS(rt); michael@0: #endif michael@0: } else { michael@0: /* a script is being destroyed. even if there is no registered hook michael@0: * we'll still need to invalidate the jsdIScript record, in order michael@0: * to remove the reference held in the JSDScript private data. */ michael@0: nsCOMPtr jsdis = michael@0: static_cast(JSD_GetScriptPrivate(jsdscript)); michael@0: if (!jsdis) michael@0: return; michael@0: michael@0: jsdis->Invalidate(); michael@0: michael@0: if (!gGCRunning) { michael@0: nsCOMPtr hook; michael@0: gJsds->GetScriptHook(getter_AddRefs(hook)); michael@0: if (!hook) michael@0: return; michael@0: michael@0: /* if GC *isn't* running, we can tell the user about the script michael@0: * delete now. */ michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JS_UNKEEP_ATOMS(rt); michael@0: #endif michael@0: michael@0: gJsds->DoPause(nullptr, true); michael@0: hook->OnScriptDestroyed (jsdis); michael@0: gJsds->DoUnPause(nullptr, true); michael@0: #ifdef CAUTIOUS_SCRIPTHOOK michael@0: JS_KEEP_ATOMS(rt); michael@0: #endif michael@0: } else { michael@0: /* if a GC *is* running, we've got to wait until it's done before michael@0: * we can execute any JS, so we queue the notification in a PRCList michael@0: * until GC tells us it's done. See jsds_GCCallbackProc(). */ michael@0: DeadScript *ds = PR_NEW(DeadScript); michael@0: if (!ds) michael@0: return; /* NS_ERROR_OUT_OF_MEMORY */ michael@0: michael@0: ds->jsdc = jsdc; michael@0: ds->script = jsdis; michael@0: NS_ADDREF(ds->script); michael@0: if (gDeadScripts) michael@0: /* if the queue exists, add to it */ michael@0: PR_APPEND_LINK(&ds->links, &gDeadScripts->links); michael@0: else { michael@0: /* otherwise create the queue */ michael@0: PR_INIT_CLIST(&ds->links); michael@0: gDeadScripts = ds; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /******************************************************************************* michael@0: * reflected jsd data structures michael@0: *******************************************************************************/ michael@0: michael@0: /* Contexts */ michael@0: /* michael@0: NS_IMPL_ISUPPORTS(jsdContext, jsdIContext, jsdIEphemeral); michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: */ michael@0: michael@0: /* Objects */ michael@0: NS_IMPL_ISUPPORTS(jsdObject, jsdIObject) michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetJSDObject(JSDObject **_rval) michael@0: { michael@0: *_rval = mObject; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetCreatorURL(nsACString &_rval) michael@0: { michael@0: _rval.Assign(JSD_GetObjectNewURL(mCx, mObject)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetCreatorLine(uint32_t *_rval) michael@0: { michael@0: *_rval = JSD_GetObjectNewLineNumber(mCx, mObject); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetConstructorURL(nsACString &_rval) michael@0: { michael@0: _rval.Assign(JSD_GetObjectConstructorURL(mCx, mObject)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetConstructorLine(uint32_t *_rval) michael@0: { michael@0: *_rval = JSD_GetObjectConstructorLineNumber(mCx, mObject); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdObject::GetValue(jsdIValue **_rval) michael@0: { michael@0: JSDValue *jsdv = JSD_GetValueForObject (mCx, mObject); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Properties */ michael@0: NS_IMPL_ISUPPORTS(jsdProperty, jsdIProperty, jsdIEphemeral) michael@0: michael@0: jsdProperty::jsdProperty (JSDContext *aCx, JSDProperty *aProperty) : michael@0: mCx(aCx), mProperty(aProperty) michael@0: { michael@0: DEBUG_CREATE ("jsdProperty", gPropertyCount); michael@0: mValid = (aCx && aProperty); michael@0: mLiveListEntry.value = this; michael@0: jsds_InsertEphemeral (&gLiveProperties, &mLiveListEntry); michael@0: } michael@0: michael@0: jsdProperty::~jsdProperty () michael@0: { michael@0: DEBUG_DESTROY ("jsdProperty", gPropertyCount); michael@0: if (mValid) michael@0: Invalidate(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::Invalidate() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: mValid = false; michael@0: jsds_RemoveEphemeral (&gLiveProperties, &mLiveListEntry); michael@0: JSD_DropProperty (mCx, mProperty); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: jsdProperty::InvalidateAll() michael@0: { michael@0: if (gLiveProperties) michael@0: jsds_InvalidateAllEphemerals (&gLiveProperties); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetJSDProperty(JSDProperty **_rval) michael@0: { michael@0: *_rval = mProperty; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetIsValid(bool *_rval) michael@0: { michael@0: *_rval = mValid; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetAlias(jsdIValue **_rval) michael@0: { michael@0: JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetFlags(uint32_t *_rval) michael@0: { michael@0: *_rval = JSD_GetPropertyFlags (mCx, mProperty); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetName(jsdIValue **_rval) michael@0: { michael@0: JSDValue *jsdv = JSD_GetPropertyName (mCx, mProperty); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdProperty::GetValue(jsdIValue **_rval) michael@0: { michael@0: JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Scripts */ michael@0: NS_IMPL_ISUPPORTS(jsdScript, jsdIScript, jsdIEphemeral) michael@0: michael@0: static NS_IMETHODIMP michael@0: AssignToJSString(JSDContext *aCx, nsACString *x, JSString *str_) michael@0: { michael@0: if (!str_) { michael@0: x->SetLength(0); michael@0: return NS_OK; michael@0: } michael@0: JS::RootedString str(JSD_GetJSRuntime(aCx), str_); michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, JSD_GetDefaultGlobal(aCx)); // Just in case. michael@0: size_t length = JS_GetStringEncodingLength(cx, str); michael@0: if (length == size_t(-1)) michael@0: return NS_ERROR_FAILURE; michael@0: x->SetLength(uint32_t(length)); michael@0: if (x->Length() != uint32_t(length)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: JS_EncodeStringToBuffer(cx, str, x->BeginWriting(), length); michael@0: return NS_OK; michael@0: } michael@0: michael@0: jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(false), michael@0: mTag(0), michael@0: mCx(aCx), michael@0: mScript(aScript), michael@0: mFileName(0), michael@0: mFunctionName(0), michael@0: mBaseLineNumber(0), michael@0: mLineExtent(0), michael@0: mPPLineMap(0), michael@0: mFirstPC(0) michael@0: { michael@0: DEBUG_CREATE ("jsdScript", gScriptCount); michael@0: michael@0: if (mScript) { michael@0: /* copy the script's information now, so we have it later, when it michael@0: * gets destroyed. */ michael@0: JSD_LockScriptSubsystem(mCx); michael@0: mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript)); michael@0: mFunctionName = new nsCString(); michael@0: if (mFunctionName) { michael@0: JSString *str = JSD_GetScriptFunctionId(mCx, mScript); michael@0: if (str) michael@0: AssignToJSString(mCx, mFunctionName, str); michael@0: } michael@0: mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript); michael@0: mLineExtent = JSD_GetScriptLineExtent(mCx, mScript); michael@0: mFirstPC = JSD_GetClosestPC(mCx, mScript, 0); michael@0: JSD_UnlockScriptSubsystem(mCx); michael@0: michael@0: mValid = true; michael@0: } michael@0: } michael@0: michael@0: jsdScript::~jsdScript () michael@0: { michael@0: DEBUG_DESTROY ("jsdScript", gScriptCount); michael@0: delete mFileName; michael@0: delete mFunctionName; michael@0: michael@0: if (mPPLineMap) michael@0: PR_Free(mPPLineMap); michael@0: michael@0: /* Invalidate() needs to be called to release an owning reference to michael@0: * ourselves, so if we got here without being invalidated, something michael@0: * has gone wrong with our ref count. */ michael@0: NS_ASSERTION (!mValid, "Script destroyed without being invalidated."); michael@0: } michael@0: michael@0: /* michael@0: * This method populates a line <-> pc map for a pretty printed version of this michael@0: * script. It does this by decompiling, and then recompiling the script. The michael@0: * resulting script is scanned for the line map, and then left as GC fodder. michael@0: */ michael@0: PCMapEntry * michael@0: jsdScript::CreatePPLineMap() michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, JSD_GetDefaultGlobal (mCx)); // Just in case. michael@0: JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!obj) michael@0: return nullptr; michael@0: JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript)); michael@0: JS::RootedScript script(cx); /* In JSD compartment */ michael@0: uint32_t baseLine; michael@0: JS::RootedString jsstr(cx); michael@0: size_t length; michael@0: const jschar *chars; michael@0: michael@0: if (fun) { michael@0: unsigned nargs; michael@0: michael@0: { michael@0: JSAutoCompartment ac(cx, JS_GetFunctionObject(fun)); michael@0: nargs = JS_GetFunctionArgumentCount(cx, fun); michael@0: if (nargs > 12) michael@0: return nullptr; michael@0: jsstr = JS_DecompileFunctionBody (cx, fun, 4); michael@0: if (!jsstr) michael@0: return nullptr; michael@0: michael@0: if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length))) michael@0: return nullptr; michael@0: } michael@0: michael@0: JS::Anchor kungFuDeathGrip(jsstr); michael@0: static const char *const argnames[] = { michael@0: "arg1", "arg2", "arg3", "arg4", michael@0: "arg5", "arg6", "arg7", "arg8", michael@0: "arg9", "arg10", "arg11", "arg12" michael@0: }; michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("x-jsd:ppbuffer?type=function", 3); michael@0: fun = JS_CompileUCFunction (cx, obj, "ppfun", nargs, argnames, chars, michael@0: length, options); michael@0: if (!fun || !(script = JS_GetFunctionScript(cx, fun))) michael@0: return nullptr; michael@0: baseLine = 3; michael@0: } else { michael@0: script = JSD_GetJSScript(mCx, mScript); michael@0: JSString *jsstr; michael@0: michael@0: { michael@0: JSAutoCompartment ac(cx, script); michael@0: michael@0: jsstr = JS_DecompileScript (cx, script, "ppscript", 4); michael@0: if (!jsstr) michael@0: return nullptr; michael@0: michael@0: if (!(chars = JS_GetStringCharsAndLength(cx, jsstr, &length))) michael@0: return nullptr; michael@0: } michael@0: michael@0: JS::Anchor kungFuDeathGrip(jsstr); michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("x-jsd:ppbuffer?type=script", 1); michael@0: script = JS_CompileUCScript(cx, obj, chars, length, options); michael@0: if (!script) michael@0: return nullptr; michael@0: baseLine = 1; michael@0: } michael@0: michael@0: uint32_t scriptExtent = JS_GetScriptLineExtent (cx, script); michael@0: jsbytecode* firstPC = JS_LineNumberToPC (cx, script, 0); michael@0: /* allocate worst case size of map (number of lines in script + 1 michael@0: * for our 0 record), we'll shrink it with a realloc later. */ michael@0: PCMapEntry *lineMap = michael@0: static_cast michael@0: (PR_Malloc((scriptExtent + 1) * sizeof (PCMapEntry))); michael@0: uint32_t lineMapSize = 0; michael@0: michael@0: if (lineMap) { michael@0: for (uint32_t line = baseLine; line < scriptExtent + baseLine; ++line) { michael@0: jsbytecode* pc = JS_LineNumberToPC (cx, script, line); michael@0: if (line == JS_PCToLineNumber (cx, script, pc)) { michael@0: lineMap[lineMapSize].line = line; michael@0: lineMap[lineMapSize].pc = pc - firstPC; michael@0: ++lineMapSize; michael@0: } michael@0: } michael@0: if (scriptExtent != lineMapSize) { michael@0: lineMap = michael@0: static_cast michael@0: (PR_Realloc(mPPLineMap = lineMap, michael@0: lineMapSize * sizeof(PCMapEntry))); michael@0: if (!lineMap) { michael@0: PR_Free(mPPLineMap); michael@0: lineMapSize = 0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mPCMapSize = lineMapSize; michael@0: return mPPLineMap = lineMap; michael@0: } michael@0: michael@0: uint32_t michael@0: jsdScript::PPPcToLine (uint32_t aPC) michael@0: { michael@0: if (!mPPLineMap && !CreatePPLineMap()) michael@0: return 0; michael@0: uint32_t i; michael@0: for (i = 1; i < mPCMapSize; ++i) { michael@0: if (mPPLineMap[i].pc > aPC) michael@0: return mPPLineMap[i - 1].line; michael@0: } michael@0: michael@0: return mPPLineMap[mPCMapSize - 1].line; michael@0: } michael@0: michael@0: uint32_t michael@0: jsdScript::PPLineToPc (uint32_t aLine) michael@0: { michael@0: if (!mPPLineMap && !CreatePPLineMap()) michael@0: return 0; michael@0: uint32_t i; michael@0: for (i = 1; i < mPCMapSize; ++i) { michael@0: if (mPPLineMap[i].line > aLine) michael@0: return mPPLineMap[i - 1].pc; michael@0: } michael@0: michael@0: return mPPLineMap[mPCMapSize - 1].pc; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetJSDScript(JSDScript **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mScript; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetVersion (int32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: AutoSafeJSContext cx; michael@0: JS::RootedScript script(cx, JSD_GetJSScript(mCx, mScript)); michael@0: JSAutoCompartment ac(cx, script); michael@0: *_rval = static_cast(JS_GetScriptVersion(cx, script)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetTag(uint32_t *_rval) michael@0: { michael@0: if (!mTag) michael@0: mTag = ++jsdScript::LastTag; michael@0: michael@0: *_rval = mTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::Invalidate() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: mValid = false; michael@0: michael@0: /* release the addref we do in FromPtr */ michael@0: jsdIScript *script = static_cast michael@0: (JSD_GetScriptPrivate(mScript)); michael@0: NS_ASSERTION (script == this, "That's not my script!"); michael@0: NS_RELEASE(script); michael@0: JSD_SetScriptPrivate(mScript, nullptr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: jsdScript::InvalidateAll () michael@0: { michael@0: JSDContext *cx; michael@0: if (NS_FAILED(gJsds->GetJSDContext (&cx))) michael@0: return; michael@0: michael@0: JSDScript *script; michael@0: JSDScript *iter = nullptr; michael@0: michael@0: JSD_LockScriptSubsystem(cx); michael@0: while((script = JSD_IterateScripts(cx, &iter)) != nullptr) { michael@0: nsCOMPtr jsdis = michael@0: static_cast(JSD_GetScriptPrivate(script)); michael@0: if (jsdis) michael@0: jsdis->Invalidate(); michael@0: } michael@0: JSD_UnlockScriptSubsystem(cx); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetIsValid(bool *_rval) michael@0: { michael@0: *_rval = mValid; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::SetFlags(uint32_t flags) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSD_SetScriptFlags(mCx, mScript, flags); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetFlags(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptFlags(mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetFileName(nsACString &_rval) michael@0: { michael@0: _rval.Assign(*mFileName); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetFunctionName(nsACString &_rval) michael@0: { michael@0: _rval.Assign(*mFunctionName); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetParameterNames(uint32_t* count, char16_t*** paramNames) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: AutoSafeJSContext cx; michael@0: JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript)); michael@0: if (!fun) { michael@0: *count = 0; michael@0: *paramNames = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: JSAutoCompartment ac(cx, JS_GetFunctionObject(fun)); michael@0: michael@0: unsigned nargs; michael@0: if (!JS_FunctionHasLocalNames(cx, fun) || michael@0: (nargs = JS_GetFunctionArgumentCount(cx, fun)) == 0) { michael@0: *count = 0; michael@0: *paramNames = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: char16_t **ret = michael@0: static_cast(NS_Alloc(nargs * sizeof(char16_t*))); michael@0: if (!ret) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: void *mark; michael@0: uintptr_t *names = JS_GetFunctionLocalNameArray(cx, fun, &mark); michael@0: if (!names) { michael@0: NS_Free(ret); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: for (unsigned i = 0; i < nargs; ++i) { michael@0: JSAtom *atom = JS_LocalNameToAtom(names[i]); michael@0: if (!atom) { michael@0: ret[i] = 0; michael@0: } else { michael@0: JSString *str = JS_AtomKey(atom); michael@0: ret[i] = NS_strndup(JS_GetInternedStringChars(str), JS_GetStringLength(str)); michael@0: if (!ret[i]) { michael@0: NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret); michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: JS_ReleaseFunctionLocalNameArray(cx, mark); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: *count = nargs; michael@0: *paramNames = ret; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetFunctionObject(jsdIValue **_rval) michael@0: { michael@0: JS::RootedFunction fun(JSD_GetJSRuntime(mCx), JSD_GetJSFunction(mCx, mScript)); michael@0: if (!fun) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: AutoSafeJSContext jsContext; michael@0: JS::RootedObject obj(jsContext, JS_GetFunctionObject(fun)); michael@0: if (!obj) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: JSDContext *cx; michael@0: if (NS_FAILED(gJsds->GetJSDContext (&cx))) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: JSDValue *jsdv = JSD_NewValue(cx, OBJECT_TO_JSVAL(obj)); michael@0: if (!jsdv) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *_rval = jsdValue::FromPtr(cx, jsdv); michael@0: if (!*_rval) { michael@0: JSD_DropValue(cx, jsdv); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetFunctionSource(nsAString & aFunctionSource) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: AutoSafeJSContext cx_; michael@0: JSContext *cx = cx_; // Appease the type system with Maybe<>s below. michael@0: JS::RootedFunction fun(cx, JSD_GetJSFunction (mCx, mScript)); michael@0: michael@0: JSString *jsstr; michael@0: mozilla::Maybe ac; michael@0: if (fun) { michael@0: ac.construct(cx, JS_GetFunctionObject(fun)); michael@0: jsstr = JS_DecompileFunction (cx, fun, 4); michael@0: } else { michael@0: JS::RootedScript script(cx, JSD_GetJSScript (mCx, mScript)); michael@0: ac.construct(cx, script); michael@0: jsstr = JS_DecompileScript (cx, script, "ppscript", 4); michael@0: } michael@0: if (!jsstr) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: size_t length; michael@0: const jschar *chars = JS_GetStringCharsZAndLength(cx, jsstr, &length); michael@0: if (!chars) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: aFunctionSource = nsDependentString(chars, length); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetBaseLineNumber(uint32_t *_rval) michael@0: { michael@0: *_rval = mBaseLineNumber; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetLineExtent(uint32_t *_rval) michael@0: { michael@0: *_rval = mLineExtent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetCallCount(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptCallCount (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetMaxRecurseDepth(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptMaxRecurseDepth (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetMinExecutionTime(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptMinExecutionTime (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetMaxExecutionTime(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptMaxExecutionTime (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetTotalExecutionTime(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptTotalExecutionTime (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetMinOwnExecutionTime(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptMinOwnExecutionTime (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetMaxOwnExecutionTime(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptMaxOwnExecutionTime (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetTotalOwnExecutionTime(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetScriptTotalOwnExecutionTime (mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::ClearProfileData() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSD_ClearScriptProfileData(mCx, mScript); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::PcToLine(uint32_t aPC, uint32_t aPcmap, uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (aPcmap == PCMAP_SOURCETEXT) { michael@0: *_rval = JSD_GetClosestLine (mCx, mScript, mFirstPC + aPC); michael@0: } else if (aPcmap == PCMAP_PRETTYPRINT) { michael@0: *_rval = PPPcToLine(aPC); michael@0: } else { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::LineToPc(uint32_t aLine, uint32_t aPcmap, uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (aPcmap == PCMAP_SOURCETEXT) { michael@0: uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine); michael@0: *_rval = pc - mFirstPC; michael@0: } else if (aPcmap == PCMAP_PRETTYPRINT) { michael@0: *_rval = PPLineToPc(aLine); michael@0: } else { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::EnableSingleStepInterrupts(bool enable) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: michael@0: /* Must have set interrupt hook before enabling */ michael@0: if (enable && !jsdService::GetService()->CheckInterruptHook()) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: return (JSD_EnableSingleStepInterrupts(mCx, mScript, enable) ? NS_OK : NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::GetExecutableLines(uint32_t aPcmap, uint32_t aStartLine, uint32_t aMaxLines, michael@0: uint32_t* aCount, uint32_t** aExecutableLines) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (aPcmap == PCMAP_SOURCETEXT) { michael@0: uintptr_t start = JSD_GetClosestPC(mCx, mScript, 0); michael@0: unsigned lastLine = JSD_GetScriptBaseLineNumber(mCx, mScript) michael@0: + JSD_GetScriptLineExtent(mCx, mScript) - 1; michael@0: uintptr_t end = JSD_GetClosestPC(mCx, mScript, lastLine + 1); michael@0: michael@0: *aExecutableLines = static_cast(NS_Alloc((end - start + 1) * sizeof(uint32_t))); michael@0: if (!JSD_GetLinePCs(mCx, mScript, aStartLine, aMaxLines, aCount, aExecutableLines, michael@0: nullptr)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aPcmap == PCMAP_PRETTYPRINT) { michael@0: if (!mPPLineMap) { michael@0: if (!CreatePPLineMap()) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsTArray lines; michael@0: uint32_t i; michael@0: michael@0: for (i = 0; i < mPCMapSize; ++i) { michael@0: if (mPPLineMap[i].line >= aStartLine) michael@0: break; michael@0: } michael@0: michael@0: for (; i < mPCMapSize && lines.Length() < aMaxLines; ++i) { michael@0: lines.AppendElement(mPPLineMap[i].line); michael@0: } michael@0: michael@0: if (aCount) michael@0: *aCount = lines.Length(); michael@0: michael@0: *aExecutableLines = static_cast(NS_Alloc(lines.Length() * sizeof(uint32_t))); michael@0: if (!*aExecutableLines) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: for (i = 0; i < lines.Length(); ++i) michael@0: (*aExecutableLines)[i] = lines[i]; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::IsLineExecutable(uint32_t aLine, uint32_t aPcmap, bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (aPcmap == PCMAP_SOURCETEXT) { michael@0: uintptr_t pc = JSD_GetClosestPC (mCx, mScript, aLine); michael@0: *_rval = (aLine == JSD_GetClosestLine (mCx, mScript, pc)); michael@0: } else if (aPcmap == PCMAP_PRETTYPRINT) { michael@0: if (!mPPLineMap && !CreatePPLineMap()) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: *_rval = false; michael@0: for (uint32_t i = 0; i < mPCMapSize; ++i) { michael@0: if (mPPLineMap[i].line >= aLine) { michael@0: *_rval = (mPPLineMap[i].line == aLine); michael@0: break; michael@0: } michael@0: } michael@0: } else { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::SetBreakpoint(uint32_t aPC) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: uintptr_t pc = mFirstPC + aPC; michael@0: JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, nullptr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::ClearBreakpoint(uint32_t aPC) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: uintptr_t pc = mFirstPC + aPC; michael@0: JSD_ClearExecutionHook (mCx, mScript, pc); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdScript::ClearAllBreakpoints() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSD_LockScriptSubsystem(mCx); michael@0: JSD_ClearAllExecutionHooksForScript (mCx, mScript); michael@0: JSD_UnlockScriptSubsystem(mCx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Contexts */ michael@0: NS_IMPL_ISUPPORTS(jsdContext, jsdIContext, jsdIEphemeral) michael@0: michael@0: jsdIContext * michael@0: jsdContext::FromPtr (JSDContext *aJSDCx, JSContext *aJSCx) michael@0: { michael@0: if (!aJSDCx || !aJSCx) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr jsdicx; michael@0: nsCOMPtr eph = michael@0: jsds_FindEphemeral (&gLiveContexts, static_cast(aJSCx)); michael@0: if (eph) michael@0: { michael@0: jsdicx = do_QueryInterface(eph); michael@0: } michael@0: else michael@0: { michael@0: nsCOMPtr iscx; michael@0: if (JS::ContextOptionsRef(aJSCx).privateIsNSISupports()) michael@0: iscx = static_cast(JS_GetContextPrivate(aJSCx)); michael@0: jsdicx = new jsdContext (aJSDCx, aJSCx, iscx); michael@0: } michael@0: michael@0: jsdIContext *ctx = nullptr; michael@0: jsdicx.swap(ctx); michael@0: return ctx; michael@0: } michael@0: michael@0: jsdContext::jsdContext (JSDContext *aJSDCx, JSContext *aJSCx, michael@0: nsISupports *aISCx) : mValid(true), michael@0: mScriptDisabledForWindowWithID(0), michael@0: mTag(0), michael@0: mJSDCx(aJSDCx), michael@0: mJSCx(aJSCx), mISCx(aISCx) michael@0: { michael@0: DEBUG_CREATE ("jsdContext", gContextCount); michael@0: mLiveListEntry.value = this; michael@0: mLiveListEntry.key = static_cast(aJSCx); michael@0: jsds_InsertEphemeral (&gLiveContexts, &mLiveListEntry); michael@0: } michael@0: michael@0: jsdContext::~jsdContext() michael@0: { michael@0: DEBUG_DESTROY ("jsdContext", gContextCount); michael@0: if (mValid) michael@0: { michael@0: /* call Invalidate() to take ourselves out of the live list */ michael@0: Invalidate(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetIsValid(bool *_rval) michael@0: { michael@0: *_rval = mValid; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::Invalidate() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: mValid = false; michael@0: jsds_RemoveEphemeral (&gLiveContexts, &mLiveListEntry); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: jsdContext::InvalidateAll() michael@0: { michael@0: if (gLiveContexts) michael@0: jsds_InvalidateAllEphemerals (&gLiveContexts); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetJSContext(JSContext **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mJSCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Simulate the old options API in terms of the new one for backwards michael@0: * compatibility */ michael@0: michael@0: #define JSOPTION_EXTRA_WARNINGS JS_BIT(0) michael@0: #define JSOPTION_WERROR JS_BIT(1) michael@0: #define JSOPTION_VAROBJFIX JS_BIT(2) michael@0: #define JSOPTION_PRIVATE_IS_NSISUPPORTS JS_BIT(3) michael@0: #define JSOPTION_DONT_REPORT_UNCAUGHT JS_BIT(8) michael@0: #define JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT JS_BIT(11) michael@0: #define JSOPTION_NO_SCRIPT_RVAL JS_BIT(12) michael@0: #define JSOPTION_STRICT_MODE JS_BIT(17) michael@0: #define JSOPTION_MASK JS_BITMASK(20) michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetOptions(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = (JS::ContextOptionsRef(mJSCx).extraWarnings() ? JSOPTION_EXTRA_WARNINGS : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).werror() ? JSOPTION_WERROR : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).varObjFix() ? JSOPTION_VAROBJFIX : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).privateIsNSISupports() ? JSOPTION_PRIVATE_IS_NSISUPPORTS : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).dontReportUncaught() ? JSOPTION_DONT_REPORT_UNCAUGHT : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).noDefaultCompartmentObject() ? JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).noScriptRval() ? JSOPTION_NO_SCRIPT_RVAL : 0) michael@0: | (JS::ContextOptionsRef(mJSCx).strictMode() ? JSOPTION_STRICT_MODE : 0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::SetOptions(uint32_t options) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: michael@0: /* don't let users change this option, they'd just be shooting themselves michael@0: * in the foot. */ michael@0: if (JS::ContextOptionsRef(mJSCx).privateIsNSISupports() != michael@0: (options & JSOPTION_PRIVATE_IS_NSISUPPORTS)) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: JS::ContextOptionsRef(mJSCx).setExtraWarnings(options & JSOPTION_EXTRA_WARNINGS) michael@0: .setWerror(options & JSOPTION_WERROR) michael@0: .setVarObjFix(options & JSOPTION_VAROBJFIX) michael@0: .setDontReportUncaught(options & JSOPTION_DONT_REPORT_UNCAUGHT) michael@0: .setNoDefaultCompartmentObject(options & JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT) michael@0: .setNoScriptRval(options & JSOPTION_NO_SCRIPT_RVAL) michael@0: .setStrictMode(options & JSOPTION_STRICT_MODE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetPrivateData(nsISupports **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (JS::ContextOptionsRef(mJSCx).privateIsNSISupports()) michael@0: { michael@0: *_rval = static_cast(JS_GetContextPrivate(mJSCx)); michael@0: NS_IF_ADDREF(*_rval); michael@0: } michael@0: else michael@0: { michael@0: *_rval = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetWrappedContext(nsISupports **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: NS_IF_ADDREF(*_rval = mISCx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetTag(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (!mTag) michael@0: mTag = ++jsdContext::LastTag; michael@0: michael@0: *_rval = mTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetGlobalObject (jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSObject *glob = GetDefaultScopeFromJSContext(mJSCx); michael@0: JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob)); michael@0: if (!jsdv) michael@0: return NS_ERROR_FAILURE; michael@0: *_rval = jsdValue::FromPtr (mJSDCx, jsdv); michael@0: if (!*_rval) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::GetScriptsEnabled (bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = IsScriptEnabled(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdContext::SetScriptsEnabled (bool _rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (_rval == IsScriptEnabled()) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr scx = do_QueryInterface(mISCx); michael@0: NS_ENSURE_TRUE(scx && scx->GetWindowProxy(), NS_ERROR_NO_INTERFACE); michael@0: nsCOMPtr piWin = do_QueryInterface(scx->GetGlobalObject()); michael@0: NS_ENSURE_TRUE(piWin, NS_ERROR_NO_INTERFACE); michael@0: uint64_t currentWindowID = piWin->WindowID(); michael@0: michael@0: if (_rval) { michael@0: if (mScriptDisabledForWindowWithID != currentWindowID) { michael@0: NS_WARNING("Please stop abusing JSD and fix your code!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: xpc::Scriptability::Get(scx->GetWindowProxy()).Unblock(); michael@0: piWin->ResumeTimeouts(); michael@0: mScriptDisabledForWindowWithID = 0; michael@0: } michael@0: else { michael@0: piWin->SuspendTimeouts(); michael@0: xpc::Scriptability::Get(scx->GetWindowProxy()).Block(); michael@0: mScriptDisabledForWindowWithID = currentWindowID; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Stack Frames */ michael@0: NS_IMPL_ISUPPORTS(jsdStackFrame, jsdIStackFrame, jsdIEphemeral) michael@0: michael@0: jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState, michael@0: JSDStackFrameInfo *aStackFrameInfo) : michael@0: mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo) michael@0: { michael@0: DEBUG_CREATE ("jsdStackFrame", gFrameCount); michael@0: mValid = (aCx && aThreadState && aStackFrameInfo); michael@0: if (mValid) { michael@0: mLiveListEntry.key = aStackFrameInfo; michael@0: mLiveListEntry.value = this; michael@0: jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry); michael@0: } michael@0: } michael@0: michael@0: jsdStackFrame::~jsdStackFrame() michael@0: { michael@0: DEBUG_DESTROY ("jsdStackFrame", gFrameCount); michael@0: if (mValid) michael@0: { michael@0: /* call Invalidate() to take ourselves out of the live list */ michael@0: Invalidate(); michael@0: } michael@0: } michael@0: michael@0: jsdIStackFrame * michael@0: jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState, michael@0: JSDStackFrameInfo *aStackFrameInfo) michael@0: { michael@0: if (!aStackFrameInfo) michael@0: return nullptr; michael@0: michael@0: jsdIStackFrame *rv; michael@0: nsCOMPtr frame; michael@0: michael@0: nsCOMPtr eph = michael@0: jsds_FindEphemeral (&gLiveStackFrames, michael@0: reinterpret_cast(aStackFrameInfo)); michael@0: michael@0: if (eph) michael@0: { michael@0: frame = do_QueryInterface(eph); michael@0: rv = frame; michael@0: } michael@0: else michael@0: { michael@0: rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo); michael@0: } michael@0: michael@0: NS_IF_ADDREF(rv); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::Invalidate() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: mValid = false; michael@0: jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: jsdStackFrame::InvalidateAll() michael@0: { michael@0: if (gLiveStackFrames) michael@0: jsds_InvalidateAllEphemerals (&gLiveStackFrames); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetJSDThreadState(JSDThreadState **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mThreadState; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetJSDStackFrameInfo(JSDStackFrameInfo **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mStackFrameInfo; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetIsValid(bool *_rval) michael@0: { michael@0: *_rval = mValid; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDStackFrameInfo *sfi = JSD_GetCallingStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetExecutionContext(jsdIContext **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSContext *cx = JSD_GetJSContext (mCx, mThreadState); michael@0: *_rval = jsdContext::FromPtr (mCx, cx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetFunctionName(nsACString &_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSString *str = JSD_GetIdForStackFrame(mCx, mThreadState, mStackFrameInfo); michael@0: if (str) michael@0: return AssignToJSString(mCx, &_rval, str); michael@0: michael@0: _rval.Assign("anonymous"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetIsDebugger(bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_IsStackFrameDebugger (mCx, mThreadState, mStackFrameInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetIsConstructing(bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_IsStackFrameConstructing (mCx, mThreadState, mStackFrameInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetScript(jsdIScript **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: *_rval = jsdScript::FromPtr (mCx, script); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetPc(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: if (!script) michael@0: return NS_ERROR_FAILURE; michael@0: uintptr_t pcbase = JSD_GetClosestPC(mCx, script, 0); michael@0: michael@0: uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); michael@0: if (pc) michael@0: *_rval = pc - pcbase; michael@0: else michael@0: *_rval = pcbase; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetLine(uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDScript *script = JSD_GetScriptForStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: if (script) { michael@0: uintptr_t pc = JSD_GetPCForStackFrame (mCx, mThreadState, mStackFrameInfo); michael@0: *_rval = JSD_GetClosestLine (mCx, script, pc); michael@0: } else { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetCallee(jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDValue *jsdv = JSD_GetCallObjectForStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetScope(jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDValue *jsdv = JSD_GetScopeChainForStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::GetThisValue(jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDValue *jsdv = JSD_GetThisForStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: jsdStackFrame::Eval (const nsAString &bytes, const nsACString &fileName, michael@0: uint32_t line, jsdIValue **result, bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: michael@0: if (bytes.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // get pointer to buffer contained in |bytes| michael@0: nsAString::const_iterator h; michael@0: bytes.BeginReading(h); michael@0: const jschar *char_bytes = reinterpret_cast(h.get()); michael@0: michael@0: JSExceptionState *estate = 0; michael@0: michael@0: AutoPushJSContext cx(JSD_GetJSContext (mCx, mThreadState)); michael@0: michael@0: JS::RootedValue jv(cx); michael@0: michael@0: estate = JS_SaveExceptionState (cx); michael@0: JS_ClearPendingException (cx); michael@0: michael@0: *_rval = JSD_AttemptUCScriptInStackFrame (mCx, mThreadState, michael@0: mStackFrameInfo, michael@0: char_bytes, bytes.Length(), michael@0: PromiseFlatCString(fileName).get(), michael@0: line, &jv); michael@0: if (!*_rval) { michael@0: if (JS_IsExceptionPending(cx)) michael@0: JS_GetPendingException (cx, &jv); michael@0: else michael@0: jv = JSVAL_NULL; michael@0: } michael@0: michael@0: JS_RestoreExceptionState (cx, estate); michael@0: michael@0: JSDValue *jsdv = JSD_NewValue (mCx, jv); michael@0: if (!jsdv) michael@0: return NS_ERROR_FAILURE; michael@0: *result = jsdValue::FromPtr (mCx, jsdv); michael@0: if (!*result) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Values */ michael@0: NS_IMPL_ISUPPORTS(jsdValue, jsdIValue, jsdIEphemeral) michael@0: jsdIValue * michael@0: jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue) michael@0: { michael@0: /* value will be dropped by te jsdValue destructor. */ michael@0: michael@0: if (!aValue) michael@0: return nullptr; michael@0: michael@0: jsdIValue *rv = new jsdValue (aCx, aValue); michael@0: NS_IF_ADDREF(rv); michael@0: return rv; michael@0: } michael@0: michael@0: jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(true), michael@0: mCx(aCx), michael@0: mValue(aValue) michael@0: { michael@0: DEBUG_CREATE ("jsdValue", gValueCount); michael@0: mLiveListEntry.value = this; michael@0: jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry); michael@0: } michael@0: michael@0: jsdValue::~jsdValue() michael@0: { michael@0: DEBUG_DESTROY ("jsdValue", gValueCount); michael@0: if (mValid) michael@0: /* call Invalidate() to take ourselves out of the live list */ michael@0: Invalidate(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetIsValid(bool *_rval) michael@0: { michael@0: *_rval = mValid; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::Invalidate() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: mValid = false; michael@0: jsds_RemoveEphemeral (&gLiveValues, &mLiveListEntry); michael@0: JSD_DropValue (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: jsdValue::InvalidateAll() michael@0: { michael@0: if (gLiveValues) michael@0: jsds_InvalidateAllEphemerals (&gLiveValues); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJSDValue (JSDValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = mValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetIsNative (bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_IsValueNative (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetIsNumber (bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_IsValueNumber (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetIsPrimitive (bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_IsValuePrimitive (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJsType (uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JS::RootedValue val(JSD_GetJSRuntime(mCx), JSD_GetValueWrappedJSVal (mCx, mValue)); michael@0: michael@0: if (JSVAL_IS_NULL(val)) michael@0: *_rval = TYPE_NULL; michael@0: else if (JSVAL_IS_BOOLEAN(val)) michael@0: *_rval = TYPE_BOOLEAN; michael@0: else if (JSVAL_IS_DOUBLE(val)) michael@0: *_rval = TYPE_DOUBLE; michael@0: else if (JSVAL_IS_INT(val)) michael@0: *_rval = TYPE_INT; michael@0: else if (JSVAL_IS_STRING(val)) michael@0: *_rval = TYPE_STRING; michael@0: else if (JSVAL_IS_VOID(val)) michael@0: *_rval = TYPE_VOID; michael@0: else if (JSD_IsValueFunction (mCx, mValue)) michael@0: *_rval = TYPE_FUNCTION; michael@0: else if (!JSVAL_IS_PRIMITIVE(val)) michael@0: *_rval = TYPE_OBJECT; michael@0: else michael@0: NS_ASSERTION (0, "Value has no discernible type."); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJsPrototype (jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDValue *jsdv = JSD_GetValuePrototype (mCx, mValue); michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJsParent (jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDValue *jsdv = JSD_GetValueParent (mCx, mValue); michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJsClassName(nsACString &_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: _rval.Assign(JSD_GetValueClassName(mCx, mValue)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJsConstructor (jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDValue *jsdv = JSD_GetValueConstructor (mCx, mValue); michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetJsFunctionName(nsACString &_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: return AssignToJSString(mCx, &_rval, JSD_GetValueFunctionId(mCx, mValue)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetBooleanValue(bool *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetValueBoolean (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetDoubleValue(double *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetValueDouble (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetIntValue(int32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *_rval = JSD_GetValueInt (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetObjectValue(jsdIObject **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDObject *obj; michael@0: obj = JSD_GetObjectForValue (mCx, mValue); michael@0: *_rval = jsdObject::FromPtr (mCx, obj); michael@0: if (!*_rval) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetStringValue(nsACString &_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: AutoSafeJSContext cx; michael@0: JSString *jstr_val = JSD_GetValueString(mCx, mValue); michael@0: if (jstr_val) { michael@0: size_t length; michael@0: const jschar *chars = JS_GetStringCharsZAndLength(cx, jstr_val, &length); michael@0: if (!chars) michael@0: return NS_ERROR_FAILURE; michael@0: nsDependentString depStr(chars, length); michael@0: CopyUTF16toUTF8(depStr, _rval); michael@0: } else { michael@0: _rval.Truncate(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetPropertyCount (int32_t *_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: if (JSD_IsValueObject(mCx, mValue)) michael@0: *_rval = JSD_GetCountOfProperties (mCx, mValue); michael@0: else michael@0: *_rval = -1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetProperties (jsdIProperty ***propArray, uint32_t *length) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: *propArray = nullptr; michael@0: if (length) michael@0: *length = 0; michael@0: michael@0: uint32_t prop_count = JSD_IsValueObject(mCx, mValue) michael@0: ? JSD_GetCountOfProperties (mCx, mValue) michael@0: : 0; michael@0: NS_ENSURE_TRUE(prop_count, NS_OK); michael@0: michael@0: jsdIProperty **pa_temp = michael@0: static_cast michael@0: (nsMemory::Alloc(sizeof (jsdIProperty *) * michael@0: prop_count)); michael@0: NS_ENSURE_TRUE(pa_temp, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: uint32_t i = 0; michael@0: JSDProperty *iter = nullptr; michael@0: JSDProperty *prop; michael@0: while ((prop = JSD_IterateProperties (mCx, mValue, &iter))) { michael@0: pa_temp[i] = jsdProperty::FromPtr (mCx, prop); michael@0: ++i; michael@0: } michael@0: michael@0: NS_ASSERTION (prop_count == i, "property count mismatch"); michael@0: michael@0: /* if caller doesn't care about length, don't bother telling them */ michael@0: *propArray = pa_temp; michael@0: if (length) michael@0: *length = prop_count; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetProperty (const nsACString &name, jsdIProperty **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, JSD_GetDefaultGlobal (mCx)); // Just in case. michael@0: michael@0: /* not rooting this */ michael@0: JSString *jstr_name = JS_NewStringCopyZ(cx, PromiseFlatCString(name).get()); michael@0: if (!jstr_name) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: JSDProperty *prop = JSD_GetValueProperty (mCx, mValue, jstr_name); michael@0: michael@0: *_rval = jsdProperty::FromPtr (mCx, prop); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::Refresh() michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSD_RefreshValue (mCx, mValue); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetWrappedValue(JSContext* aCx, JS::MutableHandle aRetval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: michael@0: aRetval.set(JSD_GetValueWrappedJSVal(mCx, mValue)); michael@0: if (!JS_WrapValue(aCx, aRetval)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdValue::GetScript(jsdIScript **_rval) michael@0: { michael@0: ASSERT_VALID_EPHEMERAL; michael@0: JSDScript *script = JSD_GetScriptForValue(mCx, mValue); michael@0: *_rval = jsdScript::FromPtr(mCx, script); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * debugger service implementation michael@0: ******************************************************************************/ michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(jsdService) michael@0: NS_INTERFACE_MAP_ENTRY(jsdIDebuggerService) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, jsdIDebuggerService) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(jsdService, michael@0: mErrorHook, mBreakpointHook, mDebugHook, michael@0: mDebuggerHook, mInterruptHook, mScriptHook, michael@0: mThrowHook, mTopLevelHook, mFunctionHook, michael@0: mActivationCallback) michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(jsdService) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(jsdService) michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetFlags (uint32_t *_rval) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: *_rval = JSD_GetContextFlags (mCx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetFlags (uint32_t flags) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: JSD_SetContextFlags (mCx, flags); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetImplementationString(nsACString &aImplementationString) michael@0: { michael@0: aImplementationString.AssignLiteral(implementationString); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetImplementationMajor(uint32_t *_rval) michael@0: { michael@0: *_rval = JSDS_MAJOR_VERSION; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetImplementationMinor(uint32_t *_rval) michael@0: { michael@0: *_rval = JSDS_MINOR_VERSION; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetIsOn (bool *_rval) michael@0: { michael@0: *_rval = mOn; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::On (void) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::AsyncOn (jsdIActivationCallback *activationCallback) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Warn that JSD is deprecated, unless the caller has told us michael@0: // that they know already. michael@0: if (mDeprecationAcknowledged) { michael@0: mDeprecationAcknowledged = false; michael@0: } else if (!mWarnedAboutDeprecation) { michael@0: // In any case, warn only once. michael@0: mWarnedAboutDeprecation = true; michael@0: michael@0: // Ignore errors: simply being unable to print the message michael@0: // shouldn't (effectively) disable JSD. michael@0: nsContentUtils::ReportToConsoleNonLocalized( michael@0: NS_LITERAL_STRING("\ michael@0: The jsdIDebuggerService and its associated interfaces are deprecated. \ michael@0: Please use Debugger, via IJSDebugger, instead."), michael@0: nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("JSD"), michael@0: nullptr); michael@0: } michael@0: michael@0: nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mActivationCallback = activationCallback; michael@0: michael@0: return xpc->SetDebugModeWhenPossible(true, true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::RecompileForDebugMode (JSContext *cx, JSCompartment *comp, bool mode) { michael@0: NS_ASSERTION(NS_IsMainThread(), "wrong thread"); michael@0: /* XPConnect now does this work itself, so this IDL entry point is no longer used. */ michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::DeactivateDebugger () michael@0: { michael@0: if (!mCx) michael@0: return NS_OK; michael@0: michael@0: jsdContext::InvalidateAll(); michael@0: jsdScript::InvalidateAll(); michael@0: jsdValue::InvalidateAll(); michael@0: jsdProperty::InvalidateAll(); michael@0: jsdStackFrame::InvalidateAll(); michael@0: ClearAllBreakpoints(); michael@0: michael@0: JSD_SetErrorReporter (mCx, nullptr, nullptr); michael@0: JSD_SetScriptHook (mCx, nullptr, nullptr); michael@0: JSD_ClearThrowHook (mCx); michael@0: JSD_ClearInterruptHook (mCx); michael@0: JSD_ClearDebuggerHook (mCx); michael@0: JSD_ClearDebugBreakHook (mCx); michael@0: JSD_ClearTopLevelHook (mCx); michael@0: JSD_ClearFunctionHook (mCx); michael@0: michael@0: JSD_DebuggerOff (mCx); michael@0: michael@0: mCx = nullptr; michael@0: mRuntime = nullptr; michael@0: mOn = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::ActivateDebugger (JSRuntime *rt) michael@0: { michael@0: if (mOn) michael@0: return (rt == mRuntime) ? NS_OK : NS_ERROR_ALREADY_INITIALIZED; michael@0: michael@0: mRuntime = rt; michael@0: michael@0: if (gPrevGCSliceCallback == jsds_GCSliceCallbackProc) michael@0: /* condition indicates that the callback proc has not been set yet */ michael@0: gPrevGCSliceCallback = JS::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc); michael@0: michael@0: mCx = JSD_DebuggerOnForUser (rt, nullptr, nullptr); michael@0: if (!mCx) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: AutoSafeJSContext cx; michael@0: JS::RootedObject glob(cx, JSD_GetDefaultGlobal (mCx)); michael@0: JSAutoCompartment ac(cx, glob); michael@0: michael@0: /* init xpconnect on the debugger's context in case xpconnect tries to michael@0: * use it for stuff. */ michael@0: nsresult rv; michael@0: nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: xpc->InitClasses (cx, glob); michael@0: michael@0: /* Start watching for script creation/destruction and manage jsdScript michael@0: * objects accordingly michael@0: */ michael@0: JSD_SetScriptHook (mCx, jsds_ScriptHookProc, nullptr); michael@0: michael@0: /* If any of these mFooHook objects are installed, do the required JSD michael@0: * hookup now. See also, jsdService::SetFooHook(). michael@0: */ michael@0: if (mErrorHook) michael@0: JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, nullptr); michael@0: if (mThrowHook) michael@0: JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: /* can't ignore script callbacks, as we need to |Release| the wrapper michael@0: * stored in private data when a script is deleted. */ michael@0: if (mInterruptHook) michael@0: JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mDebuggerHook) michael@0: JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mDebugHook) michael@0: JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mTopLevelHook) michael@0: JSD_SetTopLevelHook (mCx, jsds_CallHookProc, nullptr); michael@0: else michael@0: JSD_ClearTopLevelHook (mCx); michael@0: if (mFunctionHook) michael@0: JSD_SetFunctionHook (mCx, jsds_CallHookProc, nullptr); michael@0: else michael@0: JSD_ClearFunctionHook (mCx); michael@0: mOn = true; michael@0: michael@0: #ifdef DEBUG michael@0: printf ("+++ JavaScript debugging hooks installed.\n"); michael@0: #endif michael@0: michael@0: nsCOMPtr activationCallback; michael@0: mActivationCallback.swap(activationCallback); michael@0: if (activationCallback) michael@0: return activationCallback->OnDebuggerActivated(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::Off (void) michael@0: { michael@0: if (!mOn) michael@0: return NS_OK; michael@0: michael@0: if (!mCx || !mRuntime) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (gDeadScripts) { michael@0: if (gGCRunning) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: while (gDeadScripts) michael@0: jsds_NotifyPendingDeadScripts (JS_GetRuntime(nsContentUtils::GetSafeJSContext())); michael@0: } michael@0: michael@0: DeactivateDebugger(); michael@0: michael@0: #ifdef DEBUG michael@0: printf ("+++ JavaScript debugging hooks removed.\n"); michael@0: #endif michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: xpc->SetDebugModeWhenPossible(false, true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetPauseDepth(uint32_t *_rval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_rval); michael@0: *_rval = mPauseLevel; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::Pause(uint32_t *_rval) michael@0: { michael@0: return DoPause(_rval, false); michael@0: } michael@0: michael@0: nsresult michael@0: jsdService::DoPause(uint32_t *_rval, bool internalCall) michael@0: { michael@0: if (!mCx) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (++mPauseLevel == 1) { michael@0: JSD_SetErrorReporter (mCx, nullptr, nullptr); michael@0: JSD_ClearThrowHook (mCx); michael@0: JSD_ClearInterruptHook (mCx); michael@0: JSD_ClearDebuggerHook (mCx); michael@0: JSD_ClearDebugBreakHook (mCx); michael@0: JSD_ClearTopLevelHook (mCx); michael@0: JSD_ClearFunctionHook (mCx); michael@0: JSD_DebuggerPause (mCx); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (!internalCall) { michael@0: rv = xpc->SetDebugModeWhenPossible(false, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: if (_rval) michael@0: *_rval = mPauseLevel; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::UnPause(uint32_t *_rval) michael@0: { michael@0: return DoUnPause(_rval, false); michael@0: } michael@0: michael@0: nsresult michael@0: jsdService::DoUnPause(uint32_t *_rval, bool internalCall) michael@0: { michael@0: if (!mCx) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: if (mPauseLevel == 0) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: /* check mOn before we muck with this stuff, it's possible the debugger michael@0: * was turned off while we were paused. michael@0: */ michael@0: if (--mPauseLevel == 0 && mOn) { michael@0: JSD_DebuggerUnpause (mCx); michael@0: if (mErrorHook) michael@0: JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, nullptr); michael@0: if (mThrowHook) michael@0: JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mInterruptHook) michael@0: JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mDebuggerHook) michael@0: JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mDebugHook) michael@0: JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: if (mTopLevelHook) michael@0: JSD_SetTopLevelHook (mCx, jsds_CallHookProc, nullptr); michael@0: else michael@0: JSD_ClearTopLevelHook (mCx); michael@0: if (mFunctionHook) michael@0: JSD_SetFunctionHook (mCx, jsds_CallHookProc, nullptr); michael@0: else michael@0: JSD_ClearFunctionHook (mCx); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr xpc = do_GetService(nsIXPConnect::GetCID(), &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (!internalCall) { michael@0: rv = xpc->SetDebugModeWhenPossible(true, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: if (_rval) michael@0: *_rval = mPauseLevel; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::EnumerateContexts (jsdIContextEnumerator *enumerator) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: michael@0: if (!enumerator) michael@0: return NS_OK; michael@0: michael@0: JSContext *iter = nullptr; michael@0: JSContext *cx; michael@0: michael@0: while ((cx = JS_ContextIterator (mRuntime, &iter))) michael@0: { michael@0: nsCOMPtr jsdicx = michael@0: dont_AddRef(jsdContext::FromPtr(mCx, cx)); michael@0: if (jsdicx) michael@0: { michael@0: if (NS_FAILED(enumerator->EnumerateContext(jsdicx))) michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: michael@0: JSDScript *script; michael@0: JSDScript *iter = nullptr; michael@0: nsresult rv = NS_OK; michael@0: michael@0: JSD_LockScriptSubsystem(mCx); michael@0: while((script = JSD_IterateScripts(mCx, &iter))) { michael@0: nsCOMPtr jsdis = michael@0: dont_AddRef(jsdScript::FromPtr(mCx, script)); michael@0: rv = enumerator->EnumerateScript (jsdis); michael@0: if (NS_FAILED(rv)) michael@0: break; michael@0: } michael@0: JSD_UnlockScriptSubsystem(mCx); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GC (void) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: JSRuntime *rt = JSD_GetJSRuntime (mCx); michael@0: JS_GC(rt); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::DumpHeap(const nsACString &fileName) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: #ifndef DEBUG michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: #else michael@0: nsresult rv = NS_OK; michael@0: FILE *file = !fileName.IsEmpty() ? fopen(PromiseFlatCString(fileName).get(), "w") : stdout; michael@0: if (!file) { michael@0: rv = NS_ERROR_FAILURE; michael@0: } else { michael@0: if (!JS_DumpHeap(JS_GetRuntime(nsContentUtils::GetSafeJSContext()), michael@0: file, nullptr, JSTRACE_OBJECT, nullptr, (size_t)-1, nullptr)) michael@0: rv = NS_ERROR_FAILURE; michael@0: if (file != stdout) michael@0: fclose(file); michael@0: } michael@0: return rv; michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::ClearProfileData () michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: JSD_ClearAllProfileData (mCx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::InsertFilter (jsdIFilter *filter, jsdIFilter *after) michael@0: { michael@0: NS_ENSURE_ARG_POINTER (filter); michael@0: if (jsds_FindFilter (filter)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: FilterRecord *rec = PR_NEWZAP (FilterRecord); michael@0: if (!rec) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (!jsds_SyncFilter (rec, filter)) { michael@0: PR_Free (rec); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (gFilters) { michael@0: if (!after) { michael@0: /* insert at head of list */ michael@0: PR_INSERT_LINK(&rec->links, &gFilters->links); michael@0: gFilters = rec; michael@0: } else { michael@0: /* insert somewhere in the list */ michael@0: FilterRecord *afterRecord = jsds_FindFilter (after); michael@0: if (!afterRecord) { michael@0: jsds_FreeFilter(rec); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: PR_INSERT_AFTER(&rec->links, &afterRecord->links); michael@0: } michael@0: } else { michael@0: if (after) { michael@0: /* user asked to insert into the middle of an empty list, bail. */ michael@0: jsds_FreeFilter(rec); michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: PR_INIT_CLIST(&rec->links); michael@0: gFilters = rec; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::AppendFilter (jsdIFilter *filter) michael@0: { michael@0: NS_ENSURE_ARG_POINTER (filter); michael@0: if (jsds_FindFilter (filter)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: FilterRecord *rec = PR_NEWZAP (FilterRecord); michael@0: michael@0: if (!jsds_SyncFilter (rec, filter)) { michael@0: PR_Free (rec); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (gFilters) { michael@0: PR_INSERT_BEFORE(&rec->links, &gFilters->links); michael@0: } else { michael@0: PR_INIT_CLIST(&rec->links); michael@0: gFilters = rec; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::RemoveFilter (jsdIFilter *filter) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(filter); michael@0: FilterRecord *rec = jsds_FindFilter (filter); michael@0: if (!rec) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: if (gFilters == rec) { michael@0: gFilters = reinterpret_cast michael@0: (PR_NEXT_LINK(&rec->links)); michael@0: /* If we're the only filter left, null out the list head. */ michael@0: if (gFilters == rec) michael@0: gFilters = nullptr; michael@0: } michael@0: michael@0: michael@0: PR_REMOVE_LINK(&rec->links); michael@0: jsds_FreeFilter (rec); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SwapFilters (jsdIFilter *filter_a, jsdIFilter *filter_b) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(filter_a); michael@0: NS_ENSURE_ARG_POINTER(filter_b); michael@0: michael@0: FilterRecord *rec_a = jsds_FindFilter (filter_a); michael@0: if (!rec_a) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: if (filter_a == filter_b) { michael@0: /* just a refresh */ michael@0: if (!jsds_SyncFilter (rec_a, filter_a)) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: FilterRecord *rec_b = jsds_FindFilter (filter_b); michael@0: if (!rec_b) { michael@0: /* filter_b is not in the list, replace filter_a with filter_b. */ michael@0: if (!jsds_SyncFilter (rec_a, filter_b)) michael@0: return NS_ERROR_FAILURE; michael@0: } else { michael@0: /* both filters are in the list, swap. */ michael@0: if (!jsds_SyncFilter (rec_a, filter_b)) michael@0: return NS_ERROR_FAILURE; michael@0: if (!jsds_SyncFilter (rec_b, filter_a)) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::EnumerateFilters (jsdIFilterEnumerator *enumerator) michael@0: { michael@0: if (!gFilters) michael@0: return NS_OK; michael@0: michael@0: FilterRecord *current = gFilters; michael@0: do { michael@0: jsds_SyncFilter (current, current->filterObject); michael@0: /* SyncFilter failure would be bad, but what would we do about it? */ michael@0: if (enumerator) { michael@0: nsresult rv = enumerator->EnumerateFilter (current->filterObject); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: current = reinterpret_cast michael@0: (PR_NEXT_LINK (¤t->links)); michael@0: } while (current != gFilters); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::RefreshFilters () michael@0: { michael@0: return EnumerateFilters(nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::ClearFilters () michael@0: { michael@0: if (!gFilters) michael@0: return NS_OK; michael@0: michael@0: FilterRecord *current = reinterpret_cast michael@0: (PR_NEXT_LINK (&gFilters->links)); michael@0: do { michael@0: FilterRecord *next = reinterpret_cast michael@0: (PR_NEXT_LINK (¤t->links)); michael@0: PR_REMOVE_AND_INIT_LINK(¤t->links); michael@0: jsds_FreeFilter(current); michael@0: current = next; michael@0: } while (current != gFilters); michael@0: michael@0: jsds_FreeFilter(current); michael@0: gFilters = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::ClearAllBreakpoints (void) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: michael@0: JSD_LockScriptSubsystem(mCx); michael@0: JSD_ClearAllExecutionHooks (mCx); michael@0: JSD_UnlockScriptSubsystem(mCx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::WrapValue(JS::Handle value, jsdIValue **_rval) michael@0: { michael@0: ASSERT_VALID_CONTEXT; michael@0: JSDValue *jsdv = JSD_NewValue(mCx, value); michael@0: if (!jsdv) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::EnterNestedEventLoop (jsdINestCallback *callback, uint32_t *_rval) michael@0: { michael@0: // Nesting event queues is a thing of the past. Now, we just spin the michael@0: // current event loop. michael@0: nsresult rv = NS_OK; michael@0: AutoNoJSAPI nojsapi; michael@0: uint32_t nestLevel = ++mNestedLoopLevel; michael@0: nsCOMPtr thread = do_GetCurrentThread(); michael@0: michael@0: if (callback) { michael@0: DoPause(nullptr, true); michael@0: rv = callback->OnNest(); michael@0: DoUnPause(nullptr, true); michael@0: } michael@0: michael@0: while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) { michael@0: if (!NS_ProcessNextEvent(thread)) michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: NS_ASSERTION (mNestedLoopLevel <= nestLevel, michael@0: "nested event didn't unwind properly"); michael@0: if (mNestedLoopLevel == nestLevel) michael@0: --mNestedLoopLevel; michael@0: michael@0: *_rval = mNestedLoopLevel; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::ExitNestedEventLoop (uint32_t *_rval) michael@0: { michael@0: if (mNestedLoopLevel > 0) michael@0: --mNestedLoopLevel; michael@0: else michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: *_rval = mNestedLoopLevel; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::AcknowledgeDeprecation() michael@0: { michael@0: mDeprecationAcknowledged = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* hook attribute get/set functions */ michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetErrorHook (jsdIErrorHook *aHook) michael@0: { michael@0: mErrorHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetErrorReporter (mCx, jsds_ErrorHookProc, nullptr); michael@0: else michael@0: JSD_SetErrorReporter (mCx, nullptr, nullptr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetErrorHook (jsdIErrorHook **aHook) michael@0: { michael@0: *aHook = mErrorHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetBreakpointHook (jsdIExecutionHook *aHook) michael@0: { michael@0: mBreakpointHook = aHook; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetBreakpointHook (jsdIExecutionHook **aHook) michael@0: { michael@0: *aHook = mBreakpointHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetDebugHook (jsdIExecutionHook *aHook) michael@0: { michael@0: mDebugHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetDebugBreakHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: else michael@0: JSD_ClearDebugBreakHook (mCx); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetDebugHook (jsdIExecutionHook **aHook) michael@0: { michael@0: *aHook = mDebugHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetDebuggerHook (jsdIExecutionHook *aHook) michael@0: { michael@0: mDebuggerHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetDebuggerHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: else michael@0: JSD_ClearDebuggerHook (mCx); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetDebuggerHook (jsdIExecutionHook **aHook) michael@0: { michael@0: *aHook = mDebuggerHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetInterruptHook (jsdIExecutionHook *aHook) michael@0: { michael@0: mInterruptHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetInterruptHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: else michael@0: JSD_ClearInterruptHook (mCx); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetInterruptHook (jsdIExecutionHook **aHook) michael@0: { michael@0: *aHook = mInterruptHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetScriptHook (jsdIScriptHook *aHook) michael@0: { michael@0: mScriptHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetScriptHook (mCx, jsds_ScriptHookProc, nullptr); michael@0: /* we can't unset it if !aHook, because we still need to see script michael@0: * deletes in order to Release the jsdIScripts held in JSDScript michael@0: * private data. */ michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetScriptHook (jsdIScriptHook **aHook) michael@0: { michael@0: *aHook = mScriptHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetThrowHook (jsdIExecutionHook *aHook) michael@0: { michael@0: mThrowHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetThrowHook (mCx, jsds_ExecutionHookProc, nullptr); michael@0: else michael@0: JSD_ClearThrowHook (mCx); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetThrowHook (jsdIExecutionHook **aHook) michael@0: { michael@0: *aHook = mThrowHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetTopLevelHook (jsdICallHook *aHook) michael@0: { michael@0: mTopLevelHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetTopLevelHook (mCx, jsds_CallHookProc, nullptr); michael@0: else michael@0: JSD_ClearTopLevelHook (mCx); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetTopLevelHook (jsdICallHook **aHook) michael@0: { michael@0: *aHook = mTopLevelHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::SetFunctionHook (jsdICallHook *aHook) michael@0: { michael@0: mFunctionHook = aHook; michael@0: michael@0: /* if the debugger isn't initialized, that's all we can do for now. The michael@0: * ActivateDebugger() method will do the rest when the coast is clear. michael@0: */ michael@0: if (!mCx || mPauseLevel) michael@0: return NS_OK; michael@0: michael@0: if (aHook) michael@0: JSD_SetFunctionHook (mCx, jsds_CallHookProc, nullptr); michael@0: else michael@0: JSD_ClearFunctionHook (mCx); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdService::GetFunctionHook (jsdICallHook **aHook) michael@0: { michael@0: *aHook = mFunctionHook; michael@0: NS_IF_ADDREF(*aHook); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ michael@0: jsdService::~jsdService() michael@0: { michael@0: ClearFilters(); michael@0: mErrorHook = nullptr; michael@0: mBreakpointHook = nullptr; michael@0: mDebugHook = nullptr; michael@0: mDebuggerHook = nullptr; michael@0: mInterruptHook = nullptr; michael@0: mScriptHook = nullptr; michael@0: mThrowHook = nullptr; michael@0: mTopLevelHook = nullptr; michael@0: mFunctionHook = nullptr; michael@0: Off(); michael@0: gJsds = nullptr; michael@0: } michael@0: michael@0: jsdService * michael@0: jsdService::GetService () michael@0: { michael@0: if (!gJsds) michael@0: gJsds = new jsdService(); michael@0: michael@0: NS_IF_ADDREF(gJsds); michael@0: return gJsds; michael@0: } michael@0: michael@0: NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(jsdService, jsdService::GetService) michael@0: michael@0: /* app-start observer. turns on the debugger at app-start. this is inserted michael@0: * and/or removed from the app-start category by the jsdService::initAtStartup michael@0: * property. michael@0: */ michael@0: class jsdASObserver MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: jsdASObserver () {} michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(jsdASObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: jsdASObserver::Observe (nsISupports *aSubject, const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Hmm. Why is the app-startup observer called multiple times? michael@0: //NS_ASSERTION(!gJsds, "app startup observer called twice"); michael@0: nsCOMPtr jsds = do_GetService(jsdServiceCtrID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: bool on; michael@0: rv = jsds->GetIsOn(&on); michael@0: if (NS_FAILED(rv) || on) michael@0: return rv; michael@0: michael@0: nsCOMPtr rts = do_GetService(NS_JSRT_CTRID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: JSRuntime *rt; michael@0: rts->GetRuntime (&rt); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = jsds->ActivateDebugger(rt); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_GENERIC_FACTORY_CONSTRUCTOR(jsdASObserver) michael@0: NS_DEFINE_NAMED_CID(JSDSERVICE_CID); michael@0: NS_DEFINE_NAMED_CID(JSDASO_CID); michael@0: michael@0: static const mozilla::Module::CIDEntry kJSDCIDs[] = { michael@0: { &kJSDSERVICE_CID, false, nullptr, jsdServiceConstructor }, michael@0: { &kJSDASO_CID, false, nullptr, jsdASObserverConstructor }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kJSDContracts[] = { michael@0: { jsdServiceCtrID, &kJSDSERVICE_CID }, michael@0: { jsdARObserverCtrID, &kJSDASO_CID }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kJSDModule = { michael@0: mozilla::Module::kVersion, michael@0: kJSDCIDs, michael@0: kJSDContracts michael@0: }; michael@0: michael@0: NSMODULE_DEFN(JavaScript_Debugger) = &kJSDModule; michael@0: michael@0: void michael@0: global_finalize(JSFreeOp *aFop, JSObject *aObj) michael@0: { michael@0: nsIScriptObjectPrincipal *sop = michael@0: static_cast(js::GetObjectPrivate(aObj)); michael@0: MOZ_ASSERT(sop); michael@0: static_cast(sop)->ForgetGlobalObject(); michael@0: NS_IF_RELEASE(sop); michael@0: } michael@0: michael@0: JSObject * michael@0: CreateJSDGlobal(JSContext *aCx, const JSClass *aClasp) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr nullPrin = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: JSPrincipals *jsPrin = nsJSPrincipals::get(nullPrin); michael@0: JS::RootedObject global(aCx, JS_NewGlobalObject(aCx, aClasp, jsPrin, JS::DontFireOnNewGlobalHook)); michael@0: NS_ENSURE_TRUE(global, nullptr); michael@0: michael@0: // We have created a new global let's attach a private to it michael@0: // that implements nsIGlobalObject. michael@0: nsCOMPtr sbp = michael@0: new SandboxPrivate(nullPrin, global); michael@0: JS_SetPrivate(global, sbp.forget().take()); michael@0: michael@0: JS_FireOnNewGlobalObject(aCx, global); michael@0: michael@0: return global; michael@0: } michael@0: michael@0: /******************************************************************************** michael@0: ******************************************************************************** michael@0: * graveyard michael@0: */ michael@0: michael@0: #if 0 michael@0: /* Thread States */ michael@0: NS_IMPL_ISUPPORTS(jsdThreadState, jsdIThreadState); michael@0: michael@0: NS_IMETHODIMP michael@0: jsdThreadState::GetJSDContext(JSDContext **_rval) michael@0: { michael@0: *_rval = mCx; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdThreadState::GetJSDThreadState(JSDThreadState **_rval) michael@0: { michael@0: *_rval = mThreadState; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdThreadState::GetFrameCount (uint32_t *_rval) michael@0: { michael@0: *_rval = JSD_GetCountOfStackFrames (mCx, mThreadState); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdThreadState::GetTopFrame (jsdIStackFrame **_rval) michael@0: { michael@0: JSDStackFrameInfo *sfi = JSD_GetStackFrame (mCx, mThreadState); michael@0: michael@0: *_rval = jsdStackFrame::FromPtr (mCx, mThreadState, sfi); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdThreadState::GetPendingException(jsdIValue **_rval) michael@0: { michael@0: JSDValue *jsdv = JSD_GetException (mCx, mThreadState); michael@0: michael@0: *_rval = jsdValue::FromPtr (mCx, jsdv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: jsdThreadState::SetPendingException(jsdIValue *aException) michael@0: { michael@0: JSDValue *jsdv; michael@0: michael@0: nsresult rv = aException->GetJSDValue (&jsdv); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (!JSD_SetException (mCx, mThreadState, jsdv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #endif