xref: /PHP-8.1/Zend/zend_operators.h (revision 3725717d)
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