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