From f01ca0e4fe9ccf60fe48ed22431e2877d1fda323 Mon Sep 17 00:00:00 2001 From: Logan F Smyth Date: Thu, 12 Jul 2018 11:24:59 -0700 Subject: [PATCH 01/21] Bug 1378808 - Add a new ParseNodeKind::Arguments node type for call argument lists. r=jorendorff MozReview-Commit-ID: 7L4nNHjVoZo --- js/src/builtin/ReflectParse.cpp | 15 +- js/src/frontend/BinSource-auto.cpp | 10 +- js/src/frontend/BinSource.yaml | 10 +- js/src/frontend/BytecodeEmitter.cpp | 260 ++++++++++++++------------- js/src/frontend/BytecodeEmitter.h | 1 + js/src/frontend/FoldConstants.cpp | 31 +++- js/src/frontend/FullParseHandler.h | 25 ++- js/src/frontend/NameFunctions.cpp | 36 +++- js/src/frontend/ParseNode.h | 20 ++- js/src/frontend/Parser.cpp | 98 +++++----- js/src/frontend/Parser.h | 2 +- js/src/frontend/SyntaxParseHandler.h | 10 +- js/src/wasm/AsmJS.cpp | 17 +- 13 files changed, 301 insertions(+), 234 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index f2057ab7f829..90fff7f74982 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2702,24 +2702,25 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case ParseNodeKind::Call: case ParseNodeKind::SuperCall: { - ParseNode* next = pn->pn_head; - MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); + ParseNode* pn_callee = pn->pn_left; + ParseNode* pn_args = pn->pn_right; + MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos)); RootedValue callee(cx); if (pn->isKind(ParseNodeKind::SuperCall)) { - MOZ_ASSERT(next->isKind(ParseNodeKind::SuperBase)); - if (!builder.super(&next->pn_pos, &callee)) + MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase)); + if (!builder.super(&pn_callee->pn_pos, &callee)) return false; } else { - if (!expression(next, &callee)) + if (!expression(pn_callee, &callee)) return false; } NodeVector args(cx); - if (!args.reserve(pn->pn_count - 1)) + if (!args.reserve(pn_args->pn_count)) return false; - for (next = next->pn_next; next; next = next->pn_next) { + for (ParseNode* next = pn_args->pn_head; next; next = next->pn_next) { MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); RootedValue arg(cx); diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp index d37c30517b3d..4514af8a4c85 100644 --- a/js/src/frontend/BinSource-auto.cpp +++ b/js/src/frontend/BinSource-auto.cpp @@ -3462,9 +3462,8 @@ BinASTParser::parseInterfaceCallExpression(const size_t start, const BinKin op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; } } - auto result = arguments; - result->setKind(ParseNodeKind::Call); - result->prepend(callee); + + BINJS_TRY_DECL(result, factory_.newCall(callee, arguments)); result->setOp(op); return result; } @@ -5677,10 +5676,7 @@ BinASTParser::parseInterfaceNewExpression(const size_t start, const BinKind BINJS_MOZ_TRY_DECL(arguments, parseArguments()); - auto result = arguments; - result->setKind(ParseNodeKind::New); - result->prepend(callee); - result->setOp(JSOP_NEW); + BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments)); return result; } diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml index 0a41e3c9630d..c8020610937a 100644 --- a/js/src/frontend/BinSource.yaml +++ b/js/src/frontend/BinSource.yaml @@ -428,9 +428,8 @@ CallExpression: op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; } } - auto result = arguments; - result->setKind(ParseNodeKind::Call); - result->prepend(callee); + + BINJS_TRY_DECL(result, factory_.newCall(callee, arguments)); result->setOp(op); @@ -831,10 +830,7 @@ LiteralStringExpression: NewExpression: build: | - auto result = arguments; - result->setKind(ParseNodeKind::New); - result->prepend(callee); - result->setOp(JSOP_NEW); + BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments)); ObjectExpression: build: diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 1726b78480ac..bed6ebf6039e 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1276,6 +1276,14 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case ParseNodeKind::Call: case ParseNodeKind::TaggedTemplate: case ParseNodeKind::SuperCall: + MOZ_ASSERT(pn->isArity(PN_BINARY)); + *answer = true; + return true; + + // Function arg lists can contain arbitrary expressions. Technically + // this only causes side-effects if one of the arguments does, but since + // the call being made will always trigger side-effects, it isn't needed. + case ParseNodeKind::Arguments: MOZ_ASSERT(pn->isArity(PN_LIST)); *answer = true; return true; @@ -4873,7 +4881,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte bool allowSelfHostedIter = false; if (emitterMode == BytecodeEmitter::SelfHosting && forHeadExpr->isKind(ParseNodeKind::Call) && - forHeadExpr->pn_head->name() == cx->names().allowContentIter) + forHeadExpr->pn_left->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } @@ -6568,10 +6576,12 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) // // argc is set to the amount of actually emitted args and the // emitting of args below is disabled by setting emitArgs to false. - ParseNode* pn2 = pn->pn_head; - const char* errorName = SelfHostedCallFunctionName(pn2->name(), cx); + ParseNode* pn_callee = pn->pn_left; + ParseNode* pn_args = pn->pn_right; - if (pn->pn_count < 3) { + const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx); + + if (pn_args->pn_count < 2) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s"); return false; } @@ -6582,8 +6592,8 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - bool constructing = pn2->name() == cx->names().constructContentFunction; - ParseNode* funNode = pn2->pn_next; + bool constructing = pn_callee->name() == cx->names().constructContentFunction; + ParseNode* funNode = pn_args->pn_head; if (constructing) { callOp = JSOP_NEW; } else if (funNode->getKind() == ParseNodeKind::Name && @@ -6596,7 +6606,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) #ifdef DEBUG if (emitterMode == BytecodeEmitter::SelfHosting && - pn2->name() == cx->names().callFunction) + pn_callee->name() == cx->names().callFunction) { if (!emit1(JSOP_DEBUGCHECKSELFHOSTED)) return false; @@ -6625,7 +6635,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - uint32_t argc = pn->pn_count - 3; + uint32_t argc = pn_args->pn_count - 2; if (!emitCall(callOp, argc)) return false; @@ -6636,15 +6646,15 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn) { + ParseNode* pn_args = pn->pn_right; + // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return') - if (pn->pn_count != 4) { + if (pn_args->pn_count != 3) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s"); return false; } - ParseNode* funNode = pn->pn_head; // The resumeGenerator node. - - ParseNode* genNode = funNode->pn_next; + ParseNode* genNode = pn_args->pn_head; if (!emitTree(genNode)) return false; @@ -6676,24 +6686,26 @@ BytecodeEmitter::emitSelfHostedForceInterpreter() bool BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn) { - if (pn->pn_count != 2) { + ParseNode* pn_args = pn->pn_right; + + if (pn_args->pn_count != 1) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", ""); return false; } // We're just here as a sentinel. Pass the value through directly. - return emitTree(pn->pn_head->pn_next); + return emitTree(pn_args->pn_head); } bool BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn) { - // Only optimize when 3 arguments are passed (we use 4 to include |this|). - MOZ_ASSERT(pn->pn_count == 4); + ParseNode* pn_args = pn->pn_right; - ParseNode* funNode = pn->pn_head; // The _DefineDataProperty node. + // Only optimize when 3 arguments are passed. + MOZ_ASSERT(pn_args->pn_count == 3); - ParseNode* objNode = funNode->pn_next; + ParseNode* objNode = pn_args->pn_head; if (!emitTree(objNode)) return false; @@ -6714,14 +6726,14 @@ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn) { - if (pn->pn_count != 3) { + ParseNode* pn_args = pn->pn_right; + + if (pn_args->pn_count != 2) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", ""); return false; } - ParseNode* funNode = pn->pn_head; // The hasOwn node. - - ParseNode* idNode = funNode->pn_next; + ParseNode* idNode = pn_args->pn_head; if (!emitTree(idNode)) return false; @@ -6735,14 +6747,14 @@ BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn) { - if (pn->pn_count != 4) { + ParseNode* pn_args = pn->pn_right; + + if (pn_args->pn_count != 3) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", ""); return false; } - ParseNode* funNode = pn->pn_head; // The getPropertySuper node. - - ParseNode* objNode = funNode->pn_next; + ParseNode* objNode = pn_args->pn_head; ParseNode* idNode = objNode->pn_next; ParseNode* receiverNode = idNode->pn_next; @@ -6771,11 +6783,11 @@ BytecodeEmitter::isRestParameter(ParseNode* pn) if (!pn->isKind(ParseNodeKind::Name)) { if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) { - ParseNode* pn2 = pn->pn_head; - if (pn2->getKind() == ParseNodeKind::Name && - pn2->name() == cx->names().allowContentIter) + ParseNode* pn_callee = pn->pn_left; + if (pn_callee->getKind() == ParseNodeKind::Name && + pn_callee->name() == cx->names().allowContentIter) { - return isRestParameter(pn2->pn_next); + return isRestParameter(pn->pn_right->pn_head); } } return false; @@ -6905,101 +6917,22 @@ BytecodeEmitter::emitPipeline(ParseNode* pn) } bool -BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) +BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread) { - bool callop = - pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate); - /* - * Emit callable invocation or operator new (constructor call) code. - * First, emit code for the left operand to evaluate the callable or - * constructable object expression. - * - * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc. - * This is necessary to interpose the lambda-initialized method read - * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by - * JSOP_{SET,INIT}PROP. - * - * Then (or in a call case that has no explicit reference-base - * object) we emit JSOP_UNDEFINED to produce the undefined |this| - * value required for calls (which non-strict mode functions - * will box into the global object). - */ - uint32_t argc = pn->pn_count - 1; + uint32_t argc = pn->pn_count; if (argc >= ARGC_LIMIT) { reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS); return false; } - ParseNode* pn2 = pn->pn_head; - bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE; - - if (pn2->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) { - // Calls to "forceInterpreter", "callFunction", - // "callContentFunction", or "resumeGenerator" in self-hosted - // code generate inline bytecode. - if (pn2->name() == cx->names().callFunction || - pn2->name() == cx->names().callContentFunction || - pn2->name() == cx->names().constructContentFunction) - { - return emitSelfHostedCallFunction(pn); - } - if (pn2->name() == cx->names().resumeGenerator) - return emitSelfHostedResumeGenerator(pn); - if (pn2->name() == cx->names().forceInterpreter) - return emitSelfHostedForceInterpreter(); - if (pn2->name() == cx->names().allowContentIter) - return emitSelfHostedAllowContentIter(pn); - if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4) - return emitSelfHostedDefineDataProperty(pn); - if (pn2->name() == cx->names().hasOwn) - return emitSelfHostedHasOwn(pn); - if (pn2->name() == cx->names().getPropertySuper) - return emitSelfHostedGetPropertySuper(pn); - // Fall through - } - - if (!emitCallee(pn2, pn, &callop)) - return false; - - bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW || - pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL; - - - // Emit room for |this|. - if (!callop) { - if (isNewOp) { - if (!emit1(JSOP_IS_CONSTRUCTING)) - return false; - } else { - if (!emit1(JSOP_UNDEFINED)) - return false; - } - } - - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or - * JSOP_NEW bytecode with a two-byte immediate telling how many args - * were pushed on the operand stack. - */ if (!spread) { - for (ParseNode* pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { + for (ParseNode* pn3 = pn->pn_head; pn3; pn3 = pn3->pn_next) { if (!emitTree(pn3)) return false; } - - if (isNewOp) { - if (pn->isKind(ParseNodeKind::SuperCall)) { - if (!emit1(JSOP_NEWTARGET)) - return false; - } else { - // Repush the callee as new.target - if (!emitDupAt(argc + 1)) - return false; - } - } } else { - ParseNode* args = pn2->pn_next; + ParseNode* args = pn->pn_head; bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid); InternalIfEmitter ifNotOptimizable(this); @@ -7039,15 +6972,100 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs if (!ifNotOptimizable.emitEnd()) return false; } + } + return true; +} + +bool +BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) +{ + bool callop = + pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate); + + /* + * Emit callable invocation or operator new (constructor call) code. + * First, emit code for the left operand to evaluate the callable or + * constructable object expression. + * + * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc. + * This is necessary to interpose the lambda-initialized method read + * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by + * JSOP_{SET,INIT}PROP. + * + * Then (or in a call case that has no explicit reference-base + * object) we emit JSOP_UNDEFINED to produce the undefined |this| + * value required for calls (which non-strict mode functions + * will box into the global object). + */ + ParseNode* pn_callee = pn->pn_left; + ParseNode* pn_args = pn->pn_right; + + bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE; + + if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) { + // Calls to "forceInterpreter", "callFunction", + // "callContentFunction", or "resumeGenerator" in self-hosted + // code generate inline bytecode. + if (pn_callee->name() == cx->names().callFunction || + pn_callee->name() == cx->names().callContentFunction || + pn_callee->name() == cx->names().constructContentFunction) + { + return emitSelfHostedCallFunction(pn); + } + if (pn_callee->name() == cx->names().resumeGenerator) + return emitSelfHostedResumeGenerator(pn); + if (pn_callee->name() == cx->names().forceInterpreter) + return emitSelfHostedForceInterpreter(); + if (pn_callee->name() == cx->names().allowContentIter) + return emitSelfHostedAllowContentIter(pn); + if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && pn_args->pn_count == 3) + return emitSelfHostedDefineDataProperty(pn); + if (pn_callee->name() == cx->names().hasOwn) + return emitSelfHostedHasOwn(pn); + if (pn_callee->name() == cx->names().getPropertySuper) + return emitSelfHostedGetPropertySuper(pn); + // Fall through + } + + if (!emitCallee(pn_callee, pn, &callop)) + return false; + + bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW || + pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL; + + // Emit room for |this|. + if (!callop) { if (isNewOp) { - if (pn->isKind(ParseNodeKind::SuperCall)) { - if (!emit1(JSOP_NEWTARGET)) - return false; - } else { - if (!emitDupAt(2)) - return false; - } + if (!emit1(JSOP_IS_CONSTRUCTING)) + return false; + } else { + if (!emit1(JSOP_UNDEFINED)) + return false; + } + } + + if (!emitArguments(pn_args, callop, spread)) + return false; + + uint32_t argc = pn_args->pn_count; + + /* + * Emit code for each argument in order, then emit the JSOP_*CALL or + * JSOP_NEW bytecode with a two-byte immediate telling how many args + * were pushed on the operand stack. + */ + if (isNewOp) { + if (pn->isKind(ParseNodeKind::SuperCall)) { + if (!emit1(JSOP_NEWTARGET)) + return false; + } else if (!spread) { + // Repush the callee as new.target + if (!emitDupAt(argc + 1)) + return false; + } else { + if (!emitDupAt(2)) + return false; } } @@ -7630,7 +7648,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) if (emitterMode == BytecodeEmitter::SelfHosting && expr->isKind(ParseNodeKind::Call) && - expr->pn_head->name() == cx->names().allowContentIter) + expr->pn_left->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 2db42fb50247..7112bc067c89 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -806,6 +806,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter bool isRestParameter(ParseNode* pn); + MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread); MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 079194d5d607..581418e076b8 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -349,6 +349,7 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result) case ParseNodeKind::Object: case ParseNodeKind::Dot: case ParseNodeKind::Elem: + case ParseNodeKind::Arguments: case ParseNodeKind::Call: case ParseNodeKind::Name: case ParseNodeKind::TemplateString: @@ -1409,8 +1410,9 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser& par { MOZ_ASSERT(node->isKind(ParseNodeKind::Call) || node->isKind(ParseNodeKind::SuperCall) || + node->isKind(ParseNodeKind::New) || node->isKind(ParseNodeKind::TaggedTemplate)); - MOZ_ASSERT(node->isArity(PN_LIST)); + MOZ_ASSERT(node->isArity(PN_BINARY)); // Don't fold a parenthesized callable component in an invocation, as this // might cause a different |this| value to be used, changing semantics: @@ -1423,10 +1425,26 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser& par // assertEq(obj.f``, "obj"); // // See bug 537673 and bug 1182373. - ParseNode** listp = &node->pn_head; - if ((*listp)->isInParens()) - listp = &(*listp)->pn_next; + ParseNode** pn_callee = &node->pn_left; + if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) { + if (!Fold(cx, pn_callee, parser)) + return false; + } + ParseNode** pn_args = &node->pn_right; + if (!Fold(cx, pn_args, parser)) + return false; + + return true; +} + +static bool +FoldArguments(JSContext* cx, ParseNode* node, PerHandlerParser& parser) +{ + MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments)); + MOZ_ASSERT(node->isArity(PN_LIST)); + + ParseNode** listp = &node->pn_head; for (; *listp; listp = &(*listp)->pn_next) { if (!Fold(cx, listp, parser)) return false; @@ -1642,7 +1660,6 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) case ParseNodeKind::InstanceOf: case ParseNodeKind::In: case ParseNodeKind::Comma: - case ParseNodeKind::New: case ParseNodeKind::Array: case ParseNodeKind::Object: case ParseNodeKind::StatementList: @@ -1694,10 +1711,14 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) return FoldAdd(cx, pnp, parser); case ParseNodeKind::Call: + case ParseNodeKind::New: case ParseNodeKind::SuperCall: case ParseNodeKind::TaggedTemplate: return FoldCall(cx, pn, parser); + case ParseNodeKind::Arguments: + return FoldArguments(cx, pn, parser); + case ParseNodeKind::Switch: case ParseNodeKind::Colon: case ParseNodeKind::Assign: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index ea09cd684112..321f56b65ea4 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -275,16 +275,20 @@ class FullParseHandler addList(/* list = */ literal, /* child = */ element); } - ParseNode* newCall(const TokenPos& pos) { - return new_(ParseNodeKind::Call, JSOP_CALL, pos); + ParseNode* newCall(ParseNode* callee, ParseNode* args) { + return new_(ParseNodeKind::Call, JSOP_CALL, callee, args); } - ParseNode* newSuperCall(ParseNode* callee) { - return new_(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee); + ParseNode* newArguments(const TokenPos& pos) { + return new_(ParseNodeKind::Arguments, JSOP_NOP, pos); } - ParseNode* newTaggedTemplate(const TokenPos& pos) { - return new_(ParseNodeKind::TaggedTemplate, JSOP_CALL, pos); + ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) { + return new_(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args); + } + + ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) { + return new_(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args); } ParseNode* newObjectLiteral(uint32_t begin) { @@ -734,13 +738,8 @@ class FullParseHandler return new_(bindings, body); } - Node newNewExpression(uint32_t begin, ParseNode* ctor) { - ParseNode* newExpr = new_(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, begin + 1)); - if (!newExpr) - return nullptr; - - addList(/* list = */ newExpr, /* child = */ ctor); - return newExpr; + Node newNewExpression(uint32_t begin, ParseNode* ctor, ParseNode* args) { + return new_(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args); } ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) { diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 0528ebcadac2..9fc9a478ff51 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -315,17 +315,17 @@ class NameResolver bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) { MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate)); - ParseNode* element = node->pn_head; + ParseNode* tag = node->pn_left; - // The list head is a leading expression, e.g. |tag| in |tag`foo`|, + // The leading expression, e.g. |tag| in |tag`foo`|, // that might contain functions. - if (!resolve(element, prefix)) + if (!resolve(tag, prefix)) return false; - // Next is the callsite object node. This node only contains + // The callsite object node is first. This node only contains // internal strings or undefined and an array -- no user-controlled // expressions. - element = element->pn_next; + ParseNode* element = node->pn_right->pn_head; #ifdef DEBUG { MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj)); @@ -697,9 +697,6 @@ class NameResolver case ParseNodeKind::Pow: case ParseNodeKind::Pipeline: case ParseNodeKind::Comma: - case ParseNodeKind::New: - case ParseNodeKind::Call: - case ParseNodeKind::SuperCall: case ParseNodeKind::Array: case ParseNodeKind::StatementList: case ParseNodeKind::ParamsBody: @@ -733,11 +730,32 @@ class NameResolver break; case ParseNodeKind::TaggedTemplate: - MOZ_ASSERT(cur->isArity(PN_LIST)); + MOZ_ASSERT(cur->isArity(PN_BINARY)); if (!resolveTaggedTemplate(cur, prefix)) return false; break; + case ParseNodeKind::New: + case ParseNodeKind::Call: + case ParseNodeKind::SuperCall: + MOZ_ASSERT(cur->isArity(PN_BINARY)); + if (!resolve(cur->pn_left, prefix)) + return false; + if (!resolve(cur->pn_right, prefix)) + return false; + break; + + // Handles the arguments for new/call/supercall, but does _not_ handle + // the Arguments node used by tagged template literals, since that is + // special-cased inside of resolveTaggedTemplate. + case ParseNodeKind::Arguments: + MOZ_ASSERT(cur->isArity(PN_LIST)); + for (ParseNode* element = cur->pn_head; element; element = element->pn_next) { + if (!resolve(element, prefix)) + return false; + } + break; + // Import/export spec lists contain import/export specs containing // only pairs of names. Alternatively, an export spec lists may // contain a single export batch specifier. diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 9010f5bb5083..84b1a07bab79 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -63,6 +63,7 @@ class ObjectBox; F(Label) \ F(Object) \ F(Call) \ + F(Arguments) \ F(Name) \ F(ObjectPropertyName) \ F(ComputedName) \ @@ -370,9 +371,8 @@ IsTypeofKind(ParseNodeKind kind) * PostIncrement, * PreDecrement, * PostDecrement - * New list pn_head: list of ctor, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * ctor is a MEMBER expr + * New binary pn_left: ctor expression on the left of the ( + * pn_right: Arguments * DeleteName unary pn_kid: Name expr * DeleteProp unary pn_kid: Dot expr * DeleteElem unary pn_kid: Elem expr @@ -385,9 +385,10 @@ IsTypeofKind(ParseNodeKind kind) * pn_atom: name to right of . * Elem binary pn_left: MEMBER expr to left of [ * pn_right: expr between [ and ] - * Call list pn_head: list of call, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * call is a MEMBER expr naming a callable object + * Call binary pn_left: callee expression on the left of the ( + * pn_right: Arguments + * Arguments list pn_head: list of arg1, arg2, ... argN + * pn_count: N (where N is number of args) * Array list pn_head: list of pn_count array element exprs * [,,] holes are represented by Elision nodes * pn_xflags: PN_ENDCOMMA if extra comma at end @@ -407,8 +408,9 @@ IsTypeofKind(ParseNodeKind kind) * list * TemplateString pn_atom: template string atom nullary pn_op: JSOP_NOP - * TaggedTemplate pn_head: list of call, call site object, arg1, arg2, ... argN - * list pn_count: 2 + N (N is the number of substitutions) + * TaggedTemplate pn_left: tag expression + * binary pn_right: Arguments, with the first being the + * call site object, then arg1, arg2, ... argN * CallSiteObj list pn_head: a Array node followed by * list of pn_count - 1 TemplateString nodes * RegExp nullary pn_objbox: RegExp model object @@ -420,7 +422,7 @@ IsTypeofKind(ParseNodeKind kind) * * This, unary pn_kid: '.this' Name if function `this`, else nullptr * SuperBase unary pn_kid: '.this' Name - * + * SuperCall binary pn_left: SuperBase pn_right: Arguments * SetThis binary pn_left: '.this' Name, pn_right: SuperCall * * LexicalScope scope pn_u.scope.bindings: scope bindings diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index b264a0bf9ad0..ecd06752ed29 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3395,13 +3395,13 @@ GeneralParser::addExprAndGetNextTemplStrToken(YieldHandling template bool -GeneralParser::taggedTemplate(YieldHandling yieldHandling, Node nodeList, +GeneralParser::taggedTemplate(YieldHandling yieldHandling, Node tagArgsList, TokenKind tt) { Node callSiteObjNode = handler.newCallSiteObject(pos().begin); if (!callSiteObjNode) return false; - handler.addList(nodeList, callSiteObjNode); + handler.addList(tagArgsList, callSiteObjNode); while (true) { if (!appendToCallSiteObj(callSiteObjNode)) @@ -3409,10 +3409,10 @@ GeneralParser::taggedTemplate(YieldHandling yieldHandling, if (tt != TokenKind::TemplateHead) break; - if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) + if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt)) return false; } - handler.setEndPosition(nodeList, callSiteObjNode); + handler.setEndPosition(tagArgsList, callSiteObjNode); return true; } @@ -8631,24 +8631,25 @@ GeneralParser::assignExprWithoutYieldOrAwait(YieldHandling } template -bool -GeneralParser::argumentList(YieldHandling yieldHandling, Node listNode, - bool* isSpread, +typename ParseHandler::Node +GeneralParser::argumentList(YieldHandling yieldHandling, bool* isSpread, PossibleError* possibleError /* = nullptr */) { + Node argsList = handler.newArguments(pos()); + bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand)) - return false; + return null(); if (matched) { - handler.setEndPosition(listNode, pos().end); - return true; + handler.setEndPosition(argsList, pos().end); + return argsList; } while (true) { bool spread = false; uint32_t begin = 0; if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::Operand)) - return false; + return null(); if (matched) { spread = true; begin = pos().begin; @@ -8657,18 +8658,18 @@ GeneralParser::argumentList(YieldHandling yieldHandling, No Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!argNode) - return false; + return null(); if (spread) { argNode = handler.newSpread(begin, argNode); if (!argNode) - return false; + return null(); } - handler.addList(listNode, argNode); + handler.addList(argsList, argNode); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand)) - return false; + return null(); if (!matched) break; @@ -8681,8 +8682,8 @@ GeneralParser::argumentList(YieldHandling yieldHandling, No MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS); - handler.setEndPosition(listNode, pos().end); - return true; + handler.setEndPosition(argsList, pos().end); + return argsList; } bool @@ -8728,20 +8729,27 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, if (!ctorExpr) return null(); - lhs = handler.newNewExpression(newBegin, ctorExpr); - if (!lhs) - return null(); - bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Lp)) return null(); + + bool isSpread = false; + Node args; if (matched) { - bool isSpread = false; - if (!argumentList(yieldHandling, lhs, &isSpread)) - return null(); - if (isSpread) - handler.setOp(lhs, JSOP_SPREADNEW); + args = argumentList(yieldHandling, &isSpread); + } else { + args = handler.newArguments(pos()); } + + if (!args) + return null(); + + lhs = handler.newNewExpression(newBegin, ctorExpr, args); + if (!lhs) + return null(); + + if (isSpread) + handler.setOp(lhs, JSOP_SPREADNEW); } } else if (tt == TokenKind::Super) { Node thisName = newThisName(); @@ -8814,15 +8822,16 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, return null(); } - nextMember = handler.newSuperCall(lhs); - if (!nextMember) - return null(); - // Despite the fact that it's impossible to have |super()| in a // generator, we still inherit the yieldHandling of the // memberExpression, per spec. Curious. bool isSpread = false; - if (!argumentList(yieldHandling, nextMember, &isSpread)) + Node args = argumentList(yieldHandling, &isSpread); + if (!args) + return null(); + + nextMember = handler.newSuperCall(lhs, args); + if (!nextMember) return null(); if (isSpread) @@ -8841,13 +8850,6 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, return null(); } - TokenPos nextMemberPos = pos(); - nextMember = tt == TokenKind::Lp - ? handler.newCall(nextMemberPos) - : handler.newTaggedTemplate(nextMemberPos); - if (!nextMember) - return null(); - JSOp op = JSOP_CALL; bool maybeAsyncArrow = false; if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { @@ -8889,13 +8891,11 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, } } - handler.setBeginPosition(nextMember, lhs); - handler.addList(nextMember, lhs); - if (tt == TokenKind::Lp) { bool isSpread = false; PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr; - if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError)) + Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError); + if (!args) return null(); if (isSpread) { if (op == JSOP_EVAL) @@ -8905,8 +8905,20 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, else op = JSOP_SPREADCALL; } + + nextMember = handler.newCall(lhs, args); + if (!nextMember) + return null(); } else { - if (!taggedTemplate(yieldHandling, nextMember, tt)) + Node args = handler.newArguments(pos()); + if (!args) + return null(); + + if (!taggedTemplate(yieldHandling, args, tt)) + return null(); + + nextMember = handler.newTaggedTemplate(lhs, args); + if (!nextMember) return null(); } handler.setOp(nextMember, op); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 000dfb465395..84bfe83b0dd0 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1155,7 +1155,7 @@ class MOZ_STACK_CLASS GeneralParser Node condition(InHandling inHandling, YieldHandling yieldHandling); - bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread, + Node argumentList(YieldHandling yieldHandling, bool* isSpread, PossibleError* possibleError = nullptr); Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index eb393c698340..e19d841ef660 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -248,9 +248,11 @@ class SyntaxParseHandler MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; } void addArrayElement(Node literal, Node element) { } - Node newCall(const TokenPos& pos) { return NodeFunctionCall; } - Node newSuperCall(Node callee) { return NodeGeneric; } - Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; } + Node newArguments(const TokenPos& pos) { return NodeGeneric; } + Node newCall(Node callee, Node args) { return NodeFunctionCall; } + + Node newSuperCall(Node callee, Node args) { return NodeGeneric; } + Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; } Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } @@ -424,7 +426,7 @@ class SyntaxParseHandler list == NodeFunctionCall); } - Node newNewExpression(uint32_t begin, Node ctor) { + Node newNewExpression(uint32_t begin, Node ctor, Node args) { return NodeGeneric; } diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 80a37395cf59..e2d20dbf97f4 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -438,22 +438,21 @@ static inline ParseNode* CallCallee(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return ListHead(pn); + return BinaryLeft(pn); } static inline unsigned CallArgListLength(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - MOZ_ASSERT(ListLength(pn) >= 1); - return ListLength(pn) - 1; + return ListLength(BinaryRight(pn)); } static inline ParseNode* CallArgList(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return NextNode(ListHead(pn)); + return ListHead(BinaryRight(pn)); } static inline ParseNode* @@ -2903,9 +2902,11 @@ IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type) } static bool -CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName) +CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr, PropertyName* bufferName) { - ParseNode* bufArg = NextNode(ctorExpr); + ParseNode* ctorExpr = BinaryLeft(newExpr); + ParseNode* ctorArgs = BinaryRight(newExpr); + ParseNode* bufArg = ListHead(ctorArgs); if (!bufArg || NextNode(bufArg) != nullptr) return m.fail(ctorExpr, "array view constructor takes exactly one argument"); @@ -2926,7 +2927,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) if (!bufferName) return m.fail(newExpr, "cannot create array view without an asm.js heap parameter"); - ParseNode* ctorExpr = ListHead(newExpr); + ParseNode* ctorExpr = BinaryLeft(newExpr); PropertyName* field; Scalar::Type type; @@ -2955,7 +2956,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) type = global->viewType(); } - if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName)) + if (!CheckNewArrayViewArgs(m, newExpr, bufferName)) return false; return m.addArrayView(varName, type, field); From ad1c4ebb1d292c8eb64c0103fd19ba5fcbafe138 Mon Sep 17 00:00:00 2001 From: Logan F Smyth Date: Thu, 12 Jul 2018 11:29:05 -0700 Subject: [PATCH 02/21] Bug 1378808 - Add a new ParseNodeKind::PropertyName to hold location information about property access name. r=jorendorff MozReview-Commit-ID: J4vHz4ln5Zt --- js/src/builtin/ReflectParse.cpp | 8 ++--- js/src/frontend/BinSource-auto.cpp | 22 ++++++++++---- js/src/frontend/BinSource.yaml | 20 +++++++++++-- js/src/frontend/BytecodeEmitter.cpp | 44 +++++++++++++++------------- js/src/frontend/FoldConstants.cpp | 15 ++++++---- js/src/frontend/FullParseHandler.h | 8 +++-- js/src/frontend/NameFunctions.cpp | 9 +++--- js/src/frontend/ParseNode.cpp | 29 ++++++++++-------- js/src/frontend/ParseNode.h | 28 ++++++++++-------- js/src/frontend/Parser.cpp | 7 ++++- js/src/frontend/Parser.h | 8 +++-- js/src/frontend/SyntaxParseHandler.h | 8 +++-- js/src/wasm/AsmJS.cpp | 8 ++--- 13 files changed, 136 insertions(+), 78 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 90fff7f74982..aca2c027c22c 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2741,17 +2741,17 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case ParseNodeKind::Dot: { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); RootedValue expr(cx); RootedValue propname(cx); - RootedAtom pnAtom(cx, pn->pn_atom); + RootedAtom pnAtom(cx, pn->pn_right->pn_atom); if (pn->as().isSuper()) { - if (!builder.super(&pn->pn_expr->pn_pos, &expr)) + if (!builder.super(&pn->pn_left->pn_pos, &expr)) return false; } else { - if (!expression(pn->pn_expr, &expr)) + if (!expression(pn->pn_left, &expr)) return false; } diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp index 4514af8a4c85..573ba1ba7bb3 100644 --- a/js/src/frontend/BinSource-auto.cpp +++ b/js/src/frontend/BinSource-auto.cpp @@ -6193,13 +6193,18 @@ BinASTParser::parseInterfaceStaticMemberAssignmentTarget(const size_t start const BinField expected_fields[2] = { BinField::Object, BinField::Property }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) + TokenPos namePos; BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); - RootedAtom property(cx_); - MOZ_TRY_VAR(property, tokenizer_->readAtom()); + { + namePos = tokenizer_->pos(); + MOZ_TRY_VAR(property, tokenizer_->readAtom()); - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); + } + + BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); return result; } @@ -6238,13 +6243,18 @@ BinASTParser::parseInterfaceStaticMemberExpression(const size_t start, cons const BinField expected_fields[2] = { BinField::Object, BinField::Property }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) + TokenPos namePos; BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); - RootedAtom property(cx_); - MOZ_TRY_VAR(property, tokenizer_->readAtom()); + { + namePos = tokenizer_->pos(); + MOZ_TRY_VAR(property, tokenizer_->readAtom()); - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); + } + + BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); return result; } diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml index c8020610937a..bae52b19c9f6 100644 --- a/js/src/frontend/BinSource.yaml +++ b/js/src/frontend/BinSource.yaml @@ -918,12 +918,28 @@ SwitchStatementWithDefault: BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope)); StaticMemberAssignmentTarget: + init: + TokenPos namePos; + fields: + property: + block: + before: | + namePos = tokenizer_->pos(); build: | - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); + BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); StaticMemberExpression: + init: + TokenPos namePos; + fields: + property: + block: + before: | + namePos = tokenizer_->pos(); build: | - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); + BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); ThisExpression: build: | diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index bed6ebf6039e..e999f70e2538 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1038,7 +1038,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) // Watch out for getters! case ParseNodeKind::Dot: - MOZ_ASSERT(pn->isArity(PN_NAME)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; @@ -1418,6 +1418,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others + case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("handled by parent nodes"); case ParseNodeKind::Limit: // invalid sentinel value @@ -1889,11 +1890,11 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); MOZ_ASSERT(!pn->as().isSuper()); - ParseNode* pn2 = pn->pn_expr; + ParseNode* pn2 = pn->pn_left; /* * If the object operand is also a dotted property reference, reverse the - * list linked via pn_expr temporarily so we can iterate over it from the + * list linked via pn_left temporarily so we can iterate over it from the * bottom up (reversing again as we go), to avoid excessive recursion. */ if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as().isSuper()) { @@ -1901,9 +1902,9 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) ParseNode* pnup = nullptr; ParseNode* pndown; for (;;) { - /* Reverse pndot->pn_expr to point up, not down. */ - pndown = pndot->pn_expr; - pndot->pn_expr = pnup; + /* Reverse pndot->pn_left to point up, not down. */ + pndown = pndot->pn_left; + pndot->pn_left = pnup; if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as().isSuper()) break; pnup = pndot; @@ -1916,12 +1917,12 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) do { /* Walk back up the list, emitting annotated name ops. */ - if (!emitAtomOp(pndot, JSOP_GETPROP)) + if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP)) return false; - /* Reverse the pn_expr link again. */ - pnup = pndot->pn_expr; - pndot->pn_expr = pndown; + /* Reverse the pn_left link again. */ + pnup = pndot->pn_left; + pndot->pn_left = pndown; pndown = pndot; } while ((pndot = pnup) != nullptr); return true; @@ -1946,7 +1947,7 @@ BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall) bool BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) { - MOZ_ASSERT(pn->isArity(PN_NAME)); + MOZ_ASSERT(pn->isArity(PN_BINARY)); if (!emitPropLHS(pn)) return false; @@ -1954,7 +1955,7 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) if (op == JSOP_CALLPROP && !emit1(JSOP_DUP)) return false; - if (!emitAtomOp(pn, op)) + if (!emitAtomOp(pn->pn_right, op)) return false; if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP)) @@ -1970,7 +1971,7 @@ BytecodeEmitter::emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall) if (!emitSuperPropLHS(base, isCall)) return false; - if (!emitAtomOp(pn, op)) + if (!emitAtomOp(pn->pn_right, op)) return false; if (isCall && !emit1(JSOP_SWAP)) @@ -2000,7 +2001,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn) if (!emit1(JSOP_DUP)) // OBJ OBJ return false; } - if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V + if (!emitAtomOp(pn->pn_kid->pn_right, isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V return false; if (!emit1(JSOP_POS)) // OBJ N return false; @@ -2026,7 +2027,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn) JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER : sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1 + if (!emitAtomOp(pn->pn_kid->pn_right, setOp)) // N? N+1 return false; if (post && !emit1(JSOP_POP)) // RESULT return false; @@ -2751,7 +2752,7 @@ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted) return false; *emitted = 2; } else { - if (!emitTree(target->pn_expr)) + if (!emitTree(target->pn_left)) return false; *emitted = 1; } @@ -2869,7 +2870,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; else setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - if (!emitAtomOp(target, setOp)) + if (!emitAtomOp(target->pn_right, setOp)) return false; break; } @@ -3981,11 +3982,11 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh return false; offset += 2; } else { - if (!emitTree(lhs->expr())) + if (!emitTree(lhs->pn_left)) return false; offset += 1; } - if (!makeAtomIndex(lhs->pn_atom, &atomIndex)) + if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex)) return false; break; case ParseNodeKind::Elem: { @@ -4034,7 +4035,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh } else { if (!emit1(JSOP_DUP)) return false; - bool isLength = (lhs->pn_atom == cx->names().length); + bool isLength = (lhs->pn_right->pn_atom == cx->names().length); getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP; } if (!emitIndex32(getOp, atomIndex)) @@ -8696,8 +8697,9 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: return false; break; + case ParseNodeKind::PropertyName: case ParseNodeKind::PosHolder: - MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder"); + MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property"); default: MOZ_ASSERT(0); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 581418e076b8..dbc45264a772 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -347,6 +347,7 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result) case ParseNodeKind::Comma: case ParseNodeKind::Array: case ParseNodeKind::Object: + case ParseNodeKind::PropertyName: case ParseNodeKind::Dot: case ParseNodeKind::Elem: case ParseNodeKind::Arguments: @@ -1254,7 +1255,8 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParserpn_pos.end); + ParseNode* nameNode = parser.newPropertyName(name, key->pn_pos); + ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode); if (!dottedAccess) return false; dottedAccess->setInParens(node->isInParens()); @@ -1500,14 +1502,14 @@ static bool FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser& parser) { MOZ_ASSERT(node->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(node->isArity(PN_NAME)); + MOZ_ASSERT(node->isArity(PN_BINARY)); // Iterate through a long chain of dotted property accesses to find the // most-nested non-dotted property node, then fold that. - ParseNode** nested = &node->pn_expr; + ParseNode** nested = &node->pn_left; while ((*nested)->isKind(ParseNodeKind::Dot)) { - MOZ_ASSERT((*nested)->isArity(PN_NAME)); - nested = &(*nested)->pn_expr; + MOZ_ASSERT((*nested)->isArity(PN_BINARY)); + nested = &(*nested)->pn_left; } return Fold(cx, nested, parser); @@ -1798,6 +1800,9 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) MOZ_ASSERT(pn->isArity(PN_NAME)); return Fold(cx, &pn->pn_expr, parser); + case ParseNodeKind::PropertyName: + MOZ_CRASH("unreachable, handled by ::Dot"); + case ParseNodeKind::Dot: return FoldDottedProperty(cx, pn, parser); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 321f56b65ea4..49cbb64a0cbb 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -663,8 +663,12 @@ class FullParseHandler return new_(pos); } - ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) { - return new_(expr, key, expr->pn_pos.begin, end); + ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) { + return new_(ParseNodeKind::PropertyName, JSOP_NOP, name, pos); + } + + ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) { + return new_(expr, key, expr->pn_pos.begin, key->pn_pos.end); } ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) { diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 9fc9a478ff51..426b8a6bff62 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -75,11 +75,11 @@ class NameResolver bool nameExpression(ParseNode* n, bool* foundName) { switch (n->getKind()) { case ParseNodeKind::Dot: - if (!nameExpression(n->expr(), foundName)) + if (!nameExpression(n->pn_left, foundName)) return false; if (!*foundName) return true; - return appendPropertyReference(n->pn_atom); + return appendPropertyReference(n->pn_right->pn_atom); case ParseNodeKind::Name: *foundName = true; @@ -784,12 +784,12 @@ class NameResolver } case ParseNodeKind::Dot: - MOZ_ASSERT(cur->isArity(PN_NAME)); + MOZ_ASSERT(cur->isArity(PN_BINARY)); // Super prop nodes do not have a meaningful LHS if (cur->as().isSuper()) break; - if (!resolve(cur->expr(), prefix)) + if (!resolve(cur->pn_left, prefix)) return false; break; @@ -828,6 +828,7 @@ class NameResolver case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate case ParseNodeKind::ClassNames: // by ParseNodeKind::Class + case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("should have been handled by a parent node"); case ParseNodeKind::Limit: // invalid sentinel value diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index b54653bbb7b5..3325d2f2579e 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -216,6 +216,21 @@ UnaryNode::dump(GenericPrinter& out, int indent) void BinaryNode::dump(GenericPrinter& out, int indent) { + if (isKind(ParseNodeKind::Dot)) { + out.put("(."); + + DumpParseTree(pn_right, out, indent + 2); + + out.putChar(' '); + if (as().isSuper()) + out.put("super"); + else + DumpParseTree(pn_left, out, indent + 2); + + out.printf(")"); + return; + } + const char* name = parseNodeNames[size_t(getKind())]; out.printf("(%s ", name); indent += strlen(name) + 2; @@ -288,10 +303,7 @@ DumpName(GenericPrinter& out, const CharT* s, size_t len) void NameNode::dump(GenericPrinter& out, int indent) { - if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) { - if (isKind(ParseNodeKind::Dot)) - out.put("(."); - + if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) { if (!pn_atom) { out.put("#"); } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) { @@ -306,15 +318,6 @@ NameNode::dump(GenericPrinter& out, int indent) else DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length()); } - - if (isKind(ParseNodeKind::Dot)) { - out.putChar(' '); - if (as().isSuper()) - out.put("super"); - else - DumpParseTree(expr(), out, indent + 2); - out.printf(")"); - } return; } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 84b1a07bab79..1dfa0f9f0120 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -55,6 +55,7 @@ class ObjectBox; F(PostIncrement) \ F(PreDecrement) \ F(PostDecrement) \ + F(PropertyName) \ F(Dot) \ F(Elem) \ F(Array) \ @@ -381,8 +382,9 @@ IsTypeofKind(ParseNodeKind kind) * for a more-specific PNK_DELETE* unless constant * folding (or a similar parse tree manipulation) has * occurred - * Dot name pn_expr: MEMBER expr to left of . - * pn_atom: name to right of . + * PropertyName name pn_atom: property name being accessed + * Dot binary pn_left: MEMBER expr to left of . + * pn_right: PropertyName to right of . * Elem binary pn_left: MEMBER expr to left of [ * pn_right: expr between [ and ] * Call binary pn_left: callee expression on the left of the ( @@ -571,8 +573,7 @@ class ParseNode FunctionBox* funbox; /* function object */ }; ParseNode* expr; /* module or function body, var - initializer, argument default, or - base object of ParseNodeKind::Dot */ + initializer, or argument default */ } name; struct { LexicalScope::Data* bindings; @@ -1176,30 +1177,33 @@ class RegExpLiteral : public NullaryNode } }; -class PropertyAccess : public ParseNode +class PropertyAccess : public BinaryNode { public: - PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end) - : ParseNode(ParseNodeKind::Dot, JSOP_NOP, PN_NAME, TokenPos(begin, end)) + /* + * PropertyAccess nodes can have any expression/'super' as left-hand + * side, but the name must be a ParseNodeKind::PropertyName node. + */ + PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end) + : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name) { MOZ_ASSERT(lhs != nullptr); MOZ_ASSERT(name != nullptr); - pn_u.name.expr = lhs; - pn_u.name.atom = name; } static bool test(const ParseNode& node) { bool match = node.isKind(ParseNodeKind::Dot); - MOZ_ASSERT_IF(match, node.isArity(PN_NAME)); + MOZ_ASSERT_IF(match, node.isArity(PN_BINARY)); + MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName)); return match; } ParseNode& expression() const { - return *pn_u.name.expr; + return *pn_u.binary.left; } PropertyName& name() const { - return *pn_u.name.atom->asPropertyName(); + return *pn_u.binary.right->pn_atom->asPropertyName(); } bool isSuper() const { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index ecd06752ed29..d1a7e61c9271 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -8786,7 +8786,12 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, error(JSMSG_BAD_SUPERPROP, "property"); return null(); } - nextMember = handler.newPropertyAccess(lhs, field, pos().end); + + Node name = handler.newPropertyName(field, pos()); + if (!name) + return null(); + + nextMember = handler.newPropertyAccess(lhs, name); if (!nextMember) return null(); } else { diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 84bfe83b0dd0..b27b6bde405e 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -569,8 +569,12 @@ class MOZ_STACK_CLASS PerHandlerParser bool isValidSimpleAssignmentTarget(Node node, FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls); - Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) { - return handler.newPropertyAccess(expr, key, end); + Node newPropertyName(PropertyName* key, const TokenPos& pos) { + return handler.newPropertyName(key, pos); + } + + Node newPropertyAccess(Node expr, Node key) { + return handler.newPropertyAccess(expr, key); } FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index e19d841ef660..511d3b24aab2 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -330,8 +330,12 @@ class SyntaxParseHandler } Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; } - Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) { - lastAtom = key; + Node newPropertyName(PropertyName* name, const TokenPos& pos) { + lastAtom = name; + return NodeGeneric; + } + + Node newPropertyAccess(Node expr, Node key) { return NodeDottedProperty; } diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index e2d20dbf97f4..92d6a73f14fc 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -615,16 +615,16 @@ static ParseNode* DotBase(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(pn->isArity(PN_NAME)); - return pn->expr(); + MOZ_ASSERT(pn->isArity(PN_BINARY)); + return pn->pn_left; } static PropertyName* DotMember(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(pn->isArity(PN_NAME)); - return pn->pn_atom->asPropertyName(); + MOZ_ASSERT(pn->isArity(PN_BINARY)); + return pn->pn_right->pn_atom->asPropertyName(); } static ParseNode* From 265292f1cd0cd478673f12f84556d3963b1670b9 Mon Sep 17 00:00:00 2001 From: Logan F Smyth Date: Thu, 12 Jul 2018 11:51:17 -0700 Subject: [PATCH 03/21] Bug 1378808 - Use ::Arguments or ::PropertyName location for method call column offsets. r=jorendorff MozReview-Commit-ID: G8mG1qsIO21 --- js/src/frontend/BytecodeEmitter.cpp | 49 ++++++++++++++++- js/src/jit-test/lib/assert-offset-columns.js | 7 ++- .../tests/debug/Script-getAllColumnOffsets.js | 55 +++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index e999f70e2538..e5a6d01e0d56 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7070,17 +7070,62 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs } } + ParseNode* coordNode = pn; + if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL)) { + switch (pn_callee->getKind()) { + case ParseNodeKind::Dot: { + + // Check if this member is a simple chain of simple chain of + // property accesses, e.g. x.y.z, this.x.y, super.x.y + bool simpleDotChain = false; + for (ParseNode* cur = pn_callee; cur->isKind(ParseNodeKind::Dot); cur = cur->pn_left) { + ParseNode* left = cur->pn_left; + if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) || + left->isKind(ParseNodeKind::SuperBase)) + { + simpleDotChain = true; + } + } + + if (!simpleDotChain) { + // obj().aprop() // expression + // ^ // column coord + // + // Note: Because of the constant folding logic in FoldElement, + // this case also applies for constant string properties. + // + // obj()['aprop']() // expression + // ^ // column coord + coordNode = pn_callee->pn_right; + } + break; + } + case ParseNodeKind::Elem: + // obj[expr]() // expression + // ^ // column coord + coordNode = pn_args; + break; + default: + break; + } + } + if (!spread) { if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) { - if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn)) + if (!emitCall(JSOP_CALL_IGNORES_RV, argc, coordNode)) return false; checkTypeSet(JSOP_CALL_IGNORES_RV); } else { - if (!emitCall(pn->getOp(), argc, pn)) + if (!emitCall(pn->getOp(), argc, coordNode)) return false; checkTypeSet(pn->getOp()); } } else { + if (coordNode) { + if (!updateSourceCoordNotes(coordNode->pn_pos.begin)) + return false; + } + if (!emit1(pn->getOp())) return false; checkTypeSet(pn->getOp()); diff --git a/js/src/jit-test/lib/assert-offset-columns.js b/js/src/jit-test/lib/assert-offset-columns.js index e69d07a6dde3..0377f870dc53 100644 --- a/js/src/jit-test/lib/assert-offset-columns.js +++ b/js/src/jit-test/lib/assert-offset-columns.js @@ -33,7 +33,12 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) { // Set breakpoints everywhere and call the function. const dbg = new Debugger; - const script = dbg.addDebuggee(global).makeDebuggeeValue(global.f).script; + let debuggeeFn = dbg.addDebuggee(global).makeDebuggeeValue(global.f); + if (debuggeeFn.isBoundFunction) { + debuggeeFn = debuggeeFn.boundTargetFunction; + } + + const { script } = debuggeeFn; for (const offset of script.getAllColumnOffsets()) { assertEq(offset.lineNumber, 1); assertEq(offset.columnNumber < execCode.length, true); diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js index f12cf81fdf72..d8333155330d 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js @@ -83,3 +83,58 @@ assertOffsetColumns( "function f(n) { do { print(n); } while(false); }", " ^ ^ ^", ); + +// getColumnOffsets correctly places the part of normal ::Dot node with identifier root. +assertOffsetColumns( + "var args = [];\n" + + "var obj = { base: { a(){ return { b(){} }; } } };\n" + + "function f(n) { obj.base.a().b(...args); }", + " ^ ^ ^ ^", + "0 2 1 3", +); + +// getColumnOffsets correctly places the part of normal ::Dot node with "this" root. +assertOffsetColumns( + "var args = [];\n" + + "var obj = { base: { a(){ return { b(){} }; } } };\n" + + "var f = function() { this.base.a().b(...args); }.bind(obj);", + " ^ ^ ^ ^", + "0 2 1 3", +); + +// getColumnOffsets correctly places the part of normal ::Dot node with "super" base. +assertOffsetColumns( + "var args = [];\n" + + "var obj = { base: { a(){ return { b(){} }; } } };\n" + + "var f = { __proto__: obj, f(n) { super.base.a().b(...args); } }.f;", + " ^ ^ ^ ^", + "0 2 1 3", +); + +// getColumnOffsets correctly places the part of normal ::Dot node with other base. +assertOffsetColumns( + "var args = [];\n" + + "var obj = { base: { a(){ return { b(){} }; } } };\n" + + "function f(n) { (0, obj).base.a().b(...args); }", + " ^ ^ ^ ^ ^ ^", + "0 1 2 4 3 5", +); + +// getColumnOffsets correctly places the part of folded ::Elem node. +assertOffsetColumns( + "var args = [];\n" + + "var obj = { base: { a(){ return { b(){} }; } } };\n" + + // Constant folding makes the static string behave like a dot access. + "function f(n) { obj.base['a']()['b'](...args); }", + " ^ ^ ^ ^", + "0 2 1 3", +); + +// getColumnOffsets correctly places the part of computed ::Elem node. +assertOffsetColumns( + "var args = [], a = 'a', b = 'b';\n" + + "var obj = { base: { a(){ return { b(){} }; } } };\n" + + "function f(n) { obj.base[a]()[b](...args); }", + " ^ ^ ^^ ^", + "0 1 3 2 4", +); From 38484d357ec1bc6d470f359f252d3e1f80023503 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Fri, 27 Jul 2018 22:48:14 +0300 Subject: [PATCH 04/21] Bug 1474254 -- Disable test Jemalloc.JunkPoison for Windows ccov builds. Summary: GTest is permafailing on Windows because of timeout. Reviewers: glandium Reviewed By: glandium Bug #: 1474254 Differential Revision: https://phabricator.services.mozilla.com/D2043 --HG-- extra : rebase_source : cea68e50f96a1788bf15dc6ca7859e5f698a6209 --- memory/gtest/TestJemalloc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/memory/gtest/TestJemalloc.cpp b/memory/gtest/TestJemalloc.cpp index 248d45ec223e..63cd2e4d9b52 100644 --- a/memory/gtest/TestJemalloc.cpp +++ b/memory/gtest/TestJemalloc.cpp @@ -436,6 +436,8 @@ TEST(Jemalloc, InPlace) // moz_dispose_arena(arena); } +// Bug 1474254: disable this test for windows ccov builds because it leads to timeout. +#if !defined(XP_WIN) || !defined(MOZ_CODE_COVERAGE) TEST(Jemalloc, JunkPoison) { jemalloc_stats_t stats; @@ -622,3 +624,4 @@ TEST(Jemalloc, JunkPoison) // Until Bug 1364359 is fixed it is unsafe to call moz_dispose_arena. // moz_dispose_arena(buf_arena); } +#endif From 927abec5f27c8bad467289b318f78dfeaf1da81f Mon Sep 17 00:00:00 2001 From: Kristen Wright Date: Mon, 16 Jul 2018 14:42:07 -0700 Subject: [PATCH 05/21] Bug 1476141 - JS::ubi::Nodes represent DOM structure in more detail r=KrisWright Added a new CoarseType that refers to DOM nodes. Updated the trees to represent the CoarseType. Created a new type of count in the heap snapshot that sorts the data by a more descriptive type name. Created the descriptive in JS::ubi::Base. --HG-- extra : histedit_source : be24efa4d2ccb85e82046d7cf7d2c3a1e13b1fd7 --- .../components/tree-map/color-coarse-type.js | 2 +- devtools/client/memory/constants.js | 3 + devtools/shared/heapsnapshot/CensusUtils.js | 9 + devtools/shared/heapsnapshot/CoreDump.pb.cc | 245 ++++++++++++++++++ devtools/shared/heapsnapshot/CoreDump.pb.h | 175 ++++++++++++- devtools/shared/heapsnapshot/CoreDump.proto | 28 +- .../shared/heapsnapshot/DeserializedNode.h | 7 + devtools/shared/heapsnapshot/HeapSnapshot.cpp | 22 +- dom/base/NodeUbiReporting.cpp | 6 + dom/base/NodeUbiReporting.h | 3 +- js/public/UbiNode.h | 33 ++- js/public/UbiNodeCensus.h | 7 +- js/src/doc/Debugger/Debugger.Memory.md | 18 +- js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/UbiNodeCensus.cpp | 204 ++++++++++++++- 15 files changed, 721 insertions(+), 42 deletions(-) diff --git a/devtools/client/memory/components/tree-map/color-coarse-type.js b/devtools/client/memory/components/tree-map/color-coarse-type.js index e15216507c42..2e095fcb1198 100644 --- a/devtools/client/memory/components/tree-map/color-coarse-type.js +++ b/devtools/client/memory/components/tree-map/color-coarse-type.js @@ -8,7 +8,7 @@ * Color the boxes in the treemap */ -const TYPES = [ "objects", "other", "strings", "scripts" ]; +const TYPES = [ "objects", "other", "strings", "scripts", "domNode" ]; // The factors determine how much the hue shifts const TYPE_FACTOR = TYPES.length * 3; diff --git a/devtools/client/memory/constants.js b/devtools/client/memory/constants.js index e10f4f13bb3e..44d50d55a1e9 100644 --- a/devtools/client/memory/constants.js +++ b/devtools/client/memory/constants.js @@ -122,6 +122,7 @@ actions.RESIZE_SHORTEST_PATHS = "resize-shortest-paths"; const COUNT = Object.freeze({ by: "count", count: true, bytes: true }); const INTERNAL_TYPE = Object.freeze({ by: "internalType", then: COUNT }); +const DESCRIPTIVE_TYPE = Object.freeze({ by: "descriptiveType", then: COUNT }); const ALLOCATION_STACK = Object.freeze({ by: "allocationStack", then: COUNT, noStack: COUNT @@ -137,6 +138,7 @@ const COARSE_TYPE = Object.freeze({ noFilename: INTERNAL_TYPE }, other: INTERNAL_TYPE, + domNode: DESCRIPTIVE_TYPE, }); exports.censusDisplays = Object.freeze({ @@ -186,6 +188,7 @@ const DOMINATOR_TREE_LABEL_COARSE_TYPE = Object.freeze({ }), strings: INTERNAL_TYPE, other: INTERNAL_TYPE, + domNode: DESCRIPTIVE_TYPE, }); exports.labelDisplays = Object.freeze({ diff --git a/devtools/shared/heapsnapshot/CensusUtils.js b/devtools/shared/heapsnapshot/CensusUtils.js index 891a12bb3a18..14f0e5e25042 100644 --- a/devtools/shared/heapsnapshot/CensusUtils.js +++ b/devtools/shared/heapsnapshot/CensusUtils.js @@ -80,6 +80,14 @@ EDGES.internalType = function(breakdown, report) { })); }; +EDGES.descriptiveType = function(breakdown, report) { + return Object.keys(report).map(key => ({ + edge: key, + referent: report[key], + breakdown: breakdown.then + })); +}; + EDGES.objectClass = function(breakdown, report) { return Object.keys(report).map(key => ({ edge: key, @@ -94,6 +102,7 @@ EDGES.coarseType = function(breakdown, report) { { edge: "scripts", referent: report.scripts, breakdown: breakdown.scripts }, { edge: "strings", referent: report.strings, breakdown: breakdown.strings }, { edge: "other", referent: report.other, breakdown: breakdown.other }, + { edge: "domNode", referent: report.domNode, breakdown: breakdown.domNode }, ]; }; diff --git a/devtools/shared/heapsnapshot/CoreDump.pb.cc b/devtools/shared/heapsnapshot/CoreDump.pb.cc index dfac13a317e0..194410c75c5b 100644 --- a/devtools/shared/heapsnapshot/CoreDump.pb.cc +++ b/devtools/shared/heapsnapshot/CoreDump.pb.cc @@ -48,6 +48,8 @@ public: ::google::protobuf::uint64 jsobjectclassnameref_; ::google::protobuf::internal::ArenaStringPtr scriptfilename_; ::google::protobuf::uint64 scriptfilenameref_; + ::google::protobuf::internal::ArenaStringPtr descriptivetypename_; + ::google::protobuf::uint64 descriptivetypenameref_; } _Node_default_instance_; class EdgeDefaultTypeInternal { public: @@ -1749,6 +1751,8 @@ const int Node::kJsObjectClassNameRefFieldNumber; const int Node::kCoarseTypeFieldNumber; const int Node::kScriptFilenameFieldNumber; const int Node::kScriptFilenameRefFieldNumber; +const int Node::kDescriptiveTypeNameFieldNumber; +const int Node::kDescriptiveTypeNameRefFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 Node::Node() @@ -1816,6 +1820,20 @@ Node::Node(const Node& from) break; } } + clear_has_descriptiveTypeNameOrRef(); + switch (from.descriptiveTypeNameOrRef_case()) { + case kDescriptiveTypeName: { + set_descriptivetypename(from.descriptivetypename()); + break; + } + case kDescriptiveTypeNameRef: { + set_descriptivetypenameref(from.descriptivetypenameref()); + break; + } + case DESCRIPTIVETYPENAMEORREF_NOT_SET: { + break; + } + } // @@protoc_insertion_point(copy_constructor:mozilla.devtools.protobuf.Node) } @@ -1827,6 +1845,7 @@ void Node::SharedCtor() { clear_has_TypeNameOrRef(); clear_has_JSObjectClassNameOrRef(); clear_has_ScriptFilenameOrRef(); + clear_has_descriptiveTypeNameOrRef(); } Node::~Node() { @@ -1845,6 +1864,9 @@ void Node::SharedDtor() { if (has_ScriptFilenameOrRef()) { clear_ScriptFilenameOrRef(); } + if (has_descriptiveTypeNameOrRef()) { + clear_descriptiveTypeNameOrRef(); + } } void Node::SetCachedSize(int size) const { @@ -1919,6 +1941,24 @@ void Node::clear_ScriptFilenameOrRef() { _oneof_case_[2] = SCRIPTFILENAMEORREF_NOT_SET; } +void Node::clear_descriptiveTypeNameOrRef() { +// @@protoc_insertion_point(one_of_clear_start:mozilla.devtools.protobuf.Node) + switch (descriptiveTypeNameOrRef_case()) { + case kDescriptiveTypeName: { + descriptiveTypeNameOrRef_.descriptivetypename_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + break; + } + case kDescriptiveTypeNameRef: { + // No need to clear + break; + } + case DESCRIPTIVETYPENAMEORREF_NOT_SET: { + break; + } + } + _oneof_case_[3] = DESCRIPTIVETYPENAMEORREF_NOT_SET; +} + void Node::Clear() { // @@protoc_insertion_point(message_clear_start:mozilla.devtools.protobuf.Node) @@ -1940,6 +1980,7 @@ void Node::Clear() { clear_TypeNameOrRef(); clear_JSObjectClassNameOrRef(); clear_ScriptFilenameOrRef(); + clear_descriptiveTypeNameOrRef(); _has_bits_.Clear(); _internal_metadata_.Clear(); } @@ -2107,6 +2148,33 @@ bool Node::MergePartialFromCodedStream( break; } + // optional bytes descriptiveTypeName = 12; + case 12: { + if (static_cast< ::google::protobuf::uint8>(tag) == + static_cast< ::google::protobuf::uint8>(98u /* 98 & 0xFF */)) { + DO_(::google::protobuf::internal::WireFormatLite::ReadBytes( + input, this->mutable_descriptivetypename())); + } else { + goto handle_unusual; + } + break; + } + + // optional uint64 descriptiveTypeNameRef = 13; + case 13: { + if (static_cast< ::google::protobuf::uint8>(tag) == + static_cast< ::google::protobuf::uint8>(104u /* 104 & 0xFF */)) { + clear_descriptiveTypeNameOrRef(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>( + input, &descriptiveTypeNameOrRef_.descriptivetypenameref_))); + set_has_descriptivetypenameref(); + } else { + goto handle_unusual; + } + break; + } + default: { handle_unusual: if (tag == 0) { @@ -2192,6 +2260,16 @@ void Node::SerializeWithCachedSizes( break; default: ; } + switch (descriptiveTypeNameOrRef_case()) { + case kDescriptiveTypeName: + ::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased( + 12, this->descriptivetypename(), output); + break; + case kDescriptiveTypeNameRef: + ::google::protobuf::internal::WireFormatLite::WriteUInt64(13, this->descriptivetypenameref(), output); + break; + default: ; + } output->WriteRaw(_internal_metadata_.unknown_fields().data(), static_cast(_internal_metadata_.unknown_fields().size())); // @@protoc_insertion_point(serialize_end:mozilla.devtools.protobuf.Node) @@ -2301,6 +2379,25 @@ size_t Node::ByteSizeLong() const { break; } } + switch (descriptiveTypeNameOrRef_case()) { + // optional bytes descriptiveTypeName = 12; + case kDescriptiveTypeName: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::BytesSize( + this->descriptivetypename()); + break; + } + // optional uint64 descriptiveTypeNameRef = 13; + case kDescriptiveTypeNameRef: { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::UInt64Size( + this->descriptivetypenameref()); + break; + } + case DESCRIPTIVETYPENAMEORREF_NOT_SET: { + break; + } + } int cached_size = ::google::protobuf::internal::ToCachedSize(total_size); GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); _cached_size_ = cached_size; @@ -2376,6 +2473,19 @@ void Node::MergeFrom(const Node& from) { break; } } + switch (from.descriptiveTypeNameOrRef_case()) { + case kDescriptiveTypeName: { + set_descriptivetypename(from.descriptivetypename()); + break; + } + case kDescriptiveTypeNameRef: { + set_descriptivetypenameref(from.descriptivetypenameref()); + break; + } + case DESCRIPTIVETYPENAMEORREF_NOT_SET: { + break; + } + } } void Node::CopyFrom(const Node& from) { @@ -2406,6 +2516,8 @@ void Node::InternalSwap(Node* other) { swap(_oneof_case_[1], other->_oneof_case_[1]); swap(ScriptFilenameOrRef_, other->ScriptFilenameOrRef_); swap(_oneof_case_[2], other->_oneof_case_[2]); + swap(descriptiveTypeNameOrRef_, other->descriptiveTypeNameOrRef_); + swap(_oneof_case_[3], other->_oneof_case_[3]); swap(_has_bits_[0], other->_has_bits_[0]); _internal_metadata_.Swap(&other->_internal_metadata_); swap(_cached_size_, other->_cached_size_); @@ -2938,6 +3050,130 @@ void Node::set_scriptfilenameref(::google::protobuf::uint64 value) { // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.scriptFilenameRef) } +// optional bytes descriptiveTypeName = 12; +bool Node::has_descriptivetypename() const { + return descriptiveTypeNameOrRef_case() == kDescriptiveTypeName; +} +void Node::set_has_descriptivetypename() { + _oneof_case_[3] = kDescriptiveTypeName; +} +void Node::clear_descriptivetypename() { + if (has_descriptivetypename()) { + descriptiveTypeNameOrRef_.descriptivetypename_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + clear_has_descriptiveTypeNameOrRef(); + } +} +const ::std::string& Node::descriptivetypename() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (has_descriptivetypename()) { + return descriptiveTypeNameOrRef_.descriptivetypename_.GetNoArena(); + } + return *&::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +void Node::set_descriptivetypename(const ::std::string& value) { + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +#if LANG_CXX11 +void Node::set_descriptivetypename(::std::string&& value) { + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena( + &::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); + // @@protoc_insertion_point(field_set_rvalue:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +#endif +void Node::set_descriptivetypename(const char* value) { + GOOGLE_DCHECK(value != NULL); + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(value)); + // @@protoc_insertion_point(field_set_char:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +void Node::set_descriptivetypename(const void* value, size_t size) { + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +::std::string* Node::mutable_descriptivetypename() { + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + // @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.descriptiveTypeName) + return descriptiveTypeNameOrRef_.descriptivetypename_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +::std::string* Node::release_descriptivetypename() { + // @@protoc_insertion_point(field_release:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (has_descriptivetypename()) { + clear_has_descriptiveTypeNameOrRef(); + return descriptiveTypeNameOrRef_.descriptivetypename_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } else { + return NULL; + } +} +void Node::set_allocated_descriptivetypename(::std::string* descriptivetypename) { + if (!has_descriptivetypename()) { + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + clear_descriptiveTypeNameOrRef(); + if (descriptivetypename != NULL) { + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + descriptivetypename); + } + // @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} + +// optional uint64 descriptiveTypeNameRef = 13; +bool Node::has_descriptivetypenameref() const { + return descriptiveTypeNameOrRef_case() == kDescriptiveTypeNameRef; +} +void Node::set_has_descriptivetypenameref() { + _oneof_case_[3] = kDescriptiveTypeNameRef; +} +void Node::clear_descriptivetypenameref() { + if (has_descriptivetypenameref()) { + descriptiveTypeNameOrRef_.descriptivetypenameref_ = GOOGLE_ULONGLONG(0); + clear_has_descriptiveTypeNameOrRef(); + } +} +::google::protobuf::uint64 Node::descriptivetypenameref() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.descriptiveTypeNameRef) + if (has_descriptivetypenameref()) { + return descriptiveTypeNameOrRef_.descriptivetypenameref_; + } + return GOOGLE_ULONGLONG(0); +} +void Node::set_descriptivetypenameref(::google::protobuf::uint64 value) { + if (!has_descriptivetypenameref()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypenameref(); + } + descriptiveTypeNameOrRef_.descriptivetypenameref_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeNameRef) +} + bool Node::has_TypeNameOrRef() const { return TypeNameOrRef_case() != TYPENAMEORREF_NOT_SET; } @@ -2956,6 +3192,12 @@ bool Node::has_ScriptFilenameOrRef() const { void Node::clear_has_ScriptFilenameOrRef() { _oneof_case_[2] = SCRIPTFILENAMEORREF_NOT_SET; } +bool Node::has_descriptiveTypeNameOrRef() const { + return descriptiveTypeNameOrRef_case() != DESCRIPTIVETYPENAMEORREF_NOT_SET; +} +void Node::clear_has_descriptiveTypeNameOrRef() { + _oneof_case_[3] = DESCRIPTIVETYPENAMEORREF_NOT_SET; +} Node::TypeNameOrRefCase Node::TypeNameOrRef_case() const { return Node::TypeNameOrRefCase(_oneof_case_[0]); } @@ -2965,6 +3207,9 @@ Node::JSObjectClassNameOrRefCase Node::JSObjectClassNameOrRef_case() const { Node::ScriptFilenameOrRefCase Node::ScriptFilenameOrRef_case() const { return Node::ScriptFilenameOrRefCase(_oneof_case_[2]); } +Node::DescriptiveTypeNameOrRefCase Node::descriptiveTypeNameOrRef_case() const { + return Node::DescriptiveTypeNameOrRefCase(_oneof_case_[3]); +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS // =================================================================== diff --git a/devtools/shared/heapsnapshot/CoreDump.pb.h b/devtools/shared/heapsnapshot/CoreDump.pb.h index 3f25f4c3b352..7a627807887f 100644 --- a/devtools/shared/heapsnapshot/CoreDump.pb.h +++ b/devtools/shared/heapsnapshot/CoreDump.pb.h @@ -605,6 +605,12 @@ class Node : public ::google::protobuf::MessageLite /* @@protoc_insertion_point( SCRIPTFILENAMEORREF_NOT_SET = 0, }; + enum DescriptiveTypeNameOrRefCase { + kDescriptiveTypeName = 12, + kDescriptiveTypeNameRef = 13, + DESCRIPTIVETYPENAMEORREF_NOT_SET = 0, + }; + static inline const Node* internal_default_instance() { return reinterpret_cast( &_Node_default_instance_); @@ -764,9 +770,32 @@ class Node : public ::google::protobuf::MessageLite /* @@protoc_insertion_point( ::google::protobuf::uint64 scriptfilenameref() const; void set_scriptfilenameref(::google::protobuf::uint64 value); + // optional bytes descriptiveTypeName = 12; + bool has_descriptivetypename() const; + void clear_descriptivetypename(); + static const int kDescriptiveTypeNameFieldNumber = 12; + const ::std::string& descriptivetypename() const; + void set_descriptivetypename(const ::std::string& value); + #if LANG_CXX11 + void set_descriptivetypename(::std::string&& value); + #endif + void set_descriptivetypename(const char* value); + void set_descriptivetypename(const void* value, size_t size); + ::std::string* mutable_descriptivetypename(); + ::std::string* release_descriptivetypename(); + void set_allocated_descriptivetypename(::std::string* descriptivetypename); + + // optional uint64 descriptiveTypeNameRef = 13; + bool has_descriptivetypenameref() const; + void clear_descriptivetypenameref(); + static const int kDescriptiveTypeNameRefFieldNumber = 13; + ::google::protobuf::uint64 descriptivetypenameref() const; + void set_descriptivetypenameref(::google::protobuf::uint64 value); + TypeNameOrRefCase TypeNameOrRef_case() const; JSObjectClassNameOrRefCase JSObjectClassNameOrRef_case() const; ScriptFilenameOrRefCase ScriptFilenameOrRef_case() const; + DescriptiveTypeNameOrRefCase descriptiveTypeNameOrRef_case() const; // @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Node) private: void set_has_id(); @@ -783,6 +812,8 @@ class Node : public ::google::protobuf::MessageLite /* @@protoc_insertion_point( void clear_has_coarsetype(); void set_has_scriptfilename(); void set_has_scriptfilenameref(); + void set_has_descriptivetypename(); + void set_has_descriptivetypenameref(); inline bool has_TypeNameOrRef() const; void clear_TypeNameOrRef(); @@ -796,6 +827,10 @@ class Node : public ::google::protobuf::MessageLite /* @@protoc_insertion_point( void clear_ScriptFilenameOrRef(); inline void clear_has_ScriptFilenameOrRef(); + inline bool has_descriptiveTypeNameOrRef() const; + void clear_descriptiveTypeNameOrRef(); + inline void clear_has_descriptiveTypeNameOrRef(); + ::google::protobuf::internal::InternalMetadataWithArenaLite _internal_metadata_; ::google::protobuf::internal::HasBits<1> _has_bits_; mutable int _cached_size_; @@ -819,7 +854,12 @@ class Node : public ::google::protobuf::MessageLite /* @@protoc_insertion_point( ::google::protobuf::internal::ArenaStringPtr scriptfilename_; ::google::protobuf::uint64 scriptfilenameref_; } ScriptFilenameOrRef_; - ::google::protobuf::uint32 _oneof_case_[3]; + union DescriptiveTypeNameOrRefUnion { + DescriptiveTypeNameOrRefUnion() {} + ::google::protobuf::internal::ArenaStringPtr descriptivetypename_; + ::google::protobuf::uint64 descriptivetypenameref_; + } descriptiveTypeNameOrRef_; + ::google::protobuf::uint32 _oneof_case_[4]; friend struct protobuf_CoreDump_2eproto::TableStruct; }; @@ -2057,6 +2097,130 @@ inline void Node::set_scriptfilenameref(::google::protobuf::uint64 value) { // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.scriptFilenameRef) } +// optional bytes descriptiveTypeName = 12; +inline bool Node::has_descriptivetypename() const { + return descriptiveTypeNameOrRef_case() == kDescriptiveTypeName; +} +inline void Node::set_has_descriptivetypename() { + _oneof_case_[3] = kDescriptiveTypeName; +} +inline void Node::clear_descriptivetypename() { + if (has_descriptivetypename()) { + descriptiveTypeNameOrRef_.descriptivetypename_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + clear_has_descriptiveTypeNameOrRef(); + } +} +inline const ::std::string& Node::descriptivetypename() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (has_descriptivetypename()) { + return descriptiveTypeNameOrRef_.descriptivetypename_.GetNoArena(); + } + return *&::google::protobuf::internal::GetEmptyStringAlreadyInited(); +} +inline void Node::set_descriptivetypename(const ::std::string& value) { + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +#if LANG_CXX11 +inline void Node::set_descriptivetypename(::std::string&& value) { + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena( + &::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); + // @@protoc_insertion_point(field_set_rvalue:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +#endif +inline void Node::set_descriptivetypename(const char* value) { + GOOGLE_DCHECK(value != NULL); + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(value)); + // @@protoc_insertion_point(field_set_char:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +inline void Node::set_descriptivetypename(const void* value, size_t size) { + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + descriptiveTypeNameOrRef_.descriptivetypename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} +inline ::std::string* Node::mutable_descriptivetypename() { + if (!has_descriptivetypename()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + // @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.descriptiveTypeName) + return descriptiveTypeNameOrRef_.descriptivetypename_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* Node::release_descriptivetypename() { + // @@protoc_insertion_point(field_release:mozilla.devtools.protobuf.Node.descriptiveTypeName) + if (has_descriptivetypename()) { + clear_has_descriptiveTypeNameOrRef(); + return descriptiveTypeNameOrRef_.descriptivetypename_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } else { + return NULL; + } +} +inline void Node::set_allocated_descriptivetypename(::std::string* descriptivetypename) { + if (!has_descriptivetypename()) { + descriptiveTypeNameOrRef_.descriptivetypename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + clear_descriptiveTypeNameOrRef(); + if (descriptivetypename != NULL) { + set_has_descriptivetypename(); + descriptiveTypeNameOrRef_.descriptivetypename_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + descriptivetypename); + } + // @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Node.descriptiveTypeName) +} + +// optional uint64 descriptiveTypeNameRef = 13; +inline bool Node::has_descriptivetypenameref() const { + return descriptiveTypeNameOrRef_case() == kDescriptiveTypeNameRef; +} +inline void Node::set_has_descriptivetypenameref() { + _oneof_case_[3] = kDescriptiveTypeNameRef; +} +inline void Node::clear_descriptivetypenameref() { + if (has_descriptivetypenameref()) { + descriptiveTypeNameOrRef_.descriptivetypenameref_ = GOOGLE_ULONGLONG(0); + clear_has_descriptiveTypeNameOrRef(); + } +} +inline ::google::protobuf::uint64 Node::descriptivetypenameref() const { + // @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.descriptiveTypeNameRef) + if (has_descriptivetypenameref()) { + return descriptiveTypeNameOrRef_.descriptivetypenameref_; + } + return GOOGLE_ULONGLONG(0); +} +inline void Node::set_descriptivetypenameref(::google::protobuf::uint64 value) { + if (!has_descriptivetypenameref()) { + clear_descriptiveTypeNameOrRef(); + set_has_descriptivetypenameref(); + } + descriptiveTypeNameOrRef_.descriptivetypenameref_ = value; + // @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.descriptiveTypeNameRef) +} + inline bool Node::has_TypeNameOrRef() const { return TypeNameOrRef_case() != TYPENAMEORREF_NOT_SET; } @@ -2075,6 +2239,12 @@ inline bool Node::has_ScriptFilenameOrRef() const { inline void Node::clear_has_ScriptFilenameOrRef() { _oneof_case_[2] = SCRIPTFILENAMEORREF_NOT_SET; } +inline bool Node::has_descriptiveTypeNameOrRef() const { + return descriptiveTypeNameOrRef_case() != DESCRIPTIVETYPENAMEORREF_NOT_SET; +} +inline void Node::clear_has_descriptiveTypeNameOrRef() { + _oneof_case_[3] = DESCRIPTIVETYPENAMEORREF_NOT_SET; +} inline Node::TypeNameOrRefCase Node::TypeNameOrRef_case() const { return Node::TypeNameOrRefCase(_oneof_case_[0]); } @@ -2084,6 +2254,9 @@ inline Node::JSObjectClassNameOrRefCase Node::JSObjectClassNameOrRef_case() cons inline Node::ScriptFilenameOrRefCase Node::ScriptFilenameOrRef_case() const { return Node::ScriptFilenameOrRefCase(_oneof_case_[2]); } +inline Node::DescriptiveTypeNameOrRefCase Node::descriptiveTypeNameOrRef_case() const { + return Node::DescriptiveTypeNameOrRefCase(_oneof_case_[3]); +} // ------------------------------------------------------------------- // Edge diff --git a/devtools/shared/heapsnapshot/CoreDump.proto b/devtools/shared/heapsnapshot/CoreDump.proto index aa5fafa80420..4d0bf27f013a 100644 --- a/devtools/shared/heapsnapshot/CoreDump.proto +++ b/devtools/shared/heapsnapshot/CoreDump.proto @@ -106,31 +106,37 @@ message StackFrame { // A serialized version of `JS::ubi::Node` and its outgoing edges. message Node { - optional uint64 id = 1; + optional uint64 id = 1; // De-duplicated two-byte string. oneof TypeNameOrRef { - bytes typeName = 2; - uint64 typeNameRef = 3; + bytes typeName = 2; + uint64 typeNameRef = 3; } - optional uint64 size = 4; - repeated Edge edges = 5; - optional StackFrame allocationStack = 6; + optional uint64 size = 4; + repeated Edge edges = 5; + optional StackFrame allocationStack = 6; // De-duplicated one-byte string. oneof JSObjectClassNameOrRef { - bytes jsObjectClassName = 7; - uint64 jsObjectClassNameRef = 8; + bytes jsObjectClassName = 7; + uint64 jsObjectClassNameRef = 8; } // JS::ubi::CoarseType. Defaults to Other. - optional uint32 coarseType = 9 [default = 0]; + optional uint32 coarseType = 9 [default = 0]; // De-duplicated one-byte string. oneof ScriptFilenameOrRef { - bytes scriptFilename = 10; - uint64 scriptFilenameRef = 11; + bytes scriptFilename = 10; + uint64 scriptFilenameRef = 11; + } + + // De-duplicated one-byte string. + oneof descriptiveTypeNameOrRef { + bytes descriptiveTypeName = 12; + uint64 descriptiveTypeNameRef = 13; } } diff --git a/devtools/shared/heapsnapshot/DeserializedNode.h b/devtools/shared/heapsnapshot/DeserializedNode.h index d2770e99ddd5..c928f7725bc4 100644 --- a/devtools/shared/heapsnapshot/DeserializedNode.h +++ b/devtools/shared/heapsnapshot/DeserializedNode.h @@ -70,6 +70,8 @@ struct DeserializedNode { const char* jsObjectClassName; // A borrowed reference to a string owned by this node's owning HeapSnapshot. const char* scriptFilename; + // A borrowed reference to a string owned by this node's owning HeapSnapshot. + const char16_t* descriptiveTypeName; // A weak pointer to this node's owning `HeapSnapshot`. Safe without // AddRef'ing because this node's lifetime is equal to that of its owner. HeapSnapshot* owner; @@ -82,6 +84,7 @@ struct DeserializedNode { const Maybe& allocationStack, const char* className, const char* filename, + const char16_t* descriptiveName, HeapSnapshot& owner) : id(id) , coarseType(coarseType) @@ -91,6 +94,7 @@ struct DeserializedNode { , allocationStack(allocationStack) , jsObjectClassName(className) , scriptFilename(filename) + , descriptiveTypeName(descriptiveName) , owner(&owner) { } virtual ~DeserializedNode() { } @@ -104,6 +108,7 @@ struct DeserializedNode { , allocationStack(rhs.allocationStack) , jsObjectClassName(rhs.jsObjectClassName) , scriptFilename(rhs.scriptFilename) + , descriptiveTypeName(rhs.descriptiveTypeName) , owner(rhs.owner) { } @@ -132,6 +137,7 @@ protected: , allocationStack(Nothing()) , jsObjectClassName(nullptr) , scriptFilename(nullptr) + , descriptiveTypeName(nullptr) , owner(nullptr) { } @@ -259,6 +265,7 @@ public: Node::Size size(mozilla::MallocSizeOf mallocSizeof) const override; const char* jsObjectClassName() const override { return get().jsObjectClassName; } const char* scriptFilename() const final { return get().scriptFilename; } + const char16_t* descriptiveTypeName() const override { return get().descriptiveTypeName; } bool hasAllocationStack() const override { return get().allocationStack.isSome(); } StackFrame allocationStack() const override; diff --git a/devtools/shared/heapsnapshot/HeapSnapshot.cpp b/devtools/shared/heapsnapshot/HeapSnapshot.cpp index 6301da1fde31..b544e06cedaf 100644 --- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp +++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp @@ -284,11 +284,21 @@ HeapSnapshot::saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents) return false; } + const char16_t* descriptiveTypeName = nullptr; + if (node.descriptiveTypeNameOrRef_case() != protobuf::Node::DESCRIPTIVETYPENAMEORREF_NOT_SET) { + Maybe descriptiveTypeNameOrRef = GET_STRING_OR_REF(node, descriptivetypename); + descriptiveTypeName = getOrInternString(internedTwoByteStrings, descriptiveTypeNameOrRef); + if (NS_WARN_IF(!descriptiveTypeName)) + return false; + } + if (NS_WARN_IF(!nodes.putNew(id, DeserializedNode(id, coarseType, typeName, size, std::move(edges), allocationStack, jsObjectClassName, - scriptFilename, *this)))) + scriptFilename, + descriptiveTypeName, + *this)))) { return false; }; @@ -1340,6 +1350,16 @@ public: } } + if (ubiNode.descriptiveTypeName()) { + auto descriptiveTypeName = TwoByteString(ubiNode.descriptiveTypeName()); + if (NS_WARN_IF(!attachTwoByteString(descriptiveTypeName, + [&] (std::string* name) { protobufNode.set_allocated_descriptivetypename(name); }, + [&] (uint64_t ref) { protobufNode.set_descriptivetypenameref(ref); }))) + { + return false; + } + } + return writeMessage(protobufNode); } }; diff --git a/dom/base/NodeUbiReporting.cpp b/dom/base/NodeUbiReporting.cpp index 662afbbc04e2..68731cc0f6b7 100644 --- a/dom/base/NodeUbiReporting.cpp +++ b/dom/base/NodeUbiReporting.cpp @@ -64,6 +64,12 @@ JS::ubi::Concrete::size(mozilla::MallocSizeOf mallocSizeOf) const return n; } +const char16_t* +JS::ubi::Concrete::descriptiveTypeName() const +{ + return get().NodeName().get(); +} + JS::ubi::Node::Size JS::ubi::Concrete::size(mozilla::MallocSizeOf mallocSizeOf) const { diff --git a/dom/base/NodeUbiReporting.h b/dom/base/NodeUbiReporting.h index e825187a70bd..07a3493af36f 100644 --- a/dom/base/NodeUbiReporting.h +++ b/dom/base/NodeUbiReporting.h @@ -36,7 +36,8 @@ public: js::UniquePtr edges(JSContext* cx, bool wantNames) const override; nsINode& get() const { return *static_cast(ptr); } - CoarseType coarseType() const final { return CoarseType::Other; } + CoarseType coarseType() const final { return CoarseType::DOMNode; } + const char16_t* descriptiveTypeName() const override; }; template<> diff --git a/js/public/UbiNode.h b/js/public/UbiNode.h index 011aae134f5f..37a0af1ed055 100644 --- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -484,13 +484,14 @@ ConstructSavedFrameStackSlow(JSContext* cx, // by an offline heap snapshot from an older SpiderMonkey/Firefox version to // break. Consider this enum append only. enum class CoarseType: uint32_t { - Other = 0, - Object = 1, - Script = 2, - String = 3, + Other = 0, + Object = 1, + Script = 2, + String = 3, + DOMNode = 4, - FIRST = Other, - LAST = String + FIRST = Other, + LAST = DOMNode }; inline uint32_t @@ -619,6 +620,13 @@ class JS_PUBLIC_API(Base) { "hasAllocationStack and allocationStack."); } + // In some cases, Concrete can return a more descriptive + // referent type name than simply `T`. This method returns an + // identifier as specific as is efficiently available. + // The string returned is borrowed from the ubi::Node's referent. + // If nothing more specific than typeName() is available, return nullptr. + virtual const char16_t* descriptiveTypeName() const { return nullptr; } + // Methods for JSObject Referents // // These methods are only semantically valid if the referent is either a @@ -778,12 +786,13 @@ class Node { // not all!) JSObjects can be exposed. JS::Value exposeToJS() const; - CoarseType coarseType() const { return base()->coarseType(); } - const char16_t* typeName() const { return base()->typeName(); } - JS::Zone* zone() const { return base()->zone(); } - JS::Compartment* compartment() const { return base()->compartment(); } - JS::Realm* realm() const { return base()->realm(); } - const char* jsObjectClassName() const { return base()->jsObjectClassName(); } + CoarseType coarseType() const { return base()->coarseType(); } + const char16_t* typeName() const { return base()->typeName(); } + JS::Zone* zone() const { return base()->zone(); } + JS::Compartment* compartment() const { return base()->compartment(); } + JS::Realm* realm() const { return base()->realm(); } + const char* jsObjectClassName() const { return base()->jsObjectClassName(); } + const char16_t* descriptiveTypeName() const { return base()->descriptiveTypeName(); } MOZ_MUST_USE bool jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const { return base()->jsObjectConstructorName(cx, outName); } diff --git a/js/public/UbiNodeCensus.h b/js/public/UbiNodeCensus.h index 3760f3037084..e385cd7b9da7 100644 --- a/js/public/UbiNodeCensus.h +++ b/js/public/UbiNodeCensus.h @@ -30,6 +30,8 @@ // - objects with a specific [[Class]] * // - strings // - scripts +// - DOM nodes +// - nsINodes with a specific name (found in nsINode::NodeName()) * // - all other Node types // - nodes with a specific ubi::Node::typeName * // @@ -47,7 +49,8 @@ // { // by: "coarseType", // objects: { by: "objectClass" }, -// other: { by: "internalType" } +// other: { by: "internalType" }, +// domNode: { by: "descriptiveType" } // } // // we would build the following tree of CountType subclasses: @@ -59,6 +62,8 @@ // strings: SimpleCount // other: ByUbinodeType // each type: SimpleCount +// domNode: ByDomObjectClass +// each type: SimpleCount // // The interior nodes are all breakdown types that categorize nodes according to // one characteristic or another; and the leaf nodes are all SimpleType. diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md index 8268b1da04af..473c5ec03e36 100644 --- a/js/src/doc/Debugger/Debugger.Memory.md +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -275,13 +275,15 @@ Function Properties of the `Debugger.Memory.prototype` Object { "count": 1616, "bytes": 93240 } - Here is a breakdown that groups JavaScript objects by their class name, and - non-string, non-script items by their C++ type name: + Here is a breakdown that groups JavaScript objects by their class name, + non-string, non-script items by their C++ type name, and DOM nodes with + their node name: { by: "coarseType", objects: { by: "objectClass" }, - other: { by: "internalType" } + other: { by: "internalType" }, + domNode: { by: "descriptiveType" } } which produces a result like this: @@ -300,6 +302,9 @@ Function Properties of the `Debugger.Memory.prototype` Object "js::Shape": { "count": 450, "bytes": 0 }, "js::BaseShape": { "count": 21, "bytes": 0 }, "js::ObjectGroup": { "count": 17, "bytes": 0 } + }, + "domNode": { + "#text": { "count": 1, "bytes": 12 } } } @@ -370,7 +375,7 @@ Function Properties of the `Debugger.Memory.prototype` Object The results for non-object items appear as the value of the property named `"other"`. - { by: "coarseType", objects:objects, scripts:scripts, strings:strings, other:other } + { by: "coarseType", objects:objects, scripts:scripts, strings:strings, domNode:domNode, other:other } : Group items by their coarse type. Use the breakdown value objects for items that are JavaScript @@ -382,6 +387,8 @@ Function Properties of the `Debugger.Memory.prototype` Object Use the breakdown value strings for JavaScript strings. + Use the breakdown value domNode for DOM nodes. + Use the breakdown value other for items that don't fit into any of the above categories. @@ -393,6 +400,7 @@ Function Properties of the `Debugger.Memory.prototype` Object "objects": result, "scripts": result, "strings": result, + "domNode:" result, "other": result } @@ -438,6 +446,7 @@ Function Properties of the `Debugger.Memory.prototype` Object { by: "coarseType", objects: { by: "objectClass" }, + domNode: { by: "descriptiveType" }, other: { by: "internalType" } } @@ -449,6 +458,7 @@ Function Properties of the `Debugger.Memory.prototype` Object objects: { class: count, ... }, scripts: count, strings: count, + domNode: { node name: count, ... }, other: { type name: count, ... } } diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 0d537fbf9f07..7d3d02f3bfd3 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -102,6 +102,7 @@ macro(direction, direction, "direction") \ macro(displayURL, displayURL, "displayURL") \ macro(do, do_, "do") \ + macro(domNode, domNode, "domNode") \ macro(done, done, "done") \ macro(dotGenerator, dotGenerator, ".generator") \ macro(dotThis, dotThis, ".this") \ diff --git a/js/src/vm/UbiNodeCensus.cpp b/js/src/vm/UbiNodeCensus.cpp index 3bf7d5c903ab..852c2ff74249 100644 --- a/js/src/vm/UbiNodeCensus.cpp +++ b/js/src/vm/UbiNodeCensus.cpp @@ -177,7 +177,7 @@ BucketCount::report(JSContext* cx, CountBase& countBase, MutableHandleValue repo // A type that categorizes nodes by their JavaScript type -- 'objects', -// 'strings', 'scripts', and 'other' -- and then passes the nodes to child +// 'strings', 'scripts', 'domNode', and 'other' -- and then passes the nodes to child // types. // // Implementation details of scripts like jitted code are counted under @@ -187,36 +187,42 @@ class ByCoarseType : public CountType { CountTypePtr scripts; CountTypePtr strings; CountTypePtr other; + CountTypePtr domNode; struct Count : CountBase { Count(CountType& type, CountBasePtr& objects, CountBasePtr& scripts, CountBasePtr& strings, - CountBasePtr& other) + CountBasePtr& other, + CountBasePtr& domNode) : CountBase(type), objects(std::move(objects)), scripts(std::move(scripts)), strings(std::move(strings)), - other(std::move(other)) + other(std::move(other)), + domNode(std::move(domNode)) { } CountBasePtr objects; CountBasePtr scripts; CountBasePtr strings; CountBasePtr other; + CountBasePtr domNode; }; public: ByCoarseType(CountTypePtr& objects, CountTypePtr& scripts, CountTypePtr& strings, - CountTypePtr& other) + CountTypePtr& other, + CountTypePtr& domNode) : CountType(), objects(std::move(objects)), scripts(std::move(scripts)), strings(std::move(strings)), - other(std::move(other)) + other(std::move(other)), + domNode(std::move(domNode)) { } void destructCount(CountBase& countBase) override { @@ -237,15 +243,17 @@ ByCoarseType::makeCount() CountBasePtr scriptsCount(scripts->makeCount()); CountBasePtr stringsCount(strings->makeCount()); CountBasePtr otherCount(other->makeCount()); + CountBasePtr domNodeCount(domNode->makeCount()); - if (!objectsCount || !scriptsCount || !stringsCount || !otherCount) + if (!objectsCount || !scriptsCount || !stringsCount || !otherCount || !domNodeCount) return CountBasePtr(nullptr); return CountBasePtr(js_new(*this, objectsCount, scriptsCount, stringsCount, - otherCount)); + otherCount, + domNodeCount)); } void @@ -256,6 +264,7 @@ ByCoarseType::traceCount(CountBase& countBase, JSTracer* trc) count.scripts->trace(trc); count.strings->trace(trc); count.other->trace(trc); + count.domNode->trace(trc); } bool @@ -272,6 +281,8 @@ ByCoarseType::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, co return count.strings->count(mallocSizeOf, node); case JS::ubi::CoarseType::Other: return count.other->count(mallocSizeOf, node); + case JS::ubi::CoarseType::DOMNode: + return count.domNode->count(mallocSizeOf, node); default: MOZ_CRASH("bad JS::ubi::CoarseType in JS::ubi::ByCoarseType::count"); return false; @@ -306,6 +317,10 @@ ByCoarseType::report(JSContext* cx, CountBase& countBase, MutableHandleValue rep if (!count.other->report(cx, &otherReport) || !DefineDataProperty(cx, obj, cx->names().other, otherReport)) return false; + RootedValue domReport(cx); + if (!count.domNode->report(cx, &domReport) || + !DefineDataProperty(cx, obj, cx->names().domNode, domReport)) + return false; report.setObject(*obj); return true; @@ -386,6 +401,51 @@ countMapToObject(JSContext* cx, Map& map, GetName getName) { return obj; } +template +static PlainObject* +countMap16ToObject(JSContext* cx, Map& map, GetName getName) { + // Build a vector of pointers to entries; sort by total; and then use + // that to build the result object. This makes the ordering of entries + // more interesting, and a little less non-deterministic. + + JS::ubi::Vector entries; + if (!entries.reserve(map.count())) { + ReportOutOfMemory(cx); + return nullptr; + } + + for (auto r = map.all(); !r.empty(); r.popFront()) + entries.infallibleAppend(&r.front()); + + if (entries.length()) { + qsort(entries.begin(), entries.length(), sizeof(*entries.begin()), + compareEntries); + } + + RootedPlainObject obj(cx, NewBuiltinClassInstance(cx)); + if (!obj) + return nullptr; + + for (auto& entry : entries) { + CountBasePtr& thenCount = entry->value(); + RootedValue thenReport(cx); + if (!thenCount->report(cx, &thenReport)) + return nullptr; + + const char16_t* name = getName(entry->key()); + MOZ_ASSERT(name); + JSAtom* atom = AtomizeChars(cx, name, js_strlen(name)); + if (!atom) + return nullptr; + + RootedId entryId(cx, AtomToId(atom)); + if (!DefineDataProperty(cx, obj, entryId, thenReport)) + return nullptr; + } + + return obj; +} + // A type that categorizes nodes that are JSObjects by their class name, // and places all other nodes in an 'other' category. @@ -492,6 +552,111 @@ ByObjectClass::report(JSContext* cx, CountBase& countBase, MutableHandleValue re return true; } +class ByDomObjectClass : public CountType { + // A table mapping descriptive names to their counts. + using UniqueC16String = JS::UniqueTwoByteChars; + + struct UniqueC16StringHasher + { + using Lookup = UniqueC16String; + + static js::HashNumber hash(const Lookup& lookup) { + return mozilla::HashString(lookup.get()); + } + + static bool match(const UniqueC16String& key, const Lookup& lookup) { + return CompareChars(key.get(), js_strlen(key.get()), lookup.get(), + js_strlen(lookup.get())) == 0; + } + }; + + using Table = HashMap; + using Entry = Table::Entry; + + struct Count : public CountBase { + Table table; + + explicit Count(CountType& type) : CountBase(type) { } + + bool init() { return table.init(); } + }; + + CountTypePtr classesType; + + public: + explicit ByDomObjectClass(CountTypePtr& classesType) + : CountType(), + classesType(std::move(classesType)) + { } + + void destructCount(CountBase& countBase) override { + Count& count = static_cast(countBase); + count.~Count(); + } + + CountBasePtr makeCount() override; + void traceCount(CountBase& countBase, JSTracer* trc) override; + bool count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) override; + bool report(JSContext* cx, CountBase& countBase, MutableHandleValue report) override; +}; + +CountBasePtr +ByDomObjectClass::makeCount() +{ + auto count = js::MakeUnique(*this); + if (!count || !count->init()) + return nullptr; + + return CountBasePtr(count.release()); +} + +void +ByDomObjectClass::traceCount(CountBase& countBase, JSTracer* trc) +{ + Count& count = static_cast(countBase); + for (Table::Range r = count.table.all(); !r.empty(); r.popFront()) + r.front().value()->trace(trc); +} + +bool +ByDomObjectClass::count(CountBase& countBase, mozilla::MallocSizeOf mallocSizeOf, const Node& node) +{ + Count& count = static_cast(countBase); + + const char16_t* nodeName = node.descriptiveTypeName(); + if (!nodeName) + return false; + + UniqueC16String name = DuplicateString(nodeName); + if (!name) + return false; + + Table::AddPtr p = count.table.lookupForAdd(name); + if (!p) { + CountBasePtr classesCount(classesType->makeCount()); + if (!classesCount || !count.table.add(p, std::move(name), std::move(classesCount))) + return false; + } + return p->value()->count(mallocSizeOf, node); +} + +bool +ByDomObjectClass::report(JSContext* cx, CountBase& countBase, MutableHandleValue report) +{ + Count& count = static_cast(countBase); + + RootedPlainObject obj(cx, countMap16ToObject(cx, count.table, [](const UniqueC16String& key) { + return key.get(); + })); + if (!obj) + return false; + + report.setObject(*obj); + return true; +} // A count type that categorizes nodes by their ubi::Node::typeName. class ByUbinodeType : public CountType { @@ -1058,11 +1223,15 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue) CountTypePtr otherType(ParseChildBreakdown(cx, breakdown, cx->names().other)); if (!otherType) return nullptr; + CountTypePtr domNodeType(ParseChildBreakdown(cx, breakdown, cx->names().domNode)); + if (!domNodeType) + return nullptr; return CountTypePtr(cx->new_(objectsType, scriptsType, stringsType, - otherType)); + otherType, + domNodeType)); } if (StringEqualsAscii(by, "internalType")) { @@ -1073,6 +1242,13 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue) return CountTypePtr(cx->new_(thenType)); } + if (StringEqualsAscii(by, "descriptiveType")) { + CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); + if (!thenType) + return nullptr; + return CountTypePtr(cx->new_(thenType)); + } + if (StringEqualsAscii(by, "allocationStack")) { CountTypePtr thenType(ParseChildBreakdown(cx, breakdown, cx->names().then)); if (!thenType) @@ -1114,11 +1290,15 @@ ParseBreakdown(JSContext* cx, HandleValue breakdownValue) // // { by: "coarseType", // objects: { by: "objectClass" }, -// other: { by: "internalType" } +// other: { by: "internalType" }, +// domNode: { by: "descriptiveType" } // } static CountTypePtr GetDefaultBreakdown(JSContext* cx) { + CountTypePtr byDomClass(cx->new_()); + if (!byDomClass) + return nullptr; CountTypePtr byClass(cx->new_()); if (!byClass) return nullptr; @@ -1146,11 +1326,15 @@ GetDefaultBreakdown(JSContext* cx) CountTypePtr other(cx->new_(byType)); if (!other) return nullptr; + CountTypePtr domNode(cx->new_(byDomClass)); + if (!domNode) + return nullptr; return CountTypePtr(cx->new_(objects, scripts, strings, - other)); + other, + domNode)); } JS_PUBLIC_API(bool) From 806241e4f4dad62367a6be5cead057ffc037b3c8 Mon Sep 17 00:00:00 2001 From: Kristen Wright Date: Tue, 17 Jul 2018 16:23:29 -0700 Subject: [PATCH 06/21] Bug 1476141 - updated test files to handle a new coarsetype r=jimb Some test files were affected by adding a new CoarseType, particularly in XPCShell tests. New rules were added to handle this CoarseType, and in a few cases the IDs of deserialized nodes had to be adjusted. The colors of CoarseTypes have also changed slightly and the resulting tests had to be updated. --HG-- extra : histedit_source : 99db02f1b6acafb1376f71af3b8fceb9adbbb8e0 --- .../memory/test/unit/test_tree-map-01.js | 4 +- ...ominatorTreeNode_LabelAndShallowSize_01.js | 7 ++- ...ominatorTreeNode_LabelAndShallowSize_02.js | 7 ++- ...ominatorTreeNode_LabelAndShallowSize_03.js | 7 ++- ...ominatorTreeNode_LabelAndShallowSize_04.js | 7 ++- ...t_DominatorTreeNode_partialTraversal_01.js | 4 +- ...test_HeapAnalyses_deleteHeapSnapshot_03.js | 1 + ...st_HeapAnalyses_getCensusIndividuals_01.js | 1 + .../test_HeapAnalyses_getDominatorTree_01.js | 1 + ...HeapAnalyses_getImmediatelyDominated_01.js | 1 + .../test_HeapAnalyses_takeCensusDiff_02.js | 4 ++ .../unit/test_HeapAnalyses_takeCensus_07.js | 4 ++ .../tests/unit/test_census-tree-node-02.js | 2 + .../tests/unit/test_census-tree-node-07.js | 57 ++++++++++--------- .../tests/unit/test_census-tree-node-10.js | 2 + .../tests/unit/test_census_diff_03.js | 13 +++++ .../tests/unit/test_census_diff_06.js | 13 ++++- .../tests/unit/test_census_filtering_01.js | 25 ++++---- .../tests/unit/test_census_filtering_03.js | 9 ++- .../tests/unit/test_census_filtering_04.js | 18 +++--- .../tests/unit/test_getReportLeaves_01.js | 5 ++ 21 files changed, 136 insertions(+), 56 deletions(-) diff --git a/devtools/client/memory/test/unit/test_tree-map-01.js b/devtools/client/memory/test/unit/test_tree-map-01.js index 621eee5dc42f..6239da70ea67 100644 --- a/devtools/client/memory/test/unit/test_tree-map-01.js +++ b/devtools/client/memory/test/unit/test_tree-map-01.js @@ -32,8 +32,8 @@ add_task(async function() { }; drawBox(ctx, node, borderWidth, dragZoom, padding); ok(true, JSON.stringify([ctx, fillRectValues, strokeRectValues])); - equal(ctx.fillStyle, "hsl(210,60%,70%)", "The fillStyle is set"); - equal(ctx.strokeStyle, "hsl(210,60%,35%)", "The strokeStyle is set"); + equal(ctx.fillStyle, "hsl(204,60%,70%)", "The fillStyle is set"); + equal(ctx.strokeStyle, "hsl(204,60%,35%)", "The strokeStyle is set"); equal(ctx.lineWidth, 1, "The lineWidth is set"); deepEqual(fillRectValues, [10.5, 20.5, 49, 69], "Draws a filled rectangle"); deepEqual(strokeRectValues, [10.5, 20.5, 49, 69], "Draws a stroked rectangle"); diff --git a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js index a3db538b2fc8..076718cf110e 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_01.js @@ -23,6 +23,10 @@ const breakdown = { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const description = { @@ -32,7 +36,8 @@ const description = { }, strings: {}, scripts: {}, - other: {} + other: {}, + domNode: {} }; const expected = [ diff --git a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js index 8f502a7c9a25..66741fd9321d 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_02.js @@ -23,6 +23,10 @@ const breakdown = { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const description = { @@ -31,7 +35,8 @@ const description = { }, strings: {}, scripts: {}, - other: {} + other: {}, + domNode: {} }; const expected = [ diff --git a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js index 33e597021ebf..a77590c3c7fd 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_03.js @@ -23,6 +23,10 @@ const breakdown = { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const description = { @@ -33,7 +37,8 @@ const description = { "JSString": { count: 1, bytes: 42 }, }, scripts: {}, - other: {} + other: {}, + domNode: {} }; const expected = [ diff --git a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js index 5f1b1b45b558..12ad2c0e93a5 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_LabelAndShallowSize_04.js @@ -27,6 +27,10 @@ const breakdown = { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const stack = saveStack(); @@ -38,7 +42,8 @@ const description = { }, strings: {}, scripts: {}, - other: {} + other: {}, + domNode: {} }; const expected = [ diff --git a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js index c0fe9af2ea72..ff5289902b5c 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_DominatorTreeNode_partialTraversal_01.js @@ -38,7 +38,8 @@ const mockSnapshot = { objects: { count: 0, bytes: 0 }, strings: { count: 0, bytes: 0 }, scripts: { count: 0, bytes: 0 }, - other: { SomeType: { count: 1, bytes: 10 } } + other: { SomeType: { count: 1, bytes: 10 } }, + domNode: { count: 0, bytes: 0 }, }) }; @@ -51,6 +52,7 @@ const breakdown = { by: "internalType", then: { by: "count", count: true, bytes: true } }, + domNode: { by: "count", count: true, bytes: true }, }; const expected = { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js index 45c6f79efd66..6f9e34547d28 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_deleteHeapSnapshot_03.js @@ -10,6 +10,7 @@ const breakdown = { scripts: { by: "count", count: true, bytes: true }, strings: { by: "count", count: true, bytes: true }, other: { by: "count", count: true, bytes: true }, + domNode: { by: "count", count: true, bytes: true }, }; async function createSnapshotAndDominatorTree(client) { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js index 695279fb56e6..0f01b3b279cd 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getCensusIndividuals_01.js @@ -12,6 +12,7 @@ const CENSUS_BREAKDOWN = { strings: COUNT, scripts: COUNT, other: COUNT, + domNode: COUNT, }; const LABEL_BREAKDOWN = { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js index 2406bbdc58d5..51ed1a5e82c9 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js @@ -10,6 +10,7 @@ const breakdown = { scripts: { by: "count", count: true, bytes: true }, strings: { by: "count", count: true, bytes: true }, other: { by: "count", count: true, bytes: true }, + domNode: { by: "count", count: true, bytes: true }, }; add_task(async function() { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js index 1a436d499d03..bb27ad7918fc 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js @@ -10,6 +10,7 @@ const breakdown = { scripts: { by: "count", count: true, bytes: true }, strings: { by: "count", count: true, bytes: true }, other: { by: "count", count: true, bytes: true }, + domNode: { by: "count", count: true, bytes: true }, }; add_task(async function() { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js index 8c72ef0a1489..c54c3740b97a 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensusDiff_02.js @@ -24,6 +24,10 @@ const BREAKDOWN = { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; add_task(async function() { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js index 43084535ae39..d77ba3492736 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_07.js @@ -24,6 +24,10 @@ const BREAKDOWN = { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; add_task(async function() { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js index 0caa5ea88fd9..f5591edd0b79 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js @@ -14,6 +14,7 @@ const BREAKDOWN = { strings: countBreakdown, scripts: countBreakdown, other: { by: "internalType", then: countBreakdown }, + domNode: countBreakdown, }; const REPORT = { @@ -27,6 +28,7 @@ const REPORT = { "js::Shape": { bytes: 30, count: 3 }, "js::Shape2": { bytes: 40, count: 4 } }, + "domNode": { bytes: 0, count: 0 } }; const EXPECTED = { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-07.js b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-07.js index fcb557da966f..467d5cac654a 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-07.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-07.js @@ -26,6 +26,10 @@ function run_test() { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + } }; const REPORT = { @@ -41,7 +45,8 @@ function run_test() { }, other: { "js::Shape": { bytes: 80, count: 8 }, - } + }, + domNode: { } }; const EXPECTED = { @@ -72,18 +77,18 @@ function run_test() { count: 0, totalCount: 22, children: undefined, - id: 14, - parent: 13, + id: 15, + parent: 14, reportLeafIndex: undefined, } ], - id: 13, - parent: 12, + id: 14, + parent: 13, reportLeafIndex: undefined, } ], - id: 12, - parent: 11, + id: 13, + parent: 12, reportLeafIndex: 9, }, { @@ -107,18 +112,18 @@ function run_test() { count: 0, totalCount: 22, children: undefined, - id: 17, - parent: 16, + id: 18, + parent: 17, reportLeafIndex: undefined, } ], - id: 16, - parent: 15, + id: 17, + parent: 16, reportLeafIndex: undefined, } ], - id: 15, - parent: 11, + id: 16, + parent: 12, reportLeafIndex: 7, }, { @@ -142,18 +147,18 @@ function run_test() { count: 0, totalCount: 22, children: undefined, - id: 20, - parent: 19, + id: 21, + parent: 20, reportLeafIndex: undefined, } ], - id: 19, - parent: 18, + id: 20, + parent: 19, reportLeafIndex: undefined, } ], - id: 18, - parent: 11, + id: 19, + parent: 12, reportLeafIndex: 2, }, { @@ -177,22 +182,22 @@ function run_test() { count: 0, totalCount: 22, children: undefined, - id: 23, - parent: 22, + id: 24, + parent: 23, reportLeafIndex: undefined, } ], - id: 22, - parent: 21, + id: 23, + parent: 22, reportLeafIndex: undefined, } ], - id: 21, - parent: 11, + id: 22, + parent: 12, reportLeafIndex: 5, }, ], - id: 11, + id: 12, parent: undefined, reportLeafIndex: undefined, }; diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-10.js b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-10.js index b7798f23f872..69a32589fee9 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-10.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-10.js @@ -20,6 +20,7 @@ function run_test() { }, strings: { by: "count", count: true, bytes: true }, scripts: { by: "count", count: true, bytes: true }, + domNode: { by: "count", count: true, bytes: true }, }; const REPORT = { @@ -31,6 +32,7 @@ function run_test() { }, strings: { count: 0, bytes: 0 }, scripts: { count: 0, bytes: 0 }, + domNode: {count: 0, bytes: 0 }, }; const node = censusReportToCensusTreeNode(BREAKDOWN, REPORT, { invert: true }); diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census_diff_03.js b/devtools/shared/heapsnapshot/tests/unit/test_census_diff_03.js index a564c0ba074c..e625f4e0befd 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census_diff_03.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census_diff_03.js @@ -10,6 +10,7 @@ const BREAKDOWN = { scripts: { by: "count", count: true, bytes: true }, strings: { by: "count", count: true, bytes: true }, other: { by: "count", count: true, bytes: true }, + domNode: { by: "count", count: true, bytes: true }, }; const REPORT1 = { @@ -29,6 +30,10 @@ const REPORT1 = { count: 3, bytes: 30, }, + domNode: { + count: 0, + bytes: 0, + }, }; const REPORT2 = { @@ -48,6 +53,10 @@ const REPORT2 = { count: 4, bytes: 40, }, + domNode: { + count: 0, + bytes: 0, + }, }; const EXPECTED = { @@ -67,6 +76,10 @@ const EXPECTED = { count: 1, bytes: 10, }, + domNode: { + count: 0, + bytes: 0, + }, }; function run_test() { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census_diff_06.js b/devtools/shared/heapsnapshot/tests/unit/test_census_diff_06.js index 1833ebb11566..93d72e840763 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census_diff_06.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census_diff_06.js @@ -31,6 +31,10 @@ const BREAKDOWN = { by: "internalType", then: { by: "count", count: false, bytes: true } }, + domNode: { + by: "internalType", + then: { by: "count", count: false, bytes: true } + }, }; const stack1 = saveStack(); @@ -59,7 +63,8 @@ const REPORT1 = { }, other: { "mozilla::dom::Thing": { bytes: 1 }, - } + }, + domNode: {} }; const REPORT2 = { @@ -85,7 +90,8 @@ const REPORT2 = { }, other: { "mozilla::dom::OtherThing": { bytes: 1 }, - } + }, + domNode: {} }; const EXPECTED = { @@ -130,7 +136,8 @@ const EXPECTED = { "mozilla::dom::OtherThing": { "bytes": 1 } - } + }, + "domNode": {}, }; function run_test() { diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_01.js b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_01.js index 29da7ccdfede..77dfb53ab9d7 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_01.js @@ -24,6 +24,10 @@ function run_test() { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const REPORT = { @@ -41,7 +45,8 @@ function run_test() { }, other: { "js::Shape": { bytes: 80, count: 8 }, - } + }, + domNode: {} }; const EXPECTED = { @@ -65,8 +70,8 @@ function run_test() { count: 32, totalCount: 32, children: undefined, - id: 15, - parent: 14, + id: 16, + parent: 15, reportLeafIndex: 4, }, { @@ -76,8 +81,8 @@ function run_test() { count: 8, totalCount: 8, children: undefined, - id: 16, - parent: 14, + id: 17, + parent: 15, reportLeafIndex: 3, }, { @@ -87,17 +92,17 @@ function run_test() { count: 5, totalCount: 5, children: undefined, - id: 17, - parent: 14, + id: 18, + parent: 15, reportLeafIndex: 2, } ], - id: 14, - parent: 13, + id: 15, + parent: 14, reportLeafIndex: undefined, } ], - id: 13, + id: 14, parent: undefined, reportLeafIndex: undefined, }; diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_03.js b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_03.js index 60e123e5ffe7..53399a938b83 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_03.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_03.js @@ -24,6 +24,10 @@ function run_test() { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const REPORT = { @@ -41,7 +45,8 @@ function run_test() { }, other: { "js::Shape": { bytes: 80, count: 8 }, - } + }, + domNode: {} }; const EXPECTED = { @@ -51,7 +56,7 @@ function run_test() { count: 0, totalCount: 62, children: undefined, - id: 13, + id: 14, parent: undefined, reportLeafIndex: undefined, }; diff --git a/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_04.js b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_04.js index 529c5378dde4..87d556b542f0 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_04.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_04.js @@ -19,6 +19,7 @@ function run_test() { noFilename: INTERNAL_TYPE }, other: INTERNAL_TYPE, + domNode: { by: "descriptiveType", then: COUNT, other: COUNT }, }; const REPORT = { @@ -49,7 +50,8 @@ function run_test() { count: 1, bytes: 10 } - } + }, + domNode: {} }; const EXPECTED = { @@ -73,8 +75,8 @@ function run_test() { totalBytes: 70, count: 7, totalCount: 7, - id: 13, - parent: 12, + id: 14, + parent: 13, children: undefined, reportLeafIndex: 2, }, @@ -84,18 +86,18 @@ function run_test() { totalBytes: 60, count: 6, totalCount: 6, - id: 14, - parent: 12, + id: 15, + parent: 13, children: undefined, reportLeafIndex: 3, }, ], - id: 12, - parent: 11, + id: 13, + parent: 12, reportLeafIndex: undefined, } ], - id: 11, + id: 12, reportLeafIndex: undefined, }; diff --git a/devtools/shared/heapsnapshot/tests/unit/test_getReportLeaves_01.js b/devtools/shared/heapsnapshot/tests/unit/test_getReportLeaves_01.js index 6818f6f20c6d..e66307a8e5cd 100644 --- a/devtools/shared/heapsnapshot/tests/unit/test_getReportLeaves_01.js +++ b/devtools/shared/heapsnapshot/tests/unit/test_getReportLeaves_01.js @@ -28,6 +28,10 @@ function run_test() { by: "internalType", then: { by: "count", count: true, bytes: true }, }, + domNode: { + by: "descriptiveType", + then: { by: "count", count: true, bytes: true }, + }, }; const REPORT = { @@ -53,6 +57,7 @@ function run_test() { "js::Shape": { count: 7, bytes: 70 }, "js::BaseShape": { count: 1, bytes: 10 }, }, + domNode: { }, }; const root = censusReportToCensusTreeNode(BREAKDOWN, REPORT); From 1ea5273b59451b16407ffcc28cd33220ba8d3473 Mon Sep 17 00:00:00 2001 From: Andreea Pavel Date: Fri, 27 Jul 2018 23:06:00 +0300 Subject: [PATCH 07/21] Backed out 3 changesets (bug 1378808) for failing spidermonkey builds on a CLOSED TREE Backed out changeset 8658a25ee96b (bug 1378808) Backed out changeset e761b8eef0aa (bug 1378808) Backed out changeset b14186c3f895 (bug 1378808) --- js/src/builtin/ReflectParse.cpp | 23 +- js/src/frontend/BinSource-auto.cpp | 32 +- js/src/frontend/BinSource.yaml | 30 +- js/src/frontend/BytecodeEmitter.cpp | 351 +++++++----------- js/src/frontend/BytecodeEmitter.h | 1 - js/src/frontend/FoldConstants.cpp | 46 +-- js/src/frontend/FullParseHandler.h | 33 +- js/src/frontend/NameFunctions.cpp | 45 +-- js/src/frontend/ParseNode.cpp | 29 +- js/src/frontend/ParseNode.h | 48 ++- js/src/frontend/Parser.cpp | 109 +++--- js/src/frontend/Parser.h | 10 +- js/src/frontend/SyntaxParseHandler.h | 18 +- js/src/jit-test/lib/assert-offset-columns.js | 7 +- .../tests/debug/Script-getAllColumnOffsets.js | 55 --- js/src/wasm/AsmJS.cpp | 25 +- 16 files changed, 316 insertions(+), 546 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index aca2c027c22c..f2057ab7f829 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2702,25 +2702,24 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case ParseNodeKind::Call: case ParseNodeKind::SuperCall: { - ParseNode* pn_callee = pn->pn_left; - ParseNode* pn_args = pn->pn_right; - MOZ_ASSERT(pn->pn_pos.encloses(pn_callee->pn_pos)); + ParseNode* next = pn->pn_head; + MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); RootedValue callee(cx); if (pn->isKind(ParseNodeKind::SuperCall)) { - MOZ_ASSERT(pn_callee->isKind(ParseNodeKind::SuperBase)); - if (!builder.super(&pn_callee->pn_pos, &callee)) + MOZ_ASSERT(next->isKind(ParseNodeKind::SuperBase)); + if (!builder.super(&next->pn_pos, &callee)) return false; } else { - if (!expression(pn_callee, &callee)) + if (!expression(next, &callee)) return false; } NodeVector args(cx); - if (!args.reserve(pn_args->pn_count)) + if (!args.reserve(pn->pn_count - 1)) return false; - for (ParseNode* next = pn_args->pn_head; next; next = next->pn_next) { + for (next = next->pn_next; next; next = next->pn_next) { MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos)); RootedValue arg(cx); @@ -2741,17 +2740,17 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case ParseNodeKind::Dot: { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos)); RootedValue expr(cx); RootedValue propname(cx); - RootedAtom pnAtom(cx, pn->pn_right->pn_atom); + RootedAtom pnAtom(cx, pn->pn_atom); if (pn->as().isSuper()) { - if (!builder.super(&pn->pn_left->pn_pos, &expr)) + if (!builder.super(&pn->pn_expr->pn_pos, &expr)) return false; } else { - if (!expression(pn->pn_left, &expr)) + if (!expression(pn->pn_expr, &expr)) return false; } diff --git a/js/src/frontend/BinSource-auto.cpp b/js/src/frontend/BinSource-auto.cpp index 573ba1ba7bb3..d37c30517b3d 100644 --- a/js/src/frontend/BinSource-auto.cpp +++ b/js/src/frontend/BinSource-auto.cpp @@ -3462,8 +3462,9 @@ BinASTParser::parseInterfaceCallExpression(const size_t start, const BinKin op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; } } - - BINJS_TRY_DECL(result, factory_.newCall(callee, arguments)); + auto result = arguments; + result->setKind(ParseNodeKind::Call); + result->prepend(callee); result->setOp(op); return result; } @@ -5676,7 +5677,10 @@ BinASTParser::parseInterfaceNewExpression(const size_t start, const BinKind BINJS_MOZ_TRY_DECL(arguments, parseArguments()); - BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments)); + auto result = arguments; + result->setKind(ParseNodeKind::New); + result->prepend(callee); + result->setOp(JSOP_NEW); return result; } @@ -6193,18 +6197,13 @@ BinASTParser::parseInterfaceStaticMemberAssignmentTarget(const size_t start const BinField expected_fields[2] = { BinField::Object, BinField::Property }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) - TokenPos namePos; BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); + RootedAtom property(cx_); - { - namePos = tokenizer_->pos(); - MOZ_TRY_VAR(property, tokenizer_->readAtom()); + MOZ_TRY_VAR(property, tokenizer_->readAtom()); - } - - BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); return result; } @@ -6243,18 +6242,13 @@ BinASTParser::parseInterfaceStaticMemberExpression(const size_t start, cons const BinField expected_fields[2] = { BinField::Object, BinField::Property }; MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields)); #endif // defined(DEBUG) - TokenPos namePos; BINJS_MOZ_TRY_DECL(object, parseExpressionOrSuper()); + RootedAtom property(cx_); - { - namePos = tokenizer_->pos(); - MOZ_TRY_VAR(property, tokenizer_->readAtom()); + MOZ_TRY_VAR(property, tokenizer_->readAtom()); - } - - BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); return result; } diff --git a/js/src/frontend/BinSource.yaml b/js/src/frontend/BinSource.yaml index bae52b19c9f6..0a41e3c9630d 100644 --- a/js/src/frontend/BinSource.yaml +++ b/js/src/frontend/BinSource.yaml @@ -428,8 +428,9 @@ CallExpression: op = parseContext_->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; } } - - BINJS_TRY_DECL(result, factory_.newCall(callee, arguments)); + auto result = arguments; + result->setKind(ParseNodeKind::Call); + result->prepend(callee); result->setOp(op); @@ -830,7 +831,10 @@ LiteralStringExpression: NewExpression: build: | - BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments)); + auto result = arguments; + result->setKind(ParseNodeKind::New); + result->prepend(callee); + result->setOp(JSOP_NEW); ObjectExpression: build: @@ -918,28 +922,12 @@ SwitchStatementWithDefault: BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope)); StaticMemberAssignmentTarget: - init: - TokenPos namePos; - fields: - property: - block: - before: | - namePos = tokenizer_->pos(); build: | - BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); StaticMemberExpression: - init: - TokenPos namePos; - fields: - property: - block: - before: | - namePos = tokenizer_->pos(); build: | - BINJS_TRY_DECL(name, factory_.newPropertyName(property->asPropertyName(), namePos)); - BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, name)); + BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start)); ThisExpression: build: | diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index e5a6d01e0d56..1726b78480ac 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1038,7 +1038,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) // Watch out for getters! case ParseNodeKind::Dot: - MOZ_ASSERT(pn->isArity(PN_BINARY)); + MOZ_ASSERT(pn->isArity(PN_NAME)); *answer = true; return true; @@ -1276,14 +1276,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case ParseNodeKind::Call: case ParseNodeKind::TaggedTemplate: case ParseNodeKind::SuperCall: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - *answer = true; - return true; - - // Function arg lists can contain arbitrary expressions. Technically - // this only causes side-effects if one of the arguments does, but since - // the call being made will always trigger side-effects, it isn't needed. - case ParseNodeKind::Arguments: MOZ_ASSERT(pn->isArity(PN_LIST)); *answer = true; return true; @@ -1418,7 +1410,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate case ParseNodeKind::PosHolder: // by ParseNodeKind::NewTarget case ParseNodeKind::SuperBase: // by ParseNodeKind::Elem and others - case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("handled by parent nodes"); case ParseNodeKind::Limit: // invalid sentinel value @@ -1890,11 +1881,11 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); MOZ_ASSERT(!pn->as().isSuper()); - ParseNode* pn2 = pn->pn_left; + ParseNode* pn2 = pn->pn_expr; /* * If the object operand is also a dotted property reference, reverse the - * list linked via pn_left temporarily so we can iterate over it from the + * list linked via pn_expr temporarily so we can iterate over it from the * bottom up (reversing again as we go), to avoid excessive recursion. */ if (pn2->isKind(ParseNodeKind::Dot) && !pn2->as().isSuper()) { @@ -1902,9 +1893,9 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) ParseNode* pnup = nullptr; ParseNode* pndown; for (;;) { - /* Reverse pndot->pn_left to point up, not down. */ - pndown = pndot->pn_left; - pndot->pn_left = pnup; + /* Reverse pndot->pn_expr to point up, not down. */ + pndown = pndot->pn_expr; + pndot->pn_expr = pnup; if (!pndown->isKind(ParseNodeKind::Dot) || pndown->as().isSuper()) break; pnup = pndot; @@ -1917,12 +1908,12 @@ BytecodeEmitter::emitPropLHS(ParseNode* pn) do { /* Walk back up the list, emitting annotated name ops. */ - if (!emitAtomOp(pndot->pn_right, JSOP_GETPROP)) + if (!emitAtomOp(pndot, JSOP_GETPROP)) return false; - /* Reverse the pn_left link again. */ - pnup = pndot->pn_left; - pndot->pn_left = pndown; + /* Reverse the pn_expr link again. */ + pnup = pndot->pn_expr; + pndot->pn_expr = pndown; pndown = pndot; } while ((pndot = pnup) != nullptr); return true; @@ -1947,7 +1938,7 @@ BytecodeEmitter::emitSuperPropLHS(ParseNode* superBase, bool isCall) bool BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) { - MOZ_ASSERT(pn->isArity(PN_BINARY)); + MOZ_ASSERT(pn->isArity(PN_NAME)); if (!emitPropLHS(pn)) return false; @@ -1955,7 +1946,7 @@ BytecodeEmitter::emitPropOp(ParseNode* pn, JSOp op) if (op == JSOP_CALLPROP && !emit1(JSOP_DUP)) return false; - if (!emitAtomOp(pn->pn_right, op)) + if (!emitAtomOp(pn, op)) return false; if (op == JSOP_CALLPROP && !emit1(JSOP_SWAP)) @@ -1971,7 +1962,7 @@ BytecodeEmitter::emitSuperPropOp(ParseNode* pn, JSOp op, bool isCall) if (!emitSuperPropLHS(base, isCall)) return false; - if (!emitAtomOp(pn->pn_right, op)) + if (!emitAtomOp(pn, op)) return false; if (isCall && !emit1(JSOP_SWAP)) @@ -2001,7 +1992,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn) if (!emit1(JSOP_DUP)) // OBJ OBJ return false; } - if (!emitAtomOp(pn->pn_kid->pn_right, isSuper ? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V + if (!emitAtomOp(pn->pn_kid, isSuper? JSOP_GETPROP_SUPER : JSOP_GETPROP)) // OBJ V return false; if (!emit1(JSOP_POS)) // OBJ N return false; @@ -2027,7 +2018,7 @@ BytecodeEmitter::emitPropIncDec(ParseNode* pn) JSOp setOp = isSuper ? sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER : sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - if (!emitAtomOp(pn->pn_kid->pn_right, setOp)) // N? N+1 + if (!emitAtomOp(pn->pn_kid, setOp)) // N? N+1 return false; if (post && !emit1(JSOP_POP)) // RESULT return false; @@ -2752,7 +2743,7 @@ BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted) return false; *emitted = 2; } else { - if (!emitTree(target->pn_left)) + if (!emitTree(target->pn_expr)) return false; *emitted = 1; } @@ -2870,7 +2861,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; else setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - if (!emitAtomOp(target->pn_right, setOp)) + if (!emitAtomOp(target, setOp)) return false; break; } @@ -3982,11 +3973,11 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh return false; offset += 2; } else { - if (!emitTree(lhs->pn_left)) + if (!emitTree(lhs->expr())) return false; offset += 1; } - if (!makeAtomIndex(lhs->pn_right->pn_atom, &atomIndex)) + if (!makeAtomIndex(lhs->pn_atom, &atomIndex)) return false; break; case ParseNodeKind::Elem: { @@ -4035,7 +4026,7 @@ BytecodeEmitter::emitAssignment(ParseNode* lhs, ParseNodeKind pnk, ParseNode* rh } else { if (!emit1(JSOP_DUP)) return false; - bool isLength = (lhs->pn_right->pn_atom == cx->names().length); + bool isLength = (lhs->pn_atom == cx->names().length); getOp = isLength ? JSOP_LENGTH : JSOP_GETPROP; } if (!emitIndex32(getOp, atomIndex)) @@ -4882,7 +4873,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte bool allowSelfHostedIter = false; if (emitterMode == BytecodeEmitter::SelfHosting && forHeadExpr->isKind(ParseNodeKind::Call) && - forHeadExpr->pn_left->name() == cx->names().allowContentIter) + forHeadExpr->pn_head->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } @@ -6577,12 +6568,10 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) // // argc is set to the amount of actually emitted args and the // emitting of args below is disabled by setting emitArgs to false. - ParseNode* pn_callee = pn->pn_left; - ParseNode* pn_args = pn->pn_right; + ParseNode* pn2 = pn->pn_head; + const char* errorName = SelfHostedCallFunctionName(pn2->name(), cx); - const char* errorName = SelfHostedCallFunctionName(pn_callee->name(), cx); - - if (pn_args->pn_count < 2) { + if (pn->pn_count < 3) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, errorName, "2", "s"); return false; } @@ -6593,8 +6582,8 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - bool constructing = pn_callee->name() == cx->names().constructContentFunction; - ParseNode* funNode = pn_args->pn_head; + bool constructing = pn2->name() == cx->names().constructContentFunction; + ParseNode* funNode = pn2->pn_next; if (constructing) { callOp = JSOP_NEW; } else if (funNode->getKind() == ParseNodeKind::Name && @@ -6607,7 +6596,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) #ifdef DEBUG if (emitterMode == BytecodeEmitter::SelfHosting && - pn_callee->name() == cx->names().callFunction) + pn2->name() == cx->names().callFunction) { if (!emit1(JSOP_DEBUGCHECKSELFHOSTED)) return false; @@ -6636,7 +6625,7 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) return false; } - uint32_t argc = pn_args->pn_count - 2; + uint32_t argc = pn->pn_count - 3; if (!emitCall(callOp, argc)) return false; @@ -6647,15 +6636,15 @@ BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedResumeGenerator(ParseNode* pn) { - ParseNode* pn_args = pn->pn_right; - // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'return') - if (pn_args->pn_count != 3) { + if (pn->pn_count != 4) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s"); return false; } - ParseNode* genNode = pn_args->pn_head; + ParseNode* funNode = pn->pn_head; // The resumeGenerator node. + + ParseNode* genNode = funNode->pn_next; if (!emitTree(genNode)) return false; @@ -6687,26 +6676,24 @@ BytecodeEmitter::emitSelfHostedForceInterpreter() bool BytecodeEmitter::emitSelfHostedAllowContentIter(ParseNode* pn) { - ParseNode* pn_args = pn->pn_right; - - if (pn_args->pn_count != 1) { + if (pn->pn_count != 2) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentIter", "1", ""); return false; } // We're just here as a sentinel. Pass the value through directly. - return emitTree(pn_args->pn_head); + return emitTree(pn->pn_head->pn_next); } bool BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn) { - ParseNode* pn_args = pn->pn_right; + // Only optimize when 3 arguments are passed (we use 4 to include |this|). + MOZ_ASSERT(pn->pn_count == 4); - // Only optimize when 3 arguments are passed. - MOZ_ASSERT(pn_args->pn_count == 3); + ParseNode* funNode = pn->pn_head; // The _DefineDataProperty node. - ParseNode* objNode = pn_args->pn_head; + ParseNode* objNode = funNode->pn_next; if (!emitTree(objNode)) return false; @@ -6727,14 +6714,14 @@ BytecodeEmitter::emitSelfHostedDefineDataProperty(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn) { - ParseNode* pn_args = pn->pn_right; - - if (pn_args->pn_count != 2) { + if (pn->pn_count != 3) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", ""); return false; } - ParseNode* idNode = pn_args->pn_head; + ParseNode* funNode = pn->pn_head; // The hasOwn node. + + ParseNode* idNode = funNode->pn_next; if (!emitTree(idNode)) return false; @@ -6748,14 +6735,14 @@ BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn) bool BytecodeEmitter::emitSelfHostedGetPropertySuper(ParseNode* pn) { - ParseNode* pn_args = pn->pn_right; - - if (pn_args->pn_count != 3) { + if (pn->pn_count != 4) { reportError(pn, JSMSG_MORE_ARGS_NEEDED, "getPropertySuper", "3", ""); return false; } - ParseNode* objNode = pn_args->pn_head; + ParseNode* funNode = pn->pn_head; // The getPropertySuper node. + + ParseNode* objNode = funNode->pn_next; ParseNode* idNode = objNode->pn_next; ParseNode* receiverNode = idNode->pn_next; @@ -6784,11 +6771,11 @@ BytecodeEmitter::isRestParameter(ParseNode* pn) if (!pn->isKind(ParseNodeKind::Name)) { if (emitterMode == BytecodeEmitter::SelfHosting && pn->isKind(ParseNodeKind::Call)) { - ParseNode* pn_callee = pn->pn_left; - if (pn_callee->getKind() == ParseNodeKind::Name && - pn_callee->name() == cx->names().allowContentIter) + ParseNode* pn2 = pn->pn_head; + if (pn2->getKind() == ParseNodeKind::Name && + pn2->name() == cx->names().allowContentIter) { - return isRestParameter(pn->pn_right->pn_head); + return isRestParameter(pn2->pn_next); } } return false; @@ -6918,22 +6905,101 @@ BytecodeEmitter::emitPipeline(ParseNode* pn) } bool -BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread) +BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) { - uint32_t argc = pn->pn_count; + bool callop = + pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate); + /* + * Emit callable invocation or operator new (constructor call) code. + * First, emit code for the left operand to evaluate the callable or + * constructable object expression. + * + * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc. + * This is necessary to interpose the lambda-initialized method read + * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by + * JSOP_{SET,INIT}PROP. + * + * Then (or in a call case that has no explicit reference-base + * object) we emit JSOP_UNDEFINED to produce the undefined |this| + * value required for calls (which non-strict mode functions + * will box into the global object). + */ + uint32_t argc = pn->pn_count - 1; if (argc >= ARGC_LIMIT) { reportError(pn, callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS); return false; } + ParseNode* pn2 = pn->pn_head; + bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE; + + if (pn2->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) { + // Calls to "forceInterpreter", "callFunction", + // "callContentFunction", or "resumeGenerator" in self-hosted + // code generate inline bytecode. + if (pn2->name() == cx->names().callFunction || + pn2->name() == cx->names().callContentFunction || + pn2->name() == cx->names().constructContentFunction) + { + return emitSelfHostedCallFunction(pn); + } + if (pn2->name() == cx->names().resumeGenerator) + return emitSelfHostedResumeGenerator(pn); + if (pn2->name() == cx->names().forceInterpreter) + return emitSelfHostedForceInterpreter(); + if (pn2->name() == cx->names().allowContentIter) + return emitSelfHostedAllowContentIter(pn); + if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4) + return emitSelfHostedDefineDataProperty(pn); + if (pn2->name() == cx->names().hasOwn) + return emitSelfHostedHasOwn(pn); + if (pn2->name() == cx->names().getPropertySuper) + return emitSelfHostedGetPropertySuper(pn); + // Fall through + } + + if (!emitCallee(pn2, pn, &callop)) + return false; + + bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW || + pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL; + + + // Emit room for |this|. + if (!callop) { + if (isNewOp) { + if (!emit1(JSOP_IS_CONSTRUCTING)) + return false; + } else { + if (!emit1(JSOP_UNDEFINED)) + return false; + } + } + + /* + * Emit code for each argument in order, then emit the JSOP_*CALL or + * JSOP_NEW bytecode with a two-byte immediate telling how many args + * were pushed on the operand stack. + */ if (!spread) { - for (ParseNode* pn3 = pn->pn_head; pn3; pn3 = pn3->pn_next) { + for (ParseNode* pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) { if (!emitTree(pn3)) return false; } + + if (isNewOp) { + if (pn->isKind(ParseNodeKind::SuperCall)) { + if (!emit1(JSOP_NEWTARGET)) + return false; + } else { + // Repush the callee as new.target + if (!emitDupAt(argc + 1)) + return false; + } + } } else { - ParseNode* args = pn->pn_head; + ParseNode* args = pn2->pn_next; bool emitOptCode = (argc == 1) && isRestParameter(args->pn_kid); InternalIfEmitter ifNotOptimizable(this); @@ -6973,159 +7039,29 @@ BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread) if (!ifNotOptimizable.emitEnd()) return false; } - } - return true; -} - -bool -BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) -{ - bool callop = - pn->isKind(ParseNodeKind::Call) || pn->isKind(ParseNodeKind::TaggedTemplate); - - /* - * Emit callable invocation or operator new (constructor call) code. - * First, emit code for the left operand to evaluate the callable or - * constructable object expression. - * - * For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc. - * This is necessary to interpose the lambda-initialized method read - * barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by - * JSOP_{SET,INIT}PROP. - * - * Then (or in a call case that has no explicit reference-base - * object) we emit JSOP_UNDEFINED to produce the undefined |this| - * value required for calls (which non-strict mode functions - * will box into the global object). - */ - ParseNode* pn_callee = pn->pn_left; - ParseNode* pn_args = pn->pn_right; - - bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE; - - if (pn_callee->isKind(ParseNodeKind::Name) && emitterMode == BytecodeEmitter::SelfHosting && !spread) { - // Calls to "forceInterpreter", "callFunction", - // "callContentFunction", or "resumeGenerator" in self-hosted - // code generate inline bytecode. - if (pn_callee->name() == cx->names().callFunction || - pn_callee->name() == cx->names().callContentFunction || - pn_callee->name() == cx->names().constructContentFunction) - { - return emitSelfHostedCallFunction(pn); - } - if (pn_callee->name() == cx->names().resumeGenerator) - return emitSelfHostedResumeGenerator(pn); - if (pn_callee->name() == cx->names().forceInterpreter) - return emitSelfHostedForceInterpreter(); - if (pn_callee->name() == cx->names().allowContentIter) - return emitSelfHostedAllowContentIter(pn); - if (pn_callee->name() == cx->names().defineDataPropertyIntrinsic && pn_args->pn_count == 3) - return emitSelfHostedDefineDataProperty(pn); - if (pn_callee->name() == cx->names().hasOwn) - return emitSelfHostedHasOwn(pn); - if (pn_callee->name() == cx->names().getPropertySuper) - return emitSelfHostedGetPropertySuper(pn); - // Fall through - } - - if (!emitCallee(pn_callee, pn, &callop)) - return false; - - bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW || - pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL; - - // Emit room for |this|. - if (!callop) { if (isNewOp) { - if (!emit1(JSOP_IS_CONSTRUCTING)) - return false; - } else { - if (!emit1(JSOP_UNDEFINED)) - return false; - } - } - - if (!emitArguments(pn_args, callop, spread)) - return false; - - uint32_t argc = pn_args->pn_count; - - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or - * JSOP_NEW bytecode with a two-byte immediate telling how many args - * were pushed on the operand stack. - */ - if (isNewOp) { - if (pn->isKind(ParseNodeKind::SuperCall)) { - if (!emit1(JSOP_NEWTARGET)) - return false; - } else if (!spread) { - // Repush the callee as new.target - if (!emitDupAt(argc + 1)) - return false; - } else { - if (!emitDupAt(2)) - return false; - } - } - - ParseNode* coordNode = pn; - if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL)) { - switch (pn_callee->getKind()) { - case ParseNodeKind::Dot: { - - // Check if this member is a simple chain of simple chain of - // property accesses, e.g. x.y.z, this.x.y, super.x.y - bool simpleDotChain = false; - for (ParseNode* cur = pn_callee; cur->isKind(ParseNodeKind::Dot); cur = cur->pn_left) { - ParseNode* left = cur->pn_left; - if (left->isKind(ParseNodeKind::Name) || left->isKind(ParseNodeKind::This) || - left->isKind(ParseNodeKind::SuperBase)) - { - simpleDotChain = true; - } + if (pn->isKind(ParseNodeKind::SuperCall)) { + if (!emit1(JSOP_NEWTARGET)) + return false; + } else { + if (!emitDupAt(2)) + return false; } - - if (!simpleDotChain) { - // obj().aprop() // expression - // ^ // column coord - // - // Note: Because of the constant folding logic in FoldElement, - // this case also applies for constant string properties. - // - // obj()['aprop']() // expression - // ^ // column coord - coordNode = pn_callee->pn_right; - } - break; - } - case ParseNodeKind::Elem: - // obj[expr]() // expression - // ^ // column coord - coordNode = pn_args; - break; - default: - break; } } if (!spread) { if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) { - if (!emitCall(JSOP_CALL_IGNORES_RV, argc, coordNode)) + if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn)) return false; checkTypeSet(JSOP_CALL_IGNORES_RV); } else { - if (!emitCall(pn->getOp(), argc, coordNode)) + if (!emitCall(pn->getOp(), argc, pn)) return false; checkTypeSet(pn->getOp()); } } else { - if (coordNode) { - if (!updateSourceCoordNotes(coordNode->pn_pos.begin)) - return false; - } - if (!emit1(pn->getOp())) return false; checkTypeSet(pn->getOp()); @@ -7694,7 +7630,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) if (emitterMode == BytecodeEmitter::SelfHosting && expr->isKind(ParseNodeKind::Call) && - expr->pn_left->name() == cx->names().allowContentIter) + expr->pn_head->name() == cx->names().allowContentIter) { allowSelfHostedIter = true; } @@ -8742,9 +8678,8 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: return false; break; - case ParseNodeKind::PropertyName: case ParseNodeKind::PosHolder: - MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder or ::Property"); + MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder"); default: MOZ_ASSERT(0); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 7112bc067c89..2db42fb50247 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -806,7 +806,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter bool isRestParameter(ParseNode* pn); - MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread); MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index dbc45264a772..079194d5d607 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -347,10 +347,8 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result) case ParseNodeKind::Comma: case ParseNodeKind::Array: case ParseNodeKind::Object: - case ParseNodeKind::PropertyName: case ParseNodeKind::Dot: case ParseNodeKind::Elem: - case ParseNodeKind::Arguments: case ParseNodeKind::Call: case ParseNodeKind::Name: case ParseNodeKind::TemplateString: @@ -1255,8 +1253,7 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParserpn_pos); - ParseNode* dottedAccess = parser.newPropertyAccess(expr, nameNode); + ParseNode* dottedAccess = parser.newPropertyAccess(expr, name, node->pn_pos.end); if (!dottedAccess) return false; dottedAccess->setInParens(node->isInParens()); @@ -1412,9 +1409,8 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser& par { MOZ_ASSERT(node->isKind(ParseNodeKind::Call) || node->isKind(ParseNodeKind::SuperCall) || - node->isKind(ParseNodeKind::New) || node->isKind(ParseNodeKind::TaggedTemplate)); - MOZ_ASSERT(node->isArity(PN_BINARY)); + MOZ_ASSERT(node->isArity(PN_LIST)); // Don't fold a parenthesized callable component in an invocation, as this // might cause a different |this| value to be used, changing semantics: @@ -1427,26 +1423,10 @@ FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser& par // assertEq(obj.f``, "obj"); // // See bug 537673 and bug 1182373. - ParseNode** pn_callee = &node->pn_left; - if (node->isKind(ParseNodeKind::New) || !(*pn_callee)->isInParens()) { - if (!Fold(cx, pn_callee, parser)) - return false; - } - - ParseNode** pn_args = &node->pn_right; - if (!Fold(cx, pn_args, parser)) - return false; - - return true; -} - -static bool -FoldArguments(JSContext* cx, ParseNode* node, PerHandlerParser& parser) -{ - MOZ_ASSERT(node->isKind(ParseNodeKind::Arguments)); - MOZ_ASSERT(node->isArity(PN_LIST)); - ParseNode** listp = &node->pn_head; + if ((*listp)->isInParens()) + listp = &(*listp)->pn_next; + for (; *listp; listp = &(*listp)->pn_next) { if (!Fold(cx, listp, parser)) return false; @@ -1502,14 +1482,14 @@ static bool FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser& parser) { MOZ_ASSERT(node->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(node->isArity(PN_BINARY)); + MOZ_ASSERT(node->isArity(PN_NAME)); // Iterate through a long chain of dotted property accesses to find the // most-nested non-dotted property node, then fold that. - ParseNode** nested = &node->pn_left; + ParseNode** nested = &node->pn_expr; while ((*nested)->isKind(ParseNodeKind::Dot)) { - MOZ_ASSERT((*nested)->isArity(PN_BINARY)); - nested = &(*nested)->pn_left; + MOZ_ASSERT((*nested)->isArity(PN_NAME)); + nested = &(*nested)->pn_expr; } return Fold(cx, nested, parser); @@ -1662,6 +1642,7 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) case ParseNodeKind::InstanceOf: case ParseNodeKind::In: case ParseNodeKind::Comma: + case ParseNodeKind::New: case ParseNodeKind::Array: case ParseNodeKind::Object: case ParseNodeKind::StatementList: @@ -1713,14 +1694,10 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) return FoldAdd(cx, pnp, parser); case ParseNodeKind::Call: - case ParseNodeKind::New: case ParseNodeKind::SuperCall: case ParseNodeKind::TaggedTemplate: return FoldCall(cx, pn, parser); - case ParseNodeKind::Arguments: - return FoldArguments(cx, pn, parser); - case ParseNodeKind::Switch: case ParseNodeKind::Colon: case ParseNodeKind::Assign: @@ -1800,9 +1777,6 @@ Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser& parser) MOZ_ASSERT(pn->isArity(PN_NAME)); return Fold(cx, &pn->pn_expr, parser); - case ParseNodeKind::PropertyName: - MOZ_CRASH("unreachable, handled by ::Dot"); - case ParseNodeKind::Dot: return FoldDottedProperty(cx, pn, parser); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 49cbb64a0cbb..ea09cd684112 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -275,20 +275,16 @@ class FullParseHandler addList(/* list = */ literal, /* child = */ element); } - ParseNode* newCall(ParseNode* callee, ParseNode* args) { - return new_(ParseNodeKind::Call, JSOP_CALL, callee, args); + ParseNode* newCall(const TokenPos& pos) { + return new_(ParseNodeKind::Call, JSOP_CALL, pos); } - ParseNode* newArguments(const TokenPos& pos) { - return new_(ParseNodeKind::Arguments, JSOP_NOP, pos); + ParseNode* newSuperCall(ParseNode* callee) { + return new_(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee); } - ParseNode* newSuperCall(ParseNode* callee, ParseNode* args) { - return new_(ParseNodeKind::SuperCall, JSOP_SUPERCALL, callee, args); - } - - ParseNode* newTaggedTemplate(ParseNode* tag, ParseNode* args) { - return new_(ParseNodeKind::TaggedTemplate, JSOP_CALL, tag, args); + ParseNode* newTaggedTemplate(const TokenPos& pos) { + return new_(ParseNodeKind::TaggedTemplate, JSOP_CALL, pos); } ParseNode* newObjectLiteral(uint32_t begin) { @@ -663,12 +659,8 @@ class FullParseHandler return new_(pos); } - ParseNode* newPropertyName(PropertyName* name, const TokenPos& pos) { - return new_(ParseNodeKind::PropertyName, JSOP_NOP, name, pos); - } - - ParseNode* newPropertyAccess(ParseNode* expr, ParseNode* key) { - return new_(expr, key, expr->pn_pos.begin, key->pn_pos.end); + ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) { + return new_(expr, key, expr->pn_pos.begin, end); } ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) { @@ -742,8 +734,13 @@ class FullParseHandler return new_(bindings, body); } - Node newNewExpression(uint32_t begin, ParseNode* ctor, ParseNode* args) { - return new_(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, args->pn_pos.end), ctor, args); + Node newNewExpression(uint32_t begin, ParseNode* ctor) { + ParseNode* newExpr = new_(ParseNodeKind::New, JSOP_NEW, TokenPos(begin, begin + 1)); + if (!newExpr) + return nullptr; + + addList(/* list = */ newExpr, /* child = */ ctor); + return newExpr; } ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) { diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 426b8a6bff62..0528ebcadac2 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -75,11 +75,11 @@ class NameResolver bool nameExpression(ParseNode* n, bool* foundName) { switch (n->getKind()) { case ParseNodeKind::Dot: - if (!nameExpression(n->pn_left, foundName)) + if (!nameExpression(n->expr(), foundName)) return false; if (!*foundName) return true; - return appendPropertyReference(n->pn_right->pn_atom); + return appendPropertyReference(n->pn_atom); case ParseNodeKind::Name: *foundName = true; @@ -315,17 +315,17 @@ class NameResolver bool resolveTaggedTemplate(ParseNode* node, HandleAtom prefix) { MOZ_ASSERT(node->isKind(ParseNodeKind::TaggedTemplate)); - ParseNode* tag = node->pn_left; + ParseNode* element = node->pn_head; - // The leading expression, e.g. |tag| in |tag`foo`|, + // The list head is a leading expression, e.g. |tag| in |tag`foo`|, // that might contain functions. - if (!resolve(tag, prefix)) + if (!resolve(element, prefix)) return false; - // The callsite object node is first. This node only contains + // Next is the callsite object node. This node only contains // internal strings or undefined and an array -- no user-controlled // expressions. - ParseNode* element = node->pn_right->pn_head; + element = element->pn_next; #ifdef DEBUG { MOZ_ASSERT(element->isKind(ParseNodeKind::CallSiteObj)); @@ -697,6 +697,9 @@ class NameResolver case ParseNodeKind::Pow: case ParseNodeKind::Pipeline: case ParseNodeKind::Comma: + case ParseNodeKind::New: + case ParseNodeKind::Call: + case ParseNodeKind::SuperCall: case ParseNodeKind::Array: case ParseNodeKind::StatementList: case ParseNodeKind::ParamsBody: @@ -730,32 +733,11 @@ class NameResolver break; case ParseNodeKind::TaggedTemplate: - MOZ_ASSERT(cur->isArity(PN_BINARY)); + MOZ_ASSERT(cur->isArity(PN_LIST)); if (!resolveTaggedTemplate(cur, prefix)) return false; break; - case ParseNodeKind::New: - case ParseNodeKind::Call: - case ParseNodeKind::SuperCall: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - if (!resolve(cur->pn_left, prefix)) - return false; - if (!resolve(cur->pn_right, prefix)) - return false; - break; - - // Handles the arguments for new/call/supercall, but does _not_ handle - // the Arguments node used by tagged template literals, since that is - // special-cased inside of resolveTaggedTemplate. - case ParseNodeKind::Arguments: - MOZ_ASSERT(cur->isArity(PN_LIST)); - for (ParseNode* element = cur->pn_head; element; element = element->pn_next) { - if (!resolve(element, prefix)) - return false; - } - break; - // Import/export spec lists contain import/export specs containing // only pairs of names. Alternatively, an export spec lists may // contain a single export batch specifier. @@ -784,12 +766,12 @@ class NameResolver } case ParseNodeKind::Dot: - MOZ_ASSERT(cur->isArity(PN_BINARY)); + MOZ_ASSERT(cur->isArity(PN_NAME)); // Super prop nodes do not have a meaningful LHS if (cur->as().isSuper()) break; - if (!resolve(cur->pn_left, prefix)) + if (!resolve(cur->expr(), prefix)) return false; break; @@ -828,7 +810,6 @@ class NameResolver case ParseNodeKind::ExportSpec: // by ParseNodeKind::ExportSpecList case ParseNodeKind::CallSiteObj: // by ParseNodeKind::TaggedTemplate case ParseNodeKind::ClassNames: // by ParseNodeKind::Class - case ParseNodeKind::PropertyName: // by ParseNodeKind::Dot MOZ_CRASH("should have been handled by a parent node"); case ParseNodeKind::Limit: // invalid sentinel value diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 3325d2f2579e..b54653bbb7b5 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -216,21 +216,6 @@ UnaryNode::dump(GenericPrinter& out, int indent) void BinaryNode::dump(GenericPrinter& out, int indent) { - if (isKind(ParseNodeKind::Dot)) { - out.put("(."); - - DumpParseTree(pn_right, out, indent + 2); - - out.putChar(' '); - if (as().isSuper()) - out.put("super"); - else - DumpParseTree(pn_left, out, indent + 2); - - out.printf(")"); - return; - } - const char* name = parseNodeNames[size_t(getKind())]; out.printf("(%s ", name); indent += strlen(name) + 2; @@ -303,7 +288,10 @@ DumpName(GenericPrinter& out, const CharT* s, size_t len) void NameNode::dump(GenericPrinter& out, int indent) { - if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::PropertyName)) { + if (isKind(ParseNodeKind::Name) || isKind(ParseNodeKind::Dot)) { + if (isKind(ParseNodeKind::Dot)) + out.put("(."); + if (!pn_atom) { out.put("#"); } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) { @@ -318,6 +306,15 @@ NameNode::dump(GenericPrinter& out, int indent) else DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length()); } + + if (isKind(ParseNodeKind::Dot)) { + out.putChar(' '); + if (as().isSuper()) + out.put("super"); + else + DumpParseTree(expr(), out, indent + 2); + out.printf(")"); + } return; } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 1dfa0f9f0120..9010f5bb5083 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -55,7 +55,6 @@ class ObjectBox; F(PostIncrement) \ F(PreDecrement) \ F(PostDecrement) \ - F(PropertyName) \ F(Dot) \ F(Elem) \ F(Array) \ @@ -64,7 +63,6 @@ class ObjectBox; F(Label) \ F(Object) \ F(Call) \ - F(Arguments) \ F(Name) \ F(ObjectPropertyName) \ F(ComputedName) \ @@ -372,8 +370,9 @@ IsTypeofKind(ParseNodeKind kind) * PostIncrement, * PreDecrement, * PostDecrement - * New binary pn_left: ctor expression on the left of the ( - * pn_right: Arguments + * New list pn_head: list of ctor, arg1, arg2, ... argN + * pn_count: 1 + N (where N is number of args) + * ctor is a MEMBER expr * DeleteName unary pn_kid: Name expr * DeleteProp unary pn_kid: Dot expr * DeleteElem unary pn_kid: Elem expr @@ -382,15 +381,13 @@ IsTypeofKind(ParseNodeKind kind) * for a more-specific PNK_DELETE* unless constant * folding (or a similar parse tree manipulation) has * occurred - * PropertyName name pn_atom: property name being accessed - * Dot binary pn_left: MEMBER expr to left of . - * pn_right: PropertyName to right of . + * Dot name pn_expr: MEMBER expr to left of . + * pn_atom: name to right of . * Elem binary pn_left: MEMBER expr to left of [ * pn_right: expr between [ and ] - * Call binary pn_left: callee expression on the left of the ( - * pn_right: Arguments - * Arguments list pn_head: list of arg1, arg2, ... argN - * pn_count: N (where N is number of args) + * Call list pn_head: list of call, arg1, arg2, ... argN + * pn_count: 1 + N (where N is number of args) + * call is a MEMBER expr naming a callable object * Array list pn_head: list of pn_count array element exprs * [,,] holes are represented by Elision nodes * pn_xflags: PN_ENDCOMMA if extra comma at end @@ -410,9 +407,8 @@ IsTypeofKind(ParseNodeKind kind) * list * TemplateString pn_atom: template string atom nullary pn_op: JSOP_NOP - * TaggedTemplate pn_left: tag expression - * binary pn_right: Arguments, with the first being the - * call site object, then arg1, arg2, ... argN + * TaggedTemplate pn_head: list of call, call site object, arg1, arg2, ... argN + * list pn_count: 2 + N (N is the number of substitutions) * CallSiteObj list pn_head: a Array node followed by * list of pn_count - 1 TemplateString nodes * RegExp nullary pn_objbox: RegExp model object @@ -424,7 +420,7 @@ IsTypeofKind(ParseNodeKind kind) * * This, unary pn_kid: '.this' Name if function `this`, else nullptr * SuperBase unary pn_kid: '.this' Name - * SuperCall binary pn_left: SuperBase pn_right: Arguments + * * SetThis binary pn_left: '.this' Name, pn_right: SuperCall * * LexicalScope scope pn_u.scope.bindings: scope bindings @@ -573,7 +569,8 @@ class ParseNode FunctionBox* funbox; /* function object */ }; ParseNode* expr; /* module or function body, var - initializer, or argument default */ + initializer, argument default, or + base object of ParseNodeKind::Dot */ } name; struct { LexicalScope::Data* bindings; @@ -1177,33 +1174,30 @@ class RegExpLiteral : public NullaryNode } }; -class PropertyAccess : public BinaryNode +class PropertyAccess : public ParseNode { public: - /* - * PropertyAccess nodes can have any expression/'super' as left-hand - * side, but the name must be a ParseNodeKind::PropertyName node. - */ - PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end) - : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name) + PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end) + : ParseNode(ParseNodeKind::Dot, JSOP_NOP, PN_NAME, TokenPos(begin, end)) { MOZ_ASSERT(lhs != nullptr); MOZ_ASSERT(name != nullptr); + pn_u.name.expr = lhs; + pn_u.name.atom = name; } static bool test(const ParseNode& node) { bool match = node.isKind(ParseNodeKind::Dot); - MOZ_ASSERT_IF(match, node.isArity(PN_BINARY)); - MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName)); + MOZ_ASSERT_IF(match, node.isArity(PN_NAME)); return match; } ParseNode& expression() const { - return *pn_u.binary.left; + return *pn_u.name.expr; } PropertyName& name() const { - return *pn_u.binary.right->pn_atom->asPropertyName(); + return *pn_u.name.atom->asPropertyName(); } bool isSuper() const { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index d1a7e61c9271..b264a0bf9ad0 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3395,13 +3395,13 @@ GeneralParser::addExprAndGetNextTemplStrToken(YieldHandling template bool -GeneralParser::taggedTemplate(YieldHandling yieldHandling, Node tagArgsList, +GeneralParser::taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt) { Node callSiteObjNode = handler.newCallSiteObject(pos().begin); if (!callSiteObjNode) return false; - handler.addList(tagArgsList, callSiteObjNode); + handler.addList(nodeList, callSiteObjNode); while (true) { if (!appendToCallSiteObj(callSiteObjNode)) @@ -3409,10 +3409,10 @@ GeneralParser::taggedTemplate(YieldHandling yieldHandling, if (tt != TokenKind::TemplateHead) break; - if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt)) + if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) return false; } - handler.setEndPosition(tagArgsList, callSiteObjNode); + handler.setEndPosition(nodeList, callSiteObjNode); return true; } @@ -8631,25 +8631,24 @@ GeneralParser::assignExprWithoutYieldOrAwait(YieldHandling } template -typename ParseHandler::Node -GeneralParser::argumentList(YieldHandling yieldHandling, bool* isSpread, +bool +GeneralParser::argumentList(YieldHandling yieldHandling, Node listNode, + bool* isSpread, PossibleError* possibleError /* = nullptr */) { - Node argsList = handler.newArguments(pos()); - bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Rp, TokenStream::Operand)) - return null(); + return false; if (matched) { - handler.setEndPosition(argsList, pos().end); - return argsList; + handler.setEndPosition(listNode, pos().end); + return true; } while (true) { bool spread = false; uint32_t begin = 0; if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::Operand)) - return null(); + return false; if (matched) { spread = true; begin = pos().begin; @@ -8658,18 +8657,18 @@ GeneralParser::argumentList(YieldHandling yieldHandling, bo Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!argNode) - return null(); + return false; if (spread) { argNode = handler.newSpread(begin, argNode); if (!argNode) - return null(); + return false; } - handler.addList(argsList, argNode); + handler.addList(listNode, argNode); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::Operand)) - return null(); + return false; if (!matched) break; @@ -8682,8 +8681,8 @@ GeneralParser::argumentList(YieldHandling yieldHandling, bo MUST_MATCH_TOKEN_MOD(TokenKind::Rp, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS); - handler.setEndPosition(argsList, pos().end); - return argsList; + handler.setEndPosition(listNode, pos().end); + return true; } bool @@ -8729,27 +8728,20 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, if (!ctorExpr) return null(); - bool matched; - if (!tokenStream.matchToken(&matched, TokenKind::Lp)) - return null(); - - bool isSpread = false; - Node args; - if (matched) { - args = argumentList(yieldHandling, &isSpread); - } else { - args = handler.newArguments(pos()); - } - - if (!args) - return null(); - - lhs = handler.newNewExpression(newBegin, ctorExpr, args); + lhs = handler.newNewExpression(newBegin, ctorExpr); if (!lhs) return null(); - if (isSpread) - handler.setOp(lhs, JSOP_SPREADNEW); + bool matched; + if (!tokenStream.matchToken(&matched, TokenKind::Lp)) + return null(); + if (matched) { + bool isSpread = false; + if (!argumentList(yieldHandling, lhs, &isSpread)) + return null(); + if (isSpread) + handler.setOp(lhs, JSOP_SPREADNEW); + } } } else if (tt == TokenKind::Super) { Node thisName = newThisName(); @@ -8786,12 +8778,7 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, error(JSMSG_BAD_SUPERPROP, "property"); return null(); } - - Node name = handler.newPropertyName(field, pos()); - if (!name) - return null(); - - nextMember = handler.newPropertyAccess(lhs, name); + nextMember = handler.newPropertyAccess(lhs, field, pos().end); if (!nextMember) return null(); } else { @@ -8827,16 +8814,15 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, return null(); } + nextMember = handler.newSuperCall(lhs); + if (!nextMember) + return null(); + // Despite the fact that it's impossible to have |super()| in a // generator, we still inherit the yieldHandling of the // memberExpression, per spec. Curious. bool isSpread = false; - Node args = argumentList(yieldHandling, &isSpread); - if (!args) - return null(); - - nextMember = handler.newSuperCall(lhs, args); - if (!nextMember) + if (!argumentList(yieldHandling, nextMember, &isSpread)) return null(); if (isSpread) @@ -8855,6 +8841,13 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, return null(); } + TokenPos nextMemberPos = pos(); + nextMember = tt == TokenKind::Lp + ? handler.newCall(nextMemberPos) + : handler.newTaggedTemplate(nextMemberPos); + if (!nextMember) + return null(); + JSOp op = JSOP_CALL; bool maybeAsyncArrow = false; if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { @@ -8896,11 +8889,13 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, } } + handler.setBeginPosition(nextMember, lhs); + handler.addList(nextMember, lhs); + if (tt == TokenKind::Lp) { bool isSpread = false; PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr; - Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError); - if (!args) + if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError)) return null(); if (isSpread) { if (op == JSOP_EVAL) @@ -8910,20 +8905,8 @@ GeneralParser::memberExpr(YieldHandling yieldHandling, else op = JSOP_SPREADCALL; } - - nextMember = handler.newCall(lhs, args); - if (!nextMember) - return null(); } else { - Node args = handler.newArguments(pos()); - if (!args) - return null(); - - if (!taggedTemplate(yieldHandling, args, tt)) - return null(); - - nextMember = handler.newTaggedTemplate(lhs, args); - if (!nextMember) + if (!taggedTemplate(yieldHandling, nextMember, tt)) return null(); } handler.setOp(nextMember, op); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b27b6bde405e..000dfb465395 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -569,12 +569,8 @@ class MOZ_STACK_CLASS PerHandlerParser bool isValidSimpleAssignmentTarget(Node node, FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls); - Node newPropertyName(PropertyName* key, const TokenPos& pos) { - return handler.newPropertyName(key, pos); - } - - Node newPropertyAccess(Node expr, Node key) { - return handler.newPropertyAccess(expr, key); + Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) { + return handler.newPropertyAccess(expr, key, end); } FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, @@ -1159,7 +1155,7 @@ class MOZ_STACK_CLASS GeneralParser Node condition(InHandling inHandling, YieldHandling yieldHandling); - Node argumentList(YieldHandling yieldHandling, bool* isSpread, + bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread, PossibleError* possibleError = nullptr); Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 511d3b24aab2..eb393c698340 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -248,11 +248,9 @@ class SyntaxParseHandler MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; } void addArrayElement(Node literal, Node element) { } - Node newArguments(const TokenPos& pos) { return NodeGeneric; } - Node newCall(Node callee, Node args) { return NodeFunctionCall; } - - Node newSuperCall(Node callee, Node args) { return NodeGeneric; } - Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; } + Node newCall(const TokenPos& pos) { return NodeFunctionCall; } + Node newSuperCall(Node callee) { return NodeGeneric; } + Node newTaggedTemplate(const TokenPos& pos) { return NodeGeneric; } Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } @@ -330,12 +328,8 @@ class SyntaxParseHandler } Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; } - Node newPropertyName(PropertyName* name, const TokenPos& pos) { - lastAtom = name; - return NodeGeneric; - } - - Node newPropertyAccess(Node expr, Node key) { + Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) { + lastAtom = key; return NodeDottedProperty; } @@ -430,7 +424,7 @@ class SyntaxParseHandler list == NodeFunctionCall); } - Node newNewExpression(uint32_t begin, Node ctor, Node args) { + Node newNewExpression(uint32_t begin, Node ctor) { return NodeGeneric; } diff --git a/js/src/jit-test/lib/assert-offset-columns.js b/js/src/jit-test/lib/assert-offset-columns.js index 0377f870dc53..e69d07a6dde3 100644 --- a/js/src/jit-test/lib/assert-offset-columns.js +++ b/js/src/jit-test/lib/assert-offset-columns.js @@ -33,12 +33,7 @@ function assertOffsetColumns(code, expectedBpts, expectedOrdering = null) { // Set breakpoints everywhere and call the function. const dbg = new Debugger; - let debuggeeFn = dbg.addDebuggee(global).makeDebuggeeValue(global.f); - if (debuggeeFn.isBoundFunction) { - debuggeeFn = debuggeeFn.boundTargetFunction; - } - - const { script } = debuggeeFn; + const script = dbg.addDebuggee(global).makeDebuggeeValue(global.f).script; for (const offset of script.getAllColumnOffsets()) { assertEq(offset.lineNumber, 1); assertEq(offset.columnNumber < execCode.length, true); diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js index d8333155330d..f12cf81fdf72 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets.js @@ -83,58 +83,3 @@ assertOffsetColumns( "function f(n) { do { print(n); } while(false); }", " ^ ^ ^", ); - -// getColumnOffsets correctly places the part of normal ::Dot node with identifier root. -assertOffsetColumns( - "var args = [];\n" + - "var obj = { base: { a(){ return { b(){} }; } } };\n" + - "function f(n) { obj.base.a().b(...args); }", - " ^ ^ ^ ^", - "0 2 1 3", -); - -// getColumnOffsets correctly places the part of normal ::Dot node with "this" root. -assertOffsetColumns( - "var args = [];\n" + - "var obj = { base: { a(){ return { b(){} }; } } };\n" + - "var f = function() { this.base.a().b(...args); }.bind(obj);", - " ^ ^ ^ ^", - "0 2 1 3", -); - -// getColumnOffsets correctly places the part of normal ::Dot node with "super" base. -assertOffsetColumns( - "var args = [];\n" + - "var obj = { base: { a(){ return { b(){} }; } } };\n" + - "var f = { __proto__: obj, f(n) { super.base.a().b(...args); } }.f;", - " ^ ^ ^ ^", - "0 2 1 3", -); - -// getColumnOffsets correctly places the part of normal ::Dot node with other base. -assertOffsetColumns( - "var args = [];\n" + - "var obj = { base: { a(){ return { b(){} }; } } };\n" + - "function f(n) { (0, obj).base.a().b(...args); }", - " ^ ^ ^ ^ ^ ^", - "0 1 2 4 3 5", -); - -// getColumnOffsets correctly places the part of folded ::Elem node. -assertOffsetColumns( - "var args = [];\n" + - "var obj = { base: { a(){ return { b(){} }; } } };\n" + - // Constant folding makes the static string behave like a dot access. - "function f(n) { obj.base['a']()['b'](...args); }", - " ^ ^ ^ ^", - "0 2 1 3", -); - -// getColumnOffsets correctly places the part of computed ::Elem node. -assertOffsetColumns( - "var args = [], a = 'a', b = 'b';\n" + - "var obj = { base: { a(){ return { b(){} }; } } };\n" + - "function f(n) { obj.base[a]()[b](...args); }", - " ^ ^ ^^ ^", - "0 1 3 2 4", -); diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 92d6a73f14fc..80a37395cf59 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -438,21 +438,22 @@ static inline ParseNode* CallCallee(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return BinaryLeft(pn); + return ListHead(pn); } static inline unsigned CallArgListLength(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return ListLength(BinaryRight(pn)); + MOZ_ASSERT(ListLength(pn) >= 1); + return ListLength(pn) - 1; } static inline ParseNode* CallArgList(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Call)); - return ListHead(BinaryRight(pn)); + return NextNode(ListHead(pn)); } static inline ParseNode* @@ -615,16 +616,16 @@ static ParseNode* DotBase(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(pn->isArity(PN_BINARY)); - return pn->pn_left; + MOZ_ASSERT(pn->isArity(PN_NAME)); + return pn->expr(); } static PropertyName* DotMember(ParseNode* pn) { MOZ_ASSERT(pn->isKind(ParseNodeKind::Dot)); - MOZ_ASSERT(pn->isArity(PN_BINARY)); - return pn->pn_right->pn_atom->asPropertyName(); + MOZ_ASSERT(pn->isArity(PN_NAME)); + return pn->pn_atom->asPropertyName(); } static ParseNode* @@ -2902,11 +2903,9 @@ IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type) } static bool -CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* newExpr, PropertyName* bufferName) +CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName) { - ParseNode* ctorExpr = BinaryLeft(newExpr); - ParseNode* ctorArgs = BinaryRight(newExpr); - ParseNode* bufArg = ListHead(ctorArgs); + ParseNode* bufArg = NextNode(ctorExpr); if (!bufArg || NextNode(bufArg) != nullptr) return m.fail(ctorExpr, "array view constructor takes exactly one argument"); @@ -2927,7 +2926,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) if (!bufferName) return m.fail(newExpr, "cannot create array view without an asm.js heap parameter"); - ParseNode* ctorExpr = BinaryLeft(newExpr); + ParseNode* ctorExpr = ListHead(newExpr); PropertyName* field; Scalar::Type type; @@ -2956,7 +2955,7 @@ CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr) type = global->viewType(); } - if (!CheckNewArrayViewArgs(m, newExpr, bufferName)) + if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName)) return false; return m.addArrayView(varName, type, field); From b5730bd2b0ca567975b905a1ac53cbe7bbeec72e Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Wed, 13 Jun 2018 06:25:00 +0300 Subject: [PATCH 08/21] Bug 1445659 - Make it possible to store RefPtr in AutoCleanLinkedList. r=froydnj Add a trait method that AutoCleanLinkedList delegates to for calling delete on non-refcounted list elements. --HG-- extra : histedit_source : 5e8b05f348d734d9045621d858caed946853fc02 --- mfbt/LinkedList.h | 13 +++- mfbt/tests/gtest/TestLinkedList.cpp | 93 +++++++++++++++++++++++++++++ mfbt/tests/gtest/moz.build | 1 + 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 mfbt/tests/gtest/TestLinkedList.cpp diff --git a/mfbt/LinkedList.h b/mfbt/LinkedList.h index c299ec6fcaef..cc394f685a07 100644 --- a/mfbt/LinkedList.h +++ b/mfbt/LinkedList.h @@ -99,6 +99,11 @@ struct LinkedListElementTraits // to a list. static void enterList(LinkedListElement* elt) {} static void exitList(LinkedListElement* elt) {} + + // This method is called when AutoCleanLinkedList cleans itself + // during destruction. It can be used to call delete on elements if + // the list is the sole owner. + static void cleanElement(LinkedListElement* elt) { delete elt->asT(); } }; template @@ -111,6 +116,7 @@ struct LinkedListElementTraits> static void enterList(LinkedListElement>* elt) { elt->asT()->AddRef(); } static void exitList(LinkedListElement>* elt) { elt->asT()->Release(); } + static void cleanElement(LinkedListElement>* elt) {} }; } /* namespace detail */ @@ -655,6 +661,9 @@ private: template class AutoCleanLinkedList : public LinkedList { +private: + using Traits = detail::LinkedListElementTraits; + using ClientType = typename detail::LinkedListElementTraits::ClientType; public: ~AutoCleanLinkedList() { @@ -669,8 +678,8 @@ public: void clear() { - while (T* element = this->popFirst()) { - delete element; + while (ClientType element = this->popFirst()) { + Traits::cleanElement(element); } } }; diff --git a/mfbt/tests/gtest/TestLinkedList.cpp b/mfbt/tests/gtest/TestLinkedList.cpp new file mode 100644 index 000000000000..f6be7baf6235 --- /dev/null +++ b/mfbt/tests/gtest/TestLinkedList.cpp @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 "gtest/gtest.h" + +#include "mozilla/LinkedList.h" +#include "mozilla/RefPtr.h" + +using mozilla::AutoCleanLinkedList; +using mozilla::LinkedList; +using mozilla::LinkedListElement; + +class PtrClass : public LinkedListElement +{ +public: + bool* mResult; + + explicit PtrClass(bool* result) + : mResult(result) + { + EXPECT_TRUE(!*mResult); + } + + virtual ~PtrClass() { + *mResult = true; + } +}; + +class InheritedPtrClass : public PtrClass { +public: + bool* mInheritedResult; + + InheritedPtrClass(bool* result, bool* inheritedResult) + : PtrClass(result) + , mInheritedResult(inheritedResult) + { + EXPECT_TRUE(!*mInheritedResult); + } + + virtual ~InheritedPtrClass() { + *mInheritedResult = true; + } +}; + +TEST(LinkedList, AutoCleanLinkedList) +{ + bool rv1 = false; + bool rv2 = false; + bool rv3 = false; + { + AutoCleanLinkedList list; + list.insertBack(new PtrClass(&rv1)); + list.insertBack(new InheritedPtrClass(&rv2, &rv3)); + } + + EXPECT_TRUE(rv1); + EXPECT_TRUE(rv2); + EXPECT_TRUE(rv3); +} + +class CountedClass final : public LinkedListElement> +{ +public: + int mCount; + void AddRef() { mCount++; } + void Release() { mCount--; } + + CountedClass() + : mCount(0) + { + } + ~CountedClass() { EXPECT_TRUE(mCount == 0); } +}; + +TEST(LinkedList, AutoCleanLinkedListRefPtr) +{ + RefPtr elt1 = new CountedClass; + CountedClass* elt2 = new CountedClass; + { + AutoCleanLinkedList> list; + list.insertBack(elt1); + list.insertBack(elt2); + + EXPECT_TRUE(elt1->mCount == 2); + EXPECT_TRUE(elt2->mCount == 1); + } + + EXPECT_TRUE(elt1->mCount == 1); + EXPECT_TRUE(elt2->mCount == 0); +} diff --git a/mfbt/tests/gtest/moz.build b/mfbt/tests/gtest/moz.build index bd559d60b94e..85243ade98aa 100644 --- a/mfbt/tests/gtest/moz.build +++ b/mfbt/tests/gtest/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. UNIFIED_SOURCES += [ + 'TestLinkedList.cpp', 'TestSpan.cpp', ] From 1d151a57c8f87a45695679b6bb8d23a2b469dd5b Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Thu, 26 Jul 2018 10:31:00 +0300 Subject: [PATCH 09/21] Bug 1445659 - Create basic Browsing Context in Content Parent and Child. r=peterv Add BrowsingContext to allow the tree structure of docshells to exist in several processes simultaneously. This is a first step towards allowing a tree structure preserving separation of docshells across processes. --HG-- extra : histedit_source : d3c7f6ab4b9ae76f170c126d669ebd570e52f348 --- docshell/base/BrowsingContext.cpp | 187 ++++++++++++++++++++ docshell/base/BrowsingContext.h | 101 +++++++++++ docshell/base/moz.build | 5 + docshell/base/nsDocShell.cpp | 51 +++++- docshell/base/nsDocShell.h | 6 +- docshell/base/nsIDocShell.idl | 4 + docshell/build/nsDocShellModule.cpp | 3 + dom/base/nsContentUtils.cpp | 38 ++-- dom/base/nsContentUtils.h | 6 + dom/base/nsFrameLoader.cpp | 2 + dom/ipc/ContentParent.cpp | 108 +++++++++++ dom/ipc/ContentParent.h | 8 + dom/ipc/IdType.h | 4 +- dom/ipc/PContent.ipdl | 27 +++ toolkit/components/browser/nsWebBrowser.cpp | 1 + xpfe/appshell/nsWebShellWindow.cpp | 2 + 16 files changed, 534 insertions(+), 19 deletions(-) create mode 100644 docshell/base/BrowsingContext.cpp create mode 100644 docshell/base/BrowsingContext.h diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp new file mode 100644 index 000000000000..f745e5f83c2a --- /dev/null +++ b/docshell/base/BrowsingContext.cpp @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 "BrowsingContext.h" + +#include "mozilla/dom/ContentChild.h" +#include "mozilla/Assertions.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Logging.h" +#include "mozilla/StaticPtr.h" + +#include "nsDataHashtable.h" +#include "nsIDocShell.h" +#include "nsContentUtils.h" + +namespace mozilla { +namespace dom { + +static LazyLogModule gBrowsingContextLog("BrowsingContext"); + +static StaticAutoPtr sRootBrowsingContexts; + +static StaticAutoPtr> + sBrowsingContexts; + +/* static */ void +BrowsingContext::Init() +{ + if (!sRootBrowsingContexts) { + sRootBrowsingContexts = new BrowsingContext::Children(); + ClearOnShutdown(&sRootBrowsingContexts); + } + + if (!sBrowsingContexts) { + sBrowsingContexts = + new nsDataHashtable(); + ClearOnShutdown(&sBrowsingContexts); + } +} + +/* static */ LogModule* +BrowsingContext::GetLog() +{ + return gBrowsingContextLog; +} + +/* static */ already_AddRefed +BrowsingContext::Get(uint64_t aId) +{ + RefPtr abc = sBrowsingContexts->Get(aId); + return abc.forget(); +} + +BrowsingContext::BrowsingContext(nsIDocShell* aDocShell) + : mBrowsingContextId(nsContentUtils::GenerateBrowsingContextId()) + , mProcessId(Nothing()) + , mDocShell(aDocShell) +{ + sBrowsingContexts->Put(mBrowsingContextId, this); +} + +BrowsingContext::BrowsingContext(uint64_t aBrowsingContextId, + const nsAString& aName, + const Maybe& aProcessId) + : mBrowsingContextId(aBrowsingContextId) + , mProcessId(aProcessId) + , mName(aName) +{ + // mProcessId only really has a meaning in the parent process, where + // it keeps track of which BrowsingContext is actually holding the + // nsDocShell. + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || aProcessId.isNothing()); + sBrowsingContexts->Put(mBrowsingContextId, this); +} + +void +BrowsingContext::Attach(BrowsingContext* aParent) +{ + if (isInList()) { + MOZ_LOG(GetLog(), + LogLevel::Debug, + ("%s: Connecting already existing 0x%08" PRIx64 " to 0x%08" PRIx64, + XRE_IsParentProcess() ? "Parent" : "Child", + Id(), + aParent ? aParent->Id() : 0)); + MOZ_DIAGNOSTIC_ASSERT(sBrowsingContexts->Contains(Id())); + return; + } + + MOZ_LOG(GetLog(), + LogLevel::Debug, + ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64 "\n", + XRE_IsParentProcess() ? "Parent" : "Child", + Id(), + aParent ? aParent->Id() : 0)); + + auto* children = aParent ? &aParent->mChildren : sRootBrowsingContexts.get(); + children->insertBack(this); + mParent = aParent; + + if (!XRE_IsContentProcess()) { + return; + } + + auto cc = dom::ContentChild::GetSingleton(); + MOZ_DIAGNOSTIC_ASSERT(cc); + cc->SendAttachBrowsingContext( + dom::BrowsingContextId(mParent ? mParent->Id() : 0), + dom::BrowsingContextId(Id()), + mName); +} + +void +BrowsingContext::Detach() +{ + RefPtr kungFuDeathGrip(this); + + if (!isInList()) { + MOZ_LOG(GetLog(), + LogLevel::Debug, + ("%s: Detaching already detached 0x%08" PRIx64, + XRE_IsParentProcess() ? "Parent" : "Child", + Id())); + return; + } + + MOZ_LOG(GetLog(), + LogLevel::Debug, + ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64, + XRE_IsParentProcess() ? "Parent" : "Child", + Id(), + mParent ? mParent->Id() : 0)); + + remove(); + + if (!XRE_IsContentProcess()) { + return; + } + + auto cc = dom::ContentChild::GetSingleton(); + MOZ_DIAGNOSTIC_ASSERT(cc); + cc->SendDetachBrowsingContext(dom::BrowsingContextId(Id())); +} + +uint64_t +BrowsingContext::OwnerProcessId() const +{ + MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); + return mProcessId.value(); +} + +BrowsingContext::~BrowsingContext() +{ + MOZ_DIAGNOSTIC_ASSERT(!isInList()); + + if (sBrowsingContexts) { + sBrowsingContexts->Remove(mBrowsingContextId); + } +} + +static void +ImplCycleCollectionUnlink(BrowsingContext::Children& aField) +{ + aField.clear(); +} + +static void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + BrowsingContext::Children& aField, + const char* aName, + uint32_t aFlags = 0) +{ + for (BrowsingContext* aContext : aField) { + aCallback.NoteNativeChild(aContext, + NS_CYCLE_COLLECTION_PARTICIPANT(BrowsingContext)); + } +} + +NS_IMPL_CYCLE_COLLECTION(BrowsingContext, mDocShell, mChildren) +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(BrowsingContext, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(BrowsingContext, Release) + +} // namespace dom +} // namespace mozilla diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h new file mode 100644 index 000000000000..a88a454f71e4 --- /dev/null +++ b/docshell/base/BrowsingContext.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +#ifndef BrowsingContext_h +#define BrowsingContext_h + +#include "mozilla/LinkedList.h" +#include "mozilla/Maybe.h" +#include "mozilla/RefPtr.h" +#include "mozilla/WeakPtr.h" +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsString.h" + +class nsIDocShell; + +namespace mozilla { + +class LogModule; + +namespace dom { + +// BrowsingContext, in this context, is the cross process replicated +// environment in which information about documents is stored. In +// particular the tree structure of nested browsing contexts is +// represented by the tree of BrowsingContexts. +// +// The tree of BrowsingContexts in created in step with its +// corresponding nsDocShell, and when nsDocShells are connected +// through a parent/child relationship, so are BrowsingContexts. The +// major difference is that BrowsingContexts are replicated (synced) +// to the parent process, making it possible to traverse the +// BrowsingContext tree for a tab, in both the parent and the child +// process. +class BrowsingContext + : public SupportsWeakPtr + , public LinkedListElement> +{ +public: + static void Init(); + static LogModule* GetLog(); + + static already_AddRefed Get(uint64_t aId); + + // Create a new BrowsingContext for 'aDocShell'. The id will be + // generated so that it is unique across all content child processes + // and the content parent process. + explicit BrowsingContext(nsIDocShell* aDocShell); + // Create a BrowsingContext for a particular BrowsingContext id, in + // the case where the id is known beforehand and a nsDocShell isn't + // needed (e.g. when creating BrowsingContexts in the parent + // process). + BrowsingContext(uint64_t aBrowsingContextId, + const nsAString& aName, + const Maybe& aProcessId = Nothing()); + + // Attach the current BrowsingContext to its parent, in both the + // child and the parent process. If 'aParent' is null, 'this' is + // taken to be a root BrowsingContext. + void Attach(BrowsingContext* aParent); + + // Detach the current BrowsingContext from its parent, in both the + // child and the parent process. + void Detach(); + + void SetName(const nsAString& aName) { mName = aName; } + void GetName(nsAString& aName) { aName = mName; } + bool NameEquals(const nsAString& aName) { return mName.Equals(aName); } + + uint64_t Id() const { return mBrowsingContextId; } + uint64_t OwnerProcessId() const; + + BrowsingContext* Parent() const { return mParent; } + + MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext) + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(BrowsingContext) + + using Children = AutoCleanLinkedList>; + +private: + ~BrowsingContext(); + + const uint64_t mBrowsingContextId; + + // Indicates which process owns the docshell. Only valid in the + // parent process. + Maybe mProcessId; + + WeakPtr mParent; + Children mChildren; + nsCOMPtr mDocShell; + nsString mName; +}; + +} // namespace dom +} // namespace mozilla +#endif diff --git a/docshell/base/moz.build b/docshell/base/moz.build index c00fd84fb37a..c10f1ea43b01 100644 --- a/docshell/base/moz.build +++ b/docshell/base/moz.build @@ -77,7 +77,12 @@ EXPORTS.mozilla += [ 'LoadContext.h', ] +EXPORTS.mozilla.dom += [ + 'BrowsingContext.h', +] + UNIFIED_SOURCES += [ + 'BrowsingContext.cpp', 'LoadContext.cpp', 'nsAboutRedirector.cpp', 'nsDefaultURIFixup.cpp', diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d5a532dfca55..32fdd937fac4 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -475,6 +475,8 @@ nsDocShell::Init() rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this); NS_ENSURE_SUCCESS(rv, rv); + mBrowsingContext = new BrowsingContext(this); + // Add as |this| a progress listener to itself. A little weird, but // simpler than reproducing all the listener-notification logic in // overrides of the various methods via which nsDocLoader can be @@ -505,7 +507,8 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell, mSessionStorageManager, mScriptGlobal, mInitialClientSource, - mSessionHistory) + mSessionHistory, + mBrowsingContext) NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader) NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader) @@ -2539,14 +2542,14 @@ nsDocShell::NotifyScrollObservers() NS_IMETHODIMP nsDocShell::GetName(nsAString& aName) { - aName = mName; + mBrowsingContext->GetName(aName); return NS_OK; } NS_IMETHODIMP nsDocShell::SetName(const nsAString& aName) { - mName = aName; + mBrowsingContext->SetName(aName); return NS_OK; } @@ -2554,7 +2557,7 @@ NS_IMETHODIMP nsDocShell::NameEquals(const nsAString& aName, bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); - *aResult = mName.Equals(aName); + *aResult = mBrowsingContext->NameEquals(aName); return NS_OK; } @@ -2851,6 +2854,7 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) bool value; nsString customUserAgent; nsCOMPtr parentAsDocShell(do_QueryInterface(parent)); + if (parentAsDocShell) { if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) { SetAllowPlugins(value); @@ -3259,7 +3263,7 @@ nsDocShell::DoFindItemWithName(const nsAString& aName, nsIDocShellTreeItem** aResult) { // First we check our name. - if (mName.Equals(aName) && ItemIsActive(this) && + if (mBrowsingContext->NameEquals(aName) && ItemIsActive(this) && CanAccessItem(this, aOriginalRequestor)) { NS_ADDREF(*aResult = this); return NS_OK; @@ -3550,6 +3554,8 @@ nsDocShell::AddChild(nsIDocShellTreeItem* aChild) return NS_OK; } + childAsDocShell->AttachBrowsingContext(this); + // charset, style-disabling, and zoom will be inherited in SetupNewViewer() // Now take this document's charset and set the child's parentCharset field @@ -3612,6 +3618,11 @@ nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) nsresult rv = RemoveChildLoader(childAsDocLoader); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr childAsDocShell(do_QueryInterface(aChild)); + if (childAsDocShell) { + childAsDocShell->DetachBrowsingContext(); + } + aChild->SetTreeOwner(nullptr); return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader); @@ -5290,6 +5301,8 @@ nsDocShell::Destroy() mSessionHistory = nullptr; } + mBrowsingContext->Detach(); + SetTreeOwner(nullptr); mOnePermittedSandboxedNavigator = nullptr; @@ -14223,3 +14236,31 @@ nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); } + +already_AddRefed +nsDocShell::GetBrowsingContext() const +{ + RefPtr browsingContext = mBrowsingContext; + return browsingContext.forget(); +} + +void +nsIDocShell::AttachBrowsingContext(nsIDocShell* aParentDocShell) +{ + RefPtr childContext = + nsDocShell::Cast(this)->GetBrowsingContext(); + RefPtr parentContext; + if (aParentDocShell) { + parentContext = + nsDocShell::Cast(aParentDocShell)->GetBrowsingContext(); + } + childContext->Attach(parentContext); +} + +void +nsIDocShell::DetachBrowsingContext() +{ + RefPtr browsingContext = + nsDocShell::Cast(this)->GetBrowsingContext(); + browsingContext->Detach(); +} diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 89f3f28b89a5..f50d464ba1a6 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -15,6 +15,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" +#include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/ProfileTimelineMarkerBinding.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/dom/ChildSHistory.h" @@ -379,6 +380,9 @@ public: // shift while triggering reload) bool IsForceReloading(); + already_AddRefed + GetBrowsingContext() const; + private: // member functions friend class nsDSURIContentListener; friend class FramingChecker; @@ -907,7 +911,6 @@ private: // data members #endif /* DEBUG */ nsID mHistoryID; - nsString mName; nsString mTitle; nsString mCustomUserAgent; nsCString mOriginalUriString; @@ -932,6 +935,7 @@ private: // data members RefPtr mSessionHistory; nsCOMPtr mFind; nsCOMPtr mCommandManager; + RefPtr mBrowsingContext; // Dimensions of the docshell nsIntRect mBounds; diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index c2a52c582417..3295b129a774 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -22,6 +22,7 @@ namespace mozilla { class Encoding; class HTMLEditor; namespace dom { +class BrowsingContext; class ClientSource; } // namespace dom } @@ -1180,6 +1181,9 @@ interface nsIDocShell : nsIDocShellTreeItem */ mozilla::HTMLEditor* GetHTMLEditor(); nsresult SetHTMLEditor(mozilla::HTMLEditor* aHTMLEditor); + + void AttachBrowsingContext(nsIDocShell* aParentDocShell); + void DetachBrowsingContext(); %} /** diff --git a/docshell/build/nsDocShellModule.cpp b/docshell/build/nsDocShellModule.cpp index 47fd9b6467ff..f2553fbc97d8 100644 --- a/docshell/build/nsDocShellModule.cpp +++ b/docshell/build/nsDocShellModule.cpp @@ -7,6 +7,8 @@ #include "mozilla/ModuleUtils.h" #include "nsDocShellCID.h" +#include "mozilla/dom/BrowsingContext.h" + #include "nsDocShell.h" #include "nsDefaultURIFixup.h" #include "nsWebNavigationInfo.h" @@ -53,6 +55,7 @@ Initialize() } gInitialized = true; + mozilla::dom::BrowsingContext::Init(); nsresult rv = nsSHistory::Startup(); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4a211ea9c806..8c1b1de57ea1 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -10800,13 +10800,11 @@ nsContentUtils::IsLocalRefURL(const nsACString& aString) return ::IsLocalRefURL(aString); } -// Tab ID is composed in a similar manner of Window ID. -static uint64_t gNextTabId = 0; -static const uint64_t kTabIdProcessBits = 32; -static const uint64_t kTabIdTabBits = 64 - kTabIdProcessBits; +static const uint64_t kIdProcessBits = 32; +static const uint64_t kIdBits = 64 - kIdProcessBits; /* static */ uint64_t -nsContentUtils::GenerateTabId() +GenerateProcessSpecificId(uint64_t aId) { uint64_t processId = 0; if (XRE_IsContentProcess()) { @@ -10814,14 +10812,32 @@ nsContentUtils::GenerateTabId() processId = cc->GetID(); } - MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kTabIdProcessBits)); - uint64_t processBits = processId & ((uint64_t(1) << kTabIdProcessBits) - 1); + MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits)); + uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1); - uint64_t tabId = ++gNextTabId; - MOZ_RELEASE_ASSERT(tabId < (uint64_t(1) << kTabIdTabBits)); - uint64_t tabBits = tabId & ((uint64_t(1) << kTabIdTabBits) - 1); + uint64_t id = aId; + MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits)); + uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1); - return (processBits << kTabIdTabBits) | tabBits; + return (processBits << kIdBits) | bits; +} + +// Tab ID is composed in a similar manner of Window ID. +static uint64_t gNextTabId = 0; + +/* static */ uint64_t +nsContentUtils::GenerateTabId() +{ + return GenerateProcessSpecificId(++gNextTabId); +} + +// Browsing context ID is composed in a similar manner of Window ID. +static uint64_t gNextBrowsingContextId = 0; + +/* static */ uint64_t +nsContentUtils::GenerateBrowsingContextId() +{ + return GenerateProcessSpecificId(++gNextBrowsingContextId); } /* static */ bool diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index b514adbe0a43..8b25fb6bc1c9 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -3166,6 +3166,12 @@ public: */ static uint64_t GenerateTabId(); + /** + * Generate an id for a BrowsingContext using a range of serial + * numbers reserved for the current process. + */ + static uint64_t GenerateBrowsingContextId(); + /** * Check whether we should skip moving the cursor for a same-value .value set * on a text input or textarea. diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index b0df6c939267..76936643d54e 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -689,6 +689,8 @@ nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, // Now that we have our type set, add ourselves to the parent, as needed. if (aParentNode) { aParentNode->AddChild(aItem); + } else if (nsCOMPtr childAsDocShell = do_QueryInterface(aItem)) { + childAsDocShell->AttachBrowsingContext(aParentNode); } bool retval = false; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index fc14f334f716..fa11d77db5cb 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -36,6 +36,7 @@ #include "mozilla/DataStorage.h" #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h" #include "mozilla/docshell/OfflineCacheUpdateParent.h" +#include "mozilla/dom/BrowsingContext.h" #include "mozilla/dom/ClientManager.h" #include "mozilla/dom/ClientOpenWindowOpActors.h" #include "mozilla/dom/DataTransfer.h" @@ -5977,3 +5978,110 @@ ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(const Principal& aPar std::move(aResolver)); return IPC_OK(); } + +mozilla::ipc::IPCResult +ContentParent::RecvAttachBrowsingContext( + const BrowsingContextId& aParentId, + const BrowsingContextId& aChildId, + const nsString& aName) +{ + RefPtr parent = BrowsingContext::Get(aParentId); + if (aParentId && !parent) { + // Unless 'aParentId' is 0 (which it is when the child is a root + // BrowsingContext) there should always be a corresponding + // 'parent'. The only reason for there not beeing one is if the + // parent has already been detached, in which case the + // BrowsingContext that tries to attach itself to the context with + // 'aParentId' is surely doomed and we can safely do nothing. + + // TODO(farre): When we start syncing/moving BrowsingContexts to + // other child processes is it possible to get into races where + // constructive operations on already detached BrowsingContexts + // are requested? This needs to be answered/handled, but for now + // return early. [Bug 1471598] + MOZ_LOG( + BrowsingContext::GetLog(), + LogLevel::Debug, + ("ParentIPC: Trying to attach to already detached parent 0x%08" PRIx64, + (uint64_t)aParentId)); + return IPC_OK(); + } + + if (parent && parent->OwnerProcessId() != ChildID()) { + // Where trying attach a child BrowsingContext to a parent + // BrowsingContext in another process. This is illegal since the + // only thing that could create that child BrowsingContext is a + // parent docshell in the same process as that BrowsingContext. + + // TODO(farre): We're doing nothing now, but is that exactly what + // we want? Maybe we want to crash the child currently calling + // SendAttachBrowsingContext and/or the child that originally + // called SendAttachBrowsingContext or possibly all children that + // has a BrowsingContext connected to the child that currently + // called SendAttachBrowsingContext? [Bug 1471598] + MOZ_LOG(BrowsingContext::GetLog(), + LogLevel::Warning, + ("ParentIPC: Trying to attach to out of process parent context " + "0x%08" PRIx64, + parent->Id())); + return IPC_OK(); + } + + RefPtr child = BrowsingContext::Get(aChildId); + if (child) { + // This is highly suspicious. BrowsingContexts should only be + // attached at most once, but finding one indicates that someone + // is doing something they shouldn't. + + // TODO(farre): To crash or not to crash. Same reasoning as in + // above TODO. [Bug 1471598] + MOZ_LOG(BrowsingContext::GetLog(), + LogLevel::Warning, + ("ParentIPC: Trying to attach already attached 0x%08" PRIx64 + " to 0x%08" PRIx64, + child->Id(), + (uint64_t)aParentId)); + return IPC_OK(); + } + + if (!child) { + child = new BrowsingContext(aChildId, aName, Some(ChildID())); + } + child->Attach(parent); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult +ContentParent::RecvDetachBrowsingContext(const BrowsingContextId& aContextId, + const bool& aMoveToBFCache) +{ + RefPtr context = BrowsingContext::Get(aContextId); + + if (!context) { + MOZ_LOG(BrowsingContext::GetLog(), + LogLevel::Debug, + ("ParentIPC: Trying to detach already detached 0x%08" PRIx64, + (uint64_t)aContextId)); + return IPC_OK(); + } + + if (context->OwnerProcessId() != ChildID()) { + // Where trying to detach a child BrowsingContext in another child + // process. This is illegal since the owner of the BrowsingContext + // is the proccess with the in-process docshell, which is tracked + // by OwnerProcessId. + + // TODO(farre): To crash or not to crash. Same reasoning as in + // above TODO. [Bug 1471598] + MOZ_LOG(BrowsingContext::GetLog(), + LogLevel::Warning, + ("ParentIPC: Trying to detach out of process context 0x%08" PRIx64, + context->Id())); + return IPC_OK(); + } + + context->Detach(); + + return IPC_OK(); +} diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index a706de6a56b2..769239c5f20b 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -682,6 +682,14 @@ public: static bool IsInputEventQueueSupported(); + virtual mozilla::ipc::IPCResult RecvAttachBrowsingContext( + const BrowsingContextId& aParentContextId, + const BrowsingContextId& aContextId, + const nsString& aName) override; + + virtual mozilla::ipc::IPCResult RecvDetachBrowsingContext( + const BrowsingContextId& aContextId) override; + protected: void OnChannelConnected(int32_t pid) override; diff --git a/dom/ipc/IdType.h b/dom/ipc/IdType.h index b683178eb723..82eeea13ad9d 100644 --- a/dom/ipc/IdType.h +++ b/dom/ipc/IdType.h @@ -15,10 +15,10 @@ template struct ParamTraits; namespace mozilla { namespace dom { +class BrowsingContext; class ContentParent; class TabParent; - template class IdType { @@ -46,7 +46,7 @@ private: typedef IdType TabId; typedef IdType ContentParentId; - +typedef IdType BrowsingContextId; } // namespace dom } // namespace mozilla diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 48756a0bcd3a..81f1afbbba53 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -100,6 +100,7 @@ using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h"; using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h"; using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h"; using refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h"; +using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; union ChromeRegistryItem { @@ -1177,6 +1178,32 @@ parent: nsCString aGrantedOrigin) returns (bool unused); + /** + * Sync the BrowsingContext with id 'aContextId' and name 'aName' + * to the parent, and attach it to the BrowsingContext with id + * 'aParentContextId'. If 'aParentContextId' is '0' the + * BrowsingContext is a root in the BrowsingContext + * tree. AttachBrowsingContext must only be called at most once + * for any child BrowsingContext, and only for BrowsingContexts + * where the parent and the child context contains their + * nsDocShell. + */ + async AttachBrowsingContext(BrowsingContextId aParentContextId, + BrowsingContextId aContextId, + nsString aName); + + /** + * Remove the synced BrowsingContext with id 'aContextId' from the + * parent. DetachBrowsingContext is only needed to be called once + * for any BrowsingContext, since detaching a node in the + * BrowsingContext detaches the entire sub-tree rooted at that + * node. Calling DetachBrowsingContext with an already detached + * BrowsingContext effectively does nothing. Note that it is not + * an error to call DetachBrowsingContext on a BrowsingContext + * belonging to an already detached subtree. + */ + async DetachBrowsingContext(BrowsingContextId aContextId); + both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); diff --git a/toolkit/components/browser/nsWebBrowser.cpp b/toolkit/components/browser/nsWebBrowser.cpp index 7d0ba8669ef4..71548df173a1 100644 --- a/toolkit/components/browser/nsWebBrowser.cpp +++ b/toolkit/components/browser/nsWebBrowser.cpp @@ -1250,6 +1250,7 @@ nsWebBrowser::Create() mDocShell->SetItemType(nsIDocShellTreeItem::typeContent); } mDocShell->SetTreeOwner(mDocShellTreeOwner); + mDocShell->AttachBrowsingContext(nullptr); // If the webbrowser is a content docshell item then we won't hear any // events from subframes. To solve that we install our own chrome event diff --git a/xpfe/appshell/nsWebShellWindow.cpp b/xpfe/appshell/nsWebShellWindow.cpp index 0592adc6b591..2e0e06a59f25 100644 --- a/xpfe/appshell/nsWebShellWindow.cpp +++ b/xpfe/appshell/nsWebShellWindow.cpp @@ -199,6 +199,8 @@ nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent, docShellAsItem->SetTreeOwner(mChromeTreeOwner); docShellAsItem->SetItemType(nsIDocShellTreeItem::typeChrome); + mDocShell->AttachBrowsingContext(nullptr); + r.MoveTo(0, 0); nsCOMPtr docShellAsWin(do_QueryInterface(mDocShell)); NS_ENSURE_SUCCESS(docShellAsWin->InitWindow(nullptr, mWindow, From cbc727cdcf316ee952e15ce28bf80dae0c7b1b39 Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Thu, 28 Jun 2018 05:40:00 +0300 Subject: [PATCH 10/21] Bug 1445659 - Make BrowsingContext interact with bfcache. r=peterv Have BrowsingContext keep its own cache to enable caching of BrowsingContexts, especially in the parent process. This isn't really optimal, since it effectively duplicates the cache in the child process. BFcache keeps a list of strong pointers to the list of cached nsDocShells, where each nsDocShell in turn keeps a reciprocated strong pointer to its BrowsingContext, which in turn is held in the BrowsingContexts list of cached contexts. Ideally these caches should be merged. --HG-- extra : histedit_source : 094370f6d54d83728e8433ec5c47003086146476 --- docshell/base/BrowsingContext.cpp | 59 +++++++++++++++++++++++++++++-- docshell/base/BrowsingContext.h | 6 ++++ docshell/base/nsDocShell.cpp | 2 ++ dom/ipc/ContentParent.cpp | 8 +++-- dom/ipc/ContentParent.h | 3 +- dom/ipc/PContent.ipdl | 9 +++-- 6 files changed, 79 insertions(+), 8 deletions(-) diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index f745e5f83c2a..8cd26aa296d6 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -13,6 +13,7 @@ #include "mozilla/StaticPtr.h" #include "nsDataHashtable.h" +#include "nsRefPtrHashtable.h" #include "nsIDocShell.h" #include "nsContentUtils.h" @@ -26,6 +27,11 @@ static StaticAutoPtr sRootBrowsingContexts; static StaticAutoPtr> sBrowsingContexts; +// TODO(farre): This duplicates some of the work performed by the +// bfcache. This should be unified. [Bug 1471601] +static StaticAutoPtr> + sCachedBrowsingContexts; + /* static */ void BrowsingContext::Init() { @@ -39,6 +45,12 @@ BrowsingContext::Init() new nsDataHashtable(); ClearOnShutdown(&sBrowsingContexts); } + + if (!sCachedBrowsingContexts) { + sCachedBrowsingContexts = + new nsRefPtrHashtable(); + ClearOnShutdown(&sCachedBrowsingContexts); + } } /* static */ LogModule* @@ -87,12 +99,16 @@ BrowsingContext::Attach(BrowsingContext* aParent) Id(), aParent ? aParent->Id() : 0)); MOZ_DIAGNOSTIC_ASSERT(sBrowsingContexts->Contains(Id())); + MOZ_DIAGNOSTIC_ASSERT(!IsCached()); return; } + bool wasCached = sCachedBrowsingContexts->Remove(Id()); + MOZ_LOG(GetLog(), LogLevel::Debug, - ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64 "\n", + ("%s: %s 0x%08" PRIx64 " to 0x%08" PRIx64, + wasCached ? "Re-connecting" : "Connecting", XRE_IsParentProcess() ? "Parent" : "Child", Id(), aParent ? aParent->Id() : 0)); @@ -118,6 +134,10 @@ BrowsingContext::Detach() { RefPtr kungFuDeathGrip(this); + if (sCachedBrowsingContexts) { + sCachedBrowsingContexts->Remove(Id()); + } + if (!isInList()) { MOZ_LOG(GetLog(), LogLevel::Debug, @@ -142,7 +162,42 @@ BrowsingContext::Detach() auto cc = dom::ContentChild::GetSingleton(); MOZ_DIAGNOSTIC_ASSERT(cc); - cc->SendDetachBrowsingContext(dom::BrowsingContextId(Id())); + cc->SendDetachBrowsingContext(dom::BrowsingContextId(Id()), + false /* aMoveToBFCache */); +} + +void +BrowsingContext::CacheChildren() +{ + if (mChildren.isEmpty()) { + return; + } + + MOZ_LOG(GetLog(), + LogLevel::Debug, + ("%s: Caching children of 0x%08" PRIx64 "", + XRE_IsParentProcess() ? "Parent" : "Child", + Id())); + + while (!mChildren.isEmpty()) { + RefPtr child = mChildren.popFirst(); + sCachedBrowsingContexts->Put(child->Id(), child); + } + + if (!XRE_IsContentProcess()) { + return; + } + + auto cc = dom::ContentChild::GetSingleton(); + MOZ_DIAGNOSTIC_ASSERT(cc); + cc->SendDetachBrowsingContext(dom::BrowsingContextId(Id()), + true /* aMoveToBFCache */); +} + +bool +BrowsingContext::IsCached() +{ + return sCachedBrowsingContexts->Contains(Id()); } uint64_t diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index a88a454f71e4..35b49458a065 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -66,6 +66,12 @@ public: // child and the parent process. void Detach(); + // Remove all children from the current BrowsingContext and cache + // them to allow them to be attached again. + void CacheChildren(); + + bool IsCached(); + void SetName(const nsAString& aName) { mName = aName; } void GetName(nsAString& aName) { aName = mName; } bool NameEquals(const nsAString& aName) { return mName.Equals(aName); } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 32fdd937fac4..cd3b1b1c6707 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -7763,6 +7763,8 @@ nsDocShell::CaptureState() mOSHE->AddChildShell(childShell); } + mBrowsingContext->CacheChildren(); + return NS_OK; } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index fa11d77db5cb..11061dceaf38 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -6028,7 +6028,7 @@ ContentParent::RecvAttachBrowsingContext( } RefPtr child = BrowsingContext::Get(aChildId); - if (child) { + if (child && !child->IsCached()) { // This is highly suspicious. BrowsingContexts should only be // attached at most once, but finding one indicates that someone // is doing something they shouldn't. @@ -6081,7 +6081,11 @@ ContentParent::RecvDetachBrowsingContext(const BrowsingContextId& aContextId, return IPC_OK(); } - context->Detach(); + if (aMoveToBFCache) { + context->CacheChildren(); + } else { + context->Detach(); + } return IPC_OK(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 769239c5f20b..d7dc5853047d 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -688,7 +688,8 @@ public: const nsString& aName) override; virtual mozilla::ipc::IPCResult RecvDetachBrowsingContext( - const BrowsingContextId& aContextId) override; + const BrowsingContextId& aContextId, + const bool& aMoveToBFCache) override; protected: void OnChannelConnected(int32_t pid) override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 81f1afbbba53..799c2f196984 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1200,10 +1200,13 @@ parent: * node. Calling DetachBrowsingContext with an already detached * BrowsingContext effectively does nothing. Note that it is not * an error to call DetachBrowsingContext on a BrowsingContext - * belonging to an already detached subtree. + * belonging to an already detached subtree. The 'aMoveToBFCache' + * paramater controls if detaching a BrowsingContext should move + * it to the bfcache allowing it to be re-attached if navigated + * to. */ - async DetachBrowsingContext(BrowsingContextId aContextId); - + async DetachBrowsingContext(BrowsingContextId aContextId, + bool aMoveToBFCache); both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); From 875e9111dc4d463bbb968eabc243584337dcb65b Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Fri, 29 Jun 2018 02:41:00 +0300 Subject: [PATCH 11/21] Bug 1445659 - Remove dangling BrowsingContexts left from closing process. r=Nika To not leave dangling BrowsingContexts due to crashing child processes we need to detach all BrowsingContexts owned by a specific process when that process goes away. --HG-- extra : histedit_source : a737dd272224ae2595e8851813f3f9a66a2e01f2 --- docshell/base/BrowsingContext.cpp | 23 +++++++++++++++++++++++ docshell/base/BrowsingContext.h | 2 ++ dom/ipc/ContentParent.cpp | 2 ++ 3 files changed, 27 insertions(+) diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 8cd26aa296d6..e0b493c6e9c1 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -16,6 +16,7 @@ #include "nsRefPtrHashtable.h" #include "nsIDocShell.h" #include "nsContentUtils.h" +#include "nsThreadUtils.h" namespace mozilla { namespace dom { @@ -59,6 +60,28 @@ BrowsingContext::GetLog() return gBrowsingContextLog; } +// TODO(farre): BrowsingContext::CleanupContexts starts from the list +// of root BrowsingContexts. This isn't enough when separate +// BrowsingContext nodes of a BrowsingContext tree not in a crashing +// child process are from that process and thus needs to be +// cleaned. [Bug 1472108] +/* static */ void +BrowsingContext::CleanupContexts(uint64_t aProcessId) +{ + if (sRootBrowsingContexts) { + RefPtr context = sRootBrowsingContexts->getFirst(); + + while (context) { + RefPtr next = context->getNext(); + if (context->IsOwnedByProcess() && + aProcessId == context->OwnerProcessId()) { + context->Detach(); + } + context = next; + } + } +} + /* static */ already_AddRefed BrowsingContext::Get(uint64_t aId) { diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index 35b49458a065..caed385483f7 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -42,6 +42,7 @@ class BrowsingContext public: static void Init(); static LogModule* GetLog(); + static void CleanupContexts(uint64_t aProcessId); static already_AddRefed Get(uint64_t aId); @@ -78,6 +79,7 @@ public: uint64_t Id() const { return mBrowsingContextId; } uint64_t OwnerProcessId() const; + bool IsOwnedByProcess() const { return mProcessId.isSome(); } BrowsingContext* Parent() const { return mParent; } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 11061dceaf38..6ff7bcbf78f2 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1896,6 +1896,8 @@ ContentParent::ActorDestroy(ActorDestroyReason why) #if defined(XP_WIN32) && defined(ACCESSIBILITY) a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID()); #endif + + BrowsingContext::CleanupContexts(ChildID()); } bool From f2e5e85840e5bf7aacb862c04c0f145e1861ec67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Fri, 27 Jul 2018 23:00:27 +0200 Subject: [PATCH 12/21] Bug 1475589 - remove the focused attribute early on the urlbar when adopting a tab, to ensure the url gets properly formatted, r=Gijs. --- browser/base/content/browser.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index e9d1f0994f27..e46743e07434 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1376,6 +1376,9 @@ var gBrowserInit = { // make sure it has a docshell gBrowser.docShell; + // Remove the speculative focus from the urlbar to let the url be formatted. + gURLBar.removeAttribute("focused"); + try { gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToAdopt); } catch (e) { From d2bb806dca7c01c1cca0e84bdfc67b4c3c6650cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Fri, 27 Jul 2018 23:00:30 +0200 Subject: [PATCH 13/21] Bug 1478856 - make the about:performance sort order more stable, r=felipe. --- .../aboutperformance/content/aboutPerformance.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index f236fd96b173..3bec350ba218 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -603,7 +603,7 @@ var State = { dispatchesSincePrevious: prev ? dispatches - prev.dispatchCount : NaN, dispatchesSinceStartOfBuffer: oldest ? dispatches - oldest.dispatchCount : NaN, children: tab.children}); - }).sort((a, b) => b.dispatchesSinceStartOfBuffer - a.dispatchesSinceStartOfBuffer); + }); } }; @@ -954,7 +954,7 @@ var Control = { // Make sure that we do not keep obsolete stuff around. View.DOMCache.trimTo(state.deltas); } else { - let counters = State.getCounters(); + let counters = this._sortCounters(State.getCounters()); for (let {name, image, totalDispatches, dispatchesSincePrevious, totalDuration, durationSincePrevious, children} of counters) { function dispatchesAndDuration(dispatches, duration) { @@ -995,6 +995,15 @@ var Control = { // Inform watchers Services.obs.notifyObservers(null, UPDATE_COMPLETE_TOPIC, mode); }, + _sortCounters(counters) { + return counters.sort((a, b) => { + if (a.dispatchesSinceStartOfBuffer != b.dispatchesSinceStartOfBuffer) + return b.dispatchesSinceStartOfBuffer - a.dispatchesSinceStartOfBuffer; + if (a.totalDispatches != b.totalDispatches) + return b.totalDispatches - a.totalDispatches; + return a.name.localeCompare(b.name); + }); + }, _setOptions(options) { dump(`about:performance _setOptions ${JSON.stringify(options)}\n`); let eltRefresh = document.getElementById("check-autorefresh"); From a7d855f0373a6fecf34a265a23382f33dec4c20a Mon Sep 17 00:00:00 2001 From: Tokio Kajitsuka Date: Tue, 17 Jul 2018 08:37:37 +0900 Subject: [PATCH 14/21] Bug 1468110 - add AccessibleNode relation attributes, r=yzen, qdot based on ARIA and AOM's ARIA reflection specs (https://wicg.github.io/aom/spec/aria-reflection.html) --- accessible/aom/AccessibleNode.cpp | 1 + accessible/aom/AccessibleNode.h | 43 +++++++++++++++++++ .../tests/mochitest/aom/test_general.html | 13 ++++++ dom/webidl/AccessibleNode.webidl | 5 +++ 4 files changed, 62 insertions(+) diff --git a/accessible/aom/AccessibleNode.cpp b/accessible/aom/AccessibleNode.cpp index dcf5afdfdcee..7fb0f05c4854 100644 --- a/accessible/aom/AccessibleNode.cpp +++ b/accessible/aom/AccessibleNode.cpp @@ -47,6 +47,7 @@ AccessibleNode::AccessibleNode(nsINode* aNode) : mIntProperties(3), mUIntProperties(6), mBooleanProperties(0), + mRelationProperties(3), mStringProperties(16), mDOMNode(aNode) { diff --git a/accessible/aom/AccessibleNode.h b/accessible/aom/AccessibleNode.h index 774cec0d8a27..d56981b7c866 100644 --- a/accessible/aom/AccessibleNode.h +++ b/accessible/aom/AccessibleNode.h @@ -51,6 +51,17 @@ struct ParentObject; SetProperty(AOMStringProperty::e##name, a##name); \ } \ +#define ANODE_RELATION_FUNC(name) \ + already_AddRefed Get##name() \ + { \ + return GetProperty(AOMRelationProperty::e##name); \ + } \ + \ + void Set##name(AccessibleNode* a##name) \ + { \ + SetProperty(AOMRelationProperty::e##name, a##name); \ + } \ + #define ANODE_PROPS(typeName, type, ...) \ enum class AOM##typeName##Property { \ MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__)) \ @@ -63,6 +74,12 @@ struct ParentObject; }; \ MOZ_FOR_EACH(ANODE_STRING_FUNC, (), (__VA_ARGS__)) \ +#define ANODE_RELATION_PROPS(...) \ + enum class AOMRelationProperty { \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__)) \ + }; \ + MOZ_FOR_EACH(ANODE_RELATION_FUNC, (), (__VA_ARGS__)) \ + #define ANODE_ACCESSOR_MUTATOR(typeName, type, defVal) \ nsDataHashtable m##typeName##Properties; \ \ @@ -164,6 +181,12 @@ public: ValueNow ) + ANODE_RELATION_PROPS( + ActiveDescendant, + Details, + ErrorMessage + ) + protected: AccessibleNode(const AccessibleNode& aCopy) = delete; AccessibleNode& operator=(const AccessibleNode& aCopy) = delete; @@ -213,9 +236,29 @@ protected: ANODE_ACCESSOR_MUTATOR(Int, int32_t, 0) ANODE_ACCESSOR_MUTATOR(UInt, uint32_t, 0) + already_AddRefed GetProperty(AOMRelationProperty aProperty) + { + RefPtr data; + if (mRelationProperties.Get(static_cast(aProperty), &data)) { + return data.forget(); + } + return nullptr; + } + + void SetProperty(AOMRelationProperty aProperty, + AccessibleNode* aValue) + { + if (!aValue) { + mRelationProperties.Remove(static_cast(aProperty)); + } else { + mRelationProperties.Put(static_cast(aProperty), aValue); + } + } + // The 2k'th bit indicates whether the k'th boolean property is used(1) or not(0) // and 2k+1'th bit contains the property's value(1:true, 0:false) uint32_t mBooleanProperties; + nsDataHashtable > mRelationProperties; nsDataHashtable mStringProperties; RefPtr mIntl; diff --git a/accessible/tests/mochitest/aom/test_general.html b/accessible/tests/mochitest/aom/test_general.html index e8a379e12f46..6ae9dc48283a 100644 --- a/accessible/tests/mochitest/aom/test_general.html +++ b/accessible/tests/mochitest/aom/test_general.html @@ -84,6 +84,13 @@ is(anode[prop], null, `anode.${prop} was assigned null`); } + function testRelationProp(anode, node, prop) { + is(anode[prop], null, `anode.${prop} should be null`); + anode[prop] = node.accessibleNode; + is(anode[prop], node.accessibleNode, `anode.${prop} was assigned AccessibleNode`); + anode[prop] = null; + is(anode[prop], null, `anode.${prop} was assigned null`); + } // Check that the WebIDL is as expected. function checkImplementation(ifrDoc) { let anode = ifrDoc.accessibleNode; @@ -188,6 +195,12 @@ let adopted_node = anotherDoc.adoptNode(node); is(anode, adopted_node.accessibleNode, "adopting node to another document doesn't change node.accessibleNode"); + const relationProps = ["activeDescendant", "details", "errorMessage"]; + + for (const relationProp of relationProps) { + testRelationProp(anode, node, relationProp); + } + finish(); } diff --git a/dom/webidl/AccessibleNode.webidl b/dom/webidl/AccessibleNode.webidl index 478fe24c94dd..f121c8bad702 100644 --- a/dom/webidl/AccessibleNode.webidl +++ b/dom/webidl/AccessibleNode.webidl @@ -61,6 +61,11 @@ interface AccessibleNode { attribute DOMString? live; attribute DOMString? relevant; + // Other relationships + attribute AccessibleNode? activeDescendant; + attribute AccessibleNode? details; + attribute AccessibleNode? errorMessage; + // Collections. attribute long? colCount; attribute unsigned long? colIndex; From 0e128a3868a11b35d06a6a88fd63bba2d35a80bf Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Mon, 2 Jul 2018 20:37:44 -0700 Subject: [PATCH 15/21] Bug 1470985 - s/PodEqual/ArrayEqual/ from ArrayUtils.h. - r=waldo We can't use memcmp to compare PODs, largely because of undefined padding. The rest of the Pod* functions are fine though, since we're replicating or zeroing PODs. MozReview-Commit-ID: LSspAi8qCWw --- dom/bindings/WebIDLGlobalNameHash.cpp | 3 +- gfx/layers/LayerTreeInvalidation.cpp | 1 + gfx/src/FilterSupport.cpp | 5 +-- gfx/tests/gtest/TestSwizzle.cpp | 30 ++++++++--------- js/src/jit/CacheIRCompiler.cpp | 3 +- js/src/jsapi-tests/testExternalStrings.cpp | 5 ++- js/src/shell/js.cpp | 7 ++-- js/src/util/Text.h | 3 +- js/src/vm/Iteration.cpp | 7 ++-- js/src/vm/JSAtom.cpp | 4 +-- js/src/vm/JSScript.h | 4 +-- js/src/vm/StringType.cpp | 15 +++++---- js/src/vm/Xdr.cpp | 5 +-- js/src/wasm/AsmJS.cpp | 5 +-- js/src/wasm/WasmTypes.cpp | 2 +- js/src/wasm/WasmTypes.h | 3 +- layout/style/nsCSSPropertyIDSet.h | 3 +- mfbt/ArrayUtils.h | 19 +++++++++++ mfbt/PodOperations.h | 34 ++------------------ security/certverifier/ExtendedValidation.cpp | 8 ++--- security/certverifier/TrustOverrideUtils.h | 10 +++--- security/manager/ssl/tests/gtest/MD4Test.cpp | 4 +-- 22 files changed, 89 insertions(+), 91 deletions(-) diff --git a/dom/bindings/WebIDLGlobalNameHash.cpp b/dom/bindings/WebIDLGlobalNameHash.cpp index e67a2c64d0bc..2ca3d3e7091a 100644 --- a/dom/bindings/WebIDLGlobalNameHash.cpp +++ b/dom/bindings/WebIDLGlobalNameHash.cpp @@ -11,6 +11,7 @@ #include "js/Wrapper.h" #include "jsapi.h" #include "jsfriendapi.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/ErrorResult.h" #include "mozilla/HashFunctions.h" #include "mozilla/Maybe.h" @@ -92,7 +93,7 @@ struct WebIDLNameTableEntry : public PLDHashEntryHdr const char* name = WebIDLGlobalNameHash::sNames + mNameOffset; if (aKey->mLatin1String) { - return PodEqual(aKey->mLatin1String, name, aKey->mLength); + return ArrayEqual(aKey->mLatin1String, name, aKey->mLength); } return nsCharTraits::compareASCII(aKey->mTwoBytesString, name, diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index c09487c6abaa..6d6adf1497b0 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -13,6 +13,7 @@ #include "Units.h" // for ParentLayerIntRect #include "gfxRect.h" // for gfxRect #include "gfxUtils.h" // for gfxUtils +#include "mozilla/ArrayUtils.h" // for ArrayEqual #include "mozilla/gfx/BaseSize.h" // for BaseSize #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/mozalloc.h" // for operator new, etc diff --git a/gfx/src/FilterSupport.cpp b/gfx/src/FilterSupport.cpp index f35fea843bd3..c72651cef021 100644 --- a/gfx/src/FilterSupport.cpp +++ b/gfx/src/FilterSupport.cpp @@ -9,6 +9,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Filters.h" #include "mozilla/gfx/Logging.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/PodOperations.h" #include "gfxContext.h" @@ -763,7 +764,7 @@ FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescriptio uint32_t type = atts.GetUint(eColorMatrixType); const nsTArray& values = atts.GetFloats(eColorMatrixValues); if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix)) || - PodEqual(colorMatrix, identityMatrix)) { + ArrayEqual(colorMatrix, identityMatrix)) { RefPtr filter(aSources[0]); return filter.forget(); } @@ -974,7 +975,7 @@ FilterNodeFromPrimitiveDescription(const FilterPrimitiveDescription& aDescriptio // All-zero coefficients sometimes occur in junk filters. if (!filter || (coefficients.Length() == ArrayLength(allZero) && - PodEqual(coefficients.Elements(), allZero, ArrayLength(allZero)))) { + ArrayEqual(coefficients.Elements(), allZero, ArrayLength(allZero)))) { return nullptr; } filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS, diff --git a/gfx/tests/gtest/TestSwizzle.cpp b/gfx/tests/gtest/TestSwizzle.cpp index 28552f38cf9c..1ce7c9cea4d5 100644 --- a/gfx/tests/gtest/TestSwizzle.cpp +++ b/gfx/tests/gtest/TestSwizzle.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gtest/gtest.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/gfx/Swizzle.h" -#include "mozilla/PodOperations.h" using namespace mozilla; using namespace mozilla::gfx; @@ -50,17 +50,17 @@ TEST(Moz2D, PremultiplyData) { PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_bgra)); + EXPECT_TRUE(ArrayEqual(out, check_bgra)); PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(in_bgra), SurfaceFormat::R8G8B8A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_rgba)); + EXPECT_TRUE(ArrayEqual(out, check_rgba)); PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(in_bgra), SurfaceFormat::A8R8G8B8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_argb)); + EXPECT_TRUE(ArrayEqual(out, check_argb)); } TEST(Moz2D, UnpremultiplyData) { @@ -103,17 +103,17 @@ TEST(Moz2D, UnpremultiplyData) { UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_bgra)); + EXPECT_TRUE(ArrayEqual(out, check_bgra)); UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(in_bgra), SurfaceFormat::R8G8B8A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_rgba)); + EXPECT_TRUE(ArrayEqual(out, check_rgba)); UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(in_bgra), SurfaceFormat::A8R8G8B8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_argb)); + EXPECT_TRUE(ArrayEqual(out, check_argb)); } TEST(Moz2D, SwizzleData) { @@ -199,42 +199,42 @@ TEST(Moz2D, SwizzleData) { SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(out), SurfaceFormat::B8G8R8A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_bgra)); + EXPECT_TRUE(ArrayEqual(out, check_bgra)); SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(out), SurfaceFormat::R8G8B8A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_rgba)); + EXPECT_TRUE(ArrayEqual(out, check_rgba)); SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(out), SurfaceFormat::A8R8G8B8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_argb)); + EXPECT_TRUE(ArrayEqual(out, check_argb)); SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out, sizeof(out), SurfaceFormat::R8G8B8X8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out, check_rgbx)); + EXPECT_TRUE(ArrayEqual(out, check_rgbx)); SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out24, sizeof(out24), SurfaceFormat::B8G8R8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out24, check_bgr)); + EXPECT_TRUE(ArrayEqual(out24, check_bgr)); SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out24, sizeof(out24), SurfaceFormat::R8G8B8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out24, check_rgb)); + EXPECT_TRUE(ArrayEqual(out24, check_rgb)); SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out8, sizeof(out8), SurfaceFormat::A8, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out8, check_a)); + EXPECT_TRUE(ArrayEqual(out8, check_a)); SwizzleData(SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::A8R8G8B8 ? check_argb : check_bgra, sizeof(in_bgra), SurfaceFormat::A8R8G8B8_UINT32, reinterpret_cast(out16), sizeof(out16), SurfaceFormat::R5G6B5_UINT16, IntSize(5, 1)); - EXPECT_TRUE(PodEqual(out16, check_16)); + EXPECT_TRUE(ArrayEqual(out16, check_16)); } diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 759b81faff78..0909adb334bf 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -6,6 +6,7 @@ #include "jit/CacheIRCompiler.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/ScopeExit.h" #include @@ -1165,7 +1166,7 @@ CacheIRStubKey::match(const CacheIRStubKey& entry, const CacheIRStubKey::Lookup& if (entry.stubInfo->codeLength() != l.length) return false; - if (!mozilla::PodEqual(entry.stubInfo->code(), l.code, l.length)) + if (!mozilla::ArrayEqual(entry.stubInfo->code(), l.code, l.length)) return false; return true; diff --git a/js/src/jsapi-tests/testExternalStrings.cpp b/js/src/jsapi-tests/testExternalStrings.cpp index bc8478a05793..1e9f1812fe4a 100644 --- a/js/src/jsapi-tests/testExternalStrings.cpp +++ b/js/src/jsapi-tests/testExternalStrings.cpp @@ -3,12 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/ArrayUtils.h" -#include "mozilla/PodOperations.h" #include "jsapi-tests/tests.h" +using mozilla::ArrayEqual; using mozilla::ArrayLength; -using mozilla::PodEqual; static const char16_t arr[] = { 'h', 'i', ',', 'd', 'o', 'n', '\'', 't', ' ', 'd', 'e', 'l', 'e', 't', 'e', ' ', 'm', 'e', '\0' @@ -27,7 +26,7 @@ static const JSStringFinalizer finalizer2 = { finalize_str }; static void finalize_str(const JSStringFinalizer* fin, char16_t* chars) { - if (chars && PodEqual(const_cast(chars), arr, arrlen)) { + if (chars && ArrayEqual(const_cast(chars), arr, arrlen)) { if (fin == &finalizer1) { ++finalized1; } else if (fin == &finalizer2) { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 44f75e4f2176..7383550c01f3 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -127,14 +127,13 @@ using namespace js::shell; using js::shell::RCFile; +using mozilla::ArrayEqual; using mozilla::ArrayLength; using mozilla::Atomic; using mozilla::MakeScopeExit; using mozilla::Maybe; using mozilla::Nothing; using mozilla::NumberEqualsInt32; -using mozilla::PodCopy; -using mozilla::PodEqual; using mozilla::TimeDuration; using mozilla::TimeStamp; @@ -2074,7 +2073,7 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp) return false; } - if (!PodEqual(loadBuffer.begin(), saveBuffer.begin(), loadBuffer.length())) { + if (!ArrayEqual(loadBuffer.begin(), saveBuffer.begin(), loadBuffer.length())) { JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_CACHE_EQ_CONTENT_FAILED); return false; @@ -5582,7 +5581,7 @@ SingleStepCallback(void* arg, jit::Simulator* sim, void* pc) // Only append the stack if it differs from the last stack. if (sc->stacks.empty() || sc->stacks.back().length() != stack.length() || - !PodEqual(sc->stacks.back().begin(), stack.begin(), stack.length())) + !ArrayEqual(sc->stacks.back().begin(), stack.begin(), stack.length())) { if (!sc->stacks.append(std::move(stack))) oomUnsafe.crash("stacks.append"); diff --git a/js/src/util/Text.h b/js/src/util/Text.h index da48d0bbcd51..ff6d0af49f3b 100644 --- a/js/src/util/Text.h +++ b/js/src/util/Text.h @@ -7,6 +7,7 @@ #ifndef util_Text_h #define util_Text_h +#include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/TextUtils.h" @@ -62,7 +63,7 @@ template inline bool EqualChars(const Char1* s1, const Char1* s2, size_t len) { - return mozilla::PodEqual(s1, s2, len); + return mozilla::ArrayEqual(s1, s2, len); } template diff --git a/js/src/vm/Iteration.cpp b/js/src/vm/Iteration.cpp index 4c551917798a..d0b80ae8f7b5 100644 --- a/js/src/vm/Iteration.cpp +++ b/js/src/vm/Iteration.cpp @@ -8,6 +8,7 @@ #include "vm/Iteration.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" #include "mozilla/Maybe.h" @@ -47,10 +48,10 @@ using namespace js; using namespace js::gc; +using mozilla::ArrayEqual; using mozilla::DebugOnly; using mozilla::Maybe; using mozilla::PodCopy; -using mozilla::PodEqual; typedef Rooted RootedPropertyIteratorObject; @@ -793,8 +794,8 @@ IteratorHashPolicy::match(PropertyIteratorObject* obj, const Lookup& lookup) if (ni->guardKey() != lookup.key || ni->guardCount() != lookup.numGuards) return false; - return PodEqual(reinterpret_cast(ni->guardsBegin()), lookup.guards, - ni->guardCount()); + return ArrayEqual(reinterpret_cast(ni->guardsBegin()), lookup.guards, + ni->guardCount()); } static inline bool diff --git a/js/src/vm/JSAtom.cpp b/js/src/vm/JSAtom.cpp index d9659a5b274b..9cf61db4a2f0 100644 --- a/js/src/vm/JSAtom.cpp +++ b/js/src/vm/JSAtom.cpp @@ -96,14 +96,14 @@ js::AtomHasher::match(const AtomStateEntry& entry, const Lookup& lookup) if (key->hasLatin1Chars()) { const Latin1Char* keyChars = key->latin1Chars(lookup.nogc); if (lookup.isLatin1) - return mozilla::PodEqual(keyChars, lookup.latin1Chars, lookup.length); + return mozilla::ArrayEqual(keyChars, lookup.latin1Chars, lookup.length); return EqualChars(keyChars, lookup.twoByteChars, lookup.length); } const char16_t* keyChars = key->twoByteChars(lookup.nogc); if (lookup.isLatin1) return EqualChars(lookup.latin1Chars, keyChars, lookup.length); - return mozilla::PodEqual(keyChars, lookup.twoByteChars, lookup.length); + return mozilla::ArrayEqual(keyChars, lookup.twoByteChars, lookup.length); } inline JSAtom* diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h index 3cd1849745c4..45851b511add 100644 --- a/js/src/vm/JSScript.h +++ b/js/src/vm/JSScript.h @@ -9,10 +9,10 @@ #ifndef vm_JSScript_h #define vm_JSScript_h +#include "mozilla/ArrayUtils.h" #include "mozilla/Atomics.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/PodOperations.h" #include "mozilla/Variant.h" #include "jstypes.h" @@ -886,7 +886,7 @@ struct ScriptBytecodeHasher return false; if (entry->numNotes() != data->numNotes()) return false; - return mozilla::PodEqual(entry->data(), data->data(), data->dataLength()); + return mozilla::ArrayEqual(entry->data(), data->data(), data->dataLength()); } }; diff --git a/js/src/vm/StringType.cpp b/js/src/vm/StringType.cpp index 93b12dfe5689..0c743092910b 100644 --- a/js/src/vm/StringType.cpp +++ b/js/src/vm/StringType.cpp @@ -6,6 +6,7 @@ #include "vm/StringType-inl.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/FloatingPoint.h" #include "mozilla/HashFunctions.h" #include "mozilla/MathAlgorithms.h" @@ -30,11 +31,11 @@ using namespace js; +using mozilla::ArrayEqual; using mozilla::IsAsciiDigit; using mozilla::IsNegativeZero; using mozilla::IsSame; using mozilla::PodCopy; -using mozilla::PodEqual; using mozilla::RangedPtr; using mozilla::RoundUpPow2; using mozilla::Unused; @@ -846,13 +847,13 @@ js::EqualChars(JSLinearString* str1, JSLinearString* str2) AutoCheckCannotGC nogc; if (str1->hasTwoByteChars()) { if (str2->hasTwoByteChars()) - return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len); + return ArrayEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len); return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len); } if (str2->hasLatin1Chars()) - return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len); + return ArrayEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len); return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len); } @@ -868,14 +869,14 @@ js::HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start) if (text->hasLatin1Chars()) { const Latin1Char* textChars = text->latin1Chars(nogc) + start; if (pat->hasLatin1Chars()) - return PodEqual(textChars, pat->latin1Chars(nogc), patLen); + return ArrayEqual(textChars, pat->latin1Chars(nogc), patLen); return EqualChars(textChars, pat->twoByteChars(nogc), patLen); } const char16_t* textChars = text->twoByteChars(nogc) + start; if (pat->hasTwoByteChars()) - return PodEqual(textChars, pat->twoByteChars(nogc), patLen); + return ArrayEqual(textChars, pat->twoByteChars(nogc), patLen); return EqualChars(pat->latin1Chars(nogc), textChars, patLen); } @@ -991,7 +992,7 @@ js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes) AutoCheckCannotGC nogc; return str->hasLatin1Chars() - ? PodEqual(latin1, str->latin1Chars(nogc), length) + ? ArrayEqual(latin1, str->latin1Chars(nogc), length) : EqualChars(latin1, str->twoByteChars(nogc), length); } @@ -1757,7 +1758,7 @@ ExternalStringCache::lookup(const char16_t* chars, size_t len) const // Compare the chars. Don't do this for long strings as it will be // faster to allocate a new external string. static const size_t MaxLengthForCharComparison = 100; - if (len <= MaxLengthForCharComparison && PodEqual(chars, strChars, len)) + if (len <= MaxLengthForCharComparison && ArrayEqual(chars, strChars, len)) return str; } diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp index aab85e00a37b..6da08dae70bc 100644 --- a/js/src/vm/Xdr.cpp +++ b/js/src/vm/Xdr.cpp @@ -6,6 +6,7 @@ #include "vm/Xdr.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/PodOperations.h" #include "mozilla/ScopeExit.h" @@ -21,7 +22,7 @@ #include "vm/TraceLogging.h" using namespace js; -using mozilla::PodEqual; +using mozilla::ArrayEqual; template LifoAlloc& @@ -116,7 +117,7 @@ VersionCheck(XDRState* xdr) MOZ_TRY(xdr->codeBytes(decodedBuildId.begin(), buildIdLength)); // We do not provide binary compatibility with older scripts. - if (!PodEqual(decodedBuildId.begin(), buildId.begin(), buildIdLength)) + if (!ArrayEqual(decodedBuildId.begin(), buildId.begin(), buildIdLength)) return xdr->fail(JS::TranscodeResult_Failure_BadBuildId); } diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 80a37395cf59..49e6ca134a34 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -18,6 +18,7 @@ #include "wasm/AsmJS.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/Compression.h" #include "mozilla/MathAlgorithms.h" @@ -58,6 +59,7 @@ using namespace js::frontend; using namespace js::jit; using namespace js::wasm; +using mozilla::ArrayEqual; using mozilla::CeilingLog2; using mozilla::Compression::LZ4; using mozilla::HashGeneric; @@ -65,7 +67,6 @@ using mozilla::IsNaN; using mozilla::IsNegativeZero; using mozilla::IsPositiveZero; using mozilla::IsPowerOfTwo; -using mozilla::PodEqual; using mozilla::PodZero; using mozilla::PositiveInfinity; using mozilla::Unused; @@ -6827,7 +6828,7 @@ class ModuleCharsForLookup : ModuleChars MOZ_ASSERT(parseLimit >= parseBegin); if (uint32_t(parseLimit - parseBegin) < chars_.length()) return false; - if (!PodEqual(chars_.begin(), parseBegin, chars_.length())) + if (!ArrayEqual(chars_.begin(), parseBegin, chars_.length())) return false; if (isFunCtor_ != parser.pc->isStandaloneFunctionBody()) return false; diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index a44d7b8c7561..4e0fa81cbb05 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -541,7 +541,7 @@ Assumptions::operator==(const Assumptions& rhs) const { return cpuId == rhs.cpuId && buildId.length() == rhs.buildId.length() && - PodEqual(buildId.begin(), rhs.buildId.begin(), buildId.length()); + ArrayEqual(buildId.begin(), rhs.buildId.begin(), buildId.length()); } size_t diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 3df417468f2a..74c093b05a9b 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -20,6 +20,7 @@ #define wasm_types_h #include "mozilla/Alignment.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/Atomics.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/HashFunctions.h" @@ -75,6 +76,7 @@ typedef Rooted RootedWasmGlobalObject; namespace wasm { +using mozilla::ArrayEqual; using mozilla::Atomic; using mozilla::DebugOnly; using mozilla::EnumeratedArray; @@ -83,7 +85,6 @@ using mozilla::MallocSizeOf; using mozilla::Nothing; using mozilla::PodZero; using mozilla::PodCopy; -using mozilla::PodEqual; using mozilla::Some; using mozilla::Unused; diff --git a/layout/style/nsCSSPropertyIDSet.h b/layout/style/nsCSSPropertyIDSet.h index a120bf5add85..4c31023692ae 100644 --- a/layout/style/nsCSSPropertyIDSet.h +++ b/layout/style/nsCSSPropertyIDSet.h @@ -8,7 +8,6 @@ #define nsCSSPropertyIDSet_h__ #include "mozilla/ArrayUtils.h" -#include "mozilla/PodOperations.h" #include "nsCSSPropertyID.h" #include // for CHAR_BIT @@ -64,7 +63,7 @@ public: } bool Equals(const nsCSSPropertyIDSet& aOther) const { - return mozilla::PodEqual(mProperties, aOther.mProperties); + return mozilla::ArrayEqual(mProperties, aOther.mProperties); } bool IsEmpty() const { diff --git a/mfbt/ArrayUtils.h b/mfbt/ArrayUtils.h index 50236ccb75e7..fd33f832c367 100644 --- a/mfbt/ArrayUtils.h +++ b/mfbt/ArrayUtils.h @@ -14,6 +14,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include #include #ifdef __cplusplus @@ -98,6 +99,24 @@ ArrayEnd(const Array& aArr) return &aArr[0] + ArrayLength(aArr); } +/** + * std::equal has subpar ergonomics. + */ + +template +bool +ArrayEqual(const T (&a)[N], const U (&b)[N]) +{ + return std::equal(a, a + N, b); +} + +template +bool +ArrayEqual(const T* const a, const U* const b, const size_t n) +{ + return std::equal(a, a + n, b); +} + namespace detail { template #include #include @@ -158,38 +159,9 @@ PodMove(T* aDst, const T* aSrc, size_t aNElem) } /** - * Determine whether the |len| elements at |one| are memory-identical to the - * |len| elements at |two|. + * Looking for a PodEqual? Use ArrayEqual from ArrayUtils.h. + * Note that we *cannot* use memcmp for this, due to padding bytes, etc.. */ -template -static MOZ_ALWAYS_INLINE bool -PodEqual(const T* one, const T* two, size_t len) -{ - if (len < 128) { - const T* p1end = one + len; - const T* p1 = one; - const T* p2 = two; - for (; p1 < p1end; p1++, p2++) { - if (*p1 != *p2) { - return false; - } - } - return true; - } - - return !memcmp(one, two, len * sizeof(T)); -} - -/* - * Determine whether the |N| elements at |one| are memory-identical to the - * |N| elements at |two|. - */ -template -static MOZ_ALWAYS_INLINE bool -PodEqual(const T (&one)[N], const T (&two)[N]) -{ - return PodEqual(one, two, N); -} } // namespace mozilla diff --git a/security/certverifier/ExtendedValidation.cpp b/security/certverifier/ExtendedValidation.cpp index b13d75e03d5a..ee876886b41d 100644 --- a/security/certverifier/ExtendedValidation.cpp +++ b/security/certverifier/ExtendedValidation.cpp @@ -1102,17 +1102,17 @@ CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert, // This check ensures that only the specific roots we approve for EV get // that status, and not certs (roots or otherwise) that happen to have an // OID that's already been approved for EV. - if (!PodEqual(fingerprint, entry.sha256Fingerprint)) { + if (!ArrayEqual(fingerprint, entry.sha256Fingerprint)) { continue; } if (cabforumOIDData && cabforumOIDData->oid.len == policy.numBytes && - PodEqual(cabforumOIDData->oid.data, policy.bytes, policy.numBytes)) { + ArrayEqual(cabforumOIDData->oid.data, policy.bytes, policy.numBytes)) { return true; } const SECOidData* oidData = SECOID_FindOIDByTag(sEVInfoOIDTags[i]); if (oidData && oidData->oid.len == policy.numBytes && - PodEqual(oidData->oid.data, policy.bytes, policy.numBytes)) { + ArrayEqual(oidData->oid.data, policy.bytes, policy.numBytes)) { return true; } } @@ -1188,7 +1188,7 @@ LoadExtendedValidationInfo() if (srv != SECSuccess) { return NS_ERROR_FAILURE; } - bool same = PodEqual(certFingerprint, entry.sha256Fingerprint); + bool same = ArrayEqual(certFingerprint, entry.sha256Fingerprint); MOZ_ASSERT(same, "EV root fingerprint mismatch"); if (!same) { return NS_ERROR_FAILURE; diff --git a/security/certverifier/TrustOverrideUtils.h b/security/certverifier/TrustOverrideUtils.h index 9737f09ec960..7244fc337ff9 100644 --- a/security/certverifier/TrustOverrideUtils.h +++ b/security/certverifier/TrustOverrideUtils.h @@ -9,7 +9,7 @@ #include "nsNSSCertificate.h" #include "nsNSSCertValidity.h" -#include "mozilla/PodOperations.h" +#include "mozilla/ArrayUtils.h" using namespace mozilla; @@ -29,7 +29,7 @@ CertDNIsInList(const CERTCertificate* aCert, const DataAndLength (&aDnList)[T]) for (auto& dn: aDnList) { if (aCert->derSubject.len == dn.len && - mozilla::PodEqual(aCert->derSubject.data, dn.data, dn.len)) { + mozilla::ArrayEqual(aCert->derSubject.data, dn.data, dn.len)) { return true; } } @@ -47,7 +47,7 @@ CertSPKIIsInList(const CERTCertificate* aCert, const DataAndLength (&aSpkiList)[ for (auto& spki: aSpkiList) { if (aCert->derPublicKey.len == spki.len && - mozilla::PodEqual(aCert->derPublicKey.data, spki.data, spki.len)) { + mozilla::ArrayEqual(aCert->derPublicKey.data, spki.data, spki.len)) { return true; } } @@ -64,9 +64,9 @@ CertMatchesStaticData(const CERTCertificate* cert, return false; } return cert->derSubject.len == T && - mozilla::PodEqual(cert->derSubject.data, subject, T) && + mozilla::ArrayEqual(cert->derSubject.data, subject, T) && cert->derPublicKey.len == R && - mozilla::PodEqual(cert->derPublicKey.data, spki, R); + mozilla::ArrayEqual(cert->derPublicKey.data, spki, R); } // Implements the graduated Symantec distrust algorithm from Bug 1409257. diff --git a/security/manager/ssl/tests/gtest/MD4Test.cpp b/security/manager/ssl/tests/gtest/MD4Test.cpp index 1b635a757b03..be908959a101 100644 --- a/security/manager/ssl/tests/gtest/MD4Test.cpp +++ b/security/manager/ssl/tests/gtest/MD4Test.cpp @@ -8,8 +8,8 @@ #include "gtest/gtest.h" #include "md4.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/Casting.h" -#include "mozilla/PodOperations.h" struct RFC1320TestParams { @@ -68,7 +68,7 @@ TEST_P(psm_MD4, RFC1320TestValues) uint8_t actualHash[16]; md4sum(mozilla::BitwiseCast(params.data), strlen(params.data), actualHash); - EXPECT_TRUE(mozilla::PodEqual(actualHash, params.expectedHash)) + EXPECT_TRUE(mozilla::ArrayEqual(actualHash, params.expectedHash)) << "MD4 hashes aren't equal for input: '" << params.data << "'"; } From 080d6f25d1d270d7d7c61ef2e94b5a5f06c47cf7 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 5 Jul 2018 15:10:15 -0700 Subject: [PATCH 16/21] Bug 1470985 - EqualChars should just call ArrayEqual. - r=waldo MozReview-Commit-ID: Cmj4KwGidNv --- js/src/util/Text.h | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/js/src/util/Text.h b/js/src/util/Text.h index ff6d0af49f3b..d014d81c5cc8 100644 --- a/js/src/util/Text.h +++ b/js/src/util/Text.h @@ -55,26 +55,11 @@ namespace js { class StringBuffer; -template -inline bool -EqualChars(const Char1* s1, const Char2* s2, size_t len); - -template -inline bool -EqualChars(const Char1* s1, const Char1* s2, size_t len) -{ - return mozilla::ArrayEqual(s1, s2, len); -} - template inline bool EqualChars(const Char1* s1, const Char2* s2, size_t len) { - for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) { - if (*s1 != *s2) - return false; - } - return true; + return mozilla::ArrayEqual(s1, s2, len); } // Return less than, equal to, or greater than zero depending on whether From b4e6d208cfc92bb097634d6f7979d3a633107885 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Thu, 5 Jul 2018 15:15:22 -0700 Subject: [PATCH 17/21] Bug 1470985 - const_cast no longer necessary with ArrayEqual. - r=waldo MozReview-Commit-ID: 5iKtE8cxs6c --- js/src/jsapi-tests/testExternalStrings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsapi-tests/testExternalStrings.cpp b/js/src/jsapi-tests/testExternalStrings.cpp index 1e9f1812fe4a..c1ea888d4ff4 100644 --- a/js/src/jsapi-tests/testExternalStrings.cpp +++ b/js/src/jsapi-tests/testExternalStrings.cpp @@ -26,7 +26,7 @@ static const JSStringFinalizer finalizer2 = { finalize_str }; static void finalize_str(const JSStringFinalizer* fin, char16_t* chars) { - if (chars && ArrayEqual(const_cast(chars), arr, arrlen)) { + if (chars && ArrayEqual(chars, arr, arrlen)) { if (fin == &finalizer1) { ++finalized1; } else if (fin == &finalizer2) { From c7c03f661b52a3c221e377c163b37750d3e09e0c Mon Sep 17 00:00:00 2001 From: Mitch Ament Date: Fri, 27 Jul 2018 14:58:46 -0400 Subject: [PATCH 18/21] Bug 1477335 - Replace reference member AsyncPanZoomController::mFrameMetrics with getter functions which return mScrollMetadata.mMetrics. r=botond This fixes a const correctness loophole. MozReview-Commit-ID: I8yM74OAr8m --HG-- extra : rebase_source : 8aeea554bfeff7efe03cd9a00539a6100e040b8a --- gfx/layers/FrameMetrics.h | 2 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 295 +++++++++--------- gfx/layers/apz/src/AsyncPanZoomController.h | 28 +- gfx/layers/apz/src/Axis.h | 2 +- gfx/layers/apz/test/gtest/APZTestCommon.h | 7 +- 5 files changed, 174 insertions(+), 160 deletions(-) diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index ca3ef5e6c182..5e648330c851 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -513,7 +513,7 @@ public: // contents start at rightside also cause their horizontal scrollbars, if any, // initially start at rightside. So we can also learn about the initial side // of the horizontal scrollbar for the frame by calling this function. - bool IsHorizontalContentRightToLeft() { + bool IsHorizontalContentRightToLeft() const { return mScrollableRect.x < 0; } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index ba4e6af5989e..1a84b4a231b6 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -821,7 +821,6 @@ AsyncPanZoomController::AsyncPanZoomController(LayersId aLayersId, mRefPtrMonitor("RefPtrMonitor"), // mTreeManager must be initialized before GetFrameTime() is called mTreeManager(aTreeManager), - mFrameMetrics(mScrollMetadata.GetMetrics()), mRecursiveMutex("AsyncPanZoomController"), mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()), mX(this), @@ -829,8 +828,8 @@ AsyncPanZoomController::AsyncPanZoomController(LayersId aLayersId, mPanDirRestricted(false), mPinchLocked(false), mZoomConstraints(false, false, - mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1), - mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1)), + Metrics().GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1), + Metrics().GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1)), mLastSampleTime(GetFrameTime()), mLastCheckerboardReport(GetFrameTime()), mOverscrollEffect(MakeUnique(*this)), @@ -899,7 +898,7 @@ AsyncPanZoomController::Destroy() // Only send the release message if the SharedFrameMetrics has been created. if (mMetricsSharingController && mSharedFrameMetricsBuffer) { - Unused << mMetricsSharingController->StopSharingMetrics(mFrameMetrics.GetScrollId(), mAPZCId); + Unused << mMetricsSharingController->StopSharingMetrics(Metrics().GetScrollId(), mAPZCId); } { // scope the lock @@ -1063,16 +1062,16 @@ nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent, APZC_LOG("%p scrollbar dragged to %f percent\n", this, scrollPercent); CSSCoord minScrollPosition = - GetAxisStart(direction, mFrameMetrics.GetScrollableRect().TopLeft()); + GetAxisStart(direction, Metrics().GetScrollableRect().TopLeft()); CSSCoord maxScrollPosition = - GetAxisStart(direction, mFrameMetrics.GetScrollableRect().BottomRight()) - - GetAxisLength(direction, mFrameMetrics.CalculateCompositionBoundsInCssPixelsOfSurroundingContent()); + GetAxisStart(direction, Metrics().GetScrollableRect().BottomRight()) - + GetAxisLength(direction, Metrics().CalculateCompositionBoundsInCssPixelsOfSurroundingContent()); CSSCoord scrollPosition = minScrollPosition + (scrollPercent * (maxScrollPosition - minScrollPosition)); scrollPosition = std::max(scrollPosition, minScrollPosition); scrollPosition = std::min(scrollPosition, maxScrollPosition); - CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset(); + CSSPoint scrollOffset = Metrics().GetScrollOffset(); if (direction == ScrollDirection::eHorizontal) { scrollOffset.x = scrollPosition; } else { @@ -1463,7 +1462,7 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve SetState(PINCHING); mX.SetVelocity(0); mY.SetVelocity(0); - mLastZoomFocus = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft(); + mLastZoomFocus = aEvent.mLocalFocusPoint - Metrics().GetCompositionBounds().TopLeft(); return nsEventStatus_eConsumeNoDefault; } @@ -1484,7 +1483,7 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { { RecursiveMutexAutoLock lock(mRecursiveMutex); - focusPoint = aEvent.mLocalFocusPoint - mFrameMetrics.GetCompositionBounds().TopLeft(); + focusPoint = aEvent.mLocalFocusPoint - Metrics().GetCompositionBounds().TopLeft(); focusChange = mLastZoomFocus - focusPoint; mLastZoomFocus = focusPoint; } @@ -1517,14 +1516,14 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { // would have to be adjusted (as e.g. it would no longer be valid to take // the minimum or maximum of the ratios of the widths and heights of the // page rect and the composition bounds). - MOZ_ASSERT(mFrameMetrics.IsRootContent()); - MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame()); + MOZ_ASSERT(Metrics().IsRootContent()); + MOZ_ASSERT(Metrics().GetZoom().AreScalesSame()); { RecursiveMutexAutoLock lock(mRecursiveMutex); - CSSToParentLayerScale userZoom = mFrameMetrics.GetZoom().ToScaleFactor(); - CSSPoint cssFocusPoint = focusPoint / mFrameMetrics.GetZoom(); + CSSToParentLayerScale userZoom = Metrics().GetZoom().ToScaleFactor(); + CSSPoint cssFocusPoint = focusPoint / Metrics().GetZoom(); // If displacing by the change in focus point will take us off page bounds, // then reduce the displacement such that it doesn't. @@ -1556,9 +1555,9 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) { CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom; CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom; realMinZoom.scale = std::max(realMinZoom.scale, - mFrameMetrics.GetCompositionBounds().Width() / mFrameMetrics.GetScrollableRect().Width()); + Metrics().GetCompositionBounds().Width() / Metrics().GetScrollableRect().Width()); realMinZoom.scale = std::max(realMinZoom.scale, - mFrameMetrics.GetCompositionBounds().Height() / mFrameMetrics.GetScrollableRect().Height()); + Metrics().GetCompositionBounds().Height() / Metrics().GetScrollableRect().Height()); if (realMaxZoom < realMinZoom) { realMaxZoom = realMinZoom; } @@ -1763,13 +1762,13 @@ AsyncPanZoomController::ConvertScrollbarPoint(const ParentLayerPoint& aScrollbar RecursiveMutexAutoLock lock(mRecursiveMutex); // First, get it into the right coordinate space. - CSSPoint scrollbarPoint = aScrollbarPoint / mFrameMetrics.GetZoom(); + CSSPoint scrollbarPoint = aScrollbarPoint / Metrics().GetZoom(); // The scrollbar can be transformed with the frame but the pres shell // resolution is only applied to the scroll frame. - scrollbarPoint = scrollbarPoint * mFrameMetrics.GetPresShellResolution(); + scrollbarPoint = scrollbarPoint * Metrics().GetPresShellResolution(); // Now, get it to be relative to the beginning of the scroll track. - CSSRect cssCompositionBound = mFrameMetrics.CalculateCompositionBoundsInCssPixelsOfSurroundingContent(); + CSSRect cssCompositionBound = Metrics().CalculateCompositionBoundsInCssPixelsOfSurroundingContent(); return GetAxisStart(*aThumbData.mDirection, scrollbarPoint) - GetAxisStart(*aThumbData.mDirection, cssCompositionBound) - aThumbData.mScrollTrackStart; @@ -1808,9 +1807,9 @@ AsyncPanZoomController::GetScrollWheelDelta(const ScrollWheelInput& aEvent, LayoutDeviceIntSize scrollAmountLD = mScrollMetadata.GetLineScrollAmount(); LayoutDeviceIntSize pageScrollSizeLD = mScrollMetadata.GetPageScrollAmount(); scrollAmount = scrollAmountLD / - mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom(); + Metrics().GetDevPixelsPerCSSPixel() * Metrics().GetZoom(); pageScrollSize = pageScrollSizeLD / - mFrameMetrics.GetDevPixelsPerCSSPixel() * mFrameMetrics.GetZoom(); + Metrics().GetDevPixelsPerCSSPixel() * Metrics().GetZoom(); } ParentLayerPoint delta; @@ -1897,8 +1896,8 @@ AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) // CallDispatchScroll interprets the start and end points as the start and // end of a touch scroll so they need to be reversed. - ParentLayerPoint startPoint = destination * mFrameMetrics.GetZoom(); - ParentLayerPoint endPoint = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom(); + ParentLayerPoint startPoint = destination * Metrics().GetZoom(); + ParentLayerPoint endPoint = Metrics().GetScrollOffset() * Metrics().GetZoom(); ParentLayerPoint delta = endPoint - startPoint; ScreenPoint distance = ToScreenCoordinates( @@ -1935,7 +1934,7 @@ AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) CancelAnimation(); SetState(KEYBOARD_SCROLL); - nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); + nsPoint initialPosition = CSSPoint::ToAppUnits(Metrics().GetScrollOffset()); StartAnimation(new KeyboardScrollAnimation(*this, initialPosition, aEvent.mAction.mType)); } @@ -1944,7 +1943,7 @@ AsyncPanZoomController::OnKeyboard(const KeyboardInput& aEvent) nsPoint velocity = CSSPoint::ToAppUnits( ParentLayerPoint(mX.GetVelocity() * 1000.0f, mY.GetVelocity() * 1000.0f) / - mFrameMetrics.GetZoom()); + Metrics().GetZoom()); KeyboardScrollAnimation* animation = mAnimation->AsKeyboardScrollAnimation(); MOZ_ASSERT(animation); @@ -1969,9 +1968,9 @@ AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aActi RecursiveMutexAutoLock lock(mRecursiveMutex); lineScrollSize = mScrollMetadata.GetLineScrollAmount() / - mFrameMetrics.GetDevPixelsPerCSSPixel(); + Metrics().GetDevPixelsPerCSSPixel(); pageScrollSize = mScrollMetadata.GetPageScrollAmount() / - mFrameMetrics.GetDevPixelsPerCSSPixel(); + Metrics().GetDevPixelsPerCSSPixel(); if (mState == WHEEL_SCROLL) { scrollOffset = mAnimation->AsWheelScrollAnimation()->GetDestination(); @@ -1980,10 +1979,10 @@ AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aActi } else if (mState == KEYBOARD_SCROLL) { scrollOffset = mAnimation->AsKeyboardScrollAnimation()->GetDestination(); } else { - scrollOffset = mFrameMetrics.GetScrollOffset(); + scrollOffset = Metrics().GetScrollOffset(); } - scrollRect = mFrameMetrics.GetScrollableRect(); + scrollRect = Metrics().GetScrollableRect(); } // Calculate the scroll destination based off of the scroll type and direction @@ -2140,7 +2139,7 @@ AsyncPanZoomController::IsContentOfHonouredTargetRightToLeft( return mScrollMetadata.IsAutoDirRootContentRTL(); } RecursiveMutexAutoLock lock(mRecursiveMutex); - return mFrameMetrics.IsHorizontalContentRightToLeft(); + return Metrics().IsHorizontalContentRightToLeft(); } bool @@ -2237,7 +2236,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve gfxPrefs::MouseScrollTestingEnabled()) { if (RefPtr controller = GetGeckoContentController()) { controller->NotifyMozMouseScrollEvent( - mFrameMetrics.GetScrollId(), + Metrics().GetScrollId(), NS_LITERAL_STRING("MozMouseScrollFailed")); } } @@ -2259,7 +2258,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve // Wheel events from "clicky" mouse wheels trigger scroll snapping to the // next snap point. Check for this, and adjust the delta to take into // account the snap point. - CSSPoint startPosition = mFrameMetrics.GetScrollOffset(); + CSSPoint startPosition = Metrics().GetScrollOffset(); MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition); ScreenPoint distance = ToScreenCoordinates( @@ -2292,7 +2291,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve RecursiveMutexAutoLock lock(mRecursiveMutex); // Perform scroll snapping if appropriate. - CSSPoint startPosition = mFrameMetrics.GetScrollOffset(); + CSSPoint startPosition = Metrics().GetScrollOffset(); // If we're already in a wheel scroll or smooth scroll animation, // the delta is applied to its destination, not to the current // scroll position. Take this into account when finding a snap point. @@ -2317,13 +2316,13 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve CancelAnimation(); SetState(WHEEL_SCROLL); - nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); + nsPoint initialPosition = CSSPoint::ToAppUnits(Metrics().GetScrollOffset()); StartAnimation(new WheelScrollAnimation( *this, initialPosition, aEvent.mDeltaType)); } nsPoint deltaInAppUnits = - CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom()); + CSSPoint::ToAppUnits(delta / Metrics().GetZoom()); // Convert velocity from ParentLayerPoints/ms to ParentLayerPoints/s and // then to appunits/second. @@ -2331,7 +2330,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve CSSPoint::ToAppUnits( ParentLayerPoint(mX.GetVelocity() * 1000.0f, mY.GetVelocity() * 1000.0f) / - mFrameMetrics.GetZoom()); + Metrics().GetZoom()); WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation(); animation->UpdateDelta(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y)); @@ -2350,7 +2349,7 @@ AsyncPanZoomController::NotifyMozMouseScrollEvent(const nsString& aString) const return; } - controller->NotifyMozMouseScrollEvent(mFrameMetrics.GetScrollId(), aString); + controller->NotifyMozMouseScrollEvent(Metrics().GetScrollId(), aString); } nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) { @@ -2935,7 +2934,7 @@ bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint, } if (!IsZero(adjustedDisplacement)) { - ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom()); + ScrollBy(adjustedDisplacement / Metrics().GetZoom()); if (InputBlockState* block = GetCurrentInputBlock()) { #if defined(MOZ_WIDGET_ANDROID) if (block->AsTouchBlock() && (block->GetScrolledApzc() != this) && IsRootContent()) { @@ -3155,14 +3154,14 @@ void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination) { } else { CancelAnimation(); SetState(SMOOTH_SCROLL); - nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); + nsPoint initialPosition = CSSPoint::ToAppUnits(Metrics().GetScrollOffset()); // Convert velocity from ParentLayerPoints/ms to ParentLayerPoints/s and // then to appunits/second. nsPoint initialVelocity = CSSPoint::ToAppUnits( ParentLayerPoint(mX.GetVelocity() * 1000.0f, mY.GetVelocity() * 1000.0f) / - mFrameMetrics.GetZoom()); + Metrics().GetZoom()); nsPoint destination = CSSPoint::ToAppUnits(aDestination); @@ -3309,13 +3308,13 @@ void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShi RecursiveMutexAutoLock lock(mRecursiveMutex); CSSPoint adjustment = ViewAs(aShift, PixelCastJustification::ScreenIsParentLayerForRoot) - / mFrameMetrics.GetZoom(); + / Metrics().GetZoom(); APZC_LOG("%p adjusting scroll position by %s for surface shift\n", this, Stringify(adjustment).c_str()); - CSSRect scrollRange = mFrameMetrics.CalculateScrollRange(); - // Apply shift to mFrameMetrics.mScrollOffset. + CSSRect scrollRange = Metrics().CalculateScrollRange(); + // Apply shift to Metrics().mScrollOffset. SetScrollOffset(scrollRange.ClampPoint( - mFrameMetrics.GetScrollOffset() + adjustment)); + Metrics().GetScrollOffset() + adjustment)); // Apply shift to mCompositedScrollOffset, since the dynamic toolbar expects // the shift to take effect right away, without the usual frame delay. mCompositedScrollOffset = scrollRange.ClampPoint( @@ -3325,36 +3324,36 @@ void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShi } void AsyncPanZoomController::SetScrollOffset(const CSSPoint& aOffset) { - mFrameMetrics.SetScrollOffset(aOffset); - mFrameMetrics.RecalculateViewportOffset(); + Metrics().SetScrollOffset(aOffset); + Metrics().RecalculateViewportOffset(); } void AsyncPanZoomController::ClampAndSetScrollOffset(const CSSPoint& aOffset) { - mFrameMetrics.ClampAndSetScrollOffset(aOffset); - mFrameMetrics.RecalculateViewportOffset(); + Metrics().ClampAndSetScrollOffset(aOffset); + Metrics().RecalculateViewportOffset(); } void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) { - SetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset); + SetScrollOffset(Metrics().GetScrollOffset() + aOffset); } void AsyncPanZoomController::ScrollByAndClamp(const CSSPoint& aOffset) { - ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset); + ClampAndSetScrollOffset(Metrics().GetScrollOffset() + aOffset); } void AsyncPanZoomController::CopyScrollInfoFrom(const FrameMetrics& aFrameMetrics) { - mFrameMetrics.CopyScrollInfoFrom(aFrameMetrics); - mFrameMetrics.RecalculateViewportOffset(); + Metrics().CopyScrollInfoFrom(aFrameMetrics); + Metrics().RecalculateViewportOffset(); } void AsyncPanZoomController::ScaleWithFocus(float aScale, const CSSPoint& aFocus) { - mFrameMetrics.ZoomBy(aScale); + Metrics().ZoomBy(aScale); // We want to adjust the scroll offset such that the CSS point represented by aFocus remains // at the same position on the screen before and after the change in zoom. The below code // accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an // in-depth explanation of how. - SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale)); + SetScrollOffset((Metrics().GetScrollOffset() + aFocus) - (aFocus / aScale)); } /** @@ -3549,7 +3548,7 @@ bool AsyncPanZoomController::IsPannable() const { bool AsyncPanZoomController::IsScrollInfoLayer() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - return mFrameMetrics.IsScrollInfoLayer(); + return Metrics().IsScrollInfoLayer(); } int32_t AsyncPanZoomController::GetLastTouchIdentifier() const { @@ -3561,7 +3560,7 @@ void AsyncPanZoomController::RequestContentRepaint(bool aUserAction) { // Reinvoke this method on the repaint thread if it's not there already. It's // important to do this before the call to CalculatePendingDisplayPort, so // that CalculatePendingDisplayPort uses the most recent available version of - // mFrameMetrics, just before the paint request is dispatched to content. + // Metrics(). just before the paint request is dispatched to content. RefPtr controller = GetGeckoContentController(); if (!controller) { return; @@ -3582,11 +3581,11 @@ void AsyncPanZoomController::RequestContentRepaint(bool aUserAction) { RecursiveMutexAutoLock lock(mRecursiveMutex); ParentLayerPoint velocity = GetVelocityVector(); - mFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(mFrameMetrics, velocity)); - mFrameMetrics.SetUseDisplayPortMargins(true); - mFrameMetrics.SetPaintRequestTime(TimeStamp::Now()); - mFrameMetrics.SetRepaintDrivenByUserAction(aUserAction); - RequestContentRepaint(mFrameMetrics, velocity); + Metrics().SetDisplayPortMargins(CalculatePendingDisplayPort(Metrics(), velocity)); + Metrics().SetUseDisplayPortMargins(true); + Metrics().SetPaintRequestTime(TimeStamp::Now()); + Metrics().SetRepaintDrivenByUserAction(aUserAction); + RequestContentRepaint(Metrics(), velocity); } /*static*/ CSSRect @@ -3683,7 +3682,7 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime, mLastSampleTime = aSampleTime; if (mAnimation) { - bool continueAnimation = mAnimation->Sample(mFrameMetrics, sampleTimeDelta); + bool continueAnimation = mAnimation->Sample(Metrics(), sampleTimeDelta); bool wantsRepaints = mAnimation->WantsRepaints(); *aOutDeferredTasks = mAnimation->TakeDeferredTasks(); if (!continueAnimation) { @@ -3749,8 +3748,8 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime) if (mCheckerboardEvent) { mCheckerboardEvent->UpdateRendertraceProperty( CheckerboardEvent::UserVisible, - CSSRect(mFrameMetrics.GetScrollOffset(), - mFrameMetrics.CalculateCompositedSizeInCssPixels())); + CSSRect(Metrics().GetScrollOffset(), + Metrics().CalculateCompositedSizeInCssPixels())); } } } @@ -3771,7 +3770,7 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime) CSSRect AsyncPanZoomController::GetCurrentAsyncLayoutViewport(AsyncTransformConsumer aMode) const { RecursiveMutexAutoLock lock(mRecursiveMutex); - MOZ_ASSERT(mFrameMetrics.IsRootContent(), + MOZ_ASSERT(Metrics().IsRootContent(), "Only the root content APZC has a layout viewport"); if (aMode == eForCompositing && mScrollMetadata.IsApzForceDisabled()) { return mLastContentPaintMetrics.GetViewport(); @@ -3844,7 +3843,7 @@ AsyncPanZoomController::GetCurrentAsyncTransform(AsyncTransformConsumer aMode) c * effectiveZoom * mTestAsyncZoom.scale; LayerToParentLayerScale compositedAsyncZoom = - (effectiveZoom / mFrameMetrics.LayersPixelsPerCSSPixel()).ToScaleFactor(); + (effectiveZoom / Metrics().LayersPixelsPerCSSPixel()).ToScaleFactor(); return AsyncTransform( LayerToParentLayerScale(compositedAsyncZoom.scale * mTestAsyncZoom.scale), -translation); @@ -3856,7 +3855,7 @@ AsyncPanZoomController::GetEffectiveLayoutViewport(AsyncTransformConsumer aMode) if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) { return mCompositedLayoutViewport; } - return mFrameMetrics.GetViewport(); + return Metrics().GetViewport(); } CSSPoint @@ -3865,7 +3864,7 @@ AsyncPanZoomController::GetEffectiveScrollOffset(AsyncTransformConsumer aMode) c if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) { return mCompositedScrollOffset; } - return mFrameMetrics.GetScrollOffset(); + return Metrics().GetScrollOffset(); } CSSToParentLayerScale2D @@ -3874,18 +3873,18 @@ AsyncPanZoomController::GetEffectiveZoom(AsyncTransformConsumer aMode) const if (gfxPrefs::APZFrameDelayEnabled() && aMode == eForCompositing) { return mCompositedZoom; } - return mFrameMetrics.GetZoom(); + return Metrics().GetZoom(); } bool AsyncPanZoomController::SampleCompositedAsyncTransform() { RecursiveMutexAutoLock lock(mRecursiveMutex); - if (mCompositedScrollOffset != mFrameMetrics.GetScrollOffset() || - mCompositedZoom != mFrameMetrics.GetZoom()) { - mCompositedLayoutViewport = mFrameMetrics.GetViewport(); - mCompositedScrollOffset = mFrameMetrics.GetScrollOffset(); - mCompositedZoom = mFrameMetrics.GetZoom(); + if (mCompositedScrollOffset != Metrics().GetScrollOffset() || + mCompositedZoom != Metrics().GetZoom()) { + mCompositedLayoutViewport = Metrics().GetViewport(); + mCompositedScrollOffset = Metrics().GetScrollOffset(); + mCompositedZoom = Metrics().GetZoom(); return true; } return false; @@ -3924,9 +3923,9 @@ AsyncPanZoomController::GetCheckerboardMagnitude() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset; + CSSPoint currentScrollOffset = Metrics().GetScrollOffset() + mTestAsyncScrollOffset; CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset(); - CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels()); + CSSRect visible = CSSRect(currentScrollOffset, Metrics().CalculateCompositedSizeInCssPixels()); CSSIntRegion checkerboard; // Round so as to minimize checkerboarding; if we're only showing fractional @@ -4004,14 +4003,14 @@ bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const { return false; } - CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset; + CSSPoint currentScrollOffset = Metrics().GetScrollOffset() + mTestAsyncScrollOffset; CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset(); painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1))); // fuzz for rounding error - CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels()); + CSSRect visible = CSSRect(currentScrollOffset, Metrics().CalculateCompositedSizeInCssPixels()); if (painted.Contains(visible)) { return false; } - APZC_LOG_FM(mFrameMetrics, "%p is currently checkerboarding (painted %s visble %s)", + APZC_LOG_FM(Metrics(), "%p is currently checkerboarding (painted %s visble %s)", this, Stringify(painted).c_str(), Stringify(visible).c_str()); return true; } @@ -4033,7 +4032,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe return; } - // If the mFrameMetrics scroll offset is different from the last scroll offset + // If the Metrics scroll offset is different from the last scroll offset // that the main-thread sent us, then we know that the user has been doing // something that triggers a scroll. This check is the APZ equivalent of the // check on the main-thread at @@ -4043,8 +4042,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // how the main thread code does the same thing. CSSPoint lastScrollOffset = mLastContentPaintMetadata.GetMetrics().GetScrollOffset(); bool userScrolled = - !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().x, lastScrollOffset.x) || - !FuzzyEqualsAdditive(mFrameMetrics.GetScrollOffset().y, lastScrollOffset.y); + !FuzzyEqualsAdditive(Metrics().GetScrollOffset().x, lastScrollOffset.x) || + !FuzzyEqualsAdditive(Metrics().GetScrollOffset().y, lastScrollOffset.y); if (aLayerMetrics.GetScrollUpdateType() != FrameMetrics::ScrollOffsetUpdateType::ePending) { mLastContentPaintMetadata = aScrollMetadata; @@ -4095,7 +4094,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // to filter duplicate calls to NotifyLayersUpdated with the same scroll offset // update message. bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated() - && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration()); + && (aLayerMetrics.GetScrollGeneration() != Metrics().GetScrollGeneration()); if (scrollOffsetUpdated && userScrolled && aLayerMetrics.GetScrollUpdateType() == FrameMetrics::ScrollOffsetUpdateType::eRestore) { @@ -4104,24 +4103,24 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe } bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll() - && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration()); + && (aLayerMetrics.GetScrollGeneration() != Metrics().GetScrollGeneration()); // TODO if we're in a drag and scrollOffsetUpdated is set then we want to // ignore it bool needContentRepaint = false; bool viewportUpdated = false; - if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), mFrameMetrics.GetCompositionBounds().Width()) && - FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), mFrameMetrics.GetCompositionBounds().Height())) { + if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), Metrics().GetCompositionBounds().Width()) && + FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), Metrics().GetCompositionBounds().Height())) { // Remote content has sync'd up to the composition geometry // change, so we can accept the viewport it's calculated. - if (mFrameMetrics.GetViewport().Width() != aLayerMetrics.GetViewport().Width() || - mFrameMetrics.GetViewport().Height() != aLayerMetrics.GetViewport().Height()) { + if (Metrics().GetViewport().Width() != aLayerMetrics.GetViewport().Width() || + Metrics().GetViewport().Height() != aLayerMetrics.GetViewport().Height()) { needContentRepaint = true; viewportUpdated = true; } if (viewportUpdated || scrollOffsetUpdated) { - mFrameMetrics.SetViewport(aLayerMetrics.GetViewport()); + Metrics().SetViewport(aLayerMetrics.GetViewport()); } } @@ -4144,11 +4143,11 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe mExpectedGeckoMetrics = aLayerMetrics; ShareCompositorFrameMetrics(); - mCompositedLayoutViewport = mFrameMetrics.GetViewport(); - mCompositedScrollOffset = mFrameMetrics.GetScrollOffset(); - mCompositedZoom = mFrameMetrics.GetZoom(); + mCompositedLayoutViewport = Metrics().GetViewport(); + mCompositedScrollOffset = Metrics().GetScrollOffset(); + mCompositedZoom = Metrics().GetZoom(); - if (mFrameMetrics.GetDisplayPortMargins() != ScreenMargin()) { + if (Metrics().GetDisplayPortMargins() != ScreenMargin()) { // A non-zero display port margin here indicates a displayport has // been set by a previous APZC for the content at this guid. The // scrollable rect may have changed since then, making the margins @@ -4158,11 +4157,11 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe } } else { // If we're not taking the aLayerMetrics wholesale we still need to pull - // in some things into our local mFrameMetrics because these things are - // determined by Gecko and our copy in mFrameMetrics may be stale. + // in some things into our local Metrics() because these things are + // determined by Gecko and our copy in Metrics() may be stale. - if (FuzzyEqualsAdditive(mFrameMetrics.GetCompositionBounds().Width(), aLayerMetrics.GetCompositionBounds().Width()) && - mFrameMetrics.GetDevPixelsPerCSSPixel() == aLayerMetrics.GetDevPixelsPerCSSPixel() && + if (FuzzyEqualsAdditive(Metrics().GetCompositionBounds().Width(), aLayerMetrics.GetCompositionBounds().Width()) && + Metrics().GetDevPixelsPerCSSPixel() == aLayerMetrics.GetDevPixelsPerCSSPixel() && !viewportUpdated) { // Any change to the pres shell resolution was requested by APZ and is // already included in our zoom; however, other components of the @@ -4172,33 +4171,33 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // aLayerMetrics.mZoom because the APZ may have additional async zoom // since the repaint request. gfxSize totalResolutionChange = aLayerMetrics.GetCumulativeResolution() - / mFrameMetrics.GetCumulativeResolution(); + / Metrics().GetCumulativeResolution(); float presShellResolutionChange = aLayerMetrics.GetPresShellResolution() - / mFrameMetrics.GetPresShellResolution(); + / Metrics().GetPresShellResolution(); if (presShellResolutionChange != 1.0f) { needContentRepaint = true; } - mFrameMetrics.ZoomBy(totalResolutionChange / presShellResolutionChange); + Metrics().ZoomBy(totalResolutionChange / presShellResolutionChange); mCompositedZoom.xScale *= (totalResolutionChange / presShellResolutionChange).width; mCompositedZoom.yScale *= (totalResolutionChange / presShellResolutionChange).height; } else { // Take the new zoom as either device scale or composition width or // viewport size got changed (e.g. due to orientation change, or content // changing the meta-viewport tag). - mFrameMetrics.SetZoom(aLayerMetrics.GetZoom()); + Metrics().SetZoom(aLayerMetrics.GetZoom()); mCompositedZoom = aLayerMetrics.GetZoom(); - mFrameMetrics.SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel()); + Metrics().SetDevPixelsPerCSSPixel(aLayerMetrics.GetDevPixelsPerCSSPixel()); } bool scrollableRectChanged = false; - if (!mFrameMetrics.GetScrollableRect().IsEqualEdges(aLayerMetrics.GetScrollableRect())) { - mFrameMetrics.SetScrollableRect(aLayerMetrics.GetScrollableRect()); + if (!Metrics().GetScrollableRect().IsEqualEdges(aLayerMetrics.GetScrollableRect())) { + Metrics().SetScrollableRect(aLayerMetrics.GetScrollableRect()); needContentRepaint = true; scrollableRectChanged = true; } - mFrameMetrics.SetCompositionBounds(aLayerMetrics.GetCompositionBounds()); - mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize()); - mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution()); - mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution()); + Metrics().SetCompositionBounds(aLayerMetrics.GetCompositionBounds()); + Metrics().SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize()); + Metrics().SetPresShellResolution(aLayerMetrics.GetPresShellResolution()); + Metrics().SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution()); mScrollMetadata.SetHasScrollgrab(aScrollMetadata.GetHasScrollgrab()); mScrollMetadata.SetLineScrollAmount(aScrollMetadata.GetLineScrollAmount()); mScrollMetadata.SetPageScrollAmount(aScrollMetadata.GetPageScrollAmount()); @@ -4211,14 +4210,14 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe mScrollMetadata.SetIsAutoDirRootContentRTL( aScrollMetadata.IsAutoDirRootContentRTL()); mScrollMetadata.SetUsesContainerScrolling(aScrollMetadata.UsesContainerScrolling()); - mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer()); + Metrics().SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer()); mScrollMetadata.SetForceDisableApz(aScrollMetadata.IsApzForceDisabled()); mScrollMetadata.SetDisregardedDirection(aScrollMetadata.GetDisregardedDirection()); mScrollMetadata.SetOverscrollBehavior(aScrollMetadata.GetOverscrollBehavior()); if (scrollOffsetUpdated) { APZC_LOG("%p updating scroll offset from %s to %s\n", this, - ToString(mFrameMetrics.GetScrollOffset()).c_str(), + ToString(Metrics().GetScrollOffset()).c_str(), ToString(aLayerMetrics.GetScrollOffset()).c_str()); // Send an acknowledgement with the new scroll generation so that any @@ -4229,8 +4228,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // correct this we need to update mExpectedGeckoMetrics to be the // last thing we know was painted by Gecko. CopyScrollInfoFrom(aLayerMetrics); - mCompositedLayoutViewport = mFrameMetrics.GetViewport(); - mCompositedScrollOffset = mFrameMetrics.GetScrollOffset(); + mCompositedLayoutViewport = Metrics().GetViewport(); + mCompositedScrollOffset = Metrics().GetScrollOffset(); mExpectedGeckoMetrics = aLayerMetrics; // Cancel the animation (which might also trigger a repaint request) @@ -4253,7 +4252,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // Even if we didn't accept a new scroll offset from content, the // scrollable rect may have changed in a way that makes our local // scroll offset out of bounds, so re-clamp it. - ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset()); + ClampAndSetScrollOffset(Metrics().GetScrollOffset()); } } @@ -4263,17 +4262,17 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe // the scroll update acknowledgement. APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this, - Stringify(mFrameMetrics.GetScrollOffset()).c_str(), + Stringify(Metrics().GetScrollOffset()).c_str(), Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(), mState); // See comment on the similar code in the |if (scrollOffsetUpdated)| block // above. - mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics); + Metrics().CopySmoothScrollInfoFrom(aLayerMetrics); needContentRepaint = true; mExpectedGeckoMetrics = aLayerMetrics; - SmoothScrollTo(mFrameMetrics.GetSmoothScrollOffset()); + SmoothScrollTo(Metrics().GetSmoothScrollOffset()); } if (needContentRepaint) { @@ -4283,9 +4282,17 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe UpdateSharedCompositorFrameMetrics(); } +FrameMetrics& AsyncPanZoomController::Metrics() { + return mScrollMetadata.GetMetrics(); +} + +const FrameMetrics& AsyncPanZoomController::Metrics() const { + return mScrollMetadata.GetMetrics();; +} + const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const { mRecursiveMutex.AssertCurrentThreadIn(); - return mFrameMetrics; + return mScrollMetadata.GetMetrics();; } const ScrollMetadata& AsyncPanZoomController::GetScrollMetadata() const { @@ -4331,18 +4338,18 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) { // would have to be adjusted (as e.g. it would no longer be valid to take // the minimum or maximum of the ratios of the widths and heights of the // page rect and the composition bounds). - MOZ_ASSERT(mFrameMetrics.IsRootContent()); - MOZ_ASSERT(mFrameMetrics.GetZoom().AreScalesSame()); + MOZ_ASSERT(Metrics().IsRootContent()); + MOZ_ASSERT(Metrics().GetZoom().AreScalesSame()); SetState(ANIMATING_ZOOM); { RecursiveMutexAutoLock lock(mRecursiveMutex); - ParentLayerRect compositionBounds = mFrameMetrics.GetCompositionBounds(); - CSSRect cssPageRect = mFrameMetrics.GetScrollableRect(); - CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset(); - CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoom().ToScaleFactor(); + ParentLayerRect compositionBounds = Metrics().GetCompositionBounds(); + CSSRect cssPageRect = Metrics().GetScrollableRect(); + CSSPoint scrollOffset = Metrics().GetScrollOffset(); + CSSToParentLayerScale currentZoom = Metrics().GetZoom().ToScaleFactor(); CSSToParentLayerScale targetZoom; // The minimum zoom to prevent over-zoom-out. @@ -4377,7 +4384,7 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) { } if (zoomOut) { - CSSSize compositedSize = mFrameMetrics.CalculateCompositedSizeInCssPixels(); + CSSSize compositedSize = Metrics().CalculateCompositedSizeInCssPixels(); float y = scrollOffset.y; float newHeight = cssPageRect.Width() * (compositedSize.height / compositedSize.width); @@ -4393,12 +4400,12 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) { } targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale); - FrameMetrics endZoomToMetrics = mFrameMetrics; + FrameMetrics endZoomToMetrics = Metrics(); if (aFlags & PAN_INTO_VIEW_ONLY) { targetZoom = currentZoom; } else if(aFlags & ONLY_ZOOM_TO_DEFAULT_SCALE) { CSSToParentLayerScale zoomAtDefaultScale = - mFrameMetrics.GetDevPixelsPerCSSPixel() * LayoutDeviceToParentLayerScale(1.0); + Metrics().GetDevPixelsPerCSSPixel() * LayoutDeviceToParentLayerScale(1.0); if (targetZoom.scale > zoomAtDefaultScale.scale) { // Only change the zoom if we are less than the default zoom if (currentZoom.scale < zoomAtDefaultScale.scale) { @@ -4435,8 +4442,8 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) { StartAnimation(new ZoomAnimation( *this, - mFrameMetrics.GetScrollOffset(), - mFrameMetrics.GetZoom(), + Metrics().GetScrollOffset(), + Metrics().GetZoom(), endZoomToMetrics.GetScrollOffset(), endZoomToMetrics.GetZoom())); @@ -4562,7 +4569,7 @@ void AsyncPanZoomController::DispatchStateChangeNotification(PanZoomState aOldSt if (APZCTreeManager* manager = GetApzcTreeManager()) { AndroidDynamicToolbarAnimator* animator = manager->GetAndroidDynamicToolbarAnimator(); MOZ_ASSERT(animator); - animator->UpdateRootFrameMetrics(mFrameMetrics); + animator->UpdateRootFrameMetrics(Metrics()); } #endif @@ -4593,9 +4600,9 @@ void AsyncPanZoomController::UpdateZoomConstraints(const ZoomConstraints& aConst return; } - CSSToParentLayerScale min = mFrameMetrics.GetDevPixelsPerCSSPixel() + CSSToParentLayerScale min = Metrics().GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1); - CSSToParentLayerScale max = mFrameMetrics.GetDevPixelsPerCSSPixel() + CSSToParentLayerScale max = Metrics().GetDevPixelsPerCSSPixel() * kViewportMaxScale / ParentLayerToScreenScale(1); // inf float values and other bad cases should be sanitized by the code below. @@ -4646,7 +4653,7 @@ void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut) const ScrollableLayerGuid AsyncPanZoomController::GetGuid() const { - return ScrollableLayerGuid(mLayersId, mFrameMetrics); + return ScrollableLayerGuid(mLayersId, Metrics()); } void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics() @@ -4658,7 +4665,7 @@ void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics() if (frame && mSharedLock && gfxPrefs::ProgressivePaint()) { mSharedLock->Lock(); - *frame = mFrameMetrics; + *frame = Metrics(); mSharedLock->Unlock(); } } @@ -4683,7 +4690,7 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() { // scope the monitor, only needed to copy the FrameMetrics. RecursiveMutexAutoLock lock(mRecursiveMutex); - *frame = mFrameMetrics; + *frame = Metrics(); } // Get the process id of the content process @@ -4725,13 +4732,13 @@ Maybe AsyncPanZoomController::FindSnapPointNear( const CSSPoint& aDestination, nsIScrollableFrame::ScrollUnit aUnit) { mRecursiveMutex.AssertCurrentThreadIn(); APZC_LOG("%p scroll snapping near %s\n", this, Stringify(aDestination).c_str()); - CSSRect scrollRange = mFrameMetrics.CalculateScrollRange(); + CSSRect scrollRange = Metrics().CalculateScrollRange(); if (Maybe snapPoint = ScrollSnapUtils::GetSnapPointForDestination( mScrollMetadata.GetSnapInfo(), aUnit, - CSSSize::ToAppUnits(mFrameMetrics.CalculateCompositedSizeInCssPixels()), + CSSSize::ToAppUnits(Metrics().CalculateCompositedSizeInCssPixels()), CSSRect::ToAppUnits(scrollRange), - CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()), + CSSPoint::ToAppUnits(Metrics().GetScrollOffset()), CSSPoint::ToAppUnits(aDestination))) { CSSPoint cssSnapPoint = CSSPoint::FromAppUnits(snapPoint.ref()); // GetSnapPointForDestination() can produce a destination that's outside @@ -4746,7 +4753,7 @@ Maybe AsyncPanZoomController::FindSnapPointNear( void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) { if (Maybe snapPoint = FindSnapPointNear(aDestination, nsIScrollableFrame::DEVICE_PIXELS)) { - if (*snapPoint != mFrameMetrics.GetScrollOffset()) { + if (*snapPoint != Metrics().GetScrollOffset()) { APZC_LOG("%p smooth scrolling to snap point %s\n", this, Stringify(*snapPoint).c_str()); SmoothScrollTo(*snapPoint); } @@ -4755,7 +4762,7 @@ void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) { void AsyncPanZoomController::ScrollSnap() { RecursiveMutexAutoLock lock(mRecursiveMutex); - ScrollSnapNear(mFrameMetrics.GetScrollOffset()); + ScrollSnapNear(Metrics().GetScrollOffset()); } void AsyncPanZoomController::ScrollSnapToDestination() { @@ -4772,7 +4779,7 @@ void AsyncPanZoomController::ScrollSnapToDestination() { if (velocity.y != 0.0f) { predictedDelta.y = -velocity.y / log(1.0 - friction); } - CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom(); + CSSPoint predictedDestination = Metrics().GetScrollOffset() + predictedDelta / Metrics().GetZoom(); // If the fling will overscroll, don't scroll snap, because then the user // user would not see any overscroll animation. @@ -4783,8 +4790,8 @@ void AsyncPanZoomController::ScrollSnapToDestination() { "predictedDelta: %f, %f position: %f, %f " "predictedDestination: %f, %f\n", this, friction, velocity.x, velocity.y, (float)predictedDelta.x, - (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x, - (float)mFrameMetrics.GetScrollOffset().y, + (float)predictedDelta.y, (float)Metrics().GetScrollOffset().x, + (float)Metrics().GetScrollOffset().y, (float)predictedDestination.x, (float)predictedDestination.y); ScrollSnapNear(predictedDestination); @@ -4803,8 +4810,8 @@ bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping( } RecursiveMutexAutoLock lock(mRecursiveMutex); - CSSToParentLayerScale2D zoom = mFrameMetrics.GetZoom(); - CSSPoint destination = mFrameMetrics.CalculateScrollRange().ClampPoint( + CSSToParentLayerScale2D zoom = Metrics().GetZoom(); + CSSPoint destination = Metrics().CalculateScrollRange().ClampPoint( aStartPosition + (aDelta / zoom)); nsIScrollableFrame::ScrollUnit unit = ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType); diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 198424f6d041..c839237d3ff3 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -517,6 +517,13 @@ public: bool OverscrollBehaviorAllowsSwipe() const; + //|Metrics()| and |Metrics() const| are getter functions that both return + //mScrollMetadata.mMetrics + + const FrameMetrics& Metrics() const; + FrameMetrics& Metrics(); + + private: // Get whether the horizontal content of the honoured target of auto-dir // scrolling starts from right to left. If you don't know of auto-dir @@ -887,13 +894,12 @@ protected: PlatformSpecificStateBase* GetPlatformSpecificState(); protected: - // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the - // monitor. Do not read from or modify either of them without locking. + // Both |mScrollMetadata| and |mLastContentPaintMetrics| are protected by the + // monitor. Do not read from or modify them without locking. ScrollMetadata mScrollMetadata; - FrameMetrics& mFrameMetrics; // for convenience, refers to mScrollMetadata.mMetrics - // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|. - // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the + // Protects |mScrollMetadata|, |mLastContentPaintMetrics| and |mState|. + // Before manipulating |mScrollMetadata| or |mLastContentPaintMetrics| the // monitor should be held. When setting |mState|, either the SetState() // function can be used, or the monitor can be held and then |mState| updated. // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h. @@ -931,7 +937,7 @@ private: FrameMetrics mExpectedGeckoMetrics; // These variables cache the layout viewport, scroll offset, and zoom stored - // in |mFrameMetrics| the last time SampleCompositedAsyncTransform() was + // in |Metrics()| the last time SampleCompositedAsyncTransform() was // called. CSSRect mCompositedLayoutViewport; CSSPoint mCompositedScrollOffset; @@ -1059,20 +1065,20 @@ private: /** * Samples the composited async transform, making the result of * |GetCurrentAsyncTransform(eForCompositing)| and similar functions reflect - * the async scroll offset and zoom stored in |mFrameMetrics|. + * the async scroll offset and zoom stored in |Metrics()|. * * Returns true if the newly sampled value is different from the previously * sampled value. * * (This is only relevant when |gfxPrefs::APZFrameDelayEnabled() == true|. * Otherwise, GetCurrentAsyncTransform() always reflects what's stored in - * |mFrameMetrics| immediately, without any delay.) + * |Metrics()| immediately, without any delay.) */ bool SampleCompositedAsyncTransform(); /* * Helper functions to query the async layout viewport, scroll offset, and - * zoom either directly from |mFrameMetrics|, or from cached variables that + * zoom either directly from |Metrics()|, or from cached variables that * store the required value from the last time it was sampled by calling * SampleCompositedAsyncTransform(), depending on who is asking. */ @@ -1273,7 +1279,7 @@ public: bool IsRootContent() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - return mFrameMetrics.IsRootContent(); + return Metrics().IsRootContent(); } private: @@ -1427,7 +1433,7 @@ private: RefPtr mSharedFrameMetricsBuffer; CrossProcessMutex* mSharedLock; /** - * Called when ever mFrameMetrics is updated so that if it is being + * Called when ever Metrics() is updated so that if it is being * shared with the content process the shared FrameMetrics may be updated. */ void UpdateSharedCompositorFrameMetrics(); diff --git a/gfx/layers/apz/src/Axis.h b/gfx/layers/apz/src/Axis.h index fdc9e741acd2..25e5883d2abb 100644 --- a/gfx/layers/apz/src/Axis.h +++ b/gfx/layers/apz/src/Axis.h @@ -104,7 +104,7 @@ public: * Return the amount of overscroll on this axis, in ParentLayer pixels. * * If this amount is nonzero, the relevant component of - * mAsyncPanZoomController->mFrameMetrics.mScrollOffset must be at its + * mAsyncPanZoomController->Metrics().mScrollOffset must be at its * extreme allowed value in the relevant direction (that is, it must be at * its maximum value if we are overscrolled at our composition length, and * at its minimum value if we are overscrolled at the origin). diff --git a/gfx/layers/apz/test/gtest/APZTestCommon.h b/gfx/layers/apz/test/gtest/APZTestCommon.h index 7127751aef7b..cf6e3b09b689 100644 --- a/gfx/layers/apz/test/gtest/APZTestCommon.h +++ b/gfx/layers/apz/test/gtest/APZTestCommon.h @@ -227,6 +227,7 @@ protected: return mcc->Time(); } + private: RefPtr mcc; }; @@ -269,12 +270,12 @@ public: void SetFrameMetrics(const FrameMetrics& metrics) { RecursiveMutexAutoLock lock(mRecursiveMutex); - mFrameMetrics = metrics; + Metrics() = metrics; } FrameMetrics& GetFrameMetrics() { RecursiveMutexAutoLock lock(mRecursiveMutex); - return mFrameMetrics; + return mScrollMetadata.GetMetrics(); } ScrollMetadata& GetScrollMetadata() { @@ -284,7 +285,7 @@ public: const FrameMetrics& GetFrameMetrics() const { RecursiveMutexAutoLock lock(mRecursiveMutex); - return mFrameMetrics; + return mScrollMetadata.GetMetrics(); } using AsyncPanZoomController::GetVelocityVector; From 329ee2b3cc5a9f27a35cb65bf67a3979089fdb0e Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Fri, 27 Jul 2018 15:19:00 +0300 Subject: [PATCH 19/21] Bug 1477002 - Use message broadcaster to manage jsat content scripts. r=yzen This patch does several things: 1. When "domwindowopened" is dispatched it often doesn't have a document yet, so we need to wait for it to load before determining if we should attach it. 2. Instead of managing individual message managers use a broadcaster and load delayed scripts. This makes new window additions more robust. 3. A content script now doesn't need a ready/start message but initializes in-line. This added more complexity which we don't need. All the info that we passed to it in AccessFu:Start can be gotten in other ways (also, Services.appinfo.ID now works in child processes, so no need for that). 4. Tweaked the tests to support inline frame script initilization. 5. Removed the scroll callback from content-script.js that was not used anymore. --- accessible/jsat/AccessFu.jsm | 123 ++++------------- accessible/jsat/EventManager.jsm | 9 +- accessible/jsat/Utils.jsm | 42 +----- accessible/jsat/content-script.js | 130 ++++-------------- accessible/tests/mochitest/jsat/jsatcommon.js | 42 +++--- .../geckoview/test/AccessibilityTest.kt | 23 ++-- 6 files changed, 89 insertions(+), 280 deletions(-) diff --git a/accessible/jsat/AccessFu.jsm b/accessible/jsat/AccessFu.jsm index e10df3eb4758..2808467e109d 100644 --- a/accessible/jsat/AccessFu.jsm +++ b/accessible/jsat/AccessFu.jsm @@ -29,6 +29,13 @@ const GECKOVIEW_MESSAGE = { CLIPBOARD: "GeckoView:AccessibilityClipboard", }; +const ACCESSFU_MESSAGE = { + PRESENT: "AccessFu:Present", + DOSCROLL: "AccessFu:DoScroll", +}; + +const FRAME_SCRIPT = "chrome://global/content/accessibility/content-script.js"; + var AccessFu = { /** * A lazy getter for event handler that binds the scope to AccessFu object. @@ -64,11 +71,6 @@ var AccessFu = { this._attachWindow(windows.getNext()); } - if (this.readyCallback) { - this.readyCallback(); - delete this.readyCallback; - } - Logger.info("AccessFu:Enabled"); }, @@ -107,17 +109,10 @@ var AccessFu = { }); switch (aMessage.name) { - case "AccessFu:Ready": - let mm = Utils.getMessageManager(aMessage.target); - if (this._enabled) { - mm.sendAsyncMessage("AccessFu:Start", - {method: "start", buildApp: Utils.MozBuildApp}); - } - break; - case "AccessFu:Present": + case ACCESSFU_MESSAGE.PRESENT: this._output(aMessage.json, aMessage.target); break; - case "AccessFu:DoScroll": + case ACCESSFU_MESSAGE.DOSCROLL: this.Input.doScroll(aMessage.json, aMessage.target); break; } @@ -130,13 +125,13 @@ var AccessFu = { return; } - for (let mm of Utils.getAllMessageManagers(win)) { - this._addMessageListeners(mm); - this._loadFrameScript(mm); + // Set up frame script + let mm = win.messageManager; + for (let messageName of Object.values(ACCESSFU_MESSAGE)) { + mm.addMessageListener(messageName, this); } + mm.loadFrameScript(FRAME_SCRIPT, true); - win.addEventListener("TabOpen", this); - win.addEventListener("TabClose", this); win.addEventListener("TabSelect", this); if (win.WindowEventDispatcher) { // desktop mochitests don't have this. @@ -146,13 +141,13 @@ var AccessFu = { }, _detachWindow: function _detachWindow(win) { - for (let mm of Utils.getAllMessageManagers(win)) { - mm.sendAsyncMessage("AccessFu:Stop"); - this._removeMessageListeners(mm); + let mm = win.messageManager; + mm.broadcastAsyncMessage("AccessFu:Stop"); + mm.removeDelayedFrameScript(FRAME_SCRIPT); + for (let messageName of Object.values(ACCESSFU_MESSAGE)) { + mm.removeMessageListener(messageName, this); } - win.removeEventListener("TabOpen", this); - win.removeEventListener("TabClose", this); win.removeEventListener("TabSelect", this); if (win.WindowEventDispatcher) { // desktop mochitests don't have this. @@ -193,38 +188,6 @@ var AccessFu = { } }, - _loadFrameScript: function _loadFrameScript(aMessageManager) { - if (!this._processedMessageManagers.includes(aMessageManager)) { - aMessageManager.loadFrameScript( - "chrome://global/content/accessibility/content-script.js", true); - this._processedMessageManagers.push(aMessageManager); - } else if (this._enabled) { - // If the content-script is already loaded and AccessFu is enabled, - // send an AccessFu:Start message. - aMessageManager.sendAsyncMessage("AccessFu:Start", - {method: "start", buildApp: Utils.MozBuildApp}); - } - }, - - _addMessageListeners: function _addMessageListeners(aMessageManager) { - aMessageManager.addMessageListener("AccessFu:Present", this); - aMessageManager.addMessageListener("AccessFu:Ready", this); - aMessageManager.addMessageListener("AccessFu:DoScroll", this); - }, - - _removeMessageListeners: function _removeMessageListeners(aMessageManager) { - aMessageManager.removeMessageListener("AccessFu:Present", this); - aMessageManager.removeMessageListener("AccessFu:Ready", this); - aMessageManager.removeMessageListener("AccessFu:DoScroll", this); - }, - - _handleMessageManager: function _handleMessageManager(aMessageManager) { - if (this._enabled) { - this._addMessageListeners(aMessageManager); - } - this._loadFrameScript(aMessageManager); - }, - onEvent(event, data, callback) { switch (event) { case GECKOVIEW_MESSAGE.SETTINGS: @@ -280,19 +243,11 @@ var AccessFu = { observe: function observe(aSubject, aTopic, aData) { switch (aTopic) { - case "remote-browser-shown": - case "inprocess-browser-shown": - { - // Ignore notifications that aren't from a Browser - let frameLoader = aSubject; - if (!frameLoader.ownerIsMozBrowserFrame) { - return; - } - this._handleMessageManager(frameLoader.messageManager); - break; - } case "domwindowopened": { - this._attachWindow(aSubject.QueryInterface(Ci.nsIDOMWindow)); + let win = aSubject.QueryInterface(Ci.nsIDOMWindow); + win.addEventListener("load", () => { + this._attachWindow(win); + }, { once: true }); break; } } @@ -300,22 +255,6 @@ var AccessFu = { _handleEvent: function _handleEvent(aEvent) { switch (aEvent.type) { - case "TabOpen": - { - let mm = Utils.getMessageManager(aEvent.target); - this._handleMessageManager(mm); - break; - } - case "TabClose": - { - let mm = Utils.getMessageManager(aEvent.target); - let mmIndex = this._processedMessageManagers.indexOf(mm); - if (mmIndex > -1) { - this._removeMessageListeners(mm); - this._processedMessageManagers.splice(mmIndex, 1); - } - break; - } case "TabSelect": { if (this._focused) { @@ -350,10 +289,6 @@ var AccessFu = { // Layerview is focused _focused: false, - // Keep track of message managers tha already have a 'content-script.js' - // injected. - _processedMessageManagers: [], - /** * Adjusts the given bounds that are defined in device display pixels * to client-relative CSS pixels of the chrome window. @@ -413,18 +348,6 @@ var Input = { mm.sendAsyncMessage("AccessFu:Activate", { offset: 0 }); }, - // XXX: This is here for backwards compatability with screen reader simulator - // it should be removed when the extension is updated on amo. - scroll: function scroll(aPage, aHorizontal) { - this.sendScrollMessage(aPage, aHorizontal); - }, - - sendScrollMessage: function sendScrollMessage(aPage, aHorizontal) { - const mm = Utils.getMessageManager(); - mm.sendAsyncMessage("AccessFu:Scroll", - {page: aPage, horizontal: aHorizontal, origin: "top"}); - }, - doScroll: function doScroll(aDetails, aBrowser) { let horizontal = aDetails.horizontal; let page = aDetails.page; diff --git a/accessible/jsat/EventManager.jsm b/accessible/jsat/EventManager.jsm index 79c6bab5c981..e4fdb81f3f72 100644 --- a/accessible/jsat/EventManager.jsm +++ b/accessible/jsat/EventManager.jsm @@ -25,9 +25,8 @@ ChromeUtils.defineModuleGetter(this, "setTimeout", var EXPORTED_SYMBOLS = ["EventManager"]; -function EventManager(aContentScope, aContentControl) { +function EventManager(aContentScope) { this.contentScope = aContentScope; - this.contentControl = aContentControl; this.addEventListener = this.contentScope.addEventListener.bind( this.contentScope); this.removeEventListener = this.contentScope.removeEventListener.bind( @@ -85,6 +84,10 @@ this.EventManager.prototype = { } }, + get contentControl() { + return this.contentScope._jsat_contentControl; + }, + handleEvent: function handleEvent(aEvent) { Logger.debug(() => { return ["DOMEvent", aEvent.type]; @@ -247,7 +250,7 @@ this.EventManager.prototype = { this.present(Presentation.focused(acc)); - if (this.inTest) { + if (Utils.inTest) { this.sendMsgFunc("AccessFu:Focused"); } break; diff --git a/accessible/jsat/Utils.jsm b/accessible/jsat/Utils.jsm index 510b65c1ec27..9d2769d80da3 100644 --- a/accessible/jsat/Utils.jsm +++ b/accessible/jsat/Utils.jsm @@ -82,41 +82,6 @@ var Utils = { // jshint ignore:line return win.document.querySelector("browser[type=content][primary=true]"); }, - getAllMessageManagers: function getAllMessageManagers(aWindow) { - let messageManagers = new Set(); - - function collectLeafMessageManagers(mm) { - for (let i = 0; i < mm.childCount; i++) { - let childMM = mm.getChildAt(i); - - if ("sendAsyncMessage" in childMM) { - messageManagers.add(childMM); - } else { - collectLeafMessageManagers(childMM); - } - } - } - - collectLeafMessageManagers(aWindow.messageManager); - - let browser = this.getCurrentBrowser(aWindow); - let document = browser ? browser.contentDocument : null; - - if (document) { - let remoteframes = document.querySelectorAll("iframe"); - - for (let i = 0; i < remoteframes.length; ++i) { - let mm = this.getMessageManager(remoteframes[i]); - if (mm) { - messageManagers.add(mm); - } - } - - } - - return messageManagers; - }, - get isContentProcess() { delete this.isContentProcess; this.isContentProcess = @@ -418,7 +383,8 @@ var Logger = { // jshint ignore:line logLevel: 1, // INFO; - test: false, + // Note: used for testing purposes. If true, also log to the console service. + useConsoleService: false, log: function log(aLogLevel) { if (aLogLevel < this.logLevel) { @@ -430,9 +396,7 @@ var Logger = { // jshint ignore:line message = "[" + Utils.ScriptName + "] " + this._LEVEL_NAMES[aLogLevel + 1] + " " + message + "\n"; dump(message); - // Note: used for testing purposes. If |this.test| is true, also log to - // the console service. - if (this.test) { + if (this.useConsoleService) { try { Services.console.logStringMessage(message); } catch (ex) { diff --git a/accessible/jsat/content-script.js b/accessible/jsat/content-script.js index d7199ba3e4a9..2e00b93d90fb 100644 --- a/accessible/jsat/content-script.js +++ b/accessible/jsat/content-script.js @@ -6,129 +6,49 @@ ChromeUtils.defineModuleGetter(this, "Logger", "resource://gre/modules/accessibility/Utils.jsm"); -ChromeUtils.defineModuleGetter(this, "Presentation", - "resource://gre/modules/accessibility/Presentation.jsm"); ChromeUtils.defineModuleGetter(this, "Utils", "resource://gre/modules/accessibility/Utils.jsm"); ChromeUtils.defineModuleGetter(this, "EventManager", "resource://gre/modules/accessibility/EventManager.jsm"); ChromeUtils.defineModuleGetter(this, "ContentControl", "resource://gre/modules/accessibility/ContentControl.jsm"); -ChromeUtils.defineModuleGetter(this, "Roles", - "resource://gre/modules/accessibility/Constants.jsm"); ChromeUtils.defineModuleGetter(this, "States", "resource://gre/modules/accessibility/Constants.jsm"); Logger.info("content-script.js", content.document.location); -var eventManager = null; -var contentControl = null; +function onStop(m) { + Logger.debug("AccessFu:Stop"); -function forwardToParent(aMessage) { - // XXX: This is a silly way to make a deep copy - let newJSON = JSON.parse(JSON.stringify(aMessage.json)); - newJSON.origin = "child"; - sendAsyncMessage(aMessage.name, newJSON); + removeMessageListener("AccessFu:Stop", onStop); + + this._jsat_eventManager.stop(); + this._jsat_contentControl.stop(); } -function forwardToChild(aMessage, aListener, aVCPosition) { - let acc = aVCPosition || Utils.getVirtualCursor(content.document).position; +addMessageListener("AccessFu:Stop", onStop); - if (!Utils.isAliveAndVisible(acc) || acc.role != Roles.INTERNAL_FRAME) { - return false; - } - - Logger.debug(() => { - return ["forwardToChild", Logger.accessibleToString(acc), - aMessage.name, JSON.stringify(aMessage.json, null, " ")]; - }); - - let mm = Utils.getMessageManager(acc.DOMNode); - - if (aListener) { - mm.addMessageListener(aMessage.name, aListener); - } - - // XXX: This is a silly way to make a deep copy - let newJSON = JSON.parse(JSON.stringify(aMessage.json)); - newJSON.origin = "parent"; - if (Utils.isContentProcess) { - // XXX: OOP content's screen offset is 0, - // so we remove the real screen offset here. - newJSON.x -= content.mozInnerScreenX; - newJSON.y -= content.mozInnerScreenY; - } - mm.sendAsyncMessage(aMessage.name, newJSON); - return true; +if (!this._jsat_contentControl) { + this._jsat_contentControl = new ContentControl(this); } +this._jsat_contentControl.start(); -function presentCaretChange(aText, aOldOffset, aNewOffset) { - if (aOldOffset !== aNewOffset) { - let msg = Presentation.textSelectionChanged(aText, aNewOffset, aNewOffset, - aOldOffset, aOldOffset, true); - sendAsyncMessage("AccessFu:Present", msg); +if (!this._jsat_eventManager) { + this._jsat_eventManager = new EventManager(this); +} +this._jsat_eventManager.start(); + +function contentStarted() { + let accDoc = Utils.AccService.getAccessibleFor(content.document); + if (accDoc && !Utils.getState(accDoc).contains(States.BUSY)) { + sendAsyncMessage("AccessFu:ContentStarted"); + } else { + content.setTimeout(contentStarted, 0); } } -function scroll(aMessage) { - let position = Utils.getVirtualCursor(content.document).position; - if (!forwardToChild(aMessage, scroll, position)) { - sendAsyncMessage("AccessFu:DoScroll", - { bounds: Utils.getBounds(position), - page: aMessage.json.page, - horizontal: aMessage.json.horizontal }); - } +if (Utils.inTest) { + // During a test we want to wait for the document to finish loading for + // consistency. + contentStarted(); } - -addMessageListener( - "AccessFu:Start", - function(m) { - if (m.json.logLevel) { - Logger.logLevel = Logger[m.json.logLevel]; - } - - Logger.debug("AccessFu:Start"); - if (m.json.buildApp) - Utils.MozBuildApp = m.json.buildApp; - - addMessageListener("AccessFu:Scroll", scroll); - - if (!contentControl) { - contentControl = new ContentControl(this); - } - contentControl.start(); - - if (!eventManager) { - eventManager = new EventManager(this, contentControl); - } - eventManager.inTest = m.json.inTest; - eventManager.start(); - - function contentStarted() { - let accDoc = Utils.AccService.getAccessibleFor(content.document); - if (accDoc && !Utils.getState(accDoc).contains(States.BUSY)) { - sendAsyncMessage("AccessFu:ContentStarted"); - } else { - content.setTimeout(contentStarted, 0); - } - } - - if (m.json.inTest) { - // During a test we want to wait for the document to finish loading for - // consistency. - contentStarted(); - } - }); - -addMessageListener( - "AccessFu:Stop", - function(m) { - Logger.debug("AccessFu:Stop"); - - removeMessageListener("AccessFu:Scroll", scroll); - - eventManager.stop(); - contentControl.stop(); - }); - -sendAsyncMessage("AccessFu:Ready"); diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js index 0b99a625b9d0..b9601ed0083b 100644 --- a/accessible/tests/mochitest/jsat/jsatcommon.js +++ b/accessible/tests/mochitest/jsat/jsatcommon.js @@ -105,7 +105,7 @@ var AccessFuTest = { finish: function AccessFuTest_finish() { // Disable the console service logging. - Logger.test = false; + Logger.useConsoleService = false; Logger.logLevel = Logger.INFO; // Finish through idle callback to let AccessFu._disable complete. SimpleTest.executeSoon(function() { @@ -116,13 +116,15 @@ var AccessFuTest = { }, nextTest: function AccessFuTest_nextTest() { - var result = gIterator.next(); - if (result.done) { - this.finish(); - return; - } - var testFunc = result.value; - testFunc(); + SimpleTest.executeSoon(() => { + var result = gIterator.next(); + if (result.done) { + this.finish(); + return; + } + var testFunc = result.value; + testFunc(); + }); }, runTests: function AccessFuTest_runTests(aAdditionalPrefs) { @@ -139,15 +141,12 @@ var AccessFuTest = { } })(); + Logger.useConsoleService = true; + Logger.logLevel = Logger.DEBUG; + // Start AccessFu and put it in stand-by. ChromeUtils.import("resource://gre/modules/accessibility/AccessFu.jsm"); - AccessFu.readyCallback = function readyCallback() { - // Enable logging to the console service. - Logger.test = true; - Logger.logLevel = Logger.DEBUG; - }; - var prefs = [["accessibility.accessfu.notify_output", 1]]; prefs.push.apply(prefs, aAdditionalPrefs); @@ -216,6 +215,10 @@ class AccessFuContentTestRunner { async setupMessageManager(aMessageManager) { function contentScript() { + ChromeUtils.import("resource://gre/modules/accessibility/Utils.jsm"); + Logger.logLevel = "DEBUG"; + Utils.inTest = true; + addMessageListener("AccessFuTest:Focus", aMessage => { var elem = content.document.querySelector(aMessage.data.selector); if (elem) { @@ -231,23 +234,12 @@ class AccessFuContentTestRunner { aMessageManager.loadFrameScript( "data:,(" + contentScript.toString() + ")();", false); - let readyPromise = new Promise(resolve => - aMessageManager.addMessageListener("AccessFu:Ready", resolve)); - aMessageManager.loadFrameScript( "chrome://global/content/accessibility/content-script.js", false); - await readyPromise; - let startedPromise = new Promise(resolve => aMessageManager.addMessageListener("AccessFu:ContentStarted", resolve)); - aMessageManager.sendAsyncMessage("AccessFu:Start", - { buildApp: "browser", - androidSdkVersion: Utils.AndroidSdkVersion, - logLevel: "DEBUG", - inTest: true }); - await startedPromise; aMessageManager.addMessageListener("AccessFu:Present", this); diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt index 4637dff00a55..2e161eb08361 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt @@ -106,6 +106,13 @@ class AccessibilityTest : BaseSessionTest() { sessionRule.session.accessibility.view = null } + private fun waitForInitialFocus() { + sessionRule.waitUntilCalled(object : EventDelegate { + @AssertCalled(count = 1) + override fun onFocused(event: AccessibilityEvent) { } + }) + } + @Test fun testRootNode() { assertThat("provider is not null", provider, notNullValue()) val node = provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID) @@ -125,7 +132,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testAccessibilityFocus() { var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID sessionRule.session.loadTestPath(INPUTS_PATH) - sessionRule.waitForPageStop() + waitForInitialFocus() provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null) @@ -154,7 +161,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testTextEntryNode() { sessionRule.session.loadString("", "text/html") - sessionRule.waitForPageStop() + waitForInitialFocus() mainSession.evaluateJS("$('input').focus()") @@ -233,7 +240,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testClipboard() { var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID; sessionRule.session.loadString("", "text/html") - sessionRule.waitForPageStop() + waitForInitialFocus() mainSession.evaluateJS("$('input').focus()") @@ -284,7 +291,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testMoveByCharacter() { var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID sessionRule.session.loadTestPath(LOREM_IPSUM_HTML_PATH) - sessionRule.waitForPageStop() + waitForInitialFocus() provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null) @@ -317,7 +324,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testMoveByWord() { var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID sessionRule.session.loadTestPath(LOREM_IPSUM_HTML_PATH) - sessionRule.waitForPageStop() + waitForInitialFocus() provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null) @@ -350,7 +357,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testMoveByLine() { var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID sessionRule.session.loadTestPath(LOREM_IPSUM_HTML_PATH) - sessionRule.waitForPageStop() + waitForInitialFocus() provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null) @@ -383,7 +390,7 @@ class AccessibilityTest : BaseSessionTest() { @Test fun testCheckbox() { var nodeId = AccessibilityNodeProvider.HOST_VIEW_ID; sessionRule.session.loadString("", "text/html") - sessionRule.waitForPageStop() + waitForInitialFocus() mainSession.evaluateJS("$('#checkbox').focus()") sessionRule.waitUntilCalled(object : EventDelegate { @@ -413,7 +420,7 @@ class AccessibilityTest : BaseSessionTest() {
  • 1
  • ""","text/html") - sessionRule.waitForPageStop() + waitForInitialFocus() provider.performAction(nodeId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null) sessionRule.waitUntilCalled(object : EventDelegate { From 057c3109797c1d8f55deacebbaaf244f7f33792d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Sat, 28 Jul 2018 00:40:26 -0400 Subject: [PATCH 20/21] Bug 1478890. Stop using ToXPCOMCallback in GetUserMedia code. r=jib We don't want to expose nsXPCWrappedJS to the web, and this code is doing that. --- dom/base/Navigator.cpp | 16 +- dom/bindings/CallbackObject.h | 14 ++ dom/media/MediaDevices.cpp | 8 +- dom/media/MediaManager.cpp | 173 ++++++++++++++---- dom/media/MediaManager.h | 10 +- .../recognition/SpeechRecognition.cpp | 8 +- 6 files changed, 172 insertions(+), 57 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index d06f3b207aac..cce0d7a715f9 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1178,24 +1178,18 @@ Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints, CallerType aCallerType, ErrorResult& aRv) { - CallbackObjectHolder holder1(&aOnSuccess); - nsCOMPtr onsuccess = - holder1.ToXPCOMCallback(); - - CallbackObjectHolder holder2(&aOnError); - nsCOMPtr onerror = holder2.ToXPCOMCallback(); - if (!mWindow || !mWindow->GetOuterWindow() || mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return; } + MediaManager::GetUserMediaSuccessCallback onsuccess(&aOnSuccess); + MediaManager::GetUserMediaErrorCallback onerror(&aOnError); + MediaManager* manager = MediaManager::Get(); - aRv = manager->GetUserMedia(mWindow, aConstraints, onsuccess, onerror, - aCallerType); + aRv = manager->GetUserMedia(mWindow, aConstraints, std::move(onsuccess), + std::move(onerror), aCallerType); } void diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h index 78f58b4fddc9..67642ae0c539 100644 --- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -433,11 +433,25 @@ public: void operator=(const CallbackObjectHolder& aOther) = delete; + void Reset() + { + UnlinkSelf(); + } + nsISupports* GetISupports() const { return reinterpret_cast(mPtrBits & ~XPCOMCallbackFlag); } + already_AddRefed Forget() + { + // This can be called from random threads. Make sure to not refcount things + // in here! + nsISupports* supp = GetISupports(); + mPtrBits = 0; + return dont_AddRef(supp); + } + // Boolean conversion operator so people can use this in boolean tests explicit operator bool() const { diff --git a/dom/media/MediaDevices.cpp b/dom/media/MediaDevices.cpp index 252347198fd3..40e4d8de53aa 100644 --- a/dom/media/MediaDevices.cpp +++ b/dom/media/MediaDevices.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/MediaStreamBinding.h" #include "mozilla/dom/MediaDeviceInfo.h" #include "mozilla/dom/MediaDevicesBinding.h" +#include "mozilla/dom/NavigatorBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/MediaManager.h" #include "MediaTrackConstraints.h" @@ -182,11 +183,12 @@ MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints, RefPtr p = Promise::Create(GetParentObject(), aRv); NS_ENSURE_TRUE(!aRv.Failed(), nullptr); - RefPtr resolver = new GumResolver(p); - RefPtr rejecter = new GumRejecter(p); + MediaManager::GetUserMediaSuccessCallback resolver(new GumResolver(p)); + MediaManager::GetUserMediaErrorCallback rejecter(new GumRejecter(p)); aRv = MediaManager::Get()->GetUserMedia(GetOwner(), aConstraints, - resolver, rejecter, + std::move(resolver), + std::move(rejecter), aCallerType); return p.forget(); } diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 2113def03968..c220003e1711 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -99,6 +99,77 @@ struct nsIMediaDevice::COMTypeInfo { }; const nsIID nsIMediaDevice::COMTypeInfo::kIID = NS_IMEDIADEVICE_IID; +// A specialization of nsMainThreadPtrHolder for +// mozilla::dom::CallbackObjectHolder. See documentation for +// nsMainThreadPtrHolder in nsProxyRelease.h. This specialization lets us avoid +// wrapping the CallbackObjectHolder into a separate refcounted object. +template +class nsMainThreadPtrHolder> final +{ + typedef mozilla::dom::CallbackObjectHolder Holder; +public: + nsMainThreadPtrHolder(const char* aName, Holder&& aHolder) + : mHolder(std::move(aHolder)) +#ifndef RELEASE_OR_BETA + , mName(aName) +#endif + { + MOZ_ASSERT(NS_IsMainThread()); + } + +private: + // We can be released on any thread. + ~nsMainThreadPtrHolder() + { + if (NS_IsMainThread()) { + mHolder.Reset(); + } else if (mHolder.GetISupports()) { + nsCOMPtr target = do_GetMainThread(); + MOZ_ASSERT(target); + NS_ProxyRelease( +#ifdef RELEASE_OR_BETA + nullptr, +#else + mName, +#endif + target, mHolder.Forget()); + } + } + +public: + Holder* get() + { + // Nobody should be touching the raw pointer off-main-thread. + if (MOZ_UNLIKELY(!NS_IsMainThread())) { + NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread"); + MOZ_CRASH(); + } + return &mHolder; + } + + bool operator!() const + { + return !mHolder; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder) + +private: + // Our holder. + Holder mHolder; + +#ifndef RELEASE_OR_BETA + const char* mName = nullptr; +#endif + + // Copy constructor and operator= not implemented. Once constructed, the + // holder is immutable. + Holder& operator=(const nsMainThreadPtrHolder& aOther) = delete; + nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther) = delete; +}; + namespace { already_AddRefed GetShutdownPhase() { nsCOMPtr svc = mozilla::services::GetAsyncShutdown(); @@ -231,6 +302,30 @@ FromCaptureState(CaptureState aState) return static_cast(aState); } +static void +CallOnError(MediaManager::GetUserMediaErrorCallback* aCallback, + MediaStreamError& aError) +{ + MOZ_ASSERT(aCallback); + if (aCallback->HasWebIDLCallback()) { + aCallback->GetWebIDLCallback()->Call(aError); + } else { + aCallback->GetXPCOMCallback()->OnError(&aError); + } +} + +static void +CallOnSuccess(MediaManager::GetUserMediaSuccessCallback* aCallback, + DOMMediaStream& aStream) +{ + MOZ_ASSERT(aCallback); + if (aCallback->HasWebIDLCallback()) { + aCallback->GetWebIDLCallback()->Call(aStream); + } else { + aCallback->GetXPCOMCallback()->OnSuccess(&aStream); + } +} + /** * SourceListener has threadsafe refcounting for use across the main, media and * MSG threads. But it has a non-threadsafe SupportsWeakPtr for WeakPtr usage @@ -776,7 +871,7 @@ private: class ErrorCallbackRunnable : public Runnable { public: - ErrorCallbackRunnable(const nsMainThreadPtrHandle& aOnFailure, + ErrorCallbackRunnable(const nsMainThreadPtrHandle& aOnFailure, MediaMgrError& aError, uint64_t aWindowID) : Runnable("ErrorCallbackRunnable") @@ -801,14 +896,14 @@ public: if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID)) { RefPtr error = new MediaStreamError(window->AsInner(), *mError); - mOnFailure->OnError(error); + CallOnError(mOnFailure, *error); } return NS_OK; } private: ~ErrorCallbackRunnable() override = default; - nsMainThreadPtrHandle mOnFailure; + nsMainThreadPtrHandle mOnFailure; RefPtr mError; uint64_t mWindowID; RefPtr mManager; // get ref to this when creating the runnable @@ -1164,8 +1259,8 @@ class GetUserMediaStreamRunnable : public Runnable { public: GetUserMediaStreamRunnable( - const nsMainThreadPtrHandle& aOnSuccess, - const nsMainThreadPtrHandle& aOnFailure, + const nsMainThreadPtrHandle& aOnSuccess, + const nsMainThreadPtrHandle& aOnFailure, uint64_t aWindowID, GetUserMediaWindowListener* aWindowListener, SourceListener* aSourceListener, @@ -1196,7 +1291,7 @@ public: { public: TracksAvailableCallback(MediaManager* aManager, - const nsMainThreadPtrHandle& aSuccess, + const nsMainThreadPtrHandle& aSuccess, const RefPtr& aWindowListener, DOMMediaStream* aStream) : mWindowListener(aWindowListener), @@ -1218,10 +1313,10 @@ public: // This is safe since we're on main-thread, and the windowlist can only // be invalidated from the main-thread (see OnNavigation) LOG(("Returning success for getUserMedia()")); - mOnSuccess->OnSuccess(aStream); + CallOnSuccess(mOnSuccess, *aStream); } RefPtr mWindowListener; - nsMainThreadPtrHandle mOnSuccess; + nsMainThreadPtrHandle mOnSuccess; RefPtr mManager; // Keep the DOMMediaStream alive until the NotifyTracksAvailable callback // has fired, otherwise we might immediately destroy the DOMMediaStream and @@ -1459,7 +1554,7 @@ public: MediaStreamError::Name::AbortError, sHasShutdown ? NS_LITERAL_STRING("In shutdown") : NS_LITERAL_STRING("No stream.")); - mOnFailure->OnError(error); + CallOnError(mOnFailure, *error); } return NS_OK; } @@ -1511,7 +1606,7 @@ public: // be invalidated from the main-thread (see OnNavigation) if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(windowID)) { auto streamError = MakeRefPtr(window->AsInner(), *error); - onFailure->OnError(streamError); + CallOnError(onFailure, *streamError); } }); @@ -1525,8 +1620,8 @@ public: } private: - nsMainThreadPtrHandle mOnSuccess; - nsMainThreadPtrHandle mOnFailure; + nsMainThreadPtrHandle mOnSuccess; + nsMainThreadPtrHandle mOnFailure; MediaStreamConstraints mConstraints; RefPtr mAudioDevice; RefPtr mVideoDevice; @@ -1672,8 +1767,8 @@ class GetUserMediaTask : public Runnable public: GetUserMediaTask( const MediaStreamConstraints& aConstraints, - const nsMainThreadPtrHandle& aOnSuccess, - const nsMainThreadPtrHandle& aOnFailure, + const nsMainThreadPtrHandle& aOnSuccess, + const nsMainThreadPtrHandle& aOnFailure, uint64_t aWindowID, GetUserMediaWindowListener* aWindowListener, SourceListener* aSourceListener, @@ -1822,7 +1917,7 @@ public: if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID)) { RefPtr error = new MediaStreamError(window->AsInner(), aName, aMessage); - mOnFailure->OnError(error); + CallOnError(mOnFailure, *error); } // Should happen *after* error runs for consistency, but may not matter mWindowListener->Remove(mSourceListener); @@ -1872,8 +1967,8 @@ public: private: MediaStreamConstraints mConstraints; - nsMainThreadPtrHandle mOnSuccess; - nsMainThreadPtrHandle mOnFailure; + nsMainThreadPtrHandle mOnSuccess; + nsMainThreadPtrHandle mOnFailure; uint64_t mWindowID; RefPtr mWindowListener; RefPtr mSourceListener; @@ -2497,20 +2592,20 @@ ReduceConstraint( nsresult MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, const MediaStreamConstraints& aConstraintsPassedIn, - nsIDOMGetUserMediaSuccessCallback* aOnSuccess, - nsIDOMGetUserMediaErrorCallback* aOnFailure, + GetUserMediaSuccessCallback&& aOnSuccess, + GetUserMediaErrorCallback&& aOnFailure, dom::CallerType aCallerType) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aWindow); - MOZ_ASSERT(aOnFailure); - MOZ_ASSERT(aOnSuccess); - nsMainThreadPtrHandle onSuccess( - new nsMainThreadPtrHolder( - "GetUserMedia::SuccessCallback", aOnSuccess)); - nsMainThreadPtrHandle onFailure( - new nsMainThreadPtrHolder( - "GetUserMedia::FailureCallback", aOnFailure)); + MOZ_ASSERT(aOnFailure.GetISupports()); + MOZ_ASSERT(aOnSuccess.GetISupports()); + nsMainThreadPtrHandle onSuccess( + new nsMainThreadPtrHolder( + "GetUserMedia::SuccessCallback", std::move(aOnSuccess))); + nsMainThreadPtrHandle onFailure( + new nsMainThreadPtrHolder( + "GetUserMedia::FailureCallback", std::move(aOnFailure))); uint64_t windowID = aWindow->WindowID(); MediaStreamConstraints c(aConstraintsPassedIn); // use a modifiable copy @@ -2523,14 +2618,14 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, new MediaStreamError(aWindow, MediaStreamError::Name::TypeError, NS_LITERAL_STRING("audio and/or video is required")); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } if (!IsFullyActive(aWindow)) { RefPtr error = new MediaStreamError(aWindow, MediaStreamError::Name::InvalidStateError); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } @@ -2539,7 +2634,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, new MediaStreamError(aWindow, MediaStreamError::Name::AbortError, NS_LITERAL_STRING("In shutdown")); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } @@ -2652,7 +2747,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, RefPtr error = new MediaStreamError(aWindow, MediaStreamError::Name::NotAllowedError); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } break; @@ -2665,7 +2760,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, MediaStreamError::Name::OverconstrainedError, NS_LITERAL_STRING(""), NS_LITERAL_STRING("mediaSource")); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } } @@ -2732,7 +2827,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, RefPtr error = new MediaStreamError(aWindow, MediaStreamError::Name::NotAllowedError); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } break; @@ -2744,7 +2839,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, MediaStreamError::Name::OverconstrainedError, NS_LITERAL_STRING(""), NS_LITERAL_STRING("mediaSource")); - onFailure->OnError(error); + CallOnError(onFailure, *error); return NS_OK; } } @@ -2815,7 +2910,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION)) { RefPtr error = new MediaStreamError(aWindow, MediaStreamError::Name::NotAllowedError); - onFailure->OnError(error); + CallOnError(onFailure, *error); windowListener->Remove(sourceListener); return NS_OK; } @@ -2925,7 +3020,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, MediaStreamError::Name::OverconstrainedError, NS_LITERAL_STRING(""), constraint); - onFailure->OnError(error); + CallOnError(onFailure, *error); return; } if (!(*devices)->Length()) { @@ -2939,7 +3034,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, // device, so report NotAllowedError. resistFingerprinting ? MediaStreamError::Name::NotAllowedError : MediaStreamError::Name::NotFoundError); - onFailure->OnError(error); + CallOnError(onFailure, *error); return; } @@ -3000,11 +3095,11 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow, #endif }, [onFailure](MediaStreamError*& reason) mutable { LOG(("GetUserMedia: post enumeration pledge2 failure callback called!")); - onFailure->OnError(reason); + CallOnError(onFailure, *reason); }); }, [onFailure](MediaStreamError*& reason) mutable { LOG(("GetUserMedia: post enumeration pledge failure callback called!")); - onFailure->OnError(reason); + CallOnError(onFailure, *reason); }); return NS_OK; } diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index 8a166daea5b3..e33eac2ac9b7 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -29,6 +29,7 @@ #include "mozilla/dom/MediaStreamBinding.h" #include "mozilla/dom/MediaStreamTrackBinding.h" #include "mozilla/dom/MediaStreamError.h" +#include "mozilla/dom/NavigatorBinding.h" #include "mozilla/media/MediaChild.h" #include "mozilla/media/MediaParent.h" #include "mozilla/Logging.h" @@ -202,11 +203,16 @@ public: void RemoveFromWindowList(uint64_t aWindowID, GetUserMediaWindowListener *aListener); + typedef dom::CallbackObjectHolder GetUserMediaSuccessCallback; + typedef dom::CallbackObjectHolder GetUserMediaErrorCallback; + nsresult GetUserMedia( nsPIDOMWindowInner* aWindow, const dom::MediaStreamConstraints& aConstraints, - nsIDOMGetUserMediaSuccessCallback* onSuccess, - nsIDOMGetUserMediaErrorCallback* onError, + GetUserMediaSuccessCallback&& onSuccess, + GetUserMediaErrorCallback&& onError, dom::CallerType aCallerType); nsresult GetUserMediaDevices(nsPIDOMWindowInner* aWindow, diff --git a/dom/media/webspeech/recognition/SpeechRecognition.cpp b/dom/media/webspeech/recognition/SpeechRecognition.cpp index cdba6c81f49a..c89cca970fa3 100644 --- a/dom/media/webspeech/recognition/SpeechRecognition.cpp +++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp @@ -755,10 +755,14 @@ SpeechRecognition::Start(const Optional>& aStream, } else { AutoNoJSAPI(); MediaManager* manager = MediaManager::Get(); + MediaManager::GetUserMediaSuccessCallback onsuccess( + new GetUserMediaSuccessCallback(this)); + MediaManager::GetUserMediaErrorCallback onerror( + new GetUserMediaErrorCallback(this)); manager->GetUserMedia(GetOwner(), constraints, - new GetUserMediaSuccessCallback(this), - new GetUserMediaErrorCallback(this), + std::move(onsuccess), + std::move(onerror), aCallerType); } From 6748ffcaed51611d94ab82964cd268c947d0233b Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Sat, 28 Jul 2018 00:40:29 -0400 Subject: [PATCH 21/21] Bug 1478890. Stop using ToXPCOMCallback in GetUserMediaDevices. r=jib --- dom/base/Navigator.cpp | 12 ++---------- dom/media/MediaDevices.cpp | 2 +- dom/media/MediaManager.cpp | 7 ++----- dom/media/MediaManager.h | 3 +-- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index cce0d7a715f9..37729813e925 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1200,15 +1200,6 @@ Navigator::MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints, const nsAString& aCallID, ErrorResult& aRv) { - CallbackObjectHolder holder1(&aOnSuccess); - nsCOMPtr onsuccess = - holder1.ToXPCOMCallback(); - - CallbackObjectHolder holder2(&aOnError); - nsCOMPtr onerror = holder2.ToXPCOMCallback(); - if (!mWindow || !mWindow->GetOuterWindow() || mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); @@ -1216,7 +1207,8 @@ Navigator::MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints, } MediaManager* manager = MediaManager::Get(); - aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror, + // XXXbz aOnError seems to be unused? + aRv = manager->GetUserMediaDevices(mWindow, aConstraints, aOnSuccess, aInnerWindowID, aCallID); } diff --git a/dom/media/MediaDevices.cpp b/dom/media/MediaDevices.cpp index 40e4d8de53aa..34bce4dd6203 100644 --- a/dom/media/MediaDevices.cpp +++ b/dom/media/MediaDevices.cpp @@ -188,7 +188,7 @@ MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints, aRv = MediaManager::Get()->GetUserMedia(GetOwner(), aConstraints, std::move(resolver), - std::move(rejecter), + std::move(rejecter), aCallerType); return p.forget(); } diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index c220003e1711..69fa73976962 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -3388,14 +3388,11 @@ MediaManager::EnumerateDevices(nsPIDOMWindowInner* aWindow, nsresult MediaManager::GetUserMediaDevices(nsPIDOMWindowInner* aWindow, const MediaStreamConstraints& aConstraints, - nsIGetUserMediaDevicesSuccessCallback* aOnSuccess, - nsIDOMGetUserMediaErrorCallback* aOnFailure, + dom::MozGetUserMediaDevicesSuccessCallback& aOnSuccess, uint64_t aWindowId, const nsAString& aCallID) { MOZ_ASSERT(NS_IsMainThread()); - nsCOMPtr onSuccess(aOnSuccess); - nsCOMPtr onFailure(aOnFailure); if (!aWindowId) { aWindowId = aWindow->WindowID(); } @@ -3412,7 +3409,7 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindowInner* aWindow, if (!aCallID.Length() || aCallID == callID) { if (mActiveCallbacks.Get(callID, getter_AddRefs(task))) { nsCOMPtr array = MediaManager_ToJSArray(*task->mMediaDeviceSet); - onSuccess->OnSuccess(array); + aOnSuccess.Call(array); return NS_OK; } } diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index e33eac2ac9b7..a0882d61a0be 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -217,8 +217,7 @@ public: nsresult GetUserMediaDevices(nsPIDOMWindowInner* aWindow, const dom::MediaStreamConstraints& aConstraints, - nsIGetUserMediaDevicesSuccessCallback* onSuccess, - nsIDOMGetUserMediaErrorCallback* onError, + dom::MozGetUserMediaDevicesSuccessCallback& aOnSuccess, uint64_t aInnerWindowID = 0, const nsAString& aCallID = nsString());