xref: /PHP-8.3/ext/opcache/jit/zend_jit_helpers.c (revision 126a255d)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend JIT                                                             |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
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    | https://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    +----------------------------------------------------------------------+
15    | Authors: Dmitry Stogov <dmitry@php.net>                              |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "Zend/zend_API.h"
20 
undef_result_after_exception(void)21 static ZEND_COLD void undef_result_after_exception(void) {
22 	const zend_op *opline = EG(opline_before_exception);
23 	ZEND_ASSERT(EG(exception));
24 	if (opline && opline->result_type & (IS_VAR | IS_TMP_VAR)) {
25 		zend_execute_data *execute_data = EG(current_execute_data);
26 		ZVAL_UNDEF(EX_VAR(opline->result.var));
27 	}
28 }
29 
_zend_jit_init_func_run_time_cache(zend_op_array * op_array)30 static zend_never_inline zend_function* ZEND_FASTCALL _zend_jit_init_func_run_time_cache(zend_op_array *op_array) /* {{{ */
31 {
32 	void **run_time_cache;
33 
34 	run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
35 	memset(run_time_cache, 0, op_array->cache_size);
36 	ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache);
37 	return (zend_function*)op_array;
38 }
39 /* }}} */
40 
zend_jit_init_func_run_time_cache_helper(zend_op_array * op_array)41 static zend_never_inline zend_op_array* ZEND_FASTCALL zend_jit_init_func_run_time_cache_helper(zend_op_array *op_array) /* {{{ */
42 {
43 	void **run_time_cache;
44 
45 	if (!RUN_TIME_CACHE(op_array)) {
46 		run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
47 		memset(run_time_cache, 0, op_array->cache_size);
48 		ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache);
49 	}
50 	return op_array;
51 }
52 /* }}} */
53 
zend_jit_find_func_helper(zend_string * name,void ** cache_slot)54 static zend_function* ZEND_FASTCALL zend_jit_find_func_helper(zend_string *name, void **cache_slot)
55 {
56 	zval *func = zend_hash_find_known_hash(EG(function_table), name);
57 	zend_function *fbc;
58 
59 	if (UNEXPECTED(func == NULL)) {
60 		return NULL;
61 	}
62 	fbc = Z_FUNC_P(func);
63 	if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
64 		fbc = _zend_jit_init_func_run_time_cache(&fbc->op_array);
65 	}
66 	*cache_slot = fbc;
67 	return fbc;
68 }
69 
zend_jit_find_ns_func_helper(zval * func_name,void ** cache_slot)70 static zend_function* ZEND_FASTCALL zend_jit_find_ns_func_helper(zval *func_name, void **cache_slot)
71 {
72 	zval *func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(func_name + 1));
73 	zend_function *fbc;
74 
75 	if (func == NULL) {
76 		func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(func_name + 2));
77 		if (UNEXPECTED(func == NULL)) {
78 			return NULL;
79 		}
80 	}
81 	fbc = Z_FUNC_P(func);
82 	if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
83 		fbc = _zend_jit_init_func_run_time_cache(&fbc->op_array);
84 	}
85 	*cache_slot = fbc;
86 	return fbc;
87 }
88 
zend_jit_invalid_method_call(zval * object)89 static ZEND_COLD void ZEND_FASTCALL zend_jit_invalid_method_call(zval *object)
90 {
91 	zend_execute_data *execute_data = EG(current_execute_data);
92 	const zend_op *opline = EX(opline);
93 	zval *function_name = function_name = RT_CONSTANT(opline, opline->op2);;
94 
95 	if (Z_TYPE_P(object) == IS_UNDEF && opline->op1_type == IS_CV) {
96 		zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
97 
98 		zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
99 		if (UNEXPECTED(EG(exception) != NULL)) {
100 			return;
101 		}
102 		object = &EG(uninitialized_zval);
103 	}
104 	zend_throw_error(NULL, "Call to a member function %s() on %s",
105 		Z_STRVAL_P(function_name), zend_zval_value_name(object));
106 }
107 
zend_jit_invalid_method_call_tmp(zval * object)108 static ZEND_COLD void ZEND_FASTCALL zend_jit_invalid_method_call_tmp(zval *object)
109 {
110 	zend_execute_data *execute_data = EG(current_execute_data);
111 	const zend_op *opline = EX(opline);
112 
113 	zend_jit_invalid_method_call(object);
114 	zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
115 }
116 
zend_undefined_method(const zend_class_entry * ce,const zend_string * method)117 static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method)
118 {
119 	zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method));
120 }
121 
zend_jit_unref_helper(zval * zv)122 static void ZEND_FASTCALL zend_jit_unref_helper(zval *zv)
123 {
124 	zend_reference *ref;
125 
126 	ZEND_ASSERT(Z_ISREF_P(zv));
127 	ref = Z_REF_P(zv);
128 	ZVAL_COPY_VALUE(zv, &ref->val);
129 	if (GC_DELREF(ref) == 0) {
130 		efree_size(ref, sizeof(zend_reference));
131 	} else {
132 		Z_TRY_ADDREF_P(zv);
133 	}
134 }
135 
zend_jit_find_method_helper(zend_object * obj,zval * function_name,zend_object ** obj_ptr)136 static zend_function* ZEND_FASTCALL zend_jit_find_method_helper(zend_object *obj, zval *function_name, zend_object **obj_ptr)
137 {
138 	zend_execute_data *execute_data = EG(current_execute_data);
139 	const zend_op *opline = EX(opline);
140 	zend_class_entry *called_scope = obj->ce;
141 	zend_function *fbc;
142 
143 	fbc = obj->handlers->get_method(obj_ptr, Z_STR_P(function_name), function_name + 1);
144 	if (UNEXPECTED(fbc == NULL)) {
145 		if (EXPECTED(!EG(exception))) {
146 			zend_undefined_method(called_scope, Z_STR_P(function_name));
147 		}
148 		return NULL;
149 	}
150 
151 	if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
152 		zend_init_func_run_time_cache(&fbc->op_array);
153 	}
154 
155 	if (UNEXPECTED(obj != *obj_ptr)) {
156 		return fbc;
157 	}
158 
159 	if (EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
160 		CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc);
161 	}
162 
163 	return fbc;
164 }
165 
zend_jit_find_method_tmp_helper(zend_object * obj,zval * function_name,zend_object ** obj_ptr)166 static zend_function* ZEND_FASTCALL zend_jit_find_method_tmp_helper(zend_object *obj, zval *function_name, zend_object **obj_ptr)
167 {
168 	zend_function *fbc;
169 
170 	fbc = zend_jit_find_method_helper(obj, function_name, obj_ptr);
171 	if (!fbc) {
172 		if (GC_DELREF(obj) == 0) {
173 			zend_objects_store_del(obj);
174 		}
175 	} else if (obj != *obj_ptr) {
176 		GC_ADDREF(*obj_ptr);
177 		if (GC_DELREF(obj) == 0) {
178 			zend_objects_store_del(obj);
179 		}
180 	}
181 	return fbc;
182 }
183 
zend_jit_push_static_metod_call_frame(zend_object * obj,zend_function * fbc,uint32_t num_args)184 static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame(zend_object *obj, zend_function *fbc, uint32_t num_args)
185 {
186 	zend_class_entry *scope = obj->ce;
187 
188 	return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope);
189 }
190 
zend_jit_push_static_metod_call_frame_tmp(zend_object * obj,zend_function * fbc,uint32_t num_args)191 static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame_tmp(zend_object *obj, zend_function *fbc, uint32_t num_args)
192 {
193 	zend_class_entry *scope = obj->ce;
194 
195 	if (GC_DELREF(obj) == 0) {
196 		zend_objects_store_del(obj);
197 		if (UNEXPECTED(EG(exception))) {
198 			return NULL;
199 		}
200 	}
201 
202 	return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope);
203 }
204 
zend_jit_extend_stack_helper(uint32_t used_stack,zend_function * fbc)205 static zend_execute_data* ZEND_FASTCALL zend_jit_extend_stack_helper(uint32_t used_stack, zend_function *fbc)
206 {
207 	zend_execute_data *call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
208 	call->func = fbc;
209 	ZEND_CALL_INFO(call) = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_ALLOCATED;
210 	return call;
211 }
212 
zend_jit_int_extend_stack_helper(uint32_t used_stack)213 static zend_execute_data* ZEND_FASTCALL zend_jit_int_extend_stack_helper(uint32_t used_stack)
214 {
215 	zend_execute_data *call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
216 	ZEND_CALL_INFO(call) = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_ALLOCATED;
217 	return call;
218 }
219 
zend_jit_symtable_find(HashTable * ht,zend_string * str)220 static zval* ZEND_FASTCALL zend_jit_symtable_find(HashTable *ht, zend_string *str)
221 {
222 	zend_ulong idx;
223 	register const char *tmp = str->val;
224 
225 	do {
226 		if (*tmp > '9') {
227 			break;
228 		} else if (*tmp < '0') {
229 			if (*tmp != '-') {
230 				break;
231 			}
232 			tmp++;
233 			if (*tmp > '9' || *tmp < '0') {
234 				break;
235 			}
236 		}
237 		if (_zend_handle_numeric_str_ex(str->val, str->len, &idx)) {
238 			return zend_hash_index_find(ht, idx);
239 		}
240 	} while (0);
241 
242 	return zend_hash_find(ht, str);
243 }
244 
zend_jit_hash_index_lookup_rw_no_packed(HashTable * ht,zend_long idx)245 static zval* ZEND_FASTCALL zend_jit_hash_index_lookup_rw_no_packed(HashTable *ht, zend_long idx)
246 {
247 	zval *retval = NULL;
248 
249 	if (!HT_IS_PACKED(ht)) {
250 		retval = _zend_hash_index_find(ht, idx);
251 	}
252 	if (!retval) {
253 		retval = zend_undefined_offset_write(ht, idx);
254 	}
255 	return retval;
256 }
257 
zend_jit_hash_index_lookup_rw(HashTable * ht,zend_long idx)258 static zval* ZEND_FASTCALL zend_jit_hash_index_lookup_rw(HashTable *ht, zend_long idx)
259 {
260 	zval *retval = zend_hash_index_find(ht, idx);
261 
262 	if (!retval) {
263 		retval = zend_undefined_offset_write(ht, idx);
264 	}
265 	return retval;
266 }
267 
zend_jit_hash_lookup_rw(HashTable * ht,zend_string * str)268 static zval* ZEND_FASTCALL zend_jit_hash_lookup_rw(HashTable *ht, zend_string *str)
269 {
270 	zval *retval = zend_hash_find_known_hash(ht, str);
271 	if (!retval) {
272 		/* Key may be released while throwing the undefined index warning. */
273 		retval = zend_undefined_index_write(ht, str);
274 	}
275 	return retval;
276 }
277 
zend_jit_symtable_lookup_rw(HashTable * ht,zend_string * str)278 static zval* ZEND_FASTCALL zend_jit_symtable_lookup_rw(HashTable *ht, zend_string *str)
279 {
280 	zend_ulong idx;
281 	register const char *tmp = str->val;
282 	zval *retval;
283 
284 	do {
285 		if (*tmp > '9') {
286 			break;
287 		} else if (*tmp < '0') {
288 			if (*tmp != '-') {
289 				break;
290 			}
291 			tmp++;
292 			if (*tmp > '9' || *tmp < '0') {
293 				break;
294 			}
295 		}
296 		if (_zend_handle_numeric_str_ex(str->val, str->len, &idx)) {
297 			retval = zend_hash_index_find(ht, idx);
298 			if (!retval) {
299 				retval = zend_undefined_offset_write(ht, idx);
300 			}
301 			return retval;
302 		}
303 	} while (0);
304 
305 	retval = zend_hash_find(ht, str);
306 	if (!retval) {
307 		/* Key may be released while throwing the undefined index warning. */
308 		retval = zend_undefined_index_write(ht, str);
309 	}
310 	return retval;
311 }
312 
zend_jit_symtable_lookup_w(HashTable * ht,zend_string * str)313 static zval* ZEND_FASTCALL zend_jit_symtable_lookup_w(HashTable *ht, zend_string *str)
314 {
315 	zend_ulong idx;
316 	register const char *tmp = str->val;
317 
318 	do {
319 		if (*tmp > '9') {
320 			break;
321 		} else if (*tmp < '0') {
322 			if (*tmp != '-') {
323 				break;
324 			}
325 			tmp++;
326 			if (*tmp > '9' || *tmp < '0') {
327 				break;
328 			}
329 		}
330 		if (_zend_handle_numeric_str_ex(str->val, str->len, &idx)) {
331 			return zend_hash_index_lookup(ht, idx);
332 		}
333 	} while (0);
334 
335 	return zend_hash_lookup(ht, str);
336 }
337 
zend_jit_undefined_op_helper(uint32_t var)338 static int ZEND_FASTCALL zend_jit_undefined_op_helper(uint32_t var)
339 {
340 	const zend_execute_data *execute_data = EG(current_execute_data);
341 	zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(var)];
342 
343 	zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
344 	return EG(exception) == NULL;
345 }
346 
zend_jit_undefined_op_helper_write(HashTable * ht,uint32_t var)347 static int ZEND_FASTCALL zend_jit_undefined_op_helper_write(HashTable *ht, uint32_t var)
348 {
349 	const zend_execute_data *execute_data = EG(current_execute_data);
350 	zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(var)];
351 
352 	/* The array may be destroyed while throwing the notice.
353 	 * Temporarily increase the refcount to detect this situation. */
354 	if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
355 		GC_ADDREF(ht);
356 	}
357 	zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
358 	if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
359 		if (!GC_REFCOUNT(ht)) {
360 			zend_array_destroy(ht);
361 		}
362 		return 0;
363 	}
364 	return EG(exception) == NULL;
365 }
366 
zend_jit_fetch_dim_r_helper(zend_array * ht,zval * dim,zval * result)367 static void ZEND_FASTCALL zend_jit_fetch_dim_r_helper(zend_array *ht, zval *dim, zval *result)
368 {
369 	zend_ulong hval;
370 	zend_string *offset_key;
371 	zval *retval;
372 	zend_execute_data *execute_data;
373 	const zend_op *opline;
374 
375 	if (Z_TYPE_P(dim) == IS_REFERENCE) {
376 		dim = Z_REFVAL_P(dim);
377 	}
378 
379 	switch (Z_TYPE_P(dim)) {
380 		case IS_LONG:
381 			hval = Z_LVAL_P(dim);
382 			goto num_index;
383 		case IS_STRING:
384 			offset_key = Z_STR_P(dim);
385 			goto str_index;
386 		case IS_UNDEF:
387 			/* The array may be destroyed while throwing the notice.
388 			 * Temporarily increase the refcount to detect this situation. */
389 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
390 				GC_ADDREF(ht);
391 			}
392 			execute_data = EG(current_execute_data);
393 			opline = EX(opline);
394 			zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
395 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
396 				zend_array_destroy(ht);
397 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
398 					if (EG(exception)) {
399 						ZVAL_UNDEF(EX_VAR(opline->result.var));
400 					} else {
401 						ZVAL_NULL(EX_VAR(opline->result.var));
402 					}
403 				}
404 				return;
405 			}
406 			if (EG(exception)) {
407 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
408 					ZVAL_UNDEF(EX_VAR(opline->result.var));
409 				}
410 				return;
411 			}
412 			ZEND_FALLTHROUGH;
413 		case IS_NULL:
414 			offset_key = ZSTR_EMPTY_ALLOC();
415 			goto str_index;
416 		case IS_DOUBLE:
417 			hval = zend_dval_to_lval(Z_DVAL_P(dim));
418 			if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
419 				/* The array may be destroyed while throwing the notice.
420 				 * Temporarily increase the refcount to detect this situation. */
421 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
422 					GC_ADDREF(ht);
423 				}
424 				execute_data = EG(current_execute_data);
425 				opline = EX(opline);
426 				zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
427 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
428 					zend_array_destroy(ht);
429 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
430 						if (EG(exception)) {
431 							ZVAL_UNDEF(EX_VAR(opline->result.var));
432 						} else {
433 							ZVAL_NULL(EX_VAR(opline->result.var));
434 						}
435 					}
436 					return;
437 				}
438 				if (EG(exception)) {
439 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
440 						ZVAL_UNDEF(EX_VAR(opline->result.var));
441 					}
442 					return;
443 				}
444 			}
445 			goto num_index;
446 		case IS_RESOURCE:
447 			/* The array may be destroyed while throwing the notice.
448 			 * Temporarily increase the refcount to detect this situation. */
449 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
450 				GC_ADDREF(ht);
451 			}
452 			execute_data = EG(current_execute_data);
453 			opline = EX(opline);
454 			zend_use_resource_as_offset(dim);
455 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
456 				zend_array_destroy(ht);
457 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
458 					if (EG(exception)) {
459 						ZVAL_UNDEF(EX_VAR(opline->result.var));
460 					} else {
461 						ZVAL_NULL(EX_VAR(opline->result.var));
462 					}
463 				}
464 				return;
465 			}
466 			if (EG(exception)) {
467 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
468 					ZVAL_UNDEF(EX_VAR(opline->result.var));
469 				}
470 				return;
471 			}
472 			hval = Z_RES_HANDLE_P(dim);
473 			goto num_index;
474 		case IS_FALSE:
475 			hval = 0;
476 			goto num_index;
477 		case IS_TRUE:
478 			hval = 1;
479 			goto num_index;
480 		default:
481 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), dim, BP_VAR_R);
482 			undef_result_after_exception();
483 			return;
484 	}
485 
486 str_index:
487 	if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
488 		goto num_index;
489 	}
490 	retval = zend_hash_find(ht, offset_key);
491 	if (!retval) {
492 		zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
493 		ZVAL_NULL(result);
494 		return;
495 	}
496 	ZVAL_COPY_DEREF(result, retval);
497 	return;
498 
499 num_index:
500 	ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
501 	ZVAL_COPY_DEREF(result, retval);
502 	return;
503 
504 num_undef:
505 	zend_error(E_WARNING, "Undefined array key " ZEND_LONG_FMT, hval);
506 	ZVAL_NULL(result);
507 }
508 
zend_jit_fetch_dim_is_helper(zend_array * ht,zval * dim,zval * result)509 static void ZEND_FASTCALL zend_jit_fetch_dim_is_helper(zend_array *ht, zval *dim, zval *result)
510 {
511 	zend_ulong hval;
512 	zend_string *offset_key;
513 	zval *retval;
514 	zend_execute_data *execute_data;
515 	const zend_op *opline;
516 
517 	if (Z_TYPE_P(dim) == IS_REFERENCE) {
518 		dim = Z_REFVAL_P(dim);
519 	}
520 
521 	switch (Z_TYPE_P(dim)) {
522 		case IS_LONG:
523 			hval = Z_LVAL_P(dim);
524 			goto num_index;
525 		case IS_STRING:
526 			offset_key = Z_STR_P(dim);
527 			goto str_index;
528 		case IS_UNDEF:
529 			/* The array may be destroyed while throwing the notice.
530 			 * Temporarily increase the refcount to detect this situation. */
531 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
532 				GC_ADDREF(ht);
533 			}
534 			execute_data = EG(current_execute_data);
535 			opline = EX(opline);
536 			zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
537 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
538 				zend_array_destroy(ht);
539 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
540 					if (EG(exception)) {
541 						ZVAL_UNDEF(EX_VAR(opline->result.var));
542 					} else {
543 						ZVAL_NULL(EX_VAR(opline->result.var));
544 					}
545 				}
546 				return;
547 			}
548 			if (EG(exception)) {
549 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
550 					ZVAL_UNDEF(EX_VAR(opline->result.var));
551 				}
552 				return;
553 			}
554 			ZEND_FALLTHROUGH;
555 		case IS_NULL:
556 			offset_key = ZSTR_EMPTY_ALLOC();
557 			goto str_index;
558 		case IS_DOUBLE:
559 			hval = zend_dval_to_lval(Z_DVAL_P(dim));
560 			if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
561 				/* The array may be destroyed while throwing the notice.
562 				 * Temporarily increase the refcount to detect this situation. */
563 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
564 					GC_ADDREF(ht);
565 				}
566 				execute_data = EG(current_execute_data);
567 				opline = EX(opline);
568 				zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
569 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
570 					zend_array_destroy(ht);
571 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
572 						if (EG(exception)) {
573 							ZVAL_UNDEF(EX_VAR(opline->result.var));
574 						} else {
575 							ZVAL_NULL(EX_VAR(opline->result.var));
576 						}
577 					}
578 					return;
579 				}
580 				if (EG(exception)) {
581 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
582 						ZVAL_UNDEF(EX_VAR(opline->result.var));
583 					}
584 					return;
585 				}
586 			}
587 			goto num_index;
588 		case IS_RESOURCE:
589 			/* The array may be destroyed while throwing the notice.
590 			 * Temporarily increase the refcount to detect this situation. */
591 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
592 				GC_ADDREF(ht);
593 			}
594 			execute_data = EG(current_execute_data);
595 			opline = EX(opline);
596 			zend_use_resource_as_offset(dim);
597 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
598 				zend_array_destroy(ht);
599 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
600 					if (EG(exception)) {
601 						ZVAL_UNDEF(EX_VAR(opline->result.var));
602 					} else {
603 						ZVAL_NULL(EX_VAR(opline->result.var));
604 					}
605 				}
606 				return;
607 			}
608 			if (EG(exception)) {
609 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
610 					ZVAL_UNDEF(EX_VAR(opline->result.var));
611 				}
612 				return;
613 			}
614 			hval = Z_RES_HANDLE_P(dim);
615 			goto num_index;
616 		case IS_FALSE:
617 			hval = 0;
618 			goto num_index;
619 		case IS_TRUE:
620 			hval = 1;
621 			goto num_index;
622 		default:
623 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), dim,
624 				EG(current_execute_data)->opline->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ ?
625 					BP_VAR_IS : BP_VAR_RW);
626 			undef_result_after_exception();
627 			return;
628 	}
629 
630 str_index:
631 	if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
632 		goto num_index;
633 	}
634 	retval = zend_hash_find(ht, offset_key);
635 	if (!retval) {
636 		ZVAL_NULL(result);
637 		return;
638 	}
639 	ZVAL_COPY_DEREF(result, retval);
640 	return;
641 
642 num_index:
643 	ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
644 	ZVAL_COPY_DEREF(result, retval);
645 	return;
646 
647 num_undef:
648 	ZVAL_NULL(result);
649 }
650 
zend_jit_fetch_dim_isset_helper(zend_array * ht,zval * dim)651 static int ZEND_FASTCALL zend_jit_fetch_dim_isset_helper(zend_array *ht, zval *dim)
652 {
653 	zend_ulong hval;
654 	zend_string *offset_key;
655 	zval *retval;
656 
657 	if (Z_TYPE_P(dim) == IS_REFERENCE) {
658 		dim = Z_REFVAL_P(dim);
659 	}
660 
661 	switch (Z_TYPE_P(dim)) {
662 		case IS_LONG:
663 			hval = Z_LVAL_P(dim);
664 			goto num_index;
665 		case IS_STRING:
666 			offset_key = Z_STR_P(dim);
667 			goto str_index;
668 		case IS_UNDEF:
669 			/* The array may be destroyed while throwing the notice.
670 			 * Temporarily increase the refcount to detect this situation. */
671 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
672 				GC_ADDREF(ht);
673 			}
674 			zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
675 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
676 				zend_array_destroy(ht);
677 				return 0;
678 			}
679 			if (EG(exception)) {
680 				return 0;
681 			}
682 			ZEND_FALLTHROUGH;
683 		case IS_NULL:
684 			offset_key = ZSTR_EMPTY_ALLOC();
685 			goto str_index;
686 		case IS_DOUBLE:
687 			hval = zend_dval_to_lval(Z_DVAL_P(dim));
688 			if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
689 				/* The array may be destroyed while throwing the notice.
690 				 * Temporarily increase the refcount to detect this situation. */
691 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
692 					GC_ADDREF(ht);
693 				}
694 				zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
695 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
696 					zend_array_destroy(ht);
697 					return 0;
698 				}
699 				if (EG(exception)) {
700 					return 0;
701 				}
702 			}
703 			goto num_index;
704 		case IS_RESOURCE:
705 			/* The array may be destroyed while throwing the notice.
706 			 * Temporarily increase the refcount to detect this situation. */
707 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
708 				GC_ADDREF(ht);
709 			}
710 			zend_use_resource_as_offset(dim);
711 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
712 				zend_array_destroy(ht);
713 				return 0;
714 			}
715 			if (EG(exception)) {
716 				return 0;
717 			}
718 			hval = Z_RES_HANDLE_P(dim);
719 			goto num_index;
720 		case IS_FALSE:
721 			hval = 0;
722 			goto num_index;
723 		case IS_TRUE:
724 			hval = 1;
725 			goto num_index;
726 		default:
727 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), dim, BP_VAR_IS);
728 			return 0;
729 	}
730 
731 str_index:
732 	if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
733 		goto num_index;
734 	}
735 	retval = zend_hash_find(ht, offset_key);
736 	if (!retval) {
737 		return 0;
738 	}
739 	if (UNEXPECTED(Z_TYPE_P(retval) == IS_REFERENCE)) {
740 		retval = Z_REFVAL_P(retval);
741 	}
742 	return Z_TYPE_P(retval) > IS_NULL;
743 
744 num_index:
745 	ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
746 	if (UNEXPECTED(Z_TYPE_P(retval) == IS_REFERENCE)) {
747 		retval = Z_REFVAL_P(retval);
748 	}
749 	return (Z_TYPE_P(retval) > IS_NULL);
750 
751 num_undef:
752 	return 0;
753 }
754 
zend_jit_fetch_dim_rw_helper(zend_array * ht,zval * dim)755 static zval* ZEND_FASTCALL zend_jit_fetch_dim_rw_helper(zend_array *ht, zval *dim)
756 {
757 	zend_ulong hval;
758 	zend_string *offset_key;
759 	zval *retval;
760 	zend_execute_data *execute_data;
761 	const zend_op *opline;
762 
763 	if (Z_TYPE_P(dim) == IS_REFERENCE) {
764 		dim = Z_REFVAL_P(dim);
765 	}
766 
767 	switch (Z_TYPE_P(dim)) {
768 		case IS_LONG:
769 			hval = Z_LVAL_P(dim);
770 			goto num_index;
771 		case IS_STRING:
772 			offset_key = Z_STR_P(dim);
773 			goto str_index;
774 		case IS_UNDEF:
775 			execute_data = EG(current_execute_data);
776 			opline = EX(opline);
777 			if (UNEXPECTED(opline->opcode == ZEND_HANDLE_EXCEPTION)) {
778 				opline = EG(opline_before_exception);
779 			}
780 			if (opline && !zend_jit_undefined_op_helper_write(ht, opline->op2.var)) {
781 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
782 					if (EG(exception)) {
783 						ZVAL_UNDEF(EX_VAR(opline->result.var));
784 					} else {
785 						ZVAL_NULL(EX_VAR(opline->result.var));
786 					}
787 				}
788 				return NULL;
789 			}
790 			ZEND_FALLTHROUGH;
791 		case IS_NULL:
792 			offset_key = ZSTR_EMPTY_ALLOC();
793 			goto str_index;
794 		case IS_DOUBLE:
795 			hval = zend_dval_to_lval(Z_DVAL_P(dim));
796 			if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
797 				/* The array may be destroyed while throwing the notice.
798 				 * Temporarily increase the refcount to detect this situation. */
799 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
800 					GC_ADDREF(ht);
801 				}
802 				execute_data = EG(current_execute_data);
803 				opline = EX(opline);
804 				zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
805 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
806 					if (!GC_REFCOUNT(ht)) {
807 						zend_array_destroy(ht);
808 					}
809 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
810 						if (EG(exception)) {
811 							ZVAL_UNDEF(EX_VAR(opline->result.var));
812 						} else {
813 							ZVAL_NULL(EX_VAR(opline->result.var));
814 						}
815 					}
816 					return NULL;
817 				}
818 				if (EG(exception)) {
819 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
820 						ZVAL_UNDEF(EX_VAR(opline->result.var));
821 					}
822 					return NULL;
823 				}
824 			}
825 			goto num_index;
826 		case IS_RESOURCE:
827 			/* The array may be destroyed while throwing the notice.
828 			 * Temporarily increase the refcount to detect this situation. */
829 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
830 				GC_ADDREF(ht);
831 			}
832 			execute_data = EG(current_execute_data);
833 			opline = EX(opline);
834 			zend_use_resource_as_offset(dim);
835 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
836 				if (!GC_REFCOUNT(ht)) {
837 					zend_array_destroy(ht);
838 				}
839 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
840 					if (EG(exception)) {
841 						ZVAL_UNDEF(EX_VAR(opline->result.var));
842 					} else {
843 						ZVAL_NULL(EX_VAR(opline->result.var));
844 					}
845 				}
846 				return NULL;
847 			}
848 			if (EG(exception)) {
849 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
850 					ZVAL_UNDEF(EX_VAR(opline->result.var));
851 				}
852 				return NULL;
853 			}
854 			hval = Z_RES_HANDLE_P(dim);
855 			goto num_index;
856 		case IS_FALSE:
857 			hval = 0;
858 			goto num_index;
859 		case IS_TRUE:
860 			hval = 1;
861 			goto num_index;
862 		default:
863 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), dim, BP_VAR_RW);
864 			undef_result_after_exception();
865 			return NULL;
866 	}
867 
868 str_index:
869 	if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
870 		goto num_index;
871 	}
872 	retval = zend_hash_find(ht, offset_key);
873 	if (!retval) {
874 		/* Key may be released while throwing the undefined index warning. */
875 		retval = zend_undefined_index_write(ht, offset_key);
876 	}
877 	return retval;
878 
879 num_index:
880 	ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
881 	return retval;
882 
883 num_undef:
884 	return zend_undefined_offset_write(ht, hval);
885 }
886 
zend_jit_fetch_dim_w_helper(zend_array * ht,zval * dim)887 static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim)
888 {
889 	zend_ulong hval;
890 	zend_string *offset_key;
891 	zval *retval;
892 	zend_execute_data *execute_data;
893 	const zend_op *opline;
894 
895 	if (Z_TYPE_P(dim) == IS_REFERENCE) {
896 		dim = Z_REFVAL_P(dim);
897 	}
898 
899 	switch (Z_TYPE_P(dim)) {
900 		case IS_LONG:
901 			hval = Z_LVAL_P(dim);
902 			goto num_index;
903 		case IS_STRING:
904 			offset_key = Z_STR_P(dim);
905 			goto str_index;
906 		case IS_UNDEF:
907 			execute_data = EG(current_execute_data);
908 			opline = EX(opline);
909 			if (!zend_jit_undefined_op_helper_write(ht, opline->op2.var)) {
910 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
911 					if (EG(exception)) {
912 						ZVAL_UNDEF(EX_VAR(opline->result.var));
913 					} else {
914 						ZVAL_NULL(EX_VAR(opline->result.var));
915 					}
916 				}
917 				if (opline->opcode == ZEND_ASSIGN_DIM
918 				 && ((opline+1)->op1_type & (IS_VAR | IS_TMP_VAR))) {
919 					zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
920 				}
921 				return NULL;
922 			}
923 			ZEND_FALLTHROUGH;
924 		case IS_NULL:
925 			offset_key = ZSTR_EMPTY_ALLOC();
926 			goto str_index;
927 		case IS_DOUBLE:
928 			hval = zend_dval_to_lval(Z_DVAL_P(dim));
929 			if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
930 				/* The array may be destroyed while throwing the notice.
931 				 * Temporarily increase the refcount to detect this situation. */
932 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
933 					GC_ADDREF(ht);
934 				}
935 				execute_data = EG(current_execute_data);
936 				opline = EX(opline);
937 				zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
938 				if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
939 					if (!GC_REFCOUNT(ht)) {
940 						zend_array_destroy(ht);
941 					}
942 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
943 						if (EG(exception)) {
944 							ZVAL_UNDEF(EX_VAR(opline->result.var));
945 						} else {
946 							ZVAL_NULL(EX_VAR(opline->result.var));
947 						}
948 					}
949 					return NULL;
950 				}
951 				if (EG(exception)) {
952 					if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
953 						ZVAL_UNDEF(EX_VAR(opline->result.var));
954 					}
955 					return NULL;
956 				}
957 			}
958 			goto num_index;
959 		case IS_RESOURCE:
960 			/* The array may be destroyed while throwing the notice.
961 			 * Temporarily increase the refcount to detect this situation. */
962 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
963 				GC_ADDREF(ht);
964 			}
965 			execute_data = EG(current_execute_data);
966 			opline = EX(opline);
967 			zend_use_resource_as_offset(dim);
968 			if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
969 				if (!GC_REFCOUNT(ht)) {
970 					zend_array_destroy(ht);
971 				}
972 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
973 					if (EG(exception)) {
974 						ZVAL_UNDEF(EX_VAR(opline->result.var));
975 					} else {
976 						ZVAL_NULL(EX_VAR(opline->result.var));
977 					}
978 				}
979 				return NULL;
980 			}
981 			if (EG(exception)) {
982 				if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
983 					ZVAL_UNDEF(EX_VAR(opline->result.var));
984 				}
985 				return NULL;
986 			}
987 			hval = Z_RES_HANDLE_P(dim);
988 			goto num_index;
989 		case IS_FALSE:
990 			hval = 0;
991 			goto num_index;
992 		case IS_TRUE:
993 			hval = 1;
994 			goto num_index;
995 		default:
996 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), dim, BP_VAR_R);
997 			undef_result_after_exception();
998 			if (EG(opline_before_exception)
999 			 && (EG(opline_before_exception)+1)->opcode == ZEND_OP_DATA
1000 			 && ((EG(opline_before_exception)+1)->op1_type & (IS_VAR|IS_TMP_VAR))) {
1001 				zend_execute_data *execute_data = EG(current_execute_data);
1002 
1003 				zval_ptr_dtor_nogc(EX_VAR((EG(opline_before_exception)+1)->op1.var));
1004 			}
1005 			return NULL;
1006 	}
1007 
1008 str_index:
1009 	if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
1010 		goto num_index;
1011 	}
1012 	return zend_hash_lookup(ht, offset_key);
1013 
1014 num_index:
1015 	ZEND_HASH_INDEX_LOOKUP(ht, hval, retval);
1016 	return retval;
1017 }
1018 
1019 /* type is one of the BP_VAR_* constants */
zend_check_string_offset(zval * dim,int type)1020 static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type)
1021 {
1022 	zend_long offset;
1023 
1024 try_again:
1025 	switch(Z_TYPE_P(dim)) {
1026 		case IS_LONG:
1027 			return Z_LVAL_P(dim);
1028 		case IS_STRING:
1029 		{
1030 			bool trailing_data = false;
1031 			/* For BC reasons we allow errors so that we can warn on leading numeric string */
1032 			if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL,
1033 					/* allow errors */ true, NULL, &trailing_data)) {
1034 				if (UNEXPECTED(trailing_data)
1035 				 && EG(current_execute_data)->opline->opcode != ZEND_FETCH_DIM_UNSET) {
1036 					zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
1037 				}
1038 				return offset;
1039 			}
1040 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_STRING), dim, BP_VAR_R);
1041 			return 0;
1042 		}
1043 		case IS_UNDEF:
1044 			zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1045 			ZEND_FALLTHROUGH;
1046 		case IS_DOUBLE:
1047 		case IS_NULL:
1048 		case IS_FALSE:
1049 		case IS_TRUE:
1050 			zend_error(E_WARNING, "String offset cast occurred");
1051 			break;
1052 		case IS_REFERENCE:
1053 			dim = Z_REFVAL_P(dim);
1054 			goto try_again;
1055 		default:
1056 			zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_STRING), dim, type);
1057 			return 0;
1058 	}
1059 
1060 	return zval_get_long_func(dim, /* is_strict */ false);
1061 }
1062 
zend_jit_fetch_dim_str_offset(zend_string * str,zend_long offset)1063 static zend_always_inline zend_string* zend_jit_fetch_dim_str_offset(zend_string *str, zend_long offset)
1064 {
1065 	if (UNEXPECTED((zend_ulong)offset >= (zend_ulong)ZSTR_LEN(str))) {
1066 		if (EXPECTED(offset < 0)) {
1067 			/* Handle negative offset */
1068 			zend_long real_offset = (zend_long)ZSTR_LEN(str) + offset;
1069 
1070 			if (EXPECTED(real_offset >= 0)) {
1071 				return ZSTR_CHAR((uint8_t)ZSTR_VAL(str)[real_offset]);
1072 			}
1073 		}
1074 		zend_error(E_WARNING, "Uninitialized string offset " ZEND_LONG_FMT, offset);
1075 		return ZSTR_EMPTY_ALLOC();
1076 	} else {
1077 		return ZSTR_CHAR((uint8_t)ZSTR_VAL(str)[offset]);
1078 	}
1079 }
1080 
zend_jit_fetch_dim_str_offset_r_helper(zend_string * str,zend_long offset)1081 static zend_string* ZEND_FASTCALL zend_jit_fetch_dim_str_offset_r_helper(zend_string *str, zend_long offset)
1082 {
1083 	return zend_jit_fetch_dim_str_offset(str, offset);
1084 }
1085 
zend_jit_fetch_dim_str_r_helper(zend_string * str,zval * dim)1086 static zend_string* ZEND_FASTCALL zend_jit_fetch_dim_str_r_helper(zend_string *str, zval *dim)
1087 {
1088 	zend_long offset;
1089 
1090 	if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1091 		if (!(GC_FLAGS(str) & IS_STR_INTERNED)) {
1092 			GC_ADDREF(str);
1093 		}
1094 		offset = zend_check_string_offset(dim, BP_VAR_R);
1095 		if (!(GC_FLAGS(str) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(str) == 0)) {
1096 			zend_string *ret = zend_jit_fetch_dim_str_offset(str, offset);
1097 			zend_string_efree(str);
1098 			return ret;
1099 		}
1100 	} else {
1101 		offset = Z_LVAL_P(dim);
1102 	}
1103 	if (UNEXPECTED(EG(exception) != NULL)) {
1104 		return ZSTR_EMPTY_ALLOC();
1105 	}
1106 	return zend_jit_fetch_dim_str_offset(str, offset);
1107 }
1108 
zend_jit_fetch_dim_str_is_helper(zend_string * str,zval * dim,zval * result)1109 static void ZEND_FASTCALL zend_jit_fetch_dim_str_is_helper(zend_string *str, zval *dim, zval *result)
1110 {
1111 	zend_long offset;
1112 
1113 try_string_offset:
1114 	if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1115 		switch (Z_TYPE_P(dim)) {
1116 			/* case IS_LONG: */
1117 			case IS_STRING:
1118 				if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
1119 					break;
1120 				}
1121 				ZVAL_NULL(result);
1122 				return;
1123 			case IS_UNDEF:
1124 				zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1125 			case IS_DOUBLE:
1126 			case IS_NULL:
1127 			case IS_FALSE:
1128 			case IS_TRUE:
1129 				break;
1130 			case IS_REFERENCE:
1131 				dim = Z_REFVAL_P(dim);
1132 				goto try_string_offset;
1133 			default:
1134 				zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_STRING), dim,
1135 					EG(current_execute_data)->opline->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ ?
1136 						BP_VAR_IS : BP_VAR_RW);
1137 				ZVAL_NULL(result);
1138 				return;
1139 		}
1140 
1141 		offset = zval_get_long_func(dim, /* is_strict */ false);
1142 	} else {
1143 		offset = Z_LVAL_P(dim);
1144 	}
1145 
1146 	if ((zend_ulong)offset >= (zend_ulong)ZSTR_LEN(str)) {
1147 		if (offset < 0) {
1148 			/* Handle negative offset */
1149 			zend_long real_offset = (zend_long)ZSTR_LEN(str) + offset;
1150 
1151 			if (real_offset >= 0) {
1152 				ZVAL_CHAR(result, (uint8_t)ZSTR_VAL(str)[real_offset]);
1153 				return;
1154 			}
1155 		}
1156 		ZVAL_NULL(result);
1157 	} else {
1158 		ZVAL_CHAR(result, (uint8_t)ZSTR_VAL(str)[offset]);
1159 	}
1160 }
1161 
zend_jit_fetch_dim_obj_r_helper(zval * container,zval * dim,zval * result)1162 static void ZEND_FASTCALL zend_jit_fetch_dim_obj_r_helper(zval *container, zval *dim, zval *result)
1163 {
1164 	zval *retval;
1165 	zend_object *obj = Z_OBJ_P(container);
1166 
1167 	GC_ADDREF(obj);
1168 	if (UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
1169 		zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1170 		dim = &EG(uninitialized_zval);
1171 	}
1172 
1173 	retval = obj->handlers->read_dimension(obj, dim, BP_VAR_R, result);
1174 
1175 	if (retval) {
1176 		if (result != retval) {
1177 			ZVAL_COPY_DEREF(result, retval);
1178 		} else if (UNEXPECTED(Z_ISREF_P(retval))) {
1179 			zend_unwrap_reference(retval);
1180 		}
1181 	} else {
1182 		ZVAL_NULL(result);
1183 	}
1184 	if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1185 		zend_objects_store_del(obj);
1186 	}
1187 }
1188 
zend_jit_fetch_dim_obj_is_helper(zval * container,zval * dim,zval * result)1189 static void ZEND_FASTCALL zend_jit_fetch_dim_obj_is_helper(zval *container, zval *dim, zval *result)
1190 {
1191 	zval *retval;
1192 	zend_object *obj = Z_OBJ_P(container);
1193 
1194 	GC_ADDREF(obj);
1195 	if (UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
1196 		zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1197 		dim = &EG(uninitialized_zval);
1198 	}
1199 
1200 	retval = obj->handlers->read_dimension(obj, dim, BP_VAR_IS, result);
1201 
1202 	if (retval) {
1203 		if (result != retval) {
1204 			ZVAL_COPY_DEREF(result, retval);
1205 		} else if (UNEXPECTED(Z_ISREF_P(retval))) {
1206 			zend_unwrap_reference(result);
1207 		}
1208 	} else {
1209 		ZVAL_NULL(result);
1210 	}
1211 	if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1212 		zend_objects_store_del(obj);
1213 	}
1214 }
1215 
zend_assign_to_string_offset(zval * str,zval * dim,zval * value,zval * result)1216 static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result)
1217 {
1218 	uint8_t c;
1219 	size_t string_len;
1220 	zend_long offset;
1221 	zend_string *s;
1222 
1223 	/* separate string */
1224 	if (Z_REFCOUNTED_P(str) && Z_REFCOUNT_P(str) == 1) {
1225 		s = Z_STR_P(str);
1226 	} else {
1227 		s = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
1228 		ZSTR_H(s) = ZSTR_H(Z_STR_P(str));
1229 		if (Z_REFCOUNTED_P(str)) {
1230 			GC_DELREF(Z_STR_P(str));
1231 		}
1232 		ZVAL_NEW_STR(str, s);
1233 	}
1234 
1235 	if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1236 		/* The string may be destroyed while throwing the notice.
1237 		 * Temporarily increase the refcount to detect this situation. */
1238 		GC_ADDREF(s);
1239 		offset = zend_check_string_offset(dim, BP_VAR_W);
1240 		if (UNEXPECTED(GC_DELREF(s) == 0)) {
1241 			zend_string_efree(s);
1242 			if (result) {
1243 				ZVAL_NULL(result);
1244 			}
1245 			return;
1246 		}
1247 		if (UNEXPECTED(EG(exception) != NULL)) {
1248 			if (UNEXPECTED(result)) {
1249 				ZVAL_UNDEF(result);
1250 			}
1251 			return;
1252 		}
1253 	} else {
1254 		offset = Z_LVAL_P(dim);
1255 	}
1256 	if (offset < -(zend_long)ZSTR_LEN(s)) {
1257 		/* Error on negative offset */
1258 		zend_error(E_WARNING, "Illegal string offset " ZEND_LONG_FMT, offset);
1259 		if (result) {
1260 			ZVAL_NULL(result);
1261 		}
1262 		return;
1263 	}
1264 
1265 	if (Z_TYPE_P(value) != IS_STRING) {
1266 		zend_string *tmp;
1267 
1268 		/* The string may be destroyed while throwing the notice.
1269 		 * Temporarily increase the refcount to detect this situation. */
1270 		GC_ADDREF(s);
1271 
1272 		if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
1273 			const zend_op *op_data = EG(current_execute_data)->opline + 1;
1274 			ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
1275 			zend_jit_undefined_op_helper(op_data->op1.var);
1276 			value = &EG(uninitialized_zval);
1277 		}
1278 
1279 		/* Convert to string, just the time to pick the 1st byte */
1280 		tmp = zval_try_get_string_func(value);
1281 
1282 		if (UNEXPECTED(GC_DELREF(s) == 0)) {
1283 			zend_string_efree(s);
1284 			if (tmp) {
1285 				zend_string_release_ex(tmp, 0);
1286 			}
1287 			if (result) {
1288 				ZVAL_NULL(result);
1289 			}
1290 			return;
1291 		}
1292 		if (UNEXPECTED(!tmp)) {
1293 			if (result) {
1294 				ZVAL_UNDEF(result);
1295 			}
1296 			return;
1297 		}
1298 
1299 		if (UNEXPECTED(!tmp)) {
1300 			if (result) {
1301 				ZVAL_UNDEF(result);
1302 			}
1303 			return;
1304 		}
1305 
1306 		string_len = ZSTR_LEN(tmp);
1307 		c = (uint8_t)ZSTR_VAL(tmp)[0];
1308 		zend_string_release(tmp);
1309 	} else {
1310 		string_len = Z_STRLEN_P(value);
1311 		c = (uint8_t)Z_STRVAL_P(value)[0];
1312 	}
1313 
1314 
1315 	if (string_len != 1) {
1316 		if (string_len == 0) {
1317 			/* Error on empty input string */
1318 			zend_throw_error(NULL, "Cannot assign an empty string to a string offset");
1319 			if (result) {
1320 				ZVAL_NULL(result);
1321 			}
1322 			return;
1323 		}
1324 
1325 		/* The string may be destroyed while throwing the notice.
1326 		 * Temporarily increase the refcount to detect this situation. */
1327 		GC_ADDREF(s);
1328 		zend_error(E_WARNING, "Only the first byte will be assigned to the string offset");
1329 		if (UNEXPECTED(GC_DELREF(s) == 0)) {
1330 			zend_string_efree(s);
1331 			if (result) {
1332 				ZVAL_NULL(result);
1333 			}
1334 			return;
1335 		}
1336 		/* Illegal offset assignment */
1337 		if (UNEXPECTED(EG(exception) != NULL)) {
1338 			if (result) {
1339 				ZVAL_UNDEF(result);
1340 			}
1341 			return;
1342 		}
1343 	}
1344 
1345 	if (offset < 0) { /* Handle negative offset */
1346 		offset += (zend_long)ZSTR_LEN(s);
1347 	}
1348 
1349 	if ((size_t)offset >= ZSTR_LEN(s)) {
1350 		/* Extend string if needed */
1351 		zend_long old_len = ZSTR_LEN(s);
1352 		ZVAL_NEW_STR(str, zend_string_extend(s, (size_t)offset + 1, 0));
1353 		memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len);
1354 		Z_STRVAL_P(str)[offset+1] = 0;
1355 	} else {
1356 		zend_string_forget_hash_val(Z_STR_P(str));
1357 	}
1358 
1359 	Z_STRVAL_P(str)[offset] = c;
1360 
1361 	if (result) {
1362 		/* Return the new character */
1363 		ZVAL_CHAR(result, c);
1364 	}
1365 }
1366 
zend_jit_fetch_dim_obj_helper(zval * object_ptr,zval * dim,zval * result,int type)1367 static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval *object_ptr, zval *dim, zval *result, int type)
1368 {
1369 	zval *retval;
1370 
1371 	if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
1372 		zend_object *obj = Z_OBJ_P(object_ptr);
1373 
1374 		GC_ADDREF(obj);
1375 		if (dim && UNEXPECTED(Z_ISUNDEF_P(dim))) {
1376 			const zend_op *opline = EG(current_execute_data)->opline;
1377 			zend_jit_undefined_op_helper(opline->op2.var);
1378 			dim = &EG(uninitialized_zval);
1379 		}
1380 
1381 		retval = obj->handlers->read_dimension(obj, dim, type, result);
1382 		if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
1383 			zend_class_entry *ce = obj->ce;
1384 
1385 			ZVAL_NULL(result);
1386 			zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
1387 		} else if (EXPECTED(retval && Z_TYPE_P(retval) != IS_UNDEF)) {
1388 			if (!Z_ISREF_P(retval)) {
1389 				if (result != retval) {
1390 					ZVAL_COPY(result, retval);
1391 					retval = result;
1392 				}
1393 				if (Z_TYPE_P(retval) != IS_OBJECT) {
1394 					zend_class_entry *ce = obj->ce;
1395 					zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
1396 				}
1397 			} else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
1398 				ZVAL_UNREF(retval);
1399 			}
1400 			if (result != retval) {
1401 				ZVAL_INDIRECT(result, retval);
1402 			}
1403 		} else {
1404 			ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
1405 			ZVAL_UNDEF(result);
1406 		}
1407 		if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1408 			zend_objects_store_del(obj);
1409 		}
1410 	} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
1411 		if (!dim) {
1412 			zend_throw_error(NULL, "[] operator not supported for strings");
1413 		} else {
1414 			if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1415 				zend_check_string_offset(dim, BP_VAR_RW);
1416 			}
1417 			zend_wrong_string_offset_error();
1418 		}
1419 		ZVAL_UNDEF(result);
1420 	} else if (Z_TYPE_P(object_ptr) == IS_FALSE) {
1421 		zend_array *arr = zend_new_array(0);
1422 		ZVAL_ARR(object_ptr, arr);
1423 		GC_ADDREF(arr);
1424 		zend_false_to_array_deprecated();
1425 		if (UNEXPECTED(GC_DELREF(arr) == 0)) {
1426 			zend_array_destroy(arr);
1427 			ZVAL_NULL(result);
1428 			return;
1429 		}
1430 		SEPARATE_ARRAY(object_ptr);
1431 		arr = Z_ARRVAL_P(object_ptr);
1432 		zval *var;
1433 		if (dim) {
1434 			if (type == BP_VAR_W) {
1435 				var = zend_jit_fetch_dim_w_helper(arr, dim);
1436 			} else {
1437 				ZEND_ASSERT(type == BP_VAR_RW);
1438 				var = zend_jit_fetch_dim_rw_helper(arr, dim);
1439 			}
1440 		} else {
1441 			var = zend_hash_next_index_insert_new(arr, &EG(uninitialized_zval));
1442 		}
1443 		if (var) {
1444 			ZVAL_INDIRECT(result, var);
1445 		} else {
1446 			ZVAL_UNDEF(result);
1447 		}
1448 	} else {
1449 		if (type == BP_VAR_UNSET) {
1450 			zend_throw_error(NULL, "Cannot unset offset in a non-array variable");
1451 			ZVAL_UNDEF(result);
1452 		} else {
1453 			zend_throw_error(NULL, "Cannot use a scalar value as an array");
1454 			ZVAL_UNDEF(result);
1455 		}
1456 	}
1457 }
1458 
zend_jit_fetch_dim_obj_w_helper(zval * object_ptr,zval * dim,zval * result)1459 static void ZEND_FASTCALL zend_jit_fetch_dim_obj_w_helper(zval *object_ptr, zval *dim, zval *result)
1460 {
1461 	zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_W);
1462 }
1463 
zend_jit_fetch_dim_obj_rw_helper(zval * object_ptr,zval * dim,zval * result)1464 static void ZEND_FASTCALL zend_jit_fetch_dim_obj_rw_helper(zval *object_ptr, zval *dim, zval *result)
1465 {
1466 	zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_RW);
1467 }
1468 
1469 //static void ZEND_FASTCALL zend_jit_fetch_dim_obj_unset_helper(zval *object_ptr, zval *dim, zval *result)
1470 //{
1471 //	zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_UNSET);
1472 //}
1473 
zend_jit_assign_dim_helper(zval * object_ptr,zval * dim,zval * value,zval * result)1474 static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim, zval *value, zval *result)
1475 {
1476 	if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
1477 		zend_object *obj = Z_OBJ_P(object_ptr);
1478 
1479 		GC_ADDREF(obj);
1480 		if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
1481 			const zend_op *opline = EG(current_execute_data)->opline;
1482 			zend_jit_undefined_op_helper(opline->op2.var);
1483 			dim = &EG(uninitialized_zval);
1484 		}
1485 
1486 		if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
1487 			const zend_op *op_data = EG(current_execute_data)->opline + 1;
1488 			ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
1489 			zend_jit_undefined_op_helper(op_data->op1.var);
1490 			value = &EG(uninitialized_zval);
1491 		} else {
1492 			ZVAL_DEREF(value);
1493 		}
1494 
1495 		obj->handlers->write_dimension(obj, dim, value);
1496 		if (result) {
1497 			if (EXPECTED(!EG(exception))) {
1498 				ZVAL_COPY(result, value);
1499 			} else {
1500 				ZVAL_UNDEF(result);
1501 			}
1502 		}
1503 		if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1504 			zend_objects_store_del(obj);
1505 		}
1506 		return;
1507 	} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) && EXPECTED(dim != NULL)) {
1508 		zend_assign_to_string_offset(object_ptr, dim, value, result);
1509 		return;
1510 	}
1511 
1512 	if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
1513 		const zend_op *op_data = EG(current_execute_data)->opline + 1;
1514 		ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
1515 		zend_jit_undefined_op_helper(op_data->op1.var);
1516 		value = &EG(uninitialized_zval);
1517 	}
1518 
1519 	if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
1520 		zend_throw_error(NULL, "[] operator not supported for strings");
1521 		if (result) {
1522 			ZVAL_UNDEF(result);
1523 		}
1524 	} else if (Z_TYPE_P(object_ptr) == IS_FALSE) {
1525 		zend_array *arr = zend_new_array(0);
1526 		ZVAL_ARR(object_ptr, arr);
1527 		GC_ADDREF(arr);
1528 		zend_false_to_array_deprecated();
1529 		if (UNEXPECTED(GC_DELREF(arr) == 0)) {
1530 			zend_array_destroy(arr);
1531 			if (result) {
1532 				ZVAL_NULL(result);
1533 			}
1534 			return;
1535 		}
1536 		SEPARATE_ARRAY(object_ptr);
1537 		arr = Z_ARRVAL_P(object_ptr);
1538 		zval *var = dim
1539 			? zend_jit_fetch_dim_w_helper(arr, dim)
1540 			: zend_hash_next_index_insert_new(arr, &EG(uninitialized_zval));
1541 		if (!var) {
1542 			if (result) {
1543 				ZVAL_UNDEF(result);
1544 			}
1545 			return;
1546 		}
1547 
1548 		ZVAL_COPY_DEREF(var, value);
1549 		if (result) {
1550 			ZVAL_COPY(result, var);
1551 		}
1552 	} else {
1553 		if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
1554 			const zend_op *opline = EG(current_execute_data)->opline;
1555 			zend_jit_undefined_op_helper(opline->op2.var);
1556 			dim = &EG(uninitialized_zval);
1557 		}
1558 		zend_throw_error(NULL, "Cannot use a scalar value as an array");
1559 		if (result) {
1560 			ZVAL_UNDEF(result);
1561 		}
1562 	}
1563 }
1564 
zend_jit_assign_dim_op_helper(zval * container,zval * dim,zval * value,binary_op_type binary_op)1565 static void ZEND_FASTCALL zend_jit_assign_dim_op_helper(zval *container, zval *dim, zval *value, binary_op_type binary_op)
1566 {
1567 	if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
1568 		zend_object *obj = Z_OBJ_P(container);
1569 		zval *z;
1570 		zval rv, res;
1571 
1572 		GC_ADDREF(obj);
1573 		if (dim && UNEXPECTED(Z_ISUNDEF_P(dim))) {
1574 			const zend_op *opline = EG(current_execute_data)->opline;
1575 			zend_jit_undefined_op_helper(opline->op2.var);
1576 			dim = &EG(uninitialized_zval);
1577 		}
1578 
1579 		z = obj->handlers->read_dimension(obj, dim, BP_VAR_R, &rv);
1580 		if (z != NULL) {
1581 
1582 			if (binary_op(&res, Z_ISREF_P(z) ? Z_REFVAL_P(z) : z, value) == SUCCESS) {
1583 				obj->handlers->write_dimension(obj, dim, &res);
1584 			}
1585 			if (z == &rv) {
1586 				zval_ptr_dtor(&rv);
1587 			}
1588 			zval_ptr_dtor(&res);
1589 		} else {
1590 			/* Exception is thrown in this case */
1591 			GC_DELREF(obj);
1592 			return;
1593 		}
1594 		if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1595 			zend_objects_store_del(obj);
1596 //???		if (retval) {
1597 //???			ZVAL_NULL(retval);
1598 //???		}
1599 		}
1600 	} else if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
1601 		if (!dim) {
1602 			zend_throw_error(NULL, "[] operator not supported for strings");
1603 		} else {
1604 			if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1605 				zend_check_string_offset(dim, BP_VAR_RW);
1606 			}
1607 			zend_wrong_string_offset_error();
1608 		}
1609 	} else if (Z_TYPE_P(container) == IS_FALSE) {
1610 		zend_array *arr = zend_new_array(0);
1611 		ZVAL_ARR(container, arr);
1612 		GC_ADDREF(arr);
1613 		zend_false_to_array_deprecated();
1614 		if (UNEXPECTED(GC_DELREF(arr) == 0)) {
1615 			zend_array_destroy(arr);
1616 			return;
1617 		}
1618 		SEPARATE_ARRAY(container);
1619 		arr = Z_ARRVAL_P(container);
1620 		zval *var = dim
1621 			? zend_jit_fetch_dim_rw_helper(arr, dim)
1622 			: zend_hash_next_index_insert_new(arr, &EG(uninitialized_zval));
1623 		if (var) {
1624 			binary_op(var, var, value);
1625 		}
1626 	} else {
1627 		zend_throw_error(NULL, "Cannot use a scalar value as an array");
1628 	}
1629 }
1630 
zend_jit_fast_assign_concat_helper(zval * op1,zval * op2)1631 static void ZEND_FASTCALL zend_jit_fast_assign_concat_helper(zval *op1, zval *op2)
1632 {
1633 	size_t op1_len = Z_STRLEN_P(op1);
1634 	size_t op2_len = Z_STRLEN_P(op2);
1635 	size_t result_len = op1_len + op2_len;
1636 	zend_string *result_str;
1637 	uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES_BOTH(Z_STR_P(op1), Z_STR_P(op2));
1638 
1639 	if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
1640 		zend_throw_error(NULL, "String size overflow");
1641 		return;
1642 	}
1643 
1644 	do {
1645 		if (Z_REFCOUNTED_P(op1)) {
1646 			if (GC_REFCOUNT(Z_STR_P(op1)) == 1) {
1647 				result_str = perealloc(Z_STR_P(op1), ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(result_len)), 0);
1648 				ZSTR_LEN(result_str) = result_len;
1649 				zend_string_forget_hash_val(result_str);
1650 				if (UNEXPECTED(Z_STR_P(op1) == Z_STR_P(op2))) {
1651 					ZVAL_NEW_STR(op2, result_str);
1652 				}
1653 				break;
1654 			}
1655 			GC_DELREF(Z_STR_P(op1));
1656 		}
1657 		result_str = zend_string_alloc(result_len, 0);
1658 		memcpy(ZSTR_VAL(result_str), Z_STRVAL_P(op1), op1_len);
1659 	} while(0);
1660 
1661 	GC_ADD_FLAGS(result_str, flags);
1662 	ZVAL_NEW_STR(op1, result_str);
1663 	memcpy(ZSTR_VAL(result_str) + op1_len, Z_STRVAL_P(op2), op2_len);
1664 	ZSTR_VAL(result_str)[result_len] = '\0';
1665 }
1666 
zend_jit_fast_concat_helper(zval * result,zval * op1,zval * op2)1667 static void ZEND_FASTCALL zend_jit_fast_concat_helper(zval *result, zval *op1, zval *op2)
1668 {
1669 	size_t op1_len = Z_STRLEN_P(op1);
1670 	size_t op2_len = Z_STRLEN_P(op2);
1671 	size_t result_len = op1_len + op2_len;
1672 	zend_string *result_str;
1673 	uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES_BOTH(Z_STR_P(op1), Z_STR_P(op2));
1674 
1675 	if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
1676 		zend_throw_error(NULL, "String size overflow");
1677 		return;
1678 	}
1679 
1680 	result_str = zend_string_alloc(result_len, 0);
1681 	GC_ADD_FLAGS(result_str, flags);
1682 	memcpy(ZSTR_VAL(result_str), Z_STRVAL_P(op1), op1_len);
1683 
1684 	ZVAL_NEW_STR(result, result_str);
1685 
1686 	memcpy(ZSTR_VAL(result_str) + op1_len, Z_STRVAL_P(op2), op2_len);
1687 	ZSTR_VAL(result_str)[result_len] = '\0';
1688 }
1689 
zend_jit_fast_concat_tmp_helper(zval * result,zval * op1,zval * op2)1690 static void ZEND_FASTCALL zend_jit_fast_concat_tmp_helper(zval *result, zval *op1, zval *op2)
1691 {
1692 	zend_string *op1_str = Z_STR_P(op1);
1693 	size_t op1_len = ZSTR_LEN(op1_str);
1694 	size_t op2_len = Z_STRLEN_P(op2);
1695 	size_t result_len = op1_len + op2_len;
1696 	zend_string *result_str;
1697 	uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES_BOTH(Z_STR_P(op1), Z_STR_P(op2));
1698 
1699 	if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
1700 		zend_throw_error(NULL, "String size overflow");
1701 		return;
1702 	}
1703 
1704 	do {
1705 		if (!ZSTR_IS_INTERNED(op1_str)) {
1706 			if (GC_REFCOUNT(op1_str) == 1) {
1707 				Z_STR_P(op1) = result_str =
1708 					perealloc(op1_str, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(result_len)), 0);
1709 				ZSTR_LEN(result_str) = result_len;
1710 				zend_string_forget_hash_val(result_str);
1711 				break;
1712 			}
1713 			GC_DELREF(op1_str);
1714 		}
1715 		result_str = zend_string_alloc(result_len, 0);
1716 		memcpy(ZSTR_VAL(result_str), ZSTR_VAL(op1_str), op1_len);
1717 	} while (0);
1718 
1719 	GC_ADD_FLAGS(result_str, flags);
1720 	ZVAL_NEW_STR(result, result_str);
1721 
1722 	memcpy(ZSTR_VAL(result_str) + op1_len, Z_STRVAL_P(op2), op2_len);
1723 	ZSTR_VAL(result_str)[result_len] = '\0';
1724 }
1725 
zend_jit_isset_dim_helper(zval * container,zval * offset)1726 static int ZEND_FASTCALL zend_jit_isset_dim_helper(zval *container, zval *offset)
1727 {
1728 	if (UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) {
1729 		zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1730 		offset = &EG(uninitialized_zval);
1731 	}
1732 
1733 	if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
1734 		return Z_OBJ_HT_P(container)->has_dimension(Z_OBJ_P(container), offset, 0);
1735 	} else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */
1736 		zend_long lval;
1737 
1738 		if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
1739 			lval = Z_LVAL_P(offset);
1740 isset_str_offset:
1741 			if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
1742 				lval += (zend_long)Z_STRLEN_P(container);
1743 			}
1744 			if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
1745 				return 1;
1746 			}
1747 		} else {
1748 			ZVAL_DEREF(offset);
1749 			if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */
1750 					|| (Z_TYPE_P(offset) == IS_STRING /* or numeric string */
1751 						&& IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, false))) {
1752 				lval = zval_get_long_ex(offset, /* is_strict */ true);
1753 				goto isset_str_offset;
1754 			}
1755 		}
1756 	}
1757 	return 0;
1758 }
1759 
zend_jit_free_call_frame(zend_execute_data * call)1760 static void ZEND_FASTCALL zend_jit_free_call_frame(zend_execute_data *call)
1761 {
1762 	zend_vm_stack_free_call_frame(call);
1763 }
1764 
zend_jit_fetch_global_helper(zend_string * varname,void ** cache_slot)1765 static zend_reference* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_string *varname, void **cache_slot)
1766 {
1767 	zval *value;
1768 	uintptr_t idx;
1769 	zend_reference *ref;
1770 
1771 	/* We store "hash slot index" + 1 (NULL is a mark of uninitialized cache slot) */
1772 	idx = (uintptr_t)CACHED_PTR_EX(cache_slot) - 1;
1773 	if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket))) {
1774 		Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
1775 
1776 		if (EXPECTED(p->key == varname) ||
1777 	        (EXPECTED(p->h == ZSTR_H(varname)) &&
1778 	         EXPECTED(p->key != NULL) &&
1779 	         EXPECTED(zend_string_equal_content(p->key, varname)))) {
1780 
1781 			value = (zval*)p; /* value = &p->val; */
1782 			goto check_indirect;
1783 		}
1784 	}
1785 
1786 	value = zend_hash_find_known_hash(&EG(symbol_table), varname);
1787 	if (UNEXPECTED(value == NULL)) {
1788 		value = zend_hash_add_new(&EG(symbol_table), varname, &EG(uninitialized_zval));
1789 		idx = (char*)value - (char*)EG(symbol_table).arData;
1790 		/* Store "hash slot index" + 1 (NULL is a mark of uninitialized cache slot) */
1791 		CACHE_PTR_EX(cache_slot, (void*)(idx + 1));
1792 	} else {
1793 		idx = (char*)value - (char*)EG(symbol_table).arData;
1794 		/* Store "hash slot index" + 1 (NULL is a mark of uninitialized cache slot) */
1795 		CACHE_PTR_EX(cache_slot, (void*)(idx + 1));
1796 check_indirect:
1797 		/* GLOBAL variable may be an INDIRECT pointer to CV */
1798 		if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) {
1799 			value = Z_INDIRECT_P(value);
1800 			if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
1801 				ZVAL_NULL(value);
1802 			}
1803 		}
1804 	}
1805 
1806 	if (UNEXPECTED(!Z_ISREF_P(value))) {
1807 		ZVAL_MAKE_REF_EX(value, 2);
1808 		ref = Z_REF_P(value);
1809 	} else {
1810 		ref = Z_REF_P(value);
1811 		GC_ADDREF(ref);
1812 	}
1813 
1814 	return ref;
1815 }
1816 
zend_jit_verify_arg_slow(zval * arg,zend_arg_info * arg_info)1817 static bool ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_arg_info *arg_info)
1818 {
1819 	zend_execute_data *execute_data = EG(current_execute_data);
1820 	const zend_op *opline = EX(opline);
1821 	void **cache_slot = CACHE_ADDR(opline->extended_value);
1822 	bool ret = zend_check_user_type_slow(
1823 		&arg_info->type, arg, /* ref */ NULL, cache_slot, /* is_return_type */ false);
1824 	if (UNEXPECTED(!ret)) {
1825 		zend_verify_arg_error(EX(func), arg_info, opline->op1.num, arg);
1826 		return 0;
1827 	}
1828 	return ret;
1829 }
1830 
zend_jit_verify_return_slow(zval * arg,const zend_op_array * op_array,zend_arg_info * arg_info,void ** cache_slot)1831 static void ZEND_FASTCALL zend_jit_verify_return_slow(zval *arg, const zend_op_array *op_array, zend_arg_info *arg_info, void **cache_slot)
1832 {
1833 	if (UNEXPECTED(!zend_check_user_type_slow(
1834 			&arg_info->type, arg, /* ref */ NULL, cache_slot, /* is_return_type */ true))) {
1835 		zend_verify_return_error((zend_function*)op_array, arg);
1836 	}
1837 }
1838 
zend_jit_fetch_obj_r_slow(zend_object * zobj)1839 static void ZEND_FASTCALL zend_jit_fetch_obj_r_slow(zend_object *zobj)
1840 {
1841 	zval *retval;
1842 	zend_execute_data *execute_data = EG(current_execute_data);
1843 	const zend_op *opline = EX(opline);
1844 	zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1845 	zval *result = EX_VAR(opline->result.var);
1846 	void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1847 
1848 	retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, result);
1849 	if (retval != result) {
1850 		ZVAL_COPY_DEREF(result, retval);
1851 	} else if (UNEXPECTED(Z_ISREF_P(retval))) {
1852 		zend_unwrap_reference(retval);
1853 	}
1854 }
1855 
zend_jit_fetch_obj_r_dynamic(zend_object * zobj,intptr_t prop_offset)1856 static void ZEND_FASTCALL zend_jit_fetch_obj_r_dynamic(zend_object *zobj, intptr_t prop_offset)
1857 {
1858 	if (zobj->properties) {
1859 		zval *retval;
1860 		zend_execute_data *execute_data = EG(current_execute_data);
1861 		const zend_op *opline = EX(opline);
1862 		zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1863 		zval *result = EX_VAR(opline->result.var);
1864 		void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1865 
1866 		if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
1867 			intptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);
1868 
1869 			if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
1870 				Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
1871 
1872 				if (EXPECTED(p->key == name) ||
1873 			        (EXPECTED(p->h == ZSTR_H(name)) &&
1874 			         EXPECTED(p->key != NULL) &&
1875 			         EXPECTED(zend_string_equal_content(p->key, name)))) {
1876 					ZVAL_COPY_DEREF(result, &p->val);
1877 					return;
1878 				}
1879 			}
1880 			CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
1881 		}
1882 
1883 		retval = zend_hash_find_known_hash(zobj->properties, name);
1884 
1885 		if (EXPECTED(retval)) {
1886 			intptr_t idx = (char*)retval - (char*)zobj->properties->arData;
1887 			CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
1888 			ZVAL_COPY_DEREF(result, retval);
1889 			return;
1890 		}
1891 	}
1892 	zend_jit_fetch_obj_r_slow(zobj);
1893 }
1894 
zend_jit_fetch_obj_is_slow(zend_object * zobj)1895 static void ZEND_FASTCALL zend_jit_fetch_obj_is_slow(zend_object *zobj)
1896 {
1897 	zval *retval;
1898 	zend_execute_data *execute_data = EG(current_execute_data);
1899 	const zend_op *opline = EX(opline);
1900 	zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1901 	zval *result = EX_VAR(opline->result.var);
1902 	void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1903 
1904 	retval = zobj->handlers->read_property(zobj, name, BP_VAR_IS, cache_slot, result);
1905 	if (retval != result) {
1906 		ZVAL_COPY_DEREF(result, retval);
1907 	} else if (UNEXPECTED(Z_ISREF_P(retval))) {
1908 		zend_unwrap_reference(retval);
1909 	}
1910 }
1911 
zend_jit_fetch_obj_is_dynamic(zend_object * zobj,intptr_t prop_offset)1912 static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intptr_t prop_offset)
1913 {
1914 	if (zobj->properties) {
1915 		zval *retval;
1916 		zend_execute_data *execute_data = EG(current_execute_data);
1917 		const zend_op *opline = EX(opline);
1918 		zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1919 		zval *result = EX_VAR(opline->result.var);
1920 		void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1921 
1922 		if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
1923 			intptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);
1924 
1925 			if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
1926 				Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
1927 
1928 				if (EXPECTED(p->key == name) ||
1929 			        (EXPECTED(p->h == ZSTR_H(name)) &&
1930 			         EXPECTED(p->key != NULL) &&
1931 			         EXPECTED(zend_string_equal_content(p->key, name)))) {
1932 					ZVAL_COPY_DEREF(result, &p->val);
1933 					return;
1934 				}
1935 			}
1936 			CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
1937 		}
1938 
1939 		retval = zend_hash_find_known_hash(zobj->properties, name);
1940 
1941 		if (EXPECTED(retval)) {
1942 			intptr_t idx = (char*)retval - (char*)zobj->properties->arData;
1943 			CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
1944 			ZVAL_COPY_DEREF(result, retval);
1945 			return;
1946 		}
1947 	}
1948 	zend_jit_fetch_obj_is_slow(zobj);
1949 }
1950 
promotes_to_array(zval * val)1951 static zend_always_inline bool promotes_to_array(zval *val) {
1952 	return Z_TYPE_P(val) <= IS_FALSE
1953 		|| (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE);
1954 }
1955 
check_type_array_assignable(zend_type type)1956 static zend_always_inline bool check_type_array_assignable(zend_type type) {
1957 	if (!ZEND_TYPE_IS_SET(type)) {
1958 		return 1;
1959 	}
1960 	return (ZEND_TYPE_FULL_MASK(type) & MAY_BE_ARRAY) != 0;
1961 }
1962 
zend_object_fetch_property_type_info(zend_object * obj,zval * slot)1963 static zend_property_info *zend_object_fetch_property_type_info(
1964 		zend_object *obj, zval *slot)
1965 {
1966 	if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) {
1967 		return NULL;
1968 	}
1969 
1970 	/* Not a declared property */
1971 	if (UNEXPECTED(slot < obj->properties_table ||
1972 			slot >= obj->properties_table + obj->ce->default_properties_count)) {
1973 		return NULL;
1974 	}
1975 
1976 	return zend_get_typed_property_info_for_slot(obj, slot);
1977 }
1978 
zend_throw_auto_init_in_prop_error(zend_property_info * prop,const char * type)1979 static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) {
1980 	zend_string *type_str = zend_type_to_string(prop->type);
1981 	zend_type_error(
1982 		"Cannot auto-initialize an %s inside property %s::$%s of type %s",
1983 		type,
1984 		ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
1985 		ZSTR_VAL(type_str)
1986 	);
1987 	zend_string_release(type_str);
1988 }
1989 
zend_throw_access_uninit_prop_by_ref_error(zend_property_info * prop)1990 static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error(
1991 		zend_property_info *prop) {
1992 	zend_throw_error(NULL,
1993 		"Cannot access uninitialized non-nullable property %s::$%s by reference",
1994 		ZSTR_VAL(prop->ce->name),
1995 		zend_get_unmangled_property_name(prop->name));
1996 }
1997 
zend_handle_fetch_obj_flags(zval * result,zval * ptr,zend_object * obj,zend_property_info * prop_info,uint32_t flags)1998 static zend_never_inline bool zend_handle_fetch_obj_flags(
1999 		zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags)
2000 {
2001 	switch (flags) {
2002 		case ZEND_FETCH_DIM_WRITE:
2003 			if (promotes_to_array(ptr)) {
2004 				if (!prop_info) {
2005 					prop_info = zend_object_fetch_property_type_info(obj, ptr);
2006 					if (!prop_info) {
2007 						break;
2008 					}
2009 				}
2010 				if (!check_type_array_assignable(prop_info->type)) {
2011 					zend_throw_auto_init_in_prop_error(prop_info, "array");
2012 					if (result) ZVAL_ERROR(result);
2013 					return 0;
2014 				}
2015 			}
2016 			break;
2017 		case ZEND_FETCH_REF:
2018 			if (Z_TYPE_P(ptr) != IS_REFERENCE) {
2019 				if (!prop_info) {
2020 					prop_info = zend_object_fetch_property_type_info(obj, ptr);
2021 					if (!prop_info) {
2022 						break;
2023 					}
2024 				}
2025 				if (Z_TYPE_P(ptr) == IS_UNDEF) {
2026 					if (!ZEND_TYPE_ALLOW_NULL(prop_info->type)) {
2027 						zend_throw_access_uninit_prop_by_ref_error(prop_info);
2028 						if (result) ZVAL_ERROR(result);
2029 						return 0;
2030 					}
2031 					ZVAL_NULL(ptr);
2032 				}
2033 
2034 				ZVAL_NEW_REF(ptr, ptr);
2035 				ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(ptr), prop_info);
2036 			}
2037 			break;
2038 		EMPTY_SWITCH_DEFAULT_CASE()
2039 	}
2040 	return 1;
2041 }
2042 
zend_jit_fetch_obj_w_slow(zend_object * zobj)2043 static void ZEND_FASTCALL zend_jit_fetch_obj_w_slow(zend_object *zobj)
2044 {
2045 	zval *retval;
2046 	zend_execute_data *execute_data = EG(current_execute_data);
2047 	const zend_op *opline = EX(opline);
2048 	zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
2049 	zval *result = EX_VAR(opline->result.var);
2050 	void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
2051 
2052 	retval = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_W, cache_slot);
2053 	if (NULL == retval) {
2054 		retval = zobj->handlers->read_property(zobj, name, BP_VAR_W, cache_slot, result);
2055 		if (retval == result) {
2056 			if (UNEXPECTED(Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1)) {
2057 				ZVAL_UNREF(retval);
2058 			}
2059 			return;
2060 		}
2061 		if (UNEXPECTED(EG(exception))) {
2062 			ZVAL_ERROR(result);
2063 			return;
2064 		}
2065 	} else if (UNEXPECTED(Z_ISERROR_P(retval))) {
2066 		ZVAL_ERROR(result);
2067 		return;
2068 	}
2069 
2070 	ZVAL_INDIRECT(result, retval);
2071 
2072 	/* Support for typed properties */
2073 	do {
2074 		uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
2075 
2076 		if (flags) {
2077 			zend_property_info *prop_info = NULL;
2078 
2079 			if (opline->op2_type == IS_CONST) {
2080 				prop_info = CACHED_PTR_EX(cache_slot + 2);
2081 				if (!prop_info) {
2082 					break;
2083 				}
2084 			}
2085 			if (UNEXPECTED(!zend_handle_fetch_obj_flags(result, retval, zobj, prop_info, flags))) {
2086 				return;
2087 			}
2088 		}
2089 	} while (0);
2090 
2091 	if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
2092 		ZVAL_NULL(retval);
2093 	}
2094 }
2095 
zend_jit_check_array_promotion(zval * val,zend_property_info * prop)2096 static void ZEND_FASTCALL zend_jit_check_array_promotion(zval *val, zend_property_info *prop)
2097 {
2098 	zend_execute_data *execute_data = EG(current_execute_data);
2099 	const zend_op *opline = execute_data->opline;
2100 	zval *result = EX_VAR(opline->result.var);
2101 
2102 	if ((Z_TYPE_P(val) <= IS_FALSE
2103 		|| (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE))
2104 		&& ZEND_TYPE_IS_SET(prop->type)
2105 		&& (ZEND_TYPE_FULL_MASK(prop->type) & MAY_BE_ARRAY) == 0) {
2106 		zend_string *type_str = zend_type_to_string(prop->type);
2107 		zend_type_error(
2108 			"Cannot auto-initialize an array inside property %s::$%s of type %s",
2109 			ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
2110 			ZSTR_VAL(type_str)
2111 		);
2112 		zend_string_release(type_str);
2113 		ZVAL_ERROR(result);
2114 	} else {
2115 		ZVAL_INDIRECT(result, val);
2116 	}
2117 }
2118 
zend_jit_create_typed_ref(zval * val,zend_property_info * prop,zval * result)2119 static void ZEND_FASTCALL zend_jit_create_typed_ref(zval *val, zend_property_info *prop, zval *result)
2120 {
2121 	if (!Z_ISREF_P(val)) {
2122 		ZVAL_NEW_REF(val, val);
2123 		ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(val), prop);
2124 	}
2125 	ZVAL_INDIRECT(result, val);
2126 }
2127 
zend_jit_extract_helper(zend_refcounted * garbage)2128 static void ZEND_FASTCALL zend_jit_extract_helper(zend_refcounted *garbage)
2129 {
2130 	zend_execute_data *execute_data = EG(current_execute_data);
2131 	const zend_op *opline = execute_data->opline;
2132 	zval *zv = EX_VAR(opline->result.var);
2133 
2134 	if (EXPECTED(Z_TYPE_P(zv) == IS_INDIRECT)) {
2135 		ZVAL_COPY(zv, Z_INDIRECT_P(zv));
2136 	}
2137 	rc_dtor_func(garbage);
2138 }
2139 
zend_jit_vm_stack_free_args_helper(zend_execute_data * call)2140 static void ZEND_FASTCALL zend_jit_vm_stack_free_args_helper(zend_execute_data *call)
2141 {
2142 	zend_vm_stack_free_args(call);
2143 }
2144 
zend_jit_assign_to_typed_ref_helper(zend_reference * ref,zval * value,uint8_t value_type)2145 static zend_always_inline zval* zend_jit_assign_to_typed_ref_helper(zend_reference *ref, zval *value, uint8_t value_type)
2146 {
2147 	zval variable;
2148 
2149 	ZVAL_REF(&variable, ref);
2150 	return zend_assign_to_variable(&variable, value, value_type, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)));
2151 }
2152 
zend_jit_assign_const_to_typed_ref(zend_reference * ref,zval * value)2153 static zval* ZEND_FASTCALL zend_jit_assign_const_to_typed_ref(zend_reference *ref, zval *value)
2154 {
2155 	return zend_jit_assign_to_typed_ref_helper(ref, value, IS_CONST);
2156 }
2157 
zend_jit_assign_tmp_to_typed_ref(zend_reference * ref,zval * value)2158 static zval* ZEND_FASTCALL zend_jit_assign_tmp_to_typed_ref(zend_reference *ref, zval *value)
2159 {
2160 	return zend_jit_assign_to_typed_ref_helper(ref, value, IS_TMP_VAR);
2161 }
2162 
zend_jit_assign_var_to_typed_ref(zend_reference * ref,zval * value)2163 static zval* ZEND_FASTCALL zend_jit_assign_var_to_typed_ref(zend_reference *ref, zval *value)
2164 {
2165 	return zend_jit_assign_to_typed_ref_helper(ref, value, IS_VAR);
2166 }
2167 
zend_jit_assign_cv_to_typed_ref(zend_reference * ref,zval * value)2168 static zval* ZEND_FASTCALL zend_jit_assign_cv_to_typed_ref(zend_reference *ref, zval *value)
2169 {
2170 	if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2171 		const zend_op *opline = EG(current_execute_data)->opline;
2172 		uint32_t var;
2173 		if (opline->opcode == ZEND_ASSIGN) {
2174 			var = opline->op2.var;
2175 		} else {
2176 			ZEND_ASSERT((opline + 1)->opcode == ZEND_OP_DATA);
2177 			var = (opline + 1)->op1.var;
2178 		}
2179 		zend_jit_undefined_op_helper(var);
2180 		value = &EG(uninitialized_zval);
2181 	}
2182 	return zend_jit_assign_to_typed_ref_helper(ref, value, IS_CV);
2183 }
2184 
zend_jit_assign_to_typed_ref2_helper(zend_reference * ref,zval * value,zval * result,uint8_t value_type)2185 static zend_always_inline zval* zend_jit_assign_to_typed_ref2_helper(zend_reference *ref, zval *value, zval *result, uint8_t value_type)
2186 {
2187 	zval variable, *ret;
2188 	zend_refcounted *garbage = NULL;
2189 
2190 	ZVAL_REF(&variable, ref);
2191 	ret = zend_assign_to_variable_ex(&variable, value, value_type, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)), &garbage);
2192 	ZVAL_COPY(result, ret);
2193 	if (garbage) {
2194 		GC_DTOR(garbage);
2195 	}
2196 	return ret;
2197 }
2198 
zend_jit_assign_const_to_typed_ref2(zend_reference * ref,zval * value,zval * result)2199 static zval* ZEND_FASTCALL zend_jit_assign_const_to_typed_ref2(zend_reference *ref, zval *value, zval *result)
2200 {
2201 	return zend_jit_assign_to_typed_ref2_helper(ref, value, result, IS_CONST);
2202 }
2203 
zend_jit_assign_tmp_to_typed_ref2(zend_reference * ref,zval * value,zval * result)2204 static zval* ZEND_FASTCALL zend_jit_assign_tmp_to_typed_ref2(zend_reference *ref, zval *value, zval *result)
2205 {
2206 	return zend_jit_assign_to_typed_ref2_helper(ref, value, result, IS_TMP_VAR);
2207 }
2208 
zend_jit_assign_var_to_typed_ref2(zend_reference * ref,zval * value,zval * result)2209 static zval* ZEND_FASTCALL zend_jit_assign_var_to_typed_ref2(zend_reference *ref, zval *value, zval *result)
2210 {
2211 	return zend_jit_assign_to_typed_ref2_helper(ref, value, result, IS_VAR);
2212 }
2213 
zend_jit_assign_cv_to_typed_ref2(zend_reference * ref,zval * value,zval * result)2214 static zval* ZEND_FASTCALL zend_jit_assign_cv_to_typed_ref2(zend_reference *ref, zval *value, zval *result)
2215 {
2216 	if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2217 		const zend_op *opline = EG(current_execute_data)->opline;
2218 		uint32_t var;
2219 		if (opline->opcode == ZEND_ASSIGN) {
2220 			var = opline->op2.var;
2221 		} else {
2222 			ZEND_ASSERT((opline + 1)->opcode == ZEND_OP_DATA);
2223 			var = (opline + 1)->op1.var;
2224 		}
2225 		zend_jit_undefined_op_helper(var);
2226 		value = &EG(uninitialized_zval);
2227 	}
2228 	return zend_jit_assign_to_typed_ref2_helper(ref, value, result, IS_CV);
2229 }
2230 
zend_jit_get_prop_not_accepting_double(zend_reference * ref)2231 static zend_property_info *zend_jit_get_prop_not_accepting_double(zend_reference *ref)
2232 {
2233 	zend_property_info *prop;
2234 	ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) {
2235 		if (!(ZEND_TYPE_FULL_MASK(prop->type) & MAY_BE_DOUBLE)) {
2236 			return prop;
2237 		}
2238 	} ZEND_REF_FOREACH_TYPE_SOURCES_END();
2239 	return NULL;
2240 }
2241 
zend_jit_throw_inc_ref_error(zend_reference * ref,zend_property_info * error_prop)2242 static ZEND_COLD void zend_jit_throw_inc_ref_error(zend_reference *ref, zend_property_info *error_prop)
2243 {
2244 	zend_string *type_str = zend_type_to_string(error_prop->type);
2245 
2246 	zend_type_error(
2247 		"Cannot increment a reference held by property %s::$%s of type %s past its maximal value",
2248 		ZSTR_VAL(error_prop->ce->name),
2249 		zend_get_unmangled_property_name(error_prop->name),
2250 		ZSTR_VAL(type_str));
2251 	zend_string_release(type_str);
2252 }
2253 
zend_jit_throw_dec_ref_error(zend_reference * ref,zend_property_info * error_prop)2254 static ZEND_COLD void zend_jit_throw_dec_ref_error(zend_reference *ref, zend_property_info *error_prop)
2255 {
2256 	zend_string *type_str = zend_type_to_string(error_prop->type);
2257 
2258 	zend_type_error(
2259 		"Cannot decrement a reference held by property %s::$%s of type %s past its minimal value",
2260 		ZSTR_VAL(error_prop->ce->name),
2261 		zend_get_unmangled_property_name(error_prop->name),
2262 		ZSTR_VAL(type_str));
2263 	zend_string_release(type_str);
2264 }
2265 
zend_jit_pre_inc_typed_ref(zend_reference * ref,zval * ret)2266 static void ZEND_FASTCALL zend_jit_pre_inc_typed_ref(zend_reference *ref, zval *ret)
2267 {
2268 	zval *var_ptr = &ref->val;
2269 	zval tmp;
2270 
2271 	ZVAL_COPY(&tmp, var_ptr);
2272 
2273 	increment_function(var_ptr);
2274 
2275 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2276 		zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2277 		if (UNEXPECTED(error_prop)) {
2278 			zend_jit_throw_inc_ref_error(ref, error_prop);
2279 			ZVAL_LONG(var_ptr, ZEND_LONG_MAX);
2280 		}
2281 	} else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2282 		zval_ptr_dtor(var_ptr);
2283 		ZVAL_COPY_VALUE(var_ptr, &tmp);
2284 	} else {
2285 		zval_ptr_dtor(&tmp);
2286 	}
2287 	if (ret) {
2288 		ZVAL_COPY(ret, var_ptr);
2289 	}
2290 }
2291 
zend_jit_pre_dec_typed_ref(zend_reference * ref,zval * ret)2292 static void ZEND_FASTCALL zend_jit_pre_dec_typed_ref(zend_reference *ref, zval *ret)
2293 {
2294 	zval *var_ptr = &ref->val;
2295 	zval tmp;
2296 
2297 	ZVAL_COPY(&tmp, var_ptr);
2298 
2299 	decrement_function(var_ptr);
2300 
2301 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2302 		zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2303 		if (UNEXPECTED(error_prop)) {
2304 			zend_jit_throw_dec_ref_error(ref, error_prop);
2305 			ZVAL_LONG(var_ptr, ZEND_LONG_MIN);
2306 		}
2307 	} else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2308 		zval_ptr_dtor(var_ptr);
2309 		ZVAL_COPY_VALUE(var_ptr, &tmp);
2310 	} else {
2311 		zval_ptr_dtor(&tmp);
2312 	}
2313 	if (ret) {
2314 		ZVAL_COPY(ret, var_ptr);
2315 	}
2316 }
2317 
zend_jit_post_inc_typed_ref(zend_reference * ref,zval * ret)2318 static void ZEND_FASTCALL zend_jit_post_inc_typed_ref(zend_reference *ref, zval *ret)
2319 {
2320 	zval *var_ptr = &ref->val;
2321 	ZVAL_COPY(ret, var_ptr);
2322 
2323 	increment_function(var_ptr);
2324 
2325 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(ret) == IS_LONG) {
2326 		zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2327 		if (UNEXPECTED(error_prop)) {
2328 			zend_jit_throw_inc_ref_error(ref, error_prop);
2329 			ZVAL_LONG(var_ptr, ZEND_LONG_MAX);
2330 		}
2331 	} else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2332 		zval_ptr_dtor(var_ptr);
2333 		ZVAL_COPY_VALUE(var_ptr, ret);
2334 	}
2335 }
2336 
zend_jit_post_dec_typed_ref(zend_reference * ref,zval * ret)2337 static void ZEND_FASTCALL zend_jit_post_dec_typed_ref(zend_reference *ref, zval *ret)
2338 {
2339 	zval *var_ptr = &ref->val;
2340 	ZVAL_COPY(ret, var_ptr);
2341 
2342 	decrement_function(var_ptr);
2343 
2344 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(ret) == IS_LONG) {
2345 		zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2346 		if (UNEXPECTED(error_prop)) {
2347 			zend_jit_throw_dec_ref_error(ref, error_prop);
2348 			ZVAL_LONG(var_ptr, ZEND_LONG_MIN);
2349 		}
2350 	} else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2351 		zval_ptr_dtor(var_ptr);
2352 		ZVAL_COPY_VALUE(var_ptr, ret);
2353 	}
2354 }
2355 
zend_jit_assign_op_to_typed_ref(zend_reference * ref,zval * val,binary_op_type binary_op)2356 static void ZEND_FASTCALL zend_jit_assign_op_to_typed_ref(zend_reference *ref, zval *val, binary_op_type binary_op)
2357 {
2358 	zval z_copy;
2359 
2360 	/* Make sure that in-place concatenation is used if the LHS is a string. */
2361 	if (binary_op == concat_function && Z_TYPE(ref->val) == IS_STRING) {
2362 		concat_function(&ref->val, &ref->val, val);
2363 		ZEND_ASSERT(Z_TYPE(ref->val) == IS_STRING && "Concat should return string");
2364 		return;
2365 	}
2366 
2367 	binary_op(&z_copy, &ref->val, val);
2368 	if (EXPECTED(zend_verify_ref_assignable_zval(ref, &z_copy, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2369 		zval_ptr_dtor(&ref->val);
2370 		ZVAL_COPY_VALUE(&ref->val, &z_copy);
2371 	} else {
2372 		zval_ptr_dtor(&z_copy);
2373 	}
2374 }
2375 
zend_jit_assign_op_to_typed_ref_tmp(zend_reference * ref,zval * val,binary_op_type binary_op)2376 static void ZEND_FASTCALL zend_jit_assign_op_to_typed_ref_tmp(zend_reference *ref, zval *val, binary_op_type binary_op)
2377 {
2378 	zval z_copy;
2379 
2380 	binary_op(&z_copy, &ref->val, val);
2381 	if (EXPECTED(zend_verify_ref_assignable_zval(ref, &z_copy, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2382 		zval_ptr_dtor(&ref->val);
2383 		ZVAL_COPY_VALUE(&ref->val, &z_copy);
2384 	} else {
2385 		zval_ptr_dtor(&z_copy);
2386 	}
2387 	zval_ptr_dtor_nogc(val);
2388 }
2389 
zend_jit_only_vars_by_reference(zval * arg)2390 static void ZEND_FASTCALL zend_jit_only_vars_by_reference(zval *arg)
2391 {
2392 	ZVAL_NEW_REF(arg, arg);
2393 	zend_error(E_NOTICE, "Only variables should be passed by reference");
2394 }
2395 
zend_jit_invalid_array_access(zval * container)2396 static void ZEND_FASTCALL zend_jit_invalid_array_access(zval *container)
2397 {
2398 	zend_error(E_WARNING, "Trying to access array offset on %s", zend_zval_value_name(container));
2399 }
2400 
zend_jit_invalid_property_read(zval * container,const char * property_name)2401 static void ZEND_FASTCALL zend_jit_invalid_property_read(zval *container, const char *property_name)
2402 {
2403 	zend_error(E_WARNING, "Attempt to read property \"%s\" on %s", property_name, zend_zval_value_name(container));
2404 }
2405 
zend_jit_invalid_property_write(zval * container,const char * property_name)2406 static void ZEND_FASTCALL zend_jit_invalid_property_write(zval *container, const char *property_name)
2407 {
2408 	zend_throw_error(NULL,
2409 		"Attempt to modify property \"%s\" on %s",
2410 		property_name, zend_zval_value_name(container));
2411 }
2412 
zend_jit_invalid_property_incdec(zval * container,const char * property_name)2413 static void ZEND_FASTCALL zend_jit_invalid_property_incdec(zval *container, const char *property_name)
2414 {
2415 	zend_execute_data *execute_data = EG(current_execute_data);
2416 	const zend_op *opline = EX(opline);
2417 
2418 	if (Z_TYPE_P(container) == IS_UNDEF && opline->op1_type == IS_CV) {
2419 		zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
2420 
2421 		zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
2422 	}
2423 	if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
2424 		ZVAL_UNDEF(EX_VAR(opline->result.var));
2425 	}
2426 	zend_throw_error(NULL,
2427 		"Attempt to increment/decrement property \"%s\" on %s",
2428 		property_name, zend_zval_value_name(container));
2429 	if (opline->op1_type == IS_VAR) {
2430 		zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
2431 	}
2432 }
2433 
zend_jit_invalid_property_assign(zval * container,const char * property_name)2434 static void ZEND_FASTCALL zend_jit_invalid_property_assign(zval *container, const char *property_name)
2435 {
2436 	zend_throw_error(NULL,
2437 		"Attempt to assign property \"%s\" on %s",
2438 		property_name, zend_zval_value_name(container));
2439 }
2440 
zend_jit_invalid_property_assign_op(zval * container,const char * property_name)2441 static void ZEND_FASTCALL zend_jit_invalid_property_assign_op(zval *container, const char *property_name)
2442 {
2443 	if (Z_TYPE_P(container) == IS_UNDEF) {
2444 		const zend_execute_data *execute_data = EG(current_execute_data);
2445 
2446 		zend_jit_undefined_op_helper(EX(opline)->op1.var);
2447 	}
2448 	zend_jit_invalid_property_assign(container, property_name);
2449 }
2450 
zend_jit_prepare_assign_dim_ref(zval * ref)2451 static zval * ZEND_FASTCALL zend_jit_prepare_assign_dim_ref(zval *ref) {
2452 	zval *val = Z_REFVAL_P(ref);
2453 	if (Z_TYPE_P(val) <= IS_FALSE) {
2454 		if (ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(ref))
2455 				&& !zend_verify_ref_array_assignable(Z_REF_P(ref))) {
2456 			return NULL;
2457 		}
2458 		if (Z_TYPE_P(val) == IS_FALSE) {
2459 			ZVAL_ARR(val, zend_new_array(8));
2460 			zend_false_to_array_deprecated();
2461 			if (EG(exception)) {
2462 				return NULL;
2463 			}
2464 		} else {
2465 			ZVAL_ARR(val, zend_new_array(8));
2466 		}
2467 	}
2468 	return val;
2469 }
2470 
zend_jit_pre_inc(zval * var_ptr,zval * ret)2471 static void ZEND_FASTCALL zend_jit_pre_inc(zval *var_ptr, zval *ret)
2472 {
2473 	increment_function(var_ptr);
2474 	ZVAL_COPY(ret, var_ptr);
2475 }
2476 
zend_jit_pre_dec(zval * var_ptr,zval * ret)2477 static void ZEND_FASTCALL zend_jit_pre_dec(zval *var_ptr, zval *ret)
2478 {
2479 	decrement_function(var_ptr);
2480 	ZVAL_COPY(ret, var_ptr);
2481 }
2482 
2483 #define HT_POISONED_PTR ((HashTable *) (intptr_t) -1)
2484 
_zend_hash_iterators_remove(HashTable * ht)2485 static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(HashTable *ht)
2486 {
2487 	HashTableIterator *iter = EG(ht_iterators);
2488 	HashTableIterator *end  = iter + EG(ht_iterators_used);
2489 
2490 	while (iter != end) {
2491 		if (iter->ht == ht) {
2492 			iter->ht = HT_POISONED_PTR;
2493 		}
2494 		iter++;
2495 	}
2496 }
2497 
zend_jit_array_free(HashTable * ht)2498 static void ZEND_FASTCALL zend_jit_array_free(HashTable *ht)
2499 {
2500 	GC_REMOVE_FROM_BUFFER(ht);
2501 	if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
2502 		_zend_hash_iterators_remove(ht);
2503 	}
2504 	if (!(EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED))) {
2505 		efree(HT_GET_DATA_ADDR(ht));
2506 	}
2507 	FREE_HASHTABLE(ht);
2508 }
2509 
zend_jit_zval_array_dup(zval * arr)2510 static HashTable *ZEND_FASTCALL zend_jit_zval_array_dup(zval *arr)
2511 {
2512 	HashTable *ht;
2513 
2514 	Z_TRY_DELREF_P(arr);
2515 	ht = Z_ARRVAL_P(arr);
2516 	ht = zend_array_dup(ht);
2517 	ZVAL_ARR(arr, ht);
2518 	return ht;
2519 }
2520 
zend_jit_add_arrays_helper(zend_array * op1,zend_array * op2)2521 static zend_array *ZEND_FASTCALL zend_jit_add_arrays_helper(zend_array *op1, zend_array *op2)
2522 {
2523 	zend_array *res;
2524 	res = zend_array_dup(op1);
2525 	zend_hash_merge(res, op2, zval_add_ref, 0);
2526 	return res;
2527 }
2528 
zend_jit_assign_obj_helper(zend_object * zobj,zend_string * name,zval * value,void ** cache_slot,zval * result)2529 static void ZEND_FASTCALL zend_jit_assign_obj_helper(zend_object *zobj, zend_string *name, zval *value, void **cache_slot, zval *result)
2530 {
2531 	if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2532 		const zend_op *op_data = EG(current_execute_data)->opline + 1;
2533 		ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
2534 		zend_jit_undefined_op_helper(op_data->op1.var);
2535 		value = &EG(uninitialized_zval);
2536 	}
2537 
2538 	ZVAL_DEREF(value);
2539 	value = zobj->handlers->write_property(zobj, name, value, cache_slot);
2540 	if (result && value) {
2541 		ZVAL_COPY_DEREF(result, value);
2542 	}
2543 }
2544 
zend_jit_assign_to_typed_prop(zval * property_val,zend_property_info * info,zval * value,zval * result)2545 static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend_property_info *info, zval *value, zval *result)
2546 {
2547 	zend_execute_data *execute_data = EG(current_execute_data);
2548 	zend_refcounted *garbage = NULL;
2549 	zval tmp;
2550 
2551 	if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2552 		const zend_op *op_data = execute_data->opline + 1;
2553 		ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
2554 		zend_jit_undefined_op_helper(op_data->op1.var);
2555 		value = &EG(uninitialized_zval);
2556 	}
2557 
2558 	if (UNEXPECTED((info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE))) {
2559 		zend_readonly_property_modification_error(info);
2560 		if (result) {
2561 			ZVAL_UNDEF(result);
2562 		}
2563 		return;
2564 	}
2565 
2566 	ZVAL_DEREF(value);
2567 	ZVAL_COPY(&tmp, value);
2568 
2569 	if (UNEXPECTED(!zend_verify_property_type(info, &tmp, EX_USES_STRICT_TYPES()))) {
2570 		zval_ptr_dtor(&tmp);
2571 		if (result) {
2572 			ZVAL_NULL(result);
2573 		}
2574 		return;
2575 	}
2576 
2577 	Z_PROP_FLAG_P(property_val) &= ~IS_PROP_REINITABLE;
2578 
2579 	value = zend_assign_to_variable_ex(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES(), &garbage);
2580 	if (result) {
2581 		ZVAL_COPY_DEREF(result, value);
2582 	}
2583 	if (garbage) {
2584 		GC_DTOR(garbage);
2585 	}
2586 }
2587 
_zend_jit_assign_op_overloaded_property(zend_object * object,zend_string * name,void ** cache_slot,zval * value,binary_op_type binary_op)2588 static zend_never_inline void _zend_jit_assign_op_overloaded_property(zend_object *object, zend_string *name, void **cache_slot, zval *value, binary_op_type binary_op)
2589 {
2590 	zval *z;
2591 	zval rv, res;
2592 
2593 	GC_ADDREF(object);
2594 	z = object->handlers->read_property(object, name, BP_VAR_R, cache_slot, &rv);
2595 	if (UNEXPECTED(EG(exception))) {
2596 		OBJ_RELEASE(object);
2597 //???		if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2598 //???			ZVAL_UNDEF(EX_VAR(opline->result.var));
2599 //???		}
2600 		return;
2601 	}
2602 	if (binary_op(&res, z, value) == SUCCESS) {
2603 		object->handlers->write_property(object, name, &res, cache_slot);
2604 	}
2605 //???	if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2606 //???		ZVAL_COPY(EX_VAR(opline->result.var), &res);
2607 //???	}
2608 	if (z == &rv) {
2609 		zval_ptr_dtor(z);
2610 	}
2611 	zval_ptr_dtor(&res);
2612 	OBJ_RELEASE(object);
2613 }
2614 
zend_jit_assign_op_to_typed_prop(zval * zptr,zend_property_info * prop_info,zval * value,binary_op_type binary_op)2615 static void ZEND_FASTCALL zend_jit_assign_op_to_typed_prop(zval *zptr, zend_property_info *prop_info, zval *value, binary_op_type binary_op)
2616 {
2617 	zend_execute_data *execute_data = EG(current_execute_data);
2618 	zval z_copy;
2619 
2620 	if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(zptr) & IS_PROP_REINITABLE))) {
2621 		zend_readonly_property_modification_error(prop_info);
2622 		return;
2623 	}
2624 
2625 	ZVAL_DEREF(zptr);
2626 	/* Make sure that in-place concatenation is used if the LHS is a string. */
2627 	if (binary_op == concat_function && Z_TYPE_P(zptr) == IS_STRING) {
2628 		concat_function(zptr, zptr, value);
2629 		ZEND_ASSERT(Z_TYPE_P(zptr) == IS_STRING && "Concat should return string");
2630 		return;
2631 	}
2632 
2633 	binary_op(&z_copy, zptr, value);
2634 	if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) {
2635 		Z_PROP_FLAG_P(zptr) &= ~IS_PROP_REINITABLE;
2636 		zval_ptr_dtor(zptr);
2637 		ZVAL_COPY_VALUE(zptr, &z_copy);
2638 	} else {
2639 		zval_ptr_dtor(&z_copy);
2640 	}
2641 }
2642 
zend_jit_assign_obj_op_helper(zend_object * zobj,zend_string * name,zval * value,void ** cache_slot,binary_op_type binary_op)2643 static void ZEND_FASTCALL zend_jit_assign_obj_op_helper(zend_object *zobj, zend_string *name, zval *value, void **cache_slot, binary_op_type binary_op)
2644 {
2645 	zval *zptr;
2646 	zend_property_info *prop_info;
2647 
2648 	if (EXPECTED((zptr = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2649 		if (UNEXPECTED(Z_ISERROR_P(zptr))) {
2650 //???			if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2651 //???				ZVAL_NULL(EX_VAR(opline->result.var));
2652 //???			}
2653 		} else {
2654 //???			zval *orig_zptr = zptr;
2655 			zend_reference *ref;
2656 
2657 			do {
2658 				if (UNEXPECTED(Z_ISREF_P(zptr))) {
2659 					ref = Z_REF_P(zptr);
2660 					zptr = Z_REFVAL_P(zptr);
2661 					if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
2662 						zend_jit_assign_op_to_typed_ref(ref, value, binary_op);
2663 						break;
2664 					}
2665 				}
2666 
2667 //???				if (OP2_TYPE == IS_CONST) {
2668 				prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
2669 //???				} else {
2670 //???					prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), orig_zptr);
2671 //???				}
2672 				if (UNEXPECTED(prop_info)) {
2673 					/* special case for typed properties */
2674 					zend_jit_assign_op_to_typed_prop(zptr, prop_info, value, binary_op);
2675 				} else {
2676 					binary_op(zptr, zptr, value);
2677 				}
2678 			} while (0);
2679 
2680 //???			if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2681 //???				ZVAL_COPY(EX_VAR(opline->result.var), zptr);
2682 //???			}
2683 		}
2684 	} else {
2685 		_zend_jit_assign_op_overloaded_property(zobj, name, cache_slot, value, binary_op);
2686 	}
2687 }
2688 
_zend_jit_throw_inc_prop_error(zend_property_info * prop)2689 static ZEND_COLD zend_long _zend_jit_throw_inc_prop_error(zend_property_info *prop)
2690 {
2691 	zend_string *type_str = zend_type_to_string(prop->type);
2692 	zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value",
2693 		ZSTR_VAL(prop->ce->name),
2694 		zend_get_unmangled_property_name(prop->name),
2695 		ZSTR_VAL(type_str));
2696 	zend_string_release(type_str);
2697 	return ZEND_LONG_MAX;
2698 }
2699 
_zend_jit_throw_dec_prop_error(zend_property_info * prop)2700 static ZEND_COLD zend_long _zend_jit_throw_dec_prop_error(zend_property_info *prop)
2701 {
2702 	zend_string *type_str = zend_type_to_string(prop->type);
2703 	zend_type_error("Cannot decrement property %s::$%s of type %s past its minimal value",
2704 		ZSTR_VAL(prop->ce->name),
2705 		zend_get_unmangled_property_name(prop->name),
2706 		ZSTR_VAL(type_str));
2707 	zend_string_release(type_str);
2708 	return ZEND_LONG_MIN;
2709 }
2710 
zend_jit_inc_typed_prop(zval * var_ptr,zend_property_info * prop_info)2711 static void ZEND_FASTCALL zend_jit_inc_typed_prop(zval *var_ptr, zend_property_info *prop_info)
2712 {
2713 	ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2714 
2715 	if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
2716 		zend_readonly_property_modification_error(prop_info);
2717 		return;
2718 	}
2719 
2720 	zend_execute_data *execute_data = EG(current_execute_data);
2721 	zval tmp;
2722 
2723 	ZVAL_DEREF(var_ptr);
2724 	ZVAL_COPY(&tmp, var_ptr);
2725 
2726 	increment_function(var_ptr);
2727 
2728 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2729 		if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2730 			zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2731 			ZVAL_LONG(var_ptr, val);
2732 		} else {
2733 			Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2734 		}
2735 	} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2736 		zval_ptr_dtor(var_ptr);
2737 		ZVAL_COPY_VALUE(var_ptr, &tmp);
2738 	} else {
2739 		Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2740 		zval_ptr_dtor(&tmp);
2741 	}
2742 }
2743 
zend_jit_dec_typed_prop(zval * var_ptr,zend_property_info * prop_info)2744 static void ZEND_FASTCALL zend_jit_dec_typed_prop(zval *var_ptr, zend_property_info *prop_info)
2745 {
2746 	ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2747 
2748 	if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
2749 		zend_readonly_property_modification_error(prop_info);
2750 		return;
2751 	}
2752 
2753 	zend_execute_data *execute_data = EG(current_execute_data);
2754 	zval tmp;
2755 
2756 	ZVAL_DEREF(var_ptr);
2757 	ZVAL_COPY(&tmp, var_ptr);
2758 
2759 	decrement_function(var_ptr);
2760 
2761 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2762 		if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2763 			zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
2764 			ZVAL_LONG(var_ptr, val);
2765 		} else {
2766 			Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2767 		}
2768 	} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2769 		zval_ptr_dtor(var_ptr);
2770 		ZVAL_COPY_VALUE(var_ptr, &tmp);
2771 	} else {
2772 		Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2773 		zval_ptr_dtor(&tmp);
2774 	}
2775 }
2776 
zend_jit_pre_inc_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2777 static void ZEND_FASTCALL zend_jit_pre_inc_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2778 {
2779 	ZVAL_DEREF(var_ptr);
2780 	zend_jit_inc_typed_prop(var_ptr, prop_info);
2781 	ZVAL_COPY(result, var_ptr);
2782 }
2783 
zend_jit_pre_dec_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2784 static void ZEND_FASTCALL zend_jit_pre_dec_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2785 {
2786 	ZVAL_DEREF(var_ptr);
2787 	zend_jit_dec_typed_prop(var_ptr, prop_info);
2788 	ZVAL_COPY(result, var_ptr);
2789 }
2790 
zend_jit_post_inc_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2791 static void ZEND_FASTCALL zend_jit_post_inc_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2792 {
2793 	ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2794 
2795 	if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
2796 		zend_readonly_property_modification_error(prop_info);
2797 		if (result) {
2798 			ZVAL_UNDEF(result);
2799 		}
2800 		return;
2801 	}
2802 
2803 	zend_execute_data *execute_data = EG(current_execute_data);
2804 
2805 	ZVAL_DEREF(var_ptr);
2806 	ZVAL_COPY(result, var_ptr);
2807 
2808 	increment_function(var_ptr);
2809 
2810 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(result) == IS_LONG) {
2811 		if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2812 			zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2813 			ZVAL_LONG(var_ptr, val);
2814 		} else {
2815 			Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2816 		}
2817 	} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2818 		zval_ptr_dtor(var_ptr);
2819 		ZVAL_COPY_VALUE(var_ptr, result);
2820 		ZVAL_UNDEF(result);
2821 	} else {
2822 		Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2823 	}
2824 }
2825 
zend_jit_post_dec_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2826 static void ZEND_FASTCALL zend_jit_post_dec_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2827 {
2828 	ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2829 
2830 	if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
2831 		zend_readonly_property_modification_error(prop_info);
2832 		if (result) {
2833 			ZVAL_UNDEF(result);
2834 		}
2835 		return;
2836 	}
2837 
2838 	zend_execute_data *execute_data = EG(current_execute_data);
2839 
2840 	ZVAL_DEREF(var_ptr);
2841 	ZVAL_COPY(result, var_ptr);
2842 
2843 	decrement_function(var_ptr);
2844 
2845 	if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(result) == IS_LONG) {
2846 		if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2847 			zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
2848 			ZVAL_LONG(var_ptr, val);
2849 		} else {
2850 			Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2851 		}
2852 	} else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2853 		zval_ptr_dtor(var_ptr);
2854 		ZVAL_COPY_VALUE(var_ptr, result);
2855 		ZVAL_UNDEF(result);
2856 	} else {
2857 		Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_REINITABLE;
2858 	}
2859 }
2860 
zend_jit_pre_inc_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)2861 static void ZEND_FASTCALL zend_jit_pre_inc_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
2862 {
2863 	zval *prop;
2864 
2865 	if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2866 		if (UNEXPECTED(Z_ISERROR_P(prop))) {
2867 			if (UNEXPECTED(result)) {
2868 				ZVAL_NULL(result);
2869 			}
2870 		} else {
2871 			zend_property_info *prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2);
2872 
2873 			if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
2874 				fast_long_increment_function(prop);
2875 				if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
2876 						&& !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2877 					zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2878 					ZVAL_LONG(prop, val);
2879 				}
2880 			} else {
2881 				do {
2882 					if (Z_ISREF_P(prop)) {
2883 						zend_reference *ref = Z_REF_P(prop);
2884 						prop = Z_REFVAL_P(prop);
2885 						if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
2886 							zend_jit_pre_inc_typed_ref(ref, result);
2887 							break;
2888 						}
2889 					}
2890 
2891 					if (UNEXPECTED(prop_info)) {
2892 						zend_jit_inc_typed_prop(prop, prop_info);
2893 					} else {
2894 						increment_function(prop);
2895 					}
2896 				} while (0);
2897 			}
2898 			if (UNEXPECTED(result)) {
2899 				ZVAL_COPY(result, prop);
2900 			}
2901 		}
2902 	} else {
2903 		zval rv;
2904 		zval *z;
2905 		zval z_copy;
2906 
2907 		GC_ADDREF(zobj);
2908 		z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
2909 		if (UNEXPECTED(EG(exception))) {
2910 			OBJ_RELEASE(zobj);
2911 			if (UNEXPECTED(result)) {
2912 				ZVAL_NULL(result);
2913 			}
2914 			return;
2915 		}
2916 
2917 		ZVAL_COPY_DEREF(&z_copy, z);
2918 		increment_function(&z_copy);
2919 		if (UNEXPECTED(result)) {
2920 			ZVAL_COPY(result, &z_copy);
2921 		}
2922 		zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
2923 		OBJ_RELEASE(zobj);
2924 		zval_ptr_dtor(&z_copy);
2925 		if (z == &rv) {
2926 			zval_ptr_dtor(z);
2927 		}
2928 	}
2929 }
2930 
zend_jit_pre_dec_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)2931 static void ZEND_FASTCALL zend_jit_pre_dec_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
2932 {
2933 	zval *prop;
2934 
2935 	if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2936 		if (UNEXPECTED(Z_ISERROR_P(prop))) {
2937 			if (UNEXPECTED(result)) {
2938 				ZVAL_NULL(result);
2939 			}
2940 		} else {
2941 			zend_property_info *prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2);
2942 
2943 			if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
2944 				fast_long_decrement_function(prop);
2945 				if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
2946 						&& !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2947 					zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
2948 					ZVAL_LONG(prop, val);
2949 				}
2950 			} else {
2951 				do {
2952 					if (Z_ISREF_P(prop)) {
2953 						zend_reference *ref = Z_REF_P(prop);
2954 						prop = Z_REFVAL_P(prop);
2955 						if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
2956 							zend_jit_pre_dec_typed_ref(ref, result);
2957 							break;
2958 						}
2959 					}
2960 
2961 					if (UNEXPECTED(prop_info)) {
2962 						zend_jit_dec_typed_prop(prop, prop_info);
2963 					} else {
2964 						decrement_function(prop);
2965 					}
2966 				} while (0);
2967 			}
2968 			if (UNEXPECTED(result)) {
2969 				ZVAL_COPY(result, prop);
2970 			}
2971 		}
2972 	} else {
2973 		zval rv;
2974 		zval *z;
2975 		zval z_copy;
2976 
2977 		GC_ADDREF(zobj);
2978 		z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
2979 		if (UNEXPECTED(EG(exception))) {
2980 			OBJ_RELEASE(zobj);
2981 			if (UNEXPECTED(result)) {
2982 				ZVAL_NULL(result);
2983 			}
2984 			return;
2985 		}
2986 
2987 		ZVAL_COPY_DEREF(&z_copy, z);
2988 		decrement_function(&z_copy);
2989 		if (UNEXPECTED(result)) {
2990 			ZVAL_COPY(result, &z_copy);
2991 		}
2992 		zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
2993 		OBJ_RELEASE(zobj);
2994 		zval_ptr_dtor(&z_copy);
2995 		if (z == &rv) {
2996 			zval_ptr_dtor(z);
2997 		}
2998 	}
2999 }
3000 
zend_jit_post_inc_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)3001 static void ZEND_FASTCALL zend_jit_post_inc_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
3002 {
3003 	zval *prop;
3004 
3005 	if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
3006 		if (UNEXPECTED(Z_ISERROR_P(prop))) {
3007 			ZVAL_NULL(result);
3008 		} else {
3009 			zend_property_info *prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
3010 
3011 			if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
3012 				ZVAL_LONG(result, Z_LVAL_P(prop));
3013 				fast_long_increment_function(prop);
3014 				if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
3015 						&& !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
3016 					zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
3017 					ZVAL_LONG(prop, val);
3018 				}
3019 			} else {
3020 				if (Z_ISREF_P(prop)) {
3021 					zend_reference *ref = Z_REF_P(prop);
3022 					prop = Z_REFVAL_P(prop);
3023 					if (ZEND_REF_HAS_TYPE_SOURCES(ref)) {
3024 						zend_jit_post_inc_typed_ref(ref, result);
3025 						return;
3026 					}
3027 				}
3028 
3029 				if (UNEXPECTED(prop_info)) {
3030 					zend_jit_post_inc_typed_prop(prop, prop_info, result);
3031 				} else {
3032 					ZVAL_COPY(result, prop);
3033 					increment_function(prop);
3034 				}
3035 			}
3036 		}
3037 	} else {
3038 		zval rv;
3039 		zval *z;
3040 		zval z_copy;
3041 
3042 		GC_ADDREF(zobj);
3043 		z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
3044 		if (UNEXPECTED(EG(exception))) {
3045 			OBJ_RELEASE(zobj);
3046 			ZVAL_UNDEF(result);
3047 			return;
3048 		}
3049 
3050 		ZVAL_COPY_DEREF(&z_copy, z);
3051 		ZVAL_COPY(result, &z_copy);
3052 		increment_function(&z_copy);
3053 		zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
3054 		OBJ_RELEASE(zobj);
3055 		zval_ptr_dtor(&z_copy);
3056 		if (z == &rv) {
3057 			zval_ptr_dtor(z);
3058 		}
3059 	}
3060 }
3061 
zend_jit_post_dec_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)3062 static void ZEND_FASTCALL zend_jit_post_dec_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
3063 {
3064 	zval *prop;
3065 
3066 	if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
3067 		if (UNEXPECTED(Z_ISERROR_P(prop))) {
3068 			ZVAL_NULL(result);
3069 		} else {
3070 			zend_property_info *prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
3071 
3072 			if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
3073 				ZVAL_LONG(result, Z_LVAL_P(prop));
3074 				fast_long_decrement_function(prop);
3075 				if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
3076 						&& !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
3077 					zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
3078 					ZVAL_LONG(prop, val);
3079 				}
3080 			} else {
3081 				if (Z_ISREF_P(prop)) {
3082 					zend_reference *ref = Z_REF_P(prop);
3083 					prop = Z_REFVAL_P(prop);
3084 					if (ZEND_REF_HAS_TYPE_SOURCES(ref)) {
3085 						zend_jit_post_dec_typed_ref(ref, result);
3086 						return;
3087 					}
3088 				}
3089 
3090 				if (UNEXPECTED(prop_info)) {
3091 					zend_jit_post_dec_typed_prop(prop, prop_info, result);
3092 				} else {
3093 					ZVAL_COPY(result, prop);
3094 					decrement_function(prop);
3095 				}
3096 			}
3097 		}
3098 	} else {
3099 		zval rv;
3100 		zval *z;
3101 		zval z_copy;
3102 
3103 		GC_ADDREF(zobj);
3104 		z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
3105 		if (UNEXPECTED(EG(exception))) {
3106 			OBJ_RELEASE(zobj);
3107 			ZVAL_UNDEF(result);
3108 			return;
3109 		}
3110 
3111 		ZVAL_COPY_DEREF(&z_copy, z);
3112 		ZVAL_COPY(result, &z_copy);
3113 		decrement_function(&z_copy);
3114 		zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
3115 		OBJ_RELEASE(zobj);
3116 		zval_ptr_dtor(&z_copy);
3117 		if (z == &rv) {
3118 			zval_ptr_dtor(z);
3119 		}
3120 	}
3121 }
3122 
zend_jit_free_trampoline_helper(zend_function * func)3123 static void ZEND_FASTCALL zend_jit_free_trampoline_helper(zend_function *func)
3124 {
3125 	ZEND_ASSERT(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE);
3126 	zend_string_release_ex(func->common.function_name, 0);
3127 	zend_free_trampoline(func);
3128 }
3129 
zend_jit_exception_in_interrupt_handler_helper(void)3130 static void ZEND_FASTCALL zend_jit_exception_in_interrupt_handler_helper(void)
3131 {
3132 	if (EG(exception)) {
3133 		/* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */
3134 		const zend_op *throw_op = EG(opline_before_exception);
3135 
3136 		if (throw_op
3137 		 && throw_op->result_type & (IS_TMP_VAR|IS_VAR)
3138 		 && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT
3139 		 && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK
3140 		 && throw_op->opcode != ZEND_ROPE_INIT
3141 		 && throw_op->opcode != ZEND_ROPE_ADD) {
3142 			ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var));
3143 		}
3144 	}
3145 }
3146 
zend_jit_rope_end(zend_string ** rope,uint32_t count)3147 static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t count)
3148 {
3149 	zend_string *ret;
3150 	uint32_t i;
3151 	size_t len = 0;
3152 
3153 	uint32_t flags = ZSTR_COPYABLE_CONCAT_PROPERTIES;
3154 	for (i = 0; i <= count; i++) {
3155 		flags &= ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(rope[i]);
3156 		len += ZSTR_LEN(rope[i]);
3157 	}
3158 	ret = zend_string_alloc(len, 0);
3159 	GC_ADD_FLAGS(ret, flags);
3160 
3161 	char *target = ZSTR_VAL(ret);
3162 	for (i = 0; i <= count; i++) {
3163 		memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i]));
3164 		target += ZSTR_LEN(rope[i]);
3165 		zend_string_release_ex(rope[i], 0);
3166 	}
3167 	*target = '\0';
3168 	return ret;
3169 }
3170