/* +----------------------------------------------------------------------+ | 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; } 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 ival1 = v1.toInt64(); // 1 * $a => $a, 0 + $a => $a if ((ival1 == 1 && m_op == '*') || (ival1 == 0 && m_op == '+')) { TypePtr actType2 = m_exp2->getActualType(); TypePtr expType = getExpectedType(); if (actType2 && (actType2->mustBe(Type::KindOfNumeric) || (expType && expType->mustBe(Type::KindOfNumeric) && !actType2->couldBe(Type::KindOfArray) && Type::IsCastNeeded(ar, actType2, expType)))) { return m_exp2; } } } else if (v1.isString()) { String sval1 = v1.toString(); if ((sval1.empty() && m_op == '.')) { TypePtr actType2 = m_exp2->getActualType(); TypePtr expType = getExpectedType(); // '' . $a => $a if ((expType && expType->is(Type::KindOfString)) || (actType2 && actType2->is(Type::KindOfString))) { return m_exp2; } ExpressionPtr rep(new UnaryOpExpression( getScope(), getLocation(), m_exp2, T_STRING_CAST, true)); rep->setActualType(Type::String); return rep; } } } if (m_exp2->getScalarValue(v2)) { if (v2.isInteger()) { int64 ival2 = v2.toInt64(); // $a * 1 => $a, $a + 0 => $a if ((ival2 == 1 && m_op == '*') || (ival2 == 0 && m_op == '+')) { TypePtr actType1 = m_exp1->getActualType(); TypePtr expType = getExpectedType(); if (actType1 && (actType1->mustBe(Type::KindOfNumeric) || (expType && expType->mustBe(Type::KindOfNumeric) && !actType1->couldBe(Type::KindOfArray) && Type::IsCastNeeded(ar, actType1, expType)))) { return m_exp1; } } } else if (v2.isString()) { String sval2 = v2.toString(); if ((sval2.empty() && m_op == '.')) { TypePtr actType1 = m_exp1->getActualType(); TypePtr expType = getExpectedType(); // $a . '' => $a if ((expType && expType->is(Type::KindOfString)) || (actType1 && actType1->is(Type::KindOfString))) { return m_exp1; } ExpressionPtr rep(new UnaryOpExpression( getScope(), getLocation(), m_exp1, T_STRING_CAST, true)); rep->setActualType(Type::String); return rep; } } } return ExpressionPtr(); } void BinaryOpExpression::optimizeTypes(AnalysisResultConstPtr ar) { switch (m_op) { case '<': case T_IS_SMALLER_OR_EQUAL: case '>': case T_IS_GREATER_OR_EQUAL: case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: case T_IS_EQUAL: case T_IS_NOT_EQUAL: { // not needed for correctness, but will allow us to // generate better code, since we can use the more // specific runtime function TypePtr a1(m_exp1->getActualType()); TypePtr i1(m_exp1->getImplementedType()); if (a1 && i1 && Type::IsMappedToVariant(i1) && Type::HasFastCastMethod(a1)) { m_exp1->setExpectedType(a1); } TypePtr a2(m_exp2->getActualType()); TypePtr i2(m_exp2->getImplementedType()); if (a2 && i2 && Type::IsMappedToVariant(i2) && Type::HasFastCastMethod(a2)) { m_exp2->setExpectedType(a2); } } default: break; } } ExpressionPtr BinaryOpExpression::postOptimize(AnalysisResultConstPtr ar) { optimizeTypes(ar); ExpressionPtr optExp = simplifyArithmetic(ar); if (!optExp) { if (isShortCircuitOperator()) optExp = simplifyLogical(ar); } if (optExp) optExp = replaceValue(optExp); return optExp; } static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar, LocationPtr loc, ExpressionPtr exp, bool invert) { /* Replace "$x === null" with an is_null call; this requires slightly * less work at runtime. */ ExpressionListPtr expList = ExpressionListPtr(new ExpressionList(exp->getScope(), loc)); expList->insertElement(exp); SimpleFunctionCallPtr call (new SimpleFunctionCall(exp->getScope(), loc, "is_null", expList, ExpressionPtr())); call->setValid(); call->setActualType(Type::Boolean); call->setupScopes(ar); ExpressionPtr result(call); if (invert) { result = ExpressionPtr(new UnaryOpExpression( exp->getScope(), loc, result, '!', true)); } return result; } ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { ExpressionPtr optExp; Variant v1; Variant v2; if (!m_exp2->getScalarValue(v2)) { if (m_exp1->isScalar() && m_exp1->getScalarValue(v1)) { switch (m_op) { case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v1.isNull()) { return makeIsNull(ar, getLocation(), m_exp2, m_op == T_IS_NOT_IDENTICAL); } break; case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { ExpressionPtr rep = v1.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND) ? m_exp2 : m_exp1; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); return replaceValue(rep); } case '+': case '.': case '*': case '&': case '|': case '^': if (m_exp2->is(KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast(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 (hhvm && Option::OutputHHBC && (!Option::WholeProgram || !Option::ParseTimeOpts)) { // In the VM, don't optimize __CLASS__ if within a trait, since // __CLASS__ is not resolved yet. ClassScopeRawPtr clsScope = getOriginalClass(); if (clsScope && clsScope->isTrait()) { ScalarExpressionPtr scalar1 = dynamic_pointer_cast(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 = not_more(v1, v2); break; case '>': result = more(v1, v2); break; case T_IS_GREATER_OR_EQUAL: result = not_less(v1, v2); break; case '+': result = plus(v1, v2); break; case '-': result = minus(v1, v2); break; case '*': result = multiply(v1, v2); break; case '/': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = divide(v1, v2); break; case '%': if ((v2.isIntVal() && v2.toInt64() == 0) || v2.toDouble() == 0.0) { return ExpressionPtr(); } result = modulo(v1, v2); break; case T_SL: result = shift_left(v1, v2); break; case T_SR: result = shift_right(v1, v2); break; case T_BOOLEAN_OR: result = v1 || v2; break; case T_BOOLEAN_AND: result = v1 && v2; break; case T_LOGICAL_OR: result = v1 || v2; break; case T_LOGICAL_AND: result = v1 && v2; break; case T_INSTANCEOF: result = false; break; default: return ExpressionPtr(); } return makeScalarExpression(ar, result); } catch (...) { } } else { switch (m_op) { case T_LOGICAL_AND: case T_BOOLEAN_AND: case T_LOGICAL_OR: case T_BOOLEAN_OR: { bool useFirst = v2.toBoolean() == (m_op == T_LOGICAL_AND || m_op == T_BOOLEAN_AND); ExpressionPtr rep = useFirst ? m_exp1 : m_exp2; rep = ExpressionPtr( new UnaryOpExpression( getScope(), getLocation(), rep, T_BOOL_CAST, true)); rep->setActualType(Type::Boolean); if (!useFirst) { ExpressionListPtr l( new ExpressionList( getScope(), getLocation(), ExpressionList::ListKindComma)); l->addElement(m_exp1); l->addElement(rep); l->setActualType(Type::Boolean); rep = l; } rep->setExpectedType(getExpectedType()); return replaceValue(rep); } case T_LOGICAL_XOR: case '|': case '&': case '^': case '.': case '+': case '*': optExp = foldRightAssoc(ar); if (optExp) return optExp; break; case T_IS_IDENTICAL: case T_IS_NOT_IDENTICAL: if (v2.isNull()) { return makeIsNull(ar, getLocation(), m_exp1, m_op == T_IS_NOT_IDENTICAL); } break; default: break; } } return ExpressionPtr(); } ExpressionPtr BinaryOpExpression::foldRightAssoc(AnalysisResultConstPtr ar) { ExpressionPtr optExp1; switch (m_op) { case '.': case '+': case '*': if (m_exp1->is(Expression::KindOfBinaryOpExpression)) { BinaryOpExpressionPtr binOpExp = dynamic_pointer_cast(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); } static bool castIfNeeded(TypePtr top, TypePtr arg, CodeGenerator &cg, AnalysisResultPtr ar, BlockScopeRawPtr scope) { if (top) { if (top->isPrimitive()) { if (!arg || !arg->isPrimitive()) { top->outputCPPCast(cg, ar, scope); cg_printf("("); return true; } } else if (top->is(Type::KindOfArray)) { if (arg && arg->isExactType() && !arg->is(Type::KindOfArray)) { cg_printf("((Variant)"); return true; } } else if (top->mustBe(Type::KindOfNumeric)) { if (arg && arg->is(Type::KindOfArray)) { cg_printf("((Variant)"); return true; } } } return false; } void BinaryOpExpression::preOutputStash(CodeGenerator &cg, AnalysisResultPtr ar, int state) { if (hasCPPTemp() || isScalar()) return; if (m_op == '.' && (state & FixOrder)) { if (m_exp1) { if (!m_exp1->getActualType() && m_exp1->hasCPPTemp()) { cg_printf("id(%s);\n", m_exp1->cppTemp().c_str()); } m_exp1->preOutputStash(cg, ar, state|StashVars); } if (m_exp2) { if (!m_exp2->getActualType() && m_exp2->hasCPPTemp()) { cg_printf("id(%s);\n", m_exp2->cppTemp().c_str()); } m_exp2->preOutputStash(cg, ar, state|StashVars); } } else { Expression::preOutputStash(cg, ar, state); } } static const char *stringBufferPrefix(CodeGenerator &cg, AnalysisResultPtr ar, ExpressionPtr var) { if (var->is(Expression::KindOfSimpleVariable)) { if (LoopStatementPtr loop = cg.getLoopStatement()) { SimpleVariablePtr sv = static_pointer_cast(var); if (loop->checkStringBuf(sv->getName())) { return loop->getScope()->getVariables()-> getVariablePrefix(sv->getName()); } } } return 0; } static std::string stringBufferName(const char *temp, const char *prefix, const char *name) { return std::string(temp) + "_sbuf_" + prefix + name; } int BinaryOpExpression::getConcatList(ExpressionPtrVec &ev, ExpressionPtr exp, bool &hasVoid) { if (!exp->hasCPPTemp()) { if (exp->is(Expression::KindOfUnaryOpExpression)) { UnaryOpExpressionPtr u = static_pointer_cast(exp); if (u->getOp() == '(') { return getConcatList(ev, u->getExpression(), hasVoid); } } else if (exp->is(Expression::KindOfBinaryOpExpression)) { BinaryOpExpressionPtr b = static_pointer_cast(exp); if (b->getOp() == '.') { if (b->getExp1()->is(Expression::KindOfSimpleVariable) && b->getExp1()->isLocalExprAltered() && !b->getExp1()->hasCPPTemp() && b->getExp2()->hasEffect() && !b->getExp2()->hasCPPTemp()) { /* In this case, the simple variable must be evaluated after b->getExp2(). But when we output a concat list we explicitly order the expressions from left to right. */ } else { return getConcatList(ev, b->getExp1(), hasVoid) + getConcatList(ev, b->getExp2(), hasVoid); } } } else if (exp->is(Expression::KindOfEncapsListExpression)) { EncapsListExpressionPtr e = static_pointer_cast(exp); if (e->getType() != '`') { ExpressionListPtr el = e->getExpressions(); int num = 0; for (int i = 0, s = el->getCount(); i < s; i++) { ExpressionPtr exp = (*el)[i]; num += getConcatList(ev, exp, hasVoid); } return num; } } } else if (!exp->getActualType()) { return 0; } ev.push_back(exp); bool isVoid = !exp->getActualType(); hasVoid |= isVoid; return isVoid ? 0 : 1; } static void outputStringExpr(CodeGenerator &cg, AnalysisResultPtr ar, ExpressionPtr exp, bool asLitStr) { if (asLitStr && exp->isLiteralString()) { const std::string &s = exp->getLiteralString(); std::string enc = string_cplus_escape(s.c_str(), s.size()); cg_printf("\"%s\", %d", enc.c_str(), (int)s.size()); return; } TypePtr et(exp->getExpectedType()); exp->setExpectedType(Type::String); exp->outputCPP(cg, ar); exp->setExpectedType(et); } static void outputStringBufExprs(ExpressionPtrVec &ev, CodeGenerator &cg, AnalysisResultPtr ar) { for (size_t i = 0; i < ev.size(); i++) { ExpressionPtr exp = ev[i]; cg_printf(".addWithTaint("); outputStringExpr(cg, ar, exp, true); cg_printf(")"); } } bool BinaryOpExpression::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar, int state) { if (isOpEqual() && (m_exp1->is(KindOfArrayElementExpression) || m_exp1->is(KindOfObjectPropertyExpression))) { return Expression::preOutputCPP(cg, ar, state); } bool effect2 = m_exp2->hasEffect(); const char *prefix = 0; if (effect2 || m_exp1->hasEffect()) { ExpressionPtr self = static_pointer_cast(shared_from_this()); ExpressionPtrVec ev; bool hasVoid = false; int numConcat = 0; bool ok = false; if (m_op == '.') { numConcat = getConcatList(ev, self, hasVoid); ok = hasVoid || (numConcat > MAX_CONCAT_ARGS); } else if (effect2 && m_op == T_CONCAT_EQUAL) { prefix = stringBufferPrefix(cg, ar, m_exp1); ok = prefix; if (!ok) { if (m_exp1->is(KindOfSimpleVariable)) { ok = true; ev.push_back(m_exp1); numConcat++; } } if (ok) { numConcat += getConcatList(ev, m_exp2, hasVoid); if (numConcat <= 2 && !prefix) { return Expression::preOutputCPP(cg, ar, state); } } } if (ok) { if (!cg.inExpression()) return true; cg.wrapExpressionBegin(); std::string buf; if (prefix) { SimpleVariablePtr sv(static_pointer_cast(m_exp1)); buf = stringBufferName(Option::TempPrefix, prefix, sv->getName().c_str()); m_cppTemp = "/**/"; } else if (numConcat) { buf = m_cppTemp = genCPPTemp(cg, ar); buf += "_buf"; if (numConcat > 1) { cg_printf("StringBuffer %s;\n", buf.c_str()); } } else { m_cppTemp = "\"\""; } for (size_t i = 0; i < ev.size(); i++) { ExpressionPtr exp = ev[i]; bool is_void = !exp->getActualType(); exp->preOutputCPP(cg, ar, 0); if (!is_void) { bool asLit = true; if (numConcat > 1 || prefix) { cg_printf("%s.appendWithTaint(", buf.c_str()); } else { asLit = false; cg_printf("CStrRef %s = (", m_cppTemp.c_str()); } outputStringExpr(cg, ar, exp, asLit); cg_printf(")"); } else { exp->outputCPPUnneeded(cg, ar); } cg_printf(";\n"); } if (numConcat > 1 && !prefix) { cg_printf("CStrRef %s(%s.detachWithTaint());\n", m_cppTemp.c_str(), buf.c_str()); if (m_op == T_CONCAT_EQUAL) { m_exp1->outputCPP(cg, ar); cg_printf(" = %s;\n", m_cppTemp.c_str()); } } return true; } } if (!isShortCircuitOperator()) { return Expression::preOutputCPP(cg, ar, state); } if (!effect2) { bool ret = m_exp1->preOutputCPP(cg, ar, 0); if (state & FixOrder) { ret = true; if (cg.inExpression()) { preOutputStash(cg, ar, state); } } return ret; } bool fix_e1 = m_exp1->preOutputCPP(cg, ar, 0); if (!cg.inExpression()) { return fix_e1 || m_exp2->preOutputCPP(cg, ar, 0); } cg.setInExpression(false); bool fix_e2 = m_exp2->preOutputCPP(cg, ar, 0); cg.setInExpression(true); if (fix_e2) { cg.wrapExpressionBegin(); std::string tmp = genCPPTemp(cg, ar); cg_printf("bool %s = (", tmp.c_str()); m_exp1->outputCPP(cg, ar); cg_printf(");\n"); cg_indentBegin("if (%s%s) {\n", m_op == T_LOGICAL_OR || m_op == T_BOOLEAN_OR ? "!" : "", tmp.c_str()); m_exp2->preOutputCPP(cg, ar, 0); if (isUnused()) { if (m_exp2->outputCPPUnneeded(cg, ar)) cg_printf(";\n"); } else { cg_printf("%s = (", tmp.c_str()); m_exp2->outputCPP(cg, ar); cg_printf(");\n"); } cg_indentEnd("}\n"); m_cppTemp = tmp; } else if (state & FixOrder) { preOutputStash(cg, ar, state); fix_e1 = true; } return fix_e1 || fix_e2; } bool BinaryOpExpression::isOpEqual() { switch (m_op) { case T_CONCAT_EQUAL: case T_PLUS_EQUAL: case T_MINUS_EQUAL: case T_MUL_EQUAL: case T_DIV_EQUAL: case T_MOD_EQUAL: case T_AND_EQUAL: case T_OR_EQUAL: case T_XOR_EQUAL: case T_SL_EQUAL: case T_SR_EQUAL: return true; default: break; } return false; } bool BinaryOpExpression::outputCPPImplOpEqual(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_exp1->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast(m_exp1); if (exp->isSuperGlobal() || exp->isDynamicGlobal()) return false; if (TypePtr t = exp->getVariable()->getActualType()) { TypePtr it(exp->getVariable()->getImplementedType()); if (t->is(Type::KindOfArray) && (!it || it->is(Type::KindOfArray) || Type::IsMappedToVariant(it) /* fast cast will kick in */)) { return false; } } // turning $a['elem'] Op= $b into $a.setOpEqual('elem', $b); exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg_printf(".setOpEqual(%d, ", m_op); exp->getOffset()->outputCPP(cg, ar); cg_printf(", ("); } else { cg_printf(".appendOpEqual(%d, (", m_op); } m_exp2->outputCPP(cg, ar); cg_printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast(off); if (sc) { if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); return true; } if (m_exp1->is(Expression::KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast(m_exp1)); if (var->isValid()) return false; var->outputCPPObject(cg, ar); cg_printf("o_assign_op<%s,%d>(", isUnused() ? "void" : "Variant", m_op); var->outputCPPProperty(cg, ar); cg_printf(", "); m_exp2->outputCPP(cg, ar); cg_printf("%s)", originalClassName(cg, true).c_str()); return true; } return false; } void BinaryOpExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (isOpEqual() && outputCPPImplOpEqual(cg, ar)) return; bool wrapped = true; switch (m_op) { case T_CONCAT_EQUAL: if (const char *prefix = stringBufferPrefix(cg, ar, m_exp1)) { SimpleVariablePtr sv = static_pointer_cast(m_exp1); if (m_exp2->hasCPPTemp()) { cg_printf("%s.appendWithTaint(%s)", stringBufferName(Option::TempPrefix, prefix, sv->getName().c_str()).c_str(), m_exp2->cppTemp().c_str()); return; } else { ExpressionPtrVec ev; bool hasVoid = false; getConcatList(ev, m_exp2, hasVoid); if (!hasVoid) { cg_printf("%s", stringBufferName(Option::TempPrefix, prefix, sv->getName().c_str()).c_str()); outputStringBufExprs(ev, cg, ar); return; } } } cg_printf("concat_assign"); break; case '.': { ExpressionPtr self = static_pointer_cast(shared_from_this()); ExpressionPtrVec ev; bool hasVoid = false; int num = getConcatList(ev, self, hasVoid); if (num < 2) { cg_printf("concat"); break; } always_assert(!hasVoid); if (num <= MAX_CONCAT_ARGS) { if (num == 2) { cg_printf("concat("); } else { if (num > MAX_CONCAT_ARGS) ar->m_concatLengths.insert(num); cg_printf("concat%d(", num); } for (size_t i = 0; i < ev.size(); i++) { ExpressionPtr exp = ev[i]; if (i) cg_printf(", "); outputStringExpr(cg, ar, exp, false); } cg_printf(")"); } else { cg_printf("StringBuffer()"); outputStringBufExprs(ev, cg, ar); cg_printf(".detachWithTaint()"); } } return; case T_LOGICAL_XOR: cg_printf("logical_xor"); break; case '|': cg_printf("bitwise_or"); break; case '&': cg_printf("bitwise_and"); break; case '^': cg_printf("bitwise_xor"); break; case T_IS_IDENTICAL: cg_printf("same"); break; case T_IS_NOT_IDENTICAL: cg_printf("!same"); break; case T_IS_EQUAL: cg_printf("equal"); break; case T_IS_NOT_EQUAL: cg_printf("!equal"); break; case '<': cg_printf("less"); break; case T_IS_SMALLER_OR_EQUAL: cg_printf("not_more"); break; case '>': cg_printf("more"); break; case T_IS_GREATER_OR_EQUAL: cg_printf("not_less"); break; case '/': cg_printf("divide"); break; case '%': cg_printf("modulo"); break; case T_INSTANCEOF: cg_printf("instanceOf"); break; case T_COLLECTION: cg_printf("Object"); break; default: wrapped = !isUnused(); break; } if (wrapped) cg_printf("("); ExpressionPtr first = m_exp1; ExpressionPtr second = m_exp2; // we could implement these functions natively on String and Array classes switch (m_op) { case '+': case '-': case '*': case '/': if (!first->outputCPPArithArg(cg, ar, m_op == '+')) { TypePtr argType = first->hasCPPTemp() ? first->getType() : first->getActualType(); bool flag = castIfNeeded(getActualType(), argType, cg, ar, getScope()); first->outputCPP(cg, ar); if (flag) { cg_printf(")"); } } break; case T_SL: case T_SR: assert(first->getType()->is(Type::KindOfInt64)); first->outputCPP(cg, ar); break; case T_COLLECTION: /* do nothing */ break; default: first->outputCPP(cg, ar); break; } switch (m_op) { case T_PLUS_EQUAL: cg_printf(" += "); break; case T_MINUS_EQUAL: cg_printf(" -= "); break; case T_MUL_EQUAL: cg_printf(" *= "); break; case T_DIV_EQUAL: cg_printf(" /= "); break; case T_MOD_EQUAL: cg_printf(" %%= "); break; case T_AND_EQUAL: cg_printf(" &= "); break; case T_OR_EQUAL: cg_printf(" |= "); break; case T_XOR_EQUAL: cg_printf(" ^= "); break; case T_SL_EQUAL: cg_printf(" <<= "); break; case T_SR_EQUAL: cg_printf(" >>= "); break; case T_BOOLEAN_OR: cg_printf(" || "); break; case T_BOOLEAN_AND: cg_printf(" && "); break; case T_LOGICAL_OR: cg_printf(" || "); break; case T_LOGICAL_AND: cg_printf(" && "); break; case T_COLLECTION: /* print nothing */ break; default: switch (m_op) { case '+': cg_printf(" + "); break; case '-': cg_printf(" - "); break; case '*': cg_printf(" * "); break; case T_SL: cg_printf(" << "); break; case T_SR: cg_printf(" >> "); break; default: cg_printf(", "); break; } break; } switch (m_op) { case '+': case '-': case '*': case '/': if (!second->outputCPPArithArg(cg, ar, m_op == '+')) { TypePtr argType = second->hasCPPTemp() ? second->getType() : second->getActualType(); bool flag = castIfNeeded(getActualType(), argType, cg, ar, getScope()); second->outputCPP(cg, ar); if (flag) { cg_printf(")"); } } break; case T_INSTANCEOF: { if (second->isScalar()) { ScalarExpressionPtr scalar = dynamic_pointer_cast(second); bool notQuoted = scalar && !scalar->isQuoted(); std::string s = second->getLiteralString(); if (s == "static" && notQuoted) { cg_printf("FrameInjection::GetStaticClassName(fi.getThreadInfo())"); } else if (s != "") { if (s == "self" && notQuoted) { ClassScopeRawPtr cls = getOriginalClass(); if (cls) { s = cls->getOriginalName(); } } else if (s == "parent" && notQuoted) { ClassScopeRawPtr cls = getOriginalClass(); if (cls && !cls->getOriginalParent().empty()) { s = cls->getOriginalParent(); } } cg_printString(s, ar, shared_from_this()); } else { second->outputCPP(cg, ar); } } else { second->outputCPP(cg, ar); } break; } case T_PLUS_EQUAL: case T_MINUS_EQUAL: case T_MUL_EQUAL: { TypePtr t1 = first->getCPPType(); TypePtr t2 = second->getType(); if (t1 && !t1->is(Type::KindOfArray) && t2 && Type::IsCastNeeded(ar, t2, t1)) { t1->outputCPPCast(cg, ar, getScope()); cg_printf("("); second->outputCPP(cg, ar); cg_printf(")"); } else { second->outputCPP(cg, ar); } break; } case T_BOOLEAN_OR: case T_BOOLEAN_AND: case T_LOGICAL_AND: case T_LOGICAL_OR: if (isUnused()) { cg_printf("("); if (second->outputCPPUnneeded(cg, ar)) { cg_printf(","); } cg_printf("false)"); } else { second->outputCPP(cg, ar); } break; default: second->outputCPP(cg, ar); } if (wrapped) cg_printf(")"); }