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