Example #1
0
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;
}
Example #2
0
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);
  }
}
Example #4
0
/*
 * 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));
}
Example #5
0
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);
}
Example #6
0
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;
}
Example #7
0
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;
}
Example #8
0
File: gerror.c Project: Cobinja/cjs
/* 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);
}
Example #9
0
		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();
		}
Example #10
0
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;
}
Example #11
0
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);
}
Example #12
0
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);
}
Example #13
0
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;
}
Example #14
0
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;
}
Example #15
0
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;
}
Example #17
0
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);
}
Example #18
0
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);
}
Example #20
0
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());
}
Example #21
0
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;
}           
Example #22
0
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;
}