/*
*  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
*  Copyright (C) 2001 Peter Kelly (pmk@post.com)
*  Copyright (C) 2003-2009, 2013, 2016 Apple Inc. All rights reserved.
*  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
*  Copyright (C) 2007 Maks Orlovich
*  Copyright (C) 2007 Eric Seidel <eric@webkit.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.
*
*/

#include "config.h"
#include "Nodes.h"
#include "NodeConstructors.h"

#include "ExecutableInfo.h"
#include "ModuleScopeData.h"
#include <wtf/Assertions.h>

namespace JSC {

DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(ParserArenaRoot);

// ------------------------------ StatementNode --------------------------------

void StatementNode::setLoc(unsigned firstLine, unsigned lastLine, int startOffset, int lineStartOffset)
{
    m_lastLine = lastLine;
    m_position = JSTextPosition(firstLine, startOffset, lineStartOffset);
    ASSERT(m_position.offset >= m_position.lineStartOffset);
}

// ------------------------------ SourceElements --------------------------------

void SourceElements::append(StatementNode* statement)
{
    if (statement->isEmptyStatement())
        return;

    if (!m_head) {
        m_head = statement;
        m_tail = statement;
        return;
    }

    m_tail->setNext(statement);
    m_tail = statement;
}

StatementNode* SourceElements::singleStatement() const
{
    return m_head == m_tail ? m_head : nullptr;
}

StatementNode* SourceElements::lastStatement() const
{
    return m_tail;
}

bool SourceElements::hasCompletionValue() const
{
    for (StatementNode* statement = m_head; statement; statement = statement->next()) {
        if (statement->hasCompletionValue())
            return true;
    }

    return false;
}

bool SourceElements::hasEarlyBreakOrContinue() const
{
    for (StatementNode* statement = m_head; statement; statement = statement->next()) {
        if (statement->isBreak() || statement->isContinue())
            return true;
        if (statement->hasCompletionValue())
            return false;
    }

    return false;
}

// ------------------------------ BlockNode ------------------------------------

StatementNode* BlockNode::lastStatement() const
{
    return m_statements ? m_statements->lastStatement() : nullptr;
}

StatementNode* BlockNode::singleStatement() const
{
    return m_statements ? m_statements->singleStatement() : nullptr;
}

bool BlockNode::hasCompletionValue() const
{
    return m_statements ? m_statements->hasCompletionValue() : false;
}

bool BlockNode::hasEarlyBreakOrContinue() const
{
    return m_statements ? m_statements->hasEarlyBreakOrContinue() : false;
}

// ------------------------------ ScopeNode -----------------------------

ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, bool inStrictContext)
    : StatementNode(endLocation)
    , ParserArenaRoot(parserArena)
    , m_startLineNumber(startLocation.line)
    , m_startStartOffset(startLocation.startOffset)
    , m_startLineStartOffset(startLocation.lineStartOffset)
    , m_features(inStrictContext ? StrictModeFeature : NoFeatures)
    , m_innerArrowFunctionCodeFeatures(NoInnerArrowFunctionFeatures)
    , m_numConstants(0)
    , m_statements(nullptr)
{
}

ScopeNode::ScopeNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, const SourceCode& source, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants)
    : StatementNode(endLocation)
    , ParserArenaRoot(parserArena)
    , VariableEnvironmentNode(lexicalVariables, WTFMove(funcStack))
    , m_startLineNumber(startLocation.line)
    , m_startStartOffset(startLocation.startOffset)
    , m_startLineStartOffset(startLocation.lineStartOffset)
    , m_features(features)
    , m_innerArrowFunctionCodeFeatures(innerArrowFunctionCodeFeatures)
    , m_source(source)
    , m_sloppyModeHoistedFunctions(WTFMove(sloppyModeHoistedFunctions))
    , m_numConstants(numConstants)
    , m_statements(children)
{
    m_varDeclarations.swap(varEnvironment);
}

StatementNode* ScopeNode::singleStatement() const
{
    return m_statements ? m_statements->singleStatement() : nullptr;
}

bool ScopeNode::hasCompletionValue() const
{
    return m_statements ? m_statements->hasCompletionValue() : false;
}

bool ScopeNode::hasEarlyBreakOrContinue() const
{
    return m_statements ? m_statements->hasEarlyBreakOrContinue() : false;
}

// ------------------------------ ProgramNode -----------------------------

ProgramNode::ProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&)
    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    , m_startColumn(startColumn)
    , m_endColumn(endColumn)
{
}

// ------------------------------ ModuleProgramNode -----------------------------

ModuleProgramNode::ModuleProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&& moduleScopeData)
    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    , m_startColumn(startColumn)
    , m_endColumn(endColumn)
    , m_moduleScopeData(*WTFMove(moduleScopeData))
{
}

// ------------------------------ EvalNode -----------------------------

EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters*, const SourceCode& source, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&)
    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    , m_endColumn(endColumn)
{
}

// ------------------------------ FunctionMetadataNode -----------------------------

FunctionMetadataNode::FunctionMetadataNode(
    ParserArena&, const JSTokenLocation& startLocation,
    const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn,
    int functionKeywordStart, int functionNameStart, int parametersStart, bool isInStrictContext,
    ConstructorKind constructorKind, SuperBinding superBinding, unsigned parameterCount, SourceParseMode mode, bool isArrowFunctionBodyExpression)
        : Node(endLocation)
        , m_isInStrictContext(isInStrictContext)
        , m_superBinding(static_cast<unsigned>(superBinding))
        , m_constructorKind(static_cast<unsigned>(constructorKind))
        , m_needsClassFieldInitializer(static_cast<unsigned>(NeedsClassFieldInitializer::No))
        , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
        , m_parseMode(mode)
        , m_startColumn(startColumn)
        , m_endColumn(endColumn)
        , m_functionKeywordStart(functionKeywordStart)
        , m_functionNameStart(functionNameStart)
        , m_parametersStart(parametersStart)
        , m_startStartOffset(startLocation.startOffset)
        , m_parameterCount(parameterCount)
{
    ASSERT(m_superBinding == static_cast<unsigned>(superBinding));
    ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind));
}

FunctionMetadataNode::FunctionMetadataNode(
    const JSTokenLocation& startLocation,
    const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn,
    int functionKeywordStart, int functionNameStart, int parametersStart, bool isInStrictContext,
    ConstructorKind constructorKind, SuperBinding superBinding, unsigned parameterCount, SourceParseMode mode, bool isArrowFunctionBodyExpression)
        : Node(endLocation)
        , m_isInStrictContext(isInStrictContext)
        , m_superBinding(static_cast<unsigned>(superBinding))
        , m_constructorKind(static_cast<unsigned>(constructorKind))
        , m_needsClassFieldInitializer(static_cast<unsigned>(NeedsClassFieldInitializer::No))
        , m_isArrowFunctionBodyExpression(isArrowFunctionBodyExpression)
        , m_parseMode(mode)
        , m_startColumn(startColumn)
        , m_endColumn(endColumn)
        , m_functionKeywordStart(functionKeywordStart)
        , m_functionNameStart(functionNameStart)
        , m_parametersStart(parametersStart)
        , m_startStartOffset(startLocation.startOffset)
        , m_parameterCount(parameterCount)
{
    ASSERT(m_superBinding == static_cast<unsigned>(superBinding));
    ASSERT(m_constructorKind == static_cast<unsigned>(constructorKind));
}

void FunctionMetadataNode::finishParsing(const SourceCode& source, const Identifier& ident, FunctionMode functionMode)
{
    m_source = source;
    m_ident = ident;
    m_functionMode = functionMode;
}

void FunctionMetadataNode::setEndPosition(JSTextPosition position)
{
    m_lastLine = position.line;
    m_endColumn = position.offset - position.lineStartOffset;
}

bool FunctionMetadataNode::operator==(const FunctionMetadataNode& other) const
{
    return m_parseMode == other.m_parseMode
        && m_isInStrictContext == other.m_isInStrictContext
        && m_superBinding == other.m_superBinding
        && m_constructorKind == other.m_constructorKind
        && m_isArrowFunctionBodyExpression == other.m_isArrowFunctionBodyExpression
        && m_ident == other.m_ident
        && m_ecmaName == other.m_ecmaName
        && m_functionMode == other.m_functionMode
        && m_startColumn == other.m_startColumn
        && m_endColumn == other.m_endColumn
        && m_functionKeywordStart == other.m_functionKeywordStart
        && m_functionNameStart == other.m_functionNameStart
        && m_parametersStart == other.m_parametersStart
        && m_source == other.m_source
        && m_classSource == other.m_classSource
        && m_startStartOffset == other.m_startStartOffset
        && m_parameterCount == other.m_parameterCount
        && m_lastLine == other.m_lastLine
        && m_position == other.m_position;
}

void FunctionMetadataNode::dump(PrintStream& stream) const
{
    stream.println("m_parseMode ", static_cast<uint32_t>(m_parseMode));
    stream.println("m_isInStrictContext ", m_isInStrictContext);
    stream.println("m_superBinding ", m_superBinding);
    stream.println("m_constructorKind ", m_constructorKind);
    stream.println("m_isArrowFunctionBodyExpression ", m_isArrowFunctionBodyExpression);
    stream.println("m_ident ", m_ident);
    stream.println("m_ecmaName ", m_ecmaName);
    stream.println("m_functionMode ", static_cast<uint32_t>(m_functionMode));
    stream.println("m_startColumn ", m_startColumn);
    stream.println("m_endColumn ", m_endColumn);
    stream.println("m_functionKeywordStart ", m_functionKeywordStart);
    stream.println("m_functionNameStart ", m_functionNameStart);
    stream.println("m_parametersStart ", m_parametersStart);
    stream.println("m_classSource.isNull() ", m_classSource.isNull());
    stream.println("m_startStartOffset ", m_startStartOffset);
    stream.println("m_parameterCount ", m_parameterCount);
    stream.println("m_lastLine ", m_lastLine);
    stream.println("position().line ", position().line);
    stream.println("position().offset ", position().offset);
    stream.println("position().lineStartOffset ", position().lineStartOffset);
}

// ------------------------------ FunctionNode -----------------------------

FunctionNode::FunctionNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack&& funcStack, VariableEnvironment& lexicalVariables, UniquedStringImplPtrSet&& sloppyModeHoistedFunctions, FunctionParameters* parameters, const SourceCode& sourceCode, CodeFeatures features, InnerArrowFunctionCodeFeatures innerArrowFunctionCodeFeatures, int numConstants, RefPtr<ModuleScopeData>&&)
    : ScopeNode(parserArena, startLocation, endLocation, sourceCode, children, varEnvironment, WTFMove(funcStack), lexicalVariables, WTFMove(sloppyModeHoistedFunctions), features, innerArrowFunctionCodeFeatures, numConstants)
    , m_parameters(parameters)
    , m_startColumn(startColumn)
    , m_endColumn(endColumn)
{
}

void FunctionNode::finishParsing(const Identifier& ident, FunctionMode functionMode)
{
    ASSERT(!source().isNull());
    m_ident = ident;
    m_functionMode = functionMode;
}

bool PropertyListNode::hasStaticallyNamedProperty(const Identifier& propName)
{
    PropertyListNode* list = this;
    while (list) {
        if (list->m_node->isStaticClassProperty()) {
            const Identifier* currentNodeName = list->m_node->name();
            if (currentNodeName && *currentNodeName == propName)
                return true;
        }
        list = list->m_next;
    }
    return false;
}

// FIXME: calculate this feature once when parsing the property list.
// https://bugs.webkit.org/show_bug.cgi?id=206174
bool PropertyListNode::shouldCreateLexicalScopeForClass(PropertyListNode* list)
{
    while (list) {
        if (list->m_node->isComputedClassField() || list->m_node->isPrivate())
            return true;
        list = list->m_next;
    }
    return false;
}

// ------------------------------ ClassExprNode -----------------------------

// FIXME: calculate this feature once when parsing the property list.
// https://bugs.webkit.org/show_bug.cgi?id=206174
bool PropertyListNode::hasInstanceFields() const
{
    for (auto list = this; list; list = list->m_next) {
        if (list->m_node->isInstanceClassField())
            return true;
    }
    return false;
}

VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables)
{
    m_lexicalVariables.swap(lexicalVariables);
}

VariableEnvironmentNode::VariableEnvironmentNode(VariableEnvironment& lexicalVariables, FunctionStack&& functionStack)
{
    m_lexicalVariables.swap(lexicalVariables);
    m_functionStack = WTFMove(functionStack);
}

} // namespace JSC
