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->result_type & (IS_VAR | IS_TMP_VAR)) {
25 zend_execute_data *execute_data = EG(current_execute_data);
26 ZVAL_UNDEF(EX_VAR(opline->result.var));
27 }
28 }
29
zend_jit_illegal_offset(void)30 static ZEND_COLD void zend_jit_illegal_offset(void)
31 {
32 zend_type_error("Illegal offset type");
33 }
34
zend_jit_illegal_string_offset(zval * offset)35 static ZEND_COLD void zend_jit_illegal_string_offset(zval *offset)
36 {
37 zend_type_error("Cannot access offset of type %s on string", zend_zval_type_name(offset));
38 }
39
_zend_jit_init_func_run_time_cache(const zend_op_array * op_array)40 static zend_never_inline zend_function* ZEND_FASTCALL _zend_jit_init_func_run_time_cache(const zend_op_array *op_array) /* {{{ */
41 {
42 void **run_time_cache;
43
44 run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
45 memset(run_time_cache, 0, op_array->cache_size);
46 ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache);
47 return (zend_function*)op_array;
48 }
49 /* }}} */
50
zend_jit_init_func_run_time_cache_helper(zend_op_array * op_array)51 static zend_never_inline zend_op_array* ZEND_FASTCALL zend_jit_init_func_run_time_cache_helper(zend_op_array *op_array) /* {{{ */
52 {
53 void **run_time_cache;
54
55 if (!RUN_TIME_CACHE(op_array)) {
56 run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size);
57 memset(run_time_cache, 0, op_array->cache_size);
58 ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache);
59 }
60 return op_array;
61 }
62 /* }}} */
63
zend_jit_find_func_helper(zend_string * name,void ** cache_slot)64 static zend_function* ZEND_FASTCALL zend_jit_find_func_helper(zend_string *name, void **cache_slot)
65 {
66 zval *func = zend_hash_find_known_hash(EG(function_table), name);
67 zend_function *fbc;
68
69 if (UNEXPECTED(func == NULL)) {
70 return NULL;
71 }
72 fbc = Z_FUNC_P(func);
73 if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
74 fbc = _zend_jit_init_func_run_time_cache(&fbc->op_array);
75 }
76 *cache_slot = fbc;
77 return fbc;
78 }
79
zend_jit_find_ns_func_helper(zval * func_name,void ** cache_slot)80 static zend_function* ZEND_FASTCALL zend_jit_find_ns_func_helper(zval *func_name, void **cache_slot)
81 {
82 zval *func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(func_name + 1));
83 zend_function *fbc;
84
85 if (func == NULL) {
86 func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(func_name + 2));
87 if (UNEXPECTED(func == NULL)) {
88 return NULL;
89 }
90 }
91 fbc = Z_FUNC_P(func);
92 if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
93 fbc = _zend_jit_init_func_run_time_cache(&fbc->op_array);
94 }
95 *cache_slot = fbc;
96 return fbc;
97 }
98
zend_jit_invalid_method_call(zval * object)99 static ZEND_COLD void ZEND_FASTCALL zend_jit_invalid_method_call(zval *object)
100 {
101 zend_execute_data *execute_data = EG(current_execute_data);
102 const zend_op *opline = EX(opline);
103 zval *function_name = function_name = RT_CONSTANT(opline, opline->op2);;
104
105 if (Z_TYPE_P(object) == IS_UNDEF && opline->op1_type == IS_CV) {
106 zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
107
108 zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
109 if (UNEXPECTED(EG(exception) != NULL)) {
110 return;
111 }
112 object = &EG(uninitialized_zval);
113 }
114 zend_throw_error(NULL, "Call to a member function %s() on %s",
115 Z_STRVAL_P(function_name), zend_zval_type_name(object));
116 }
117
zend_jit_invalid_method_call_tmp(zval * object)118 static ZEND_COLD void ZEND_FASTCALL zend_jit_invalid_method_call_tmp(zval *object)
119 {
120 zend_execute_data *execute_data = EG(current_execute_data);
121 const zend_op *opline = EX(opline);
122
123 zend_jit_invalid_method_call(object);
124 zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
125 }
126
zend_undefined_method(const zend_class_entry * ce,const zend_string * method)127 static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method)
128 {
129 zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method));
130 }
131
zend_jit_unref_helper(zval * zv)132 static void ZEND_FASTCALL zend_jit_unref_helper(zval *zv)
133 {
134 zend_reference *ref;
135
136 ZEND_ASSERT(Z_ISREF_P(zv));
137 ref = Z_REF_P(zv);
138 ZVAL_COPY_VALUE(zv, &ref->val);
139 if (GC_DELREF(ref) == 0) {
140 efree_size(ref, sizeof(zend_reference));
141 } else {
142 Z_TRY_ADDREF_P(zv);
143 }
144 }
145
zend_jit_find_method_helper(zend_object * obj,zval * function_name,zend_object ** obj_ptr)146 static zend_function* ZEND_FASTCALL zend_jit_find_method_helper(zend_object *obj, zval *function_name, zend_object **obj_ptr)
147 {
148 zend_execute_data *execute_data = EG(current_execute_data);
149 const zend_op *opline = EX(opline);
150 zend_class_entry *called_scope = obj->ce;
151 zend_function *fbc;
152
153 fbc = obj->handlers->get_method(obj_ptr, Z_STR_P(function_name), function_name + 1);
154 if (UNEXPECTED(fbc == NULL)) {
155 if (EXPECTED(!EG(exception))) {
156 zend_undefined_method(called_scope, Z_STR_P(function_name));
157 }
158 return NULL;
159 }
160
161 if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {
162 zend_init_func_run_time_cache(&fbc->op_array);
163 }
164
165 if (UNEXPECTED(obj != *obj_ptr)) {
166 return fbc;
167 }
168
169 if (EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) {
170 CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc);
171 }
172
173 return fbc;
174 }
175
zend_jit_find_method_tmp_helper(zend_object * obj,zval * function_name,zend_object ** obj_ptr)176 static zend_function* ZEND_FASTCALL zend_jit_find_method_tmp_helper(zend_object *obj, zval *function_name, zend_object **obj_ptr)
177 {
178 zend_function *fbc;
179
180 fbc = zend_jit_find_method_helper(obj, function_name, obj_ptr);
181 if (!fbc) {
182 if (GC_DELREF(obj) == 0) {
183 zend_objects_store_del(obj);
184 }
185 } else if (obj != *obj_ptr) {
186 GC_ADDREF(*obj_ptr);
187 if (GC_DELREF(obj) == 0) {
188 zend_objects_store_del(obj);
189 }
190 }
191 return fbc;
192 }
193
zend_jit_push_static_metod_call_frame(zend_object * obj,zend_function * fbc,uint32_t num_args)194 static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame(zend_object *obj, zend_function *fbc, uint32_t num_args)
195 {
196 zend_class_entry *scope = obj->ce;
197
198 return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope);
199 }
200
zend_jit_push_static_metod_call_frame_tmp(zend_object * obj,zend_function * fbc,uint32_t num_args)201 static zend_execute_data* ZEND_FASTCALL zend_jit_push_static_metod_call_frame_tmp(zend_object *obj, zend_function *fbc, uint32_t num_args)
202 {
203 zend_class_entry *scope = obj->ce;
204
205 if (GC_DELREF(obj) == 0) {
206 zend_objects_store_del(obj);
207 if (UNEXPECTED(EG(exception))) {
208 return NULL;
209 }
210 }
211
212 return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION, fbc, num_args, scope);
213 }
214
zend_jit_extend_stack_helper(uint32_t used_stack,zend_function * fbc)215 static zend_execute_data* ZEND_FASTCALL zend_jit_extend_stack_helper(uint32_t used_stack, zend_function *fbc)
216 {
217 zend_execute_data *call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
218 call->func = fbc;
219 ZEND_CALL_INFO(call) = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_ALLOCATED;
220 return call;
221 }
222
zend_jit_int_extend_stack_helper(uint32_t used_stack)223 static zend_execute_data* ZEND_FASTCALL zend_jit_int_extend_stack_helper(uint32_t used_stack)
224 {
225 zend_execute_data *call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
226 ZEND_CALL_INFO(call) = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_ALLOCATED;
227 return call;
228 }
229
zend_jit_symtable_find(HashTable * ht,zend_string * str)230 static zval* ZEND_FASTCALL zend_jit_symtable_find(HashTable *ht, zend_string *str)
231 {
232 zend_ulong idx;
233 register const char *tmp = str->val;
234
235 do {
236 if (*tmp > '9') {
237 break;
238 } else if (*tmp < '0') {
239 if (*tmp != '-') {
240 break;
241 }
242 tmp++;
243 if (*tmp > '9' || *tmp < '0') {
244 break;
245 }
246 }
247 if (_zend_handle_numeric_str_ex(str->val, str->len, &idx)) {
248 return zend_hash_index_find(ht, idx);
249 }
250 } while (0);
251
252 return zend_hash_find(ht, str);
253 }
254
zend_jit_hash_index_lookup_rw_no_packed(HashTable * ht,zend_long idx)255 static zval* ZEND_FASTCALL zend_jit_hash_index_lookup_rw_no_packed(HashTable *ht, zend_long idx)
256 {
257 zval *retval = NULL;
258
259 if (!HT_IS_PACKED(ht)) {
260 retval = _zend_hash_index_find(ht, idx);
261 }
262 if (!retval) {
263 retval = zend_undefined_offset_write(ht, idx);
264 }
265 return retval;
266 }
267
zend_jit_hash_index_lookup_rw(HashTable * ht,zend_long idx)268 static zval* ZEND_FASTCALL zend_jit_hash_index_lookup_rw(HashTable *ht, zend_long idx)
269 {
270 zval *retval = zend_hash_index_find(ht, idx);
271
272 if (!retval) {
273 retval = zend_undefined_offset_write(ht, idx);
274 }
275 return retval;
276 }
277
zend_jit_hash_lookup_rw(HashTable * ht,zend_string * str)278 static zval* ZEND_FASTCALL zend_jit_hash_lookup_rw(HashTable *ht, zend_string *str)
279 {
280 zval *retval = zend_hash_find_known_hash(ht, str);
281 if (!retval) {
282 /* Key may be released while throwing the undefined index warning. */
283 retval = zend_undefined_index_write(ht, str);
284 }
285 return retval;
286 }
287
zend_jit_symtable_lookup_rw(HashTable * ht,zend_string * str)288 static zval* ZEND_FASTCALL zend_jit_symtable_lookup_rw(HashTable *ht, zend_string *str)
289 {
290 zend_ulong idx;
291 register const char *tmp = str->val;
292 zval *retval;
293
294 do {
295 if (*tmp > '9') {
296 break;
297 } else if (*tmp < '0') {
298 if (*tmp != '-') {
299 break;
300 }
301 tmp++;
302 if (*tmp > '9' || *tmp < '0') {
303 break;
304 }
305 }
306 if (_zend_handle_numeric_str_ex(str->val, str->len, &idx)) {
307 retval = zend_hash_index_find(ht, idx);
308 if (!retval) {
309 retval = zend_undefined_offset_write(ht, idx);
310 }
311 return retval;
312 }
313 } while (0);
314
315 retval = zend_hash_find(ht, str);
316 if (!retval) {
317 /* Key may be released while throwing the undefined index warning. */
318 retval = zend_undefined_index_write(ht, str);
319 }
320 return retval;
321 }
322
zend_jit_symtable_lookup_w(HashTable * ht,zend_string * str)323 static zval* ZEND_FASTCALL zend_jit_symtable_lookup_w(HashTable *ht, zend_string *str)
324 {
325 zend_ulong idx;
326 register const char *tmp = str->val;
327
328 do {
329 if (*tmp > '9') {
330 break;
331 } else if (*tmp < '0') {
332 if (*tmp != '-') {
333 break;
334 }
335 tmp++;
336 if (*tmp > '9' || *tmp < '0') {
337 break;
338 }
339 }
340 if (_zend_handle_numeric_str_ex(str->val, str->len, &idx)) {
341 return zend_hash_index_lookup(ht, idx);
342 }
343 } while (0);
344
345 return zend_hash_lookup(ht, str);
346 }
347
zend_jit_undefined_op_helper(uint32_t var)348 static int ZEND_FASTCALL zend_jit_undefined_op_helper(uint32_t var)
349 {
350 const zend_execute_data *execute_data = EG(current_execute_data);
351 zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(var)];
352
353 zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
354 return EG(exception) == NULL;
355 }
356
zend_jit_undefined_op_helper_write(HashTable * ht,uint32_t var)357 static int ZEND_FASTCALL zend_jit_undefined_op_helper_write(HashTable *ht, uint32_t var)
358 {
359 const zend_execute_data *execute_data = EG(current_execute_data);
360 zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(var)];
361
362 /* The array may be destroyed while throwing the notice.
363 * Temporarily increase the refcount to detect this situation. */
364 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
365 GC_ADDREF(ht);
366 }
367 zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
368 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && GC_DELREF(ht) != 1) {
369 if (!GC_REFCOUNT(ht)) {
370 zend_array_destroy(ht);
371 }
372 return 0;
373 }
374 return EG(exception) == NULL;
375 }
376
zend_jit_fetch_dim_r_helper(zend_array * ht,zval * dim,zval * result)377 static void ZEND_FASTCALL zend_jit_fetch_dim_r_helper(zend_array *ht, zval *dim, zval *result)
378 {
379 zend_ulong hval;
380 zend_string *offset_key;
381 zval *retval;
382 zend_execute_data *execute_data;
383 const zend_op *opline;
384
385 if (Z_TYPE_P(dim) == IS_REFERENCE) {
386 dim = Z_REFVAL_P(dim);
387 }
388
389 switch (Z_TYPE_P(dim)) {
390 case IS_LONG:
391 hval = Z_LVAL_P(dim);
392 goto num_index;
393 case IS_STRING:
394 offset_key = Z_STR_P(dim);
395 goto str_index;
396 case IS_UNDEF:
397 /* The array may be destroyed while throwing the notice.
398 * Temporarily increase the refcount to detect this situation. */
399 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
400 GC_ADDREF(ht);
401 }
402 execute_data = EG(current_execute_data);
403 opline = EX(opline);
404 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
405 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
406 zend_array_destroy(ht);
407 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
408 if (EG(exception)) {
409 ZVAL_UNDEF(EX_VAR(opline->result.var));
410 } else {
411 ZVAL_NULL(EX_VAR(opline->result.var));
412 }
413 }
414 return;
415 }
416 if (EG(exception)) {
417 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
418 ZVAL_UNDEF(EX_VAR(opline->result.var));
419 }
420 return;
421 }
422 ZEND_FALLTHROUGH;
423 case IS_NULL:
424 offset_key = ZSTR_EMPTY_ALLOC();
425 goto str_index;
426 case IS_DOUBLE:
427 hval = zend_dval_to_lval(Z_DVAL_P(dim));
428 if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
429 /* The array may be destroyed while throwing the notice.
430 * Temporarily increase the refcount to detect this situation. */
431 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
432 GC_ADDREF(ht);
433 }
434 execute_data = EG(current_execute_data);
435 opline = EX(opline);
436 zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
437 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
438 zend_array_destroy(ht);
439 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
440 if (EG(exception)) {
441 ZVAL_UNDEF(EX_VAR(opline->result.var));
442 } else {
443 ZVAL_NULL(EX_VAR(opline->result.var));
444 }
445 }
446 return;
447 }
448 if (EG(exception)) {
449 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
450 ZVAL_UNDEF(EX_VAR(opline->result.var));
451 }
452 return;
453 }
454 }
455 goto num_index;
456 case IS_RESOURCE:
457 /* The array may be destroyed while throwing the notice.
458 * Temporarily increase the refcount to detect this situation. */
459 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
460 GC_ADDREF(ht);
461 }
462 execute_data = EG(current_execute_data);
463 opline = EX(opline);
464 zend_use_resource_as_offset(dim);
465 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
466 zend_array_destroy(ht);
467 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
468 if (EG(exception)) {
469 ZVAL_UNDEF(EX_VAR(opline->result.var));
470 } else {
471 ZVAL_NULL(EX_VAR(opline->result.var));
472 }
473 }
474 return;
475 }
476 if (EG(exception)) {
477 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
478 ZVAL_UNDEF(EX_VAR(opline->result.var));
479 }
480 return;
481 }
482 hval = Z_RES_HANDLE_P(dim);
483 goto num_index;
484 case IS_FALSE:
485 hval = 0;
486 goto num_index;
487 case IS_TRUE:
488 hval = 1;
489 goto num_index;
490 default:
491 zend_jit_illegal_offset();
492 undef_result_after_exception();
493 return;
494 }
495
496 str_index:
497 if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
498 goto num_index;
499 }
500 retval = zend_hash_find(ht, offset_key);
501 if (!retval) {
502 zend_error(E_WARNING, "Undefined array key \"%s\"", ZSTR_VAL(offset_key));
503 ZVAL_NULL(result);
504 return;
505 }
506 ZVAL_COPY_DEREF(result, retval);
507 return;
508
509 num_index:
510 ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
511 ZVAL_COPY_DEREF(result, retval);
512 return;
513
514 num_undef:
515 zend_error(E_WARNING, "Undefined array key " ZEND_LONG_FMT, hval);
516 ZVAL_NULL(result);
517 }
518
zend_jit_fetch_dim_is_helper(zend_array * ht,zval * dim,zval * result)519 static void ZEND_FASTCALL zend_jit_fetch_dim_is_helper(zend_array *ht, zval *dim, zval *result)
520 {
521 zend_ulong hval;
522 zend_string *offset_key;
523 zval *retval;
524 zend_execute_data *execute_data;
525 const zend_op *opline;
526
527 if (Z_TYPE_P(dim) == IS_REFERENCE) {
528 dim = Z_REFVAL_P(dim);
529 }
530
531 switch (Z_TYPE_P(dim)) {
532 case IS_LONG:
533 hval = Z_LVAL_P(dim);
534 goto num_index;
535 case IS_STRING:
536 offset_key = Z_STR_P(dim);
537 goto str_index;
538 case IS_UNDEF:
539 /* The array may be destroyed while throwing the notice.
540 * Temporarily increase the refcount to detect this situation. */
541 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
542 GC_ADDREF(ht);
543 }
544 execute_data = EG(current_execute_data);
545 opline = EX(opline);
546 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
547 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
548 zend_array_destroy(ht);
549 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
550 if (EG(exception)) {
551 ZVAL_UNDEF(EX_VAR(opline->result.var));
552 } else {
553 ZVAL_NULL(EX_VAR(opline->result.var));
554 }
555 }
556 return;
557 }
558 if (EG(exception)) {
559 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
560 ZVAL_UNDEF(EX_VAR(opline->result.var));
561 }
562 return;
563 }
564 ZEND_FALLTHROUGH;
565 case IS_NULL:
566 offset_key = ZSTR_EMPTY_ALLOC();
567 goto str_index;
568 case IS_DOUBLE:
569 hval = zend_dval_to_lval(Z_DVAL_P(dim));
570 if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) {
571 /* The array may be destroyed while throwing the notice.
572 * Temporarily increase the refcount to detect this situation. */
573 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
574 GC_ADDREF(ht);
575 }
576 execute_data = EG(current_execute_data);
577 opline = EX(opline);
578 zend_incompatible_double_to_long_error(Z_DVAL_P(dim));
579 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
580 zend_array_destroy(ht);
581 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
582 if (EG(exception)) {
583 ZVAL_UNDEF(EX_VAR(opline->result.var));
584 } else {
585 ZVAL_NULL(EX_VAR(opline->result.var));
586 }
587 }
588 return;
589 }
590 if (EG(exception)) {
591 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
592 ZVAL_UNDEF(EX_VAR(opline->result.var));
593 }
594 return;
595 }
596 }
597 goto num_index;
598 case IS_RESOURCE:
599 /* The array may be destroyed while throwing the notice.
600 * Temporarily increase the refcount to detect this situation. */
601 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) {
602 GC_ADDREF(ht);
603 }
604 execute_data = EG(current_execute_data);
605 opline = EX(opline);
606 zend_use_resource_as_offset(dim);
607 if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) {
608 zend_array_destroy(ht);
609 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
610 if (EG(exception)) {
611 ZVAL_UNDEF(EX_VAR(opline->result.var));
612 } else {
613 ZVAL_NULL(EX_VAR(opline->result.var));
614 }
615 }
616 return;
617 }
618 if (EG(exception)) {
619 if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
620 ZVAL_UNDEF(EX_VAR(opline->result.var));
621 }
622 return;
623 }
624 hval = Z_RES_HANDLE_P(dim);
625 goto num_index;
626 case IS_FALSE:
627 hval = 0;
628 goto num_index;
629 case IS_TRUE:
630 hval = 1;
631 goto num_index;
632 default:
633 zend_jit_illegal_offset();
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_type_error("Illegal offset type in isset or empty");
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 (!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_jit_illegal_offset();
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_jit_illegal_offset();
1005 undef_result_after_exception();
1006 if ((EG(opline_before_exception)+1)->opcode == ZEND_OP_DATA
1007 && ((EG(opline_before_exception)+1)->op1_type & (IS_VAR|IS_TMP_VAR))) {
1008 zend_execute_data *execute_data = EG(current_execute_data);
1009
1010 zval_ptr_dtor_nogc(EX_VAR((EG(opline_before_exception)+1)->op1.var));
1011 }
1012 return NULL;
1013 }
1014
1015 str_index:
1016 if (ZEND_HANDLE_NUMERIC(offset_key, hval)) {
1017 goto num_index;
1018 }
1019 return zend_hash_lookup(ht, offset_key);
1020
1021 num_index:
1022 ZEND_HASH_INDEX_LOOKUP(ht, hval, retval);
1023 return retval;
1024 }
1025
zend_check_string_offset(zval * dim)1026 static zend_never_inline zend_long zend_check_string_offset(zval *dim/*, int type*/)
1027 {
1028 zend_long offset;
1029
1030 try_again:
1031 switch(Z_TYPE_P(dim)) {
1032 case IS_LONG:
1033 return Z_LVAL_P(dim);
1034 case IS_STRING:
1035 {
1036 bool trailing_data = false;
1037 /* For BC reasons we allow errors so that we can warn on leading numeric string */
1038 if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL,
1039 /* allow errors */ true, NULL, &trailing_data)) {
1040 if (UNEXPECTED(trailing_data)
1041 && EG(current_execute_data)->opline->opcode != ZEND_FETCH_DIM_UNSET) {
1042 zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
1043 }
1044 return offset;
1045 }
1046 zend_jit_illegal_string_offset(dim);
1047 return 0;
1048 }
1049 case IS_UNDEF:
1050 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1051 ZEND_FALLTHROUGH;
1052 case IS_DOUBLE:
1053 case IS_NULL:
1054 case IS_FALSE:
1055 case IS_TRUE:
1056 zend_error(E_WARNING, "String offset cast occurred");
1057 break;
1058 case IS_REFERENCE:
1059 dim = Z_REFVAL_P(dim);
1060 goto try_again;
1061 default:
1062 zend_jit_illegal_string_offset(dim);
1063 return 0;
1064 }
1065
1066 return zval_get_long_func(dim, /* is_strict */ false);
1067 }
1068
zend_jit_fetch_dim_str_offset(zend_string * str,zend_long offset)1069 static zend_always_inline zend_string* zend_jit_fetch_dim_str_offset(zend_string *str, zend_long offset)
1070 {
1071 if (UNEXPECTED((zend_ulong)offset >= (zend_ulong)ZSTR_LEN(str))) {
1072 if (EXPECTED(offset < 0)) {
1073 /* Handle negative offset */
1074 zend_long real_offset = (zend_long)ZSTR_LEN(str) + offset;
1075
1076 if (EXPECTED(real_offset >= 0)) {
1077 return ZSTR_CHAR((zend_uchar)ZSTR_VAL(str)[real_offset]);
1078 }
1079 }
1080 zend_error(E_WARNING, "Uninitialized string offset " ZEND_LONG_FMT, offset);
1081 return ZSTR_EMPTY_ALLOC();
1082 } else {
1083 return ZSTR_CHAR((zend_uchar)ZSTR_VAL(str)[offset]);
1084 }
1085 }
1086
zend_jit_fetch_dim_str_offset_r_helper(zend_string * str,zend_long offset)1087 static zend_string* ZEND_FASTCALL zend_jit_fetch_dim_str_offset_r_helper(zend_string *str, zend_long offset)
1088 {
1089 return zend_jit_fetch_dim_str_offset(str, offset);
1090 }
1091
zend_jit_fetch_dim_str_r_helper(zend_string * str,zval * dim)1092 static zend_string* ZEND_FASTCALL zend_jit_fetch_dim_str_r_helper(zend_string *str, zval *dim)
1093 {
1094 zend_long offset;
1095
1096 if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1097 if (!(GC_FLAGS(str) & IS_STR_INTERNED)) {
1098 GC_ADDREF(str);
1099 }
1100 offset = zend_check_string_offset(dim/*, BP_VAR_R*/);
1101 if (!(GC_FLAGS(str) & IS_STR_INTERNED) && UNEXPECTED(GC_DELREF(str) == 0)) {
1102 zend_string *ret = zend_jit_fetch_dim_str_offset(str, offset);
1103 zend_string_efree(str);
1104 return ret;
1105 }
1106 } else {
1107 offset = Z_LVAL_P(dim);
1108 }
1109 return zend_jit_fetch_dim_str_offset(str, offset);
1110 }
1111
zend_jit_fetch_dim_str_is_helper(zend_string * str,zval * dim,zval * result)1112 static void ZEND_FASTCALL zend_jit_fetch_dim_str_is_helper(zend_string *str, zval *dim, zval *result)
1113 {
1114 zend_long offset;
1115
1116 try_string_offset:
1117 if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1118 switch (Z_TYPE_P(dim)) {
1119 /* case IS_LONG: */
1120 case IS_STRING:
1121 if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, false)) {
1122 break;
1123 }
1124 ZVAL_NULL(result);
1125 return;
1126 case IS_UNDEF:
1127 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1128 case IS_DOUBLE:
1129 case IS_NULL:
1130 case IS_FALSE:
1131 case IS_TRUE:
1132 break;
1133 case IS_REFERENCE:
1134 dim = Z_REFVAL_P(dim);
1135 goto try_string_offset;
1136 default:
1137 zend_jit_illegal_string_offset(dim);
1138 ZVAL_NULL(result);
1139 return;
1140 }
1141
1142 offset = zval_get_long_func(dim, /* is_strict */ false);
1143 } else {
1144 offset = Z_LVAL_P(dim);
1145 }
1146
1147 if ((zend_ulong)offset >= (zend_ulong)ZSTR_LEN(str)) {
1148 if (offset < 0) {
1149 /* Handle negative offset */
1150 zend_long real_offset = (zend_long)ZSTR_LEN(str) + offset;
1151
1152 if (real_offset >= 0) {
1153 ZVAL_CHAR(result, (zend_uchar)ZSTR_VAL(str)[real_offset]);
1154 return;
1155 }
1156 }
1157 ZVAL_NULL(result);
1158 } else {
1159 ZVAL_CHAR(result, (zend_uchar)ZSTR_VAL(str)[offset]);
1160 }
1161 }
1162
zend_jit_fetch_dim_obj_r_helper(zval * container,zval * dim,zval * result)1163 static void ZEND_FASTCALL zend_jit_fetch_dim_obj_r_helper(zval *container, zval *dim, zval *result)
1164 {
1165 zval *retval;
1166 zend_object *obj = Z_OBJ_P(container);
1167
1168 GC_ADDREF(obj);
1169 if (UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
1170 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1171 dim = &EG(uninitialized_zval);
1172 }
1173
1174 retval = obj->handlers->read_dimension(obj, dim, BP_VAR_R, result);
1175
1176 if (retval) {
1177 if (result != retval) {
1178 ZVAL_COPY_DEREF(result, retval);
1179 } else if (UNEXPECTED(Z_ISREF_P(retval))) {
1180 zend_unwrap_reference(retval);
1181 }
1182 } else {
1183 ZVAL_NULL(result);
1184 }
1185 if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1186 zend_objects_store_del(obj);
1187 }
1188 }
1189
zend_jit_fetch_dim_obj_is_helper(zval * container,zval * dim,zval * result)1190 static void ZEND_FASTCALL zend_jit_fetch_dim_obj_is_helper(zval *container, zval *dim, zval *result)
1191 {
1192 zval *retval;
1193 zend_object *obj = Z_OBJ_P(container);
1194
1195 GC_ADDREF(obj);
1196 if (UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {
1197 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1198 dim = &EG(uninitialized_zval);
1199 }
1200
1201 retval = obj->handlers->read_dimension(obj, dim, BP_VAR_IS, result);
1202
1203 if (retval) {
1204 if (result != retval) {
1205 ZVAL_COPY_DEREF(result, retval);
1206 } else if (UNEXPECTED(Z_ISREF_P(retval))) {
1207 zend_unwrap_reference(result);
1208 }
1209 } else {
1210 ZVAL_NULL(result);
1211 }
1212 if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1213 zend_objects_store_del(obj);
1214 }
1215 }
1216
zend_assign_to_string_offset(zval * str,zval * dim,zval * value,zval * result)1217 static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result)
1218 {
1219 zend_uchar c;
1220 size_t string_len;
1221 zend_long offset;
1222 zend_string *s;
1223
1224 /* separate string */
1225 if (Z_REFCOUNTED_P(str) && Z_REFCOUNT_P(str) == 1) {
1226 s = Z_STR_P(str);
1227 } else {
1228 s = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0);
1229 ZSTR_H(s) = ZSTR_H(Z_STR_P(str));
1230 if (Z_REFCOUNTED_P(str)) {
1231 GC_DELREF(Z_STR_P(str));
1232 }
1233 ZVAL_NEW_STR(str, s);
1234 }
1235
1236 if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1237 /* The string may be destroyed while throwing the notice.
1238 * Temporarily increase the refcount to detect this situation. */
1239 GC_ADDREF(s);
1240 offset = zend_check_string_offset(dim/*, BP_VAR_W*/);
1241 if (UNEXPECTED(GC_DELREF(s) == 0)) {
1242 zend_string_efree(s);
1243 if (result) {
1244 ZVAL_NULL(result);
1245 }
1246 return;
1247 }
1248 if (UNEXPECTED(EG(exception) != NULL)) {
1249 if (UNEXPECTED(result)) {
1250 ZVAL_UNDEF(result);
1251 }
1252 return;
1253 }
1254 } else {
1255 offset = Z_LVAL_P(dim);
1256 }
1257 if (offset < -(zend_long)ZSTR_LEN(s)) {
1258 /* Error on negative offset */
1259 zend_error(E_WARNING, "Illegal string offset " ZEND_LONG_FMT, offset);
1260 if (result) {
1261 ZVAL_NULL(result);
1262 }
1263 return;
1264 }
1265
1266 if (Z_TYPE_P(value) != IS_STRING) {
1267 zend_string *tmp;
1268
1269 /* The string may be destroyed while throwing the notice.
1270 * Temporarily increase the refcount to detect this situation. */
1271 GC_ADDREF(s);
1272
1273 if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
1274 const zend_op *op_data = EG(current_execute_data)->opline + 1;
1275 ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
1276 zend_jit_undefined_op_helper(op_data->op1.var);
1277 value = &EG(uninitialized_zval);
1278 }
1279
1280 /* Convert to string, just the time to pick the 1st byte */
1281 tmp = zval_try_get_string_func(value);
1282
1283 if (UNEXPECTED(GC_DELREF(s) == 0)) {
1284 zend_string_efree(s);
1285 if (tmp) {
1286 zend_string_release_ex(tmp, 0);
1287 }
1288 if (result) {
1289 ZVAL_NULL(result);
1290 }
1291 return;
1292 }
1293 if (UNEXPECTED(!tmp)) {
1294 if (result) {
1295 ZVAL_UNDEF(result);
1296 }
1297 return;
1298 }
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 = (zend_uchar)ZSTR_VAL(tmp)[0];
1309 zend_string_release(tmp);
1310 } else {
1311 string_len = Z_STRLEN_P(value);
1312 c = (zend_uchar)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 zend_error(E_WARNING, "Attempt to assign property of non-object");
1592 }
1593 if (UNEXPECTED(GC_DELREF(obj) == 0)) {
1594 zend_objects_store_del(obj);
1595 //??? if (retval) {
1596 //??? ZVAL_NULL(retval);
1597 //??? }
1598 }
1599 } else if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) {
1600 if (!dim) {
1601 zend_throw_error(NULL, "[] operator not supported for strings");
1602 } else {
1603 if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
1604 zend_check_string_offset(dim/*, BP_VAR_RW*/);
1605 }
1606 zend_wrong_string_offset_error();
1607 }
1608 } else if (Z_TYPE_P(container) == IS_FALSE) {
1609 zend_array *arr = zend_new_array(0);
1610 ZVAL_ARR(container, arr);
1611 GC_ADDREF(arr);
1612 zend_false_to_array_deprecated();
1613 if (UNEXPECTED(GC_DELREF(arr) == 0)) {
1614 zend_array_destroy(arr);
1615 return;
1616 }
1617 SEPARATE_ARRAY(container);
1618 arr = Z_ARRVAL_P(container);
1619 zval *var = dim
1620 ? zend_jit_fetch_dim_rw_helper(arr, dim)
1621 : zend_hash_next_index_insert_new(arr, &EG(uninitialized_zval));
1622 if (var) {
1623 binary_op(var, var, value);
1624 }
1625 } else {
1626 zend_throw_error(NULL, "Cannot use a scalar value as an array");
1627 }
1628 }
1629
zend_jit_fast_assign_concat_helper(zval * op1,zval * op2)1630 static void ZEND_FASTCALL zend_jit_fast_assign_concat_helper(zval *op1, zval *op2)
1631 {
1632 size_t op1_len = Z_STRLEN_P(op1);
1633 size_t op2_len = Z_STRLEN_P(op2);
1634 size_t result_len = op1_len + op2_len;
1635 zend_string *result_str;
1636
1637 if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
1638 zend_throw_error(NULL, "String size overflow");
1639 return;
1640 }
1641
1642 do {
1643 if (Z_REFCOUNTED_P(op1)) {
1644 if (GC_REFCOUNT(Z_STR_P(op1)) == 1) {
1645 result_str = perealloc(Z_STR_P(op1), ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(result_len)), 0);
1646 ZSTR_LEN(result_str) = result_len;
1647 zend_string_forget_hash_val(result_str);
1648 if (UNEXPECTED(Z_STR_P(op1) == Z_STR_P(op2))) {
1649 ZVAL_NEW_STR(op2, result_str);
1650 }
1651 break;
1652 }
1653 GC_DELREF(Z_STR_P(op1));
1654 }
1655 result_str = zend_string_alloc(result_len, 0);
1656 memcpy(ZSTR_VAL(result_str), Z_STRVAL_P(op1), op1_len);
1657 } while(0);
1658
1659 ZVAL_NEW_STR(op1, result_str);
1660 memcpy(ZSTR_VAL(result_str) + op1_len, Z_STRVAL_P(op2), op2_len);
1661 ZSTR_VAL(result_str)[result_len] = '\0';
1662 }
1663
zend_jit_fast_concat_helper(zval * result,zval * op1,zval * op2)1664 static void ZEND_FASTCALL zend_jit_fast_concat_helper(zval *result, zval *op1, zval *op2)
1665 {
1666 size_t op1_len = Z_STRLEN_P(op1);
1667 size_t op2_len = Z_STRLEN_P(op2);
1668 size_t result_len = op1_len + op2_len;
1669 zend_string *result_str;
1670
1671 if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
1672 zend_throw_error(NULL, "String size overflow");
1673 return;
1674 }
1675
1676 result_str = zend_string_alloc(result_len, 0);
1677 memcpy(ZSTR_VAL(result_str), Z_STRVAL_P(op1), op1_len);
1678
1679 ZVAL_NEW_STR(result, result_str);
1680
1681 memcpy(ZSTR_VAL(result_str) + op1_len, Z_STRVAL_P(op2), op2_len);
1682 ZSTR_VAL(result_str)[result_len] = '\0';
1683 }
1684
zend_jit_fast_concat_tmp_helper(zval * result,zval * op1,zval * op2)1685 static void ZEND_FASTCALL zend_jit_fast_concat_tmp_helper(zval *result, zval *op1, zval *op2)
1686 {
1687 zend_string *op1_str = Z_STR_P(op1);
1688 size_t op1_len = ZSTR_LEN(op1_str);
1689 size_t op2_len = Z_STRLEN_P(op2);
1690 size_t result_len = op1_len + op2_len;
1691 zend_string *result_str;
1692
1693 if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
1694 zend_throw_error(NULL, "String size overflow");
1695 return;
1696 }
1697
1698 do {
1699 if (!ZSTR_IS_INTERNED(op1_str)) {
1700 if (GC_REFCOUNT(op1_str) == 1) {
1701 Z_STR_P(op1) = result_str =
1702 perealloc(op1_str, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(result_len)), 0);
1703 ZSTR_LEN(result_str) = result_len;
1704 zend_string_forget_hash_val(result_str);
1705 break;
1706 }
1707 GC_DELREF(op1_str);
1708 }
1709 result_str = zend_string_alloc(result_len, 0);
1710 memcpy(ZSTR_VAL(result_str), ZSTR_VAL(op1_str), op1_len);
1711 } while (0);
1712
1713 ZVAL_NEW_STR(result, result_str);
1714
1715 memcpy(ZSTR_VAL(result_str) + op1_len, Z_STRVAL_P(op2), op2_len);
1716 ZSTR_VAL(result_str)[result_len] = '\0';
1717 }
1718
zend_jit_isset_dim_helper(zval * container,zval * offset)1719 static int ZEND_FASTCALL zend_jit_isset_dim_helper(zval *container, zval *offset)
1720 {
1721 if (UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) {
1722 zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var);
1723 offset = &EG(uninitialized_zval);
1724 }
1725
1726 if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
1727 return Z_OBJ_HT_P(container)->has_dimension(Z_OBJ_P(container), offset, 0);
1728 } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */
1729 zend_long lval;
1730
1731 if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) {
1732 lval = Z_LVAL_P(offset);
1733 isset_str_offset:
1734 if (UNEXPECTED(lval < 0)) { /* Handle negative offset */
1735 lval += (zend_long)Z_STRLEN_P(container);
1736 }
1737 if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) {
1738 return 1;
1739 }
1740 } else {
1741 ZVAL_DEREF(offset);
1742 if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */
1743 || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */
1744 && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, false))) {
1745 lval = zval_get_long_ex(offset, /* is_strict */ true);
1746 goto isset_str_offset;
1747 }
1748 }
1749 }
1750 return 0;
1751 }
1752
zend_jit_free_call_frame(zend_execute_data * call)1753 static void ZEND_FASTCALL zend_jit_free_call_frame(zend_execute_data *call)
1754 {
1755 zend_vm_stack_free_call_frame(call);
1756 }
1757
zend_jit_fetch_global_helper(zend_string * varname,void ** cache_slot)1758 static zend_reference* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_string *varname, void **cache_slot)
1759 {
1760 zval *value;
1761 uintptr_t idx;
1762 zend_reference *ref;
1763
1764 /* We store "hash slot index" + 1 (NULL is a mark of uninitialized cache slot) */
1765 idx = (uintptr_t)CACHED_PTR_EX(cache_slot) - 1;
1766 if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket))) {
1767 Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx);
1768
1769 if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
1770 (EXPECTED(p->key == varname) ||
1771 (EXPECTED(p->h == ZSTR_H(varname)) &&
1772 EXPECTED(p->key != NULL) &&
1773 EXPECTED(zend_string_equal_content(p->key, varname))))) {
1774
1775 value = (zval*)p; /* value = &p->val; */
1776 goto check_indirect;
1777 }
1778 }
1779
1780 value = zend_hash_find_known_hash(&EG(symbol_table), varname);
1781 if (UNEXPECTED(value == NULL)) {
1782 value = zend_hash_add_new(&EG(symbol_table), varname, &EG(uninitialized_zval));
1783 idx = (char*)value - (char*)EG(symbol_table).arData;
1784 /* Store "hash slot index" + 1 (NULL is a mark of uninitialized cache slot) */
1785 CACHE_PTR_EX(cache_slot, (void*)(idx + 1));
1786 } else {
1787 idx = (char*)value - (char*)EG(symbol_table).arData;
1788 /* Store "hash slot index" + 1 (NULL is a mark of uninitialized cache slot) */
1789 CACHE_PTR_EX(cache_slot, (void*)(idx + 1));
1790 check_indirect:
1791 /* GLOBAL variable may be an INDIRECT pointer to CV */
1792 if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) {
1793 value = Z_INDIRECT_P(value);
1794 if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
1795 ZVAL_NULL(value);
1796 }
1797 }
1798 }
1799
1800 if (UNEXPECTED(!Z_ISREF_P(value))) {
1801 ZVAL_MAKE_REF_EX(value, 2);
1802 ref = Z_REF_P(value);
1803 } else {
1804 ref = Z_REF_P(value);
1805 GC_ADDREF(ref);
1806 }
1807
1808 return ref;
1809 }
1810
zend_jit_verify_arg_slow(zval * arg,zend_arg_info * arg_info)1811 static bool ZEND_FASTCALL zend_jit_verify_arg_slow(zval *arg, zend_arg_info *arg_info)
1812 {
1813 zend_execute_data *execute_data = EG(current_execute_data);
1814 const zend_op *opline = EX(opline);
1815 void **cache_slot = CACHE_ADDR(opline->extended_value);
1816 bool ret = zend_check_user_type_slow(
1817 &arg_info->type, arg, /* ref */ NULL, cache_slot, /* is_return_type */ false);
1818 if (UNEXPECTED(!ret)) {
1819 zend_verify_arg_error(EX(func), arg_info, opline->op1.num, arg);
1820 return 0;
1821 }
1822 return ret;
1823 }
1824
zend_jit_verify_return_slow(zval * arg,const zend_op_array * op_array,zend_arg_info * arg_info,void ** cache_slot)1825 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)
1826 {
1827 if (UNEXPECTED(!zend_check_user_type_slow(
1828 &arg_info->type, arg, /* ref */ NULL, cache_slot, /* is_return_type */ true))) {
1829 zend_verify_return_error((zend_function*)op_array, arg);
1830 }
1831 }
1832
zend_jit_fetch_obj_r_slow(zend_object * zobj)1833 static void ZEND_FASTCALL zend_jit_fetch_obj_r_slow(zend_object *zobj)
1834 {
1835 zval *retval;
1836 zend_execute_data *execute_data = EG(current_execute_data);
1837 const zend_op *opline = EX(opline);
1838 zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1839 zval *result = EX_VAR(opline->result.var);
1840 void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1841
1842 retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, result);
1843 if (retval != result) {
1844 ZVAL_COPY_DEREF(result, retval);
1845 } else if (UNEXPECTED(Z_ISREF_P(retval))) {
1846 zend_unwrap_reference(retval);
1847 }
1848 }
1849
zend_jit_fetch_obj_r_dynamic(zend_object * zobj,intptr_t prop_offset)1850 static void ZEND_FASTCALL zend_jit_fetch_obj_r_dynamic(zend_object *zobj, intptr_t prop_offset)
1851 {
1852 if (zobj->properties) {
1853 zval *retval;
1854 zend_execute_data *execute_data = EG(current_execute_data);
1855 const zend_op *opline = EX(opline);
1856 zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1857 zval *result = EX_VAR(opline->result.var);
1858 void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1859
1860 if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
1861 intptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);
1862
1863 if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
1864 Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
1865
1866 if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
1867 (EXPECTED(p->key == name) ||
1868 (EXPECTED(p->h == ZSTR_H(name)) &&
1869 EXPECTED(p->key != NULL) &&
1870 EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(name)) &&
1871 EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)))) {
1872 ZVAL_COPY_DEREF(result, &p->val);
1873 return;
1874 }
1875 }
1876 CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
1877 }
1878
1879 retval = zend_hash_find_known_hash(zobj->properties, name);
1880
1881 if (EXPECTED(retval)) {
1882 intptr_t idx = (char*)retval - (char*)zobj->properties->arData;
1883 CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
1884 ZVAL_COPY_DEREF(result, retval);
1885 return;
1886 }
1887 }
1888 zend_jit_fetch_obj_r_slow(zobj);
1889 }
1890
zend_jit_fetch_obj_is_slow(zend_object * zobj)1891 static void ZEND_FASTCALL zend_jit_fetch_obj_is_slow(zend_object *zobj)
1892 {
1893 zval *retval;
1894 zend_execute_data *execute_data = EG(current_execute_data);
1895 const zend_op *opline = EX(opline);
1896 zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1897 zval *result = EX_VAR(opline->result.var);
1898 void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1899
1900 retval = zobj->handlers->read_property(zobj, name, BP_VAR_IS, cache_slot, result);
1901 if (retval != result) {
1902 ZVAL_COPY_DEREF(result, retval);
1903 } else if (UNEXPECTED(Z_ISREF_P(retval))) {
1904 zend_unwrap_reference(retval);
1905 }
1906 }
1907
zend_jit_fetch_obj_is_dynamic(zend_object * zobj,intptr_t prop_offset)1908 static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intptr_t prop_offset)
1909 {
1910 if (zobj->properties) {
1911 zval *retval;
1912 zend_execute_data *execute_data = EG(current_execute_data);
1913 const zend_op *opline = EX(opline);
1914 zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
1915 zval *result = EX_VAR(opline->result.var);
1916 void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
1917
1918 if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
1919 intptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);
1920
1921 if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
1922 Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
1923
1924 if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
1925 (EXPECTED(p->key == name) ||
1926 (EXPECTED(p->h == ZSTR_H(name)) &&
1927 EXPECTED(p->key != NULL) &&
1928 EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(name)) &&
1929 EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)))) {
1930 ZVAL_COPY_DEREF(result, &p->val);
1931 return;
1932 }
1933 }
1934 CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
1935 }
1936
1937 retval = zend_hash_find_known_hash(zobj->properties, name);
1938
1939 if (EXPECTED(retval)) {
1940 intptr_t idx = (char*)retval - (char*)zobj->properties->arData;
1941 CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
1942 ZVAL_COPY_DEREF(result, retval);
1943 return;
1944 }
1945 }
1946 zend_jit_fetch_obj_is_slow(zobj);
1947 }
1948
promotes_to_array(zval * val)1949 static zend_always_inline bool promotes_to_array(zval *val) {
1950 return Z_TYPE_P(val) <= IS_FALSE
1951 || (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE);
1952 }
1953
check_type_array_assignable(zend_type type)1954 static zend_always_inline bool check_type_array_assignable(zend_type type) {
1955 if (!ZEND_TYPE_IS_SET(type)) {
1956 return 1;
1957 }
1958 return (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) != 0;
1959 }
1960
zend_object_fetch_property_type_info(zend_object * obj,zval * slot)1961 static zend_property_info *zend_object_fetch_property_type_info(
1962 zend_object *obj, zval *slot)
1963 {
1964 if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) {
1965 return NULL;
1966 }
1967
1968 /* Not a declared property */
1969 if (UNEXPECTED(slot < obj->properties_table ||
1970 slot >= obj->properties_table + obj->ce->default_properties_count)) {
1971 return NULL;
1972 }
1973
1974 return zend_get_typed_property_info_for_slot(obj, slot);
1975 }
1976
zend_throw_auto_init_in_prop_error(zend_property_info * prop,const char * type)1977 static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) {
1978 zend_string *type_str = zend_type_to_string(prop->type);
1979 zend_type_error(
1980 "Cannot auto-initialize an %s inside property %s::$%s of type %s",
1981 type,
1982 ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
1983 ZSTR_VAL(type_str)
1984 );
1985 zend_string_release(type_str);
1986 }
1987
zend_throw_access_uninit_prop_by_ref_error(zend_property_info * prop)1988 static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error(
1989 zend_property_info *prop) {
1990 zend_throw_error(NULL,
1991 "Cannot access uninitialized non-nullable property %s::$%s by reference",
1992 ZSTR_VAL(prop->ce->name),
1993 zend_get_unmangled_property_name(prop->name));
1994 }
1995
zend_handle_fetch_obj_flags(zval * result,zval * ptr,zend_object * obj,zend_property_info * prop_info,uint32_t flags)1996 static zend_never_inline bool zend_handle_fetch_obj_flags(
1997 zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags)
1998 {
1999 switch (flags) {
2000 case ZEND_FETCH_DIM_WRITE:
2001 if (promotes_to_array(ptr)) {
2002 if (!prop_info) {
2003 prop_info = zend_object_fetch_property_type_info(obj, ptr);
2004 if (!prop_info) {
2005 break;
2006 }
2007 }
2008 if (!check_type_array_assignable(prop_info->type)) {
2009 zend_throw_auto_init_in_prop_error(prop_info, "array");
2010 if (result) ZVAL_ERROR(result);
2011 return 0;
2012 }
2013 }
2014 break;
2015 case ZEND_FETCH_REF:
2016 if (Z_TYPE_P(ptr) != IS_REFERENCE) {
2017 if (!prop_info) {
2018 prop_info = zend_object_fetch_property_type_info(obj, ptr);
2019 if (!prop_info) {
2020 break;
2021 }
2022 }
2023 if (Z_TYPE_P(ptr) == IS_UNDEF) {
2024 if (!ZEND_TYPE_ALLOW_NULL(prop_info->type)) {
2025 zend_throw_access_uninit_prop_by_ref_error(prop_info);
2026 if (result) ZVAL_ERROR(result);
2027 return 0;
2028 }
2029 ZVAL_NULL(ptr);
2030 }
2031
2032 ZVAL_NEW_REF(ptr, ptr);
2033 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(ptr), prop_info);
2034 }
2035 break;
2036 EMPTY_SWITCH_DEFAULT_CASE()
2037 }
2038 return 1;
2039 }
2040
zend_jit_fetch_obj_w_slow(zend_object * zobj)2041 static void ZEND_FASTCALL zend_jit_fetch_obj_w_slow(zend_object *zobj)
2042 {
2043 zval *retval;
2044 zend_execute_data *execute_data = EG(current_execute_data);
2045 const zend_op *opline = EX(opline);
2046 zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
2047 zval *result = EX_VAR(opline->result.var);
2048 void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);
2049
2050 retval = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_W, cache_slot);
2051 if (NULL == retval) {
2052 retval = zobj->handlers->read_property(zobj, name, BP_VAR_W, cache_slot, result);
2053 if (retval == result) {
2054 if (UNEXPECTED(Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1)) {
2055 ZVAL_UNREF(retval);
2056 }
2057 return;
2058 }
2059 if (UNEXPECTED(EG(exception))) {
2060 ZVAL_ERROR(result);
2061 return;
2062 }
2063 } else if (UNEXPECTED(Z_ISERROR_P(retval))) {
2064 ZVAL_ERROR(result);
2065 return;
2066 }
2067
2068 ZVAL_INDIRECT(result, retval);
2069
2070 /* Support for typed properties */
2071 do {
2072 uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
2073
2074 if (flags) {
2075 zend_property_info *prop_info = NULL;
2076
2077 if (opline->op2_type == IS_CONST) {
2078 prop_info = CACHED_PTR_EX(cache_slot + 2);
2079 if (!prop_info) {
2080 break;
2081 }
2082 }
2083 if (UNEXPECTED(!zend_handle_fetch_obj_flags(result, retval, zobj, prop_info, flags))) {
2084 return;
2085 }
2086 }
2087 } while (0);
2088
2089 if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
2090 ZVAL_NULL(retval);
2091 }
2092 }
2093
zend_jit_check_array_promotion(zval * val,zend_property_info * prop)2094 static void ZEND_FASTCALL zend_jit_check_array_promotion(zval *val, zend_property_info *prop)
2095 {
2096 zend_execute_data *execute_data = EG(current_execute_data);
2097 const zend_op *opline = execute_data->opline;
2098 zval *result = EX_VAR(opline->result.var);
2099
2100 if ((Z_TYPE_P(val) <= IS_FALSE
2101 || (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE))
2102 && ZEND_TYPE_IS_SET(prop->type)
2103 && (ZEND_TYPE_FULL_MASK(prop->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) {
2104 zend_string *type_str = zend_type_to_string(prop->type);
2105 zend_type_error(
2106 "Cannot auto-initialize an array inside property %s::$%s of type %s",
2107 ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
2108 ZSTR_VAL(type_str)
2109 );
2110 zend_string_release(type_str);
2111 ZVAL_ERROR(result);
2112 } else {
2113 ZVAL_INDIRECT(result, val);
2114 }
2115 }
2116
zend_jit_create_typed_ref(zval * val,zend_property_info * prop,zval * result)2117 static void ZEND_FASTCALL zend_jit_create_typed_ref(zval *val, zend_property_info *prop, zval *result)
2118 {
2119 if (!Z_ISREF_P(val)) {
2120 ZVAL_NEW_REF(val, val);
2121 ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(val), prop);
2122 }
2123 ZVAL_INDIRECT(result, val);
2124 }
2125
zend_jit_extract_helper(zend_refcounted * garbage)2126 static void ZEND_FASTCALL zend_jit_extract_helper(zend_refcounted *garbage)
2127 {
2128 zend_execute_data *execute_data = EG(current_execute_data);
2129 const zend_op *opline = execute_data->opline;
2130 zval *zv = EX_VAR(opline->result.var);
2131
2132 if (EXPECTED(Z_TYPE_P(zv) == IS_INDIRECT)) {
2133 ZVAL_COPY(zv, Z_INDIRECT_P(zv));
2134 }
2135 rc_dtor_func(garbage);
2136 }
2137
zend_jit_vm_stack_free_args_helper(zend_execute_data * call)2138 static void ZEND_FASTCALL zend_jit_vm_stack_free_args_helper(zend_execute_data *call)
2139 {
2140 zend_vm_stack_free_args(call);
2141 }
2142
zend_jit_assign_to_typed_ref_helper(zend_reference * ref,zval * value,zend_uchar value_type)2143 static zend_always_inline zval* zend_jit_assign_to_typed_ref_helper(zend_reference *ref, zval *value, zend_uchar value_type)
2144 {
2145 zval variable;
2146
2147 ZVAL_REF(&variable, ref);
2148 return zend_assign_to_variable(&variable, value, value_type, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)));
2149 }
2150
zend_jit_assign_const_to_typed_ref(zend_reference * ref,zval * value)2151 static zval* ZEND_FASTCALL zend_jit_assign_const_to_typed_ref(zend_reference *ref, zval *value)
2152 {
2153 return zend_jit_assign_to_typed_ref_helper(ref, value, IS_CONST);
2154 }
2155
zend_jit_assign_tmp_to_typed_ref(zend_reference * ref,zval * value)2156 static zval* ZEND_FASTCALL zend_jit_assign_tmp_to_typed_ref(zend_reference *ref, zval *value)
2157 {
2158 return zend_jit_assign_to_typed_ref_helper(ref, value, IS_TMP_VAR);
2159 }
2160
zend_jit_assign_var_to_typed_ref(zend_reference * ref,zval * value)2161 static zval* ZEND_FASTCALL zend_jit_assign_var_to_typed_ref(zend_reference *ref, zval *value)
2162 {
2163 return zend_jit_assign_to_typed_ref_helper(ref, value, IS_VAR);
2164 }
2165
zend_jit_assign_cv_to_typed_ref(zend_reference * ref,zval * value)2166 static zval* ZEND_FASTCALL zend_jit_assign_cv_to_typed_ref(zend_reference *ref, zval *value)
2167 {
2168 if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2169 const zend_op *opline = EG(current_execute_data)->opline;
2170 uint32_t var;
2171 if (opline->opcode == ZEND_ASSIGN) {
2172 var = opline->op2.var;
2173 } else {
2174 ZEND_ASSERT((opline + 1)->opcode == ZEND_OP_DATA);
2175 var = (opline + 1)->op1.var;
2176 }
2177 zend_jit_undefined_op_helper(var);
2178 value = &EG(uninitialized_zval);
2179 }
2180 return zend_jit_assign_to_typed_ref_helper(ref, value, IS_CV);
2181 }
2182
2183
zend_jit_get_prop_not_accepting_double(zend_reference * ref)2184 static zend_property_info *zend_jit_get_prop_not_accepting_double(zend_reference *ref)
2185 {
2186 zend_property_info *prop;
2187 ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) {
2188 if (!(ZEND_TYPE_FULL_MASK(prop->type) & MAY_BE_DOUBLE)) {
2189 return prop;
2190 }
2191 } ZEND_REF_FOREACH_TYPE_SOURCES_END();
2192 return NULL;
2193 }
2194
zend_jit_throw_inc_ref_error(zend_reference * ref,zend_property_info * error_prop)2195 static ZEND_COLD void zend_jit_throw_inc_ref_error(zend_reference *ref, zend_property_info *error_prop)
2196 {
2197 zend_string *type_str = zend_type_to_string(error_prop->type);
2198
2199 zend_type_error(
2200 "Cannot increment a reference held by property %s::$%s of type %s past its maximal value",
2201 ZSTR_VAL(error_prop->ce->name),
2202 zend_get_unmangled_property_name(error_prop->name),
2203 ZSTR_VAL(type_str));
2204 zend_string_release(type_str);
2205 }
2206
zend_jit_throw_dec_ref_error(zend_reference * ref,zend_property_info * error_prop)2207 static ZEND_COLD void zend_jit_throw_dec_ref_error(zend_reference *ref, zend_property_info *error_prop)
2208 {
2209 zend_string *type_str = zend_type_to_string(error_prop->type);
2210
2211 zend_type_error(
2212 "Cannot decrement a reference held by property %s::$%s of type %s past its minimal value",
2213 ZSTR_VAL(error_prop->ce->name),
2214 zend_get_unmangled_property_name(error_prop->name),
2215 ZSTR_VAL(type_str));
2216 zend_string_release(type_str);
2217 }
2218
zend_jit_pre_inc_typed_ref(zend_reference * ref,zval * ret)2219 static void ZEND_FASTCALL zend_jit_pre_inc_typed_ref(zend_reference *ref, zval *ret)
2220 {
2221 zval *var_ptr = &ref->val;
2222 zval tmp;
2223
2224 ZVAL_COPY(&tmp, var_ptr);
2225
2226 increment_function(var_ptr);
2227
2228 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2229 zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2230 if (UNEXPECTED(error_prop)) {
2231 zend_jit_throw_inc_ref_error(ref, error_prop);
2232 ZVAL_LONG(var_ptr, ZEND_LONG_MAX);
2233 }
2234 } else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2235 zval_ptr_dtor(var_ptr);
2236 ZVAL_COPY_VALUE(var_ptr, &tmp);
2237 } else {
2238 zval_ptr_dtor(&tmp);
2239 }
2240 if (ret) {
2241 ZVAL_COPY(ret, var_ptr);
2242 }
2243 }
2244
zend_jit_pre_dec_typed_ref(zend_reference * ref,zval * ret)2245 static void ZEND_FASTCALL zend_jit_pre_dec_typed_ref(zend_reference *ref, zval *ret)
2246 {
2247 zval *var_ptr = &ref->val;
2248 zval tmp;
2249
2250 ZVAL_COPY(&tmp, var_ptr);
2251
2252 decrement_function(var_ptr);
2253
2254 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2255 zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2256 if (UNEXPECTED(error_prop)) {
2257 zend_jit_throw_dec_ref_error(ref, error_prop);
2258 ZVAL_LONG(var_ptr, ZEND_LONG_MIN);
2259 }
2260 } else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2261 zval_ptr_dtor(var_ptr);
2262 ZVAL_COPY_VALUE(var_ptr, &tmp);
2263 } else {
2264 zval_ptr_dtor(&tmp);
2265 }
2266 if (ret) {
2267 ZVAL_COPY(ret, var_ptr);
2268 }
2269 }
2270
zend_jit_post_inc_typed_ref(zend_reference * ref,zval * ret)2271 static void ZEND_FASTCALL zend_jit_post_inc_typed_ref(zend_reference *ref, zval *ret)
2272 {
2273 zval *var_ptr = &ref->val;
2274 ZVAL_COPY(ret, var_ptr);
2275
2276 increment_function(var_ptr);
2277
2278 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(ret) == IS_LONG) {
2279 zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2280 if (UNEXPECTED(error_prop)) {
2281 zend_jit_throw_inc_ref_error(ref, error_prop);
2282 ZVAL_LONG(var_ptr, ZEND_LONG_MAX);
2283 }
2284 } else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2285 zval_ptr_dtor(var_ptr);
2286 ZVAL_COPY_VALUE(var_ptr, ret);
2287 }
2288 }
2289
zend_jit_post_dec_typed_ref(zend_reference * ref,zval * ret)2290 static void ZEND_FASTCALL zend_jit_post_dec_typed_ref(zend_reference *ref, zval *ret)
2291 {
2292 zval *var_ptr = &ref->val;
2293 ZVAL_COPY(ret, var_ptr);
2294
2295 decrement_function(var_ptr);
2296
2297 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(ret) == IS_LONG) {
2298 zend_property_info *error_prop = zend_jit_get_prop_not_accepting_double(ref);
2299 if (UNEXPECTED(error_prop)) {
2300 zend_jit_throw_dec_ref_error(ref, error_prop);
2301 ZVAL_LONG(var_ptr, ZEND_LONG_MIN);
2302 }
2303 } else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2304 zval_ptr_dtor(var_ptr);
2305 ZVAL_COPY_VALUE(var_ptr, ret);
2306 }
2307 }
2308
zend_jit_assign_op_to_typed_ref(zend_reference * ref,zval * val,binary_op_type binary_op)2309 static void ZEND_FASTCALL zend_jit_assign_op_to_typed_ref(zend_reference *ref, zval *val, binary_op_type binary_op)
2310 {
2311 zval z_copy;
2312
2313 /* Make sure that in-place concatenation is used if the LHS is a string. */
2314 if (binary_op == concat_function && Z_TYPE(ref->val) == IS_STRING) {
2315 concat_function(&ref->val, &ref->val, val);
2316 ZEND_ASSERT(Z_TYPE(ref->val) == IS_STRING && "Concat should return string");
2317 return;
2318 }
2319
2320 binary_op(&z_copy, &ref->val, val);
2321 if (EXPECTED(zend_verify_ref_assignable_zval(ref, &z_copy, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2322 zval_ptr_dtor(&ref->val);
2323 ZVAL_COPY_VALUE(&ref->val, &z_copy);
2324 } else {
2325 zval_ptr_dtor(&z_copy);
2326 }
2327 }
2328
zend_jit_assign_op_to_typed_ref_tmp(zend_reference * ref,zval * val,binary_op_type binary_op)2329 static void ZEND_FASTCALL zend_jit_assign_op_to_typed_ref_tmp(zend_reference *ref, zval *val, binary_op_type binary_op)
2330 {
2331 zval z_copy;
2332
2333 binary_op(&z_copy, &ref->val, val);
2334 if (EXPECTED(zend_verify_ref_assignable_zval(ref, &z_copy, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
2335 zval_ptr_dtor(&ref->val);
2336 ZVAL_COPY_VALUE(&ref->val, &z_copy);
2337 } else {
2338 zval_ptr_dtor(&z_copy);
2339 }
2340 zval_ptr_dtor_nogc(val);
2341 }
2342
zend_jit_only_vars_by_reference(zval * arg)2343 static void ZEND_FASTCALL zend_jit_only_vars_by_reference(zval *arg)
2344 {
2345 ZVAL_NEW_REF(arg, arg);
2346 zend_error(E_NOTICE, "Only variables should be passed by reference");
2347 }
2348
zend_jit_invalid_array_access(zval * container)2349 static void ZEND_FASTCALL zend_jit_invalid_array_access(zval *container)
2350 {
2351 zend_error(E_WARNING, "Trying to access array offset on value of type %s", zend_zval_type_name(container));
2352 }
2353
zend_jit_invalid_property_read(zval * container,const char * property_name)2354 static void ZEND_FASTCALL zend_jit_invalid_property_read(zval *container, const char *property_name)
2355 {
2356 zend_error(E_WARNING, "Attempt to read property \"%s\" on %s", property_name, zend_zval_type_name(container));
2357 }
2358
zend_jit_invalid_property_write(zval * container,const char * property_name)2359 static void ZEND_FASTCALL zend_jit_invalid_property_write(zval *container, const char *property_name)
2360 {
2361 zend_throw_error(NULL,
2362 "Attempt to modify property \"%s\" on %s",
2363 property_name, zend_zval_type_name(container));
2364 }
2365
zend_jit_invalid_property_incdec(zval * container,const char * property_name)2366 static void ZEND_FASTCALL zend_jit_invalid_property_incdec(zval *container, const char *property_name)
2367 {
2368 zend_execute_data *execute_data = EG(current_execute_data);
2369 const zend_op *opline = EX(opline);
2370
2371 if (Z_TYPE_P(container) == IS_UNDEF && opline->op1_type == IS_CV) {
2372 zend_string *cv = EX(func)->op_array.vars[EX_VAR_TO_NUM(opline->op1.var)];
2373
2374 zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(cv));
2375 }
2376 if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
2377 ZVAL_UNDEF(EX_VAR(opline->result.var));
2378 }
2379 zend_throw_error(NULL,
2380 "Attempt to increment/decrement property \"%s\" on %s",
2381 property_name, zend_zval_type_name(container));
2382 if (opline->op1_type == IS_VAR) {
2383 zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
2384 }
2385 }
2386
zend_jit_invalid_property_assign(zval * container,const char * property_name)2387 static void ZEND_FASTCALL zend_jit_invalid_property_assign(zval *container, const char *property_name)
2388 {
2389 zend_throw_error(NULL,
2390 "Attempt to assign property \"%s\" on %s",
2391 property_name, zend_zval_type_name(container));
2392 }
2393
zend_jit_invalid_property_assign_op(zval * container,const char * property_name)2394 static void ZEND_FASTCALL zend_jit_invalid_property_assign_op(zval *container, const char *property_name)
2395 {
2396 if (Z_TYPE_P(container) == IS_UNDEF) {
2397 const zend_execute_data *execute_data = EG(current_execute_data);
2398
2399 zend_jit_undefined_op_helper(EX(opline)->op1.var);
2400 }
2401 zend_jit_invalid_property_assign(container, property_name);
2402 }
2403
zend_jit_prepare_assign_dim_ref(zval * ref)2404 static zval * ZEND_FASTCALL zend_jit_prepare_assign_dim_ref(zval *ref) {
2405 zval *val = Z_REFVAL_P(ref);
2406 if (Z_TYPE_P(val) <= IS_FALSE) {
2407 if (ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(ref))
2408 && !zend_verify_ref_array_assignable(Z_REF_P(ref))) {
2409 return NULL;
2410 }
2411 if (Z_TYPE_P(val) == IS_FALSE) {
2412 ZVAL_ARR(val, zend_new_array(8));
2413 zend_false_to_array_deprecated();
2414 if (EG(exception)) {
2415 return NULL;
2416 }
2417 } else {
2418 ZVAL_ARR(val, zend_new_array(8));
2419 }
2420 }
2421 return val;
2422 }
2423
zend_jit_pre_inc(zval * var_ptr,zval * ret)2424 static void ZEND_FASTCALL zend_jit_pre_inc(zval *var_ptr, zval *ret)
2425 {
2426 increment_function(var_ptr);
2427 ZVAL_COPY(ret, var_ptr);
2428 }
2429
zend_jit_pre_dec(zval * var_ptr,zval * ret)2430 static void ZEND_FASTCALL zend_jit_pre_dec(zval *var_ptr, zval *ret)
2431 {
2432 decrement_function(var_ptr);
2433 ZVAL_COPY(ret, var_ptr);
2434 }
2435
2436 #define HT_POISONED_PTR ((HashTable *) (intptr_t) -1)
2437
_zend_hash_iterators_remove(HashTable * ht)2438 static zend_never_inline void ZEND_FASTCALL _zend_hash_iterators_remove(HashTable *ht)
2439 {
2440 HashTableIterator *iter = EG(ht_iterators);
2441 HashTableIterator *end = iter + EG(ht_iterators_used);
2442
2443 while (iter != end) {
2444 if (iter->ht == ht) {
2445 iter->ht = HT_POISONED_PTR;
2446 }
2447 iter++;
2448 }
2449 }
2450
zend_jit_array_free(HashTable * ht)2451 static void ZEND_FASTCALL zend_jit_array_free(HashTable *ht)
2452 {
2453 GC_REMOVE_FROM_BUFFER(ht);
2454 if (UNEXPECTED(HT_HAS_ITERATORS(ht))) {
2455 _zend_hash_iterators_remove(ht);
2456 }
2457 if (!(EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED))) {
2458 efree(HT_GET_DATA_ADDR(ht));
2459 }
2460 FREE_HASHTABLE(ht);
2461 }
2462
zend_jit_zval_array_dup(zval * arr)2463 static HashTable *ZEND_FASTCALL zend_jit_zval_array_dup(zval *arr)
2464 {
2465 HashTable *ht;
2466
2467 Z_TRY_DELREF_P(arr);
2468 ht = Z_ARRVAL_P(arr);
2469 ht = zend_array_dup(ht);
2470 ZVAL_ARR(arr, ht);
2471 return ht;
2472 }
2473
zend_jit_add_arrays_helper(zend_array * op1,zend_array * op2)2474 static zend_array *ZEND_FASTCALL zend_jit_add_arrays_helper(zend_array *op1, zend_array *op2)
2475 {
2476 zend_array *res;
2477 res = zend_array_dup(op1);
2478 zend_hash_merge(res, op2, zval_add_ref, 0);
2479 return res;
2480 }
2481
zend_jit_assign_obj_helper(zend_object * zobj,zend_string * name,zval * value,void ** cache_slot,zval * result)2482 static void ZEND_FASTCALL zend_jit_assign_obj_helper(zend_object *zobj, zend_string *name, zval *value, void **cache_slot, zval *result)
2483 {
2484 if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2485 const zend_op *op_data = EG(current_execute_data)->opline + 1;
2486 ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
2487 zend_jit_undefined_op_helper(op_data->op1.var);
2488 value = &EG(uninitialized_zval);
2489 }
2490
2491 ZVAL_DEREF(value);
2492 value = zobj->handlers->write_property(zobj, name, value, cache_slot);
2493 if (result) {
2494 ZVAL_COPY_DEREF(result, value);
2495 }
2496 }
2497
zend_jit_assign_to_typed_prop(zval * property_val,zend_property_info * info,zval * value,zval * result)2498 static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend_property_info *info, zval *value, zval *result)
2499 {
2500 zend_execute_data *execute_data = EG(current_execute_data);
2501 zval tmp;
2502
2503 if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) {
2504 const zend_op *op_data = execute_data->opline + 1;
2505 ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV);
2506 zend_jit_undefined_op_helper(op_data->op1.var);
2507 value = &EG(uninitialized_zval);
2508 }
2509
2510 if (UNEXPECTED(info->flags & ZEND_ACC_READONLY)) {
2511 zend_readonly_property_modification_error(info);
2512 if (result) {
2513 ZVAL_UNDEF(result);
2514 }
2515 return;
2516 }
2517
2518 ZVAL_DEREF(value);
2519 ZVAL_COPY(&tmp, value);
2520
2521 if (UNEXPECTED(!zend_verify_property_type(info, &tmp, EX_USES_STRICT_TYPES()))) {
2522 zval_ptr_dtor(&tmp);
2523 if (result) {
2524 ZVAL_NULL(result);
2525 }
2526 return;
2527 }
2528
2529 value = zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES());
2530 if (result) {
2531 ZVAL_COPY_DEREF(result, value);
2532 }
2533 }
2534
_zend_jit_assign_op_overloaded_property(zend_object * object,zend_string * name,void ** cache_slot,zval * value,binary_op_type binary_op)2535 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)
2536 {
2537 zval *z;
2538 zval rv, res;
2539
2540 GC_ADDREF(object);
2541 z = object->handlers->read_property(object, name, BP_VAR_R, cache_slot, &rv);
2542 if (UNEXPECTED(EG(exception))) {
2543 OBJ_RELEASE(object);
2544 //??? if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2545 //??? ZVAL_UNDEF(EX_VAR(opline->result.var));
2546 //??? }
2547 return;
2548 }
2549 if (binary_op(&res, z, value) == SUCCESS) {
2550 object->handlers->write_property(object, name, &res, cache_slot);
2551 }
2552 //??? if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2553 //??? ZVAL_COPY(EX_VAR(opline->result.var), &res);
2554 //??? }
2555 if (z == &rv) {
2556 zval_ptr_dtor(z);
2557 }
2558 zval_ptr_dtor(&res);
2559 OBJ_RELEASE(object);
2560 }
2561
zend_jit_assign_op_to_typed_prop(zval * zptr,zend_property_info * prop_info,zval * value,binary_op_type binary_op)2562 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)
2563 {
2564 zend_execute_data *execute_data = EG(current_execute_data);
2565 zval z_copy;
2566
2567 if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
2568 zend_readonly_property_modification_error(prop_info);
2569 return;
2570 }
2571
2572 ZVAL_DEREF(zptr);
2573 /* Make sure that in-place concatenation is used if the LHS is a string. */
2574 if (binary_op == concat_function && Z_TYPE_P(zptr) == IS_STRING) {
2575 concat_function(zptr, zptr, value);
2576 ZEND_ASSERT(Z_TYPE_P(zptr) == IS_STRING && "Concat should return string");
2577 return;
2578 }
2579
2580 binary_op(&z_copy, zptr, value);
2581 if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) {
2582 zval_ptr_dtor(zptr);
2583 ZVAL_COPY_VALUE(zptr, &z_copy);
2584 } else {
2585 zval_ptr_dtor(&z_copy);
2586 }
2587 }
2588
zend_jit_assign_obj_op_helper(zend_object * zobj,zend_string * name,zval * value,void ** cache_slot,binary_op_type binary_op)2589 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)
2590 {
2591 zval *zptr;
2592 zend_property_info *prop_info;
2593
2594 if (EXPECTED((zptr = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2595 if (UNEXPECTED(Z_ISERROR_P(zptr))) {
2596 //??? if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2597 //??? ZVAL_NULL(EX_VAR(opline->result.var));
2598 //??? }
2599 } else {
2600 //??? zval *orig_zptr = zptr;
2601 zend_reference *ref;
2602
2603 do {
2604 if (UNEXPECTED(Z_ISREF_P(zptr))) {
2605 ref = Z_REF_P(zptr);
2606 zptr = Z_REFVAL_P(zptr);
2607 if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
2608 zend_jit_assign_op_to_typed_ref(ref, value, binary_op);
2609 break;
2610 }
2611 }
2612
2613 //??? if (OP2_TYPE == IS_CONST) {
2614 prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
2615 //??? } else {
2616 //??? prop_info = zend_object_fetch_property_type_info(Z_OBJ_P(object), orig_zptr);
2617 //??? }
2618 if (UNEXPECTED(prop_info)) {
2619 /* special case for typed properties */
2620 zend_jit_assign_op_to_typed_prop(zptr, prop_info, value, binary_op);
2621 } else {
2622 binary_op(zptr, zptr, value);
2623 }
2624 } while (0);
2625
2626 //??? if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
2627 //??? ZVAL_COPY(EX_VAR(opline->result.var), zptr);
2628 //??? }
2629 }
2630 } else {
2631 _zend_jit_assign_op_overloaded_property(zobj, name, cache_slot, value, binary_op);
2632 }
2633 }
2634
_zend_jit_throw_inc_prop_error(zend_property_info * prop)2635 static ZEND_COLD zend_long _zend_jit_throw_inc_prop_error(zend_property_info *prop)
2636 {
2637 zend_string *type_str = zend_type_to_string(prop->type);
2638 zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value",
2639 ZSTR_VAL(prop->ce->name),
2640 zend_get_unmangled_property_name(prop->name),
2641 ZSTR_VAL(type_str));
2642 zend_string_release(type_str);
2643 return ZEND_LONG_MAX;
2644 }
2645
_zend_jit_throw_dec_prop_error(zend_property_info * prop)2646 static ZEND_COLD zend_long _zend_jit_throw_dec_prop_error(zend_property_info *prop)
2647 {
2648 zend_string *type_str = zend_type_to_string(prop->type);
2649 zend_type_error("Cannot decrement property %s::$%s of type %s past its minimal value",
2650 ZSTR_VAL(prop->ce->name),
2651 zend_get_unmangled_property_name(prop->name),
2652 ZSTR_VAL(type_str));
2653 zend_string_release(type_str);
2654 return ZEND_LONG_MIN;
2655 }
2656
zend_jit_inc_typed_prop(zval * var_ptr,zend_property_info * prop_info)2657 static void ZEND_FASTCALL zend_jit_inc_typed_prop(zval *var_ptr, zend_property_info *prop_info)
2658 {
2659 ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2660
2661 if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY))) {
2662 zend_readonly_property_modification_error(prop_info);
2663 return;
2664 }
2665
2666 zend_execute_data *execute_data = EG(current_execute_data);
2667 zval tmp;
2668
2669 ZVAL_DEREF(var_ptr);
2670 ZVAL_COPY(&tmp, var_ptr);
2671
2672 increment_function(var_ptr);
2673
2674 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2675 if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2676 zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2677 ZVAL_LONG(var_ptr, val);
2678 }
2679 } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2680 zval_ptr_dtor(var_ptr);
2681 ZVAL_COPY_VALUE(var_ptr, &tmp);
2682 } else {
2683 zval_ptr_dtor(&tmp);
2684 }
2685 }
2686
zend_jit_dec_typed_prop(zval * var_ptr,zend_property_info * prop_info)2687 static void ZEND_FASTCALL zend_jit_dec_typed_prop(zval *var_ptr, zend_property_info *prop_info)
2688 {
2689 ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2690
2691 if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY))) {
2692 zend_readonly_property_modification_error(prop_info);
2693 return;
2694 }
2695
2696 zend_execute_data *execute_data = EG(current_execute_data);
2697 zval tmp;
2698
2699 ZVAL_DEREF(var_ptr);
2700 ZVAL_COPY(&tmp, var_ptr);
2701
2702 decrement_function(var_ptr);
2703
2704 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE(tmp) == IS_LONG) {
2705 if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2706 zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
2707 ZVAL_LONG(var_ptr, val);
2708 }
2709 } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2710 zval_ptr_dtor(var_ptr);
2711 ZVAL_COPY_VALUE(var_ptr, &tmp);
2712 } else {
2713 zval_ptr_dtor(&tmp);
2714 }
2715 }
2716
zend_jit_pre_inc_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2717 static void ZEND_FASTCALL zend_jit_pre_inc_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2718 {
2719 ZVAL_DEREF(var_ptr);
2720 zend_jit_inc_typed_prop(var_ptr, prop_info);
2721 ZVAL_COPY(result, var_ptr);
2722 }
2723
zend_jit_pre_dec_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2724 static void ZEND_FASTCALL zend_jit_pre_dec_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2725 {
2726 ZVAL_DEREF(var_ptr);
2727 zend_jit_dec_typed_prop(var_ptr, prop_info);
2728 ZVAL_COPY(result, var_ptr);
2729 }
2730
zend_jit_post_inc_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2731 static void ZEND_FASTCALL zend_jit_post_inc_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2732 {
2733 ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2734
2735 if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY))) {
2736 zend_readonly_property_modification_error(prop_info);
2737 if (result) {
2738 ZVAL_UNDEF(result);
2739 }
2740 return;
2741 }
2742
2743 zend_execute_data *execute_data = EG(current_execute_data);
2744
2745 ZVAL_DEREF(var_ptr);
2746 ZVAL_COPY(result, var_ptr);
2747
2748 increment_function(var_ptr);
2749
2750 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(result) == IS_LONG) {
2751 if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2752 zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2753 ZVAL_LONG(var_ptr, val);
2754 }
2755 } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2756 zval_ptr_dtor(var_ptr);
2757 ZVAL_COPY_VALUE(var_ptr, result);
2758 ZVAL_UNDEF(result);
2759 }
2760 }
2761
zend_jit_post_dec_typed_prop(zval * var_ptr,zend_property_info * prop_info,zval * result)2762 static void ZEND_FASTCALL zend_jit_post_dec_typed_prop(zval *var_ptr, zend_property_info *prop_info, zval *result)
2763 {
2764 ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
2765
2766 if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY))) {
2767 zend_readonly_property_modification_error(prop_info);
2768 if (result) {
2769 ZVAL_UNDEF(result);
2770 }
2771 return;
2772 }
2773
2774 zend_execute_data *execute_data = EG(current_execute_data);
2775
2776 ZVAL_DEREF(var_ptr);
2777 ZVAL_COPY(result, var_ptr);
2778
2779 decrement_function(var_ptr);
2780
2781 if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(result) == IS_LONG) {
2782 if (!(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2783 zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
2784 ZVAL_LONG(var_ptr, val);
2785 }
2786 } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) {
2787 zval_ptr_dtor(var_ptr);
2788 ZVAL_COPY_VALUE(var_ptr, result);
2789 ZVAL_UNDEF(result);
2790 }
2791 }
2792
zend_jit_pre_inc_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)2793 static void ZEND_FASTCALL zend_jit_pre_inc_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
2794 {
2795 zval *prop;
2796
2797 if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2798 if (UNEXPECTED(Z_ISERROR_P(prop))) {
2799 if (UNEXPECTED(result)) {
2800 ZVAL_NULL(result);
2801 }
2802 } else {
2803 zend_property_info *prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2);
2804
2805 if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
2806 fast_long_increment_function(prop);
2807 if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
2808 && !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2809 zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2810 ZVAL_LONG(prop, val);
2811 }
2812 } else {
2813 do {
2814 if (Z_ISREF_P(prop)) {
2815 zend_reference *ref = Z_REF_P(prop);
2816 prop = Z_REFVAL_P(prop);
2817 if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
2818 zend_jit_pre_inc_typed_ref(ref, result);
2819 break;
2820 }
2821 }
2822
2823 if (UNEXPECTED(prop_info)) {
2824 zend_jit_inc_typed_prop(prop, prop_info);
2825 } else {
2826 increment_function(prop);
2827 }
2828 } while (0);
2829 }
2830 if (UNEXPECTED(result)) {
2831 ZVAL_COPY(result, prop);
2832 }
2833 }
2834 } else {
2835 zval rv;
2836 zval *z;
2837 zval z_copy;
2838
2839 GC_ADDREF(zobj);
2840 z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
2841 if (UNEXPECTED(EG(exception))) {
2842 OBJ_RELEASE(zobj);
2843 if (UNEXPECTED(result)) {
2844 ZVAL_NULL(result);
2845 }
2846 return;
2847 }
2848
2849 ZVAL_COPY_DEREF(&z_copy, z);
2850 increment_function(&z_copy);
2851 if (UNEXPECTED(result)) {
2852 ZVAL_COPY(result, &z_copy);
2853 }
2854 zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
2855 OBJ_RELEASE(zobj);
2856 zval_ptr_dtor(&z_copy);
2857 if (z == &rv) {
2858 zval_ptr_dtor(z);
2859 }
2860 }
2861 }
2862
zend_jit_pre_dec_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)2863 static void ZEND_FASTCALL zend_jit_pre_dec_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
2864 {
2865 zval *prop;
2866
2867 if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2868 if (UNEXPECTED(Z_ISERROR_P(prop))) {
2869 if (UNEXPECTED(result)) {
2870 ZVAL_NULL(result);
2871 }
2872 } else {
2873 zend_property_info *prop_info = (zend_property_info *) CACHED_PTR_EX(cache_slot + 2);
2874
2875 if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
2876 fast_long_decrement_function(prop);
2877 if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
2878 && !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2879 zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
2880 ZVAL_LONG(prop, val);
2881 }
2882 } else {
2883 do {
2884 if (Z_ISREF_P(prop)) {
2885 zend_reference *ref = Z_REF_P(prop);
2886 prop = Z_REFVAL_P(prop);
2887 if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) {
2888 zend_jit_pre_dec_typed_ref(ref, result);
2889 break;
2890 }
2891 }
2892
2893 if (UNEXPECTED(prop_info)) {
2894 zend_jit_dec_typed_prop(prop, prop_info);
2895 } else {
2896 decrement_function(prop);
2897 }
2898 } while (0);
2899 }
2900 if (UNEXPECTED(result)) {
2901 ZVAL_COPY(result, prop);
2902 }
2903 }
2904 } else {
2905 zval rv;
2906 zval *z;
2907 zval z_copy;
2908
2909 GC_ADDREF(zobj);
2910 z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
2911 if (UNEXPECTED(EG(exception))) {
2912 OBJ_RELEASE(zobj);
2913 if (UNEXPECTED(result)) {
2914 ZVAL_NULL(result);
2915 }
2916 return;
2917 }
2918
2919 ZVAL_COPY_DEREF(&z_copy, z);
2920 decrement_function(&z_copy);
2921 if (UNEXPECTED(result)) {
2922 ZVAL_COPY(result, &z_copy);
2923 }
2924 zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
2925 OBJ_RELEASE(zobj);
2926 zval_ptr_dtor(&z_copy);
2927 if (z == &rv) {
2928 zval_ptr_dtor(z);
2929 }
2930 }
2931 }
2932
zend_jit_post_inc_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)2933 static void ZEND_FASTCALL zend_jit_post_inc_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
2934 {
2935 zval *prop;
2936
2937 if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2938 if (UNEXPECTED(Z_ISERROR_P(prop))) {
2939 ZVAL_NULL(result);
2940 } else {
2941 zend_property_info *prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
2942
2943 if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
2944 ZVAL_LONG(result, Z_LVAL_P(prop));
2945 fast_long_increment_function(prop);
2946 if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
2947 && !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
2948 zend_long val = _zend_jit_throw_inc_prop_error(prop_info);
2949 ZVAL_LONG(prop, val);
2950 }
2951 } else {
2952 if (Z_ISREF_P(prop)) {
2953 zend_reference *ref = Z_REF_P(prop);
2954 prop = Z_REFVAL_P(prop);
2955 if (ZEND_REF_HAS_TYPE_SOURCES(ref)) {
2956 zend_jit_post_inc_typed_ref(ref, result);
2957 return;
2958 }
2959 }
2960
2961 if (UNEXPECTED(prop_info)) {
2962 zend_jit_post_inc_typed_prop(prop, prop_info, result);
2963 } else {
2964 ZVAL_COPY(result, prop);
2965 increment_function(prop);
2966 }
2967 }
2968 }
2969 } else {
2970 zval rv;
2971 zval *z;
2972 zval z_copy;
2973
2974 GC_ADDREF(zobj);
2975 z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
2976 if (UNEXPECTED(EG(exception))) {
2977 OBJ_RELEASE(zobj);
2978 ZVAL_UNDEF(result);
2979 return;
2980 }
2981
2982 ZVAL_COPY_DEREF(&z_copy, z);
2983 ZVAL_COPY(result, &z_copy);
2984 increment_function(&z_copy);
2985 zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
2986 OBJ_RELEASE(zobj);
2987 zval_ptr_dtor(&z_copy);
2988 if (z == &rv) {
2989 zval_ptr_dtor(z);
2990 }
2991 }
2992 }
2993
zend_jit_post_dec_obj_helper(zend_object * zobj,zend_string * name,void ** cache_slot,zval * result)2994 static void ZEND_FASTCALL zend_jit_post_dec_obj_helper(zend_object *zobj, zend_string *name, void **cache_slot, zval *result)
2995 {
2996 zval *prop;
2997
2998 if (EXPECTED((prop = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_RW, cache_slot)) != NULL)) {
2999 if (UNEXPECTED(Z_ISERROR_P(prop))) {
3000 ZVAL_NULL(result);
3001 } else {
3002 zend_property_info *prop_info = (zend_property_info*)CACHED_PTR_EX(cache_slot + 2);
3003
3004 if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) {
3005 ZVAL_LONG(result, Z_LVAL_P(prop));
3006 fast_long_decrement_function(prop);
3007 if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)
3008 && !(ZEND_TYPE_FULL_MASK(prop_info->type) & MAY_BE_DOUBLE)) {
3009 zend_long val = _zend_jit_throw_dec_prop_error(prop_info);
3010 ZVAL_LONG(prop, val);
3011 }
3012 } else {
3013 if (Z_ISREF_P(prop)) {
3014 zend_reference *ref = Z_REF_P(prop);
3015 prop = Z_REFVAL_P(prop);
3016 if (ZEND_REF_HAS_TYPE_SOURCES(ref)) {
3017 zend_jit_post_dec_typed_ref(ref, result);
3018 return;
3019 }
3020 }
3021
3022 if (UNEXPECTED(prop_info)) {
3023 zend_jit_post_dec_typed_prop(prop, prop_info, result);
3024 } else {
3025 ZVAL_COPY(result, prop);
3026 decrement_function(prop);
3027 }
3028 }
3029 }
3030 } else {
3031 zval rv;
3032 zval *z;
3033 zval z_copy;
3034
3035 GC_ADDREF(zobj);
3036 z = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, &rv);
3037 if (UNEXPECTED(EG(exception))) {
3038 OBJ_RELEASE(zobj);
3039 ZVAL_UNDEF(result);
3040 return;
3041 }
3042
3043 ZVAL_COPY_DEREF(&z_copy, z);
3044 ZVAL_COPY(result, &z_copy);
3045 decrement_function(&z_copy);
3046 zobj->handlers->write_property(zobj, name, &z_copy, cache_slot);
3047 OBJ_RELEASE(zobj);
3048 zval_ptr_dtor(&z_copy);
3049 if (z == &rv) {
3050 zval_ptr_dtor(z);
3051 }
3052 }
3053 }
3054
3055 #if (PHP_VERSION_ID <= 80100) && (SIZEOF_SIZE_T == 4)
zval_jit_update_constant_ex(zval * p,zend_class_entry * scope)3056 static zend_result ZEND_FASTCALL zval_jit_update_constant_ex(zval *p, zend_class_entry *scope)
3057 {
3058 if (Z_TYPE_P(p) == IS_CONSTANT_AST) {
3059 zend_ast *ast = Z_ASTVAL_P(p);
3060
3061 if (ast->kind == ZEND_AST_CONSTANT) {
3062 zend_string *name = zend_ast_get_constant_name(ast);
3063 zval *zv = zend_get_constant_ex(name, scope, ast->attr);
3064 if (UNEXPECTED(zv == NULL)) {
3065 return FAILURE;
3066 }
3067
3068 zval_ptr_dtor_nogc(p);
3069 ZVAL_COPY_OR_DUP(p, zv);
3070 } else {
3071 zval tmp;
3072
3073 // Increase the refcount during zend_ast_evaluate to avoid releasing the ast too early
3074 // on nested calls to zval_update_constant_ex which can happen when retriggering ast
3075 // evaluation during autoloading.
3076 zend_ast_ref *ast_ref = Z_AST_P(p);
3077 bool ast_is_refcounted = !(GC_FLAGS(ast_ref) & GC_IMMUTABLE);
3078 if (ast_is_refcounted) {
3079 GC_ADDREF(ast_ref);
3080 }
3081 zend_result result = zend_ast_evaluate(&tmp, ast, scope);
3082 if (ast_is_refcounted && !GC_DELREF(ast_ref)) {
3083 rc_dtor_func((zend_refcounted *)ast_ref);
3084 }
3085 if (UNEXPECTED(result != SUCCESS)) {
3086 return FAILURE;
3087 }
3088 zval_ptr_dtor_nogc(p);
3089 ZVAL_COPY_VALUE(p, &tmp);
3090 }
3091 }
3092 return SUCCESS;
3093 }
3094 #endif
3095
zend_jit_free_trampoline_helper(zend_function * func)3096 static void ZEND_FASTCALL zend_jit_free_trampoline_helper(zend_function *func)
3097 {
3098 ZEND_ASSERT(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE);
3099 zend_string_release_ex(func->common.function_name, 0);
3100 zend_free_trampoline(func);
3101 }
3102
zend_jit_exception_in_interrupt_handler_helper(void)3103 static void ZEND_FASTCALL zend_jit_exception_in_interrupt_handler_helper(void)
3104 {
3105 if (EG(exception)) {
3106 /* We have to UNDEF result, because ZEND_HANDLE_EXCEPTION is going to free it */
3107 const zend_op *throw_op = EG(opline_before_exception);
3108
3109 if (throw_op
3110 && throw_op->result_type & (IS_TMP_VAR|IS_VAR)
3111 && throw_op->opcode != ZEND_ADD_ARRAY_ELEMENT
3112 && throw_op->opcode != ZEND_ADD_ARRAY_UNPACK
3113 && throw_op->opcode != ZEND_ROPE_INIT
3114 && throw_op->opcode != ZEND_ROPE_ADD) {
3115 ZVAL_UNDEF(ZEND_CALL_VAR(EG(current_execute_data), throw_op->result.var));
3116 }
3117 }
3118 }
3119
zend_jit_rope_end(zend_string ** rope,uint32_t count)3120 static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t count)
3121 {
3122 zend_string *ret;
3123 uint32_t i;
3124 size_t len = 0;
3125 char *target;
3126
3127 for (i = 0; i <= count; i++) {
3128 len += ZSTR_LEN(rope[i]);
3129 }
3130 ret = zend_string_alloc(len, 0);
3131 target = ZSTR_VAL(ret);
3132 for (i = 0; i <= count; i++) {
3133 memcpy(target, ZSTR_VAL(rope[i]), ZSTR_LEN(rope[i]));
3134 target += ZSTR_LEN(rope[i]);
3135 zend_string_release_ex(rope[i], 0);
3136 }
3137 *target = '\0';
3138 return ret;
3139 }
3140