/* -*- 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 frontend_ParseContext_inl_h #define frontend_ParseContext_inl_h #include "frontend/ParseContext.h" #include "mozilla/ResultVariant.h" #include "frontend/Parser.h" #include "vm/JSContext.h" namespace js { namespace frontend { template <> inline bool ParseContext::Statement::is() const { return kind_ == StatementKind::Label; } template <> inline bool ParseContext::Statement::is() const { return kind_ == StatementKind::Class; } template inline T& ParseContext::Statement::as() { MOZ_ASSERT(is()); return static_cast(*this); } inline ParseContext::Scope::BindingIter ParseContext::Scope::bindings( ParseContext* pc) { // In function scopes with parameter expressions, function special names // (like '.this') are declared as vars in the function scope, despite its // not being the var scope. return BindingIter(*this, pc->varScope_ == this || pc->functionScope_.ptrOr(nullptr) == this); } inline ParseContext::Scope::Scope(ParserBase* parser) : Nestable(&parser->pc_->innermostScope_), declared_(parser->cx_->frontendCollectionPool()), possibleAnnexBFunctionBoxes_(parser->cx_->frontendCollectionPool()), id_(parser->usedNames_.nextScopeId()) {} inline ParseContext::Scope::Scope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames) : Nestable(&pc->innermostScope_), declared_(cx->frontendCollectionPool()), possibleAnnexBFunctionBoxes_(cx->frontendCollectionPool()), id_(usedNames.nextScopeId()) {} inline ParseContext::VarScope::VarScope(ParserBase* parser) : Scope(parser) { useAsVarScope(parser->pc_); } inline ParseContext::VarScope::VarScope(JSContext* cx, ParseContext* pc, UsedNameTracker& usedNames) : Scope(cx, pc, usedNames) { useAsVarScope(pc); } inline JS::Result ParseContext::checkBreakStatement(const ParserName* label) { // Labeled 'break' statements target the nearest labeled statements (could // be any kind) with the same label. Unlabeled 'break' statements target // the innermost loop or switch statement. if (label) { auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) { MOZ_ASSERT(stmt); return stmt->label() == label; }; if (!findInnermostStatement(hasSameLabel)) { return mozilla::Err(ParseContext::BreakStatementError::LabelNotFound); } } else { auto isBreakTarget = [](ParseContext::Statement* stmt) { return StatementKindIsUnlabeledBreakTarget(stmt->kind()); }; if (!findInnermostStatement(isBreakTarget)) { return mozilla::Err(ParseContext::BreakStatementError::ToughBreak); } } return Ok(); } inline JS::Result ParseContext::checkContinueStatement(const ParserName* label) { // Labeled 'continue' statements target the nearest labeled loop // statements with the same label. Unlabeled 'continue' statements target // the innermost loop statement. auto isLoop = [](ParseContext::Statement* stmt) { MOZ_ASSERT(stmt); return StatementKindIsLoop(stmt->kind()); }; if (!label) { // Unlabeled statement: we target the innermost loop, so make sure that // there is an innermost loop. if (!findInnermostStatement(isLoop)) { return mozilla::Err(ParseContext::ContinueStatementError::NotInALoop); } return Ok(); } // Labeled statement: targest the nearest labeled loop with the same label. ParseContext::Statement* stmt = innermostStatement(); bool foundLoop = false; // True if we have encountered at least one loop. for (;;) { stmt = ParseContext::Statement::findNearest(stmt, isLoop); if (!stmt) { return foundLoop ? mozilla::Err( ParseContext::ContinueStatementError::LabelNotFound) : mozilla::Err( ParseContext::ContinueStatementError::NotInALoop); } foundLoop = true; // Is it labeled by our label? stmt = stmt->enclosing(); while (stmt && stmt->is()) { if (stmt->as().label() == label) { return Ok(); } stmt = stmt->enclosing(); } } } template inline void RedeclareVar(DeclaredNamePtrT ptr, DeclarationKind kind) { #ifdef DEBUG DeclarationKind declaredKind = ptr->value()->kind(); MOZ_ASSERT(DeclarationKindIsVar(declaredKind)); #endif // Any vars that are redeclared as body-level functions must // be recorded as body-level functions. // // In the case of global and eval scripts, GlobalDeclaration- // Instantiation [1] and EvalDeclarationInstantiation [2] // check for the declarability of global var and function // bindings via CanDeclareVar [3] and CanDeclareGlobal- // Function [4]. CanDeclareGlobalFunction is strictly more // restrictive than CanDeclareGlobalVar, so record the more // restrictive kind. These semantics are implemented in // CheckCanDeclareGlobalBinding. // // VarForAnnexBLexicalFunction declarations are declared when // the var scope exits. It is not possible for a var to be // previously declared as VarForAnnexBLexicalFunction and // checked for redeclaration. // // [1] ES 15.1.11 // [2] ES 18.2.1.3 // [3] ES 8.1.1.4.15 // [4] ES 8.1.1.4.16 if (kind == DeclarationKind::BodyLevelFunction) { MOZ_ASSERT(declaredKind != DeclarationKind::VarForAnnexBLexicalFunction); ptr->value()->alterKind(kind); } } } // namespace frontend } // namespace js #endif // frontend_ParseContext_inl_h