implement if() by means of precompilation

the performance is about the same (depending on the expression type it's
better or worse), but a lot of code just disappears.

Change-Id: I60eb9b87f23cc811d3f9577841c38966ecfd8e43
Reviewed-by: Daniel Teske <daniel.teske@nokia.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@nokia.com>
This commit is contained in:
Oswald Buddenhagen
2012-07-27 21:26:10 +02:00
parent 75cc8575b4
commit e739841c63
5 changed files with 48 additions and 402 deletions

View File

@@ -1368,278 +1368,6 @@ QString QMakeEvaluator::currentDirectory() const
return QString();
}
// The (QChar*)current->constData() constructs below avoid pointless detach() calls
// FIXME: This is inefficient. Should not make new string if it is a straight subsegment
static ALWAYS_INLINE void appendChar(ushort unicode,
QString *current, QChar **ptr, ProString *pending)
{
if (!pending->isEmpty()) {
int len = pending->size();
current->resize(current->size() + len);
::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
pending->clear();
*ptr = (QChar*)current->constData() + len;
}
*(*ptr)++ = QChar(unicode);
}
static void appendString(const ProString &string,
QString *current, QChar **ptr, ProString *pending)
{
if (string.isEmpty())
return;
QChar *uc = (QChar*)current->constData();
int len;
if (*ptr != uc) {
len = *ptr - uc;
current->resize(current->size() + string.size());
} else if (!pending->isEmpty()) {
len = pending->size();
current->resize(current->size() + len + string.size());
::memcpy((QChar*)current->constData(), pending->constData(), len * 2);
pending->clear();
} else {
*pending = string;
return;
}
*ptr = (QChar*)current->constData() + len;
::memcpy(*ptr, string.constData(), string.size() * 2);
*ptr += string.size();
}
static void flushCurrent(ProStringList *ret,
QString *current, QChar **ptr, ProString *pending, bool joined)
{
QChar *uc = (QChar*)current->constData();
int len = *ptr - uc;
if (len) {
ret->append(ProString(QString(uc, len), NoHash));
*ptr = uc;
} else if (!pending->isEmpty()) {
ret->append(*pending);
pending->clear();
} else if (joined) {
ret->append(ProString());
}
}
static inline void flushFinal(ProStringList *ret,
const QString &current, const QChar *ptr, const ProString &pending,
const ProString &str, bool replaced, bool joined)
{
int len = ptr - current.data();
if (len) {
if (!replaced && len == str.size())
ret->append(str);
else
ret->append(ProString(QString(current.data(), len), NoHash));
} else if (!pending.isEmpty()) {
ret->append(pending);
} else if (joined) {
ret->append(ProString());
}
}
ProStringList QMakeEvaluator::expandVariableReferences(
const ProString &str, int *pos, bool joined)
{
ProStringList ret;
// if (ok)
// *ok = true;
if (str.isEmpty() && !pos)
return ret;
const ushort LSQUARE = '[';
const ushort RSQUARE = ']';
const ushort LCURLY = '{';
const ushort RCURLY = '}';
const ushort LPAREN = '(';
const ushort RPAREN = ')';
const ushort DOLLAR = '$';
const ushort BACKSLASH = '\\';
const ushort UNDERSCORE = '_';
const ushort DOT = '.';
const ushort SPACE = ' ';
const ushort TAB = '\t';
const ushort COMMA = ',';
const ushort SINGLEQUOTE = '\'';
const ushort DOUBLEQUOTE = '"';
ushort unicode, quote = 0, parens = 0;
const ushort *str_data = (const ushort *)str.constData();
const int str_len = str.size();
ProString var, args;
bool replaced = false;
bool putSpace = false;
QString current; // Buffer for successively assembled string segments
current.resize(str.size());
QChar *ptr = current.data();
ProString pending; // Buffer for string segments from variables
// Only one of the above buffers can be filled at a given time.
for (int i = pos ? *pos : 0; i < str_len; ++i) {
unicode = str_data[i];
if (unicode == DOLLAR) {
if (str_len > i+2 && str_data[i+1] == DOLLAR) {
++i;
ushort term = 0;
enum { VAR, ENVIRON, FUNCTION, PROPERTY } var_type = VAR;
unicode = str_data[++i];
if (unicode == LSQUARE) {
unicode = str_data[++i];
term = RSQUARE;
var_type = PROPERTY;
} else if (unicode == LCURLY) {
unicode = str_data[++i];
var_type = VAR;
term = RCURLY;
} else if (unicode == LPAREN) {
unicode = str_data[++i];
var_type = ENVIRON;
term = RPAREN;
}
int name_start = i;
forever {
if (!(unicode & (0xFF<<8)) &&
unicode != DOT && unicode != UNDERSCORE &&
//unicode != SINGLEQUOTE && unicode != DOUBLEQUOTE &&
(unicode < 'a' || unicode > 'z') && (unicode < 'A' || unicode > 'Z') &&
(unicode < '0' || unicode > '9'))
break;
if (++i == str_len)
break;
unicode = str_data[i];
// at this point, i points to either the 'term' or 'next' character (which is in unicode)
}
var = str.mid(name_start, i - name_start);
if (var_type == VAR && unicode == LPAREN) {
var_type = FUNCTION;
name_start = i + 1;
int depth = 0;
forever {
if (++i == str_len)
break;
unicode = str_data[i];
if (unicode == LPAREN) {
depth++;
} else if (unicode == RPAREN) {
if (!depth)
break;
--depth;
}
}
args = str.mid(name_start, i - name_start);
if (++i < str_len)
unicode = str_data[i];
else
unicode = 0;
// at this point i is pointing to the 'next' character (which is in unicode)
// this might actually be a term character since you can do $${func()}
}
if (term) {
if (unicode != term) {
evalError(fL1S("Missing %1 terminator [found %2]")
.arg(QChar(term))
.arg(unicode ? QString(unicode) : fL1S("end-of-line")));
// if (ok)
// *ok = false;
if (pos)
*pos = str_len;
return ProStringList();
}
} else {
// move the 'cursor' back to the last char of the thing we were looking at
--i;
}
ProStringList replacement;
if (var_type == ENVIRON) {
replacement = split_value_list(m_option->getEnv(var.toQString(m_tmp1)));
} else if (var_type == PROPERTY) {
replacement << propertyValue(var);
} else if (var_type == FUNCTION) {
replacement += evaluateExpandFunction(var, args);
} else if (var_type == VAR) {
replacement = values(map(var));
}
if (!replacement.isEmpty()) {
if (quote || joined) {
if (putSpace) {
putSpace = false;
if (!replacement.at(0).isEmpty()) // Bizarre, indeed
appendChar(' ', &current, &ptr, &pending);
}
appendString(ProString(replacement.join(statics.field_sep), NoHash),
&current, &ptr, &pending);
} else {
appendString(replacement.at(0), &current, &ptr, &pending);
if (replacement.size() > 1) {
flushCurrent(&ret, &current, &ptr, &pending, false);
int j = 1;
if (replacement.size() > 2) {
// FIXME: ret.reserve(ret.size() + replacement.size() - 2);
for (; j < replacement.size() - 1; ++j)
ret << replacement.at(j);
}
pending = replacement.at(j);
}
}
replaced = true;
}
continue;
}
} else if (unicode == BACKSLASH) {
static const char symbols[] = "[]{}()$\\'\"";
ushort unicode2 = str_data[i+1];
if (!(unicode2 & 0xff00) && strchr(symbols, unicode2)) {
unicode = unicode2;
++i;
}
} else if (quote) {
if (unicode == quote) {
quote = 0;
continue;
}
} else {
if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) {
quote = unicode;
continue;
} else if (unicode == SPACE || unicode == TAB) {
if (!joined)
flushCurrent(&ret, &current, &ptr, &pending, false);
else if ((ptr - (QChar*)current.constData()) || !pending.isEmpty())
putSpace = true;
continue;
} else if (pos) {
if (unicode == LPAREN) {
++parens;
} else if (unicode == RPAREN) {
--parens;
} else if (!parens && unicode == COMMA) {
if (!joined) {
*pos = i + 1;
flushFinal(&ret, current, ptr, pending, str, replaced, false);
return ret;
}
flushCurrent(&ret, &current, &ptr, &pending, true);
putSpace = false;
continue;
}
}
}
if (putSpace) {
putSpace = false;
appendChar(' ', &current, &ptr, &pending);
}
appendChar(unicode, &current, &ptr, &pending);
}
if (pos)
*pos = str_len;
flushFinal(&ret, current, ptr, pending, str, replaced, joined);
return ret;
}
bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex)
{
// magic types for easy flipping
@@ -1722,14 +1450,6 @@ QList<ProStringList> QMakeEvaluator::prepareFunctionArgs(const ushort *&tokPtr)
return args_list;
}
QList<ProStringList> QMakeEvaluator::prepareFunctionArgs(const ProString &arguments)
{
QList<ProStringList> args_list;
for (int pos = 0; pos < arguments.size(); )
args_list << expandVariableReferences(arguments, &pos);
return args_list;
}
ProStringList QMakeEvaluator::evaluateFunction(
const ProFunctionDef &func, const QList<ProStringList> &argumentsList, bool *ok)
{
@@ -1792,54 +1512,19 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction(
return ReturnFalse;
}
ProStringList QMakeEvaluator::evaluateExpandFunction(
const ProString &func, const ushort *&tokPtr)
bool QMakeEvaluator::evaluateConditional(const QString &cond, const QString &context)
{
QHash<ProString, ProFunctionDef>::ConstIterator it =
m_functionDefs.replaceFunctions.constFind(func);
if (it != m_functionDefs.replaceFunctions.constEnd())
return evaluateFunction(*it, prepareFunctionArgs(tokPtr), 0);
//why don't the builtin functions just use args_list? --Sam
return evaluateExpandFunction(func, expandVariableReferences(tokPtr, 5, true));
}
ProStringList QMakeEvaluator::evaluateExpandFunction(
const ProString &func, const ProString &arguments)
{
QHash<ProString, ProFunctionDef>::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;
return evaluateExpandFunction(func, expandVariableReferences(arguments, &pos, true));
}
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
const ProString &function, const ProString &arguments)
{
QHash<ProString, ProFunctionDef>::ConstIterator it =
m_functionDefs.testFunctions.constFind(function);
if (it != m_functionDefs.testFunctions.constEnd())
return evaluateBoolFunction(*it, prepareFunctionArgs(arguments), function);
//why don't the builtin functions just use args_list? --Sam
int pos = 0;
return evaluateConditionalFunction(function, expandVariableReferences(arguments, &pos, true));
}
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction(
const ProString &function, const ushort *&tokPtr)
{
QHash<ProString, ProFunctionDef>::ConstIterator it =
m_functionDefs.testFunctions.constFind(function);
if (it != m_functionDefs.testFunctions.constEnd())
return evaluateBoolFunction(*it, prepareFunctionArgs(tokPtr), function);
//why don't the builtin functions just use args_list? --Sam
return evaluateConditionalFunction(function, expandVariableReferences(tokPtr, 5, true));
bool ret = false;
ProFile *pro = m_parser->parsedProBlock(context, cond, QMakeParser::TestGrammar);
if (pro) {
if (pro->isOk()) {
m_locationStack.push(m_current);
ret = visitProBlock(pro, pro->tokPtr()) == ReturnTrue;
m_current = m_locationStack.pop();
}
pro->deref();
}
return ret;
}
ProValueMap *QMakeEvaluator::findValues(const ProString &variableName, ProValueMap::Iterator *rit)