//===-- AMDGPUAsmBackend.cpp - AMDGPU Assembler Backend -------------------===//
//                     The LLVM Compiler Infrastructure
/// \file

#include "MCTargetDesc/AMDGPUMCTargetDesc.h"
#include "MCTargetDesc/AMDGPUFixupKinds.h"
#include "llvm/ADT/

StringRef.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

namespace {

class AMDGPUMCObjectWriter : public MCObjectWriter {
public:
  AMDGPUMCObjectWriter(raw_pwrite_stream &OS) : MCObjectWriter(OS, true) {}
  void executePostLayoutBinding(MCAssembler &Asm,
                                const MCAsmLayout &Layout) override {
  }
  void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                        const MCFragment *Fragment, const MCFixup &Fixup,
                        MCValue Target, bool &IsPCRel,
                        uint64_t &FixedValue) override {
    assert(!"Not implemented");
  }

  void writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;

};

class AMDGPUAsmBackend : public MCAsmBackend {
public:
  AMDGPUAsmBackend(const Target &T)
    : MCAsmBackend() {}

  unsigned getNumFixupKinds() const override { return AMDGPU::NumTargetFixupKinds; };
  void applyFixup(const MCFixup &Fixup, char *Data, unsigned DataSize,
                  uint64_t Value, bool IsPCRel) const override;
  bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                            const MCRelaxableFragment *DF,
                            const MCAsmLayout &Layout) const override {
    return false;
  }
  void relaxInstruction(const MCInst &Inst, MCInst &Res) const override {
    assert(!"Not implemented");
  }
  bool mayNeedRelaxation(const MCInst &Inst) const override { return false; }
  bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override;

  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
};

} //End anonymous namespace

void AMDGPUMCObjectWriter::writeObject(MCAssembler &Asm,
                                       const MCAsmLayout &Layout) {
  for (MCAssembler::iterator I = Asm.begin(), E = Asm.end(); I != E; ++I) {
    Asm.writeSectionData(&*I, Layout);
  }
}

static unsigned getFixupKindNumBytes(unsigned Kind) {
  switch (Kind) {
  case FK_Data_1:
    return 1;
  case FK_Data_2:
    return 2;
  case FK_Data_4:
    return 4;
  case FK_Data_8:
    return 8;
  default:
    llvm_unreachable("Unknown fixup kind!");
  }
}

void AMDGPUAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
                                  unsigned DataSize, uint64_t Value,
                                  bool IsPCRel) const {

  switch ((unsigned)Fixup.getKind()) {
    case AMDGPU::fixup_si_sopp_br: {
      uint16_t *Dst = (uint16_t*)(Data + Fixup.getOffset());
      *Dst = (Value - 4) / 4;
      break;
    }

    case AMDGPU::fixup_si_rodata: {
      uint32_t *Dst = (uint32_t*)(Data + Fixup.getOffset());
      // We emit constant data at the end of the text section and generate its
      // address using the following code sequence:
      // s_add_u32 s0, s0, $symbol
      // s_getpc_b64 returns the address of the s_add_u32 instruction and then
      // the fixup replaces $symbol with a literal constant, which is a
      // pc-relative  offset from the encoding of the $symbol operand to the
      // What we want here is an offset from the start of the s_add_u32
      // instruction to the constant data, but since the encoding of $symbol
      // starts 4 bytes after the start of the add instruction, we end up
      // with an offset that is 4 bytes too small.  This requires us to
      // add 4 to the fixup value before applying it.
      *Dst = Value + 4;
      break;
    }
    default: {
      // FIXME: Copied from AArch64
      unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
      if (!Value)
        return; // Doesn't change encoding.
      MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());

      // Shift the value into position.
      Value <<= Info.TargetOffset;

      unsigned Offset = Fixup.getOffset();
      assert(Offset + NumBytes <= DataSize && "Invalid fixup offset!");

      // For each byte of the fragment that the fixup touches, mask in the
      // bits from the fixup value.
      for (unsigned i = 0; i != NumBytes; ++i)
        Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
    }
  }
}

const MCFixupKindInfo &AMDGPUAsmBackend::getFixupKindInfo(
                                                       MCFixupKind Kind) const {
  const static MCFixupKindInfo Infos[AMDGPU::NumTargetFixupKinds] = {
    // name                   offset bits  flags
    { "fixup_si_sopp_br",     0,     16,   MCFixupKindInfo::FKF_IsPCRel },
    { "fixup_si_rodata",      0,     32,   MCFixupKindInfo::FKF_IsPCRel }
  };

  if (Kind < FirstTargetFixupKind)
    return MCAsmBackend::getFixupKindInfo(Kind);

  return Infos[Kind - FirstTargetFixupKind];
}

bool AMDGPUAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
  OW->WriteZeros(Count);

  return true;
}

namespace {

class ELFAMDGPUAsmBackend : public AMDGPUAsmBackend {
  bool Is64Bit;

public:
  ELFAMDGPUAsmBackend(const Target &T, bool Is64Bit) :
      AMDGPUAsmBackend(T), Is64Bit(Is64Bit) { }

  MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
    return createAMDGPUELFObjectWriter(Is64Bit, OS);
  }
};

} // end anonymous namespace

MCAsmBackend *llvm::createAMDGPUAsmBackend(const Target &T,
                                           const MCRegisterInfo &MRI,
                                           const Triple &TT, StringRef CPU) {
  Triple TargetTriple(TT);

  // Use 64-bit ELF for amdgcn
  return new ELFAMDGPUAsmBackend(T, TargetTriple.getArch() == Triple::amdgcn);
}


int QTabWidget_setCornerWidget(lua_State* const state)

#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)
{
    return 0;
}
int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}

{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)
{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;
    mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;
    mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::tabText;
    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"] = &QTabWidget::widget;
}

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)

int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_setCornerWidget(lua_State* const state)
{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)
{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;
    mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;
    mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::tabText;
    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"] = &QTabWidget::widget;
}

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;

#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)
{
    return 0;
}
int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_setCornerWidget(lua_State* const state)
{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)

    mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;
    mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::tabText;
    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"] = &QTabWidget::widget;
}

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;

#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)
{
    return 0;
}
int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_setCornerWidget(lua_State* const state)
{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)
{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;

    mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::tabText;
    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"] = &QTabWidget::widget;
}

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::tabText;
    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"] = &QTabWidget::widget;
}

#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)
{
    return 0;
}
int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_setCornerWidget(lua_State* const state)
{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)
{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;
    mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)
{
    return 0;
}
int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_setCornerWidget(lua_State* const state)
{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)
{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;
    mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;
    mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::

    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"] = &QTabWidget::widget;
}

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


#include "QTabWidget.hpp"
#include "../convert/callable.hpp"
#include "../thread.hpp"

#include "QWidget.hpp"
#include "../Qt5Core/Qt.hpp"
#include "../Qt5Core/QSize.hpp"
#include "../Qt5Core/QString.hpp"
#include "../Qt5Gui/QIcon.hpp"
#include "QTabBar.hpp"

int QTabWidget_addTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_cornerWidget(lua_State* const state)
{
    return 0;
}
int QTabWidget_insertTab(lua_State* const state)
{
    return 0;
}
int QTabWidget_setCornerWidget(lua_State* const state)
{
    return 0;
}

void lua::QTabWidget_metatable(lua_State* const state, const int pos)
{
    lua::QWidget_metatable(state, pos);

    lua::index mt(state, pos);
    mt["addTab"] = QTabWidget_addTab;
    mt["clear"] = &QTabWidget::clear;
    mt["cornerWidget"] = QTabWidget_cornerWidget;
    mt["count"] = &QTabWidget::count;
    mt["currentIndex"] = &QTabWidget::currentIndex;
    mt["currentWidget"] = &QTabWidget::currentWidget;
    mt["documentMode"] = &QTabWidget::documentMode;
    mt["elideMode"] = &QTabWidget::elideMode;
    mt["iconSize"] = &QTabWidget::iconSize;
    mt["indexOf"] = &QTabWidget::indexOf;
    mt["insertTab"] = QTabWidget_insertTab;
    mt["isMovable"] = &QTabWidget::isMovable;
    mt["isTabEnabled"] = &QTabWidget::isTabEnabled;
    mt["removeTab"] = &QTabWidget::removeTab;
    mt["setCornerWidget"] = QTabWidget_setCornerWidget;
    mt["setDocumentMode"] = &QTabWidget::setDocumentMode;
    mt["setElideMode"] = &QTabWidget::setElideMode;
    mt["setIconSize"] = &QTabWidget::setIconSize;
    mt["setMovable"] = &QTabWidget::setMovable;
    mt["setTabEnabled"] = &QTabWidget::setTabEnabled;
    mt["setTabIcon"] = &QTabWidget::setTabIcon;
    mt["setTabPosition"] = &QTabWidget::setTabPosition;
    mt["setTabShape"] = &QTabWidget::setTabShape;
    mt["setTabText"] = &QTabWidget::setTabText;
    mt["setTabToolTip"] = &QTabWidget::setTabToolTip;
    mt["setTabWhatsThis"] = &QTabWidget::setTabWhatsThis;
    mt["setTabsClosable"] = &QTabWidget::setTabsClosable;
    mt["setUsesScrollButtons"] = &QTabWidget::setUsesScrollButtons;
    mt["tabBar"] = &QTabWidget::tabBar;
    mt["tabIcon"] = &QTabWidget::tabIcon;
    mt["tabPosition"] = &QTabWidget::tabPosition;
    mt["tabShape"] = &QTabWidget::tabShape;
    mt["tabText"] = &QTabWidget::tabText;
    mt["tabToolTip"] = &QTabWidget::tabToolTip;
    mt["tabWhatsThis"] = &QTabWidget::tabWhatsThis;
    mt["tabsClosable"] = &QTabWidget::tabsClosable;
    mt["usesScrollButtons"] = &QTabWidget::usesScrollButtons;
    mt["widget"]

= &QTabWidget::widget;
}

int QTabWidget_new(lua_State* const state)
{
    lua::make<QTabWidget>(state);
    // TODO Set up object-specific methods

    return 1;
}

int luaopen_Qt5Widgets_QTabWidget(lua_State* const state)
{
    lua::thread env(state);

    env["QTabWidget"] = lua::value::table;
    env["QTabWidget"]["new"] = QTabWidget_new;
    auto t = env["QTabWidget"];

    t["North"] = QTabWidget::North;
    t["South"] = QTabWidget::South;
    t["West"] = QTabWidget::West;
    t["East"] = QTabWidget::East;

    t["Rounded"] = QTabWidget::Rounded;
    t["Triangular"] = QTabWidget::Triangular;

    return 0;
}


int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])

#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }
  // Indicates successful parsing of the command line
  return 0;
}

{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );
    if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exception: " << ex << std::endl;
    return 1;
  }

  return 0;
}


{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }
  // Indicates successful parsing of the command line
  return 0;
}

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );
    if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exception: " << ex << std::endl;
    return 1;
  }

  return 0;
}


while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }
  // Indicates successful parsing of the command line

#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

  return 0;
}

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );
    if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exception: " << ex << std::endl;
    return 1;
  }

  return 0;
}


#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }
  // Indicates successful parsing of the command line

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );
    if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exception: " << ex << std::endl;
    return 1;
  }

  return 0;
}


#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int


if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }
  // Indicates successful parsing of the command line
  return 0;
}

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exception: " << ex << std::endl;
    return 1;
  }

  return 0;
}


switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }

#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

  while ((c = get_opts ()) != -1)

  // Indicates successful parsing of the command line
  return 0;
}

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );
    if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exception: " << ex << std::endl;
    return 1;
  }

  return 0;
}


while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }


#include "MessengerC.h"
#include <iostream>
#include "ace/Get_Opt.h"

const ACE_TCHAR *ior = ACE_TEXT("file://Messenger.ior");

int
parse_args (int argc, ACE_TCHAR *argv[])
{
  ACE_Get_Opt get_opts (argc, argv, ACE_TEXT("k:"));
  int c;

  while ((c = get_opts ()) != -1)
    switch (c)
      {
      case 'k':
        ior = get_opts.opt_arg ();
        break;

      case '?':
      default:
        ACE_ERROR_RETURN ((LM_ERROR,
                           "usage:  %s "
                           "-k <ior> "
                           "\n",
                           argv [0]),
                          -1);
      }
  // Indicates successful parsing of the command line
  return 0;
}

int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
  try {
    // Initialize orb
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );

    if (parse_args (argc, argv) != 0)
      return 1;

    // Destringify ior
    CORBA::Object_var obj = orb->string_to_object( ior );
    if (CORBA::is_nil(obj.in())) {
      std::cerr << "Nil Messenger reference" << std::endl;
      return 1;
    }

    // Narrow
    Messenger_var messenger = Messenger::_narrow( obj.in() );
    if (CORBA::is_nil(messenger.in())) {
      std::cerr << "Argument is not a Messenger reference" << std::endl;
      return 1;
    }

    CORBA::String_var message = CORBA::string_dup( "Hello!" );
    messenger->send_message( "TAO User", "TAO Test", message.inout() );

    std::cout << "message was sent" << std::endl;
  }
  catch(const CORBA::Exception& ex) {
    std::cerr << "Client main() Caught CORBA::Exc

eption: " << ex << std::endl;
    return 1;
  }

  return 0;
}


static bool isCompiledSelector(SelectorCompilationStatus compilationStatus)

/*
 *
 */

#include "config.h"
#include "SelectorQuery.h"

#include "CSSParser.h"
#include "ElementDescendantIterator.h"
#include "HTMLNames.h"
#include "SelectorChecker.h"
#include "StaticNodeList.h"
#include "StyledElement.h"

namespace WebCore {

#if !ASSERT_DISABLED
static bool isSingleTagNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag;
}

static bool isSingleClassNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Class;
}
#endif

enum class IdMatchingType : uint8_t {
    None,
    Rightmost,
    Filter
};

static bool canBeUsedForIdFastPath(const CSSSelector& selector)
{
    return selector.match() == CSSSelector::Id
        || (selector.match() == CSSSelector::Exact && selector.attribute() == HTMLNames::idAttr && !selector.attributeValueMatchingIsCaseInsensitive());
}

static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
{
    bool inRightmost = true;
    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            if (inRightmost)
                return IdMatchingType::Rightmost;
            return IdMatchingType::Filter;
        }
        if (selector->relation() != CSSSelector::Subselector)
            inRightmost = false;
    }
    return IdMatchingType::None;
}

SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
{
    unsigned selectorCount = 0;
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        selectorCount++;

    m_selectors.reserveInitialCapacity(selectorCount);
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        m_selectors.uncheckedAppend(SelectorData(selector));

    if (selectorCount == 1) {
        const CSSSelector& selector = *m_selectors.first().selector;
        if (selector.isLastInTagHistory()) {
            switch (selector.match()) {
            case CSSSelector::Tag:
                m_matchType = TagNameMatch;
                break;
            case CSSSelector::Class:
                m_matchType = ClassNameMatch;
                break;
            default:
                if (canBeUsedForIdFastPath(selector))
                    m_matchType = RightMostWithIdMatch;
                else
                    m_matchType = CompilableSingle;
                break;
            }
        } else {
            switch (findIdMatchingType(selector)) {
            case IdMatchingType::None:
                m_matchType = CompilableSingle;
                break;
            case IdMatchingType::Rightmost:
                m_matchType = RightMostWithIdMatch;
                break;
            case IdMatchingType::Filter:
                m_matchType = CompilableSingleWithRootFilter;
                break;
            }
        }
    } else
        m_matchType = CompilableMultipleSelectorMatch;
}

inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity);
}

inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity))
        return nullptr;
    return &element;
}

bool SelectorDataList::matches(Element& targetElement) const
{
    for (auto& selctor : m_selectors) {
        if (selectorMatches(selctor, targetElement, targetElement))
            return true;
    }
    return false;
}

Element* SelectorDataList::closest(Element& targetElement) const
{
    Element* currentNode = &targetElement;
    do {
        for (auto& selector : m_selectors) {
            Element* candidateElement = selectorClosest(selector, *currentNode, targetElement);
            if (candidateElement)
                return candidateElement;
        }
        currentNode = currentNode->parentElement();
    } while (currentNode);
    return nullptr;
}

struct AllElementExtractorSelectorQueryTrait {
    typedef Vector<Ref<Element>> OutputType;
    static const bool shouldOnlyMatchFirstElement = false;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element) { ASSERT(element); output.append(*element); }
};

Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const
{
    Vector<Ref<Element>> result;
    execute<AllElementExtractorSelectorQueryTrait>(rootNode, result);
    return StaticElementList::create(WTFMove(result));
}

struct SingleElementExtractorSelectorQueryTrait {
    typedef Element* OutputType;
    static const bool shouldOnlyMatchFirstElement = true;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element)
    {
        ASSERT(element);
        ASSERT(!output);
        output = element;
    }
};

Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const
{
    Element* result = nullptr;
    execute<SingleElementExtractorSelectorQueryTrait>(rootNode, result);
    return result;
}

static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return nullptr;
    if (rootNode.document().inQuirksMode())
        return nullptr;

    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector))
            return selector;
        if (selector->relation() != CSSSelector::Subselector)
            break;
    }

    return nullptr;
}

static inline bool isTreeScopeRoot(const ContainerNode& node)
{
    return node.isDocumentNode() || node.isShadowRoot();
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(idSelector);

    const AtomString& idToMatch = idSelector->value();
    if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
        const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch);
        ASSERT(elements);
        bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode);
        for (auto& element : *elements) {
            if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
        return;
    }

    Element* element = rootNode.treeScope().getElementById(idToMatch);
    if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)))
        return;
    if (selectorMatches(selectorData, *element, rootNode))
        SelectorQueryTrait::appendOutputForElement(output, element);
}

static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return rootNode;
    if (rootNode.document().inQuirksMode())
        return rootNode;

    // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter.
    // Thus we can skip the rightmost match.
    const CSSSelector* selector = &firstSelector;
    do {
        ASSERT(!canBeUsedForIdFastPath(*selector));
        if (selector->relation() != CSSSelector::Subselector)
            break;
        selector = selector->tagHistory();
    } while (selector);

    bool inAdjacentChain = false;
    for (; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            const AtomString& idToMatch = selector->value();
            if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) {
                if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
                    if (inAdjacentChain)
                        searchRoot = searchRoot->parentNode();
                    if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(rootNode)))
                        return *searchRoot;
                }
            }
        }
        if (selector->relation() == CSSSelector::Subselector)
            continue;
        if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
            inAdjacentChain = true;
        else
            inAdjacentChain = false;
    }
    return rootNode;
}

static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomString& localName, const AtomString& lowercaseLocalName)
{
    if (element.isHTMLElement() && element.document().isHTMLDocument())
        return element.localName() == lowercaseLocalName;
    return element.localName() == localName;

}

template <typename SelectorQueryTrait>
static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomString& localName, const AtomString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output)
{
    if (localName == lowercaseLocalName) {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.tagQName().localName() == localName) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    } else {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (localNameMatches(element, localName, lowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output)
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        SelectorQueryTrait::appendOutputForElement(output, &element);
        if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
            return;
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleTagNameSelector(*selectorData.selector));

    const QualifiedName& tagQualifiedName = selectorData.selector->tagQName();
    const AtomString& selectorLocalName = tagQualifiedName.localName();
    const AtomString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName();
    const AtomString& selectorNamespaceURI = tagQualifiedName.namespaceURI();

    if (selectorNamespaceURI == starAtom()) {
        if (selectorLocalName != starAtom()) {
            // Common case: name defined, selectorNamespaceURI is a wildcard.
            elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, selectorLowercaseLocalName, output);
        } else {
            // Other fairly common case: both are wildcards.
            anyElement<SelectorQueryTrait>(rootNode, output);
        }
    } else {
        // Fallback: NamespaceURI is set, selectorLocalName may be starAtom().
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleClassNameSelector(*selectorData.selector));

    const AtomString& className = selectorData.selector->value();
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        if (element.hasClass() && element.classNames().contains(className)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
        if (selectorMatches(selectorData, element, rootNode)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
            if (selectorMatches(selector, element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

#if ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element, &checkingContext)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
#if CSS_SELECTOR_JIT_PROFILING
            selector.compiledSelectorUsed();
#endif
            bool matched = false;
            void* compiledSelectorChecker = selector.compiledSelectorCodeRef.code().executableAddress();
            if (selector.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
                auto selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element);
            } else {
                ASSERT(selector.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
                auto selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element, &checkingContext);
            }
            if (matched) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

{
    return compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker || compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext;
}

bool SelectorDataList::compileSelector(const SelectorData& selectorData)
{
    if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled)
        return isCompiledSelector(selectorData.compilationStatus);

    selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
    return isCompiledSelector(selectorData.compilationStatus);
}

#endif // ENABLE(CSS_SELECTOR_JIT)

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    ContainerNode* searchRootNode = &rootNode;
    switch (m_matchType) {
    case RightMostWithIdMatch:
        {
        const SelectorData& selectorData = m_selectors.first();
        if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) {
            executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
            break;
        }
#if ENABLE(CSS_SELECTOR_JIT)
        if (compileSelector(selectorData))
            goto CompiledSingleCase;
#endif // ENABLE(CSS_SELECTOR_JIT)
        goto SingleSelectorCase;
        ASSERT_NOT_REACHED();
        }

    case CompilableSingleWithRootFilter:
    case CompilableSingle:
        {
#if ENABLE(CSS_SELECTOR_JIT)
        const SelectorData& selectorData = m_selectors.first();
        ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);
        ASSERT(m_matchType == CompilableSingle || m_matchType == CompilableSingleWithRootFilter);
        if (compileSelector(selectorData)) {
            if (m_matchType == CompilableSingle) {
                m_matchType = CompiledSingle;
                goto CompiledSingleCase;
            }
            ASSERT(m_matchType == CompilableSingleWithRootFilter);
            m_matchType = CompiledSingleWithRootFilter;
            goto CompiledSingleWithRootFilterCase;
        }
#endif // ENABLE(CSS_SELECTOR_JIT)
        if (m_matchType == CompilableSingle) {
            m_matchType = SingleSelector;
            goto SingleSelectorCase;
        }
        ASSERT(m_matchType == CompilableSingleWithRootFilter);
        m_matchType = SingleSelectorWithRootFilter;
        goto SingleSelectorWithRootFilterCase;
        ASSERT_NOT_REACHED();
        }

#if ENABLE(CSS_SELECTOR_JIT)
    case CompiledSingleWithRootFilter:
        CompiledSingleWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case CompiledSingle:
        {
        CompiledSingleCase:
        const SelectorData& selectorData = m_selectors.first();
        void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
        if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
            SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData);
        } else {
            ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
            SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData);
        }
        break;
        }
#else
    case CompiledSingleWithRootFilter:
    case CompiledSingle:
        ASSERT_NOT_REACHED();
#if ASSERT_DISABLED
        FALLTHROUGH;
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)

    case SingleSelectorWithRootFilter:
        SingleSelectorWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case SingleSelector:
        SingleSelectorCase:
        executeSingleSelectorData<SelectorQueryTrait>(rootNode, *searchRootNode, m_selectors.first(), output);
        break;

    case TagNameMatch:
        executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case ClassNameMatch:
        executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case CompilableMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        {
        for (auto& selector : m_selectors) {
            if (!compileSelector(selector)) {
                m_matchType = MultipleSelectorMatch;
                goto MultipleSelectorMatch;
            }
        }
        m_matchType = CompiledMultipleSelectorMatch;
        goto CompiledMultipleSelectorMatch;
        }
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case CompiledMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        CompiledMultipleSelectorMatch:
        executeCompiledSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case MultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        MultipleSelectorMatch:
#endif
        executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
    }
}

SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList)
    : m_selectorList(WTFMove(selectorList))
    , m_selectors(m_selectorList)
{
}

ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document)
{
    if (auto* entry = m_entries.get(selectors))
        return *entry;

    CSSParser parser(document);
    CSSSelectorList selectorList;
    parser.parseSelector(selectors, selectorList);

    if (!selectorList.first() || selectorList.hasInvalidSelector())
        return Exception { SyntaxError };

    if (selectorList.selectorsNeedNamespaceResolution())
        return Exception { SyntaxError };

    const int maximumSelectorQueryCacheSize = 256;
    if (m_entries.size() == maximumSelectorQueryCacheSize)
        m_entries.remove(m_entries.random());

    return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value;
}

}


{
    Element* currentNode = &targetElement;
    do {
        for (auto& selector : m_selectors) {
            Element* candidateElement = selectorClosest(selector, *currentNode, targetElement);
            if (candidateElement)
                return candidateElement;
        }
        currentNode = currentNode->parentElement();
    } while (currentNode);
    return nullptr;
}

/*
 *
 */

#include "config.h"
#include "SelectorQuery.h"

#include "CSSParser.h"
#include "ElementDescendantIterator.h"
#include "HTMLNames.h"
#include "SelectorChecker.h"
#include "StaticNodeList.h"
#include "StyledElement.h"

namespace WebCore {

#if !ASSERT_DISABLED
static bool isSingleTagNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Tag;
}

static bool isSingleClassNameSelector(const CSSSelector& selector)
{
    return selector.isLastInTagHistory() && selector.match() == CSSSelector::Class;
}
#endif

enum class IdMatchingType : uint8_t {
    None,
    Rightmost,
    Filter
};

static bool canBeUsedForIdFastPath(const CSSSelector& selector)
{
    return selector.match() == CSSSelector::Id
        || (selector.match() == CSSSelector::Exact && selector.attribute() == HTMLNames::idAttr && !selector.attributeValueMatchingIsCaseInsensitive());
}

static IdMatchingType findIdMatchingType(const CSSSelector& firstSelector)
{
    bool inRightmost = true;
    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            if (inRightmost)
                return IdMatchingType::Rightmost;
            return IdMatchingType::Filter;
        }
        if (selector->relation() != CSSSelector::Subselector)
            inRightmost = false;
    }
    return IdMatchingType::None;
}

SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList)
{
    unsigned selectorCount = 0;
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        selectorCount++;

    m_selectors.reserveInitialCapacity(selectorCount);
    for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
        m_selectors.uncheckedAppend(SelectorData(selector));

    if (selectorCount == 1) {
        const CSSSelector& selector = *m_selectors.first().selector;
        if (selector.isLastInTagHistory()) {
            switch (selector.match()) {
            case CSSSelector::Tag:
                m_matchType = TagNameMatch;
                break;
            case CSSSelector::Class:
                m_matchType = ClassNameMatch;
                break;
            default:
                if (canBeUsedForIdFastPath(selector))
                    m_matchType = RightMostWithIdMatch;
                else
                    m_matchType = CompilableSingle;
                break;
            }
        } else {
            switch (findIdMatchingType(selector)) {
            case IdMatchingType::None:
                m_matchType = CompilableSingle;
                break;
            case IdMatchingType::Rightmost:
                m_matchType = RightMostWithIdMatch;
                break;
            case IdMatchingType::Filter:
                m_matchType = CompilableSingleWithRootFilter;
                break;
            }
        }
    } else
        m_matchType = CompilableMultipleSelectorMatch;
}

inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    return selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity);
}

inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
{
    SelectorChecker selectorChecker(element.document());
    SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
    selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    unsigned ignoredSpecificity;
    if (!selectorChecker.match(*selectorData.selector, element, selectorCheckingContext, ignoredSpecificity))
        return nullptr;
    return &element;
}

bool SelectorDataList::matches(Element& targetElement) const
{
    for (auto& selctor : m_selectors) {
        if (selectorMatches(selctor, targetElement, targetElement))
            return true;
    }
    return false;
}

Element* SelectorDataList::closest(Element& targetElement) const

struct AllElementExtractorSelectorQueryTrait {
    typedef Vector<Ref<Element>> OutputType;
    static const bool shouldOnlyMatchFirstElement = false;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element) { ASSERT(element); output.append(*element); }
};

Ref<NodeList> SelectorDataList::queryAll(ContainerNode& rootNode) const
{
    Vector<Ref<Element>> result;
    execute<AllElementExtractorSelectorQueryTrait>(rootNode, result);
    return StaticElementList::create(WTFMove(result));
}

struct SingleElementExtractorSelectorQueryTrait {
    typedef Element* OutputType;
    static const bool shouldOnlyMatchFirstElement = true;
    ALWAYS_INLINE static void appendOutputForElement(OutputType& output, Element* element)
    {
        ASSERT(element);
        ASSERT(!output);
        output = element;
    }
};

Element* SelectorDataList::queryFirst(ContainerNode& rootNode) const
{
    Element* result = nullptr;
    execute<SingleElementExtractorSelectorQueryTrait>(rootNode, result);
    return result;
}

static const CSSSelector* selectorForIdLookup(const ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return nullptr;
    if (rootNode.document().inQuirksMode())
        return nullptr;

    for (const CSSSelector* selector = &firstSelector; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector))
            return selector;
        if (selector->relation() != CSSSelector::Subselector)
            break;
    }

    return nullptr;
}

static inline bool isTreeScopeRoot(const ContainerNode& node)
{
    return node.isDocumentNode() || node.isShadowRoot();
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeFastPathForIdSelector(const ContainerNode& rootNode, const SelectorData& selectorData, const CSSSelector* idSelector, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(idSelector);

    const AtomString& idToMatch = idSelector->value();
    if (UNLIKELY(rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
        const Vector<Element*>* elements = rootNode.treeScope().getAllElementsById(idToMatch);
        ASSERT(elements);
        bool rootNodeIsTreeScopeRoot = isTreeScopeRoot(rootNode);
        for (auto& element : *elements) {
            if ((rootNodeIsTreeScopeRoot || element->isDescendantOf(rootNode)) && selectorMatches(selectorData, *element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
        return;
    }

    Element* element = rootNode.treeScope().getElementById(idToMatch);
    if (!element || !(isTreeScopeRoot(rootNode) || element->isDescendantOf(rootNode)))
        return;
    if (selectorMatches(selectorData, *element, rootNode))
        SelectorQueryTrait::appendOutputForElement(output, element);
}

static ContainerNode& filterRootById(ContainerNode& rootNode, const CSSSelector& firstSelector)
{
    if (!rootNode.isConnected())
        return rootNode;
    if (rootNode.document().inQuirksMode())
        return rootNode;

    // If there was an Id match in the rightmost Simple Selector, we should be in a RightMostWithIdMatch, not in filter.
    // Thus we can skip the rightmost match.
    const CSSSelector* selector = &firstSelector;
    do {
        ASSERT(!canBeUsedForIdFastPath(*selector));
        if (selector->relation() != CSSSelector::Subselector)
            break;
        selector = selector->tagHistory();
    } while (selector);

    bool inAdjacentChain = false;
    for (; selector; selector = selector->tagHistory()) {
        if (canBeUsedForIdFastPath(*selector)) {
            const AtomString& idToMatch = selector->value();
            if (ContainerNode* searchRoot = rootNode.treeScope().getElementById(idToMatch)) {
                if (LIKELY(!rootNode.treeScope().containsMultipleElementsWithId(idToMatch))) {
                    if (inAdjacentChain)
                        searchRoot = searchRoot->parentNode();
                    if (searchRoot && (isTreeScopeRoot(rootNode) || searchRoot == &rootNode || searchRoot->isDescendantOf(rootNode)))
                        return *searchRoot;
                }
            }
        }
        if (selector->relation() == CSSSelector::Subselector)
            continue;
        if (selector->relation() == CSSSelector::DirectAdjacent || selector->relation() == CSSSelector::IndirectAdjacent)
            inAdjacentChain = true;
        else
            inAdjacentChain = false;
    }
    return rootNode;
}

static ALWAYS_INLINE bool localNameMatches(const Element& element, const AtomString& localName, const AtomString& lowercaseLocalName)
{
    if (element.isHTMLElement() && element.document().isHTMLDocument())
        return element.localName() == lowercaseLocalName;
    return element.localName() == localName;

}

template <typename SelectorQueryTrait>
static inline void elementsForLocalName(const ContainerNode& rootNode, const AtomString& localName, const AtomString& lowercaseLocalName, typename SelectorQueryTrait::OutputType& output)
{
    if (localName == lowercaseLocalName) {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.tagQName().localName() == localName) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    } else {
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (localNameMatches(element, localName, lowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
static inline void anyElement(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output)
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        SelectorQueryTrait::appendOutputForElement(output, &element);
        if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
            return;
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleTagNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleTagNameSelector(*selectorData.selector));

    const QualifiedName& tagQualifiedName = selectorData.selector->tagQName();
    const AtomString& selectorLocalName = tagQualifiedName.localName();
    const AtomString& selectorLowercaseLocalName = selectorData.selector->tagLowercaseLocalName();
    const AtomString& selectorNamespaceURI = tagQualifiedName.namespaceURI();

    if (selectorNamespaceURI == starAtom()) {
        if (selectorLocalName != starAtom()) {
            // Common case: name defined, selectorNamespaceURI is a wildcard.
            elementsForLocalName<SelectorQueryTrait>(rootNode, selectorLocalName, selectorLowercaseLocalName, output);
        } else {
            // Other fairly common case: both are wildcards.
            anyElement<SelectorQueryTrait>(rootNode, output);
        }
    } else {
        // Fallback: NamespaceURI is set, selectorLocalName may be starAtom().
        for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
            if (element.namespaceURI() == selectorNamespaceURI && localNameMatches(element, selectorLocalName, selectorLowercaseLocalName)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
            }
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleClassNameSelectorData(const ContainerNode& rootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);
    ASSERT(isSingleClassNameSelector(*selectorData.selector));

    const AtomString& className = selectorData.selector->value();
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        if (element.hasClass() && element.classNames().contains(className)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleSelectorData(const ContainerNode& rootNode, const ContainerNode& searchRootNode, const SelectorData& selectorData, typename SelectorQueryTrait::OutputType& output) const
{
    ASSERT(m_selectors.size() == 1);

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
        if (selectorMatches(selectorData, element, rootNode)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
            if (selectorMatches(selector, element, rootNode)) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

#if ENABLE(CSS_SELECTOR_JIT)
template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;

    for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
#if CSS_SELECTOR_JIT_PROFILING
        selectorData.compiledSelectorUsed();
#else
        UNUSED_PARAM(selectorData);
#endif
        if (selectorChecker(&element, &checkingContext)) {
            SelectorQueryTrait::appendOutputForElement(output, &element);
            if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                return;
        }
    }
}

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
    checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
    for (auto& element : elementDescendants(const_cast<ContainerNode&>(rootNode))) {
        for (auto& selector : m_selectors) {
#if CSS_SELECTOR_JIT_PROFILING
            selector.compiledSelectorUsed();
#endif
            bool matched = false;
            void* compiledSelectorChecker = selector.compiledSelectorCodeRef.code().executableAddress();
            if (selector.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
                auto selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element);
            } else {
                ASSERT(selector.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
                auto selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selector.compilationStatus);
                matched = selectorChecker(&element, &checkingContext);
            }
            if (matched) {
                SelectorQueryTrait::appendOutputForElement(output, &element);
                if (SelectorQueryTrait::shouldOnlyMatchFirstElement)
                    return;
                break;
            }
        }
    }
}

static bool isCompiledSelector(SelectorCompilationStatus compilationStatus)
{
    return compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker || compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext;
}

bool SelectorDataList::compileSelector(const SelectorData& selectorData)
{
    if (selectorData.compilationStatus != SelectorCompilationStatus::NotCompiled)
        return isCompiledSelector(selectorData.compilationStatus);

    selectorData.compilationStatus = SelectorCompiler::compileSelector(selectorData.selector, SelectorCompiler::SelectorContext::QuerySelector, selectorData.compiledSelectorCodeRef);
    return isCompiledSelector(selectorData.compilationStatus);
}

#endif // ENABLE(CSS_SELECTOR_JIT)

template <typename SelectorQueryTrait>
ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename SelectorQueryTrait::OutputType& output) const
{
    ContainerNode* searchRootNode = &rootNode;
    switch (m_matchType) {
    case RightMostWithIdMatch:
        {
        const SelectorData& selectorData = m_selectors.first();
        if (const CSSSelector* idSelector = selectorForIdLookup(*searchRootNode, *selectorData.selector)) {
            executeFastPathForIdSelector<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), idSelector, output);
            break;
        }
#if ENABLE(CSS_SELECTOR_JIT)
        if (compileSelector(selectorData))
            goto CompiledSingleCase;
#endif // ENABLE(CSS_SELECTOR_JIT)
        goto SingleSelectorCase;
        ASSERT_NOT_REACHED();
        }

    case CompilableSingleWithRootFilter:
    case CompilableSingle:
        {
#if ENABLE(CSS_SELECTOR_JIT)
        const SelectorData& selectorData = m_selectors.first();
        ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::NotCompiled);
        ASSERT(m_matchType == CompilableSingle || m_matchType == CompilableSingleWithRootFilter);
        if (compileSelector(selectorData)) {
            if (m_matchType == CompilableSingle) {
                m_matchType = CompiledSingle;
                goto CompiledSingleCase;
            }
            ASSERT(m_matchType == CompilableSingleWithRootFilter);
            m_matchType = CompiledSingleWithRootFilter;
            goto CompiledSingleWithRootFilterCase;
        }
#endif // ENABLE(CSS_SELECTOR_JIT)
        if (m_matchType == CompilableSingle) {
            m_matchType = SingleSelector;
            goto SingleSelectorCase;
        }
        ASSERT(m_matchType == CompilableSingleWithRootFilter);
        m_matchType = SingleSelectorWithRootFilter;
        goto SingleSelectorWithRootFilterCase;
        ASSERT_NOT_REACHED();
        }

#if ENABLE(CSS_SELECTOR_JIT)
    case CompiledSingleWithRootFilter:
        CompiledSingleWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case CompiledSingle:
        {
        CompiledSingleCase:
        const SelectorData& selectorData = m_selectors.first();
        void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
        if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
            SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData);
        } else {
            ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
            SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
            executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData);
        }
        break;
        }
#else
    case CompiledSingleWithRootFilter:
    case CompiledSingle:
        ASSERT_NOT_REACHED();
#if ASSERT_DISABLED
        FALLTHROUGH;
#endif
#endif // ENABLE(CSS_SELECTOR_JIT)

    case SingleSelectorWithRootFilter:
        SingleSelectorWithRootFilterCase:
        searchRootNode = &filterRootById(*searchRootNode, *m_selectors.first().selector);
        FALLTHROUGH;
    case SingleSelector:
        SingleSelectorCase:
        executeSingleSelectorData<SelectorQueryTrait>(rootNode, *searchRootNode, m_selectors.first(), output);
        break;

    case TagNameMatch:
        executeSingleTagNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case ClassNameMatch:
        executeSingleClassNameSelectorData<SelectorQueryTrait>(*searchRootNode, m_selectors.first(), output);
        break;
    case CompilableMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        {
        for (auto& selector : m_selectors) {
            if (!compileSelector(selector)) {
                m_matchType = MultipleSelectorMatch;
                goto MultipleSelectorMatch;
            }
        }
        m_matchType = CompiledMultipleSelectorMatch;
        goto CompiledMultipleSelectorMatch;
        }
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case CompiledMultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        CompiledMultipleSelectorMatch:
        executeCompiledSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
#else
        FALLTHROUGH;
#endif // ENABLE(CSS_SELECTOR_JIT)
    case MultipleSelectorMatch:
#if ENABLE(CSS_SELECTOR_JIT)
        MultipleSelectorMatch:
#endif
        executeSingleMultiSelectorData<SelectorQueryTrait>(*searchRootNode, output);
        break;
    }
}

SelectorQuery::SelectorQuery(CSSSelectorList&& selectorList)
    : m_selectorList(WTFMove(selectorList))
    , m_selectors(m_selectorList)
{
}

ExceptionOr<SelectorQuery&> SelectorQueryCache::add(const String& selectors, Document& document)
{
    if (auto* entry = m_entries.get(selectors))
        return *entry;

    CSSParser parser(document);
    CSSSelectorList selectorList;
    parser.parseSelector(selectors, selectorList);

    if (!selectorList.first() || selectorList.hasInvalidSelector())
        return Exception { SyntaxError };

    if (selectorList.selectorsNeedNamespaceResolution())
        return Exception { SyntaxError };

    const int maximumSelectorQueryCacheSize = 256;
    if (m_entries.size() == maximumSelectorQueryCacheSize)
        m_entries.remove(m_entries.random());

    return *m_entries.add(selectors, std::make_unique<SelectorQuery>(WTFMove(selectorList))).iterator->value;
}

}