//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once #include "ParseFlags.h" namespace Js { class ScopeInfo; class ByteCodeCache; }; // Operator precedence levels enum { koplNo, // not an operator koplCma, // , koplSpr, // ... koplAsg, // = += etc koplQue, // ?: koplLor, // || koplLan, // && koplBor, // | koplXor, // ^ koplBan, // & koplEqu, // == != koplCmp, // < <= > >= koplShf, // << >> >>> koplAdd, // + - koplMul, // * / % koplExpo, // ** koplUni, // unary operators koplLim }; enum ParseType { ParseType_Upfront, ParseType_Deferred }; enum DestructuringInitializerContext { DIC_None, DIC_ShouldNotParseInitializer, // e.g. We don't want to parse the initializer even though we found assignment DIC_ForceErrorOnInitializer, // e.g. Catch param where we explicitly want to raise an error when the initializer found }; enum ScopeType: int; enum SymbolType : byte; // Representation of a label used when no AST is being built. struct LabelId { IdentPtr pid; struct LabelId* next; }; typedef ArenaAllocator ParseNodeAllocator; /*************************************************************************** Parser object. ***************************************************************************/ class CompileScriptException; class Parser; class SourceContextInfo; struct BlockIdsStack; class Span; class BackgroundParser; struct BackgroundParseItem; struct PnClass; class HashTbl; struct PidRefStack; struct DeferredFunctionStub; struct BlockInfoStack; struct StmtNest { union { struct { ParseNodeStmt * pnodeStmt; // This statement node. }; struct { bool isDeferred : 1; OpCode op; // This statement operation. }; }; LabelId* pLabelId; // Labels for this statement. StmtNest *pstmtOuter; // Enclosing statement. inline OpCode GetNop() const { AnalysisAssert(isDeferred || pnodeStmt != nullptr); return isDeferred ? op : pnodeStmt->nop; } }; struct ParseContext { LPCUTF8 pszSrc; size_t offset; size_t length; charcount_t characterOffset; int nextBlockId; ULONG grfscr; ULONG lineNumber; ParseNodeProg * pnodeProg; SourceContextInfo* sourceContextInfo; BlockInfoStack* currentBlockInfo; bool strictMode; bool isUtf8; }; // DeferredFunctionStub is part of the parser state cache we serialize and restore in an // attempt to avoid doing another upfront parse of the same source. // Each deferred stub contains information needed to identify the function location in source, // flags for the function, the set of names captured by this function, and links to deferred // stubs for further nested functions. // These stubs are only created for defer-parsed functions and we create one stub for each // nested function. When we fully parse the defer-parsed function, we will use information // in these stubs to skip scanning the nested functions again. // // Example code: // let a, b; // function foo() { // function bar() { // return a; // } // function baz() { // return b; // } // } // // Deferred stubs for foo: // capturedNames: { a, b } // nestedCount: 2 // deferredStubs : // [ // // 0 = bar: // { // capturedNames: { a } // nestedCount: 0 // deferredStubs : nullptr // ... // }, // // 1 = baz: // { // capturedNames: { b } // nestedCount: 0 // deferredStubs : nullptr // ... // } // ] // ... struct DeferredFunctionStub { Field(RestorePoint) restorePoint; Field(FncFlags) fncFlags; Field(uint) nestedCount; Field(charcount_t) ichMin; // Number of names captured by this function. // This is used as length for capturedNameSerializedIds but should // also be equal to the length of capturedNamePointers when // capturedNamePointers is not nullptr. Field(uint) capturedNameCount; // After the parser memory is cleaned-up, we no longer have access to // the IdentPtrs allocated from the Parser arena. We keep a list of // ids into the string table deserialized from the parser state cache. // This list is Recycler-allocated. Field(int *) capturedNameSerializedIds; // The set of names which are captured by this function. // A function captures a name when it references a name not defined within // the function. // A function also captures all names captured by nested functions. // The IdentPtrs in this set and the set itself are allocated from Parser // arena memory. Field(IdentPtrSet *) capturedNamePointers; // List of deferred stubs for further nested functions. // Length of this list is equal to nestedCount. Field(DeferredFunctionStub *) deferredStubs; Field(Js::ByteCodeCache *) byteCodeCache; }; template class UTF8EncodingPolicyBase; typedef UTF8EncodingPolicyBase NotNullTerminatedUTF8EncodingPolicy; template class Scanner; namespace Js { class ParseableFunctionInfo; class FunctionBody; template class TempArenaAllocatorWrapper; }; class Parser { typedef Scanner Scanner_t; public: #if DEBUG Parser(Js::ScriptContext* scriptContext, BOOL strictMode = FALSE, PageAllocator *alloc = nullptr, bool isBackground = false, size_t size = sizeof(Parser)); #else Parser(Js::ScriptContext* scriptContext, BOOL strictMode = FALSE, PageAllocator *alloc = nullptr, bool isBackground = false); #endif ~Parser(void); Js::ScriptContext* GetScriptContext() const { return m_scriptContext; } void ReleaseTemporaryGuestArena(); bool IsCreatingStateCache(); #if ENABLE_BACKGROUND_PARSING bool IsBackgroundParser() const { return m_isInBackground; } bool IsDoingFastScan() const { return m_doingFastScan; } #else bool IsBackgroundParser() const { return false; } bool IsDoingFastScan() const { return false; } #endif bool GetIsInParsingArgList() const { return m_isInParsingArgList; } void SetIsInParsingArgList(bool set) { m_isInParsingArgList = set; } bool GetHasDestructuringPattern() const { return m_hasDestructuringPattern; } void SetHasDestructuringPattern(bool set) { m_hasDestructuringPattern = set; } ParseNode* CopyPnode(ParseNode* pnode); ArenaAllocator *GetAllocator() { return &m_nodeAllocator;} size_t GetSourceLength() { return m_length; } size_t GetOriginalSourceLength() { return m_originalLength; } static ULONG GetDeferralThreshold(bool isProfileLoaded); BOOL WillDeferParse(Js::LocalFunctionId functionId); BOOL IsDeferredFnc(); void ReduceDeferredScriptLength(size_t chars); static DeferredFunctionStub * BuildDeferredStubTree(ParseNodeFnc *pnodeFnc, Recycler *recycler); void RestorePidRefForSym(Symbol *sym); HRESULT ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isGenerator, bool isAsync, CompileScriptException *pse, void (Parser::*validateFunction)()); // Should be called when the UTF-8 source was produced from UTF-16. This is really CESU-8 source in that it encodes surrogate pairs // as 2 three byte sequences instead of 4 bytes as required by UTF-8. It also is a lossless conversion of invalid UTF-16 sequences. // This is important in Javascript because Javascript engines are required not to report invalid UTF-16 sequences and to consider // the UTF-16 characters pre-canonicalization. Converting this UTF-16 with invalid sequences to valid UTF-8 and back would cause // all invalid UTF-16 sequences to be replaced by one or more Unicode replacement characters (0xFFFD), losing the original // invalid sequences. HRESULT ParseCesu8Source(__out ParseNodeProg ** parseTree, LPCUTF8 pSrc, size_t length, ULONG grfsrc, CompileScriptException *pse, Js::LocalFunctionId * nextFunctionId, SourceContextInfo * sourceContextInfo); // Should be called when the source is UTF-8 and invalid UTF-8 sequences should be replaced with the unicode replacement character // (0xFFFD). Security concerns require externally produced UTF-8 only allow valid UTF-8 otherwise an attacker could use invalid // UTF-8 sequences to fool a filter and cause Javascript to be executed that might otherwise have been rejected. HRESULT ParseUtf8Source(__out ParseNodeProg ** parseTree, LPCUTF8 pSrc, size_t length, ULONG grfsrc, CompileScriptException *pse, Js::LocalFunctionId * nextFunctionId, SourceContextInfo * sourceContextInfo); // Used by deferred parsing to parse a deferred function. HRESULT ParseSourceWithOffset(__out ParseNodeProg ** parseTree, LPCUTF8 pSrc, size_t offset, size_t cbLength, charcount_t cchOffset, bool isCesu8, ULONG grfscr, CompileScriptException *pse, Js::LocalFunctionId * nextFunctionId, ULONG lineNumber, SourceContextInfo * sourceContextInfo, Js::ParseableFunctionInfo* functionInfo); protected: static uint BuildDeferredStubTreeHelper(ParseNodeBlock* pnodeBlock, DeferredFunctionStub* deferredStubs, uint currentStubIndex, uint deferredStubCount, Recycler *recycler); void ShiftCurrDeferredStubToChildFunction(ParseNodeFnc* pnodeFnc, ParseNodeFnc* pnodeFncParent); HRESULT ParseSourceInternal( __out ParseNodeProg ** parseTree, LPCUTF8 pszSrc, size_t offsetInBytes, size_t lengthInCodePoints, charcount_t offsetInChars, bool isUtf8, ULONG grfscr, CompileScriptException *pse, Js::LocalFunctionId * nextFunctionId, ULONG lineNumber, SourceContextInfo * sourceContextInfo); ParseNodeProg * Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcount_t charOffset, bool isUtf8, ULONG grfscr, ULONG lineNumber, Js::LocalFunctionId * nextFunctionId, CompileScriptException *pse); private: /*********************************************************************** Core members. ***********************************************************************/ ParseNodeAllocator m_nodeAllocator; int32 m_cactIdentToNodeLookup; uint32 m_grfscr; size_t m_length; // source length in characters excluding comments and literals size_t m_originalLength; // source length in characters excluding comments and literals Js::LocalFunctionId * m_nextFunctionId; SourceContextInfo* m_sourceContextInfo; #if ENABLE_BACKGROUND_PARSING bool m_hasParallelJob; bool m_isInBackground; bool m_doingFastScan; #endif bool m_tempGuestArenaReleased; int m_nextBlockId; AutoRecyclerRootPtr> m_tempGuestArena; // RegexPattern objects created for literal regexes are recycler-allocated and need to be kept alive until the function body // is created during byte code generation. The RegexPattern pointer is stored in a temporary guest // arena for that purpose. This list is then unregistered from the guest arena at the end of parsing/scanning. SList m_registeredRegexPatterns; protected: Js::ScriptContext* m_scriptContext; HashTbl * GetHashTbl() { return this->GetScanner()->GetHashTbl(); } LPCWSTR GetTokenString(tokens token); __declspec(noreturn) void Error(HRESULT hr, LPCWSTR stringOne = _u(""), LPCWSTR stringTwo = _u("")); private: __declspec(noreturn) void Error(HRESULT hr, ParseNodePtr pnode); __declspec(noreturn) void Error(HRESULT hr, charcount_t ichMin, charcount_t ichLim); __declspec(noreturn) static void OutOfMemory(); void EnsureStackAvailable(); void IdentifierExpectedError(const Token& token); bool CheckForDirective(bool* pIsUseStrict, bool* pIsUseAsm, bool* pIsOctalInString); bool CheckStrictModeStrPid(IdentPtr pid); bool CheckAsmjsModeStrPid(IdentPtr pid); bool IsCurBlockInLoop() const; void InitPids(); /*********************************************************************** Members needed just for parsing. ***********************************************************************/ protected: Token m_token; Scanner_t m_scan; Scanner_t const * GetScanner() const { return &m_scan; } Scanner_t * GetScanner() { return &m_scan; } void AddAstSize(int size); void AddAstSizeAllowDefer(int size); template typename OpCodeTrait::ParseNodeType * CreateNodeForOpT() { return CreateNodeForOpT(this->GetScanner()->IchMinTok()); } template typename OpCodeTrait::ParseNodeType * CreateNodeForOpT(charcount_t ichMin) { return CreateNodeForOpT(ichMin, this->GetScanner()->IchLimTok()); } template typename OpCodeTrait::ParseNodeType * CreateNodeForOpT(charcount_t ichMin, charcount_t ichLim); template typename OpCodeTrait::ParseNodeType * CreateAllowDeferNodeForOpT() { return CreateAllowDeferNodeForOpT(this->GetScanner()->IchMinTok()); } template typename OpCodeTrait::ParseNodeType * CreateAllowDeferNodeForOpT(charcount_t ichMin) { return CreateAllowDeferNodeForOpT(ichMin, this->GetScanner()->IchLimTok()); } template typename OpCodeTrait::ParseNodeType * CreateAllowDeferNodeForOpT(charcount_t ichMin, charcount_t ichLim); public: // create nodes using arena allocator; used by AST transformation template static typename OpCodeTrait::ParseNodeType * StaticCreateNodeT(ArenaAllocator* alloc, charcount_t ichMin = 0, charcount_t ichLim = 0) { return Anew(alloc, typename OpCodeTrait::ParseNodeType, nop, ichMin, ichLim); } static ParseNodeBin * StaticCreateBinNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2, ArenaAllocator* alloc, charcount_t ichMin = 0, charcount_t ichLim = 0); static ParseNodeBlock * StaticCreateBlockNode(ArenaAllocator* alloc, charcount_t ichMin = 0, charcount_t ichLim = 0, int blockId = -1, PnodeBlockType blockType = PnodeBlockType::Regular); static ParseNodeVar * StaticCreateTempNode(ParseNode* initExpr, ArenaAllocator* alloc); static ParseNodeUni * StaticCreateTempRef(ParseNode* tempNode, ArenaAllocator* alloc); private: ParseNodeUni * CreateUniNode(OpCode nop, ParseNodePtr pnodeOp); ParseNodeUni * CreateUniNode(OpCode nop, ParseNodePtr pnode1, charcount_t ichMin, charcount_t ichLim); ParseNodeBin * CreateBinNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2); ParseNodeBin * CreateBinNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2, charcount_t ichMin, charcount_t ichLim); ParseNodeTri * CreateTriNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2, ParseNodePtr pnode3); ParseNodeTri * CreateTriNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2, ParseNodePtr pnode3, charcount_t ichMin, charcount_t ichLim); ParseNodeBlock * CreateBlockNode(PnodeBlockType blockType = PnodeBlockType::Regular); ParseNodeBlock * CreateBlockNode(charcount_t ichMin, charcount_t ichLim, PnodeBlockType blockType = PnodeBlockType::Regular); ParseNodeVar * CreateDeclNode(OpCode nop, IdentPtr pid, SymbolType symbolType, bool errorOnRedecl = true); ParseNodeInt * CreateIntNode(int32 lw); ParseNodeStr * CreateStrNode(IdentPtr pid); ParseNodeBigInt * CreateBigIntNode(IdentPtr pid); ParseNodeName * CreateNameNode(IdentPtr pid); ParseNodeName * CreateNameNode(IdentPtr pid, PidRefStack * ref, charcount_t ichMin, charcount_t ichLim); ParseNodeSpecialName * CreateSpecialNameNode(IdentPtr pid, PidRefStack * ref, charcount_t ichMin, charcount_t ichLim); ParseNodeSuperReference * CreateSuperReferenceNode(OpCode nop, ParseNodeSpecialName * pnode1, ParseNodePtr pnode2); ParseNodeProg * CreateProgNode(bool isModuleSource, ULONG lineNumber); ParseNodeCall * CreateCallNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2); ParseNodeCall * CreateCallNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2, charcount_t ichMin, charcount_t ichLim); ParseNodeSuperCall * CreateSuperCallNode(ParseNodeSpecialName * pnode1, ParseNodePtr pnode2); ParseNodeFnc * CreateDummyFuncNode(bool fDeclaration); ParseNodeParamPattern * CreateParamPatternNode(ParseNodePtr pnode1); ParseNodeParamPattern * CreateDummyParamPatternNode(charcount_t ichMin); ParseNodeObjLit * CreateObjectPatternNode(ParseNodePtr pnodeMemberList, charcount_t ichMin, charcount_t ichLim, bool convertToPattern=false); Symbol* AddDeclForPid(ParseNodeVar * pnode, IdentPtr pid, SymbolType symbolType, bool errorOnRedecl); void CheckRedeclarationErrorForBlockId(IdentPtr pid, int blockId); public: #if ENABLE_BACKGROUND_PARSING void PrepareForBackgroundParse(); void AddFastScannedRegExpNode(ParseNodePtr const pnode); void AddBackgroundRegExpNode(ParseNodePtr const pnode); void AddBackgroundParseItem(BackgroundParseItem *const item); void FinishBackgroundRegExpNodes(); void FinishBackgroundPidRefs(BackgroundParseItem *const item, bool isOtherParser); void WaitForBackgroundJobs(BackgroundParser *bgp, CompileScriptException *pse); HRESULT ParseFunctionInBackground(ParseNodeFnc * pnodeFnc, ParseContext *parseContext, bool topLevelDeferred, CompileScriptException *pse); #endif void CheckPidIsValid(IdentPtr pid, bool autoArgumentsObject = false); void AddVarDeclToBlock(ParseNodeVar *pnode); // Add a var declaration. Only use while parsing. Assumes m_ppnodeVar is pointing to the right place already ParseNodeVar * CreateVarDeclNode(IdentPtr pid, SymbolType symbolType, bool autoArgumentsObject = false, ParseNodePtr pnodeFnc = NULL, bool checkReDecl = true); // Add a var declaration, during parse tree rewriting. Will setup m_ppnodeVar for the given pnodeFnc ParseNodeVar * AddVarDeclNode(IdentPtr pid, ParseNodeFnc * pnodeFnc); // Add a 'const' or 'let' declaration. ParseNodeVar * CreateBlockScopedDeclNode(IdentPtr pid, OpCode nodeType); void RegisterRegexPattern(UnifiedRegex::RegexPattern *const regexPattern); #ifdef ENABLE_DEBUG_CONFIG_OPTIONS LPCWSTR GetParseType() const { switch(m_parseType) { case ParseType_Upfront: return _u("Upfront"); case ParseType_Deferred: return _u("Deferred"); } Assert(false); return NULL; } #endif void CaptureContext(ParseContext *parseContext) const; void RestoreContext(ParseContext *const parseContext); int GetLastBlockId() const { Assert(m_nextBlockId > 0); return m_nextBlockId - 1; } private: ParseNodeFnc * m_currentNodeNonLambdaFunc; // current function or NULL ParseNodeFnc * m_currentNodeNonLambdaDeferredFunc; // current function or NULL ParseNodeFnc * m_currentNodeFunc; // current function or NULL ParseNodeFnc * m_currentNodeDeferredFunc; // current function or NULL ParseNodeProg * m_currentNodeProg; // current program DeferredFunctionStub *m_currDeferredStub; uint m_currDeferredStubCount; int32 * m_pCurrentAstSize; ParseNodePtr * m_ppnodeScope; // function list tail ParseNodePtr * m_ppnodeExprScope; // function expression list tail ParseNodePtr * m_ppnodeVar; // variable list tail bool m_inDeferredNestedFunc; // true if parsing a function in deferred mode, nested within the current node bool m_reparsingLambdaParams; bool m_disallowImportExportStmt; bool m_isInParsingArgList; bool m_hasDestructuringPattern; // This bool is used for deferring the shorthand initializer error ( {x = 1}) - as it is allowed in the destructuring grammar. bool m_hasDeferredShorthandInitError; bool m_deferEllipsisError; bool m_deferCommaError; uint * m_pnestedCount; // count of functions nested at one level below the current node struct WellKnownPropertyPids { IdentPtr arguments; IdentPtr async; IdentPtr eval; IdentPtr set; IdentPtr get; IdentPtr let; IdentPtr constructor; IdentPtr prototype; IdentPtr __proto__; IdentPtr of; IdentPtr target; IdentPtr from; IdentPtr as; IdentPtr _default; IdentPtr _star; // Special '*' identifier for modules IdentPtr _starDefaultStar; // Special '*default*' identifier for modules IdentPtr _this; // Special 'this' identifier IdentPtr _newTarget; // Special new.target identifier IdentPtr _super; // Special super identifier IdentPtr _superConstructor; // Special super constructor identifier }; WellKnownPropertyPids wellKnownPropertyPids; charcount_t m_sourceLim; // The actual number of characters parsed. Js::ParseableFunctionInfo* m_functionBody; // For a deferred parsed function, the function body is non-null ParseType m_parseType; uint m_arrayDepth; uint m_funcInArrayDepth; // Count func depth within array literal charcount_t m_funcInArray; uint m_scopeCountNoAst; // Used for issuing spread and rest errors when there is ambiguity with lambda parameter lists and parenthesized expressions uint m_funcParenExprDepth; RestorePoint m_deferEllipsisErrorLoc; RestorePoint m_deferCommaErrorLoc; uint m_tryCatchOrFinallyDepth; // Used to determine if parsing is currently in a try/catch/finally block in order to throw error on yield expressions inside them StmtNest *m_pstmtCur; // current statement or NULL BlockInfoStack *m_currentBlockInfo; Scope *m_currentScope; BackgroundParseItem *currBackgroundParseItem; BackgroundParseItem *backgroundParseItems; typedef DList NodeDList; NodeDList* fastScannedRegExpNodes; BlockIdsStack *m_currentDynamicBlock; int GetCurrentDynamicBlockId() const; void AppendFunctionToScopeList(bool fDeclaration, ParseNodeFnc * pnodeFnc); // block scoped content helpers void SetCurrentStatement(StmtNest *stmt); ParseNodeBlock* GetCurrentBlock(); ParseNodeBlock* GetFunctionBlock(); BlockInfoStack* GetCurrentBlockInfo(); BlockInfoStack* GetCurrentFunctionBlockInfo(); ParseNodeFnc *GetCurrentFunctionNode(); ParseNodeFnc *GetCurrentNonLambdaFunctionNode(); bool NextTokenConfirmsLetDecl() const { return m_token.tk == tkID || m_token.tk == tkLBrack || m_token.tk == tkLCurly || m_token.IsReservedWord(); } bool NextTokenIsPropertyNameStart() const { return m_token.tk == tkID || m_token.tk == tkStrCon || m_token.tk == tkIntCon || m_token.tk == tkFltCon || m_token.tk == tkLBrack || m_token.IsReservedWord(); } template void PushStmt(StmtNest *pStmt, ParseNodeStmt * pnode, OpCode op, LabelId* pLabelIdList) { if (buildAST) { pnode->grfnop = 0; pnode->pnodeOuter = (NULL == m_pstmtCur) ? NULL : m_pstmtCur->pnodeStmt; pStmt->pnodeStmt = pnode; } else { // Assign to pnodeStmt rather than op so that we initialize the whole field. pStmt->pnodeStmt = 0; pStmt->isDeferred = true; pStmt->op = op; } pStmt->pLabelId = pLabelIdList; pStmt->pstmtOuter = m_pstmtCur; SetCurrentStatement(pStmt); } void PopStmt(StmtNest *pStmt); BlockInfoStack *PushBlockInfo(ParseNodeBlock * pnodeBlock); void PopBlockInfo(); void PushDynamicBlock(); void PopDynamicBlock(); void MarkEvalCaller() { if (this->GetCurrentFunctionNode()) { ParseNodeFnc *pnodeFunc = GetCurrentFunctionNode(); pnodeFunc->SetCallsEval(true); } ParseNodeBlock *pnodeBlock = GetCurrentBlock(); if (pnodeBlock != NULL) { pnodeBlock->SetCallsEval(true); PushDynamicBlock(); } } struct ParserState { ParseNodePtr *m_ppnodeScopeSave; ParseNodePtr *m_ppnodeExprScopeSave; charcount_t m_funcInArraySave; int32 *m_pCurrentAstSizeSave; uint m_funcInArrayDepthSave; uint m_nestedCountSave; int m_nextBlockId; #if DEBUG // For very basic validation purpose - to check that we are not going restore to some other block. BlockInfoStack *m_currentBlockInfo; #endif }; class AutoDeferErrorsRestore { public: AutoDeferErrorsRestore(Parser *p) : m_parser(p) { m_deferEllipsisErrorSave = m_parser->m_deferEllipsisError; m_deferCommaError = m_parser->m_deferCommaError; m_ellipsisErrorLocSave = m_parser->m_deferEllipsisErrorLoc; m_commaErrorLocSave = m_parser->m_deferCommaErrorLoc; } ~AutoDeferErrorsRestore() { m_parser->m_deferEllipsisError = m_deferEllipsisErrorSave; m_parser->m_deferCommaError = m_deferCommaError; m_parser->m_deferEllipsisErrorLoc = m_ellipsisErrorLocSave; m_parser->m_deferCommaErrorLoc = m_commaErrorLocSave; } private: Parser *m_parser; RestorePoint m_ellipsisErrorLocSave; RestorePoint m_commaErrorLocSave; bool m_deferEllipsisErrorSave; bool m_deferCommaError; }; // This function is going to capture some of the important current state of the parser to an object. Once we learn // that we need to reparse the grammar again we could use RestoreStateFrom to restore that state to the parser. void CaptureState(ParserState *state); void RestoreStateFrom(ParserState *state); // Future recommendation : Consider consolidating Parser::CaptureState and Scanner::Capture together if we do CaptureState more often. public: IdentPtrList* GetRequestedModulesList(); ModuleImportOrExportEntryList* GetModuleImportEntryList(); ModuleImportOrExportEntryList* GetModuleLocalExportEntryList(); ModuleImportOrExportEntryList* GetModuleIndirectExportEntryList(); ModuleImportOrExportEntryList* GetModuleStarExportEntryList(); protected: IdentPtrList* EnsureRequestedModulesList(); ModuleImportOrExportEntryList* EnsureModuleImportEntryList(); ModuleImportOrExportEntryList* EnsureModuleLocalExportEntryList(); ModuleImportOrExportEntryList* EnsureModuleIndirectExportEntryList(); ModuleImportOrExportEntryList* EnsureModuleStarExportEntryList(); void AddModuleSpecifier(IdentPtr moduleRequest); ModuleImportOrExportEntry* AddModuleImportOrExportEntry(ModuleImportOrExportEntryList* importOrExportEntryList, IdentPtr importName, IdentPtr localName, IdentPtr exportName, IdentPtr moduleRequest); ModuleImportOrExportEntry* AddModuleImportOrExportEntry(ModuleImportOrExportEntryList* importOrExportEntryList, ModuleImportOrExportEntry* importOrExportEntry); void AddModuleLocalExportEntry(ParseNodePtr varDeclNode); void CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportEntryList, IdentPtr exportName); ParseNodeVar * CreateModuleImportDeclNode(IdentPtr localName); public: WellKnownPropertyPids* names(){ return &wellKnownPropertyPids; } IdentPtr CreatePid(__in_ecount(len) LPCOLESTR name, charcount_t len) { return this->GetHashTbl()->PidHashNameLen(name, len); } bool KnownIdent(__in_ecount(len) LPCOLESTR name, charcount_t len) { return this->GetHashTbl()->Contains(name, len); } template static void ForEachItemRefInList(ParseNodePtr *list, THandler handler) { ParseNodePtr *current = list; while (current != nullptr && (*current) != nullptr) { if ((*current)->nop == knopList) { handler(&(*current)->AsParseNodeBin()->pnode1); // Advance to the next node current = &(*current)->AsParseNodeBin()->pnode2; } else { // The last node handler(current); current = nullptr; } } } template static void ForEachItemInList(ParseNodePtr list, THandler handler) { ForEachItemRefInList(&list, [&](ParseNodePtr * item) { Assert(item != nullptr); handler(*item); }); } template static void MapBindIdentifierFromElement(ParseNodePtr elementNode, THandler handler) { ParseNodePtr bindIdentNode = elementNode; if (bindIdentNode->nop == knopAsg) { bindIdentNode = bindIdentNode->AsParseNodeBin()->pnode1; } else if (bindIdentNode->nop == knopEllipsis) { bindIdentNode = bindIdentNode->AsParseNodeUni()->pnode1; } if (bindIdentNode->IsPattern()) { MapBindIdentifier(bindIdentNode, handler); } else if (bindIdentNode->IsVarLetOrConst()) { handler(bindIdentNode); } else { AssertMsg(bindIdentNode->nop == knopEmpty, "Invalid bind identifier"); } } template static void MapBindIdentifier(ParseNodePtr patternNode, THandler handler) { if (patternNode->nop == knopAsg) { patternNode = patternNode->AsParseNodeBin()->pnode1; } Assert(patternNode->IsPattern()); if (patternNode->nop == knopArrayPattern) { ForEachItemInList(patternNode->AsParseNodeArrLit()->pnode1, [&](ParseNodePtr item) { MapBindIdentifierFromElement(item, handler); }); } else { ForEachItemInList(patternNode->AsParseNodeUni()->pnode1, [&](ParseNodePtr item) { Assert(item->nop == knopObjectPatternMember || item->nop == knopEllipsis); if (item->nop == knopObjectPatternMember) { MapBindIdentifierFromElement(item->AsParseNodeBin()->pnode2, handler); } else { MapBindIdentifierFromElement(item->AsParseNodeUni()->pnode1, handler); } }); } } private: struct IdentToken { tokens tk; IdentPtr pid; charcount_t ichMin; charcount_t ichLim; IdentToken() : tk(tkNone), pid(NULL) { } }; void CheckArguments(ParseNodePtr pnode); void CheckArgumentsUse(IdentPtr pid, ParseNodeFnc * pnodeFnc); void CheckStrictModeEvalArgumentsUsage(IdentPtr pid, ParseNodePtr pnode = NULL); // environments on which the strict mode is set, if found enum StrictModeEnvironment { SM_NotUsed, // StrictMode environment is don't care SM_OnGlobalCode, // The current environment is a global code SM_OnFunctionCode, // The current environment is a function code SM_DeferredParse // StrictMode used in deferred parse cases }; template ParseNodeArrLit * ParseArrayLiteral(); template ParseNodePtr ParseStatement(); template ParseNodePtr ParseVariableDeclaration( tokens declarationType, charcount_t ichMin, BOOL fAllowIn = TRUE, BOOL* pfForInOk = nullptr, BOOL singleDefOnly = FALSE, BOOL allowInit = TRUE, BOOL isTopVarParse = TRUE, BOOL isFor = FALSE, BOOL* nativeForOk = nullptr); BOOL TokIsForInOrForOf(); template void ParseStmtList( ParseNodePtr *ppnodeList, ParseNodePtr **pppnodeLast = NULL, StrictModeEnvironment smEnvironment = SM_NotUsed, const bool isSourceElementList = false, bool* strictModeOn = NULL); #if ENABLE_BACKGROUND_PARSING bool FastScanFormalsAndBody(); #endif bool ScanAheadToFunctionEnd(uint count); bool DoParallelParse(ParseNodeFnc * pnodeFnc) const; // TODO: We should really call this StartScope and separate out the notion of scopes and blocks; // blocks refer to actual curly braced syntax, whereas scopes contain symbols. All blocks have // a scope, but some statements like for loops or the with statement introduce a block-less scope. template ParseNodeBlock * StartParseBlock(PnodeBlockType blockType, ScopeType scopeType, LabelId* pLabelId = nullptr); template ParseNodeBlock * StartParseBlockWithCapacity(PnodeBlockType blockType, ScopeType scopeType, int capacity); template ParseNodeBlock * StartParseBlockHelper(PnodeBlockType blockType, Scope *scope, LabelId* pLabelId); void PushFuncBlockScope(ParseNodeBlock * pnodeBlock, ParseNodePtr **ppnodeScopeSave, ParseNodePtr **ppnodeExprScopeSave); void PopFuncBlockScope(ParseNodePtr *ppnodeScopeSave, ParseNodePtr *ppnodeExprScopeSave); template ParseNodeBlock * ParseBlock(LabelId* pLabelId); void FinishParseBlock(ParseNodeBlock * pnodeBlock, bool needScanRCurly = true); void FinishParseFncExprScope(ParseNodeFnc * pnodeFnc, ParseNodeBlock * pnodeFncExprScope); bool IsSpecialName(IdentPtr pid); void CreateSpecialSymbolDeclarations(ParseNodeFnc * pnodeFnc); ParseNodeSpecialName * ReferenceSpecialName(IdentPtr pid, charcount_t ichMin = 0, charcount_t ichLim = 0, bool createNode = false); ParseNodeVar * CreateSpecialVarDeclIfNeeded(ParseNodeFnc * pnodeFnc, IdentPtr pid, bool forceCreate = false); void ProcessCapturedNames(ParseNodeFnc* pnodeFnc); void AddNestedCapturedNames(ParseNodeFnc* pnodeChildFnc); template void BindPidRefs(BlockInfoStack *blockInfo, uint maxBlockId = (uint)-1); void BindPidRefsInScope(IdentPtr pid, Symbol *sym, int blockId, uint maxBlockId = (uint)-1); void MarkEscapingRef(ParseNodePtr pnode, IdentToken *pToken); void SetNestedFuncEscapes() const; void SetSymHasNonLocalReference(Symbol *sym); void PushScope(Scope *scope); void PopScope(Scope *scope); template ParseNodePtr ParseArgList(bool *pCallOfConstants, uint16 *pSpreadArgCount, uint16 * pCount); template ParseNodePtr ParseArrayList(bool *pArrayOfTaggedInts, bool *pArrayOfInts, bool *pArrayOfNumbers, bool *pHasMissingValues, uint *count, uint *spreadCount); template ParseNodePtr ParseMemberList(LPCOLESTR pNameHint, uint32 *pHintLength, tokens declarationType = tkNone); template IdentPtr ParseSuper(bool fAllowCall); bool IsTerminateToken(bool fAllowIn); // Used to determine the type of JavaScript object member. // The values can be combined using bitwise OR. // specifically, it is valid to have getter and setter at the same time. enum MemberType { MemberTypeDataProperty = 1 << 0, // { foo: 1 }, MemberTypeGetter = 1 << 1, // { get foo() } MemberTypeSetter = 1 << 2, // { set foo(arg) {} } MemberTypeMethod = 1 << 3, // { foo() {} } MemberTypeIdentifier = 1 << 4 // { foo } (shorthand for { foo: foo }) }; // Used to map JavaScript object member name to member type. typedef JsUtil::BaseDictionary MemberNameToTypeMap; static MemberNameToTypeMap* CreateMemberNameMap(ArenaAllocator* pAllocator); template void ParseComputedName(ParseNodePtr* ppnodeName, LPCOLESTR* ppNameHint, LPCOLESTR* ppFullNameHint = nullptr, uint32 *pNameLength = nullptr, uint32 *pShortNameOffset = nullptr); template ParseNodeBin * ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint,size_t iecpMin, charcount_t ichMin); template ParseNode * ParseFncDeclCheckScope(ushort flags, bool fAllowIn = true); template ParseNodeFnc * ParseFncDeclNoCheckScope(ushort flags, SuperRestrictionState::State superRestrictionState = SuperRestrictionState::Disallowed, LPCOLESTR pNameHint = nullptr, const bool needsPIDOnRCurlyScan = false, bool fUnaryOrParen = false, bool fAllowIn = true); template ParseNodeFnc * ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, const bool needsPIDOnRCurlyScan, bool fUnaryOrParen, bool noStmtContext, SuperRestrictionState::State superRestrictionState = SuperRestrictionState::Disallowed, bool fAllowIn = true); template void ParseFncName(ParseNodeFnc * pnodeFnc, ushort flags, IdentPtr* pFncNamePid = nullptr); template void ParseFncFormals(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeParentFnc, ushort flags, bool isTopLevelDeferredFunc = false); template void ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, ushort flags, bool fUnaryOrParen, bool noStmtContext, bool *pNeedScanRCurly, bool skipFormals = false, IdentPtr* pFncNamePid = nullptr, bool fAllowIn = true); template void ParseExpressionLambdaBody(ParseNodeFnc * pnodeFnc, bool fAllowIn = true); template void UpdateCurrentNodeFunc(ParseNodeFnc * pnodeFnc, bool fLambda); bool FncDeclAllowedWithoutContext(ushort flags); void FinishFncDecl(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, bool fLambda, bool skipCurlyBraces = false, bool fAllowIn = true); void ParseTopLevelDeferredFunc(ParseNodeFnc * pnodeFnc, ParseNodeFnc * pnodeFncParent, LPCOLESTR pNameHint, bool fLambda, bool *pNeedScanRCurly = nullptr, bool fAllowIn = true); void ParseNestedDeferredFunc(ParseNodeFnc * pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn, bool fAllowIn = true); void CheckStrictFormalParameters(); ParseNodeVar * AddArgumentsNodeToVars(ParseNodeFnc * pnodeFnc); ParseNodeVar * InsertVarAtBeginning(ParseNodeFnc * pnodeFnc, IdentPtr pid); ParseNodeVar * CreateSpecialVarDeclNode(ParseNodeFnc * pnodeFnc, IdentPtr pid); void UpdateArgumentsNode(ParseNodeFnc * pnodeFnc, ParseNodeVar * argNode); void UpdateOrCheckForDuplicateInFormals(IdentPtr pid, SList *formals); LPCOLESTR GetFunctionName(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint); uint CalculateFunctionColumnNumber(); template ParseNodeFnc * GenerateEmptyConstructor(bool extends = false); template ParseNodePtr GenerateModuleFunctionWrapper(); IdentPtr ParseClassPropertyName(IdentPtr * hint); template ParseNodeClass * ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint, uint32 *pHintLength, uint32 *pShortNameOffset); template ParseNodePtr ParseStringTemplateDecl(ParseNodePtr pnodeTagFnc); // This is used in the es6 class pattern. LPCOLESTR ConstructFinalHintNode(IdentPtr pClassName, IdentPtr pMemberName, IdentPtr pGetSet, bool isStatic, uint32* nameLength, uint32* pShortNameOffset, bool isComputedName = false, LPCOLESTR pMemberNameHint = nullptr); // Construct the name from the parse node. LPCOLESTR FormatPropertyString(LPCOLESTR propertyString, ParseNodePtr pNode, uint32 *fullNameHintLength, uint32 *pShortNameOffset); LPCOLESTR ConstructNameHint(ParseNodeBin * pNode, uint32* fullNameHintLength, uint32 *pShortNameOffset); LPCOLESTR AppendNameHints(IdentPtr left, IdentPtr right, uint32 *pNameLength, uint32 *pShortNameOffset, bool ignoreAddDotWithSpace = false, bool wrapInBrackets = false); LPCOLESTR AppendNameHints(IdentPtr left, LPCOLESTR right, uint32 *pNameLength, uint32 *pShortNameOffset, bool ignoreAddDotWithSpace = false, bool wrapInBrackets = false); LPCOLESTR AppendNameHints(LPCOLESTR left, IdentPtr right, uint32 *pNameLength, uint32 *pShortNameOffset, bool ignoreAddDotWithSpace = false, bool wrapInBrackets = false); LPCOLESTR AppendNameHints(LPCOLESTR left, LPCOLESTR right, uint32 *pNameLength, uint32 *pShortNameOffset, bool ignoreAddDotWithSpace = false, bool wrapInBrackets = false); LPCOLESTR AppendNameHints(LPCOLESTR leftStr, uint32 leftLen, LPCOLESTR rightStr, uint32 rightLen, uint32 *pNameLength, uint32 *pShortNameOffset, bool ignoreAddDotWithSpace = false, bool wrapInBrackets = false); WCHAR * AllocateStringOfLength(ULONG length); void FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn = true); template bool ParseOptionalExpr( ParseNodePtr* pnode, bool fUnaryOrParen = false, int oplMin = koplNo, BOOL *pfCanAssign = NULL, BOOL fAllowIn = TRUE, BOOL fAllowEllipsis = FALSE, _Inout_opt_ IdentToken* pToken = NULL); template ParseNodePtr ParseExpr( int oplMin = koplNo, BOOL *pfCanAssign = NULL, BOOL fAllowIn = TRUE, BOOL fAllowEllipsis = FALSE, LPCOLESTR pHint = NULL, uint32 *pHintLength = nullptr, uint32 *pShortNameOffset = nullptr, _Inout_opt_ IdentToken* pToken = NULL, bool fUnaryOrParen = false, _Inout_opt_ bool* pfLikelyPattern = nullptr, _Inout_opt_ charcount_t *plastRParen = nullptr); template ParseNodePtr ParseTerm( BOOL fAllowCall = TRUE, LPCOLESTR pNameHint = nullptr, uint32 *pHintLength = nullptr, uint32 *pShortNameOffset = nullptr, _Inout_opt_ IdentToken* pToken = nullptr, bool fUnaryOrParen = false, BOOL fCanAssignToCall = TRUE, _Out_opt_ BOOL* pfCanAssign = nullptr, _Inout_opt_ BOOL* pfLikelyPattern = nullptr, _Out_opt_ bool* pfIsDotOrIndex = nullptr, _Inout_opt_ charcount_t *plastRParen = nullptr); template ParseNodePtr ParsePostfixOperators( ParseNodePtr pnode, BOOL fAllowCall, BOOL fInNew, BOOL isAsyncExpr, BOOL fCanAssignToCallResult, BOOL *pfCanAssign, _Inout_ IdentToken* pToken, _Out_opt_ bool* pfIsDotOrIndex = nullptr); void ThrowNewTargetSyntaxErrForGlobalScope(); template IdentPtr ParseMetaProperty( tokens metaParentKeyword, charcount_t ichMin, _Out_opt_ BOOL* pfCanAssign = nullptr); bool IsImportOrExportStatementValidHere(); bool IsTopLevelModuleFunc(); template ParseNodePtr ParseImport(); template void ParseImportClause(ModuleImportOrExportEntryList* importEntryList, bool parsingAfterComma = false); template ParseNodePtr ParseImportCall(); template ParseNodePtr ParseExportDeclaration(bool *needTerminator = nullptr); template ParseNodePtr ParseDefaultExportClause(); template void ParseNamedImportOrExportClause(ModuleImportOrExportEntryList* importOrExportEntryList, bool isExportClause); template IdentPtr ParseImportOrExportFromClause(bool throwIfNotFound); BOOL NodeIsIdent(ParseNodePtr pnode, IdentPtr pid); BOOL NodeIsEvalName(ParseNodePtr pnode); BOOL NodeIsSuperName(ParseNodePtr pnode); BOOL IsJSONValid(ParseNodePtr pnodeExpr) { OpCode jnop = (knopNeg == pnodeExpr->nop) ? pnodeExpr->AsParseNodeUni()->pnode1->nop : pnodeExpr->nop; if (knopNeg == pnodeExpr->nop) { return (knopInt == jnop || knopFlt == jnop); } else { return (knopInt == jnop || knopFlt == jnop || knopStr == jnop || knopNull == jnop || knopTrue == jnop || knopFalse == jnop || knopObject == jnop || knopArray == jnop); } } BOOL IsConstantInFunctionCall(ParseNodePtr pnode); BOOL IsConstantInArrayLiteral(ParseNodePtr pnode); ParseNodePtr ConvertMemberToMemberPattern(ParseNodePtr pnodeMember); ParseNodeUni * ConvertObjectToObjectPattern(ParseNodePtr pnodeMemberList); ParseNodePtr GetRightSideNodeFromPattern(ParseNodePtr pnode); ParseNodePtr ConvertArrayToArrayPattern(ParseNodePtr pnode); ParseNodePtr ConvertToPattern(ParseNodePtr pnode); void AppendToList(ParseNodePtr * node, ParseNodePtr nodeToAppend); bool IsES6DestructuringEnabled() const; bool IsPossiblePatternStart() const { return m_token.tk == tkLCurly || m_token.tk == tkLBrack; } bool IsPostFixOperators() const { return m_token.tk == tkLParen || m_token.tk == tkLBrack || m_token.tk == tkDot || m_token.tk == tkStrTmplBasic || m_token.tk == tkStrTmplBegin; } template ParseNodeStmt * ParseTryCatchFinally(); template ParseNodeTry * ParseTry(); template ParseNodeCatch * ParseCatch(); template ParseNodeFinally * ParseFinally(); template ParseNodeCase * ParseCase(ParseNodePtr *ppnodeBody); template ParseNodeRegExp * ParseRegExp(); template ParseNodeUni * ParseDestructuredArrayLiteral(tokens declarationType, bool isDecl, bool topLevel = true); template ParseNodeUni * ParseDestructuredObjectLiteral(tokens declarationType, bool isDecl, bool topLevel = true); template ParseNodePtr ParseDestructuredLiteral(tokens declarationType, bool isDecl, bool topLevel = true, DestructuringInitializerContext initializerContext = DIC_None, bool allowIn = true, BOOL *forInOfOkay = nullptr, BOOL *nativeForOkay = nullptr); template ParseNodePtr ParseDestructuredVarDecl(tokens declarationType, bool isDecl, bool *hasSeenRest, bool topLevel = true, bool allowEmptyExpression = true, bool isObjectPattern = false); template ParseNodePtr ParseDestructuredInitializer(ParseNodeUni * lhsNode, bool isDecl, bool topLevel, DestructuringInitializerContext initializerContext, bool allowIn, BOOL *forInOfOkay, BOOL *nativeForOkay); template static bool IsNaNOrInfinityLiteral(LPCOLESTR str); void ParseDestructuredLiteralWithScopeSave(tokens declarationType, bool isDecl, bool topLevel, DestructuringInitializerContext initializerContext = DIC_None, bool allowIn = true); public: void ValidateSourceElementList(); void ValidateFormals(); bool IsStrictMode() const; BOOL ExpectingExternalSource(); IdentPtr GetArgumentsPid() const { return wellKnownPropertyPids.arguments; } IdentPtr GetEvalPid() const { return wellKnownPropertyPids.eval; } IdentPtr GetTargetPid() const { return wellKnownPropertyPids.target; } BackgroundParseItem *GetCurrBackgroundParseItem() const { return currBackgroundParseItem; } void SetCurrBackgroundParseItem(BackgroundParseItem *item) { currBackgroundParseItem = item; } private: void DeferOrEmitPotentialSpreadError(ParseNodePtr pnodeT); template void TrackAssignment(ParseNodePtr pnodeT, IdentToken* pToken); PidRefStack* PushPidRef(IdentPtr pid); PidRefStack* FindOrAddPidRef(IdentPtr pid, int blockId, Js::LocalFunctionId funcId); void RemovePrevPidRef(IdentPtr pid, PidRefStack *lastRef); void SetPidRefsInScopeDynamic(IdentPtr pid, int blockId); void RestoreScopeInfo(Js::ScopeInfo * scopeInfo); void FinishScopeInfo(Js::ScopeInfo * scopeInfo); bool LabelExists(IdentPtr pid, LabelId* pLabelIdList); LabelId* CreateLabelId(IdentPtr pid); void AddToNodeList(ParseNode ** ppnodeList, ParseNode *** pppnodeLast, ParseNode * pnodeAdd); void AddToNodeListEscapedUse(ParseNode ** ppnodeList, ParseNode *** pppnodeLast, ParseNode * pnodeAdd); void ChkCurTokNoScan(int tk, int wErr) { if (m_token.tk != tk) { Error(wErr); } } void ChkCurTok(int tk, int wErr) { if (m_token.tk != tk) { Error(wErr); } else { this->GetScanner()->Scan(); } } void ChkNxtTok(int tk, int wErr) { this->GetScanner()->Scan(); ChkCurTok(tk, wErr); } template void FinishFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn); void FinishDeferredFunction(ParseNodeBlock * pnodeScopeList); /*********************************************************************** Misc ***********************************************************************/ bool m_UsesArgumentsAtGlobal; // "arguments" used at global code. BOOL m_fUseStrictMode; // ES5 Use Strict mode. In AST mode this is a global flag; in NoAST mode it is pushed and popped. bool m_InAsmMode; // Currently parsing Asm.Js module bool m_deferAsmJs; BOOL m_fExpectExternalSource; BOOL m_deferringAST; BOOL m_stoppedDeferredParse; enum FncDeclFlag : ushort { fFncNoFlgs = 0, fFncDeclaration = 1 << 0, fFncNoArg = 1 << 1, fFncOneArg = 1 << 2, //Force exactly one argument. fFncNoName = 1 << 3, fFncLambda = 1 << 4, fFncMethod = 1 << 5, fFncClassMember = 1 << 6, fFncGenerator = 1 << 7, fFncAsync = 1 << 8, fFncModule = 1 << 9, fFncClassConstructor = 1 << 10, fFncBaseClassConstructor = 1 << 11, }; // // If we need the scanner to force PID creation temporarily, use this auto object // to turn scanner deferred parsing off temporarily and restore at destructor. // class AutoTempForcePid { private: Scanner_t* m_scanner; bool m_forcePid; BYTE m_oldScannerDeferredParseFlags; public: AutoTempForcePid(Scanner_t* scanner, bool forcePid) : m_scanner(scanner), m_forcePid(forcePid) { if (forcePid) { m_oldScannerDeferredParseFlags = scanner->SetDeferredParse(FALSE); } } ~AutoTempForcePid() { if (m_forcePid) { m_scanner->SetDeferredParseFlags(m_oldScannerDeferredParseFlags); } } }; class AutoMarkInParsingArgs { public: AutoMarkInParsingArgs(Parser * parser) : m_parser(parser) { m_prevState = m_parser->GetIsInParsingArgList(); m_prevDestructuringState = m_parser->GetHasDestructuringPattern(); m_parser->SetHasDestructuringPattern(false); m_parser->SetIsInParsingArgList(true); } ~AutoMarkInParsingArgs() { m_parser->SetIsInParsingArgList(m_prevState); if (!m_prevState) { m_parser->SetHasDestructuringPattern(false); } else { // Reset back to previous state only when the current call node does not have usage of destructuring expression. if (!m_parser->GetHasDestructuringPattern()) { m_parser->SetHasDestructuringPattern(m_prevDestructuringState); } } } private: Parser *m_parser; bool m_prevState; bool m_prevDestructuringState; }; public: charcount_t GetSourceIchLim() { return m_sourceLim; } static BOOL NodeEqualsName(ParseNodePtr pnode, LPCOLESTR sz, uint32 cch); };