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