From ae49af00b59628623bc15e19974ee5af5f0121ba Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 29 Sep 2022 13:30:42 +0200 Subject: QML: Track the statement indices together with line numbers We will need the statement indices when tracking value type references. New value type references shall only be written back in the same statement they were created in. Task-number: QTBUG-99766 Change-Id: I83f908df034e7da8ba46ccacaa29bd9d78020d20 Reviewed-by: Fabian Kosmale --- src/qml/common/qv4compileddata_p.h | 46 ++++++++++++++++++++++--------- src/qml/compiler/qv4bytecodegenerator.cpp | 30 ++++++++++++++++---- src/qml/compiler/qv4bytecodegenerator_p.h | 3 ++ src/qml/compiler/qv4codegen.cpp | 4 ++- src/qml/compiler/qv4compiler.cpp | 20 +++++++++----- src/qml/compiler/qv4compilercontext_p.h | 2 +- src/qml/compiler/qv4instr_moth.cpp | 11 ++++++-- src/qml/compiler/qv4instr_moth_p.h | 11 +++++--- src/qml/jsruntime/qv4stackframe.cpp | 36 +++++++++++++++++------- src/qml/jsruntime/qv4stackframe_p.h | 1 + src/qmlcompiler/qqmljscodegenerator.cpp | 8 +++--- 11 files changed, 123 insertions(+), 49 deletions(-) diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h index ff08f03683..f87578daa1 100644 --- a/src/qml/common/qv4compileddata_p.h +++ b/src/qml/common/qv4compileddata_p.h @@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x38 // Added context to translation data +#define QV4_DATA_STRUCTURE_VERSION 0x39 // Changed CodeOffsetToLine(AndStatement) struct class QIODevice; class QQmlTypeNameCache; @@ -237,11 +237,12 @@ struct String static_assert (sizeof (String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); -struct CodeOffsetToLine { +struct CodeOffsetToLineAndStatement { quint32_le codeOffset; - quint32_le line; + qint32_le line; // signed because debug instructions get negative line numbers + quint32_le statement; }; -static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(CodeOffsetToLineAndStatement) == 12, "CodeOffsetToLineAndStatement structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Block { @@ -325,8 +326,8 @@ struct Function ParameterType returnType; quint32_le localsOffset; quint16_le nLocals; - quint16_le nLineNumbers; - size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); } + quint16_le nLineAndStatementNumbers; + size_t lineAndStatementNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); } quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers quint32_le nRegisters; @@ -337,7 +338,10 @@ struct Function quint16_le firstTemporalDeadZoneRegister; quint16_le sizeOfRegisterTemporalDeadZone; - size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } + size_t labelInfosOffset() const + { + return lineAndStatementNumberOffset() + nLineAndStatementNumbers * sizeof(CodeOffsetToLineAndStatement); + } // Keep all unaligned data at the end quint8 flags; @@ -346,9 +350,21 @@ struct Function // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] - const Parameter *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } - const quint32_le *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } - const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast(reinterpret_cast(this) + lineNumberOffset()); } + const Parameter *formalsTable() const + { + return reinterpret_cast( + reinterpret_cast(this) + formalsOffset); + } + const quint32_le *localsTable() const + { + return reinterpret_cast( + reinterpret_cast(this) + localsOffset); + } + const CodeOffsetToLineAndStatement *lineAndStatementNumberTable() const + { + return reinterpret_cast( + reinterpret_cast(this) + lineAndStatementNumberOffset()); + } // --- QQmlPropertyCacheCreator interface const Parameter *formalsBegin() const { return formalsTable(); } @@ -359,9 +375,13 @@ struct Function const char *code() const { return reinterpret_cast(this) + codeOffset; } - static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { - int trailingData = nFormals * sizeof(Parameter) + (nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) - + nLines*sizeof(CodeOffsetToLine); + static int calculateSize( + int nFormals, int nLocals, int nLinesAndStatements, int nInnerfunctions, + int labelInfoSize, int codeSize) + { + int trailingData = nFormals * sizeof(Parameter) + + (nLocals + nInnerfunctions + labelInfoSize) * sizeof (quint32) + + nLinesAndStatements * sizeof(CodeOffsetToLineAndStatement); size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); Q_ASSERT(size < INT_MAX); return int(size); diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index 8f0518093a..b88c83512f 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -15,6 +15,11 @@ void BytecodeGenerator::setLocation(const QQmlJS::SourceLocation &loc) currentSourceLocation = loc; } +void BytecodeGenerator::incrementStatement() +{ + ++currentStatement; +} + int BytecodeGenerator::newRegister() { int t = currentReg++; @@ -129,17 +134,21 @@ void BytecodeGenerator::finalize(Compiler::Context *context) // collect content and line numbers QByteArray code; - QVector lineNumbers; + QVector lineAndStatementNumbers; currentLine = -1; + currentStatement = -1; + Q_UNUSED(startLine); for (qsizetype i = 0; i < instructions.size(); i++) { - if (instructions[i].line != currentLine) { + if (instructions[i].line != currentLine || instructions[i].statement != currentStatement) { currentLine = instructions[i].line; - CompiledData::CodeOffsetToLine entry; + currentStatement = instructions[i].statement; + CompiledData::CodeOffsetToLineAndStatement entry; entry.codeOffset = code.size(); entry.line = currentLine; - lineNumbers.append(entry); + entry.statement = currentStatement; + lineAndStatementNumbers.append(entry); } if (m_sourceLocationTable) @@ -149,7 +158,7 @@ void BytecodeGenerator::finalize(Compiler::Context *context) } context->code = code; - context->lineNumberMapping = lineNumbers; + context->lineAndStatementNumberMapping = lineAndStatementNumbers; context->sourceLocationTable = std::move(m_sourceLocationTable); context->labelInfo.reserve(context->labelInfo.size() + _labelInfos.size()); @@ -197,7 +206,16 @@ QT_WARNING_POP int s = argCount*sizeof(int); if (offsetOfOffset != -1) offsetOfOffset += Instr::encodedLength(type); - I instr{type, static_cast(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" }; + I instr { + type, + static_cast(s + Instr::encodedLength(type)), + 0, + currentLine, + currentStatement, + offsetOfOffset, + -1, + "\0\0" + }; uchar *code = instr.packed; code = Instr::pack(code, Instr::wideInstructionType(type)); diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 85d260ed7b..35e539fb5e 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -229,6 +229,7 @@ QT_WARNING_POP void setLocation(const QQmlJS::SourceLocation &loc); + void incrementStatement(); ExceptionHandler *exceptionHandler() const { return currentExceptionHandler; @@ -279,6 +280,7 @@ private: short size; uint position; int line; + int statement; int offsetForJump; int linkedLabel; unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type @@ -297,6 +299,7 @@ public: private: int startLine = 0; int currentLine = 0; + int currentStatement = 0; QQmlJS::SourceLocation currentSourceLocation; std::unique_ptr m_sourceLocationTable; bool debugMode = false; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 9b0efcba41..2f5a9bd55f 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -373,6 +373,7 @@ void Codegen::statement(Statement *ast) { RegisterScope scope(this); + bytecodeGenerator->incrementStatement(); bytecodeGenerator->setLocation(ast->firstSourceLocation()); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); @@ -388,6 +389,7 @@ void Codegen::statement(ExpressionNode *ast) } else { RegisterScope scope(this); + bytecodeGenerator->incrementStatement(); pushExpr(Result(nx)); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); qSwap(_volatileMemoryLocations, vLocs); @@ -3382,7 +3384,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalPara qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue; QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(), - _context->line, _context->lineNumberMapping); + _context->line, _context->lineAndStatementNumberMapping); qDebug(); } } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index bb5c7d109f..e1e26715cd 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -424,9 +424,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->localsOffset = currentOffset; currentOffset += function->nLocals * sizeof(quint32); - function->nLineNumbers = irFunction->lineNumberMapping.size(); - Q_ASSERT(function->lineNumberOffset() == currentOffset); - currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); + function->nLineAndStatementNumbers + = irFunction->lineAndStatementNumberMapping.size(); + Q_ASSERT(function->lineAndStatementNumberOffset() == currentOffset); + currentOffset += function->nLineAndStatementNumbers + * sizeof(CompiledData::CodeOffsetToLineAndStatement); function->nRegisters = irFunction->registerCountInFunction; @@ -453,8 +455,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte for (int i = 0; i < irFunction->locals.size(); ++i) locals[i] = getStringId(irFunction->locals.at(i)); - // write line numbers - memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); + // write line and statement numbers + memcpy(f + function->lineAndStatementNumberOffset(), + irFunction->lineAndStatementNumberMapping.constData(), + irFunction->lineAndStatementNumberMapping.size() + * sizeof(CompiledData::CodeOffsetToLineAndStatement)); quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset()); for (unsigned u : irFunction->labelInfo) { @@ -658,8 +663,9 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp Context *f = module->functions.at(i); blockAndFunctionOffsets[i] = nextOffset; - quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), - int(f->labelInfo.size()), f->code.size()); + quint32 size = QV4::CompiledData::Function::calculateSize( + f->arguments.size(), f->locals.size(), f->lineAndStatementNumberMapping.size(), + f->nestedContexts.size(), int(f->labelInfo.size()), f->code.size()); functionSize += size - f->code.size(); nextOffset += size; } diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 3e80079b9a..e0f3547d0b 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -184,7 +184,7 @@ struct Context { ControlFlow *controlFlow = nullptr; QByteArray code; - QVector lineNumberMapping; + QVector lineAndStatementNumberMapping; std::unique_ptr sourceLocationTable; std::vector labelInfo; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index bdd6ee683a..03ca29f1e1 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -106,11 +106,13 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } -void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector &lineNumberMapping) +void dumpBytecode( + const char *code, int len, int nLocals, int nFormals, int /*startLine*/, + const QVector &lineAndStatementNumberMapping) { MOTH_JUMP_TABLE; - auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + auto findLine = [](const CompiledData::CodeOffsetToLineAndStatement &entry, uint offset) { return entry.codeOffset < offset; }; @@ -118,7 +120,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st const char *start = code; const char *end = code + len; while (code < end) { - const auto codeToLine = std::lower_bound(lineNumberMapping.constBegin(), lineNumberMapping.constEnd(), static_cast(code - start) + 1, findLine) - 1; + const auto codeToLine = std::lower_bound( + lineAndStatementNumberMapping.constBegin(), + lineAndStatementNumberMapping.constEnd(), + static_cast(code - start) + 1, findLine) - 1; int line = int(codeToLine->line); if (line != lastLine) lastLine = line; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 75408fd348..5b6d676782 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -474,7 +474,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace CompiledData { -struct CodeOffsetToLine; +struct CodeOffsetToLineAndStatement; } namespace Moth { @@ -500,10 +500,13 @@ inline bool operator!=(const StackSlot &l, const StackSlot &r) { return l.stackS // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h void dumpBytecode(const char *bytecode, int len, int nLocals, int nFormals, int startLine = 1, - const QVector &lineNumberMapping = QVector()); + const QVector &lineAndStatementNumberMapping + = QVector()); inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals, int startLine = 1, - const QVector &lineNumberMapping = QVector()) { - dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine, lineNumberMapping); + const QVector &lineAndStatementNumberMapping + = QVector()) { + dumpBytecode(bytecode.constData(), bytecode.size(), nLocals, nFormals, startLine, + lineAndStatementNumberMapping); } union Instr diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp index a02ce0edc5..e8ff9a89bc 100644 --- a/src/qml/jsruntime/qv4stackframe.cpp +++ b/src/qml/jsruntime/qv4stackframe.cpp @@ -17,21 +17,37 @@ QString CppStackFrame::function() const return v4Function ? v4Function->name()->toQString() : QString(); } -int CppStackFrame::lineNumber() const +static const CompiledData::CodeOffsetToLineAndStatement *lineAndStatement(const CppStackFrame *frame) { - if (!v4Function || instructionPointer <= 0) - return -1; + if (!frame->v4Function || frame->instructionPointer <= 0) + return nullptr; - auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + auto findLine = [](const CompiledData::CodeOffsetToLineAndStatement &entry, uint offset) { return entry.codeOffset < offset; }; - const QV4::CompiledData::Function *cf = v4Function->compiledFunction; - const uint offset = instructionPointer; - const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); - const uint nLineNumbers = cf->nLineNumbers; - const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; - return line->line; + const QV4::CompiledData::Function *cf = frame->v4Function->compiledFunction; + const uint offset = frame->instructionPointer; + const CompiledData::CodeOffsetToLineAndStatement *lineAndStatementNumbers + = cf->lineAndStatementNumberTable(); + const uint nLineAndStatementNumbers = cf->nLineAndStatementNumbers; + return std::lower_bound( + lineAndStatementNumbers, lineAndStatementNumbers + nLineAndStatementNumbers, + offset, findLine) - 1; +} + +int CppStackFrame::lineNumber() const +{ + if (auto *line = lineAndStatement(this)) + return line->line; + return -1; +} + +int CppStackFrame::statementNumber() const +{ + if (auto *statement = lineAndStatement(this)) + return statement->statement; + return -1; } ReturnedValue QV4::CppStackFrame::thisObject() const diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h index b9d57d5f54..2777d79c31 100644 --- a/src/qml/jsruntime/qv4stackframe_p.h +++ b/src/qml/jsruntime/qv4stackframe_p.h @@ -81,6 +81,7 @@ struct Q_QML_PRIVATE_EXPORT CppStackFrame : protected CppStackFrameBase QString source() const; QString function() const; int lineNumber() const; + int statementNumber() const; CppStackFrame *parentFrame() const { return parent; } void setParentFrame(CppStackFrame *parentFrame) { parent = parentFrame; } diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp index 3fc818531c..e84456907e 100644 --- a/src/qmlcompiler/qqmljscodegenerator.cpp +++ b/src/qmlcompiler/qqmljscodegenerator.cpp @@ -2897,15 +2897,15 @@ QString QQmlJSCodeGenerator::conversion(const QQmlJSScope::ConstPtr &from, int QQmlJSCodeGenerator::nextJSLine(uint line) const { - auto findLine = [](uint line, const QV4::CompiledData::CodeOffsetToLine &entry) { + auto findLine = [](int line, const QV4::CompiledData::CodeOffsetToLineAndStatement &entry) { return entry.line > line; }; const auto codeToLine - = std::upper_bound(m_context->lineNumberMapping.constBegin(), - m_context->lineNumberMapping.constEnd(), + = std::upper_bound(m_context->lineAndStatementNumberMapping.constBegin(), + m_context->lineAndStatementNumberMapping.constEnd(), line, findLine); - bool bNoNextLine = m_context->lineNumberMapping.constEnd() == codeToLine; + bool bNoNextLine = m_context->lineAndStatementNumberMapping.constEnd() == codeToLine; return static_cast(bNoNextLine ? -1 : codeToLine->line); } -- cgit v1.2.3