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