2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/expression/binary_op_expression.h"
18 #include "hphp/compiler/expression/array_element_expression.h"
19 #include "hphp/compiler/expression/object_property_expression.h"
20 #include "hphp/compiler/expression/unary_op_expression.h"
21 #include "hphp/util/parser/hphp.tab.hpp"
22 #include "hphp/compiler/expression/scalar_expression.h"
23 #include "hphp/compiler/expression/constant_expression.h"
24 #include "hphp/runtime/base/complex_types.h"
25 #include "hphp/runtime/base/type_conversions.h"
26 #include "hphp/runtime/base/builtin_functions.h"
27 #include "hphp/runtime/base/comparisons.h"
28 #include "hphp/runtime/base/zend-string.h"
29 #include "hphp/compiler/expression/expression_list.h"
30 #include "hphp/compiler/expression/encaps_list_expression.h"
31 #include "hphp/compiler/expression/simple_function_call.h"
32 #include "hphp/compiler/expression/simple_variable.h"
33 #include "hphp/compiler/statement/loop_statement.h"
34 #include "hphp/runtime/base/tv_arith.h"
38 ///////////////////////////////////////////////////////////////////////////////
39 // constructors/destructors
41 BinaryOpExpression::BinaryOpExpression
42 (EXPRESSION_CONSTRUCTOR_PARAMETERS
,
43 ExpressionPtr exp1
, ExpressionPtr exp2
, int op
)
44 : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(BinaryOpExpression
)),
45 m_exp1(exp1
), m_exp2(exp2
), m_op(op
), m_assign(false), m_canThrow(false) {
59 m_exp1
->setContext(Expression::LValue
);
60 m_exp1
->setContext(Expression::OprLValue
);
61 m_exp1
->setContext(Expression::DeepOprLValue
);
62 if (m_exp1
->is(Expression::KindOfObjectPropertyExpression
)) {
63 m_exp1
->setContext(Expression::NoLValueWrapper
);
67 std::string s
= m_exp1
->getLiteralString();
69 if (strcasecmp(s
.c_str(), "vector") == 0) {
70 cType
= Collection::VectorType
;
71 } else if (strcasecmp(s
.c_str(), "map") == 0) {
72 cType
= Collection::MapType
;
73 } else if (strcasecmp(s
.c_str(), "stablemap") == 0) {
74 cType
= Collection::StableMapType
;
75 } else if (strcasecmp(s
.c_str(), "set") == 0) {
76 cType
= Collection::SetType
;
77 } else if (strcasecmp(s
.c_str(), "pair") == 0) {
78 cType
= Collection::PairType
;
80 ExpressionListPtr el
= static_pointer_cast
<ExpressionList
>(m_exp2
);
81 el
->setCollectionType(cType
);
89 ExpressionPtr
BinaryOpExpression::clone() {
90 BinaryOpExpressionPtr
exp(new BinaryOpExpression(*this));
91 Expression::deepCopy(exp
);
92 exp
->m_exp1
= Clone(m_exp1
);
93 exp
->m_exp2
= Clone(m_exp2
);
97 bool BinaryOpExpression::isTemporary() const {
116 bool BinaryOpExpression::isRefable(bool checkError
/* = false */) const {
117 return checkError
&& m_assign
;
120 bool BinaryOpExpression::isLiteralString() const {
122 return m_exp1
->isLiteralString() && m_exp2
->isLiteralString();
127 std::string
BinaryOpExpression::getLiteralString() const {
129 return m_exp1
->getLiteralString() + m_exp2
->getLiteralString();
134 bool BinaryOpExpression::containsDynamicConstant(AnalysisResultPtr ar
) const {
137 return m_exp2
->containsDynamicConstant(ar
);
144 bool BinaryOpExpression::isShortCircuitOperator() const {
157 bool BinaryOpExpression::isLogicalOrOperator() const {
168 ExpressionPtr
BinaryOpExpression::unneededHelper() {
169 bool shortCircuit
= isShortCircuitOperator();
170 if (!m_exp2
->getContainedEffects() ||
171 (!shortCircuit
&& !m_exp1
->getContainedEffects())) {
172 return Expression::unneededHelper();
176 m_exp2
= m_exp2
->unneeded();
177 m_exp2
->setExpectedType(Type::Boolean
);
179 return static_pointer_cast
<Expression
>(shared_from_this());
182 ///////////////////////////////////////////////////////////////////////////////
183 // static analysis functions
185 int BinaryOpExpression::getLocalEffects() const {
186 int effect
= NoEffect
;
194 if (!m_exp2
->getScalarValue(v2
) || equal(v2
, 0)) {
203 if (m_assign
) effect
|= AssignEffect
;
207 void BinaryOpExpression::analyzeProgram(AnalysisResultPtr ar
) {
208 if (ar
->getPhase() == AnalysisResult::AnalyzeFinal
&&
209 m_op
== T_INSTANCEOF
&& m_exp2
->is(Expression::KindOfScalarExpression
)) {
210 ScalarExpressionPtr s
= dynamic_pointer_cast
<ScalarExpression
>(m_exp2
);
211 addUserClass(ar
, s
->getString());
213 m_exp1
->analyzeProgram(ar
);
214 m_exp2
->analyzeProgram(ar
);
217 ExpressionPtr
BinaryOpExpression::simplifyLogical(AnalysisResultConstPtr ar
) {
219 ExpressionPtr rep
= foldConst(ar
);
220 if (rep
) return replaceValue(rep
);
221 } catch (const Exception
& e
) {
223 return ExpressionPtr();
226 ConstructPtr
BinaryOpExpression::getNthKid(int n
) const {
236 return ConstructPtr();
239 int BinaryOpExpression::getKidCount() const {
243 void BinaryOpExpression::setNthKid(int n
, ConstructPtr cp
) {
246 m_exp1
= boost::dynamic_pointer_cast
<Expression
>(cp
);
249 m_exp2
= boost::dynamic_pointer_cast
<Expression
>(cp
);
257 bool BinaryOpExpression::canonCompare(ExpressionPtr e
) const {
258 return Expression::canonCompare(e
) &&
259 getOp() == static_cast<BinaryOpExpression
*>(e
.get())->getOp();
262 ExpressionPtr
BinaryOpExpression::preOptimize(AnalysisResultConstPtr ar
) {
263 if (!m_exp2
->isScalar()) {
264 if (!m_exp1
->isScalar()) {
265 if (m_exp1
->is(KindOfBinaryOpExpression
)) {
266 BinaryOpExpressionPtr
b(
267 dynamic_pointer_cast
<BinaryOpExpression
>(m_exp1
));
268 if (b
->m_op
== m_op
&& b
->m_exp1
->isScalar()) {
269 return foldRightAssoc(ar
);
272 return ExpressionPtr();
274 } else if (m_canThrow
&& !(getLocalEffects() & CanThrow
)) {
277 ExpressionPtr optExp
;
279 optExp
= foldConst(ar
);
280 } catch (Exception
&e
) {
281 // runtime/base threw an exception, perhaps bad operands
283 if (optExp
) optExp
= replaceValue(optExp
);
287 ExpressionPtr
BinaryOpExpression::simplifyArithmetic(
288 AnalysisResultConstPtr ar
) {
291 if (m_exp1
->getScalarValue(v1
)) {
292 if (v1
.isInteger()) {
293 int64_t ival1
= v1
.toInt64();
294 // 1 * $a => $a, 0 + $a => $a
295 if ((ival1
== 1 && m_op
== '*') || (ival1
== 0 && m_op
== '+')) {
296 TypePtr actType2
= m_exp2
->getActualType();
297 TypePtr expType
= getExpectedType();
299 (actType2
->mustBe(Type::KindOfNumeric
) ||
300 (expType
&& expType
->mustBe(Type::KindOfNumeric
) &&
301 !actType2
->couldBe(Type::KindOfArray
) &&
302 Type::IsCastNeeded(ar
, actType2
, expType
)))) {
306 } else if (v1
.isString()) {
307 String sval1
= v1
.toString();
308 if ((sval1
.empty() && m_op
== '.')) {
309 TypePtr actType2
= m_exp2
->getActualType();
310 TypePtr expType
= getExpectedType();
312 if ((expType
&& expType
->is(Type::KindOfString
)) ||
313 (actType2
&& actType2
->is(Type::KindOfString
))) {
316 ExpressionPtr
rep(new UnaryOpExpression(
317 getScope(), getLocation(),
318 m_exp2
, T_STRING_CAST
, true));
319 rep
->setActualType(Type::String
);
324 if (m_exp2
->getScalarValue(v2
)) {
325 if (v2
.isInteger()) {
326 int64_t ival2
= v2
.toInt64();
327 // $a * 1 => $a, $a + 0 => $a
328 if ((ival2
== 1 && m_op
== '*') || (ival2
== 0 && m_op
== '+')) {
329 TypePtr actType1
= m_exp1
->getActualType();
330 TypePtr expType
= getExpectedType();
332 (actType1
->mustBe(Type::KindOfNumeric
) ||
333 (expType
&& expType
->mustBe(Type::KindOfNumeric
) &&
334 !actType1
->couldBe(Type::KindOfArray
) &&
335 Type::IsCastNeeded(ar
, actType1
, expType
)))) {
339 } else if (v2
.isString()) {
340 String sval2
= v2
.toString();
341 if ((sval2
.empty() && m_op
== '.')) {
342 TypePtr actType1
= m_exp1
->getActualType();
343 TypePtr expType
= getExpectedType();
345 if ((expType
&& expType
->is(Type::KindOfString
)) ||
346 (actType1
&& actType1
->is(Type::KindOfString
))) {
349 ExpressionPtr
rep(new UnaryOpExpression(
350 getScope(), getLocation(),
351 m_exp1
, T_STRING_CAST
, true));
352 rep
->setActualType(Type::String
);
357 return ExpressionPtr();
360 void BinaryOpExpression::optimizeTypes(AnalysisResultConstPtr ar
) {
363 case T_IS_SMALLER_OR_EQUAL
:
365 case T_IS_GREATER_OR_EQUAL
:
367 case T_IS_NOT_IDENTICAL
:
371 // not needed for correctness, but will allow us to
372 // generate better code, since we can use the more
373 // specific runtime function
375 TypePtr
a1(m_exp1
->getActualType());
376 TypePtr
i1(m_exp1
->getImplementedType());
378 Type::IsMappedToVariant(i1
) && Type::HasFastCastMethod(a1
)) {
379 m_exp1
->setExpectedType(a1
);
381 TypePtr
a2(m_exp2
->getActualType());
382 TypePtr
i2(m_exp2
->getImplementedType());
384 Type::IsMappedToVariant(i2
) && Type::HasFastCastMethod(a2
)) {
385 m_exp2
->setExpectedType(a2
);
392 ExpressionPtr
BinaryOpExpression::postOptimize(AnalysisResultConstPtr ar
) {
394 ExpressionPtr optExp
= simplifyArithmetic(ar
);
396 if (isShortCircuitOperator()) optExp
= simplifyLogical(ar
);
398 if (optExp
) optExp
= replaceValue(optExp
);
402 static ExpressionPtr
makeIsNull(AnalysisResultConstPtr ar
,
403 LocationPtr loc
, ExpressionPtr exp
,
405 /* Replace "$x === null" with an is_null call; this requires slightly
406 * less work at runtime. */
407 ExpressionListPtr expList
=
408 ExpressionListPtr(new ExpressionList(exp
->getScope(), loc
));
409 expList
->insertElement(exp
);
411 SimpleFunctionCallPtr call
412 (new SimpleFunctionCall(exp
->getScope(), loc
,
413 "is_null", false, expList
, ExpressionPtr()));
416 call
->setActualType(Type::Boolean
);
417 call
->setupScopes(ar
);
419 ExpressionPtr
result(call
);
421 result
= ExpressionPtr(new UnaryOpExpression(
422 exp
->getScope(), loc
,
429 // foldConst() is callable from the parse phase as well as the analysis phase.
430 // We take advantage of this during the parse phase to reduce very simple
431 // expressions down to a single scalar and keep the parse tree smaller,
432 // especially in cases of long chains of binary operators. However, we limit
433 // the effectivness of this during parse to ensure that we eliminate only
434 // very simple scalars that don't require analysis in later phases. For now,
435 // that's just simply scalar values.
436 ExpressionPtr
BinaryOpExpression::foldConst(AnalysisResultConstPtr ar
) {
437 ExpressionPtr optExp
;
441 if (!m_exp2
->getScalarValue(v2
)) {
442 if ((ar
->getPhase() != AnalysisResult::ParseAllFiles
) &&
443 m_exp1
->isScalar() && m_exp1
->getScalarValue(v1
)) {
446 case T_IS_NOT_IDENTICAL
:
448 return makeIsNull(ar
, getLocation(), m_exp2
,
449 m_op
== T_IS_NOT_IDENTICAL
);
457 v1
.toBoolean() == (m_op
== T_LOGICAL_AND
||
458 m_op
== T_BOOLEAN_AND
) ? m_exp2
: m_exp1
;
460 new UnaryOpExpression(
461 getScope(), getLocation(),
462 rep
, T_BOOL_CAST
, true));
463 rep
->setActualType(Type::Boolean
);
464 return replaceValue(rep
);
472 if (m_exp2
->is(KindOfBinaryOpExpression
)) {
473 BinaryOpExpressionPtr binOpExp
=
474 dynamic_pointer_cast
<BinaryOpExpression
>(m_exp2
);
475 if (binOpExp
->m_op
== m_op
&& binOpExp
->m_exp1
->isScalar()) {
476 ExpressionPtr aExp
= m_exp1
;
477 ExpressionPtr bExp
= binOpExp
->m_exp1
;
478 ExpressionPtr cExp
= binOpExp
->m_exp2
;
479 m_exp1
= binOpExp
= Clone(binOpExp
);
481 binOpExp
->m_exp1
= aExp
;
482 binOpExp
->m_exp2
= bExp
;
483 if (ExpressionPtr optExp
= binOpExp
->foldConst(ar
)) {
486 return static_pointer_cast
<Expression
>(shared_from_this());
495 return ExpressionPtr();
498 if (m_exp1
->isScalar()) {
499 if (!m_exp1
->getScalarValue(v1
)) return ExpressionPtr();
501 ScalarExpressionPtr scalar1
=
502 dynamic_pointer_cast
<ScalarExpression
>(m_exp1
);
503 ScalarExpressionPtr scalar2
=
504 dynamic_pointer_cast
<ScalarExpression
>(m_exp2
);
505 // Some data, like the values of __CLASS__ and friends, are not available
506 // while we're still in the initial parse phase.
507 if (ar
->getPhase() == AnalysisResult::ParseAllFiles
) {
508 if ((scalar1
&& scalar1
->needsTranslation()) ||
509 (scalar2
&& scalar2
->needsTranslation())) {
510 return ExpressionPtr();
513 if (!Option::WholeProgram
|| !Option::ParseTimeOpts
) {
514 // In the VM, don't optimize __CLASS__ if within a trait, since
515 // __CLASS__ is not resolved yet.
516 ClassScopeRawPtr clsScope
= getOriginalClass();
517 if (clsScope
&& clsScope
->isTrait()) {
518 if ((scalar1
&& scalar1
->getType() == T_CLASS_C
) ||
519 (scalar2
&& scalar2
->getType() == T_CLASS_C
)) {
520 return ExpressionPtr();
527 result
= static_cast<bool>(v1
.toBoolean() ^ v2
.toBoolean());
530 *result
.asCell() = cellBitOr(*v1
.asCell(), *v2
.asCell());
533 *result
.asCell() = cellBitAnd(*v1
.asCell(), *v2
.asCell());
536 *result
.asCell() = cellBitXor(*v1
.asCell(), *v2
.asCell());
539 result
= concat(v1
.toString(), v2
.toString());
542 result
= same(v1
, v2
);
544 case T_IS_NOT_IDENTICAL
:
545 result
= !same(v1
, v2
);
548 result
= equal(v1
, v2
);
551 result
= !equal(v1
, v2
);
554 result
= less(v1
, v2
);
556 case T_IS_SMALLER_OR_EQUAL
:
557 result
= cellLessOrEqual(*v1
.asCell(), *v2
.asCell());
560 result
= more(v1
, v2
);
562 case T_IS_GREATER_OR_EQUAL
:
563 result
= cellGreaterOrEqual(*v1
.asCell(), *v2
.asCell());
566 *result
.asCell() = cellAdd(*v1
.asCell(), *v2
.asCell());
569 *result
.asCell() = cellSub(*v1
.asCell(), *v2
.asCell());
572 *result
.asCell() = cellMul(*v1
.asCell(), *v2
.asCell());
575 if ((v2
.isIntVal() && v2
.toInt64() == 0) || v2
.toDouble() == 0.0) {
576 return ExpressionPtr();
578 *result
.asCell() = cellDiv(*v1
.asCell(), *v2
.asCell());
581 if ((v2
.isIntVal() && v2
.toInt64() == 0) || v2
.toDouble() == 0.0) {
582 return ExpressionPtr();
584 *result
.asCell() = cellMod(*v1
.asCell(), *v2
.asCell());
587 result
= v1
.toInt64() << v2
.toInt64();
590 result
= v1
.toInt64() >> v2
.toInt64();
593 result
= v1
.toBoolean() || v2
.toBoolean(); break;
595 result
= v1
.toBoolean() && v2
.toBoolean(); break;
597 result
= v1
.toBoolean() || v2
.toBoolean(); break;
599 result
= v1
.toBoolean() && v2
.toBoolean(); break;
601 if (v1
.isArray() && v2
.isString() &&
602 interface_supports_array(v2
.getStringData())) {
610 return ExpressionPtr();
612 return makeScalarExpression(ar
, result
);
615 } else if (ar
->getPhase() != AnalysisResult::ParseAllFiles
) {
621 bool useFirst
= v2
.toBoolean() == (m_op
== T_LOGICAL_AND
||
622 m_op
== T_BOOLEAN_AND
);
623 ExpressionPtr rep
= useFirst
? m_exp1
: m_exp2
;
625 new UnaryOpExpression(
626 getScope(), getLocation(),
627 rep
, T_BOOL_CAST
, true));
628 rep
->setActualType(Type::Boolean
);
632 getScope(), getLocation(),
633 ExpressionList::ListKindComma
));
634 l
->addElement(m_exp1
);
636 l
->setActualType(Type::Boolean
);
639 rep
->setExpectedType(getExpectedType());
640 return replaceValue(rep
);
649 optExp
= foldRightAssoc(ar
);
650 if (optExp
) return optExp
;
653 case T_IS_NOT_IDENTICAL
:
655 return makeIsNull(ar
, getLocation(), m_exp1
,
656 m_op
== T_IS_NOT_IDENTICAL
);
663 return ExpressionPtr();
667 BinaryOpExpression::foldRightAssoc(AnalysisResultConstPtr ar
) {
668 ExpressionPtr optExp1
;
673 if (m_exp1
->is(Expression::KindOfBinaryOpExpression
)) {
674 BinaryOpExpressionPtr binOpExp
=
675 dynamic_pointer_cast
<BinaryOpExpression
>(m_exp1
);
676 if (binOpExp
->m_op
== m_op
) {
677 // turn a Op b Op c, namely (a Op b) Op c into a Op (b Op c)
678 ExpressionPtr aExp
= binOpExp
->m_exp1
;
679 ExpressionPtr bExp
= binOpExp
->m_exp2
;
680 ExpressionPtr cExp
= m_exp2
;
682 m_exp2
= binOpExp
= Clone(binOpExp
);
683 binOpExp
->m_exp1
= bExp
;
684 binOpExp
->m_exp2
= cExp
;
685 if (ExpressionPtr optExp
= binOpExp
->foldConst(ar
)) {
688 return static_pointer_cast
<Expression
>(shared_from_this());
695 return ExpressionPtr();
698 TypePtr
BinaryOpExpression::inferTypes(AnalysisResultPtr ar
, TypePtr type
,
700 TypePtr et1
; // expected m_exp1's type
701 bool coerce1
= false; // whether m_exp1 needs to coerce to et1
702 TypePtr et2
; // expected m_exp2's type
703 bool coerce2
= false; // whether m_exp2 needs to coerce to et2
704 TypePtr rt
; // return type
709 if (coerce
&& Type::SameType(type
, Type::Array
)) {
710 et1
= et2
= Type::Array
;
711 coerce1
= coerce2
= true;
714 et1
= Type::PlusOperand
;
715 et2
= Type::PlusOperand
;
716 rt
= Type::PlusOperand
;
730 et1
= et2
= rt
= Type::String
;
733 et1
= et2
= Type::String
;
737 et1
= et2
= Type::Int64
;
751 et1
= Type::Primitive
;
752 et2
= Type::Primitive
;
753 rt
= Type::Primitive
;
759 et1
= et2
= rt
= Type::Int64
;
766 et1
= et2
= rt
= Type::Boolean
;
769 case T_IS_SMALLER_OR_EQUAL
:
771 case T_IS_GREATER_OR_EQUAL
:
773 case T_IS_NOT_IDENTICAL
:
797 TypePtr rhs
= m_exp2
->inferAndCheck(ar
, et2
, coerce2
);
798 TypePtr lhs
= m_exp1
->inferAndCheck(ar
, Type::Any
, true);
800 if (lhs
->mustBe(Type::KindOfArray
)) {
801 TypePtr
a2(m_exp2
->getActualType());
802 if (a2
&& a2
->is(Type::KindOfArray
)) {
803 m_exp2
->setExpectedType(a2
);
808 if (lhs
->mustBe(Type::KindOfNumeric
)) {
809 if (!rhs
->mustBe(lhs
->getKindOf())) {
810 rhs
= Type::combinedArithmeticType(lhs
, rhs
);
811 if (!rhs
) rhs
= Type::Numeric
;
812 m_exp1
->inferAndCheck(ar
, rhs
, true);
814 TypePtr
a1(m_exp1
->getCPPType());
815 TypePtr
a2(m_exp2
->getActualType());
816 if (a1
&& a1
->mustBe(Type::KindOfNumeric
) &&
817 a2
&& a2
->mustBe(Type::KindOfNumeric
)) {
818 // both LHS and RHS are numeric.
819 // Set the expected type of RHS to be
821 TypePtr t
= a1
->getKindOf() > a2
->getKindOf() ? a1
: a2
;
822 m_exp2
->setExpectedType(t
);
828 m_exp1
->inferAndCheck(ar
, rhs
, true);
841 TypePtr ret
= m_exp2
->inferAndCheck(ar
, et2
, coerce2
);
842 m_exp1
->inferAndCheck(ar
, ret
, true);
847 TypePtr ret
= m_exp2
->inferAndCheck(ar
, et2
, coerce2
);
848 m_exp1
->inferAndCheck(ar
, Type::String
, true);
849 TypePtr act1
= m_exp1
->getActualType();
850 if (act1
&& act1
->is(Type::KindOfString
)) rt
= Type::String
;
857 m_exp1
->inferAndCheck(ar
, et1
, coerce1
);
858 m_exp2
->inferAndCheck(ar
, et2
, coerce2
);
859 TypePtr act1
= m_exp1
->getActualType();
860 TypePtr act2
= m_exp2
->getActualType();
862 TypePtr combined
= Type::combinedArithmeticType(act1
, act2
);
863 if (combined
&& combined
->isSubsetOf(rt
)) {
864 if (act1
) m_exp1
->setExpectedType(act1
);
865 if (act2
) m_exp2
->setExpectedType(act2
);
867 } else if (m_op
== '+') {
868 bool a1
= act1
&& act1
->is(Type::KindOfArray
);
869 bool a2
= act2
&& act2
->is(Type::KindOfArray
);
871 m_implementedType
.reset();
873 m_implementedType
= Type::Variant
;
875 m_exp1
->setExpectedType(Type::Array
);
876 // in this case, the implemented type will
877 // actually be Type::Array (since Array::operator+
880 m_exp1
->setExpectedType(Type::Array
);
881 m_exp2
->setExpectedType(Type::Array
);
889 m_exp1
->inferAndCheck(ar
, et1
, coerce1
);
890 m_exp2
->inferAndCheck(ar
, et2
, coerce2
);
897 ///////////////////////////////////////////////////////////////////////////////
898 // code generation functions
900 void BinaryOpExpression::outputPHP(CodeGenerator
&cg
, AnalysisResultPtr ar
) {
901 m_exp1
->outputPHP(cg
, ar
);
904 case T_PLUS_EQUAL
: cg_printf(" += "); break;
905 case T_MINUS_EQUAL
: cg_printf(" -= "); break;
906 case T_MUL_EQUAL
: cg_printf(" *= "); break;
907 case T_DIV_EQUAL
: cg_printf(" /= "); break;
908 case T_CONCAT_EQUAL
: cg_printf(" .= "); break;
909 case T_MOD_EQUAL
: cg_printf(" %%= "); break;
910 case T_AND_EQUAL
: cg_printf(" &= "); break;
911 case T_OR_EQUAL
: cg_printf(" |= "); break;
912 case T_XOR_EQUAL
: cg_printf(" ^= "); break;
913 case T_SL_EQUAL
: cg_printf(" <<= "); break;
914 case T_SR_EQUAL
: cg_printf(" >>= "); break;
915 case T_BOOLEAN_OR
: cg_printf(" || "); break;
916 case T_BOOLEAN_AND
: cg_printf(" && "); break;
917 case T_LOGICAL_OR
: cg_printf(" or "); break;
918 case T_LOGICAL_AND
: cg_printf(" and "); break;
919 case T_LOGICAL_XOR
: cg_printf(" xor "); break;
920 case '|': cg_printf(" | "); break;
921 case '&': cg_printf(" & "); break;
922 case '^': cg_printf(" ^ "); break;
923 case '.': cg_printf(" . "); break;
924 case '+': cg_printf(" + "); break;
925 case '-': cg_printf(" - "); break;
926 case '*': cg_printf(" * "); break;
927 case '/': cg_printf(" / "); break;
928 case '%': cg_printf(" %% "); break;
929 case T_SL
: cg_printf(" << "); break;
930 case T_SR
: cg_printf(" >> "); break;
931 case T_IS_IDENTICAL
: cg_printf(" === "); break;
932 case T_IS_NOT_IDENTICAL
: cg_printf(" !== "); break;
933 case T_IS_EQUAL
: cg_printf(" == "); break;
934 case T_IS_NOT_EQUAL
: cg_printf(" != "); break;
935 case '<': cg_printf(" < "); break;
936 case T_IS_SMALLER_OR_EQUAL
: cg_printf(" <= "); break;
937 case '>': cg_printf(" > "); break;
938 case T_IS_GREATER_OR_EQUAL
: cg_printf(" >= "); break;
939 case T_INSTANCEOF
: cg_printf(" instanceof "); break;
941 ExpressionListPtr el
= static_pointer_cast
<ExpressionList
>(m_exp2
);
942 if (el
->getCount() == 0) {
946 el
->outputPHP(cg
, ar
);
955 m_exp2
->outputPHP(cg
, ar
);
958 bool BinaryOpExpression::isOpEqual() {