diff options
Diffstat (limited to 'js/src/frontend')
-rw-r--r-- | js/src/frontend/BytecodeEmitter.cpp | 255 | ||||
-rw-r--r-- | js/src/frontend/CompileScript.cpp | 81 | ||||
-rw-r--r-- | js/src/frontend/FoldConstants.cpp | 1 | ||||
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 18 | ||||
-rw-r--r-- | js/src/frontend/NameFunctions.cpp | 1 | ||||
-rw-r--r-- | js/src/frontend/ParseContext.cpp | 69 | ||||
-rw-r--r-- | js/src/frontend/ParseContext.h | 11 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.h | 28 | ||||
-rw-r--r-- | js/src/frontend/Parser.cpp | 36 | ||||
-rw-r--r-- | js/src/frontend/SharedContext.cpp | 3 | ||||
-rw-r--r-- | js/src/frontend/SharedContext.h | 9 | ||||
-rw-r--r-- | js/src/frontend/Stencil.cpp | 12 | ||||
-rw-r--r-- | js/src/frontend/SyntaxParseHandler.h | 25 | ||||
-rw-r--r-- | js/src/frontend/UsedNameTracker.h | 4 | ||||
-rwxr-xr-x | js/src/frontend/align_stack_comment.py | 2 |
15 files changed, 372 insertions, 183 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index df44743768..2759fa5924 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -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; @@ -2601,6 +2602,7 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, *emitted = 0; break; + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &target->as<PropertyAccess>(); bool isSuper = prop->isSuper(); @@ -2760,6 +2762,7 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { // The reference is already pushed by emitDestructuringLHSRef. // [stack] # if Super @@ -4367,6 +4370,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 +4470,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 +4517,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 +4585,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 +4673,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 +4809,7 @@ bool BytecodeEmitter::emitShortCircuitAssignment(AssignmentNode* node) { } break; } - + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &lhs->as<PropertyAccess>(); @@ -7326,6 +7333,7 @@ bool BytecodeEmitter::emitDeleteOptionalChain(UnaryNode* deleteNode) { break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: case ParseNodeKind::OptionalDotExpr: { auto* propExpr = &kid->as<PropertyAccessBase>(); @@ -7978,6 +7986,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 +8085,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 +8207,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 +8669,7 @@ bool BytecodeEmitter::emitOptionalTree( } break; } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &pn->as<PropertyAccess>(); bool isSuper = prop->isSuper(); @@ -9015,6 +9027,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: @@ -9859,6 +9872,12 @@ static bool NeedsPrivateBrand(ParseNode* member) { !member->as<ClassMethod>().isStatic(); } +#ifdef ENABLE_DECORATORS +static bool HasDecorators(ParseNode* member) { + return member->is<ClassMethod>() && member->as<ClassMethod>().decorators(); +} +#endif + mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( ListNode* classMembers, FieldPlacement placement) { bool isStatic = placement == FieldPlacement::Static; @@ -9866,6 +9885,9 @@ mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( size_t numFields = 0; size_t numPrivateInitializers = 0; bool hasPrivateBrand = false; +#ifdef ENABLE_DECORATORS + bool hasDecorators = false; +#endif for (ParseNode* member : classMembers->contents()) { if (NeedsFieldInitializer(member, isStatic)) { numFields++; @@ -9875,6 +9897,11 @@ mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( } else if (NeedsPrivateBrand(member)) { hasPrivateBrand = true; } +#ifdef ENABLE_DECORATORS + if (!hasDecorators && HasDecorators(member)) { + hasDecorators = true; + } +#endif } // If there are more initializers than can be represented, return invalid. @@ -9882,8 +9909,11 @@ mozilla::Maybe<MemberInitializers> BytecodeEmitter::setupMemberInitializers( MemberInitializers::MaxInitializers) { return Nothing(); } - return Some( - MemberInitializers(hasPrivateBrand, numFields + numPrivateInitializers)); + return Some(MemberInitializers(hasPrivateBrand, +#ifdef ENABLE_DECORATORS + hasDecorators, +#endif + numFields + numPrivateInitializers)); } // Purpose of .fieldKeys: @@ -10691,119 +10721,122 @@ bool BytecodeEmitter::emitInitializeInstanceMembers( } } #ifdef ENABLE_DECORATORS - // Decorators Proposal - // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-initializeinstanceelements - // 4. For each element e of elements, do - // 4.a. If elementRecord.[[Kind]] is field or accessor, then - // 4.a.i. Perform ? InitializeFieldOrAccessor(O, elementRecord). - // + if (memberInitializers.hasDecorators) { + // Decorators Proposal + // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-initializeinstanceelements + // 4. For each element e of elements, do + // 4.a. If elementRecord.[[Kind]] is field or accessor, then + // 4.a.i. Perform ? InitializeFieldOrAccessor(O, elementRecord). + // - // TODO: (See Bug 1817993) At the moment, we're applying the initialization - // logic in two steps. The pre-decorator initialization code runs, stores - // the initial value, and then we retrieve it here and apply the - // initializers added by decorators. We should unify these two steps. - if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) { - // [stack] ARRAY - return false; - } + // TODO: (See Bug 1817993) At the moment, we're applying the + // initialization logic in two steps. The pre-decorator initialization + // code runs, stores the initial value, and then we retrieve it here and + // apply the initializers added by decorators. We should unify these two + // steps. + if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) { + // [stack] ARRAY + return false; + } - if (!emit1(JSOp::Dup)) { - // [stack] ARRAY ARRAY - return false; - } + if (!emit1(JSOp::Dup)) { + // [stack] ARRAY ARRAY + return false; + } - if (!emitAtomOp(JSOp::GetProp, - TaggedParserAtomIndex::WellKnown::length())) { - // [stack] ARRAY LENGTH - return false; - } + if (!emitAtomOp(JSOp::GetProp, + TaggedParserAtomIndex::WellKnown::length())) { + // [stack] ARRAY LENGTH + return false; + } - if (!emitNumberOp(static_cast<double>(numInitializers))) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!emitNumberOp(static_cast<double>(numInitializers))) { + // [stack] ARRAY LENGTH INDEX + return false; + } - InternalWhileEmitter wh(this); - // At this point, we have no context to determine offsets in the - // code for this while statement. Ideally, it would correspond to - // the field we're initializing. - if (!wh.emitCond()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + InternalWhileEmitter wh(this); + // At this point, we have no context to determine offsets in the + // code for this while statement. Ideally, it would correspond to + // the field we're initializing. + if (!wh.emitCond()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emit1(JSOp::Dup)) { - // [stack] ARRAY LENGTH INDEX INDEX - return false; - } + if (!emit1(JSOp::Dup)) { + // [stack] ARRAY LENGTH INDEX INDEX + return false; + } - if (!emitDupAt(2)) { - // [stack] ARRAY LENGTH INDEX INDEX LENGTH - return false; - } + if (!emitDupAt(2)) { + // [stack] ARRAY LENGTH INDEX INDEX LENGTH + return false; + } - if (!emit1(JSOp::Lt)) { - // [stack] ARRAY LENGTH INDEX BOOL - return false; - } + if (!emit1(JSOp::Lt)) { + // [stack] ARRAY LENGTH INDEX BOOL + return false; + } - if (!wh.emitBody()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!wh.emitBody()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emitDupAt(2)) { - // [stack] ARRAY LENGTH INDEX ARRAY - return false; - } + if (!emitDupAt(2)) { + // [stack] ARRAY LENGTH INDEX ARRAY + return false; + } - if (!emitDupAt(1)) { - // [stack] ARRAY LENGTH INDEX ARRAY INDEX - return false; - } + if (!emitDupAt(1)) { + // [stack] ARRAY LENGTH INDEX ARRAY INDEX + return false; + } - // Retrieve initializers for this field - if (!emit1(JSOp::GetElem)) { - // [stack] ARRAY LENGTH INDEX INITIALIZERS - return false; - } + // Retrieve initializers for this field + if (!emit1(JSOp::GetElem)) { + // [stack] ARRAY LENGTH INDEX INITIALIZERS + return false; + } - // This is guaranteed to run after super(), so we don't need TDZ checks. - if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) { - // [stack] ARRAY LENGTH INDEX INITIALIZERS THIS - return false; - } + // This is guaranteed to run after super(), so we don't need TDZ checks. + if (!emitGetName(TaggedParserAtomIndex::WellKnown::dot_this_())) { + // [stack] ARRAY LENGTH INDEX INITIALIZERS THIS + return false; + } - if (!emit1(JSOp::Swap)) { - // [stack] ARRAY LENGTH INDEX THIS INITIALIZERS - return false; - } + if (!emit1(JSOp::Swap)) { + // [stack] ARRAY LENGTH INDEX THIS INITIALIZERS + return false; + } - DecoratorEmitter de(this); - if (!de.emitInitializeFieldOrAccessor()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + DecoratorEmitter de(this); + if (!de.emitInitializeFieldOrAccessor()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emit1(JSOp::Inc)) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!emit1(JSOp::Inc)) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!wh.emitEnd()) { - // [stack] ARRAY LENGTH INDEX - return false; - } + if (!wh.emitEnd()) { + // [stack] ARRAY LENGTH INDEX + return false; + } - if (!emitPopN(3)) { - // [stack] - return false; - } - // 5. Return unused. + if (!emitPopN(3)) { + // [stack] + return false; + } + // 5. Return unused. - if (!de.emitCallExtraInitializers(TaggedParserAtomIndex::WellKnown:: - dot_instanceExtraInitializers_())) { - return false; + if (!de.emitCallExtraInitializers(TaggedParserAtomIndex::WellKnown:: + dot_instanceExtraInitializers_())) { + return false; + } } #endif } @@ -12502,6 +12535,32 @@ bool BytecodeEmitter::emitTree( break; } + case ParseNodeKind::ArgumentsLength: { + if (sc->isFunctionBox() && + sc->asFunctionBox()->isEligibleForArgumentsLength() && + !sc->asFunctionBox()->needsArgsObj()) { + if (!emit1(JSOp::ArgumentsLength)) { + return false; + } + } else { + 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(); diff --git a/js/src/frontend/CompileScript.cpp b/js/src/frontend/CompileScript.cpp index 925b8201a2..b561d7d124 100644 --- a/js/src/frontend/CompileScript.cpp +++ b/js/src/frontend/CompileScript.cpp @@ -87,73 +87,46 @@ JS_PUBLIC_API const JSErrorReport* JS::GetFrontendWarningAt( return &fc->warnings()[index]; } -JS::CompilationStorage::~CompilationStorage() { - if (input_ && !isBorrowed_) { - js_delete(input_); - input_ = nullptr; - } -} - -size_t JS::CompilationStorage::sizeOfIncludingThis( - mozilla::MallocSizeOf mallocSizeOf) const { - size_t sizeOfCompilationInput = - input_ ? input_->sizeOfExcludingThis(mallocSizeOf) : 0; - return mallocSizeOf(this) + sizeOfCompilationInput; -} - -bool JS::CompilationStorage::allocateInput( - FrontendContext* fc, const JS::ReadOnlyCompileOptions& options) { - MOZ_ASSERT(!input_); - input_ = fc->getAllocator()->new_<frontend::CompilationInput>(options); - return !!input_; -} - -void JS::CompilationStorage::trace(JSTracer* trc) { - if (input_) { - input_->trace(trc); - } -} - template <typename CharT> static already_AddRefed<JS::Stencil> CompileGlobalScriptToStencilImpl( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, - JS::SourceText<CharT>& srcBuf, JS::CompilationStorage& compilationStorage) { + JS::SourceText<CharT>& srcBuf) { ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global; JS::SourceText<CharT> data(std::move(srcBuf)); - compilationStorage.allocateInput(fc, options); - if (!compilationStorage.hasInput()) { - return nullptr; - } + frontend::CompilationInput compilationInput(options); frontend::NoScopeBindingCache scopeCache; LifoAlloc tempLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); - RefPtr<frontend::CompilationStencil> stencil_ = - frontend::CompileGlobalScriptToStencil(nullptr, fc, tempLifoAlloc, - compilationStorage.getInput(), - &scopeCache, data, scopeKind); + RefPtr<JS::Stencil> stencil_ = frontend::CompileGlobalScriptToStencil( + nullptr, fc, tempLifoAlloc, compilationInput, &scopeCache, data, + scopeKind); + // CompilationInput initialized with CompileGlobalScriptToStencil only + // references information from the JS::Stencil context and the + // ref-counted ScriptSource, which are both GC-free. + JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); return stencil_.forget(); } template <typename CharT> static already_AddRefed<JS::Stencil> CompileModuleScriptToStencilImpl( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& optionsInput, - JS::SourceText<CharT>& srcBuf, JS::CompilationStorage& compilationStorage) { + JS::SourceText<CharT>& srcBuf) { JS::CompileOptions options(nullptr, optionsInput); options.setModule(); - compilationStorage.allocateInput(fc, options); - if (!compilationStorage.hasInput()) { - return nullptr; - } + frontend::CompilationInput compilationInput(options); NoScopeBindingCache scopeCache; js::LifoAlloc tempLifoAlloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE); - RefPtr<JS::Stencil> stencil = - ParseModuleToStencil(nullptr, fc, tempLifoAlloc, - compilationStorage.getInput(), &scopeCache, srcBuf); + RefPtr<JS::Stencil> stencil = ParseModuleToStencil( + nullptr, fc, tempLifoAlloc, compilationInput, &scopeCache, srcBuf); + // CompilationInput initialized with ParseModuleToStencil only + // references information from the JS::Stencil context and the + // ref-counted ScriptSource, which are both GC-free. + JS_HAZ_VALUE_IS_GC_SAFE(compilationInput); if (!stencil) { return nullptr; } @@ -164,42 +137,38 @@ static already_AddRefed<JS::Stencil> CompileModuleScriptToStencilImpl( already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, - JS::SourceText<mozilla::Utf8Unit>& srcBuf, - JS::CompilationStorage& compileStorage) { + JS::SourceText<mozilla::Utf8Unit>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileGlobalScriptToStencilImpl(fc, options, srcBuf, compileStorage); + return CompileGlobalScriptToStencilImpl(fc, options, srcBuf); } already_AddRefed<JS::Stencil> JS::CompileGlobalScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, - JS::SourceText<char16_t>& srcBuf, JS::CompilationStorage& compileStorage) { + JS::SourceText<char16_t>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileGlobalScriptToStencilImpl(fc, options, srcBuf, compileStorage); + return CompileGlobalScriptToStencilImpl(fc, options, srcBuf); } already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& optionsInput, - JS::SourceText<mozilla::Utf8Unit>& srcBuf, - JS::CompilationStorage& compileStorage) { + JS::SourceText<mozilla::Utf8Unit>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf, - compileStorage); + return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf); } already_AddRefed<JS::Stencil> JS::CompileModuleScriptToStencil( JS::FrontendContext* fc, const JS::ReadOnlyCompileOptions& optionsInput, - JS::SourceText<char16_t>& srcBuf, JS::CompilationStorage& compileStorage) { + JS::SourceText<char16_t>& srcBuf) { #ifdef DEBUG fc->assertNativeStackLimitThread(); #endif - return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf, - compileStorage); + return CompileModuleScriptToStencilImpl(fc, optionsInput, srcBuf); } bool JS::PrepareForInstantiate(JS::FrontendContext* fc, JS::Stencil& stencil, diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 8a434418b1..b72c6d4726 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -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: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 384d16b7d8..9209ba4d67 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -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() == @@ -1150,6 +1162,10 @@ class FullParseHandler { TaggedParserAtomIndex::WellKnown::async(); } + bool isArgumentsLength(Node node) { + return node->isKind(ParseNodeKind::ArgumentsLength); + } + bool isPrivateName(Node node) { return node->isKind(ParseNodeKind::PrivateName); } diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 0ad8e55758..46b5bb074c 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -92,6 +92,7 @@ class NameResolver : public ParseNodeVisitor<NameResolver> { */ bool nameExpression(ParseNode* n, bool* foundName) { switch (n->getKind()) { + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &n->as<PropertyAccess>(); if (!nameExpression(&prop->expression(), foundName)) { diff --git a/js/src/frontend/ParseContext.cpp b/js/src/frontend/ParseContext.cpp index ececac705b..622c467822 100644 --- a/js/src/frontend/ParseContext.cpp +++ b/js/src/frontend/ParseContext.cpp @@ -593,6 +593,14 @@ bool ParseContext::hasUsedName(const UsedNameTracker& usedNames, return false; } +bool ParseContext::hasClosedOverName(const UsedNameTracker& usedNames, + TaggedParserAtomIndex name) { + if (auto p = usedNames.lookup(name)) { + return p->value().isClosedOver(scriptId()); + } + return false; +} + bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames, TaggedParserAtomIndex name) { MOZ_ASSERT(name == TaggedParserAtomIndex::WellKnown::arguments() || @@ -602,6 +610,13 @@ bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames, functionBox()->bindingsAccessedDynamically(); } +bool ParseContext::hasClosedOverFunctionSpecialName( + const UsedNameTracker& usedNames, TaggedParserAtomIndex name) { + MOZ_ASSERT(name == TaggedParserAtomIndex::WellKnown::arguments()); + return hasClosedOverName(usedNames, name) || + functionBox()->bindingsAccessedDynamically(); +} + bool ParseContext::declareFunctionThis(const UsedNameTracker& usedNames, bool canSkipLazyClosedOverBindings) { // The asm.js validator does all its own symbol-table management so, as an @@ -644,17 +659,41 @@ bool ParseContext::declareFunctionArgumentsObject( ParseContext::Scope& funScope = functionScope(); ParseContext::Scope& _varScope = varScope(); - bool usesArguments = false; bool hasExtraBodyVarScope = &funScope != &_varScope; // Time to implement the odd semantics of 'arguments'. auto argumentsName = TaggedParserAtomIndex::WellKnown::arguments(); - bool tryDeclareArguments; + bool tryDeclareArguments = false; + bool needsArgsObject = false; + + // When delazifying simply defer to the function box. if (canSkipLazyClosedOverBindings) { tryDeclareArguments = funbox->shouldDeclareArguments(); + needsArgsObject = funbox->needsArgsObj(); } else { - tryDeclareArguments = hasUsedFunctionSpecialName(usedNames, argumentsName); + // We cannot compute these values when delazifying, hence why we need to + // rely on the function box flags instead. + bool bindingClosedOver = + hasClosedOverFunctionSpecialName(usedNames, argumentsName); + bool bindingUsedOnlyHere = + hasUsedFunctionSpecialName(usedNames, argumentsName) && + !bindingClosedOver; + + // Declare arguments if there's a closed-over consumer of the binding, or if + // there is a non-length use and we will reference the binding during + // bytecode emission. + tryDeclareArguments = + !funbox->isEligibleForArgumentsLength() || bindingClosedOver; + // If we have a use and the binding isn't closed over, then we will do + // bytecode emission with the arguments intrinsic. + if (bindingUsedOnlyHere && funbox->isEligibleForArgumentsLength()) { + // If we're using the intrinsic we should not be declaring the binding. + MOZ_ASSERT(!tryDeclareArguments); + funbox->setUsesArgumentsIntrinsics(); + } else if (tryDeclareArguments) { + needsArgsObject = true; + } } // ES 9.2.12 steps 19 and 20 say formal parameters, lexical bindings, @@ -670,9 +709,19 @@ bool ParseContext::declareFunctionArgumentsObject( DeclaredNamePtr p = _varScope.lookupDeclaredName(argumentsName); if (p && p->value()->kind() == DeclarationKind::Var) { if (hasExtraBodyVarScope) { + // While there is a binding in the var scope, we should declare + // the binding in the function scope. tryDeclareArguments = true; } else { - usesArguments = true; + // A binding in the function scope (since varScope and functionScope are + // the same) exists, so arguments is used. + if (needsArgsObject) { + funbox->setNeedsArgsObj(); + } + + // There is no point in continuing on below: We know we already have + // a declaration of arguments in the function scope. + return true; } } @@ -685,17 +734,11 @@ bool ParseContext::declareFunctionArgumentsObject( return false; } funbox->setShouldDeclareArguments(); - usesArguments = true; - } else if (hasExtraBodyVarScope) { - // Formal parameters shadow the arguments object. - return true; + if (needsArgsObject) { + funbox->setNeedsArgsObj(); + } } } - - if (usesArguments) { - funbox->setNeedsArgsObj(); - } - return true; } diff --git a/js/src/frontend/ParseContext.h b/js/src/frontend/ParseContext.h index 8124073bf9..796b776d85 100644 --- a/js/src/frontend/ParseContext.h +++ b/js/src/frontend/ParseContext.h @@ -661,8 +661,12 @@ class ParseContext : public Nestable<ParseContext> { bool hasUsedName(const UsedNameTracker& usedNames, TaggedParserAtomIndex name); + bool hasClosedOverName(const UsedNameTracker& usedNames, + TaggedParserAtomIndex name); bool hasUsedFunctionSpecialName(const UsedNameTracker& usedNames, TaggedParserAtomIndex name); + bool hasClosedOverFunctionSpecialName(const UsedNameTracker& usedNames, + TaggedParserAtomIndex name); bool declareFunctionThis(const UsedNameTracker& usedNames, bool canSkipLazyClosedOverBindings); @@ -673,6 +677,13 @@ class ParseContext : public Nestable<ParseContext> { bool declareDotGeneratorName(); bool declareTopLevelDotGeneratorName(); + // Used to determine if we have non-length uses of the arguments binding. + // This works by incrementing this counter each time we encounter the + // arguments name, and decrementing each time it is combined into + // arguments.length; as a result, if this is non-zero at the end of parsing, + // we have identified a non-length use of the arguments binding. + size_t numberOfArgumentsNames = 0; + private: [[nodiscard]] bool isVarRedeclaredInInnermostScope( TaggedParserAtomIndex name, ParserBase* parser, DeclarationKind kind, diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 61c009c6e4..a6747897d6 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -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 { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index bcd6c30c02..5cb47f2425 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2472,6 +2472,11 @@ GeneralParser<ParseHandler, Unit>::functionBody(InHandling inHandling, } } + if (pc_->numberOfArgumentsNames > 0 || kind == FunctionSyntaxKind::Arrow) { + MOZ_ASSERT(pc_->isFunctionBox()); + pc_->sc()->setIneligibleForArgumentsLength(); + } + // Declare the 'arguments', 'this', and 'new.target' bindings if necessary // before finishing up the scope so these special bindings get marked as // closed over if necessary. Arrow functions don't have these bindings. @@ -6570,6 +6575,8 @@ bool GeneralParser<ParseHandler, Unit>::forHeadStart( return false; } } + } else if (handler_.isArgumentsLength(*forInitialPart)) { + pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(*forInitialPart)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(*forInitialPart)) { @@ -7917,7 +7924,12 @@ bool GeneralParser<ParseHandler, Unit>::finishClassConstructor( bool hasPrivateBrand = classInitializedMembers.hasPrivateBrand(); if (hasPrivateBrand || numMemberInitializers > 0) { // Now that we have full set of initializers, update the constructor. - MemberInitializers initializers(hasPrivateBrand, numMemberInitializers); + MemberInitializers initializers( + hasPrivateBrand, +#ifdef ENABLE_DECORATORS + classInitializedMembers.hasInstanceDecorators, +#endif + numMemberInitializers); ctorbox->setMemberInitializers(initializers); // Field initialization need access to `this`. @@ -10220,6 +10232,8 @@ typename ParseHandler::NodeResult GeneralParser<ParseHandler, Unit>::assignExpr( return errorResult(); } } + } else if (handler_.isArgumentsLength(lhs)) { + pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(lhs)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(lhs)) { @@ -10280,6 +10294,8 @@ bool GeneralParser<ParseHandler, Unit>::checkIncDecOperand( return false; } } + } else if (handler_.isArgumentsLength(operand)) { + pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(operand)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(operand)) { @@ -10898,6 +10914,9 @@ template <class ParseHandler> inline typename ParseHandler::NameNodeResult PerHandlerParser<ParseHandler>::newName(TaggedParserAtomIndex name, TokenPos pos) { + if (name == TaggedParserAtomIndex::WellKnown::arguments()) { + this->pc_->numberOfArgumentsNames++; + } return handler_.newName(name, pos); } @@ -10926,6 +10945,13 @@ GeneralParser<ParseHandler, Unit>::memberPropertyAccess( MOZ_ASSERT(!handler_.isSuperBase(lhs)); return handler_.newOptionalPropertyAccess(lhs, name); } + + if (handler_.isArgumentsName(lhs) && handler_.isLengthName(name)) { + MOZ_ASSERT(pc_->numberOfArgumentsNames > 0); + pc_->numberOfArgumentsNames--; + return handler_.newArgumentsLength(lhs, name); + } + return handler_.newPropertyAccess(lhs, name); } @@ -11484,6 +11510,10 @@ void GeneralParser<ParseHandler, Unit>::checkDestructuringAssignmentName( return; } + if (handler_.isArgumentsLength(name)) { + pc_->sc()->setIneligibleForArgumentsLength(); + } + if (pc_->sc()->strict()) { if (handler_.isArgumentsName(name)) { if (pc_->sc()->strict()) { @@ -12143,6 +12173,10 @@ GeneralParser<ParseHandler, Unit>::objectLiteral(YieldHandling yieldHandling, } } + if (handler_.isArgumentsLength(lhs)) { + pc_->sc()->setIneligibleForArgumentsLength(); + } + Node rhs; MOZ_TRY_VAR(rhs, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); diff --git a/js/src/frontend/SharedContext.cpp b/js/src/frontend/SharedContext.cpp index 7fa3b724fb..488e3bd384 100644 --- a/js/src/frontend/SharedContext.cpp +++ b/js/src/frontend/SharedContext.cpp @@ -45,7 +45,8 @@ SharedContext::SharedContext(FrontendContext* fc, Kind kind, inClass_(false), localStrict(false), hasExplicitUseStrict_(false), - isScriptExtraFieldCopiedToStencil(false) { + isScriptExtraFieldCopiedToStencil(false), + eligibleForArgumentsLength(true) { // Compute the script kind "input" flags. if (kind == Kind::FunctionBox) { setFlag(ImmutableFlags::IsFunction); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 12f9a6ed12..ab0606527e 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -180,6 +180,10 @@ class SharedContext { // FunctionBox::copyUpdated* methods. bool isScriptExtraFieldCopiedToStencil : 1; + // Indicates this shared context is eligible to use JSOp::ArgumentsLength + // when emitting the ArgumentsLength parse node. + bool eligibleForArgumentsLength : 1; + // End of fields. enum class Kind : uint8_t { FunctionBox, Global, Eval, Module }; @@ -273,6 +277,11 @@ class SharedContext { return retVal; } + bool isEligibleForArgumentsLength() { + return eligibleForArgumentsLength && !bindingsAccessedDynamically(); + } + void setIneligibleForArgumentsLength() { eligibleForArgumentsLength = false; } + void copyScriptExtraFields(ScriptStencilExtra& scriptExtra); }; diff --git a/js/src/frontend/Stencil.cpp b/js/src/frontend/Stencil.cpp index 7c6eba4c5a..30d1588415 100644 --- a/js/src/frontend/Stencil.cpp +++ b/js/src/frontend/Stencil.cpp @@ -575,7 +575,7 @@ void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { } bool hasEnv = si.hasSyntacticEnvironment(); - auto setCacthAll = [&](NameLocation loc) { + auto setCatchAll = [&](NameLocation loc) { return si.scope().match([&](auto& scope_ref) { using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref)); BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref); @@ -604,7 +604,7 @@ void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { case ScopeKind::Function: if (hasEnv) { if (si.scope().funHasExtensibleScope()) { - setCacthAll(NameLocation::Dynamic()); + setCatchAll(NameLocation::Dynamic()); return; } @@ -733,21 +733,21 @@ void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) { if (!hasEnv) { ScopeKind kind = si.scope().enclosing().kind(); if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) { - setCacthAll(NameLocation::Global(BindingKind::Var)); + setCatchAll(NameLocation::Global(BindingKind::Var)); return; } } - setCacthAll(NameLocation::Dynamic()); + setCatchAll(NameLocation::Dynamic()); return; case ScopeKind::Global: - setCacthAll(NameLocation::Global(BindingKind::Var)); + setCatchAll(NameLocation::Global(BindingKind::Var)); return; case ScopeKind::With: case ScopeKind::NonSyntactic: - setCacthAll(NameLocation::Dynamic()); + setCatchAll(NameLocation::Dynamic()); return; case ScopeKind::WasmInstance: diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index aa06eaa246..fa63b1e9d3 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -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) { @@ -572,6 +577,9 @@ class SyntaxParseHandler { NameNodeResult newPropertyName(TaggedParserAtomIndex name, const TokenPos& pos) { lastAtom = name; + if (name == TaggedParserAtomIndex::WellKnown::length()) { + return NodeLengthName; + } return NodeGeneric; } @@ -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,13 +789,17 @@ 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; } + bool isArgumentsLength(Node node) { return node == NodeArgumentsLength; } + bool isPrivateName(Node node) { return node == NodePrivateName; } bool isPrivateMemberAccess(Node node) { return node == NodePrivateMemberAccess; @@ -795,7 +811,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; diff --git a/js/src/frontend/UsedNameTracker.h b/js/src/frontend/UsedNameTracker.h index 2a52208128..f118d6101b 100644 --- a/js/src/frontend/UsedNameTracker.h +++ b/js/src/frontend/UsedNameTracker.h @@ -160,6 +160,10 @@ class UsedNameTracker { return !uses_.empty() && uses_.back().scriptId >= scriptId; } + bool isClosedOver(uint32_t scriptId) const { + return !uses_.empty() && uses_.back().scriptId > scriptId; + } + // To allow disambiguating public and private symbols bool isPublic() { return visibility_ == NameVisibility::Public; } diff --git a/js/src/frontend/align_stack_comment.py b/js/src/frontend/align_stack_comment.py index 28d5d8cf7f..6e279a90c6 100755 --- a/js/src/frontend/align_stack_comment.py +++ b/js/src/frontend/align_stack_comment.py @@ -22,7 +22,7 @@ ALIGNMENT_COLUMN = 20 # The maximum column for comment MAX_CHARS_PER_LINE = 80 -stack_comment_pat = re.compile("^( *//) *(\[stack\].*)$") +stack_comment_pat = re.compile(r"^( *//) *(\[stack\].*)$") def align_stack_comment(path): |