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; }
static void* jshook_function(JSContext* cx, JSStackFrame* fp, JSBool before, JSBool* UNUSED(ok), void* closure) { if (!before) { g_Profiler.Stop(); return closure; } JSFunction* fn = JS_GetFrameFunction(cx, fp); if (!fn) { g_Profiler.StartScript("(function)"); return closure; } // Try to get the name of non-anonymous functions JSString* name = JS_GetFunctionId(fn); if (name) { char* chars = JS_EncodeString(cx, name); if (chars) { g_Profiler.StartScript(StringFlyweight(chars).get().c_str()); JS_free(cx, chars); return closure; } } // No name - compute from the location instead ScriptLocation loc = { cx, JS_GetFrameScript(cx, fp), JS_GetFramePC(cx, fp) }; g_Profiler.StartScript(LocFlyweight(loc).get().name.c_str()); return closure; }
/* * call-seq: * frame_pc(context, frame) * * Get the frame parse context */ static VALUE frame_pc(VALUE UNUSED(self), VALUE context, VALUE frame) { JSContext * js = NULL; JSStackFrame * fp = NULL; Data_Get_Struct(context, JSContext, js); Data_Get_Struct(frame, JSStackFrame, fp); return Data_Wrap_Struct(rb_cObject, NULL, NULL, JS_GetFramePC(js, fp)); }
/* 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); }
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; }
JSDThreadState* jsd_NewThreadState(JSDContext* jsdc, JSContext *cx ) { JSDThreadState* jsdthreadstate; JSStackFrame * iter = NULL; JSStackFrame * fp; jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState)); if( ! jsdthreadstate ) return NULL; jsdthreadstate->context = cx; jsdthreadstate->thread = JSD_CURRENT_THREAD(); JS_INIT_CLIST(&jsdthreadstate->stack); jsdthreadstate->stackDepth = 0; JS_BeginRequest(jsdthreadstate->context); while( NULL != (fp = JS_FrameIterator(cx, &iter)) ) { JSScript* script = JS_GetFrameScript(cx, fp); jsuword pc = (jsuword) JS_GetFramePC(cx, fp); /* * don't construct a JSDStackFrame for dummy frames (those without a * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES * isn't set. */ if (JS_GetFrameThis(cx, fp) && ((jsdc->flags & JSD_INCLUDE_NATIVE_FRAMES) || !JS_IsNativeFrame(cx, fp))) { JSDStackFrameInfo *frame; frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp ); if ((jsdthreadstate->stackDepth == 0 && !frame) || (jsdthreadstate->stackDepth == 1 && frame && frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript))) { /* * if we failed to create the first frame, or the top frame * is not enabled for debugging, fail the entire thread state. */ JS_INIT_CLIST(&jsdthreadstate->links); JS_EndRequest(jsdthreadstate->context); jsd_DestroyThreadState(jsdc, jsdthreadstate); return NULL; } } } JS_EndRequest(jsdthreadstate->context); if (jsdthreadstate->stackDepth == 0) { free(jsdthreadstate); return NULL; } JSD_LOCK_THREADSTATES(jsdc); JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates); JSD_UNLOCK_THREADSTATES(jsdc); return jsdthreadstate; }
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; }
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); }