diff options
-rw-r--r-- | src/processor/postfix_evaluator-inl.h | 264 | ||||
-rw-r--r-- | src/processor/postfix_evaluator.h | 4 | ||||
-rw-r--r-- | src/processor/postfix_evaluator_unittest.cc | 3 |
3 files changed, 151 insertions, 120 deletions
diff --git a/src/processor/postfix_evaluator-inl.h b/src/processor/postfix_evaluator-inl.h index 332ba1af..d7dbeac2 100644 --- a/src/processor/postfix_evaluator-inl.h +++ b/src/processor/postfix_evaluator-inl.h @@ -67,139 +67,163 @@ class AutoStackClearer { template<typename ValueType> -bool PostfixEvaluator<ValueType>::EvaluateInternal( +bool PostfixEvaluator<ValueType>::EvaluateToken( + const string &token, const string &expression, DictionaryValidityType *assigned) { - // Tokenize, splitting on whitespace. - istringstream stream(expression); - string token; - while (stream >> token) { - // There are enough binary operations that do exactly the same thing - // (other than the specific operation, of course) that it makes sense - // to share as much code as possible. - enum BinaryOperation { - BINARY_OP_NONE = 0, - BINARY_OP_ADD, - BINARY_OP_SUBTRACT, - BINARY_OP_MULTIPLY, - BINARY_OP_DIVIDE_QUOTIENT, - BINARY_OP_DIVIDE_MODULUS, - BINARY_OP_ALIGN - }; - - BinaryOperation operation = BINARY_OP_NONE; - if (token == "+") - operation = BINARY_OP_ADD; - else if (token == "-") - operation = BINARY_OP_SUBTRACT; - else if (token == "*") - operation = BINARY_OP_MULTIPLY; - else if (token == "/") - operation = BINARY_OP_DIVIDE_QUOTIENT; - else if (token == "%") - operation = BINARY_OP_DIVIDE_MODULUS; - else if (token == "@") - operation = BINARY_OP_ALIGN; - - if (operation != BINARY_OP_NONE) { - // Get the operands. - ValueType operand1 = ValueType(); - ValueType operand2 = ValueType(); - if (!PopValues(&operand1, &operand2)) { - BPLOG(ERROR) << "Could not PopValues to get two values for binary " - "operation " << token << ": " << expression; + // There are enough binary operations that do exactly the same thing + // (other than the specific operation, of course) that it makes sense + // to share as much code as possible. + enum BinaryOperation { + BINARY_OP_NONE = 0, + BINARY_OP_ADD, + BINARY_OP_SUBTRACT, + BINARY_OP_MULTIPLY, + BINARY_OP_DIVIDE_QUOTIENT, + BINARY_OP_DIVIDE_MODULUS, + BINARY_OP_ALIGN + }; + + BinaryOperation operation = BINARY_OP_NONE; + if (token == "+") + operation = BINARY_OP_ADD; + else if (token == "-") + operation = BINARY_OP_SUBTRACT; + else if (token == "*") + operation = BINARY_OP_MULTIPLY; + else if (token == "/") + operation = BINARY_OP_DIVIDE_QUOTIENT; + else if (token == "%") + operation = BINARY_OP_DIVIDE_MODULUS; + else if (token == "@") + operation = BINARY_OP_ALIGN; + + if (operation != BINARY_OP_NONE) { + // Get the operands. + ValueType operand1 = ValueType(); + ValueType operand2 = ValueType(); + if (!PopValues(&operand1, &operand2)) { + BPLOG(ERROR) << "Could not PopValues to get two values for binary " + "operation " << token << ": " << expression; + return false; + } + + // Perform the operation. + ValueType result; + switch (operation) { + case BINARY_OP_ADD: + result = operand1 + operand2; + break; + case BINARY_OP_SUBTRACT: + result = operand1 - operand2; + break; + case BINARY_OP_MULTIPLY: + result = operand1 * operand2; + break; + case BINARY_OP_DIVIDE_QUOTIENT: + result = operand1 / operand2; + break; + case BINARY_OP_DIVIDE_MODULUS: + result = operand1 % operand2; + break; + case BINARY_OP_ALIGN: + result = + operand1 & (static_cast<ValueType>(-1) ^ (operand2 - 1)); + break; + case BINARY_OP_NONE: + // This will not happen, but compilers will want a default or + // BINARY_OP_NONE case. + BPLOG(ERROR) << "Not reached!"; return false; - } + break; + } - // Perform the operation. - ValueType result; - switch (operation) { - case BINARY_OP_ADD: - result = operand1 + operand2; - break; - case BINARY_OP_SUBTRACT: - result = operand1 - operand2; - break; - case BINARY_OP_MULTIPLY: - result = operand1 * operand2; - break; - case BINARY_OP_DIVIDE_QUOTIENT: - result = operand1 / operand2; - break; - case BINARY_OP_DIVIDE_MODULUS: - result = operand1 % operand2; - break; - case BINARY_OP_ALIGN: - result = - operand1 & (static_cast<ValueType>(-1) ^ (operand2 - 1)); - break; - case BINARY_OP_NONE: - // This will not happen, but compilers will want a default or - // BINARY_OP_NONE case. - BPLOG(ERROR) << "Not reached!"; - return false; - break; - } + // Save the result. + PushValue(result); + } else if (token == "^") { + // ^ for unary dereference. Can't dereference without memory. + if (!memory_) { + BPLOG(ERROR) << "Attempt to dereference without memory: " << + expression; + return false; + } - // Save the result. - PushValue(result); - } else if (token == "^") { - // ^ for unary dereference. Can't dereference without memory. - if (!memory_) { - BPLOG(ERROR) << "Attempt to dereference without memory: " << - expression; - return false; - } + ValueType address; + if (!PopValue(&address)) { + BPLOG(ERROR) << "Could not PopValue to get value to derefence: " << + expression; + return false; + } - ValueType address; - if (!PopValue(&address)) { - BPLOG(ERROR) << "Could not PopValue to get value to derefence: " << - expression; - return false; - } + ValueType value; + if (!memory_->GetMemoryAtAddress(address, &value)) { + BPLOG(ERROR) << "Could not dereference memory at address " << + HexString(address) << ": " << expression; + return false; + } - ValueType value; - if (!memory_->GetMemoryAtAddress(address, &value)) { - BPLOG(ERROR) << "Could not dereference memory at address " << - HexString(address) << ": " << expression; - return false; - } + PushValue(value); + } else if (token == "=") { + // = for assignment. + ValueType value; + if (!PopValue(&value)) { + BPLOG(INFO) << "Could not PopValue to get value to assign: " << + expression; + return false; + } - PushValue(value); - } else if (token == "=") { - // = for assignment. - ValueType value; - if (!PopValue(&value)) { - BPLOG(INFO) << "Could not PopValue to get value to assign: " << - expression; - return false; - } + // Assignment is only meaningful when assigning into an identifier. + // The identifier must name a variable, not a constant. Variables + // begin with '$'. + string identifier; + if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { + BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " + "identifier is needed to assign " << + HexString(value) << ": " << expression; + return false; + } + if (identifier.empty() || identifier[0] != '$') { + BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << + identifier << ": " << expression; + return false; + } + + (*dictionary_)[identifier] = value; + if (assigned) + (*assigned)[identifier] = true; + } else { + // The token is not an operator, it's a literal value or an identifier. + // Push it onto the stack as-is. Use push_back instead of PushValue + // because PushValue pushes ValueType as a string, but token is already + // a string. + stack_.push_back(token); + } + return true; +} - // Assignment is only meaningful when assigning into an identifier. - // The identifier must name a variable, not a constant. Variables - // begin with '$'. - string identifier; - if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { - BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " - "identifier is needed to assign " << - HexString(value) << ": " << expression; +template<typename ValueType> +bool PostfixEvaluator<ValueType>::EvaluateInternal( + const string &expression, + DictionaryValidityType *assigned) { + // Tokenize, splitting on whitespace. + istringstream stream(expression); + string token; + while (stream >> token) { + // Normally, tokens are whitespace-separated, but occasionally, the + // assignment operator is smashed up against the next token, i.e. + // $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ = + // This has been observed in program strings produced by MSVS 2010 in LTO + // mode. + if (token.size() > 1 && token[0] == '=') { + if (!EvaluateToken("=", expression, assigned)) { return false; } - if (identifier.empty() || identifier[0] != '$') { - BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << - identifier << ": " << expression; + + if (!EvaluateToken(token.substr(1), expression, assigned)) { return false; } - - (*dictionary_)[identifier] = value; - if (assigned) - (*assigned)[identifier] = true; - } else { - // The token is not an operator, it's a literal value or an identifier. - // Push it onto the stack as-is. Use push_back instead of PushValue - // because PushValue pushes ValueType as a string, but token is already - // a string. - stack_.push_back(token); + } else if (!EvaluateToken(token, expression, assigned)) { + return false; } } diff --git a/src/processor/postfix_evaluator.h b/src/processor/postfix_evaluator.h index 35f79f76..eb53b1eb 100644 --- a/src/processor/postfix_evaluator.h +++ b/src/processor/postfix_evaluator.h @@ -153,6 +153,10 @@ class PostfixEvaluator { bool EvaluateInternal(const string &expression, DictionaryValidityType *assigned); + bool EvaluateToken(const string &token, + const string &expression, + DictionaryValidityType *assigned); + // The dictionary mapping constant and variable identifiers (strings) to // values. Keys beginning with '$' are treated as variable names, and // PostfixEvaluator is free to create and modify these keys. Weak pointer. diff --git a/src/processor/postfix_evaluator_unittest.cc b/src/processor/postfix_evaluator_unittest.cc index 427ee505..f6cbcf08 100644 --- a/src/processor/postfix_evaluator_unittest.cc +++ b/src/processor/postfix_evaluator_unittest.cc @@ -150,6 +150,7 @@ static bool RunTests() { { "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3 { "$rDeref 9 ^ =", true }, // $rDeref = ^9 = 10 (FakeMemoryRegion) { "$rAlign 36 8 @ =", true }, // $rAlign = 36 @ 8 + { "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization }; map<string, unsigned int> validate_data_0; validate_data_0["$rAdd"] = 8; @@ -160,6 +161,8 @@ static bool RunTests() { validate_data_0["$rDivM"] = 3; validate_data_0["$rDeref"] = 10; validate_data_0["$rAlign"] = 32; + validate_data_0["$rAdd3"] = 4; + validate_data_0["$rMul2"] = 54; // The second test set simulates a couple of MSVC program strings. // The data is fudged a little bit because the tests use FakeMemoryRegion |