blob: 1e97fbb58a6263150354fbb9531f0bae90779c77 [file]
//-------------------------------------------------------------------------------------------------------
// 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;
};
// 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;
typedef void (*ParseErrorCallback)(void *data, charcount_t position, charcount_t length, HRESULT hr);
struct PidRefStack;
struct DeferredFunctionStub;
DeferredFunctionStub * BuildDeferredStubTree(ParseNode *pnodeFnc, Recycler *recycler);
struct StmtNest;
struct BlockInfoStack;
struct ParseContext
{
LPCUTF8 pszSrc;
size_t offset;
size_t length;
charcount_t characterOffset;
int nextBlockId;
ULONG grfscr;
ULONG lineNumber;
ParseNodePtr pnodeProg;
SourceContextInfo* sourceContextInfo;
BlockInfoStack* currentBlockInfo;
bool strictMode;
bool fromExternal;
};
template <bool nullTerminated> class UTF8EncodingPolicyBase;
typedef UTF8EncodingPolicyBase<false> NotNullTerminatedUTF8EncodingPolicy;
template <typename T> class Scanner;
namespace Js
{
class ParseableFunctionInfo;
class FunctionBody;
};
class Parser
{
typedef Scanner<NotNullTerminatedUTF8EncodingPolicy> Scanner_t;
private:
template <OpCode nop> static int GetNodeSize();
template <OpCode nop> static ParseNodePtr StaticAllocNode(ArenaAllocator * alloc)
{
ParseNodePtr pnode = (ParseNodePtr)alloc->Alloc(GetNodeSize<nop>());
Assert(pnode != nullptr);
return pnode;
}
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 ClearScriptContext() { m_scriptContext = nullptr; }
bool IsBackgroundParser() const { return m_isInBackground; }
bool IsDoingFastScan() const { return m_doingFastScan; }
static IdentPtr PidFromNode(ParseNodePtr pnode);
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 DeferredParse(Js::LocalFunctionId functionId);
BOOL IsDeferredFnc();
void ReduceDeferredScriptLength(size_t chars);
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 ParseNodePtr* 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 ParseNodePtr* 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 ParseNodePtr* 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:
HRESULT ParseSourceInternal(
__out ParseNodePtr* parseTree, LPCUTF8 pszSrc, size_t offsetInBytes,
size_t lengthInCodePoints, charcount_t offsetInChars, bool fromExternal,
ULONG grfscr, CompileScriptException *pse, Js::LocalFunctionId * nextFunctionId, ULONG lineNumber, SourceContextInfo * sourceContextInfo);
ParseNodePtr Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcount_t charOffset, 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;
ParseErrorCallback m_errorCallback;
void * m_errorCallbackData;
BOOL m_uncertainStructure;
bool m_hasParallelJob;
bool m_doingFastScan;
int m_nextBlockId;
// 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 the script context's guest
// arena for that purpose. This list is then unregistered from the guest arena at the end of parsing/scanning.
SList<UnifiedRegex::RegexPattern *, ArenaAllocator> m_registeredRegexPatterns;
protected:
Js::ScriptContext* m_scriptContext;
HashTbl * m_phtbl;
static const uint HASH_TABLE_SIZE = 256;
__declspec(noreturn) void Error(HRESULT hr);
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 GenerateCode(ParseNodePtr pnode, void *pvUser, int32 cbUser,
LPCOLESTR pszSrc, int32 cchSrc, LPCOLESTR pszTitle);
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_pscan;
public:
// create nodes using arena allocator; used by AST transformation
template <OpCode nop>
static ParseNodePtr StaticCreateNodeT(ArenaAllocator* alloc, charcount_t ichMin = 0, charcount_t ichLim = 0)
{
ParseNodePtr pnode = StaticAllocNode<nop>(alloc);
InitNode(nop,pnode);
// default - may be changed
pnode->ichMin = ichMin;
pnode->ichLim = ichLim;
return pnode;
}
static ParseNodePtr StaticCreateBinNode(OpCode nop, ParseNodePtr pnode1,ParseNodePtr pnode2,ArenaAllocator* alloc);
static ParseNodePtr StaticCreateBlockNode(ArenaAllocator* alloc, charcount_t ichMin = 0, charcount_t ichLim = 0, int blockId = -1, PnodeBlockType blockType = PnodeBlockType::Regular);
ParseNodePtr CreateNode(OpCode nop, charcount_t ichMin,charcount_t ichLim);
ParseNodePtr CreateDummyFuncNode(bool fDeclaration);
ParseNodePtr CreateTriNode(OpCode nop, ParseNodePtr pnode1,
ParseNodePtr pnode2, ParseNodePtr pnode3,
charcount_t ichMin,charcount_t ichLim);
ParseNodePtr CreateTempNode(ParseNode* initExpr);
ParseNodePtr CreateTempRef(ParseNode* tempNode);
ParseNodePtr CreateNode(OpCode nop) { return CreateNode(nop, m_pscan? m_pscan->IchMinTok() : 0); }
ParseNodePtr CreateDeclNode(OpCode nop, IdentPtr pid, SymbolType symbolType, bool errorOnRedecl = true, bool *isRedecl = nullptr);
Symbol* AddDeclForPid(ParseNodePtr pnode, IdentPtr pid, SymbolType symbolType, bool errorOnRedecl, bool *isRedecl = nullptr);
ParseNodePtr CreateNameNode(IdentPtr pid)
{
ParseNodePtr pnode = CreateNode(knopName);
pnode->sxPid.pid = pid;
pnode->sxPid.sym=NULL;
pnode->sxPid.symRef=NULL;
return pnode;
}
ParseNodePtr CreateBlockNode(PnodeBlockType blockType = PnodeBlockType::Regular)
{
ParseNodePtr pnode = CreateNode(knopBlock);
InitBlockNode(pnode, m_nextBlockId++, blockType);
return pnode;
}
// Creating parse nodes.
ParseNodePtr CreateNode(OpCode nop, charcount_t ichMin);
ParseNodePtr CreateTriNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2, ParseNodePtr pnode3);
ParseNodePtr CreateIntNode(int32 lw);
ParseNodePtr CreateStrNode(IdentPtr pid);
ParseNodePtr CreateUniNode(OpCode nop, ParseNodePtr pnodeOp);
ParseNodePtr CreateBinNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2);
ParseNodePtr CreateCallNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2);
// Create parse node with token limis
template <OpCode nop>
ParseNodePtr CreateNodeT(charcount_t ichMin,charcount_t ichLim);
ParseNodePtr CreateUniNode(OpCode nop, ParseNodePtr pnode1, charcount_t ichMin,charcount_t ichLim);
ParseNodePtr CreateBlockNode(charcount_t ichMin,charcount_t ichLim, PnodeBlockType blockType = PnodeBlockType::Regular);
ParseNodePtr CreateNameNode(IdentPtr pid,charcount_t ichMin,charcount_t ichLim);
ParseNodePtr CreateBinNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2,
charcount_t ichMin,charcount_t ichLim);
ParseNodePtr CreateCallNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2,
charcount_t ichMin,charcount_t ichLim);
void PrepareScanner(bool fromExternal);
void PrepareForBackgroundParse();
void AddFastScannedRegExpNode(ParseNodePtr const pnode);
#if ENABLE_BACKGROUND_PARSING
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(ParseNodePtr pnodeFunc, ParseContext *parseContext, bool topLevelDeferred, CompileScriptException *pse);
#endif
void CheckPidIsValid(IdentPtr pid, bool autoArgumentsObject = false);
void AddVarDeclToBlock(ParseNode *pnode);
// Add a var declaration. Only use while parsing. Assumes m_ppnodeVar is pointing to the right place already
ParseNodePtr CreateVarDeclNode(IdentPtr pid, SymbolType symbolType, bool autoArgumentsObject = false, ParseNodePtr pnodeFnc = NULL, bool checkReDecl = true, bool *isRedecl = nullptr);
// Add a var declaration, during parse tree rewriting. Will setup m_ppnodeVar for the given pnodeFnc
ParseNodePtr AddVarDeclNode(IdentPtr pid, ParseNodePtr pnodeFnc);
// Add a 'const' or 'let' declaration.
ParseNodePtr 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:
template <OpCode nop> ParseNodePtr CreateNodeWithScanner();
template <OpCode nop> ParseNodePtr CreateNodeWithScanner(charcount_t ichMin);
ParseNodePtr CreateStrNodeWithScanner(IdentPtr pid);
ParseNodePtr CreateIntNodeWithScanner(int32 lw);
ParseNodePtr CreateProgNodeWithScanner(bool isModuleSource);
static void InitNode(OpCode nop,ParseNodePtr pnode);
static void InitBlockNode(ParseNodePtr pnode, int blockId, PnodeBlockType blockType);
private:
ParseNodePtr m_currentNodeNonLambdaFunc; // current function or NULL
ParseNodePtr m_currentNodeNonLambdaDeferredFunc; // current function or NULL
ParseNodePtr m_currentNodeFunc; // current function or NULL
ParseNodePtr m_currentNodeDeferredFunc; // current function or NULL
ParseNodePtr m_currentNodeProg; // current program
DeferredFunctionStub *m_currDeferredStub;
DeferredFunctionStub *m_prevSiblingDeferredStub;
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_isInBackground;
bool m_reparsingLambdaParams;
// This bool is used for deferring the shorthand initializer error ( {x = 1}) - as it is allowed in the destructuring grammar.
bool m_hasDeferredShorthandInitError;
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
};
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;
/*
* Parsing states for super restriction
*/
static const uint ParsingSuperRestrictionState_SuperDisallowed = 0;
static const uint ParsingSuperRestrictionState_SuperCallAndPropertyAllowed = 1;
static const uint ParsingSuperRestrictionState_SuperPropertyAllowed = 2;
uint m_parsingSuperRestrictionState;
friend class AutoParsingSuperRestrictionStateRestorer;
// Used for issuing spread and rest errors when there is ambiguity with parameter list and parenthesized expressions
uint m_parenDepth;
bool m_deferEllipsisError;
RestorePoint m_EllipsisErrLoc;
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<ParseNodePtr, ArenaAllocator> NodeDList;
NodeDList* fastScannedRegExpNodes;
BlockIdsStack *m_currentDynamicBlock;
int GetCurrentDynamicBlockId() const;
void AppendFunctionToScopeList(bool fDeclaration, ParseNodePtr pnodeFnc);
// block scoped content helpers
void SetCurrentStatement(StmtNest *stmt);
ParseNode* GetCurrentBlock();
ParseNode* GetFunctionBlock();
BlockInfoStack* GetCurrentBlockInfo();
BlockInfoStack* GetCurrentFunctionBlockInfo();
ParseNode *GetCurrentFunctionNode();
ParseNode *GetCurrentNonLambdaFunctionNode();
bool IsNodeAllowedInCurrentDeferralState(OpCode op)
{
if (!this->m_deferringAST)
{
return true;
}
switch(op)
{
case knopBlock:
case knopVarDecl:
case knopConstDecl:
case knopLetDecl:
case knopFncDecl:
case knopName:
return true;
default:
return false;
}
}
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<bool buildAST>
void PushStmt(StmtNest *pStmt, ParseNodePtr pnode, OpCode op, ParseNodePtr pnodeLab, LabelId* pLabelIdList)
{
AssertMem(pStmt);
if (buildAST)
{
AssertNodeMem(pnode);
AssertNodeMemN(pnodeLab);
pnode->sxStmt.grfnop = 0;
pnode->sxStmt.pnodeOuter = (NULL == m_pstmtCur) ? NULL : m_pstmtCur->pnodeStmt;
pStmt->pnodeStmt = pnode;
pStmt->pnodeLab = pnodeLab;
}
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(ParseNodePtr pnodeBlock);
void PopBlockInfo();
void PushDynamicBlock();
void PopDynamicBlock();
ParseNodePtr PnodeLabel(IdentPtr pid, ParseNodePtr pnodeLabels);
void MarkEvalCaller()
{
if (this->GetCurrentFunctionNode())
{
ParseNodePtr pnodeFunc = GetCurrentFunctionNode();
pnodeFunc->sxFnc.SetCallsEval(true);
}
ParseNode *pnodeBlock = GetCurrentBlock();
if (pnodeBlock != NULL)
{
pnodeBlock->sxBlock.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
};
// 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);
ParseNodePtr CreateModuleImportDeclNode(IdentPtr localName);
void MarkIdentifierReferenceIsModuleExport(IdentPtr localName);
public:
WellKnownPropertyPids* names(){ return &wellKnownPropertyPids; }
IdentPtr CreatePid(__in_ecount(len) LPCOLESTR name, charcount_t len)
{
return m_phtbl->PidHashNameLen(name, len);
}
bool KnownIdent(__in_ecount(len) LPCOLESTR name, charcount_t len)
{
return m_phtbl->Contains(name, len);
}
template <typename THandler>
static void ForEachItemRefInList(ParseNodePtr *list, THandler handler)
{
ParseNodePtr *current = list;
while (current != nullptr && (*current) != nullptr)
{
if ((*current)->nop == knopList)
{
handler(&(*current)->sxBin.pnode1);
// Advance to the next node
current = &(*current)->sxBin.pnode2;
}
else
{
// The last node
handler(current);
current = nullptr;
}
}
}
template <typename THandler>
static void ForEachItemInList(ParseNodePtr list, THandler handler)
{
ForEachItemRefInList(&list, [&](ParseNodePtr * item) {
Assert(item != nullptr);
handler(*item);
});
}
template <class THandler>
static void MapBindIdentifierFromElement(ParseNodePtr elementNode, THandler handler)
{
ParseNodePtr bindIdentNode = elementNode;
if (bindIdentNode->nop == knopAsg)
{
bindIdentNode = bindIdentNode->sxBin.pnode1;
}
else if (bindIdentNode->nop == knopEllipsis)
{
bindIdentNode = bindIdentNode->sxUni.pnode1;
}
if (bindIdentNode->IsPattern())
{
MapBindIdentifier(bindIdentNode, handler);
}
else if (bindIdentNode->IsVarLetOrConst())
{
handler(bindIdentNode);
}
else
{
AssertMsg(bindIdentNode->nop == knopEmpty, "Invalid bind identifier");
}
}
template <class THandler>
static void MapBindIdentifier(ParseNodePtr patternNode, THandler handler)
{
if (patternNode->nop == knopAsg)
{
patternNode = patternNode->sxBin.pnode1;
}
Assert(patternNode->IsPattern());
if (patternNode->nop == knopArrayPattern)
{
ForEachItemInList(patternNode->sxArrLit.pnode1, [&](ParseNodePtr item) {
MapBindIdentifierFromElement(item, handler);
});
}
else
{
ForEachItemInList(patternNode->sxUni.pnode1, [&](ParseNodePtr item) {
Assert(item->nop == knopObjectPatternMember);
MapBindIdentifierFromElement(item->sxBin.pnode2, 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, ParseNodePtr 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<bool buildAST> ParseNodePtr ParseArrayLiteral();
template<bool buildAST> ParseNodePtr ParseStatement();
template<bool buildAST> 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<bool buildAST>
void ParseStmtList(
ParseNodePtr *ppnodeList,
ParseNodePtr **pppnodeLast = NULL,
StrictModeEnvironment smEnvironment = SM_NotUsed,
const bool isSourceElementList = false,
bool* strictModeOn = NULL);
bool FastScanFormalsAndBody();
bool ScanAheadToFunctionEnd(uint count);
bool DoParallelParse(ParseNodePtr 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<bool buildAST> ParseNodePtr StartParseBlock(PnodeBlockType blockType, ScopeType scopeType, ParseNodePtr pnodeLabel = NULL, LabelId* pLabelId = NULL);
template<bool buildAST> ParseNodePtr StartParseBlockWithCapacity(PnodeBlockType blockType, ScopeType scopeType, int capacity);
template<bool buildAST> ParseNodePtr StartParseBlockHelper(PnodeBlockType blockType, Scope *scope, ParseNodePtr pnodeLabel, LabelId* pLabelId);
void PushFuncBlockScope(ParseNodePtr pnodeBlock, ParseNodePtr **ppnodeScopeSave, ParseNodePtr **ppnodeExprScopeSave);
void PopFuncBlockScope(ParseNodePtr *ppnodeScopeSave, ParseNodePtr *ppnodeExprScopeSave);
template<bool buildAST> ParseNodePtr ParseBlock(ParseNodePtr pnodeLabel, LabelId* pLabelId);
void FinishParseBlock(ParseNode *pnodeBlock, bool needScanRCurly = true);
void FinishParseFncExprScope(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncExprScope);
template<const bool backgroundPidRefs>
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<bool buildAST> ParseNodePtr ParseArgList(bool *pCallOfConstants, uint16 *pSpreadArgCount, uint16 * pCount);
template<bool buildAST> ParseNodePtr ParseArrayList(bool *pArrayOfTaggedInts, bool *pArrayOfInts, bool *pArrayOfNumbers, bool *pHasMissingValues, uint *count, uint *spreadCount);
template<bool buildAST> ParseNodePtr ParseMemberList(LPCOLESTR pNameHint, uint32 *pHintLength, tokens declarationType = tkNone);
template<bool buildAST> ParseNodePtr ParseSuper(ParseNodePtr pnode, bool fAllowCall);
// 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<WCHAR*, MemberType, ArenaAllocator, PrimeSizePolicy> MemberNameToTypeMap;
static MemberNameToTypeMap* CreateMemberNameMap(ArenaAllocator* pAllocator);
template<bool buildAST> void ParseComputedName(ParseNodePtr* ppnodeName, LPCOLESTR* ppNameHint, LPCOLESTR* ppFullNameHint = nullptr, uint32 *pNameLength = nullptr, uint32 *pShortNameOffset = nullptr);
template<bool buildAST> ParseNodePtr ParseMemberGetSet(OpCode nop, LPCOLESTR* ppNameHint);
template<bool buildAST> ParseNodePtr ParseFncDecl(ushort flags, LPCOLESTR pNameHint = NULL, const bool needsPIDOnRCurlyScan = false, bool resetParsingSuperRestrictionState = true, bool fUnaryOrParen = false);
template<bool buildAST> bool ParseFncNames(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncParent, ushort flags, ParseNodePtr **pLastNodeRef);
template<bool buildAST> void ParseFncFormals(ParseNodePtr pnodeFnc, ParseNodePtr pnodeParentFnc, ushort flags);
template<bool buildAST> bool ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, ushort flags, bool *pHasName, bool fUnaryOrParen, bool noStmtContext, bool *pNeedScanRCurly, bool skipFormals = false);
template<bool buildAST> void ParseExpressionLambdaBody(ParseNodePtr pnodeFnc);
template<bool buildAST> void UpdateCurrentNodeFunc(ParseNodePtr pnodeFnc, bool fLambda);
bool FncDeclAllowedWithoutContext(ushort flags);
void FinishFncDecl(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, ParseNodePtr *lastNodeRef, bool skipCurlyBraces = false);
void ParseTopLevelDeferredFunc(ParseNodePtr pnodeFnc, ParseNodePtr pnodeFncParent, LPCOLESTR pNameHint);
void ParseNestedDeferredFunc(ParseNodePtr pnodeFnc, bool fLambda, bool *pNeedScanRCurly, bool *pStrictModeTurnedOn);
void CheckStrictFormalParameters();
ParseNodePtr AddArgumentsNodeToVars(ParseNodePtr pnodeFnc);
void UpdateArgumentsNode(ParseNodePtr pnodeFnc, ParseNodePtr argNode);
void UpdateOrCheckForDuplicateInFormals(IdentPtr pid, SList<IdentPtr> *formals);
LPCOLESTR GetFunctionName(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint);
uint CalculateFunctionColumnNumber();
template<bool buildAST> ParseNodePtr GenerateEmptyConstructor(bool extends = false);
template<bool buildAST> ParseNodePtr GenerateModuleFunctionWrapper();
IdentPtr ParseClassPropertyName(IdentPtr * hint);
template<bool buildAST> ParseNodePtr ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint, uint32 *pHintLength, uint32 *pShortNameOffset);
template<bool buildAST> 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(ParseNodePtr 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(ParseNodePtr pnodeFnc);
template<bool buildAST> 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<bool buildAST> 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<bool buildAST> ParseNodePtr ParseTerm(
BOOL fAllowCall = TRUE,
LPCOLESTR pNameHint = nullptr,
uint32 *pHintLength = nullptr,
uint32 *pShortNameOffset = nullptr,
_Inout_opt_ IdentToken* pToken = nullptr,
bool fUnaryOrParen = false,
_Out_opt_ BOOL* pfCanAssign = nullptr,
_Inout_opt_ BOOL* pfLikelyPattern = nullptr,
_Out_opt_ bool* pfIsDotOrIndex = nullptr,
_Inout_opt_ charcount_t *plastRParen = nullptr);
template<bool buildAST> ParseNodePtr ParsePostfixOperators(
ParseNodePtr pnode,
BOOL fAllowCall,
BOOL fInNew,
BOOL isAsyncExpr,
BOOL *pfCanAssign,
_Inout_ IdentToken* pToken,
_Out_opt_ bool* pfIsDotOrIndex = nullptr);
void ThrowNewTargetSyntaxErrForGlobalScope();
template<bool buildAST> ParseNodePtr ParseMetaProperty(
tokens metaParentKeyword,
charcount_t ichMin,
_Out_opt_ BOOL* pfCanAssign = nullptr);
bool IsImportOrExportStatementValidHere();
template<bool buildAST> ParseNodePtr ParseImport();
template<bool buildAST> void ParseImportClause(ModuleImportOrExportEntryList* importEntryList, bool parsingAfterComma = false);
template<bool buildAST> ParseNodePtr ParseImportCall();
template<bool buildAST> ParseNodePtr ParseExportDeclaration();
template<bool buildAST> ParseNodePtr ParseDefaultExportClause();
template<bool buildAST> void ParseNamedImportOrExportClause(ModuleImportOrExportEntryList* importOrExportEntryList, bool isExportClause);
template<bool buildAST> IdentPtr ParseImportOrExportFromClause(bool throwIfNotFound);
BOOL NodeIsIdent(ParseNodePtr pnode, IdentPtr pid);
BOOL NodeIsEvalName(ParseNodePtr pnode);
BOOL IsJSONValid(ParseNodePtr pnodeExpr)
{
OpCode jnop = (knopNeg == pnodeExpr->nop) ? pnodeExpr->sxUni.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 CreateParamPatternNode(ParseNodePtr pnode1);
ParseNodePtr ConvertMemberToMemberPattern(ParseNodePtr pnodeMember);
ParseNodePtr 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<bool buildAST> ParseNodePtr ParseTryCatchFinally();
template<bool buildAST> ParseNodePtr ParseTry();
template<bool buildAST> ParseNodePtr ParseCatch();
template<bool buildAST> ParseNodePtr ParseFinally();
template<bool buildAST> ParseNodePtr ParseCase(ParseNodePtr *ppnodeBody);
template<bool buildAST> ParseNodePtr ParseRegExp();
template <bool buildAST>
ParseNodePtr ParseDestructuredArrayLiteral(tokens declarationType, bool isDecl, bool topLevel = true);
template <bool buildAST>
ParseNodePtr ParseDestructuredObjectLiteral(tokens declarationType, bool isDecl, bool topLevel = true);
template <bool buildAST>
ParseNodePtr ParseDestructuredLiteral(tokens declarationType,
bool isDecl,
bool topLevel = true,
DestructuringInitializerContext initializerContext = DIC_None,
bool allowIn = true,
BOOL *forInOfOkay = nullptr,
BOOL *nativeForOkay = nullptr);
template <bool buildAST>
ParseNodePtr ParseDestructuredVarDecl(tokens declarationType, bool isDecl, bool *hasSeenRest, bool topLevel = true, bool allowEmptyExpression = true);
template <bool buildAST>
ParseNodePtr ParseDestructuredInitializer(ParseNodePtr lhsNode,
bool isDecl,
bool topLevel,
DestructuringInitializerContext initializerContext,
bool allowIn,
BOOL *forInOfOkay,
BOOL *nativeForOkay);
template<bool CheckForNegativeInfinity> 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; }
void Release()
{
RELEASEPTR(m_pscan);
RELEASEPTR(m_phtbl);
}
private:
void DeferOrEmitPotentialSpreadError(ParseNodePtr pnodeT);
template<bool buildAST> 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 PnodeLabelNoAST(IdentToken* pToken, LabelId* pLabelIdList);
LabelId* CreateLabelId(IdentToken* pToken);
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
{
m_pscan->Scan();
}
}
void ChkNxtTok(int tk, int wErr)
{
m_pscan->Scan();
ChkCurTok(tk, wErr);
}
template <class Fn>
void FinishFunctionsInScope(ParseNodePtr pnodeScopeList, Fn fn);
void FinishDeferredFunction(ParseNodePtr 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,
};
//
// 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);
}
}
};
public:
charcount_t GetSourceIchLim() { return m_sourceLim; }
static BOOL NodeEqualsName(ParseNodePtr pnode, LPCOLESTR sz, uint32 cch);
};
#define PTNODE(nop,sn,pc,nk,ok,json) \
template<> inline int Parser::GetNodeSize<nop>() { return kcbPn##nk; }
#include "ptlist.h"