Rename runtime/base/zend_* to zend-
[hiphop-php.git] / hphp / compiler / expression / binary_op_expression.cpp
blob63d32d8f20f3bf806a4ea32b7f23a1f0336f9f00
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
36 using namespace HPHP;
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) {
46 switch (m_op) {
47 case T_PLUS_EQUAL:
48 case T_MINUS_EQUAL:
49 case T_MUL_EQUAL:
50 case T_DIV_EQUAL:
51 case T_CONCAT_EQUAL:
52 case T_MOD_EQUAL:
53 case T_AND_EQUAL:
54 case T_OR_EQUAL:
55 case T_XOR_EQUAL:
56 case T_SL_EQUAL:
57 case T_SR_EQUAL:
58 m_assign = true;
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);
65 break;
66 case T_COLLECTION: {
67 std::string s = m_exp1->getLiteralString();
68 int cType = 0;
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);
82 break;
84 default:
85 break;
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);
94 return exp;
97 bool BinaryOpExpression::isTemporary() const {
98 switch (m_op) {
99 case '+':
100 case '-':
101 case '*':
102 case '/':
103 case T_SL:
104 case T_SR:
105 case T_BOOLEAN_OR:
106 case T_BOOLEAN_AND:
107 case T_LOGICAL_OR:
108 case T_LOGICAL_AND:
109 case T_INSTANCEOF:
110 case T_COLLECTION:
111 return true;
113 return false;
116 bool BinaryOpExpression::isRefable(bool checkError /* = false */) const {
117 return checkError && m_assign;
120 bool BinaryOpExpression::isLiteralString() const {
121 if (m_op == '.') {
122 return m_exp1->isLiteralString() && m_exp2->isLiteralString();
124 return false;
127 std::string BinaryOpExpression::getLiteralString() const {
128 if (m_op == '.') {
129 return m_exp1->getLiteralString() + m_exp2->getLiteralString();
131 return "";
134 bool BinaryOpExpression::containsDynamicConstant(AnalysisResultPtr ar) const {
135 switch (m_op) {
136 case T_COLLECTION:
137 return m_exp2->containsDynamicConstant(ar);
138 default:
139 break;
141 return false;
144 bool BinaryOpExpression::isShortCircuitOperator() const {
145 switch (m_op) {
146 case T_BOOLEAN_OR:
147 case T_BOOLEAN_AND:
148 case T_LOGICAL_OR:
149 case T_LOGICAL_AND:
150 return true;
151 default:
152 break;
154 return false;
157 bool BinaryOpExpression::isLogicalOrOperator() const {
158 switch (m_op) {
159 case T_BOOLEAN_OR:
160 case T_LOGICAL_OR:
161 return true;
162 default:
163 break;
165 return false;
168 ExpressionPtr BinaryOpExpression::unneededHelper() {
169 bool shortCircuit = isShortCircuitOperator();
170 if (!m_exp2->getContainedEffects() ||
171 (!shortCircuit && !m_exp1->getContainedEffects())) {
172 return Expression::unneededHelper();
175 if (shortCircuit) {
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;
187 m_canThrow = false;
188 switch (m_op) {
189 case '/':
190 case '%':
191 case T_DIV_EQUAL:
192 case T_MOD_EQUAL: {
193 Variant v2;
194 if (!m_exp2->getScalarValue(v2) || equal(v2, 0)) {
195 effect = CanThrow;
196 m_canThrow = true;
198 break;
200 default:
201 break;
203 if (m_assign) effect |= AssignEffect;
204 return effect;
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) {
218 try {
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 {
227 switch (n) {
228 case 0:
229 return m_exp1;
230 case 1:
231 return m_exp2;
232 default:
233 assert(false);
234 break;
236 return ConstructPtr();
239 int BinaryOpExpression::getKidCount() const {
240 return 2;
243 void BinaryOpExpression::setNthKid(int n, ConstructPtr cp) {
244 switch (n) {
245 case 0:
246 m_exp1 = boost::dynamic_pointer_cast<Expression>(cp);
247 break;
248 case 1:
249 m_exp2 = boost::dynamic_pointer_cast<Expression>(cp);
250 break;
251 default:
252 assert(false);
253 break;
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)) {
275 recomputeEffects();
277 ExpressionPtr optExp;
278 try {
279 optExp = foldConst(ar);
280 } catch (Exception &e) {
281 // runtime/base threw an exception, perhaps bad operands
283 if (optExp) optExp = replaceValue(optExp);
284 return optExp;
287 ExpressionPtr BinaryOpExpression::simplifyArithmetic(
288 AnalysisResultConstPtr ar) {
289 Variant v1;
290 Variant v2;
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();
298 if (actType2 &&
299 (actType2->mustBe(Type::KindOfNumeric) ||
300 (expType && expType->mustBe(Type::KindOfNumeric) &&
301 !actType2->couldBe(Type::KindOfArray) &&
302 Type::IsCastNeeded(ar, actType2, expType)))) {
303 return m_exp2;
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();
311 // '' . $a => $a
312 if ((expType && expType->is(Type::KindOfString)) ||
313 (actType2 && actType2->is(Type::KindOfString))) {
314 return m_exp2;
316 ExpressionPtr rep(new UnaryOpExpression(
317 getScope(), getLocation(),
318 m_exp2, T_STRING_CAST, true));
319 rep->setActualType(Type::String);
320 return rep;
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();
331 if (actType1 &&
332 (actType1->mustBe(Type::KindOfNumeric) ||
333 (expType && expType->mustBe(Type::KindOfNumeric) &&
334 !actType1->couldBe(Type::KindOfArray) &&
335 Type::IsCastNeeded(ar, actType1, expType)))) {
336 return m_exp1;
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();
344 // $a . '' => $a
345 if ((expType && expType->is(Type::KindOfString)) ||
346 (actType1 && actType1->is(Type::KindOfString))) {
347 return m_exp1;
349 ExpressionPtr rep(new UnaryOpExpression(
350 getScope(), getLocation(),
351 m_exp1, T_STRING_CAST, true));
352 rep->setActualType(Type::String);
353 return rep;
357 return ExpressionPtr();
360 void BinaryOpExpression::optimizeTypes(AnalysisResultConstPtr ar) {
361 switch (m_op) {
362 case '<':
363 case T_IS_SMALLER_OR_EQUAL:
364 case '>':
365 case T_IS_GREATER_OR_EQUAL:
366 case T_IS_IDENTICAL:
367 case T_IS_NOT_IDENTICAL:
368 case T_IS_EQUAL:
369 case T_IS_NOT_EQUAL:
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());
377 if (a1 && i1 &&
378 Type::IsMappedToVariant(i1) && Type::HasFastCastMethod(a1)) {
379 m_exp1->setExpectedType(a1);
381 TypePtr a2(m_exp2->getActualType());
382 TypePtr i2(m_exp2->getImplementedType());
383 if (a2 && i2 &&
384 Type::IsMappedToVariant(i2) && Type::HasFastCastMethod(a2)) {
385 m_exp2->setExpectedType(a2);
388 default: break;
392 ExpressionPtr BinaryOpExpression::postOptimize(AnalysisResultConstPtr ar) {
393 optimizeTypes(ar);
394 ExpressionPtr optExp = simplifyArithmetic(ar);
395 if (!optExp) {
396 if (isShortCircuitOperator()) optExp = simplifyLogical(ar);
398 if (optExp) optExp = replaceValue(optExp);
399 return optExp;
402 static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar,
403 LocationPtr loc, ExpressionPtr exp,
404 bool invert) {
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()));
415 call->setValid();
416 call->setActualType(Type::Boolean);
417 call->setupScopes(ar);
419 ExpressionPtr result(call);
420 if (invert) {
421 result = ExpressionPtr(new UnaryOpExpression(
422 exp->getScope(), loc,
423 result, '!', true));
426 return result;
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;
438 Variant v1;
439 Variant v2;
441 if (!m_exp2->getScalarValue(v2)) {
442 if ((ar->getPhase() != AnalysisResult::ParseAllFiles) &&
443 m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
444 switch (m_op) {
445 case T_IS_IDENTICAL:
446 case T_IS_NOT_IDENTICAL:
447 if (v1.isNull()) {
448 return makeIsNull(ar, getLocation(), m_exp2,
449 m_op == T_IS_NOT_IDENTICAL);
451 break;
452 case T_LOGICAL_AND:
453 case T_BOOLEAN_AND:
454 case T_LOGICAL_OR:
455 case T_BOOLEAN_OR: {
456 ExpressionPtr rep =
457 v1.toBoolean() == (m_op == T_LOGICAL_AND ||
458 m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1;
459 rep = ExpressionPtr(
460 new UnaryOpExpression(
461 getScope(), getLocation(),
462 rep, T_BOOL_CAST, true));
463 rep->setActualType(Type::Boolean);
464 return replaceValue(rep);
466 case '+':
467 case '.':
468 case '*':
469 case '&':
470 case '|':
471 case '^':
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);
480 m_exp2 = cExp;
481 binOpExp->m_exp1 = aExp;
482 binOpExp->m_exp2 = bExp;
483 if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
484 m_exp1 = optExp;
486 return static_pointer_cast<Expression>(shared_from_this());
489 break;
490 default:
491 break;
495 return ExpressionPtr();
498 if (m_exp1->isScalar()) {
499 if (!m_exp1->getScalarValue(v1)) return ExpressionPtr();
500 try {
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();
524 Variant result;
525 switch (m_op) {
526 case T_LOGICAL_XOR:
527 result = static_cast<bool>(v1.toBoolean() ^ v2.toBoolean());
528 break;
529 case '|':
530 *result.asCell() = cellBitOr(*v1.asCell(), *v2.asCell());
531 break;
532 case '&':
533 *result.asCell() = cellBitAnd(*v1.asCell(), *v2.asCell());
534 break;
535 case '^':
536 *result.asCell() = cellBitXor(*v1.asCell(), *v2.asCell());
537 break;
538 case '.':
539 result = concat(v1.toString(), v2.toString());
540 break;
541 case T_IS_IDENTICAL:
542 result = same(v1, v2);
543 break;
544 case T_IS_NOT_IDENTICAL:
545 result = !same(v1, v2);
546 break;
547 case T_IS_EQUAL:
548 result = equal(v1, v2);
549 break;
550 case T_IS_NOT_EQUAL:
551 result = !equal(v1, v2);
552 break;
553 case '<':
554 result = less(v1, v2);
555 break;
556 case T_IS_SMALLER_OR_EQUAL:
557 result = cellLessOrEqual(*v1.asCell(), *v2.asCell());
558 break;
559 case '>':
560 result = more(v1, v2);
561 break;
562 case T_IS_GREATER_OR_EQUAL:
563 result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell());
564 break;
565 case '+':
566 *result.asCell() = cellAdd(*v1.asCell(), *v2.asCell());
567 break;
568 case '-':
569 *result.asCell() = cellSub(*v1.asCell(), *v2.asCell());
570 break;
571 case '*':
572 *result.asCell() = cellMul(*v1.asCell(), *v2.asCell());
573 break;
574 case '/':
575 if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
576 return ExpressionPtr();
578 *result.asCell() = cellDiv(*v1.asCell(), *v2.asCell());
579 break;
580 case '%':
581 if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) {
582 return ExpressionPtr();
584 *result.asCell() = cellMod(*v1.asCell(), *v2.asCell());
585 break;
586 case T_SL:
587 result = v1.toInt64() << v2.toInt64();
588 break;
589 case T_SR:
590 result = v1.toInt64() >> v2.toInt64();
591 break;
592 case T_BOOLEAN_OR:
593 result = v1.toBoolean() || v2.toBoolean(); break;
594 case T_BOOLEAN_AND:
595 result = v1.toBoolean() && v2.toBoolean(); break;
596 case T_LOGICAL_OR:
597 result = v1.toBoolean() || v2.toBoolean(); break;
598 case T_LOGICAL_AND:
599 result = v1.toBoolean() && v2.toBoolean(); break;
600 case T_INSTANCEOF: {
601 if (v1.isArray() && v2.isString() &&
602 interface_supports_array(v2.getStringData())) {
603 result = true;
604 break;
606 result = false;
607 break;
609 default:
610 return ExpressionPtr();
612 return makeScalarExpression(ar, result);
613 } catch (...) {
615 } else if (ar->getPhase() != AnalysisResult::ParseAllFiles) {
616 switch (m_op) {
617 case T_LOGICAL_AND:
618 case T_BOOLEAN_AND:
619 case T_LOGICAL_OR:
620 case T_BOOLEAN_OR: {
621 bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND ||
622 m_op == T_BOOLEAN_AND);
623 ExpressionPtr rep = useFirst ? m_exp1 : m_exp2;
624 rep = ExpressionPtr(
625 new UnaryOpExpression(
626 getScope(), getLocation(),
627 rep, T_BOOL_CAST, true));
628 rep->setActualType(Type::Boolean);
629 if (!useFirst) {
630 ExpressionListPtr l(
631 new ExpressionList(
632 getScope(), getLocation(),
633 ExpressionList::ListKindComma));
634 l->addElement(m_exp1);
635 l->addElement(rep);
636 l->setActualType(Type::Boolean);
637 rep = l;
639 rep->setExpectedType(getExpectedType());
640 return replaceValue(rep);
642 case T_LOGICAL_XOR:
643 case '|':
644 case '&':
645 case '^':
646 case '.':
647 case '+':
648 case '*':
649 optExp = foldRightAssoc(ar);
650 if (optExp) return optExp;
651 break;
652 case T_IS_IDENTICAL:
653 case T_IS_NOT_IDENTICAL:
654 if (v2.isNull()) {
655 return makeIsNull(ar, getLocation(), m_exp1,
656 m_op == T_IS_NOT_IDENTICAL);
658 break;
659 default:
660 break;
663 return ExpressionPtr();
666 ExpressionPtr
667 BinaryOpExpression::foldRightAssoc(AnalysisResultConstPtr ar) {
668 ExpressionPtr optExp1;
669 switch (m_op) {
670 case '.':
671 case '+':
672 case '*':
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;
681 m_exp1 = aExp;
682 m_exp2 = binOpExp = Clone(binOpExp);
683 binOpExp->m_exp1 = bExp;
684 binOpExp->m_exp2 = cExp;
685 if (ExpressionPtr optExp = binOpExp->foldConst(ar)) {
686 m_exp2 = optExp;
688 return static_pointer_cast<Expression>(shared_from_this());
691 break;
692 default:
693 break;
695 return ExpressionPtr();
698 TypePtr BinaryOpExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
699 bool coerce) {
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
706 switch (m_op) {
707 case '+':
708 case T_PLUS_EQUAL:
709 if (coerce && Type::SameType(type, Type::Array)) {
710 et1 = et2 = Type::Array;
711 coerce1 = coerce2 = true;
712 rt = Type::Array;
713 } else {
714 et1 = Type::PlusOperand;
715 et2 = Type::PlusOperand;
716 rt = Type::PlusOperand;
718 break;
719 case '-':
720 case '*':
721 case T_MINUS_EQUAL:
722 case T_MUL_EQUAL:
723 case '/':
724 case T_DIV_EQUAL:
725 et1 = Type::Numeric;
726 et2 = Type::Numeric;
727 rt = Type::Numeric;
728 break;
729 case '.':
730 et1 = et2 = rt = Type::String;
731 break;
732 case T_CONCAT_EQUAL:
733 et1 = et2 = Type::String;
734 rt = Type::Variant;
735 break;
736 case '%':
737 et1 = et2 = Type::Int64;
738 rt = Type::Numeric;
739 break;
740 case T_MOD_EQUAL:
741 et1 = Type::Numeric;
742 et2 = Type::Int64;
743 rt = Type::Numeric;
744 break;
745 case '|':
746 case '&':
747 case '^':
748 case T_AND_EQUAL:
749 case T_OR_EQUAL:
750 case T_XOR_EQUAL:
751 et1 = Type::Primitive;
752 et2 = Type::Primitive;
753 rt = Type::Primitive;
754 break;
755 case T_SL:
756 case T_SR:
757 case T_SL_EQUAL:
758 case T_SR_EQUAL:
759 et1 = et2 = rt = Type::Int64;
760 break;
761 case T_BOOLEAN_OR:
762 case T_BOOLEAN_AND:
763 case T_LOGICAL_OR:
764 case T_LOGICAL_AND:
765 case T_LOGICAL_XOR:
766 et1 = et2 = rt = Type::Boolean;
767 break;
768 case '<':
769 case T_IS_SMALLER_OR_EQUAL:
770 case '>':
771 case T_IS_GREATER_OR_EQUAL:
772 case T_IS_IDENTICAL:
773 case T_IS_NOT_IDENTICAL:
774 case T_IS_EQUAL:
775 case T_IS_NOT_EQUAL:
776 et1 = Type::Some;
777 et2 = Type::Some;
778 rt = Type::Boolean;
779 break;
780 case T_INSTANCEOF:
781 et1 = Type::Any;
782 et2 = Type::String;
783 rt = Type::Boolean;
784 break;
785 case T_COLLECTION:
786 et1 = Type::Any;
787 et2 = Type::Any;
788 rt = Type::Object;
789 break;
790 default:
791 assert(false);
794 switch (m_op) {
795 case T_PLUS_EQUAL:
797 TypePtr rhs = m_exp2->inferAndCheck(ar, et2, coerce2);
798 TypePtr lhs = m_exp1->inferAndCheck(ar, Type::Any, true);
799 if (lhs) {
800 if (lhs->mustBe(Type::KindOfArray)) {
801 TypePtr a2(m_exp2->getActualType());
802 if (a2 && a2->is(Type::KindOfArray)) {
803 m_exp2->setExpectedType(a2);
805 rt = Type::Array;
806 break;
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
820 // the stronger type
821 TypePtr t = a1->getKindOf() > a2->getKindOf() ? a1 : a2;
822 m_exp2->setExpectedType(t);
824 rt = Type::Numeric;
825 break;
828 m_exp1->inferAndCheck(ar, rhs, true);
830 break;
831 case T_MINUS_EQUAL:
832 case T_MUL_EQUAL:
833 case T_DIV_EQUAL:
834 case T_MOD_EQUAL:
835 case T_AND_EQUAL:
836 case T_OR_EQUAL:
837 case T_XOR_EQUAL:
838 case T_SL_EQUAL:
839 case T_SR_EQUAL:
841 TypePtr ret = m_exp2->inferAndCheck(ar, et2, coerce2);
842 m_exp1->inferAndCheck(ar, ret, true);
844 break;
845 case T_CONCAT_EQUAL:
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;
852 break;
853 case '+':
854 case '-':
855 case '*':
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);
866 rt = combined;
867 } else if (m_op == '+') {
868 bool a1 = act1 && act1->is(Type::KindOfArray);
869 bool a2 = act2 && act2->is(Type::KindOfArray);
870 if (a1 || a2) {
871 m_implementedType.reset();
872 if (!a1) {
873 m_implementedType = Type::Variant;
874 } else if (!a2) {
875 m_exp1->setExpectedType(Type::Array);
876 // in this case, the implemented type will
877 // actually be Type::Array (since Array::operator+
878 // returns an Array)
879 } else {
880 m_exp1->setExpectedType(Type::Array);
881 m_exp2->setExpectedType(Type::Array);
883 rt = Type::Array;
887 break;
888 default:
889 m_exp1->inferAndCheck(ar, et1, coerce1);
890 m_exp2->inferAndCheck(ar, et2, coerce2);
891 break;
894 return rt;
897 ///////////////////////////////////////////////////////////////////////////////
898 // code generation functions
900 void BinaryOpExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
901 m_exp1->outputPHP(cg, ar);
903 switch (m_op) {
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;
940 case T_COLLECTION: {
941 ExpressionListPtr el = static_pointer_cast<ExpressionList>(m_exp2);
942 if (el->getCount() == 0) {
943 cg_printf(" {}");
944 } else {
945 cg_printf(" { ");
946 el->outputPHP(cg, ar);
947 cg_printf(" }");
949 return;
951 default:
952 assert(false);
955 m_exp2->outputPHP(cg, ar);
958 bool BinaryOpExpression::isOpEqual() {
959 switch (m_op) {
960 case T_CONCAT_EQUAL:
961 case T_PLUS_EQUAL:
962 case T_MINUS_EQUAL:
963 case T_MUL_EQUAL:
964 case T_DIV_EQUAL:
965 case T_MOD_EQUAL:
966 case T_AND_EQUAL:
967 case T_OR_EQUAL:
968 case T_XOR_EQUAL:
969 case T_SL_EQUAL:
970 case T_SR_EQUAL:
971 return true;
972 default:
973 break;
975 return false;