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