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