/* +----------------------------------------------------------------------+ | 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; } else if (strcasecmp(s.c_str(), "tuple") == 0) { cType = Collection::TupleType; } ExpressionListPtr el = static_pointer_cast(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(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(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(cp); break; case 1: m_exp2 = boost::dynamic_pointer_cast(cp); break; default: assert(false); break; } } bool BinaryOpExpression::canonCompare(ExpressionPtr e) const { return Expression::canonCompare(e) && getOp() == static_cast(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(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_t 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_t 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(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(shared_from_this()); } } break; default: break; } } return ExpressionPtr(); } if (m_exp1->isScalar()) { if (!m_exp1->getScalarValue(v1)) return ExpressionPtr(); try { if (!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(m_exp1); ScalarExpressionPtr scalar2 = dynamic_pointer_cast(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 = less_or_equal(v1, v2); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = more_or_equal(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(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(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(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); } 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; }