1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Dmitry Stogov <dmitry@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include <stdio.h>
22
23 #include "zend.h"
24 #include "zend_alloc.h"
25 #include "zend_compile.h"
26 #include "zend_extensions.h"
27 #include "zend_API.h"
28 #include "zend_sort.h"
29
30 #include "zend_vm.h"
31
zend_extension_op_array_ctor_handler(zend_extension * extension,zend_op_array * op_array)32 static void zend_extension_op_array_ctor_handler(zend_extension *extension, zend_op_array *op_array)
33 {
34 if (extension->op_array_ctor) {
35 extension->op_array_ctor(op_array);
36 }
37 }
38
zend_extension_op_array_dtor_handler(zend_extension * extension,zend_op_array * op_array)39 static void zend_extension_op_array_dtor_handler(zend_extension *extension, zend_op_array *op_array)
40 {
41 if (extension->op_array_dtor) {
42 extension->op_array_dtor(op_array);
43 }
44 }
45
init_op_array(zend_op_array * op_array,zend_uchar type,int initial_ops_size)46 void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size)
47 {
48 op_array->type = type;
49 op_array->arg_flags[0] = 0;
50 op_array->arg_flags[1] = 0;
51 op_array->arg_flags[2] = 0;
52
53 op_array->refcount = (uint32_t *) emalloc(sizeof(uint32_t));
54 *op_array->refcount = 1;
55 op_array->last = 0;
56 op_array->opcodes = emalloc(initial_ops_size * sizeof(zend_op));
57
58 op_array->last_var = 0;
59 op_array->vars = NULL;
60
61 op_array->T = 0;
62
63 op_array->function_name = NULL;
64 op_array->filename = zend_string_copy(zend_get_compiled_filename());
65 op_array->doc_comment = NULL;
66 op_array->attributes = NULL;
67
68 op_array->arg_info = NULL;
69 op_array->num_args = 0;
70 op_array->required_num_args = 0;
71
72 op_array->scope = NULL;
73 op_array->prototype = NULL;
74
75 op_array->live_range = NULL;
76 op_array->try_catch_array = NULL;
77 op_array->last_live_range = 0;
78
79 op_array->static_variables = NULL;
80 ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
81 op_array->last_try_catch = 0;
82
83 op_array->fn_flags = 0;
84
85 op_array->last_literal = 0;
86 op_array->literals = NULL;
87
88 op_array->num_dynamic_func_defs = 0;
89 op_array->dynamic_func_defs = NULL;
90
91 ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
92 op_array->cache_size = zend_op_array_extension_handles * sizeof(void*);
93
94 memset(op_array->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*));
95
96 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_CTOR) {
97 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array);
98 }
99 }
100
destroy_zend_function(zend_function * function)101 ZEND_API void destroy_zend_function(zend_function *function)
102 {
103 zval tmp;
104
105 ZVAL_PTR(&tmp, function);
106 zend_function_dtor(&tmp);
107 }
108
zend_type_release(zend_type type,bool persistent)109 ZEND_API void zend_type_release(zend_type type, bool persistent) {
110 if (ZEND_TYPE_HAS_LIST(type)) {
111 zend_type *list_type;
112 ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
113 if (ZEND_TYPE_HAS_NAME(*list_type)) {
114 zend_string_release(ZEND_TYPE_NAME(*list_type));
115 }
116 } ZEND_TYPE_LIST_FOREACH_END();
117 if (!ZEND_TYPE_USES_ARENA(type)) {
118 pefree(ZEND_TYPE_LIST(type), persistent);
119 }
120 } else if (ZEND_TYPE_HAS_NAME(type)) {
121 zend_string_release(ZEND_TYPE_NAME(type));
122 }
123 }
124
zend_free_internal_arg_info(zend_internal_function * function)125 void zend_free_internal_arg_info(zend_internal_function *function) {
126 if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
127 function->arg_info) {
128
129 uint32_t i;
130 uint32_t num_args = function->num_args + 1;
131 zend_internal_arg_info *arg_info = function->arg_info - 1;
132
133 if (function->fn_flags & ZEND_ACC_VARIADIC) {
134 num_args++;
135 }
136 for (i = 0 ; i < num_args; i++) {
137 zend_type_release(arg_info[i].type, /* persistent */ 1);
138 }
139 free(arg_info);
140 }
141 }
142
zend_function_dtor(zval * zv)143 ZEND_API void zend_function_dtor(zval *zv)
144 {
145 zend_function *function = Z_PTR_P(zv);
146
147 if (function->type == ZEND_USER_FUNCTION) {
148 ZEND_ASSERT(function->common.function_name);
149 destroy_op_array(&function->op_array);
150 /* op_arrays are allocated on arena, so we don't have to free them */
151 } else {
152 ZEND_ASSERT(function->type == ZEND_INTERNAL_FUNCTION);
153 ZEND_ASSERT(function->common.function_name);
154 zend_string_release_ex(function->common.function_name, 1);
155
156 /* For methods this will be called explicitly. */
157 if (!function->common.scope) {
158 zend_free_internal_arg_info(&function->internal_function);
159 }
160
161 if (!(function->common.fn_flags & ZEND_ACC_ARENA_ALLOCATED)) {
162 pefree(function, 1);
163 }
164 }
165 }
166
zend_cleanup_internal_class_data(zend_class_entry * ce)167 ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce)
168 {
169 if (ZEND_MAP_PTR(ce->static_members_table) && CE_STATIC_MEMBERS(ce)) {
170 zval *static_members = CE_STATIC_MEMBERS(ce);
171 zval *p = static_members;
172 zval *end = p + ce->default_static_members_count;
173 ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
174 while (p != end) {
175 if (UNEXPECTED(Z_ISREF_P(p))) {
176 zend_property_info *prop_info;
177 ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
178 if (prop_info->ce == ce && p - static_members == prop_info->offset) {
179 ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
180 break; /* stop iteration here, the array might be realloc()'ed */
181 }
182 } ZEND_REF_FOREACH_TYPE_SOURCES_END();
183 }
184 i_zval_ptr_dtor(p);
185 p++;
186 }
187 efree(static_members);
188 }
189 }
190
_destroy_zend_class_traits_info(zend_class_entry * ce)191 static void _destroy_zend_class_traits_info(zend_class_entry *ce)
192 {
193 uint32_t i;
194
195 for (i = 0; i < ce->num_traits; i++) {
196 zend_string_release_ex(ce->trait_names[i].name, 0);
197 zend_string_release_ex(ce->trait_names[i].lc_name, 0);
198 }
199 efree(ce->trait_names);
200
201 if (ce->trait_aliases) {
202 i = 0;
203 while (ce->trait_aliases[i]) {
204 if (ce->trait_aliases[i]->trait_method.method_name) {
205 zend_string_release_ex(ce->trait_aliases[i]->trait_method.method_name, 0);
206 }
207 if (ce->trait_aliases[i]->trait_method.class_name) {
208 zend_string_release_ex(ce->trait_aliases[i]->trait_method.class_name, 0);
209 }
210
211 if (ce->trait_aliases[i]->alias) {
212 zend_string_release_ex(ce->trait_aliases[i]->alias, 0);
213 }
214
215 efree(ce->trait_aliases[i]);
216 i++;
217 }
218
219 efree(ce->trait_aliases);
220 }
221
222 if (ce->trait_precedences) {
223 uint32_t j;
224
225 i = 0;
226 while (ce->trait_precedences[i]) {
227 zend_string_release_ex(ce->trait_precedences[i]->trait_method.method_name, 0);
228 zend_string_release_ex(ce->trait_precedences[i]->trait_method.class_name, 0);
229
230 for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) {
231 zend_string_release_ex(ce->trait_precedences[i]->exclude_class_names[j], 0);
232 }
233 efree(ce->trait_precedences[i]);
234 i++;
235 }
236 efree(ce->trait_precedences);
237 }
238 }
239
zend_cleanup_mutable_class_data(zend_class_entry * ce)240 ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
241 {
242 zend_class_mutable_data *mutable_data = ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
243
244 if (mutable_data) {
245 HashTable *constants_table;
246 zval *p;
247
248 constants_table = mutable_data->constants_table;
249 if (constants_table && constants_table != &ce->constants_table) {
250 zend_class_constant *c;
251
252 ZEND_HASH_FOREACH_PTR(constants_table, c) {
253 if (c->ce == ce) {
254 zval_ptr_dtor_nogc(&c->value);
255 }
256 } ZEND_HASH_FOREACH_END();
257 zend_hash_destroy(constants_table);
258 mutable_data->constants_table = NULL;
259 }
260
261 p = mutable_data->default_properties_table;
262 if (p && p != ce->default_properties_table) {
263 zval *end = p + ce->default_properties_count;
264
265 while (p < end) {
266 zval_ptr_dtor_nogc(p);
267 p++;
268 }
269 mutable_data->default_properties_table = NULL;
270 }
271
272 ZEND_MAP_PTR_SET_IMM(ce->mutable_data, NULL);
273 }
274 }
275
destroy_zend_class(zval * zv)276 ZEND_API void destroy_zend_class(zval *zv)
277 {
278 zend_property_info *prop_info;
279 zend_class_entry *ce = Z_PTR_P(zv);
280 zend_function *fn;
281
282 if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
283 return;
284 }
285
286 if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
287 zend_class_constant *c;
288 zval *p, *end;
289
290 ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
291 if (c->ce == ce) {
292 zval_ptr_dtor_nogc(&c->value);
293 }
294 } ZEND_HASH_FOREACH_END();
295
296 p = ce->default_properties_table;
297 end = p + ce->default_properties_count;
298
299 while (p < end) {
300 zval_ptr_dtor_nogc(p);
301 p++;
302 }
303 return;
304 }
305
306 if (--ce->refcount > 0) {
307 return;
308 }
309
310 switch (ce->type) {
311 case ZEND_USER_CLASS:
312 if (!(ce->ce_flags & ZEND_ACC_CACHED)) {
313 if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) {
314 zend_string_release_ex(ce->parent_name, 0);
315 }
316
317 zend_string_release_ex(ce->name, 0);
318 zend_string_release_ex(ce->info.user.filename, 0);
319
320 if (ce->info.user.doc_comment) {
321 zend_string_release_ex(ce->info.user.doc_comment, 0);
322 }
323
324 if (ce->attributes) {
325 zend_hash_release(ce->attributes);
326 }
327 if (ce->backed_enum_table) {
328 zend_hash_release(ce->backed_enum_table);
329 }
330
331 if (ce->num_interfaces > 0 && !(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
332 uint32_t i;
333
334 for (i = 0; i < ce->num_interfaces; i++) {
335 zend_string_release_ex(ce->interface_names[i].name, 0);
336 zend_string_release_ex(ce->interface_names[i].lc_name, 0);
337 }
338 efree(ce->interface_names);
339 }
340
341 if (ce->num_traits > 0) {
342 _destroy_zend_class_traits_info(ce);
343 }
344 }
345
346 if (ce->default_properties_table) {
347 zval *p = ce->default_properties_table;
348 zval *end = p + ce->default_properties_count;
349
350 while (p != end) {
351 i_zval_ptr_dtor(p);
352 p++;
353 }
354 efree(ce->default_properties_table);
355 }
356 if (ce->default_static_members_table) {
357 zval *p = ce->default_static_members_table;
358 zval *end = p + ce->default_static_members_count;
359
360 while (p != end) {
361 ZEND_ASSERT(!Z_ISREF_P(p));
362 i_zval_ptr_dtor(p);
363 p++;
364 }
365 efree(ce->default_static_members_table);
366 }
367 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
368 if (prop_info->ce == ce) {
369 zend_string_release_ex(prop_info->name, 0);
370 if (prop_info->doc_comment) {
371 zend_string_release_ex(prop_info->doc_comment, 0);
372 }
373 if (prop_info->attributes) {
374 zend_hash_release(prop_info->attributes);
375 }
376 zend_type_release(prop_info->type, /* persistent */ 0);
377 }
378 } ZEND_HASH_FOREACH_END();
379 zend_hash_destroy(&ce->properties_info);
380 zend_hash_destroy(&ce->function_table);
381 if (zend_hash_num_elements(&ce->constants_table)) {
382 zend_class_constant *c;
383
384 ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
385 if (c->ce == ce) {
386 zval_ptr_dtor_nogc(&c->value);
387 if (c->doc_comment) {
388 zend_string_release_ex(c->doc_comment, 0);
389 }
390 if (c->attributes) {
391 zend_hash_release(c->attributes);
392 }
393 }
394 } ZEND_HASH_FOREACH_END();
395 }
396 zend_hash_destroy(&ce->constants_table);
397 if (ce->num_interfaces > 0 && (ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
398 efree(ce->interfaces);
399 }
400 break;
401 case ZEND_INTERNAL_CLASS:
402 if (ce->backed_enum_table) {
403 zend_hash_release(ce->backed_enum_table);
404 }
405 if (ce->default_properties_table) {
406 zval *p = ce->default_properties_table;
407 zval *end = p + ce->default_properties_count;
408
409 while (p != end) {
410 zval_internal_ptr_dtor(p);
411 p++;
412 }
413 free(ce->default_properties_table);
414 }
415 if (ce->default_static_members_table) {
416 zval *p = ce->default_static_members_table;
417 zval *end = p + ce->default_static_members_count;
418
419 while (p != end) {
420 zval_internal_ptr_dtor(p);
421 p++;
422 }
423 free(ce->default_static_members_table);
424 }
425
426 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
427 if (prop_info->ce == ce) {
428 zend_string_release(prop_info->name);
429 zend_type_release(prop_info->type, /* persistent */ 1);
430 free(prop_info);
431 }
432 } ZEND_HASH_FOREACH_END();
433 zend_hash_destroy(&ce->properties_info);
434 zend_string_release_ex(ce->name, 1);
435
436 /* TODO: eliminate this loop for classes without functions with arg_info */
437 ZEND_HASH_FOREACH_PTR(&ce->function_table, fn) {
438 if ((fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
439 fn->common.scope == ce) {
440 zend_free_internal_arg_info(&fn->internal_function);
441 }
442 } ZEND_HASH_FOREACH_END();
443
444 zend_hash_destroy(&ce->function_table);
445 if (zend_hash_num_elements(&ce->constants_table)) {
446 zend_class_constant *c;
447
448 ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
449 if (c->ce == ce) {
450 if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
451 /* We marked this as IMMUTABLE, but do need to free it when the
452 * class is destroyed. */
453 ZEND_ASSERT(Z_ASTVAL(c->value)->kind == ZEND_AST_CONST_ENUM_INIT);
454 free(Z_AST(c->value));
455 } else {
456 zval_internal_ptr_dtor(&c->value);
457 }
458 if (c->doc_comment) {
459 zend_string_release_ex(c->doc_comment, 1);
460 }
461 if (c->attributes) {
462 zend_hash_release(c->attributes);
463 }
464 }
465 free(c);
466 } ZEND_HASH_FOREACH_END();
467 zend_hash_destroy(&ce->constants_table);
468 }
469 if (ce->iterator_funcs_ptr) {
470 free(ce->iterator_funcs_ptr);
471 }
472 if (ce->num_interfaces > 0) {
473 free(ce->interfaces);
474 }
475 if (ce->properties_info_table) {
476 free(ce->properties_info_table);
477 }
478 if (ce->attributes) {
479 zend_hash_release(ce->attributes);
480 }
481 free(ce);
482 break;
483 }
484 }
485
zend_class_add_ref(zval * zv)486 void zend_class_add_ref(zval *zv)
487 {
488 zend_class_entry *ce = Z_PTR_P(zv);
489
490 if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
491 ce->refcount++;
492 }
493 }
494
zend_destroy_static_vars(zend_op_array * op_array)495 ZEND_API void zend_destroy_static_vars(zend_op_array *op_array)
496 {
497 if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
498 HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
499 if (ht) {
500 zend_array_destroy(ht);
501 ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
502 }
503 }
504 }
505
destroy_op_array(zend_op_array * op_array)506 ZEND_API void destroy_op_array(zend_op_array *op_array)
507 {
508 uint32_t i;
509
510 if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE)
511 && ZEND_MAP_PTR(op_array->run_time_cache)) {
512 efree(ZEND_MAP_PTR(op_array->run_time_cache));
513 }
514
515 if (op_array->function_name) {
516 zend_string_release_ex(op_array->function_name, 0);
517 }
518
519 if (!op_array->refcount || --(*op_array->refcount) > 0) {
520 return;
521 }
522
523 efree_size(op_array->refcount, sizeof(*(op_array->refcount)));
524
525 if (op_array->vars) {
526 i = op_array->last_var;
527 while (i > 0) {
528 i--;
529 zend_string_release_ex(op_array->vars[i], 0);
530 }
531 efree(op_array->vars);
532 }
533
534 if (op_array->literals) {
535 zval *literal = op_array->literals;
536 zval *end = literal + op_array->last_literal;
537 while (literal < end) {
538 zval_ptr_dtor_nogc(literal);
539 literal++;
540 }
541 if (ZEND_USE_ABS_CONST_ADDR
542 || !(op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
543 efree(op_array->literals);
544 }
545 }
546 efree(op_array->opcodes);
547
548 zend_string_release_ex(op_array->filename, 0);
549 if (op_array->doc_comment) {
550 zend_string_release_ex(op_array->doc_comment, 0);
551 }
552 if (op_array->attributes) {
553 zend_hash_release(op_array->attributes);
554 }
555 if (op_array->live_range) {
556 efree(op_array->live_range);
557 }
558 if (op_array->try_catch_array) {
559 efree(op_array->try_catch_array);
560 }
561 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_DTOR) {
562 if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
563 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array);
564 }
565 }
566 if (op_array->arg_info) {
567 uint32_t num_args = op_array->num_args;
568 zend_arg_info *arg_info = op_array->arg_info;
569
570 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
571 arg_info--;
572 num_args++;
573 }
574 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
575 num_args++;
576 }
577 for (i = 0 ; i < num_args; i++) {
578 if (arg_info[i].name) {
579 zend_string_release_ex(arg_info[i].name, 0);
580 }
581 zend_type_release(arg_info[i].type, /* persistent */ 0);
582 }
583 efree(arg_info);
584 }
585 if (op_array->static_variables) {
586 zend_array_destroy(op_array->static_variables);
587 }
588 if (op_array->num_dynamic_func_defs) {
589 for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
590 /* Closures overwrite static_variables in their copy.
591 * Make sure to destroy them when the prototype function is destroyed. */
592 if (op_array->dynamic_func_defs[i]->static_variables
593 && (op_array->dynamic_func_defs[i]->fn_flags & ZEND_ACC_CLOSURE)) {
594 zend_array_destroy(op_array->dynamic_func_defs[i]->static_variables);
595 op_array->dynamic_func_defs[i]->static_variables = NULL;
596 }
597 destroy_op_array(op_array->dynamic_func_defs[i]);
598 }
599 efree(op_array->dynamic_func_defs);
600 }
601 }
602
zend_update_extended_stmts(zend_op_array * op_array)603 static void zend_update_extended_stmts(zend_op_array *op_array)
604 {
605 zend_op *opline = op_array->opcodes, *end=opline+op_array->last;
606
607 while (opline<end) {
608 if (opline->opcode == ZEND_EXT_STMT) {
609 if (opline+1<end) {
610 if ((opline+1)->opcode == ZEND_EXT_STMT) {
611 opline->opcode = ZEND_NOP;
612 opline++;
613 continue;
614 }
615 if (opline+1<end) {
616 opline->lineno = (opline+1)->lineno;
617 }
618 } else {
619 opline->opcode = ZEND_NOP;
620 }
621 }
622 opline++;
623 }
624 }
625
zend_extension_op_array_handler(zend_extension * extension,zend_op_array * op_array)626 static void zend_extension_op_array_handler(zend_extension *extension, zend_op_array *op_array)
627 {
628 if (extension->op_array_handler) {
629 extension->op_array_handler(op_array);
630 }
631 }
632
zend_check_finally_breakout(zend_op_array * op_array,uint32_t op_num,uint32_t dst_num)633 static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num)
634 {
635 int i;
636
637 for (i = 0; i < op_array->last_try_catch; i++) {
638 if ((op_num < op_array->try_catch_array[i].finally_op ||
639 op_num >= op_array->try_catch_array[i].finally_end)
640 && (dst_num >= op_array->try_catch_array[i].finally_op &&
641 dst_num <= op_array->try_catch_array[i].finally_end)) {
642 CG(in_compilation) = 1;
643 CG(active_op_array) = op_array;
644 CG(zend_lineno) = op_array->opcodes[op_num].lineno;
645 zend_error_noreturn(E_COMPILE_ERROR, "jump into a finally block is disallowed");
646 } else if ((op_num >= op_array->try_catch_array[i].finally_op
647 && op_num <= op_array->try_catch_array[i].finally_end)
648 && (dst_num > op_array->try_catch_array[i].finally_end
649 || dst_num < op_array->try_catch_array[i].finally_op)) {
650 CG(in_compilation) = 1;
651 CG(active_op_array) = op_array;
652 CG(zend_lineno) = op_array->opcodes[op_num].lineno;
653 zend_error_noreturn(E_COMPILE_ERROR, "jump out of a finally block is disallowed");
654 }
655 }
656 }
657
zend_get_brk_cont_target(const zend_op_array * op_array,const zend_op * opline)658 static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) {
659 int nest_levels = opline->op2.num;
660 int array_offset = opline->op1.num;
661 zend_brk_cont_element *jmp_to;
662 do {
663 jmp_to = &CG(context).brk_cont_array[array_offset];
664 if (nest_levels > 1) {
665 array_offset = jmp_to->parent;
666 }
667 } while (--nest_levels > 0);
668
669 return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont;
670 }
671
emit_live_range_raw(zend_op_array * op_array,uint32_t var_num,uint32_t kind,uint32_t start,uint32_t end)672 static void emit_live_range_raw(
673 zend_op_array *op_array, uint32_t var_num, uint32_t kind, uint32_t start, uint32_t end) {
674 zend_live_range *range;
675
676 op_array->last_live_range++;
677 op_array->live_range = erealloc(op_array->live_range,
678 sizeof(zend_live_range) * op_array->last_live_range);
679
680 ZEND_ASSERT(start < end);
681 range = &op_array->live_range[op_array->last_live_range - 1];
682 range->var = EX_NUM_TO_VAR(op_array->last_var + var_num);
683 range->var |= kind;
684 range->start = start;
685 range->end = end;
686 }
687
emit_live_range(zend_op_array * op_array,uint32_t var_num,uint32_t start,uint32_t end,zend_needs_live_range_cb needs_live_range)688 static void emit_live_range(
689 zend_op_array *op_array, uint32_t var_num, uint32_t start, uint32_t end,
690 zend_needs_live_range_cb needs_live_range) {
691 zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline;
692 zend_op *use_opline = &op_array->opcodes[end];
693 uint32_t kind;
694
695 switch (def_opline->opcode) {
696 /* These should never be the first def. */
697 case ZEND_ADD_ARRAY_ELEMENT:
698 case ZEND_ADD_ARRAY_UNPACK:
699 case ZEND_ROPE_ADD:
700 ZEND_UNREACHABLE();
701 return;
702 /* Result is boolean, it doesn't have to be destroyed. */
703 case ZEND_JMPZ_EX:
704 case ZEND_JMPNZ_EX:
705 case ZEND_BOOL:
706 case ZEND_BOOL_NOT:
707 /* Classes don't have to be destroyed. */
708 case ZEND_FETCH_CLASS:
709 case ZEND_DECLARE_ANON_CLASS:
710 /* FAST_CALLs don't have to be destroyed. */
711 case ZEND_FAST_CALL:
712 return;
713 case ZEND_BEGIN_SILENCE:
714 kind = ZEND_LIVE_SILENCE;
715 start++;
716 break;
717 case ZEND_ROPE_INIT:
718 kind = ZEND_LIVE_ROPE;
719 /* ROPE live ranges include the generating opcode. */
720 def_opline--;
721 break;
722 case ZEND_FE_RESET_R:
723 case ZEND_FE_RESET_RW:
724 kind = ZEND_LIVE_LOOP;
725 start++;
726 break;
727 /* Objects created via ZEND_NEW are only fully initialized
728 * after the DO_FCALL (constructor call).
729 * We are creating two live-ranges: ZEND_LINE_NEW for uninitialized
730 * part, and ZEND_LIVE_TMPVAR for initialized.
731 */
732 case ZEND_NEW:
733 {
734 int level = 0;
735 uint32_t orig_start = start;
736
737 while (def_opline + 1 < use_opline) {
738 def_opline++;
739 start++;
740 if (def_opline->opcode == ZEND_DO_FCALL) {
741 if (level == 0) {
742 break;
743 }
744 level--;
745 } else {
746 switch (def_opline->opcode) {
747 case ZEND_INIT_FCALL:
748 case ZEND_INIT_FCALL_BY_NAME:
749 case ZEND_INIT_NS_FCALL_BY_NAME:
750 case ZEND_INIT_DYNAMIC_CALL:
751 case ZEND_INIT_USER_CALL:
752 case ZEND_INIT_METHOD_CALL:
753 case ZEND_INIT_STATIC_METHOD_CALL:
754 case ZEND_NEW:
755 level++;
756 break;
757 case ZEND_DO_ICALL:
758 case ZEND_DO_UCALL:
759 case ZEND_DO_FCALL_BY_NAME:
760 level--;
761 break;
762 }
763 }
764 }
765 emit_live_range_raw(op_array, var_num, ZEND_LIVE_NEW, orig_start + 1, start + 1);
766 if (start + 1 == end) {
767 /* Trivial live-range, no need to store it. */
768 return;
769 }
770 }
771 ZEND_FALLTHROUGH;
772 default:
773 start++;
774 kind = ZEND_LIVE_TMPVAR;
775
776 /* Check hook to determine whether a live range is necessary,
777 * e.g. based on type info. */
778 if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
779 return;
780 }
781 break;
782 case ZEND_COPY_TMP:
783 {
784 /* COPY_TMP has a split live-range: One from the definition until the use in
785 * "null" branch, and another from the start of the "non-null" branch to the
786 * FREE opcode. */
787 uint32_t rt_var_num = EX_NUM_TO_VAR(op_array->last_var + var_num);
788 if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
789 return;
790 }
791
792 kind = ZEND_LIVE_TMPVAR;
793 if (use_opline->opcode != ZEND_FREE) {
794 /* This can happen if one branch of the coalesce has been optimized away.
795 * In this case we should emit a normal live-range instead. */
796 start++;
797 break;
798 }
799
800 zend_op *block_start_op = use_opline;
801 while ((block_start_op-1)->opcode == ZEND_FREE) {
802 block_start_op--;
803 }
804
805 start = block_start_op - op_array->opcodes;
806 if (start != end) {
807 emit_live_range_raw(op_array, var_num, kind, start, end);
808 }
809
810 do {
811 use_opline--;
812
813 /* The use might have been optimized away, in which case we will hit the def
814 * instead. */
815 if (use_opline->opcode == ZEND_COPY_TMP && use_opline->result.var == rt_var_num) {
816 return;
817 }
818 } while (!(
819 ((use_opline->op1_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op1.var == rt_var_num) ||
820 ((use_opline->op2_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op2.var == rt_var_num)
821 ));
822
823 start = def_opline + 1 - op_array->opcodes;
824 end = use_opline - op_array->opcodes;
825 emit_live_range_raw(op_array, var_num, kind, start, end);
826 return;
827 }
828 }
829
830 emit_live_range_raw(op_array, var_num, kind, start, end);
831 }
832
is_fake_def(zend_op * opline)833 static bool is_fake_def(zend_op *opline) {
834 /* These opcodes only modify the result, not create it. */
835 return opline->opcode == ZEND_ROPE_ADD
836 || opline->opcode == ZEND_ADD_ARRAY_ELEMENT
837 || opline->opcode == ZEND_ADD_ARRAY_UNPACK;
838 }
839
keeps_op1_alive(zend_op * opline)840 static bool keeps_op1_alive(zend_op *opline) {
841 /* These opcodes don't consume their OP1 operand,
842 * it is later freed by something else. */
843 if (opline->opcode == ZEND_CASE
844 || opline->opcode == ZEND_CASE_STRICT
845 || opline->opcode == ZEND_SWITCH_LONG
846 || opline->opcode == ZEND_SWITCH_STRING
847 || opline->opcode == ZEND_MATCH
848 || opline->opcode == ZEND_FETCH_LIST_R
849 || opline->opcode == ZEND_COPY_TMP) {
850 return 1;
851 }
852 ZEND_ASSERT(opline->opcode != ZEND_FE_FETCH_R
853 && opline->opcode != ZEND_FE_FETCH_RW
854 && opline->opcode != ZEND_FETCH_LIST_W
855 && opline->opcode != ZEND_VERIFY_RETURN_TYPE
856 && opline->opcode != ZEND_BIND_LEXICAL
857 && opline->opcode != ZEND_ROPE_ADD);
858 return 0;
859 }
860
861 /* Live ranges must be sorted by increasing start opline */
cmp_live_range(const zend_live_range * a,const zend_live_range * b)862 static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) {
863 return a->start - b->start;
864 }
swap_live_range(zend_live_range * a,zend_live_range * b)865 static void swap_live_range(zend_live_range *a, zend_live_range *b) {
866 uint32_t tmp;
867 tmp = a->var;
868 a->var = b->var;
869 b->var = tmp;
870 tmp = a->start;
871 a->start = b->start;
872 b->start = tmp;
873 tmp = a->end;
874 a->end = b->end;
875 b->end = tmp;
876 }
877
zend_calc_live_ranges(zend_op_array * op_array,zend_needs_live_range_cb needs_live_range)878 static void zend_calc_live_ranges(
879 zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
880 uint32_t opnum = op_array->last;
881 zend_op *opline = &op_array->opcodes[opnum];
882 ALLOCA_FLAG(use_heap)
883 uint32_t var_offset = op_array->last_var;
884 uint32_t *last_use = do_alloca(sizeof(uint32_t) * op_array->T, use_heap);
885 memset(last_use, -1, sizeof(uint32_t) * op_array->T);
886
887 ZEND_ASSERT(!op_array->live_range);
888 while (opnum > 0) {
889 opnum--;
890 opline--;
891
892 if ((opline->result_type & (IS_TMP_VAR|IS_VAR)) && !is_fake_def(opline)) {
893 uint32_t var_num = EX_VAR_TO_NUM(opline->result.var) - var_offset;
894 /* Defs without uses can occur for two reasons: Either because the result is
895 * genuinely unused (e.g. omitted FREE opcode for an unused boolean result), or
896 * because there are multiple defining opcodes (e.g. JMPZ_EX and QM_ASSIGN), in
897 * which case the last one starts the live range. As such, we can simply ignore
898 * missing uses here. */
899 if (EXPECTED(last_use[var_num] != (uint32_t) -1)) {
900 /* Skip trivial live-range */
901 if (opnum + 1 != last_use[var_num]) {
902 uint32_t num;
903
904 #if 1
905 /* OP_DATA uses only op1 operand */
906 ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
907 num = opnum;
908 #else
909 /* OP_DATA is really part of the previous opcode. */
910 num = opnum - (opline->opcode == ZEND_OP_DATA);
911 #endif
912 emit_live_range(op_array, var_num, num, last_use[var_num], needs_live_range);
913 }
914 last_use[var_num] = (uint32_t) -1;
915 }
916 }
917
918 if ((opline->op1_type & (IS_TMP_VAR|IS_VAR))) {
919 uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var) - var_offset;
920 if (EXPECTED(last_use[var_num] == (uint32_t) -1)) {
921 if (EXPECTED(!keeps_op1_alive(opline))) {
922 /* OP_DATA is really part of the previous opcode. */
923 last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
924 }
925 }
926 }
927 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
928 uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var) - var_offset;
929 if (UNEXPECTED(opline->opcode == ZEND_FE_FETCH_R
930 || opline->opcode == ZEND_FE_FETCH_RW)) {
931 /* OP2 of FE_FETCH is actually a def, not a use. */
932 if (last_use[var_num] != (uint32_t) -1) {
933 if (opnum + 1 != last_use[var_num]) {
934 emit_live_range(
935 op_array, var_num, opnum, last_use[var_num], needs_live_range);
936 }
937 last_use[var_num] = (uint32_t) -1;
938 }
939 } else if (EXPECTED(last_use[var_num] == (uint32_t) -1)) {
940 #if 1
941 /* OP_DATA uses only op1 operand */
942 ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
943 last_use[var_num] = opnum;
944 #else
945 /* OP_DATA is really part of the previous opcode. */
946 last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
947 #endif
948 }
949 }
950 }
951
952 if (op_array->last_live_range > 1) {
953 zend_live_range *r1 = op_array->live_range;
954 zend_live_range *r2 = r1 + op_array->last_live_range - 1;
955
956 /* In most cases we need just revert the array */
957 while (r1 < r2) {
958 swap_live_range(r1, r2);
959 r1++;
960 r2--;
961 }
962
963 r1 = op_array->live_range;
964 r2 = r1 + op_array->last_live_range - 1;
965 while (r1 < r2) {
966 if (r1->start > (r1+1)->start) {
967 zend_sort(r1, r2 - r1 + 1, sizeof(zend_live_range),
968 (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
969 break;
970 }
971 r1++;
972 }
973 }
974
975 free_alloca(last_use, use_heap);
976 }
977
zend_recalc_live_ranges(zend_op_array * op_array,zend_needs_live_range_cb needs_live_range)978 ZEND_API void zend_recalc_live_ranges(
979 zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
980 /* We assume that we never create live-ranges where there were none before. */
981 ZEND_ASSERT(op_array->live_range);
982 efree(op_array->live_range);
983 op_array->live_range = NULL;
984 op_array->last_live_range = 0;
985 zend_calc_live_ranges(op_array, needs_live_range);
986 }
987
pass_two(zend_op_array * op_array)988 ZEND_API void pass_two(zend_op_array *op_array)
989 {
990 zend_op *opline, *end;
991
992 if (!ZEND_USER_CODE(op_array->type)) {
993 return;
994 }
995 if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_STMT) {
996 zend_update_extended_stmts(op_array);
997 }
998 if (CG(compiler_options) & ZEND_COMPILE_HANDLE_OP_ARRAY) {
999 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_HANDLER) {
1000 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_handler, op_array);
1001 }
1002 }
1003
1004 if (CG(context).vars_size != op_array->last_var) {
1005 op_array->vars = (zend_string**) erealloc(op_array->vars, sizeof(zend_string*)*op_array->last_var);
1006 CG(context).vars_size = op_array->last_var;
1007 }
1008
1009 #if ZEND_USE_ABS_CONST_ADDR
1010 if (CG(context).opcodes_size != op_array->last) {
1011 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
1012 CG(context).opcodes_size = op_array->last;
1013 }
1014 if (CG(context).literals_size != op_array->last_literal) {
1015 op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
1016 CG(context).literals_size = op_array->last_literal;
1017 }
1018 #else
1019 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1020 ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1021 sizeof(zval) * op_array->last_literal);
1022 if (op_array->literals) {
1023 memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1024 op_array->literals, sizeof(zval) * op_array->last_literal);
1025 efree(op_array->literals);
1026 op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1027 }
1028 CG(context).opcodes_size = op_array->last;
1029 CG(context).literals_size = op_array->last_literal;
1030 #endif
1031
1032 /* Needs to be set directly after the opcode/literal reallocation, to ensure destruction
1033 * happens correctly if any of the following fixups generate a fatal error. */
1034 op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
1035
1036 opline = op_array->opcodes;
1037 end = opline + op_array->last;
1038 while (opline < end) {
1039 switch (opline->opcode) {
1040 case ZEND_RECV_INIT:
1041 {
1042 zval *val = CT_CONSTANT(opline->op2);
1043 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
1044 uint32_t slot = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
1045 Z_CACHE_SLOT_P(val) = slot;
1046 op_array->cache_size += sizeof(zval);
1047 }
1048 }
1049 break;
1050 case ZEND_FAST_CALL:
1051 opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
1052 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1053 break;
1054 case ZEND_BRK:
1055 case ZEND_CONT:
1056 {
1057 uint32_t jmp_target = zend_get_brk_cont_target(op_array, opline);
1058
1059 if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1060 zend_check_finally_breakout(op_array, opline - op_array->opcodes, jmp_target);
1061 }
1062 opline->opcode = ZEND_JMP;
1063 opline->op1.opline_num = jmp_target;
1064 opline->op2.num = 0;
1065 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1066 }
1067 break;
1068 case ZEND_GOTO:
1069 zend_resolve_goto_label(op_array, opline);
1070 if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1071 zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1072 }
1073 ZEND_FALLTHROUGH;
1074 case ZEND_JMP:
1075 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1076 break;
1077 case ZEND_JMPZNZ:
1078 /* absolute index to relative offset */
1079 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1080 ZEND_FALLTHROUGH;
1081 case ZEND_JMPZ:
1082 case ZEND_JMPNZ:
1083 case ZEND_JMPZ_EX:
1084 case ZEND_JMPNZ_EX:
1085 case ZEND_JMP_SET:
1086 case ZEND_COALESCE:
1087 case ZEND_FE_RESET_R:
1088 case ZEND_FE_RESET_RW:
1089 case ZEND_JMP_NULL:
1090 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1091 break;
1092 case ZEND_ASSERT_CHECK:
1093 {
1094 /* If result of assert is unused, result of check is unused as well */
1095 zend_op *call = &op_array->opcodes[opline->op2.opline_num - 1];
1096 if (call->opcode == ZEND_EXT_FCALL_END) {
1097 call--;
1098 }
1099 if (call->result_type == IS_UNUSED) {
1100 opline->result_type = IS_UNUSED;
1101 }
1102 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1103 break;
1104 }
1105 case ZEND_FE_FETCH_R:
1106 case ZEND_FE_FETCH_RW:
1107 /* absolute index to relative offset */
1108 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1109 break;
1110 case ZEND_CATCH:
1111 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1112 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1113 }
1114 break;
1115 case ZEND_RETURN:
1116 case ZEND_RETURN_BY_REF:
1117 if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
1118 opline->opcode = ZEND_GENERATOR_RETURN;
1119 }
1120 break;
1121 case ZEND_SWITCH_LONG:
1122 case ZEND_SWITCH_STRING:
1123 case ZEND_MATCH:
1124 {
1125 /* absolute indexes to relative offsets */
1126 HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2));
1127 zval *zv;
1128 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
1129 Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, Z_LVAL_P(zv));
1130 } ZEND_HASH_FOREACH_END();
1131
1132 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1133 break;
1134 }
1135 }
1136 if (opline->op1_type == IS_CONST) {
1137 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1138 } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1139 opline->op1.var = EX_NUM_TO_VAR(op_array->last_var + opline->op1.var);
1140 }
1141 if (opline->op2_type == IS_CONST) {
1142 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1143 } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
1144 opline->op2.var = EX_NUM_TO_VAR(op_array->last_var + opline->op2.var);
1145 }
1146 if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
1147 opline->result.var = EX_NUM_TO_VAR(op_array->last_var + opline->result.var);
1148 }
1149 ZEND_VM_SET_OPCODE_HANDLER(opline);
1150 opline++;
1151 }
1152
1153 zend_calc_live_ranges(op_array, NULL);
1154
1155 return;
1156 }
1157
get_unary_op(int opcode)1158 ZEND_API unary_op_type get_unary_op(int opcode)
1159 {
1160 switch (opcode) {
1161 case ZEND_BW_NOT:
1162 return (unary_op_type) bitwise_not_function;
1163 case ZEND_BOOL_NOT:
1164 return (unary_op_type) boolean_not_function;
1165 default:
1166 return (unary_op_type) NULL;
1167 }
1168 }
1169
get_binary_op(int opcode)1170 ZEND_API binary_op_type get_binary_op(int opcode)
1171 {
1172 switch (opcode) {
1173 case ZEND_ADD:
1174 return (binary_op_type) add_function;
1175 case ZEND_SUB:
1176 return (binary_op_type) sub_function;
1177 case ZEND_MUL:
1178 return (binary_op_type) mul_function;
1179 case ZEND_POW:
1180 return (binary_op_type) pow_function;
1181 case ZEND_DIV:
1182 return (binary_op_type) div_function;
1183 case ZEND_MOD:
1184 return (binary_op_type) mod_function;
1185 case ZEND_SL:
1186 return (binary_op_type) shift_left_function;
1187 case ZEND_SR:
1188 return (binary_op_type) shift_right_function;
1189 case ZEND_FAST_CONCAT:
1190 case ZEND_CONCAT:
1191 return (binary_op_type) concat_function;
1192 case ZEND_IS_IDENTICAL:
1193 case ZEND_CASE_STRICT:
1194 return (binary_op_type) is_identical_function;
1195 case ZEND_IS_NOT_IDENTICAL:
1196 return (binary_op_type) is_not_identical_function;
1197 case ZEND_IS_EQUAL:
1198 case ZEND_CASE:
1199 return (binary_op_type) is_equal_function;
1200 case ZEND_IS_NOT_EQUAL:
1201 return (binary_op_type) is_not_equal_function;
1202 case ZEND_IS_SMALLER:
1203 return (binary_op_type) is_smaller_function;
1204 case ZEND_IS_SMALLER_OR_EQUAL:
1205 return (binary_op_type) is_smaller_or_equal_function;
1206 case ZEND_SPACESHIP:
1207 return (binary_op_type) compare_function;
1208 case ZEND_BW_OR:
1209 return (binary_op_type) bitwise_or_function;
1210 case ZEND_BW_AND:
1211 return (binary_op_type) bitwise_and_function;
1212 case ZEND_BW_XOR:
1213 return (binary_op_type) bitwise_xor_function;
1214 case ZEND_BOOL_XOR:
1215 return (binary_op_type) boolean_xor_function;
1216 default:
1217 ZEND_UNREACHABLE();
1218 return (binary_op_type) NULL;
1219 }
1220 }
1221