//-------------------------------------------------------------------------------------------------------
// 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

struct Ident;
typedef Ident *IdentPtr;

class Scope;

/***************************************************************************
Flags for classifying node operators.
***************************************************************************/
const uint fnopNone     = 0x0000;
const uint fnopConst    = 0x0001; // constant
const uint fnopLeaf     = 0x0002; // leaf
const uint fnopUni      = 0x0004; // unary
const uint fnopBin      = 0x0008; // binary
const uint fnopRel      = 0x0010; // relational
const uint fnopAsg      = 0x0020; // assignment
const uint fnopBreak    = 0x0040; // break can be used within this statement
const uint fnopContinue = 0x0080; // continue can be used within this statement
const uint fnopCleanup  = 0x0100; // requires cleanup (eg, with or for-in).
const uint fnopJump     = 0x0200;
const uint fnopNotExprStmt = 0x0400;
const uint fnopBinList  = 0x0800;
const uint fnopExprMask = (fnopLeaf|fnopUni|fnopBin);
const uint fnopAllowDefer = 0x1000; // allow to be created during defer parse

/***************************************************************************
Flags for classifying parse nodes.
***************************************************************************/
enum PNodeFlags : ushort
{
    fpnNone                                  = 0x0000,

    // knopFncDecl nodes.
    fpnArguments_overriddenByDecl            = 0x0001, // function has a let/const decl, class or nested function named 'arguments', which overrides the built-in arguments object in the body
    fpnArguments_overriddenInParam           = 0x0002, // function has a parameter named arguments
    fpnArguments_varDeclaration              = 0x0004, // function has a var declaration named 'arguments', which may change the way an 'arguments' identifier is resolved

    // knopVarDecl nodes.
    fpnArguments                             = 0x0008,
    fpnHidden                                = 0x0010,

    // Statement nodes.
    fpnExplicitSemicolon                     = 0x0020, // statement terminated by an explicit semicolon
    fpnAutomaticSemicolon                    = 0x0040, // statement terminated by an automatic semicolon
    fpnMissingSemicolon                      = 0x0080, // statement missing terminating semicolon, and is not applicable for automatic semicolon insertion
    fpnDclList                               = 0x0100, // statement is a declaration list
    fpnSyntheticNode                         = 0x0200, // node is added by the parser or does it represent user code
    fpnIndexOperator                         = 0x0400, // dot operator is an optimization of an index operator
    fpnJumbStatement                         = 0x0800, // break or continue that was removed by error recovery

    // Unary/Binary nodes
    fpnCanFlattenConcatExpr                  = 0x1000, // the result of the binary operation can participate in concat N

    // Potentially overlapping traversal flags
    // These flags are set and cleared during a single node traversal and their values can be used in other node traversals.
    fpnMemberReference                       = 0x2000, // The node is a member reference symbol
    fpnCapturesSyms                          = 0x4000, // The node is a statement (or contains a sub-statement)
                                                       // that captures symbols.

    fpnSpecialSymbol                         = 0x8000,
};

/***************************************************************************
Data structs for ParseNodes.
***************************************************************************/
class ParseNodeUni;
class ParseNodeBin;
class ParseNodeTri;
class ParseNodeInt;
class ParseNodeFloat;
class ParseNodeRegExp;
class ParseNodeStr;
class ParseNodeName;
class ParseNodeVar;
class ParseNodeCall;
class ParseNodeSuperCall;
class ParseNodeSpecialName;
class ParseNodeExportDefault;
class ParseNodeStrTemplate;
class ParseNodeSuperReference;
class ParseNodeArrLit;
class ParseNodeClass;
class ParseNodeParamPattern;

class ParseNodeStmt;
class ParseNodeBlock;
class ParseNodeJump;
class ParseNodeWith;
class ParseNodeIf;
class ParseNodeSwitch;
class ParseNodeCase;
class ParseNodeReturn;
class ParseNodeTryFinally;
class ParseNodeTryCatch;
class ParseNodeTry;
class ParseNodeCatch;
class ParseNodeFinally;
class ParseNodeLoop;
class ParseNodeWhile;
class ParseNodeFor;
class ParseNodeForInOrForOf;

class ParseNodeFnc;
class ParseNodeProg;
class ParseNodeModule;

class ParseNode
{
public:
    ParseNode(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeUni * AsParseNodeUni();
    ParseNodeBin * AsParseNodeBin();
    ParseNodeTri * AsParseNodeTri();
    ParseNodeInt * AsParseNodeInt();
    ParseNodeFloat * AsParseNodeFloat();
    ParseNodeRegExp * AsParseNodeRegExp();
    ParseNodeVar * AsParseNodeVar();
    ParseNodeStr * AsParseNodeStr();
    ParseNodeName * AsParseNodeName();

    ParseNodeSpecialName * AsParseNodeSpecialName();
    ParseNodeExportDefault * AsParseNodeExportDefault();
    ParseNodeStrTemplate * AsParseNodeStrTemplate();
    ParseNodeSuperReference * AsParseNodeSuperReference();
    ParseNodeArrLit * AsParseNodeArrLit();

    ParseNodeCall * AsParseNodeCall();
    ParseNodeSuperCall * AsParseNodeSuperCall();
    ParseNodeClass * AsParseNodeClass();
    ParseNodeParamPattern * AsParseNodeParamPattern();

    ParseNodeStmt * AsParseNodeStmt();
    ParseNodeBlock * AsParseNodeBlock();
    ParseNodeJump * AsParseNodeJump();
    ParseNodeWith * AsParseNodeWith();
    ParseNodeIf * AsParseNodeIf();
    ParseNodeSwitch * AsParseNodeSwitch();
    ParseNodeCase * AsParseNodeCase();
    ParseNodeReturn * AsParseNodeReturn();

    ParseNodeTryFinally * AsParseNodeTryFinally();
    ParseNodeTryCatch * AsParseNodeTryCatch();
    ParseNodeTry * AsParseNodeTry();
    ParseNodeCatch * AsParseNodeCatch();
    ParseNodeFinally * AsParseNodeFinally();

    ParseNodeLoop * AsParseNodeLoop();
    ParseNodeWhile * AsParseNodeWhile();
    ParseNodeFor * AsParseNodeFor();
    ParseNodeForInOrForOf * AsParseNodeForInOrForOf();

    ParseNodeFnc * AsParseNodeFnc();
    ParseNodeProg * AsParseNodeProg();
    ParseNodeModule * AsParseNodeModule();

    static uint Grfnop(int nop)
    {
        Assert(nop < knopLim);
        return nop < knopLim ? mpnopgrfnop[nop] : fnopNone;
    }

    BOOL IsStatement()
    {
        return nop >= knopList || ((Grfnop(nop) & fnopAsg) != 0);
    }

    uint Grfnop(void)
    {
        Assert(nop < knopLim);
        return nop < knopLim ? mpnopgrfnop[nop] : fnopNone;
    }

    IdentPtr name();

    charcount_t LengthInCodepoints() const
    {
        return (this->ichLim - this->ichMin);
    }

    // This node is a function decl node and function has a var declaration named 'arguments',
    bool HasVarArguments() const
    {
        return ((nop == knopFncDecl) && (grfpn & PNodeFlags::fpnArguments_varDeclaration));
    }

    bool CapturesSyms() const
    {
        return (grfpn & PNodeFlags::fpnCapturesSyms) != 0;
    }

    void SetCapturesSyms()
    {
        grfpn |= PNodeFlags::fpnCapturesSyms;
    }

    bool IsInList() const { return this->isInList; }
    void SetIsInList() { this->isInList = true; }

    bool IsNotEscapedUse() const { return this->notEscapedUse; }
    void SetNotEscapedUse() { this->notEscapedUse = true; }

    bool CanFlattenConcatExpr() const { return !!(this->grfpn & PNodeFlags::fpnCanFlattenConcatExpr); }

    bool IsCallApplyTargetLoad() { return isCallApplyTargetLoad; }
    void SetIsCallApplyTargetLoad() { isCallApplyTargetLoad = true; }    

    bool IsPatternDeclaration() { return isPatternDeclaration; }
    void SetIsPatternDeclaration() { isPatternDeclaration = true; }

    bool IsUserIdentifier();

    bool IsVarLetOrConst() const
    {
        return this->nop == knopVarDecl || this->nop == knopLetDecl || this->nop == knopConstDecl;
    }

    ParseNodePtr GetFormalNext();

    bool IsPattern() const
    {
        return nop == knopObjectPattern || nop == knopArrayPattern;
    }

#if DBG_DUMP
    void Dump();
#endif

public:
    static const uint mpnopgrfnop[knopLim];

    OpCode nop;

    bool isUsed : 1;                // indicates whether an expression such as x++ is used

private:
    bool isInList : 1;
    // Use by byte code generator
    bool notEscapedUse : 1;         // Currently, only used by child of knopComma
    bool isCallApplyTargetLoad : 1;

    // Use by bytecodegen to identify the current node is a destructuring pattern declaration node.
    bool isPatternDeclaration : 1;

public:
    ushort grfpn;

    charcount_t ichMin;         // start offset into the original source buffer
    charcount_t ichLim;         // end offset into the original source buffer
    Js::RegSlot location;

#ifdef EDIT_AND_CONTINUE
    ParseNodePtr parent;
#endif
};

#define DISABLE_SELF_CAST(T) private: T * As##T()
// unary operators
class ParseNodeUni : public ParseNode
{
public:
    ParseNodeUni(OpCode nop, charcount_t ichMin, charcount_t ichLim, ParseNode * pnode1);

    ParseNodePtr pnode1;

    DISABLE_SELF_CAST(ParseNodeUni);
};

// binary operators
class ParseNodeBin : public ParseNode
{
public:
    ParseNodeBin(OpCode nop, charcount_t ichMin, charcount_t ichLim, ParseNode * pnode1, ParseNode * pnode2);

    ParseNodePtr pnode1;
    ParseNodePtr pnode2;

    DISABLE_SELF_CAST(ParseNodeBin);
};

// ternary operator
class ParseNodeTri : public ParseNode
{
public:
    ParseNodeTri(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnode1;
    ParseNodePtr pnode2;
    ParseNodePtr pnode3;

    DISABLE_SELF_CAST(ParseNodeTri);
};

// integer constant
class ParseNodeInt : public ParseNode
{
public:
    ParseNodeInt(charcount_t ichMin, charcount_t ichMax, int32 lw);

    int32 lw;

    DISABLE_SELF_CAST(ParseNodeInt);
};

// double constant
class ParseNodeFloat : public ParseNode
{
public:
    ParseNodeFloat(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    double dbl;
    bool maybeInt : 1;

    DISABLE_SELF_CAST(ParseNodeFloat);
};

class ParseNodeRegExp : public ParseNode
{
public:
    ParseNodeRegExp(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    UnifiedRegex::RegexPattern* regexPattern;
    uint regexPatternIndex;

    DISABLE_SELF_CAST(ParseNodeRegExp);
};

// identifier or string

class ParseNodeStr : public ParseNode
{
public:
    ParseNodeStr(charcount_t ichMin, charcount_t ichLim, IdentPtr pid);

    IdentPtr const pid;

    DISABLE_SELF_CAST(ParseNodeStr);

};

class Symbol;
struct PidRefStack;
class ParseNodeName : public ParseNode
{
public:
    ParseNodeName(charcount_t ichMin, charcount_t ichLim, IdentPtr pid);

    IdentPtr const pid;
private:
    Symbol **symRef;
public:
    Symbol *sym;

    void SetSymRef(PidRefStack *ref);
    void ClearSymRef() { symRef = nullptr; }
    Symbol **GetSymRef() const { return symRef; }
    Js::PropertyId PropertyIdFromNameNode() const;

    bool IsSpecialName() { return isSpecialName; }

    DISABLE_SELF_CAST(ParseNodeName);

protected:
    void SetIsSpecialName() { isSpecialName = true; }

private:
    bool isSpecialName;         // indicates a PnPid (knopName) is a special name like 'this' or 'super'
};

// variable declaration
class ParseNodeVar : public ParseNode
{
public:
    ParseNodeVar(OpCode nop, charcount_t ichMin, charcount_t ichLim, IdentPtr name);

    ParseNodePtr pnodeNext;
    IdentPtr const pid;
    Symbol *sym;
    Symbol **symRef;
    ParseNodePtr pnodeInit;
    BOOLEAN isSwitchStmtDecl;
    BOOLEAN isBlockScopeFncDeclVar;

    DISABLE_SELF_CAST(ParseNodeVar);
};

// Array literal
class ParseNodeArrLit : public ParseNodeUni
{
public:
    ParseNodeArrLit(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    uint count;
    uint spreadCount;
    BYTE arrayOfTaggedInts:1;     // indicates that array initializer nodes are all tagged ints
    BYTE arrayOfInts:1;           // indicates that array initializer nodes are all ints
    BYTE arrayOfNumbers:1;        // indicates that array initializer nodes are all numbers
    BYTE hasMissingValues:1;

    DISABLE_SELF_CAST(ParseNodeArrLit);
};

class FuncInfo;

enum PnodeBlockType : unsigned
{
    Global,
    Function,
    Regular,
    Parameter
};

enum FncFlags : uint
{
    kFunctionNone                               = 0,
    kFunctionNested                             = 1 << 0, // True if function is nested in another.
    kFunctionDeclaration                        = 1 << 1, // is this a declaration or an expression?
    kFunctionCallsEval                          = 1 << 2, // function uses eval
    kFunctionUsesArguments                      = 1 << 3, // function uses arguments
    kFunctionHasHeapArguments                   = 1 << 4, // function's "arguments" escape the scope
    kFunctionHasReferenceableBuiltInArguments   = 1 << 5, // the built-in 'arguments' object is referenceable in the function
    kFunctionIsAccessor                         = 1 << 6, // function is a property getter or setter
    kFunctionHasNonThisStmt                     = 1 << 7,
    kFunctionStrictMode                         = 1 << 8,
    kFunctionHasDestructuredParams              = 1 << 9,
    kFunctionIsModule                           = 1 << 10, // function is a module body
    kFunctionHasComputedName                    = 1 << 11,
    kFunctionHasWithStmt                        = 1 << 12, // function (or child) uses with
    kFunctionIsLambda                           = 1 << 13,
    kFunctionChildCallsEval                     = 1 << 14,
    kFunctionHasNonSimpleParameterList          = 1 << 15,
    kFunctionHasSuperReference                  = 1 << 16,
    kFunctionIsMethod                           = 1 << 17,
    kFunctionIsClassConstructor                 = 1 << 18, // function is a class constructor
    kFunctionIsBaseClassConstructor             = 1 << 19, // function is a base class constructor
    kFunctionIsClassMember                      = 1 << 20, // function is a class member
    kFunctionHasHomeObj                         = 1 << 21, // function has home object. Class or member functions need to have home obj.
    kFunctionIsGeneratedDefault                 = 1 << 22, // Is the function generated by us as a default (e.g. default class constructor)
    kFunctionHasDefaultArguments                = 1 << 23, // Function has one or more ES6 default arguments
    kFunctionIsStaticMember                     = 1 << 24,
    kFunctionIsGenerator                        = 1 << 25, // Function is an ES6 generator function
    kFunctionAsmjsMode                          = 1 << 26,
    kFunctionIsDeclaredInParamScope             = 1 << 27, // Function is declared in parameter scope (ex: inside default argument)
    kFunctionIsAsync                            = 1 << 28, // function is async
    kFunctionHasDirectSuper                     = 1 << 29, // super()
    kFunctionIsDefaultModuleExport              = 1 << 30, // function is the default export of a module
    kFunctionHasAnyWriteToFormals               = (uint)1 << 31  // To Track if there are any writes to formals.
};

struct RestorePoint;
struct DeferredFunctionStub;

namespace SuperRestrictionState {
    enum State {
        Disallowed = 0,
        CallAndPropertyAllowed = 1,
        PropertyAllowed = 2
    };
}

// function declaration
class ParseNodeFnc : public ParseNode
{
public:
    ParseNodeFnc(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeNext;
    ParseNodeVar * pnodeName;
    IdentPtr pid;
    LPCOLESTR hint;
    uint32 hintLength;
    uint32 hintOffset;
    bool  isNameIdentifierRef;
    bool  nestedFuncEscapes;
    ParseNodeBlock * pnodeScopes;
    ParseNodeBlock * pnodeBodyScope;
    ParseNodePtr pnodeParams;
    ParseNodePtr pnodeVars;
    ParseNodePtr pnodeBody;
    ParseNodeVar * pnodeRest;

    FuncInfo *funcInfo; // function information gathered during byte code generation
    Scope *scope;

    uint nestedCount; // Nested function count (valid until children have been processed)
    uint nestedIndex; // Index within the parent function (Used by ByteCodeGenerator)

    uint16 firstDefaultArg; // Position of the first default argument, if any

    FncFlags fncFlags;
    int32 astSize;
    size_t cbMin; // Min an Lim UTF8 offsets.
    size_t cbLim;
    ULONG lineNumber;   // Line number relative to the current source buffer of the function declaration.
    ULONG columnNumber; // Column number of the declaration.
    Js::LocalFunctionId functionId;
#if DBG
    Js::LocalFunctionId deferredParseNextFunctionId;
#endif
    RestorePoint *pRestorePoint;
    DeferredFunctionStub *deferredStub;
    IdentPtrSet *capturedNames;
    bool canBeDeferred;
    bool isBodyAndParamScopeMerged; // Indicates whether the param scope and the body scope of the function can be merged together or not.
                                    // We cannot merge both scopes together if there is any closure capture or eval is present in the param scope.
    Js::RegSlot homeObjLocation;    // Stores the RegSlot from where the home object needs to be copied

    static const int32 MaxStackClosureAST = 800000;

    SuperRestrictionState::State superRestrictionState;

    static bool CanBeRedeferred(FncFlags flags) { return !(flags & (kFunctionIsGenerator | kFunctionIsAsync)); }

private:
    void SetFlags(uint flags, bool set)
    {
        if (set)
        {
            fncFlags = (FncFlags)(fncFlags | flags);
        }
        else
        {
            fncFlags = (FncFlags)(fncFlags & ~flags);
        }
    }

    bool HasFlags(uint flags) const
    {
        return (fncFlags & flags) == flags;
    }

    bool HasAnyFlags(uint flags) const
    {
        return (fncFlags & flags) != 0;
    }

    bool HasNoFlags(uint flags) const
    {
        return (fncFlags & flags) == 0;
    }

public:
    void ClearFlags()
    {
        fncFlags = kFunctionNone;
        canBeDeferred = false;
        isBodyAndParamScopeMerged = true;
    }

    void SetAsmjsMode(bool set = true) { SetFlags(kFunctionAsmjsMode, set); }
    void SetCallsEval(bool set = true) { SetFlags(kFunctionCallsEval, set); }
    void SetChildCallsEval(bool set = true) { SetFlags(kFunctionChildCallsEval, set); }
    void SetDeclaration(bool set = true) { SetFlags(kFunctionDeclaration, set); }
    void SetHasDefaultArguments(bool set = true) { SetFlags(kFunctionHasDefaultArguments, set); }
    void SetHasDestructuredParams(bool set = true) { SetFlags(kFunctionHasDestructuredParams, set); }
    void SetHasHeapArguments(bool set = true) { SetFlags(kFunctionHasHeapArguments, set); }
    void SetHasAnyWriteToFormals(bool set = true) { SetFlags((uint)kFunctionHasAnyWriteToFormals, set); }
    void SetHasNonSimpleParameterList(bool set = true) { SetFlags(kFunctionHasNonSimpleParameterList, set); }
    void SetHasNonThisStmt(bool set = true) { SetFlags(kFunctionHasNonThisStmt, set); }
    void SetHasReferenceableBuiltInArguments(bool set = true) { SetFlags(kFunctionHasReferenceableBuiltInArguments, set); }
    void SetHasSuperReference(bool set = true) { SetFlags(kFunctionHasSuperReference, set); }
    void SetHasDirectSuper(bool set = true) { SetFlags(kFunctionHasDirectSuper, set); }
    void SetHasWithStmt(bool set = true) { SetFlags(kFunctionHasWithStmt, set); }
    void SetIsAccessor(bool set = true) { SetFlags(kFunctionIsAccessor, set); }
    void SetIsAsync(bool set = true) { SetFlags(kFunctionIsAsync, set); }
    void SetIsClassConstructor(bool set = true) { SetFlags(kFunctionIsClassConstructor, set); }
    void SetIsBaseClassConstructor(bool set = true) { SetFlags(kFunctionIsBaseClassConstructor, set); }
    void SetIsClassMember(bool set = true) { SetFlags(kFunctionIsClassMember, set); }
    void SetIsGeneratedDefault(bool set = true) { SetFlags(kFunctionIsGeneratedDefault, set); }
    void SetIsGenerator(bool set = true) { SetFlags(kFunctionIsGenerator, set); }
    void SetIsLambda(bool set = true) { SetFlags(kFunctionIsLambda, set); }
    void SetIsMethod(bool set = true) { SetFlags(kFunctionIsMethod, set); }
    void SetIsStaticMember(bool set = true) { SetFlags(kFunctionIsStaticMember, set); }
    void SetNested(bool set = true) { SetFlags(kFunctionNested, set); }
    void SetStrictMode(bool set = true) { SetFlags(kFunctionStrictMode, set); }
    void SetIsModule(bool set = true) { SetFlags(kFunctionIsModule, set); }
    void SetHasComputedName(bool set = true) { SetFlags(kFunctionHasComputedName, set); }
    void SetHasHomeObj(bool set = true) { SetFlags(kFunctionHasHomeObj, set); }
    void SetUsesArguments(bool set = true) { SetFlags(kFunctionUsesArguments, set); }
    void SetIsDefaultModuleExport(bool set = true) { SetFlags(kFunctionIsDefaultModuleExport, set); }
    void SetIsDeclaredInParamScope(bool set = true) { SetFlags(kFunctionIsDeclaredInParamScope, set); }
    void SetNestedFuncEscapes(bool set = true) { nestedFuncEscapes = set; }
    void SetCanBeDeferred(bool set = true) { canBeDeferred = set; }
    void ResetBodyAndParamScopeMerged() { isBodyAndParamScopeMerged = false; }
    void SetHomeObjLocation(Js::RegSlot location) { this->homeObjLocation = location; }

    bool CallsEval() const { return HasFlags(kFunctionCallsEval); }
    bool ChildCallsEval() const { return HasFlags(kFunctionChildCallsEval); }
    bool GetArgumentsObjectEscapes() const { return HasFlags(kFunctionHasHeapArguments); }
    bool GetAsmjsMode() const { return HasFlags(kFunctionAsmjsMode); }
    bool GetStrictMode() const { return HasFlags(kFunctionStrictMode); }
    bool HasDefaultArguments() const { return HasFlags(kFunctionHasDefaultArguments); }
    bool HasDestructuredParams() const { return HasFlags(kFunctionHasDestructuredParams); }
    bool HasHeapArguments() const { return true; /* HasFlags(kFunctionHasHeapArguments); Disabling stack arguments. Always return HeapArguments as True */ }
    bool HasAnyWriteToFormals() const { return HasFlags((uint)kFunctionHasAnyWriteToFormals); }
    bool HasOnlyThisStmts() const { return !HasFlags(kFunctionHasNonThisStmt); }
    bool HasReferenceableBuiltInArguments() const { return HasFlags(kFunctionHasReferenceableBuiltInArguments); }
    bool HasSuperReference() const { return HasFlags(kFunctionHasSuperReference); }
    bool HasDirectSuper() const { return HasFlags(kFunctionHasDirectSuper); }
    bool HasNonSimpleParameterList() { return HasFlags(kFunctionHasNonSimpleParameterList); }
    bool HasWithStmt() const { return HasFlags(kFunctionHasWithStmt); }
    bool IsAccessor() const { return HasFlags(kFunctionIsAccessor); }
    bool IsAsync() const { return HasFlags(kFunctionIsAsync); }
    bool IsConstructor() const { return HasNoFlags(kFunctionIsAsync|kFunctionIsLambda|kFunctionIsAccessor);  }
    bool IsClassConstructor() const { return HasFlags(kFunctionIsClassConstructor); }
    bool IsBaseClassConstructor() const { return HasFlags(kFunctionIsBaseClassConstructor); }
    bool IsDerivedClassConstructor() const { return IsClassConstructor() && !IsBaseClassConstructor(); }
    bool IsClassMember() const { return HasFlags(kFunctionIsClassMember); }
    bool IsDeclaration() const { return HasFlags(kFunctionDeclaration); }
    bool IsGeneratedDefault() const { return HasFlags(kFunctionIsGeneratedDefault); }
    bool IsGenerator() const { return HasFlags(kFunctionIsGenerator); }
    bool IsCoroutine() const { return HasAnyFlags(kFunctionIsGenerator | kFunctionIsAsync); }
    bool IsLambda() const { return HasFlags(kFunctionIsLambda); }
    bool IsMethod() const { return HasFlags(kFunctionIsMethod); }
    bool IsNested() const { return HasFlags(kFunctionNested); }
    bool IsStaticMember() const { return HasFlags(kFunctionIsStaticMember); }
    bool IsModule() const { return HasFlags(kFunctionIsModule); }
    bool HasComputedName() const { return HasFlags(kFunctionHasComputedName); }
    bool HasHomeObj() const { return HasFlags(kFunctionHasHomeObj); }
    bool UsesArguments() const { return HasFlags(kFunctionUsesArguments); }
    bool IsDefaultModuleExport() const { return HasFlags(kFunctionIsDefaultModuleExport); }
    bool IsDeclaredInParamScope() const { return HasFlags(kFunctionIsDeclaredInParamScope); }
    bool NestedFuncEscapes() const { return nestedFuncEscapes; }
    bool CanBeDeferred() const { return canBeDeferred; }
    bool IsBodyAndParamScopeMerged() { return isBodyAndParamScopeMerged; }
    Js::RegSlot GetHomeObjLocation() const { return homeObjLocation; }
    // Only allow the normal functions to be asm.js
    bool IsAsmJsAllowed() const { return (fncFlags & ~(
        kFunctionAsmjsMode |
        kFunctionNested |
        kFunctionDeclaration |
        kFunctionStrictMode |
        kFunctionHasReferenceableBuiltInArguments |
        kFunctionHasNonThisStmt |
        // todo:: we shouldn't accept kFunctionHasAnyWriteToFormals on the asm module, but it looks like a bug is setting that flag incorrectly
        kFunctionHasAnyWriteToFormals
    )) == 0; }

    size_t LengthInBytes()
    {
        return cbLim - cbMin;
    }

    Symbol *GetFuncSymbol();
    void SetFuncSymbol(Symbol *sym);

    ParseNodePtr GetParamScope() const;
    ParseNodePtr GetBodyScope() const;
    ParseNodePtr GetTopLevelScope() const
    {
        // Top level scope will be the same for knopProg and knopFncDecl.
        return GetParamScope();
    }

    template<typename Fn>
    void MapContainerScopes(Fn fn)
    {
        fn(this->pnodeScopes->pnodeScopes);
        if (this->pnodeBodyScope != nullptr)
        {
            fn(this->pnodeBodyScope->pnodeScopes);
        }
    }

    IdentPtrSet* EnsureCapturedNames(ArenaAllocator* alloc);
    IdentPtrSet* GetCapturedNames();
    bool HasAnyCapturedNames();

    DISABLE_SELF_CAST(ParseNodeFnc);
};

// class declaration
class ParseNodeClass : public ParseNode
{
public:
    ParseNodeClass(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeVar * pnodeName;
    ParseNodeVar * pnodeDeclName;
    ParseNodeBlock * pnodeBlock;
    ParseNodeFnc * pnodeConstructor;
    ParseNodePtr pnodeMembers;
    ParseNodePtr pnodeStaticMembers;
    ParseNodePtr pnodeExtends;

    bool isDefaultModuleExport;

    void SetIsDefaultModuleExport(bool set) { isDefaultModuleExport = set; }
    bool IsDefaultModuleExport() const { return isDefaultModuleExport; }

    DISABLE_SELF_CAST(ParseNodeClass);
};

// export default expr
class ParseNodeExportDefault : public ParseNode
{
public:
    ParseNodeExportDefault(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeExpr;

    DISABLE_SELF_CAST(ParseNodeExportDefault);
};

// string template declaration
class ParseNodeStrTemplate : public ParseNode
{
public:
    ParseNodeStrTemplate(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeStringLiterals;
    ParseNodePtr pnodeStringRawLiterals;
    ParseNodePtr pnodeSubstitutionExpressions;
    uint16 countStringLiterals;
    BYTE isTaggedTemplate:1;

    DISABLE_SELF_CAST(ParseNodeStrTemplate);
};

// global program
class ParseNodeProg : public ParseNodeFnc
{
public:
    ParseNodeProg(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeLastValStmt;  // Used by ByteCodeGenerator
    bool m_UsesArgumentsAtGlobal;

    DISABLE_SELF_CAST(ParseNodeProg);
};

// global module
class ParseNodeModule : public ParseNodeProg
{
public:
    ParseNodeModule(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ModuleImportOrExportEntryList* localExportEntries;
    ModuleImportOrExportEntryList* indirectExportEntries;
    ModuleImportOrExportEntryList* starExportEntries;
    ModuleImportOrExportEntryList* importEntries;
    IdentPtrList* requestedModules;

    DISABLE_SELF_CAST(ParseNodeModule);
};

// function call
class ParseNodeCall : public ParseNode
{
public:
    ParseNodeCall(OpCode nop, charcount_t ichMin, charcount_t ichLim, ParseNodePtr pnodeTarget, ParseNodePtr pnodeArgs);

    ParseNodePtr pnodeTarget;
    ParseNodePtr pnodeArgs;
    uint16 argCount;
    uint16 spreadArgCount;
    BYTE callOfConstants : 1;
    BYTE isApplyCall : 1;
    BYTE isEvalCall : 1;
    BYTE isSuperCall : 1;
    BYTE hasDestructuring : 1;

    DISABLE_SELF_CAST(ParseNodeCall);
};

// base for statement nodes
class ParseNodeStmt : public ParseNode
{
public:
    ParseNodeStmt(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeStmt * pnodeOuter;

    // Set by parsing code, used by code gen.
    uint grfnop;

    // Needed for byte code gen.
    Js::ByteCodeLabel breakLabel;
    Js::ByteCodeLabel continueLabel;

    bool emitLabels;

    DISABLE_SELF_CAST(ParseNodeStmt);
};

// block { }
class ParseNodeBlock : public ParseNodeStmt
{
public:
    ParseNodeBlock(charcount_t ichMin, charcount_t ichLim, int blockId, PnodeBlockType blockType);

    ParseNodePtr pnodeStmt;
    ParseNodePtr pnodeLastValStmt;
    ParseNodePtr pnodeLexVars;
    ParseNodePtr pnodeScopes;
    ParseNodePtr pnodeNext;
    Scope        *scope;

    ParseNodeBlock * enclosingBlock;
    int blockId;
    PnodeBlockType blockType:2;
    BYTE         callsEval:1;
    BYTE         childCallsEval:1;

    void SetCallsEval(bool does) { callsEval = does; }
    bool GetCallsEval() const { return callsEval; }

    void SetChildCallsEval(bool does) { childCallsEval = does; }
    bool GetChildCallsEval() const { return childCallsEval; }

    void SetEnclosingBlock(ParseNodeBlock * pnode) { enclosingBlock = pnode; }
    ParseNodeBlock * GetEnclosingBlock() const { return enclosingBlock; }

    bool HasBlockScopedContent() const;

    DISABLE_SELF_CAST(ParseNodeBlock);
};

// break and continue
class ParseNodeJump : public ParseNodeStmt
{
public:
    ParseNodeJump(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeStmt * pnodeTarget;
    bool hasExplicitTarget;

    DISABLE_SELF_CAST(ParseNodeJump);
};

// base for loop nodes
class ParseNodeLoop : public ParseNodeStmt
{
public:
    ParseNodeLoop(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    // Needed for byte code gen
    uint loopId;


    DISABLE_SELF_CAST(ParseNodeLoop);
};

// while and do-while loops
class ParseNodeWhile : public ParseNodeLoop
{
public:
    ParseNodeWhile(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeCond;
    ParseNodePtr pnodeBody;

    DISABLE_SELF_CAST(ParseNodeWhile);
};

// with
class ParseNodeWith : public ParseNodeStmt
{
public:
    ParseNodeWith(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeObj;
    ParseNodePtr pnodeBody;
    ParseNodePtr pnodeScopes;
    ParseNodePtr pnodeNext;
    Scope        *scope;

    DISABLE_SELF_CAST(ParseNodeWith);
};

// Destructure pattern for function/catch parameter
class ParseNodeParamPattern : public ParseNodeUni
{
public:
    ParseNodeParamPattern(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeNext;
    Js::RegSlot location;
    DISABLE_SELF_CAST(ParseNodeParamPattern);
};

// if
class ParseNodeIf : public ParseNodeStmt
{
public:
    ParseNodeIf(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeCond;
    ParseNodePtr pnodeTrue;
    ParseNodePtr pnodeFalse;

    DISABLE_SELF_CAST(ParseNodeIf);
};

// for-in loop
class ParseNodeForInOrForOf : public ParseNodeLoop
{
public:
    ParseNodeForInOrForOf(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeObj;
    ParseNodePtr pnodeBody;
    ParseNodePtr pnodeLval;
    ParseNodeBlock * pnodeBlock;
    Js::RegSlot itemLocation;

    DISABLE_SELF_CAST(ParseNodeForInOrForOf);
};

// for loop
class ParseNodeFor : public ParseNodeLoop
{
public:
    ParseNodeFor(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeCond;
    ParseNodePtr pnodeBody;
    ParseNodePtr pnodeInit;
    ParseNodePtr pnodeIncr;
    ParseNodeBlock * pnodeBlock;
    ParseNodeFor * pnodeInverted;

    DISABLE_SELF_CAST(ParseNodeFor);
};

// switch
class ParseNodeSwitch : public ParseNodeStmt
{
public:
    ParseNodeSwitch(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeVal;
    ParseNodeCase * pnodeCases;
    ParseNodeCase * pnodeDefault;
    ParseNodeBlock * pnodeBlock;

    DISABLE_SELF_CAST(ParseNodeSwitch);
};

// switch case
class ParseNodeCase : public ParseNodeStmt
{
public:
    ParseNodeCase(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeCase * pnodeNext;
    ParseNodePtr pnodeExpr; // nullptr for default
    ParseNodeBlock * pnodeBody;
    Js::ByteCodeLabel labelCase;


    DISABLE_SELF_CAST(ParseNodeCase);
};

// return [expr]
class ParseNodeReturn : public ParseNodeStmt
{
public:
    ParseNodeReturn(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeExpr;

    DISABLE_SELF_CAST(ParseNodeReturn);
};

// try-catch-finally
class ParseNodeTryFinally : public ParseNodeStmt
{
public:
    ParseNodeTryFinally(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeTry * pnodeTry;
    ParseNodeFinally * pnodeFinally;

    DISABLE_SELF_CAST(ParseNodeTryFinally);
};

// try-catch
class ParseNodeTryCatch : public ParseNodeStmt
{
public:
    ParseNodeTryCatch(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodeTry * pnodeTry;
    ParseNodeCatch * pnodeCatch;

    DISABLE_SELF_CAST(ParseNodeTryCatch);
};

// try-catch
class ParseNodeTry : public ParseNodeStmt
{
public:
    ParseNodeTry(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeBody;

    DISABLE_SELF_CAST(ParseNodeTry);
};

// { catch(e : expr) {body} }
class ParseNodeCatch : public ParseNodeStmt
{
public:
    ParseNodeCatch(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr GetParam() { return pnodeParam; }
    void SetParam(ParseNodeName * pnode) { pnodeParam = pnode;  }
    void SetParam(ParseNodeParamPattern * pnode) { pnodeParam = pnode; }    

    ParseNodePtr pnodeNext;

private:
    ParseNode *  pnodeParam;   // Name or ParamPattern
public:
    ParseNodePtr pnodeBody;
    ParseNodePtr pnodeScopes;
    Scope        *scope;

    DISABLE_SELF_CAST(ParseNodeCatch);
};

// finally
class ParseNodeFinally : public ParseNodeStmt
{
public:
    ParseNodeFinally(OpCode nop, charcount_t ichMin, charcount_t ichLim);

    ParseNodePtr pnodeBody;

    DISABLE_SELF_CAST(ParseNodeFinally);
};

// special name like 'this'
class ParseNodeSpecialName : public ParseNodeName
{
public:
    ParseNodeSpecialName(charcount_t ichMin, charcount_t ichLim, IdentPtr pid);

    bool isThis : 1;
    bool isSuper : 1;

    DISABLE_SELF_CAST(ParseNodeSpecialName);
};

// binary operator with super reference
class ParseNodeSuperReference : public ParseNodeBin
{
public:
    ParseNodeSuperReference(OpCode nop, charcount_t ichMin, charcount_t ichLim, ParseNode * pnode1, ParseNode * pnode2);

    ParseNodeSpecialName * pnodeThis;

    DISABLE_SELF_CAST(ParseNodeSuperReference);
};

// call node with super target
class ParseNodeSuperCall : public ParseNodeCall
{
public:
    ParseNodeSuperCall(OpCode nop, charcount_t ichMin, charcount_t ichLim, ParseNode * pnodeTarget, ParseNode * pnodeArgs);

    ParseNodeSpecialName * pnodeThis;
    ParseNodeSpecialName * pnodeNewTarget;

    DISABLE_SELF_CAST(ParseNodeSuperCall);
};

typedef ParseNode ParseNodeNone;
template <OpCode nop> class OpCodeTrait;
#define PTNODE(nop,sn,pc,nk,ok,json) \
    template<> \
    class OpCodeTrait<nop>  \
    { \
    public:  \
        typedef ParseNode##nk ParseNodeType; \
        static const bool AllowDefer = ((ok) & fnopAllowDefer) != 0; \
    };
#include "ptlist.h"