363d1bb20f
This change is mostly for FB internal organizational reasons. Building is not effected beyond the fact that the target now lands in hphp/hhvm/hhvm rather than src/hhvm/hhvm.
1497 linhas
45 KiB
C++
1497 linhas
45 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <compiler/expression/binary_op_expression.h>
|
|
#include <compiler/expression/array_element_expression.h>
|
|
#include <compiler/expression/object_property_expression.h>
|
|
#include <compiler/expression/unary_op_expression.h>
|
|
#include <util/parser/hphp.tab.hpp>
|
|
#include <compiler/expression/scalar_expression.h>
|
|
#include <compiler/expression/constant_expression.h>
|
|
#include <runtime/base/complex_types.h>
|
|
#include <runtime/base/type_conversions.h>
|
|
#include <runtime/base/builtin_functions.h>
|
|
#include <runtime/base/comparisons.h>
|
|
#include <runtime/base/zend/zend_string.h>
|
|
#include <compiler/expression/expression_list.h>
|
|
#include <compiler/expression/encaps_list_expression.h>
|
|
#include <compiler/expression/simple_function_call.h>
|
|
#include <compiler/expression/simple_variable.h>
|
|
#include <compiler/statement/loop_statement.h>
|
|
|
|
using namespace HPHP;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors/destructors
|
|
|
|
BinaryOpExpression::BinaryOpExpression
|
|
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
|
|
ExpressionPtr exp1, ExpressionPtr exp2, int op)
|
|
: Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(BinaryOpExpression)),
|
|
m_exp1(exp1), m_exp2(exp2), m_op(op), m_assign(false), m_canThrow(false) {
|
|
switch (m_op) {
|
|
case T_PLUS_EQUAL:
|
|
case T_MINUS_EQUAL:
|
|
case T_MUL_EQUAL:
|
|
case T_DIV_EQUAL:
|
|
case T_CONCAT_EQUAL:
|
|
case T_MOD_EQUAL:
|
|
case T_AND_EQUAL:
|
|
case T_OR_EQUAL:
|
|
case T_XOR_EQUAL:
|
|
case T_SL_EQUAL:
|
|
case T_SR_EQUAL:
|
|
m_assign = true;
|
|
m_exp1->setContext(Expression::LValue);
|
|
m_exp1->setContext(Expression::OprLValue);
|
|
m_exp1->setContext(Expression::DeepOprLValue);
|
|
if (m_exp1->is(Expression::KindOfObjectPropertyExpression)) {
|
|
m_exp1->setContext(Expression::NoLValueWrapper);
|
|
}
|
|
break;
|
|
case T_COLLECTION: {
|
|
std::string s = m_exp1->getLiteralString();
|
|
int cType = 0;
|
|
if (strcasecmp(s.c_str(), "vector") == 0) {
|
|
cType = Collection::VectorType;
|
|
} else if (strcasecmp(s.c_str(), "map") == 0) {
|
|
cType = Collection::MapType;
|
|
} else if (strcasecmp(s.c_str(), "stablemap") == 0) {
|
|
cType = Collection::StableMapType;
|
|
}
|
|
ExpressionListPtr el = static_pointer_cast<ExpressionList>(m_exp2);
|
|
el->setCollectionType(cType);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::clone() {
|
|
BinaryOpExpressionPtr exp(new BinaryOpExpression(*this));
|
|
Expression::deepCopy(exp);
|
|
exp->m_exp1 = Clone(m_exp1);
|
|
exp->m_exp2 = Clone(m_exp2);
|
|
return exp;
|
|
}
|
|
|
|
bool BinaryOpExpression::isTemporary() const {
|
|
switch (m_op) {
|
|
case '+':
|
|
case '-':
|
|
case '*':
|
|
case '/':
|
|
case T_SL:
|
|
case T_SR:
|
|
case T_BOOLEAN_OR:
|
|
case T_BOOLEAN_AND:
|
|
case T_LOGICAL_OR:
|
|
case T_LOGICAL_AND:
|
|
case T_INSTANCEOF:
|
|
case T_COLLECTION:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BinaryOpExpression::isRefable(bool checkError /* = false */) const {
|
|
return checkError && m_assign;
|
|
}
|
|
|
|
bool BinaryOpExpression::isLiteralString() const {
|
|
if (m_op == '.') {
|
|
return m_exp1->isLiteralString() && m_exp2->isLiteralString();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string BinaryOpExpression::getLiteralString() const {
|
|
if (m_op == '.') {
|
|
return m_exp1->getLiteralString() + m_exp2->getLiteralString();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
bool BinaryOpExpression::containsDynamicConstant(AnalysisResultPtr ar) const {
|
|
switch (m_op) {
|
|
case T_COLLECTION:
|
|
return m_exp2->containsDynamicConstant(ar);
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BinaryOpExpression::isShortCircuitOperator() const {
|
|
switch (m_op) {
|
|
case T_BOOLEAN_OR:
|
|
case T_BOOLEAN_AND:
|
|
case T_LOGICAL_OR:
|
|
case T_LOGICAL_AND:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BinaryOpExpression::isLogicalOrOperator() const {
|
|
switch (m_op) {
|
|
case T_BOOLEAN_OR:
|
|
case T_LOGICAL_OR:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::unneededHelper() {
|
|
if (!isShortCircuitOperator() || !m_exp2->getContainedEffects()) {
|
|
return Expression::unneededHelper();
|
|
}
|
|
|
|
m_exp2 = m_exp2->unneeded();
|
|
m_exp2->setExpectedType(Type::Boolean);
|
|
return static_pointer_cast<Expression>(shared_from_this());
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser functions
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static analysis functions
|
|
|
|
int BinaryOpExpression::getLocalEffects() const {
|
|
int effect = NoEffect;
|
|
m_canThrow = false;
|
|
switch (m_op) {
|
|
case '/':
|
|
case '%':
|
|
case T_DIV_EQUAL:
|
|
case T_MOD_EQUAL: {
|
|
Variant v2;
|
|
if (!m_exp2->getScalarValue(v2) || v2.equal(0)) {
|
|
effect = CanThrow;
|
|
m_canThrow = true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (m_assign) effect |= AssignEffect;
|
|
return effect;
|
|
}
|
|
|
|
void BinaryOpExpression::analyzeProgram(AnalysisResultPtr ar) {
|
|
if (ar->getPhase() == AnalysisResult::AnalyzeFinal &&
|
|
m_op == T_INSTANCEOF && m_exp2->is(Expression::KindOfScalarExpression)) {
|
|
ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exp2);
|
|
addUserClass(ar, s->getString());
|
|
}
|
|
m_exp1->analyzeProgram(ar);
|
|
m_exp2->analyzeProgram(ar);
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::simplifyLogical(AnalysisResultConstPtr ar) {
|
|
try {
|
|
ExpressionPtr rep = foldConst(ar);
|
|
if (rep) return replaceValue(rep);
|
|
} catch (Exception e) {
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ConstructPtr BinaryOpExpression::getNthKid(int n) const {
|
|
switch (n) {
|
|
case 0:
|
|
return m_exp1;
|
|
case 1:
|
|
return m_exp2;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return ConstructPtr();
|
|
}
|
|
|
|
int BinaryOpExpression::getKidCount() const {
|
|
return 2;
|
|
}
|
|
|
|
void BinaryOpExpression::setNthKid(int n, ConstructPtr cp) {
|
|
switch (n) {
|
|
case 0:
|
|
m_exp1 = boost::dynamic_pointer_cast<Expression>(cp);
|
|
break;
|
|
case 1:
|
|
m_exp2 = boost::dynamic_pointer_cast<Expression>(cp);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool BinaryOpExpression::canonCompare(ExpressionPtr e) const {
|
|
return Expression::canonCompare(e) &&
|
|
getOp() == static_cast<BinaryOpExpression*>(e.get())->getOp();
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::preOptimize(AnalysisResultConstPtr ar) {
|
|
if (!m_exp2->isScalar()) {
|
|
if (!m_exp1->isScalar()) {
|
|
if (m_exp1->is(KindOfBinaryOpExpression)) {
|
|
BinaryOpExpressionPtr b(
|
|
dynamic_pointer_cast<BinaryOpExpression>(m_exp1));
|
|
if (b->m_op == m_op && b->m_exp1->isScalar()) {
|
|
return foldRightAssoc(ar);
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
} else if (m_canThrow && !(getLocalEffects() & CanThrow)) {
|
|
recomputeEffects();
|
|
}
|
|
ExpressionPtr optExp;
|
|
try {
|
|
optExp = foldConst(ar);
|
|
} catch (Exception &e) {
|
|
// runtime/base threw an exception, perhaps bad operands
|
|
}
|
|
if (optExp) optExp = replaceValue(optExp);
|
|
return optExp;
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::simplifyArithmetic(
|
|
AnalysisResultConstPtr ar) {
|
|
Variant v1;
|
|
Variant v2;
|
|
if (m_exp1->getScalarValue(v1)) {
|
|
if (v1.isInteger()) {
|
|
int64 ival1 = v1.toInt64();
|
|
// 1 * $a => $a, 0 + $a => $a
|
|
if ((ival1 == 1 && m_op == '*') || (ival1 == 0 && m_op == '+')) {
|
|
TypePtr actType2 = m_exp2->getActualType();
|
|
TypePtr expType = getExpectedType();
|
|
if (actType2 &&
|
|
(actType2->mustBe(Type::KindOfNumeric) ||
|
|
(expType && expType->mustBe(Type::KindOfNumeric) &&
|
|
!actType2->couldBe(Type::KindOfArray) &&
|
|
Type::IsCastNeeded(ar, actType2, expType)))) {
|
|
return m_exp2;
|
|
}
|
|
}
|
|
} else if (v1.isString()) {
|
|
String sval1 = v1.toString();
|
|
if ((sval1.empty() && m_op == '.')) {
|
|
TypePtr actType2 = m_exp2->getActualType();
|
|
TypePtr expType = getExpectedType();
|
|
// '' . $a => $a
|
|
if ((expType && expType->is(Type::KindOfString)) ||
|
|
(actType2 && actType2->is(Type::KindOfString))) {
|
|
return m_exp2;
|
|
}
|
|
ExpressionPtr rep(new UnaryOpExpression(
|
|
getScope(), getLocation(),
|
|
m_exp2, T_STRING_CAST, true));
|
|
rep->setActualType(Type::String);
|
|
return rep;
|
|
}
|
|
}
|
|
}
|
|
if (m_exp2->getScalarValue(v2)) {
|
|
if (v2.isInteger()) {
|
|
int64 ival2 = v2.toInt64();
|
|
// $a * 1 => $a, $a + 0 => $a
|
|
if ((ival2 == 1 && m_op == '*') || (ival2 == 0 && m_op == '+')) {
|
|
TypePtr actType1 = m_exp1->getActualType();
|
|
TypePtr expType = getExpectedType();
|
|
if (actType1 &&
|
|
(actType1->mustBe(Type::KindOfNumeric) ||
|
|
(expType && expType->mustBe(Type::KindOfNumeric) &&
|
|
!actType1->couldBe(Type::KindOfArray) &&
|
|
Type::IsCastNeeded(ar, actType1, expType)))) {
|
|
return m_exp1;
|
|
}
|
|
}
|
|
} else if (v2.isString()) {
|
|
String sval2 = v2.toString();
|
|
if ((sval2.empty() && m_op == '.')) {
|
|
TypePtr actType1 = m_exp1->getActualType();
|
|
TypePtr expType = getExpectedType();
|
|
// $a . '' => $a
|
|
if ((expType && expType->is(Type::KindOfString)) ||
|
|
(actType1 && actType1->is(Type::KindOfString))) {
|
|
return m_exp1;
|
|
}
|
|
ExpressionPtr rep(new UnaryOpExpression(
|
|
getScope(), getLocation(),
|
|
m_exp1, T_STRING_CAST, true));
|
|
rep->setActualType(Type::String);
|
|
return rep;
|
|
}
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
void BinaryOpExpression::optimizeTypes(AnalysisResultConstPtr ar) {
|
|
switch (m_op) {
|
|
case '<':
|
|
case T_IS_SMALLER_OR_EQUAL:
|
|
case '>':
|
|
case T_IS_GREATER_OR_EQUAL:
|
|
case T_IS_IDENTICAL:
|
|
case T_IS_NOT_IDENTICAL:
|
|
case T_IS_EQUAL:
|
|
case T_IS_NOT_EQUAL:
|
|
{
|
|
// not needed for correctness, but will allow us to
|
|
// generate better code, since we can use the more
|
|
// specific runtime function
|
|
|
|
TypePtr a1(m_exp1->getActualType());
|
|
TypePtr i1(m_exp1->getImplementedType());
|
|
if (a1 && i1 &&
|
|
Type::IsMappedToVariant(i1) && Type::HasFastCastMethod(a1)) {
|
|
m_exp1->setExpectedType(a1);
|
|
}
|
|
TypePtr a2(m_exp2->getActualType());
|
|
TypePtr i2(m_exp2->getImplementedType());
|
|
if (a2 && i2 &&
|
|
Type::IsMappedToVariant(i2) && Type::HasFastCastMethod(a2)) {
|
|
m_exp2->setExpectedType(a2);
|
|
}
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::postOptimize(AnalysisResultConstPtr ar) {
|
|
optimizeTypes(ar);
|
|
ExpressionPtr optExp = simplifyArithmetic(ar);
|
|
if (!optExp) {
|
|
if (isShortCircuitOperator()) optExp = simplifyLogical(ar);
|
|
}
|
|
if (optExp) optExp = replaceValue(optExp);
|
|
return optExp;
|
|
}
|
|
|
|
static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar,
|
|
LocationPtr loc, ExpressionPtr exp,
|
|
bool invert) {
|
|
/* Replace "$x === null" with an is_null call; this requires slightly
|
|
* less work at runtime. */
|
|
ExpressionListPtr expList =
|
|
ExpressionListPtr(new ExpressionList(exp->getScope(), loc));
|
|
expList->insertElement(exp);
|
|
|
|
SimpleFunctionCallPtr call
|
|
(new SimpleFunctionCall(exp->getScope(), loc,
|
|
"is_null", expList, ExpressionPtr()));
|
|
|
|
call->setValid();
|
|
call->setActualType(Type::Boolean);
|
|
call->setupScopes(ar);
|
|
|
|
ExpressionPtr result(call);
|
|
if (invert) {
|
|
result = ExpressionPtr(new UnaryOpExpression(
|
|
exp->getScope(), loc,
|
|
result, '!', true));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
|
|
ExpressionPtr optExp;
|
|
Variant v1;
|
|
Variant v2;
|
|
|
|
if (!m_exp2->getScalarValue(v2)) {
|
|
if (m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
|
|
switch (m_op) {
|
|
case T_IS_IDENTICAL:
|
|
case T_IS_NOT_IDENTICAL:
|
|
if (v1.isNull()) {
|
|
return makeIsNull(ar, getLocation(), m_exp2,
|
|
m_op == T_IS_NOT_IDENTICAL);
|
|
}
|
|
break;
|
|
case T_LOGICAL_AND:
|
|
case T_BOOLEAN_AND:
|
|
case T_LOGICAL_OR:
|
|
case T_BOOLEAN_OR: {
|
|
ExpressionPtr rep =
|
|
v1.toBoolean() == (m_op == T_LOGICAL_AND ||
|
|
m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1;
|
|
rep = ExpressionPtr(
|
|
new UnaryOpExpression(
|
|
getScope(), getLocation(),
|
|
rep, T_BOOL_CAST, true));
|
|
rep->setActualType(Type::Boolean);
|
|
return replaceValue(rep);
|
|
}
|
|
case '+':
|
|
case '.':
|
|
case '*':
|
|
case '&':
|
|
case '|':
|
|
case '^':
|
|
if (m_exp2->is(KindOfBinaryOpExpression)) {
|
|
BinaryOpExpressionPtr binOpExp =
|
|
dynamic_pointer_cast<BinaryOpExpression>(m_exp2);
|
|
if (binOpExp->m_op == m_op && binOpExp->m_exp1->isScalar()) {
|
|
ExpressionPtr aExp = m_exp1;
|
|
ExpressionPtr bExp = binOpExp->m_exp1;
|
|
ExpressionPtr cExp = binOpExp->m_exp2;
|
|
m_exp1 = binOpExp = Clone(binOpExp);
|
|
m_exp2 = cExp;
|
|
binOpExp->m_exp1 = aExp;
|
|
binOpExp->m_exp2 = bExp;
|
|
if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
|
|
m_exp1 = optExp;
|
|
}
|
|
return static_pointer_cast<Expression>(shared_from_this());
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
if (m_exp1->isScalar()) {
|
|
if (!m_exp1->getScalarValue(v1)) return ExpressionPtr();
|
|
try {
|
|
if (hhvm &&
|
|
Option::OutputHHBC &&
|
|
(!Option::WholeProgram || !Option::ParseTimeOpts)) {
|
|
// In the VM, don't optimize __CLASS__ if within a trait, since
|
|
// __CLASS__ is not resolved yet.
|
|
ClassScopeRawPtr clsScope = getOriginalClass();
|
|
if (clsScope && clsScope->isTrait()) {
|
|
ScalarExpressionPtr scalar1 =
|
|
dynamic_pointer_cast<ScalarExpression>(m_exp1);
|
|
ScalarExpressionPtr scalar2 =
|
|
dynamic_pointer_cast<ScalarExpression>(m_exp2);
|
|
if ((scalar1 && scalar1->getType() == T_CLASS_C) ||
|
|
(scalar2 && scalar2->getType() == T_CLASS_C)) {
|
|
return ExpressionPtr();
|
|
}
|
|
}
|
|
}
|
|
Variant result;
|
|
switch (m_op) {
|
|
case T_LOGICAL_XOR:
|
|
result = logical_xor(v1, v2); break;
|
|
case '|':
|
|
result = bitwise_or(v1, v2); break;
|
|
case '&':
|
|
result = bitwise_and(v1, v2); break;
|
|
case '^':
|
|
result = bitwise_xor(v1, v2); break;
|
|
case '.':
|
|
result = concat(v1, v2); break;
|
|
case T_IS_IDENTICAL:
|
|
result = same(v1, v2); break;
|
|
case T_IS_NOT_IDENTICAL:
|
|
result = !same(v1, v2); break;
|
|
case T_IS_EQUAL:
|
|
result = equal(v1, v2); break;
|
|
case T_IS_NOT_EQUAL:
|
|
result = !equal(v1, v2); break;
|
|
case '<':
|
|
result = less(v1, v2); break;
|
|
case T_IS_SMALLER_OR_EQUAL:
|
|
result = not_more(v1, v2); break;
|
|
case '>':
|
|
result = more(v1, v2); break;
|
|
case T_IS_GREATER_OR_EQUAL:
|
|
result = not_less(v1, v2); break;
|
|
case '+':
|
|
result = plus(v1, v2); break;
|
|
case '-':
|
|
result = minus(v1, v2); break;
|
|
case '*':
|
|
result = multiply(v1, v2); break;
|
|
case '/':
|
|
if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
|
|
return ExpressionPtr();
|
|
}
|
|
result = divide(v1, v2); break;
|
|
case '%':
|
|
if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
|
|
return ExpressionPtr();
|
|
}
|
|
result = modulo(v1, v2); break;
|
|
case T_SL:
|
|
result = shift_left(v1, v2); break;
|
|
case T_SR:
|
|
result = shift_right(v1, v2); break;
|
|
case T_BOOLEAN_OR:
|
|
result = v1 || v2; break;
|
|
case T_BOOLEAN_AND:
|
|
result = v1 && v2; break;
|
|
case T_LOGICAL_OR:
|
|
result = v1 || v2; break;
|
|
case T_LOGICAL_AND:
|
|
result = v1 && v2; break;
|
|
case T_INSTANCEOF:
|
|
result = false; break;
|
|
default:
|
|
return ExpressionPtr();
|
|
}
|
|
return makeScalarExpression(ar, result);
|
|
} catch (...) {
|
|
}
|
|
} else {
|
|
switch (m_op) {
|
|
case T_LOGICAL_AND:
|
|
case T_BOOLEAN_AND:
|
|
case T_LOGICAL_OR:
|
|
case T_BOOLEAN_OR: {
|
|
bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND ||
|
|
m_op == T_BOOLEAN_AND);
|
|
ExpressionPtr rep = useFirst ? m_exp1 : m_exp2;
|
|
rep = ExpressionPtr(
|
|
new UnaryOpExpression(
|
|
getScope(), getLocation(),
|
|
rep, T_BOOL_CAST, true));
|
|
rep->setActualType(Type::Boolean);
|
|
if (!useFirst) {
|
|
ExpressionListPtr l(
|
|
new ExpressionList(
|
|
getScope(), getLocation(),
|
|
ExpressionList::ListKindComma));
|
|
l->addElement(m_exp1);
|
|
l->addElement(rep);
|
|
l->setActualType(Type::Boolean);
|
|
rep = l;
|
|
}
|
|
rep->setExpectedType(getExpectedType());
|
|
return replaceValue(rep);
|
|
}
|
|
case T_LOGICAL_XOR:
|
|
case '|':
|
|
case '&':
|
|
case '^':
|
|
case '.':
|
|
case '+':
|
|
case '*':
|
|
optExp = foldRightAssoc(ar);
|
|
if (optExp) return optExp;
|
|
break;
|
|
case T_IS_IDENTICAL:
|
|
case T_IS_NOT_IDENTICAL:
|
|
if (v2.isNull()) {
|
|
return makeIsNull(ar, getLocation(), m_exp1,
|
|
m_op == T_IS_NOT_IDENTICAL);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
ExpressionPtr
|
|
BinaryOpExpression::foldRightAssoc(AnalysisResultConstPtr ar) {
|
|
ExpressionPtr optExp1;
|
|
switch (m_op) {
|
|
case '.':
|
|
case '+':
|
|
case '*':
|
|
if (m_exp1->is(Expression::KindOfBinaryOpExpression)) {
|
|
BinaryOpExpressionPtr binOpExp =
|
|
dynamic_pointer_cast<BinaryOpExpression>(m_exp1);
|
|
if (binOpExp->m_op == m_op) {
|
|
// turn a Op b Op c, namely (a Op b) Op c into a Op (b Op c)
|
|
ExpressionPtr aExp = binOpExp->m_exp1;
|
|
ExpressionPtr bExp = binOpExp->m_exp2;
|
|
ExpressionPtr cExp = m_exp2;
|
|
m_exp1 = aExp;
|
|
m_exp2 = binOpExp = Clone(binOpExp);
|
|
binOpExp->m_exp1 = bExp;
|
|
binOpExp->m_exp2 = cExp;
|
|
if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
|
|
m_exp2 = optExp;
|
|
}
|
|
return static_pointer_cast<Expression>(shared_from_this());
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ExpressionPtr();
|
|
}
|
|
|
|
TypePtr BinaryOpExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
|
|
bool coerce) {
|
|
TypePtr et1; // expected m_exp1's type
|
|
bool coerce1 = false; // whether m_exp1 needs to coerce to et1
|
|
TypePtr et2; // expected m_exp2's type
|
|
bool coerce2 = false; // whether m_exp2 needs to coerce to et2
|
|
TypePtr rt; // return type
|
|
|
|
switch (m_op) {
|
|
case '+':
|
|
case T_PLUS_EQUAL:
|
|
if (coerce && Type::SameType(type, Type::Array)) {
|
|
et1 = et2 = Type::Array;
|
|
coerce1 = coerce2 = true;
|
|
rt = Type::Array;
|
|
} else {
|
|
et1 = Type::PlusOperand;
|
|
et2 = Type::PlusOperand;
|
|
rt = Type::PlusOperand;
|
|
}
|
|
break;
|
|
case '-':
|
|
case '*':
|
|
case T_MINUS_EQUAL:
|
|
case T_MUL_EQUAL:
|
|
case '/':
|
|
case T_DIV_EQUAL:
|
|
et1 = Type::Numeric;
|
|
et2 = Type::Numeric;
|
|
rt = Type::Numeric;
|
|
break;
|
|
case '.':
|
|
et1 = et2 = rt = Type::String;
|
|
break;
|
|
case T_CONCAT_EQUAL:
|
|
et1 = et2 = Type::String;
|
|
rt = Type::Variant;
|
|
break;
|
|
case '%':
|
|
et1 = et2 = Type::Int64;
|
|
rt = Type::Numeric;
|
|
break;
|
|
case T_MOD_EQUAL:
|
|
et1 = Type::Numeric;
|
|
et2 = Type::Int64;
|
|
rt = Type::Numeric;
|
|
break;
|
|
case '|':
|
|
case '&':
|
|
case '^':
|
|
case T_AND_EQUAL:
|
|
case T_OR_EQUAL:
|
|
case T_XOR_EQUAL:
|
|
et1 = Type::Primitive;
|
|
et2 = Type::Primitive;
|
|
rt = Type::Primitive;
|
|
break;
|
|
case T_SL:
|
|
case T_SR:
|
|
case T_SL_EQUAL:
|
|
case T_SR_EQUAL:
|
|
et1 = et2 = rt = Type::Int64;
|
|
break;
|
|
case T_BOOLEAN_OR:
|
|
case T_BOOLEAN_AND:
|
|
case T_LOGICAL_OR:
|
|
case T_LOGICAL_AND:
|
|
case T_LOGICAL_XOR:
|
|
et1 = et2 = rt = Type::Boolean;
|
|
break;
|
|
case '<':
|
|
case T_IS_SMALLER_OR_EQUAL:
|
|
case '>':
|
|
case T_IS_GREATER_OR_EQUAL:
|
|
case T_IS_IDENTICAL:
|
|
case T_IS_NOT_IDENTICAL:
|
|
case T_IS_EQUAL:
|
|
case T_IS_NOT_EQUAL:
|
|
et1 = Type::Some;
|
|
et2 = Type::Some;
|
|
rt = Type::Boolean;
|
|
break;
|
|
case T_INSTANCEOF:
|
|
et1 = Type::Any;
|
|
et2 = Type::String;
|
|
rt = Type::Boolean;
|
|
break;
|
|
case T_COLLECTION:
|
|
et1 = Type::Any;
|
|
et2 = Type::Any;
|
|
rt = Type::Object;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
switch (m_op) {
|
|
case T_PLUS_EQUAL:
|
|
{
|
|
TypePtr rhs = m_exp2->inferAndCheck(ar, et2, coerce2);
|
|
TypePtr lhs = m_exp1->inferAndCheck(ar, Type::Any, true);
|
|
if (lhs) {
|
|
if (lhs->mustBe(Type::KindOfArray)) {
|
|
TypePtr a2(m_exp2->getActualType());
|
|
if (a2 && a2->is(Type::KindOfArray)) {
|
|
m_exp2->setExpectedType(a2);
|
|
}
|
|
rt = Type::Array;
|
|
break;
|
|
}
|
|
if (lhs->mustBe(Type::KindOfNumeric)) {
|
|
if (!rhs->mustBe(lhs->getKindOf())) {
|
|
rhs = Type::combinedArithmeticType(lhs, rhs);
|
|
if (!rhs) rhs = Type::Numeric;
|
|
m_exp1->inferAndCheck(ar, rhs, true);
|
|
}
|
|
TypePtr a1(m_exp1->getCPPType());
|
|
TypePtr a2(m_exp2->getActualType());
|
|
if (a1 && a1->mustBe(Type::KindOfNumeric) &&
|
|
a2 && a2->mustBe(Type::KindOfNumeric)) {
|
|
// both LHS and RHS are numeric.
|
|
// Set the expected type of RHS to be
|
|
// the stronger type
|
|
TypePtr t = a1->getKindOf() > a2->getKindOf() ? a1 : a2;
|
|
m_exp2->setExpectedType(t);
|
|
}
|
|
rt = Type::Numeric;
|
|
break;
|
|
}
|
|
}
|
|
m_exp1->inferAndCheck(ar, rhs, true);
|
|
}
|
|
break;
|
|
case T_MINUS_EQUAL:
|
|
case T_MUL_EQUAL:
|
|
case T_DIV_EQUAL:
|
|
case T_MOD_EQUAL:
|
|
case T_AND_EQUAL:
|
|
case T_OR_EQUAL:
|
|
case T_XOR_EQUAL:
|
|
case T_SL_EQUAL:
|
|
case T_SR_EQUAL:
|
|
{
|
|
TypePtr ret = m_exp2->inferAndCheck(ar, et2, coerce2);
|
|
m_exp1->inferAndCheck(ar, ret, true);
|
|
}
|
|
break;
|
|
case T_CONCAT_EQUAL:
|
|
{
|
|
TypePtr ret = m_exp2->inferAndCheck(ar, et2, coerce2);
|
|
m_exp1->inferAndCheck(ar, Type::String, true);
|
|
TypePtr act1 = m_exp1->getActualType();
|
|
if (act1 && act1->is(Type::KindOfString)) rt = Type::String;
|
|
}
|
|
break;
|
|
case '+':
|
|
case '-':
|
|
case '*':
|
|
{
|
|
m_exp1->inferAndCheck(ar, et1, coerce1);
|
|
m_exp2->inferAndCheck(ar, et2, coerce2);
|
|
TypePtr act1 = m_exp1->getActualType();
|
|
TypePtr act2 = m_exp2->getActualType();
|
|
|
|
TypePtr combined = Type::combinedArithmeticType(act1, act2);
|
|
if (combined && combined->isSubsetOf(rt)) {
|
|
if (act1) m_exp1->setExpectedType(act1);
|
|
if (act2) m_exp2->setExpectedType(act2);
|
|
rt = combined;
|
|
} else if (m_op == '+') {
|
|
bool a1 = act1 && act1->is(Type::KindOfArray);
|
|
bool a2 = act2 && act2->is(Type::KindOfArray);
|
|
if (a1 || a2) {
|
|
m_implementedType.reset();
|
|
if (!a1) {
|
|
m_implementedType = Type::Variant;
|
|
} else if (!a2) {
|
|
m_exp1->setExpectedType(Type::Array);
|
|
// in this case, the implemented type will
|
|
// actually be Type::Array (since Array::operator+
|
|
// returns an Array)
|
|
} else {
|
|
m_exp1->setExpectedType(Type::Array);
|
|
m_exp2->setExpectedType(Type::Array);
|
|
}
|
|
rt = Type::Array;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
m_exp1->inferAndCheck(ar, et1, coerce1);
|
|
m_exp2->inferAndCheck(ar, et2, coerce2);
|
|
break;
|
|
}
|
|
|
|
return rt;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// code generation functions
|
|
|
|
void BinaryOpExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
m_exp1->outputPHP(cg, ar);
|
|
|
|
switch (m_op) {
|
|
case T_PLUS_EQUAL: cg_printf(" += "); break;
|
|
case T_MINUS_EQUAL: cg_printf(" -= "); break;
|
|
case T_MUL_EQUAL: cg_printf(" *= "); break;
|
|
case T_DIV_EQUAL: cg_printf(" /= "); break;
|
|
case T_CONCAT_EQUAL: cg_printf(" .= "); break;
|
|
case T_MOD_EQUAL: cg_printf(" %%= "); break;
|
|
case T_AND_EQUAL: cg_printf(" &= "); break;
|
|
case T_OR_EQUAL: cg_printf(" |= "); break;
|
|
case T_XOR_EQUAL: cg_printf(" ^= "); break;
|
|
case T_SL_EQUAL: cg_printf(" <<= "); break;
|
|
case T_SR_EQUAL: cg_printf(" >>= "); break;
|
|
case T_BOOLEAN_OR: cg_printf(" || "); break;
|
|
case T_BOOLEAN_AND: cg_printf(" && "); break;
|
|
case T_LOGICAL_OR: cg_printf(" or "); break;
|
|
case T_LOGICAL_AND: cg_printf(" and "); break;
|
|
case T_LOGICAL_XOR: cg_printf(" xor "); break;
|
|
case '|': cg_printf(" | "); break;
|
|
case '&': cg_printf(" & "); break;
|
|
case '^': cg_printf(" ^ "); break;
|
|
case '.': cg_printf(" . "); break;
|
|
case '+': cg_printf(" + "); break;
|
|
case '-': cg_printf(" - "); break;
|
|
case '*': cg_printf(" * "); break;
|
|
case '/': cg_printf(" / "); break;
|
|
case '%': cg_printf(" %% "); break;
|
|
case T_SL: cg_printf(" << "); break;
|
|
case T_SR: cg_printf(" >> "); break;
|
|
case T_IS_IDENTICAL: cg_printf(" === "); break;
|
|
case T_IS_NOT_IDENTICAL: cg_printf(" !== "); break;
|
|
case T_IS_EQUAL: cg_printf(" == "); break;
|
|
case T_IS_NOT_EQUAL: cg_printf(" != "); break;
|
|
case '<': cg_printf(" < "); break;
|
|
case T_IS_SMALLER_OR_EQUAL: cg_printf(" <= "); break;
|
|
case '>': cg_printf(" > "); break;
|
|
case T_IS_GREATER_OR_EQUAL: cg_printf(" >= "); break;
|
|
case T_INSTANCEOF: cg_printf(" instanceof "); break;
|
|
case T_COLLECTION: {
|
|
ExpressionListPtr el = static_pointer_cast<ExpressionList>(m_exp2);
|
|
if (el->getCount() == 0) {
|
|
cg_printf(" {}");
|
|
} else {
|
|
cg_printf(" { ");
|
|
el->outputPHP(cg, ar);
|
|
cg_printf(" }");
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
m_exp2->outputPHP(cg, ar);
|
|
}
|
|
|
|
static bool castIfNeeded(TypePtr top, TypePtr arg,
|
|
CodeGenerator &cg, AnalysisResultPtr ar,
|
|
BlockScopeRawPtr scope) {
|
|
if (top) {
|
|
if (top->isPrimitive()) {
|
|
if (!arg || !arg->isPrimitive()) {
|
|
top->outputCPPCast(cg, ar, scope);
|
|
cg_printf("(");
|
|
return true;
|
|
}
|
|
} else if (top->is(Type::KindOfArray)) {
|
|
if (arg && arg->isExactType() && !arg->is(Type::KindOfArray)) {
|
|
cg_printf("((Variant)");
|
|
return true;
|
|
}
|
|
} else if (top->mustBe(Type::KindOfNumeric)) {
|
|
if (arg && arg->is(Type::KindOfArray)) {
|
|
cg_printf("((Variant)");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void BinaryOpExpression::preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar,
|
|
int state) {
|
|
if (hasCPPTemp() || isScalar()) return;
|
|
if (m_op == '.' && (state & FixOrder)) {
|
|
if (m_exp1) {
|
|
if (!m_exp1->getActualType() && m_exp1->hasCPPTemp()) {
|
|
cg_printf("id(%s);\n", m_exp1->cppTemp().c_str());
|
|
}
|
|
m_exp1->preOutputStash(cg, ar, state|StashVars);
|
|
}
|
|
if (m_exp2) {
|
|
if (!m_exp2->getActualType() && m_exp2->hasCPPTemp()) {
|
|
cg_printf("id(%s);\n", m_exp2->cppTemp().c_str());
|
|
}
|
|
m_exp2->preOutputStash(cg, ar, state|StashVars);
|
|
}
|
|
} else {
|
|
Expression::preOutputStash(cg, ar, state);
|
|
}
|
|
}
|
|
|
|
static const char *stringBufferPrefix(CodeGenerator &cg, AnalysisResultPtr ar,
|
|
ExpressionPtr var) {
|
|
if (var->is(Expression::KindOfSimpleVariable)) {
|
|
if (LoopStatementPtr loop = cg.getLoopStatement()) {
|
|
SimpleVariablePtr sv = static_pointer_cast<SimpleVariable>(var);
|
|
if (loop->checkStringBuf(sv->getName())) {
|
|
return loop->getScope()->getVariables()->
|
|
getVariablePrefix(sv->getName());
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static std::string stringBufferName(const char *temp, const char *prefix,
|
|
const char *name)
|
|
{
|
|
return std::string(temp) + "_sbuf_" + prefix + name;
|
|
}
|
|
|
|
int BinaryOpExpression::getConcatList(ExpressionPtrVec &ev, ExpressionPtr exp,
|
|
bool &hasVoid) {
|
|
if (!exp->hasCPPTemp()) {
|
|
if (exp->is(Expression::KindOfUnaryOpExpression)) {
|
|
UnaryOpExpressionPtr u = static_pointer_cast<UnaryOpExpression>(exp);
|
|
if (u->getOp() == '(') {
|
|
return getConcatList(ev, u->getExpression(), hasVoid);
|
|
}
|
|
} else if (exp->is(Expression::KindOfBinaryOpExpression)) {
|
|
BinaryOpExpressionPtr b = static_pointer_cast<BinaryOpExpression>(exp);
|
|
if (b->getOp() == '.') {
|
|
if (b->getExp1()->is(Expression::KindOfSimpleVariable) &&
|
|
b->getExp1()->isLocalExprAltered() &&
|
|
!b->getExp1()->hasCPPTemp() &&
|
|
b->getExp2()->hasEffect() &&
|
|
!b->getExp2()->hasCPPTemp()) {
|
|
/*
|
|
In this case, the simple variable must be evaluated
|
|
after b->getExp2(). But when we output a concat list we
|
|
explicitly order the expressions from left to right.
|
|
*/
|
|
} else {
|
|
return getConcatList(ev, b->getExp1(), hasVoid) +
|
|
getConcatList(ev, b->getExp2(), hasVoid);
|
|
}
|
|
}
|
|
} else if (exp->is(Expression::KindOfEncapsListExpression)) {
|
|
EncapsListExpressionPtr e =
|
|
static_pointer_cast<EncapsListExpression>(exp);
|
|
if (e->getType() != '`') {
|
|
ExpressionListPtr el = e->getExpressions();
|
|
int num = 0;
|
|
for (int i = 0, s = el->getCount(); i < s; i++) {
|
|
ExpressionPtr exp = (*el)[i];
|
|
num += getConcatList(ev, exp, hasVoid);
|
|
}
|
|
return num;
|
|
}
|
|
}
|
|
} else if (!exp->getActualType()) {
|
|
return 0;
|
|
}
|
|
|
|
ev.push_back(exp);
|
|
bool isVoid = !exp->getActualType();
|
|
hasVoid |= isVoid;
|
|
return isVoid ? 0 : 1;
|
|
}
|
|
|
|
static void outputStringExpr(CodeGenerator &cg, AnalysisResultPtr ar,
|
|
ExpressionPtr exp, bool asLitStr) {
|
|
if (asLitStr && exp->isLiteralString()) {
|
|
const std::string &s = exp->getLiteralString();
|
|
std::string enc = string_cplus_escape(s.c_str(), s.size());
|
|
cg_printf("\"%s\", %d", enc.c_str(), (int)s.size());
|
|
return;
|
|
}
|
|
|
|
TypePtr et(exp->getExpectedType());
|
|
exp->setExpectedType(Type::String);
|
|
exp->outputCPP(cg, ar);
|
|
exp->setExpectedType(et);
|
|
}
|
|
|
|
static void outputStringBufExprs(ExpressionPtrVec &ev,
|
|
CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
for (size_t i = 0; i < ev.size(); i++) {
|
|
ExpressionPtr exp = ev[i];
|
|
cg_printf(".addWithTaint(");
|
|
outputStringExpr(cg, ar, exp, true);
|
|
cg_printf(")");
|
|
}
|
|
}
|
|
|
|
bool BinaryOpExpression::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
|
|
int state) {
|
|
if (isOpEqual() && (m_exp1->is(KindOfArrayElementExpression) ||
|
|
m_exp1->is(KindOfObjectPropertyExpression))) {
|
|
return Expression::preOutputCPP(cg, ar, state);
|
|
}
|
|
|
|
bool effect2 = m_exp2->hasEffect();
|
|
const char *prefix = 0;
|
|
if (effect2 || m_exp1->hasEffect()) {
|
|
ExpressionPtr self = static_pointer_cast<Expression>(shared_from_this());
|
|
ExpressionPtrVec ev;
|
|
bool hasVoid = false;
|
|
int numConcat = 0;
|
|
bool ok = false;
|
|
if (m_op == '.') {
|
|
numConcat = getConcatList(ev, self, hasVoid);
|
|
ok = hasVoid || (numConcat > MAX_CONCAT_ARGS);
|
|
} else if (effect2 && m_op == T_CONCAT_EQUAL) {
|
|
prefix = stringBufferPrefix(cg, ar, m_exp1);
|
|
ok = prefix;
|
|
if (!ok) {
|
|
if (m_exp1->is(KindOfSimpleVariable)) {
|
|
ok = true;
|
|
ev.push_back(m_exp1);
|
|
numConcat++;
|
|
}
|
|
}
|
|
if (ok) {
|
|
numConcat += getConcatList(ev, m_exp2, hasVoid);
|
|
if (numConcat <= 2 && !prefix) {
|
|
return Expression::preOutputCPP(cg, ar, state);
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
if (!cg.inExpression()) return true;
|
|
|
|
cg.wrapExpressionBegin();
|
|
std::string buf;
|
|
if (prefix) {
|
|
SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(m_exp1));
|
|
buf = stringBufferName(Option::TempPrefix, prefix,
|
|
sv->getName().c_str());
|
|
m_cppTemp = "/**/";
|
|
} else if (numConcat) {
|
|
buf = m_cppTemp = genCPPTemp(cg, ar);
|
|
buf += "_buf";
|
|
if (numConcat > 1) {
|
|
cg_printf("StringBuffer %s;\n", buf.c_str());
|
|
}
|
|
} else {
|
|
m_cppTemp = "\"\"";
|
|
}
|
|
|
|
for (size_t i = 0; i < ev.size(); i++) {
|
|
ExpressionPtr exp = ev[i];
|
|
bool is_void = !exp->getActualType();
|
|
exp->preOutputCPP(cg, ar, 0);
|
|
if (!is_void) {
|
|
bool asLit = true;
|
|
if (numConcat > 1 || prefix) {
|
|
cg_printf("%s.appendWithTaint(", buf.c_str());
|
|
} else {
|
|
asLit = false;
|
|
cg_printf("CStrRef %s = (", m_cppTemp.c_str());
|
|
}
|
|
outputStringExpr(cg, ar, exp, asLit);
|
|
cg_printf(")");
|
|
} else {
|
|
exp->outputCPPUnneeded(cg, ar);
|
|
}
|
|
cg_printf(";\n");
|
|
}
|
|
|
|
if (numConcat > 1 && !prefix) {
|
|
cg_printf("CStrRef %s(%s.detachWithTaint());\n",
|
|
m_cppTemp.c_str(), buf.c_str());
|
|
if (m_op == T_CONCAT_EQUAL) {
|
|
m_exp1->outputCPP(cg, ar);
|
|
cg_printf(" = %s;\n", m_cppTemp.c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!isShortCircuitOperator()) {
|
|
return Expression::preOutputCPP(cg, ar, state);
|
|
}
|
|
|
|
if (!effect2) {
|
|
bool ret = m_exp1->preOutputCPP(cg, ar, 0);
|
|
if (state & FixOrder) {
|
|
ret = true;
|
|
if (cg.inExpression()) {
|
|
preOutputStash(cg, ar, state);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool fix_e1 = m_exp1->preOutputCPP(cg, ar, 0);
|
|
if (!cg.inExpression()) {
|
|
return fix_e1 || m_exp2->preOutputCPP(cg, ar, 0);
|
|
}
|
|
|
|
cg.setInExpression(false);
|
|
bool fix_e2 = m_exp2->preOutputCPP(cg, ar, 0);
|
|
cg.setInExpression(true);
|
|
|
|
if (fix_e2) {
|
|
cg.wrapExpressionBegin();
|
|
std::string tmp = genCPPTemp(cg, ar);
|
|
cg_printf("bool %s = (", tmp.c_str());
|
|
m_exp1->outputCPP(cg, ar);
|
|
cg_printf(");\n");
|
|
cg_indentBegin("if (%s%s) {\n",
|
|
m_op == T_LOGICAL_OR || m_op == T_BOOLEAN_OR ? "!" : "",
|
|
tmp.c_str());
|
|
m_exp2->preOutputCPP(cg, ar, 0);
|
|
if (isUnused()) {
|
|
if (m_exp2->outputCPPUnneeded(cg, ar)) cg_printf(";\n");
|
|
} else {
|
|
cg_printf("%s = (", tmp.c_str());
|
|
m_exp2->outputCPP(cg, ar);
|
|
cg_printf(");\n");
|
|
}
|
|
cg_indentEnd("}\n");
|
|
m_cppTemp = tmp;
|
|
} else if (state & FixOrder) {
|
|
preOutputStash(cg, ar, state);
|
|
fix_e1 = true;
|
|
}
|
|
return fix_e1 || fix_e2;
|
|
}
|
|
|
|
bool BinaryOpExpression::isOpEqual() {
|
|
switch (m_op) {
|
|
case T_CONCAT_EQUAL:
|
|
case T_PLUS_EQUAL:
|
|
case T_MINUS_EQUAL:
|
|
case T_MUL_EQUAL:
|
|
case T_DIV_EQUAL:
|
|
case T_MOD_EQUAL:
|
|
case T_AND_EQUAL:
|
|
case T_OR_EQUAL:
|
|
case T_XOR_EQUAL:
|
|
case T_SL_EQUAL:
|
|
case T_SR_EQUAL:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BinaryOpExpression::outputCPPImplOpEqual(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
if (m_exp1->is(Expression::KindOfArrayElementExpression)) {
|
|
ArrayElementExpressionPtr exp =
|
|
dynamic_pointer_cast<ArrayElementExpression>(m_exp1);
|
|
if (exp->isSuperGlobal() || exp->isDynamicGlobal()) return false;
|
|
if (TypePtr t = exp->getVariable()->getActualType()) {
|
|
TypePtr it(exp->getVariable()->getImplementedType());
|
|
if (t->is(Type::KindOfArray) &&
|
|
(!it ||
|
|
it->is(Type::KindOfArray) ||
|
|
Type::IsMappedToVariant(it) /* fast cast will kick in */)) {
|
|
return false;
|
|
}
|
|
}
|
|
// turning $a['elem'] Op= $b into $a.setOpEqual('elem', $b);
|
|
exp->getVariable()->outputCPP(cg, ar);
|
|
if (exp->getOffset()) {
|
|
cg_printf(".setOpEqual(%d, ", m_op);
|
|
exp->getOffset()->outputCPP(cg, ar);
|
|
cg_printf(", (");
|
|
} else {
|
|
cg_printf(".appendOpEqual(%d, (", m_op);
|
|
}
|
|
m_exp2->outputCPP(cg, ar);
|
|
cg_printf(")");
|
|
ExpressionPtr off = exp->getOffset();
|
|
if (off) {
|
|
ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off);
|
|
if (sc) {
|
|
if (sc->isLiteralString()) {
|
|
String s(sc->getLiteralString());
|
|
int64 n;
|
|
if (!s.get()->isStrictlyInteger(n)) {
|
|
cg_printf(", true"); // skip toKey() at run time
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cg_printf(")");
|
|
|
|
return true;
|
|
}
|
|
if (m_exp1->is(Expression::KindOfObjectPropertyExpression)) {
|
|
ObjectPropertyExpressionPtr var(
|
|
dynamic_pointer_cast<ObjectPropertyExpression>(m_exp1));
|
|
if (var->isValid()) return false;
|
|
var->outputCPPObject(cg, ar);
|
|
cg_printf("o_assign_op<%s,%d>(",
|
|
isUnused() ? "void" : "Variant", m_op);
|
|
var->outputCPPProperty(cg, ar);
|
|
cg_printf(", ");
|
|
m_exp2->outputCPP(cg, ar);
|
|
cg_printf("%s)", originalClassName(cg, true).c_str());
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void BinaryOpExpression::outputCPPImpl(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
|
|
if (isOpEqual() && outputCPPImplOpEqual(cg, ar)) return;
|
|
|
|
bool wrapped = true;
|
|
switch (m_op) {
|
|
case T_CONCAT_EQUAL:
|
|
if (const char *prefix = stringBufferPrefix(cg, ar, m_exp1)) {
|
|
SimpleVariablePtr sv = static_pointer_cast<SimpleVariable>(m_exp1);
|
|
if (m_exp2->hasCPPTemp()) {
|
|
cg_printf("%s.appendWithTaint(%s)",
|
|
stringBufferName(Option::TempPrefix, prefix,
|
|
sv->getName().c_str()).c_str(),
|
|
m_exp2->cppTemp().c_str());
|
|
return;
|
|
} else {
|
|
ExpressionPtrVec ev;
|
|
bool hasVoid = false;
|
|
getConcatList(ev, m_exp2, hasVoid);
|
|
if (!hasVoid) {
|
|
cg_printf("%s", stringBufferName(Option::TempPrefix, prefix,
|
|
sv->getName().c_str()).c_str());
|
|
outputStringBufExprs(ev, cg, ar);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
cg_printf("concat_assign");
|
|
break;
|
|
case '.':
|
|
{
|
|
ExpressionPtr self = static_pointer_cast<Expression>(shared_from_this());
|
|
ExpressionPtrVec ev;
|
|
bool hasVoid = false;
|
|
int num = getConcatList(ev, self, hasVoid);
|
|
if (num < 2) {
|
|
cg_printf("concat");
|
|
break;
|
|
}
|
|
always_assert(!hasVoid);
|
|
if (num <= MAX_CONCAT_ARGS) {
|
|
if (num == 2) {
|
|
cg_printf("concat(");
|
|
} else {
|
|
if (num > MAX_CONCAT_ARGS) ar->m_concatLengths.insert(num);
|
|
cg_printf("concat%d(", num);
|
|
}
|
|
for (size_t i = 0; i < ev.size(); i++) {
|
|
ExpressionPtr exp = ev[i];
|
|
if (i) cg_printf(", ");
|
|
outputStringExpr(cg, ar, exp, false);
|
|
}
|
|
cg_printf(")");
|
|
} else {
|
|
cg_printf("StringBuffer()");
|
|
outputStringBufExprs(ev, cg, ar);
|
|
cg_printf(".detachWithTaint()");
|
|
}
|
|
}
|
|
return;
|
|
case T_LOGICAL_XOR: cg_printf("logical_xor"); break;
|
|
case '|': cg_printf("bitwise_or"); break;
|
|
case '&': cg_printf("bitwise_and"); break;
|
|
case '^': cg_printf("bitwise_xor"); break;
|
|
case T_IS_IDENTICAL: cg_printf("same"); break;
|
|
case T_IS_NOT_IDENTICAL: cg_printf("!same"); break;
|
|
case T_IS_EQUAL: cg_printf("equal"); break;
|
|
case T_IS_NOT_EQUAL: cg_printf("!equal"); break;
|
|
case '<': cg_printf("less"); break;
|
|
case T_IS_SMALLER_OR_EQUAL: cg_printf("not_more"); break;
|
|
case '>': cg_printf("more"); break;
|
|
case T_IS_GREATER_OR_EQUAL: cg_printf("not_less"); break;
|
|
case '/': cg_printf("divide"); break;
|
|
case '%': cg_printf("modulo"); break;
|
|
case T_INSTANCEOF: cg_printf("instanceOf"); break;
|
|
case T_COLLECTION: cg_printf("Object"); break;
|
|
default:
|
|
wrapped = !isUnused();
|
|
break;
|
|
}
|
|
|
|
if (wrapped) cg_printf("(");
|
|
|
|
ExpressionPtr first = m_exp1;
|
|
ExpressionPtr second = m_exp2;
|
|
|
|
// we could implement these functions natively on String and Array classes
|
|
switch (m_op) {
|
|
case '+':
|
|
case '-':
|
|
case '*':
|
|
case '/':
|
|
if (!first->outputCPPArithArg(cg, ar, m_op == '+')) {
|
|
TypePtr argType = first->hasCPPTemp() ?
|
|
first->getType() : first->getActualType();
|
|
bool flag = castIfNeeded(getActualType(), argType, cg, ar, getScope());
|
|
first->outputCPP(cg, ar);
|
|
if (flag) {
|
|
cg_printf(")");
|
|
}
|
|
}
|
|
break;
|
|
case T_SL:
|
|
case T_SR:
|
|
assert(first->getType()->is(Type::KindOfInt64));
|
|
first->outputCPP(cg, ar);
|
|
break;
|
|
case T_COLLECTION:
|
|
/* do nothing */
|
|
break;
|
|
default:
|
|
first->outputCPP(cg, ar);
|
|
break;
|
|
}
|
|
|
|
switch (m_op) {
|
|
case T_PLUS_EQUAL: cg_printf(" += "); break;
|
|
case T_MINUS_EQUAL: cg_printf(" -= "); break;
|
|
case T_MUL_EQUAL: cg_printf(" *= "); break;
|
|
case T_DIV_EQUAL: cg_printf(" /= "); break;
|
|
case T_MOD_EQUAL: cg_printf(" %%= "); break;
|
|
case T_AND_EQUAL: cg_printf(" &= "); break;
|
|
case T_OR_EQUAL: cg_printf(" |= "); break;
|
|
case T_XOR_EQUAL: cg_printf(" ^= "); break;
|
|
case T_SL_EQUAL: cg_printf(" <<= "); break;
|
|
case T_SR_EQUAL: cg_printf(" >>= "); break;
|
|
case T_BOOLEAN_OR: cg_printf(" || "); break;
|
|
case T_BOOLEAN_AND: cg_printf(" && "); break;
|
|
case T_LOGICAL_OR: cg_printf(" || "); break;
|
|
case T_LOGICAL_AND: cg_printf(" && "); break;
|
|
case T_COLLECTION: /* print nothing */ break;
|
|
default:
|
|
switch (m_op) {
|
|
case '+': cg_printf(" + "); break;
|
|
case '-': cg_printf(" - "); break;
|
|
case '*': cg_printf(" * "); break;
|
|
case T_SL: cg_printf(" << "); break;
|
|
case T_SR: cg_printf(" >> "); break;
|
|
default:
|
|
cg_printf(", ");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (m_op) {
|
|
case '+':
|
|
case '-':
|
|
case '*':
|
|
case '/':
|
|
if (!second->outputCPPArithArg(cg, ar, m_op == '+')) {
|
|
TypePtr argType = second->hasCPPTemp() ?
|
|
second->getType() : second->getActualType();
|
|
bool flag = castIfNeeded(getActualType(), argType, cg, ar, getScope());
|
|
second->outputCPP(cg, ar);
|
|
if (flag) {
|
|
cg_printf(")");
|
|
}
|
|
}
|
|
break;
|
|
case T_INSTANCEOF:
|
|
{
|
|
if (second->isScalar()) {
|
|
ScalarExpressionPtr scalar =
|
|
dynamic_pointer_cast<ScalarExpression>(second);
|
|
bool notQuoted = scalar && !scalar->isQuoted();
|
|
std::string s = second->getLiteralString();
|
|
if (s == "static" && notQuoted) {
|
|
cg_printf("FrameInjection::GetStaticClassName(fi.getThreadInfo())");
|
|
} else if (s != "") {
|
|
if (s == "self" && notQuoted) {
|
|
ClassScopeRawPtr cls = getOriginalClass();
|
|
if (cls) {
|
|
s = cls->getOriginalName();
|
|
}
|
|
} else if (s == "parent" && notQuoted) {
|
|
ClassScopeRawPtr cls = getOriginalClass();
|
|
if (cls && !cls->getOriginalParent().empty()) {
|
|
s = cls->getOriginalParent();
|
|
}
|
|
}
|
|
cg_printString(s, ar, shared_from_this());
|
|
} else {
|
|
second->outputCPP(cg, ar);
|
|
}
|
|
} else {
|
|
second->outputCPP(cg, ar);
|
|
}
|
|
break;
|
|
}
|
|
case T_PLUS_EQUAL:
|
|
case T_MINUS_EQUAL:
|
|
case T_MUL_EQUAL:
|
|
{
|
|
TypePtr t1 = first->getCPPType();
|
|
TypePtr t2 = second->getType();
|
|
if (t1 && !t1->is(Type::KindOfArray) &&
|
|
t2 && Type::IsCastNeeded(ar, t2, t1)) {
|
|
t1->outputCPPCast(cg, ar, getScope());
|
|
cg_printf("(");
|
|
second->outputCPP(cg, ar);
|
|
cg_printf(")");
|
|
} else {
|
|
second->outputCPP(cg, ar);
|
|
}
|
|
break;
|
|
}
|
|
case T_BOOLEAN_OR:
|
|
case T_BOOLEAN_AND:
|
|
case T_LOGICAL_AND:
|
|
case T_LOGICAL_OR:
|
|
if (isUnused()) {
|
|
cg_printf("(");
|
|
if (second->outputCPPUnneeded(cg, ar)) {
|
|
cg_printf(",");
|
|
}
|
|
cg_printf("false)");
|
|
} else {
|
|
second->outputCPP(cg, ar);
|
|
}
|
|
break;
|
|
default:
|
|
second->outputCPP(cg, ar);
|
|
}
|
|
|
|
if (wrapped) cg_printf(")");
|
|
}
|