From 8591d77fa6d3383c07c373970e59362ff16b143f Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Tue, 11 Sep 2012 19:30:29 +0200 Subject: [PATCH] don't pretend that break()/next()/return() are functions it's a pretty braindead thing to implement control flow statements as (built-in) functions. as a "side effect", this fixes return() value handling for lists. (cherry picked from qtcreator/f53ed6c4b3feca59a94d4f0de8b1a7411122e30e) (cherry picked from qtcreator/f529e22ec38fb9a656d74394e484d2453cf42c69) Change-Id: I59c8efa0e4d65329327115f7f8ed20719e7f7546 Reviewed-by: Qt Doc Bot Reviewed-by: Joerg Bornemann Reviewed-by: Oswald Buddenhagen (cherry picked from qtbase/8400896cfe3fbef7666329a2920bd0dbdd5890af) Reviewed-by: Joerg Bornemann --- src/linguist/shared/proitems.h | 3 +++ src/linguist/shared/qmakebuiltins.cpp | 30 +--------------------- src/linguist/shared/qmakeevaluator.cpp | 24 ++++++++++++----- src/linguist/shared/qmakeevaluator.h | 1 - src/linguist/shared/qmakeparser.cpp | 47 ++++++++++++++++++++++++++++++++++ src/linguist/shared/qmakeparser.h | 9 ++++++- 6 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/linguist/shared/proitems.h b/src/linguist/shared/proitems.h index 05f7925..71db004 100644 --- a/src/linguist/shared/proitems.h +++ b/src/linguist/shared/proitems.h @@ -307,6 +307,9 @@ enum ProToken { TokTestCall, // previous literal/expansion is a test function call // - ((nested expansion + TokArgSeparator)* + nested expansion)? // - TokFuncTerminator + TokReturn, // previous literal/expansion is a return value + TokBreak, // break loop + TokNext, // shortcut to next loop iteration TokNot, // '!' operator TokAnd, // ':' operator TokOr, // '|' operator diff --git a/src/linguist/shared/qmakebuiltins.cpp b/src/linguist/shared/qmakebuiltins.cpp index dfa55a0..4ef48b5 100644 --- a/src/linguist/shared/qmakebuiltins.cpp +++ b/src/linguist/shared/qmakebuiltins.cpp @@ -96,7 +96,7 @@ enum ExpandFunc { enum TestFunc { T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, - T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE, + T_DEFINED, T_CONTAINS, T_INFILE, T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF, T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE }; @@ -168,9 +168,6 @@ void QMakeEvaluator::initFunctionStatics() { "if", T_IF }, { "isActiveConfig", T_CONFIG }, { "system", T_SYSTEM }, - { "return", T_RETURN }, - { "break", T_BREAK }, - { "next", T_NEXT }, { "defined", T_DEFINED }, { "contains", T_CONTAINS }, { "infile", T_INFILE }, @@ -1071,17 +1068,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return returnBool(m_functionDefs.replaceFunctions.contains(var) || m_functionDefs.testFunctions.contains(var)); } - case T_RETURN: - m_returnValue = args; - // It is "safe" to ignore returns - due to qmake brokeness - // they cannot be used to terminate loops anyway. - if (m_cumulative) - return ReturnTrue; - if (m_valuemapStack.size() == 1) { - evalError(fL1S("unexpected return().")); - return ReturnFalse; - } - return ReturnReturn; case T_EXPORT: { if (args.count() != 1) { evalError(fL1S("export(variable) requires one argument.")); @@ -1152,20 +1138,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } return ret; } - case T_BREAK: - if (m_skipLevel) - return ReturnFalse; - if (m_loopLevel) - return ReturnBreak; - evalError(fL1S("Unexpected break().")); - return ReturnFalse; - case T_NEXT: - if (m_skipLevel) - return ReturnFalse; - if (m_loopLevel) - return ReturnNext; - evalError(fL1S("Unexpected next().")); - return ReturnFalse; case T_IF: { if (args.count() != 1) { evalError(fL1S("if(condition) requires one argument.")); diff --git a/src/linguist/shared/qmakeevaluator.cpp b/src/linguist/shared/qmakeevaluator.cpp index 5860d62..e9f0f20 100644 --- a/src/linguist/shared/qmakeevaluator.cpp +++ b/src/linguist/shared/qmakeevaluator.cpp @@ -195,7 +195,6 @@ QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, #ifdef PROEVALUATOR_CUMULATIVE m_skipLevel = 0; #endif - m_loopLevel = 0; m_listCount = 0; m_valuemapStack.push(ProValueMap()); m_valuemapInited = false; @@ -683,6 +682,24 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( invert = false; curr.clear(); continue; + case TokReturn: + m_returnValue = curr; + curr.clear(); + ret = ReturnReturn; + goto ctrlstm; + case TokBreak: + ret = ReturnBreak; + goto ctrlstm; + case TokNext: + ret = ReturnNext; + ctrlstm: + if (!m_skipLevel && okey != or_op) { + traceMsg("flow control statement '%s', aborting block", dbgReturn(ret)); + return ret; + } + traceMsg("skipped flow control statement '%s'", dbgReturn(ret)); + okey = false, or_op = true; // force next evaluation + continue; default: { const ushort *oTokPtr = --tokPtr; evaluateExpression(tokPtr, &curr, false); @@ -762,7 +779,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( else traceMsg("entering loop for %s over %s", dbgKey(variable), dbgStrList(list)); - m_loopLevel++; forever { if (infinite) { if (!variable.isEmpty()) @@ -799,7 +815,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( } } do_break: - m_loopLevel--; traceMsg("done looping"); @@ -1589,8 +1604,6 @@ ProStringList QMakeEvaluator::evaluateFunction( } else { m_valuemapStack.push(ProValueMap()); m_locationStack.push(m_current); - int loopLevel = m_loopLevel; - m_loopLevel = 0; ProStringList args; for (int i = 0; i < argumentsList.count(); ++i) { @@ -1603,7 +1616,6 @@ ProStringList QMakeEvaluator::evaluateFunction( ret = m_returnValue; m_returnValue.clear(); - m_loopLevel = loopLevel; m_current = m_locationStack.pop(); m_valuemapStack.pop(); } diff --git a/src/linguist/shared/qmakeevaluator.h b/src/linguist/shared/qmakeevaluator.h index 69f4e96..2eb7623 100644 --- a/src/linguist/shared/qmakeevaluator.h +++ b/src/linguist/shared/qmakeevaluator.h @@ -233,7 +233,6 @@ public: static void removeEach(ProStringList *varlist, const ProStringList &value); QMakeEvaluator *m_caller; - int m_loopLevel; // To report unexpected break() and next()s #ifdef PROEVALUATOR_CUMULATIVE bool m_cumulative; int m_skipLevel; diff --git a/src/linguist/shared/qmakeparser.cpp b/src/linguist/shared/qmakeparser.cpp index ef0f193..0dbc3ce 100644 --- a/src/linguist/shared/qmakeparser.cpp +++ b/src/linguist/shared/qmakeparser.cpp @@ -108,6 +108,9 @@ static struct { QString strdefineTest; QString strdefineReplace; QString stroption; + QString strreturn; + QString strnext; + QString strbreak; QString strhost_build; QString strLINE; QString strFILE; @@ -128,6 +131,9 @@ void QMakeParser::initialize() statics.strdefineTest = QLatin1String("defineTest"); statics.strdefineReplace = QLatin1String("defineReplace"); statics.stroption = QLatin1String("option"); + statics.strreturn = QLatin1String("return"); + statics.strnext = QLatin1String("next"); + statics.strbreak = QLatin1String("break"); statics.strhost_build = QLatin1String("host_build"); statics.strLINE = QLatin1String("_LINE_"); statics.strFILE = QLatin1String("_FILE_"); @@ -874,9 +880,11 @@ void QMakeParser::putLineMarker(ushort *&tokPtr) void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state) { + uchar nest = m_blockstack.top().nest; m_blockstack.resize(m_blockstack.size() + 1); m_blockstack.top().special = special; m_blockstack.top().start = tokPtr; + m_blockstack.top().nest = nest; tokPtr += 2; m_state = state; m_canElse = false; @@ -1016,6 +1024,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg m_tmp.setRawData((QChar *)uc + 4, nlen); const QString *defName; ushort defType; + uchar nest; if (m_tmp == statics.strfor) { if (m_invert || m_operator == OrOperator) { // '|' could actually work reasonably, but qmake does nonsense here. @@ -1038,6 +1047,7 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg didFor: putTok(tokPtr, TokValueTerminator); enterScope(tokPtr, true, StCtrl); + m_blockstack.top().nest |= NestLoop; return; } else if (*uc == TokArgSeparator && argc == 2) { // for(var, something) @@ -1084,11 +1094,48 @@ void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int arg putTok(tokPtr, defType); putHashStr(tokPtr, uce + 2, nlen); enterScope(tokPtr, true, StCtrl); + m_blockstack.top().nest = NestFunction; return; } } parseError(fL1S("%1(function) requires one literal argument.").arg(*defName)); return; + } else if (m_tmp == statics.strreturn) { + if (argc > 1) { + parseError(fL1S("return() requires zero or one argument.")); + bogusTest(tokPtr); + return; + } + defType = TokReturn; + nest = NestFunction; + goto ctrlstm2; + } else if (m_tmp == statics.strnext) { + defType = TokNext; + goto ctrlstm; + } else if (m_tmp == statics.strbreak) { + defType = TokBreak; + ctrlstm: + if (*uce != TokFuncTerminator) { + parseError(fL1S("%1() requires zero arguments.").arg(m_tmp)); + bogusTest(tokPtr); + return; + } + nest = NestLoop; + ctrlstm2: + if (m_invert) { + parseError(fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp)); + bogusTest(tokPtr); + return; + } + if (!(m_blockstack.top().nest & nest)) { + parseError(fL1S("Unexpected %1().").arg(m_tmp)); + bogusTest(tokPtr); + return; + } + finalizeTest(tokPtr); + putBlock(tokPtr, uce, ptr - uce - 1); // Only for TokReturn + putTok(tokPtr, defType); + return; } else if (m_tmp == statics.stroption) { if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1 || m_invert || m_operator != NoOperator) { diff --git a/src/linguist/shared/qmakeparser.h b/src/linguist/shared/qmakeparser.h index 4f21d8e..8d5c3ed 100644 --- a/src/linguist/shared/qmakeparser.h +++ b/src/linguist/shared/qmakeparser.h @@ -97,13 +97,20 @@ public: void discardFileFromCache(const QString &fileName); private: + enum ScopeNesting { + NestNone = 0, + NestLoop = 1, + NestFunction = 2 + }; + struct BlockScope { - BlockScope() : start(0), braceLevel(0), special(false), inBranch(false) {} + BlockScope() : start(0), braceLevel(0), special(false), inBranch(false), nest(NestNone) {} BlockScope(const BlockScope &other) { *this = other; } ushort *start; // Where this block started; store length here int braceLevel; // Nesting of braces in scope bool special; // Single-line conditionals inside loops, etc. cannot have else branches bool inBranch; // The 'else' branch of the previous TokBranch is still open + uchar nest; // Into what control structures we are nested }; enum ScopeState { -- 2.7.4