Add some explanations to parser options
[hiphop-php.git] / hphp / runtime / base / tv-arith.cpp
blob2e8530f218d024b18a499544ad8a4b7d12a0db9a
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
19 #include <limits>
20 #include <algorithm>
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"
34 namespace HPHP {
36 //////////////////////////////////////////////////////////////////////
38 namespace {
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";
46 always_assert(false);
47 }();
48 SystemLib::throwInvalidOperationExceptionObject(
49 folly::sformat(
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
59 * with something like
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) {
70 return 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);
100 template<class Op>
101 TypedValue tvArith(Op o, TypedValue c1, TypedValue c2) {
102 check_numeric(c1, c2);
103 if (tvIsInt(c1)) {
104 return tvIsInt(c2)
105 ? o(c1.m_data.num, c2.m_data.num)
106 : o(c1.m_data.num, c2.m_data.dbl);
108 return tvIsInt(c2)
109 ? o(c1.m_data.dbl, c2.m_data.num)
110 : o(c1.m_data.dbl, c2.m_data.dbl);
114 * Template params:
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>
120 struct ArithBase {
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>;
145 struct Div {
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
152 // by -1.
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,
164 TypedValue
165 >::type operator()(T t, U u) const {
166 if (UNLIKELY(u == 0)) {
167 SystemLib::throwDivisionByZeroExceptionObject();
169 return make_dbl(t / u);
173 template<class Op>
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);
179 return;
182 assertx(tvIsInt(c1));
183 if (tvIsInt(c2)) {
184 val(c1).num = op(val(c1).num, c2.m_data.num);
185 } else {
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);
207 return newStr;
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>(
216 stringBitOp(
217 BitOp<char>(),
218 strLenOp,
219 c1.m_data.pstr,
220 c2.m_data.pstr
225 if (!tvIsInt(c1) || !tvIsInt(c2)) throwBitOpBadTypesException(&c1, &c2);
226 return make_int(BitOp<int64_t>()(c1.m_data.num, c2.m_data.num));
229 template<class Op>
230 void tvBitOpEq(Op op, tv_lval c1, TypedValue c2) {
231 auto const result = op(*c1, c2);
232 auto const old = *c1;
233 tvCopy(result, c1);
234 tvDecRefGen(old);
237 // Op must implement the interface described for cellIncDecOp.
238 template<class Op>
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) {
250 case 0:
251 break;
252 case 1:
253 raise_warning("Unsupported operand type (%s) for IncDec",
254 describe_actual_type(cell).c_str());
255 break;
256 case 2:
257 raise_error("Unsupported operand type (%s) for IncDec",
258 describe_actual_type(cell).c_str());
259 [[fallthrough]];
260 default:
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.
276 template<class Op>
277 void tvIncDecOp(Op op, tv_lval cell) {
278 assertx(tvIsPlausible(*cell));
280 auto const source = "increment op";
281 switch (type(cell)) {
282 case KindOfUninit:
283 case KindOfNull:
284 raiseIncDecInvalidType(cell);
285 op.nullCase(cell);
286 return;
288 case KindOfInt64:
289 op.intCase(cell);
290 return;
292 case KindOfDouble:
293 op.dblCase(cell);
294 return;
296 case KindOfFunc: {
297 raiseIncDecInvalidType(cell);
298 invalidFuncConversion("int");
301 case KindOfClass: {
302 raiseIncDecInvalidType(cell);
303 auto s = classToStringHelper(val(cell).pclass, source);
304 stringIncDecOp(op, cell, const_cast<StringData*>(s));
305 return;
308 case KindOfLazyClass: {
309 raiseIncDecInvalidType(cell);
310 auto s = lazyClassToStringHelper(val(cell).plazyclass, source);
311 stringIncDecOp(op, cell, const_cast<StringData*>(s));
312 return;
315 case KindOfPersistentString:
316 case KindOfString:
317 raiseIncDecInvalidType(cell);
318 stringIncDecOp(op, cell, val(cell).pstr);
319 return;
321 case KindOfBoolean:
322 case KindOfPersistentVec:
323 case KindOfVec:
324 case KindOfPersistentDict:
325 case KindOfDict:
326 case KindOfPersistentKeyset:
327 case KindOfKeyset:
328 case KindOfObject:
329 case KindOfResource:
330 case KindOfClsMeth:
331 case KindOfRClsMeth:
332 case KindOfRFunc:
333 case KindOfEnumClassLabel:
334 raiseIncDecInvalidType(cell);
335 return;
337 not_reached();
340 const StaticString s_1("1");
343 struct Inc {
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");
351 not_reached();
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();
359 if (tmp2 != tmp) {
360 assertx(tmp->hasExactlyOneRef());
361 tmp->release();
362 return tmp2;
364 return tmp;
365 }();
366 decRefStr(sd);
367 tvCopy(make_tv<KindOfString>(newSd), cell);
371 struct Dec {
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); },
421 c1, c2
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); },
428 c1, c2
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); },
435 c1, c2
439 StringData* strBitXor(StringData* s1, StringData* s2) {
440 return stringBitOp(
441 std::bit_xor<char>(),
442 [] (uint32_t a, uint32_t b) { return std::min(a, b); },
443 s1, s2
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) {
506 case KindOfInt64:
507 cell.m_data.num = ~cell.m_data.num;
508 break;
510 case KindOfClass:
511 [[fallthrough]];
512 case KindOfLazyClass:
514 auto const o = "increment op";
515 cell.m_data.pstr =
516 isClassType(cell.m_type) ?
517 const_cast<StringData*>(classToStringHelper(cell.m_data.pclass, o)) :
518 const_cast<StringData*>(lazyClassToStringHelper(cell.m_data.plazyclass,
519 o));
521 cell.m_type = KindOfString;
522 [[fallthrough]];
523 case KindOfString:
524 if (cell.m_data.pstr->cowCheck()) {
525 [[fallthrough]];
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;
533 } else {
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) {
547 data[i] = ~data[i];
550 break;
552 case KindOfDouble:
553 SystemLib::throwInvalidOperationExceptionObject(
554 "Cannot perform a bitwise not on float");
555 case KindOfFunc:
556 invalidFuncConversion("int");
557 case KindOfUninit:
558 case KindOfNull:
559 case KindOfBoolean:
560 case KindOfPersistentVec:
561 case KindOfVec:
562 case KindOfPersistentDict:
563 case KindOfDict:
564 case KindOfPersistentKeyset:
565 case KindOfKeyset:
566 case KindOfObject:
567 case KindOfResource:
568 case KindOfClsMeth:
569 case KindOfRClsMeth:
570 case KindOfRFunc:
571 case KindOfEnumClassLabel:
572 raise_error("Unsupported operand type for ~");
576 //////////////////////////////////////////////////////////////////////