Use folly::dynamic::object and folly::dynamic::string exclusivly
[hiphop-php.git] / hphp / runtime / base / tv-arith.cpp
blob3bc32bbd60b34dfac9cde65ead0cf93ee794ec9f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
19 #include <limits>
20 #include <algorithm>
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"
28 namespace HPHP {
30 //////////////////////////////////////////////////////////////////////
32 namespace {
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) {
40 case KindOfString:
41 case KindOfStaticString: return stringToNumeric(cell.m_data.pstr);
42 case KindOfBoolean: return make_tv<KindOfInt64>(cell.m_data.num);
43 case KindOfUninit:
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();
50 default: break;
52 not_reached();
55 template<class Op>
56 Cell cellArith(Op o, Cell c1, Cell c2) {
57 again:
58 if (c1.m_type == KindOfInt64) {
59 for (;;) {
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) {
68 for (;;) {
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);
82 goto again;
85 Cell num(int64_t n) { return make_tv<KindOfInt64>(n); }
86 Cell dbl(double d) { return make_tv<KindOfDouble>(d); }
88 struct Add {
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);
101 struct Sub {
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();
112 struct Mul {
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();
123 struct Div {
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
131 // by -1.
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,
144 Cell
145 >::type operator()(T t, U u) const {
146 static_assert(
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();
161 template<class Op>
162 void cellOpEq(Op op, Cell& c1, Cell c2) {
163 again:
164 if (c1.m_type == KindOfInt64) {
165 for (;;) {
166 if (c2.m_type == KindOfInt64) {
167 c1.m_data.num = op(c1.m_data.num, c2.m_data.num);
168 return;
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);
173 return;
175 c2 = numericConvHelper(c2);
176 assert(c2.m_type == KindOfInt64 || c2.m_type == KindOfDouble);
180 if (c1.m_type == KindOfDouble) {
181 for (;;) {
182 if (c2.m_type == KindOfInt64) {
183 c1.m_data.dbl = op(c1.m_data.dbl, c2.m_data.num);
184 return;
186 if (c2.m_type == KindOfDouble) {
187 c1.m_data.dbl = op(c1.m_data.dbl, c2.m_data.dbl);
188 return;
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);
198 if (newArr != ad1) {
199 c1.m_data.parr = newArr;
200 decRefArr(ad1);
202 return;
205 cellSet(numericConvHelper(c1), c1);
206 assert(c1.m_type == KindOfInt64 || c1.m_type == KindOfDouble);
207 goto again;
210 struct AddEq {
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;
218 if (ad1->empty()) {
219 ad2->incRefCount();
220 return ad2;
222 return ad1->plusEq(ad2);
226 struct SubEq {
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();
237 struct MulEq {
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);
265 return newStr;
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>(
275 stringBitOp(
276 BitOp<char>(),
277 strLenOp,
278 c1.m_data.pstr,
279 c2.m_data.pstr
284 return make_tv<KindOfInt64>(
285 BitOp<int64_t>()(cellToInt(c1), cellToInt(c2))
289 template<class Op>
290 void cellBitOpEq(Op op, Cell& c1, Cell c2) {
291 auto const result = op(c1, c2);
292 cellSet(result, c1);
295 // Op must implement the interface described for cellIncDecOp.
296 template<class Op>
297 void stringIncDecOp(Op op, Cell& cell) {
298 assert(IS_STRING_TYPE(cell.m_type));
300 auto const sd = cell.m_data.pstr;
301 if (sd->empty()) {
302 decRefStr(sd);
303 cellCopy(op.emptyString(), cell);
304 return;
307 int64_t ival;
308 double dval;
309 auto const dt = sd->isNumericWithVal(ival, dval, true /* allow_errors */);
310 switch (dt) {
311 case KindOfInt64:
312 decRefStr(sd);
313 op(ival);
314 cellCopy(num(ival), cell);
315 break;
316 case KindOfDouble:
317 decRefStr(sd);
318 op(dval);
319 cellCopy(dbl(dval), cell);
320 break;
321 default:
322 assert(dt == KindOfNull);
323 op.nonNumericString(cell);
324 break;
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.
339 template<class Op>
340 void cellIncDecOp(Op op, Cell& cell) {
341 assert(cellIsPlausible(cell));
343 switch (cell.m_type) {
344 case KindOfInt64:
345 op(cell.m_data.num);
346 break;
347 case KindOfDouble:
348 op(cell.m_data.dbl);
349 break;
351 case KindOfString:
352 case KindOfStaticString:
353 stringIncDecOp(op, cell);
354 break;
356 case KindOfUninit:
357 case KindOfNull:
358 op.nullCase(cell);
359 break;
361 case KindOfBoolean:
362 case KindOfObject:
363 case KindOfResource:
364 case KindOfArray:
365 break;
366 default:
367 not_reached();
371 const StaticString s_1("1");
373 struct Inc {
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();
386 if (tmp2 != tmp) {
387 assert(tmp->getCount() == 0);
388 tmp->release();
389 return tmp2;
391 return tmp;
392 }();
393 newSd->incRefCount();
394 decRefStr(sd);
395 cellCopy(make_tv<KindOfString>(newSd), cell);
399 struct Dec {
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); },
443 c1, c2
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); },
450 c1, c2
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); },
457 c1, c2
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) {
510 case KindOfInt64:
511 cell.m_data.num = ~cell.m_data.num;
512 break;
513 case KindOfDouble:
514 cell.m_type = KindOfInt64;
515 cell.m_data.num = ~toInt64(cell.m_data.dbl);
516 break;
518 case KindOfString:
519 if (cell.m_data.pstr->hasMultipleRefs()) {
520 case KindOfStaticString:
521 auto const newSd = StringData::Make(
522 cell.m_data.pstr->slice(),
523 CopyString
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;
529 } else {
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) {
542 data[i] = ~data[i];
545 break;
547 default:
548 raise_error("Unsupported operand type for ~");
552 //////////////////////////////////////////////////////////////////////