Arquivos
hhvm/hphp/runtime/base/array/sort_helpers.h
T
Mark Williams 6f4b1c76a8 Fix issue with call_user_func_array and by-ref params
We can't box a non-ref value for a ref param because
we could be modifying an array with refCount != 1.

zend warns, skips the call and returns null. For now, this
makes us warn, but do the call (without modifying the array).

A file scope flag controls whether to skip the call or not,
and should be changed (and eliminated) once all our tests pass.

With the flag turned on, we still dont match zend's behavior,
because its happy to go ahead and call the function with no
warning in the case of a literal array parameter
(cuf('foo', array(1))), even though it warns and skips it when
the array is in a variable ($a=array(1); cuf('foo', $a)).
2013-04-30 09:58:57 -07:00

289 linhas
11 KiB
C++

/*
+----------------------------------------------------------------------+
| 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. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_SORT_HELPERS_H_
#define incl_HPHP_SORT_HELPERS_H_
#include <runtime/base/complex_types.h>
#include <runtime/base/builtin_functions.h>
#include <runtime/base/comparisons.h>
#include <runtime/base/zend/zend_functions.h>
#include <runtime/base/sort_flags.h>
#include <util/safesort.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
template <typename AccessorT, int sort_flags, bool ascending>
struct IntElmCompare {
typedef typename AccessorT::ElmT ElmT;
AccessorT acc;
bool operator()(ElmT left, ElmT right) const {
int64_t iLeft = acc.getInt(left);
int64_t iRight = acc.getInt(right);
if (sort_flags == SORT_REGULAR || sort_flags == SORT_NUMERIC) {
return ascending ? (iLeft < iRight) : (iLeft > iRight);
}
int isNegative;
char bufLeft[21];
char bufRight[21];
const char* sLeft;
const char* sRight;
int lenLeft;
int lenRight;
// Take advantage of precomputed StringDatas if they are available
const StringData* sdLeft = String::GetIntegerStringData(iLeft);
if (sdLeft) {
sLeft = sdLeft->data();
lenLeft = sdLeft->size();
} else {
bufLeft[20] = '\0';
sLeft = conv_10(iLeft, &isNegative, &bufLeft[20], &lenLeft);
}
const StringData* sdRight = String::GetIntegerStringData(iRight);
if (sdRight) {
sRight = sdRight->data();
lenRight = sdRight->size();
} else {
bufRight[20] = '\0';
sRight = conv_10(iRight, &isNegative, &bufRight[20], &lenRight);
}
if (sort_flags == SORT_STRING) {
return ascending ?
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
}
if (sort_flags == SORT_LOCALE_STRING) {
return ascending ? (strcoll(sLeft, sRight) < 0) :
(strcoll(sLeft, sRight) > 0);
}
if (sort_flags == SORT_NATURAL) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
}
if (sort_flags == SORT_NATURAL_CASE) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
}
assert(false);
return true;
}
};
template <typename AccessorT, int sort_flags, bool ascending>
struct StrElmCompare {
typedef typename AccessorT::ElmT ElmT;
AccessorT acc;
bool operator()(ElmT left, ElmT right) const {
StringData* sdLeft = acc.getStr(left);
StringData* sdRight = acc.getStr(right);
if (sort_flags == SORT_REGULAR) {
return ascending ? (sdLeft->compare(sdRight) < 0) :
(sdLeft->compare(sdRight) > 0);
}
if (sort_flags == SORT_NUMERIC) {
double dLeft = sdLeft->toDouble();
double dRight = sdRight->toDouble();
return ascending ? (dLeft < dRight) : (dLeft > dRight);
}
const char* sLeft = sdLeft->data();
int lenLeft = sdLeft->size();
const char* sRight = sdRight->data();
int lenRight = sdRight->size();
if (sort_flags == SORT_STRING) {
return ascending ?
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
}
if (sort_flags == SORT_LOCALE_STRING) {
return ascending ? (strcoll(sLeft, sRight) < 0) :
(strcoll(sLeft, sRight) > 0);
}
if (sort_flags == SORT_NATURAL) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
}
if (sort_flags == SORT_NATURAL_CASE) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
}
assert(false);
return true;
}
};
template <typename AccessorT, int sort_flags, bool ascending>
struct ElmCompare {
typedef typename AccessorT::ElmT ElmT;
AccessorT acc;
bool operator()(ElmT left, ElmT right) const {
// Fast paths
if (sort_flags == SORT_REGULAR) {
if (acc.isStr(left)) {
if (LIKELY(acc.isStr(right))) {
StringData* sLeft = acc.getStr(left);
StringData* sRight = acc.getStr(right);
return ascending ? (sLeft->compare(sRight) < 0) :
(sLeft->compare(sRight) > 0);
}
} else if (acc.isInt(left)) {
if (LIKELY(acc.isInt(right))) {
int64_t iLeft = acc.getInt(left);
int64_t iRight = acc.getInt(right);
return ascending ? (iLeft < iRight) : (iLeft > iRight);
}
}
}
if (sort_flags == SORT_NUMERIC) {
if (acc.isInt(left)) {
if (LIKELY(acc.isInt(right))) {
int64_t iLeft = acc.getInt(left);
int64_t iRight = acc.getInt(right);
return ascending ? (iLeft < iRight) : (iLeft > iRight);
}
}
}
if (sort_flags == SORT_STRING || sort_flags == SORT_LOCALE_STRING ||
sort_flags == SORT_NATURAL || sort_flags == SORT_NATURAL_CASE) {
if (acc.isStr(left)) {
if (LIKELY(acc.isStr(right))) {
StringData* sdLeft = acc.getStr(left);
StringData* sdRight = acc.getStr(right);
const char* sLeft = sdLeft->data();
int lenLeft = sdLeft->size();
const char* sRight = sdRight->data();
int lenRight = sdRight->size();
if (sort_flags == SORT_STRING) {
return ascending ?
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
}
if (sort_flags == SORT_LOCALE_STRING) {
return ascending ? (strcoll(sLeft, sRight) < 0) :
(strcoll(sLeft, sRight) > 0);
}
if (sort_flags == SORT_NATURAL) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
}
if (sort_flags == SORT_NATURAL_CASE) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
}
}
}
}
// Slow paths
Variant vLeft = acc.getValue(left);
Variant vRight = acc.getValue(right);
if (sort_flags == SORT_REGULAR) {
return ascending ? HPHP::less(vLeft, vRight) : HPHP::more(vLeft, vRight);
}
if (sort_flags == SORT_NUMERIC) {
double dLeft = vLeft.toDouble();
double dRight = vRight.toDouble();
return ascending ? dLeft < dRight : dLeft > dRight;
}
String strLeft = vLeft.toString();
String strRight = vRight.toString();
const char* sLeft = strLeft.data();
int lenLeft = strLeft.size();
const char* sRight = strRight.data();
int lenRight = strRight.size();
if (sort_flags == SORT_STRING) {
return ascending ?
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
}
if (sort_flags == SORT_LOCALE_STRING) {
return ascending ?
(strcoll(sLeft, sRight) < 0) :
(strcoll(sLeft, sRight) > 0);
}
if (sort_flags == SORT_NATURAL) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
}
if (sort_flags == SORT_NATURAL_CASE) {
return ascending ?
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
}
assert(false);
return true;
}
};
template <typename AccessorT>
struct ElmUCompare {
typedef typename AccessorT::ElmT ElmT;
AccessorT acc;
const CallCtx* ctx;
bool operator()(ElmT left, ElmT right) const {
Variant ret;
g_vmContext->invokeFunc(ret.asTypedValue(), *ctx,
CREATE_VECTOR2(acc.getValue(left),
acc.getValue(right)));
if (ret.isInteger()) {
return ret.toInt64() < 0;
}
if (ret.isDouble()) {
return ret.toDouble() < 0.0;
}
if (ret.isString()) {
int64_t lval; double dval;
switch (ret.getStringData()->isNumericWithVal(lval, dval, 0)) {
case KindOfInt64: return lval < 0;
case KindOfDouble: return dval < 0;
default: /* fall through */ break;
}
}
// Task #1839416: Raise a warning indicating that the comparator
// returned something other than an integer, double, or numeric
// string
if (ret.isBoolean()) {
// Match the behavior of Zend PHP for comparators that return
// boolean values
bool b = ret.toBoolean();
if (b) {
return false;
}
Variant ret2;
g_vmContext->invokeFunc(ret.asTypedValue(), *ctx,
CREATE_VECTOR2(acc.getValue(right),
acc.getValue(left)));
if (ret2.isBoolean()) {
return ret2.toBoolean();
}
// We have a wild comparator that returns boolean and non-boolean
// values; give up and fall through to the logic below
}
return ret.toInt64() < 0;
}
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // incl_HPHP_SORT_HELPERS_H_