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