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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/member-operations.h"
19 #include "hphp/runtime/base/array-iterator.h"
20 #include "hphp/runtime/base/packed-array.h"
21 #include "hphp/runtime/base/packed-array-defs.h"
22 #include "hphp/runtime/base/tv-refcount.h"
23 #include "hphp/runtime/base/type-string.h"
25 #include "hphp/system/systemlib.h"
27 #include <type_traits>
31 StringData
* prepareAnyKey(TypedValue
* tv
) {
32 if (isStringType(tv
->m_type
)) {
33 StringData
* str
= tv
->m_data
.pstr
;
37 return tvAsCVarRef(tv
).toString().detach();
41 void unknownBaseType(DataType type
) {
44 "Unknown KindOf: {} in member operation base",
45 static_cast<uint8_t>(type
)
49 // Mutable collections support appending new elements using [] without a key
50 // like so: "$vector[] = 123;". However, collections do not support using []
51 // without a key to implicitly create a new element without supplying assigning
52 // an initial value (ex "$vector[]['a'] = 73;").
53 void throw_cannot_use_newelem_for_lval_read_col() {
54 SystemLib::throwInvalidOperationExceptionObject(
55 "Cannot use [] with collections for reading in an lvalue context");
58 void throw_cannot_use_newelem_for_lval_read_vec() {
59 SystemLib::throwInvalidOperationExceptionObject(
60 "Cannot use [] with vecs for reading in an lvalue context"
64 void throw_cannot_use_newelem_for_lval_read_dict() {
65 SystemLib::throwInvalidOperationExceptionObject(
66 "Cannot use [] with dicts for reading in an lvalue context"
70 void throw_cannot_use_newelem_for_lval_read_keyset() {
71 SystemLib::throwInvalidOperationExceptionObject(
72 "Cannot use [] with keysets for reading in an lvalue context"
76 void throw_cannot_use_newelem_for_lval_read_clsmeth() {
77 SystemLib::throwInvalidOperationExceptionObject(
78 "Cannot use [] with clsmeth for reading in an lvalue context"
82 void throw_cannot_use_newelem_for_lval_read_record() {
83 SystemLib::throwInvalidOperationExceptionObject(
84 "Cannot use [] with record for reading in an lvalue context"
88 TypedValue
incDecBodySlow(IncDecOp op
, tv_lval fr
) {
89 assertx(tvIsPlausible(*fr
));
90 assertx(type(fr
) != KindOfUninit
);
92 auto dup
= [&]() { tvIncRefGen(*fr
); return *fr
; };
95 case IncDecOp::PreInc
:
98 case IncDecOp::PostInc
: {
99 auto const tmp
= dup();
103 case IncDecOp::PreDec
:
106 case IncDecOp::PostDec
: {
107 auto const tmp
= dup();
111 case IncDecOp::PreIncO
:
114 case IncDecOp::PostIncO
: {
115 auto const tmp
= dup();
119 case IncDecOp::PreDecO
:
122 case IncDecOp::PostDecO
: {
123 auto const tmp
= dup();
133 void copy_int(void* dest
, int64_t src
, size_t size
) {
137 } else if (size
== 2) {
140 } else if (size
== 4) {
150 template<typename
... Args
>
151 [[noreturn
]] NEVER_INLINE
152 void fail_oob(folly::StringPiece fmt
, Args
&&... args
) {
153 SystemLib::throwOutOfBoundsExceptionObject(
154 folly::sformat(fmt
, std::forward
<Args
>(args
)...)
158 template<typename
... Args
>
159 [[noreturn
]] NEVER_INLINE
160 void fail_invalid(folly::StringPiece fmt
, Args
&&... args
) {
161 SystemLib::throwInvalidOperationExceptionObject(
162 folly::sformat(fmt
, std::forward
<Args
>(args
)...)
166 template<bool reverse
, typename F
>
168 char* dest
, TypedValue src
, int64_t count
, int64_t size
, F range_check
170 auto const src_str
= val(src
).pstr
;
172 count
= src_str
->size();
173 } else if (count
< 0 || count
> src_str
->size()) {
174 fail_oob("Cannot read {}-byte range from {}-byte source string",
175 count
, src_str
->size());
178 fail_invalid("Invalid size {} for string source", size
);
182 auto const data
= src_str
->data();
184 auto end
= data
+ count
;
185 for (int64_t i
= 0; i
< count
% 8; ++i
) {
188 for (int64_t i
= 0; i
< count
/ 8; ++i
) {
191 memcpy(&tmp
, end
, 8);
192 tmp
= __builtin_bswap64(tmp
);
193 memcpy(dest
, &tmp
, 8);
197 memcpy(dest
, data
, count
);
202 * The main vec loop is a template to avoid calling memcpy() with a
203 * non-constant size, and to avoid the check for KindOfDouble in the more
206 template<bool reverse
, int64_t size
, DataType elem_type
>
207 void setRangeVecLoop(char* dest
, const ArrayData
* vec
, int64_t count
) {
209 (isBoolType(elem_type
) && size
== 1) ||
210 (isIntType(elem_type
) &&
211 (size
== 1 || size
== 2 || size
== 4 || size
== 8)) ||
212 (isDoubleType(elem_type
) && (size
== 4 || size
== 8)),
213 "Unsupported elem_type and/or size"
217 if (reverse
) dest
+= (count
- 1) * size
;
218 IterateV(vec
, [&](TypedValue elem
) {
219 if (UNLIKELY(type(elem
) != elem_type
)) {
221 "Multiple types in vec source: {} at index 0; {} at index {}",
222 getDataTypeString(elem_type
).data(),
223 getDataTypeString(type(elem
)).data(),
228 if (isDoubleType(elem_type
)) {
229 std::conditional_t
<size
== 4, float, double> f
= val(elem
).dbl
;
230 memcpy(dest
, &f
, size
);
232 copy_int(dest
, val(elem
).num
, size
);
234 dest
+= reverse
? -size
: size
;
235 return (++i
) == count
;
239 template<bool reverse
, typename F
>
241 char* dest
, const ArrayData
* vec
, int64_t count
, int64_t size
, F range_check
243 auto const vec_size
= vec
->size();
246 } else if (count
< 0 || count
> vec_size
) {
247 fail_oob("Cannot read {} elements from vec of size {}", count
, vec_size
);
250 range_check(count
* size
);
251 auto const elem_type
= type(vec
->at(int64_t{0}));
252 assertx(elem_type
!= KindOfUninit
);
253 auto bad_type
= [&]() {
255 "Bad type ({}) and element size ({}) combination in vec source",
256 getDataTypeString(elem_type
).data(), size
262 if (isIntType(elem_type
)) {
263 setRangeVecLoop
<reverse
, 1, KindOfInt64
>(dest
, vec
, count
);
264 } else if (isBoolType(elem_type
)) {
265 setRangeVecLoop
<reverse
, 1, KindOfBoolean
>(dest
, vec
, count
);
272 if (isIntType(elem_type
)) {
273 setRangeVecLoop
<reverse
, 2, KindOfInt64
>(dest
, vec
, count
);
280 if (isIntType(elem_type
)) {
281 setRangeVecLoop
<reverse
, 4, KindOfInt64
>(dest
, vec
, count
);
282 } else if (isDoubleType(elem_type
)) {
283 setRangeVecLoop
<reverse
, 4, KindOfDouble
>(dest
, vec
, count
);
290 if (isIntType(elem_type
)) {
291 setRangeVecLoop
<reverse
, 8, KindOfInt64
>(dest
, vec
, count
);
292 } else if (isDoubleType(elem_type
)) {
293 setRangeVecLoop
<reverse
, 8, KindOfDouble
>(dest
, vec
, count
);
305 template<bool reverse
>
307 tv_lval base
, int64_t offset
, TypedValue src
, int64_t count
, int64_t size
309 if (!tvIsString(base
)) {
310 fail_invalid("Invalid base type {} for range set operation",
311 getDataTypeString(type(base
)).data());
313 if (count
== 0) return;
314 type(base
) = KindOfString
;
316 auto& base_str
= val(base
).pstr
;
317 if (base_str
->cowCheck()) {
318 auto const old_str
= base_str
;
319 base_str
= StringData::Make(old_str
, CopyStringMode
{});
320 FOLLY_SDT(hhvm
, hhvm_cow_setrange
, base_str
->size(), size
*count
);
323 FOLLY_SDT(hhvm
, hhvm_mut_setrange
, base_str
->size(), size
*count
);
326 auto range_check
= [&](size_t data_len
) {
327 if (offset
< 0 || offset
+ data_len
> base_str
->size()) {
329 "Cannot set {}-byte range at offset {} in string of length {}",
337 auto dest
= base_str
->mutableData() + offset
;
339 auto fail_if_reverse
= [&]() {
341 fail_invalid("Cannot set reverse range for primitive type {}",
342 getDataTypeString(type(src
)).data());
350 fail_invalid("Invalid size {} for bool source", size
);
354 copy_int(dest
, val(src
).num
, 1);
355 } else if (tvIsInt(src
)) {
359 copy_int(dest
, val(src
).num
, size
);
360 } else if (tvIsDouble(src
)) {
364 float f
= val(src
).dbl
;
367 } else if (size
== 8) {
369 memcpy(dest
, &val(src
).dbl
, 8);
371 fail_invalid("Invalid size {} for double source", size
);
373 } else if (tvIsString(src
)) {
374 setRangeString
<reverse
>(dest
, src
, count
, size
, range_check
);
375 } else if (tvIsVec(src
)) {
376 setRangeVec
<reverse
>(dest
, val(src
).parr
, count
, size
, range_check
);
378 fail_invalid("Invalid source type %s for range set operation",
383 template void SetRange
<true>(tv_lval
, int64_t, TypedValue
, int64_t, int64_t);
384 template void SetRange
<false>(tv_lval
, int64_t, TypedValue
, int64_t, int64_t);
386 ///////////////////////////////////////////////////////////////////////////////