qmake: introduce magic bypassNesting() scope

will be needed by configure.

Change-Id: If14e6944fe84767bd67604ecde98076f873749ef
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Oswald Buddenhagen 2016-11-11 11:40:24 +01:00
parent 169a40d511
commit b6b44b368c
5 changed files with 140 additions and 1 deletions

View File

@ -329,6 +329,9 @@ enum ProToken {
// - function name: hash (2), length (1), chars (length)
// - body length (2)
// - body + TokTerminator (body length)
TokBypassNesting, // escape from function local variable scopes:
// - block length (2)
// - block + TokTerminator (block length)
TokMask = 0xff,
TokQuoted = 0x100, // The expression is quoted => join expanded stringlist
TokNewStr = 0x200 // Next stringlist element

View File

@ -594,6 +594,24 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
tokPtr += blockLen;
okey = true, or_op = false; // force next evaluation
break;
case TokBypassNesting:
blockLen = getBlockLen(tokPtr);
if ((m_cumulative || okey != or_op) && blockLen) {
ProValueMapStack savedValuemapStack = m_valuemapStack;
m_valuemapStack.clear();
m_valuemapStack.append(savedValuemapStack.takeFirst());
traceMsg("visiting nesting-bypassing block");
ret = visitProBlock(tokPtr);
traceMsg("visited nesting-bypassing block");
savedValuemapStack.prepend(m_valuemapStack.first());
m_valuemapStack = savedValuemapStack;
} else {
traceMsg("skipped nesting-bypassing block");
ret = ReturnTrue;
}
tokPtr += blockLen;
okey = true, or_op = false; // force next evaluation
break;
case TokTestDef:
case TokReplaceDef:
if (m_cumulative || okey != or_op) {

View File

@ -118,6 +118,7 @@ static struct {
QString strfor;
QString strdefineTest;
QString strdefineReplace;
QString strbypassNesting;
QString stroption;
QString strreturn;
QString strnext;
@ -141,6 +142,7 @@ void QMakeParser::initialize()
statics.strfor = QLatin1String("for");
statics.strdefineTest = QLatin1String("defineTest");
statics.strdefineReplace = QLatin1String("defineReplace");
statics.strbypassNesting = QLatin1String("bypassNesting");
statics.stroption = QLatin1String("option");
statics.strreturn = QLatin1String("return");
statics.strnext = QLatin1String("next");
@ -1157,6 +1159,25 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg
}
parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
return;
} else if (m_tmp == statics.strbypassNesting) {
if (*uce != TokFuncTerminator) {
bogusTest(tokPtr, fL1S("%1() requires zero arguments.").arg(m_tmp));
return;
}
if (!(m_blockstack.top().nest & NestFunction)) {
bogusTest(tokPtr, fL1S("Unexpected %1().").arg(m_tmp));
return;
}
if (m_invert) {
bogusTest(tokPtr, fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp));
return;
}
flushScopes(tokPtr);
putLineMarker(tokPtr);
putOperator(tokPtr);
putTok(tokPtr, TokBypassNesting);
enterScope(tokPtr, true, StCtrl);
return;
} else if (m_tmp == statics.strreturn) {
if (m_blockstack.top().nest & NestFunction) {
if (argc > 1) {
@ -1425,7 +1446,7 @@ static bool getBlock(const ushort *tokens, int limit, int &offset, QString *outS
"TokReturn", "TokBreak", "TokNext",
"TokNot", "TokAnd", "TokOr",
"TokBranch", "TokForLoop",
"TokTestDef", "TokReplaceDef"
"TokTestDef", "TokReplaceDef", "TokBypassNesting"
};
while (offset != limit) {
@ -1509,6 +1530,9 @@ static bool getBlock(const ushort *tokens, int limit, int &offset, QString *outS
if (ok)
ok = getSubBlock(tokens, limit, offset, outStr, indent, "body");
break;
case TokBypassNesting:
ok = getSubBlock(tokens, limit, offset, outStr, indent, "block");
break;
default:
Q_ASSERT(!"unhandled token");
}

View File

@ -633,6 +633,31 @@ void tst_qmakelib::addControlStructs()
<< ""
<< true;
QTest::newRow("bypassNesting()")
<< "defineTest(func) {\n"
"LOCAL = 1\n"
"bypassNesting() {\n"
"OUT = 1\n"
"!isEmpty(GLOBAL): OUT1 = 1\n"
"!isEmpty(LOCAL): OUT2 = 1\n"
"}\n"
"}\n"
"GLOBAL = 1\n"
"func()"
<< "GLOBAL = 1\nLOCAL = UNDEF\nOUT = 1\nOUT1 = 1\nOUT2 = UNDEF"
<< ""
<< true;
QTest::newRow("error() from bypassNesting()")
<< "defineTest(func) {\n"
"bypassNesting() { error(error) }\n"
"}\n"
"func()\n"
"OKE = 1"
<< "OKE = UNDEF"
<< "Project ERROR: error"
<< false;
QTest::newRow("top-level return()")
<< "VAR = good\nreturn()\nVAR = bad"
<< "VAR = good"

View File

@ -1684,6 +1684,57 @@ void tst_qmakelib::addParseCustomFunctions()
/* 22 */ << H(TokTerminator))
<< ""
<< true;
QTest::newRow("bypassNesting()-{return}")
<< "defineTest(test) { bypassNesting() { return(true) } }"
<< TS(
/* 0 */ << H(TokLine) << H(1)
/* 2 */ << H(TokTestDef) << HS(L"test")
/* 10 */ /* body */ << I(16)
/* 12 */ << H(TokLine) << H(1)
/* 14 */ << H(TokBypassNesting)
/* 15 */ /* block */ << I(10)
/* 17 */ << H(TokLine) << H(1)
/* 19 */ << H(TokLiteral | TokNewStr) << S(L"true")
/* 25 */ << H(TokReturn)
/* 26 */ << H(TokTerminator)
/* 27 */ << H(TokTerminator))
<< ""
<< true;
QTest::newRow("test-AND-bypassNesting()-{}")
<< "defineTest(test) { test: bypassNesting() {} }"
<< TS(
/* 0 */ << H(TokLine) << H(1)
/* 2 */ << H(TokTestDef) << HS(L"test")
/* 10 */ /* body */ << I(17)
/* 12 */ << H(TokLine) << H(1)
/* 14 */ << H(TokHashLiteral) << HS(L"test")
/* 22 */ << H(TokCondition)
/* 23 */ << H(TokAnd)
/* 24 */ << H(TokBypassNesting)
/* 25 */ /* block */ << I(1)
/* 27 */ << H(TokTerminator)
/* 28 */ << H(TokTerminator))
<< ""
<< true;
QTest::newRow("test-OR-bypassNesting()-{}")
<< "defineTest(test) { test| bypassNesting() {} }"
<< TS(
/* 0 */ << H(TokLine) << H(1)
/* 2 */ << H(TokTestDef) << HS(L"test")
/* 10 */ /* body */ << I(17)
/* 12 */ << H(TokLine) << H(1)
/* 14 */ << H(TokHashLiteral) << HS(L"test")
/* 22 */ << H(TokCondition)
/* 23 */ << H(TokOr)
/* 24 */ << H(TokBypassNesting)
/* 25 */ /* block */ << I(1)
/* 27 */ << H(TokTerminator)
/* 28 */ << H(TokTerminator))
<< ""
<< true;
}
void tst_qmakelib::addParseAbuse()
@ -1736,6 +1787,24 @@ void tst_qmakelib::addParseAbuse()
<< "in:1: Unexpected NOT operator in front of function definition."
<< false;
QTest::newRow("outer-bypassNesting()-{}")
<< "bypassNesting() {}"
<< TS()
<< "in:1: Unexpected bypassNesting()."
<< false;
QTest::newRow("bypassNesting(arg)-{}")
<< "defineTest(test) { bypassNesting(arg) {} }"
<< TS()
<< "in:1: bypassNesting() requires zero arguments."
<< false;
QTest::newRow("NOT-bypassNesting()-{}")
<< "defineTest(test) { !bypassNesting() {} }"
<< TS()
<< "in:1: Unexpected NOT operator in front of bypassNesting()."
<< false;
QTest::newRow("AND-test")
<< ":test"
<< TS(