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