/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
 *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
 *  Copyright (C) 2007, 2008 Maksim Orlovich (maksim@kde.org)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#ifndef NODES_H_
#define NODES_H_

#include "Parser.h"
#include "internal.h"
#include "CompileState.h"
#include "operations.h"
#include "SymbolTable.h"
#include "opcodes.h"
#include "bytecode/opargs.h"
#include <wtf/ListRefPtr.h>
#include <wtf/Vector.h>

namespace KJS
{
class ProgramNode;
class PropertyNameNode;
class PropertyListNode;
class RegExp;
class SourceElementsNode;
class SourceStream;
class PackageObject;
class FuncDeclNode;
class FunctionBodyNode;
class Node;

class VarDeclVisitor;
class FuncDeclVisitor;

class CompileState;
struct CompileReference;

class NodeVisitor
{
public:
    virtual ~NodeVisitor() {}
    /**
     This method should be overridden by subclasses to process nodes, and
     perhaps return pointers for replacement nodes. If the node should not be
     changed, return 0. Otherwise, return the replacement node.

     The default implementation asks the node to visit its kids, and do
     replacements on them if needed, but does not change anything for this node
    */
    virtual Node *visit(Node *node);
};

class Node
{
public:
    enum NodeType {
        UnknownNodeType,
        NullNodeType,
        BooleanNodeType,
        NumberNodeType,
        StringNodeType,
        RegExpNodeType,
        TryNodeType,
        GroupNodeType,
        LabelNodeType
    };

    Node();
    virtual ~Node();

    virtual NodeType type() const
    {
        return UnknownNodeType;
    }

    UString toString() const;

    // This updates line numbers to the pretty-printed version, and
    // returns it out.
    UString reindent(int baseLine = 0) const;

    virtual void streamTo(SourceStream &) const = 0;
    int lineNo() const
    {
        return m_line;
    }

    void ref();
    void deref();
    unsigned refcount();
    static void clearNewNodes();

    virtual Node *nodeInsideAllParens();

    virtual bool isLocation() const
    {
        return false;
    }
    virtual bool isVarAccessNode() const
    {
        return false;
    }
    bool isNumber() const
    {
        return type() == NumberNodeType;
    }
    bool isString() const
    {
        return type() == StringNodeType;
    }
    bool isGroupNode() const
    {
        return type() == GroupNodeType;
    }
    bool isTryNode() const
    {
        return type() == TryNodeType;
    }
    bool isLabelNode() const
    {
        return type() == LabelNodeType;
    }
    virtual bool scanForDeclarations() const
    {
        return true;
    }
    virtual bool isIterationStatement()      const
    {
        return false;
    }

    virtual void breakCycle() { }

    // Processes all function and variable declarations below this node,
    // adding them to symbol table or the current object depending on the
    // execution context..
    void processDecls(ExecState *);

    /*
      Implementations of this method should call visitor->visit on all the
      children nodes, and if they return value is non-0, update the link to the child.
      The recurseVisitLink helper takes care of this
    */
    virtual void recurseVisit(NodeVisitor * /*visitor*/) {}

    template<typename T>
    static void recurseVisitLink(NodeVisitor *visitor, RefPtr<T> &link)
    {
        if (!link) {
            return;
        }

        T *newLink = static_cast<T *>(visitor->visit(link.get()));
        if (newLink) {
            link = newLink;
        }
    }

    template<typename T>
    static void recurseVisitLink(NodeVisitor *visitor, ListRefPtr<T> &link)
    {
        if (!link) {
            return;
        }

        T *newLink = static_cast<T *>(visitor->visit(link.get()));
        if (newLink) {
            link = newLink;
        }
    }

    JSValue *throwError(ExecState *, ErrorType, const UString &msg);
    JSValue *throwError(ExecState *, ErrorType, const UString &msg, const Identifier &);
    JSValue *throwUndefinedVariableError(ExecState *, const Identifier &);

    virtual OpValue generateEvalCode(CompileState *comp);
protected:
    mutable int m_line;
private:
    virtual void processVarDecl(ExecState *state);
    virtual void processFuncDecl(ExecState *state);
    friend class VarDeclVisitor;
    friend class FuncDeclVisitor;

    // disallow assignment
    Node &operator=(const Node &);
    Node(const Node &other);
};

class LocationNode : public Node
{
public:
    bool isLocation() const override
    {
        return true;
    }

    // For assignments, we need to conceptually evaluate the LHS to a reference before looking at the RHS
    // generateRefBind corresponds to that action. It never issues an error. The returned
    // reference should be passed to generateRefWrite when needed
    virtual CompileReference *generateRefBind(CompileState *) = 0;

    // When we are doing a read-modify-write style op, or just plain read, we want to do a read
    // right after the binding. This does that, and returns a reference for use of follow up
    // writes.
    virtual CompileReference *generateRefRead(CompileState *, OpValue *out) = 0;

    // Writes to a bound reference.
    virtual void generateRefWrite(CompileState *,
                                  CompileReference *ref, OpValue &valToStore) = 0;

    // The location nodes also handle deletes themselves. Note that this is called
    // w/o generateRefBegin
    virtual OpValue generateRefDelete(CompileState *) = 0;

    // For function calls, we also do a specialized lookup, getting both the value and the
    // scope/this, also making sure it's not an activation.
    virtual void generateRefFunc(CompileState *comp, OpValue *funOut, OpValue *thisOut) = 0;
};

class StatementNode : public Node
{
public:
    StatementNode();
    void setLoc(int line0, int line1) const;
    int firstLine() const
    {
        return lineNo();
    }
    int lastLine() const
    {
        return m_lastLine;
    }
    void hitStatement(ExecState *);

    void generateDebugInfoIfNeeded(CompileState *comp);

    virtual void generateExecCode(CompileState *);
private:
    void generateDebugInfo(CompileState *comp);
    mutable int m_lastLine;
};

inline void StatementNode::generateDebugInfoIfNeeded(CompileState *comp)
{
    if (comp->compileType() == Debug) {
        generateDebugInfo(comp);
    }
}

class NullNode : public Node
{
public:
    NullNode() {}
    NodeType type() const override
    {
        return NullNodeType;
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
};

class BooleanNode : public Node
{
public:
    BooleanNode(bool v) : val(v) {}
    bool value() const
    {
        return val;
    }

    NodeType type() const override
    {
        return BooleanNodeType;
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
private:
    bool val;
};

class NumberNode : public Node
{
public:
    NumberNode(double v) : val(v) {}
    double value() const
    {
        return val;
    }
    void setValue(double v)
    {
        val = v;
    }

    NodeType type() const override
    {
        return NumberNodeType;
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
private:
    double val;
};

class StringNode : public Node
{
public:
    StringNode(const UString *v) : val(*v), interned(nullptr) { }
    ~StringNode() override; // in nodes2bytecode.cpp
    UString value() const
    {
        return val;
    }
    void setValue(const UString &v)
    {
        val = v;
    }

    NodeType type() const override
    {
        return StringNodeType;
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
private:
    UString val;
    StringImp *interned;
};

class RegExpNode : public Node
{
public:
    RegExpNode(const UString &p, const UString &f)
        : pattern(p), flags(f) { }
    NodeType type() const override
    {
        return RegExpNodeType;
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
private:
    UString pattern, flags;
};

class ThisNode : public Node
{
public:
    ThisNode() {}
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
};

class VarAccessNode : public LocationNode
{
public:
    VarAccessNode(const Identifier &s) : ident(s) {}

    bool isVarAccessNode() const override
    {
        return true;
    }
    void streamTo(SourceStream &) const override;
    OpValue generateEvalCode(CompileState *comp) override;

    CompileReference *generateRefBind(CompileState *) override;
    CompileReference *generateRefRead(CompileState *, OpValue *out) override;
    void generateRefWrite(CompileState *,
                                  CompileReference *ref, OpValue &valToStore) override;
    OpValue generateRefDelete(CompileState *) override;
    void generateRefFunc(CompileState *comp, OpValue *funOut, OpValue *thisOut) override;

    // This one never fails..
    OpValue valueForTypeOf(CompileState *comp);

    // Returns the ID this variable should be accessed as, or
    // missingSymbolMarker(), along with the variable's classification
    enum Classification {
        Local,      // local variable accessed by register #
        NonLocal,    // one scope above, unless local injected
        Dynamic,    // need to do a full lookup
        Global      // in the global object, if anywhere.
    };

    size_t classifyVariable(CompileState *, Classification &classify);
protected:
    Identifier ident;
};

class GroupNode : public Node
{
public:
    GroupNode(Node *g) : group(g) { }
    NodeType type() const override
    {
        return GroupNodeType;
    }

    OpValue generateEvalCode(CompileState *comp) override;
    Node *nodeInsideAllParens() override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> group;
};

class ElementNode : public Node
{
public:
    // list pointer is tail of a circular list, cracked in the ArrayNode ctor
    ElementNode(int e, Node *n) : next(this), elision(e), node(n)
    {
        Parser::noteNodeCycle(this);
    }
    ElementNode(ElementNode *l, int e, Node *n)
        : next(l->next), elision(e), node(n)
    {
        l->next = this;
    }

    void streamTo(SourceStream &) const override;
    PassRefPtr<ElementNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class ArrayNode;
    ListRefPtr<ElementNode> next;
    int elision;
    RefPtr<Node> node;
};

class ArrayNode : public Node
{
public:
    ArrayNode(int e) : elision(e), opt(true) { }
    ArrayNode(ElementNode *ele)
        : element(ele->next.release()), elision(0), opt(false)
    {
        Parser::removeNodeCycle(element.get());
    }
    ArrayNode(int eli, ElementNode *ele)
        : element(ele->next.release()), elision(eli), opt(true)
    {
        Parser::removeNodeCycle(element.get());
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    bool scanForDeclarations() const override
    {
        return false;
    }
private:
    RefPtr<ElementNode> element;
    int elision;
    bool opt;
};

class PropertyNameNode : public Node
{
public:
    PropertyNameNode(const Identifier &s) : str(s) { }
    void streamTo(SourceStream &) const override;
private:
    friend class ObjectLiteralNode;
    Identifier str;
};

class PropertyNode : public Node
{
public:
    enum Type { Constant, Getter, Setter };
    PropertyNode(PropertyNameNode *n, Node *a, Type t)
        : name(n), assign(a), type(t) { }
    void streamTo(SourceStream &) const override;
    friend class PropertyListNode;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class ObjectLiteralNode;
    RefPtr<PropertyNameNode> name;
    RefPtr<Node> assign;
    Type type;
};

class PropertyListNode : public Node
{
public:
    // list pointer is tail of a circular list, cracked in the ObjectLiteralNode ctor
    PropertyListNode(PropertyNode *n)
        : node(n), next(this)
    {
        Parser::noteNodeCycle(this);
    }
    PropertyListNode(PropertyNode *n, PropertyListNode *l)
        : node(n), next(l->next)
    {
        l->next = this;
    }
    void streamTo(SourceStream &) const override;
    PassRefPtr<PropertyListNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class ObjectLiteralNode;
    RefPtr<PropertyNode> node;
    ListRefPtr<PropertyListNode> next;
};

class ObjectLiteralNode : public Node
{
public:
    ObjectLiteralNode() { }
    ObjectLiteralNode(PropertyListNode *l) : list(l->next.release())
    {
        Parser::removeNodeCycle(list.get());
    }
    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    bool scanForDeclarations() const override
    {
        return false;
    }
private:
    RefPtr<PropertyListNode> list;
};

class BracketAccessorNode : public LocationNode
{
public:
    BracketAccessorNode(Node *e1, Node *e2) : expr1(e1), expr2(e2) {}
    void streamTo(SourceStream &) const override;

    OpValue generateEvalCode(CompileState *comp) override;

    CompileReference *generateRefBind(CompileState *) override;
    CompileReference *generateRefRead(CompileState *, OpValue *out) override;
    void generateRefWrite(CompileState *,
                                  CompileReference *ref, OpValue &valToStore) override;
    OpValue generateRefDelete(CompileState *) override;
    void generateRefFunc(CompileState *comp, OpValue *funOut, OpValue *thisOut) override;

    Node *base()
    {
        return expr1.get();
    }
    Node *subscript()
    {
        return expr2.get();
    }

    void recurseVisit(NodeVisitor *visitor) override;
protected:
    RefPtr<Node> expr1;
    RefPtr<Node> expr2;
};

class DotAccessorNode : public LocationNode
{
public:
    DotAccessorNode(Node *e, const Identifier &s) : expr(e), ident(s) { }
    void streamTo(SourceStream &) const override;

    OpValue generateEvalCode(CompileState *comp) override;

    CompileReference *generateRefBind(CompileState *) override;
    CompileReference *generateRefRead(CompileState *, OpValue *out) override;
    void generateRefWrite(CompileState *,
                                  CompileReference *ref, OpValue &valToStore) override;
    OpValue generateRefDelete(CompileState *) override;
    void generateRefFunc(CompileState *comp, OpValue *funOut, OpValue *thisOut) override;

    Node *base() const
    {
        return expr.get();
    }
    const Identifier &identifier() const
    {
        return ident;
    }

    void recurseVisit(NodeVisitor *visitor) override;
protected:
    RefPtr<Node> expr;
    Identifier ident;
};

class ArgumentListNode : public Node
{
public:
    // list pointer is tail of a circular list, cracked in the ArgumentsNode ctor
    ArgumentListNode(Node *e) : next(this), expr(e)
    {
        Parser::noteNodeCycle(this);
    }
    ArgumentListNode(ArgumentListNode *l, Node *e)
        : next(l->next), expr(e)
    {
        l->next = this;
    }

    void streamTo(SourceStream &) const override;
    PassRefPtr<ArgumentListNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;

    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class ArgumentsNode;
    ListRefPtr<ArgumentListNode> next;
    RefPtr<Node> expr;
};

class ArgumentsNode : public Node
{
public:
    ArgumentsNode() { }
    ArgumentsNode(ArgumentListNode *l)
        : list(l->next.release())
    {
        Parser::removeNodeCycle(list.get());
    }

    void generateEvalArguments(CompileState *comp);
    void streamTo(SourceStream &) const override;

    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<ArgumentListNode> list;
};

class NewExprNode : public Node
{
public:
    NewExprNode(Node *e) : expr(e) {}
    NewExprNode(Node *e, ArgumentsNode *a) : expr(e), args(a) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
    RefPtr<ArgumentsNode> args;
};

class FunctionCallValueNode : public Node
{
public:
    FunctionCallValueNode(Node *e, ArgumentsNode *a) : expr(e), args(a) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
    RefPtr<ArgumentsNode> args;
};

class FunctionCallReferenceNode : public Node
{
public:
    FunctionCallReferenceNode(Node *e, ArgumentsNode *a) : expr(e), args(a) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
    RefPtr<ArgumentsNode> args;
};

class PostfixNode : public Node
{
public:
    PostfixNode(Node *l, Operator o) : m_loc(l), m_oper(o) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    OpValue generateEvalCode(CompileState *comp) override;
protected:
    RefPtr<Node> m_loc;
    Operator m_oper;
};

class DeleteReferenceNode : public Node
{
public:
    DeleteReferenceNode(LocationNode *l) : loc(l) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    OpValue generateEvalCode(CompileState *comp) override;
private:
    RefPtr<LocationNode> loc;
};

class DeleteValueNode : public Node
{
public:
    DeleteValueNode(Node *e) : m_expr(e) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    OpValue generateEvalCode(CompileState *comp) override;
private:
    RefPtr<Node> m_expr;
};

class VoidNode : public Node
{
public:
    VoidNode(Node *e) : expr(e) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
};

class TypeOfVarNode : public Node
{
public:
    TypeOfVarNode(VarAccessNode *l) : loc(l) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<VarAccessNode> loc;
};

class TypeOfValueNode : public Node
{
public:
    TypeOfValueNode(Node *e) : m_expr(e) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> m_expr;
};

class PrefixNode : public Node
{
public:
    PrefixNode(Node *l, Operator o) : m_loc(l), m_oper(o) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
protected:
    RefPtr<Node> m_loc;
    Operator m_oper;
};

class UnaryPlusNode : public Node
{
public:
    UnaryPlusNode(Node *e) : expr(e) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
};

class NegateNode : public Node
{
public:
    NegateNode(Node *e) : expr(e) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
};

class BitwiseNotNode : public Node
{
public:
    BitwiseNotNode(Node *e) : expr(e) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
};

class LogicalNotNode : public Node
{
public:
    LogicalNotNode(Node *e) : expr(e) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
};

class BinaryOperatorNode : public Node
{
public:
    BinaryOperatorNode(Node *e1, Node *e2, Operator op)
        : expr1(e1), expr2(e2), oper(op) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr1;
    RefPtr<Node> expr2;
    Operator oper;
};

/**
 * expr1 && expr2, expr1 || expr2
 */
class BinaryLogicalNode : public Node
{
public:
    BinaryLogicalNode(Node *e1, Operator o, Node *e2) :
        expr1(e1), expr2(e2), oper(o) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr1;
    RefPtr<Node> expr2;
    Operator oper;
};

/**
 * The ternary operator, "logical ? expr1 : expr2"
 */
class ConditionalNode : public Node
{
public:
    ConditionalNode(Node *l, Node *e1, Node *e2) :
        logical(l), expr1(e1), expr2(e2) {}

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> logical;
    RefPtr<Node> expr1;
    RefPtr<Node> expr2;
};

class AssignNode : public Node
{
public:
    AssignNode(Node *loc, Operator oper, Node *right)
        : m_loc(loc), m_oper(oper), m_right(right) {}

    void streamTo(SourceStream &) const override;
    OpValue generateEvalCode(CompileState *comp) override;
    void recurseVisit(NodeVisitor *visitor) override;
protected:
    RefPtr<Node> m_loc;
    Operator m_oper;
    RefPtr<Node> m_right;
};

class CommaNode : public Node
{
public:
    CommaNode(Node *e1, Node *e2) : expr1(e1), expr2(e2) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    OpValue generateEvalCode(CompileState *comp) override;
private:
    RefPtr<Node> expr1;
    RefPtr<Node> expr2;
};

class AssignExprNode : public Node
{
public:
    AssignExprNode(Node *e) : expr(e) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    OpValue generateEvalCode(CompileState *comp) override;

    Node *getExpr()
    {
        return expr.get();
    }
private:
    RefPtr<Node> expr;
};

class VarDeclNode : public Node
{
public:
    enum Type { Variable, Constant };
    VarDeclNode(const Identifier &id, AssignExprNode *in, Type t);

    void generateCode(CompileState *comp);

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;

    void processVarDecl(ExecState *) override;
private:
    friend class VarStatementNode;
    friend class VarDeclListNode;
    Type varType;
    Identifier ident;
    RefPtr<AssignExprNode> init;
};

class VarDeclListNode : public Node
{
public:
    // list pointer is tail of a circular list, cracked in the ForNode/VarStatementNode ctor
    VarDeclListNode(VarDeclNode *v) : next(this), var(v)
    {
        Parser::noteNodeCycle(this);
    }
    VarDeclListNode(VarDeclListNode *l, VarDeclNode *v)
        : next(l->next), var(v)
    {
        l->next = this;
    }

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    PassRefPtr<VarDeclListNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class ForNode;
    friend class VarStatementNode;
    ListRefPtr<VarDeclListNode> next;
    RefPtr<VarDeclNode> var;
};

class VarStatementNode : public StatementNode
{
public:
    VarStatementNode(VarDeclListNode *l) : next(l->next.release())
    {
        Parser::removeNodeCycle(next.get());
    }

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
private:
    RefPtr<VarDeclListNode> next;
};

class BlockNode : public StatementNode
{
public:
    BlockNode(SourceElementsNode *s);

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
protected:
    RefPtr<SourceElementsNode> source;
};

class EmptyStatementNode : public StatementNode
{
public:
    EmptyStatementNode() { } // debug

    void streamTo(SourceStream &) const override;
    void generateExecCode(CompileState *) override;
};

class ExprStatementNode : public StatementNode
{
public:
    ExprStatementNode(Node *e) : expr(e) { }

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
private:
    RefPtr<Node> expr;
};

class IfNode : public StatementNode
{
public:
    IfNode(Node *e, StatementNode *s1, StatementNode *s2)
        : expr(e), statement1(s1), statement2(s2) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
private:
    RefPtr<Node> expr;
    RefPtr<StatementNode> statement1;
    RefPtr<StatementNode> statement2;
};

class DoWhileNode : public StatementNode
{
public:
    DoWhileNode(StatementNode *s, Node *e) : statement(s), expr(e) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
    bool isIterationStatement() const override
    {
        return true;
    }
private:
    RefPtr<StatementNode> statement;
    RefPtr<Node> expr;
};

class WhileNode : public StatementNode
{
public:
    WhileNode(Node *e, StatementNode *s) : expr(e), statement(s) {}

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
    bool isIterationStatement() const override
    {
        return true;
    }
private:
    RefPtr<Node> expr;
    RefPtr<StatementNode> statement;
};

class ForNode : public StatementNode
{
public:
    ForNode(Node *e1, Node *e2, Node *e3, StatementNode *s) :
        expr1(e1), expr2(e2), expr3(e3), statement(s) {}
    ForNode(VarDeclListNode *e1, Node *e2, Node *e3, StatementNode *s) :
        expr1(e1->next.release()), expr2(e2), expr3(e3), statement(s)
    {
        Parser::removeNodeCycle(expr1.get());
    }

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    bool isIterationStatement() const override
    {
        return true;
    }
private:
    RefPtr<Node> expr1;
    RefPtr<Node> expr2;
    RefPtr<Node> expr3;
    RefPtr<StatementNode> statement;
};

class ForInNode : public StatementNode
{
public:
    ForInNode(Node *l, Node *e, StatementNode *s);
    ForInNode(const Identifier &i, AssignExprNode *in, Node *e, StatementNode *s);

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    bool isIterationStatement() const override
    {
        return true;
    }
private:
    Identifier ident;
    RefPtr<AssignExprNode> init;
    RefPtr<Node> lexpr;
    RefPtr<Node> expr;
    RefPtr<VarDeclNode> varDecl;
    RefPtr<StatementNode> statement;
};

class ContinueNode : public StatementNode
{
public:
    ContinueNode() : target(nullptr) { }
    ContinueNode(const Identifier &i) : ident(i), target(nullptr) { }

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
private:
    Identifier  ident;
    const Node *target;
};

class BreakNode : public StatementNode
{
public:
    BreakNode() : target(nullptr) { }
    BreakNode(const Identifier &i) : ident(i), target(nullptr) { }

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
private:
    Identifier ident;
    const Node *target;
};

class ReturnNode : public StatementNode
{
public:
    ReturnNode(Node *v) : value(v) {}

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> value;
};

class WithNode : public StatementNode
{
public:
    WithNode(Node *e, StatementNode *s) : expr(e), statement(s) {}

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
    RefPtr<StatementNode> statement;
};

class LabelNode : public StatementNode
{
public:
    LabelNode(const Identifier &l, StatementNode *s) : label(l), statement(s) { }

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *) override;
    NodeType type() const override
    {
        return LabelNodeType;
    }
private:
    Identifier label;
    RefPtr<StatementNode> statement;
};

class ThrowNode : public StatementNode
{
public:
    ThrowNode(Node *e) : expr(e) {}

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<Node> expr;
};

class TryNode : public StatementNode
{
public:
    TryNode(StatementNode *b, const Identifier &e, StatementNode *c, StatementNode *f)
        : tryBlock(b), exceptionIdent(e), catchBlock(c), finallyBlock(f) { }
    NodeType type() const override
    {
        return TryNodeType;
    }

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    RefPtr<StatementNode> tryBlock;
    Identifier exceptionIdent;
    RefPtr<StatementNode> catchBlock;
    RefPtr<StatementNode> finallyBlock;
};

class ParameterNode : public Node
{
public:
    // list pointer is tail of a circular list, cracked in the FuncDeclNode/FuncExprNode ctor
    ParameterNode(const Identifier &i) : id(i), next(this)
    {
        Parser::noteNodeCycle(this);
    }
    ParameterNode(ParameterNode *next, const Identifier &i)
        : id(i), next(next->next)
    {
        next->next = this;
    }

    const Identifier &ident() const
    {
        return id;
    }
    ParameterNode *nextParam() const
    {
        return next.get();
    }
    void streamTo(SourceStream &) const override;
    PassRefPtr<ParameterNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;

    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class FuncDeclNode;
    friend class FuncExprNode;
    Identifier id;
    ListRefPtr<ParameterNode> next;
};

// Flags about function bodies we care about for codegen
enum FunctionBodyFlags {
    // note: neither of the two below is set for things created via
    // top-level, eval, or function ctor
    FuncFl_Decl      = 1,
    FuncFl_Expr      = 2,
    FuncFl_HasEvalOp = 4
};

/**
 This AST node corresponds to the function body or top-level code in the AST, but is used to
 keep track of much of the information relevant to the whole function,
 such as parameter names and symbol tables. This is because there are both function
 declarations and expressions, so there is no natural single place to put this stuff
 above the body

 inherited by ProgramNode
*/
class FunctionBodyNode : public BlockNode
{
public:
    struct SymbolInfo {
        SymbolInfo(int _attr, FuncDeclNode *_funcDecl) : funcDecl(_funcDecl), attr(_attr) {}
        SymbolInfo() {}
        FuncDeclNode *funcDecl;
        int           attr;
    };
    FunctionBodyNode(SourceElementsNode *);
    int sourceId()
    {
        return m_sourceId;
    }
    const UString &sourceURL()
    {
        return m_sourceURL;
    }

    bool isCompiled() const
    {
        return m_compType != NotCompiled;
    }
    void compileIfNeeded(CodeType ctype, CompileType compType);
    void compile(CodeType ctype, CompileType compType);
    CompileType compileState() const
    {
        return m_compType;
    }

    void generateExecCode(CompileState *) override;

    // Reserves a register for private use, making sure that id is in the right spot..
    void reserveSlot(size_t id, bool shouldMark);

    // Symbol table functions
    SymbolTable &symbolTable()
    {
        return m_symbolTable;
    }
    size_t lookupSymbolID(const Identifier &id) const
    {
        return m_symbolTable.get(id.ustring().rep());
    }

    int  numLocalsAndRegisters() const
    {
        return m_symbolList.size();
    }
    SymbolInfo *getLocalInfo()
    {
        return m_symbolList.data();
    }

    size_t  numFunctionLocals() const
    {
        return m_functionLocals.size();
    }
    size_t *getFunctionLocalInfo()
    {
        return m_functionLocals.data();
    }

    // Parameter stuff. We only collect the names during the parsing/
    // while FunctionImp is responsible for managing the IDs.
    void addParam(const Identifier &ident);
    size_t numParams() const
    {
        return m_paramList.size();
    }
    const Identifier &paramName(size_t pos) const
    {
        return m_paramList[pos];
    }

    void addVarDecl(const Identifier &ident, int attr, ExecState *exec);
    void addFunDecl(const Identifier &ident, int attr, FuncDeclNode *funcDecl);

    // Adds a new symbol, killing any previous ID.
    void addSymbolOverwriteID(size_t id, const Identifier &ident, int attr);

    // Runs the code, compiling if needed. This should only be used for non-function ExecStates
    Completion execute(ExecState *exec);

    bool tearOffAtEnd() const
    {
        return m_tearOffAtEnd;
    }

    const CodeBlock &code() const
    {
        return m_compiledCode;
    }
    CodeBlock &code()
    {
        return m_compiledCode;
    }

    // Collection of FuncFl_* flags describing information collected about this function
    // during the parsing.
    unsigned flags() const
    {
        return m_flags;
    }

private:
    size_t addSymbol(const Identifier &ident, int attr, FuncDeclNode *funcDecl = nullptr);
    UString m_sourceURL;
    int m_sourceId : 31;
    bool m_tearOffAtEnd : 1;
    CompileType m_compType;

    // Flags
    unsigned m_flags;

    // This maps id -> attributes and function decl info
    WTF::Vector<SymbolInfo> m_symbolList;

    // This contains the list of locals which contains function declarations
    WTF::Vector<size_t> m_functionLocals;

    // This maps name -> id
    SymbolTable m_symbolTable;

    // The list of parameter names
    WTF::Vector<Identifier> m_paramList;

    CodeBlock m_compiledCode;
};

inline void FunctionBodyNode::compileIfNeeded(CodeType ctype, CompileType compType)
{
    if (m_compType != compType) {
        compile(ctype, compType);
    }
}

class FuncExprNode : public Node
{
public:
    FuncExprNode(const Identifier &i, FunctionBodyNode *b, ParameterNode *p = nullptr)
        : ident(i), param(p ? p->next.release() : PassRefPtr<ParameterNode>(nullptr)), body(b)
    {
        if (p) {
            Parser::removeNodeCycle(param.get());
        } addParams();
    }

    OpValue generateEvalCode(CompileState *comp) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    bool scanForDeclarations() const override
    {
        return false;
    }
private:
    void addParams();
    // Used for streamTo
    friend class PropertyNode;
    Identifier ident;
    RefPtr<ParameterNode> param;
    RefPtr<FunctionBodyNode> body;
};

class FuncDeclNode : public StatementNode
{
public:
    FuncDeclNode(const Identifier &i, FunctionBodyNode *b)
        : ident(i), body(b)
    {
        addParams();
    }
    FuncDeclNode(const Identifier &i, ParameterNode *p, FunctionBodyNode *b)
        : ident(i), param(p->next.release()), body(b)
    {
        Parser::removeNodeCycle(param.get());
        addParams();
    }

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    bool scanForDeclarations() const override
    {
        return false;
    }

    void processFuncDecl(ExecState *) override;
    FunctionImp *makeFunctionObject(ExecState *);
private:
    void addParams();
    Identifier ident;
    RefPtr<ParameterNode> param;
    RefPtr<FunctionBodyNode> body;
};

// A linked list of source element nodes
class SourceElementsNode : public StatementNode
{
public:
    // list pointer is tail of a circular list, cracked in the BlockNode (or subclass) ctor
    SourceElementsNode(StatementNode *);
    SourceElementsNode(SourceElementsNode *s1, StatementNode *s2);

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    PassRefPtr<SourceElementsNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class BlockNode;
    friend class CaseClauseNode;
    RefPtr<StatementNode> node;
    ListRefPtr<SourceElementsNode> next;
};

class CaseClauseNode : public Node
{
public:
    CaseClauseNode(Node *e) : expr(e) { }
    CaseClauseNode(Node *e, SourceElementsNode *s)
        : expr(e), source(s->next.release())
    {
        Parser::removeNodeCycle(source.get());
    }

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class SwitchNode;
    RefPtr<Node> expr;
    RefPtr<SourceElementsNode> source;
};

class ClauseListNode : public Node
{
public:
    // list pointer is tail of a circular list, cracked in the CaseBlockNode ctor
    ClauseListNode(CaseClauseNode *c) : clause(c), next(this)
    {
        Parser::noteNodeCycle(this);
    }
    ClauseListNode(ClauseListNode *n, CaseClauseNode *c)
        : clause(c), next(n->next)
    {
        n->next = this;
    }

    CaseClauseNode *getClause() const
    {
        return clause.get();
    }
    ClauseListNode *getNext() const
    {
        return next.get();
    }
    void streamTo(SourceStream &) const override;
    PassRefPtr<ClauseListNode> releaseNext()
    {
        return next.release();
    }
    void breakCycle() override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class SwitchNode;
    friend class CaseBlockNode;
    RefPtr<CaseClauseNode> clause;
    ListRefPtr<ClauseListNode> next;
};

class CaseBlockNode : public Node
{
public:
    CaseBlockNode(ClauseListNode *l1, CaseClauseNode *d, ClauseListNode *l2);

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    friend class SwitchNode;
    RefPtr<ClauseListNode> list1;
    RefPtr<CaseClauseNode> def;
    RefPtr<ClauseListNode> list2;
};

class SwitchNode : public StatementNode
{
public:
    SwitchNode(Node *e, CaseBlockNode *b) : expr(e), block(b) { }

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
    void generateExecCode(CompileState *comp) override;
private:
    RefPtr<Node> expr;
    RefPtr<CaseBlockNode> block;
};

// important: these are also built when compiling things via the Function constructor
// (see FunctionObjectImp::construct() and Parser::parseFunctionBody, so the existence
// of this class rather than the bare FunctionBodyNode does not care much information.
class ProgramNode : public FunctionBodyNode
{
public:
    ProgramNode(SourceElementsNode *s);
    void streamTo(SourceStream &) const override;
};

class PackageNameNode : public Node
{
public:
    PackageNameNode(const Identifier &i) : names(nullptr), id(i) { }
    PackageNameNode(PackageNameNode *n,
                    const Identifier &i) : names(n), id(i) { }

    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;

    Completion loadSymbol(ExecState *exec, bool wildcard);
    PackageObject *resolvePackage(ExecState *exec);

private:
    PackageObject *resolvePackage(ExecState *exec,
                                  JSObject *baseObject, Package *basePackage);
    RefPtr<PackageNameNode> names;
    Identifier id;
};

class ImportStatement : public StatementNode
{
public:
    ImportStatement(PackageNameNode *n) : name(n), wld(false) {}
    void enableWildcard()
    {
        wld = true;
    }
    void setAlias(const Identifier &a)
    {
        al = a;
    }

    void generateExecCode(CompileState *) override;
    void streamTo(SourceStream &) const override;
    void recurseVisit(NodeVisitor *visitor) override;
private:
    void processVarDecl(ExecState *state) override;
    RefPtr<PackageNameNode> name;
    Identifier al;
    bool wld;
};

} // namespace

#endif
