Arquivos
hhvm/hphp/runtime/base/tv_comparisons.cpp
T
Jordan DeLong 8e3ee5a87f Use stringToNumeric in tv_comparisons
I like this api slightly better than passing in multiple
output refs, but when I added I didn't change these so I could see
perflabs separately.
2013-07-15 15:46:27 -07:00

542 linhas
15 KiB
C++

/*
+----------------------------------------------------------------------+
| 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. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/base/tv_comparisons.h"
#include <type_traits>
#include "hphp/runtime/base/tv_conversions.h"
#include "hphp/runtime/base/comparisons.h"
#include "hphp/runtime/base/type_conversions.h"
namespace HPHP {
//////////////////////////////////////////////////////////////////////
extern bool collectionEquals(ObjectData*, ObjectData*);
//////////////////////////////////////////////////////////////////////
namespace {
//////////////////////////////////////////////////////////////////////
/*
* 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<class Op>
bool cellRelOp(Op op, Cell cell, bool val) {
return op(cellToBool(cell), val);
}
template<class Op>
bool cellRelOp(Op op, Cell cell, int64_t val) {
assert(cellIsPlausible(&cell));
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 num = stringToNumeric(cell.m_data.pstr);
return num.m_type == KindOfInt64 ? op(num.m_data.num, val) :
num.m_type == KindOfDouble ? op(num.m_data.dbl, val) :
op(0, val);
}
default:
break;
}
not_reached();
}
template<class Op>
bool cellRelOp(Op op, Cell cell, double val) {
assert(cellIsPlausible(&cell));
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<class Op>
bool cellRelOp(Op op, Cell cell, const StringData* val) {
assert(cellIsPlausible(&cell));
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:
{
auto const num = stringToNumeric(val);
return num.m_type == KindOfInt64 ? op(cell.m_data.num, num.m_data.num) :
num.m_type == KindOfDouble ? op(cell.m_data.num, num.m_data.dbl) :
op(cell.m_data.num, 0);
}
case KindOfObject:
{
auto const od = cell.m_data.pobj;
if (od->isResource()) return op(od->o_toDouble(), val->toDouble());
if (od->isCollection()) return op.collectionVsNonObj();
try {
String str(const_cast<ObjectData*>(od)->t___tostring());
return op(str.get(), val);
} catch (BadTypeConversionException&) {
return op(true, false);
}
}
default:
break;
}
not_reached();
}
template<class Op>
bool cellRelOp(Op op, Cell cell, const ArrayData* ad) {
assert(cellIsPlausible(&cell));
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:
{
auto const od = cell.m_data.pobj;
if (od->isResource()) return op(false, true);
return od->isCollection()
? op.collectionVsNonObj()
: op(true, false);
}
default:
break;
}
not_reached();
}
template<class Op>
bool cellRelOp(Op op, Cell cell, const ObjectData* od) {
assert(cellIsPlausible(&cell));
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:
if (od->isResource()) return op(true, false);
return od->isCollection() ? op.collectionVsNonObj() : op(false, true);
case KindOfString:
case KindOfStaticString:
if (od->isResource()) return op(cell.m_data.pstr->toDouble(),
od->o_toDouble());
if (od->isCollection()) return op.collectionVsNonObj();
try {
String str(const_cast<ObjectData*>(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<class Op>
bool cellRelOp(Op op, Cell c1, Cell c2) {
assert(cellIsPlausible(&c1));
assert(cellIsPlausible(&c2));
switch (c2.m_type) {
case KindOfUninit:
case KindOfNull:
return IS_STRING_TYPE(c1.m_type)
? op(c1.m_data.pstr, empty_string.get())
: cellRelOp(op, c1, false);
case KindOfInt64: return cellRelOp(op, c1, c2.m_data.num);
case KindOfBoolean: return cellRelOp(op, c1, !!c2.m_data.num);
case KindOfDouble: return cellRelOp(op, c1, c2.m_data.dbl);
case KindOfStaticString:
case KindOfString: return cellRelOp(op, c1, c2.m_data.pstr);
case KindOfArray: return cellRelOp(op, c1, c2.m_data.parr);
case KindOfObject: return cellRelOp(op, c1, c2.m_data.pobj);
default:
break;
}
not_reached();
}
template<class Op>
bool tvRelOp(Op op, TypedValue tv1, TypedValue tv2) {
assert(tvIsPlausible(&tv1));
assert(tvIsPlausible(&tv2));
return cellRelOp(op, *tvToCell(&tv1), *tvToCell(&tv2));
}
/*
* 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<class T, class U>
typename std::enable_if<
!std::is_pointer<T>::value &&
!std::is_pointer<U>::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<ObjectData*>(od1),
const_cast<ObjectData*>(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<class T, class U>
typename std::enable_if<
!std::is_pointer<T>::value &&
!std::is_pointer<U>::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<class T, class U>
typename std::enable_if<
!std::is_pointer<T>::value &&
!std::is_pointer<U>::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 cellSame(Cell c1, Cell c2) {
assert(cellIsPlausible(&c1));
assert(cellIsPlausible(&c2));
bool const null1 = IS_NULL_TYPE(c1.m_type);
bool const null2 = IS_NULL_TYPE(c2.m_type);
if (null1 && null2) return true;
if (null1 || null2) return false;
switch (c1.m_type) {
case KindOfInt64:
case KindOfBoolean:
if (c2.m_type != c1.m_type) return false;
return c1.m_data.num == c2.m_data.num;
case KindOfDouble:
if (c2.m_type != c1.m_type) return false;
return c1.m_data.dbl == c2.m_data.dbl;
case KindOfStaticString:
case KindOfString:
if (!IS_STRING_TYPE(c2.m_type)) return false;
return c1.m_data.pstr->same(c2.m_data.pstr);
case KindOfArray:
if (c2.m_type != KindOfArray) return false;
return c1.m_data.parr->equal(c2.m_data.parr, true);
case KindOfObject:
return c2.m_type == KindOfObject &&
c1.m_data.pobj == c2.m_data.pobj;
default:
break;
}
not_reached();
}
bool tvSame(TypedValue tv1, TypedValue tv2) {
assert(tvIsPlausible(&tv1));
assert(tvIsPlausible(&tv2));
return cellSame(*tvToCell(&tv1), *tvToCell(&tv2));
}
//////////////////////////////////////////////////////////////////////
/*
* XXX: HOT_FUNC selections are basically whatever random choices were
* in the old code ... we should probably re-evaluate this.
*/
bool cellEqual(Cell cell, bool val) {
return cellRelOp(Eq(), cell, val);
}
HOT_FUNC
bool cellEqual(Cell cell, int64_t val) {
return cellRelOp(Eq(), cell, val);
}
bool cellEqual(Cell cell, double val) {
return cellRelOp(Eq(), cell, val);
}
bool cellEqual(Cell cell, const StringData* val) {
return cellRelOp(Eq(), cell, val);
}
bool cellEqual(Cell cell, const ArrayData* val) {
return cellRelOp(Eq(), cell, val);
}
bool cellEqual(Cell cell, const ObjectData* val) {
return cellRelOp(Eq(), cell, val);
}
bool cellEqual(Cell c1, Cell c2) {
return cellRelOp(Eq(), c1, c2);
}
HOT_FUNC
bool tvEqual(TypedValue tv1, TypedValue tv2) {
return tvRelOp(Eq(), tv1, tv2);
}
bool cellLess(Cell cell, bool val) {
return cellRelOp(Lt(), cell, val);
}
bool cellLess(Cell cell, int64_t val) {
return cellRelOp(Lt(), cell, val);
}
bool cellLess(Cell cell, double val) {
return cellRelOp(Lt(), cell, val);
}
bool cellLess(Cell cell, const StringData* val) {
return cellRelOp(Lt(), cell, val);
}
bool cellLess(Cell cell, const ArrayData* val) {
return cellRelOp(Lt(), cell, val);
}
bool cellLess(Cell cell, const ObjectData* val) {
return cellRelOp(Lt(), cell, val);
}
bool cellLess(Cell c1, Cell c2) {
return cellRelOp(Lt(), c1, c2);
}
HOT_FUNC
bool tvLess(TypedValue tv1, TypedValue tv2) {
return tvRelOp(Lt(), tv1, tv2);
}
bool cellGreater(Cell cell, bool val) {
return cellRelOp(Gt(), cell, val);
}
//NB: was HOT_FUNC in old code ... dunno if this makes sense anymore.
bool cellGreater(Cell cell, int64_t val) {
return cellRelOp(Gt(), cell, val);
}
bool cellGreater(Cell cell, double val) {
return cellRelOp(Gt(), cell, val);
}
bool cellGreater(Cell cell, const StringData* val) {
return cellRelOp(Gt(), cell, val);
}
bool cellGreater(Cell cell, const ArrayData* val) {
return cellRelOp(Gt(), cell, val);
}
bool cellGreater(Cell cell, const ObjectData* val) {
return cellRelOp(Gt(), cell, val);
}
bool cellGreater(Cell c1, Cell c2) {
return cellRelOp(Gt(), c1, c2);
}
bool tvGreater(TypedValue tv1, TypedValue tv2) {
return tvRelOp(Gt(), tv1, tv2);
}
//////////////////////////////////////////////////////////////////////
bool cellLessOrEqual(Cell c1, Cell c2) {
assert(cellIsPlausible(&c1));
assert(cellIsPlausible(&c2));
if ((c1.m_type == KindOfArray && c2.m_type == KindOfArray) ||
(c1.m_type == KindOfObject && c2.m_type == KindOfObject)) {
return cellLess(c1, c2) || cellEqual(c1, c2);
}
return !cellGreater(c1, c2);
}
bool cellGreaterOrEqual(Cell c1, Cell c2) {
assert(cellIsPlausible(&c1));
assert(cellIsPlausible(&c2));
if ((c1.m_type == KindOfArray && c2.m_type == KindOfArray) ||
(c1.m_type == KindOfObject && c2.m_type == KindOfObject)) {
return cellGreater(c1, c2) || cellEqual(c1, c2);
}
return !cellLess(c1, c2);
}
//////////////////////////////////////////////////////////////////////
}