mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1149486 - Regroup PerformanceStats by window. r=jandem, r=bholley (bedbaab6b) - fix build.... we have a nwer GetScriptableTop() now... (d4c5b9833) (properly fixed by me) - Bug 1167308 - Add a simpler to use CallTyped for the many simple pre-cast cases; r=jonco (73124e936) - Bug 1167313 - Simplify UbiNode::construct dispatch using CallTyped; r=jimb (ce8e33fdd) - Bug 1167314 - Use CallTyped to simplify PushArena; r=jonco (4e07c7a96) - Bug 1167309 - Remove MaybeCompartment using CallTyped; r=jonco (b85699798) - missing bits of bug 1156708: Part2. Add pref to toggle new code. (122a90f03) - Bug 1164777 - Part 1: move evalStaticScope to GlobalSharedContext. (r=shu) (b38066072) - Bug 1164777 - Part 2: Make super.prop parse inside inside eval inside arrow functions. (r=shu) (f7a2fc1fa) - Bug 1141865 - Part 5: Implement new.target inside eval. (r=jorendorff, r=jandem) (4e558cb93) - Bug 1141865 - Part 6: Implement new.target in arrow functions. (r=jandem, r=jorendorff) (f7694ad0b) - Bug 1156264 - Activate/deactivate jank and CPOW monitoring separately (low-level). r=jandem (3837061f6) - Bug 1150259 - Deactivating subtest under old Windows/old Linux. r=yoric (128dc82d4) - Bug 1150514 - in about:performance, fixing CSS of jank levels. r=Yoric (136e06827) - Bug 1150548 - MISBEHAVING_ADDONS_CPOW_TIME_MS is in milliseconds, but is filled with microseconds data. r=yoric (b477de514) - Bug 1155355, e10s, rewrite and reenable browser tests in layout/xul/t#est, don't show tooltips during a drag, use system event listeners for tooltips, r=billm (858570301) (partly) - Bug 1142814 (attempt 2) - Optimize String.fromCharCode() when the number of args is small but > 1. r=jandem. (7c03f649e) and partly import changes from mozilla in order to fix build: - Bug 1149486 - Extract a window title and window ID for PerformanceStats. r=mossop - Bug 1156264 - Activate/deactivate jank and CPOW monitoring separately (high-level). r=mossop
This commit is contained in:
@@ -1466,19 +1466,11 @@ void MediaFormatReader::ReleaseMediaResources()
|
||||
}
|
||||
}
|
||||
|
||||
void MediaFormatReader::NotifyResourcesStatusChanged()
|
||||
{
|
||||
if (mDecoder) {
|
||||
mDecoder->NotifyWaitingForResourcesStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaFormatReader::SetIdle()
|
||||
{
|
||||
if (mSharedDecoderManager && mVideo.mDecoder) {
|
||||
mSharedDecoderManager->SetIdle(mVideo.mDecoder);
|
||||
NotifyResourcesStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,6 @@ private:
|
||||
void DrainComplete(TrackType aTrack);
|
||||
bool IsSupportedAudioMimeType(const nsACString& aMimeType);
|
||||
bool IsSupportedVideoMimeType(const nsACString& aMimeType);
|
||||
void NotifyResourcesStatusChanged();
|
||||
|
||||
bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
|
||||
|
||||
@@ -183,9 +182,6 @@ private:
|
||||
virtual void DrainComplete() override {
|
||||
mReader->DrainComplete(mType);
|
||||
}
|
||||
virtual void NotifyResourcesStatusChanged() override {
|
||||
mReader->NotifyResourcesStatusChanged();
|
||||
}
|
||||
virtual void ReleaseMediaResources() override {
|
||||
mReader->ReleaseMediaResources();
|
||||
}
|
||||
|
||||
@@ -1174,17 +1174,17 @@ NS_IMETHODIMP
|
||||
ChromeTooltipListener::AddTooltipListener()
|
||||
{
|
||||
if (mEventTarget) {
|
||||
nsresult rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("keydown"),
|
||||
this, false, false);
|
||||
nsresult rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
|
||||
this, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), this,
|
||||
false, false);
|
||||
rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), this,
|
||||
false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mouseout"), this,
|
||||
false, false);
|
||||
rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), this,
|
||||
false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mEventTarget->AddEventListener(NS_LITERAL_STRING("mousemove"), this,
|
||||
false, false);
|
||||
rv = mEventTarget->AddSystemEventListener(NS_LITERAL_STRING("mousemove"), this,
|
||||
false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mTooltipListenerInstalled = true;
|
||||
@@ -1226,17 +1226,17 @@ ChromeTooltipListener::RemoveTooltipListener()
|
||||
{
|
||||
if (mEventTarget) {
|
||||
nsresult rv =
|
||||
mEventTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), this,
|
||||
false);
|
||||
mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), this,
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"),
|
||||
this, false);
|
||||
rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"),
|
||||
this, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this,
|
||||
rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), this,
|
||||
false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
|
||||
this, false);
|
||||
rv = mEventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousemove"),
|
||||
this, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mTooltipListenerInstalled = false;
|
||||
|
||||
+19
-5
@@ -9,17 +9,31 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "CPOWTimer.h"
|
||||
|
||||
CPOWTimer::CPOWTimer(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
||||
: cx_(nullptr)
|
||||
, startInterval_(0)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
JSRuntime* runtime = JS_GetRuntime(cx);
|
||||
if (!js::GetStopwatchIsMonitoringCPOW(runtime))
|
||||
return;
|
||||
cx_ = cx;
|
||||
startInterval_ = PR_IntervalNow();
|
||||
}
|
||||
CPOWTimer::~CPOWTimer()
|
||||
{
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
|
||||
if (!cx)
|
||||
if (!cx_) {
|
||||
// Monitoring was off when we started the timer.
|
||||
return;
|
||||
}
|
||||
|
||||
JSRuntime* runtime = JS_GetRuntime(cx);
|
||||
if (!js::IsStopwatchActive(runtime))
|
||||
JSRuntime* runtime = JS_GetRuntime(cx_);
|
||||
if (!js::GetStopwatchIsMonitoringCPOW(runtime)) {
|
||||
// Monitoring has been deactivated while we were in the timer.
|
||||
return;
|
||||
}
|
||||
|
||||
js::PerformanceData* performance = js::GetPerformanceData(runtime);
|
||||
uint64_t duration = PR_IntervalToMicroseconds(PR_IntervalNow() - startInterval);
|
||||
uint64_t duration = PR_IntervalToMicroseconds(PR_IntervalNow() - startInterval_);
|
||||
performance->totalCPOWTime += duration;
|
||||
}
|
||||
|
||||
+11
-7
@@ -9,6 +9,7 @@
|
||||
#define CPOWTIMER_H
|
||||
|
||||
#include "prinrval.h"
|
||||
#include "jsapi.h"
|
||||
|
||||
/**
|
||||
* A stopwatch measuring the duration of a CPOW call.
|
||||
@@ -21,20 +22,23 @@
|
||||
*/
|
||||
class MOZ_STACK_CLASS CPOWTimer final {
|
||||
public:
|
||||
explicit inline CPOWTimer(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
|
||||
: startInterval(PR_IntervalNow())
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
explicit inline CPOWTimer(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
|
||||
~CPOWTimer();
|
||||
|
||||
private:
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
|
||||
/**
|
||||
* The instant at which the stopwatch was started.
|
||||
* The context in which this timer was created, or `nullptr` if
|
||||
* CPOW monitoring was off when the timer was created.
|
||||
*/
|
||||
PRIntervalTime startInterval;
|
||||
JSContext* cx_;
|
||||
|
||||
/**
|
||||
* The instant at which the stopwatch was started. Undefined
|
||||
* if CPOW monitoring was off when the timer was created.
|
||||
*/
|
||||
PRIntervalTime startInterval_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -146,7 +146,7 @@ const CPOWProxyHandler CPOWProxyHandler::singleton;
|
||||
return false; \
|
||||
} \
|
||||
{ \
|
||||
CPOWTimer timer; \
|
||||
CPOWTimer timer(cx); \
|
||||
return owner->call args; \
|
||||
}
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ class Node {
|
||||
"ubi::Base specializations must be the same size as ubi::Base");
|
||||
Concrete<T>::construct(base(), ptr);
|
||||
}
|
||||
struct ConstructFunctor;
|
||||
|
||||
public:
|
||||
Node() { construct<void>(nullptr); }
|
||||
|
||||
@@ -361,15 +361,17 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
||||
esg.setNewScript(compiled);
|
||||
}
|
||||
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
|
||||
// Look up the newTarget from the frame iterator.
|
||||
Value newTargetVal = NullValue();
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, newTargetVal, ExecuteType(evalType),
|
||||
NullFramePtr() /* evalInFrame */, args.rval().address());
|
||||
}
|
||||
|
||||
bool
|
||||
js::DirectEvalStringFromIon(JSContext* cx,
|
||||
HandleObject scopeobj, HandleScript callerScript,
|
||||
HandleValue thisValue, HandleString str,
|
||||
jsbytecode* pc, MutableHandleValue vp)
|
||||
HandleValue thisValue, HandleValue newTargetValue,
|
||||
HandleString str, jsbytecode* pc, MutableHandleValue vp)
|
||||
{
|
||||
AssertInnerizedScopeChain(cx, *scopeobj);
|
||||
|
||||
@@ -461,8 +463,8 @@ js::DirectEvalStringFromIon(JSContext* cx,
|
||||
nthisValue = ObjectValue(*obj);
|
||||
}
|
||||
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, ExecuteType(DIRECT_EVAL),
|
||||
NullFramePtr() /* evalInFrame */, vp.address());
|
||||
return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, newTargetValue,
|
||||
ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address());
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -534,7 +536,7 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
|
||||
RootedValue rval(cx);
|
||||
// XXXbz when this is fixed to pass in an actual ScopeObject, fix
|
||||
// up the assert in js::CloneFunctionObject accordingly.
|
||||
if (!ExecuteKernel(cx, script, *scope, thisv, EXECUTE_GLOBAL,
|
||||
if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
|
||||
NullFramePtr() /* evalInFrame */, rval.address()))
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -30,8 +30,8 @@ DirectEval(JSContext* cx, const CallArgs& args);
|
||||
extern bool
|
||||
DirectEvalStringFromIon(JSContext* cx,
|
||||
HandleObject scopeObj, HandleScript callerScript,
|
||||
HandleValue thisValue, HandleString str,
|
||||
jsbytecode * pc, MutableHandleValue vp);
|
||||
HandleValue thisValue, HandleValue newTargetValue,
|
||||
HandleString str, jsbytecode * pc, MutableHandleValue vp);
|
||||
|
||||
// True iff fun is a built-in eval function.
|
||||
extern bool
|
||||
|
||||
@@ -269,11 +269,8 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
|
||||
return nullptr;
|
||||
|
||||
bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
|
||||
bool allowSuperProperty = savedCallerFun &&
|
||||
evalCaller->functionOrCallerFunction()->allowSuperProperty();
|
||||
|
||||
Directives directives(options.strictOption);
|
||||
GlobalSharedContext globalsc(cx, directives, options.extraWarningsOption, allowSuperProperty);
|
||||
GlobalSharedContext globalsc(cx, directives, evalStaticScope, options.extraWarningsOption);
|
||||
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
|
||||
options, staticLevel, sourceObject, 0,
|
||||
@@ -287,7 +284,7 @@ frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject sco
|
||||
options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
|
||||
BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script,
|
||||
/* lazyScript = */ nullptr, options.forEval,
|
||||
evalCaller, evalStaticScope, insideNonGlobalEval,
|
||||
evalCaller, insideNonGlobalEval,
|
||||
options.lineno, emitterMode);
|
||||
if (!bce.init())
|
||||
return nullptr;
|
||||
@@ -510,7 +507,6 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
||||
MOZ_ASSERT(!options.forEval);
|
||||
BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
|
||||
/* insideEval = */ false, /* evalCaller = */ nullptr,
|
||||
/* evalStaticScope = */ nullptr,
|
||||
/* insideNonGlobalEval = */ false, options.lineno,
|
||||
BytecodeEmitter::LazyFunction);
|
||||
if (!bce.init())
|
||||
@@ -638,7 +634,6 @@ CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyComp
|
||||
BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script,
|
||||
/* lazyScript = */ nullptr, /* insideEval = */ false,
|
||||
/* evalCaller = */ nullptr,
|
||||
/* evalStaticScope = */ nullptr,
|
||||
/* insideNonGlobalEval = */ false, options.lineno);
|
||||
if (!funbce.init())
|
||||
return false;
|
||||
|
||||
@@ -105,7 +105,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||
Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||
HandleScript script, Handle<LazyScript*> lazyScript,
|
||||
bool insideEval, HandleScript evalCaller,
|
||||
Handle<StaticEvalObject*> staticEvalScope,
|
||||
bool insideNonGlobalEval, uint32_t lineNum,
|
||||
EmitterMode emitterMode)
|
||||
: sc(sc),
|
||||
@@ -118,7 +117,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||
current(&main),
|
||||
parser(parser),
|
||||
evalCaller(evalCaller),
|
||||
evalStaticScope(staticEvalScope),
|
||||
topStmt(nullptr),
|
||||
topScopeStmt(nullptr),
|
||||
staticScope(cx),
|
||||
@@ -143,8 +141,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
||||
{
|
||||
MOZ_ASSERT_IF(evalCaller, insideEval);
|
||||
MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
|
||||
// Function scripts are never eval scripts.
|
||||
MOZ_ASSERT_IF(evalStaticScope, !sc->isFunctionBox());
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -773,7 +769,7 @@ BytecodeEmitter::enclosingStaticScope()
|
||||
|
||||
// Top-level eval scripts have a placeholder static scope so that
|
||||
// StaticScopeIter may iterate through evals.
|
||||
return evalStaticScope;
|
||||
return sc->asGlobalSharedContext()->evalStaticScope();
|
||||
}
|
||||
|
||||
return sc->asFunctionBox()->function();
|
||||
@@ -5416,6 +5412,19 @@ BytecodeEmitter::emitFor(ParseNode* pn, ptrdiff_t top)
|
||||
return emitNormalFor(pn, top);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::arrowNeedsNewTarget()
|
||||
{
|
||||
for (BytecodeEmitter* bce = this; bce; bce = bce->parent) {
|
||||
SharedContext *sc = bce->sc;
|
||||
if (sc->isFunctionBox() && sc->asFunctionBox()->function()->isArrow())
|
||||
continue;
|
||||
|
||||
return sc->allowSyntax(SharedContext::AllowedSyntax::NewTarget);
|
||||
}
|
||||
MOZ_CRASH("impossible parent chain");
|
||||
}
|
||||
|
||||
MOZ_NEVER_INLINE bool
|
||||
BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
{
|
||||
@@ -5487,7 +5496,6 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
|
||||
BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
|
||||
insideEval, evalCaller,
|
||||
/* evalStaticScope = */ nullptr,
|
||||
insideNonGlobalEval, lineNum, emitterMode);
|
||||
if (!bce2.init())
|
||||
return false;
|
||||
@@ -5508,10 +5516,21 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
|
||||
/* Non-hoisted functions simply emit their respective op. */
|
||||
if (!pn->functionIsHoisted()) {
|
||||
/* JSOP_LAMBDA_ARROW is always preceded by JSOP_THIS. */
|
||||
/* JSOP_LAMBDA_ARROW is always preceded by JSOP_THIS and a new.target */
|
||||
MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW));
|
||||
if (fun->isArrow() && !emit1(JSOP_THIS))
|
||||
return false;
|
||||
if (fun->isArrow()) {
|
||||
if (!emit1(JSOP_THIS))
|
||||
return false;
|
||||
|
||||
if (arrowNeedsNewTarget()) {
|
||||
if (!emit1(JSOP_NEWTARGET))
|
||||
return false;
|
||||
} else {
|
||||
if (!emit1(JSOP_NULL))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsProto) {
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
|
||||
pn->setOp(JSOP_FUNWITHPROTO);
|
||||
|
||||
@@ -136,8 +136,6 @@ struct BytecodeEmitter
|
||||
Parser<FullParseHandler>* const parser;
|
||||
|
||||
HandleScript evalCaller; /* scripted caller info for eval and dbgapi */
|
||||
Handle<StaticEvalObject*> evalStaticScope;
|
||||
/* compile time scope for eval; does not imply stmt stack */
|
||||
|
||||
StmtInfoBCE* topStmt; /* top of statement info stack */
|
||||
StmtInfoBCE* topScopeStmt; /* top lexical scope statement */
|
||||
@@ -222,8 +220,7 @@ struct BytecodeEmitter
|
||||
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
|
||||
HandleScript script, Handle<LazyScript*> lazyScript,
|
||||
bool insideEval, HandleScript evalCaller,
|
||||
Handle<StaticEvalObject*> evalStaticScope, bool insideNonGlobalEval,
|
||||
uint32_t lineNum, EmitterMode emitterMode = Normal);
|
||||
bool insideNonGlobalEval, uint32_t lineNum, EmitterMode emitterMode = Normal);
|
||||
bool init();
|
||||
bool updateLocalsToFrameSlots();
|
||||
|
||||
@@ -435,6 +432,7 @@ struct BytecodeEmitter
|
||||
bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
|
||||
bool emitRegExp(uint32_t index);
|
||||
|
||||
bool arrowNeedsNewTarget();
|
||||
MOZ_NEVER_INLINE bool emitFunction(ParseNode* pn, bool needsProto = false);
|
||||
MOZ_NEVER_INLINE bool emitObject(ParseNode* pn);
|
||||
|
||||
|
||||
+31
-15
@@ -701,8 +701,9 @@ Parser<ParseHandler>::parse(JSObject* chain)
|
||||
* protected from the GC by a root or a stack frame reference.
|
||||
*/
|
||||
Directives directives(options().strictOption);
|
||||
GlobalSharedContext globalsc(context, directives, options().extraWarningsOption,
|
||||
/* allowSuperProperty = */ false);
|
||||
GlobalSharedContext globalsc(context, directives,
|
||||
/* staticEvalScope = */ nullptr,
|
||||
options().extraWarningsOption);
|
||||
ParseContext<ParseHandler> globalpc(this, /* parent = */ nullptr, ParseHandler::null(),
|
||||
&globalsc, /* newDirectives = */ nullptr,
|
||||
/* staticLevel = */ 0, /* bodyid = */ 0,
|
||||
@@ -7744,18 +7745,35 @@ Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, b
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkAndMarkSuperScope()
|
||||
Parser<ParseHandler>::checkAllowedNestedSyntax(SharedContext::AllowedSyntax allowed,
|
||||
SharedContext** allowingContext)
|
||||
{
|
||||
for (GenericParseContext* gpc = pc; gpc; gpc = gpc->parent) {
|
||||
SharedContext* sc = gpc->sc;
|
||||
if (sc->allowSuperProperty()) {
|
||||
if (sc->isFunctionBox())
|
||||
sc->asFunctionBox()->setNeedsHomeObject();
|
||||
return true;
|
||||
} else if (sc->isFunctionBox() && !sc->asFunctionBox()->function()->isArrow()) {
|
||||
// super is not legal in normal functions.
|
||||
break;
|
||||
}
|
||||
|
||||
// Arrow functions don't help decide whether we should allow nested
|
||||
// syntax, as they don't store any of the necessary state for themselves.
|
||||
if (sc->isFunctionBox() && sc->asFunctionBox()->function()->isArrow())
|
||||
continue;
|
||||
|
||||
if (!sc->allowSyntax(allowed))
|
||||
return false;
|
||||
if (allowingContext)
|
||||
*allowingContext = sc;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkAndMarkSuperScope()
|
||||
{
|
||||
SharedContext* foundContext = nullptr;
|
||||
if (checkAllowedNestedSyntax(SharedContext::AllowedSyntax::SuperProperty, &foundContext)) {
|
||||
if (foundContext->isFunctionBox())
|
||||
foundContext->asFunctionBox()->setNeedsHomeObject();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -7894,7 +7912,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool
|
||||
|
||||
// If we're in a method, mark the method as requiring
|
||||
// support for 'super', since direct eval code can use it.
|
||||
// (If we're not in a method, that's fine, so ignore the
|
||||
// (If we're not in a method, that's fine, so ignore the
|
||||
// return value.)
|
||||
checkAndMarkSuperScope();
|
||||
}
|
||||
@@ -8552,9 +8570,7 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pc->sc->isFunctionBox() || pc->sc->asFunctionBox()->isGenerator() ||
|
||||
pc->sc->asFunctionBox()->function()->isArrow())
|
||||
{
|
||||
if (!checkAllowedNestedSyntax(SharedContext::AllowedSyntax::NewTarget)) {
|
||||
reportWithOffset(ParseError, false, begin, JSMSG_BAD_NEWTARGET);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -588,6 +588,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling);
|
||||
Node exprInParens(InHandling inHandling, YieldHandling yieldHandling);
|
||||
|
||||
bool checkAllowedNestedSyntax(SharedContext::AllowedSyntax allowed,
|
||||
SharedContext** allowingContext = nullptr);
|
||||
bool tryNewTarget(Node& newTarget);
|
||||
bool checkAndMarkSuperScope();
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ class SharedContext
|
||||
virtual ObjectBox* toObjectBox() = 0;
|
||||
inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
|
||||
inline FunctionBox* asFunctionBox();
|
||||
inline GlobalSharedContext* asGlobalSharedContext();
|
||||
|
||||
bool hasExplicitUseStrict() const { return anyCxFlags.hasExplicitUseStrict; }
|
||||
bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; }
|
||||
@@ -239,23 +240,56 @@ class SharedContext
|
||||
return atom == context->names().dotGenerator || atom == context->names().dotGenRVal;
|
||||
}
|
||||
|
||||
virtual bool allowSuperProperty() const = 0;
|
||||
enum class AllowedSyntax {
|
||||
NewTarget,
|
||||
SuperProperty
|
||||
};
|
||||
virtual bool allowSyntax(AllowedSyntax allowed) const = 0;
|
||||
|
||||
protected:
|
||||
static bool FunctionAllowsSyntax(JSFunction* func, AllowedSyntax allowed)
|
||||
{
|
||||
MOZ_ASSERT(!func->isArrow());
|
||||
|
||||
switch (allowed) {
|
||||
case AllowedSyntax::NewTarget:
|
||||
// For now, disallow new.target inside generators
|
||||
return !func->isGenerator();
|
||||
case AllowedSyntax::SuperProperty:
|
||||
return func->allowSuperProperty();
|
||||
default:;
|
||||
}
|
||||
MOZ_CRASH("Unknown AllowedSyntax query");
|
||||
}
|
||||
};
|
||||
|
||||
class GlobalSharedContext : public SharedContext
|
||||
{
|
||||
private:
|
||||
bool allowSuperProperty_;
|
||||
Handle<StaticEvalObject*> staticEvalScope_;
|
||||
|
||||
public:
|
||||
GlobalSharedContext(ExclusiveContext* cx,
|
||||
Directives directives, bool extraWarnings, bool allowSuperProperty)
|
||||
Directives directives, Handle<StaticEvalObject*> staticEvalScope,
|
||||
bool extraWarnings)
|
||||
: SharedContext(cx, directives, extraWarnings),
|
||||
allowSuperProperty_(allowSuperProperty)
|
||||
staticEvalScope_(staticEvalScope)
|
||||
{}
|
||||
|
||||
ObjectBox* toObjectBox() { return nullptr; }
|
||||
bool allowSuperProperty() const { return allowSuperProperty_; }
|
||||
HandleObject evalStaticScope() const { return staticEvalScope_; }
|
||||
|
||||
bool allowSyntax(AllowedSyntax allowed) const {
|
||||
StaticScopeIter<CanGC> it(context, staticEvalScope_);
|
||||
for (; !it.done(); it++) {
|
||||
if (it.type() == StaticScopeIter<CanGC>::Function &&
|
||||
!it.fun().isArrow())
|
||||
{
|
||||
return FunctionAllowsSyntax(&it.fun(), allowed);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class FunctionBox : public ObjectBox, public SharedContext
|
||||
@@ -316,7 +350,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; }
|
||||
void setDefinitelyNeedsArgsObj() { MOZ_ASSERT(funCxFlags.argumentsHasLocalBinding);
|
||||
funCxFlags.definitelyNeedsArgsObj = true; }
|
||||
void setNeedsHomeObject() { MOZ_ASSERT(allowSuperProperty());
|
||||
void setNeedsHomeObject() { MOZ_ASSERT(function()->allowSuperProperty());
|
||||
funCxFlags.needsHomeObject = true; }
|
||||
|
||||
FunctionContextFlags flagsForNestedGeneratorComprehensionLambda() const {
|
||||
@@ -352,8 +386,13 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
isGenerator();
|
||||
}
|
||||
|
||||
bool allowSuperProperty() const {
|
||||
return function()->allowSuperProperty();
|
||||
bool allowSyntax(AllowedSyntax allowed) const {
|
||||
// For now (!) we don't allow new.target in generators, and can't
|
||||
// check that for functions we haven't finished parsing, as they
|
||||
// don't have initialized scripts. Check from our stashed bits instead.
|
||||
if (allowed == AllowedSyntax::NewTarget)
|
||||
return !isGenerator();
|
||||
return FunctionAllowsSyntax(function(), allowed);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -364,6 +403,14 @@ SharedContext::asFunctionBox()
|
||||
return static_cast<FunctionBox*>(this);
|
||||
}
|
||||
|
||||
inline GlobalSharedContext*
|
||||
SharedContext::asGlobalSharedContext()
|
||||
{
|
||||
MOZ_ASSERT(!isFunctionBox());
|
||||
return static_cast<GlobalSharedContext*>(this);
|
||||
}
|
||||
|
||||
|
||||
// In generators, we treat all locals as aliased so that they get stored on the
|
||||
// heap. This way there is less information to copy off the stack when
|
||||
// suspending, and back on when resuming. It also avoids the need to create and
|
||||
|
||||
+7
-40
@@ -1693,49 +1693,16 @@ PushArenaTyped(GCMarker* gcmarker, ArenaHeader* aheader)
|
||||
gcmarker->traverse(i.get<T>());
|
||||
}
|
||||
|
||||
struct PushArenaFunctor {
|
||||
template <typename T> void operator()(GCMarker* gcmarker, ArenaHeader* aheader) {
|
||||
PushArenaTyped<T>(gcmarker, aheader);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
gc::PushArena(GCMarker* gcmarker, ArenaHeader* aheader)
|
||||
{
|
||||
switch (MapAllocToTraceKind(aheader->getAllocKind())) {
|
||||
case JSTRACE_OBJECT:
|
||||
PushArenaTyped<JSObject>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_SCRIPT:
|
||||
PushArenaTyped<JSScript>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_STRING:
|
||||
PushArenaTyped<JSString>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_SYMBOL:
|
||||
PushArenaTyped<JS::Symbol>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
PushArenaTyped<js::BaseShape>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_JITCODE:
|
||||
PushArenaTyped<js::jit::JitCode>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
PushArenaTyped<LazyScript>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_SHAPE:
|
||||
PushArenaTyped<js::Shape>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
case JSTRACE_OBJECT_GROUP:
|
||||
PushArenaTyped<js::ObjectGroup>(gcmarker, aheader);
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Invalid trace kind in PushArena.");
|
||||
}
|
||||
CallTyped(PushArenaFunctor(), MapAllocToTraceKind(aheader->getAllocKind()), gcmarker, aheader);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
@@ -592,10 +592,7 @@ js::gc::GCRuntime::bufferGrayRoots()
|
||||
}
|
||||
|
||||
struct SetMaybeAliveFunctor {
|
||||
template <typename T>
|
||||
void operator()(Cell* cell) {
|
||||
SetMaybeAliveFlag<T>(static_cast<T*>(cell));
|
||||
}
|
||||
template <typename T> void operator()(T* t) { SetMaybeAliveFlag(t); }
|
||||
};
|
||||
|
||||
void
|
||||
@@ -612,7 +609,7 @@ BufferGrayRootsTracer::appendGrayRoot(TenuredCell* thing, JSGCTraceKind kind)
|
||||
// objects and scripts. We rely on gray root buffering for this to work,
|
||||
// but we only need to worry about uncollected dead compartments during
|
||||
// incremental GCs (when we do gray root buffering).
|
||||
CallTyped(SetMaybeAliveFunctor(), kind, thing);
|
||||
CallTyped(SetMaybeAliveFunctor(), thing, kind);
|
||||
|
||||
if (!zone->gcGrayRoots.append(thing))
|
||||
bufferingGrayRootsFailed = true;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Test that new.target is acceptably usable in RematerializedFrames.
|
||||
|
||||
load(libdir + "jitopts.js");
|
||||
|
||||
if (!jitTogglesMatch(Opts_Ion2NoOffthreadCompilation))
|
||||
quit();
|
||||
|
||||
withJitOptions(Opts_Ion2NoOffthreadCompilation, function () {
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger;
|
||||
|
||||
g.toggle = function toggle(d, expected) {
|
||||
if (d) {
|
||||
dbg.addDebuggee(g);
|
||||
|
||||
var frame = dbg.getNewestFrame();
|
||||
assertEq(frame.implementation, "ion");
|
||||
assertEq(frame.constructing, true);
|
||||
|
||||
// CONGRATS IF THIS FAILS! You, proud saviour, have made new.target parse
|
||||
// in debug frame evals (presumably by hooking up static scope walks).
|
||||
// Uncomment the assert below for efaust's undying gratitude.
|
||||
// Note that we use .name here because of CCW nonsense.
|
||||
assertEq(frame.eval('new.target').throw.unsafeDereference().name, "SyntaxError");
|
||||
// assertEq(frame.eval('new.target').value.unsafeDereference(), expected);
|
||||
}
|
||||
};
|
||||
|
||||
g.eval("" + function f(d) { new g(d, g, 15); });
|
||||
|
||||
g.eval("" + function g(d, expected) { toggle(d, expected); });
|
||||
|
||||
g.eval("(" + function test() {
|
||||
for (var i = 0; i < 5; i++)
|
||||
f(false);
|
||||
f(true);
|
||||
} + ")();");
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
// Test that new.target is acceptably usable in RematerializedFrames.
|
||||
|
||||
load(libdir + "jitopts.js");
|
||||
|
||||
if (!jitTogglesMatch(Opts_Ion2NoOffthreadCompilation))
|
||||
quit();
|
||||
|
||||
withJitOptions(Opts_Ion2NoOffthreadCompilation, function () {
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger;
|
||||
|
||||
g.toggle = function toggle(d, expected) {
|
||||
if (d) {
|
||||
dbg.addDebuggee(g);
|
||||
|
||||
var frame = dbg.getNewestFrame();
|
||||
assertEq(frame.implementation, "ion");
|
||||
|
||||
// the arrow function will not be constructing, even though it has a
|
||||
// new.target value.
|
||||
assertEq(frame.constructing, false);
|
||||
|
||||
// CONGRATS IF THIS FAILS! You, proud saviour, have made new.target parse
|
||||
// in debug frame evals (presumably by hooking up static scope walks).
|
||||
// Uncomment the assert below for efaust's undying gratitude.
|
||||
// Note that we use .name here because of CCW nonsense.
|
||||
assertEq(frame.eval('new.target').throw.unsafeDereference().name, "SyntaxError");
|
||||
// assertEq(frame.eval('new.target').return.unsafeDereference(), expected);
|
||||
}
|
||||
};
|
||||
|
||||
g.eval("" + function f(d) { new g(d, g, 15); });
|
||||
|
||||
g.eval("" + function g(d, expected) { (() => toggle(d, expected))(); });
|
||||
|
||||
g.eval("(" + function test() {
|
||||
for (var i = 0; i < 5; i++)
|
||||
f(false);
|
||||
f(true);
|
||||
} + ")();");
|
||||
});
|
||||
@@ -1452,22 +1452,24 @@ BaselineCompiler::emit_JSOP_LAMBDA()
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject,
|
||||
HandleValue, HandleValue);
|
||||
static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LAMBDA_ARROW()
|
||||
{
|
||||
// Keep pushed |this| in R0.
|
||||
frame.popRegsAndSync(1);
|
||||
// Keep pushed |this| in R0, and newTarget in R1.
|
||||
frame.popRegsAndSync(2);
|
||||
|
||||
RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc)));
|
||||
|
||||
prepareVMCall();
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg());
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R2.scratchReg());
|
||||
|
||||
pushArg(R1);
|
||||
pushArg(R0);
|
||||
pushArg(R1.scratchReg());
|
||||
pushArg(R2.scratchReg());
|
||||
pushArg(ImmGCPtr(fun));
|
||||
|
||||
if (!callVM(LambdaArrowInfo))
|
||||
@@ -1502,6 +1504,11 @@ BaselineCompiler::storeValue(const StackValue* source, const Address& dest,
|
||||
masm.loadValue(frame.addressOfThis(), scratch);
|
||||
masm.storeValue(scratch, dest);
|
||||
break;
|
||||
case StackValue::EvalNewTargetSlot:
|
||||
MOZ_ASSERT(script->isForEval());
|
||||
masm.loadValue(frame.addressOfEvalNewTarget(), scratch);
|
||||
masm.storeValue(scratch, dest);
|
||||
break;
|
||||
case StackValue::Stack:
|
||||
masm.loadValue(frame.addressOfStackValue(source), scratch);
|
||||
masm.storeValue(scratch, dest);
|
||||
@@ -2670,9 +2677,24 @@ BaselineCompiler::emit_JSOP_SETARG()
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_NEWTARGET()
|
||||
{
|
||||
if (script->isForEval()) {
|
||||
frame.pushEvalNewTarget();
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(function());
|
||||
frame.syncStack(0);
|
||||
|
||||
if (function()->isArrow()) {
|
||||
// Arrow functions store their |new.target| value in an
|
||||
// extended slot.
|
||||
Register scratch = R0.scratchReg();
|
||||
masm.loadFunctionFromCalleeToken(frame.addressOfCalleeToken(), scratch);
|
||||
masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowNewTargetSlot()), R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if (!isConstructing()) push(undefined)
|
||||
Label constructing, done;
|
||||
masm.branchTestPtr(Assembler::NonZero, frame.addressOfCalleeToken(),
|
||||
|
||||
@@ -48,8 +48,10 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator)
|
||||
if (hasReturnValue())
|
||||
TraceRoot(trc, returnValue().address(), "baseline-rval");
|
||||
|
||||
if (isEvalFrame())
|
||||
if (isEvalFrame()) {
|
||||
TraceRoot(trc, &evalScript_, "baseline-evalscript");
|
||||
TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget");
|
||||
}
|
||||
|
||||
if (hasArgsObj())
|
||||
TraceRoot(trc, &argsObj_, "baseline-args-obj");
|
||||
|
||||
@@ -217,6 +217,28 @@ class BaselineFrame
|
||||
offsetOfArg(0));
|
||||
}
|
||||
|
||||
private:
|
||||
Value* evalNewTargetAddress() const {
|
||||
MOZ_ASSERT(isEvalFrame());
|
||||
return (Value*)(reinterpret_cast<const uint8_t*>(this) +
|
||||
BaselineFrame::Size() +
|
||||
offsetOfEvalNewTarget());
|
||||
}
|
||||
|
||||
public:
|
||||
Value newTarget() const {
|
||||
MOZ_ASSERT(isFunctionFrame());
|
||||
if (isEvalFrame())
|
||||
return *evalNewTargetAddress();
|
||||
if (fun()->isArrow())
|
||||
return fun()->getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT);
|
||||
if (isConstructing())
|
||||
return *(Value*)(reinterpret_cast<const uint8_t*>(this) +
|
||||
BaselineFrame::Size() +
|
||||
offsetOfArg(Max(numFormalArgs(), numActualArgs())));
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
bool copyRawFrameSlots(AutoValueVector* vec) const;
|
||||
|
||||
bool hasReturnValue() const {
|
||||
@@ -399,6 +421,9 @@ class BaselineFrame
|
||||
static size_t offsetOfThis() {
|
||||
return FramePointerOffset + js::jit::JitFrameLayout::offsetOfThis();
|
||||
}
|
||||
static size_t offsetOfEvalNewTarget() {
|
||||
return offsetOfArg(0);
|
||||
}
|
||||
static size_t offsetOfArg(size_t index) {
|
||||
return FramePointerOffset + js::jit::JitFrameLayout::offsetOfActualArg(index);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ FrameInfo::sync(StackValue* val)
|
||||
case StackValue::ThisSlot:
|
||||
masm.pushValue(addressOfThis());
|
||||
break;
|
||||
case StackValue::EvalNewTargetSlot:
|
||||
MOZ_ASSERT(script->isForEval());
|
||||
masm.pushValue(addressOfEvalNewTarget());
|
||||
break;
|
||||
case StackValue::Register:
|
||||
masm.pushValue(val->reg());
|
||||
break;
|
||||
@@ -95,6 +99,9 @@ FrameInfo::popValue(ValueOperand dest)
|
||||
case StackValue::ThisSlot:
|
||||
masm.loadValue(addressOfThis(), dest);
|
||||
break;
|
||||
case StackValue::EvalNewTargetSlot:
|
||||
masm.loadValue(addressOfEvalNewTarget(), dest);
|
||||
break;
|
||||
case StackValue::Stack:
|
||||
masm.popValue(dest);
|
||||
break;
|
||||
|
||||
@@ -54,7 +54,8 @@ class StackValue
|
||||
Stack,
|
||||
LocalSlot,
|
||||
ArgSlot,
|
||||
ThisSlot
|
||||
ThisSlot,
|
||||
EvalNewTargetSlot
|
||||
#ifdef DEBUG
|
||||
// In debug builds, assert Kind is initialized.
|
||||
, Uninitialized
|
||||
@@ -150,6 +151,10 @@ class StackValue
|
||||
kind_ = ThisSlot;
|
||||
knownType_ = JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
void setEvalNewTarget() {
|
||||
kind_ = EvalNewTargetSlot;
|
||||
knownType_ = JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
void setStack() {
|
||||
kind_ = Stack;
|
||||
knownType_ = JSVAL_TYPE_UNKNOWN;
|
||||
@@ -259,6 +264,12 @@ class FrameInfo
|
||||
StackValue* sv = rawPush();
|
||||
sv->setThis();
|
||||
}
|
||||
inline void pushEvalNewTarget() {
|
||||
MOZ_ASSERT(script->isForEval());
|
||||
StackValue* sv = rawPush();
|
||||
sv->setEvalNewTarget();
|
||||
}
|
||||
|
||||
inline void pushScratchValue() {
|
||||
masm.pushValue(addressOfScratchValue());
|
||||
StackValue* sv = rawPush();
|
||||
@@ -275,6 +286,9 @@ class FrameInfo
|
||||
Address addressOfThis() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::offsetOfThis());
|
||||
}
|
||||
Address addressOfEvalNewTarget() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::offsetOfEvalNewTarget());
|
||||
}
|
||||
Address addressOfCalleeToken() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::offsetOfCalleeToken());
|
||||
}
|
||||
|
||||
@@ -181,6 +181,7 @@ jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc)
|
||||
data.osrFrame = fp;
|
||||
data.osrNumStackValues = fp->script()->nfixed() + cx->interpreterRegs().stackDepth();
|
||||
|
||||
AutoValueVector vals(cx);
|
||||
RootedValue thisv(cx);
|
||||
|
||||
if (fp->isNonEvalFunctionFrame()) {
|
||||
@@ -203,6 +204,21 @@ jit::EnterBaselineAtBranch(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc)
|
||||
data.calleeToken = CalleeToToken(&fp->callee(), /* constructing = */ false);
|
||||
else
|
||||
data.calleeToken = CalleeToToken(fp->script());
|
||||
|
||||
if (fp->isEvalFrame()) {
|
||||
if (!vals.reserve(2))
|
||||
return JitExec_Aborted;
|
||||
|
||||
vals.infallibleAppend(thisv);
|
||||
|
||||
if (fp->isFunctionFrame())
|
||||
vals.infallibleAppend(fp->newTarget());
|
||||
else
|
||||
vals.infallibleAppend(NullValue());
|
||||
|
||||
data.maxArgc = 2;
|
||||
data.maxArgv = vals.begin();
|
||||
}
|
||||
}
|
||||
|
||||
TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
|
||||
|
||||
@@ -1720,42 +1720,100 @@ CodeGenerator::visitLambda(LLambda* lir)
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
|
||||
class OutOfLineLambdaArrow : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
public:
|
||||
LLambdaArrow* lir;
|
||||
Label entryNoPop_;
|
||||
|
||||
explicit OutOfLineLambdaArrow(LLambdaArrow* lir)
|
||||
: lir(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGenerator* codegen) {
|
||||
codegen->visitOutOfLineLambdaArrow(this);
|
||||
}
|
||||
|
||||
Label* entryNoPop() {
|
||||
return &entryNoPop_;
|
||||
}
|
||||
};
|
||||
|
||||
typedef JSObject* (*LambdaArrowFn)(JSContext*, HandleFunction, HandleObject, HandleValue, HandleValue);
|
||||
static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool)
|
||||
{
|
||||
Register scopeChain = ToRegister(ool->lir->scopeChain());
|
||||
ValueOperand thisv = ToValue(ool->lir, LLambdaArrow::ThisValue);
|
||||
ValueOperand newTarget = ToValue(ool->lir, LLambdaArrow::NewTargetValue);
|
||||
Register output = ToRegister(ool->lir->output());
|
||||
const LambdaFunctionInfo& info = ool->lir->mir()->info();
|
||||
|
||||
// When we get here, we may need to restore part of the newTarget,
|
||||
// which has been conscripted into service as a temp register.
|
||||
masm.pop(newTarget.scratchReg());
|
||||
|
||||
masm.bind(ool->entryNoPop());
|
||||
|
||||
saveLive(ool->lir);
|
||||
|
||||
pushArg(newTarget);
|
||||
pushArg(thisv);
|
||||
pushArg(scopeChain);
|
||||
pushArg(ImmGCPtr(info.fun));
|
||||
|
||||
callVM(LambdaArrowInfo, ool->lir);
|
||||
StoreRegisterTo(output).generate(this);
|
||||
|
||||
restoreLiveIgnore(ool->lir, StoreRegisterTo(output).clobbered());
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitLambdaArrow(LLambdaArrow* lir)
|
||||
{
|
||||
Register scopeChain = ToRegister(lir->scopeChain());
|
||||
ValueOperand thisv = ToValue(lir, LLambdaArrow::ThisValue);
|
||||
ValueOperand newTarget = ToValue(lir, LLambdaArrow::NewTargetValue);
|
||||
Register output = ToRegister(lir->output());
|
||||
Register tempReg = ToRegister(lir->temp());
|
||||
const LambdaFunctionInfo& info = lir->mir()->info();
|
||||
|
||||
OutOfLineCode* ool = oolCallVM(LambdaArrowInfo, lir,
|
||||
(ArgList(), ImmGCPtr(info.fun), scopeChain, thisv),
|
||||
StoreRegisterTo(output));
|
||||
OutOfLineLambdaArrow* ool = new (alloc()) OutOfLineLambdaArrow(lir);
|
||||
addOutOfLineCode(ool, lir->mir());
|
||||
|
||||
MOZ_ASSERT(!info.useSingletonForClone);
|
||||
|
||||
if (info.singletonType) {
|
||||
// If the function has a singleton type, this instruction will only be
|
||||
// executed once so we don't bother inlining it.
|
||||
masm.jump(ool->entry());
|
||||
masm.jump(ool->entryNoPop());
|
||||
masm.bind(ool->rejoin());
|
||||
return;
|
||||
}
|
||||
|
||||
// There's not enough registers on x86 with the profiler enabled to request
|
||||
// a temp. Instead, spill part of one of the values, being prepared to
|
||||
// restore it if necessary on the out of line path.
|
||||
Register tempReg = newTarget.scratchReg();
|
||||
masm.push(newTarget.scratchReg());
|
||||
|
||||
masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry());
|
||||
|
||||
masm.pop(newTarget.scratchReg());
|
||||
|
||||
emitLambdaInit(output, scopeChain, info);
|
||||
|
||||
// Initialize extended slots. Lexical |this| is stored in the first one.
|
||||
MOZ_ASSERT(info.flags & JSFunction::EXTENDED);
|
||||
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
|
||||
static_assert(FunctionExtended::ARROW_THIS_SLOT == 0, "|this| must be stored in first slot");
|
||||
static_assert(FunctionExtended::ARROW_NEWTARGET_SLOT == 1,
|
||||
"|new.target| must be stored in second slot");
|
||||
masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
|
||||
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
||||
masm.storeValue(newTarget, Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
@@ -3446,8 +3504,8 @@ CodeGenerator::visitGetDynamicName(LGetDynamicName* lir)
|
||||
bailoutFrom(&undefined, lir->snapshot());
|
||||
}
|
||||
|
||||
typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleString,
|
||||
jsbytecode*, MutableHandleValue);
|
||||
typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleValue,
|
||||
HandleString, jsbytecode*, MutableHandleValue);
|
||||
static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon);
|
||||
|
||||
void
|
||||
@@ -3458,6 +3516,7 @@ CodeGenerator::visitCallDirectEval(LCallDirectEval* lir)
|
||||
|
||||
pushArg(ImmPtr(lir->mir()->pc()));
|
||||
pushArg(string);
|
||||
pushArg(ToValue(lir, LCallDirectEval::NewTarget));
|
||||
pushArg(ToValue(lir, LCallDirectEval::ThisValue));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
pushArg(scopeChain);
|
||||
@@ -4925,6 +4984,14 @@ CodeGenerator::visitLoadArrowThis(LLoadArrowThis* lir)
|
||||
masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowThisSlot()), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitArrowNewTarget(LArrowNewTarget* lir)
|
||||
{
|
||||
Register callee = ToRegister(lir->callee());
|
||||
ValueOperand output = ToOutValue(lir);
|
||||
masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowNewTargetSlot()), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitArrayLength(LArrayLength* lir)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,7 @@ class OutOfLineCallPostWriteBarrier;
|
||||
class OutOfLineIsCallable;
|
||||
class OutOfLineRegExpExec;
|
||||
class OutOfLineRegExpTest;
|
||||
class OutOfLineLambdaArrow;
|
||||
|
||||
class CodeGenerator : public CodeGeneratorSpecific
|
||||
{
|
||||
@@ -104,6 +105,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitRegExpReplace(LRegExpReplace* lir);
|
||||
void visitStringReplace(LStringReplace* lir);
|
||||
void visitLambda(LLambda* lir);
|
||||
void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool);
|
||||
void visitLambdaArrow(LLambdaArrow* lir);
|
||||
void visitLambdaForSingleton(LLambdaForSingleton* lir);
|
||||
void visitPointer(LPointer* lir);
|
||||
@@ -323,6 +325,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitThrowUninitializedLexical(LThrowUninitializedLexical* ins);
|
||||
void visitDebugger(LDebugger* ins);
|
||||
void visitNewTarget(LNewTarget* ins);
|
||||
void visitArrowNewTarget(LArrowNewTarget* ins);
|
||||
|
||||
void visitCheckOverRecursed(LCheckOverRecursed* lir);
|
||||
void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
|
||||
|
||||
@@ -2602,6 +2602,22 @@ jit::SetEnterJitData(JSContext* cx, EnterJitData& data, RunState& state, AutoVal
|
||||
ScriptFrameIter iter(cx);
|
||||
if (iter.isFunctionFrame())
|
||||
data.calleeToken = CalleeToToken(iter.callee(cx), /* constructing = */ false);
|
||||
|
||||
// Push newTarget onto the stack, as well as Argv.
|
||||
if (!vals.reserve(2))
|
||||
return false;
|
||||
|
||||
data.maxArgc = 2;
|
||||
data.maxArgv = vals.begin();
|
||||
vals.infallibleAppend(state.asExecute()->thisv());
|
||||
if (iter.isFunctionFrame()) {
|
||||
if (state.asExecute()->newTarget().isNull())
|
||||
vals.infallibleAppend(iter.newTarget());
|
||||
else
|
||||
vals.infallibleAppend(state.asExecute()->newTarget());
|
||||
} else {
|
||||
vals.infallibleAppend(NullValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6447,6 +6447,10 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
current->pushSlot(info().thisSlot());
|
||||
MDefinition* thisValue = current->pop();
|
||||
|
||||
if (!jsop_newtarget())
|
||||
return false;
|
||||
MDefinition* newTargetValue = current->pop();
|
||||
|
||||
// Try to pattern match 'eval(v + "()")'. In this case v is likely a
|
||||
// name on the scope chain and the eval is performing a call on that
|
||||
// value. Use a dynamic scope chain lookup rather than a full eval.
|
||||
@@ -6472,7 +6476,8 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
}
|
||||
}
|
||||
|
||||
MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string, thisValue, pc);
|
||||
MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string,
|
||||
thisValue, newTargetValue, pc);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
@@ -9425,7 +9430,21 @@ IonBuilder::jsop_arguments()
|
||||
bool
|
||||
IonBuilder::jsop_newtarget()
|
||||
{
|
||||
if (!info().funMaybeLazy()) {
|
||||
MOZ_ASSERT(!info().script()->isForEval());
|
||||
pushConstant(NullValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(info().funMaybeLazy());
|
||||
|
||||
if (info().funMaybeLazy()->isArrow()) {
|
||||
MArrowNewTarget* arrowNewTarget = MArrowNewTarget::New(alloc(), getCallee());
|
||||
current->add(arrowNewTarget);
|
||||
current->push(arrowNewTarget);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (inliningDepth_ == 0) {
|
||||
MNewTarget* newTarget = MNewTarget::New(alloc());
|
||||
current->add(newTarget);
|
||||
@@ -11874,10 +11893,11 @@ IonBuilder::jsop_lambda_arrow(JSFunction* fun)
|
||||
MOZ_ASSERT(fun->isArrow());
|
||||
MOZ_ASSERT(!fun->isNative());
|
||||
|
||||
MDefinition* newTargetDef = current->pop();
|
||||
MDefinition* thisDef = current->pop();
|
||||
|
||||
MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->scopeChain(),
|
||||
thisDef, fun);
|
||||
thisDef, newTargetDef, fun);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
|
||||
@@ -701,8 +701,8 @@ class InlineFrameIterator
|
||||
|
||||
template <class ArgOp, class LocalOp>
|
||||
void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp,
|
||||
JSObject** scopeChain, bool* hasCallObj, Value* rval,
|
||||
ArgumentsObject** argsObj, Value* thisv,
|
||||
JSObject** scopeChain, bool* hasCallObj,
|
||||
Value* rval, ArgumentsObject** argsObj, Value* thisv,
|
||||
ReadFrameArgsBehavior behavior,
|
||||
MaybeReadFallback& fallback) const
|
||||
{
|
||||
@@ -762,13 +762,13 @@ class InlineFrameIterator
|
||||
parent_s.skip(); // scope chain
|
||||
parent_s.skip(); // return value
|
||||
parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr,
|
||||
nformal, nactual, it.script(),
|
||||
nformal, nactual + isConstructing(), it.script(),
|
||||
fallback);
|
||||
} else {
|
||||
// There is no parent frame to this inlined frame, we can read
|
||||
// from the frame's Value vector directly.
|
||||
Value* argv = frame_->actualArgs();
|
||||
for (unsigned i = nformal; i < nactual; i++)
|
||||
for (unsigned i = nformal; i < nactual + isConstructing(); i++)
|
||||
argOp(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
+19
-7
@@ -1870,7 +1870,7 @@ class LGetDynamicName : public LCallInstructionHelper<BOX_PIECES, 2, 3>
|
||||
}
|
||||
};
|
||||
|
||||
class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + BOX_PIECES, 0>
|
||||
class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + (2 * BOX_PIECES), 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CallDirectEval)
|
||||
@@ -1882,6 +1882,7 @@ class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + BOX_PIECES
|
||||
}
|
||||
|
||||
static const size_t ThisValue = 2;
|
||||
static const size_t NewTarget = 2 + BOX_PIECES;
|
||||
|
||||
MCallDirectEval* mir() const {
|
||||
return mir_->toCallDirectEval();
|
||||
@@ -3949,23 +3950,20 @@ class LLambda : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
class LLambdaArrow : public LInstructionHelper<1, 1 + BOX_PIECES, 1>
|
||||
class LLambdaArrow : public LInstructionHelper<1, 1 + (2 * BOX_PIECES), 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(LambdaArrow)
|
||||
|
||||
static const size_t ThisValue = 1;
|
||||
static const size_t NewTargetValue = ThisValue + BOX_PIECES;
|
||||
|
||||
LLambdaArrow(const LAllocation& scopeChain, const LDefinition& temp) {
|
||||
explicit LLambdaArrow(const LAllocation& scopeChain) {
|
||||
setOperand(0, scopeChain);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
const LAllocation* scopeChain() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const MLambdaArrow* mir() const {
|
||||
return mir_->toLambdaArrow();
|
||||
}
|
||||
@@ -6973,6 +6971,20 @@ class LNewTarget : public LInstructionHelper<BOX_PIECES, 0, 0>
|
||||
LIR_HEADER(NewTarget)
|
||||
};
|
||||
|
||||
class LArrowNewTarget : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
{
|
||||
public:
|
||||
explicit LArrowNewTarget(const LAllocation& callee) {
|
||||
setOperand(0, callee);
|
||||
}
|
||||
|
||||
LIR_HEADER(ArrowNewTarget)
|
||||
|
||||
const LAllocation* callee() {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -348,7 +348,8 @@
|
||||
_(ThrowUninitializedLexical) \
|
||||
_(NurseryObject) \
|
||||
_(Debugger) \
|
||||
_(NewTarget)
|
||||
_(NewTarget) \
|
||||
_(ArrowNewTarget)
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
# include "jit/x86/LOpcodes-x86.h"
|
||||
|
||||
+15
-1
@@ -385,6 +385,16 @@ LIRGenerator::visitLoadArrowThis(MLoadArrowThis* ins)
|
||||
defineBox(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitArrowNewTarget(MArrowNewTarget* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->type() == MIRType_Value);
|
||||
MOZ_ASSERT(ins->callee()->type() == MIRType_Object);
|
||||
|
||||
LArrowNewTarget* lir = new(alloc()) LArrowNewTarget(useRegister(ins->callee()));
|
||||
defineBox(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::lowerCallArguments(MCall* call)
|
||||
{
|
||||
@@ -585,10 +595,12 @@ LIRGenerator::visitCallDirectEval(MCallDirectEval* ins)
|
||||
MOZ_ASSERT(string->type() == MIRType_String);
|
||||
|
||||
MDefinition* thisValue = ins->getThisValue();
|
||||
MDefinition* newTargetValue = ins->getNewTargetValue();
|
||||
|
||||
LInstruction* lir = new(alloc()) LCallDirectEval(useRegisterAtStart(scopeChain),
|
||||
useRegisterAtStart(string));
|
||||
useBoxAtStart(lir, LCallDirectEval::ThisValue, thisValue);
|
||||
useBoxAtStart(lir, LCallDirectEval::NewTarget, newTargetValue);
|
||||
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
@@ -2129,9 +2141,11 @@ LIRGenerator::visitLambdaArrow(MLambdaArrow* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object);
|
||||
MOZ_ASSERT(ins->thisDef()->type() == MIRType_Value);
|
||||
MOZ_ASSERT(ins->newTargetDef()->type() == MIRType_Value);
|
||||
|
||||
LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()), temp());
|
||||
LLambdaArrow* lir = new(alloc()) LLambdaArrow(useRegister(ins->scopeChain()));
|
||||
useBox(lir, LLambdaArrow::ThisValue, ins->thisDef());
|
||||
useBox(lir, LLambdaArrow::NewTargetValue, ins->newTargetDef());
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
@@ -297,6 +297,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitDebugger(MDebugger* ins);
|
||||
void visitNurseryObject(MNurseryObject* ins);
|
||||
void visitNewTarget(MNewTarget* ins);
|
||||
void visitArrowNewTarget(MArrowNewTarget* ins);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
||||
+51
-12
@@ -4025,19 +4025,21 @@ class MGetDynamicName
|
||||
};
|
||||
|
||||
class MCallDirectEval
|
||||
: public MAryInstruction<3>,
|
||||
public Mix3Policy<ObjectPolicy<0>,
|
||||
: public MAryInstruction<4>,
|
||||
public Mix4Policy<ObjectPolicy<0>,
|
||||
StringPolicy<1>,
|
||||
BoxPolicy<2> >::Data
|
||||
BoxPolicy<2>,
|
||||
BoxPolicy<3> >::Data
|
||||
{
|
||||
protected:
|
||||
MCallDirectEval(MDefinition* scopeChain, MDefinition* string, MDefinition* thisValue,
|
||||
jsbytecode* pc)
|
||||
MDefinition* newTargetValue, jsbytecode* pc)
|
||||
: pc_(pc)
|
||||
{
|
||||
initOperand(0, scopeChain);
|
||||
initOperand(1, string);
|
||||
initOperand(2, thisValue);
|
||||
initOperand(3, newTargetValue);
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
@@ -4046,9 +4048,9 @@ class MCallDirectEval
|
||||
|
||||
static MCallDirectEval*
|
||||
New(TempAllocator& alloc, MDefinition* scopeChain, MDefinition* string, MDefinition* thisValue,
|
||||
jsbytecode* pc)
|
||||
MDefinition* newTargetValue, jsbytecode* pc)
|
||||
{
|
||||
return new(alloc) MCallDirectEval(scopeChain, string, thisValue, pc);
|
||||
return new(alloc) MCallDirectEval(scopeChain, string, thisValue, newTargetValue, pc);
|
||||
}
|
||||
|
||||
MDefinition* getScopeChain() const {
|
||||
@@ -4060,6 +4062,9 @@ class MCallDirectEval
|
||||
MDefinition* getThisValue() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
MDefinition* getNewTargetValue() const {
|
||||
return getOperand(3);
|
||||
}
|
||||
|
||||
jsbytecode* pc() const {
|
||||
return pc_;
|
||||
@@ -6656,6 +6661,36 @@ class MLoadArrowThis
|
||||
}
|
||||
};
|
||||
|
||||
// Load an arrow function's |new.target| value.
|
||||
class MArrowNewTarget
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MArrowNewTarget(MDefinition* callee)
|
||||
: MUnaryInstruction(callee)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ArrowNewTarget)
|
||||
|
||||
static MArrowNewTarget* New(TempAllocator& alloc, MDefinition* callee) {
|
||||
return new(alloc) MArrowNewTarget(callee);
|
||||
}
|
||||
MDefinition* callee() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
// An arrow function's lexical |this| value is immutable.
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MPhi final
|
||||
: public MDefinition,
|
||||
public InlineListNode<MPhi>,
|
||||
@@ -7488,14 +7523,14 @@ class MLambda
|
||||
};
|
||||
|
||||
class MLambdaArrow
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
|
||||
: public MTernaryInstruction,
|
||||
public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
|
||||
{
|
||||
const LambdaFunctionInfo info_;
|
||||
|
||||
MLambdaArrow(CompilerConstraintList* constraints, MDefinition* scopeChain,
|
||||
MDefinition* this_, JSFunction* fun)
|
||||
: MBinaryInstruction(scopeChain, this_), info_(fun)
|
||||
MDefinition* this_, MDefinition* newTarget_, JSFunction* fun)
|
||||
: MTernaryInstruction(scopeChain, this_, newTarget_), info_(fun)
|
||||
{
|
||||
setResultType(MIRType_Object);
|
||||
MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun));
|
||||
@@ -7507,9 +7542,10 @@ class MLambdaArrow
|
||||
INSTRUCTION_HEADER(LambdaArrow)
|
||||
|
||||
static MLambdaArrow* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
MDefinition* scopeChain, MDefinition* this_, JSFunction* fun)
|
||||
MDefinition* scopeChain, MDefinition* this_, MDefinition* newTarget_,
|
||||
JSFunction* fun)
|
||||
{
|
||||
return new(alloc) MLambdaArrow(constraints, scopeChain, this_, fun);
|
||||
return new(alloc) MLambdaArrow(constraints, scopeChain, this_, newTarget_, fun);
|
||||
}
|
||||
MDefinition* scopeChain() const {
|
||||
return getOperand(0);
|
||||
@@ -7517,6 +7553,9 @@ class MLambdaArrow
|
||||
MDefinition* thisDef() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
MDefinition* newTargetDef() const {
|
||||
return getOperand(2);
|
||||
}
|
||||
const LambdaFunctionInfo& info() const {
|
||||
return info_;
|
||||
}
|
||||
|
||||
@@ -268,7 +268,8 @@ namespace jit {
|
||||
_(LexicalCheck) \
|
||||
_(ThrowUninitializedLexical) \
|
||||
_(Debugger) \
|
||||
_(NewTarget)
|
||||
_(NewTarget) \
|
||||
_(ArrowNewTarget)
|
||||
|
||||
// Forward declarations of MIR types.
|
||||
#define FORWARD_DECLARE(op) class M##op;
|
||||
|
||||
@@ -35,6 +35,7 @@ RematerializedFrame::RematerializedFrame(JSContext* cx, uint8_t* top, unsigned n
|
||||
InlineFrameIterator& iter, MaybeReadFallback& fallback)
|
||||
: prevUpToDate_(false),
|
||||
isDebuggee_(iter.script()->isDebuggee()),
|
||||
isConstructing_(iter.isConstructing()),
|
||||
top_(top),
|
||||
pc_(iter.pc()),
|
||||
frameNo_(iter.frameNo()),
|
||||
@@ -52,7 +53,7 @@ RematerializedFrame::New(JSContext* cx, uint8_t* top, InlineFrameIterator& iter,
|
||||
MaybeReadFallback& fallback)
|
||||
{
|
||||
unsigned numFormals = iter.isFunctionFrame() ? iter.calleeTemplate()->nargs() : 0;
|
||||
unsigned argSlots = Max(numFormals, iter.numActualArgs());
|
||||
unsigned argSlots = Max(numFormals, iter.numActualArgs()) + iter.isConstructing();
|
||||
size_t numBytes = sizeof(RematerializedFrame) +
|
||||
(argSlots + iter.script()->nfixed()) * sizeof(Value) -
|
||||
sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
|
||||
@@ -151,7 +152,8 @@ RematerializedFrame::mark(JSTracer* trc)
|
||||
TraceRoot(trc, &scopeChain_, "remat ion frame scope chain");
|
||||
TraceRoot(trc, &returnValue_, "remat ion frame return value");
|
||||
TraceRoot(trc, &thisValue_, "remat ion frame this");
|
||||
TraceRootRange(trc, numActualArgs_ + script_->nfixed(), slots_, "remat ion frame stack");
|
||||
TraceRootRange(trc, numActualArgs_ + isConstructing_ + script_->nfixed(),
|
||||
slots_, "remat ion frame stack");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -32,6 +32,9 @@ class RematerializedFrame
|
||||
// Has a call object been pushed?
|
||||
bool hasCallObj_;
|
||||
|
||||
// Is this frame constructing?
|
||||
bool isConstructing_;
|
||||
|
||||
// The fp of the top frame associated with this possibly inlined frame.
|
||||
uint8_t* top_;
|
||||
|
||||
@@ -160,6 +163,10 @@ class RematerializedFrame
|
||||
return thisValue_;
|
||||
}
|
||||
|
||||
bool isConstructing() const {
|
||||
return isConstructing_;
|
||||
}
|
||||
|
||||
unsigned numFormalArgs() const {
|
||||
return maybeFun() ? fun()->nargs() : 0;
|
||||
}
|
||||
@@ -171,7 +178,7 @@ class RematerializedFrame
|
||||
return slots_;
|
||||
}
|
||||
Value* locals() {
|
||||
return slots_ + numActualArgs_;
|
||||
return slots_ + numActualArgs_ + isConstructing_;
|
||||
}
|
||||
|
||||
Value& unaliasedLocal(unsigned i) {
|
||||
@@ -191,6 +198,15 @@ class RematerializedFrame
|
||||
return argv()[i];
|
||||
}
|
||||
|
||||
Value newTarget() {
|
||||
MOZ_ASSERT(isFunctionFrame());
|
||||
if (callee()->isArrow())
|
||||
return callee()->getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT);
|
||||
if (isConstructing())
|
||||
return argv()[numActualArgs()];
|
||||
return UndefinedValue();
|
||||
}
|
||||
|
||||
Value returnValue() const {
|
||||
return returnValue_;
|
||||
}
|
||||
|
||||
@@ -1155,6 +1155,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||
_(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>) \
|
||||
_(Mix3Policy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2> >) \
|
||||
_(Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >) \
|
||||
_(Mix4Policy<ObjectPolicy<0>, StringPolicy<1>, BoxPolicy<2>, BoxPolicy<3>>) \
|
||||
_(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \
|
||||
_(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
|
||||
_(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >) \
|
||||
|
||||
@@ -451,6 +451,7 @@ template <> struct MatchContext<ExclusiveContext*> {
|
||||
#define FOR_EACH_ARGS_4(Macro, Sep, Last) FOR_EACH_ARGS_3(Macro, Sep, Sep) Macro(4) Last(4)
|
||||
#define FOR_EACH_ARGS_5(Macro, Sep, Last) FOR_EACH_ARGS_4(Macro, Sep, Sep) Macro(5) Last(5)
|
||||
#define FOR_EACH_ARGS_6(Macro, Sep, Last) FOR_EACH_ARGS_5(Macro, Sep, Sep) Macro(6) Last(6)
|
||||
#define FOR_EACH_ARGS_7(Macro, Sep, Last) FOR_EACH_ARGS_6(Macro, Sep, Sep) Macro(7) Last(7)
|
||||
|
||||
#define COMPUTE_INDEX(NbArg) NbArg
|
||||
#define COMPUTE_OUTPARAM_RESULT(NbArg) OutParamToDataType<A ## NbArg>::result
|
||||
@@ -590,8 +591,15 @@ template <class R, class Context, class A1, class A2, class A3, class A4, class
|
||||
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_6)
|
||||
};
|
||||
|
||||
template <class R, class Context, class A1, class A2, class A3, class A4, class A5, class A6, class A7>
|
||||
struct FunctionInfo<R (*)(Context, A1, A2, A3, A4, A5, A6, A7)> : public VMFunction {
|
||||
typedef R (*pf)(Context, A1, A2, A3, A4, A5, A6, A7);
|
||||
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_7)
|
||||
};
|
||||
|
||||
#undef FUNCTION_INFO_STRUCT_BODY
|
||||
|
||||
#undef FOR_EACH_ARGS_7
|
||||
#undef FOR_EACH_ARGS_6
|
||||
#undef FOR_EACH_ARGS_5
|
||||
#undef FOR_EACH_ARGS_4
|
||||
|
||||
+12
-29
@@ -274,9 +274,10 @@ JS_GetEmptyString(JSRuntime* rt)
|
||||
namespace js {
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
GetPerformanceStats(JSRuntime* rt,
|
||||
PerformanceStatsVector& stats,
|
||||
PerformanceStats& processStats)
|
||||
IterPerformanceStats(JSContext* cx,
|
||||
PerformanceStatsWalker walker,
|
||||
PerformanceData* processStats,
|
||||
void* closure)
|
||||
{
|
||||
// As a PerformanceGroup is typically associated to several
|
||||
// compartments, use a HashSet to make sure that we only report
|
||||
@@ -289,13 +290,16 @@ GetPerformanceStats(JSRuntime* rt,
|
||||
return false;
|
||||
}
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
|
||||
JSCompartment* compartment = c.get();
|
||||
if (!compartment->performanceMonitoring.isLinked()) {
|
||||
// Don't report compartments that do not even have a PerformanceGroup.
|
||||
continue;
|
||||
}
|
||||
PerformanceGroup* group = compartment->performanceMonitoring.getGroup();
|
||||
|
||||
js::AutoCompartment autoCompartment(cx, compartment);
|
||||
PerformanceGroup* group = compartment->performanceMonitoring.getGroup(cx);
|
||||
|
||||
if (group->data.ticks == 0) {
|
||||
// Don't report compartments that have never been used.
|
||||
@@ -308,38 +312,16 @@ GetPerformanceStats(JSRuntime* rt,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!stats.growBy(1)) {
|
||||
// Memory issue
|
||||
if (!(*walker)(cx, group->data, closure)) {
|
||||
// Issue in callback
|
||||
return false;
|
||||
}
|
||||
PerformanceStats* stat = &stats.back();
|
||||
stat->isSystem = compartment->isSystem();
|
||||
if (compartment->addonId)
|
||||
stat->addonId = compartment->addonId;
|
||||
|
||||
if (compartment->addonId || !compartment->isSystem()) {
|
||||
if (rt->compartmentNameCallback) {
|
||||
(*rt->compartmentNameCallback)(rt, compartment,
|
||||
stat->name,
|
||||
mozilla::ArrayLength(stat->name));
|
||||
} else {
|
||||
strcpy(stat->name, "<unknown>");
|
||||
}
|
||||
} else {
|
||||
strcpy(stat->name, "<platform>");
|
||||
}
|
||||
stat->performance = group->data;
|
||||
if (!set.add(ptr, group)) {
|
||||
// Memory issue
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(processStats.name, "<process>");
|
||||
processStats.addonId = nullptr;
|
||||
processStats.isSystem = true;
|
||||
processStats.performance = rt->stopwatch.performance;
|
||||
|
||||
*processStats = rt->stopwatch.performance;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -925,6 +907,7 @@ JSAutoCompartment::JSAutoCompartment(JSContext* cx, JSScript* target)
|
||||
cx_->enterCompartment(target->compartment());
|
||||
}
|
||||
|
||||
|
||||
JSAutoCompartment::~JSAutoCompartment()
|
||||
{
|
||||
cx_->leaveCompartment(oldCompartment_);
|
||||
|
||||
+50
-54
@@ -712,6 +712,18 @@ typedef void
|
||||
(* JSCompartmentNameCallback)(JSRuntime* rt, JSCompartment* compartment,
|
||||
char* buf, size_t bufsize);
|
||||
|
||||
/**
|
||||
* Callback used to ask the embedding to determine in which
|
||||
* Performance Group the current execution belongs. Typically, this is
|
||||
* used to regroup JSCompartments from several iframes from the same
|
||||
* page or from several compartments of the same addon into a single
|
||||
* Performance Group.
|
||||
*
|
||||
* Returns an opaque key.
|
||||
*/
|
||||
typedef void*
|
||||
(* JSCurrentPerfGroupCallback)(JSContext*);
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static MOZ_ALWAYS_INLINE jsval
|
||||
@@ -5413,9 +5425,10 @@ struct PerformanceGroup {
|
||||
stopwatch_ = nullptr;
|
||||
}
|
||||
|
||||
PerformanceGroup()
|
||||
explicit PerformanceGroup(void* key)
|
||||
: stopwatch_(nullptr)
|
||||
, iteration_(0)
|
||||
, key_(key)
|
||||
, refCount_(0)
|
||||
{ }
|
||||
~PerformanceGroup()
|
||||
@@ -5434,6 +5447,9 @@ struct PerformanceGroup {
|
||||
// may safely overflow.
|
||||
uint64_t iteration_;
|
||||
|
||||
// The hash key for this PerformanceGroup.
|
||||
void* const key_;
|
||||
|
||||
// Increment/decrement the refcounter, return the updated value.
|
||||
uint64_t incRefCount() {
|
||||
MOZ_ASSERT(refCount_ + 1 > 0);
|
||||
@@ -5445,7 +5461,7 @@ struct PerformanceGroup {
|
||||
}
|
||||
friend struct PerformanceGroupHolder;
|
||||
|
||||
private:
|
||||
private:
|
||||
// A reference counter. Maintained by PerformanceGroupHolder.
|
||||
uint64_t refCount_;
|
||||
};
|
||||
@@ -5458,7 +5474,7 @@ struct PerformanceGroupHolder {
|
||||
// Get the group.
|
||||
// On first call, this causes a single Hashtable lookup.
|
||||
// Successive calls do not require further lookups.
|
||||
js::PerformanceGroup* getGroup();
|
||||
js::PerformanceGroup* getGroup(JSContext*);
|
||||
|
||||
// `true` if the this holder is currently associated to a
|
||||
// PerformanceGroup, `false` otherwise. Use this method to avoid
|
||||
@@ -5473,9 +5489,8 @@ struct PerformanceGroupHolder {
|
||||
// (new values of `isSystem()`, `principals()` or `addonId`).
|
||||
void unlink();
|
||||
|
||||
PerformanceGroupHolder(JSRuntime* runtime, JSCompartment* compartment)
|
||||
explicit PerformanceGroupHolder(JSRuntime* runtime)
|
||||
: runtime_(runtime)
|
||||
, compartment_(compartment)
|
||||
, group_(nullptr)
|
||||
{ }
|
||||
~PerformanceGroupHolder();
|
||||
@@ -5483,10 +5498,9 @@ private:
|
||||
// Return the key representing this PerformanceGroup in
|
||||
// Runtime::Stopwatch.
|
||||
// Do not deallocate the key.
|
||||
void* getHashKey();
|
||||
void* getHashKey(JSContext* cx);
|
||||
|
||||
JSRuntime* runtime_;
|
||||
JSCompartment* compartment_;
|
||||
JSRuntime *runtime_;
|
||||
|
||||
// The PerformanceGroup held by this object.
|
||||
// Initially set to `nullptr` until the first cal to `getGroup`.
|
||||
@@ -5505,11 +5519,19 @@ ResetStopwatches(JSRuntime*);
|
||||
/**
|
||||
* Turn on/off stopwatch-based CPU monitoring.
|
||||
*
|
||||
* `SetStopwatchActive` may return `false` if monitoring could not be
|
||||
* activated, which may happen if we are out of memory.
|
||||
* `SetStopwatchIsMonitoringCPOW` or `SetStopwatchIsMonitoringJank`
|
||||
* may return `false` if monitoring could not be activated, which may
|
||||
* happen if we are out of memory.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
SetStopwatchActive(JSRuntime*, bool);
|
||||
SetStopwatchIsMonitoringCPOW(JSRuntime*, bool);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
GetStopwatchIsMonitoringCPOW(JSRuntime*);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
SetStopwatchIsMonitoringJank(JSRuntime*, bool);
|
||||
extern JS_PUBLIC_API(bool)
|
||||
GetStopwatchIsMonitoringJank(JSRuntime*);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
IsStopwatchActive(JSRuntime*);
|
||||
|
||||
@@ -5519,56 +5541,30 @@ IsStopwatchActive(JSRuntime*);
|
||||
extern JS_PUBLIC_API(PerformanceData*)
|
||||
GetPerformanceData(JSRuntime*);
|
||||
|
||||
typedef bool
|
||||
(PerformanceStatsWalker)(JSContext* cx, const PerformanceData& stats, void* closure);
|
||||
|
||||
/**
|
||||
* Performance statistics for a performance group (a process, an
|
||||
* add-on, a webpage, the built-ins or a special compartment).
|
||||
*/
|
||||
struct PerformanceStats {
|
||||
/**
|
||||
* If this group represents an add-on, the ID of the addon,
|
||||
* otherwise `nullptr`.
|
||||
*/
|
||||
JSAddonId* addonId;
|
||||
|
||||
/**
|
||||
* If this group represents a webpage, the process itself or a special
|
||||
* compartment, a human-readable name. Unspecified for add-ons.
|
||||
*/
|
||||
char name[1024];
|
||||
|
||||
/**
|
||||
* `true` if the group represents in system compartments, `false`
|
||||
* otherwise. A group may never contain both system and non-system
|
||||
* compartments.
|
||||
*/
|
||||
bool isSystem;
|
||||
|
||||
/**
|
||||
* Performance information.
|
||||
*/
|
||||
js::PerformanceData performance;
|
||||
|
||||
PerformanceStats()
|
||||
: addonId(nullptr)
|
||||
, isSystem(false)
|
||||
{
|
||||
name[0] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
typedef js::Vector<PerformanceStats, 0, js::SystemAllocPolicy> PerformanceStatsVector;
|
||||
|
||||
/**
|
||||
* Extract the performance statistics.
|
||||
*
|
||||
* After a successful call, `stats` holds the `PerformanceStats` for
|
||||
* all performance groups, and `global` holds a `PerformanceStats`
|
||||
* representing the entire process.
|
||||
* Note that before calling `walker`, we enter the corresponding context.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
GetPerformanceStats(JSRuntime* rt, js::PerformanceStatsVector& stats, js::PerformanceStats& global);
|
||||
IterPerformanceStats(JSContext* cx, PerformanceStatsWalker* walker, js::PerformanceData* process, void* closure);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/**
|
||||
* Callback used to ask the embedding to determine in which
|
||||
* Performance Group a compartment belongs. Typically, this is used to
|
||||
* regroup JSCompartments from several iframes from the same page or
|
||||
* from several compartments of the same addon into a single
|
||||
* Performance Group.
|
||||
*
|
||||
* Returns an opaque key.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetCurrentPerfGroupCallback(JSRuntime *rt, JSCurrentPerfGroupCallback cb);
|
||||
|
||||
|
||||
#endif /* jsapi_h */
|
||||
|
||||
@@ -53,7 +53,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
||||
#endif
|
||||
global_(nullptr),
|
||||
enterCompartmentDepth(0),
|
||||
performanceMonitoring(runtime_, this),
|
||||
performanceMonitoring(runtime_),
|
||||
data(nullptr),
|
||||
objectMetadataCallback(nullptr),
|
||||
lastAnimationTime(0),
|
||||
|
||||
@@ -635,6 +635,7 @@ class FunctionExtended : public JSFunction
|
||||
|
||||
/* Arrow functions store their lexical |this| in the first extended slot. */
|
||||
static const unsigned ARROW_THIS_SLOT = 0;
|
||||
static const unsigned ARROW_NEWTARGET_SLOT = 1;
|
||||
|
||||
static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
|
||||
|
||||
@@ -645,6 +646,9 @@ class FunctionExtended : public JSFunction
|
||||
static inline size_t offsetOfArrowThisSlot() {
|
||||
return offsetOfExtendedSlot(ARROW_THIS_SLOT);
|
||||
}
|
||||
static inline size_t offsetOfArrowNewTargetSlot() {
|
||||
return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class JSFunction;
|
||||
|
||||
+7
-20
@@ -3725,20 +3725,9 @@ CheckCompartment(CompartmentCheckTracer* trc, JSCompartment* thingCompartment,
|
||||
InCrossCompartmentMap((JSObject*)trc->src, thing, kind)));
|
||||
}
|
||||
|
||||
static JSCompartment*
|
||||
CompartmentOfCell(Cell* thing, JSGCTraceKind kind)
|
||||
{
|
||||
if (kind == JSTRACE_OBJECT)
|
||||
return static_cast<JSObject*>(thing)->compartment();
|
||||
else if (kind == JSTRACE_SHAPE)
|
||||
return static_cast<Shape*>(thing)->compartment();
|
||||
else if (kind == JSTRACE_BASE_SHAPE)
|
||||
return static_cast<BaseShape*>(thing)->compartment();
|
||||
else if (kind == JSTRACE_SCRIPT)
|
||||
return static_cast<JSScript*>(thing)->compartment();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
struct MaybeCompartmentFunctor {
|
||||
template <typename T> JSCompartment* operator()(T* t) { return t->maybeCompartment(); }
|
||||
};
|
||||
|
||||
static void
|
||||
CheckCompartmentCallback(JS::CallbackTracer* trcArg, void** thingp, JSGCTraceKind kind)
|
||||
@@ -3746,7 +3735,7 @@ CheckCompartmentCallback(JS::CallbackTracer* trcArg, void** thingp, JSGCTraceKin
|
||||
CompartmentCheckTracer* trc = static_cast<CompartmentCheckTracer*>(trcArg);
|
||||
TenuredCell* thing = TenuredCell::fromPointer(*thingp);
|
||||
|
||||
JSCompartment* comp = CompartmentOfCell(thing, kind);
|
||||
JSCompartment* comp = CallTyped(MaybeCompartmentFunctor(), thing, kind);
|
||||
if (comp && trc->compartment)
|
||||
CheckCompartment(trc, comp, thing, kind);
|
||||
else
|
||||
@@ -3766,7 +3755,7 @@ GCRuntime::checkForCompartmentMismatches()
|
||||
for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
|
||||
trc.src = i.getCell();
|
||||
trc.srcKind = MapAllocToTraceKind(thingKind);
|
||||
trc.compartment = CompartmentOfCell(trc.src, trc.srcKind);
|
||||
trc.compartment = CallTyped(MaybeCompartmentFunctor(), trc.src, trc.srcKind);
|
||||
JS_TraceChildren(&trc, trc.src, trc.srcKind);
|
||||
}
|
||||
}
|
||||
@@ -7172,9 +7161,7 @@ JS::IsIncrementalBarrierNeeded(JSContext* cx)
|
||||
}
|
||||
|
||||
struct IncrementalReferenceBarrierFunctor {
|
||||
template <typename T> void operator()(gc::Cell* cell) {
|
||||
T::writeBarrierPre(static_cast<T*>(cell));
|
||||
}
|
||||
template <typename T> void operator()(T* t) { T::writeBarrierPre(t); }
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
@@ -7183,7 +7170,7 @@ JS::IncrementalReferenceBarrier(GCCellPtr thing)
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
CallTyped(IncrementalReferenceBarrierFunctor(), thing.kind(), thing.asCell());
|
||||
CallTyped(IncrementalReferenceBarrierFunctor(), thing.asCell(), thing.kind());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
||||
@@ -224,6 +224,34 @@ CallTyped(F f, JSGCTraceKind traceKind, Args&&... args)
|
||||
}
|
||||
#undef DEPENDENT_TEMPLATE_HINT
|
||||
|
||||
template <typename F, typename... Args>
|
||||
auto
|
||||
CallTyped(F f, void* thing, JSGCTraceKind traceKind, Args&&... args)
|
||||
-> decltype(f(reinterpret_cast<JSObject*>(0), mozilla::Forward<Args>(args)...))
|
||||
{
|
||||
switch (traceKind) {
|
||||
case JSTRACE_OBJECT:
|
||||
return f(static_cast<JSObject*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_SCRIPT:
|
||||
return f(static_cast<JSScript*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_STRING:
|
||||
return f(static_cast<JSString*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_SYMBOL:
|
||||
return f(static_cast<JS::Symbol*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_BASE_SHAPE:
|
||||
return f(static_cast<BaseShape*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_JITCODE:
|
||||
return f(static_cast<jit::JitCode*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_LAZY_SCRIPT:
|
||||
return f(static_cast<LazyScript*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_SHAPE:
|
||||
return f(static_cast<Shape*>(thing), mozilla::Forward<Args>(args)...);
|
||||
case JSTRACE_OBJECT_GROUP:
|
||||
return f(static_cast<ObjectGroup*>(thing), mozilla::Forward<Args>(args)...);
|
||||
default:
|
||||
MOZ_CRASH("Invalid trace kind in CallTyped.");
|
||||
}
|
||||
}
|
||||
/* Capacity for slotsToThingKind */
|
||||
const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
|
||||
|
||||
|
||||
@@ -4071,15 +4071,44 @@ js::StringConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
str_fromCharCode_few_args(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(args.length() <= JSFatInlineString::MAX_LENGTH_TWO_BYTE);
|
||||
|
||||
char16_t chars[JSFatInlineString::MAX_LENGTH_TWO_BYTE];
|
||||
for (unsigned i = 0; i < args.length(); i++) {
|
||||
uint16_t code;
|
||||
if (!ToUint16(cx, args[i], &code))
|
||||
return false;
|
||||
chars[i] = char16_t(code);
|
||||
}
|
||||
JSString* str = NewStringCopyN<CanGC>(cx, chars, args.length());
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::str_fromCharCode(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
MOZ_ASSERT(args.length() <= ARGS_LENGTH_MAX);
|
||||
|
||||
// Optimize the single-char case.
|
||||
if (args.length() == 1)
|
||||
return str_fromCharCode_one_arg(cx, args[0], args.rval());
|
||||
|
||||
// Optimize the case where the result will definitely fit in an inline
|
||||
// string (thin or fat) and so we don't need to malloc the chars. (We could
|
||||
// cover some cases where args.length() goes up to
|
||||
// JSFatInlineString::MAX_LENGTH_LATIN1 if we also checked if the chars are
|
||||
// all Latin1, but it doesn't seem worth the effort.)
|
||||
if (args.length() <= JSFatInlineString::MAX_LENGTH_TWO_BYTE)
|
||||
return str_fromCharCode_few_args(cx, args);
|
||||
|
||||
char16_t* chars = cx->pod_malloc<char16_t>(args.length() + 1);
|
||||
if (!chars)
|
||||
return false;
|
||||
|
||||
@@ -153,4 +153,24 @@ new TestCase( SECTION, "String.fromCharCode(0x007D)", "}", String.fromCh
|
||||
new TestCase( SECTION, "String.fromCharCode(0x007E)", "~", String.fromCharCode(0x007E) );
|
||||
// new TestCase( SECTION, "String.fromCharCode(0x0020, 0x007F)", "", String.fromCharCode(0x0040, 0x007F) );
|
||||
|
||||
new TestCase( SECTION, "String.fromCharCode(<2 args>)", "ab", String.fromCharCode(97,98));
|
||||
new TestCase( SECTION, "String.fromCharCode(<3 args>)", "abc", String.fromCharCode(97,98,99));
|
||||
new TestCase( SECTION, "String.fromCharCode(<4 args>)", "abcd", String.fromCharCode(97,98,99,100));
|
||||
new TestCase( SECTION, "String.fromCharCode(<5 args>)", "abcde", String.fromCharCode(97,98,99,100,101));
|
||||
new TestCase( SECTION, "String.fromCharCode(<6 args>)", "abcdef", String.fromCharCode(97,98,99,100,101,102));
|
||||
new TestCase( SECTION, "String.fromCharCode(<7 args>)", "abcdefg", String.fromCharCode(97,98,99,100,101,102,103));
|
||||
new TestCase( SECTION, "String.fromCharCode(<8 args>)", "abcdefgh", String.fromCharCode(97,98,99,100,101,102,103,104));
|
||||
new TestCase( SECTION, "String.fromCharCode(<9 args>)", "abcdefghi", String.fromCharCode(97,98,99,100,101,102,103,104,105));
|
||||
new TestCase( SECTION, "String.fromCharCode(<10 args>)", "abcdefghij", String.fromCharCode(97,98,99,100,101,102,103,104,105,106));
|
||||
new TestCase( SECTION, "String.fromCharCode(<11 args>)", "abcdefghijk", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107));
|
||||
new TestCase( SECTION, "String.fromCharCode(<12 args>)", "abcdefghijkl", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108));
|
||||
new TestCase( SECTION, "String.fromCharCode(<13 args>)", "abcdefghijklm", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109));
|
||||
new TestCase( SECTION, "String.fromCharCode(<14 args>)", "abcdefghijklmn", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110));
|
||||
new TestCase( SECTION, "String.fromCharCode(<15 args>)", "abcdefghijklmno", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110,111));
|
||||
new TestCase( SECTION, "String.fromCharCode(<16 args>)", "abcdefghijklmnop", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112));
|
||||
new TestCase( SECTION, "String.fromCharCode(<17 args>)", "abcdefghijklmnopq", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113));
|
||||
new TestCase( SECTION, "String.fromCharCode(<18 args>)", "abcdefghijklmnopqr", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114));
|
||||
new TestCase( SECTION, "String.fromCharCode(<19 args>)", "abcdefghijklmnopqrs", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115));
|
||||
new TestCase( SECTION, "String.fromCharCode(<20 args>)", "abcdefghijklmnopqrst", String.fromCharCode(97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116));
|
||||
|
||||
test();
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// new.target is valid in any arrow function not in a global context.
|
||||
new Function('(() => new.target)()');
|
||||
|
||||
// It's also good inside eval, but not global eval
|
||||
assertThrowsInstanceOf(() => eval('() => new.target'), SyntaxError);
|
||||
|
||||
function assertNewTarget(expected) {
|
||||
assertEq((()=>new.target)(), expected);
|
||||
assertEq(eval('()=>new.target')(), expected);
|
||||
|
||||
// Make sure that arrow functions can escape their original context and
|
||||
// still get the right answer.
|
||||
return (() => new.target);
|
||||
}
|
||||
|
||||
const ITERATIONS = 550;
|
||||
for (let i = 0; i < ITERATIONS; i++)
|
||||
assertEq(assertNewTarget(undefined)(), undefined);
|
||||
|
||||
for (let i = 0; i < ITERATIONS; i++)
|
||||
assertEq(new assertNewTarget(assertNewTarget)(), assertNewTarget);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
||||
@@ -1,11 +0,0 @@
|
||||
// new.target is valid inside Function() invocations
|
||||
var func = new Function("new.target");
|
||||
|
||||
// new.target is invalid inside eval, even (for now!) eval inside a function.
|
||||
assertThrowsInstanceOf(() => eval('new.target'), SyntaxError);
|
||||
|
||||
function evalInFunction() { eval('new.target'); }
|
||||
assertThrowsInstanceOf(() => evalInFunction(), SyntaxError);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
||||
@@ -1,3 +1,6 @@
|
||||
// new.target is valid inside Function() invocations
|
||||
var func = new Function("new.target");
|
||||
|
||||
// Note that this will also test new.target in ion inlines. When the toplevel
|
||||
// script is compiled, assertNewTarget will be inlined.
|
||||
function assertNewTarget(expected, unused) { assertEq(new.target, expected); }
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Eval of new.target is invalid outside functions.
|
||||
try {
|
||||
eval('new.target');
|
||||
assertEq(false, true);
|
||||
} catch (e if e instanceof SyntaxError) { }
|
||||
|
||||
// new.target is invalid inside eval inside top-level arrow functions
|
||||
assertThrowsInstanceOf(() => eval('new.target'), SyntaxError);
|
||||
|
||||
// new.target is invalid inside indirect eval.
|
||||
let ieval = eval;
|
||||
try {
|
||||
(function () ieval('new.target'))();
|
||||
assertEq(false, true);
|
||||
} catch (e if e instanceof SyntaxError) { }
|
||||
|
||||
function assertNewTarget(expected) {
|
||||
assertEq(eval('new.target'), expected);
|
||||
assertEq((()=>eval('new.target'))(), expected);
|
||||
|
||||
// Also test nestings "by induction"
|
||||
assertEq(eval('eval("new.target")'), expected);
|
||||
assertEq(eval("eval('eval(`new.target`)')"), expected);
|
||||
}
|
||||
|
||||
const ITERATIONS = 550;
|
||||
for (let i = 0; i < ITERATIONS; i++)
|
||||
assertNewTarget(undefined);
|
||||
|
||||
for (let i = 0; i < ITERATIONS; i++)
|
||||
new assertNewTarget(assertNewTarget);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
||||
@@ -0,0 +1,11 @@
|
||||
class foo {
|
||||
constructor() { }
|
||||
|
||||
method() {
|
||||
return (() => eval('super.toString'));
|
||||
}
|
||||
}
|
||||
assertEq(new foo().method()(), Object.prototype.toString);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0,0,"OK");
|
||||
@@ -0,0 +1,13 @@
|
||||
// It's invalid to eval super.prop inside a nested non-method, even if it
|
||||
// appears inside a method definition
|
||||
assertThrowsInstanceOf(() =>
|
||||
({
|
||||
method() {
|
||||
(function () {
|
||||
eval('super.toString');
|
||||
})();
|
||||
}
|
||||
}).method(), SyntaxError);
|
||||
|
||||
if (typeof reportCompare === 'function')
|
||||
reportCompare(0,0,"OK");
|
||||
@@ -11,8 +11,8 @@ function testNewTarget() {
|
||||
// invalid in top-level scripts
|
||||
assertError("new.target", SyntaxError);
|
||||
|
||||
// invalid (for now!) in any arrow function
|
||||
assertError("function foo() { (() => new.target) }", SyntaxError);
|
||||
// valid in arrow functions inside functions
|
||||
assertInFunctionExpr("()=>new.target", arrowExpr([], newTarget()));
|
||||
assertError("(() => new.target))", SyntaxError);
|
||||
|
||||
// invalid (for now!) in generators
|
||||
|
||||
@@ -6288,7 +6288,7 @@ EvaluateInEnv(JSContext* cx, Handle<Env*> env, HandleValue thisv, AbstractFrameP
|
||||
|
||||
script->setActiveEval();
|
||||
ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG;
|
||||
return ExecuteKernel(cx, script, *env, thisv, type, frame, rval.address());
|
||||
return ExecuteKernel(cx, script, *env, thisv, NullValue(), type, frame, rval.address());
|
||||
}
|
||||
|
||||
enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false };
|
||||
|
||||
+50
-44
@@ -362,8 +362,8 @@ InvokeState::pushInterpreterFrame(JSContext* cx)
|
||||
InterpreterFrame*
|
||||
ExecuteState::pushInterpreterFrame(JSContext* cx)
|
||||
{
|
||||
return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, scopeChain_,
|
||||
type_, evalInFrame_);
|
||||
return cx->runtime()->interpreterStack().pushExecuteFrame(cx, script_, thisv_, newTargetValue_,
|
||||
scopeChain_, type_, evalInFrame_);
|
||||
}
|
||||
namespace js {
|
||||
|
||||
@@ -381,8 +381,7 @@ struct AutoStopwatch final
|
||||
//
|
||||
// Previous owner is restored upon destruction.
|
||||
explicit inline AutoStopwatch(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: compartment_(nullptr)
|
||||
, runtime_(nullptr)
|
||||
: cx_(cx)
|
||||
, iteration_(0)
|
||||
, isActive_(false)
|
||||
, isTop_(false)
|
||||
@@ -391,16 +390,18 @@ struct AutoStopwatch final
|
||||
, CPOWTimeStart_(0)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
runtime_ = cx->runtime();
|
||||
if (!runtime_->stopwatch.isActive())
|
||||
return;
|
||||
compartment_ = cx->compartment();
|
||||
MOZ_ASSERT(compartment_);
|
||||
if (compartment_->scheduledForDestruction)
|
||||
return;
|
||||
iteration_ = runtime_->stopwatch.iteration;
|
||||
|
||||
PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
|
||||
JSRuntime* runtime = JS_GetRuntime(cx_);
|
||||
if (!runtime->stopwatch.isMonitoringJank())
|
||||
return;
|
||||
|
||||
JSCompartment* compartment = cx_->compartment();
|
||||
if (compartment->scheduledForDestruction)
|
||||
return;
|
||||
|
||||
iteration_ = runtime->stopwatch.iteration;
|
||||
|
||||
PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx);
|
||||
MOZ_ASSERT(group);
|
||||
|
||||
if (group->hasStopwatch(iteration_)) {
|
||||
@@ -410,22 +411,22 @@ struct AutoStopwatch final
|
||||
}
|
||||
|
||||
// Start the stopwatch.
|
||||
if (!this->getTimes(&userTimeStart_, &systemTimeStart_))
|
||||
if (!this->getTimes(runtime, &userTimeStart_, &systemTimeStart_))
|
||||
return;
|
||||
isActive_ = true;
|
||||
CPOWTimeStart_ = runtime_->stopwatch.performance.totalCPOWTime;
|
||||
CPOWTimeStart_ = runtime->stopwatch.performance.totalCPOWTime;
|
||||
|
||||
// We are now in charge of monitoring this group for the tick,
|
||||
// until destruction of `this` or until we enter a nested event
|
||||
// loop and `iteration_` is incremented.
|
||||
group->acquireStopwatch(iteration_, this);
|
||||
|
||||
if (runtime_->stopwatch.isEmpty) {
|
||||
if (runtime->stopwatch.isEmpty) {
|
||||
// This is the topmost stopwatch on the stack.
|
||||
// It will be in charge of updating the per-process
|
||||
// performance data.
|
||||
runtime_->stopwatch.isEmpty = false;
|
||||
runtime_->stopwatch.performance.ticks++;
|
||||
runtime->stopwatch.isEmpty = false;
|
||||
runtime->stopwatch.performance.ticks++;
|
||||
isTop_ = true;
|
||||
}
|
||||
}
|
||||
@@ -435,32 +436,35 @@ struct AutoStopwatch final
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!compartment_->scheduledForDestruction);
|
||||
JSRuntime* runtime = JS_GetRuntime(cx_);
|
||||
JSCompartment* compartment = cx_->compartment();
|
||||
|
||||
if (!runtime_->stopwatch.isActive()) {
|
||||
MOZ_ASSERT(!compartment->scheduledForDestruction);
|
||||
|
||||
if (!runtime->stopwatch.isMonitoringJank()) {
|
||||
// Monitoring has been stopped while we were
|
||||
// executing the code. Drop everything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (iteration_ != runtime_->stopwatch.iteration) {
|
||||
if (iteration_ != runtime->stopwatch.iteration) {
|
||||
// We have entered a nested event loop at some point.
|
||||
// Any information we may have is obsolete.
|
||||
return;
|
||||
}
|
||||
|
||||
PerformanceGroup* group = compartment_->performanceMonitoring.getGroup();
|
||||
PerformanceGroup *group = compartment->performanceMonitoring.getGroup(cx_);
|
||||
MOZ_ASSERT(group);
|
||||
|
||||
// Compute time spent.
|
||||
group->releaseStopwatch(iteration_, this);
|
||||
uint64_t userTimeEnd, systemTimeEnd;
|
||||
if (!this->getTimes(&userTimeEnd, &systemTimeEnd))
|
||||
if (!this->getTimes(runtime, &userTimeEnd, &systemTimeEnd))
|
||||
return;
|
||||
|
||||
uint64_t userTimeDelta = userTimeEnd - userTimeStart_;
|
||||
uint64_t systemTimeDelta = systemTimeEnd - systemTimeStart_;
|
||||
uint64_t CPOWTimeDelta = runtime_->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
|
||||
uint64_t CPOWTimeDelta = runtime->stopwatch.performance.totalCPOWTime - CPOWTimeStart_;
|
||||
group->data.totalUserTime += userTimeDelta;
|
||||
group->data.totalSystemTime += systemTimeDelta;
|
||||
group->data.totalCPOWTime += CPOWTimeDelta;
|
||||
@@ -472,10 +476,10 @@ struct AutoStopwatch final
|
||||
if (isTop_) {
|
||||
// This is the topmost stopwatch on the stack.
|
||||
// Record the timing information.
|
||||
runtime_->stopwatch.performance.totalUserTime = userTimeEnd;
|
||||
runtime_->stopwatch.performance.totalSystemTime = systemTimeEnd;
|
||||
updateDurations(totalTimeDelta, runtime_->stopwatch.performance.durations);
|
||||
runtime_->stopwatch.isEmpty = true;
|
||||
runtime->stopwatch.performance.totalUserTime = userTimeEnd;
|
||||
runtime->stopwatch.performance.totalSystemTime = systemTimeEnd;
|
||||
updateDurations(totalTimeDelta, runtime->stopwatch.performance.durations);
|
||||
runtime->stopwatch.isEmpty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,7 +503,7 @@ struct AutoStopwatch final
|
||||
// Get the OS-reported time spent in userland/systemland, in
|
||||
// microseconds. On most platforms, this data is per-thread,
|
||||
// but on some platforms we need to fall back to per-process.
|
||||
bool getTimes(uint64_t* userTime, uint64_t* systemTime) const {
|
||||
bool getTimes(JSRuntime* runtime, uint64_t* userTime, uint64_t* systemTime) const {
|
||||
MOZ_ASSERT(userTime);
|
||||
MOZ_ASSERT(systemTime);
|
||||
|
||||
@@ -563,12 +567,12 @@ struct AutoStopwatch final
|
||||
kernelTimeInt.LowPart = kernelFileTime.dwLowDateTime;
|
||||
kernelTimeInt.HighPart = kernelFileTime.dwHighDateTime;
|
||||
// Convert 100 ns to 1 us, make sure that the result is monotonic
|
||||
*systemTime = runtime_-> stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
|
||||
*systemTime = runtime->stopwatch.systemTimeFix.monotonize(kernelTimeInt.QuadPart / 10);
|
||||
|
||||
userTimeInt.LowPart = userFileTime.dwLowDateTime;
|
||||
userTimeInt.HighPart = userFileTime.dwHighDateTime;
|
||||
// Convert 100 ns to 1 us, make sure that the result is monotonic
|
||||
*userTime = runtime_-> stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
|
||||
*userTime = runtime->stopwatch.userTimeFix.monotonize(userTimeInt.QuadPart / 10);
|
||||
|
||||
#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
||||
@@ -576,13 +580,9 @@ struct AutoStopwatch final
|
||||
}
|
||||
|
||||
private:
|
||||
// The compartment with which this object was initialized.
|
||||
// The context with which this object was initialized.
|
||||
// Non-null.
|
||||
JSCompartment* compartment_;
|
||||
|
||||
// The runtime with which this object was initialized.
|
||||
// Non-null.
|
||||
JSRuntime* runtime_;
|
||||
JSContext* const cx_;
|
||||
|
||||
// An indication of the number of times we have entered the event
|
||||
// loop. Used only for comparison.
|
||||
@@ -844,7 +844,8 @@ js::InvokeSetter(JSContext* cx, const Value& thisv, Value fval, HandleValue v)
|
||||
|
||||
bool
|
||||
js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, const Value& thisv,
|
||||
ExecuteType type, AbstractFramePtr evalInFrame, Value* result)
|
||||
const Value& newTargetValue, ExecuteType type, AbstractFramePtr evalInFrame,
|
||||
Value* result)
|
||||
{
|
||||
MOZ_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
|
||||
MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, !IsSyntacticScope(&scopeChainArg));
|
||||
@@ -879,7 +880,7 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c
|
||||
TypeScript::SetThis(cx, script, thisv);
|
||||
|
||||
probes::StartExecution(script);
|
||||
ExecuteState state(cx, script, thisv, scopeChainArg, type, evalInFrame, result);
|
||||
ExecuteState state(cx, script, thisv, newTargetValue, scopeChainArg, type, evalInFrame, result);
|
||||
bool ok = RunScript(cx, state);
|
||||
probes::StopExecution(script);
|
||||
|
||||
@@ -919,7 +920,7 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value*
|
||||
return false;
|
||||
Value thisv = ObjectValue(*thisObj);
|
||||
|
||||
return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
|
||||
return ExecuteKernel(cx, script, *scopeChain, thisv, NullValue(), EXECUTE_GLOBAL,
|
||||
NullFramePtr() /* evalInFrame */, rval);
|
||||
}
|
||||
|
||||
@@ -3465,12 +3466,14 @@ CASE(JSOP_LAMBDA_ARROW)
|
||||
{
|
||||
/* Load the specified function object literal. */
|
||||
ReservedRooted<JSFunction*> fun(&rootFunction0, script->getFunction(GET_UINT32_INDEX(REGS.pc)));
|
||||
ReservedRooted<Value> thisv(&rootValue0, REGS.sp[-1]);
|
||||
JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv);
|
||||
ReservedRooted<Value> thisv(&rootValue0, REGS.sp[-2]);
|
||||
ReservedRooted<Value> newTarget(&rootValue1, REGS.sp[-1]);
|
||||
JSObject* obj = LambdaArrow(cx, fun, REGS.fp()->scopeChain(), thisv, newTarget);
|
||||
if (!obj)
|
||||
goto error;
|
||||
MOZ_ASSERT(obj->getProto());
|
||||
REGS.sp[-1].setObject(*obj);
|
||||
REGS.sp[-2].setObject(*obj);
|
||||
REGS.sp--;
|
||||
}
|
||||
END_CASE(JSOP_LAMBDA_ARROW)
|
||||
|
||||
@@ -4010,6 +4013,7 @@ END_CASE(JSOP_SUPERBASE)
|
||||
|
||||
CASE(JSOP_NEWTARGET)
|
||||
PUSH_COPY(REGS.fp()->newTarget());
|
||||
MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
|
||||
END_CASE(JSOP_NEWTARGET)
|
||||
|
||||
DEFAULT()
|
||||
@@ -4211,7 +4215,8 @@ js::Lambda(JSContext* cx, HandleFunction fun, HandleObject parent)
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue thisv)
|
||||
js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue thisv,
|
||||
HandleValue newTargetv)
|
||||
{
|
||||
MOZ_ASSERT(fun->isArrow());
|
||||
|
||||
@@ -4222,6 +4227,7 @@ js::LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleVa
|
||||
|
||||
MOZ_ASSERT(clone->as<JSFunction>().isArrow());
|
||||
clone->as<JSFunction>().setExtendedSlot(0, thisv);
|
||||
clone->as<JSFunction>().setExtendedSlot(1, newTargetv);
|
||||
|
||||
MOZ_ASSERT(fun->global() == clone->global());
|
||||
return clone;
|
||||
|
||||
+11
-4
@@ -105,7 +105,8 @@ InvokeConstructor(JSContext* cx, Value fval, unsigned argc, const Value* argv,
|
||||
*/
|
||||
extern bool
|
||||
ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChain, const Value& thisv,
|
||||
ExecuteType type, AbstractFramePtr evalInFrame, Value* result);
|
||||
const Value& newTargetVal, ExecuteType type, AbstractFramePtr evalInFrame,
|
||||
Value* result);
|
||||
|
||||
/* Execute a script with the given scopeChain as global code. */
|
||||
extern bool
|
||||
@@ -163,23 +164,28 @@ class ExecuteState : public RunState
|
||||
ExecuteType type_;
|
||||
|
||||
RootedValue thisv_;
|
||||
RootedValue newTargetValue_;
|
||||
RootedObject scopeChain_;
|
||||
|
||||
AbstractFramePtr evalInFrame_;
|
||||
Value* result_;
|
||||
|
||||
public:
|
||||
ExecuteState(JSContext* cx, JSScript* script, const Value& thisv, JSObject& scopeChain,
|
||||
ExecuteType type, AbstractFramePtr evalInFrame, Value* result)
|
||||
ExecuteState(JSContext* cx, JSScript* script, const Value& thisv, const Value& newTargetValue,
|
||||
JSObject& scopeChain, ExecuteType type, AbstractFramePtr evalInFrame,
|
||||
Value* result)
|
||||
: RunState(cx, Execute, script),
|
||||
type_(type),
|
||||
thisv_(cx, thisv),
|
||||
newTargetValue_(cx, newTargetValue),
|
||||
scopeChain_(cx, &scopeChain),
|
||||
evalInFrame_(evalInFrame),
|
||||
result_(result)
|
||||
{ }
|
||||
|
||||
Value* addressOfThisv() { return thisv_.address(); }
|
||||
Value thisv() { return thisv_; }
|
||||
Value newTarget() { return newTargetValue_; }
|
||||
JSObject* scopeChain() const { return scopeChain_; }
|
||||
ExecuteType type() const { return type_; }
|
||||
|
||||
@@ -353,7 +359,8 @@ JSObject*
|
||||
Lambda(JSContext* cx, HandleFunction fun, HandleObject parent);
|
||||
|
||||
JSObject*
|
||||
LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue thisv);
|
||||
LambdaArrow(JSContext* cx, HandleFunction fun, HandleObject parent, HandleValue thisv,
|
||||
HandleValue newTargetv);
|
||||
|
||||
bool
|
||||
GetElement(JSContext* cx, MutableHandleValue lref, HandleValue rref, MutableHandleValue res);
|
||||
|
||||
+1
-1
@@ -1340,7 +1340,7 @@
|
||||
* Operands: uint32_t funcIndex
|
||||
* Stack: this => obj
|
||||
*/ \
|
||||
macro(JSOP_LAMBDA_ARROW, 131, "lambda_arrow", NULL, 5, 1, 1, JOF_OBJECT) \
|
||||
macro(JSOP_LAMBDA_ARROW, 131, "lambda_arrow", NULL, 5, 2, 1, JOF_OBJECT) \
|
||||
\
|
||||
/*
|
||||
* Pushes current callee onto the stack.
|
||||
|
||||
+31
-18
@@ -860,17 +860,25 @@ js::ResetStopwatches(JSRuntime* rt)
|
||||
}
|
||||
|
||||
bool
|
||||
js::SetStopwatchActive(JSRuntime* rt, bool isActive)
|
||||
js::SetStopwatchIsMonitoringJank(JSRuntime* rt, bool value)
|
||||
{
|
||||
MOZ_ASSERT(rt);
|
||||
return rt->stopwatch.setIsActive(isActive);
|
||||
return rt->stopwatch.setIsMonitoringJank(value);
|
||||
}
|
||||
bool
|
||||
js::GetStopwatchIsMonitoringJank(JSRuntime* rt)
|
||||
{
|
||||
return rt->stopwatch.isMonitoringJank();
|
||||
}
|
||||
|
||||
bool
|
||||
js::IsStopwatchActive(JSRuntime* rt)
|
||||
js::SetStopwatchIsMonitoringCPOW(JSRuntime* rt, bool value)
|
||||
{
|
||||
MOZ_ASSERT(rt);
|
||||
return rt->stopwatch.isActive();
|
||||
return rt->stopwatch.setIsMonitoringCPOW(value);
|
||||
}
|
||||
bool
|
||||
js::GetStopwatchIsMonitoringCPOW(JSRuntime* rt)
|
||||
{
|
||||
return rt->stopwatch.isMonitoringCPOW();
|
||||
}
|
||||
|
||||
js::PerformanceGroupHolder::~PerformanceGroupHolder()
|
||||
@@ -879,15 +887,14 @@ js::PerformanceGroupHolder::~PerformanceGroupHolder()
|
||||
}
|
||||
|
||||
void*
|
||||
js::PerformanceGroupHolder::getHashKey()
|
||||
js::PerformanceGroupHolder::getHashKey(JSContext* cx)
|
||||
{
|
||||
return compartment_->isSystem() ?
|
||||
(void*)compartment_->addonId :
|
||||
(void*)JS_GetCompartmentPrincipals(compartment_);
|
||||
// This key may be `nullptr` if we have `isSystem() == true`
|
||||
// and `compartment_->addonId`. This is absolutely correct,
|
||||
// and this represents the `PerformanceGroup` used to track
|
||||
// the performance of the the platform compartments.
|
||||
if (runtime_->stopwatch.currentPerfGroupCallback) {
|
||||
return (*runtime_->stopwatch.currentPerfGroupCallback)(cx);
|
||||
}
|
||||
|
||||
// As a fallback, put everything in the same PerformanceGroup.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -908,26 +915,26 @@ js::PerformanceGroupHolder::unlink()
|
||||
|
||||
|
||||
JSRuntime::Stopwatch::Groups::Ptr ptr =
|
||||
runtime_->stopwatch.groups_.lookup(getHashKey());
|
||||
runtime_->stopwatch.groups_.lookup(group->key_);
|
||||
MOZ_ASSERT(ptr);
|
||||
runtime_->stopwatch.groups_.remove(ptr);
|
||||
js_delete(group);
|
||||
}
|
||||
|
||||
PerformanceGroup*
|
||||
js::PerformanceGroupHolder::getGroup()
|
||||
js::PerformanceGroupHolder::getGroup(JSContext* cx)
|
||||
{
|
||||
if (group_)
|
||||
return group_;
|
||||
|
||||
void* key = getHashKey();
|
||||
void* key = getHashKey(cx);
|
||||
JSRuntime::Stopwatch::Groups::AddPtr ptr =
|
||||
runtime_->stopwatch.groups_.lookupForAdd(key);
|
||||
if (ptr) {
|
||||
group_ = ptr->value();
|
||||
MOZ_ASSERT(group_);
|
||||
} else {
|
||||
group_ = runtime_->new_<PerformanceGroup>();
|
||||
group_ = runtime_->new_<PerformanceGroup>(key);
|
||||
runtime_->stopwatch.groups_.add(ptr, key, group_);
|
||||
}
|
||||
|
||||
@@ -941,3 +948,9 @@ js::GetPerformanceData(JSRuntime* rt)
|
||||
{
|
||||
return &rt->stopwatch.performance;
|
||||
}
|
||||
|
||||
void
|
||||
JS_SetCurrentPerfGroupCallback(JSRuntime *rt, JSCurrentPerfGroupCallback cb)
|
||||
{
|
||||
rt->stopwatch.currentPerfGroupCallback = cb;
|
||||
}
|
||||
|
||||
+35
-12
@@ -1472,10 +1472,24 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
*/
|
||||
js::PerformanceData performance;
|
||||
|
||||
/**
|
||||
* Callback used to ask the embedding to determine in which
|
||||
* Performance Group the current execution belongs. Typically, this is
|
||||
* used to regroup JSCompartments from several iframes from the same
|
||||
* page or from several compartments of the same addon into a single
|
||||
* Performance Group.
|
||||
*
|
||||
* May be `nullptr`, in which case we put all the JSCompartments
|
||||
* in the same PerformanceGroup.
|
||||
*/
|
||||
JSCurrentPerfGroupCallback currentPerfGroupCallback;
|
||||
|
||||
Stopwatch()
|
||||
: iteration(0)
|
||||
, isEmpty(true)
|
||||
, isActive_(false)
|
||||
, currentPerfGroupCallback(nullptr)
|
||||
, isMonitoringJank_(false)
|
||||
, isMonitoringCPOW_(false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
@@ -1489,9 +1503,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
++iteration;
|
||||
isEmpty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate/deactivate stopwatch measurement.
|
||||
* Activate/deactivate stopwatch measurement of jank.
|
||||
*
|
||||
* Noop if `value` is `true` and the stopwatch is already active,
|
||||
* or if `value` is `false` and the stopwatch is already inactive.
|
||||
@@ -1501,8 +1514,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
*
|
||||
* May return `false` if the underlying hashtable cannot be allocated.
|
||||
*/
|
||||
bool setIsActive(bool value) {
|
||||
if (isActive_ != value)
|
||||
bool setIsMonitoringJank(bool value) {
|
||||
if (isMonitoringJank_ != value)
|
||||
reset();
|
||||
|
||||
if (value && !groups_.initialized()) {
|
||||
@@ -1510,15 +1523,24 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
return false;
|
||||
}
|
||||
|
||||
isActive_ = value;
|
||||
isMonitoringJank_ = value;
|
||||
return true;
|
||||
}
|
||||
bool isMonitoringJank() const {
|
||||
return isMonitoringJank_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Activate/deactivate stopwatch measurement of CPOW.
|
||||
*/
|
||||
bool setIsMonitoringCPOW(bool value) {
|
||||
isMonitoringCPOW_ = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the stopwatch is currently monitoring, `false` otherwise.
|
||||
*/
|
||||
bool isActive() const {
|
||||
return isActive_;
|
||||
bool isMonitoringCPOW() const {
|
||||
return isMonitoringCPOW_;
|
||||
}
|
||||
|
||||
// Some systems have non-monotonic clocks. While we cannot
|
||||
@@ -1569,7 +1591,8 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
/**
|
||||
* `true` if stopwatch monitoring is active, `false` otherwise.
|
||||
*/
|
||||
bool isActive_;
|
||||
bool isMonitoringJank_;
|
||||
bool isMonitoringCPOW_;
|
||||
};
|
||||
Stopwatch stopwatch;
|
||||
};
|
||||
|
||||
@@ -816,6 +816,16 @@ AbstractFramePtr::thisValue() const
|
||||
return asRematerializedFrame()->thisValue();
|
||||
}
|
||||
|
||||
inline Value
|
||||
AbstractFramePtr::newTarget() const
|
||||
{
|
||||
if (isInterpreterFrame())
|
||||
return asInterpreterFrame()->newTarget();
|
||||
if (isBaselineFrame())
|
||||
return asBaselineFrame()->newTarget();
|
||||
return asRematerializedFrame()->newTarget();
|
||||
}
|
||||
|
||||
inline bool
|
||||
AbstractFramePtr::freshenBlock(JSContext *cx) const
|
||||
{
|
||||
|
||||
+38
-12
@@ -33,7 +33,8 @@ using mozilla::PodCopy;
|
||||
|
||||
void
|
||||
InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr evalInFramePrev,
|
||||
const Value& thisv, HandleObject scopeChain, ExecuteType type)
|
||||
const Value& thisv, const Value& newTargetValue, HandleObject scopeChain,
|
||||
ExecuteType type)
|
||||
{
|
||||
/*
|
||||
* See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
|
||||
@@ -43,11 +44,17 @@ InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractF
|
||||
flags_ = type | HAS_SCOPECHAIN;
|
||||
|
||||
JSObject* callee = nullptr;
|
||||
|
||||
// newTarget = NullValue is an initial sentinel for "please fill me in from the stack".
|
||||
// It should never be passed from Ion code.
|
||||
RootedValue newTarget(cx, newTargetValue);
|
||||
if (!(flags_ & (GLOBAL))) {
|
||||
if (evalInFramePrev) {
|
||||
MOZ_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame());
|
||||
if (evalInFramePrev.isFunctionFrame()) {
|
||||
callee = evalInFramePrev.callee();
|
||||
if (newTarget.isNull())
|
||||
newTarget = evalInFramePrev.newTarget();
|
||||
flags_ |= FUNCTION;
|
||||
} else {
|
||||
flags_ |= GLOBAL;
|
||||
@@ -57,6 +64,8 @@ InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractF
|
||||
MOZ_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame());
|
||||
MOZ_ASSERT(!iter.isAsmJS());
|
||||
if (iter.isFunctionFrame()) {
|
||||
if (newTarget.isNull())
|
||||
newTarget = iter.newTarget();
|
||||
callee = iter.callee(cx);
|
||||
flags_ |= FUNCTION;
|
||||
} else {
|
||||
@@ -65,21 +74,22 @@ InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractF
|
||||
}
|
||||
}
|
||||
|
||||
Value* dstvp = (Value*)this - 2;
|
||||
dstvp[1] = thisv;
|
||||
Value* dstvp = (Value*)this - 3;
|
||||
dstvp[2] = thisv;
|
||||
|
||||
if (isFunctionFrame()) {
|
||||
dstvp[0] = ObjectValue(*callee);
|
||||
dstvp[1] = ObjectValue(*callee);
|
||||
exec.fun = &callee->as<JSFunction>();
|
||||
u.evalScript = script;
|
||||
} else {
|
||||
MOZ_ASSERT(isGlobalFrame());
|
||||
dstvp[0] = NullValue();
|
||||
dstvp[1] = NullValue();
|
||||
exec.script = script;
|
||||
#ifdef DEBUG
|
||||
u.evalScript = (JSScript*)0xbad;
|
||||
#endif
|
||||
}
|
||||
dstvp[0] = newTarget;
|
||||
|
||||
scopeChain_ = scopeChain.get();
|
||||
prev_ = nullptr;
|
||||
@@ -399,8 +409,8 @@ InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc)
|
||||
unsigned argc = Max(numActualArgs(), numFormalArgs());
|
||||
TraceRootRange(trc, argc + 2 + isConstructing(), argv_ - 2, "fp argv");
|
||||
} else {
|
||||
// Mark callee and |this|
|
||||
TraceRootRange(trc, 2, ((Value*)this) - 2, "stack callee and this");
|
||||
// Mark callee, |this|, and newTarget
|
||||
TraceRootRange(trc, 3, ((Value*)this) - 3, "stack callee, this, newTarget");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,19 +468,19 @@ InterpreterStack::pushInvokeFrame(JSContext* cx, const CallArgs& args, InitialFr
|
||||
|
||||
InterpreterFrame*
|
||||
InterpreterStack::pushExecuteFrame(JSContext* cx, HandleScript script, const Value& thisv,
|
||||
HandleObject scopeChain, ExecuteType type,
|
||||
AbstractFramePtr evalInFrame)
|
||||
const Value& newTargetValue, HandleObject scopeChain,
|
||||
ExecuteType type, AbstractFramePtr evalInFrame)
|
||||
{
|
||||
LifoAlloc::Mark mark = allocator_.mark();
|
||||
|
||||
unsigned nvars = 2 /* callee, this */ + script->nslots();
|
||||
unsigned nvars = 3 /* callee, this, newTarget */ + script->nslots();
|
||||
uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
|
||||
if (!buffer)
|
||||
return nullptr;
|
||||
|
||||
InterpreterFrame* fp = reinterpret_cast<InterpreterFrame*>(buffer + 2 * sizeof(Value));
|
||||
InterpreterFrame* fp = reinterpret_cast<InterpreterFrame*>(buffer + 3 * sizeof(Value));
|
||||
fp->mark_ = mark;
|
||||
fp->initExecuteFrame(cx, script, evalInFrame, thisv, scopeChain, type);
|
||||
fp->initExecuteFrame(cx, script, evalInFrame, thisv, newTargetValue, scopeChain, type);
|
||||
fp->initLocals();
|
||||
|
||||
return fp;
|
||||
@@ -1252,6 +1262,22 @@ FrameIter::thisv(JSContext* cx) const
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
||||
Value
|
||||
FrameIter::newTarget() const
|
||||
{
|
||||
switch (data_.state_) {
|
||||
case DONE:
|
||||
case ASMJS:
|
||||
break;
|
||||
case INTERP:
|
||||
return interpFrame()->newTarget();
|
||||
case JIT:
|
||||
MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
return data_.jitFrames_.baselineFrame()->newTarget();
|
||||
}
|
||||
MOZ_CRASH("Unexpected state");
|
||||
}
|
||||
|
||||
Value
|
||||
FrameIter::returnValue() const
|
||||
{
|
||||
|
||||
+15
-5
@@ -204,6 +204,8 @@ class AbstractFramePtr
|
||||
inline Value calleev() const;
|
||||
inline Value& thisValue() const;
|
||||
|
||||
inline Value newTarget() const;
|
||||
|
||||
inline bool isNonEvalFunctionFrame() const;
|
||||
inline bool isNonStrictDirectEvalFrame() const;
|
||||
inline bool isStrictEvalFrame() const;
|
||||
@@ -400,7 +402,8 @@ class InterpreterFrame
|
||||
|
||||
/* Used for global and eval frames. */
|
||||
void initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr prev,
|
||||
const Value& thisv, HandleObject scopeChain, ExecuteType type);
|
||||
const Value& thisv, const Value& newTargetValue,
|
||||
HandleObject scopeChain, ExecuteType type);
|
||||
|
||||
public:
|
||||
/*
|
||||
@@ -743,8 +746,13 @@ class InterpreterFrame
|
||||
* frame.
|
||||
*/
|
||||
Value newTarget() const {
|
||||
// new.target in eval() NYI.
|
||||
MOZ_ASSERT(isNonEvalFunctionFrame());
|
||||
MOZ_ASSERT(isFunctionFrame());
|
||||
if (isEvalFrame())
|
||||
return ((Value*)this)[-3];
|
||||
|
||||
if (callee().isArrow())
|
||||
return callee().getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT);
|
||||
|
||||
if (isConstructing()) {
|
||||
unsigned pushedArgs = Max(numFormalArgs(), numActualArgs());
|
||||
return argv()[pushedArgs];
|
||||
@@ -1009,8 +1017,8 @@ class InterpreterStack
|
||||
|
||||
// For execution of eval or global code.
|
||||
InterpreterFrame* pushExecuteFrame(JSContext* cx, HandleScript script, const Value& thisv,
|
||||
HandleObject scopeChain, ExecuteType type,
|
||||
AbstractFramePtr evalInFrame);
|
||||
const Value& newTargetValue, HandleObject scopeChain,
|
||||
ExecuteType type, AbstractFramePtr evalInFrame);
|
||||
|
||||
// Called to invoke a function.
|
||||
InterpreterFrame* pushInvokeFrame(JSContext* cx, const CallArgs& args,
|
||||
@@ -1746,6 +1754,8 @@ class FrameIter
|
||||
Value computedThisValue() const;
|
||||
Value thisv(JSContext* cx) const;
|
||||
|
||||
Value newTarget() const;
|
||||
|
||||
Value returnValue() const;
|
||||
void setReturnValue(const Value& v);
|
||||
|
||||
|
||||
+6
-21
@@ -58,33 +58,18 @@ Concrete<void>::size(mozilla::MallocSizeOf mallocSizeof) const
|
||||
MOZ_CRASH("null ubi::Node");
|
||||
}
|
||||
|
||||
struct Node::ConstructFunctor : public BoolDefaultAdaptor<Value, false> {
|
||||
template <typename T> bool operator()(T* t, Node* node) { node->construct(t); return true; }
|
||||
};
|
||||
|
||||
Node::Node(JSGCTraceKind kind, void* ptr)
|
||||
{
|
||||
switch (kind) {
|
||||
case JSTRACE_OBJECT: construct(static_cast<JSObject*>(ptr)); break;
|
||||
case JSTRACE_SCRIPT: construct(static_cast<JSScript*>(ptr)); break;
|
||||
case JSTRACE_STRING: construct(static_cast<JSString*>(ptr)); break;
|
||||
case JSTRACE_SYMBOL: construct(static_cast<JS::Symbol*>(ptr)); break;
|
||||
case JSTRACE_BASE_SHAPE: construct(static_cast<js::BaseShape*>(ptr)); break;
|
||||
case JSTRACE_JITCODE: construct(static_cast<js::jit::JitCode*>(ptr)); break;
|
||||
case JSTRACE_LAZY_SCRIPT: construct(static_cast<js::LazyScript*>(ptr)); break;
|
||||
case JSTRACE_SHAPE: construct(static_cast<js::Shape*>(ptr)); break;
|
||||
case JSTRACE_OBJECT_GROUP: construct(static_cast<js::ObjectGroup*>(ptr)); break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("bad JSGCTraceKind passed to JS::ubi::Node::Node");
|
||||
}
|
||||
CallTyped(ConstructFunctor(), ptr, kind, this);
|
||||
}
|
||||
|
||||
Node::Node(HandleValue value)
|
||||
{
|
||||
if (value.isObject())
|
||||
construct(&value.toObject());
|
||||
else if (value.isString())
|
||||
construct(value.toString());
|
||||
else if (value.isSymbol())
|
||||
construct(value.toSymbol());
|
||||
else
|
||||
if (!DispatchValueTyped(ConstructFunctor(), value, this))
|
||||
construct<void>(nullptr);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 281;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 282;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
|
||||
@@ -1754,6 +1754,20 @@ GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID,
|
||||
}
|
||||
}
|
||||
|
||||
extern void
|
||||
xpc::GetCurrentCompartmentName(JSContext* cx, nsCString& name)
|
||||
{
|
||||
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
if (!global) {
|
||||
name.AssignLiteral("no global");
|
||||
return;
|
||||
}
|
||||
|
||||
JSCompartment* compartment = GetObjectCompartment(global);
|
||||
int anonymizeID = 0;
|
||||
GetCompartmentName(compartment, name, &anonymizeID, false);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
JSMainRuntimeGCHeapDistinguishedAmount()
|
||||
{
|
||||
@@ -3208,6 +3222,47 @@ static const JSWrapObjectCallbacks WrapObjectCallbacks = {
|
||||
xpc::WrapperFactory::PrepareForWrapping
|
||||
};
|
||||
|
||||
/**
|
||||
* Group JSCompartments into PerformanceGroups.
|
||||
*
|
||||
* - All JSCompartments from the same add-on belong to the same
|
||||
* PerformanceGroup.
|
||||
* - All JSCompartments from the same same webpage (including
|
||||
* frames) belong to the same PerformanceGroup.
|
||||
* - All other JSCompartments (normally, system add-ons)
|
||||
* belong to to a big uncategorized PerformanceGroup.
|
||||
*/
|
||||
static void*
|
||||
GetCurrentPerfGroupCallback(JSContext* cx) {
|
||||
RootedObject global(cx, CurrentGlobalOrNull(cx));
|
||||
if (!global) {
|
||||
// This can happen for the atom compartments, which is system
|
||||
// code.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSAddonId* addonId = AddonIdOfObject(global);
|
||||
if (addonId) {
|
||||
// If this is an add-on, use the id as key.
|
||||
return addonId;
|
||||
}
|
||||
|
||||
// If the compartment belongs to a webpage, use the address of the
|
||||
// topmost scriptable window, hence regrouping all frames of a
|
||||
// window.
|
||||
nsRefPtr<nsGlobalWindow> win = WindowOrNull(global);
|
||||
if (win) {
|
||||
nsCOMPtr<nsPIDOMWindow> top = win->GetScriptableTop();
|
||||
if (!top) {
|
||||
return nullptr;
|
||||
}
|
||||
return top.get();
|
||||
}
|
||||
|
||||
// Otherwise, this is platform code, use `nullptr` as key.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
: CycleCollectedJSRuntime(nullptr, JS::DefaultHeapMaxBytes, JS::DefaultNurseryBytes),
|
||||
mJSContextStack(new XPCJSContextStack(this)),
|
||||
@@ -3382,6 +3437,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
// Watch for the JS boolean options.
|
||||
ReloadPrefsCallback(nullptr, this);
|
||||
Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
|
||||
|
||||
JS_SetCurrentPerfGroupCallback(runtime, ::GetCurrentPerfGroupCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -144,9 +144,6 @@ XrayAwareCalleeGlobal(JSObject* fun);
|
||||
void
|
||||
TraceXPCGlobal(JSTracer* trc, JSObject* obj);
|
||||
|
||||
uint64_t
|
||||
GetCompartmentCPOWMicroseconds(JSCompartment* compartment);
|
||||
|
||||
} /* namespace xpc */
|
||||
|
||||
namespace JS {
|
||||
@@ -537,6 +534,12 @@ void
|
||||
DispatchScriptErrorEvent(nsPIDOMWindow* win, JSRuntime* rt, xpc::ErrorReport* xpcReport,
|
||||
JS::Handle<JS::Value> exception);
|
||||
|
||||
// Return a name for the compartment.
|
||||
// This function makes reasonable efforts to make this name both mostly human-readable
|
||||
// and unique. However, there are no guarantees of either property.
|
||||
extern void
|
||||
GetCurrentCompartmentName(JSContext*, nsCString& name);
|
||||
|
||||
} // namespace xpc
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
[DEFAULT]
|
||||
|
||||
[browser_bug685470.js]
|
||||
skip-if = e10s # Bug ?????? - test touches content (TypeError: doc.documentElement is null)
|
||||
[browser_bug703210.js]
|
||||
skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
[browser_bug706743.js]
|
||||
skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
add_task(function* () {
|
||||
const html = "<p id=\"p1\" title=\"tooltip is here\">This paragraph has a tooltip.</p>";
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "data:text/html," + html);
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
SpecialPowers.setIntPref("ui.tooltipDelay", 0);
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [["ui.tooltipDelay", 0]]}, resolve);
|
||||
});
|
||||
|
||||
var popup = false;
|
||||
var doc;
|
||||
@@ -13,39 +14,20 @@ function test() {
|
||||
let onPopupShown = function(aEvent) {
|
||||
popup = true;
|
||||
}
|
||||
document.addEventListener("popupshown", onPopupShown, true);
|
||||
|
||||
// test that a mouse click prior to the tooltip appearing blocks it
|
||||
let runTest = function() {
|
||||
EventUtils.synthesizeMouseAtCenter(p1, { type: "mousemove" }, win);
|
||||
EventUtils.sendMouseEvent({type:'mousedown'}, p1, win);
|
||||
EventUtils.sendMouseEvent({type:'mouseup'}, p1, win);
|
||||
// Send a mousemove at a known position to start the test.
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#p1", { type: "mousemove" },
|
||||
gBrowser.selectedBrowser);
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#p1", { }, gBrowser.selectedBrowser);
|
||||
|
||||
yield new Promise(resolve => {
|
||||
setTimeout(function() {
|
||||
is(popup, false, "shouldn't get tooltip after click");
|
||||
|
||||
document.removeEventListener("popupshown", onPopupShown, true);
|
||||
SpecialPowers.clearUserPref("ui.tooltipDelay");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
resolve();
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
||||
let onLoad = function (aEvent) {
|
||||
doc = gBrowser.contentDocument;
|
||||
win = gBrowser.contentWindow;
|
||||
p1 = doc.getElementById("p1");
|
||||
|
||||
document.addEventListener("popupshown", onPopupShown, true);
|
||||
|
||||
runTest();
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
|
||||
setTimeout(onLoad, 0);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html," +
|
||||
"<p id=\"p1\" title=\"tooltip is here\">This paragraph has a tooltip.</p>";
|
||||
}
|
||||
document.removeEventListener("popupshown", onPopupShown, true);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
@@ -1,72 +1,33 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
SpecialPowers.setIntPref("ui.tooltipDelay", 0);
|
||||
|
||||
let doStopPropagation = function (aEvent)
|
||||
{
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
|
||||
let onPopupShown = function (aEvent)
|
||||
{
|
||||
is(aEvent.originalTarget.localName, "tooltip", "tooltip is showing");
|
||||
|
||||
let doc = gBrowser.contentDocument;
|
||||
let win = gBrowser.contentWindow;
|
||||
let p2 = doc.getElementById("p2");
|
||||
setTimeout(function () {
|
||||
EventUtils.synthesizeMouseAtCenter(p2, { type: "mousemove" }, win); }, 0);
|
||||
}
|
||||
|
||||
let onPopupHiding = function (aEvent)
|
||||
{
|
||||
is(aEvent.originalTarget.localName, "tooltip", "tooltip is hiding");
|
||||
|
||||
let doc = gBrowser.contentDocument;
|
||||
|
||||
doc.removeEventListener("mousemove", doStopPropagation, true);
|
||||
doc.removeEventListener("mouseenter", doStopPropagation, true);
|
||||
doc.removeEventListener("mouseleave", doStopPropagation, true);
|
||||
doc.removeEventListener("mouseover", doStopPropagation, true);
|
||||
doc.removeEventListener("mouseout", doStopPropagation, true);
|
||||
document.removeEventListener("popupshown", onPopupShown, true);
|
||||
document.removeEventListener("popuphiding", onPopupHiding, true);
|
||||
|
||||
SpecialPowers.clearUserPref("ui.tooltipDelay");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
let onLoad = function (aEvent)
|
||||
{
|
||||
let doc = gBrowser.contentDocument;
|
||||
let win = gBrowser.contentWindow;
|
||||
let p1 = doc.getElementById("p1");
|
||||
let p2 = doc.getElementById("p2");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(p2, { type: "mousemove" }, win);
|
||||
|
||||
doc.addEventListener("mousemove", doStopPropagation, true);
|
||||
doc.addEventListener("mouseenter", doStopPropagation, true);
|
||||
doc.addEventListener("mouseleave", doStopPropagation, true);
|
||||
doc.addEventListener("mouseover", doStopPropagation, true);
|
||||
doc.addEventListener("mouseout", doStopPropagation, true);
|
||||
document.addEventListener("popupshown", onPopupShown, true);
|
||||
document.addEventListener("popuphiding", onPopupHiding, true);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(p1, { type: "mousemove" }, win);
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
|
||||
setTimeout(onLoad, 0);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html," +
|
||||
add_task(function* () {
|
||||
const url = "data:text/html," +
|
||||
"<html onmousemove='event.stopPropagation()'" +
|
||||
" onmouseenter='event.stopPropagation()' onmouseleave='event.stopPropagation()'" +
|
||||
" onmouseover='event.stopPropagation()' onmouseout='event.stopPropagation()'>" +
|
||||
"<p id=\"p1\" title=\"tooltip is here\">This paragraph has a tooltip.</p>" +
|
||||
"<p id=\"p2\">This paragraph doesn't have tooltip.</p>";
|
||||
}
|
||||
"<p id=\"p2\">This paragraph doesn't have tooltip.</p></html>";
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [["ui.tooltipDelay", 0]]}, resolve);
|
||||
});
|
||||
|
||||
// Send a mousemove at a known position to start the test.
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#p2", { type: "mousemove" }, browser);
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown", event => {
|
||||
is(event.originalTarget.localName, "tooltip", "tooltip is showing");
|
||||
return true;
|
||||
});
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#p1", { type: "mousemove" }, browser);
|
||||
yield popupShownPromise;
|
||||
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(document, "popuphidden", event => {
|
||||
is(event.originalTarget.localName, "tooltip", "tooltip is hidden");
|
||||
return true;
|
||||
});
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#p2", { type: "mousemove" }, browser);
|
||||
yield popupHiddenPromise;
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
@@ -1,154 +1,84 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
add_task(function* () {
|
||||
const url = "data:text/html,<html><head></head><body>" +
|
||||
"<a id=\"target\" href=\"about:blank\" title=\"This is tooltip text\" " +
|
||||
"style=\"display:block;height:20px;margin:10px;\" " +
|
||||
"onclick=\"return false;\">here is an anchor element</a></body></html>";
|
||||
|
||||
SpecialPowers.setIntPref("ui.tooltipDelay", 0);
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [["ui.tooltipDelay", 0]]}, resolve);
|
||||
});
|
||||
|
||||
// Send a mousemove at a known position to start the test.
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", -5, -5, { type: "mousemove" }, browser);
|
||||
|
||||
// show tooltip by mousemove into target.
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", 5, 15, { type: "mousemove" }, browser);
|
||||
yield popupShownPromise;
|
||||
|
||||
// hide tooltip by mousemove to outside.
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(document, "popuphidden");
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", -5, 15, { type: "mousemove" }, browser);
|
||||
yield popupHiddenPromise;
|
||||
|
||||
// mousemove into the target and start drag by emulation via nsIDragService.
|
||||
// Note that on some platforms, we cannot actually start the drag by
|
||||
// synthesized events. E.g., Windows waits an actual mousemove event after
|
||||
// dragstart.
|
||||
|
||||
// Emulate a buggy mousemove event. widget might dispatch mousemove event
|
||||
// during drag.
|
||||
|
||||
function tooltipNotExpected()
|
||||
{
|
||||
ok(false, "tooltip is shown during drag");
|
||||
}
|
||||
addEventListener("popupshown", tooltipNotExpected, true);
|
||||
|
||||
let target;
|
||||
let doc;
|
||||
let win;
|
||||
let callbackOnPopupShown;
|
||||
let callbackOnPopupHidden;
|
||||
let dragService = Components.classes["@mozilla.org/widget/dragservice;1"].
|
||||
getService(Components.interfaces.nsIDragService);
|
||||
dragService.startDragSession();
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", 5, 15, { type: "mousemove" }, browser);
|
||||
|
||||
let onPopupHidden = function (aEvent)
|
||||
{
|
||||
if (aEvent.originalTarget.localName != "tooltip") {
|
||||
return;
|
||||
}
|
||||
if (callbackOnPopupHidden) {
|
||||
setTimeout(callbackOnPopupHidden, 0);
|
||||
}
|
||||
}
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
removeEventListener("popupshown", tooltipNotExpected, true);
|
||||
dragService.endDragSession(true);
|
||||
|
||||
let onPopupShown = function (aEvent)
|
||||
{
|
||||
if (aEvent.originalTarget.localName != "tooltip") {
|
||||
return;
|
||||
}
|
||||
if (callbackOnPopupShown) {
|
||||
setTimeout(callbackOnPopupShown, 0);
|
||||
}
|
||||
}
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", -5, -5, { type: "mousemove" }, browser);
|
||||
|
||||
let finishTest = function ()
|
||||
{
|
||||
document.removeEventListener("popupshown", onPopupShown, true);
|
||||
document.removeEventListener("popuphidden", onPopupHidden, true);
|
||||
// If tooltip listener used a flag for managing D&D state, we would need
|
||||
// to test if the tooltip is shown after drag.
|
||||
|
||||
SpecialPowers.clearUserPref("ui.tooltipDelay");
|
||||
// show tooltip by mousemove into target.
|
||||
popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", 5, 15, { type: "mousemove" }, browser);
|
||||
yield popupShownPromise;
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
// hide tooltip by mousemove to outside.
|
||||
popupHiddenPromise = BrowserTestUtils.waitForEvent(document, "popuphidden");
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", -5, 15, { type: "mousemove" }, browser);
|
||||
yield popupHiddenPromise;
|
||||
|
||||
let testHideTooltipByMouseDown = function ()
|
||||
{
|
||||
callbackOnPopupShown = function () {
|
||||
callbackOnPopupShown = null;
|
||||
ok(true, "tooltip is shown before testing mousedown");
|
||||
// Show tooltip after mousedown
|
||||
popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", 5, 15, { type: "mousemove" }, browser);
|
||||
yield popupShownPromise;
|
||||
|
||||
// hide tooltip by mousemove to outside.
|
||||
callbackOnPopupHidden = function () {
|
||||
callbackOnPopupHidden = null;
|
||||
ok(true, "The tooltip is hidden by mousedown");
|
||||
popupHiddenPromise = BrowserTestUtils.waitForEvent(document, "popuphidden");
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", 5, 15, { type: "mousedown" }, browser);
|
||||
yield popupHiddenPromise;
|
||||
|
||||
EventUtils.synthesizeMouse(target, 5, 15, { type: "mouseup" }, win);
|
||||
EventUtils.synthesizeMouse(target, -5, 15, { type: "mousemove" }, win);
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", 5, 15, { type: "mouseup" }, browser);
|
||||
yield BrowserTestUtils.synthesizeMouse("#target", -5, 15, { type: "mousemove" }, browser);
|
||||
|
||||
setTimeout(finishTest, 0);
|
||||
}
|
||||
EventUtils.synthesizeMouse(target, 5, 15, { type: "mousedown" }, win);
|
||||
}
|
||||
EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win);
|
||||
}
|
||||
ok(true, "tooltips appear properly");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
let testShowTooltipAgain = function ()
|
||||
{
|
||||
// If tooltip listener used a flag for managing D&D state, we would need
|
||||
// to test if the tooltip is shown after drag.
|
||||
callbackOnPopupShown = function () {
|
||||
callbackOnPopupShown = null;
|
||||
ok(true, "tooltip is shown after drag");
|
||||
|
||||
// hide tooltip by mousemove to outside.
|
||||
callbackOnPopupHidden = function () {
|
||||
callbackOnPopupHidden = null;
|
||||
ok(true, "The tooltip is hidden again");
|
||||
|
||||
setTimeout(testHideTooltipByMouseDown, 0);
|
||||
}
|
||||
EventUtils.synthesizeMouse(target, -5, 15, { type: "mousemove" }, win);
|
||||
}
|
||||
EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win);
|
||||
}
|
||||
|
||||
let testDuringDnD = function ()
|
||||
{
|
||||
// mousemove into the target and start drag by emulation via nsIDragService.
|
||||
// Note that on some platforms, we cannot actually start the drag by
|
||||
// synthesized events. E.g., Windows waits an actual mousemove event after
|
||||
// dragstart.
|
||||
callbackOnPopupShown = function () {
|
||||
callbackOnPopupShown = null;
|
||||
ok(false, "tooltip is shown during drag");
|
||||
}
|
||||
dragService.startDragSession();
|
||||
// Emulate a buggy mousemove event. widget might dispatch mousemove event
|
||||
// during drag.
|
||||
EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win);
|
||||
setTimeout(function () {
|
||||
callbackOnPopupShown = null;
|
||||
ok(true, "tooltip isn't shown during drag");
|
||||
dragService.endDragSession(true);
|
||||
EventUtils.synthesizeMouse(target, -5, -5, { type: "mousemove" }, win);
|
||||
|
||||
setTimeout(testShowTooltipAgain, 0);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
let onLoad = function (aEvent)
|
||||
{
|
||||
doc = gBrowser.contentDocument;
|
||||
win = gBrowser.contentWindow;
|
||||
target = doc.getElementById("target");
|
||||
|
||||
document.addEventListener("popupshown", onPopupShown, true);
|
||||
document.addEventListener("popuphidden", onPopupHidden, true);
|
||||
|
||||
EventUtils.synthesizeMouse(target, -5, -5, { type: "mousemove" }, win);
|
||||
|
||||
// show tooltip by mousemove into target.
|
||||
callbackOnPopupShown = function ()
|
||||
{
|
||||
callbackOnPopupShown = null;
|
||||
ok(true, "The tooltip is shown");
|
||||
|
||||
// hide tooltip by mousemove to outside.
|
||||
callbackOnPopupHidden = function () {
|
||||
callbackOnPopupHidden = null;
|
||||
ok(true, "The tooltip is hidden");
|
||||
|
||||
setTimeout(testDuringDnD, 0);
|
||||
}
|
||||
EventUtils.synthesizeMouse(target, -5, 15, { type: "mousemove" }, win);
|
||||
}
|
||||
EventUtils.synthesizeMouse(target, 5, 15, { type: "mousemove" }, win);
|
||||
}
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load",
|
||||
function () {
|
||||
gBrowser.selectedBrowser.
|
||||
removeEventListener("load", arguments.callee, true);
|
||||
setTimeout(onLoad, 0);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,<html><head></head><body>" +
|
||||
"<a id=\"target\" href=\"about:blank\" title=\"This is tooltip text\" " +
|
||||
"style=\"display:block;height:20px;margin:10px;\" " +
|
||||
"onclick=\"return false;\">here is an anchor element</a></body></html>";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -40,36 +40,47 @@
|
||||
}
|
||||
td.jank0 {
|
||||
color: rgb(0, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank1 {
|
||||
color: rgb(25, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank2 {
|
||||
color: rgb(50, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank3 {
|
||||
color: rgb(75, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank4 {
|
||||
color: rgb(100, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank5 {
|
||||
color: rgb(125, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank6 {
|
||||
color: rgb(150, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank7 {
|
||||
color: rgb(175, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank8 {
|
||||
color: rgb(200, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank9 {
|
||||
color: rgb(225, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
td.jank9 {
|
||||
td.jank10 {
|
||||
color: rgb(255, 0, 0);
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -22,6 +22,10 @@ EXPORTS += [
|
||||
'nsPerformanceStats.h'
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIArray.idl"
|
||||
#include "nsIDOMWindow.idl"
|
||||
|
||||
/**
|
||||
* Mechanisms for querying the current process about performance
|
||||
@@ -21,7 +22,7 @@
|
||||
* All values are monotonic and are updated only when
|
||||
* `nsIPerformanceStatsService.isStopwatchActive` is `true`.
|
||||
*/
|
||||
[scriptable, uuid(f015cbad-e16f-4982-a080-67e4e69a5b2e)]
|
||||
[scriptable, uuid(b060d75d-55bc-4c82-a4ff-458fc5ab2a69)]
|
||||
interface nsIPerformanceStats: nsISupports {
|
||||
/**
|
||||
* The name of the component:
|
||||
@@ -38,6 +39,18 @@ interface nsIPerformanceStats: nsISupports {
|
||||
*/
|
||||
readonly attribute AString addonId;
|
||||
|
||||
/**
|
||||
* If the component is code executed in a window, the ID of the topmost
|
||||
* outer window (i.e. the tab), otherwise 0.
|
||||
*/
|
||||
readonly attribute uint64_t windowId;
|
||||
|
||||
/**
|
||||
* If the component is code executed in a window, the title of the topmost
|
||||
* window (i.e. the tab), otherwise an empty string.
|
||||
*/
|
||||
readonly attribute AString title;
|
||||
|
||||
/**
|
||||
* Total amount of time spent executing code in this group, in
|
||||
* microseconds.
|
||||
@@ -91,17 +104,22 @@ interface nsIPerformanceSnapshot: nsISupports {
|
||||
nsIPerformanceStats getProcessData();
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(5795113a-39a1-4087-ba09-98b7d07d025a)]
|
||||
[scriptable, builtinclass, uuid(0469e6af-95c3-4961-a385-4bc009128939)]
|
||||
interface nsIPerformanceStatsService : nsISupports {
|
||||
/**
|
||||
* `true` if we should monitor performance, `false` otherwise.
|
||||
* `true` if we should monitor CPOW, `false` otherwise.
|
||||
*/
|
||||
[implicit_jscontext] attribute bool isStopwatchActive;
|
||||
[implicit_jscontext] attribute bool isMonitoringCPOW;
|
||||
|
||||
/**
|
||||
* `true` if we should monitor jank, `false` otherwise.
|
||||
*/
|
||||
[implicit_jscontext] attribute bool isMonitoringJank;
|
||||
|
||||
/**
|
||||
* Capture a snapshot of the performance data.
|
||||
*/
|
||||
nsIPerformanceSnapshot getSnapshot();
|
||||
[implicit_jscontext] nsIPerformanceSnapshot getSnapshot();
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
||||
@@ -3,24 +3,37 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsPerformanceStats.h"
|
||||
|
||||
#include "nsMemory.h"
|
||||
#include "nsLiteralString.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "jspubtd.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
class nsPerformanceStats: public nsIPerformanceStats {
|
||||
public:
|
||||
nsPerformanceStats(nsAString& aName, nsAString& aAddonId, bool aIsSystem, js::PerformanceData& aPerformanceData)
|
||||
nsPerformanceStats(const nsAString& aName,
|
||||
const nsAString& aAddonId,
|
||||
const nsAString& aTitle,
|
||||
const uint64_t aWindowId,
|
||||
const bool aIsSystem,
|
||||
const js::PerformanceData& aPerformanceData)
|
||||
: mName(aName)
|
||||
, mAddonId(aAddonId)
|
||||
, mTitle(aTitle)
|
||||
, mWindowId(aWindowId)
|
||||
, mIsSystem(aIsSystem)
|
||||
, mPerformanceData(aPerformanceData)
|
||||
{
|
||||
@@ -41,6 +54,24 @@ public:
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute uint64_t windowId; */
|
||||
NS_IMETHOD GetWindowId(uint64_t *aWindowId) override {
|
||||
*aWindowId = mWindowId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute AString title; */
|
||||
NS_IMETHOD GetTitle(nsAString & aTitle) override {
|
||||
aTitle.Assign(mTitle);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute bool isSystem; */
|
||||
NS_IMETHOD GetIsSystem(bool *_retval) override {
|
||||
*_retval = mIsSystem;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute unsigned long long totalUserTime; */
|
||||
NS_IMETHOD GetTotalUserTime(uint64_t *aTotalUserTime) override {
|
||||
*aTotalUserTime = mPerformanceData.totalUserTime;
|
||||
@@ -78,16 +109,13 @@ public:
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
/* readonly attribute bool isSystem; */
|
||||
NS_IMETHOD GetIsSystem(bool *_retval) override {
|
||||
*_retval = mIsSystem;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mName;
|
||||
nsString mAddonId;
|
||||
nsString mTitle;
|
||||
uint64_t mWindowId;
|
||||
bool mIsSystem;
|
||||
|
||||
js::PerformanceData mPerformanceData;
|
||||
|
||||
virtual ~nsPerformanceStats() {}
|
||||
@@ -103,15 +131,45 @@ public:
|
||||
NS_DECL_NSIPERFORMANCESNAPSHOT
|
||||
|
||||
nsPerformanceSnapshot();
|
||||
nsresult Init();
|
||||
nsresult Init(JSContext*);
|
||||
private:
|
||||
virtual ~nsPerformanceSnapshot();
|
||||
|
||||
/**
|
||||
* Import a `PerformanceStats` as a `nsIPerformanceStats`.
|
||||
* Import a `js::PerformanceStats` as a `nsIPerformanceStats`.
|
||||
*
|
||||
* Precondition: this method assumes that we have entered the JSCompartment for which data `c`
|
||||
* has been collected.
|
||||
*
|
||||
* `cx` may be `nullptr` if we are importing the statistics for the
|
||||
* entire process, rather than the statistics for a specific set of
|
||||
* compartments.
|
||||
*/
|
||||
already_AddRefed<nsIPerformanceStats> ImportStats(js::PerformanceStats* c);
|
||||
already_AddRefed<nsIPerformanceStats> ImportStats(JSContext* cx, const js::PerformanceData& data);
|
||||
|
||||
/**
|
||||
* Callbacks for iterating through the `PerformanceStats` of a runtime.
|
||||
*/
|
||||
bool IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats);
|
||||
static bool IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self);
|
||||
|
||||
// If the context represents a window, extract the title and window ID.
|
||||
// Otherwise, extract "" and 0.
|
||||
static void GetWindowData(JSContext*,
|
||||
nsString& title,
|
||||
uint64_t* windowId);
|
||||
|
||||
// If the context presents an add-on, extract the addon ID.
|
||||
// Otherwise, extract "".
|
||||
static void GetAddonId(JSContext*,
|
||||
JS::Handle<JSObject*> global,
|
||||
nsAString& addonId);
|
||||
|
||||
// Determine whether a context is part of the system principals.
|
||||
static bool GetIsSystem(JSContext*,
|
||||
JS::Handle<JSObject*> global);
|
||||
|
||||
private:
|
||||
nsCOMArray<nsIPerformanceStats> mComponentsData;
|
||||
nsCOMPtr<nsIPerformanceStats> mProcessData;
|
||||
};
|
||||
@@ -126,36 +184,118 @@ nsPerformanceSnapshot::~nsPerformanceSnapshot()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPerformanceStats>
|
||||
nsPerformanceSnapshot::ImportStats(js::PerformanceStats* c) {
|
||||
nsString addonId;
|
||||
if (c->addonId) {
|
||||
AssignJSFlatString(addonId, (JSFlatString*)c->addonId);
|
||||
/* static */ void
|
||||
nsPerformanceSnapshot::GetWindowData(JSContext* cx,
|
||||
nsString& title,
|
||||
uint64_t* windowId)
|
||||
{
|
||||
MOZ_ASSERT(windowId);
|
||||
|
||||
title.SetIsVoid(true);
|
||||
*windowId = 0;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = xpc::CurrentWindowOrNull(cx);
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
nsCString cname(c->name);
|
||||
NS_ConvertUTF8toUTF16 name(cname);
|
||||
nsCOMPtr<nsIPerformanceStats> result = new nsPerformanceStats(name, addonId, c->isSystem, c->performance);
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> top = win->GetTop();
|
||||
if (!top) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> ptop = do_QueryInterface(top);
|
||||
if (!ptop) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = ptop->GetExtantDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
doc->GetTitle(title);
|
||||
*windowId = ptop->WindowID();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsPerformanceSnapshot::GetAddonId(JSContext*,
|
||||
JS::Handle<JSObject*> global,
|
||||
nsAString& addonId)
|
||||
{
|
||||
addonId.AssignLiteral("");
|
||||
|
||||
JSAddonId* jsid = AddonIdOfObject(global);
|
||||
if (!jsid) {
|
||||
return;
|
||||
}
|
||||
AssignJSFlatString(addonId, (JSFlatString*)jsid);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsPerformanceSnapshot::GetIsSystem(JSContext*,
|
||||
JS::Handle<JSObject*> global)
|
||||
{
|
||||
return nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(global));
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPerformanceStats>
|
||||
nsPerformanceSnapshot::ImportStats(JSContext* cx, const js::PerformanceData& performance) {
|
||||
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
|
||||
if (!global) {
|
||||
// While it is possible for a compartment to have no global
|
||||
// (e.g. atoms), this compartment is not very interesting for us.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString addonId;
|
||||
GetAddonId(cx, global, addonId);
|
||||
|
||||
nsString title;
|
||||
uint64_t windowId;
|
||||
GetWindowData(cx, title, &windowId);
|
||||
|
||||
nsAutoString name;
|
||||
nsAutoCString cname;
|
||||
xpc::GetCurrentCompartmentName(cx, cname);
|
||||
name.Assign(NS_ConvertUTF8toUTF16(cname));
|
||||
|
||||
bool isSystem = GetIsSystem(cx, global);
|
||||
|
||||
nsCOMPtr<nsIPerformanceStats> result =
|
||||
new nsPerformanceStats(name, addonId, title, windowId, isSystem, performance);
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPerformanceSnapshot::Init() {
|
||||
JSRuntime* rt;
|
||||
nsCOMPtr<nsIJSRuntimeService> svc(do_GetService("@mozilla.org/js/xpc/RuntimeService;1"));
|
||||
NS_ENSURE_TRUE(svc, NS_ERROR_FAILURE);
|
||||
svc->GetRuntime(&rt);
|
||||
js::PerformanceStats processStats;
|
||||
js::PerformanceStatsVector componentsStats;
|
||||
if (!js::GetPerformanceStats(rt, componentsStats, processStats)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
/*static*/ bool
|
||||
nsPerformanceSnapshot::IterPerformanceStatsCallback(JSContext* cx, const js::PerformanceData& stats, void* self) {
|
||||
return reinterpret_cast<nsPerformanceSnapshot*>(self)->IterPerformanceStatsCallbackInternal(cx, stats);
|
||||
}
|
||||
|
||||
bool
|
||||
nsPerformanceSnapshot::IterPerformanceStatsCallbackInternal(JSContext* cx, const js::PerformanceData& stats) {
|
||||
nsCOMPtr<nsIPerformanceStats> result = ImportStats(cx, stats);
|
||||
if (result) {
|
||||
mComponentsData.AppendElement(result);
|
||||
}
|
||||
|
||||
size_t num = componentsStats.length();
|
||||
for (size_t pos = 0; pos < num; pos++) {
|
||||
nsCOMPtr<nsIPerformanceStats> stats = ImportStats(&componentsStats[pos]);
|
||||
mComponentsData.AppendObject(stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsPerformanceSnapshot::Init(JSContext* cx) {
|
||||
js::PerformanceData processStats;
|
||||
if (!js::IterPerformanceStats(cx, nsPerformanceSnapshot::IterPerformanceStatsCallback, &processStats, this)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mProcessData = ImportStats(&processStats);
|
||||
|
||||
mProcessData = new nsPerformanceStats(NS_LITERAL_STRING("<process>"), // name
|
||||
NS_LITERAL_STRING(""), // add-on id
|
||||
NS_LITERAL_STRING(""), // title
|
||||
0, // window id
|
||||
true, // isSystem
|
||||
processStats);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -192,27 +332,41 @@ nsPerformanceStatsService::~nsPerformanceStatsService()
|
||||
{
|
||||
}
|
||||
|
||||
/* [implicit_jscontext] attribute bool isStopwatchActive; */
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetIsStopwatchActive(JSContext* cx, bool *aIsStopwatchActive)
|
||||
//[implicit_jscontext] attribute bool isMonitoringCPOW;
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringCPOW(JSContext* cx, bool *aIsStopwatchActive)
|
||||
{
|
||||
JSRuntime *runtime = JS_GetRuntime(cx);
|
||||
*aIsStopwatchActive = js::IsStopwatchActive(runtime);
|
||||
*aIsStopwatchActive = js::GetStopwatchIsMonitoringCPOW(runtime);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHODIMP nsPerformanceStatsService::SetIsStopwatchActive(JSContext* cx, bool aIsStopwatchActive)
|
||||
NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringCPOW(JSContext* cx, bool aIsStopwatchActive)
|
||||
{
|
||||
JSRuntime *runtime = JS_GetRuntime(cx);
|
||||
if (!js::SetStopwatchActive(runtime, aIsStopwatchActive)) {
|
||||
if (!js::SetStopwatchIsMonitoringCPOW(runtime, aIsStopwatchActive)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetIsMonitoringJank(JSContext* cx, bool *aIsStopwatchActive)
|
||||
{
|
||||
JSRuntime *runtime = JS_GetRuntime(cx);
|
||||
*aIsStopwatchActive = js::GetStopwatchIsMonitoringJank(runtime);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHODIMP nsPerformanceStatsService::SetIsMonitoringJank(JSContext* cx, bool aIsStopwatchActive)
|
||||
{
|
||||
JSRuntime *runtime = JS_GetRuntime(cx);
|
||||
if (!js::SetStopwatchIsMonitoringJank(runtime, aIsStopwatchActive)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* readonly attribute nsIPerformanceSnapshot snapshot; */
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(nsIPerformanceSnapshot * *aSnapshot)
|
||||
NS_IMETHODIMP nsPerformanceStatsService::GetSnapshot(JSContext* cx, nsIPerformanceSnapshot * *aSnapshot)
|
||||
{
|
||||
nsRefPtr<nsPerformanceSnapshot> snapshot = new nsPerformanceSnapshot();
|
||||
nsresult rv = snapshot->Init();
|
||||
nsresult rv = snapshot->Init(cx);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -2,24 +2,19 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
browser_compartments.html
|
||||
Main frame for test browser_aboutperformance.js
|
||||
</title>
|
||||
<script type="text/javascript">
|
||||
// Use some CPU.
|
||||
window.setInterval(() => {
|
||||
// Compute an arbitrary value, print it out to make sure that the JS
|
||||
// engine doesn't discard all our computation.
|
||||
var date = Date.now();
|
||||
var array = [];
|
||||
var i = 0;
|
||||
while (Date.now() - date <= 100) {
|
||||
array[i%2] = i++;
|
||||
}
|
||||
console.log("Arbitrary value", array);
|
||||
}, 300);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
browser_compartments.html
|
||||
Main frame.
|
||||
|
||||
<iframe src="browser_compartments_frame.html?frame=1">
|
||||
Subframe 1
|
||||
</iframe>
|
||||
|
||||
<iframe src="browser_compartments_frame.html?frame=2">
|
||||
Subframe 2.
|
||||
</iframe>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -141,13 +141,16 @@ add_task(function* test() {
|
||||
monotinicity_tester(() => PerformanceStats.getSnapshot(), "parent process");
|
||||
monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" );
|
||||
|
||||
let skipTotalUserTime = hasLowPrecision();
|
||||
|
||||
while (true) {
|
||||
let stats = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null));
|
||||
let found = stats.componentsData.find(stat => {
|
||||
return (stat.name.indexOf(URL) != -1)
|
||||
&& (stat.totalUserTime > 1000)
|
||||
&& (skipTotalUserTime || stat.totalUserTime > 1000)
|
||||
});
|
||||
if (found) {
|
||||
info(`Expected totalUserTime > 1000, got ${found.totalUserTime}`);
|
||||
break;
|
||||
}
|
||||
yield new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Subframe for test browser_compartments.html (do not change this title)
|
||||
</title>
|
||||
<script src="browser_compartments_script.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
Subframe loaded.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
var carryOn = true;
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
console.log("frame content", "message", e);
|
||||
if ("title" in e.data) {
|
||||
document.title = e.data.title;
|
||||
}
|
||||
if ("stop" in e.data) {
|
||||
carryOn = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Use some CPU.
|
||||
var interval = window.setInterval(() => {
|
||||
if (!carryOn) {
|
||||
window.clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute an arbitrary value, print it out to make sure that the JS
|
||||
// engine doesn't discard all our computation.
|
||||
var date = Date.now();
|
||||
var array = [];
|
||||
var i = 0;
|
||||
while (Date.now() - date <= 100) {
|
||||
array[i%2] = i++;
|
||||
}
|
||||
}, 300);
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
function promiseContentResponse(browser, name, message) {
|
||||
let mm = browser.messageManager;
|
||||
let promise = new Promise(resolve => {
|
||||
@@ -27,3 +29,24 @@ function promiseContentResponseOrNull(browser, name, message) {
|
||||
}
|
||||
return promiseContentResponse(browser, name, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if we are running an OS in which the OS performance
|
||||
* clock has a low precision and might unpredictably
|
||||
* never be updated during the execution of the test.
|
||||
*/
|
||||
function hasLowPrecision() {
|
||||
let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsDouble("version")];
|
||||
info(`Running ${sysName} version ${sysVersion}`);
|
||||
|
||||
if (sysName == "Windows_NT" && sysVersion < 6) {
|
||||
info("Running old Windows, need to deactivate tests due to bad precision.");
|
||||
return true;
|
||||
}
|
||||
if (sysName == "Linux" && sysVersion <= 2.6) {
|
||||
info("Running old Linux, need to deactivate tests due to bad precision.");
|
||||
return true;
|
||||
}
|
||||
info("This platform has good precision.")
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ let AddonWatcher = {
|
||||
}
|
||||
if (diff.totalCPOWTime > 0) {
|
||||
Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_CPOW_TIME_MS").
|
||||
add(addonId, diff.totalCPOWTime);
|
||||
add(addonId, diff.totalCPOWTime / 1000);
|
||||
}
|
||||
|
||||
// Report mibehaviors to the user.
|
||||
|
||||
@@ -82,7 +82,7 @@ let burn_rubber = Task.async(function*({histogramName, topic, expectedReason, pr
|
||||
Assert.equal(reason, expectedReason, "Reason is valid");
|
||||
let snap2 = histogram.snapshot(ADDON_ID);
|
||||
|
||||
Assert.ok(snap2.sum > expectedMinSum, `Histogram ${histogramName} recorded a gravity of ${snap2.sum}, expecting at least ${expectedMinSum}.`);
|
||||
Assert.ok(snap2.sum >= expectedMinSum, `Histogram ${histogramName} recorded a gravity of ${snap2.sum}, expecting at least ${expectedMinSum}.`);
|
||||
} finally {
|
||||
AddonWatcher.uninit();
|
||||
for (let key of Object.keys(prefs)) {
|
||||
@@ -121,6 +121,6 @@ add_task(function* test_burn_CPOW() {
|
||||
histogramName: "MISBEHAVING_ADDONS_CPOW_TIME_MS",
|
||||
topic: "test-addonwatcher-burn-some-cpow",
|
||||
expectedReason: "totalCPOWTime",
|
||||
expectedMinSum: 1000,
|
||||
expectedMinSum: 400,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user