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