From a8d53e0ae30178a071f12e6992b062febeef6fb9 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen <oswald.buddenhagen@nokia.com> Date: Tue, 11 May 2010 12:24:05 +0200 Subject: [PATCH] serialize AST into a single string this saves quite some mallocs in the parsing pass. --- src/shared/proparser/profileevaluator.cpp | 624 ++++++++++++++-------- src/shared/proparser/profileevaluator.h | 11 +- src/shared/proparser/proitems.cpp | 57 +- src/shared/proparser/proitems.h | 167 ++---- src/shared/proparser/prowriter.cpp | 333 +++++++----- 5 files changed, 666 insertions(+), 526 deletions(-) diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp index 1c3bd53c4aa..2fcac8fcca3 100644 --- a/src/shared/proparser/profileevaluator.cpp +++ b/src/shared/proparser/profileevaluator.cpp @@ -81,26 +81,6 @@ QT_BEGIN_NAMESPACE using namespace ProStringConstants; -static void refFunctions(QHash<ProString, ProFunctionDef *> *defs) -{ - foreach (ProFunctionDef *itm, *defs) - itm->ref(); -} - -static void clearFunctions(QHash<ProString, ProFunctionDef *> *defs) -{ - foreach (ProFunctionDef *itm, *defs) - itm->deref(); - defs->clear(); -} - -static void clearFunctions(ProFileEvaluator::FunctionDefs *defs) -{ - clearFunctions(&defs->replaceFunctions); - clearFunctions(&defs->testFunctions); -} - - /////////////////////////////////////////////////////////////////////// // // ProFileCache @@ -177,7 +157,6 @@ ProFileOption::ProFileOption() ProFileOption::~ProFileOption() { - clearFunctions(&base_functions); } /////////////////////////////////////////////////////////////////////// @@ -199,29 +178,15 @@ public: /////////////// Reading pro file - struct BlockCursor { - BlockCursor() : cursor(0) {} - BlockCursor(BlockCursor &other) : cursor(other.cursor) { other.cursor = 0; } - BlockCursor(ProItem **itp) : cursor(itp) {} - ~BlockCursor() { if (cursor) *cursor = 0; } - BlockCursor &operator=(BlockCursor &other) { cursor = other.cursor; other.cursor = 0; return *this; } - void set(ProItem **itp) { if (cursor) *cursor = 0; cursor = itp; } - void reset() { if (cursor) { *cursor = 0; cursor = 0; } } - void append(ProItem *item) { *cursor = item; cursor = item->nextRef(); } - bool isValid() const { return cursor != 0; } - private: - ProItem **cursor; - }; - struct BlockScope { - BlockScope() : braceLevel(0), special(false) {} + BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {} BlockScope(BlockScope &other) : - cursor(other.cursor), elseCursor(other.elseCursor), - braceLevel(other.braceLevel), special(other.special) {} - BlockCursor cursor; // Current appending position - BlockCursor elseCursor; // Appending position for else branch of last conditional in scope + start(other.start), braceLevel(other.braceLevel), + special(other.special), inBranch(other.inBranch) {} + ushort *start; // Where this block started; store length here int braceLevel; // Nesting of braces in scope bool special; // Single-line conditionals cannot have else branches + bool inBranch; // The 'else' branch of the previous TokBranch is still open }; enum ScopeState { @@ -232,21 +197,26 @@ public: bool read(ProFile *pro); bool read(ProFile *pro, const QString &content); - bool read(ProItem **itp, const QString &content); - bool readInternal(ProItem **itp, const QString &content, ushort *buf); - - void updateItem(ushort *uc, ushort *ptr); - ProVariable *startVariable(ushort *uc, ushort *ptr); - void finalizeVariable(ProVariable *var, ushort *uc, ushort *ptr); - void insertOperator(ProItem::ProItemKind opkind); - void enterScope(BlockCursor &cursor, bool special, ScopeState state); - void enterScope(ProItem **itp, bool special, ScopeState state) - { BlockCursor curs(itp); enterScope(curs, special, state); } - void flushCond(); - void flushScopes(); + bool read(QString *out, const QString &content); + bool readInternal(QString *out, const QString &content, ushort *buf); + + ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok); + ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len); + ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len); + void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len); + ALWAYS_INLINE void putHashStr(ushort *&pTokPtr, const ushort *buf, const ushort *ptr); + ALWAYS_INLINE void putHashStr(ushort *&pTokPtr, const QString &str); + void putLineMarker(ushort *&tokPtr); + void updateItem(ushort *&tokPtr, ushort *uc, ushort *ptr); + bool startVariable(ushort *&tokPtr, ushort *uc, ushort *ptr); + void enterScope(ushort *&tokPtr, bool special, ScopeState state); + void leaveScope(ushort *&tokPtr); + void flushCond(ushort *&tokPtr); + void flushScopes(ushort *&tokPtr); QStack<BlockScope> m_blockstack; ScopeState m_state; + bool m_lineMarked; // Current line already got a marker bool m_canElse; // Conditionals met on previous line, but no scope was opened bool m_invert; // Pending conditional is negated enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed @@ -261,12 +231,21 @@ public: ReturnReturn }; + static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr); + ProString getStr(const ushort *&tokPtr); + ProString getHashStr(const ushort *&tokPtr); + ProString getLongStr(const ushort *&tokPtr); + static ALWAYS_INLINE void skipStr(const ushort *&tokPtr); + static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr); + inline void skipLongStr(const ushort *&tokPtr); + VisitReturn visitProFile(ProFile *pro); - VisitReturn visitProBlock(ProItem *items); - VisitReturn visitProLoop(ProLoop *loop); - void visitProFunctionDef(ProFunctionDef *def); - void visitProVariable(ProVariable *variable); - VisitReturn visitProCondition(ProCondition *condition); + VisitReturn visitProBlock(const ushort *tokPtr); + VisitReturn visitProLoop(const ProString &variable, const ProString &expression, + const ushort *tokPtr); + void visitProFunctionDef(ushort tok, const ProString &name, const ushort *tokPtr); + void visitProVariable(ushort tok, const ProString &varName, const ProString &value); + VisitReturn visitProCondition(const ProString &text); static inline ProString map(const ProString &var); QHash<ProString, ProStringList> *findValues(const ProString &variableName, @@ -304,7 +283,7 @@ public: { return b ? ReturnTrue : ReturnFalse; } QList<ProStringList> prepareFunctionArgs(const ProString &arguments); - ProStringList evaluateFunction(ProFunctionDef *func, const QList<ProStringList> &argumentsList, bool *ok); + ProStringList evaluateFunction(const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok); QStringList qmakeMkspecPaths() const; QStringList qmakeFeaturePaths() const; @@ -313,6 +292,7 @@ public: int m_loopLevel; // To report unexpected break() and next()s bool m_cumulative; QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri' + QStack<QString> m_stringStack; // To handle token string refcounting QString m_outputDir; @@ -356,7 +336,6 @@ public: }; #if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) -Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::BlockCursor, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(ProFileEvaluator::Private::BlockScope, Q_MOVABLE_TYPE); #endif @@ -557,7 +536,6 @@ ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option) ProFileEvaluator::Private::~Private() { - clearFunctions(&m_functionDefs); } ////////// Parser /////////// @@ -591,21 +569,85 @@ bool ProFileEvaluator::Private::read(ProFile *pro, const QString &content) return ret; } -bool ProFileEvaluator::Private::read(ProItem **itp, const QString &content) +bool ProFileEvaluator::Private::read(QString *out, const QString &content) { QString buf; buf.reserve(content.size()); m_lineNo = 0; - return readInternal(itp, content, (ushort*)buf.data()); + return readInternal(out, content, (ushort*)buf.data()); +} + +void ProFileEvaluator::Private::putTok(ushort *&tokPtr, ushort tok) +{ + *tokPtr++ = tok; +} + +void ProFileEvaluator::Private::putBlockLen(ushort *&tokPtr, uint len) +{ + *tokPtr++ = (ushort)len; + *tokPtr++ = (ushort)(len >> 16); +} + +static void putStr(ushort *&pTokPtr, const QString &str) +{ + ushort *tokPtr = pTokPtr; + uint len = str.length(); + *tokPtr++ = (ushort)len; + memcpy(tokPtr, str.constData(), len * 2); + pTokPtr = tokPtr + len; +} + +void ProFileEvaluator::Private::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len) +{ + uint hash = ProString::hash((const QChar *)buf, len); + ushort *tokPtr = pTokPtr; + *tokPtr++ = (ushort)hash; + *tokPtr++ = (ushort)(hash >> 16); + *tokPtr++ = (ushort)len; + memcpy(tokPtr, buf, len * 2); + pTokPtr = tokPtr + len; +} + +void ProFileEvaluator::Private::putHashStr(ushort *&pTokPtr, const ushort *buf, const ushort *ptr) +{ + putHashStr(pTokPtr, buf, ptr - buf); +} + +void ProFileEvaluator::Private::putHashStr(ushort *&pTokPtr, const QString &str) +{ + putHashStr(pTokPtr, (const ushort *)str.constData(), str.length()); +} + +static void putLongStr(ushort *&pTokPtr, const ushort *buf, uint len) +{ + ushort *tokPtr = pTokPtr; + *tokPtr++ = (ushort)len; + *tokPtr++ = (ushort)(len >> 16); + memcpy(tokPtr, buf, len * 2); + pTokPtr = tokPtr + len; +} + +static void putLongStr(ushort *&pTokPtr, const ushort *buf, const ushort *ptr) +{ + putLongStr(pTokPtr, buf, ptr - buf); +} + +static void putLongStr(ushort *&pTokPtr, const QString &str) +{ + putLongStr(pTokPtr, (const ushort *)str.constData(), str.length()); } // We know that the buffer cannot grow larger than the input string, // and the read() functions rely on it. -bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, ushort *buf) +bool ProFileEvaluator::Private::readInternal(QString *out, const QString &in, ushort *buf) { + // Expression precompiler buffer + QString tokBuff; + tokBuff.reserve((in.size() + 1) * 5); + ushort *tokPtr = (ushort *)tokBuff.constData(); // Current writing position + // Parser state m_blockstack.resize(m_blockstack.size() + 1); - m_blockstack.top().cursor.set(itp); // We rely on QStrings being null-terminated, so don't maintain a global end pointer. const ushort *cur = (const ushort *)in.unicode(); @@ -614,7 +656,8 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u m_state = StNew; m_invert = false; m_operator = NoOperator; - ProVariable *currAssignment = 0; + m_lineMarked = false; + bool inAssignment = false; ushort *ptr = buf; int parens = 0; bool inError = false; @@ -627,19 +670,23 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u for (;; ++cur) { c = *cur; if (c == '\n' || !c) { // Entirely empty line (sans whitespace) - if (currAssignment) { - finalizeVariable(currAssignment, buf, ptr); - currAssignment = 0; + if (inAssignment) { + putLongStr(tokPtr, buf, ptr); + inAssignment = false; } else { - updateItem(buf, ptr); + updateItem(tokPtr, buf, ptr); } if (c) { ++cur; ++m_lineNo; goto freshLine; } + flushScopes(tokPtr); if (m_blockstack.size() > 1) logMessage(format("Missing closing brace(s).")); + while (m_blockstack.size()) + leaveScope(tokPtr); + *out = QString(tokBuff.constData(), tokPtr - (ushort *)tokBuff.constData()); return true; } if (c != ' ' && c != '\t' && c != '\r') @@ -694,7 +741,7 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u if (!inError) { // Finally, do the tokenization - if (!currAssignment) { + if (!inAssignment) { newItem: do { if (cur == end) @@ -714,7 +761,7 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u --parens; } else if (!parens) { if (c == ':') { - updateItem(buf, ptr); + updateItem(tokPtr, buf, ptr); if (m_state == StNew) logMessage(format("And operator without prior condition.")); else @@ -725,7 +772,7 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u goto newItem; } if (c == '|') { - updateItem(buf, ptr); + updateItem(tokPtr, buf, ptr); if (m_state != StCond) logMessage(format("Or operator without prior condition.")); else @@ -733,26 +780,27 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u goto nextItem; } if (c == '{') { - updateItem(buf, ptr); - flushCond(); + updateItem(tokPtr, buf, ptr); + flushCond(tokPtr); ++m_blockstack.top().braceLevel; goto nextItem; } if (c == '}') { - updateItem(buf, ptr); - flushScopes(); + updateItem(tokPtr, buf, ptr); + flushScopes(tokPtr); if (!m_blockstack.top().braceLevel) errorMessage(format("Excess closing brace.")); else if (!--m_blockstack.top().braceLevel && m_blockstack.count() != 1) { - m_blockstack.resize(m_blockstack.size() - 1); + leaveScope(tokPtr); m_state = StNew; m_canElse = false; + m_lineMarked = false; } goto nextItem; } if (c == '=') { - if ((currAssignment = startVariable(buf, ptr))) { + if ((inAssignment = startVariable(tokPtr, buf, ptr))) { ptr = buf; putSpace = false; break; @@ -779,7 +827,7 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u putSpace = true; } } - } // !currAssignment + } // !inAssignment do { if (cur == end) @@ -806,11 +854,11 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u if (lineCont) { putSpace = (ptr != buf); } else { - if (currAssignment) { - finalizeVariable(currAssignment, buf, ptr); - currAssignment = 0; + if (inAssignment) { + putLongStr(tokPtr, buf, ptr); + inAssignment = false; } else { - updateItem(buf, ptr); + updateItem(tokPtr, buf, ptr); } ptr = buf; putSpace = false; @@ -828,101 +876,108 @@ bool ProFileEvaluator::Private::readInternal(ProItem **itp, const QString &in, u } } -ProVariable *ProFileEvaluator::Private::startVariable(ushort *uc, ushort *ptr) +bool ProFileEvaluator::Private::startVariable(ushort *&tokPtr, ushort *uc, ushort *ptr) { - flushCond(); + flushCond(tokPtr); if (ptr == uc) // Line starting with '=', like a conflict marker - return 0; + return false; - ProVariable::VariableOperator opkind; + putLineMarker(tokPtr); + + ProToken opkind; switch (*(ptr - 1)) { case '+': --ptr; - opkind = ProVariable::AddOperator; + opkind = TokAppend; break; case '-': --ptr; - opkind = ProVariable::RemoveOperator; + opkind = TokRemove; break; case '*': --ptr; - opkind = ProVariable::UniqueAddOperator; + opkind = TokAppendUnique; break; case '~': --ptr; - opkind = ProVariable::ReplaceOperator; + opkind = TokReplace; break; default: - opkind = ProVariable::SetOperator; + opkind = TokAssign; goto skipTrunc; } if (ptr == uc) // Line starting with manipulation operator - return 0; + return false; if (*(ptr - 1) == ' ') --ptr; skipTrunc: - ProVariable *variable = new ProVariable(map(ProString(QString((QChar*)uc, ptr - uc)))); - variable->setLineNumber(m_lineNo); - variable->setVariableOperator(opkind); - return variable; + putTok(tokPtr, opkind); + putHashStr(tokPtr, uc, ptr); + return true; } -void ProFileEvaluator::Private::finalizeVariable(ProVariable *variable, ushort *uc, ushort *ptr) +void ProFileEvaluator::Private::putLineMarker(ushort *&tokPtr) { - variable->setValue(ProString(QString((QChar*)uc, ptr - uc), NoHash)); - - m_blockstack.top().cursor.append(variable); + if (!m_lineMarked) { + m_lineMarked = true; + *tokPtr++ = TokLine; + *tokPtr++ = (ushort)m_lineNo; + } } -void ProFileEvaluator::Private::enterScope(BlockCursor &cursor, bool special, ScopeState state) +void ProFileEvaluator::Private::enterScope(ushort *&tokPtr, bool special, ScopeState state) { m_blockstack.resize(m_blockstack.size() + 1); - m_blockstack.top().cursor = cursor; m_blockstack.top().special = special; + m_blockstack.top().start = tokPtr; + tokPtr += 2; m_state = state; m_canElse = false; } -void ProFileEvaluator::Private::flushScopes() +void ProFileEvaluator::Private::leaveScope(ushort *&tokPtr) +{ + if (m_blockstack.top().inBranch) { + // Put empty else block + putBlockLen(tokPtr, 0); + } + if (ushort *start = m_blockstack.top().start) { + putTok(tokPtr, TokTerminator); + uint len = tokPtr - start - 2; + start[0] = (ushort)len; + start[1] = (ushort)(len >> 16); + } + m_blockstack.resize(m_blockstack.size() - 1); +} + +void ProFileEvaluator::Private::flushScopes(ushort *&tokPtr) { if (m_state == StNew) { while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1) - m_blockstack.resize(m_blockstack.size() - 1); - m_blockstack.top().elseCursor.reset(); + leaveScope(tokPtr); + if (m_blockstack.top().inBranch) { + m_blockstack.top().inBranch = false; + // Put empty else block + putBlockLen(tokPtr, 0); + } m_canElse = false; } } -void ProFileEvaluator::Private::flushCond() +void ProFileEvaluator::Private::flushCond(ushort *&tokPtr) { if (m_state == StCond) { - ProBranch *blk = new ProBranch(); - blk->setLineNumber(m_lineNo); - BlockScope &top = m_blockstack.top(); - top.cursor.append(blk); - if (!top.special || top.braceLevel) { - top.elseCursor.set(blk->elseItemsRef()); - } else { - *blk->elseItemsRef() = 0; - // elseCursor was either never set in this scope - // or the last flushScopes reset it. - } - enterScope(blk->thenItemsRef(), false, StNew); + putTok(tokPtr, TokBranch); + m_blockstack.top().inBranch = true; + enterScope(tokPtr, false, StNew); } else { - flushScopes(); + flushScopes(tokPtr); } } -void ProFileEvaluator::Private::insertOperator(ProItem::ProItemKind opkind) -{ - ProItem *proOp = new ProItem(opkind); - proOp->setLineNumber(m_lineNo); - m_blockstack.top().cursor.append(proOp); -} - static bool get_next_arg(const QString ¶ms, int *pos, QString *out) { int quote = 0; @@ -979,7 +1034,7 @@ static bool isKeyFunc(const QString &str, const QString &key, int *pos) return true; } -void ProFileEvaluator::Private::updateItem(ushort *uc, ushort *ptr) +void ProFileEvaluator::Private::updateItem(ushort *&tokPtr, ushort *uc, ushort *ptr) { if (ptr == uc) return; @@ -987,7 +1042,7 @@ void ProFileEvaluator::Private::updateItem(ushort *uc, ushort *ptr) QString str((QChar*)uc, ptr - uc); int pos; const QString *defName; - ProFunctionDef::FunctionType defType; + ushort defType; if (!str.compare(statics.strelse, Qt::CaseInsensitive)) { if (m_invert || m_operator != NoOperator) { logMessage(format("Unexpected operator in front of else.")); @@ -995,27 +1050,28 @@ void ProFileEvaluator::Private::updateItem(ushort *uc, ushort *ptr) } BlockScope &top = m_blockstack.top(); if (m_canElse && (!top.special || top.braceLevel)) { - ProBranch *blk = new ProBranch(); - blk->setLineNumber(m_lineNo); - *blk->thenItemsRef() = 0; - top.cursor.append(blk); - enterScope(blk->elseItemsRef(), false, StCtrl); + putTok(tokPtr, TokBranch); + // Put empty then block + *tokPtr++ = 0; + *tokPtr++ = 0; + enterScope(tokPtr, false, StCtrl); return; } forever { BlockScope &top = m_blockstack.top(); - if (top.elseCursor.isValid()) { - BlockCursor cursor(top.elseCursor); // ref into stack becomes stale - enterScope(cursor, false, StCtrl); + if (top.inBranch && (!top.special || top.braceLevel)) { + top.inBranch = false; + enterScope(tokPtr, false, StCtrl); return; } if (top.braceLevel || m_blockstack.size() == 1) break; - m_blockstack.resize(m_blockstack.size() - 1); + leaveScope(tokPtr); } errorMessage(format("Unexpected 'else'.")); } else if (isKeyFunc(str, statics.strfor, &pos)) { - flushCond(); + flushCond(tokPtr); + putLineMarker(tokPtr); if (m_invert || m_operator == OrOperator) { // '|' could actually work reasonably, but qmake does nonsense here. logMessage(format("Unexpected operator in front of for().")); @@ -1031,18 +1087,20 @@ void ProFileEvaluator::Private::updateItem(ushort *uc, ushort *ptr) expr = var; var.clear(); } - ProLoop *loop = new ProLoop(var, expr); - m_blockstack.top().cursor.append(loop); - enterScope(loop->itemsRef(), true, StCtrl); + putTok(tokPtr, TokForLoop); + putHashStr(tokPtr, var); + putLongStr(tokPtr, expr); + enterScope(tokPtr, true, StCtrl); } else if (isKeyFunc(str, statics.strdefineReplace, &pos)) { defName = &statics.strdefineReplace; - defType = ProFunctionDef::ReplaceFunction; + defType = TokReplaceDef; goto deffunc; } else if (isKeyFunc(str, statics.strdefineTest, &pos)) { defName = &statics.strdefineTest; - defType = ProFunctionDef::TestFunction; + defType = TokTestDef; deffunc: - flushScopes(); + flushScopes(tokPtr); + putLineMarker(tokPtr); if (m_invert) { logMessage(format("Unexpected operator in front of function definition.")); return; @@ -1053,25 +1111,25 @@ void ProFileEvaluator::Private::updateItem(ushort *uc, ushort *ptr) return; } if (m_operator != NoOperator) { - insertOperator(m_operator == AndOperator ? ProItem::OpAndKind : ProItem::OpOrKind); + putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr); m_operator = NoOperator; } - ProFunctionDef *def = new ProFunctionDef(func, defType); - m_blockstack.top().cursor.append(def); - enterScope(def->itemsRef(), true, StCtrl); + putTok(tokPtr, defType); + putHashStr(tokPtr, func); + enterScope(tokPtr, true, StCtrl); } else { - flushScopes(); + flushScopes(tokPtr); + putLineMarker(tokPtr); if (m_operator != NoOperator) { - insertOperator(m_operator == AndOperator ? ProItem::OpAndKind : ProItem::OpOrKind); + putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr); m_operator = NoOperator; } if (m_invert) { - insertOperator(ProItem::OpNotKind); + putTok(tokPtr, TokNot); m_invert = false; } - ProItem *item = new ProCondition(str); - item->setLineNumber(m_lineNo); - m_blockstack.top().cursor.append(item); + putTok(tokPtr, TokCondition); + putStr(tokPtr, str); m_state = StCond; m_canElse = true; } @@ -1079,6 +1137,60 @@ void ProFileEvaluator::Private::updateItem(ushort *uc, ushort *ptr) //////// Evaluator tools ///////// +uint ProFileEvaluator::Private::getBlockLen(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + len |= (uint)*tokPtr++ << 16; + return len; +} + +ProString ProFileEvaluator::Private::getStr(const ushort *&tokPtr) +{ + const QString &str(m_stringStack.top()); + uint len = *tokPtr++; + ProString ret(str, tokPtr - (const ushort *)str.constData(), len, NoHash); + tokPtr += len; + return ret; +} + +ProString ProFileEvaluator::Private::getHashStr(const ushort *&tokPtr) +{ + uint hash = getBlockLen(tokPtr); + uint len = *tokPtr++; + const QString &str(m_stringStack.top()); + ProString ret(str, tokPtr - (const ushort *)str.constData(), len, hash); + tokPtr += len; + return ret; +} + +ProString ProFileEvaluator::Private::getLongStr(const ushort *&tokPtr) +{ + uint len = getBlockLen(tokPtr); + const QString &str(m_stringStack.top()); + ProString ret(str, tokPtr - (const ushort *)str.constData(), len, NoHash); + tokPtr += len; + return ret; +} + +void ProFileEvaluator::Private::skipStr(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + tokPtr += len; +} + +void ProFileEvaluator::Private::skipHashStr(const ushort *&tokPtr) +{ + tokPtr += 2; + uint len = *tokPtr++; + tokPtr += len; +} + +void ProFileEvaluator::Private::skipLongStr(const ushort *&tokPtr) +{ + uint len = getBlockLen(tokPtr); + tokPtr += len; +} + // FIXME: this should not build new strings for direct sections. // Note that the E_SPRINTF and E_LIST implementations rely on the deep copy. ProStringList ProFileEvaluator::Private::split_value_list(const QString &vals) @@ -1201,58 +1313,98 @@ static bool isTrue(const ProString &_str, QString &tmp) //////// Evaluator ///////// -ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock(ProItem *items) +ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock( + const ushort *tokPtr) { bool okey = true, or_op = false, invert = false; + uint blockLen; VisitReturn ret = ReturnTrue; - for (ProItem *itm = items; itm; itm = itm->next()) { - switch (itm->kind()) { - case ProItem::VariableKind: - visitProVariable(static_cast<ProVariable*>(itm)); + while (ushort tok = *tokPtr++) { + switch (tok) { + case TokLine: + m_lineNo = *tokPtr++; continue; - case ProItem::BranchKind: + case TokAssign: + case TokAppend: + case TokAppendUnique: + case TokRemove: + case TokReplace: { + const ProString &varName = getHashStr(tokPtr); + const ProString &varVal = getLongStr(tokPtr); + visitProVariable(tok, varName, varVal); + continue; } + case TokBranch: + blockLen = getBlockLen(tokPtr); if (m_cumulative) { if (!okey) m_skipLevel++; - ret = visitProBlock(static_cast<ProBranch*>(itm)->thenItems()); + ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; + tokPtr += blockLen; + blockLen = getBlockLen(tokPtr); if (!okey) m_skipLevel--; else m_skipLevel++; - if (ret == ReturnTrue || ret == ReturnFalse) - ret = visitProBlock(static_cast<ProBranch*>(itm)->elseItems()); + if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen) + ret = visitProBlock(tokPtr); if (okey) m_skipLevel--; } else { - ret = visitProBlock(okey ? static_cast<ProBranch*>(itm)->thenItems() - : static_cast<ProBranch*>(itm)->elseItems()); + if (okey) + ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; + tokPtr += blockLen; + blockLen = getBlockLen(tokPtr); + if (!okey) + ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; } + tokPtr += blockLen; okey = true, or_op = false; // force next evaluation break; - case ProItem::LoopKind: - if (m_cumulative) // This is a no-win situation, so just pretend it's no loop - ret = visitProBlock(static_cast<ProLoop*>(itm)->items()); - else if (okey != or_op) - ret = visitProLoop(static_cast<ProLoop*>(itm)); + case TokForLoop: + if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop + skipHashStr(tokPtr); + skipLongStr(tokPtr); + blockLen = getBlockLen(tokPtr); + ret = visitProBlock(tokPtr); + } else if (okey != or_op) { + const ProString &variable = getHashStr(tokPtr); + const ProString &expression = getLongStr(tokPtr); + blockLen = getBlockLen(tokPtr); + ret = visitProLoop(variable, expression, tokPtr); + } else { + skipHashStr(tokPtr); + skipLongStr(tokPtr); + blockLen = getBlockLen(tokPtr); + ret = ReturnTrue; + } + tokPtr += blockLen; okey = true, or_op = false; // force next evaluation break; - case ProItem::FunctionDefKind: - if (m_cumulative || okey != or_op) - visitProFunctionDef(static_cast<ProFunctionDef *>(itm)); + case TokTestDef: + case TokReplaceDef: + if (m_cumulative || okey != or_op) { + const ProString &name = getHashStr(tokPtr); + blockLen = getBlockLen(tokPtr); + visitProFunctionDef(tok, name, tokPtr); + } else { + skipHashStr(tokPtr); + blockLen = getBlockLen(tokPtr); + } + tokPtr += blockLen; okey = true, or_op = false; // force next evaluation continue; - case ProItem::OpNotKind: + case TokNot: invert ^= true; - break; - case ProItem::OpAndKind: + continue; + case TokAnd: or_op = false; - break; - case ProItem::OpOrKind: + continue; + case TokOr: or_op = true; - break; - case ProItem::ConditionKind: + continue; + case TokCondition: if (!m_skipLevel && okey != or_op) { - ret = visitProCondition(static_cast<ProCondition*>(itm)); + ret = visitProCondition(getStr(tokPtr)); switch (ret) { case ReturnTrue: okey = true; break; case ReturnFalse: okey = false; break; @@ -1261,12 +1413,14 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock( okey ^= invert; } else if (m_cumulative) { m_skipLevel++; - visitProCondition(static_cast<ProCondition*>(itm)); + visitProCondition(getStr(tokPtr)); m_skipLevel--; + } else { + skipStr(tokPtr); } or_op = !okey; // tentatively force next evaluation invert = false; - break; + continue; default: Q_ASSERT_X(false, "visitProBlock", "unexpected item type"); } if (ret != ReturnTrue && ret != ReturnFalse) @@ -1276,34 +1430,36 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProBlock( } -void ProFileEvaluator::Private::visitProFunctionDef(ProFunctionDef *def) +void ProFileEvaluator::Private::visitProFunctionDef( + ushort tok, const ProString &name, const ushort *tokPtr) { - QHash<ProString, ProFunctionDef *> *hash = - (def->type() == ProFunctionDef::TestFunction + QHash<ProString, FunctionDef> *hash = + (tok == TokTestDef ? &m_functionDefs.testFunctions : &m_functionDefs.replaceFunctions); - if (ProFunctionDef *xdef = hash->value(def->name())) - xdef->deref(); - hash->insert(def->name(), def); - def->ref(); + FunctionDef def; + def.string = m_stringStack.top(); + def.offset = tokPtr - (const ushort *)def.string.constData(); + hash->insert(name, def); } -ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(ProLoop *loop) +ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop( + const ProString &_variable, const ProString &expression, const ushort *tokPtr) { VisitReturn ret = ReturnTrue; bool infinite = false; int index = 0; ProString variable; ProStringList oldVarVal; - ProString it_list = expandVariableReferences(loop->expression(), 0, true).first(); - if (loop->variable().isEmpty()) { + ProString it_list = expandVariableReferences(expression, 0, true).first(); + if (_variable.isEmpty()) { if (it_list != statics.strever) { logMessage(format("Invalid loop expression.")); return ReturnFalse; } it_list = ProString(statics.strforever); } else { - variable = map(loop->variable()); + variable = map(_variable); oldVarVal = valuesDirect(variable); } ProStringList list = valuesDirect(it_list); @@ -1351,7 +1507,7 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(P m_valuemapStack.top()[variable] = ProStringList(val); } - ret = visitProBlock(loop->items()); + ret = visitProBlock(tokPtr); switch (ret) { case ReturnTrue: case ReturnFalse: @@ -1374,15 +1530,13 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProLoop(P return ret; } -void ProFileEvaluator::Private::visitProVariable(ProVariable *var) +void ProFileEvaluator::Private::visitProVariable( + ushort tok, const ProString &varName, const ProString &value) { - m_lineNo = var->lineNumber(); - const ProString &varName = var->variable(); - - if (var->variableOperator() == ProVariable::ReplaceOperator) { // ~= + if (tok == TokReplace) { // ~= // DEFINES ~= s/a/b/?[gqi] - const ProStringList &varVal = expandVariableReferences(var->value(), 0, true); + const ProStringList &varVal = expandVariableReferences(value, 0, true); const QString &val = varVal.at(0).toQString(m_tmp1); if (val.length() < 4 || val.at(0) != QLatin1Char('s')) { logMessage(format("the ~= operator can handle only the s/// function.")); @@ -1415,10 +1569,10 @@ void ProFileEvaluator::Private::visitProVariable(ProVariable *var) replaceInList(&m_filevaluemap[currentProFile()][varName], regexp, replace, global, m_tmp2); } } else { - const ProStringList &varVal = expandVariableReferences(var->value()); - switch (var->variableOperator()) { - default: // ReplaceOperator - cannot happen - case ProVariable::SetOperator: // = + const ProStringList &varVal = expandVariableReferences(value); + switch (tok) { + default: // whatever - cannot happen + case TokAssign: // = if (!m_cumulative) { if (!m_skipLevel) { m_valuemapStack.top()[varName] = varVal; @@ -1430,19 +1584,19 @@ void ProFileEvaluator::Private::visitProVariable(ProVariable *var) m_filevaluemap[currentProFile()][varName] += varVal; } break; - case ProVariable::UniqueAddOperator: // *= + case TokAppendUnique: // *= if (!m_skipLevel || m_cumulative) { insertUnique(&valuesRef(varName), varVal); insertUnique(&m_filevaluemap[currentProFile()][varName], varVal); } break; - case ProVariable::AddOperator: // += + case TokAppend: // += if (!m_skipLevel || m_cumulative) { valuesRef(varName) += varVal; m_filevaluemap[currentProFile()][varName] += varVal; } break; - case ProVariable::RemoveOperator: // -= + case TokRemove: // -= if (!m_cumulative) { if (!m_skipLevel) { removeEach(&valuesRef(varName), varVal); @@ -1456,21 +1610,21 @@ void ProFileEvaluator::Private::visitProVariable(ProVariable *var) } } -ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProCondition(ProCondition *cond) +ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProCondition( + const ProString &text) { - if (cond->text().endsWith(QLatin1Char(')'))) { + const QString &txt = text.toQString(m_tmp1); + if (txt.endsWith(QLatin1Char(')'))) { if (!m_cumulative && m_skipLevel) return ReturnTrue; - int lparen = cond->text().indexOf(QLatin1Char('(')); - ProString text(cond->text()); + int lparen = txt.indexOf(QLatin1Char('(')); ProString arguments = text.mid(lparen + 1, text.size() - lparen - 2); ProString funcName = text.left(lparen).trimmed(); - m_lineNo = cond->lineNumber(); return evaluateConditionalFunction(funcName, arguments); } else { if (m_skipLevel) return ReturnTrue; - return returnBool(isActiveConfig(cond->text(), true)); + return returnBool(isActiveConfig(txt, true)); } } @@ -1595,11 +1749,7 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(P } m_valuemapStack.top() = m_option->base_valuemap; - - clearFunctions(&m_functionDefs); m_functionDefs = m_option->base_functions; - refFunctions(&m_functionDefs.testFunctions); - refFunctions(&m_functionDefs.replaceFunctions); ProStringList &tgt = m_valuemapStack.top()[ProString("TARGET")]; if (tgt.isEmpty()) @@ -1613,7 +1763,9 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::visitProFile(P } } - visitProBlock(pro->items()); + m_stringStack.push(pro->items()); + visitProBlock((const ushort *)pro->items().constData()); + m_stringStack.pop(); if (m_profileStack.count() == 1) { if (m_parsePreAndPostFiles) { @@ -2119,16 +2271,18 @@ QList<ProStringList> ProFileEvaluator::Private::prepareFunctionArgs(const ProStr } ProStringList ProFileEvaluator::Private::evaluateFunction( - ProFunctionDef *funcPtr, const QList<ProStringList> &argumentsList, bool *ok) + const FunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok) { bool oki; ProStringList ret; + const ushort *tokPtr = (const ushort *)func.string.constData() + func.offset; if (m_valuemapStack.count() >= 100) { errorMessage(format("ran into infinite recursion (depth > 100).")); oki = false; } else { m_valuemapStack.push(QHash<ProString, ProStringList>()); + m_stringStack.push(func.string); int loopLevel = m_loopLevel; m_loopLevel = 0; @@ -2138,11 +2292,12 @@ ProStringList ProFileEvaluator::Private::evaluateFunction( m_valuemapStack.top()[ProString(QString::number(i+1))] = argumentsList[i]; } m_valuemapStack.top()[statics.strARGS] = args; - oki = (visitProBlock(funcPtr->items()) != ReturnFalse); // True || Return + oki = (visitProBlock(tokPtr) != ReturnFalse); // True || Return ret = m_returnValue; m_returnValue.clear(); m_loopLevel = loopLevel; + m_stringStack.pop(); m_valuemapStack.pop(); } if (ok) @@ -2155,8 +2310,10 @@ ProStringList ProFileEvaluator::Private::evaluateFunction( ProStringList ProFileEvaluator::Private::evaluateExpandFunction( const ProString &func, const ProString &arguments) { - if (ProFunctionDef *funcPtr = m_functionDefs.replaceFunctions.value(func, 0)) - return evaluateFunction(funcPtr, prepareFunctionArgs(arguments), 0); + QHash<ProString, FunctionDef>::ConstIterator it = + m_functionDefs.replaceFunctions.constFind(func); + if (it != m_functionDefs.replaceFunctions.constEnd()) + return evaluateFunction(*it, prepareFunctionArgs(arguments), 0); //why don't the builtin functions just use args_list? --Sam int pos = 0; @@ -2542,9 +2699,11 @@ ProStringList ProFileEvaluator::Private::evaluateExpandFunction( ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateConditionalFunction( const ProString &function, const ProString &arguments) { - if (ProFunctionDef *funcPtr = m_functionDefs.testFunctions.value(function, 0)) { + QHash<ProString, FunctionDef>::ConstIterator it = + m_functionDefs.testFunctions.constFind(function); + if (it != m_functionDefs.testFunctions.constEnd()) { bool ok; - ProStringList ret = evaluateFunction(funcPtr, prepareFunctionArgs(arguments), &ok); + ProStringList ret = evaluateFunction(*it, prepareFunctionArgs(arguments), &ok); if (ok) { if (ret.isEmpty()) { return ReturnTrue; @@ -2658,12 +2817,13 @@ ProFileEvaluator::Private::VisitReturn ProFileEvaluator::Private::evaluateCondit case T_REQUIRES: #endif case T_EVAL: { - ProItem *pro; + QString pro; QString buf = args.join(QLatin1String(" ")); if (!readInternal(&pro, buf, (ushort*)buf.data())) return ReturnFalse; - VisitReturn ret = visitProBlock(pro); - ProItem::disposeItems(pro); + m_stringStack.push(pro); + VisitReturn ret = visitProBlock((const ushort *)pro.constData()); + m_stringStack.pop(); return ret; } case T_BREAK: @@ -3354,12 +3514,8 @@ bool ProFileEvaluator::Private::evaluateFileInto( if (!visitor.d->evaluateFile(fileName)) return false; *values = visitor.d->m_valuemapStack.top(); - if (funcs) { + if (funcs) *funcs = visitor.d->m_functionDefs; - // So they are not unref'd - visitor.d->m_functionDefs.testFunctions.clear(); - visitor.d->m_functionDefs.replaceFunctions.clear(); - } return true; } diff --git a/src/shared/proparser/profileevaluator.h b/src/shared/proparser/profileevaluator.h index 7bc6fc9dd12..58f682e6a9c 100644 --- a/src/shared/proparser/profileevaluator.h +++ b/src/shared/proparser/profileevaluator.h @@ -50,9 +50,14 @@ class ProFileEvaluator class Private; public: + struct FunctionDef { + QString string; + int offset; + }; + struct FunctionDefs { - QHash<ProString, ProFunctionDef *> testFunctions; - QHash<ProString, ProFunctionDef *> replaceFunctions; + QHash<ProString, FunctionDef> testFunctions; + QHash<ProString, FunctionDef> replaceFunctions; }; enum TemplateType { @@ -109,6 +114,8 @@ private: friend class ProFileCache; }; +Q_DECLARE_TYPEINFO(ProFileEvaluator::FunctionDef, Q_MOVABLE_TYPE); + class ProFileCache { public: diff --git a/src/shared/proparser/proitems.cpp b/src/shared/proparser/proitems.cpp index 6909c5c1c47..9abb554cc29 100644 --- a/src/shared/proparser/proitems.cpp +++ b/src/shared/proparser/proitems.cpp @@ -37,7 +37,7 @@ QT_BEGIN_NAMESPACE using namespace ProStringConstants; // from qhash.cpp -static uint hash(const QChar *p, int n) +uint ProString::hash(const QChar *p, int n) { uint h = 0; @@ -86,6 +86,22 @@ ProString::ProString(const char *str, OmitPreHashing) : { } +ProString::ProString(const QString &str, int offset, int length) : + m_string(str), m_offset(offset), m_length(length) +{ + updatedHash(); +} + +ProString::ProString(const QString &str, int offset, int length, uint hash) : + m_string(str), m_offset(offset), m_length(length), m_hash(hash) +{ +} + +ProString::ProString(const QString &str, int offset, int length, ProStringConstants::OmitPreHashing) : + m_string(str), m_offset(offset), m_length(length), m_hash(0x80000000) +{ +} + uint ProString::updatedHash() const { return (m_hash = hash(m_string.constData() + m_offset, m_length)); @@ -231,44 +247,8 @@ void ProStringList::removeDuplicates() erase(begin() + j, end()); } -void ProItem::disposeItems(ProItem *nitm) -{ - for (ProItem *itm; (itm = nitm); ) { - nitm = itm->next(); - switch (itm->kind()) { - case ProItem::ConditionKind: delete static_cast<ProCondition *>(itm); break; - case ProItem::VariableKind: delete static_cast<ProVariable *>(itm); break; - case ProItem::BranchKind: delete static_cast<ProBranch *>(itm); break; - case ProItem::LoopKind: delete static_cast<ProLoop *>(itm); break; - case ProItem::FunctionDefKind: static_cast<ProFunctionDef *>(itm)->deref(); break; - case ProItem::OpNotKind: - case ProItem::OpAndKind: - case ProItem::OpOrKind: - delete itm; - break; - } - } -} - -ProBranch::~ProBranch() -{ - disposeItems(m_thenItems); - disposeItems(m_elseItems); -} - -ProLoop::~ProLoop() -{ - disposeItems(m_proitems); -} - -ProFunctionDef::~ProFunctionDef() -{ - disposeItems(m_proitems); -} - ProFile::ProFile(const QString &fileName) - : m_proitems(0), - m_refCount(1), + : m_refCount(1), m_fileName(fileName) { int nameOff = fileName.lastIndexOf(QLatin1Char('/')); @@ -279,7 +259,6 @@ ProFile::ProFile(const QString &fileName) ProFile::~ProFile() { - ProItem::disposeItems(m_proitems); } QT_END_NAMESPACE diff --git a/src/shared/proparser/proitems.h b/src/shared/proparser/proitems.h index c92771d8336..6da0d80f14c 100644 --- a/src/shared/proparser/proitems.h +++ b/src/shared/proparser/proitems.h @@ -62,6 +62,9 @@ public: ProString(const QString &str, ProStringConstants::OmitPreHashing); explicit ProString(const char *str); ProString(const char *str, ProStringConstants::OmitPreHashing); + ProString(const QString &str, int offset, int length); + ProString(const QString &str, int offset, int length, uint hash); + ProString(const QString &str, int offset, int length, ProStringConstants::OmitPreHashing); QString toQString() const; QString &toQString(QString &tmp) const; bool operator==(const ProString &other) const; @@ -79,6 +82,8 @@ public: ProString trimmed() const; void clear() { m_string.clear(); m_length = 0; } + static uint hash(const QChar *p, int n); + private: QString m_string; int m_offset, m_length; @@ -104,130 +109,38 @@ public: void removeDuplicates(); }; -class ProItem -{ -public: - enum ProItemKind { - ConditionKind, - OpNotKind, - OpAndKind, - OpOrKind, - VariableKind, - BranchKind, - LoopKind, - FunctionDefKind - }; - - ProItem(ProItemKind kind) : m_kind(kind), m_lineNumber(0) {} - - ProItemKind kind() const { return m_kind; } - - int lineNumber() const { return m_lineNumber; } - void setLineNumber(int lineNumber) { m_lineNumber = lineNumber; } - - ProItem *next() const { return m_next; } - ProItem **nextRef() { return &m_next; } - - static void disposeItems(ProItem *nitm); - -private: - ProItem *m_next; - ProItemKind m_kind; - int m_lineNumber; -}; - -class ProVariable : public ProItem -{ -public: - enum VariableOperator { - AddOperator = 0, - RemoveOperator = 1, - ReplaceOperator = 2, - SetOperator = 3, - UniqueAddOperator = 4 - }; - - ProVariable(const ProString &name) : ProItem(VariableKind), - m_variableKind(SetOperator), m_variable(name) {} - void setVariableOperator(VariableOperator variableKind) { m_variableKind = variableKind; } - VariableOperator variableOperator() const { return m_variableKind; } - ProString variable() const { return m_variable; } - void setValue(const ProString &value) { m_value = value; } - ProString value() const { return m_value; } - -private: - VariableOperator m_variableKind; - ProString m_variable; - ProString m_value; -}; - -class ProCondition : public ProItem -{ -public: - explicit ProCondition(const QString &text) : ProItem(ConditionKind), m_text(text) {} - QString text() const { return m_text; } - -private: - QString m_text; -}; - -class ProBranch : public ProItem -{ -public: - ProBranch() : ProItem(BranchKind) {} - ~ProBranch(); - - ProItem *thenItems() const { return m_thenItems; } - ProItem **thenItemsRef() { return &m_thenItems; } - ProItem *elseItems() const { return m_elseItems; } - ProItem **elseItemsRef() { return &m_elseItems; } - -private: - ProItem *m_thenItems; - ProItem *m_elseItems; -}; - -class ProLoop : public ProItem -{ -public: - ProLoop(const QString &var, const QString &expr) - : ProItem(LoopKind), m_variable(ProString(var)), - m_expression(ProString(expr, ProStringConstants::NoHash)) {} - ~ProLoop(); - - ProString variable() const { return m_variable; } - ProString expression() const { return m_expression; } - ProItem *items() const { return m_proitems; } - ProItem **itemsRef() { return &m_proitems; } - -private: - ProString m_variable; - ProString m_expression; - ProItem *m_proitems; -}; - -class ProFunctionDef : public ProItem -{ -public: - enum FunctionType { TestFunction, ReplaceFunction }; - - ProFunctionDef(const QString &name, FunctionType type) - : ProItem(FunctionDefKind), m_name(ProString(name)), m_type(type), m_refCount(1) {} - ~ProFunctionDef(); - - ProString name() const { return m_name; } - FunctionType type() const { return m_type; } - ProItem *items() const { return m_proitems; } - ProItem **itemsRef() { return &m_proitems; } - - void ref() { m_refCount.ref(); } - void deref() { if (!m_refCount.deref()) delete this; } - -private: - ProString m_name; - FunctionType m_type; - ProItemRefCount m_refCount; - ProItem *m_proitems; +// These token definitions affect both ProFileEvaluator and ProWriter +enum ProToken { + TokTerminator = 0, // end of stream (possibly not included in length; must be zero) + TokLine, // line marker: // +1 (2-nl) to 1st token of each line + // - line (1) + TokAssign, // variable = // "A=":2 => 1+4+2=7 (8) + TokAppend, // variable += // "A+=":3 => 1+4+2=7 (8) + TokAppendUnique, // variable *= // "A*=":3 => 1+4+2=7 (8) + TokRemove, // variable -= // "A-=":3 => 1+4+2=7 (8) + TokReplace, // variable ~= // "A~=":3 => 1+4+2=7 (8) + // - variable name: hash (2), length (1), chars (length) + // - expression: length (2), chars (length) + TokCondition, // CONFIG test: // "A":1 => 1+2=3 (4) + // - test name: lenght (1), chars (length) + TokNot, // '!' operator + TokAnd, // ':' operator + TokOr, // '|' operator + TokBranch, // branch point: // "X:A=":4 => [5]+1+4+1+1+[7]=19 (20) + // - then block length (2) + // - then block + TokTerminator (then block length) + // - else block length (2) + // - else block + TokTerminator (else block length) + TokForLoop, // for loop: // "for(A,B)":8 => 1+4+3+2+1=11 (12) + // - variable name: hash (2), length (1), chars (length) + // - expression: length (2), chars (length) + // - body length (2) + // - body + TokTerminator (body length) + TokTestDef, // test function definition: // "defineTest(A):":14 => 1+4+2+1=8 (9) + TokReplaceDef, // replace function definition: // "defineReplace(A):":17 => 1+4+2+1=8 (9) + // - function name: hash (2), length (1), chars (length) + // - body length (2) + // - body + TokTerminator (body length) }; class ProFile @@ -239,15 +152,15 @@ public: QString displayFileName() const { return m_displayFileName; } QString fileName() const { return m_fileName; } QString directoryName() const { return m_directoryName; } - ProItem *items() const { return m_proitems; } - ProItem **itemsRef() { return &m_proitems; } + const QString &items() const { return m_proitems; } + QString *itemsRef() { return &m_proitems; } void ref() { m_refCount.ref(); } void deref() { if (!m_refCount.deref()) delete this; } private: - ProItem *m_proitems; ProItemRefCount m_refCount; + QString m_proitems; QString m_fileName; QString m_displayFileName; QString m_directoryName; diff --git a/src/shared/proparser/prowriter.cpp b/src/shared/proparser/prowriter.cpp index c72f88bbe9f..3a238b6b60b 100644 --- a/src/shared/proparser/prowriter.cpp +++ b/src/shared/proparser/prowriter.cpp @@ -35,20 +35,98 @@ using namespace Qt4ProjectManager::Internal; +static uint getBlockLen(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + len |= (uint)*tokPtr++ << 16; + return len; +} + +static QString &getHashStr(const ushort *&tokPtr, QString &tmp) +{ + tokPtr += 2; // ignore hash + uint len = *tokPtr++; + tmp.setRawData((const QChar *)tokPtr, len); + tokPtr += len; + return tmp; +} + +static void skipStr(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + tokPtr += len; +} + +static void skipHashStr(const ushort *&tokPtr) +{ + tokPtr += 2; + uint len = *tokPtr++; + tokPtr += len; +} + +static void skipLongStr(const ushort *&tokPtr) +{ + uint len = getBlockLen(tokPtr); + tokPtr += len; +} + +static void skipBlock(const ushort *&tokPtr) +{ + uint len = getBlockLen(tokPtr); + tokPtr += len; +} + +static void skipToken(ushort tok, const ushort *&tokPtr, int &lineNo) +{ + switch (tok) { + case TokLine: + lineNo = *tokPtr++; + break; + case TokAssign: + case TokAppend: + case TokAppendUnique: + case TokRemove: + case TokReplace: + skipHashStr(tokPtr); + skipLongStr(tokPtr); + break; + case TokBranch: + skipBlock(tokPtr); + skipBlock(tokPtr); + break; + case TokForLoop: + skipHashStr(tokPtr); + skipLongStr(tokPtr); + skipBlock(tokPtr); + break; + case TokTestDef: + case TokReplaceDef: + skipHashStr(tokPtr); + skipBlock(tokPtr); + break; + case TokNot: + case TokAnd: + case TokOr: + break; + case TokCondition: + skipStr(tokPtr); + break; + default: Q_ASSERT_X(false, "skipToken", "unexpected item type"); + } +} + void ProWriter::addFiles(ProFile *profile, QStringList *lines, const QDir &proFileDir, const QStringList &filePaths, const QString &var) { // Check if variable item exists as child of root item - for (ProItem *item = profile->items(); item; item = item->next()) { - if (item->kind() == ProItem::VariableKind) { - ProVariable *proVar = static_cast<ProVariable*>(item); - if (var == proVar->variable().toQString() - && proVar->variableOperator() != ProVariable::RemoveOperator - && proVar->variableOperator() != ProVariable::ReplaceOperator) { - - int lineNo = proVar->lineNumber() - 1; - for (; lineNo < lines->count(); lineNo++) { + const ushort *tokPtr = (const ushort *)profile->items().constData(); + int lineNo = 0; + QString tmp; + while (ushort tok = *tokPtr++) { + if (tok == TokAssign || tok == TokAppend || tok == TokAppendUnique) { + if (var == getHashStr(tokPtr, tmp)) { + for (--lineNo; lineNo < lines->count(); lineNo++) { QString line = lines->at(lineNo); int idx = line.indexOf(QLatin1Char('#')); if (idx >= 0) @@ -74,6 +152,9 @@ void ProWriter::addFiles(ProFile *profile, QStringList *lines, lines->insert(lineNo, added); return; } + skipLongStr(tokPtr); + } else { + skipToken(tok, tokPtr, lineNo); } } @@ -84,17 +165,25 @@ void ProWriter::addFiles(ProFile *profile, QStringList *lines, *lines << added; } -static void findProVariables(ProItem *item, const QStringList &vars, - QList<ProVariable *> *proVars) +static void findProVariables(const ushort *tokPtr, const QStringList &vars, + QList<int> *proVars) { - for (; item; item = item->next()) { - if (item->kind() == ProItem::BranchKind) { - findProVariables(static_cast<ProBranch*>(item)->thenItems(), vars, proVars); - findProVariables(static_cast<ProBranch*>(item)->elseItems(), vars, proVars); - } else if (item->kind() == ProItem::VariableKind) { - ProVariable *proVar = static_cast<ProVariable*>(item); - if (vars.contains(proVar->variable().toQString())) - *proVars << proVar; + int lineNo = 0; + QString tmp; + while (ushort tok = *tokPtr++) { + if (tok == TokBranch) { + uint blockLen = getBlockLen(tokPtr); + findProVariables(tokPtr, vars, proVars); + tokPtr += blockLen; + blockLen = getBlockLen(tokPtr); + findProVariables(tokPtr, vars, proVars); + tokPtr += blockLen; + } else if (tok == TokAssign || tok == TokAppend || tok == TokAppendUnique) { + if (vars.contains(getHashStr(tokPtr, tmp))) + *proVars << lineNo; + skipLongStr(tokPtr); + } else { + skipToken(tok, tokPtr, lineNo); } } } @@ -105,8 +194,8 @@ QStringList ProWriter::removeFiles(ProFile *profile, QStringList *lines, { QStringList notChanged = filePaths; - QList<ProVariable *> proVars; - findProVariables(profile->items(), vars, &proVars); + QList<int> varLines; + findProVariables((const ushort *)profile->items().constData(), vars, &varLines); // This is a tad stupid - basically, it can remove only entries which // the above code added. @@ -116,109 +205,105 @@ QStringList ProWriter::removeFiles(ProFile *profile, QStringList *lines, // This code expects proVars to be sorted by the variables' appearance in the file. int delta = 1; - foreach (ProVariable *proVar, proVars) { - if (proVar->variableOperator() != ProVariable::RemoveOperator - && proVar->variableOperator() != ProVariable::ReplaceOperator) { - - bool first = true; - int lineNo = proVar->lineNumber() - delta; - typedef QPair<int, int> ContPos; - QList<ContPos> contPos; - while (lineNo < lines->count()) { - QString &line = (*lines)[lineNo]; - int lineLen = line.length(); - bool killed = false; - bool saved = false; - int idx = line.indexOf(QLatin1Char('#')); - if (idx >= 0) - lineLen = idx; - QChar *chars = line.data(); - forever { - if (!lineLen) { - if (idx >= 0) - goto nextLine; - goto nextVar; - } - QChar c = chars[lineLen - 1]; - if (c != QLatin1Char(' ') && c != QLatin1Char('\t')) - break; - lineLen--; - } - { - int contCol = -1; - if (chars[lineLen - 1] == QLatin1Char('\\')) - contCol = --lineLen; - int colNo = 0; - if (first) { - colNo = line.indexOf(QLatin1Char('=')) + 1; - first = false; - saved = true; - } - while (colNo < lineLen) { - QChar c = chars[colNo]; - if (c == QLatin1Char(' ') || c == QLatin1Char('\t')) { - colNo++; - continue; - } - int varCol = colNo; - while (colNo < lineLen) { - QChar c = chars[colNo]; - if (c == QLatin1Char(' ') || c == QLatin1Char('\t')) - break; - colNo++; - } - QString fn = line.mid(varCol, colNo - varCol); - if (relativeFilePaths.contains(fn)) { - notChanged.removeOne(QDir::cleanPath(proFileDir.absoluteFilePath(fn))); - if (colNo < lineLen) - colNo++; - else if (varCol) - varCol--; - int len = colNo - varCol; - colNo = varCol; - line.remove(varCol, len); - lineLen -= len; - contCol -= len; - idx -= len; - if (idx >= 0) - line.insert(idx, QLatin1String("# ") + fn + QLatin1Char(' ')); - killed = true; - } else { - saved = true; - } - } - if (saved) { - // Entries remained - contPos.clear(); - } else if (killed) { - // Entries existed, but were all removed - if (contCol < 0) { - // This is the last line, so clear continuations leading to it - foreach (const ContPos &pos, contPos) { - QString &bline = (*lines)[pos.first]; - bline.remove(pos.second, 1); - if (pos.second == bline.length()) - while (bline.endsWith(QLatin1Char(' ')) - || bline.endsWith(QLatin1Char('\t'))) - bline.chop(1); - } - contPos.clear(); - } - if (idx < 0) { - // Not even a comment stayed behind, so zap the line - lines->removeAt(lineNo); - delta++; - continue; - } - } - if (contCol >= 0) - contPos.append(qMakePair(lineNo, contCol)); - } - nextLine: - lineNo++; - } - nextVar: ; - } + foreach (int ln, varLines) { + bool first = true; + int lineNo = ln - delta; + typedef QPair<int, int> ContPos; + QList<ContPos> contPos; + while (lineNo < lines->count()) { + QString &line = (*lines)[lineNo]; + int lineLen = line.length(); + bool killed = false; + bool saved = false; + int idx = line.indexOf(QLatin1Char('#')); + if (idx >= 0) + lineLen = idx; + QChar *chars = line.data(); + forever { + if (!lineLen) { + if (idx >= 0) + goto nextLine; + goto nextVar; + } + QChar c = chars[lineLen - 1]; + if (c != QLatin1Char(' ') && c != QLatin1Char('\t')) + break; + lineLen--; + } + { + int contCol = -1; + if (chars[lineLen - 1] == QLatin1Char('\\')) + contCol = --lineLen; + int colNo = 0; + if (first) { + colNo = line.indexOf(QLatin1Char('=')) + 1; + first = false; + saved = true; + } + while (colNo < lineLen) { + QChar c = chars[colNo]; + if (c == QLatin1Char(' ') || c == QLatin1Char('\t')) { + colNo++; + continue; + } + int varCol = colNo; + while (colNo < lineLen) { + QChar c = chars[colNo]; + if (c == QLatin1Char(' ') || c == QLatin1Char('\t')) + break; + colNo++; + } + QString fn = line.mid(varCol, colNo - varCol); + if (relativeFilePaths.contains(fn)) { + notChanged.removeOne(QDir::cleanPath(proFileDir.absoluteFilePath(fn))); + if (colNo < lineLen) + colNo++; + else if (varCol) + varCol--; + int len = colNo - varCol; + colNo = varCol; + line.remove(varCol, len); + lineLen -= len; + contCol -= len; + idx -= len; + if (idx >= 0) + line.insert(idx, QLatin1String("# ") + fn + QLatin1Char(' ')); + killed = true; + } else { + saved = true; + } + } + if (saved) { + // Entries remained + contPos.clear(); + } else if (killed) { + // Entries existed, but were all removed + if (contCol < 0) { + // This is the last line, so clear continuations leading to it + foreach (const ContPos &pos, contPos) { + QString &bline = (*lines)[pos.first]; + bline.remove(pos.second, 1); + if (pos.second == bline.length()) + while (bline.endsWith(QLatin1Char(' ')) + || bline.endsWith(QLatin1Char('\t'))) + bline.chop(1); + } + contPos.clear(); + } + if (idx < 0) { + // Not even a comment stayed behind, so zap the line + lines->removeAt(lineNo); + delta++; + continue; + } + } + if (contCol >= 0) + contPos.append(qMakePair(lineNo, contCol)); + } + nextLine: + lineNo++; + } + nextVar: ; } return notChanged; } -- GitLab