mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 1177488 - use |const char*| for representing async call reasons; r=bz,fitzgen
Using a simple |const char*| is more memory-efficient than allocating a JS string. We still have to allocate the JS string for passing things into JS, but ideally we will be able to move the point of allocation much closer to where it's actually needed, rather than indiscriminantly doing it all the time.
This commit is contained in:
parent
16d14681ea
commit
cccdd9fbca
@ -14108,13 +14108,14 @@ nsDocShell::GetOpener()
|
||||
return opener;
|
||||
}
|
||||
|
||||
// The caller owns |aAsyncCause| here.
|
||||
void
|
||||
nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
|
||||
const char16_t* aFunctionName,
|
||||
const char16_t* aFilename,
|
||||
const uint32_t aLineNumber,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JS::Value> aAsyncCause)
|
||||
const char* aAsyncCause)
|
||||
{
|
||||
// If first start, mark interval start.
|
||||
if (mJSRunToCompletionDepth == 0) {
|
||||
|
@ -1059,7 +1059,7 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
in wstring fileName,
|
||||
in unsigned long lineNumber,
|
||||
in jsval asyncStack,
|
||||
in jsval asyncCause);
|
||||
in string asyncCause);
|
||||
[noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStop();
|
||||
|
||||
/**
|
||||
|
@ -17,23 +17,25 @@ namespace mozilla {
|
||||
class JavascriptTimelineMarker : public TimelineMarker
|
||||
{
|
||||
public:
|
||||
// The caller owns |aAsyncCause| here, so we must copy it into a separate
|
||||
// string for use later on.
|
||||
JavascriptTimelineMarker(const char* aReason,
|
||||
const char16_t* aFunctionName,
|
||||
const char16_t* aFileName,
|
||||
uint32_t aLineNumber,
|
||||
MarkerTracingType aTracingType,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JS::Value> aAsyncCause)
|
||||
const char* aAsyncCause)
|
||||
: TimelineMarker("Javascript", aTracingType, MarkerStackRequest::NO_STACK)
|
||||
, mCause(NS_ConvertUTF8toUTF16(aReason))
|
||||
, mFunctionName(aFunctionName)
|
||||
, mFileName(aFileName)
|
||||
, mLineNumber(aLineNumber)
|
||||
, mAsyncCause(aAsyncCause)
|
||||
{
|
||||
JSContext* ctx = nsContentUtils::GetCurrentJSContext();
|
||||
if (ctx) {
|
||||
mAsyncStack.init(ctx, aAsyncStack);
|
||||
mAsyncCause.init(ctx, aAsyncCause);
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,10 +52,16 @@ public:
|
||||
stackFrame.mFunctionDisplayName.Construct(mFunctionName);
|
||||
|
||||
if (mAsyncStack.isObject() && !mAsyncStack.isNullOrUndefined() &&
|
||||
mAsyncCause.isString()) {
|
||||
!mAsyncCause.IsEmpty()) {
|
||||
JS::Rooted<JSObject*> asyncStack(aCx, mAsyncStack.toObjectOrNull());
|
||||
JS::Rooted<JSString*> asyncCause(aCx, mAsyncCause.toString());
|
||||
JS::Rooted<JSObject*> parentFrame(aCx);
|
||||
JS::Rooted<JSString*> asyncCause(aCx, JS_NewUCStringCopyN(aCx, mAsyncCause.BeginReading(),
|
||||
mAsyncCause.Length()));
|
||||
if (!asyncCause) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JS::CopyAsyncStack(aCx, asyncStack, asyncCause, &parentFrame, 0)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
} else {
|
||||
@ -78,7 +86,7 @@ private:
|
||||
nsString mFileName;
|
||||
uint32_t mLineNumber;
|
||||
JS::PersistentRooted<JS::Value> mAsyncStack;
|
||||
JS::PersistentRooted<JS::Value> mAsyncCause;
|
||||
NS_ConvertUTF8toUTF16 mAsyncCause;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -683,7 +683,7 @@ AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
|
||||
void
|
||||
AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFunction,
|
||||
JSScript* aScript, JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause)
|
||||
const char* aAsyncCause)
|
||||
{
|
||||
JS::Rooted<JSFunction*> rootedFunction(aCx);
|
||||
if (aFunction) {
|
||||
@ -728,13 +728,11 @@ AutoEntryScript::DocshellEntryMonitor::Entry(JSContext* aCx, JSFunction* aFuncti
|
||||
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);
|
||||
aAsyncCause);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,16 +356,21 @@ private:
|
||||
public:
|
||||
DocshellEntryMonitor(JSContext* aCx, const char* aReason);
|
||||
|
||||
// Please note that |aAsyncCause| here is owned by the caller, and its
|
||||
// lifetime must outlive the lifetime of the DocshellEntryMonitor object.
|
||||
// In practice, |aAsyncCause| is identical to |aReason| passed into
|
||||
// the AutoEntryScript constructor, so the lifetime requirements are
|
||||
// trivially satisfied by |aReason| being a statically allocated string.
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause) override
|
||||
const char* aAsyncCause) override
|
||||
{
|
||||
Entry(aCx, aFunction, nullptr, aAsyncStack, aAsyncCause);
|
||||
}
|
||||
|
||||
void Entry(JSContext* aCx, JSScript* aScript,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause) override
|
||||
const char* aAsyncCause) override
|
||||
{
|
||||
Entry(aCx, nullptr, aScript, aAsyncStack, aAsyncCause);
|
||||
}
|
||||
@ -375,7 +380,7 @@ private:
|
||||
private:
|
||||
void Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript,
|
||||
JS::Handle<JS::Value> aAsyncStack,
|
||||
JS::Handle<JSString*> aAsyncCause);
|
||||
const char* aAsyncCause);
|
||||
|
||||
const char* mReason;
|
||||
};
|
||||
|
@ -172,12 +172,7 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
|
||||
|
||||
mAsyncStack.emplace(cx, aCallback->GetCreationStack());
|
||||
if (*mAsyncStack) {
|
||||
mAsyncCause.emplace(cx, JS_NewStringCopyZ(cx, aExecutionReason));
|
||||
if (*mAsyncCause) {
|
||||
mAsyncStackSetter.emplace(cx, *mAsyncStack, *mAsyncCause);
|
||||
} else {
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
mAsyncStackSetter.emplace(cx, *mAsyncStack, aExecutionReason);
|
||||
}
|
||||
|
||||
// Enter the compartment of our callback, so we can actually work with it.
|
||||
|
@ -257,7 +257,6 @@ protected:
|
||||
|
||||
// Members which are used to set the async stack.
|
||||
Maybe<JS::Rooted<JSObject*>> mAsyncStack;
|
||||
Maybe<JS::Rooted<JSString*>> mAsyncCause;
|
||||
Maybe<JS::AutoSetAsyncStackForNewCalls> mAsyncStackSetter;
|
||||
|
||||
// Can't construct a JSAutoCompartment without a JSContext either. Also,
|
||||
|
@ -96,16 +96,11 @@ protected:
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> asyncStack(cx, mPromise->mAllocationStack);
|
||||
JS::Rooted<JSString*> asyncCause(cx, JS_NewStringCopyZ(cx, "Promise"));
|
||||
if (!asyncCause) {
|
||||
JS_ClearPendingException(cx);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
{
|
||||
Maybe<JS::AutoSetAsyncStackForNewCalls> sas;
|
||||
if (asyncStack) {
|
||||
sas.emplace(cx, asyncStack, asyncCause);
|
||||
sas.emplace(cx, asyncStack, "Promise");
|
||||
}
|
||||
mCallback->Call(cx, value);
|
||||
}
|
||||
|
@ -351,20 +351,25 @@ class MOZ_STACK_CLASS AutoEntryMonitor {
|
||||
// SpiderMonkey reports the JavaScript entry points occuring within this
|
||||
// AutoEntryMonitor's scope to the following member functions, which the
|
||||
// embedding is expected to override.
|
||||
//
|
||||
// It is important to note that |asyncCause| is owned by the caller and its
|
||||
// lifetime must outlive the lifetime of the AutoEntryMonitor object. It is
|
||||
// strongly encouraged that |asyncCause| be a string constant or similar
|
||||
// statically allocated string.
|
||||
|
||||
// We have begun executing |function|. Note that |function| may not be the
|
||||
// actual closure we are running, but only the canonical function object to
|
||||
// which the script refers.
|
||||
virtual void Entry(JSContext* cx, JSFunction* function,
|
||||
HandleValue asyncStack,
|
||||
HandleString asyncCause) = 0;
|
||||
const char* asyncCause) = 0;
|
||||
|
||||
// Execution has begun at the entry point of |script|, which is not a
|
||||
// function body. (This is probably being executed by 'eval' or some
|
||||
// JSAPI equivalent.)
|
||||
virtual void Entry(JSContext* cx, JSScript* script,
|
||||
HandleValue asyncStack,
|
||||
HandleString asyncCause) = 0;
|
||||
const char* asyncCause) = 0;
|
||||
|
||||
// Execution of the function or script has ended.
|
||||
virtual void Exit(JSContext* cx) { }
|
||||
|
@ -1122,8 +1122,13 @@ CallFunctionWithAsyncStack(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedObject function(cx, &args[0].toObject());
|
||||
RootedObject stack(cx, &args[1].toObject());
|
||||
RootedString asyncCause(cx, args[2].toString());
|
||||
JSAutoByteString utf8Cause;
|
||||
if (!utf8Cause.encodeUtf8(cx, asyncCause)) {
|
||||
MOZ_ASSERT(cx->isExceptionPending());
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::AutoSetAsyncStackForNewCalls sas(cx, stack, asyncCause,
|
||||
JS::AutoSetAsyncStackForNewCalls sas(cx, stack, utf8Cause.ptr(),
|
||||
JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
|
||||
return Call(cx, UndefinedHandleValue, function,
|
||||
JS::HandleValueArray::empty(), args.rval());
|
||||
|
@ -4865,11 +4865,11 @@ JS_RestoreFrameChain(JSContext* cx)
|
||||
}
|
||||
|
||||
JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
|
||||
JSContext* cx, HandleObject stack, HandleString asyncCause,
|
||||
JSContext* cx, HandleObject stack, const char* asyncCause,
|
||||
JS::AutoSetAsyncStackForNewCalls::AsyncCallKind kind)
|
||||
: cx(cx),
|
||||
oldAsyncStack(cx, cx->runtime()->asyncStackForNewActivations),
|
||||
oldAsyncCause(cx, cx->runtime()->asyncCauseForNewActivations),
|
||||
oldAsyncCause(cx->runtime()->asyncCauseForNewActivations),
|
||||
oldAsyncCallIsExplicit(cx->runtime()->asyncCallIsExplicit)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
@ -4881,7 +4881,6 @@ JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
|
||||
return;
|
||||
|
||||
SavedFrame* asyncStack = &stack->as<SavedFrame>();
|
||||
MOZ_ASSERT(!asyncCause->empty());
|
||||
|
||||
cx->runtime()->asyncStackForNewActivations = asyncStack;
|
||||
cx->runtime()->asyncCauseForNewActivations = asyncCause;
|
||||
|
@ -4479,7 +4479,7 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(AutoSetAsyncStackForNewCalls)
|
||||
{
|
||||
JSContext* cx;
|
||||
RootedObject oldAsyncStack;
|
||||
RootedString oldAsyncCause;
|
||||
const char* oldAsyncCause;
|
||||
bool oldAsyncCallIsExplicit;
|
||||
|
||||
public:
|
||||
@ -4496,8 +4496,13 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(AutoSetAsyncStackForNewCalls)
|
||||
// ambiguous whether that would clear any scheduled async stack and make the
|
||||
// normal stack reappear in the new call, or just keep the async stack
|
||||
// already scheduled for the new call, if any.
|
||||
//
|
||||
// asyncCause is owned by the caller and its lifetime must outlive the
|
||||
// lifetime of the AutoSetAsyncStackForNewCalls object. It is strongly
|
||||
// encouraged that asyncCause be a string constant or similar statically
|
||||
// allocated string.
|
||||
AutoSetAsyncStackForNewCalls(JSContext* cx, HandleObject stack,
|
||||
HandleString asyncCause,
|
||||
const char* asyncCause,
|
||||
AsyncCallKind kind = AsyncCallKind::IMPLICIT);
|
||||
~AutoSetAsyncStackForNewCalls();
|
||||
};
|
||||
|
@ -4959,7 +4959,7 @@ class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
|
||||
}
|
||||
|
||||
void Entry(JSContext* cx, JSFunction* function, JS::HandleValue asyncStack,
|
||||
JS::HandleString asyncCause) override {
|
||||
const char* asyncCause) override {
|
||||
MOZ_ASSERT(!enteredWithoutExit);
|
||||
enteredWithoutExit = true;
|
||||
|
||||
@ -4974,7 +4974,7 @@ class ShellAutoEntryMonitor : JS::dbg::AutoEntryMonitor {
|
||||
}
|
||||
|
||||
void Entry(JSContext* cx, JSScript* script, JS::HandleValue asyncStack,
|
||||
JS::HandleString asyncCause) override {
|
||||
const char* asyncCause) override {
|
||||
MOZ_ASSERT(!enteredWithoutExit);
|
||||
enteredWithoutExit = true;
|
||||
|
||||
|
@ -138,7 +138,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
||||
profilerSampleBufferLapCount_(1),
|
||||
wasmActivationStack_(nullptr),
|
||||
asyncStackForNewActivations(this),
|
||||
asyncCauseForNewActivations(this),
|
||||
asyncCauseForNewActivations(nullptr),
|
||||
asyncCallIsExplicit(false),
|
||||
entryMonitor(nullptr),
|
||||
noExecuteDebuggerTop(nullptr),
|
||||
|
@ -708,7 +708,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
/*
|
||||
* Value of asyncCause to be attached to asyncStackForNewActivations.
|
||||
*/
|
||||
JS::PersistentRooted<JSString*> asyncCauseForNewActivations;
|
||||
const char* asyncCauseForNewActivations;
|
||||
|
||||
/*
|
||||
* True if the async call was explicitly requested, e.g. via
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "gc/Marking.h"
|
||||
#include "gc/Policy.h"
|
||||
#include "gc/Rooting.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/Vector.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/SavedFrame.h"
|
||||
@ -1164,7 +1165,23 @@ SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFram
|
||||
// youngest frame of the async stack as the parent of the oldest
|
||||
// frame of this activation. We still need to iterate over other
|
||||
// frames in this activation before reaching the oldest frame.
|
||||
asyncCause = activation.asyncCause();
|
||||
AutoCompartment ac(cx, iter.compartment());
|
||||
const char* cause = activation.asyncCause();
|
||||
UTF8Chars utf8Chars(cause, strlen(cause));
|
||||
size_t twoByteCharsLen = 0;
|
||||
char16_t* twoByteChars = UTF8CharsToNewTwoByteCharsZ(cx, utf8Chars,
|
||||
&twoByteCharsLen).get();
|
||||
if (!twoByteChars)
|
||||
return false;
|
||||
|
||||
// We expect that there will be a relatively small set of
|
||||
// asyncCause reasons ("setTimeout", "promise", etc.), so we
|
||||
// atomize the cause here in hopes of being able to benefit
|
||||
// from reuse.
|
||||
asyncCause = JS_AtomizeUCStringN(cx, twoByteChars, twoByteCharsLen);
|
||||
js_free(twoByteChars);
|
||||
if (!asyncCause)
|
||||
return false;
|
||||
asyncActivation = &activation;
|
||||
}
|
||||
}
|
||||
|
@ -873,7 +873,7 @@ Activation::Activation(JSContext* cx, Kind kind)
|
||||
hideScriptedCallerCount_(0),
|
||||
frameCache_(cx),
|
||||
asyncStack_(cx, cx->runtime_->asyncStackForNewActivations),
|
||||
asyncCause_(cx, cx->runtime_->asyncCauseForNewActivations),
|
||||
asyncCause_(cx->runtime_->asyncCauseForNewActivations),
|
||||
asyncCallIsExplicit_(cx->runtime_->asyncCallIsExplicit),
|
||||
kind_(kind)
|
||||
{
|
||||
|
@ -1401,7 +1401,7 @@ ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, InterpreterFrame*
|
||||
// be traced if we trigger GC here. Suppress GC to avoid this.
|
||||
gc::AutoSuppressGC suppressGC(cx);
|
||||
RootedValue stack(cx, asyncStack(cx));
|
||||
RootedString asyncCause(cx, cx->runtime()->asyncCauseForNewActivations);
|
||||
const char* asyncCause = cx->runtime()->asyncCauseForNewActivations;
|
||||
if (entryFrame->isFunctionFrame())
|
||||
entryMonitor_->Entry(cx, &entryFrame->callee(), stack, asyncCause);
|
||||
else
|
||||
@ -1417,7 +1417,7 @@ ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, jit::CalleeToken e
|
||||
// a GC to discard the code we're about to enter, so we suppress GC.
|
||||
gc::AutoSuppressGC suppressGC(cx);
|
||||
RootedValue stack(cx, asyncStack(cx));
|
||||
RootedString asyncCause(cx, cx->runtime()->asyncCauseForNewActivations);
|
||||
const char* asyncCause = cx->runtime()->asyncCauseForNewActivations;
|
||||
if (jit::CalleeTokenIsFunction(entryToken))
|
||||
entryMonitor_->Entry(cx_, jit::CalleeTokenToFunction(entryToken), stack, asyncCause);
|
||||
else
|
||||
|
@ -1237,7 +1237,7 @@ class Activation
|
||||
Rooted<SavedFrame*> asyncStack_;
|
||||
|
||||
// Value of asyncCause to be attached to asyncStack_.
|
||||
RootedString asyncCause_;
|
||||
const char* asyncCause_;
|
||||
|
||||
// True if the async call was explicitly requested, e.g. via
|
||||
// callFunctionWithAsyncStack.
|
||||
@ -1319,7 +1319,7 @@ class Activation
|
||||
return asyncStack_;
|
||||
}
|
||||
|
||||
JSString* asyncCause() {
|
||||
const char* asyncCause() const {
|
||||
return asyncCause_;
|
||||
}
|
||||
|
||||
|
@ -2712,12 +2712,9 @@ nsXPCComponents_Utils::CallFunctionWithAsyncStack(HandleValue function,
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> asyncStackObj(cx, &asyncStack.toObject());
|
||||
JS::Rooted<JSString*> asyncCauseString(cx, JS_NewUCStringCopyN(cx, asyncCause.BeginReading(),
|
||||
asyncCause.Length()));
|
||||
if (!asyncCauseString)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
JS::AutoSetAsyncStackForNewCalls sas(cx, asyncStackObj, asyncCauseString,
|
||||
NS_ConvertUTF16toUTF8 utf8Cause(asyncCause);
|
||||
JS::AutoSetAsyncStackForNewCalls sas(cx, asyncStackObj, utf8Cause.get(),
|
||||
JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
|
||||
|
||||
if (!JS_CallFunctionValue(cx, nullptr, function,
|
||||
|
Loading…
Reference in New Issue
Block a user