/*
 * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved.
 * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
 * Copyright (C) 2012 Igalia, S.L.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BytecodeGenerator_h
#define BytecodeGenerator_h

#include "CodeBlock.h"
#include <wtf/HashTraits.h>
#include "Instruction.h"
#include "Label.h"
#include "LabelScope.h"
#include "Interpreter.h"
#include "ParserError.h"
#include "RegisterID.h"
#include "SetForScope.h"
#include "SymbolTable.h"
#include "Debugger.h"
#include "Nodes.h"
#include "Operands.h"
#include "StaticPropertyAnalyzer.h"
#include "TemplateRegistryKey.h"
#include <wtf/CheckedArithmetic.h>
#include <wtf/Optional.h>
#include <wtf/PassRefPtr.h>
#include <wtf/SegmentedVector.h>
#include <wtf/Vector.h>

namespace JSC {

class ExecState;
class Identifier;
class IndexedForInContext;
class StructureForInContext;
class JSTemplateRegistryKey;
class Label;
class JSScope;

enum ExpectedFunction {
	NoExpectedFunction,
	ExpectObjectConstructor,
	ExpectArrayConstructor
};

enum class ThisResolutionType { Local, Scoped };

class CallArguments {
public:
	CallArguments(BytecodeGenerator&, ArgumentsNode*, unsigned additionalArguments = 0);

	RegisterID* thisRegister() { return m_argv[0].get(); }
	RegisterID* argumentRegister(unsigned i) { return m_argv[i + 1].get(); }
	unsigned registerOffset() { return -m_argv.last()->index() + CallFrame::offsetFor(argumentCountIncludingThis()); }
	unsigned argumentCountIncludingThis() { return m_argv.size(); }
	RegisterID* profileHookRegister() { return m_profileHookRegister.get(); }
	ArgumentsNode* argumentsNode() { return m_argumentsNode; }

private:
	void newArgument(BytecodeGenerator&);

	RefPtr<RegisterID> m_profileHookRegister;
	ArgumentsNode* m_argumentsNode;
	Vector<RefPtr<RegisterID>, 8> m_argv;
};

// https://tc39.github.io/ecma262/#sec-completion-record-specification-type
//
// For the Break and Continue cases, instead of using the Break and Continue enum values
// below, we use the unique jumpID of the break and continue statement as the encoding
// for the CompletionType value. emitFinallyCompletion() uses this jumpID value later
// to determine the appropriate jump target to jump to after executing the relevant finally
// blocks. The jumpID is computed as:
//     jumpID = bytecodeOffset (of the break/continue node) + CompletionType::NumberOfTypes.
// Hence, there won't be any collision between jumpIDs and CompletionType enums.
enum class CompletionType : int {
    Normal,
    Break,
    Continue,
    Return,
    Throw,

    NumberOfTypes
};

inline CompletionType bytecodeOffsetToJumpID(unsigned offset)
{
    int jumpIDAsInt = offset + static_cast<int>(CompletionType::NumberOfTypes);
    ASSERT(jumpIDAsInt >= static_cast<int>(CompletionType::NumberOfTypes));
    return static_cast<CompletionType>(jumpIDAsInt);
}

struct FinallyJump {
    FinallyJump(CompletionType jumpID, int targetLexicalScopeIndex, Label* targetLabel)
        : jumpID(jumpID)
        , targetLexicalScopeIndex(targetLexicalScopeIndex)
        , targetLabel(targetLabel)
    { }

    CompletionType jumpID;
    int targetLexicalScopeIndex;
    RefPtr<Label> targetLabel;
};

struct FinallyContext {
    FinallyContext() : m_outerContext(0), m_finallyLabel(0), m_handlesReturns(false) { }
    FinallyContext(FinallyContext* outerContext, Label* finallyLabel)
        : m_outerContext(outerContext)
        , m_finallyLabel(finallyLabel)
    	, m_handlesReturns(false)
    {
        ASSERT(m_jumps.isEmpty());
    }

    FinallyContext* outerContext() const { return m_outerContext; }
    Label* finallyLabel() const { return m_finallyLabel; }

    uint32_t numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues.unsafeGet(); }
    void incNumberOfBreaksOrContinues() { m_numberOfBreaksOrContinues++; }

    bool handlesReturns() const { return m_handlesReturns; }
    void setHandlesReturns() { m_handlesReturns = true; }

    void registerJump(CompletionType jumpID, int lexicalScopeIndex, Label* targetLabel)
    {
        m_jumps.append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel));
    }

    size_t numberOfJumps() const { return m_jumps.size(); }
    FinallyJump& jumps(size_t i) { return m_jumps[i]; }

private:
    FinallyContext* m_outerContext;
    Label* m_finallyLabel;
    Checked<uint32_t, WTF::CrashOnOverflow> m_numberOfBreaksOrContinues;
    bool m_handlesReturns;
    Vector<FinallyJump> m_jumps;
};

struct ControlFlowScope {
    typedef uint8_t Type;
    enum {
        Label,
        Finally
    };
    ControlFlowScope(Type type, int lexicalScopeIndex)
        : type(type)
        , lexicalScopeIndex(lexicalScopeIndex)
    { }
    ControlFlowScope(Type type, int lexicalScopeIndex, const FinallyContext& finallyContext)
        : type(type)
        , lexicalScopeIndex(lexicalScopeIndex)
        , finallyContext(finallyContext)
    { }

    bool isLabelScope() const { return type == Label; }
    bool isFinallyScope() const { return type == Finally; }

    Type type;
    int lexicalScopeIndex;
    FinallyContext finallyContext;
};

class ForInContext : public RefCounted<ForInContext> {
    WTF_MAKE_FAST_ALLOCATED;
    WTF_MAKE_NONCOPYABLE(ForInContext);
public:
    virtual ~ForInContext()
    {
    }

    bool isValid() const { return m_isValid; }
    void invalidate() { m_isValid = false; }

    enum class Type : uint8_t {
        IndexedForIn,
        StructureForIn
    };

    Type type() const { return m_type; }
    bool isIndexedForInContext() const { return m_type == Type::IndexedForIn; }
    bool isStructureForInContext() const { return m_type == Type::StructureForIn; }

    IndexedForInContext& asIndexedForInContext()
    {
        ASSERT(isIndexedForInContext());
        return *reinterpret_cast<IndexedForInContext*>(this);
    }

    StructureForInContext& asStructureForInContext()
    {
        ASSERT(isStructureForInContext());
        return *reinterpret_cast<StructureForInContext*>(this);
    }

    RegisterID* local() const { return m_localRegister.get(); }

protected:
    ForInContext(RegisterID* localRegister, Type type, unsigned bodyBytecodeStartOffset)
		: m_localRegister(localRegister)
		, m_isValid(true)
        , m_type(type)
		, m_bodyBytecodeStartOffset(bodyBytecodeStartOffset)
    { }

    unsigned bodyBytecodeStartOffset() const { return m_bodyBytecodeStartOffset; }

    void finalize(BytecodeGenerator&, CodeBlock*, unsigned bodyBytecodeEndOffset);

private:
    RefPtr<RegisterID> m_localRegister;
    bool m_isValid;
    Type m_type;
    unsigned m_bodyBytecodeStartOffset;
};

class StructureForInContext : public ForInContext {
    typedef ForInContext Base;
public:
    StructureForInContext(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister, unsigned bodyBytecodeStartOffset)
        : ForInContext(localRegister, Type::StructureForIn, bodyBytecodeStartOffset)
        , m_indexRegister(indexRegister)
        , m_propertyRegister(propertyRegister)
        , m_enumeratorRegister(enumeratorRegister)
    {
    }

    RegisterID* index() const { return m_indexRegister.get(); }
    RegisterID* property() const { return m_propertyRegister.get(); }
    RegisterID* enumerator() const { return m_enumeratorRegister.get(); }

    struct GetInstData
	{
    	GetInstData(unsigned instIndex, int propertyRegIndex, ArrayProfile* arrayProfile, UnlinkedValueProfile valueProfile)
    	: instIndex(instIndex)
    	, propertyRegIndex(propertyRegIndex)
    	, arrayProfile(arrayProfile)
    	, valueProfile(valueProfile)
    	{
    	}
    	unsigned instIndex;
    	int propertyRegIndex;
    	ArrayProfile* arrayProfile;
    	UnlinkedValueProfile valueProfile;
	};
    void addGetInst(unsigned instIndex, int propertyRegIndex, ArrayProfile* arrayProfile, UnlinkedValueProfile valueProfile)
    {
        m_getInsts.append(GetInstData(instIndex, propertyRegIndex, arrayProfile, valueProfile));
    }

    void finalize(BytecodeGenerator&, CodeBlock*, unsigned bodyBytecodeEndOffset);

private:
    RefPtr<RegisterID> m_indexRegister;
    RefPtr<RegisterID> m_propertyRegister;
    RefPtr<RegisterID> m_enumeratorRegister;
    Vector<GetInstData> m_getInsts;
};

class IndexedForInContext : public ForInContext {
    typedef ForInContext Base;
public:
    IndexedForInContext(RegisterID* localRegister, RegisterID* indexRegister, unsigned bodyBytecodeStartOffset)
        : ForInContext(localRegister, Type::IndexedForIn, bodyBytecodeStartOffset)
        , m_indexRegister(indexRegister)
    {
    }

    RegisterID* index() const { return m_indexRegister.get(); }

    void finalize(BytecodeGenerator&, CodeBlock*, unsigned bodyBytecodeEndOffset);
    void addGetInst(unsigned instIndex, int propertyIndex) { m_getInsts.append(std::make_pair(instIndex, propertyIndex)); }

private:
    RefPtr<RegisterID> m_indexRegister;
    Vector<std::pair<unsigned, int>> m_getInsts;
};

struct TryData {
	RefPtr<Label> target;
    HandlerType handlerType;
};

struct TryContext {
	RefPtr<Label> start;
	TryData* tryData;
};

enum CaptureMode {
    NotCaptured,
    IsCaptured
};

class Variable {
public:
    enum VariableKind { NormalVariable, SpecialVariable };

    Variable()
        : m_offset()
        , m_local(0)
        , m_attributes(0)
        , m_kind(NormalVariable)
    	, m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable.
    	, m_isLexicallyScoped(false)
    {
    }

    Variable(const Identifier& ident)
        : m_ident(ident)
        , m_local(0)
        , m_attributes(0)
        , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable.
    	, m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable.
    	, m_isLexicallyScoped(false)
	{
	}

    Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex, bool isLexicallyScoped)
        : m_ident(ident)
        , m_offset(offset)
        , m_local(local)
        , m_attributes(attributes)
        , m_kind(kind)
    	, m_symbolTableConstantIndex(symbolTableConstantIndex)
    	, m_isLexicallyScoped(isLexicallyScoped)
    {
    }

    // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be
    // a stack variable, a scoped variable in a local scope, or a variable captured in the
    // direct arguments object.
    bool isResolved() const { return !!m_offset; }
    int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; }

    const Identifier& ident() const { return m_ident; }

    VarOffset offset() const { return m_offset; }
    bool isLocal() const { return m_offset.isStack(); }
    RegisterID* local() const { return m_local; }

    bool isReadOnly() const { return m_attributes & ReadOnly; }
    bool isSpecial() const { return m_kind != NormalVariable; }
    bool isConst() const { return isReadOnly() && m_isLexicallyScoped; }
    void setIsReadOnly() { m_attributes |= ReadOnly; }

private:
    Identifier m_ident;
    VarOffset m_offset;
    RegisterID* m_local;
    unsigned m_attributes;
    VariableKind m_kind;
    int m_symbolTableConstantIndex;
    bool m_isLexicallyScoped;
};

struct TryRange {
    RefPtr<Label> start;
    RefPtr<Label> end;
    TryData* tryData;
};

enum ProfileTypeBytecodeFlag {
    ProfileTypeBytecodePutToScope,
    ProfileTypeBytecodeGetFromScope,
    ProfileTypeBytecodePutToLocalScope,
    ProfileTypeBytecodeGetFromLocalScope,
    ProfileTypeBytecodeHasGlobalID,
    ProfileTypeBytecodeDoesNotHaveGlobalID,
    ProfileTypeBytecodeFunctionArgument,
    ProfileTypeBytecodeFunctionReturnStatement
};

class BytecodeGenerator {
	WTF_MAKE_FAST_ALLOCATED;
public:
	typedef DeclarationStacks::FunctionStack FunctionStack;

	JS_EXPORT_PRIVATE static void setDumpsGeneratedCode(bool dumpsGeneratedCode);
	static bool dumpsGeneratedCode();

	BytecodeGenerator(ProgramNode*, JSGlobalObject*, ProgramCodeBlock*, const VariableEnvironment*);
	BytecodeGenerator(FunctionNode*, JSGlobalObject*, CodeBlock*, const VariableEnvironment*);
	BytecodeGenerator(EvalNode*, JSGlobalObject*, EvalCodeBlock*, const VariableEnvironment*);
	BytecodeGenerator(ModuleProgramNode*, JSGlobalObject*, ModuleProgramCodeBlock*, const VariableEnvironment*);

	~BytecodeGenerator();

	JSGlobalData* vm() const { return m_vm; }
	ParserArena& parserArena() const { return m_scopeNode->parserArena(); }
	const CommonIdentifiers& propertyNames() const { return *m_vm->propertyNames; }

	bool isConstructor() const { return m_codeBlock->isConstructor(); }
	DerivedContextType derivedContextType() const { return m_derivedContextType; }
	bool usesArrowFunction() const { return m_scopeNode->usesArrowFunction(); }
	bool needsToUpdateArrowFunctionContext() const { return m_needsToUpdateArrowFunctionContext; }
	bool usesEval() const { return m_scopeNode->usesEval(); }
	bool usesThis() const { return m_scopeNode->usesThis(); }
#if ENABLE(ES6_CLASS_SYNTAX)
	ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); }
#else
	ConstructorKind constructorKind() const { return ConstructorKind::None; }
#endif
    SuperBinding superBinding() const { return m_codeBlock->superBinding(); }

	ParserError generate(ExecState*, JSScope*);

	bool isArgumentNumber(const Identifier&, int);

    Variable variable(const Identifier&, ThisResolutionType = ThisResolutionType::Local);

    enum ExistingVariableMode { VerifyExisting, IgnoreExisting };
    void createVariable(const Identifier&, VarKind, SymbolTable*, ExistingVariableMode = VerifyExisting); // Creates the variable, or asserts that the already-created variable is sufficiently compatible.

	// Returns the register storing "this"
	RegisterID* thisRegister() { return &m_thisRegister; }
    RegisterID* argumentsRegister() { return m_argumentsRegister; }
    RegisterID* newTarget()
	{
        return m_newTargetRegister;
	}

    RegisterID* scopeRegister() { return m_scopeRegister; }

    RegisterID* generatorRegister() { return m_generatorRegister; }

	// Returns the next available temporary register. Registers returned by
	// newTemporary require a modified form of reference counting: any
	// register with a refcount of 0 is considered "available", meaning that
	// the next instruction may overwrite it.
	RegisterID* newTemporary();

	// The same as newTemporary(), but this function returns "suggestion" if
	// "suggestion" is a temporary. This function is helpful in situations
	// where you've put "suggestion" in a RefPtr, but you'd like to allow
	// the next instruction to overwrite it anyway.
	RegisterID* newTemporaryOr(RegisterID* suggestion) { return suggestion->isTemporary() ? suggestion : newTemporary(); }

	// Functions for handling of dst register

	RegisterID* ignoredResult() { return &m_ignoredResultRegister; }

    // This will be allocated in the temporary region of registers, but it will
    // not be marked as a temporary. This will ensure that finalDestination() does
    // not overwrite a block scope variable that it mistakes as a temporary. These
    // registers can be (and are) reclaimed when the lexical scope they belong to
    // is no longer on the symbol table stack.
    RegisterID* newBlockScopeVariable();

	// Returns a place to write intermediate values of an operation
	// which reuses dst if it is safe to do so.
	RegisterID* tempDestination(RegisterID* dst)
	{
		return (dst && dst != ignoredResult() && dst->isTemporary()) ? dst : newTemporary();
	}

	// Returns the place to write the final output of an operation.
	ALWAYS_INLINE RegisterID* finalDestination(RegisterID* originalDst, RegisterID* tempDst = 0)
	{
		if (originalDst && originalDst != ignoredResult())
			return originalDst;
		ASSERT(tempDst != ignoredResult());
		if (tempDst && tempDst->isTemporary())
			return tempDst;
		return newTemporary();
	}

	RegisterID* destinationForAssignResult(RegisterID* dst)
	{
		if (dst && dst != ignoredResult())
			return dst->isTemporary() ? dst : newTemporary();
		return 0;
	}

	// Moves src to dst if dst is not null and is different from src, otherwise just returns src.
	ALWAYS_INLINE RegisterID* move(RegisterID* dst, RegisterID* src)
	{
		return dst == ignoredResult() ? 0 : (dst && dst != src) ? emitMove(dst, src) : src;
	}

	LabelScopePtr newLabelScope(LabelScope::Type, const Identifier* = 0);
	PassRefPtr<Label> newLabel();
	PassRefPtr<Label> newEmittedLabel();

	ALWAYS_INLINE void emitNode(RegisterID* dst, StatementNode* n)
    {
        SetForScope<bool> tailPositionPoisoner(m_inTailPosition, false);
        return emitNodeInTailPosition(dst, n);
    }

    void emitNodeInTailPosition(RegisterID* dst, StatementNode* n)
    {
        // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary.
        ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount());
        if (LIKELY(m_stack.isSafeToRecurse()))
        	n->emitBytecode(*this, dst);
        else
            emitThrowExpressionTooDeepException();
    }

    ALWAYS_INLINE void emitNode(StatementNode* n)
    {
        emitNode(0, n);
    }

    void emitNodeInTailPosition(StatementNode* n)
    {
        emitNodeInTailPosition(0, n);
    }

    ALWAYS_INLINE RegisterID* emitNode(RegisterID* dst, ExpressionNode* n)
    {
        SetForScope<bool> tailPositionPoisoner(m_inTailPosition, false);
        return emitNodeInTailPosition(dst, n);
    }

    RegisterID* emitNodeInTailPosition(RegisterID* dst, ExpressionNode* n)
    {
        // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary.
        ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount());
        if (LIKELY(m_stack.isSafeToRecurse()))
        	return n->emitBytecode(*this, dst);
        return emitThrowExpressionTooDeepException();
    }

    ALWAYS_INLINE RegisterID* emitNode(ExpressionNode* n)
    {
        return emitNode(0, n);
    }

    RegisterID* emitNodeInTailPosition(ExpressionNode* n)
    {
        return emitNodeInTailPosition(0, n);
    }

    RegisterID* emitNodeForProperty(RegisterID* dst, ExpressionNode* node)
    {
        if (node->isString()) {
            if (Optional<uint32_t> index = parseIndex(static_cast<StringNode*>(node)->value()))
                return emitLoad(dst, jsNumber(index.value()));
        }
        return emitNode(dst, node);
    }

    RegisterID* emitNodeForProperty(ExpressionNode* n)
    {
        return emitNodeForProperty(0, n);
    }

	void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, FallThroughMode fallThroughMode)
	{
        if (!m_stack.isSafeToRecurse()) {
            emitThrowExpressionTooDeepException();
            return;
        }

        n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMode);
    }

    void emitExpressionInfo(const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd)
    {
        ASSERT(divot.offset >= divotStart.offset);
        ASSERT(divotEnd.offset >= divot.offset);

        int sourceOffset = m_scopeNode->source().startOffset();
        unsigned firstLine = m_scopeNode->source().firstLine();

        int divotOffset = divot.offset - sourceOffset;
        int startOffset = divot.offset - divotStart.offset;
        int endOffset = divotEnd.offset - divot.offset;

        unsigned line = divot.line;
        ASSERT(line >= firstLine);
        line -= firstLine;

        int lineStart = divot.lineStartOffset;
        if (lineStart > sourceOffset)
            lineStart -= sourceOffset;
        else
            lineStart = 0;
        ASSERT(divotOffset >= lineStart);
        unsigned column = divotOffset - lineStart;

        unsigned instructionOffset = instructions().size();
        if (!m_isBuiltinFunction)
        	m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column);
    }

	ALWAYS_INLINE bool leftHandSideNeedsCopy(bool rightHasAssignments, bool rightIsPure)
	{
		return (m_codeType != FunctionCode || rightHasAssignments) && !rightIsPure;
	}

	ALWAYS_INLINE PassRefPtr<RegisterID> emitNodeForLeftHandSide(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure)
	{
		if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) {
			PassRefPtr<RegisterID> dst = newTemporary();
			emitNode(dst.get(), n);
			return dst;
		}

		return emitNode(0, n);
	}

    ALWAYS_INLINE RefPtr<RegisterID> emitNodeForLeftHandSideForProperty(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure)
    {
        if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) {
            RefPtr<RegisterID> dst = newTemporary();
            emitNodeForProperty(dst.get(), n);
            return dst;
        }

        return emitNodeForProperty(n);
    }

    void hoistSloppyModeFunctionIfNecessary(const Identifier& functionName);

    RegisterID* emitLoadArrowFunctionLexicalEnvironment(const Identifier&);
    RegisterID* ensureThis();
    void emitLoadThisFromArrowFunctionLexicalEnvironment();
    RegisterID* emitLoadNewTargetFromArrowFunctionLexicalEnvironment();

	RegisterID* emitLoad(RegisterID* dst, bool);
	RegisterID* emitLoad(RegisterID* dst, double);
	RegisterID* emitLoad(RegisterID* dst, const Identifier&);
	RegisterID* emitLoad(RegisterID* dst, JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other);
    RegisterID* emitLoadGlobalObject(RegisterID* dst);

	RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src);
	RegisterID* emitUnaryOpProfiled(OpcodeID, RegisterID* dst, RegisterID* src);
	RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes);
	RegisterID* emitEqualityOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2);
	bool emitEqualityOpImpl(RegisterID* dst, RegisterID* src1, RegisterID* src2);
	RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src);

    RegisterID* emitCreateThis(RegisterID* dst);
    void emitTDZCheck(RegisterID* target);
    bool needsTDZCheck(const Variable&);
	void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope);
	void liftTDZCheckIfPossible(const Variable&);
	RegisterID* emitNewObject(RegisterID* dst);
	RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision
	RegisterID* emitNewArrayWithSize(RegisterID* dst, RegisterID* length);

	RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*);
	RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*);
	RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource);
	RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*);
	RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*);
	RegisterID* emitNewRegExp(RegisterID* dst, RegExp*);

    void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name);

	RegisterID* emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant);
    RegisterID* emitMoveEmptyValue(RegisterID* dst);

	RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOpProfiled(op_to_number, dst, src); }
	RegisterID* emitToString(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_string, dst, src); }
	RegisterID* emitToObject(RegisterID* dst, RegisterID* src, const Identifier& message);
	RegisterID* emitInc(RegisterID* srcDst);
	RegisterID* emitDec(RegisterID* srcDst);

	RegisterID* emitOverridesHasInstance(RegisterID* dst, RegisterID* constructor, RegisterID* hasInstanceValue);
	RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* basePrototype);
	RegisterID* emitInstanceOfCustom(RegisterID* dst, RegisterID* value, RegisterID* constructor, RegisterID* hasInstanceValue);
	RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); }
	RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base);

	RegisterID* emitTryGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
	RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property);
	RegisterID* emitGetById(RegisterID* dst, RegisterID* base, RegisterID* thisVal, const Identifier& property);
	RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value);
	RegisterID* emitPutById(RegisterID* base, RegisterID* thisValue, const Identifier& property, RegisterID* value);
	RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType);
	RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&);
	RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
	RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* thisValue, RegisterID* property);
	RegisterID* emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
	RegisterID* emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value);
	RegisterID* emitPutByVal(RegisterID* base, RegisterID* thisValue, RegisterID* property, RegisterID* value);
	RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value);
	RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
	RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value);

	void emitPutGetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* getter);
	void emitPutSetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* setter);
	void emitPutGetterSetter(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* getter, RegisterID* setter);
	void emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* getter);
	void emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* setter);

    RegisterID* emitGetArgument(RegisterID* dst, int32_t index);

	ExpectedFunction expectedFunctionForIdentifier(const Identifier&);
	RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
	RegisterID* emitCallInTailPosition(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
	RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
	RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
	RegisterID* emitCallVarargsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);

    enum PropertyDescriptorOption {
        PropertyConfigurable = 1,
        PropertyWritable     = 1 << 1,
        PropertyEnumerable   = 1 << 2,
    };
    void emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister,
        RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&);

	class EnumerationFunctor
	{
	public:
		virtual ~EnumerationFunctor() { }
		virtual void operator()(BytecodeGenerator&, RegisterID*) = 0;
	};
	void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, EnumerationFunctor& callBack, VariableEnvironmentNode* = 0, RegisterID* forLoopSymbolTable = 0);

#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX)
	RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*);
#endif

    enum class ReturnFrom { Normal, Finally };
    RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal);
	RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); }

	RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, RegisterID* lazyThis, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
	RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count);
	void emitToPrimitive(RegisterID* dst, RegisterID* src);

    ResolveType resolveType();
    RegisterID* emitResolveConstantLocal(RegisterID* dst, const Variable&);
    RegisterID* emitResolveScope(RegisterID* dst, const Variable&);
    RegisterID* emitGetFromScope(RegisterID* dst, RegisterID* scope, const Variable&, ResolveMode);
    RegisterID* emitPutToScope(RegisterID* scope, const Variable&, RegisterID* value, ResolveMode, InitializationMode);
    RegisterID* initializeVariable(const Variable&, RegisterID* value);

	void emitLabel(Label*);
	void emitLoopHint();
	PassRefPtr<Label> emitJump(Label* target);
	PassRefPtr<Label> emitJumpIfTrue(RegisterID* cond, Label* target);
	PassRefPtr<Label> emitJumpIfFalse(RegisterID* cond, Label* target);
	PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target);
	PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target);

    RegisterID* emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName);
    RegisterID* emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator);
    RegisterID* emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName);
    RegisterID* emitGetPropertyEnumerator(RegisterID* dst, RegisterID* base);
	RegisterID* emitGetEnumerableLength(RegisterID* dst, RegisterID* base);
    RegisterID* emitGetStructurePropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length);
    RegisterID* emitGetGenericPropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length, RegisterID* structureEnumerator);
    RegisterID* emitEnumeratorStructurePropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index);
	RegisterID* emitEnumeratorGenericPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index);
	RegisterID* emitToIndexString(RegisterID* dst, RegisterID* index);

	RegisterID* emitIsJSArray(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_is_jsarray, dst, src); }
    RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
    RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src);
    RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
    RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
    void emitRequireObjectCoercible(RegisterID* value, const String& error);

    RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node);
    RegisterID* emitIteratorNextWithValue(RegisterID* dst, RegisterID* iterator, RegisterID* value, const ThrowableExpressionData* node);
	void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node);

    RegisterID* emitRestParameter(RegisterID* result, unsigned numParametersToSkip);

    bool emitReadOnlyExceptionIfNeeded(const Variable&);

    // Start a try block. 'start' must have been emitted.
    TryData* pushTry(Label* start, Label* handlerLabel, HandlerType);
    // End a try block. 'end' must have been emitted.
    void popTry(TryData*, Label* end);
    void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister);

private:
    static const int CurrentLexicalScopeIndex = -2;
    static const int OutermostLexicalScopeIndex = -1;

    int currentLexicalScopeIndex() const
    {
        int size = static_cast<int>(m_lexicalScopeStack.size());
        ASSERT(static_cast<size_t>(size) == m_lexicalScopeStack.size());
        ASSERT(size >= 0);
        if (!size)
            return OutermostLexicalScopeIndex;
        return size - 1;
    }

public:
    void restoreScopeRegister();
    void restoreScopeRegister(int lexicalScopeIndex);

    int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth);

	void emitThrow(RegisterID* exc)
	{
		m_usesExceptions = true;
		emitUnaryNoDstOp(op_throw, exc);
	}

    void emitThrowStaticError(ErrorType, RegisterID*);
    void emitThrowStaticError(ErrorType, const Identifier& message);
	void emitThrowReferenceError(const String& message);
    void emitThrowTypeError(const String& message);
    void emitThrowTypeError(const Identifier& message);
    void emitThrowRangeError(const Identifier& message);
    void emitThrowOutOfMemoryError();

    void emitPushCatchScope(VariableEnvironment&);
    void emitPopCatchScope(VariableEnvironment&);

    void emitGetScope();
    RegisterID* emitPushWithScope(RegisterID* objectScope);
    void emitPopWithScope();
    void emitPutThisToArrowFunctionContextScope();
	void emitPutNewTargetToArrowFunctionContextScope();
	void emitPutDerivedConstructorToArrowFunctionContextScope();
	RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment();
	void emitPushFunctionNameScope(const Identifier& property, RegisterID* value, bool isCaptured);

	void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart);
	void emitWillLeaveCallFrameDebugHook();

    class CompletionRecordScope {
    public:
        CompletionRecordScope(BytecodeGenerator& generator, bool needCompletionRecordRegisters = true)
            : m_generator(generator)
    		, m_needToReleaseOnDestruction(false)
        {
            if (needCompletionRecordRegisters && m_generator.allocateCompletionRecordRegisters())
                m_needToReleaseOnDestruction = true;
        }
        ~CompletionRecordScope()
        {
            if (m_needToReleaseOnDestruction)
                m_generator.releaseCompletionRecordRegisters();
        }

    private:
        BytecodeGenerator& m_generator;
        bool m_needToReleaseOnDestruction;
    };

    RegisterID* completionTypeRegister() const
    {
        ASSERT(m_completionTypeRegister);
        return m_completionTypeRegister.get();
    }
    RegisterID* completionValueRegister() const
    {
        ASSERT(m_completionValueRegister);
        return m_completionValueRegister.get();
    }

    void emitSetCompletionType(CompletionType type)
    {
        emitLoad(completionTypeRegister(), JSValue(static_cast<int>(type)));
    }
    void emitSetCompletionValue(RegisterID* reg)
    {
        emitMove(completionValueRegister(), reg);
    }

    void emitJumpIf(OpcodeID compareOpcode, RegisterID* completionTypeRegister, CompletionType, Label* jumpTarget);

    bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget);
    bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister);
    void emitFinallyCompletion(FinallyContext&, RegisterID* completionTypeRegister, Label* normalCompletionLabel);

private:
    bool allocateCompletionRecordRegisters();
    void releaseCompletionRecordRegisters();

public:
    FinallyContext* pushFinallyControlFlowScope(Label* finallyLabel);
    FinallyContext popFinallyControlFlowScope();

    void pushIndexedForInScope(RegisterID* local, RegisterID* index);
    void popIndexedForInScope(RegisterID* local);
    void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator);
    void popStructureForInScope(RegisterID* local);

	LabelScopePtr breakTarget(const Identifier&);
	LabelScopePtr continueTarget(const Identifier&);

	void beginSwitch(RegisterID*, SwitchInfo::SwitchType);
	void endSwitch(uint32_t clauseCount, RefPtr<Label>*, ExpressionNode**, Label* defaultLabel, int32_t min, int32_t range);

    void emitYieldPoint(RegisterID*);
    void emitSave(Label* mergePoint, unsigned liveCalleeLocalsIndex);
    void emitResume(Label* mergePoint, unsigned liveCalleeLocalsIndex);

    void emitGeneratorStateLabel();
    void emitGeneratorStateChange(int32_t state);
    RegisterID* emitYield(RegisterID* argument);
    RegisterID* emitDelegateYield(RegisterID* argument, ThrowableExpressionData*);
    void beginGenerator(RegisterID*);
    void endGenerator(Label* defaultLabel);
    RegisterID* generatorStateRegister() { return &m_parameters[2]; }
    RegisterID* generatorValueRegister() { return &m_parameters[3]; }
    RegisterID* generatorResumeModeRegister() { return &m_parameters[4]; }

	CodeType codeType() const { return m_codeType; }

	bool shouldEmitProfileHooks() { return m_shouldEmitProfileHooks; }

    bool shouldBeConcernedWithCompletionValue() const { return m_codeType != FunctionCode; }

    bool shouldEmitDebugHooks() const { return m_shouldEmitDebugHooks; }

	bool isStrictMode() const { return m_codeBlock->isStrictMode(); }

    SourceParseMode parseMode() const { return m_codeBlock->parseMode(); }

    bool isBuiltinFunction() const { return m_isBuiltinFunction; }

    ArrayProfile* newArrayProfile();

    bool isDerivedConstructorContext() const { return m_derivedContextType == DerivedContextType::DerivedConstructorContext; }
    bool isDerivedClassContext() const { return m_derivedContextType == DerivedContextType::DerivedMethodContext; }
    bool isArrowFunction() const { return m_codeBlock->isArrowFunction(); }

    enum class TDZCheckOptimization { Optimize, DoNotOptimize };
    enum class NestedScopeType { IsNested, IsNotNested };
private:
    enum class TDZRequirement { UnderTDZ, NotUnderTDZ };
    enum class ScopeType { CatchScope, LetConstScope, FunctionNameScope };
	enum class ScopeRegisterType { Var, Block };
	void pushLexicalScopeInternal(VariableEnvironment&, TDZCheckOptimization, NestedScopeType, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType, ScopeRegisterType);
	void initializeBlockScopedFunctions(VariableEnvironment&, FunctionStack&, RegisterID* constantSymbolTable);
	template<typename LookUpVarKindFunctor>
	bool instantiateLexicalVariables(const VariableEnvironment&, SymbolTable*, ScopeRegisterType, LookUpVarKindFunctor);
	void emitPrefillStackTDZVariables(const VariableEnvironment&, SymbolTable*);
	void popLexicalScopeInternal(VariableEnvironment&);
    void emitPopScope(RegisterID* dst, RegisterID* scope);
    RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope);
    void emitNewFunctionExpressionCommon(RegisterID*, FunctionMetadataNode*);

    bool isNewTargetUsedInInnerArrowFunction();
    bool isSuperUsedInInnerArrowFunction();
    bool isArgumentsUsedInInnerArrowFunction();

    void emitToThis();

public:
    bool isSuperCallUsedInInnerArrowFunction();
    bool isThisUsedInInnerArrowFunction();
    void pushLexicalScope(VariableEnvironmentNode*, TDZCheckOptimization, NestedScopeType = NestedScopeType::IsNotNested, RegisterID** constantSymbolTableResult = 0, bool shouldInitializeBlockScopedFunctions = true);
    void popLexicalScope(VariableEnvironmentNode*);
    void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
    int labelScopeDepth() const;

private:
	friend class Label;

    void reclaimFreeRegisters();
    Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&, int symbolTableConstantIndex, bool isLexicallyScoped);

	void emitOpcode(OpcodeID);
    UnlinkedArrayAllocationProfile newArrayAllocationProfile();
    UnlinkedObjectAllocationProfile newObjectAllocationProfile();
    UnlinkedValueProfile emitProfiledOpcode(OpcodeID);
	int kill(RegisterID* dst)
	{
		int index = dst->index();
		m_staticPropertyAnalyzer.kill(index);
		return index;
	}

	void retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index);
	void retrieveLastUnaryOp(int& dstIndex, int& srcIndex);
	ALWAYS_INLINE void rewindBinaryOp();
	ALWAYS_INLINE void rewindUnaryOp();

    void allocateAndEmitScope();

	typedef HashMap<double, JSValue> NumberMap;
	typedef HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap;
	typedef HashMap<RefPtr<TemplateRegistryKey>, JSTemplateRegistryKey*> TemplateRegistryKeyMap;

	// Helper for emitCall() and emitConstruct(). This works because the set of
	// expected functions have identical behavior for both call and construct
	// (i.e. "Object()" is identical to "new Object()").
	ExpectedFunction emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, Label* done);

	RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);

	RegisterID* newRegister();

    // Adds an anonymous local var slot. To give this slot a name, add it to symbolTable().
	RegisterID* addVar()
	{
		++m_codeBlock->m_numVars;
        RegisterID* result = newRegister();
        ASSERT(VirtualRegister_(result->index()).toLocal() == m_codeBlock->m_numVars - 1);
        result->ref(); // We should never free this slot.
        return result;
	}

	void addParameter(const Identifier&, int parameterIndex);

    // Initializes the stack form the parameter; does nothing for the symbol table.
    RegisterID* initializeNextParameter();
    UniquedStringImpl* visibleNameForParameter(DestructuringPatternNode*);

    RegisterID& registerFor(VirtualRegister_ reg)
    {
        if (reg.isLocal())
            return m_calleeLocals[reg.toLocal()];

        if (reg.offset() == JSStack::Callee)
            return m_calleeRegister;

        ASSERT(m_parameters.size());
        return m_parameters[reg.toArgument()];
    }

	RegisterID& registerFor(int index)
	{
		return registerFor(VirtualRegister_(index));
	}

    bool hasConstant(const Identifier&) const;
	unsigned addConstant(const Identifier&);
	RegisterID* addConstantValue(JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other);
	RegisterID* addConstantEmptyValue();

	unsigned addConstantBuffer(unsigned length);

    FunctionExecutable* makeFunction(FunctionMetadataNode* metadata) const
    {
        DerivedContextType newDerivedContextType = DerivedContextType::None;

        if (metadata->parseMode() == SourceParseMode::ArrowFunctionMode) {
            if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())
                newDerivedContextType = DerivedContextType::DerivedConstructorContext;
            else if (m_codeBlock->isClassContext() || isDerivedClassContext())
                newDerivedContextType = DerivedContextType::DerivedMethodContext;
        }

        VariableEnvironment variablesUnderTDZ;
        getVariablesUnderTDZ(variablesUnderTDZ);

        // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized.
        // https://bugs.webkit.org/show_bug.cgi?id=151547
        SourceParseMode parseMode = metadata->parseMode();
        ConstructAbility constructAbility = ConstructAbility::CanConstruct;
        if (parseMode == SourceParseMode::GetterMode || parseMode == SourceParseMode::SetterMode || parseMode == SourceParseMode::ArrowFunctionMode || parseMode == SourceParseMode::GeneratorWrapperFunctionMode)
			constructAbility = ConstructAbility::CannotConstruct;
		else if (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() == ConstructorKind::None)
			constructAbility = ConstructAbility::CannotConstruct;

        return FunctionExecutable::create(*m_vm, metadata, metadata->startColumn(), m_isBuiltinFunction, constructAbility, variablesUnderTDZ, newDerivedContextType);
    }

    void getVariablesUnderTDZ(VariableEnvironment&) const;

	RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
	RegisterID* emitCallVarargs(OpcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);

    void initializeParameters(FunctionParameters&);
    void initializeVarLexicalEnvironment(int symbolTableConstantIndex, SymbolTable* functionSymbolTable, bool hasCapturedVariables);
    template<typename CapturesFunctor>
    void initializeDefaultParameterValuesAndSetupFunctionScopeStack(FunctionParameters&, bool isSimpleParameterList, FunctionNode*, SymbolTable*, int symbolTableConstantIndex, CapturesFunctor& captures, bool shouldCreateArgumentsVariableInParameterScope);
    void initializeArrowFunctionContextScopeIfNeeded(SymbolTable* functionSymbolTable = 0, bool canReuseLexicalEnvironment = false);

    RegisterID* emitMove(RegisterID* dst, RegisterID* src);

    bool canDoPeepholeOptimization() const { return m_lastOpcodeID != op_end; }

public:
    JSString* addStringConstant(const Identifier&);
    JSTemplateRegistryKey* addTemplateRegistryKeyConstant(RefPtr<TemplateRegistryKey>);

	Vector<Instruction>& instructions() { return m_instructions; }

	RegisterID* emitThrowExpressionTooDeepException();

	Vector<Instruction> m_instructions;

	bool m_shouldEmitDebugHooks;
	bool m_shouldEmitProfileHooks;
	bool m_shouldEmitRichSourceInfo;

    struct LexicalScopeStackEntry {
        SymbolTable* m_symbolTable;
        RegisterID* m_scope;
        bool m_isWithScope;
        int m_symbolTableConstantIndex;
    };
    Vector<LexicalScopeStackEntry> m_lexicalScopeStack;
    enum class TDZNecessityLevel {
        NotNeeded,
        Optimize,
        DoNotOptimize
    };
    typedef HashMap<RefPtr<UniquedStringImpl>, TDZNecessityLevel, IdentifierRepHash> TDZMap;
    Vector<TDZMap> m_TDZStack;
    Optional<size_t> m_varScopeLexicalScopeStackIndex;
    void pushTDZVariables(const VariableEnvironment&, TDZCheckOptimization, TDZRequirement);

	ScopeNode* m_scopeNode;
	FunctionNode* m_functionNode;
	JSGlobalObject* m_globalObject;
	CodeBlock* m_codeBlock;

	// Some of these objects keep pointers to one another. They are arranged
	// to ensure a sane destruction order that avoids references to freed memory.
	HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> m_functions;
	RegisterID m_ignoredResultRegister;
	RegisterID m_thisRegister;
	RegisterID m_calleeRegister;
    RegisterID* m_scopeRegister;
    RegisterID* m_topMostScope;
    RegisterID* m_argumentsRegister;
	RegisterID* m_lexicalEnvironmentRegister;
	RegisterID* m_generatorRegister;
	RegisterID* m_emptyValueRegister;
    RegisterID* m_globalObjectRegister;
    RegisterID* m_newTargetRegister;
    RegisterID* m_isDerivedConstuctor;
    RegisterID* m_linkTimeConstantRegisters[LinkTimeConstantCount];
    RegisterID* m_arrowFunctionContextLexicalEnvironmentRegister;

    RefPtr<RegisterID> m_completionTypeRegister;
    RefPtr<RegisterID> m_completionValueRegister;

    FinallyContext* m_currentFinallyContext;

	SegmentedVector<RegisterID, 32> m_constantPoolRegisters;
	SegmentedVector<RegisterID, 64> m_calleeLocals;
	SegmentedVector<RegisterID, 16> m_parameters;
	SegmentedVector<Label, 32> m_labels;
	LabelScopeStore m_labelScopes;
	unsigned m_finallyDepth;
	int m_localScopeDepth;
	CodeType m_codeType;

    int localScopeDepth() const;
    void pushLocalControlFlowScope();
    void popLocalControlFlowScope();

    // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it.
    // https://bugs.webkit.org/show_bug.cgi?id=165980
    SegmentedVector<ControlFlowScope, 16> m_controlFlowScopeStack;
	Vector<SwitchInfo> m_switchContextStack;
    Vector<RefPtr<ForInContext>> m_forInContextStack;
	Vector<TryContext> m_tryContextStack;
    Vector<RefPtr<Label>> m_generatorResumeLabels;
	enum FunctionVariableType : uint8_t { NormalFunctionVariable, GlobalFunctionVariable };
	Vector<std::pair<FunctionMetadataNode*, FunctionVariableType>> m_functionsToInitialize;
	bool m_needToInitializeArguments;
	RestParameterNode* m_restParameter;

	Vector<TryRange> m_tryRanges;
	SegmentedVector<TryData, 8> m_tryData;

	int m_nextConstantOffset;
	unsigned m_globalConstantIndex;

	int m_globalVarStorageOffset;

	typedef HashMap<FunctionMetadataNode*, unsigned> FunctionOffsetMap;
	FunctionOffsetMap m_functionOffsets;

	// Constant pool
	IdentifierMap m_identifierMap;

    typedef HashMap<EncodedJSValueWithRepresentation, unsigned, EncodedJSValueWithRepresentationHash, EncodedJSValueWithRepresentationHashTraits> JSValueMap;
	JSValueMap m_jsValueMap;
	NumberMap m_numberMap;
	IdentifierStringMap m_stringMap;
	TemplateRegistryKeyMap m_templateRegistryKeyMap;

    StaticPropertyAnalyzer m_staticPropertyAnalyzer;

	JSGlobalData* m_vm;

	OpcodeID m_lastOpcodeID;
#ifndef NDEBUG
	size_t m_lastOpcodePosition;
#endif

	StackBounds m_stack;

	bool m_usesExceptions;
	bool m_expressionTooDeep;
    bool m_isBuiltinFunction;
    bool m_usesNonStrictEval;
    bool m_inTailPosition;
    bool m_needsToUpdateArrowFunctionContext;
    DerivedContextType m_derivedContextType;

    unsigned m_arrayAllocationProfileCount;
    unsigned m_objectAllocationProfileCount;
    unsigned m_valueProfileCount;

    Identifier m_functionBodyIdent;
};

ALWAYS_INLINE void BytecodeGenerator::emitOpcode(OpcodeID opcodeID)
{
#ifndef NDEBUG
    size_t opcodePosition = instructions().size();
    ASSERT(opcodePosition - m_lastOpcodePosition == opcodeLength(m_lastOpcodeID) || m_lastOpcodeID == op_end);
    m_lastOpcodePosition = opcodePosition;
#endif
    instructions().append(vm()->interpreter->getOpcode(opcodeID));
    m_lastOpcodeID = opcodeID;
}

inline RegisterID* BytecodeGenerator::emitUnaryNoDstOp(OpcodeID opcodeID, RegisterID* src)
{
    emitOpcode(opcodeID);
    instructions().append(src->index());
    return src;
}

}

#endif // BytecodeGenerator_h
