diff --git a/hphp/compiler/expression/binary_op_expression.cpp b/hphp/compiler/expression/binary_op_expression.cpp index 8fcf950c7..6ad86c9b8 100644 --- a/hphp/compiler/expression/binary_op_expression.cpp +++ b/hphp/compiler/expression/binary_op_expression.cpp @@ -178,9 +178,6 @@ ExpressionPtr BinaryOpExpression::unneededHelper() { return static_pointer_cast(shared_from_this()); } -/////////////////////////////////////////////////////////////////////////////// -// parser functions - /////////////////////////////////////////////////////////////////////////////// // static analysis functions @@ -193,7 +190,7 @@ int BinaryOpExpression::getLocalEffects() const { case T_DIV_EQUAL: case T_MOD_EQUAL: { Variant v2; - if (!m_exp2->getScalarValue(v2) || v2.equal(0)) { + if (!m_exp2->getScalarValue(v2) || equal(v2, 0)) { effect = CanThrow; m_canThrow = true; } diff --git a/hphp/runtime/base/array/array_data.cpp b/hphp/runtime/base/array/array_data.cpp index e31f81893..d59b3298e 100644 --- a/hphp/runtime/base/array/array_data.cpp +++ b/hphp/runtime/base/array/array_data.cpp @@ -156,8 +156,8 @@ int ArrayData::compare(const ArrayData *v2) const { if (!v2->exists(key)) return 1; auto value1 = iter.second(); auto value2 = v2->get(key); - if (value1.more(value2)) return 1; - if (value1.less(value2)) return -1; + if (HPHP::more(value1, value2)) return 1; + if (HPHP::less(value1, value2)) return -1; } return 0; @@ -184,7 +184,10 @@ bool ArrayData::equal(const ArrayData *v2, bool strict) const { for (ArrayIter iter(this); iter; ++iter) { Variant key(iter.first()); if (!v2->exists(key)) return false; - if (!iter.second().equal(v2->get(key))) return false; + if (!tvEqual(iter.second().asTypedValue(), + v2->get(key).asTypedValue())) { + return false; + } } } diff --git a/hphp/runtime/base/array/array_util.cpp b/hphp/runtime/base/array/array_util.cpp index a1abd81a5..0f645f792 100644 --- a/hphp/runtime/base/array/array_util.cpp +++ b/hphp/runtime/base/array/array_util.cpp @@ -673,7 +673,7 @@ Variant ArrayUtil::RegularSortUnique(CArrRef input) { for (unsigned int i = 1; i < indices.size(); ++i) { int currentIdx = indices[i]; Variant current = input->getValue(opaque.positions[currentIdx]); - if (current.equal(last)) { + if (equal(current, last)) { if (currentIdx > lastIdx) { duplicates[currentIdx] = true; continue; diff --git a/hphp/runtime/base/builtin_functions.cpp b/hphp/runtime/base/builtin_functions.cpp index 5cd7a15d2..c2ff731e9 100644 --- a/hphp/runtime/base/builtin_functions.cpp +++ b/hphp/runtime/base/builtin_functions.cpp @@ -473,6 +473,7 @@ void throw_collection_compare_exception() { raise_warning(msg); } +// TODO void check_collection_compare(ObjectData* obj) { if (obj && obj->isCollection()) throw_collection_compare_exception(); } diff --git a/hphp/runtime/base/comparisons.h b/hphp/runtime/base/comparisons.h index d9da3df14..414223089 100644 --- a/hphp/runtime/base/comparisons.h +++ b/hphp/runtime/base/comparisons.h @@ -39,16 +39,29 @@ inline bool same(CVarRef v1, CVarRef v2) { return tvSame(v1.asTypedValue(), v2.asTypedValue()); } -inline bool equal(CVarRef v1, bool v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, int v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, int64_t v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, double v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, const StringData *v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, CStrRef v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, litstr v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, CArrRef v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, CObjRef v2) { return v1.equal(v2);} -inline bool equal(CVarRef v1, CVarRef v2) { return v1.equal(v2);} +inline bool equal(CVarRef v1, bool v2) { return cellEqual(v1.asCell(), v2); } +inline bool equal(CVarRef v1, int v2) { return cellEqual(v1.asCell(), v2); } +inline bool equal(CVarRef v1, int64_t v2) { return cellEqual(v1.asCell(), v2);} +inline bool equal(CVarRef v1, double v2) { return cellEqual(v1.asCell(), v2);} +inline bool equal(CVarRef v1, const StringData* v2) { + return cellEqual(v1.asCell(), v2); +} +inline bool equal(CVarRef v1, CStrRef v2) { + if (!v2.get()) return cellEqual(v1.asCell(), false); + return cellEqual(v1.asCell(), v2.get()); +} +inline bool equal(CVarRef v1, litstr v2) = delete; +inline bool equal(CVarRef v1, CArrRef v2) { + if (!v2.get()) return cellEqual(v1.asCell(), false); + return cellEqual(v1.asCell(), v2.get()); +} +inline bool equal(CVarRef v1, CObjRef v2) { + if (!v2.get()) return cellEqual(v1.asCell(), false); + return cellEqual(v1.asCell(), v2.get()); +} +inline bool equal(CVarRef v1, CVarRef v2) { + return tvEqual(v1.asTypedValue(), v2.asTypedValue()); +} inline bool equalAsStr(CVarRef v1, bool v2) { return v1.equalAsStr(v2);} inline bool equalAsStr(CVarRef v1, int v2) { return v1.equalAsStr(v2);} @@ -64,27 +77,69 @@ inline bool equalAsStr(CVarRef v1, CObjRef v2) { return v1.equalAsStr(v2);} inline bool equalAsStr(CVarRef v1, CVarRef v2) { return v1.equalAsStr(v2);} -inline bool less(CVarRef v1, bool v2) { return v1.less(v2);} -inline bool less(CVarRef v1, int v2) { return v1.less(v2);} -inline bool less(CVarRef v1, int64_t v2) { return v1.less(v2);} -inline bool less(CVarRef v1, double v2) { return v1.less(v2);} -inline bool less(CVarRef v1, const StringData *v2) { return v1.less(v2);} -inline bool less(CVarRef v1, CStrRef v2) { return v1.less(v2);} -inline bool less(CVarRef v1, litstr v2) { return v1.less(v2);} -inline bool less(CVarRef v1, CArrRef v2) { return v1.less(v2);} -inline bool less(CVarRef v1, CObjRef v2) { return v1.less(v2);} -inline bool less(CVarRef v1, CVarRef v2) { return v1.less(v2);} +inline bool less(CVarRef v1, bool v2) { + return cellLess(v1.asCell(), v2); +} +inline bool less(CVarRef v1, int v2) { + return cellLess(v1.asCell(), v2); +} +inline bool less(CVarRef v1, int64_t v2) { + return cellLess(v1.asCell(), v2); +} +inline bool less(CVarRef v1, double v2) { + return cellLess(v1.asCell(), v2); +} +inline bool less(CVarRef v1, const StringData* v2) { + return cellLess(v1.asCell(), v2); +} +inline bool less(CVarRef v1, CStrRef v2) { + if (!v2.get()) return cellLess(v1.asCell(), false); + return cellLess(v1.asCell(), v2.get()); +} +inline bool less(CVarRef v1, litstr v2) = delete; +inline bool less(CVarRef v1, CArrRef v2) { + if (!v2.get()) return cellLess(v1.asCell(), false); + return cellLess(v1.asCell(), v2.get()); +} +inline bool less(CVarRef v1, CObjRef v2) { + if (!v2.get()) return cellLess(v1.asCell(), false); + return cellLess(v1.asCell(), v2.get()); +} +inline bool less(CVarRef v1, CVarRef v2) { + return tvLess(v1.asTypedValue(), v2.asTypedValue()); +} -inline bool more(CVarRef v1, bool v2) { return v1.more(v2);} -inline bool more(CVarRef v1, int v2) { return v1.more(v2);} -inline bool more(CVarRef v1, int64_t v2) { return v1.more(v2);} -inline bool more(CVarRef v1, double v2) { return v1.more(v2);} -inline bool more(CVarRef v1, const StringData *v2) { return v1.more(v2);} -inline bool more(CVarRef v1, CStrRef v2) { return v1.more(v2);} -inline bool more(CVarRef v1, litstr v2) { return v1.more(v2);} -inline bool more(CVarRef v1, CArrRef v2) { return v1.more(v2);} -inline bool more(CVarRef v1, CObjRef v2) { return v1.more(v2);} -inline bool more(CVarRef v1, CVarRef v2) { return v1.more(v2);} +inline bool more(CVarRef v1, bool v2) { + return cellGreater(v1.asCell(), v2); +} +inline bool more(CVarRef v1, int v2) { + return cellGreater(v1.asCell(), v2); +} +inline bool more(CVarRef v1, int64_t v2) { + return cellGreater(v1.asCell(), v2); +} +inline bool more(CVarRef v1, double v2) { + return cellGreater(v1.asCell(), v2); +} +inline bool more(CVarRef v1, const StringData* v2) { + return cellGreater(v1.asCell(), v2); +} +inline bool more(CVarRef v1, CStrRef v2) { + if (!v2.get()) return cellGreater(v1.asCell(), false); + return cellGreater(v1.asCell(), v2.get()); +} +inline bool more(CVarRef v1, litstr v2) = delete; +inline bool more(CVarRef v1, CArrRef v2) { + if (!v2.get()) return cellGreater(v1.asCell(), false); + return cellGreater(v1.asCell(), v2.get()); +} +inline bool more(CVarRef v1, CObjRef v2) { + if (!v2.get()) return cellGreater(v1.asCell(), false); + return cellGreater(v1.asCell(), v2.get()); +} +inline bool more(CVarRef v1, CVarRef v2) { + return tvGreater(v1.asTypedValue(), v2.asTypedValue()); +} /////////////////////////////////////////////////////////////////////////////// // bool diff --git a/hphp/runtime/base/complex_types.h b/hphp/runtime/base/complex_types.h index a9f95cdce..ef4577893 100644 --- a/hphp/runtime/base/complex_types.h +++ b/hphp/runtime/base/complex_types.h @@ -24,8 +24,8 @@ #include "hphp/runtime/base/type_array.h" #include "hphp/runtime/base/type_object.h" #include "hphp/runtime/base/ref_data.h" -#include "hphp/runtime/base/type_variant.h" #include "hphp/runtime/base/tv_helpers.h" +#include "hphp/runtime/base/type_variant.h" #include "hphp/runtime/base/array/array_inline.h" #undef incl_HPHP_INSIDE_HPHP_COMPLEX_TYPES_H_ diff --git a/hphp/runtime/base/hphp_value.h b/hphp/runtime/base/hphp_value.h index 05533112f..1702a670c 100644 --- a/hphp/runtime/base/hphp_value.h +++ b/hphp/runtime/base/hphp_value.h @@ -128,6 +128,16 @@ private: } }; +/* + * These may be used to provide a little more self-documentation about + * whether typed values must be cells (not KindOfRef) or var (must be + * KindOfRef). + * + * See bytecode.specification for details. + */ +typedef TypedValue Cell; +typedef TypedValue Var; + /////////////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/base/tv_comparisons-inl.h b/hphp/runtime/base/tv_comparisons-inl.h new file mode 100644 index 000000000..5b0e24fae --- /dev/null +++ b/hphp/runtime/base/tv_comparisons-inl.h @@ -0,0 +1,35 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 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. | + +----------------------------------------------------------------------+ +*/ + +namespace HPHP { + +////////////////////////////////////////////////////////////////////// + +inline bool cellEqual(const Cell* cell, int ival) { + return cellEqual(cell, static_cast(ival)); +} + +inline bool cellLess(const Cell* cell, int ival) { + return cellLess(cell, static_cast(ival)); +} + +inline bool cellGreater(const Cell* cell, int ival) { + return cellGreater(cell, static_cast(ival)); +} + +////////////////////////////////////////////////////////////////////// + +} diff --git a/hphp/runtime/base/tv_comparisons.cpp b/hphp/runtime/base/tv_comparisons.cpp index 750c1d045..839822a3c 100644 --- a/hphp/runtime/base/tv_comparisons.cpp +++ b/hphp/runtime/base/tv_comparisons.cpp @@ -15,21 +15,389 @@ */ #include "hphp/runtime/base/tv_comparisons.h" -#include "hphp/runtime/base/complex_types.h" +#include + #include "hphp/runtime/base/comparisons.h" +#include "hphp/runtime/base/type_conversions.h" namespace HPHP { ////////////////////////////////////////////////////////////////////// +extern bool collectionEquals(ObjectData*, ObjectData*); + +////////////////////////////////////////////////////////////////////// + +namespace { + +////////////////////////////////////////////////////////////////////// + +bool cellToBool(const Cell* cell) { + assert(tvIsPlausible(cell)); + assert(cell->m_type != KindOfRef); + + switch (cell->m_type) { + case KindOfUninit: + case KindOfNull: return false; + case KindOfInt64: return cell->m_data.num != 0; + case KindOfBoolean: return cell->m_data.num; + case KindOfDouble: return cell->m_data.dbl != 0; + case KindOfStaticString: + case KindOfString: return cell->m_data.pstr->toBoolean(); + case KindOfArray: return !cell->m_data.parr->empty(); + case KindOfObject: // TODO: should handle o_toBoolean? + return true; + default: break; + } + not_reached(); +} + +////////////////////////////////////////////////////////////////////// + +/* + * Family of relative op functions. + * + * These are used to implement the common parts of the php operators + * ==, <, and >. They handle some of the php behavior with regard to + * numeric-ish strings, and delegate to the 'op' functor to perform + * the actual comparison on primitive types, and between complex php + * types of the same type. + * + * See below for the implementations of the Op template parameter. + */ + +template +bool cellRelOp(Op op, const Cell* cell, bool val) { + return op(cellToBool(cell), val); +} + +template +bool cellRelOp(Op op, const Cell* cell, int64_t val) { + assert(tvIsPlausible(cell)); + assert(cell->m_type != KindOfRef); + + switch (cell->m_type) { + case KindOfUninit: + case KindOfNull: return op(false, !!val); + case KindOfBoolean: return op(!!cell->m_data.num, val != 0); + case KindOfInt64: return op(cell->m_data.num, val); + case KindOfDouble: return op(cell->m_data.dbl, val); + case KindOfArray: return op(true, false); + + case KindOfObject: + return cell->m_data.pobj->isCollection() + ? op.collectionVsNonObj() + : op(cell->m_data.pobj->o_toInt64(), val); + + case KindOfStaticString: + case KindOfString: + { + auto const sdata = cell->m_data.pstr; + int64_t ival; + double dval; + auto const dt = sdata->isNumericWithVal(ival, dval, + /* allow_error */ true); + return dt == KindOfInt64 ? op(ival, val) : + dt == KindOfDouble ? op(dval, val) : + op(0, val); + } + + default: + break; + } + not_reached(); +} + +template +bool cellRelOp(Op op, const Cell* cell, double val) { + assert(tvIsPlausible(cell)); + assert(cell->m_type != KindOfRef); + + switch (cell->m_type) { + case KindOfUninit: + case KindOfNull: return op(false, val != 0); + case KindOfBoolean: return op(!!cell->m_data.num, val != 0); + case KindOfInt64: return op(cell->m_data.num, val); + case KindOfDouble: return op(cell->m_data.dbl, val); + case KindOfArray: return op(true, false); + + case KindOfObject: + return cell->m_data.pobj->isCollection() + ? op.collectionVsNonObj() + : op(cell->m_data.pobj->o_toDouble(), val); + + case KindOfStaticString: + case KindOfString: + return op(toDouble(cell->m_data.pstr), val); + + default: + break; + } + not_reached(); +} + +template +bool cellRelOp(Op op, const Cell* cell, const StringData* val) { + assert(tvIsPlausible(cell)); + assert(cell->m_type != KindOfRef); + + switch (cell->m_type) { + case KindOfUninit: + case KindOfNull: return op(empty_string.get(), val); + case KindOfBoolean: return op(!!cell->m_data.num, toBoolean(val)); + case KindOfDouble: return op(cell->m_data.dbl, val->toDouble()); + case KindOfArray: return op(true, false); + case KindOfString: + case KindOfStaticString: return op(cell->m_data.pstr, val); + + case KindOfInt64: + { + int64_t ival; + double dval; + auto const dt = val->isNumericWithVal(ival, dval, + /* allow_error */ true); + return dt == KindOfInt64 ? op(cell->m_data.num, ival) : + dt == KindOfDouble ? op(cell->m_data.num, dval) : + op(cell->m_data.num, 0); + } + + case KindOfObject: + { + auto const od = cell->m_data.pobj; + if (od->isResource()) return op(true, false); + if (od->isCollection()) return op.collectionVsNonObj(); + try { + String str(const_cast(od)->t___tostring()); + return op(str.get(), val); + } catch (BadTypeConversionException&) { + return op(true, false); + } + } + + default: + break; + } + not_reached(); +} + +template +bool cellRelOp(Op op, const Cell* cell, const ArrayData* ad) { + assert(tvIsPlausible(cell)); + assert(cell->m_type != KindOfRef); + + switch (cell->m_type) { + case KindOfUninit: + case KindOfNull: return op(false, !ad->empty()); + case KindOfBoolean: return op(cell->m_data.num, !ad->empty()); + case KindOfInt64: return op(false, true); + case KindOfDouble: return op(false, true); + case KindOfArray: return op(cell->m_data.parr, ad); + case KindOfStaticString: + case KindOfString: return op(false, true); + case KindOfObject: + return cell->m_data.pobj->isCollection() + ? op.collectionVsNonObj() + : op(true, false); + + default: + break; + } + + not_reached(); +} + +template +bool cellRelOp(Op op, const Cell* cell, const ObjectData* od) { + assert(tvIsPlausible(cell)); + assert(cell->m_type != KindOfRef); + + switch (cell->m_type) { + case KindOfUninit: + case KindOfNull: // TODO: should use o_toBoolean + return op(false, true); + case KindOfBoolean: return op(!!cell->m_data.num, true); + case KindOfInt64: + return od->isCollection() ? op.collectionVsNonObj() + : op(cell->m_data.num, od->o_toInt64()); + case KindOfDouble: + return od->isCollection() ? op.collectionVsNonObj() + : op(cell->m_data.dbl, od->o_toDouble()); + case KindOfArray: + return od->isCollection() ? op.collectionVsNonObj() : op(false, true); + + case KindOfString: + case KindOfStaticString: + if (od->isResource()) return op(false, true); + if (od->isCollection()) return op.collectionVsNonObj(); + try { + String str(const_cast(od)->t___tostring()); + return op(cell->m_data.pstr, str.get()); + } catch (BadTypeConversionException&) { + return op(false, true); + } + + case KindOfObject: + return op(cell->m_data.pobj, od); + + default: + break; + } + + not_reached(); +} + +template +bool tvRelOp(Op op, const TypedValue* tv1, const TypedValue* tv2) { + assert(tvIsPlausible(tv1)); + assert(tvIsPlausible(tv2)); + tv1 = tvToCell(tv1); + tv2 = tvToCell(tv2); + + switch (tv2->m_type) { + case KindOfUninit: + case KindOfNull: + return IS_STRING_TYPE(tv1->m_type) + ? op(tv1->m_data.pstr, empty_string.get()) + : cellRelOp(op, tv1, false); + case KindOfInt64: return cellRelOp(op, tv1, tv2->m_data.num); + case KindOfBoolean: return cellRelOp(op, tv1, !!tv2->m_data.num); + case KindOfDouble: return cellRelOp(op, tv1, tv2->m_data.dbl); + case KindOfStaticString: + case KindOfString: return cellRelOp(op, tv1, tv2->m_data.pstr); + case KindOfArray: return cellRelOp(op, tv1, tv2->m_data.parr); + case KindOfObject: return cellRelOp(op, tv1, tv2->m_data.pobj); + default: + break; + } + not_reached(); +} + +/* + * These relative ops helper function objects define operator() for + * each primitive type, and for the case of a complex type being + * compared with itself (that is obj with obj, string with string, + * array with array). + * + * They must also define a function called collectionVsNonObj() which + * is used when comparing collections with non-object types. (The obj + * vs obj function should handle the collection vs collection and + * collection vs non-collection object cases.) This is just to handle + * that php operator == returns false in these cases, while the Lt/Gt + * operators throw and exception. + */ + +struct Eq { + template + typename std::enable_if< + !std::is_pointer::value && + !std::is_pointer::value, + bool + >::type operator()(T t, U u) const { return t == u; } + + bool operator()(const StringData* sd1, const StringData* sd2) const { + return sd1->equal(sd2); + } + + bool operator()(const ArrayData* ad1, const ArrayData* ad2) const { + return ad1->equal(ad2, false); + } + + bool operator()(const ObjectData* od1, const ObjectData* od2) const { + if (od1 == od2) return true; + if (od1->isResource() || od2->isResource()) return false; + if (od1->getVMClass() != od2->getVMClass()) return false; + if (od1->isCollection()) { + // TODO constness + return collectionEquals(const_cast(od1), + const_cast(od2)); + } + Array ar1(od1->o_toArray()); + Array ar2(od2->o_toArray()); + return ar1->equal(ar2.get(), false); + } + + bool collectionVsNonObj() const { return false; } +}; + +struct Lt { + template + typename std::enable_if< + !std::is_pointer::value && + !std::is_pointer::value, + bool + >::type operator()(T t, U u) const { return t < u; } + + bool operator()(const StringData* sd1, const StringData* sd2) const { + return sd1->compare(sd2) < 0; + } + + bool operator()(const ArrayData* ad1, const ArrayData* ad2) const { + return ad1->compare(ad2) < 0; + } + + bool operator()(const ObjectData* od1, const ObjectData* od2) const { + if (od1->isCollection() || od2->isCollection()) { + throw_collection_compare_exception(); + } + if (od1 == od2) return false; + Array ar1(od1->o_toArray()); + Array ar2(od2->o_toArray()); + return (*this)(ar1.get(), ar2.get()); + } + + bool collectionVsNonObj() const { + throw_collection_compare_exception(); + not_reached(); + } +}; + +struct Gt { + template + typename std::enable_if< + !std::is_pointer::value && + !std::is_pointer::value, + bool + >::type operator()(T t, U u) const { return t > u; } + + bool operator()(const StringData* sd1, const StringData* sd2) const { + return sd1->compare(sd2) > 0; + } + + bool operator()(const ArrayData* ad1, const ArrayData* ad2) const { + return 0 > ad2->compare(ad1); // Not symmetric; order matters here. + } + + bool operator()(const ObjectData* od1, const ObjectData* od2) const { + if (od1->isCollection() || od2->isCollection()) { + throw_collection_compare_exception(); + } + if (od1 == od2) return false; + Array ar1(od1->o_toArray()); + Array ar2(od2->o_toArray()); + return (*this)(ar1.get(), ar2.get()); + } + + bool collectionVsNonObj() const { + throw_collection_compare_exception(); + not_reached(); + } +}; + +////////////////////////////////////////////////////////////////////// + +} + bool tvSame(const TypedValue* tv1, const TypedValue* tv2) { + assert(tvIsPlausible(tv1)); + assert(tvIsPlausible(tv2)); + bool const null1 = IS_NULL_TYPE(tv1->m_type); bool const null2 = IS_NULL_TYPE(tv2->m_type); if (null1 && null2) return true; if (null1 || null2) return false; - if (tv1->m_type == KindOfRef) tv1 = tv1->m_data.pref->tv(); - if (tv2->m_type == KindOfRef) tv2 = tv2->m_data.pref->tv(); + tv1 = tvToCell(tv1); + tv2 = tvToCell(tv2); switch (tv1->m_type) { case KindOfInt64: @@ -61,5 +429,100 @@ bool tvSame(const TypedValue* tv1, const TypedValue* tv2) { ////////////////////////////////////////////////////////////////////// +/* + * XXX: HOT_FUNC selections are basically whatever random choices were + * in the old code ... we should probably re-evaluate this. + */ + +bool cellEqual(const Cell* cell, bool val) { + return cellRelOp(Eq(), cell, val); +} + +HOT_FUNC +bool cellEqual(const Cell* cell, int64_t val) { + return cellRelOp(Eq(), cell, val); +} + +bool cellEqual(const Cell* cell, double val) { + return cellRelOp(Eq(), cell, val); +} + +bool cellEqual(const Cell* cell, const StringData* val) { + return cellRelOp(Eq(), cell, val); +} + +bool cellEqual(const Cell* cell, const ArrayData* val) { + return cellRelOp(Eq(), cell, val); +} + +bool cellEqual(const Cell* cell, const ObjectData* val) { + return cellRelOp(Eq(), cell, val); +} + +HOT_FUNC +bool tvEqual(const TypedValue* tv1, const TypedValue* tv2) { + return tvRelOp(Eq(), tv1, tv2); +} + +bool cellLess(const Cell* cell, bool val) { + return cellRelOp(Lt(), cell, val); +} + +bool cellLess(const Cell* cell, int64_t val) { + return cellRelOp(Lt(), cell, val); +} + +bool cellLess(const Cell* cell, double val) { + return cellRelOp(Lt(), cell, val); +} + +bool cellLess(const Cell* cell, const StringData* val) { + return cellRelOp(Lt(), cell, val); +} + +bool cellLess(const Cell* cell, const ArrayData* val) { + return cellRelOp(Lt(), cell, val); +} + +bool cellLess(const Cell* cell, const ObjectData* val) { + return cellRelOp(Lt(), cell, val); +} + +HOT_FUNC +bool tvLess(const TypedValue* tv1, const TypedValue* tv2) { + return tvRelOp(Lt(), tv1, tv2); +} + +bool cellGreater(const Cell* cell, bool val) { + return cellRelOp(Gt(), cell, val); +} + +//NB: was HOT_FUNC in old code ... dunno if this makes sense anymore. +bool cellGreater(const Cell* cell, int64_t val) { + return cellRelOp(Gt(), cell, val); +} + +bool cellGreater(const Cell* cell, double val) { + return cellRelOp(Gt(), cell, val); +} + +bool cellGreater(const Cell* cell, const StringData* val) { + return cellRelOp(Gt(), cell, val); +} + +bool cellGreater(const Cell* cell, const ArrayData* val) { + return cellRelOp(Gt(), cell, val); +} + +bool cellGreater(const Cell* cell, const ObjectData* val) { + return cellRelOp(Gt(), cell, val); +} + +bool tvGreater(const TypedValue* tv1, const TypedValue* tv2) { + return tvRelOp(Gt(), tv1, tv2); +} + +////////////////////////////////////////////////////////////////////// + } diff --git a/hphp/runtime/base/tv_comparisons.h b/hphp/runtime/base/tv_comparisons.h index 4f6e6874c..3b867a166 100644 --- a/hphp/runtime/base/tv_comparisons.h +++ b/hphp/runtime/base/tv_comparisons.h @@ -16,13 +16,12 @@ #ifndef incl_HPHP_TV_COMPARISONS_H_ #define incl_HPHP_TV_COMPARISONS_H_ +#include "hphp/runtime/vm/core_types.h" + namespace HPHP { ////////////////////////////////////////////////////////////////////// - -struct TypedValue; - -////////////////////////////////////////////////////////////////////// +// Php's operator === /* * Returns whether two TypedValues have the same value, in sense of @@ -30,8 +29,71 @@ struct TypedValue; */ bool tvSame(const TypedValue*, const TypedValue*); +////////////////////////////////////////////////////////////////////// +// Php's operator == + +/* + * Returns whether a Cell has the same value as an unpackaged type, in + * the sense of php's == operator. + */ +bool cellEqual(const Cell*, bool); +bool cellEqual(const Cell*, int); +bool cellEqual(const Cell*, int64_t); +bool cellEqual(const Cell*, double); +bool cellEqual(const Cell*, const StringData*); +bool cellEqual(const Cell*, const ArrayData*); +bool cellEqual(const Cell*, const ObjectData*); + +/* + * Returns whether two TypedValues have the same value, in the sense + * of php's == operator. + */ +bool tvEqual(const TypedValue*, const TypedValue*); + +////////////////////////////////////////////////////////////////////// +// Php's operator < + +/* + * Returns whether a Cell is less than an unpackaged type, in the + * sense of php's < operator. + */ +bool cellLess(const Cell*, bool); +bool cellLess(const Cell*, int); +bool cellLess(const Cell*, int64_t); +bool cellLess(const Cell*, double); +bool cellLess(const Cell*, const StringData*); +bool cellLess(const Cell*, const ArrayData*); +bool cellLess(const Cell*, const ObjectData*); + +/* + * Returns whether tv1 is less than tv2, as in php's < operator. + */ +bool tvLess(const TypedValue*, const TypedValue*); + +////////////////////////////////////////////////////////////////////// +// Php's operator > + +/* + * Returns whether a Cell is greater than an unpackaged type, in the + * sense of php's > operator. + */ +bool cellGreater(const Cell*, bool); +bool cellGreater(const Cell*, int); +bool cellGreater(const Cell*, int64_t); +bool cellGreater(const Cell*, double); +bool cellGreater(const Cell*, const StringData*); +bool cellGreater(const Cell*, const ArrayData*); +bool cellGreater(const Cell*, const ObjectData*); + +/* + * Returns whether tv1 is greather than tv2, as in php's > operator. + */ +bool tvGreater(const TypedValue*, const TypedValue*); + ////////////////////////////////////////////////////////////////////// } +#include "hphp/runtime/base/tv_comparisons-inl.h" + #endif diff --git a/hphp/runtime/base/tv_helpers.h b/hphp/runtime/base/tv_helpers.h index 101c0c218..efe7bb4a3 100644 --- a/hphp/runtime/base/tv_helpers.h +++ b/hphp/runtime/base/tv_helpers.h @@ -27,6 +27,8 @@ namespace HPHP { /////////////////////////////////////////////////////////////////////////////// +class Variant; + template inline TypedValue tv(DataType type, Data data) { static_assert(sizeof(Data) == sizeof(int64_t), @@ -242,12 +244,12 @@ inline void tvWriteObject(ObjectData* pobj, TypedValue* tv) { } // conditionally unbox tv -inline TypedValue* tvToCell(TypedValue* tv) { +inline Cell* tvToCell(TypedValue* tv) { return LIKELY(tv->m_type != KindOfRef) ? tv : tv->m_data.pref->tv(); } // conditionally unbox tv, preserve constness. -inline const TypedValue* tvToCell(const TypedValue* tv) { +inline const Cell* tvToCell(const TypedValue* tv) { return LIKELY(tv->m_type != KindOfRef) ? tv : tv->m_data.pref->tv(); } diff --git a/hphp/runtime/base/type_array.cpp b/hphp/runtime/base/type_array.cpp index 75d295fba..926659647 100644 --- a/hphp/runtime/base/type_array.cpp +++ b/hphp/runtime/base/type_array.cpp @@ -358,7 +358,7 @@ bool Array::less(CVarRef v2) const { if (v2.getType() == KindOfArray) { return m_px->compare(v2.toArray().get()) < 0; } - return v2.more(*this); + return HPHP::more(v2, *this); } bool Array::more(CArrRef v2, bool flip /* = true */) const { @@ -386,7 +386,7 @@ bool Array::more(CVarRef v2) const { if (v2.getType() == KindOfArray) { return v2.toArray().get()->compare(m_px) < 0; } - return v2.less(*this); + return HPHP::less(v2, *this); } /////////////////////////////////////////////////////////////////////////////// @@ -634,7 +634,7 @@ bool Array::valueExists(CVarRef search_value, bool strict /* = false */) const { for (ArrayIter iter(*this); iter; ++iter) { if ((strict && HPHP::same(iter.secondRef(), search_value)) || - (!strict && iter.secondRef().equal(search_value))) { + (!strict && HPHP::equal(iter.secondRef(), search_value))) { return true; } } @@ -644,7 +644,7 @@ bool Array::valueExists(CVarRef search_value, Variant Array::key(CVarRef search_value, bool strict /* = false */) const { for (ArrayIter iter(*this); iter; ++iter) { if ((strict && HPHP::same(iter.secondRef(), search_value)) || - (!strict && iter.secondRef().equal(search_value))) { + (!strict && HPHP::equal(iter.secondRef(), search_value))) { return iter.first(); } } @@ -663,7 +663,7 @@ Array Array::keys(CVarRef search_value /* = null_variant */, Array ret = Array::Create(); for (ArrayIter iter(*this); iter; ++iter) { if ((strict && HPHP::same(iter.secondRef(), search_value)) || - (!strict && iter.secondRef().equal(search_value))) { + (!strict && HPHP::equal(iter.secondRef(), search_value))) { ret.append(iter.first()); } } @@ -1006,13 +1006,13 @@ bool Array::MultiSort(std::vector &data, bool renumber) { } int Array::SortRegularAscending(CVarRef v1, CVarRef v2, const void *data) { - if (v1.less(v2)) return -1; - if (v1.equal(v2)) return 0; + if (HPHP::less(v1, v2)) return -1; + if (tvEqual(v1.asTypedValue(), v2.asTypedValue())) return 0; return 1; } int Array::SortRegularDescending(CVarRef v1, CVarRef v2, const void *data) { - if (v1.less(v2)) return 1; - if (v1.equal(v2)) return 0; + if (HPHP::less(v1, v2)) return 1; + if (tvEqual(v1.asTypedValue(), v2.asTypedValue())) return 0; return -1; } diff --git a/hphp/runtime/base/type_variant.cpp b/hphp/runtime/base/type_variant.cpp index 0b99da5c9..23c0e1dbb 100644 --- a/hphp/runtime/base/type_variant.cpp +++ b/hphp/runtime/base/type_variant.cpp @@ -1649,20 +1649,6 @@ Variant::operator Object() const { } \ return false; \ -bool Variant::equal(bool v2) const { UNWRAP(equal);} -bool Variant::equal(int v2) const { UNWRAP(equal);} -HOT_FUNC -bool Variant::equal(int64_t v2) const { UNWRAP(equal);} -bool Variant::equal(double v2) const { UNWRAP(equal);} -bool Variant::equal(litstr v2) const { UNWRAP_STR(equal);} -bool Variant::equal(const StringData *v2) const { UNWRAP_STR(equal);} -HOT_FUNC -bool Variant::equal(CStrRef v2) const { UNWRAP_STR(equal);} -bool Variant::equal(CArrRef v2) const { UNWRAP(equal);} -bool Variant::equal(CObjRef v2) const { UNWRAP(equal);} -HOT_FUNC -bool Variant::equal(CVarRef v2) const { UNWRAP_VAR(equal,equal);} - bool Variant::equalAsStr(bool v2) const { UNWRAP_STRING(equalAsStr);} bool Variant::equalAsStr(int v2) const { UNWRAP_STRING(equalAsStr);} bool Variant::equalAsStr(int64_t v2) const { UNWRAP_STRING(equalAsStr);} @@ -1676,30 +1662,6 @@ bool Variant::equalAsStr(CArrRef v2) const { UNWRAP_STRING(equalAsStr);} bool Variant::equalAsStr(CObjRef v2) const { UNWRAP_STRING(equalAsStr);} bool Variant::equalAsStr(CVarRef v2) const { UNWRAP_STRING(equalAsStr);} -bool Variant::less(bool v2) const { UNWRAP(more);} -bool Variant::less(int v2) const { UNWRAP(more);} -bool Variant::less(int64_t v2) const { UNWRAP(more);} -bool Variant::less(double v2) const { UNWRAP(more);} -bool Variant::less(litstr v2) const { UNWRAP_STR(more);} -bool Variant::less(const StringData *v2) const { UNWRAP_STR(more);} -bool Variant::less(CStrRef v2) const { UNWRAP_STR(more);} -bool Variant::less(CArrRef v2) const { UNWRAP_ARR(less,more);} -bool Variant::less(CObjRef v2) const { UNWRAP(more);} -HOT_FUNC -bool Variant::less(CVarRef v2) const { UNWRAP_VAR(less,more);} - -bool Variant::more(bool v2) const { UNWRAP(less);} -bool Variant::more(int v2) const { UNWRAP(less);} -HOT_FUNC -bool Variant::more(int64_t v2) const { UNWRAP(less);} -bool Variant::more(double v2) const { UNWRAP(less);} -bool Variant::more(litstr v2) const { UNWRAP_STR(less);} -bool Variant::more(const StringData *v2) const { UNWRAP_STR(less);} -bool Variant::more(CStrRef v2) const { UNWRAP_STR(less);} -bool Variant::more(CArrRef v2) const { UNWRAP_ARR(more,less);} -bool Variant::more(CObjRef v2) const { UNWRAP(less);} -bool Variant::more(CVarRef v2) const { UNWRAP_VAR(more,less);} - /////////////////////////////////////////////////////////////////////////////// // offset functions diff --git a/hphp/runtime/base/type_variant.h b/hphp/runtime/base/type_variant.h index 8ba6297d7..9f44efd0e 100644 --- a/hphp/runtime/base/type_variant.h +++ b/hphp/runtime/base/type_variant.h @@ -698,17 +698,6 @@ class Variant : private TypedValue { * Comparisons */ - bool equal(bool v2) const; - bool equal(int v2) const; - bool equal(int64_t v2) const; - bool equal(double v2) const; - bool equal(litstr v2) const; - bool equal(const StringData *v2) const; - bool equal(CStrRef v2) const; - bool equal(CArrRef v2) const; - bool equal(CObjRef v2) const; - bool equal(CVarRef v2) const; - bool equalAsStr(bool v2) const; bool equalAsStr(int v2) const; bool equalAsStr(int64_t v2) const; @@ -720,28 +709,6 @@ class Variant : private TypedValue { bool equalAsStr(CObjRef v2) const; bool equalAsStr(CVarRef v2) const; - bool less(bool v2) const; - bool less(int v2) const; - bool less(int64_t v2) const; - bool less(double v2) const; - bool less(litstr v2) const; - bool less(const StringData *v2) const; - bool less(CStrRef v2) const; - bool less(CArrRef v2) const; - bool less(CObjRef v2) const; - bool less(CVarRef v2) const; - - bool more(bool v2) const; - bool more(int v2) const; - bool more(int64_t v2) const; - bool more(double v2) const; - bool more(litstr v2) const; - bool more(const StringData *v2) const; - bool more(CStrRef v2) const; - bool more(CArrRef v2) const; - bool more(CObjRef v2) const; - bool more(CVarRef v2) const; - /** * Output functions */ @@ -1008,16 +975,17 @@ class Variant : private TypedValue { void setToDefaultObject(); /* - * Access this Variant as a TypedValue. Differs slightly from - * getTypedAccessor() by returning the outer TypedValue if it is + * Access this Variant as a TypedValue. Does not unbox refs, etc. + */ + const TypedValue* asTypedValue() const { return this; } + TypedValue* asTypedValue() { return this; } + + /* + * Access this Variant as a Cell. I.e. unboxes it if it was a * KindOfRef. */ - TypedValue* asTypedValue() { - return reinterpret_cast(this); - } - const TypedValue* asTypedValue() const { - return reinterpret_cast(this); - } + const Cell* asCell() const { return tvToCell(asTypedValue()); } + Cell* asCell() { return tvToCell(asTypedValue()); } /** * Based on the order in complex_types.h, TypedValue is defined before. diff --git a/hphp/runtime/base/zend/zend_collator.cpp b/hphp/runtime/base/zend/zend_collator.cpp index f76748952..6abaccf66 100644 --- a/hphp/runtime/base/zend/zend_collator.cpp +++ b/hphp/runtime/base/zend/zend_collator.cpp @@ -454,12 +454,12 @@ static int collator_regular_compare_function(CVarRef v1, CVarRef v2, norm2 = collator_normalize_sort_argument(str2); } if (ascending) { - if (norm1.less(norm2)) return -1; - if (norm1.equal(norm2)) return 0; + if (less(norm1, norm2)) return -1; + if (equal(norm1, norm2)) return 0; return 1; } - if (norm1.less(norm2)) return 1; - if (norm1.equal(norm2)) return 0; + if (less(norm1, norm2)) return 1; + if (equal(norm1, norm2)) return 0; return -1; } @@ -490,12 +490,12 @@ static int collator_numeric_compare_function(CVarRef v1, CVarRef v2, num2 = v2.toDouble(); } if (ascending) { - if (num1.less(num2)) return -1; - if (num1.equal(num2)) return 0; + if (less(num1, num2)) return -1; + if (equal(num1, num2)) return 0; return 1; } - if (num1.less(num2)) return 1; - if (num1.equal(num2)) return 0; + if (less(num1, num2)) return 1; + if (equal(num1, num2)) return 0; return -1; } diff --git a/hphp/runtime/debugger/cmd/cmd_where.cpp b/hphp/runtime/debugger/cmd/cmd_where.cpp index 9f23febb8..4f4c42982 100644 --- a/hphp/runtime/debugger/cmd/cmd_where.cpp +++ b/hphp/runtime/debugger/cmd/cmd_where.cpp @@ -16,6 +16,7 @@ #include "hphp/runtime/debugger/cmd/cmd_where.h" #include "hphp/runtime/base/array/array_iterator.h" +#include "hphp/runtime/base/comparisons.h" namespace HPHP { namespace Eval { /////////////////////////////////////////////////////////////////////////////// @@ -143,7 +144,7 @@ void CmdWhere::processStackTrace() { CArrRef frame(iter.secondRef()); Array smallFrame; for (ArrayIter iter2(frame); iter2; ++iter2) { - if (iter2.first().equal(s_args)) { + if (equal(iter2.first(), s_args)) { continue; } smallFrame.set(iter2.first(), iter2.secondRef()); diff --git a/hphp/runtime/ext/ext_misc.cpp b/hphp/runtime/ext/ext_misc.cpp index 80fef7270..9cac86783 100644 --- a/hphp/runtime/ext/ext_misc.cpp +++ b/hphp/runtime/ext/ext_misc.cpp @@ -373,7 +373,7 @@ String f_token_name(int64_t token) { static const StaticString s_marauder("I solemnly swear that I am up to no good."); Variant f_hphp_process_abort(CVarRef magic) { - if (magic.equal(s_marauder)) { + if (equal(magic, s_marauder)) { *((int*)0) = 0xdead; } return null_variant; diff --git a/hphp/runtime/vm/bytecode.cpp b/hphp/runtime/vm/bytecode.cpp index 012553600..abddcdaba 100644 --- a/hphp/runtime/vm/bytecode.cpp +++ b/hphp/runtime/vm/bytecode.cpp @@ -4551,13 +4551,13 @@ inline void OPTBLD_INLINE VMExecutionContext::iopSSwitch(PC& pc) { StrVecItem* jmptab = (StrVecItem*)pc; pc += veclen * sizeof(*jmptab); - TypedValue* val = m_stack.topTV(); + Cell* val = tvToCell(m_stack.topTV()); Unit* u = m_fp->m_func->unit(); unsigned i; for (i = 0; i < cases; ++i) { auto& item = jmptab[i]; const StringData* str = u->lookupLitstrId(item.str); - if (tvAsVariant(val).equal(str)) { + if (cellEqual(val, str)) { pc = origPC + item.dest; break; } diff --git a/hphp/runtime/vm/core_types.h b/hphp/runtime/vm/core_types.h index 930b7efdf..bdb420b39 100644 --- a/hphp/runtime/vm/core_types.h +++ b/hphp/runtime/vm/core_types.h @@ -24,14 +24,6 @@ namespace HPHP { /////////////////////////////////////////////////////////////////////////////// -/* - * These may be used to provide a little more self-documentation about - * whether execution stack typed values are assumed to be cells or - * vars. (See bytecode.specification for details.) - */ -typedef TypedValue Cell; -typedef TypedValue Var; - /* * Non-enumerated version of type for referring to opcodes or the * bytecode stream. (Use the enum Op in hhbc.h for an enumerated diff --git a/hphp/runtime/vm/jit/codegen.cpp b/hphp/runtime/vm/jit/codegen.cpp index 17835e6dc..c53e044b5 100644 --- a/hphp/runtime/vm/jit/codegen.cpp +++ b/hphp/runtime/vm/jit/codegen.cpp @@ -2453,9 +2453,9 @@ static TCA sswitchHelperSlow(TypedValue typedVal, const StringData** strs, int numStrs, TCA* jmptab) { - TypedValue* cell = tvToCell(&typedVal); + Cell* cell = tvToCell(&typedVal); for (int i = 0; i < numStrs; ++i) { - if (tvAsCVarRef(cell).equal(strs[i])) return jmptab[i]; + if (cellEqual(cell, strs[i])) return jmptab[i]; } return jmptab[numStrs]; // default case } diff --git a/hphp/test/quick/some_cmp_tests.php b/hphp/test/quick/some_cmp_tests.php new file mode 100644 index 000000000..d54821bfe --- /dev/null +++ b/hphp/test/quick/some_cmp_tests.php @@ -0,0 +1,58 @@ + $y); +} + +echo "======\n"; + +eq('Array', array(1,2)); +eq('Array', array()); +eq(array(), 'Array'); +eq(array('a', 'b'), 'Array'); +echo "\n"; +lt('Array', array(1,2)); +lt('Array', array()); +lt(array(), 'Array'); +lt(array('a', 'b'), 'Array'); +echo "\n"; +gt('Array', array(1,2)); +gt('Array', array()); +gt(array(), 'Array'); +gt(array('a', 'b'), 'Array'); + +echo "======\n"; + +eq('', null); +eq(null, null); +eq(null, ''); +eq('', ''); +echo "\n"; +lt('', null); +lt(null, null); +lt(null, ''); +lt('', ''); +echo "\n"; +gt('', null); +gt(null, null); +gt(null, ''); +gt('', ''); + +echo "======\n"; + +eq(-1.0, null); +eq(null, -1.0); +echo "\n"; +lt(-1.0, null); +lt(null, -1.0); +echo "\n"; +gt(-1.0, null); +gt(null, -1.0); diff --git a/hphp/test/quick/some_cmp_tests.php.expect b/hphp/test/quick/some_cmp_tests.php.expect new file mode 100644 index 000000000..ddcafad20 --- /dev/null +++ b/hphp/test/quick/some_cmp_tests.php.expect @@ -0,0 +1,39 @@ +====== +bool(false) +bool(false) +bool(false) +bool(false) + +bool(true) +bool(true) +bool(false) +bool(false) + +bool(false) +bool(false) +bool(true) +bool(true) +====== +bool(true) +bool(true) +bool(true) +bool(true) + +bool(false) +bool(false) +bool(false) +bool(false) + +bool(false) +bool(false) +bool(false) +bool(false) +====== +bool(false) +bool(false) + +bool(false) +bool(true) + +bool(true) +bool(false)