nsresult XPCJSStackFrame::CreateStack(JSContext* cx, JSStackFrame* fp, XPCJSStackFrame** stack) { static const unsigned MAX_FRAMES = 100; unsigned numFrames = 0; nsRefPtr<XPCJSStackFrame> first = new XPCJSStackFrame(); nsRefPtr<XPCJSStackFrame> self = first; while (fp && self) { if (!JS_IsScriptFrame(cx, fp)) { self->mLanguage = nsIProgrammingLanguage::CPLUSPLUS; } else { self->mLanguage = nsIProgrammingLanguage::JAVASCRIPT; JSScript* script = JS_GetFrameScript(cx, fp); jsbytecode* pc = JS_GetFramePC(cx, fp); if (script && pc) { JS::AutoEnterFrameCompartment ac; if (ac.enter(cx, fp)) { const char* filename = JS_GetScriptFilename(cx, script); if (filename) { self->mFilename = (char*) nsMemory::Clone(filename, sizeof(char)*(strlen(filename)+1)); } self->mLineno = (int32_t) JS_PCToLineNumber(cx, script, pc); JSFunction* fun = JS_GetFrameFunction(cx, fp); if (fun) { JSString *funid = JS_GetFunctionId(fun); if (funid) { size_t length = JS_GetStringEncodingLength(cx, funid); if (length != size_t(-1)) { self->mFunname = static_cast<char *>(nsMemory::Alloc(length + 1)); if (self->mFunname) { JS_EncodeStringToBuffer(funid, self->mFunname, length); self->mFunname[length] = '\0'; } } } } } } else { self->mLanguage = nsIProgrammingLanguage::CPLUSPLUS; } } if (++numFrames > MAX_FRAMES) { fp = NULL; } else if (JS_FrameIterator(cx, &fp)) { XPCJSStackFrame* frame = new XPCJSStackFrame(); self->mCaller = frame; self = frame; } } *stack = first.forget().get(); return NS_OK; }
void CThreadDebugger::DestroyScriptHook(JSContext* cx, JSScript* script) { uint scriptExtent = JS_GetScriptLineExtent (cx, script); uint baseLine = JS_GetScriptBaseLineNumber(cx, script); char* pStr = NULL; pStr = (char*)JS_GetScriptFilename(cx, script); if (pStr != NULL) { std::string fileName(pStr); for (uint line = baseLine; line < scriptExtent + baseLine; ++line) { if (CheckIfMappingPresent(fileName, line)) { if (m->m_LineToPCMap[fileName][line].pScript == script) { ReturnActiveBreakPoints(m->m_LineToPCMap[fileName][line].pBytecode); m->m_LineToPCMap[fileName].erase(line); if (m->m_LineToPCMap[fileName].empty()) m->m_LineToPCMap.erase(fileName); } } } } }
void AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript, JS::Handle<JS::Value> aAsyncStack, JS::Handle<JSString*> aAsyncCause) { JS::Rooted<JSFunction*> rootedFunction(aCx); if (aFunction) { rootedFunction = aFunction; } JS::Rooted<JSScript*> rootedScript(aCx); if (aScript) { rootedScript = aScript; } nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx))); if (!window || !window->GetDocShell() || !window->GetDocShell()->GetRecordProfileTimelineMarkers()) { return; } nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell(); nsString filename; uint32_t lineNumber = 0; js::AutoStableStringChars functionName(aCx); if (rootedFunction) { JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction)); if (displayId) { if (!functionName.initTwoByte(aCx, displayId)) { JS_ClearPendingException(aCx); return; } } } if (!rootedScript) { rootedScript = JS_GetFunctionScript(aCx, rootedFunction); } if (rootedScript) { filename = NS_ConvertUTF8toUTF16(JS_GetScriptFilename(rootedScript)); lineNumber = JS_GetScriptBaseLineNumber(aCx, rootedScript); } if (!filename.IsEmpty() || functionName.isTwoByte()) { const char16_t* functionNameChars = functionName.isTwoByte() ? functionName.twoByteChars() : nullptr; JS::Rooted<JS::Value> asyncCauseValue(aCx, aAsyncCause ? StringValue(aAsyncCause) : JS::NullValue()); docShellForJSRunToCompletion->NotifyJSRunToCompletionStart(mReason, functionNameChars, filename.BeginReading(), lineNumber, aAsyncStack, asyncCauseValue); } }
/* * call-seq: * file_name(context, script) * * Get the file name of the +script+ given +context+ */ static VALUE file_name(VALUE UNUSED(self), VALUE context, VALUE script) { JSContext * js = NULL; JSScript * js_script = NULL; Data_Get_Struct(context, JSContext, js); Data_Get_Struct(script, JSScript, js_script); return rb_str_new2(JS_GetScriptFilename(js, js_script)); }
const char* __Core_getScriptName (JSContext* cx) { JSStackFrame* fp = NULL; JSScript* script = NULL; do { fp = JS_FrameIterator(cx, &fp); script = JS_GetFrameScript(cx, fp); } while (!script && fp); return JS_GetScriptFilename(cx, script); }
nsresult XPCJSStackFrame::CreateStack(JSContext* cx, XPCJSStackFrame** stack) { static const unsigned MAX_FRAMES = 100; nsRefPtr<XPCJSStackFrame> first = new XPCJSStackFrame(); nsRefPtr<XPCJSStackFrame> self = first; JS::StackDescription* desc = JS::DescribeStack(cx, MAX_FRAMES); if (!desc) return NS_ERROR_FAILURE; for (size_t i = 0; i < desc->nframes && self; i++) { self->mLanguage = nsIProgrammingLanguage::JAVASCRIPT; JSAutoCompartment ac(cx, desc->frames[i].script); const char* filename = JS_GetScriptFilename(cx, desc->frames[i].script); if (filename) { self->mFilename = (char*) nsMemory::Clone(filename, sizeof(char)*(strlen(filename)+1)); } self->mLineno = desc->frames[i].lineno; JSFunction* fun = desc->frames[i].fun; if (fun) { JS::RootedString funid(cx, JS_GetFunctionDisplayId(fun)); if (funid) { size_t length = JS_GetStringEncodingLength(cx, funid); if (length != size_t(-1)) { self->mFunname = static_cast<char *>(nsMemory::Alloc(length + 1)); if (self->mFunname) { JS_EncodeStringToBuffer(cx, funid, self->mFunname, length); self->mFunname[length] = '\0'; } } } } XPCJSStackFrame* frame = new XPCJSStackFrame(); self->mCaller = frame; self = frame; } JS::FreeStackDescription(cx, desc); *stack = first.forget().get(); return NS_OK; }
static void* CallHook_(JSContext* cx, JSStackFrame* fp, JSBool before, JSBool* UNUSED(ok), void* closure) { CScopeLock lock(CallHookMutex); CThreadDebugger* pThreadDebugger = (CThreadDebugger*) closure; if (before) { JSScript* script; script = JS_GetFrameScript(cx, fp); const char* fileName = JS_GetScriptFilename(cx, script); uint lineno = JS_GetScriptBaseLineNumber(cx, script); JSFunction* fun = JS_GetFrameFunction(cx, fp); pThreadDebugger->ExecuteHook(cx, fileName, lineno, script, fun, closure); } return closure; }
/* define properties that JS Error() expose, such as fileName, lineNumber and stack */ static void define_error_properties(JSContext *context, JSObject *obj) { JSStackFrame *frame; JSScript *script; jsbytecode *pc; jsval v; GString *stack; const char *filename; GjsContext *gjs_context; /* find the JS frame that triggered the error */ frame = NULL; while (JS_FrameIterator(context, &frame)) { if (JS_IsScriptFrame(context, frame)) break; } /* someone called gjs_throw at top of the stack? well, no stack in that case */ if (!frame) return; script = JS_GetFrameScript(context, frame); pc = JS_GetFramePC(context, frame); stack = g_string_new(NULL); gjs_context = JS_GetContextPrivate(context); gjs_context_print_stack_to_buffer(gjs_context, frame, stack); if (gjs_string_from_utf8(context, stack->str, stack->len, &v)) JS_DefineProperty(context, obj, "stack", v, NULL, NULL, JSPROP_ENUMERATE); filename = JS_GetScriptFilename(context, script); if (gjs_string_from_filename(context, filename, -1, &v)) JS_DefineProperty(context, obj, "fileName", v, NULL, NULL, JSPROP_ENUMERATE); v = INT_TO_JSVAL(JS_PCToLineNumber(context, script, pc)); JS_DefineProperty(context, obj, "lineNumber", v, NULL, NULL, JSPROP_ENUMERATE); g_string_free(stack, TRUE); }
ScriptLocationName(const ScriptLocation& loc) { JSContext* cx = loc.cx; JSScript* script = loc.script; jsbytecode* pc = loc.pc; std::string filename = JS_GetScriptFilename(cx, script); size_t slash = filename.rfind('/'); if (slash != filename.npos) filename = filename.substr(slash+1); uintN line = JS_PCToLineNumber(cx, script, pc); std::stringstream ss; ss << "(" << filename << ":" << line << ")"; name = ss.str(); }
static JSDObject* _createJSDObject(JSDContext* jsdc, JSContext *cx, JSObject *obj) { JSDObject* jsdobj; JSStackFrame* fp; JSStackFrame* iter = NULL; const char* newURL; jsbytecode* pc; JS_ASSERT(JSD_OBJECTS_LOCKED(jsdc)); jsdobj = (JSDObject*) calloc(1, sizeof(JSDObject)); if (jsdobj) { JS_INIT_CLIST(&jsdobj->links); JS_APPEND_LINK(&jsdobj->links, &jsdc->objectsList); jsdobj->obj = obj; JS_HashTableAdd(jsdc->objectsTable, obj, jsdobj); if (jsdc->flags & JSD_DISABLE_OBJECT_TRACE) return jsdobj; /* walk the stack to find js frame (if any) causing creation */ while (NULL != (fp = JS_FrameIterator(cx, &iter))) { if( !JS_IsNativeFrame(cx, fp) ) { JSScript* script = JS_GetFrameScript(cx, fp); if( !script ) continue; newURL = JS_GetScriptFilename(cx, script); if( newURL ) jsdobj->newURL = jsd_AddAtom(jsdc, newURL); pc = JS_GetFramePC(cx, fp); if( pc ) jsdobj->newLineno = JS_PCToLineNumber(cx, script, pc); break; } } } return jsdobj; }
void jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj, JSAbstractFramePtr frame) { JSDObject* jsdobj; JS::RootedScript script(cx); JSDScript* jsdscript; const char* ctorURL; JSString* ctorNameStr; const char* ctorName; JSD_LOCK_OBJECTS(jsdc); jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj); if( jsdobj && !jsdobj->ctorURL ) { script = frame.script(); if( script ) { ctorURL = JS_GetScriptFilename(cx, script); if( ctorURL ) jsdobj->ctorURL = jsd_AddAtom(jsdc, ctorURL); JSD_LOCK_SCRIPTS(jsdc); jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame); JSD_UNLOCK_SCRIPTS(jsdc); if( jsdscript && (ctorNameStr = jsd_GetScriptFunctionId(jsdc, jsdscript)) ) { if( (ctorName = JS_EncodeString(cx, ctorNameStr)) ) { jsdobj->ctorName = jsd_AddAtom(jsdc, ctorName); JS_free(cx, (void *) ctorName); } } jsdobj->ctorLineno = JS_GetScriptBaseLineNumber(cx, script); } } TRACEOBJ(jsdc, jsdobj, 3); JSD_UNLOCK_OBJECTS(jsdc); }
void jsd_Constructing(JSDContext* jsdc, JSContext *cx, JSObject *obj, JSStackFrame *fp) { JSDObject* jsdobj; JSScript* script; JSDScript* jsdscript; const char* ctorURL; const char* ctorName; JSD_LOCK_OBJECTS(jsdc); jsdobj = jsd_GetJSDObjectForJSObject(jsdc, obj); if( jsdobj && !jsdobj->ctorURL && !JS_IsNativeFrame(cx, fp) ) { script = JS_GetFrameScript(cx, fp); if( script ) { ctorURL = JS_GetScriptFilename(cx, script); if( ctorURL ) jsdobj->ctorURL = jsd_AddAtom(jsdc, ctorURL); JSD_LOCK_SCRIPTS(jsdc); jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, fp); JSD_UNLOCK_SCRIPTS(jsdc); if( jsdscript ) { ctorName = jsd_GetScriptFunctionName(jsdc, jsdscript); if( ctorName ) jsdobj->ctorName = jsd_AddAtom(jsdc, ctorName); } jsdobj->ctorLineno = JS_GetScriptBaseLineNumber(cx, script); } } TRACEOBJ(jsdc, jsdobj, 3); JSD_UNLOCK_OBJECTS(jsdc); }
JSTrapStatus CThreadDebugger::BreakHandler(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* UNUSED(rval), jsval UNUSED(closure), BREAK_SRC breakSrc) { uint line = JS_PCToLineNumber(cx, script, pc); std::string filename(JS_GetScriptFilename(cx, script)); SetIsInBreak(true); SaveCallstack(); SetLastBreakLine(line); SetBreakFileName(filename); *m->m_pLastBreakFrame = NULL; if (breakSrc == BREAK_SRC_INTERRUP) { JS_ClearInterrupt(m->m_pScriptInterface->GetRuntime(), NULL, NULL); JS_SetSingleStepMode(cx, script, false); } if (m->m_pDebuggingServer->GetSettingSimultaneousThreadBreak()) { m->m_pDebuggingServer->SetBreakRequestedByThread(true); } // Wait until the user continues the execution while (1) { DBGCMD nextDbgCmd = GetNextDbgCmd(); while (!m->m_StackInfoRequests.empty()) { StackInfoRequest request = m->m_StackInfoRequests.front(); SaveStackFrameData(request.requestType, request.nestingLevel); SDL_SemPost(request.semaphore); m->m_StackInfoRequests.pop(); } if (nextDbgCmd == DBG_CMD_NONE) { // Wait a while before checking for new m_NextDbgCmd again. // We don't want this loop to take 100% of a CPU core for each thread that is in break mode. // On the other hande we don't want the debugger to become unresponsive. SDL_Delay(100); } else if (nextDbgCmd == DBG_CMD_SINGLESTEP || nextDbgCmd == DBG_CMD_STEPINTO || nextDbgCmd == DBG_CMD_STEPOUT) { JSStackFrame* iter = NULL; *m->m_pLastBreakFrame = JS_FrameIterator(m->m_pScriptInterface->GetContext(), &iter); if (!JS_SetSingleStepMode(cx, script, true)) LOGERROR(L"JS_SetSingleStepMode returned false!"); // TODO: When can this happen? else { if (nextDbgCmd == DBG_CMD_SINGLESTEP) { JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepHandler_, this); break; } else if (nextDbgCmd == DBG_CMD_STEPINTO) { JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepIntoHandler_, this); break; } else if (nextDbgCmd == DBG_CMD_STEPOUT) { JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepOutHandler_, this); break; } } } else if (nextDbgCmd == DBG_CMD_CONTINUE) { if (!JS_SetSingleStepMode(cx, script, true)) LOGERROR(L"JS_SetSingleStepMode returned false!"); // TODO: When can this happen? else { // Setup a handler to check for break-requests from the DebuggingServer regularly JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), CheckForBreakRequestHandler_, this); } break; } else debug_warn("Invalid DBGCMD found in CThreadDebugger::BreakHandler!"); } ClearTrapsToRemove(); SetAllNewTraps(); SetNextDbgCmd(DBG_CMD_NONE); SetIsInBreak(false); SetBreakFileName(""); // All saved stack data becomes invalid { CScopeLock lock(m->m_Mutex); m->m_StackFrameData.clear(); } return JSTRAP_CONTINUE; }
const char* ThreadStackHelper::AppendJSEntry(const volatile StackEntry* aEntry, intptr_t& aAvailableBufferSize, const char* aPrevLabel) { // May be called from another thread or inside a signal handler. // We assume querying the script is safe but we must not manupulate it. // Also we must not allocate any memory from heap. MOZ_ASSERT(aEntry->isJs()); MOZ_ASSERT(aEntry->script()); const char* label; if (IsChromeJSScript(aEntry->script())) { const char* filename = JS_GetScriptFilename(aEntry->script()); const unsigned lineno = JS_PCToLineNumber(aEntry->script(), aEntry->pc()); MOZ_ASSERT(filename); char buffer[128]; // Enough to fit longest js file name from the tree // Some script names are in the form "foo -> bar -> baz". // Here we find the origin of these redirected scripts. const char* basename = GetPathAfterComponent(filename, " -> "); if (basename) { filename = basename; } basename = GetFullPathForScheme(filename, "chrome://"); if (!basename) { basename = GetFullPathForScheme(filename, "resource://"); } if (!basename) { // If the (add-on) script is located under the {profile}/extensions // directory, extract the path after the /extensions/ part. basename = GetPathAfterComponent(filename, "/extensions/"); } if (!basename) { // Only keep the file base name for paths outside the above formats. basename = strrchr(filename, '/'); basename = basename ? basename + 1 : filename; // Look for Windows path separator as well. filename = strrchr(basename, '\\'); if (filename) { basename = filename + 1; } } size_t len = snprintf_literal(buffer, "%s:%u", basename, lineno); if (len < sizeof(buffer)) { if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) { return aPrevLabel; } // Keep track of the required buffer size aAvailableBufferSize -= (len + 1); if (aAvailableBufferSize >= 0) { // Buffer is big enough. return mStackToFill->InfallibleAppendViaBuffer(buffer, len); } // Buffer is not big enough; fall through to using static label below. } // snprintf failed or buffer is not big enough. label = "(chrome script)"; } else { label = "(content script)"; } if (mStackToFill->IsSameAsEntry(aPrevLabel, label)) { return aPrevLabel; } mStackToFill->infallibleAppend(label); return label; }
void WrapperPromiseCallback::Call(JS::Handle<JS::Value> aValue) { // AutoCxPusher and co. interact with xpconnect, which crashes on // workers. On workers we'll get the right context from // GetDefaultJSContextForThread(), and since there is only one context, we // don't need to push or pop it from the stack. JSContext* cx = nsContentUtils::GetDefaultJSContextForThread(); Maybe<AutoCxPusher> pusher; if (NS_IsMainThread()) { pusher.construct(cx); } Maybe<JSAutoCompartment> ac; EnterCompartment(ac, cx, aValue); ErrorResult rv; // If invoking callback threw an exception, run resolver's reject with the // thrown exception as argument and the synchronous flag set. JS::Rooted<JS::Value> value(cx, mCallback->Call(aValue, rv, CallbackObject::eRethrowExceptions)); rv.WouldReportJSException(); if (rv.Failed() && rv.IsJSException()) { JS::Rooted<JS::Value> value(cx); rv.StealJSException(cx, &value); Maybe<JSAutoCompartment> ac2; EnterCompartment(ac2, cx, value); mNextPromise->RejectInternal(cx, value, Promise::SyncTask); return; } // If the return value is the same as the promise itself, throw TypeError. if (value.isObject()) { JS::Rooted<JSObject*> valueObj(cx, &value.toObject()); Promise* returnedPromise; nsresult r = UNWRAP_OBJECT(Promise, valueObj, returnedPromise); if (NS_SUCCEEDED(r) && returnedPromise == mNextPromise) { const char* fileName = nullptr; uint32_t lineNumber = 0; // Try to get some information about the callback to report a sane error, // but don't try too hard (only deals with scripted functions). JS::Rooted<JSObject*> unwrapped(cx, js::CheckedUnwrap(mCallback->Callback())); if (unwrapped) { JSAutoCompartment ac(cx, unwrapped); if (JS_ObjectIsFunction(cx, unwrapped)) { JS::Rooted<JS::Value> asValue(cx, JS::ObjectValue(*unwrapped)); JS::Rooted<JSFunction*> func(cx, JS_ValueToFunction(cx, asValue)); MOZ_ASSERT(func); JSScript* script = JS_GetFunctionScript(cx, func); if (script) { fileName = JS_GetScriptFilename(cx, script); lineNumber = JS_GetScriptBaseLineNumber(cx, script); } } } // We're back in aValue's compartment here. JS::Rooted<JSString*> stack(cx, JS_GetEmptyString(JS_GetRuntime(cx))); JS::Rooted<JSString*> fn(cx, JS_NewStringCopyZ(cx, fileName)); if (!fn) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(cx); return; } JS::Rooted<JSString*> message(cx, JS_NewStringCopyZ(cx, "then() cannot return same Promise that it resolves.")); if (!message) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(cx); return; } JS::Rooted<JS::Value> typeError(cx); if (!JS::CreateTypeError(cx, stack, fn, lineNumber, 0, nullptr, message, &typeError)) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(cx); return; } mNextPromise->RejectInternal(cx, typeError, Promise::SyncTask); return; } } // Otherwise, run resolver's resolve with value and the synchronous flag // set. Maybe<JSAutoCompartment> ac2; EnterCompartment(ac2, cx, value); mNextPromise->ResolveInternal(cx, value, Promise::SyncTask); }
nsresult leakmonJSObjectInfo::Init(leakmonObjectsInReportTable &aObjectsInReport) { mIsInitialized = PR_TRUE; JSContext *cx = leakmonService::GetJSContext(); NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED); JSAutoRequest ar(cx); if (!JSVAL_IS_PRIMITIVE(mJSValue)) { JSObject *obj = JSVAL_TO_OBJECT(mJSValue); // All of the objects in obj's prototype chain, and all // objects reachable from JS_NewPropertyIterator should // (I think?) be in the same compartment. JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) { return NS_ERROR_FAILURE; } JSObject *p; for (p = obj; p; p = JS_GetPrototype(cx, p)) { // Stack-scanning protects newly-created objects // (etor) from GC. (And protecting |etor| // should in turn protect |id|.) // JS_NewPropertyIterator has the nice property that it // avoids JS_Enumerate on native objects (where it can // execute code) and uses the scope properties, but doesn't // require this code to use the unstable OBJ_IS_NATIVE API. JSObject *etor = JS_NewPropertyIterator(cx, p); if (!etor) return NS_ERROR_OUT_OF_MEMORY; jsid id; while (JS_NextProperty(cx, etor, &id) && !JSID_IS_VOID(id)) { nsresult rv = AppendProperty(id, cx, aObjectsInReport); NS_ENSURE_SUCCESS(rv, rv); } } if (JS_ObjectIsFunction(cx, obj)) { JSFunction *fun = JS_ValueToFunction(cx, mJSValue); NS_ENSURE_TRUE(fun, NS_ERROR_UNEXPECTED); JSScript *script = JS_GetFunctionScript(cx, fun); if (script) { // null for native code const char *fname = JS_GetScriptFilename(cx, script); // XXX Do we know the encoding of this file name? mFileName = NS_ConvertUTF8toUTF16(fname); mLineStart = JS_GetScriptBaseLineNumber(cx, script); mLineEnd = mLineStart + JS_GetScriptLineExtent(cx, script) - 1; } } } ValueToString(cx, mJSValue, mString); return NS_OK; }
static void format_frame(JSContext* cx, JSStackFrame* fp, GString *buf, int num) { JSPropertyDescArray call_props = { 0, NULL }; JSObject* call_obj = NULL; char* funname_str = NULL; const char* filename = NULL; guint32 lineno = 0; guint32 named_arg_count = 0; JSFunction* fun = NULL; JSScript* script; guchar* pc; guint32 i; gboolean is_string; jsval val; (void)JS_EnterLocalRootScope(cx); if (!JS_IsScriptFrame(cx, fp)) { g_string_append_printf(buf, "%d [native frame]\n", num); goto out; } /* get the info for this stack frame */ script = JS_GetFrameScript(cx, fp); pc = JS_GetFramePC(cx, fp); if (script && pc) { filename = JS_GetScriptFilename(cx, script); lineno = (guint32) JS_PCToLineNumber(cx, script, pc); fun = JS_GetFrameFunction(cx, fp); if (fun) { JSString* funname = JS_GetFunctionId(fun); if (funname) funname_str = gjs_string_get_ascii(cx, STRING_TO_JSVAL(funname)); } call_obj = JS_GetFrameCallObject(cx, fp); if (call_obj) { if (!JS_GetPropertyDescArray(cx, call_obj, &call_props)) call_props.array = NULL; } } /* print the frame number and function name */ if (funname_str) { g_string_append_printf(buf, "%d %s(", num, funname_str); g_free(funname_str); } else if (fun) g_string_append_printf(buf, "%d anonymous(", num); else g_string_append_printf(buf, "%d <TOP LEVEL>", num); for (i = 0; i < call_props.length; i++) { char *name = NULL; char *value = NULL; JSPropertyDesc* desc = &call_props.array[i]; if(desc->flags & JSPD_ARGUMENT) { name = jsvalue_to_string(cx, desc->id, &is_string); if(!is_string) { g_free(name); name = NULL; } value = jsvalue_to_string(cx, desc->value, &is_string); g_string_append_printf(buf, "%s%s%s%s%s%s", named_arg_count ? ", " : "", name ? name :"", name ? " = " : "", is_string ? "\"" : "", value ? value : "?unknown?", is_string ? "\"" : ""); named_arg_count++; } g_free(name); g_free(value); } /* print any unnamed trailing args (found in 'arguments' object) */ if (call_obj != NULL && JS_GetProperty(cx, call_obj, "arguments", &val) && JSVAL_IS_OBJECT(val)) { guint32 k; guint32 arg_count; JSObject* args_obj = JSVAL_TO_OBJECT(val); if (JS_GetArrayLength(cx, args_obj, &arg_count) && arg_count > named_arg_count) { for (k = named_arg_count; k < arg_count; k++) { if (JS_GetElement(cx, args_obj, k, &val)) { char *value = jsvalue_to_string(cx, val, &is_string); g_string_append_printf(buf, "%s%s%s%s", k ? ", " : "", is_string ? "\"" : "", value ? value : "?unknown?", is_string ? "\"" : ""); g_free(value); } } } } /* print filename and line number */ g_string_append_printf(buf, "%s@%s:%d\n", fun ? ")" : "", filename ? filename : "", lineno); out: if (call_props.array) JS_PutPropertyDescArray(cx, &call_props); JS_LeaveLocalRootScope(cx); }
static JSDScript* _newJSDScript(JSDContext* jsdc, JSContext *cx, JSScript *script, JSFunction* function) { JSDScript* jsdscript; uintN lineno; const char* raw_filename; JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); /* these are inlined javascript: urls and we can't handle them now */ lineno = (uintN) JS_GetScriptBaseLineNumber(cx, script); if( lineno == 0 ) return NULL; jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript)); if( ! jsdscript ) return NULL; raw_filename = JS_GetScriptFilename(cx,script); JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript); JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts); jsdscript->jsdc = jsdc; jsdscript->script = script; jsdscript->function = function; jsdscript->lineBase = lineno; jsdscript->lineExtent = (uintN)NOT_SET_YET; jsdscript->data = NULL; #ifndef LIVEWIRE jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename); #else jsdscript->app = LWDBG_GetCurrentApp(); if( jsdscript->app && raw_filename ) { jsdscript->url = jsdlw_BuildAppRelativeFilename(jsdscript->app, raw_filename); if( function ) { jsdscript->lwscript = LWDBG_GetScriptOfFunction(jsdscript->app, JS_GetFunctionName(function)); /* also, make sure this file is added to filelist if is .js file */ if( HasFileExtention(raw_filename,"js") || HasFileExtention(raw_filename,"sjs") ) { jsdlw_PreLoadSource(jsdc, jsdscript->app, raw_filename, JS_FALSE); } } else { jsdscript->lwscript = LWDBG_GetCurrentTopLevelScript(); } } #endif JS_INIT_CLIST(&jsdscript->hooks); return jsdscript; }
void WrapperPromiseCallback::Call(JSContext* aCx, JS::Handle<JS::Value> aValue) { JSAutoCompartment ac(aCx, mGlobal); JS::Rooted<JS::Value> value(aCx, aValue); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return; } ErrorResult rv; // If invoking callback threw an exception, run resolver's reject with the // thrown exception as argument and the synchronous flag set. JS::Rooted<JS::Value> retValue(aCx); mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions); rv.WouldReportJSException(); if (rv.Failed() && rv.IsJSException()) { JS::Rooted<JS::Value> value(aCx); rv.StealJSException(aCx, &value); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return; } mNextPromise->RejectInternal(aCx, value, Promise::SyncTask); return; } // If the return value is the same as the promise itself, throw TypeError. if (retValue.isObject()) { JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject()); Promise* returnedPromise; nsresult r = UNWRAP_OBJECT(Promise, valueObj, returnedPromise); if (NS_SUCCEEDED(r) && returnedPromise == mNextPromise) { const char* fileName = nullptr; uint32_t lineNumber = 0; // Try to get some information about the callback to report a sane error, // but don't try too hard (only deals with scripted functions). JS::Rooted<JSObject*> unwrapped(aCx, js::CheckedUnwrap(mCallback->Callback())); if (unwrapped) { JSAutoCompartment ac(aCx, unwrapped); if (JS_ObjectIsFunction(aCx, unwrapped)) { JS::Rooted<JS::Value> asValue(aCx, JS::ObjectValue(*unwrapped)); JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, asValue)); MOZ_ASSERT(func); JSScript* script = JS_GetFunctionScript(aCx, func); if (script) { fileName = JS_GetScriptFilename(script); lineNumber = JS_GetScriptBaseLineNumber(aCx, script); } } } // We're back in aValue's compartment here. JS::Rooted<JSString*> stack(aCx, JS_GetEmptyString(JS_GetRuntime(aCx))); JS::Rooted<JSString*> fn(aCx, JS_NewStringCopyZ(aCx, fileName)); if (!fn) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return; } JS::Rooted<JSString*> message(aCx, JS_NewStringCopyZ(aCx, "then() cannot return same Promise that it resolves.")); if (!message) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return; } JS::Rooted<JS::Value> typeError(aCx); if (!JS::CreateTypeError(aCx, stack, fn, lineNumber, 0, nullptr, message, &typeError)) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return; } mNextPromise->RejectInternal(aCx, typeError, Promise::SyncTask); return; } } // Otherwise, run resolver's resolve with value and the synchronous flag // set. if (!JS_WrapValue(aCx, &retValue)) { NS_WARNING("Failed to wrap value into the right compartment."); return; } mNextPromise->ResolveInternal(aCx, retValue, Promise::SyncTask); }
void ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry) { // For non-js frames we just include the raw label. if (!aEntry.isJs()) { const char* entryLabel = aEntry.label(); // entryLabel is a statically allocated string, so we want to store a // reference to it without performing any allocations. This is important, as // we aren't allowed to allocate within this function. // // The variant for this kind of label in our HangStack object is a // `nsCString`, which normally contains heap allocated string data. However, // `nsCString` has an optimization for literal strings which causes the // backing data to not be copied when being copied between nsCString // objects. // // We take advantage of that optimization by creating a nsCString object // which has the LITERAL flag set. Without this optimization, this code // would be incorrect. nsCString label; label.AssignLiteral(entryLabel, strlen(entryLabel)); // Let's make sure we don't deadlock here, by asserting that `label`'s // backing data matches. MOZ_RELEASE_ASSERT(label.BeginReading() == entryLabel, "String copy performed during ThreadStackHelper::CollectPseudoEntry"); TryAppendFrame(label); return; } if (!aEntry.script()) { TryAppendFrame(HangEntrySuppressed()); return; } if (!IsChromeJSScript(aEntry.script())) { TryAppendFrame(HangEntryContent()); return; } // Rather than using the profiler's dynamic string, we compute our own string. // This is because we want to do some size-saving strategies, and throw out // information which won't help us as much. // XXX: We currently don't collect the function name which hung. const char* filename = JS_GetScriptFilename(aEntry.script()); unsigned lineno = JS_PCToLineNumber(aEntry.script(), aEntry.pc()); // Some script names are in the form "foo -> bar -> baz". // Here we find the origin of these redirected scripts. const char* basename = GetPathAfterComponent(filename, " -> "); if (basename) { filename = basename; } // Strip chrome:// or resource:// off of the filename if present. basename = GetFullPathForScheme(filename, "chrome://"); if (!basename) { basename = GetFullPathForScheme(filename, "resource://"); } if (!basename) { // If we're in an add-on script, under the {profile}/extensions // directory, extract the path after the /extensions/ part. basename = GetPathAfterComponent(filename, "/extensions/"); } if (!basename) { // Only keep the file base name for paths outside the above formats. basename = strrchr(filename, '/'); basename = basename ? basename + 1 : filename; // Look for Windows path separator as well. filename = strrchr(basename, '\\'); if (filename) { basename = filename + 1; } } char buffer[128]; // Enough to fit longest js file name from the tree size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno); if (len < sizeof(buffer)) { mDesiredBufferSize += len + 1; if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length() && (mStackToFill->strbuffer().Capacity() - mStackToFill->strbuffer().Length()) > len + 1) { // NOTE: We only increment this if we're going to successfully append. mDesiredStackSize += 1; uint32_t start = mStackToFill->strbuffer().Length(); mStackToFill->strbuffer().AppendElements(buffer, len); mStackToFill->strbuffer().AppendElement('\0'); mStackToFill->stack().AppendElement(HangEntryBufOffset(start)); return; } } TryAppendFrame(HangEntryChromeScript()); }
static JSDScript* _newJSDScript(JSDContext* jsdc, JSContext *cx, JSScript *script_) { JS::RootedScript script(cx, script_); if ( JS_GetScriptIsSelfHosted(script) ) return NULL; JSDScript* jsdscript; unsigned lineno; const char* raw_filename; JS_ASSERT(JSD_SCRIPTS_LOCKED(jsdc)); /* these are inlined javascript: urls and we can't handle them now */ lineno = (unsigned) JS_GetScriptBaseLineNumber(cx, script); if( lineno == 0 ) return NULL; jsdscript = (JSDScript*) calloc(1, sizeof(JSDScript)); if( ! jsdscript ) return NULL; raw_filename = JS_GetScriptFilename(cx,script); JS_HashTableAdd(jsdc->scriptsTable, (void *)script, (void *)jsdscript); JS_APPEND_LINK(&jsdscript->links, &jsdc->scripts); jsdscript->jsdc = jsdc; jsdscript->script = script; jsdscript->lineBase = lineno; jsdscript->lineExtent = (unsigned)NOT_SET_YET; jsdscript->data = NULL; #ifndef LIVEWIRE jsdscript->url = (char*) jsd_BuildNormalizedURL(raw_filename); #else jsdscript->app = LWDBG_GetCurrentApp(); if( jsdscript->app && raw_filename ) { jsdscript->url = jsdlw_BuildAppRelativeFilename(jsdscript->app, raw_filename); if( function ) { JSString* funid = JS_GetFunctionId(function); char* funbytes; const char* funnanme; if( fuinid ) { funbytes = JS_EncodeString(cx, funid); funname = funbytes ? funbytes : ""; } else { funbytes = NULL; funname = "anonymous"; } jsdscript->lwscript = LWDBG_GetScriptOfFunction(jsdscript->app,funname); JS_Free(cx, funbytes); /* also, make sure this file is added to filelist if is .js file */ if( HasFileExtention(raw_filename,"js") || HasFileExtention(raw_filename,"sjs") ) { jsdlw_PreLoadSource(jsdc, jsdscript->app, raw_filename, JS_FALSE); } } else { jsdscript->lwscript = LWDBG_GetCurrentTopLevelScript(); } } #endif JS_INIT_CLIST(&jsdscript->hooks); return jsdscript; }
static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, char* buf, int num, JSBool showArgs, JSBool showLocals, JSBool showThisProps) { JSPropertyDescArray callProps = {0, nsnull}; JSPropertyDescArray thisProps = {0, nsnull}; JSBool gotThisVal = JS_FALSE; jsval thisVal; JSObject* callObj = nsnull; JSString* funname = nsnull; JSAutoByteString funbytes; const char* filename = nsnull; PRInt32 lineno = 0; JSFunction* fun = nsnull; uint32 namedArgCount = 0; jsval val; JSBool isString; // get the info for this stack frame JSScript* script = JS_GetFrameScript(cx, fp); jsbytecode* pc = JS_GetFramePC(cx, fp); JSAutoRequest ar(cx); JSAutoEnterCompartment ac; if(!ac.enter(cx, JS_GetFrameScopeChain(cx, fp))) return buf; if(script && pc) { filename = JS_GetScriptFilename(cx, script); lineno = (PRInt32) JS_PCToLineNumber(cx, script, pc); fun = JS_GetFrameFunction(cx, fp); if(fun) funname = JS_GetFunctionId(fun); if(showArgs || showLocals) { callObj = JS_GetFrameCallObject(cx, fp); if(callObj) if(!JS_GetPropertyDescArray(cx, callObj, &callProps)) callProps.array = nsnull; // just to be sure } gotThisVal = JS_GetFrameThis(cx, fp, &thisVal); if (!gotThisVal || !showThisProps || JSVAL_IS_PRIMITIVE(thisVal) || !JS_GetPropertyDescArray(cx, JSVAL_TO_OBJECT(thisVal), &thisProps)) { thisProps.array = nsnull; // just to be sure } } // print the frame number and function name if(funname) buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encode(cx, funname)); else if(fun) buf = JS_sprintf_append(buf, "%d anonymous(", num); else buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); if(!buf) goto out; // print the function arguments if(showArgs && callObj) { for(uint32 i = 0; i < callProps.length; i++) { JSPropertyDesc* desc = &callProps.array[i]; if(desc->flags & JSPD_ARGUMENT) { JSAutoByteString nameBytes; const char* name = JSVAL2String(cx, desc->id, &isString, &nameBytes); if(!isString) name = nsnull; JSAutoByteString valueBytes; const char* value = JSVAL2String(cx, desc->value, &isString, &valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", namedArgCount ? ", " : "", name ? name :"", name ? " = " : "", isString ? "\"" : "", value ? value : "?unknown?", isString ? "\"" : ""); if(!buf) goto out; namedArgCount++; } } // print any unnamed trailing args (found in 'arguments' object) if(JS_GetProperty(cx, callObj, "arguments", &val) && JSVAL_IS_OBJECT(val)) { uint32 argCount; JSObject* argsObj = JSVAL_TO_OBJECT(val); if(JS_GetProperty(cx, argsObj, "length", &val) && JS_ValueToECMAUint32(cx, val, &argCount) && argCount > namedArgCount) { for(uint32 k = namedArgCount; k < argCount; k++) { char number[8]; JS_snprintf(number, 8, "%d", (int) k); if(JS_GetProperty(cx, argsObj, number, &val)) { JSAutoByteString valueBytes; const char *value = JSVAL2String(cx, val, &isString, &valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s", k ? ", " : "", isString ? "\"" : "", value ? value : "?unknown?", isString ? "\"" : ""); if(!buf) goto out; } } } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if(!buf) goto out; // print local variables if(showLocals && callProps.array) { for(uint32 i = 0; i < callProps.length; i++) { JSPropertyDesc* desc = &callProps.array[i]; if(desc->flags & JSPD_VARIABLE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = JSVAL2String(cx, desc->id, nsnull, &nameBytes); const char *value = JSVAL2String(cx, desc->value, &isString, &valueBytes); if(name && value) { buf = JS_sprintf_append(buf, TAB "%s = %s%s%s\n", name, isString ? "\"" : "", value, isString ? "\"" : ""); if(!buf) goto out; } } } } // print the value of 'this' if(showLocals) { if(gotThisVal) { JSString* thisValStr; JSAutoByteString thisValBytes; if(nsnull != (thisValStr = JS_ValueToString(cx, thisVal)) && thisValBytes.encode(cx, thisValStr)) { buf = JS_sprintf_append(buf, TAB "this = %s\n", thisValBytes.ptr()); if(!buf) goto out; } } else buf = JS_sprintf_append(buf, TAB "<failed to get 'this' value>\n"); } // print the properties of 'this', if it is an object if(showThisProps && thisProps.array) { for(uint32 i = 0; i < thisProps.length; i++) { JSPropertyDesc* desc = &thisProps.array[i]; if(desc->flags & JSPD_ENUMERATE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = JSVAL2String(cx, desc->id, nsnull, &nameBytes); const char *value = JSVAL2String(cx, desc->value, &isString, &valueBytes); if(name && value) { buf = JS_sprintf_append(buf, TAB "this.%s = %s%s%s\n", name, isString ? "\"" : "", value, isString ? "\"" : ""); if(!buf) goto out; } } } } out: if(callProps.array) JS_PutPropertyDescArray(cx, &callProps); if(thisProps.array) JS_PutPropertyDescArray(cx, &thisProps); return buf; }
nsresult WrapperPromiseCallback::Call(JSContext* aCx, JS::Handle<JS::Value> aValue) { JS::ExposeObjectToActiveJS(mGlobal); JS::ExposeValueToActiveJS(aValue); JSAutoCompartment ac(aCx, mGlobal); JS::Rooted<JS::Value> value(aCx, aValue); if (!JS_WrapValue(aCx, &value)) { NS_WARNING("Failed to wrap value into the right compartment."); return NS_ERROR_FAILURE; } ErrorResult rv; // PromiseReactionTask step 6 JS::Rooted<JS::Value> retValue(aCx); JSCompartment* compartment; if (mNextPromise) { compartment = mNextPromise->Compartment(); } else { MOZ_ASSERT(mNextPromiseObj); compartment = js::GetObjectCompartment(mNextPromiseObj); } mCallback->Call(value, &retValue, rv, "promise callback", CallbackObject::eRethrowExceptions, compartment); rv.WouldReportJSException(); // PromiseReactionTask step 7 if (rv.Failed()) { JS::Rooted<JS::Value> value(aCx); { // Scope for JSAutoCompartment // Convert the ErrorResult to a JS exception object that we can reject // ourselves with. This will be exactly the exception that would get // thrown from a binding method whose ErrorResult ended up with whatever // is on "rv" right now. Do this in the promise reflector compartment. Maybe<JSAutoCompartment> ac; if (mNextPromise) { ac.emplace(aCx, mNextPromise->GlobalJSObject()); } else { ac.emplace(aCx, mNextPromiseObj); } DebugOnly<bool> conversionResult = ToJSValue(aCx, rv, &value); MOZ_ASSERT(conversionResult); } if (mNextPromise) { mNextPromise->RejectInternal(aCx, value); } else { JS::Rooted<JS::Value> ignored(aCx); ErrorResult rejectRv; mRejectFunc->Call(value, &ignored, rejectRv); // This reported any JS exceptions; we just have a pointless exception on // there now. rejectRv.SuppressException(); } return NS_OK; } // If the return value is the same as the promise itself, throw TypeError. if (retValue.isObject()) { JS::Rooted<JSObject*> valueObj(aCx, &retValue.toObject()); valueObj = js::CheckedUnwrap(valueObj); JS::Rooted<JSObject*> nextPromiseObj(aCx); if (mNextPromise) { nextPromiseObj = mNextPromise->GetWrapper(); } else { MOZ_ASSERT(mNextPromiseObj); nextPromiseObj = mNextPromiseObj; } // XXXbz shouldn't this check be over in ResolveInternal anyway? if (valueObj == nextPromiseObj) { const char* fileName = nullptr; uint32_t lineNumber = 0; // Try to get some information about the callback to report a sane error, // but don't try too hard (only deals with scripted functions). JS::Rooted<JSObject*> unwrapped(aCx, js::CheckedUnwrap(mCallback->Callback())); if (unwrapped) { JSAutoCompartment ac(aCx, unwrapped); if (JS_ObjectIsFunction(aCx, unwrapped)) { JS::Rooted<JS::Value> asValue(aCx, JS::ObjectValue(*unwrapped)); JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, asValue)); MOZ_ASSERT(func); JSScript* script = JS_GetFunctionScript(aCx, func); if (script) { fileName = JS_GetScriptFilename(script); lineNumber = JS_GetScriptBaseLineNumber(aCx, script); } } } // We're back in aValue's compartment here. JS::Rooted<JSString*> fn(aCx, JS_NewStringCopyZ(aCx, fileName)); if (!fn) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted<JSString*> message(aCx, JS_NewStringCopyZ(aCx, "then() cannot return same Promise that it resolves.")); if (!message) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; } JS::Rooted<JS::Value> typeError(aCx); if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, fn, lineNumber, 0, nullptr, message, &typeError)) { // Out of memory. Promise will stay unresolved. JS_ClearPendingException(aCx); return NS_ERROR_OUT_OF_MEMORY; } if (mNextPromise) { mNextPromise->RejectInternal(aCx, typeError); } else { JS::Rooted<JS::Value> ignored(aCx); ErrorResult rejectRv; mRejectFunc->Call(typeError, &ignored, rejectRv); // This reported any JS exceptions; we just have a pointless exception // on there now. rejectRv.SuppressException(); } return NS_OK; } } // Otherwise, run resolver's resolve with value. if (!JS_WrapValue(aCx, &retValue)) { NS_WARNING("Failed to wrap value into the right compartment."); return NS_ERROR_FAILURE; } if (mNextPromise) { mNextPromise->ResolveInternal(aCx, retValue); } else { JS::Rooted<JS::Value> ignored(aCx); ErrorResult resolveRv; mResolveFunc->Call(retValue, &ignored, resolveRv); // This reported any JS exceptions; we just have a pointless exception // on there now. resolveRv.SuppressException(); } return NS_OK; }