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