diff --git a/hphp/compiler/expression/binary_op_expression.cpp b/hphp/compiler/expression/binary_op_expression.cpp index 7749412ff..c74ca6d83 100644 --- a/hphp/compiler/expression/binary_op_expression.cpp +++ b/hphp/compiler/expression/binary_op_expression.cpp @@ -524,31 +524,44 @@ ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) { Variant result; switch (m_op) { case T_LOGICAL_XOR: - result = static_cast(v1.toBoolean() ^ v2.toBoolean()); break; + result = static_cast(v1.toBoolean() ^ v2.toBoolean()); + break; case '|': - result = bitwise_or(v1, v2); break; + *result.asCell() = cellBitOr(*v1.asCell(), *v2.asCell()); + break; case '&': - result = bitwise_and(v1, v2); break; + *result.asCell() = cellBitAnd(*v1.asCell(), *v2.asCell()); + break; case '^': - result = bitwise_xor(v1, v2); break; + *result.asCell() = cellBitXor(*v1.asCell(), *v2.asCell()); + break; case '.': - result = concat(v1.toString(), v2.toString()); break; + result = concat(v1.toString(), v2.toString()); + break; case T_IS_IDENTICAL: - result = same(v1, v2); break; + result = same(v1, v2); + break; case T_IS_NOT_IDENTICAL: - result = !same(v1, v2); break; + result = !same(v1, v2); + break; case T_IS_EQUAL: - result = equal(v1, v2); break; + result = equal(v1, v2); + break; case T_IS_NOT_EQUAL: - result = !equal(v1, v2); break; + result = !equal(v1, v2); + break; case '<': - result = less(v1, v2); break; + result = less(v1, v2); + break; case T_IS_SMALLER_OR_EQUAL: - result = cellLessOrEqual(*v1.asCell(), *v2.asCell()); break; + result = cellLessOrEqual(*v1.asCell(), *v2.asCell()); + break; case '>': - result = more(v1, v2); break; + result = more(v1, v2); + break; case T_IS_GREATER_OR_EQUAL: - result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell()); break; + result = cellGreaterOrEqual(*v1.asCell(), *v2.asCell()); + break; case '+': *result.asCell() = cellAdd(*v1.asCell(), *v2.asCell()); break; diff --git a/hphp/runtime/base/builtin_functions.h b/hphp/runtime/base/builtin_functions.h index a0fe4c5fb..23257c57a 100644 --- a/hphp/runtime/base/builtin_functions.h +++ b/hphp/runtime/base/builtin_functions.h @@ -90,9 +90,6 @@ bool empty(CVarRef v, CStrRef, bool = false) = delete; /////////////////////////////////////////////////////////////////////////////// // operators -inline Variant bitwise_or (CVarRef v1, CVarRef v2) { return v1 | v2;} -inline Variant bitwise_and(CVarRef v1, CVarRef v2) { return v1 & v2;} -inline Variant bitwise_xor(CVarRef v1, CVarRef v2) { return v1 ^ v2;} inline int64_t shift_left(int64_t v1, int64_t v2) { return v1 << v2; } inline int64_t shift_right(int64_t v1, int64_t v2) { return v1 >> v2; } diff --git a/hphp/runtime/base/datatype.h b/hphp/runtime/base/datatype.h index 967286848..289b7d574 100644 --- a/hphp/runtime/base/datatype.h +++ b/hphp/runtime/base/datatype.h @@ -230,10 +230,12 @@ inline ALWAYS_INLINE unsigned typeToDestrIndex(DataType t) { // Helper macro for checking if a given type is refcounted #define IS_REFCOUNTED_TYPE(t) ((t) > KindOfRefCountThreshold) -// Helper macro for checking if a type is KindOfString or KindOfStaticString. +// Helper function for checking if a type is KindOfString or KindOfStaticString. static_assert(KindOfStaticString == 0x0C, ""); static_assert(KindOfString == 0x14, ""); -#define IS_STRING_TYPE(t) (((t) & ~0x18) == KindOfStringBit) +inline bool IS_STRING_TYPE(DataType t) { + return (t & ~0x18) == KindOfStringBit; +} // Check if a type is KindOfUninit or KindOfNull #define IS_NULL_TYPE(t) (unsigned(t) <= KindOfNull) diff --git a/hphp/runtime/base/string_data.h b/hphp/runtime/base/string_data.h index 09a9f80ad..c0d08c6fe 100644 --- a/hphp/runtime/base/string_data.h +++ b/hphp/runtime/base/string_data.h @@ -234,15 +234,13 @@ public: Util::s_stackSize)); } - /** - * Informational. - */ const char *data() const { // TODO: t1800106: re-enable this assert //assert(rawdata()[size()] == 0); // all strings must be null-terminated return rawdata(); } - // This method should only be used internally by the String class. + char* mutableData() const { return m_data; } + int size() const { return m_len; } static uint sizeOffset() { return offsetof(StringData, m_len); } int capacity() const { return isSmall() ? MaxSmallSize : bigCap(); } diff --git a/hphp/runtime/base/tv_arith.cpp b/hphp/runtime/base/tv_arith.cpp index 28b71ecbd..58d259b7a 100644 --- a/hphp/runtime/base/tv_arith.cpp +++ b/hphp/runtime/base/tv_arith.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/base/tv_conversions.h" @@ -237,6 +238,51 @@ struct MulEq { } }; + +template +StringData* stringBitOp(BitOp bop, SzOp sop, StringData* s1, StringData* s2) { + auto const newLen = sop(s1->size(), s2->size()); + auto const newStr = NEW(StringData)(newLen); + auto const s1Data = s1->data(); + auto const s2Data = s2->data(); + auto const outData = newStr->mutableData(); + + for (uint32_t i = 0; i < newLen; ++i) { + outData[i] = bop(s1Data[i], s2Data[i]); + } + newStr->setSize(newLen); + + newStr->setRefCount(1); + return newStr; +} + +template class BitOp, class StrLenOp> +Cell cellBitOp(StrLenOp strLenOp, Cell c1, Cell c2) { + assert(cellIsPlausible(&c1)); + assert(cellIsPlausible(&c2)); + + if (IS_STRING_TYPE(c1.m_type) && IS_STRING_TYPE(c2.m_type)) { + return make_tv( + stringBitOp( + BitOp(), + strLenOp, + c1.m_data.pstr, + c2.m_data.pstr + ) + ); + } + + return make_tv( + BitOp()(cellToInt(c1), cellToInt(c2)) + ); +} + +template +void cellBitOpEq(Op op, Cell& c1, Cell c2) { + auto const result = op(c1, c2); + cellSet(result, c1); +} + } ////////////////////////////////////////////////////////////////////// @@ -271,6 +317,27 @@ Cell cellMod(Cell c1, Cell c2) { return make_tv(i1 % i2); } +Cell cellBitAnd(Cell c1, Cell c2) { + return cellBitOp( + [] (uint32_t a, uint32_t b) { return std::min(a, b); }, + c1, c2 + ); +} + +Cell cellBitOr(Cell c1, Cell c2) { + return cellBitOp( + [] (uint32_t a, uint32_t b) { return std::max(a, b); }, + c1, c2 + ); +} + +Cell cellBitXor(Cell c1, Cell c2) { + return cellBitOp( + [] (uint32_t a, uint32_t b) { return std::min(a, b); }, + c1, c2 + ); +} + void cellAddEq(Cell& c1, Cell c2) { cellOpEq(AddEq(), c1, c2); } @@ -296,6 +363,18 @@ void cellModEq(Cell& c1, Cell c2) { cellCopy(cellMod(c1, c2), c1); } +void cellBitAndEq(Cell& c1, Cell c2) { + cellBitOpEq(cellBitAnd, c1, c2); +} + +void cellBitOrEq(Cell& c1, Cell c2) { + cellBitOpEq(cellBitOr, c1, c2); +} + +void cellBitXorEq(Cell& c1, Cell c2) { + cellBitOpEq(cellBitXor, c1, c2); +} + ////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/base/tv_arith.h b/hphp/runtime/base/tv_arith.h index e29837c98..9198d151d 100644 --- a/hphp/runtime/base/tv_arith.h +++ b/hphp/runtime/base/tv_arith.h @@ -53,6 +53,16 @@ TypedNum cellMul(Cell, Cell); Cell cellDiv(Cell, Cell); Cell cellMod(Cell, Cell); +/* + * PHP operators &, |, and ^. + * + * These functions return a KindOfInt64, unless both arguments are + * KindOfString, in which case they return a KindOfString. + */ +Cell cellBitAnd(Cell, Cell); +Cell cellBitOr(Cell, Cell); +Cell cellBitXor(Cell, Cell); + ////////////////////////////////////////////////////////////////////// /* @@ -89,6 +99,18 @@ void cellMulEq(Cell& c1, Cell); void cellDivEq(Cell& c1, Cell); void cellModEq(Cell& c1, Cell); +/* + * PHP operators &=, |=, and ^=. + * + * Mutates the first argument in place, by combining the second + * argument with it in the sense of the appropriate operator. + * + * Post: c1.m_type == KindOfString || c1.m_type == KindOfInt64 + */ +void cellBitAndEq(Cell& c1, Cell); +void cellBitOrEq(Cell& c1, Cell); +void cellBitXorEq(Cell& c1, Cell); + ////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/base/type_string.cpp b/hphp/runtime/base/type_string.cpp index b3950a807..dcd6f8a5f 100644 --- a/hphp/runtime/base/type_string.cpp +++ b/hphp/runtime/base/type_string.cpp @@ -401,84 +401,6 @@ String String::operator~() const { return ret; } -String String::operator|(CStrRef v) const { - return String(m_px).operator|=(v); -} - -String String::operator&(CStrRef v) const { - return String(m_px).operator&=(v); -} - -String String::operator^(CStrRef v) const { - return String(m_px).operator^=(v); -} - -String &String::operator|=(CStrRef v) { - const char *s1 = data(); - const char *s2 = v.data(); - int len1 = size(); - int len2 = v.size(); - int len; - char *copy = nullptr; - if (len2 > len1) { - len = len2; - copy = string_duplicate(s2, len2); - for (int i = 0; i < len1; i++) copy[i] |= s1[i]; - } else { - len = len1; - copy = string_duplicate(s1, len1); - for (int i = 0; i < len2; i++) copy[i] |= s2[i]; - } - if (m_px) decRefStr(m_px); - m_px = NEW(StringData)(copy, len, AttachString); - m_px->setRefCount(1); - return *this; -} - -String &String::operator&=(CStrRef v) { - const char *s1 = data(); - const char *s2 = v.data(); - int len1 = size(); - int len2 = v.size(); - int len; - char *copy = nullptr; - if (len2 < len1) { - len = len2; - copy = string_duplicate(s2, len2); - for (int i = 0; i < len2; i++) copy[i] &= s1[i]; - } else { - len = len1; - copy = string_duplicate(s1, len1); - for (int i = 0; i < len1; i++) copy[i] &= s2[i]; - } - if (m_px) decRefStr(m_px); - m_px = NEW(StringData)(copy, len, AttachString); - m_px->setRefCount(1); - return *this; -} - -String &String::operator^=(CStrRef v) { - const char *s1 = data(); - const char *s2 = v.data(); - int len1 = size(); - int len2 = v.size(); - int len; - char *copy = nullptr; - if (len2 < len1) { - len = len2; - copy = string_duplicate(s2, len2); - for (int i = 0; i < len2; i++) copy[i] ^= s1[i]; - } else { - len = len1; - copy = string_duplicate(s1, len1); - for (int i = 0; i < len1; i++) copy[i] ^= s2[i]; - } - if (m_px) decRefStr(m_px); - m_px = NEW(StringData)(copy, len, AttachString); - m_px->setRefCount(1); - return *this; -} - /////////////////////////////////////////////////////////////////////////////// // conversions diff --git a/hphp/runtime/base/type_string.h b/hphp/runtime/base/type_string.h index bb0aaf371..fec445bee 100644 --- a/hphp/runtime/base/type_string.h +++ b/hphp/runtime/base/type_string.h @@ -311,12 +311,12 @@ public: String &operator += (CStrRef v); String &operator += (const StringSlice& slice); String &operator += (const MutableSlice& slice); - String operator | (CStrRef v) const; - String operator & (CStrRef v) const; - String operator ^ (CStrRef v) const; - String &operator |= (CStrRef v); - String &operator &= (CStrRef v); - String &operator ^= (CStrRef v); + String operator | (CStrRef v) const = delete; + String operator & (CStrRef v) const = delete; + String operator ^ (CStrRef v) const = delete; + String &operator |= (CStrRef v) = delete; + String &operator &= (CStrRef v) = delete; + String &operator ^= (CStrRef v) = delete; String operator ~ () const; 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 2068f75c6..3688c1447 100644 --- a/hphp/runtime/base/type_variant.cpp +++ b/hphp/runtime/base/type_variant.cpp @@ -624,54 +624,6 @@ Variant Variant::bitNot() const { throw InvalidOperandException("only numerics and strings can be negated"); } -Variant Variant::operator|(CVarRef v) const { - if (isString() && v.isString()) { - return toString() | v.toString(); - } - return toInt64() | v.toInt64(); -} - -Variant Variant::operator&(CVarRef v) const { - if (isString() && v.isString()) { - return toString() & v.toString(); - } - return toInt64() & v.toInt64(); -} - -Variant Variant::operator^(CVarRef v) const { - if (isString() && v.isString()) { - return toString() ^ v.toString(); - } - return toInt64() ^ v.toInt64(); -} - -Variant &Variant::operator|=(CVarRef v) { - if (isString() && v.isString()) { - set(toString() | v.toString()); - } else { - set(toInt64() | v.toInt64()); - } - return *this; -} - -Variant &Variant::operator&=(CVarRef v) { - if (isString() && v.isString()) { - set(toString() & v.toString()); - } else { - set(toInt64() & v.toInt64()); - } - return *this; -} - -Variant &Variant::operator^=(CVarRef v) { - if (isString() && v.isString()) { - set(toString() ^ v.toString()); - } else { - set(toInt64() ^ v.toInt64()); - } - return *this; -} - Variant &Variant::operator<<=(int64_t n) { set(toInt64() << n); return *this; diff --git a/hphp/runtime/base/type_variant.h b/hphp/runtime/base/type_variant.h index 3c9017170..06cc90772 100644 --- a/hphp/runtime/base/type_variant.h +++ b/hphp/runtime/base/type_variant.h @@ -575,12 +575,12 @@ class Variant : private TypedValue { Variant &operator %= (int64_t n) = delete; Variant &operator %= (double n) = delete; - Variant operator | (CVarRef v) const; - Variant &operator |= (CVarRef v); - Variant operator & (CVarRef v) const; - Variant &operator &= (CVarRef v); - Variant operator ^ (CVarRef v) const; - Variant &operator ^= (CVarRef v); + Variant operator | (CVarRef v) const = delete; + Variant &operator |= (CVarRef v) = delete; + Variant operator & (CVarRef v) const = delete; + Variant &operator &= (CVarRef v) = delete; + Variant operator ^ (CVarRef v) const = delete; + Variant &operator ^= (CVarRef v) = delete; Variant &operator <<=(int64_t n); Variant &operator >>=(int64_t n); diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 7370875c1..5856f8bed 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -3708,6 +3708,18 @@ inline void OPTBLD_INLINE VMExecutionContext::iopMod(PC& pc) { implCellBinOp(pc, cellMod); } +inline void OPTBLD_INLINE VMExecutionContext::iopBitAnd(PC& pc) { + implCellBinOp(pc, cellBitAnd); +} + +inline void OPTBLD_INLINE VMExecutionContext::iopBitOr(PC& pc) { + implCellBinOp(pc, cellBitOr); +} + +inline void OPTBLD_INLINE VMExecutionContext::iopBitXor(PC& pc) { + implCellBinOp(pc, cellBitXor); +} + template void OPTBLD_INLINE VMExecutionContext::implCellBinOpBool(PC& pc, Op op) { NEXT(); @@ -3767,41 +3779,6 @@ inline void OPTBLD_INLINE VMExecutionContext::iopGte(PC& pc) { implCellBinOpBool(pc, cellGreaterOrEqual); } -#define MATHOP(OP, VOP) do { \ - NEXT(); \ - Cell* c1 = m_stack.topC(); \ - Cell* c2 = m_stack.indC(1); \ - if (c2->m_type == KindOfInt64 && c1->m_type == KindOfInt64) { \ - int64_t a = c2->m_data.num; \ - int64_t b = c1->m_data.num; \ - MATHOP_DIVCHECK(0) \ - c2->m_data.num = a OP b; \ - m_stack.popX(); \ - } \ - MATHOP_DOUBLE(OP) \ - else { \ - tvCellAsVariant(c2) = VOP(tvCellAsVariant(c2), tvCellAsCVarRef(c1)); \ - m_stack.popC(); \ - } \ -} while (0) - -#define MATHOP_DOUBLE(OP) -#define MATHOP_DIVCHECK(x) -inline void OPTBLD_INLINE VMExecutionContext::iopBitAnd(PC& pc) { - MATHOP(&, bitwise_and); -} - -inline void OPTBLD_INLINE VMExecutionContext::iopBitOr(PC& pc) { - MATHOP(|, bitwise_or); -} - -inline void OPTBLD_INLINE VMExecutionContext::iopBitXor(PC& pc) { - MATHOP(^, bitwise_xor); -} -#undef MATHOP -#undef MATHOP_DOUBLE -#undef MATHOP_DIVCHECK - inline void OPTBLD_INLINE VMExecutionContext::iopBitNot(PC& pc) { NEXT(); Cell* c1 = m_stack.topC(); diff --git a/hphp/runtime/vm/bytecode.h b/hphp/runtime/vm/bytecode.h index 9ed1eae88..b46f0e53c 100644 --- a/hphp/runtime/vm/bytecode.h +++ b/hphp/runtime/vm/bytecode.h @@ -47,9 +47,9 @@ void SETOP_BODY(TypedValue* lhs, unsigned char op, Cell* rhs) { case SetOpConcatEqual: concat_assign(tvAsVariant(lhs), tvCellAsCVarRef(rhs).toString()); break; - case SetOpAndEqual: tvAsVariant(lhs) &= tvCellAsCVarRef(rhs); break; - case SetOpOrEqual: tvAsVariant(lhs) |= tvCellAsCVarRef(rhs); break; - case SetOpXorEqual: tvAsVariant(lhs) ^= tvCellAsCVarRef(rhs); break; + case SetOpAndEqual: cellBitAndEq(*lhs, *rhs); break; + case SetOpOrEqual: cellBitOrEq(*lhs, *rhs); break; + case SetOpXorEqual: cellBitXorEq(*lhs, *rhs); break; case SetOpSlEqual: tvAsVariant(lhs) <<= tvCellAsCVarRef(rhs).toInt64(); break; case SetOpSrEqual: tvAsVariant(lhs) >>= diff --git a/hphp/test/ext/test_cpp_base.cpp b/hphp/test/ext/test_cpp_base.cpp index 689451c5c..ea1827269 100644 --- a/hphp/test/ext/test_cpp_base.cpp +++ b/hphp/test/ext/test_cpp_base.cpp @@ -168,14 +168,6 @@ bool TestCppBase::TestString() { s = String("c") + String("d"); VS(s.c_str(), "cd"); s += "efg"; VS(s.c_str(), "cdefg"); s += String("hij"); VS(s.c_str(), "cdefghij"); - - s = String("\x50\x51") | "\x51\x51"; VS(s.c_str(), "\x51\x51"); - s = String("\x50\x51") & "\x51\x51"; VS(s.c_str(), "\x50\x51"); - s = String("\x50\x51") ^ "\x51\x51"; VS(s.c_str(), "\x01"); - s = "\x50\x51"; s |= "\x51\x51"; VS(s.c_str(), "\x51\x51"); - s = "\x50\x51"; s &= "\x51\x51"; VS(s.c_str(), "\x50\x51"); - s = "\x50\x51"; s ^= "\x51\x51"; VS(s.c_str(), "\x01"); - s = "\x50\x51"; s = ~s; VS(s.c_str(), "\xAF\xAE"); } // manipulations diff --git a/hphp/test/quick/bitwise_string.php b/hphp/test/quick/bitwise_string.php new file mode 100644 index 000000000..1ba38a9ae --- /dev/null +++ b/hphp/test/quick/bitwise_string.php @@ -0,0 +1,17 @@ +