/* {{{ proto Closure Closure::bind(callable old, object to [, mixed scope]) Create a closure from another one and bind to another object and scope */ ZEND_METHOD(Closure, bind) { zval *newthis, *zclosure, *scope_arg = NULL; zend_closure *closure, *new_closure; zend_class_entry *ce, *called_scope; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) { RETURN_NULL(); } closure = (zend_closure *)Z_OBJ_P(zclosure); if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); } if (scope_arg != NULL) { /* scope argument was given */ if (Z_TYPE_P(scope_arg) == IS_OBJECT) { ce = Z_OBJCE_P(scope_arg); } else if (Z_TYPE_P(scope_arg) == IS_NULL) { ce = NULL; } else { zend_string *class_name = zval_get_string(scope_arg); if (zend_string_equals_literal(class_name, "static")) { ce = closure->func.common.scope; } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) { zend_error(E_WARNING, "Class '%s' not found", ZSTR_VAL(class_name)); zend_string_release(class_name); RETURN_NULL(); } zend_string_release(class_name); } if(ce && ce != closure->func.common.scope && ce->type == ZEND_INTERNAL_CLASS) { /* rebinding to internal class is not allowed */ zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s", ZSTR_VAL(ce->name)); return; } } else { /* scope argument not given; do not change the scope by default */ ce = closure->func.common.scope; } if (newthis) { called_scope = Z_OBJCE_P(newthis); } else { called_scope = ce; } zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); new_closure = (zend_closure *) Z_OBJ_P(return_value); /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */ if (ZEND_USER_CODE(closure->func.type) && (closure->func.common.scope != new_closure->func.common.scope || (closure->func.op_array.fn_flags & ZEND_ACC_NO_RT_ARENA))) { new_closure->func.op_array.run_time_cache = emalloc(new_closure->func.op_array.cache_size); memset(new_closure->func.op_array.run_time_cache, 0, new_closure->func.op_array.cache_size); new_closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA; } }
static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */ { zend_string *lc_name; lc_name = zend_string_tolower(method); if (zend_string_equals_literal(method, ZEND_INVOKE_FUNC_NAME)) { zend_string_release(lc_name); return zend_get_closure_invoke_method(*object); } zend_string_release(lc_name); return std_object_handlers.get_method(object, method, key); }
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] ) Create a closure from another one and bind to another object and scope */ ZEND_METHOD(Closure, bind) { zval *newthis, *zclosure, *scope_arg = NULL; zend_closure *closure; zend_class_entry *ce, *called_scope; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) { RETURN_NULL(); } closure = (zend_closure *)Z_OBJ_P(zclosure); if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); } if (scope_arg != NULL) { /* scope argument was given */ if (Z_TYPE_P(scope_arg) == IS_OBJECT) { ce = Z_OBJCE_P(scope_arg); } else if (Z_TYPE_P(scope_arg) == IS_NULL) { ce = NULL; } else { zend_string *class_name = zval_get_string(scope_arg); if (zend_string_equals_literal(class_name, "static")) { ce = closure->func.common.scope; } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) { zend_error(E_WARNING, "Class '%s' not found", class_name->val); zend_string_release(class_name); RETURN_NULL(); } zend_string_release(class_name); } if(ce && ce != closure->func.common.scope && ce->type == ZEND_INTERNAL_CLASS) { /* rebinding to internal class is not allowed */ zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s", ce->name->val); return; } } else { /* scope argument not given; do not change the scope by default */ ce = closure->func.common.scope; } if (newthis) { called_scope = Z_OBJCE_P(newthis); } else { called_scope = ce; } zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); }
ZEND_API int zend_register_constant(zend_constant *c) { zend_string *lowercase_name = NULL; zend_string *name; int ret = SUCCESS; #if 0 printf("Registering constant for module %d\n", c->module_number); #endif if (!(c->flags & CONST_CS)) { lowercase_name = zend_string_tolower_ex(c->name, c->flags & CONST_PERSISTENT); lowercase_name = zend_new_interned_string(lowercase_name); name = lowercase_name; } else { char *slash = strrchr(ZSTR_VAL(c->name), '\\'); if (slash) { lowercase_name = zend_string_init(ZSTR_VAL(c->name), ZSTR_LEN(c->name), c->flags & CONST_PERSISTENT); zend_str_tolower(ZSTR_VAL(lowercase_name), slash - ZSTR_VAL(c->name)); lowercase_name = zend_new_interned_string(lowercase_name); name = lowercase_name; } else { name = c->name; } } /* Check if the user is trying to define the internal pseudo constant name __COMPILER_HALT_OFFSET__ */ if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__") || zend_hash_add_constant(EG(zend_constants), name, c) == NULL) { /* The internal __COMPILER_HALT_OFFSET__ is prefixed by NULL byte */ if (ZSTR_VAL(c->name)[0] == '\0' && ZSTR_LEN(c->name) > sizeof("\0__COMPILER_HALT_OFFSET__")-1 && memcmp(ZSTR_VAL(name), "\0__COMPILER_HALT_OFFSET__", sizeof("\0__COMPILER_HALT_OFFSET__")) == 0) { } zend_error(E_NOTICE,"Constant %s already defined", ZSTR_VAL(name)); zend_string_release(c->name); if (!(c->flags & CONST_PERSISTENT)) { zval_ptr_dtor_nogc(&c->value); } ret = FAILURE; } if (lowercase_name) { zend_string_release(lowercase_name); } return ret; }
/* {{{ proto Closure Closure::bind(callable old, object to [, mixed scope]) Create a closure from another one and bind to another object and scope */ ZEND_METHOD(Closure, bind) { zval *newthis, *zclosure, *scope_arg = NULL; zend_closure *closure; zend_class_entry *ce, *called_scope; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) { return; } closure = (zend_closure *)Z_OBJ_P(zclosure); if (scope_arg != NULL) { /* scope argument was given */ if (Z_TYPE_P(scope_arg) == IS_OBJECT) { ce = Z_OBJCE_P(scope_arg); } else if (Z_TYPE_P(scope_arg) == IS_NULL) { ce = NULL; } else { zend_string *tmp_class_name; zend_string *class_name = zval_get_tmp_string(scope_arg, &tmp_class_name); if (zend_string_equals_literal(class_name, "static")) { ce = closure->func.common.scope; } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) { zend_error(E_WARNING, "Class '%s' not found", ZSTR_VAL(class_name)); zend_string_release_ex(class_name, 0); RETURN_NULL(); } zend_tmp_string_release(tmp_class_name); } } else { /* scope argument not given; do not change the scope by default */ ce = closure->func.common.scope; } if (!zend_valid_closure_binding(closure, newthis, ce)) { return; } if (newthis) { called_scope = Z_OBJCE_P(newthis); } else { called_scope = ce; } zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); }
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] ) Create a closure from another one and bind to another object and scope */ ZEND_METHOD(Closure, bind) { zval *newthis, *zclosure, *scope_arg = NULL; zend_closure *closure; zend_class_entry *ce; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) { RETURN_NULL(); } closure = (zend_closure *)Z_OBJ_P(zclosure); if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); } if (scope_arg != NULL) { /* scope argument was given */ if (IS_ZEND_STD_OBJECT(*scope_arg)) { ce = Z_OBJCE_P(scope_arg); } else if (Z_TYPE_P(scope_arg) == IS_NULL) { ce = NULL; } else { zend_string *class_name = zval_get_string(scope_arg); if (zend_string_equals_literal(class_name, "static")) { ce = closure->func.common.scope; } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1 TSRMLS_CC)) == NULL) { zend_error(E_WARNING, "Class '%s' not found", class_name->val); zend_string_release(class_name); RETURN_NULL(); } zend_string_release(class_name); } } else { /* scope argument not given; do not change the scope by default */ ce = closure->func.common.scope; } zend_create_closure(return_value, &closure->func, ce, newthis TSRMLS_CC); }
ZEND_API int zend_register_constant(zend_constant *c) { zend_string *lowercase_name = NULL; zend_string *name; int ret = SUCCESS; zend_bool persistent = (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) != 0; #if 0 printf("Registering constant for module %d\n", c->module_number); #endif char *slash = strrchr(ZSTR_VAL(c->name), '\\'); if (slash) { lowercase_name = zend_string_init(ZSTR_VAL(c->name), ZSTR_LEN(c->name), persistent); zend_str_tolower(ZSTR_VAL(lowercase_name), slash - ZSTR_VAL(c->name)); lowercase_name = zend_new_interned_string(lowercase_name); name = lowercase_name; } else { name = c->name; } /* Check if the user is trying to define any special constant */ if (zend_string_equals_literal(name, "__COMPILER_HALT_OFFSET__") || (!persistent && zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name))) || zend_hash_add_constant(EG(zend_constants), name, c) == NULL ) { zend_error(E_NOTICE,"Constant %s already defined", ZSTR_VAL(name)); zend_string_release(c->name); if (!persistent) { zval_ptr_dtor_nogc(&c->value); } ret = FAILURE; } if (lowercase_name) { zend_string_release(lowercase_name); } return ret; }
php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message) { char *str = NULL; size_t len = 0; size_t cut = 0; while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) { #if DBG_PARSER fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n", php_http_message_parser_state_name(php_http_message_parser_state_is(parser)), flags, message && *message ? (*message)->type : -1, buffer->used ); _dpf(0, buffer->data, buffer->used); #endif switch (php_http_message_parser_state_pop(parser)) { case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE: return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); case PHP_HTTP_MESSAGE_PARSER_STATE_START: { char *ptr = buffer->data; while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) { ++ptr; } php_http_buffer_cut(buffer, 0, ptr - buffer->data); if (buffer->used) { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER); } break; } case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER: { unsigned header_parser_flags = (flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP) ? PHP_HTTP_HEADER_PARSER_CLEANUP : 0; switch (php_http_header_parser_parse(&parser->header, buffer, header_parser_flags, *message ? &(*message)->hdrs : NULL, (php_http_info_callback_t) php_http_message_info_callback, message)) { case PHP_HTTP_HEADER_PARSER_STATE_FAILURE: return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE; case PHP_HTTP_HEADER_PARSER_STATE_DONE: php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE); break; default: if (buffer->used || !(flags & PHP_HTTP_MESSAGE_PARSER_CLEANUP)) { return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER); } else { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE); } } break; } case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: { zval h, *h_ptr, *h_loc = NULL, *h_con = NULL, *h_ce; zend_bool chunked = 0; zend_long content_length = -1; zend_string *content_range = NULL; /* Content-Range has higher precedence than Content-Length, * and content-length denotes the original length of the entity, * so let's *NOT* remove CR/CL, because that would fundamentally * change the meaning of the whole message */ if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Transfer-Encoding")))) { zend_string *zs = zval_get_string(h_ptr); chunked = zend_string_equals_literal(zs, "chunked"); zend_string_release(zs); Z_TRY_ADDREF_P(h_ptr); zend_hash_str_update(&(*message)->hdrs, "X-Original-Transfer-Encoding", lenof("X-Original-Transfer-Encoding"), h_ptr); zend_hash_str_del(&(*message)->hdrs, "Transfer-Encoding", lenof("Transfer-Encoding")); /* reset */ ZVAL_LONG(&h, 0); zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &h); } else if ((h_ptr = php_http_message_header(*message, ZEND_STRL("Content-Length")))) { content_length = zval_get_long(h_ptr); Z_TRY_ADDREF_P(h_ptr); zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Length", lenof("X-Original-Content-Length"), h_ptr); } if ((content_range = php_http_message_header_string(*message, ZEND_STRL("Content-Range")))) { ZVAL_STR_COPY(&h, content_range); zend_hash_str_update(&(*message)->hdrs, "Content-Range", lenof("Content-Range"), &h); } /* so, if curl sees a 3xx code, a Location header and a Connection:close header * it decides not to read the response body. */ if ((flags & PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS) && (*message)->type == PHP_HTTP_RESPONSE && (*message)->http.info.response.code/100 == 3 && (h_loc = php_http_message_header(*message, ZEND_STRL("Location"))) && (h_con = php_http_message_header(*message, ZEND_STRL("Connection"))) ) { zend_string *con = zval_get_string(h_con); if (php_http_match(con->val, "close", PHP_HTTP_MATCH_WORD)) { zend_string_release(con); php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); break; } zend_string_release(con); } if ((h_ce = php_http_message_header(*message, ZEND_STRL("Content-Encoding")))) { zend_string *ce = zval_get_string(h_ce); if (php_http_match(ce->val, "gzip", PHP_HTTP_MATCH_WORD) || php_http_match(ce->val, "x-gzip", PHP_HTTP_MATCH_WORD) || php_http_match(ce->val, "deflate", PHP_HTTP_MATCH_WORD) ) { if (parser->inflate) { php_http_encoding_stream_reset(&parser->inflate); } else { parser->inflate = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_inflate_ops(), 0); } Z_TRY_ADDREF_P(h_ce); zend_hash_str_update(&(*message)->hdrs, "X-Original-Content-Encoding", lenof("X-Original-Content-Encoding"), h_ce); zend_hash_str_del(&(*message)->hdrs, "Content-Encoding", lenof("Content-Encoding")); } zend_string_release(ce); } if ((flags & PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES)) { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); } else { if (chunked) { parser->dechunk = php_http_encoding_stream_init(parser->dechunk, php_http_encoding_stream_get_dechunk_ops(), 0); php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED); break; } if (content_range) { ulong total = 0, start = 0, end = 0; if (!strncasecmp(content_range->val, "bytes", lenof("bytes")) && ( content_range->val[lenof("bytes")] == ':' || content_range->val[lenof("bytes")] == ' ' || content_range->val[lenof("bytes")] == '=' ) ) { char *total_at = NULL, *end_at = NULL; char *start_at = content_range->val + sizeof("bytes"); start = strtoul(start_at, &end_at, 10); if (end_at) { end = strtoul(end_at + 1, &total_at, 10); if (total_at && strncmp(total_at + 1, "*", 1)) { total = strtoul(total_at + 1, NULL, 10); } if (end >= start && (!total || end <= total)) { parser->body_length = end + 1 - start; php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); zend_string_release(content_range); break; } } } zend_string_release(content_range); } if (content_length >= 0) { parser->body_length = content_length; php_http_message_parser_state_push(parser, 1, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH); break; } if ((*message)->type == PHP_HTTP_REQUEST) { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); } else { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB); } } break; } case PHP_HTTP_MESSAGE_PARSER_STATE_BODY: { if (len) { if (parser->inflate) { char *dec_str = NULL; size_t dec_len; if (SUCCESS != php_http_encoding_stream_update(parser->inflate, str, len, &dec_str, &dec_len)) { return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); } if (str != buffer->data) { PTR_FREE(str); } str = dec_str; len = dec_len; } php_stream_write(php_http_message_body_stream((*message)->body), str, len); } if (cut) { php_http_buffer_cut(buffer, 0, cut); } if (str != buffer->data) { PTR_FREE(str); } str = NULL; len = 0; cut = 0; break; } case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB: { str = buffer->data; len = buffer->used; cut = len; php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); break; } case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH: { len = MIN(parser->body_length, buffer->used); str = buffer->data; cut = len; parser->body_length -= len; php_http_message_parser_state_push(parser, 2, !parser->body_length?PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE:PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); break; } case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED: { /* * - pass available data through the dechunk stream * - pass decoded data along * - if stream zeroed: * Y: - cut processed string out of buffer, but leave length of unprocessed dechunk stream data untouched * - body done * N: - parse ahaed */ char *dec_str = NULL; size_t dec_len; if (SUCCESS != php_http_encoding_stream_update(parser->dechunk, buffer->data, buffer->used, &dec_str, &dec_len)) { return PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE; } str = dec_str; len = dec_len; if (php_http_encoding_stream_done(parser->dechunk)) { cut = buffer->used - PHP_HTTP_BUFFER(parser->dechunk->ctx)->used; php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); } else { cut = buffer->used; php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); } break; } case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE: { php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_DONE); if (parser->dechunk && parser->dechunk->ctx) { char *dec_str = NULL; size_t dec_len; if (SUCCESS != php_http_encoding_stream_finish(parser->dechunk, &dec_str, &dec_len)) { return php_http_message_parser_state_push(parser, 1, PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE); } php_http_encoding_stream_dtor(parser->dechunk); if (dec_str && dec_len) { str = dec_str; len = dec_len; cut = 0; php_http_message_parser_state_push(parser, 2, PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL, PHP_HTTP_MESSAGE_PARSER_STATE_BODY); } } break; } case PHP_HTTP_MESSAGE_PARSER_STATE_UPDATE_CL: { zval zcl; ZVAL_LONG(&zcl, php_http_message_body_size((*message)->body)); zend_hash_str_update(&(*message)->hdrs, "Content-Length", lenof("Content-Length"), &zcl); break; } case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: { char *ptr = buffer->data; while (ptr - buffer->data < buffer->used && PHP_HTTP_IS_CTYPE(space, *ptr)) { ++ptr; } php_http_buffer_cut(buffer, 0, ptr - buffer->data); if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) { return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; } break; } } } return php_http_message_parser_state_is(parser); }
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */ { uint32_t flags = 0; uint32_t i; int j; uint32_t *block_map; zend_function *fn; int blocks_count = 0; zend_basic_block *blocks; zval *zv; cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t)); if (!block_map) { return FAILURE; } /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */ BB_START(0); for (i = 0; i < op_array->last; i++) { zend_op *opline = op_array->opcodes + i; switch(opline->opcode) { case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_INCLUDE_OR_EVAL: flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; case ZEND_YIELD: case ZEND_YIELD_FROM: if (build_flags & ZEND_CFG_STACKLESS) { BB_START(i + 1); } break; case ZEND_DO_FCALL: case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: flags |= ZEND_FUNC_HAS_CALLS; if (build_flags & ZEND_CFG_STACKLESS) { BB_START(i + 1); } break; case ZEND_DO_ICALL: flags |= ZEND_FUNC_HAS_CALLS; break; case ZEND_INIT_FCALL: case ZEND_INIT_NS_FCALL_BY_NAME: zv = CRT_CONSTANT(opline->op2); if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { /* The third literal is the lowercased unqualified name */ zv += 2; } if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) { if (fn->type == ZEND_INTERNAL_FUNCTION) { if (zend_string_equals_literal(Z_STR_P(zv), "extract")) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (zend_string_equals_literal(Z_STR_P(zv), "compact")) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (zend_string_equals_literal(Z_STR_P(zv), "parse_str") && opline->extended_value == 1) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (zend_string_equals_literal(Z_STR_P(zv), "mb_parse_str") && opline->extended_value == 1) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (zend_string_equals_literal(Z_STR_P(zv), "get_defined_vars")) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (zend_string_equals_literal(Z_STR_P(zv), "func_num_args")) { flags |= ZEND_FUNC_VARARG; } else if (zend_string_equals_literal(Z_STR_P(zv), "func_get_arg")) { flags |= ZEND_FUNC_VARARG; } else if (zend_string_equals_literal(Z_STR_P(zv), "func_get_args")) { flags |= ZEND_FUNC_VARARG; } } } break; case ZEND_FAST_CALL: BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); BB_START(i + 1); break; case ZEND_FAST_RET: if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMP: BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMPZNZ: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; case ZEND_CATCH: if (!opline->result.num) { BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); } BB_START(i + 1); break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); BB_START(i + 1); break; case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; case ZEND_UNSET_VAR: case ZEND_ISSET_ISEMPTY_VAR: if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) && !(opline->extended_value & ZEND_QUICK_SET)) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL || (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) && !op_array->function_name) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } break; case ZEND_FETCH_R: case ZEND_FETCH_W: case ZEND_FETCH_RW: case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_IS: case ZEND_FETCH_UNSET: if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL || (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) && !op_array->function_name) { flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; } break; } } for (j = 0; j < op_array->last_live_range; j++) { BB_START(op_array->live_range[j].start); BB_START(op_array->live_range[j].end); } if (op_array->last_try_catch) { for (j = 0; j < op_array->last_try_catch; j++) { BB_START(op_array->try_catch_array[j].try_op); if (op_array->try_catch_array[j].catch_op) { BB_START(op_array->try_catch_array[j].catch_op); } if (op_array->try_catch_array[j].finally_op) { BB_START(op_array->try_catch_array[j].finally_op); } if (op_array->try_catch_array[j].finally_end) { BB_START(op_array->try_catch_array[j].finally_end); } } } cfg->blocks_count = blocks_count; /* Build CFG, Step 2: Build Array of Basic Blocks */ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count); if (!blocks) { return FAILURE; } for (i = 0, blocks_count = -1; i < op_array->last; i++) { if (block_map[i]) { if (blocks_count >= 0) { blocks[blocks_count].end = i - 1; } blocks_count++; blocks[blocks_count].flags = 0; blocks[blocks_count].start = i; blocks[blocks_count].successors[0] = -1; blocks[blocks_count].successors[1] = -1; blocks[blocks_count].predecessors_count = 0; blocks[blocks_count].predecessor_offset = -1; blocks[blocks_count].idom = -1; blocks[blocks_count].loop_header = -1; blocks[blocks_count].level = -1; blocks[blocks_count].children = -1; blocks[blocks_count].next_child = -1; block_map[i] = blocks_count; } else { block_map[i] = (uint32_t)-1; } } blocks[blocks_count].end = i - 1; blocks_count++; /* Build CFG, Step 3: Calculate successors */ for (j = 0; j < blocks_count; j++) { zend_op *opline = op_array->opcodes + blocks[j].end; switch(opline->opcode) { case ZEND_FAST_RET: case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: break; case ZEND_JMP: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]); break; case ZEND_JMPZNZ: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; case ZEND_CATCH: if (!opline->result.num) { record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); record_successor(blocks, j, 1, j + 1); } else { record_successor(blocks, j, 0, j + 1); } break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); record_successor(blocks, j, 1, j + 1); break; case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; case ZEND_FAST_CALL: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; default: record_successor(blocks, j, 0, j + 1); break; } } /* Build CFG, Step 4, Mark Reachable Basic Blocks */ zend_mark_reachable_blocks(op_array, cfg, 0); if (func_flags) { *func_flags |= flags; } return SUCCESS; }