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