Что выполняет данный участок кода
Смотрю исходники PHP и возник вопрос понимания, что делает данная конструкция и что она в себя включает вообще. файл zend_ast.h, строка 300
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);
Поскольку это заголовочный файл, смотрим файл zend_ast.c начиная с 508:
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope)
{
zval op1, op2;
zend_result ret = SUCCESS;
switch (ast->kind) {
case ZEND_AST_BINARY_OP:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
} else {
binary_op_type op = get_binary_op(ast->attr);
ret = op(result, &op1, &op2);
zval_ptr_dtor_nogc(&op1);
zval_ptr_dtor_nogc(&op2);
}
break;
case ZEND_AST_GREATER:
case ZEND_AST_GREATER_EQUAL:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
} else {
/* op1 > op2 is the same as op2 < op1 */
binary_op_type op = ast->kind == ZEND_AST_GREATER
? is_smaller_function : is_smaller_or_equal_function;
ret = op(result, &op2, &op1);
zval_ptr_dtor_nogc(&op1);
zval_ptr_dtor_nogc(&op2);
}
break;
case ZEND_AST_UNARY_OP:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else {
unary_op_type op = get_unary_op(ast->attr);
ret = op(result, &op1);
zval_ptr_dtor_nogc(&op1);
}
break;
case ZEND_AST_ZVAL:
{
zval *zv = zend_ast_get_zval(ast);
ZVAL_COPY(result, zv);
break;
}
case ZEND_AST_CONSTANT:
{
zend_string *name = zend_ast_get_constant_name(ast);
zval *zv = zend_get_constant_ex(name, scope, ast->attr);
if (UNEXPECTED(zv == NULL)) {
ZVAL_UNDEF(result);
return FAILURE;
}
ZVAL_COPY_OR_DUP(result, zv);
break;
}
case ZEND_AST_CONSTANT_CLASS:
if (scope) {
ZVAL_STR_COPY(result, scope->name);
} else {
ZVAL_EMPTY_STRING(result);
}
break;
case ZEND_AST_CLASS_NAME:
if (!scope) {
zend_throw_error(NULL, "Cannot use \"self\" when no class scope is active");
return FAILURE;
}
if (ast->attr == ZEND_FETCH_CLASS_SELF) {
ZVAL_STR_COPY(result, scope->name);
} else if (ast->attr == ZEND_FETCH_CLASS_PARENT) {
if (!scope->parent) {
zend_throw_error(NULL,
"Cannot use \"parent\" when current class scope has no parent");
return FAILURE;
}
ZVAL_STR_COPY(result, scope->parent->name);
} else {
ZEND_ASSERT(0 && "Should have errored during compilation");
}
break;
case ZEND_AST_AND:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (zend_is_true(&op1)) {
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
ZVAL_BOOL(result, zend_is_true(&op2));
zval_ptr_dtor_nogc(&op2);
} else {
ZVAL_FALSE(result);
}
zval_ptr_dtor_nogc(&op1);
break;
case ZEND_AST_OR:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (zend_is_true(&op1)) {
ZVAL_TRUE(result);
} else {
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
ZVAL_BOOL(result, zend_is_true(&op2));
zval_ptr_dtor_nogc(&op2);
}
zval_ptr_dtor_nogc(&op1);
break;
case ZEND_AST_CONDITIONAL:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (zend_is_true(&op1)) {
if (!ast->child[1]) {
*result = op1;
} else {
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
zval_ptr_dtor_nogc(&op1);
}
} else {
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[2], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
zval_ptr_dtor_nogc(&op1);
}
break;
case ZEND_AST_COALESCE:
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
if (Z_TYPE(op1) > IS_NULL) {
*result = op1;
} else {
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
zval_ptr_dtor_nogc(&op1);
}
break;
case ZEND_AST_UNARY_PLUS:
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else {
ZVAL_LONG(&op1, 0);
ret = add_function(result, &op1, &op2);
zval_ptr_dtor_nogc(&op2);
}
break;
case ZEND_AST_UNARY_MINUS:
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
} else {
ZVAL_LONG(&op1, 0);
ret = sub_function(result, &op1, &op2);
zval_ptr_dtor_nogc(&op2);
}
break;
case ZEND_AST_ARRAY:
{
uint32_t i;
zend_ast_list *list = zend_ast_get_list(ast);
if (!list->children) {
ZVAL_EMPTY_ARRAY(result);
break;
}
array_init(result);
for (i = 0; i < list->children; i++) {
zend_ast *elem = list->child[i];
if (elem->kind == ZEND_AST_UNPACK) {
if (UNEXPECTED(zend_ast_evaluate(&op1, elem->child[0], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(result);
return FAILURE;
}
if (UNEXPECTED(zend_ast_add_unpacked_element(result, &op1) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
zval_ptr_dtor_nogc(result);
return FAILURE;
}
zval_ptr_dtor_nogc(&op1);
continue;
}
if (elem->child[1]) {
if (UNEXPECTED(zend_ast_evaluate(&op1, elem->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(result);
return FAILURE;
}
} else {
ZVAL_UNDEF(&op1);
}
if (UNEXPECTED(zend_ast_evaluate(&op2, elem->child[0], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
zval_ptr_dtor_nogc(result);
return FAILURE;
}
if (UNEXPECTED(zend_ast_add_array_element(result, &op1, &op2) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
zval_ptr_dtor_nogc(&op2);
zval_ptr_dtor_nogc(result);
return FAILURE;
}
}
}
break;
case ZEND_AST_DIM:
if (ast->child[1] == NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
}
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
ret = FAILURE;
break;
}
// DIM on objects is disallowed because it allows executing arbitrary expressions
if (Z_TYPE(op1) == IS_OBJECT) {
zval_ptr_dtor_nogc(&op1);
zend_throw_error(NULL, "Cannot use [] on objects in constant expression");
ret = FAILURE;
break;
}
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&op1);
ret = FAILURE;
break;
}
zend_fetch_dimension_const(result, &op1, &op2, (ast->attr & ZEND_DIM_IS) ? BP_VAR_IS : BP_VAR_R);
zval_ptr_dtor_nogc(&op1);
zval_ptr_dtor_nogc(&op2);
if (UNEXPECTED(EG(exception))) {
return FAILURE;
}
break;
case ZEND_AST_CONST_ENUM_INIT:
{
zend_ast *class_name_ast = ast->child[0];
zend_string *class_name = zend_ast_get_str(class_name_ast);
zend_ast *case_name_ast = ast->child[1];
zend_string *case_name = zend_ast_get_str(case_name_ast);
zend_ast *case_value_ast = ast->child[2];
zval *case_value_zv = case_value_ast != NULL
? zend_ast_get_zval(case_value_ast)
: NULL;
zend_class_entry *ce = zend_lookup_class(class_name);
if (!ce) {
/* Class may not be available when resolving constants on a dynamically
* declared enum during preloading. */
ZEND_ASSERT(CG(compiler_options) & ZEND_COMPILE_PRELOAD);
return FAILURE;
}
zend_enum_new(result, ce, case_name, case_value_zv);
break;
}
case ZEND_AST_CLASS_CONST:
{
zend_string *class_name = zend_ast_get_str(ast->child[0]);
zend_string *const_name = zend_ast_get_str(ast->child[1]);
zval *zv = zend_get_class_constant_ex(class_name, const_name, scope, ast->attr);
if (UNEXPECTED(zv == NULL)) {
ZVAL_UNDEF(result);
return FAILURE;
}
ZVAL_COPY_OR_DUP(result, zv);
break;
}
case ZEND_AST_NEW:
{
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
if (!ce) {
return FAILURE;
}
if (object_init_ex(result, ce) != SUCCESS) {
return FAILURE;
}
zend_ast_list *args_ast = zend_ast_get_list(ast->child[1]);
if (args_ast->attr) {
/* Has named arguments. */
HashTable *args = zend_new_array(args_ast->children);
for (uint32_t i = 0; i < args_ast->children; i++) {
zend_ast *arg_ast = args_ast->child[i];
zend_string *name = NULL;
zval arg;
if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
name = zend_ast_get_str(arg_ast->child[0]);
arg_ast = arg_ast->child[1];
}
if (zend_ast_evaluate(&arg, arg_ast, scope) == FAILURE) {
zend_array_destroy(args);
zval_ptr_dtor(result);
return FAILURE;
}
if (name) {
if (!zend_hash_add(args, name, &arg)) {
zend_throw_error(NULL,
"Named parameter $%s overwrites previous argument",
ZSTR_VAL(name));
zend_array_destroy(args);
zval_ptr_dtor(result);
return FAILURE;
}
} else {
zend_hash_next_index_insert(args, &arg);
}
}
zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (ctor) {
zend_call_known_function(
ctor, Z_OBJ_P(result), Z_OBJCE_P(result), NULL, 0, NULL, args);
}
zend_array_destroy(args);
} else {
ALLOCA_FLAG(use_heap)
zval *args = do_alloca(sizeof(zval) * args_ast->children, use_heap);
for (uint32_t i = 0; i < args_ast->children; i++) {
if (zend_ast_evaluate(&args[i], args_ast->child[i], scope) == FAILURE) {
for (uint32_t j = 0; j < i; j++) {
zval_ptr_dtor(&args[j]);
}
free_alloca(args, use_heap);
zval_ptr_dtor(result);
return FAILURE;
}
}
zend_function *ctor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (ctor) {
zend_call_known_instance_method(
ctor, Z_OBJ_P(result), NULL, args_ast->children, args);
}
for (uint32_t i = 0; i < args_ast->children; i++) {
zval_ptr_dtor(&args[i]);
}
free_alloca(args, use_heap);
}
if (EG(exception)) {
zend_object_store_ctor_failed(Z_OBJ_P(result));
zval_ptr_dtor(result);
return FAILURE;
}
return SUCCESS;
}
default:
zend_throw_error(NULL, "Unsupported constant expression");
ret = FAILURE;
}
return ret;
}
и видим, что это полноценная функция. Но! где ключевое слово function, что обозначает аж три слова перед именем функции(ZEND_API, zend_result, ZEND_FASTCALL) Макросы это, структуры, объединения? Где возвращаемый тип? И почему в других языках функции задаются проще, например:
final static public function get_name(int|float $var): int|float
{
return $var;
}
Где сразу понятно, что возвращает функция и есть ключевое слово function
Ответы (1 шт):
где ключевое слово function
В Си нет такого ключевого слова.
что обозначает аж три слова перед именем функции(ZEND_API, zend_result, ZEND_FASTCALL) Макросы это, структуры, объединения
ZEND_API и ZEND_FASTCALL — это макросы; по болей-менее общепринятым соглашениям все идентификаторы, которые состоят из заглавных букв являются макросами. Оба, вероятнее всего, раскрываются в компиляторо/платформо-зависимые атрибуты. Определяются такие вещи обычно где-то в недрах системах сборки.
ZEND_API, исходя из названия, скорей всего, говорит о том, что функция является частью публичного API разделяемой библиотеки. В зависимости от платформы и компилятора, стоит ожидать, что оно раскроется во что-то вроде__declspec(dllexport)на win и__attribute__ ((visibility ("default")))или пустую строку в *nix'ах.ZEND_FASTCALL, опять же, исходя из названия, говорит, что для этой функции нужно использоватьfastcallABI. Что это значит конкретно зависит от платформы. Может раскрыться во что-то вроде__attribute__ ((fastcall)).
Где возвращаемый тип?
zend_result — это возвращаемый тип. Вероятнее всего, является просто int'ом.
И почему в других языках функции задаются проще
На вкус и цвет все фломастеры разные.
Дополнение
Тогда вопросы. Это все-таки функция да?
да
Немного про то, что макросы вообще такое
И сколько таких макросов можно записать у функции? Например ZEND_API ANOTHER_API zend_result ZEND_SLOWCALL ZEND_FASTCALL zend_ast_evaluate(){} прокатит ли?
Макросы обрабатываются препроцессором до компиляции. Вместо них просто подставляется заданная строка (возможно пустая). т.е. в каком-то файле, который это включает задано
#define ZEND_API __attribute__ ((visibility ("default"))
#define ZEND_FASTCALL __attribute__ ((fastcall))
т.е. вместо строки
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);
компилятор получит на вход строку вида:
__attribute__ ((visibility ("default")) zend_result __attribute__ ((fastcall)) zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);
А если они определены как пустые строки:
#define ZEND_API
#define ZEND_FASTCALL
то компилятор получит уже строку вида:
zend_result zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);
т.е., вообще говоря, макросов может быть сколько угодно и в любом месте, но будет ли код корректен после препроцессирования — зависит от того, как они определены.
«Атрибуты» из примеров выше в свою очередь не определены стандартом языка и являются расширениями компилятора. Для разных компиляторов они зачастую разные. Их возможные значения и семантику стоит смотреть в документации своего компилятора. Поэтому (а также потому что они обычно выглядят моструозно) их обычно и прячут внутрь макроопределений — так, изменяя их, можно относительно легко сконфигурировать один и тот же исходный код, чтобы он компилировался разными компиляторами и под разные платформы.