Arquivos
hhvm/hphp/compiler/expression/binary_op_expression.cpp
T
andrewparoski a8b6ba6962 Implement Tuple
Implement a Tuple class as part of collections.
2013-03-19 14:11:00 -07:00

932 linhas
28 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;
} else if (strcasecmp(s.c_str(), "tuple") == 0) {
cType = Collection::TupleType;
}
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_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<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 (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 = 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<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);
}
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;
}