2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/tv-arith.h"
18 #include <type_traits>
22 #include "folly/ScopeGuard.h"
24 #include "hphp/runtime/base/strings.h"
25 #include "hphp/runtime/base/runtime-error.h"
26 #include "hphp/runtime/base/tv-conversions.h"
30 //////////////////////////////////////////////////////////////////////
34 // Helper for converting String, Array, Bool, Null or Obj to Dbl|Int.
35 // Other types (i.e. Int and Double) must be handled outside of this.
36 TypedNum
numericConvHelper(Cell cell
) {
37 assert(cellIsPlausible(cell
));
39 switch (cell
.m_type
) {
41 case KindOfStaticString
: return stringToNumeric(cell
.m_data
.pstr
);
42 case KindOfBoolean
: return make_tv
<KindOfInt64
>(cell
.m_data
.num
);
44 case KindOfNull
: return make_tv
<KindOfInt64
>(0);
45 case KindOfObject
: return make_tv
<KindOfInt64
>(
46 cell
.m_data
.pobj
->o_toInt64());
47 case KindOfResource
: return make_tv
<KindOfInt64
>(
48 cell
.m_data
.pres
->o_toInt64());
49 case KindOfArray
: throw BadArrayOperandException();
56 Cell
cellArith(Op o
, Cell c1
, Cell c2
) {
58 if (c1
.m_type
== KindOfInt64
) {
60 if (c2
.m_type
== KindOfInt64
) return o(c1
.m_data
.num
, c2
.m_data
.num
);
61 if (c2
.m_type
== KindOfDouble
) return o(c1
.m_data
.num
, c2
.m_data
.dbl
);
62 c2
= numericConvHelper(c2
);
63 assert(c2
.m_type
== KindOfInt64
|| c2
.m_type
== KindOfDouble
);
67 if (c1
.m_type
== KindOfDouble
) {
69 if (c2
.m_type
== KindOfDouble
) return o(c1
.m_data
.dbl
, c2
.m_data
.dbl
);
70 if (c2
.m_type
== KindOfInt64
) return o(c1
.m_data
.dbl
, c2
.m_data
.num
);
71 c2
= numericConvHelper(c2
);
72 assert(c2
.m_type
== KindOfInt64
|| c2
.m_type
== KindOfDouble
);
76 if (c1
.m_type
== KindOfArray
&& c2
.m_type
== KindOfArray
) {
77 return make_tv
<KindOfArray
>(o(c1
.m_data
.parr
, c2
.m_data
.parr
));
80 c1
= numericConvHelper(c1
);
81 assert(c1
.m_type
== KindOfInt64
|| c1
.m_type
== KindOfDouble
);
85 Cell
num(int64_t n
) { return make_tv
<KindOfInt64
>(n
); }
86 Cell
dbl(double d
) { return make_tv
<KindOfDouble
>(d
); }
89 Cell
operator()(double a
, int64_t b
) const { return dbl(a
+ b
); }
90 Cell
operator()(double a
, double b
) const { return dbl(a
+ b
); }
91 Cell
operator()(int64_t a
, double b
) const { return dbl(a
+ b
); }
92 Cell
operator()(int64_t a
, int64_t b
) const { return num(a
+ b
); }
94 ArrayData
* operator()(ArrayData
* a1
, ArrayData
* a2
) const {
95 a1
->incRefCount(); // force COW
96 SCOPE_EXIT
{ a1
->decRefCount(); };
97 return a1
->plusEq(a2
);
102 Cell
operator()(double a
, int64_t b
) const { return dbl(a
- b
); }
103 Cell
operator()(double a
, double b
) const { return dbl(a
- b
); }
104 Cell
operator()(int64_t a
, double b
) const { return dbl(a
- b
); }
105 Cell
operator()(int64_t a
, int64_t b
) const { return num(a
- b
); }
107 ArrayData
* operator()(ArrayData
* a1
, ArrayData
* a2
) const {
108 throw BadArrayOperandException();
113 Cell
operator()(double a
, int64_t b
) const { return dbl(a
* b
); }
114 Cell
operator()(double a
, double b
) const { return dbl(a
* b
); }
115 Cell
operator()(int64_t a
, double b
) const { return dbl(a
* b
); }
116 Cell
operator()(int64_t a
, int64_t b
) const { return num(a
* b
); }
118 ArrayData
* operator()(ArrayData
* a1
, ArrayData
* a2
) const {
119 throw BadArrayOperandException();
124 Cell
operator()(int64_t t
, int64_t u
) const {
125 if (UNLIKELY(u
== 0)) {
126 raise_warning(Strings::DIVISION_BY_ZERO
);
127 return make_tv
<KindOfBoolean
>(false);
130 // Avoid SIGFPE when dividing the miniumum respresentable integer
132 auto const minInt
= std::numeric_limits
<int64_t>::min();
133 if (UNLIKELY(u
== -1 && t
== minInt
)) {
134 return make_tv
<KindOfDouble
>(static_cast<double>(minInt
) / -1);
137 if (t
% u
== 0) return make_tv
<KindOfInt64
>(t
/ u
);
138 return make_tv
<KindOfDouble
>(static_cast<double>(t
) / u
);
141 template<class T
, class U
>
142 typename
std::enable_if
<
143 std::is_floating_point
<T
>::value
|| std::is_floating_point
<U
>::value
,
145 >::type
operator()(T t
, U u
) const {
147 !(std::is_integral
<T
>::value
&& std::is_integral
<U
>::value
), ""
149 if (UNLIKELY(u
== 0)) {
150 raise_warning(Strings::DIVISION_BY_ZERO
);
151 return make_tv
<KindOfBoolean
>(false);
153 return make_tv
<KindOfDouble
>(t
/ u
);
156 ArrayData
* operator()(ArrayData
* a1
, ArrayData
* a2
) const {
157 throw BadArrayOperandException();
162 void cellOpEq(Op op
, Cell
& c1
, Cell c2
) {
164 if (c1
.m_type
== KindOfInt64
) {
166 if (c2
.m_type
== KindOfInt64
) {
167 c1
.m_data
.num
= op(c1
.m_data
.num
, c2
.m_data
.num
);
170 if (c2
.m_type
== KindOfDouble
) {
171 c1
.m_type
= KindOfDouble
;
172 c1
.m_data
.dbl
= op(c1
.m_data
.num
, c2
.m_data
.dbl
);
175 c2
= numericConvHelper(c2
);
176 assert(c2
.m_type
== KindOfInt64
|| c2
.m_type
== KindOfDouble
);
180 if (c1
.m_type
== KindOfDouble
) {
182 if (c2
.m_type
== KindOfInt64
) {
183 c1
.m_data
.dbl
= op(c1
.m_data
.dbl
, c2
.m_data
.num
);
186 if (c2
.m_type
== KindOfDouble
) {
187 c1
.m_data
.dbl
= op(c1
.m_data
.dbl
, c2
.m_data
.dbl
);
190 c2
= numericConvHelper(c2
);
191 assert(c2
.m_type
== KindOfInt64
|| c2
.m_type
== KindOfDouble
);
195 if (c1
.m_type
== KindOfArray
&& c2
.m_type
== KindOfArray
) {
196 auto const ad1
= c1
.m_data
.parr
;
197 auto const newArr
= op(ad1
, c2
.m_data
.parr
);
199 c1
.m_data
.parr
= newArr
;
205 cellSet(numericConvHelper(c1
), c1
);
206 assert(c1
.m_type
== KindOfInt64
|| c1
.m_type
== KindOfDouble
);
211 int64_t operator()(int64_t a
, int64_t b
) const { return a
+ b
; }
212 double operator()(double a
, int64_t b
) const { return a
+ b
; }
213 double operator()(int64_t a
, double b
) const { return a
+ b
; }
214 double operator()(double a
, double b
) const { return a
+ b
; }
216 ArrayData
* operator()(ArrayData
* ad1
, ArrayData
* ad2
) const {
217 if (ad2
->empty() || ad1
== ad2
) return ad1
;
222 return ad1
->plusEq(ad2
);
227 int64_t operator()(int64_t a
, int64_t b
) const { return a
- b
; }
228 double operator()(double a
, int64_t b
) const { return a
- b
; }
229 double operator()(int64_t a
, double b
) const { return a
- b
; }
230 double operator()(double a
, double b
) const { return a
- b
; }
232 ArrayData
* operator()(ArrayData
* ad1
, ArrayData
* ad2
) const {
233 throw BadArrayOperandException();
238 int64_t operator()(int64_t a
, int64_t b
) const { return a
* b
; }
239 double operator()(double a
, int64_t b
) const { return a
* b
; }
240 double operator()(int64_t a
, double b
) const { return a
* b
; }
241 double operator()(double a
, double b
) const { return a
* b
; }
243 ArrayData
* operator()(ArrayData
* ad1
, ArrayData
* ad2
) const {
244 throw BadArrayOperandException();
248 template<class SzOp
, class BitOp
>
249 StringData
* stringBitOp(BitOp bop
, SzOp sop
, StringData
* s1
, StringData
* s2
) {
250 auto const s1Size
= s1
->size();
251 auto const s2Size
= s2
->size();
252 auto const newLen
= sop(s1Size
, s2Size
);
253 auto const newStr
= StringData::Make(newLen
);
254 auto const s1Data
= s1
->data();
255 auto const s2Data
= s2
->data();
256 auto const outData
= newStr
->mutableData();
258 for (uint32_t i
= 0; i
< newLen
; ++i
) {
259 outData
[i
] = bop((i
< s1Size
) ? s1Data
[i
] : 0,
260 (i
< s2Size
) ? s2Data
[i
] : 0);
262 newStr
->setSize(newLen
);
264 newStr
->setRefCount(1);
268 template<template<class> class BitOp
, class StrLenOp
>
269 Cell
cellBitOp(StrLenOp strLenOp
, Cell c1
, Cell c2
) {
270 assert(cellIsPlausible(c1
));
271 assert(cellIsPlausible(c2
));
273 if (IS_STRING_TYPE(c1
.m_type
) && IS_STRING_TYPE(c2
.m_type
)) {
274 return make_tv
<KindOfString
>(
284 return make_tv
<KindOfInt64
>(
285 BitOp
<int64_t>()(cellToInt(c1
), cellToInt(c2
))
290 void cellBitOpEq(Op op
, Cell
& c1
, Cell c2
) {
291 auto const result
= op(c1
, c2
);
295 // Op must implement the interface described for cellIncDecOp.
297 void stringIncDecOp(Op op
, Cell
& cell
) {
298 assert(IS_STRING_TYPE(cell
.m_type
));
300 auto const sd
= cell
.m_data
.pstr
;
303 cellCopy(op
.emptyString(), cell
);
309 auto const dt
= sd
->isNumericWithVal(ival
, dval
, true /* allow_errors */);
314 cellCopy(num(ival
), cell
);
319 cellCopy(dbl(dval
), cell
);
322 assert(dt
== KindOfNull
);
323 op
.nonNumericString(cell
);
329 * Inc or Dec for a string, depending on Op. Op must implement
331 * - a function call operator for numeric types
332 * - a nullCase(Cell&) function that returns the result for null types
333 * - an emptyString() function that performs the operation for empty strings
334 * - and a nonNumericString(Cell&) function used for non-numeric strings
336 * PHP's Inc and Dec behave differently in all these cases, so this
337 * abstracts out the common parts from those differences.
340 void cellIncDecOp(Op op
, Cell
& cell
) {
341 assert(cellIsPlausible(cell
));
343 switch (cell
.m_type
) {
352 case KindOfStaticString
:
353 stringIncDecOp(op
, cell
);
371 const StaticString
s_1("1");
374 template<class T
> void operator()(T
& t
) const { ++t
; }
375 void nullCase(Cell
& cell
) const { cellCopy(num(1), cell
); }
377 Cell
emptyString() const {
378 return make_tv
<KindOfStaticString
>(s_1
.get());
381 void nonNumericString(Cell
& cell
) const {
382 auto const sd
= cell
.m_data
.pstr
;
383 auto const newSd
= [&]() -> StringData
* {
384 auto const tmp
= StringData::Make(sd
, CopyString
);
385 auto const tmp2
= tmp
->increment();
387 assert(tmp
->getCount() == 0);
393 newSd
->incRefCount();
395 cellCopy(make_tv
<KindOfString
>(newSd
), cell
);
400 template<class T
> void operator()(T
& t
) const { --t
; }
401 void nullCase(Cell
&) const {}
402 Cell
emptyString() const { return num(-1); }
403 void nonNumericString(Cell
&) const {}
408 //////////////////////////////////////////////////////////////////////
410 Cell
cellAdd(Cell c1
, Cell c2
) {
411 return cellArith(Add(), c1
, c2
);
414 TypedNum
cellSub(Cell c1
, Cell c2
) {
415 return cellArith(Sub(), c1
, c2
);
418 TypedNum
cellMul(Cell c1
, Cell c2
) {
419 return cellArith(Mul(), c1
, c2
);
422 Cell
cellDiv(Cell c1
, Cell c2
) {
423 return cellArith(Div(), c1
, c2
);
426 Cell
cellMod(Cell c1
, Cell c2
) {
427 auto const i1
= cellToInt(c1
);
428 auto const i2
= cellToInt(c2
);
429 if (UNLIKELY(i2
== 0)) {
430 raise_warning(Strings::DIVISION_BY_ZERO
);
431 return make_tv
<KindOfBoolean
>(false);
434 // This is to avoid SIGFPE in the case of INT64_MIN % -1.
435 if (i2
== -1) return make_tv
<KindOfInt64
>(0);
437 return make_tv
<KindOfInt64
>(i1
% i2
);
440 Cell
cellBitAnd(Cell c1
, Cell c2
) {
441 return cellBitOp
<std::bit_and
>(
442 [] (uint32_t a
, uint32_t b
) { return std::min(a
, b
); },
447 Cell
cellBitOr(Cell c1
, Cell c2
) {
448 return cellBitOp
<std::bit_or
>(
449 [] (uint32_t a
, uint32_t b
) { return std::max(a
, b
); },
454 Cell
cellBitXor(Cell c1
, Cell c2
) {
455 return cellBitOp
<std::bit_xor
>(
456 [] (uint32_t a
, uint32_t b
) { return std::min(a
, b
); },
461 void cellAddEq(Cell
& c1
, Cell c2
) {
462 cellOpEq(AddEq(), c1
, c2
);
465 void cellSubEq(Cell
& c1
, Cell c2
) {
466 cellOpEq(SubEq(), c1
, c2
);
469 void cellMulEq(Cell
& c1
, Cell c2
) {
470 cellOpEq(MulEq(), c1
, c2
);
473 void cellDivEq(Cell
& c1
, Cell c2
) {
474 assert(cellIsPlausible(c1
));
475 assert(cellIsPlausible(c2
));
476 if (!isTypedNum(c1
)) {
477 cellSet(numericConvHelper(c1
), c1
);
479 cellCopy(cellDiv(c1
, c2
), c1
);
482 void cellModEq(Cell
& c1
, Cell c2
) {
483 cellCopy(cellMod(c1
, c2
), c1
);
486 void cellBitAndEq(Cell
& c1
, Cell c2
) {
487 cellBitOpEq(cellBitAnd
, c1
, c2
);
490 void cellBitOrEq(Cell
& c1
, Cell c2
) {
491 cellBitOpEq(cellBitOr
, c1
, c2
);
494 void cellBitXorEq(Cell
& c1
, Cell c2
) {
495 cellBitOpEq(cellBitXor
, c1
, c2
);
498 void cellInc(Cell
& cell
) {
499 cellIncDecOp(Inc(), cell
);
502 void cellDec(Cell
& cell
) {
503 cellIncDecOp(Dec(), cell
);
506 void cellBitNot(Cell
& cell
) {
507 assert(cellIsPlausible(cell
));
509 switch (cell
.m_type
) {
511 cell
.m_data
.num
= ~cell
.m_data
.num
;
514 cell
.m_type
= KindOfInt64
;
515 cell
.m_data
.num
= ~toInt64(cell
.m_data
.dbl
);
519 if (cell
.m_data
.pstr
->hasMultipleRefs()) {
520 case KindOfStaticString
:
521 auto const newSd
= StringData::Make(
522 cell
.m_data
.pstr
->slice(),
525 newSd
->incRefCount();
526 cell
.m_data
.pstr
->decRefCount(); // can't go to zero
527 cell
.m_data
.pstr
= newSd
;
528 cell
.m_type
= KindOfString
;
530 // Unless we go through this branch, the string was just freshly
531 // created, so the following mutation will be safe wrt its
532 // internal hash caching.
533 cell
.m_data
.pstr
->invalidateHash();
537 auto const sd
= cell
.m_data
.pstr
;
538 auto const len
= sd
->size();
539 auto const data
= sd
->mutableData();
540 assert(sd
->getCount() == 1);
541 for (uint32_t i
= 0; i
< len
; ++i
) {
548 raise_error("Unsupported operand type for ~");
552 //////////////////////////////////////////////////////////////////////