Revision control

++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #ifndef QV4BYTECODEGENERATOR_P_H
++ #define QV4BYTECODEGENERATOR_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++ #include <private/qv4instr_moth_p.h>
++ #include <private/qv4compileddata_p.h>
++ #include <private/qv4compilercontext_p.h>
++ #include <private/qqmljssourcelocation_p.h>
++
++ #include <memory>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QQmlJS {
++ class SourceLocation;
++ }
++
++ namespace QV4 {
++
++ namespace Compiler {
++ struct Context;
++ }
++
++ namespace Moth {
++
++ class BytecodeGenerator {
++ public:
++ BytecodeGenerator(int line, bool debug, bool storeSourceLocation = false)
++ : startLine(line), debugMode(debug)
++ {
++ if (storeSourceLocation)
++ m_sourceLocationTable.reset(new QV4::Compiler::Context::SourceLocationTable {});
++ }
++
++ struct Label {
++ enum LinkMode {
++ LinkNow,
++ LinkLater
++ };
++ Label() = default;
++ Label(BytecodeGenerator *generator, LinkMode mode = LinkNow)
++ : generator(generator),
++ index(generator->labels.size()) {
++ generator->labels.append(-1);
++ if (mode == LinkNow)
++ link();
++ }
++
++ void link() const {
++ Q_ASSERT(index >= 0);
++ Q_ASSERT(generator->labels[index] == -1);
++ generator->labels[index] = generator->instructions.size();
++ generator->clearLastInstruction();
++ }
++ bool isValid() const { return generator != nullptr; }
++
++ BytecodeGenerator *generator = nullptr;
++ int index = -1;
++ };
++
++ struct Jump {
++ Jump(BytecodeGenerator *generator, int instruction)
++ : generator(generator),
++ index(instruction)
++ { Q_ASSERT(generator && index != -1); }
++
++ ~Jump() {
++ Q_ASSERT(index == -1 || generator->hasError || generator->instructions[index].linkedLabel != -1); // make sure link() got called
++ }
++
++ Jump(Jump &&j) {
++ std::swap(generator, j.generator);
++ std::swap(index, j.index);
++ }
++
++ BytecodeGenerator *generator = nullptr;
++ int index = -1;
++
++ void link() {
++ link(generator->label());
++ }
++ void link(Label l) {
++ Q_ASSERT(l.index >= 0);
++ Q_ASSERT(generator->instructions[index].linkedLabel == -1);
++ generator->instructions[index].linkedLabel = l.index;
++ }
++
++ private:
++ // make this type move-only:
++ Q_DISABLE_COPY(Jump)
++ // we never move-assign this type anywhere, so disable it:
++ Jump &operator=(Jump &&) = delete;
++ };
++
++ struct ExceptionHandler : public Label {
++ ExceptionHandler() = default;
++ ExceptionHandler(BytecodeGenerator *generator)
++ : Label(generator, LinkLater)
++ {
++ }
++ ~ExceptionHandler()
++ {
++ Q_ASSERT(!generator || generator->currentExceptionHandler != this);
++ }
++ ExceptionHandler(const ExceptionHandler &) = default;
++ ExceptionHandler(ExceptionHandler &&) = default;
++ ExceptionHandler &operator=(const ExceptionHandler &) = default;
++ ExceptionHandler &operator=(ExceptionHandler &&) = default;
++ bool isValid() const { return generator != nullptr; }
++ };
++
++ Label label() {
++ return Label(this, Label::LinkNow);
++ }
++
++ Label newLabel() {
++ return Label(this, Label::LinkLater);
++ }
++
++ ExceptionHandler newExceptionHandler() {
++ return ExceptionHandler(this);
++ }
++
++ template<int InstrT>
++ void addInstruction(const InstrData<InstrT> &data)
++ {
++ Instr genericInstr;
++ InstrMeta<InstrT>::setData(genericInstr, data);
++ addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr);
++ }
++
++ Q_REQUIRED_RESULT Jump jump()
++ {
++ QT_WARNING_PUSH
++ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
++ Instruction::Jump data;
++ return addJumpInstruction(data);
++ QT_WARNING_POP
++ }
++
++ Q_REQUIRED_RESULT Jump jumpTrue()
++ {
++ return addJumpInstruction(Instruction::JumpTrue());
++ }
++
++ Q_REQUIRED_RESULT Jump jumpFalse()
++ {
++ return addJumpInstruction(Instruction::JumpFalse());
++ }
++
++ Q_REQUIRED_RESULT Jump jumpNotUndefined()
++ {
++ Instruction::JumpNotUndefined data{};
++ return addJumpInstruction(data);
++ }
++
++ Q_REQUIRED_RESULT Jump jumpNoException()
++ {
++ Instruction::JumpNoException data{};
++ return addJumpInstruction(data);
++ }
++
++ Q_REQUIRED_RESULT Jump jumpOptionalLookup(int index)
++ {
++ Instruction::GetOptionalLookup data{};
++ data.index = index;
++ return addJumpInstruction(data);
++ }
++
++ Q_REQUIRED_RESULT Jump jumpOptionalProperty(int name)
++ {
++ Instruction::LoadOptionalProperty data{};
++ data.name = name;
++ return addJumpInstruction(data);
++ }
++
++ void jumpStrictEqual(const StackSlot &lhs, const Label &target)
++ {
++ Instruction::CmpStrictEqual cmp;
++ cmp.lhs = lhs;
++ addInstruction(std::move(cmp));
++ addJumpInstruction(Instruction::JumpTrue()).link(target);
++ }
++
++ void jumpStrictNotEqual(const StackSlot &lhs, const Label &target)
++ {
++ Instruction::CmpStrictNotEqual cmp;
++ cmp.lhs = lhs;
++ addInstruction(std::move(cmp));
++ addJumpInstruction(Instruction::JumpTrue()).link(target);
++ }
++
++ void checkException()
++ {
++ Instruction::CheckException chk;
++ addInstruction(chk);
++ }
++
++ void setUnwindHandler(ExceptionHandler *handler)
++ {
++ currentExceptionHandler = handler;
++ Instruction::SetUnwindHandler data;
++ data.offset = 0;
++ if (!handler)
++ addInstruction(data);
++ else
++ addJumpInstruction(data).link(*handler);
++ }
++
++ void unwindToLabel(int level, const Label &target)
++ {
++ if (level) {
++ Instruction::UnwindToLabel unwind;
++ unwind.level = level;
++ addJumpInstruction(unwind).link(target);
++ } else {
++ jump().link(target);
++ }
++ }
++
++
++
++ void setLocation(const QQmlJS::SourceLocation &loc);
++ void incrementStatement();
++
++ ExceptionHandler *exceptionHandler() const {
++ return currentExceptionHandler;
++ }
++
++ int newRegister();
++ int newRegisterArray(int n);
++ int registerCount() const { return regCount; }
++ int currentRegister() const { return currentReg; }
++
++ void finalize(Compiler::Context *context);
++
++ template<int InstrT>
++ Jump addJumpInstruction(const InstrData<InstrT> &data)
++ {
++ Instr genericInstr;
++ InstrMeta<InstrT>::setData(genericInstr, data);
++ return Jump(this, addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr, offsetof(InstrData<InstrT>, offset)));
++ }
++
++ void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel)
++ {
++ if (jumpOnFalse)
++ addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel);
++ else
++ addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
++ }
++
++ void clearLastInstruction()
++ {
++ lastInstrType = -1;
++ }
++
++ void addLoopStart(const Label &start)
++ {
++ _labelInfos.push_back({ start.index });
++ }
++
++ void setError(bool b) { hasError = b; }
++
++ private:
++ friend struct Jump;
++ friend struct Label;
++ friend struct ExceptionHandler;
++
++ int addInstructionHelper(Moth::Instr::Type type, const Instr &i, int offsetOfOffset = -1);
++
++ struct I {
++ Moth::Instr::Type type;
++ short size;
++ uint position;
++ int line;
++ int statement;
++ int offsetForJump;
++ int linkedLabel;
++ unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type
++ };
++
++ void compressInstructions();
++ void packInstruction(I &i);
++ void adjustJumpOffsets();
++
++ QList<I> instructions;
++ QList<int> labels;
++ ExceptionHandler *currentExceptionHandler = nullptr;
++ int regCount = 0;
++ public:
++ int currentReg = 0;
++ private:
++ int startLine = 0;
++ int currentLine = 0;
++ int currentStatement = 0;
++ QQmlJS::SourceLocation currentSourceLocation;
++ std::unique_ptr<QV4::Compiler::Context::SourceLocationTable> m_sourceLocationTable;
++ bool debugMode = false;
++ bool hasError = false;
++
++ int lastInstrType = -1;
++ Moth::Instr lastInstr;
++
++ struct LabelInfo {
++ int labelIndex;
++ };
++ std::vector<LabelInfo> _labelInfos;
++ };
++
++ }
++ }
++
++ QT_END_NAMESPACE
++
++ #endif