Bug 1825722 - Add an ArgumentsLength parse node r=arai

By adding a separate parse node for `arguments.length` we will be able to
do bytecode generation differently in some circumstances.

Differential Revision: https://phabricator.services.mozilla.com/D203143
This commit is contained in:
Matthew Gaudet 2024-03-04 16:25:47 +00:00
Родитель eda5af5f2d
Коммит 11e59aece7
8 изменённых файлов: 111 добавлений и 10 удалений

Просмотреть файл

@ -2885,7 +2885,8 @@ bool ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) {
}
case ParseNodeKind::DotExpr:
case ParseNodeKind::OptionalDotExpr: {
case ParseNodeKind::OptionalDotExpr:
case ParseNodeKind::ArgumentsLength: {
PropertyAccessBase* prop = &pn->as<PropertyAccessBase>();
MOZ_ASSERT(prop->pn_pos.encloses(prop->expression().pn_pos));

Просмотреть файл

@ -925,6 +925,7 @@ restart:
// Watch out for getters!
case ParseNodeKind::OptionalDotExpr:
case ParseNodeKind::DotExpr:
case ParseNodeKind::ArgumentsLength:
MOZ_ASSERT(pn->is<BinaryNode>());
*answer = true;
return true;
@ -4367,6 +4368,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
: NameOpEmitter::Kind::SimpleAssignment);
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
PropertyAccess* prop = &lhs->as<PropertyAccess>();
bool isSuper = prop->isSuper();
@ -4466,6 +4468,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
if (isCompound) {
MOZ_ASSERT(rhs);
switch (lhs->getKind()) {
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
PropertyAccess* prop = &lhs->as<PropertyAccess>();
if (!poe->emitGet(prop->key().atom())) {
@ -4512,6 +4515,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
}
offset += noe->emittedBindOp();
break;
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr:
if (!poe->prepareForRhs()) {
// [stack] # if Simple Assignment with Super
@ -4579,6 +4583,7 @@ bool BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs,
}
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
PropertyAccess* prop = &lhs->as<PropertyAccess>();
if (!poe->emitAssignment(prop->key().atom())) {
@ -4666,7 +4671,7 @@ bool BytecodeEmitter::emitShortCircuitAssignment(AssignmentNode* node) {
numPushed = noe->emittedBindOp();
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
PropertyAccess* prop = &lhs->as<PropertyAccess>();
bool isSuper = prop->isSuper();
@ -4802,7 +4807,7 @@ bool BytecodeEmitter::emitShortCircuitAssignment(AssignmentNode* node) {
}
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
PropertyAccess* prop = &lhs->as<PropertyAccess>();
@ -7326,6 +7331,7 @@ bool BytecodeEmitter::emitDeleteOptionalChain(UnaryNode* deleteNode) {
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr:
case ParseNodeKind::OptionalDotExpr: {
auto* propExpr = &kid->as<PropertyAccessBase>();
@ -7978,6 +7984,7 @@ bool BytecodeEmitter::emitOptionalCalleeAndThis(ParseNode* callee,
}
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
PropertyAccess* prop = &callee->as<PropertyAccess>();
@ -8076,6 +8083,7 @@ bool BytecodeEmitter::emitCalleeAndThis(ParseNode* callee, CallNode* maybeCall,
}
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
PropertyAccess* prop = &callee->as<PropertyAccess>();
@ -8197,6 +8205,7 @@ ParseNode* BytecodeEmitter::getCoordNode(ParseNode* callNode,
coordNode = argsList;
switch (calleeNode->getKind()) {
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr:
// Use the position of a property access identifier.
//
@ -8658,6 +8667,7 @@ bool BytecodeEmitter::emitOptionalTree(
}
break;
}
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr: {
PropertyAccess* prop = &pn->as<PropertyAccess>();
bool isSuper = prop->isSuper();
@ -9015,6 +9025,7 @@ bool BytecodeEmitter::emitSequenceExpr(ListNode* node, ValueUsage valueUsage) {
MOZ_NEVER_INLINE bool BytecodeEmitter::emitIncOrDec(UnaryNode* incDec,
ValueUsage valueUsage) {
switch (incDec->kid()->getKind()) {
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::DotExpr:
return emitPropIncDec(incDec, valueUsage);
case ParseNodeKind::ElemExpr:
@ -12502,6 +12513,24 @@ bool BytecodeEmitter::emitTree(
break;
}
case ParseNodeKind::ArgumentsLength: {
PropOpEmitter poe(this, PropOpEmitter::Kind::Get,
PropOpEmitter::ObjKind::Other);
if (!poe.prepareForObj()) {
return false;
}
NameOpEmitter noe(this, TaggedParserAtomIndex::WellKnown::arguments(),
NameOpEmitter::Kind::Get);
if (!noe.emitGet()) {
return false;
}
if (!poe.emitGet(TaggedParserAtomIndex::WellKnown::length())) {
return false;
}
break;
}
case ParseNodeKind::ElemExpr: {
PropertyByValue* elem = &pn->as<PropertyByValue>();
bool isSuper = elem->isSuper();

Просмотреть файл

@ -407,6 +407,7 @@ restart:
case ParseNodeKind::ObjectExpr:
case ParseNodeKind::PropertyNameExpr:
case ParseNodeKind::DotExpr:
case ParseNodeKind::ArgumentsLength:
case ParseNodeKind::ElemExpr:
case ParseNodeKind::Arguments:
case ParseNodeKind::CallExpr:

Просмотреть файл

@ -103,7 +103,8 @@ class FullParseHandler {
bool isPropertyOrPrivateMemberAccess(Node node) {
return node->isKind(ParseNodeKind::DotExpr) ||
node->isKind(ParseNodeKind::ElemExpr) ||
node->isKind(ParseNodeKind::PrivateMemberExpr);
node->isKind(ParseNodeKind::PrivateMemberExpr) ||
node->isKind(ParseNodeKind::ArgumentsLength);
}
bool isOptionalPropertyOrPrivateMemberAccess(Node node) {
@ -887,6 +888,11 @@ class FullParseHandler {
key->pn_pos.end);
}
ArgumentsLengthResult newArgumentsLength(Node expr, NameNodeType key) {
return newResult<ArgumentsLength>(expr, key, expr->pn_pos.begin,
key->pn_pos.end);
}
PropertyByValueResult newPropertyByValue(Node lhs, Node index, uint32_t end) {
return newResult<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
}
@ -1137,6 +1143,12 @@ class FullParseHandler {
TaggedParserAtomIndex::WellKnown::arguments();
}
bool isLengthName(Node node) {
return node->isKind(ParseNodeKind::PropertyNameExpr) &&
node->as<NameNode>().atom() ==
TaggedParserAtomIndex::WellKnown::length();
}
bool isEvalName(Node node) {
return node->isKind(ParseNodeKind::Name) &&
node->as<NameNode>().atom() ==

Просмотреть файл

@ -75,6 +75,7 @@ class FunctionBox;
F(PostDecrementExpr, UnaryNode) \
F(PropertyNameExpr, NameNode) \
F(DotExpr, PropertyAccess) \
F(ArgumentsLength, ArgumentsLength) \
F(ElemExpr, PropertyByValue) \
F(PrivateMemberExpr, PrivateMemberAccess) \
F(OptionalDotExpr, OptionalPropertyAccess) \
@ -616,6 +617,7 @@ inline bool IsTypeofKind(ParseNodeKind kind) {
MACRO(ClassNames) \
MACRO(ForNode) \
MACRO(PropertyAccess) \
MACRO(ArgumentsLength) \
MACRO(OptionalPropertyAccess) \
MACRO(PropertyByValue) \
MACRO(OptionalPropertyByValue) \
@ -2014,7 +2016,8 @@ class PropertyAccessBase : public BinaryNode {
static bool test(const ParseNode& node) {
bool match = node.isKind(ParseNodeKind::DotExpr) ||
node.isKind(ParseNodeKind::OptionalDotExpr);
node.isKind(ParseNodeKind::OptionalDotExpr) ||
node.isKind(ParseNodeKind::ArgumentsLength);
MOZ_ASSERT_IF(match, node.is<BinaryNode>());
MOZ_ASSERT_IF(match, node.as<BinaryNode>().right()->isKind(
ParseNodeKind::PropertyNameExpr));
@ -2042,7 +2045,8 @@ class PropertyAccess : public PropertyAccessBase {
}
static bool test(const ParseNode& node) {
bool match = node.isKind(ParseNodeKind::DotExpr);
bool match = node.isKind(ParseNodeKind::DotExpr) ||
node.isKind(ParseNodeKind::ArgumentsLength);
MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>());
return match;
}
@ -2051,6 +2055,26 @@ class PropertyAccess : public PropertyAccessBase {
// ParseNodeKind::SuperBase cannot result from any expression syntax.
return expression().isKind(ParseNodeKind::SuperBase);
}
protected:
using PropertyAccessBase::PropertyAccessBase;
};
class ArgumentsLength : public PropertyAccess {
public:
ArgumentsLength(ParseNode* lhs, NameNode* name, uint32_t begin, uint32_t end)
: PropertyAccess(ParseNodeKind::ArgumentsLength, lhs, name, begin, end) {
MOZ_ASSERT(lhs);
MOZ_ASSERT(name);
}
static bool test(const ParseNode& node) {
bool match = node.isKind(ParseNodeKind::ArgumentsLength);
MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>());
return match;
}
bool isSuper() const { return false; }
};
class OptionalPropertyAccess : public PropertyAccessBase {

Просмотреть файл

@ -10926,6 +10926,11 @@ GeneralParser<ParseHandler, Unit>::memberPropertyAccess(
MOZ_ASSERT(!handler_.isSuperBase(lhs));
return handler_.newOptionalPropertyAccess(lhs, name);
}
if (handler_.isArgumentsName(lhs) && handler_.isLengthName(name)) {
return handler_.newArgumentsLength(lhs, name);
}
return handler_.newPropertyAccess(lhs, name);
}

Просмотреть файл

@ -57,8 +57,9 @@ enum SyntaxParseHandlerNode {
// casing.
NodeName,
// Nodes representing the names "arguments" and "eval".
// Nodes representing the names "arguments", "length" and "eval".
NodeArgumentsName,
NodeLengthName,
NodeEvalName,
// Node representing the "async" name, which may actually be a
@ -77,6 +78,10 @@ enum SyntaxParseHandlerNode {
NodePrivateMemberAccess,
NodeOptionalPrivateMemberAccess,
// Node representing the compound Arguments.length expression;
// Used only for property access, not assignment.
NodeArgumentsLength,
// Destructuring target patterns can't be parenthesized: |([a]) = [3];|
// must be a syntax error. (We can't use NodeGeneric instead of these
// because that would trigger invalid-left-hand-side ReferenceError
@ -164,7 +169,7 @@ class SyntaxParseHandler {
bool isPropertyOrPrivateMemberAccess(Node node) {
return node == NodeDottedProperty || node == NodeElement ||
node == NodePrivateMemberAccess;
node == NodePrivateMemberAccess || node == NodeArgumentsLength;
}
bool isOptionalPropertyOrPrivateMemberAccess(Node node) {
@ -210,6 +215,9 @@ class SyntaxParseHandler {
if (name == TaggedParserAtomIndex::WellKnown::arguments()) {
return NodeArgumentsName;
}
if (name == TaggedParserAtomIndex::WellKnown::length()) {
return NodeLengthName;
}
if (pos.begin + strlen("async") == pos.end &&
name == TaggedParserAtomIndex::WellKnown::async()) {
return NodePotentialAsyncKeyword;
@ -579,6 +587,10 @@ class SyntaxParseHandler {
return NodeDottedProperty;
}
PropertyAccessResult newArgumentsLength(Node expr, NameNodeType key) {
return NodeArgumentsLength;
}
PropertyAccessResult newOptionalPropertyAccess(Node expr, NameNodeType key) {
return NodeOptionalDottedProperty;
}
@ -777,10 +789,12 @@ class SyntaxParseHandler {
bool isName(Node node) {
return node == NodeName || node == NodeArgumentsName ||
node == NodeEvalName || node == NodePotentialAsyncKeyword;
node == NodeLengthName || node == NodeEvalName ||
node == NodePotentialAsyncKeyword;
}
bool isArgumentsName(Node node) { return node == NodeArgumentsName; }
bool isLengthName(Node node) { return node == NodeLengthName; }
bool isEvalName(Node node) { return node == NodeEvalName; }
bool isAsyncKeyword(Node node) { return node == NodePotentialAsyncKeyword; }
@ -795,7 +809,8 @@ class SyntaxParseHandler {
// |this|. It's not really eligible for the funapply/funcall
// optimizations as they're currently implemented (assuming a single
// value is used for both retrieval and |this|).
if (node != NodeDottedProperty && node != NodeOptionalDottedProperty) {
if (node != NodeDottedProperty && node != NodeOptionalDottedProperty &&
node != NodeArgumentsLength) {
return TaggedParserAtomIndex::null();
}
return lastAtom;

Просмотреть файл

@ -0,0 +1,14 @@
// |reftest| skip-if(!xulRuntime.shell)
// Test reflect.parse on a function with arguments.length
let ast = Reflect.parse(`function f10() {
return arguments.length;
}`);
assertEq(ast.body[0].body.body[0].argument.object.type, "Identifier");
assertEq(ast.body[0].body.body[0].argument.object.name, "arguments");
assertEq(ast.body[0].body.body[0].argument.property.type, "Identifier");
assertEq(ast.body[0].body.body[0].argument.property.name, "length");
if (typeof reportCompare === "function")
reportCompare(0, 0, "ok");