diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp index 90c8051c0cdc5ba983849524218a993fc837b7d1..5892eed499d1bc2bfbeef475416ff1772215bc95 100644 --- a/src/shared/proparser/profileevaluator.cpp +++ b/src/shared/proparser/profileevaluator.cpp @@ -229,7 +229,10 @@ public: void visitProOperator(ProOperator *oper); void visitProCondition(ProCondition *condition); - QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; } + QHash<QString, QStringList> *findValues(const QString &variableName, + QHash<QString, QStringList>::Iterator *it); + QStringList &valuesRef(const QString &variableName); + QStringList valuesDirect(const QString &variableName) const; QStringList values(const QString &variableName) const; QString propertyValue(const QString &val, bool complain = true) const; @@ -286,8 +289,6 @@ public: }; QStack<ProLoop> m_loopStack; - QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii. - QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file QString m_outputDir; int m_listCount; @@ -295,7 +296,8 @@ public: QString m_definingFunc; FunctionDefs m_functionDefs; QStringList m_returnValue; - QStack<QHash<QString, QStringList> > m_valuemapStack; + QStack<QHash<QString, QStringList> > m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii. + QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file QStringList m_addUserConfigCmdArgs; QStringList m_removeUserConfigCmdArgs; @@ -361,6 +363,7 @@ static struct { QHash<QString, int> functions; QHash<QString, int> varList; QRegExp reg_variableName; + QStringList fakeValue; } statics; void ProFileEvaluator::Private::initStatics() @@ -392,6 +395,8 @@ void ProFileEvaluator::Private::initStatics() statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)")); statics.reg_variableName.setMinimal(true); + statics.fakeValue.detach(); // It has to have a unique begin() value + static const struct { const char * const name; const ExpandFunc func; @@ -495,6 +500,7 @@ ProFileEvaluator::Private::Private(ProFileEvaluator *q_, ProFileOption *option) m_skipLevel = 0; m_definingFunc.clear(); m_listCount = 0; + m_valuemapStack.push(QHash<QString, QStringList>()); } ProFileEvaluator::Private::~Private() @@ -1077,7 +1083,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration() if (loop.infinite) { if (!loop.variable.isEmpty()) - m_valuemap[loop.variable] = QStringList(QString::number(loop.index++)); + m_valuemapStack.top()[loop.variable] = QStringList(QString::number(loop.index++)); if (loop.index > 1000) { errorMessage(format("ran into infinite loop (> 1000 iterations).")); return ProItem::ReturnFalse; @@ -1089,7 +1095,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration() return ProItem::ReturnFalse; val = loop.list.at(loop.index++); } while (val.isEmpty()); // stupid, but qmake is like that - m_valuemap[loop.variable] = QStringList(val); + m_valuemapStack.top()[loop.variable] = QStringList(val); } return ProItem::ReturnTrue; } @@ -1097,7 +1103,7 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitProLoopIteration() void ProFileEvaluator::Private::visitProLoopCleanup() { ProLoop &loop = m_loopStack.top(); - m_valuemap[loop.variable] = loop.oldVarVal; + m_valuemapStack.top()[loop.variable] = loop.oldVarVal; m_loopStack.pop_back(); } @@ -1138,7 +1144,7 @@ void ProFileEvaluator::Private::visitProVariable(ProVariable *var) if (!m_skipLevel || m_cumulative) { // We could make a union of modified and unmodified values, // but this will break just as much as it fixes, so leave it as is. - replaceInList(&m_valuemap[varName], regexp, replace, global); + replaceInList(&valuesRef(varName), regexp, replace, global); replaceInList(&m_filevaluemap[currentProFile()][varName], regexp, replace, global); } } else { @@ -1150,31 +1156,31 @@ void ProFileEvaluator::Private::visitProVariable(ProVariable *var) case ProVariable::SetOperator: // = if (!m_cumulative) { if (!m_skipLevel) { - m_valuemap[varName] = varVal; + m_valuemapStack.top()[varName] = varVal; m_filevaluemap[currentProFile()][varName] = varVal; } } else { // We are greedy for values. - m_valuemap[varName] += varVal; + valuesRef(varName) += varVal; m_filevaluemap[currentProFile()][varName] += varVal; } break; case ProVariable::UniqueAddOperator: // *= if (!m_skipLevel || m_cumulative) { - insertUnique(&m_valuemap[varName], varVal); + insertUnique(&valuesRef(varName), varVal); insertUnique(&m_filevaluemap[currentProFile()][varName], varVal); } break; case ProVariable::AddOperator: // += if (!m_skipLevel || m_cumulative) { - m_valuemap[varName] += varVal; + valuesRef(varName) += varVal; m_filevaluemap[currentProFile()][varName] += varVal; } break; case ProVariable::RemoveOperator: // -= if (!m_cumulative) { if (!m_skipLevel) { - removeEach(&m_valuemap[varName], varVal); + removeEach(&valuesRef(varName), varVal); removeEach(&m_filevaluemap[currentProFile()][varName], varVal); } } else { @@ -1307,18 +1313,18 @@ ProItem::ProItemReturn ProFileEvaluator::Private::visitProFile(ProFile *pro) &m_option->base_valuemap, &m_option->base_functions); } - m_valuemap = m_option->base_valuemap; + m_valuemapStack.top() = m_option->base_valuemap; clearFunctions(&m_functionDefs); m_functionDefs = m_option->base_functions; refFunctions(&m_functionDefs.testFunctions); refFunctions(&m_functionDefs.replaceFunctions); - QStringList &tgt = m_valuemap[QLatin1String("TARGET")]; + QStringList &tgt = m_valuemapStack.top()[QLatin1String("TARGET")]; if (tgt.isEmpty()) tgt.append(QFileInfo(pro->fileName()).baseName()); - QStringList &tmp = m_valuemap[QLatin1String("CONFIG")]; + QStringList &tmp = m_valuemapStack.top()[QLatin1String("CONFIG")]; tmp.append(m_addUserConfigCmdArgs); foreach (const QString &remove, m_removeUserConfigCmdArgs) tmp.removeAll(remove); @@ -1817,7 +1823,7 @@ bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex return true; // CONFIG variable - foreach (const QString &configValue, m_valuemap.value(statics.strCONFIG)) { + foreach (const QString &configValue, valuesDirect(statics.strCONFIG)) { if (re.exactMatch(configValue)) return true; } @@ -1827,7 +1833,7 @@ bool ProFileEvaluator::Private::isActiveConfig(const QString &config, bool regex return true; // CONFIG variable - foreach (const QString &configValue, m_valuemap.value(statics.strCONFIG)) { + foreach (const QString &configValue, valuesDirect(statics.strCONFIG)) { if (configValue == config) return true; } @@ -1855,19 +1861,19 @@ QStringList ProFileEvaluator::Private::evaluateFunction( oki = false; } else { State sts = m_sts; - m_valuemapStack.push(m_valuemap); + m_valuemapStack.push(QHash<QString, QStringList>()); QStringList args; for (int i = 0; i < argumentsList.count(); ++i) { args += argumentsList[i]; - m_valuemap[QString::number(i+1)] = argumentsList[i]; + m_valuemapStack.top()[QString::number(i+1)] = argumentsList[i]; } - m_valuemap[statics.strARGS] = args; + m_valuemapStack.top()[statics.strARGS] = args; oki = (visitProBlock(funcPtr) != ProItem::ReturnFalse); // True || Return ret = m_returnValue; m_returnValue.clear(); - m_valuemap = m_valuemapStack.pop(); + m_valuemapStack.pop(); m_sts = sts; } if (ok) @@ -2080,7 +2086,7 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun QStringList lst; foreach (const QString &arg, args) lst += split_value_list(arg); - m_valuemap[tmp] = lst; + m_valuemapStack.top()[tmp] = lst; break; } case E_FIND: if (args.count() != 2) { @@ -2323,8 +2329,21 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( logMessage(format("export(variable) requires one argument.")); return ProItem::ReturnFalse; } - for (int i = 0; i < m_valuemapStack.size(); ++i) - m_valuemapStack[i][args[0]] = m_valuemap.value(args[0]); + for (int i = m_valuemapStack.size(); --i > 0; ) { + QHash<QString, QStringList>::Iterator it = m_valuemapStack[i].find(args.at(0)); + if (it != m_valuemapStack.at(i).end()) { + if (it->constBegin() == statics.fakeValue.constBegin()) { + // This is stupid, but qmake doesn't propagate deletions + m_valuemapStack[0][args.at(0)] = QStringList(); + } else { + m_valuemapStack[0][args.at(0)] = *it; + } + m_valuemapStack[i].erase(it); + while (--i) + m_valuemapStack[i].remove(args.at(0)); + break; + } + } return ProItem::ReturnTrue; case T_INFILE: if (args.count() < 2 || args.count() > 3) { @@ -2383,11 +2402,11 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( it_list = statics.strforever; } else { loop.variable = args[0]; - loop.oldVarVal = m_valuemap.value(loop.variable); + loop.oldVarVal = valuesDirect(loop.variable); doVariableReplace(&args[1]); it_list = args[1]; } - loop.list = m_valuemap.value(it_list); + loop.list = valuesDirect(it_list); if (loop.list.isEmpty()) { if (it_list == statics.strforever) { loop.infinite = true; @@ -2621,10 +2640,14 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( logMessage(format("%1(variable) requires one argument.").arg(function)); return ProItem::ReturnFalse; } - QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]); - if (it == m_valuemap.end()) + QHash<QString, QStringList> *hsh; + QHash<QString, QStringList>::Iterator it; + if (!(hsh = findValues(args.at(0), &it))) return ProItem::ReturnFalse; - it->clear(); + if (hsh == &m_valuemapStack.top()) + it->clear(); + else + m_valuemapStack.top()[args.at(0)].clear(); return ProItem::ReturnTrue; } case T_UNSET: { @@ -2634,10 +2657,16 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( logMessage(format("%1(variable) requires one argument.").arg(function)); return ProItem::ReturnFalse; } - QHash<QString, QStringList>::Iterator it = m_valuemap.find(args[0]); - if (it == m_valuemap.end()) + QHash<QString, QStringList> *hsh; + QHash<QString, QStringList>::Iterator it; + if (!(hsh = findValues(args.at(0), &it))) return ProItem::ReturnFalse; - m_valuemap.erase(it); + if (m_valuemapStack.size() == 1) + hsh->erase(it); + else if (hsh == &m_valuemapStack.top()) + *it = statics.fakeValue; + else + m_valuemapStack.top()[args.at(0)] = statics.fakeValue; return ProItem::ReturnTrue; } case T_INCLUDE: { @@ -2744,6 +2773,50 @@ ProItem::ProItemReturn ProFileEvaluator::Private::evaluateConditionalFunction( } } +QHash<QString, QStringList> *ProFileEvaluator::Private::findValues( + const QString &variableName, QHash<QString, QStringList>::Iterator *rit) +{ + for (int i = m_valuemapStack.size(); --i >= 0; ) { + QHash<QString, QStringList>::Iterator it = m_valuemapStack[i].find(variableName); + if (it != m_valuemapStack[i].end()) { + if (it->constBegin() == statics.fakeValue.constBegin()) + return 0; + *rit = it; + return &m_valuemapStack[i]; + } + } + return 0; +} + +QStringList &ProFileEvaluator::Private::valuesRef(const QString &variableName) +{ + QHash<QString, QStringList>::Iterator it = m_valuemapStack.top().find(variableName); + if (it != m_valuemapStack.top().end()) + return *it; + for (int i = m_valuemapStack.size() - 1; --i >= 0; ) { + QHash<QString, QStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName); + if (it != m_valuemapStack.at(i).constEnd()) { + QStringList &ret = m_valuemapStack.top()[variableName]; + ret = *it; + return ret; + } + } + return m_valuemapStack.top()[variableName]; +} + +QStringList ProFileEvaluator::Private::valuesDirect(const QString &variableName) const +{ + for (int i = m_valuemapStack.size(); --i >= 0; ) { + QHash<QString, QStringList>::ConstIterator it = m_valuemapStack.at(i).constFind(variableName); + if (it != m_valuemapStack.at(i).constEnd()) { + if (it->constBegin() == statics.fakeValue.constBegin()) + break; + return *it; + } + } + return QStringList(); +} + QStringList ProFileEvaluator::Private::values(const QString &variableName) const { QHash<QString, int>::ConstIterator vli = statics.varList.find(variableName); @@ -2934,7 +3007,7 @@ bool ProFileEvaluator::Private::evaluateFeatureFile( cool: // It's beyond me why qmake has this inside this if ... - QStringList &already = m_valuemap[QLatin1String("QMAKE_INTERNAL_INCLUDED_FEATURES")]; + QStringList &already = valuesRef(QLatin1String("QMAKE_INTERNAL_INCLUDED_FEATURES")); if (already.contains(fn)) return true; already.append(fn); @@ -2968,12 +3041,12 @@ bool ProFileEvaluator::Private::evaluateFileInto( visitor.d->m_cumulative = false; visitor.d->m_parsePreAndPostFiles = false; visitor.d->m_verbose = m_verbose; - visitor.d->m_valuemap = *values; + visitor.d->m_valuemapStack.top() = *values; if (funcs) visitor.d->m_functionDefs = *funcs; if (!visitor.d->evaluateFile(fileName)) return false; - *values = visitor.d->m_valuemap; + *values = visitor.d->m_valuemapStack.top(); if (funcs) { *funcs = visitor.d->m_functionDefs; // So they are not unref'd @@ -3033,7 +3106,7 @@ ProFileEvaluator::~ProFileEvaluator() bool ProFileEvaluator::contains(const QString &variableName) const { - return d->m_valuemap.contains(variableName); + return d->m_valuemapStack.top().contains(variableName); } QStringList ProFileEvaluator::values(const QString &variableName) const