2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/CPortability.h>
23 #include <folly/ScopeGuard.h>
24 #include <folly/tracing/StaticTracepoint.h>
26 #include "hphp/runtime/base/array-data-defs.h"
27 #include "hphp/runtime/base/double-to-int64.h"
28 #include "hphp/runtime/base/runtime-error.h"
29 #include "hphp/runtime/base/tv-conversions.h"
30 #include "hphp/runtime/base/tv-refcount.h"
31 #include "hphp/runtime/ext/std/ext_std_math.h"
32 #include "hphp/runtime/vm/class-meth-data-ref.h"
36 //////////////////////////////////////////////////////////////////////
40 [[noreturn
]] NEVER_INLINE
41 void throw_bad_array_operand(const ArrayData
* ad
) {
42 const char* type
= [&]{
43 if (ad
->isVecType()) return "vecs";
44 if (ad
->isDictType()) return "dicts";
45 if (ad
->isKeysetType()) return "keysets";
48 SystemLib::throwInvalidOperationExceptionObject(
50 "Invalid operand type was used: "
51 "cannot perform this operation with {}", type
57 * unsigned to signed conversion when the unsigned value is out of
58 * range is implementation defined behavior. We can work around it
61 * return v > std::numeric_limits<int64_t>::max() ?
62 * -int64_t(~v) - 1 : int64_t(v);
64 * which gcc appears to optimize to a no-op. But I'd rather avoid that
65 * until it becomes a problem.
67 * Putting this here as a placeholder.
69 inline int64_t u2s(uint64_t v
) {
73 inline int64_t add_ignore_overflow(int64_t a
, int64_t b
) {
74 return u2s(static_cast<uint64_t>(a
) + b
);
77 inline int64_t sub_ignore_overflow(int64_t a
, int64_t b
) {
78 return u2s(static_cast<uint64_t>(a
) - b
);
81 inline int64_t mul_ignore_overflow(int64_t a
, int64_t b
) {
82 return u2s(static_cast<uint64_t>(a
) * b
);
85 inline int64_t shl_ignore_overflow(int64_t a
, int64_t b
) {
86 return u2s(static_cast<uint64_t>(a
) << (b
& 63));
89 TypedValue
make_int(int64_t n
) { return make_tv
<KindOfInt64
>(n
); }
90 TypedValue
make_dbl(double d
) { return make_tv
<KindOfDouble
>(d
); }
92 bool is_numeric(TypedValue tv
) { return tvIsInt(tv
) || tvIsDouble(tv
); }
94 inline void check_numeric(const TypedValue
& c1
, const TypedValue
& c2
) {
95 if (UNLIKELY(!is_numeric(c1
) || !is_numeric(c2
))) {
96 throwMathBadTypesException(&c1
, &c2
);
101 TypedValue
tvArith(Op o
, TypedValue c1
, TypedValue c2
) {
102 check_numeric(c1
, c2
);
105 ? o(c1
.m_data
.num
, c2
.m_data
.num
)
106 : o(c1
.m_data
.num
, c2
.m_data
.dbl
);
109 ? o(c1
.m_data
.dbl
, c2
.m_data
.num
)
110 : o(c1
.m_data
.dbl
, c2
.m_data
.dbl
);
115 * 1. A functor class for doing the standard mathematical operation
116 * 2. A fn ptr for doing the mathematical operation ignoring int overflow issues
117 * 3. true if the result should be wrapped in a typedvalue or left as a double
119 template <typename Op
, int64_t (*ovrflw
)(int64_t a
, int64_t b
), bool tv_ret
>
121 template<class T
, class U
> typename
std::enable_if_t
<
122 std::is_floating_point_v
<T
> || std::is_floating_point_v
<U
>,
123 std::conditional_t
<tv_ret
, TypedValue
, double>
124 > operator()(T a
, U b
) const {
125 if constexpr (tv_ret
) return make_dbl(Op()(a
, b
));
126 else return Op()(a
, b
);
129 std::conditional_t
<tv_ret
, TypedValue
, int64_t>
130 operator()(int64_t a
, int64_t b
) const {
131 if constexpr (tv_ret
) return make_int(ovrflw(a
, b
));
132 else return ovrflw(a
, b
);
136 using Add
= ArithBase
<std::plus
<>, add_ignore_overflow
, true>;
137 using Sub
= ArithBase
<std::minus
<>, sub_ignore_overflow
, true>;
138 using Mul
= ArithBase
<std::multiplies
<>, mul_ignore_overflow
, true>;
140 using AddEq
= ArithBase
<std::plus
<>, add_ignore_overflow
, false>;
141 using SubEq
= ArithBase
<std::minus
<>, sub_ignore_overflow
, false>;
142 int64_t mul(int64_t a
, int64_t b
) { return a
* b
; }
143 using MulEq
= ArithBase
<std::multiplies
<>, mul
, false>;
146 TypedValue
operator()(int64_t t
, int64_t u
) const {
147 if (UNLIKELY(u
== 0)) {
148 SystemLib::throwDivisionByZeroExceptionObject();
151 // Avoid SIGFPE when dividing the miniumum respresentable integer
153 auto const minInt
= std::numeric_limits
<int64_t>::min();
154 if (UNLIKELY(u
== -1 && t
== minInt
)) {
155 return make_dbl(static_cast<double>(minInt
) / -1);
158 return (t
% u
== 0) ? make_int(t
/ u
) : make_dbl(double(t
) / u
);
161 template<class T
, class U
>
162 typename
std::enable_if
<
163 std::is_floating_point
<T
>::value
|| std::is_floating_point
<U
>::value
,
165 >::type
operator()(T t
, U u
) const {
166 if (UNLIKELY(u
== 0)) {
167 SystemLib::throwDivisionByZeroExceptionObject();
169 return make_dbl(t
/ u
);
174 void tvOpEq(Op op
, tv_lval c1
, TypedValue c2
) {
175 check_numeric(*c1
, c2
);
177 if (tvIsDouble(c1
)) {
178 val(c1
).dbl
= op(val(c1
).dbl
, tvIsInt(c2
) ? c2
.m_data
.num
: c2
.m_data
.dbl
);
182 assertx(tvIsInt(c1
));
184 val(c1
).num
= op(val(c1
).num
, c2
.m_data
.num
);
186 type(c1
) = KindOfDouble
;
187 val(c1
).dbl
= op(val(c1
).num
, c2
.m_data
.dbl
);
191 template<class SzOp
, class BitOp
>
192 StringData
* stringBitOp(BitOp bop
, SzOp sop
, StringData
* s1
, StringData
* s2
) {
193 auto const s1Size
= s1
->size();
194 auto const s2Size
= s2
->size();
195 auto const newLen
= sop(s1Size
, s2Size
);
196 auto const newStr
= StringData::Make(newLen
);
197 auto const s1Data
= s1
->data();
198 auto const s2Data
= s2
->data();
199 auto const outData
= newStr
->mutableData();
201 for (uint32_t i
= 0; i
< newLen
; ++i
) {
202 outData
[i
] = bop((i
< s1Size
) ? s1Data
[i
] : 0,
203 (i
< s2Size
) ? s2Data
[i
] : 0);
205 newStr
->setSize(newLen
);
210 template<template<class> class BitOp
, class StrLenOp
>
211 TypedValue
tvBitOp(StrLenOp strLenOp
, TypedValue c1
, TypedValue c2
) {
212 assertx(tvIsPlausible(c1
));
213 assertx(tvIsPlausible(c2
));
214 if (isStringType(c1
.m_type
) && isStringType(c2
.m_type
)) {
215 return make_tv
<KindOfString
>(
225 if (!tvIsInt(c1
) || !tvIsInt(c2
)) throwBitOpBadTypesException(&c1
, &c2
);
226 return make_int(BitOp
<int64_t>()(c1
.m_data
.num
, c2
.m_data
.num
));
230 void tvBitOpEq(Op op
, tv_lval c1
, TypedValue c2
) {
231 auto const result
= op(*c1
, c2
);
232 auto const old
= *c1
;
237 // Op must implement the interface described for cellIncDecOp.
239 void stringIncDecOp(Op op
, tv_lval cell
, StringData
* sd
) {
240 assertx(isStringType(type(cell
)) || isFuncType(type(cell
)) ||
241 isClassType(type(cell
)));
243 if (sd
->empty()) throwIncDecBadTypeException("empty string");
244 if (sd
->isNumeric()) throwIncDecBadTypeException("numeric string");
245 op
.nonNumericString(cell
);
248 void raiseIncDecInvalidType(tv_lval cell
) {
249 switch (RuntimeOption::EvalWarnOnIncDecInvalidType
) {
253 raise_warning("Unsupported operand type (%s) for IncDec",
254 describe_actual_type(cell
).c_str());
257 raise_error("Unsupported operand type (%s) for IncDec",
258 describe_actual_type(cell
).c_str());
261 always_assert(false);
266 * Inc or Dec for a string, depending on Op. Op must implement
268 * - a function call operator for numeric types
269 * - a nullCase(TypedValue&) function that returns the result for null types
270 * - an emptyString() function that performs the operation for empty strings
271 * - and a nonNumericString(TypedValue&) function used for non-numeric strings
273 * PHP's Inc and Dec behave differently in all these cases, so this
274 * abstracts out the common parts from those differences.
277 void tvIncDecOp(Op op
, tv_lval cell
) {
278 assertx(tvIsPlausible(*cell
));
280 auto const source
= "increment op";
281 switch (type(cell
)) {
284 raiseIncDecInvalidType(cell
);
297 raiseIncDecInvalidType(cell
);
298 invalidFuncConversion("int");
302 raiseIncDecInvalidType(cell
);
303 auto s
= classToStringHelper(val(cell
).pclass
, source
);
304 stringIncDecOp(op
, cell
, const_cast<StringData
*>(s
));
308 case KindOfLazyClass
: {
309 raiseIncDecInvalidType(cell
);
310 auto s
= lazyClassToStringHelper(val(cell
).plazyclass
, source
);
311 stringIncDecOp(op
, cell
, const_cast<StringData
*>(s
));
315 case KindOfPersistentString
:
317 raiseIncDecInvalidType(cell
);
318 stringIncDecOp(op
, cell
, val(cell
).pstr
);
322 case KindOfPersistentVec
:
324 case KindOfPersistentDict
:
326 case KindOfPersistentKeyset
:
333 case KindOfEnumClassLabel
:
334 raiseIncDecInvalidType(cell
);
340 const StaticString
s_1("1");
344 void intCase(tv_lval cell
) const {
345 auto& n
= val(cell
).num
;
346 n
= add_ignore_overflow(n
, 1);
348 void dblCase(tv_lval cell
) const { ++val(cell
).dbl
; }
349 void nullCase(tv_lval cell
) const {
350 throwIncDecBadTypeException("null");
354 void nonNumericString(tv_lval cell
) const {
355 auto const sd
= val(cell
).pstr
;
356 auto const newSd
= [&]() -> StringData
* {
357 auto const tmp
= StringData::Make(sd
, CopyString
);
358 auto const tmp2
= tmp
->increment();
360 assertx(tmp
->hasExactlyOneRef());
367 tvCopy(make_tv
<KindOfString
>(newSd
), cell
);
372 void intCase(tv_lval cell
) {
373 auto& n
= val(cell
).num
;
374 n
= sub_ignore_overflow(n
, 1);
376 void dblCase(tv_lval cell
) { --val(cell
).dbl
; }
377 void nullCase(tv_lval
) const {}
378 void nonNumericString(tv_lval cell
) const {
379 raise_notice("Decrement on string '%s'", val(cell
).pstr
->data());
385 //////////////////////////////////////////////////////////////////////
387 TypedValue
tvAdd(TypedValue c1
, TypedValue c2
) {
388 return tvArith(Add(), c1
, c2
);
391 TypedValue
tvSub(TypedValue c1
, TypedValue c2
) {
392 return tvArith(Sub(), c1
, c2
);
395 TypedValue
tvMul(TypedValue c1
, TypedValue c2
) {
396 return tvArith(Mul(), c1
, c2
);
399 TypedValue
tvDiv(TypedValue c1
, TypedValue c2
) {
400 return tvArith(Div(), c1
, c2
);
403 TypedValue
tvPow(TypedValue c1
, TypedValue c2
) {
404 return *HHVM_FN(pow
)(tvAsVariant(&c1
), tvAsVariant(&c2
)).asTypedValue();
407 TypedValue
tvMod(TypedValue c1
, TypedValue c2
) {
408 check_numeric(c1
, c2
);
409 auto to_int
= [](TypedValue tv
) {
410 return tvIsInt(tv
) ? tv
.m_data
.num
: double_to_int64(tv
.m_data
.dbl
);
412 auto const i2
= to_int(c2
);
413 if (UNLIKELY(i2
== 0)) SystemLib::throwDivisionByZeroExceptionObject();
414 // This is to avoid SIGFPE in the case of INT64_MIN % -1.
415 return make_int(UNLIKELY(i2
== -1) ? 0 : to_int(c1
) % i2
);
418 TypedValue
tvBitAnd(TypedValue c1
, TypedValue c2
) {
419 return tvBitOp
<std::bit_and
>(
420 [] (uint32_t a
, uint32_t b
) { return std::min(a
, b
); },
425 TypedValue
tvBitOr(TypedValue c1
, TypedValue c2
) {
426 return tvBitOp
<std::bit_or
>(
427 [] (uint32_t a
, uint32_t b
) { return std::max(a
, b
); },
432 TypedValue
tvBitXor(TypedValue c1
, TypedValue c2
) {
433 return tvBitOp
<std::bit_xor
>(
434 [] (uint32_t a
, uint32_t b
) { return std::min(a
, b
); },
439 StringData
* strBitXor(StringData
* s1
, StringData
* s2
) {
441 std::bit_xor
<char>(),
442 [] (uint32_t a
, uint32_t b
) { return std::min(a
, b
); },
447 TypedValue
tvShl(TypedValue c1
, TypedValue c2
) {
448 if (!tvIsInt(c1
) || !tvIsInt(c2
)) throwBitOpBadTypesException(&c1
, &c2
);
449 return make_int(shl_ignore_overflow(c1
.m_data
.num
, c2
.m_data
.num
));
452 TypedValue
tvShr(TypedValue c1
, TypedValue c2
) {
453 if (!tvIsInt(c1
) || !tvIsInt(c2
)) throwBitOpBadTypesException(&c1
, &c2
);
454 return make_int(c1
.m_data
.num
>> (c2
.m_data
.num
& 63));
457 void tvAddEq(tv_lval c1
, TypedValue c2
) {
458 tvOpEq(AddEq(), c1
, c2
);
461 void tvSubEq(tv_lval c1
, TypedValue c2
) {
462 tvOpEq(SubEq(), c1
, c2
);
465 void tvMulEq(tv_lval c1
, TypedValue c2
) {
466 tvOpEq(MulEq(), c1
, c2
);
469 void tvDivEq(tv_lval c1
, TypedValue c2
) {
470 assertx(tvIsPlausible(*c1
));
471 assertx(tvIsPlausible(c2
));
472 if (UNLIKELY(!is_numeric(*c1
))) throwMathBadTypesException(c1
, &c2
);
473 tvCopy(tvDiv(*c1
, c2
), c1
);
476 void tvPowEq(tv_lval c1
, TypedValue c2
) {
477 tvSet(tvPow(*c1
, c2
), c1
);
480 void tvModEq(tv_lval c1
, TypedValue c2
) {
481 tvSet(tvMod(*c1
, c2
), c1
);
484 void tvBitAndEq(tv_lval c1
, TypedValue c2
) {
485 tvBitOpEq(tvBitAnd
, c1
, c2
);
488 void tvBitOrEq(tv_lval c1
, TypedValue c2
) {
489 tvBitOpEq(tvBitOr
, c1
, c2
);
492 void tvBitXorEq(tv_lval c1
, TypedValue c2
) {
493 tvBitOpEq(tvBitXor
, c1
, c2
);
496 void tvShlEq(tv_lval c1
, TypedValue c2
) { tvSet(tvShl(*c1
, c2
), c1
); }
497 void tvShrEq(tv_lval c1
, TypedValue c2
) { tvSet(tvShr(*c1
, c2
), c1
); }
499 void tvInc(tv_lval cell
) { tvIncDecOp(Inc(), cell
); }
500 void tvDec(tv_lval cell
) { tvIncDecOp(Dec(), cell
); }
502 void tvBitNot(TypedValue
& cell
) {
503 assertx(tvIsPlausible(cell
));
505 switch (cell
.m_type
) {
507 cell
.m_data
.num
= ~cell
.m_data
.num
;
512 case KindOfLazyClass
:
514 auto const o
= "increment op";
516 isClassType(cell
.m_type
) ?
517 const_cast<StringData
*>(classToStringHelper(cell
.m_data
.pclass
, o
)) :
518 const_cast<StringData
*>(lazyClassToStringHelper(cell
.m_data
.plazyclass
,
521 cell
.m_type
= KindOfString
;
524 if (cell
.m_data
.pstr
->cowCheck()) {
526 case KindOfPersistentString
:
527 auto const sl
= cell
.m_data
.pstr
->slice();
528 FOLLY_SDT(hhvm
, hhvm_cow_bitnot
, sl
.size());
529 auto const newSd
= StringData::Make(sl
, CopyString
);
530 cell
.m_data
.pstr
->decRefCount(); // can't go to zero
531 cell
.m_data
.pstr
= newSd
;
532 cell
.m_type
= KindOfString
;
534 // Unless we go through this branch, the string was just freshly
535 // created, so the following mutation will be safe wrt its
536 // internal hash caching.
537 cell
.m_data
.pstr
->invalidateHash();
538 FOLLY_SDT(hhvm
, hhvm_mut_bitnot
, cell
.m_data
.pstr
->size());
542 auto const sd
= cell
.m_data
.pstr
;
543 auto const len
= sd
->size();
544 auto const data
= sd
->mutableData();
545 assertx(sd
->hasExactlyOneRef());
546 for (uint32_t i
= 0; i
< len
; ++i
) {
553 SystemLib::throwInvalidOperationExceptionObject(
554 "Cannot perform a bitwise not on float");
556 invalidFuncConversion("int");
560 case KindOfPersistentVec
:
562 case KindOfPersistentDict
:
564 case KindOfPersistentKeyset
:
571 case KindOfEnumClassLabel
:
572 raise_error("Unsupported operand type for ~");
576 //////////////////////////////////////////////////////////////////////