diff --git a/hphp/compiler/expression/unary_op_expression.cpp b/hphp/compiler/expression/unary_op_expression.cpp index 98d78b4cc..f47ceb2b8 100644 --- a/hphp/compiler/expression/unary_op_expression.cpp +++ b/hphp/compiler/expression/unary_op_expression.cpp @@ -237,21 +237,30 @@ bool UnaryOpExpression::preCompute(CVarRef value, Variant &result) { *result.asCell()); break; case '~': - result = value.bitNot(); break; + tvSet(*value.asCell(), *result.asTypedValue()); + cellBitNot(*result.asCell()); + break; case '@': - result = value; break; + result = value; + break; case T_INT_CAST: - result = value.toInt64(); break; + result = value.toInt64(); + break; case T_DOUBLE_CAST: - result = toDouble(value); break; + result = toDouble(value); + break; case T_STRING_CAST: - result = toString(value); break; + result = toString(value); + break; case T_BOOL_CAST: - result = toBoolean(value); break; + result = toBoolean(value); + break; case T_EMPTY: - result = empty(value); break; + result = empty(value); + break; case T_ISSET: - result = isset(value); break; + result = isset(value); + break; case T_INC: case T_DEC: assert(false); diff --git a/hphp/runtime/base/tv_arith.cpp b/hphp/runtime/base/tv_arith.cpp index 97a68fd78..26cc75add 100644 --- a/hphp/runtime/base/tv_arith.cpp +++ b/hphp/runtime/base/tv_arith.cpp @@ -239,7 +239,6 @@ struct MulEq { } }; - template StringData* stringBitOp(BitOp bop, SzOp sop, StringData* s1, StringData* s2) { auto const s1Size = s1->size(); @@ -489,6 +488,38 @@ void cellDec(Cell& cell) { cellIncDecOp(Dec(), cell); } +void cellBitNot(Cell& cell) { + assert(cellIsPlausible(&cell)); + + switch (cell.m_type) { + case KindOfInt64: + cell.m_data.num = ~cell.m_data.num; + break; + case KindOfDouble: + cell.m_type = KindOfInt64; + cell.m_data.num = ~toInt64(cell.m_data.dbl); + break; + + case KindOfString: + if (cell.m_data.pstr->getCount() > 1) { + case KindOfStaticString: + auto const newSd = NEW(StringData)( + cell.m_data.pstr->slice(), + CopyString + ); + newSd->incRefCount(); + cell.m_data.pstr->decRefCount(); // can't go to zero + cell.m_data.pstr = newSd; + cell.m_type = KindOfString; + } + cell.m_data.pstr->negate(); + break; + + default: + raise_error("Unsupported operand type for ~"); + } +} + ////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/base/tv_arith.h b/hphp/runtime/base/tv_arith.h index 625ff36dc..8e3cf252b 100644 --- a/hphp/runtime/base/tv_arith.h +++ b/hphp/runtime/base/tv_arith.h @@ -112,6 +112,8 @@ void cellBitAndEq(Cell& c1, Cell); void cellBitOrEq(Cell& c1, Cell); void cellBitXorEq(Cell& c1, Cell); +////////////////////////////////////////////////////////////////////// + /* * PHP operator ++ and --. * @@ -121,6 +123,15 @@ void cellBitXorEq(Cell& c1, Cell); void cellInc(Cell&); void cellDec(Cell&); + +/* + * PHP unary operator ~. + * + * Mutates the argument in place, with the effects of php's unary + * bitwise not operator. + */ +void cellBitNot(Cell&); + ////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/base/type_string.cpp b/hphp/runtime/base/type_string.cpp index 9a01e1615..ecf99e8b1 100644 --- a/hphp/runtime/base/type_string.cpp +++ b/hphp/runtime/base/type_string.cpp @@ -396,12 +396,6 @@ String operator+(const String & lhs, const String & rhs) { return NEW(StringData)(lhs.slice(), rhs.slice()); } -String String::operator~() const { - String ret(NEW(StringData)(slice(), CopyString)); - ret->negate(); - return ret; -} - /////////////////////////////////////////////////////////////////////////////// // conversions diff --git a/hphp/runtime/base/type_string.h b/hphp/runtime/base/type_string.h index 2cdb98c3a..290106200 100644 --- a/hphp/runtime/base/type_string.h +++ b/hphp/runtime/base/type_string.h @@ -318,7 +318,7 @@ public: String &operator |= (CStrRef v) = delete; String &operator &= (CStrRef v) = delete; String &operator ^= (CStrRef v) = delete; - String operator ~ () const; + String operator ~ () const = delete; explicit operator std::string () const { return std::string(c_str(), size()); } diff --git a/hphp/runtime/base/type_variant.cpp b/hphp/runtime/base/type_variant.cpp index 3cf643335..f0b871197 100644 --- a/hphp/runtime/base/type_variant.cpp +++ b/hphp/runtime/base/type_variant.cpp @@ -522,25 +522,6 @@ inline DataType Variant::convertToNumeric(int64_t *lval, double *dval) const { return s->isNumericWithVal(*lval, *dval, 1); } -/////////////////////////////////////////////////////////////////////////////// -// bitwise - -Variant Variant::bitNot() const { - auto const cell = asCell(); - switch (cell->m_type) { - case KindOfInt64: - return ~cell->m_data.num; - case KindOfDouble: - return ~toInt64(cell->m_data.dbl); - case KindOfStaticString: - case KindOfString: - return ~String(cell->m_data.pstr); - default: - break; - } - throw InvalidOperandException("only numerics and strings can be negated"); -} - /////////////////////////////////////////////////////////////////////////////// // iterator functions diff --git a/hphp/runtime/base/type_variant.h b/hphp/runtime/base/type_variant.h index 01498a98f..37b7d8b49 100644 --- a/hphp/runtime/base/type_variant.h +++ b/hphp/runtime/base/type_variant.h @@ -590,10 +590,6 @@ class Variant : private TypedValue { Variant &operator -- () = delete; Variant operator -- (int) = delete; - // Return the result of applying the php bitwise not operator to - // this value. - Variant bitNot() const; - /** * Iterator functions. See array_iterator.h for end() and next(). */ diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index c3fbc451c..48bca008f 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -3768,17 +3768,7 @@ inline void OPTBLD_INLINE VMExecutionContext::iopShr(PC& pc) { inline void OPTBLD_INLINE VMExecutionContext::iopBitNot(PC& pc) { NEXT(); - Cell* c1 = m_stack.topC(); - if (LIKELY(c1->m_type == KindOfInt64)) { - c1->m_data.num = ~c1->m_data.num; - } else if (c1->m_type == KindOfDouble) { - c1->m_type = KindOfInt64; - c1->m_data.num = ~int64_t(c1->m_data.dbl); - } else if (IS_STRING_TYPE(c1->m_type)) { - cellAsVariant(*c1) = cellAsVariant(*c1).bitNot(); - } else { - raise_error("Unsupported operand type for ~"); - } + cellBitNot(*m_stack.topC()); } inline void OPTBLD_INLINE VMExecutionContext::iopCastBool(PC& pc) {