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
++
++ #include <private/qv4bytecodegenerator_p.h>
++ #include <private/qv4compilercontext_p.h>
++ #include <private/qqmljsastfwd_p.h>
++
++ QT_USE_NAMESPACE
++ using namespace QV4;
++ using namespace Moth;
++
++ void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc)
++ {
++ currentLine = static_cast<int>(loc.startLine);
++ currentSourceLocation = loc;
++ }
++
++ void BytecodeGenerator::incrementStatement()
++ {
++ ++currentStatement;
++ }
++
++ int BytecodeGenerator::newRegister()
++ {
++ int t = currentReg++;
++ if (regCount < currentReg)
++ regCount = currentReg;
++ return t;
++ }
++
++ int BytecodeGenerator::newRegisterArray(int n)
++ {
++ int t = currentReg;
++ currentReg += n;
++ if (regCount < currentReg)
++ regCount = currentReg;
++ return t;
++ }
++
++ void BytecodeGenerator::packInstruction(I &i)
++ {
++ Instr::Type type = Instr::unpack(i.packed);
++ Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
++ type = Instr::narrowInstructionType(type);
++ int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
++ int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
++ uchar *code = i.packed + Instr::encodedLength(type);
++ for (int j = 0; j < nMembers; ++j) {
++ instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int));
++ }
++ enum {
++ Normal,
++ Wide
++ } width = Normal;
++ for (int n = 0; n < nMembers; ++n) {
++ if (width == Normal && (static_cast<qint8>(instructionsAsInts[n]) != instructionsAsInts[n])) {
++ width = Wide;
++ break;
++ }
++ }
++ code = i.packed;
++ switch (width) {
++ case Normal:
++ code = Instr::pack(code, type);
++ for (int n = 0; n < nMembers; ++n) {
++ qint8 v = static_cast<qint8>(instructionsAsInts[n]);
++ memcpy(code, &v, 1);
++ code += 1;
++ }
++ i.size = code - i.packed;
++ if (i.offsetForJump != -1)
++ i.offsetForJump = i.size - 1;
++ break;
++ case Wide:
++ // nothing to do
++ break;
++ }
++ }
++
++ void BytecodeGenerator::adjustJumpOffsets()
++ {
++ for (int index = 0; index < instructions.size(); ++index) {
++ auto &i = instructions[index];
++ if (i.offsetForJump == -1) // no jump
++ continue;
++ Q_ASSERT(i.linkedLabel != -1 && labels.at(i.linkedLabel) != -1);
++ const auto &linkedInstruction = instructions.at(labels.at(i.linkedLabel));
++ qint8 *c = reinterpret_cast<qint8*>(i.packed + i.offsetForJump);
++ int jumpOffset = linkedInstruction.position - (i.position + i.size);
++ // qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
++ // << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
++ Instr::Type type = Instr::unpack(i.packed);
++ if (Instr::isWide(type)) {
++ Q_ASSERT(i.offsetForJump == i.size - 4);
++ qToLittleEndian<qint32>(jumpOffset, c);
++ } else {
++ Q_ASSERT(i.offsetForJump == i.size - 1);
++ qint8 o = jumpOffset;
++ Q_ASSERT(o == jumpOffset);
++ *c = o;
++ }
++ }
++ }
++
++ void BytecodeGenerator::compressInstructions()
++ {
++ // first round: compress all non jump instructions
++ int position = 0;
++ for (auto &i : instructions) {
++ i.position = position;
++ if (i.offsetForJump == -1)
++ packInstruction(i);
++ position += i.size;
++ }
++
++ adjustJumpOffsets();
++
++ // compress all jumps
++ position = 0;
++ for (auto &i : instructions) {
++ i.position = position;
++ if (i.offsetForJump != -1)
++ packInstruction(i);
++ position += i.size;
++ }
++
++ // adjust once again, as the packing above could have changed offsets
++ adjustJumpOffsets();
++ }
++
++ void BytecodeGenerator::finalize(Compiler::Context *context)
++ {
++ compressInstructions();
++
++ // collect content and line numbers
++ QByteArray code;
++ QList<CompiledData::CodeOffsetToLineAndStatement> lineAndStatementNumbers;
++
++ currentLine = -1;
++ currentStatement = -1;
++
++ Q_UNUSED(startLine);
++ for (qsizetype i = 0; i < instructions.size(); i++) {
++ if (instructions[i].line != currentLine || instructions[i].statement != currentStatement) {
++ currentLine = instructions[i].line;
++ currentStatement = instructions[i].statement;
++ CompiledData::CodeOffsetToLineAndStatement entry;
++ entry.codeOffset = code.size();
++ entry.line = currentLine;
++ entry.statement = currentStatement;
++ lineAndStatementNumbers.append(entry);
++ }
++
++ if (m_sourceLocationTable)
++ m_sourceLocationTable->entries[i].offset = static_cast<quint32>(code.size());
++
++ code.append(reinterpret_cast<const char *>(instructions[i].packed), instructions[i].size);
++ }
++
++ context->code = code;
++ context->lineAndStatementNumberMapping = lineAndStatementNumbers;
++ context->sourceLocationTable = std::move(m_sourceLocationTable);
++
++ context->labelInfo.reserve(context->labelInfo.size() + _labelInfos.size());
++ for (const auto &li : _labelInfos)
++ context->labelInfo.push_back(instructions.at(labels.at(li.labelIndex)).position);
++ }
++
++ int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
++ if (lastInstrType == int(Instr::Type::StoreReg)) {
++ if (type == Instr::Type::LoadReg) {
++ if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
++ // value is already in the accumulator
++ return -1;
++ }
++ }
++ if (type == Instr::Type::MoveReg) {
++ if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
++ Instruction::StoreReg store;
++ store.reg = i.MoveReg.destReg;
++ addInstruction(store);
++ return -1;
++ }
++ }
++ }
++ lastInstrType = int(type);
++ lastInstr = i;
++
++ if (debugMode && type != Instr::Type::Debug) {
++ QT_WARNING_PUSH
++ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
++ if (instructions.isEmpty() || currentLine != instructions.constLast().line) {
++ addInstruction(Instruction::Debug());
++ } else if (type == Instr::Type::Ret) {
++ currentLine = -currentLine;
++ addInstruction(Instruction::Debug());
++ currentLine = -currentLine;
++ currentSourceLocation = QQmlJS::SourceLocation();
++ }
++ QT_WARNING_POP
++ }
++
++ const int pos = instructions.size();
++
++ const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
++ int s = argCount*sizeof(int);
++ if (offsetOfOffset != -1)
++ offsetOfOffset += Instr::encodedLength(type);
++ I instr {
++ type,
++ static_cast<short>(s + Instr::encodedLength(type)),
++ 0,
++ currentLine,
++ currentStatement,
++ offsetOfOffset,
++ -1,
++ "\0\0"
++ };
++ uchar *code = instr.packed;
++ code = Instr::pack(code, Instr::wideInstructionType(type));
++
++ for (int j = 0; j < argCount; ++j) {
++ qToLittleEndian<qint32>(i.argumentsAsInts[j], code);
++ code += sizeof(int);
++ }
++
++ instructions.append(instr);
++ if (m_sourceLocationTable)
++ m_sourceLocationTable->entries.append({ 0, currentSourceLocation });
++
++ return pos;
++ }