1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Dmitry Stogov <dmitry@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #ifndef ZEND_OPERATORS_H
22 #define ZEND_OPERATORS_H
23
24 #include <errno.h>
25 #include <math.h>
26 #include <assert.h>
27 #include <stddef.h>
28
29 #ifdef HAVE_IEEEFP_H
30 #include <ieeefp.h>
31 #endif
32
33 #include "zend_portability.h"
34 #include "zend_strtod.h"
35 #include "zend_multiply.h"
36 #include "zend_object_handlers.h"
37
38 #define LONG_SIGN_MASK ZEND_LONG_MIN
39
40 BEGIN_EXTERN_C()
41 ZEND_API zend_result ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2);
42 ZEND_API zend_result ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2);
43 ZEND_API zend_result ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2);
44 ZEND_API zend_result ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2);
45 ZEND_API zend_result ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2);
46 ZEND_API zend_result ZEND_FASTCALL mod_function(zval *result, zval *op1, zval *op2);
47 ZEND_API zend_result ZEND_FASTCALL boolean_xor_function(zval *result, zval *op1, zval *op2);
48 ZEND_API zend_result ZEND_FASTCALL boolean_not_function(zval *result, zval *op1);
49 ZEND_API zend_result ZEND_FASTCALL bitwise_not_function(zval *result, zval *op1);
50 ZEND_API zend_result ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op2);
51 ZEND_API zend_result ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *op2);
52 ZEND_API zend_result ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *op2);
53 ZEND_API zend_result ZEND_FASTCALL shift_left_function(zval *result, zval *op1, zval *op2);
54 ZEND_API zend_result ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *op2);
55 ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2);
56
57 ZEND_API bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2);
58
59 ZEND_API zend_result ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2);
60 ZEND_API zend_result ZEND_FASTCALL is_identical_function(zval *result, zval *op1, zval *op2);
61 ZEND_API zend_result ZEND_FASTCALL is_not_identical_function(zval *result, zval *op1, zval *op2);
62 ZEND_API zend_result ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1, zval *op2);
63 ZEND_API zend_result ZEND_FASTCALL is_smaller_function(zval *result, zval *op1, zval *op2);
64 ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zval *op1, zval *op2);
65
66 ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce);
67 ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce);
68
instanceof_function(const zend_class_entry * instance_ce,const zend_class_entry * ce)69 static zend_always_inline bool instanceof_function(
70 const zend_class_entry *instance_ce, const zend_class_entry *ce) {
71 return instance_ce == ce || instanceof_function_slow(instance_ce, ce);
72 }
73
74 /**
75 * Checks whether the string "str" with length "length" is numeric. The value
76 * of allow_errors determines whether it's required to be entirely numeric, or
77 * just its prefix. Leading whitespace is allowed.
78 *
79 * The function returns 0 if the string did not contain a valid number; IS_LONG
80 * if it contained a number that fits within the range of a long; or IS_DOUBLE
81 * if the number was out of long range or contained a decimal point/exponent.
82 * The number's value is returned into the respective pointer, *lval or *dval,
83 * if that pointer is not NULL.
84 *
85 * This variant also gives information if a string that represents an integer
86 * could not be represented as such due to overflow. It writes 1 to oflow_info
87 * if the integer is larger than ZEND_LONG_MAX and -1 if it's smaller than ZEND_LONG_MIN.
88 */
89 ZEND_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(const char *str, size_t length, zend_long *lval,
90 double *dval, bool allow_errors, int *oflow_info, bool *trailing_data);
91
92 ZEND_API const char* ZEND_FASTCALL zend_memnstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end);
93 ZEND_API const char* ZEND_FASTCALL zend_memnrstr_ex(const char *haystack, const char *needle, size_t needle_len, const char *end);
94
95 #if SIZEOF_ZEND_LONG == 4
96 # define ZEND_DOUBLE_FITS_LONG(d) (!((d) > (double)ZEND_LONG_MAX || (d) < (double)ZEND_LONG_MIN))
97 #else
98 /* >= as (double)ZEND_LONG_MAX is outside signed range */
99 # define ZEND_DOUBLE_FITS_LONG(d) (!((d) >= (double)ZEND_LONG_MAX || (d) < (double)ZEND_LONG_MIN))
100 #endif
101
102 ZEND_API zend_long ZEND_FASTCALL zend_dval_to_lval_slow(double d);
103
zend_dval_to_lval(double d)104 static zend_always_inline zend_long zend_dval_to_lval(double d)
105 {
106 if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
107 return 0;
108 } else if (!ZEND_DOUBLE_FITS_LONG(d)) {
109 return zend_dval_to_lval_slow(d);
110 }
111 return (zend_long)d;
112 }
113
114 /* Used to convert a string float to integer during an (int) cast */
zend_dval_to_lval_cap(double d)115 static zend_always_inline zend_long zend_dval_to_lval_cap(double d)
116 {
117 if (UNEXPECTED(!zend_finite(d)) || UNEXPECTED(zend_isnan(d))) {
118 return 0;
119 } else if (!ZEND_DOUBLE_FITS_LONG(d)) {
120 return (d > 0 ? ZEND_LONG_MAX : ZEND_LONG_MIN);
121 }
122 return (zend_long)d;
123 }
124 /* }}} */
125
zend_is_long_compatible(double d,zend_long l)126 static zend_always_inline bool zend_is_long_compatible(double d, zend_long l) {
127 return (double)l == d;
128 }
129
130 ZEND_API void zend_incompatible_double_to_long_error(double d);
131 ZEND_API void zend_incompatible_string_to_long_error(const zend_string *s);
132
zend_dval_to_lval_safe(double d)133 static zend_always_inline zend_long zend_dval_to_lval_safe(double d)
134 {
135 zend_long l = zend_dval_to_lval(d);
136 if (!zend_is_long_compatible(d, l)) {
137 zend_incompatible_double_to_long_error(d);
138 }
139 return l;
140 }
141
142 #define ZEND_IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
143 #define ZEND_IS_XDIGIT(c) (((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
144
is_numeric_string_ex(const char * str,size_t length,zend_long * lval,double * dval,bool allow_errors,int * oflow_info,bool * trailing_data)145 static zend_always_inline zend_uchar is_numeric_string_ex(const char *str, size_t length, zend_long *lval,
146 double *dval, bool allow_errors, int *oflow_info, bool *trailing_data)
147 {
148 if (*str > '9') {
149 return 0;
150 }
151 return _is_numeric_string_ex(str, length, lval, dval, allow_errors, oflow_info, trailing_data);
152 }
153
is_numeric_string(const char * str,size_t length,zend_long * lval,double * dval,bool allow_errors)154 static zend_always_inline zend_uchar is_numeric_string(const char *str, size_t length, zend_long *lval, double *dval, bool allow_errors) {
155 return is_numeric_string_ex(str, length, lval, dval, allow_errors, NULL, NULL);
156 }
157
158 ZEND_API zend_uchar ZEND_FASTCALL is_numeric_str_function(const zend_string *str, zend_long *lval, double *dval);
159
160 static zend_always_inline const char *
zend_memnstr(const char * haystack,const char * needle,size_t needle_len,const char * end)161 zend_memnstr(const char *haystack, const char *needle, size_t needle_len, const char *end)
162 {
163 const char *p = haystack;
164 ptrdiff_t off_p;
165 size_t off_s;
166
167 if (needle_len == 0) {
168 return p;
169 }
170
171 if (needle_len == 1) {
172 return (const char *)memchr(p, *needle, (end-p));
173 }
174
175 off_p = end - haystack;
176 off_s = (off_p > 0) ? (size_t)off_p : 0;
177
178 if (needle_len > off_s) {
179 return NULL;
180 }
181
182 if (EXPECTED(off_s < 1024 || needle_len < 9)) { /* glibc memchr is faster when needle is too short */
183 const char ne = needle[needle_len-1];
184 end -= needle_len;
185
186 while (p <= end) {
187 if ((p = (const char *)memchr(p, *needle, (end-p+1))) && ne == p[needle_len-1]) {
188 if (!memcmp(needle+1, p+1, needle_len-2)) {
189 return p;
190 }
191 }
192
193 if (p == NULL) {
194 return NULL;
195 }
196
197 p++;
198 }
199
200 return NULL;
201 } else {
202 return zend_memnstr_ex(haystack, needle, needle_len, end);
203 }
204 }
205
zend_memrchr(const void * s,int c,size_t n)206 static zend_always_inline const void *zend_memrchr(const void *s, int c, size_t n)
207 {
208 const unsigned char *e;
209 if (0 == n) {
210 return NULL;
211 }
212
213 for (e = (const unsigned char *)s + n - 1; e >= (const unsigned char *)s; e--) {
214 if (*e == (unsigned char)c) {
215 return (const void *)e;
216 }
217 }
218 return NULL;
219 }
220
221
222 static zend_always_inline const char *
zend_memnrstr(const char * haystack,const char * needle,size_t needle_len,const char * end)223 zend_memnrstr(const char *haystack, const char *needle, size_t needle_len, const char *end)
224 {
225 const char *p = end;
226 ptrdiff_t off_p;
227 size_t off_s;
228
229 if (needle_len == 0) {
230 return p;
231 }
232
233 if (needle_len == 1) {
234 return (const char *)zend_memrchr(haystack, *needle, (p - haystack));
235 }
236
237 off_p = end - haystack;
238 off_s = (off_p > 0) ? (size_t)off_p : 0;
239
240 if (needle_len > off_s) {
241 return NULL;
242 }
243
244 if (EXPECTED(off_s < 1024 || needle_len < 3)) {
245 const char ne = needle[needle_len-1];
246 p -= needle_len;
247
248 do {
249 p = (const char *)zend_memrchr(haystack, *needle, (p - haystack) + 1);
250 if (!p) {
251 return NULL;
252 }
253 if (ne == p[needle_len-1] && !memcmp(needle + 1, p + 1, needle_len - 2)) {
254 return p;
255 }
256 } while (p-- >= haystack);
257
258 return NULL;
259 } else {
260 return zend_memnrstr_ex(haystack, needle, needle_len, end);
261 }
262 }
263
264 ZEND_API zend_result ZEND_FASTCALL increment_function(zval *op1);
265 ZEND_API zend_result ZEND_FASTCALL decrement_function(zval *op2);
266
267 ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op);
268 ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op);
269 ZEND_API void ZEND_FASTCALL convert_to_long(zval *op);
270 ZEND_API void ZEND_FASTCALL convert_to_double(zval *op);
271 ZEND_API void ZEND_FASTCALL convert_to_null(zval *op);
272 ZEND_API void ZEND_FASTCALL convert_to_boolean(zval *op);
273 ZEND_API void ZEND_FASTCALL convert_to_array(zval *op);
274 ZEND_API void ZEND_FASTCALL convert_to_object(zval *op);
275
276 ZEND_API zend_long ZEND_FASTCALL zval_get_long_func(zval *op, bool is_strict);
277 ZEND_API double ZEND_FASTCALL zval_get_double_func(zval *op);
278 ZEND_API zend_string* ZEND_FASTCALL zval_get_string_func(zval *op);
279 ZEND_API zend_string* ZEND_FASTCALL zval_try_get_string_func(zval *op);
280
zval_get_long(zval * op)281 static zend_always_inline zend_long zval_get_long(zval *op) {
282 return EXPECTED(Z_TYPE_P(op) == IS_LONG) ? Z_LVAL_P(op) : zval_get_long_func(op, false);
283 }
zval_get_long_ex(zval * op,bool is_strict)284 static zend_always_inline zend_long zval_get_long_ex(zval *op, bool is_strict) {
285 return EXPECTED(Z_TYPE_P(op) == IS_LONG) ? Z_LVAL_P(op) : zval_get_long_func(op, is_strict);
286 }
zval_get_double(zval * op)287 static zend_always_inline double zval_get_double(zval *op) {
288 return EXPECTED(Z_TYPE_P(op) == IS_DOUBLE) ? Z_DVAL_P(op) : zval_get_double_func(op);
289 }
zval_get_string(zval * op)290 static zend_always_inline zend_string *zval_get_string(zval *op) {
291 return EXPECTED(Z_TYPE_P(op) == IS_STRING) ? zend_string_copy(Z_STR_P(op)) : zval_get_string_func(op);
292 }
293
zval_get_tmp_string(zval * op,zend_string ** tmp)294 static zend_always_inline zend_string *zval_get_tmp_string(zval *op, zend_string **tmp) {
295 if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) {
296 *tmp = NULL;
297 return Z_STR_P(op);
298 } else {
299 return *tmp = zval_get_string_func(op);
300 }
301 }
zend_tmp_string_release(zend_string * tmp)302 static zend_always_inline void zend_tmp_string_release(zend_string *tmp) {
303 if (UNEXPECTED(tmp)) {
304 zend_string_release_ex(tmp, 0);
305 }
306 }
307
308 /* Like zval_get_string, but returns NULL if the conversion fails with an exception. */
zval_try_get_string(zval * op)309 static zend_always_inline zend_string *zval_try_get_string(zval *op) {
310 if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) {
311 zend_string *ret = zend_string_copy(Z_STR_P(op));
312 ZEND_ASSUME(ret != NULL);
313 return ret;
314 } else {
315 return zval_try_get_string_func(op);
316 }
317 }
318
319 /* Like zval_get_tmp_string, but returns NULL if the conversion fails with an exception. */
zval_try_get_tmp_string(zval * op,zend_string ** tmp)320 static zend_always_inline zend_string *zval_try_get_tmp_string(zval *op, zend_string **tmp) {
321 if (EXPECTED(Z_TYPE_P(op) == IS_STRING)) {
322 zend_string *ret = Z_STR_P(op);
323 *tmp = NULL;
324 ZEND_ASSUME(ret != NULL);
325 return ret;
326 } else {
327 return *tmp = zval_try_get_string_func(op);
328 }
329 }
330
331 /* Like convert_to_string(), but returns whether the conversion succeeded and does not modify the
332 * zval in-place if it fails. */
333 ZEND_API bool ZEND_FASTCALL _try_convert_to_string(zval *op);
try_convert_to_string(zval * op)334 static zend_always_inline bool try_convert_to_string(zval *op) {
335 if (Z_TYPE_P(op) == IS_STRING) {
336 return 1;
337 }
338 return _try_convert_to_string(op);
339 }
340
341 /* Compatibility macros for 7.2 and below */
342 #define _zval_get_long(op) zval_get_long(op)
343 #define _zval_get_double(op) zval_get_double(op)
344 #define _zval_get_string(op) zval_get_string(op)
345 #define _zval_get_long_func(op) zval_get_long_func(op)
346 #define _zval_get_double_func(op) zval_get_double_func(op)
347 #define _zval_get_string_func(op) zval_get_string_func(op)
348
349 #define convert_to_string(op) if (Z_TYPE_P(op) != IS_STRING) { _convert_to_string((op)); }
350
351
352 ZEND_API int ZEND_FASTCALL zend_is_true(zval *op);
353 ZEND_API bool ZEND_FASTCALL zend_object_is_true(zval *op);
354
355 #define zval_is_true(op) \
356 zend_is_true(op)
357
i_zend_is_true(zval * op)358 static zend_always_inline bool i_zend_is_true(zval *op)
359 {
360 bool result = 0;
361
362 again:
363 switch (Z_TYPE_P(op)) {
364 case IS_TRUE:
365 result = 1;
366 break;
367 case IS_LONG:
368 if (Z_LVAL_P(op)) {
369 result = 1;
370 }
371 break;
372 case IS_DOUBLE:
373 if (Z_DVAL_P(op)) {
374 result = 1;
375 }
376 break;
377 case IS_STRING:
378 if (Z_STRLEN_P(op) > 1 || (Z_STRLEN_P(op) && Z_STRVAL_P(op)[0] != '0')) {
379 result = 1;
380 }
381 break;
382 case IS_ARRAY:
383 if (zend_hash_num_elements(Z_ARRVAL_P(op))) {
384 result = 1;
385 }
386 break;
387 case IS_OBJECT:
388 if (EXPECTED(Z_OBJ_HT_P(op)->cast_object == zend_std_cast_object_tostring)) {
389 result = 1;
390 } else {
391 result = zend_object_is_true(op);
392 }
393 break;
394 case IS_RESOURCE:
395 if (EXPECTED(Z_RES_HANDLE_P(op))) {
396 result = 1;
397 }
398 break;
399 case IS_REFERENCE:
400 op = Z_REFVAL_P(op);
401 goto again;
402 break;
403 default:
404 break;
405 }
406 return result;
407 }
408
409 /* Indicate that two values cannot be compared. This value should be returned for both orderings
410 * of the operands. This implies that all of ==, <, <= and >, >= will return false, because we
411 * canonicalize >/>= to </<= with swapped operands. */
412 // TODO: Use a different value to allow an actual distinction here.
413 #define ZEND_UNCOMPARABLE 1
414
415 ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2);
416
417 ZEND_API zend_result ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2);
418
419 ZEND_API int ZEND_FASTCALL numeric_compare_function(zval *op1, zval *op2);
420 ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, bool case_insensitive);
421 ZEND_API int ZEND_FASTCALL string_compare_function(zval *op1, zval *op2);
422 ZEND_API int ZEND_FASTCALL string_case_compare_function(zval *op1, zval *op2);
423 ZEND_API int ZEND_FASTCALL string_locale_compare_function(zval *op1, zval *op2);
424
425 ZEND_API void ZEND_FASTCALL zend_str_tolower(char *str, size_t length);
426 ZEND_API char* ZEND_FASTCALL zend_str_tolower_copy(char *dest, const char *source, size_t length);
427 ZEND_API char* ZEND_FASTCALL zend_str_tolower_dup(const char *source, size_t length);
428 ZEND_API char* ZEND_FASTCALL zend_str_tolower_dup_ex(const char *source, size_t length);
429 ZEND_API zend_string* ZEND_FASTCALL zend_string_tolower_ex(zend_string *str, bool persistent);
430
431 #define zend_string_tolower(str) zend_string_tolower_ex(str, 0)
432
433 ZEND_API int ZEND_FASTCALL zend_binary_zval_strcmp(zval *s1, zval *s2);
434 ZEND_API int ZEND_FASTCALL zend_binary_zval_strncmp(zval *s1, zval *s2, zval *s3);
435 ZEND_API int ZEND_FASTCALL zend_binary_zval_strcasecmp(zval *s1, zval *s2);
436 ZEND_API int ZEND_FASTCALL zend_binary_zval_strncasecmp(zval *s1, zval *s2, zval *s3);
437 ZEND_API int ZEND_FASTCALL zend_binary_strcmp(const char *s1, size_t len1, const char *s2, size_t len2);
438 ZEND_API int ZEND_FASTCALL zend_binary_strncmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length);
439 ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp(const char *s1, size_t len1, const char *s2, size_t len2);
440 ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp(const char *s1, size_t len1, const char *s2, size_t len2, size_t length);
441 ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp_l(const char *s1, size_t len1, const char *s2, size_t len2);
442 ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp_l(const char *s1, size_t len1, const char *s2, size_t len2, size_t length);
443
444 ZEND_API bool ZEND_FASTCALL zendi_smart_streq(zend_string *s1, zend_string *s2);
445 ZEND_API int ZEND_FASTCALL zendi_smart_strcmp(zend_string *s1, zend_string *s2);
446 ZEND_API int ZEND_FASTCALL zend_compare_symbol_tables(HashTable *ht1, HashTable *ht2);
447 ZEND_API int ZEND_FASTCALL zend_compare_arrays(zval *a1, zval *a2);
448 ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2);
449
450 ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len);
451 ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len);
452
453 #define convert_to_null_ex(zv) convert_to_null(zv)
454 #define convert_to_boolean_ex(zv) convert_to_boolean(zv)
455 #define convert_to_long_ex(zv) convert_to_long(zv)
456 #define convert_to_double_ex(zv) convert_to_double(zv)
457 #define convert_to_string_ex(zv) convert_to_string(zv)
458 #define convert_to_array_ex(zv) convert_to_array(zv)
459 #define convert_to_object_ex(zv) convert_to_object(zv)
460 #define convert_scalar_to_number_ex(zv) convert_scalar_to_number(zv)
461
462 ZEND_API void zend_update_current_locale(void);
463
464 ZEND_API void zend_reset_lc_ctype_locale(void);
465
466 /* The offset in bytes between the value and type fields of a zval */
467 #define ZVAL_OFFSETOF_TYPE \
468 (offsetof(zval, u1.type_info) - offsetof(zval, value))
469
470 #if defined(HAVE_ASM_GOTO) && !__has_feature(memory_sanitizer)
471 # define ZEND_USE_ASM_ARITHMETIC 1
472 #else
473 # define ZEND_USE_ASM_ARITHMETIC 0
474 #endif
475
fast_long_increment_function(zval * op1)476 static zend_always_inline void fast_long_increment_function(zval *op1)
477 {
478 #if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
479 __asm__ goto(
480 "addl $1,(%0)\n\t"
481 "jo %l1\n"
482 :
483 : "r"(&op1->value)
484 : "cc", "memory"
485 : overflow);
486 return;
487 overflow: ZEND_ATTRIBUTE_COLD_LABEL
488 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
489 #elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
490 __asm__ goto(
491 "addq $1,(%0)\n\t"
492 "jo %l1\n"
493 :
494 : "r"(&op1->value)
495 : "cc", "memory"
496 : overflow);
497 return;
498 overflow: ZEND_ATTRIBUTE_COLD_LABEL
499 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
500 #elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
501 __asm__ goto (
502 "ldr x5, [%0]\n\t"
503 "adds x5, x5, 1\n\t"
504 "bvs %l1\n"
505 "str x5, [%0]"
506 :
507 : "r"(&op1->value)
508 : "x5", "cc", "memory"
509 : overflow);
510 return;
511 overflow: ZEND_ATTRIBUTE_COLD_LABEL
512 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
513 #elif PHP_HAVE_BUILTIN_SADDL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
514 long lresult;
515 if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), 1, &lresult))) {
516 /* switch to double */
517 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
518 } else {
519 Z_LVAL_P(op1) = lresult;
520 }
521 #elif PHP_HAVE_BUILTIN_SADDLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
522 long long llresult;
523 if (UNEXPECTED(__builtin_saddll_overflow(Z_LVAL_P(op1), 1, &llresult))) {
524 /* switch to double */
525 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
526 } else {
527 Z_LVAL_P(op1) = llresult;
528 }
529 #else
530 if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MAX)) {
531 /* switch to double */
532 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MAX + 1.0);
533 } else {
534 Z_LVAL_P(op1)++;
535 }
536 #endif
537 }
538
fast_long_decrement_function(zval * op1)539 static zend_always_inline void fast_long_decrement_function(zval *op1)
540 {
541 #if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
542 __asm__ goto(
543 "subl $1,(%0)\n\t"
544 "jo %l1\n"
545 :
546 : "r"(&op1->value)
547 : "cc", "memory"
548 : overflow);
549 return;
550 overflow: ZEND_ATTRIBUTE_COLD_LABEL
551 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
552 #elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
553 __asm__ goto(
554 "subq $1,(%0)\n\t"
555 "jo %l1\n"
556 :
557 : "r"(&op1->value)
558 : "cc", "memory"
559 : overflow);
560 return;
561 overflow: ZEND_ATTRIBUTE_COLD_LABEL
562 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
563 #elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
564 __asm__ goto (
565 "ldr x5, [%0]\n\t"
566 "subs x5 ,x5, 1\n\t"
567 "bvs %l1\n"
568 "str x5, [%0]"
569 :
570 : "r"(&op1->value)
571 : "x5", "cc", "memory"
572 : overflow);
573 return;
574 overflow: ZEND_ATTRIBUTE_COLD_LABEL
575 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
576 #elif PHP_HAVE_BUILTIN_SSUBL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
577 long lresult;
578 if (UNEXPECTED(__builtin_ssubl_overflow(Z_LVAL_P(op1), 1, &lresult))) {
579 /* switch to double */
580 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
581 } else {
582 Z_LVAL_P(op1) = lresult;
583 }
584 #elif PHP_HAVE_BUILTIN_SSUBLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
585 long long llresult;
586 if (UNEXPECTED(__builtin_ssubll_overflow(Z_LVAL_P(op1), 1, &llresult))) {
587 /* switch to double */
588 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
589 } else {
590 Z_LVAL_P(op1) = llresult;
591 }
592 #else
593 if (UNEXPECTED(Z_LVAL_P(op1) == ZEND_LONG_MIN)) {
594 /* switch to double */
595 ZVAL_DOUBLE(op1, (double)ZEND_LONG_MIN - 1.0);
596 } else {
597 Z_LVAL_P(op1)--;
598 }
599 #endif
600 }
601
fast_long_add_function(zval * result,zval * op1,zval * op2)602 static zend_always_inline void fast_long_add_function(zval *result, zval *op1, zval *op2)
603 {
604 #if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
605 __asm__ goto(
606 "movl (%1), %%eax\n\t"
607 "addl (%2), %%eax\n\t"
608 "jo %l5\n\t"
609 "movl %%eax, (%0)\n\t"
610 "movl %3, %c4(%0)\n"
611 :
612 : "r"(&result->value),
613 "r"(&op1->value),
614 "r"(&op2->value),
615 "n"(IS_LONG),
616 "n"(ZVAL_OFFSETOF_TYPE)
617 : "eax","cc", "memory"
618 : overflow);
619 return;
620 overflow: ZEND_ATTRIBUTE_COLD_LABEL
621 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
622 #elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
623 __asm__ goto(
624 "movq (%1), %%rax\n\t"
625 "addq (%2), %%rax\n\t"
626 "jo %l5\n\t"
627 "movq %%rax, (%0)\n\t"
628 "movl %3, %c4(%0)\n"
629 :
630 : "r"(&result->value),
631 "r"(&op1->value),
632 "r"(&op2->value),
633 "n"(IS_LONG),
634 "n"(ZVAL_OFFSETOF_TYPE)
635 : "rax","cc", "memory"
636 : overflow);
637 return;
638 overflow: ZEND_ATTRIBUTE_COLD_LABEL
639 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
640 #elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
641 __asm__ goto(
642 "ldr x5, [%1]\n\t"
643 "ldr x6, [%2]\n\t"
644 "adds x5, x5, x6\n\t"
645 "bvs %l5\n\t"
646 "mov w6, %3\n\t"
647 "str x5, [%0]\n\t"
648 "str w6, [%0, %c4]\n"
649 :
650 : "r"(&result->value),
651 "r"(&op1->value),
652 "r"(&op2->value),
653 "n"(IS_LONG),
654 "n"(ZVAL_OFFSETOF_TYPE)
655 : "x5", "x6", "cc", "memory"
656 : overflow);
657 return;
658 overflow: ZEND_ATTRIBUTE_COLD_LABEL
659 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
660 #elif PHP_HAVE_BUILTIN_SADDL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
661 long lresult;
662 if (UNEXPECTED(__builtin_saddl_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &lresult))) {
663 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
664 } else {
665 ZVAL_LONG(result, lresult);
666 }
667 #elif PHP_HAVE_BUILTIN_SADDLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
668 long long llresult;
669 if (UNEXPECTED(__builtin_saddll_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &llresult))) {
670 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
671 } else {
672 ZVAL_LONG(result, llresult);
673 }
674 #else
675 /*
676 * 'result' may alias with op1 or op2, so we need to
677 * ensure that 'result' is not updated until after we
678 * have read the values of op1 and op2.
679 */
680
681 if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) == (Z_LVAL_P(op2) & LONG_SIGN_MASK)
682 && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != ((Z_LVAL_P(op1) + Z_LVAL_P(op2)) & LONG_SIGN_MASK))) {
683 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) + (double) Z_LVAL_P(op2));
684 } else {
685 ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
686 }
687 #endif
688 }
689
fast_add_function(zval * result,zval * op1,zval * op2)690 static zend_always_inline zend_result fast_add_function(zval *result, zval *op1, zval *op2)
691 {
692 if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
693 if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
694 fast_long_add_function(result, op1, op2);
695 return SUCCESS;
696 } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
697 ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
698 return SUCCESS;
699 }
700 } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
701 if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
702 ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
703 return SUCCESS;
704 } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
705 ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2)));
706 return SUCCESS;
707 }
708 }
709 return add_function(result, op1, op2);
710 }
711
fast_long_sub_function(zval * result,zval * op1,zval * op2)712 static zend_always_inline void fast_long_sub_function(zval *result, zval *op1, zval *op2)
713 {
714 #if ZEND_USE_ASM_ARITHMETIC && defined(__i386__) && !(4 == __GNUC__ && 8 == __GNUC_MINOR__)
715 __asm__ goto(
716 "movl (%1), %%eax\n\t"
717 "subl (%2), %%eax\n\t"
718 "jo %l5\n\t"
719 "movl %%eax, (%0)\n\t"
720 "movl %3, %c4(%0)\n"
721 :
722 : "r"(&result->value),
723 "r"(&op1->value),
724 "r"(&op2->value),
725 "n"(IS_LONG),
726 "n"(ZVAL_OFFSETOF_TYPE)
727 : "eax","cc", "memory"
728 : overflow);
729 return;
730 overflow: ZEND_ATTRIBUTE_COLD_LABEL
731 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
732 #elif ZEND_USE_ASM_ARITHMETIC && defined(__x86_64__)
733 __asm__ goto(
734 "movq (%1), %%rax\n\t"
735 "subq (%2), %%rax\n\t"
736 "jo %l5\n\t"
737 "movq %%rax, (%0)\n\t"
738 "movl %3, %c4(%0)\n"
739 :
740 : "r"(&result->value),
741 "r"(&op1->value),
742 "r"(&op2->value),
743 "n"(IS_LONG),
744 "n"(ZVAL_OFFSETOF_TYPE)
745 : "rax","cc", "memory"
746 : overflow);
747 return;
748 overflow: ZEND_ATTRIBUTE_COLD_LABEL
749 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
750 #elif ZEND_USE_ASM_ARITHMETIC && defined(__aarch64__)
751 __asm__ goto(
752 "ldr x5, [%1]\n\t"
753 "ldr x6, [%2]\n\t"
754 "subs x5, x5, x6\n\t"
755 "bvs %l5\n\t"
756 "mov w6, %3\n\t"
757 "str x5, [%0]\n\t"
758 "str w6, [%0, %c4]\n"
759 :
760 : "r"(&result->value),
761 "r"(&op1->value),
762 "r"(&op2->value),
763 "n"(IS_LONG),
764 "n"(ZVAL_OFFSETOF_TYPE)
765 : "x5", "x6", "cc", "memory"
766 : overflow);
767 return;
768 overflow: ZEND_ATTRIBUTE_COLD_LABEL
769 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
770 #elif PHP_HAVE_BUILTIN_SSUBL_OVERFLOW && SIZEOF_LONG == SIZEOF_ZEND_LONG
771 long lresult;
772 if (UNEXPECTED(__builtin_ssubl_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &lresult))) {
773 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
774 } else {
775 ZVAL_LONG(result, lresult);
776 }
777 #elif PHP_HAVE_BUILTIN_SSUBLL_OVERFLOW && SIZEOF_LONG_LONG == SIZEOF_ZEND_LONG
778 long long llresult;
779 if (UNEXPECTED(__builtin_ssubll_overflow(Z_LVAL_P(op1), Z_LVAL_P(op2), &llresult))) {
780 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
781 } else {
782 ZVAL_LONG(result, llresult);
783 }
784 #else
785 ZVAL_LONG(result, Z_LVAL_P(op1) - Z_LVAL_P(op2));
786
787 if (UNEXPECTED((Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(op2) & LONG_SIGN_MASK)
788 && (Z_LVAL_P(op1) & LONG_SIGN_MASK) != (Z_LVAL_P(result) & LONG_SIGN_MASK))) {
789 ZVAL_DOUBLE(result, (double) Z_LVAL_P(op1) - (double) Z_LVAL_P(op2));
790 }
791 #endif
792 }
793
zend_fast_equal_strings(zend_string * s1,zend_string * s2)794 static zend_always_inline bool zend_fast_equal_strings(zend_string *s1, zend_string *s2)
795 {
796 if (s1 == s2) {
797 return 1;
798 } else if (ZSTR_VAL(s1)[0] > '9' || ZSTR_VAL(s2)[0] > '9') {
799 return zend_string_equal_content(s1, s2);
800 } else {
801 return zendi_smart_streq(s1, s2);
802 }
803 }
804
fast_equal_check_function(zval * op1,zval * op2)805 static zend_always_inline bool fast_equal_check_function(zval *op1, zval *op2)
806 {
807 if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
808 if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
809 return Z_LVAL_P(op1) == Z_LVAL_P(op2);
810 } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
811 return ((double)Z_LVAL_P(op1)) == Z_DVAL_P(op2);
812 }
813 } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
814 if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
815 return Z_DVAL_P(op1) == Z_DVAL_P(op2);
816 } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
817 return Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2));
818 }
819 } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
820 if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
821 return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
822 }
823 }
824 return zend_compare(op1, op2) == 0;
825 }
826
fast_equal_check_long(zval * op1,zval * op2)827 static zend_always_inline bool fast_equal_check_long(zval *op1, zval *op2)
828 {
829 if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
830 return Z_LVAL_P(op1) == Z_LVAL_P(op2);
831 }
832 return zend_compare(op1, op2) == 0;
833 }
834
fast_equal_check_string(zval * op1,zval * op2)835 static zend_always_inline bool fast_equal_check_string(zval *op1, zval *op2)
836 {
837 if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
838 return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
839 }
840 return zend_compare(op1, op2) == 0;
841 }
842
fast_is_identical_function(zval * op1,zval * op2)843 static zend_always_inline bool fast_is_identical_function(zval *op1, zval *op2)
844 {
845 if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
846 return 0;
847 } else if (Z_TYPE_P(op1) <= IS_TRUE) {
848 return 1;
849 }
850 return zend_is_identical(op1, op2);
851 }
852
fast_is_not_identical_function(zval * op1,zval * op2)853 static zend_always_inline bool fast_is_not_identical_function(zval *op1, zval *op2)
854 {
855 if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
856 return 1;
857 } else if (Z_TYPE_P(op1) <= IS_TRUE) {
858 return 0;
859 }
860 return !zend_is_identical(op1, op2);
861 }
862
863 /* buf points to the END of the buffer */
zend_print_ulong_to_buf(char * buf,zend_ulong num)864 static zend_always_inline char *zend_print_ulong_to_buf(char *buf, zend_ulong num) {
865 *buf = '\0';
866 do {
867 *--buf = (char) (num % 10) + '0';
868 num /= 10;
869 } while (num > 0);
870 return buf;
871 }
872
873 /* buf points to the END of the buffer */
zend_print_long_to_buf(char * buf,zend_long num)874 static zend_always_inline char *zend_print_long_to_buf(char *buf, zend_long num) {
875 if (num < 0) {
876 char *result = zend_print_ulong_to_buf(buf, ~((zend_ulong) num) + 1);
877 *--result = '-';
878 return result;
879 } else {
880 return zend_print_ulong_to_buf(buf, num);
881 }
882 }
883
884 ZEND_API zend_string* ZEND_FASTCALL zend_long_to_str(zend_long num);
885 ZEND_API zend_string* ZEND_FASTCALL zend_ulong_to_str(zend_ulong num);
886 ZEND_API zend_string* ZEND_FASTCALL zend_u64_to_str(uint64_t num);
887 ZEND_API zend_string* ZEND_FASTCALL zend_i64_to_str(int64_t num);
888 ZEND_API zend_string* ZEND_FASTCALL zend_double_to_str(double num);
889
zend_unwrap_reference(zval * op)890 static zend_always_inline void zend_unwrap_reference(zval *op) /* {{{ */
891 {
892 if (Z_REFCOUNT_P(op) == 1) {
893 ZVAL_UNREF(op);
894 } else {
895 Z_DELREF_P(op);
896 ZVAL_COPY(op, Z_REFVAL_P(op));
897 }
898 }
899 /* }}} */
900
901
902 END_EXTERN_C()
903
904 #endif
905