mirror of
https://github.com/rizinorg/rizin
synced 2026-08-22 20:26:16 -04:00
709 lines
21 KiB
C
709 lines
21 KiB
C
// SPDX-FileCopyrightText: 2013-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-FileCopyrightText: 2013-2020 oddcoder <ahmedsoliman@oddcoder.com>
|
|
// SPDX-FileCopyrightText: 2013-2020 sivaramaaa <sivaramaaa@gmail.com>
|
|
// SPDX-FileCopyrightText: 2021 Anton Kochkov <anton.kochkov@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_util.h>
|
|
#include <rz_type.h>
|
|
#include <string.h>
|
|
|
|
/**
|
|
* \brief Creates a new RzCallable type
|
|
*
|
|
* \param name Name of the callable type
|
|
* \param type A return type of the callable type
|
|
*/
|
|
RZ_API RZ_OWN RzCallable *rz_type_callable_new(RZ_NULLABLE const char *name) {
|
|
RzCallable *callable = RZ_NEW0(RzCallable);
|
|
if (!callable) {
|
|
return NULL;
|
|
}
|
|
callable->ret = NULL;
|
|
callable->name = rz_str_dup(name);
|
|
callable->args = rz_pvector_new((RzPVectorFree)rz_type_callable_arg_free);
|
|
return callable;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates an exact clone of the RzCallable type
|
|
*
|
|
* \param callable RzCallable instance to clone
|
|
*/
|
|
RZ_API RZ_OWN RzCallable *rz_type_callable_clone(RZ_BORROW RZ_NONNULL const RzCallable *callable) {
|
|
rz_return_val_if_fail(callable, NULL);
|
|
RzCallable *newcallable = RZ_NEWCOPY(RzCallable, callable);
|
|
if (!newcallable) {
|
|
return NULL;
|
|
}
|
|
newcallable->ret = callable->ret ? rz_type_clone_shallow(callable->ret) : NULL;
|
|
newcallable->name = rz_str_dup(callable->name);
|
|
newcallable->args = rz_pvector_new((RzPVectorFree)rz_type_callable_arg_free);
|
|
void **it;
|
|
rz_pvector_foreach (callable->args, it) {
|
|
RzCallableArg *arg = *it;
|
|
rz_pvector_push(newcallable->args, rz_type_callable_arg_clone(arg));
|
|
}
|
|
return newcallable;
|
|
}
|
|
|
|
/**
|
|
* \brief Frees the RzCallable
|
|
*
|
|
* \param callable RzCallable type
|
|
*/
|
|
RZ_API void rz_type_callable_free(RZ_NONNULL RzCallable *callable) {
|
|
rz_type_free(callable->ret);
|
|
rz_pvector_free(callable->args);
|
|
free(callable->name);
|
|
free(callable);
|
|
}
|
|
|
|
/**
|
|
* \brief Creates a new RzCallableArg given the name and type
|
|
*
|
|
* \param typedb RzTypeDB instance
|
|
* \param name Name of the argument
|
|
* \param type RzType type of the argument
|
|
*/
|
|
RZ_API RZ_OWN RzCallableArg *rz_type_callable_arg_new(RzTypeDB *typedb, RZ_NONNULL const char *name, RZ_OWN RZ_NONNULL RzType *type) {
|
|
rz_return_val_if_fail(typedb && name && type, NULL);
|
|
RzCallableArg *arg = RZ_NEW0(RzCallableArg);
|
|
if (!arg) {
|
|
rz_type_free(type);
|
|
return NULL;
|
|
}
|
|
arg->name = rz_str_dup(name);
|
|
arg->type = type;
|
|
return arg;
|
|
}
|
|
|
|
/**
|
|
* \brief Creates am exact clone of RzCallableArg
|
|
*
|
|
* \param arg RzCallable argument pointer
|
|
*/
|
|
RZ_API RZ_OWN RzCallableArg *rz_type_callable_arg_clone(RZ_BORROW RZ_NONNULL const RzCallableArg *arg) {
|
|
rz_return_val_if_fail(arg, NULL);
|
|
RzCallableArg *newarg = RZ_NEW0(RzCallableArg);
|
|
if (!newarg) {
|
|
return NULL;
|
|
}
|
|
newarg->name = rz_str_dup(arg->name);
|
|
newarg->type = rz_type_clone_shallow(arg->type);
|
|
return newarg;
|
|
}
|
|
|
|
/**
|
|
* \brief Frees the RzCallableArg
|
|
*
|
|
* \param arg RzCallableArg instance
|
|
*/
|
|
RZ_API void rz_type_callable_arg_free(RzCallableArg *arg) {
|
|
if (!arg) {
|
|
return;
|
|
}
|
|
free(arg->name);
|
|
rz_type_free(arg->type);
|
|
free(arg);
|
|
}
|
|
|
|
/**
|
|
* \brief Adds a new argument to the RzCallable
|
|
*
|
|
* \param callable RzCallable instance
|
|
* \param arg Argument to add
|
|
*/
|
|
RZ_API bool rz_type_callable_arg_add(RZ_NONNULL RzCallable *callable, RZ_OWN RZ_NONNULL RzCallableArg *arg) {
|
|
rz_return_val_if_fail(callable && arg, false);
|
|
rz_pvector_push(callable->args, arg);
|
|
return true;
|
|
}
|
|
|
|
// Function prototypes api
|
|
|
|
/**
|
|
* \brief Creates a new RzCallable type
|
|
*
|
|
* \param typedb RzTypeDB instance
|
|
* \param name Name of the callable type
|
|
* \param type A return type of the callable type
|
|
*/
|
|
RZ_API RZ_OWN RzCallable *rz_type_func_new(RzTypeDB *typedb, RZ_NONNULL const char *name, RZ_OWN RZ_NULLABLE RzType *type) {
|
|
rz_return_val_if_fail(typedb && name, NULL);
|
|
RzCallable *callable = rz_type_callable_new(name);
|
|
if (!callable) {
|
|
rz_type_free(type);
|
|
return NULL;
|
|
}
|
|
callable->ret = type;
|
|
return callable;
|
|
}
|
|
|
|
/**
|
|
* \brief Stores the given RzCallable type in the types database
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param callable RzCallable to store; ownership is transferred only on success
|
|
* \return true if \p callable was stored, false if a callable with the same
|
|
* name already exists or the arguments are invalid
|
|
*
|
|
* On failure the database does not take ownership of \p callable, so the
|
|
* caller remains responsible for freeing it with rz_type_callable_free().
|
|
*/
|
|
RZ_API bool rz_type_func_save(RzTypeDB *typedb, RZ_NONNULL RzCallable *callable) {
|
|
rz_return_val_if_fail(typedb && callable && callable->name, false);
|
|
if (rz_type_func_exist(typedb, callable->name)) {
|
|
return false;
|
|
}
|
|
ht_sp_insert(typedb->callables, callable->name, callable);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Update RzCallable type in the types database
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param callable RzCallable type to save
|
|
*/
|
|
RZ_API bool rz_type_func_update(RzTypeDB *typedb, RZ_NONNULL RzCallable *callable) {
|
|
rz_return_val_if_fail(typedb && callable && callable->name, false);
|
|
if (!ht_sp_update(typedb->callables, callable->name, (void *)callable)) {
|
|
rz_type_callable_free(callable);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the RzCallable from the database by name
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name RzCallable (function) name to search
|
|
*/
|
|
RZ_API RZ_BORROW RzCallable *rz_type_func_get(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, NULL);
|
|
bool found = false;
|
|
RzCallable *callable = ht_sp_find(typedb->callables, name, &found);
|
|
if (!found || !callable) {
|
|
RZ_LOG_DEBUG("Cannot find function type \"%s\"\n", name);
|
|
return NULL;
|
|
}
|
|
return callable;
|
|
}
|
|
|
|
/**
|
|
* \brief Removes RzCallable type from the types database
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
*/
|
|
RZ_API bool rz_type_func_delete(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, false);
|
|
ht_sp_delete(typedb->callables, name);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Removes all RzCallable types
|
|
*/
|
|
RZ_API void rz_type_func_delete_all(RzTypeDB *typedb) {
|
|
ht_sp_free(typedb->callables);
|
|
typedb->callables = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_type_callable_free);
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the RzCallable type exists in the database given the name
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
*/
|
|
RZ_API bool rz_type_func_exist(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, false);
|
|
bool found = false;
|
|
return ht_sp_find(typedb->callables, name, &found) && found;
|
|
}
|
|
|
|
/**
|
|
* \brief Searches for the RzCallable type in types database and returns return type
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
*/
|
|
RZ_API RZ_BORROW RzType *rz_type_func_ret(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, NULL);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return NULL;
|
|
}
|
|
return callable->ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Searches for the RzCallable type in types database and returns calling convention
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
*/
|
|
RZ_API RZ_BORROW const char *rz_type_func_cc(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, NULL);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return NULL;
|
|
}
|
|
return callable->cc;
|
|
}
|
|
|
|
/**
|
|
* \brief Searches for the RzCallable type in types database and set the calling convention
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
* \param name Name of the calling convention to set
|
|
*/
|
|
RZ_API bool rz_type_func_cc_set(RzTypeDB *typedb, const char *name, const char *cc) {
|
|
rz_return_val_if_fail(typedb && name, false);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return false;
|
|
}
|
|
callable->cc = rz_str_dup(cc);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Searches for the RzCallable type in types database and returns arguments' count
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
*/
|
|
RZ_API int rz_type_func_args_count(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, 0);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return -1;
|
|
}
|
|
return rz_pvector_len(callable->args);
|
|
}
|
|
|
|
/**
|
|
* \brief Searches for the RzCallable type in types database and returns argument type
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
* \param i Index of the argument go get type of
|
|
*/
|
|
RZ_API RZ_BORROW RzType *rz_type_func_args_type(RzTypeDB *typedb, RZ_NONNULL const char *name, int i) {
|
|
rz_return_val_if_fail(typedb && name, NULL);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return NULL;
|
|
}
|
|
if (i >= rz_pvector_len(callable->args)) {
|
|
return NULL;
|
|
}
|
|
RzCallableArg *arg = rz_pvector_at(callable->args, i);
|
|
if (!arg) {
|
|
rz_warn_if_reached(); // should not happen in the types database
|
|
return NULL;
|
|
}
|
|
return arg->type;
|
|
}
|
|
|
|
/**
|
|
* \brief Searches for the RzCallable type in types database and returns argument name
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
* \param i Index of the argument go get type of
|
|
*/
|
|
RZ_API RZ_BORROW const char *rz_type_func_args_name(RzTypeDB *typedb, RZ_NONNULL const char *name, int i) {
|
|
rz_return_val_if_fail(typedb && name, NULL);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return NULL;
|
|
}
|
|
if (i >= rz_pvector_len(callable->args)) {
|
|
return NULL;
|
|
}
|
|
RzCallableArg *arg = rz_pvector_at(callable->args, i);
|
|
if (!arg) {
|
|
rz_warn_if_reached(); // should not happen in the types database
|
|
return NULL;
|
|
}
|
|
return arg->name;
|
|
}
|
|
|
|
/**
|
|
* \brief Adds a new argument to the RzCallable type at the end of the arguments vector
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param func_name Name of the callable to search
|
|
* \param arg_name Name of the new argument
|
|
* \param arg_type RzType type of the new argument
|
|
*/
|
|
RZ_API bool rz_type_func_arg_add(RzTypeDB *typedb, RZ_NONNULL const char *func_name, RZ_NONNULL const char *arg_name, RZ_OWN RZ_NONNULL RzType *arg_type) {
|
|
rz_return_val_if_fail(typedb && func_name, false);
|
|
RzCallable *callable = rz_type_func_get(typedb, func_name);
|
|
if (!callable) {
|
|
rz_type_free(arg_type);
|
|
return false;
|
|
}
|
|
RzCallableArg *arg = rz_type_callable_arg_new(typedb, arg_name, arg_type);
|
|
if (!arg) {
|
|
return false;
|
|
}
|
|
rz_pvector_push(callable->args, arg);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the new return type for the RzCallable
|
|
*
|
|
* \param typedb Type Database instance
|
|
* \param name Name of the callable to search
|
|
* \param type RzType return type
|
|
*/
|
|
RZ_API bool rz_type_func_ret_set(RzTypeDB *typedb, const char *name, RZ_BORROW RZ_NONNULL RzType *type) {
|
|
rz_return_val_if_fail(typedb && name && type, false);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return false;
|
|
}
|
|
rz_type_free(callable->ret);
|
|
callable->ret = rz_type_clone_shallow(type);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the RzType is the pointer to the RzCallable
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param type RzType
|
|
*/
|
|
RZ_API bool rz_type_is_callable(RZ_NONNULL const RzType *type) {
|
|
rz_return_val_if_fail(type, false);
|
|
return type->kind == RZ_TYPE_KIND_CALLABLE;
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the RzType is the pointer to the RzCallable
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param type RzType
|
|
*/
|
|
RZ_API bool rz_type_is_callable_ptr(RZ_NONNULL const RzType *type) {
|
|
rz_return_val_if_fail(type, false);
|
|
if (type->kind != RZ_TYPE_KIND_POINTER) {
|
|
return false;
|
|
}
|
|
if (type->pointer.type->kind == RZ_TYPE_KIND_CALLABLE) {
|
|
return true;
|
|
} else if (type->pointer.type->kind == RZ_TYPE_KIND_POINTER) {
|
|
return rz_type_is_callable_ptr(type->pointer.type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool could_be_callable(RzTypeKind kind) {
|
|
/* We can have function pointers and arrays to function pointers as well. */
|
|
return kind == RZ_TYPE_KIND_POINTER || kind == RZ_TYPE_KIND_ARRAY;
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the RzType is the nested pointer to the RzCallable
|
|
*
|
|
* For example it could be one of those:
|
|
* - int (*func)(int a, char *b)
|
|
* - int (**func)(int a, char *b)
|
|
* - int (****func)(int a, char *b)
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param type RzType
|
|
*/
|
|
RZ_API bool rz_type_is_callable_ptr_nested(RZ_NONNULL const RzType *type) {
|
|
rz_return_val_if_fail(type, false);
|
|
if (!could_be_callable(type->kind)) {
|
|
return false;
|
|
}
|
|
|
|
RzType *ptr = NULL;
|
|
if (type->kind == RZ_TYPE_KIND_POINTER) {
|
|
ptr = type->pointer.type;
|
|
} else if (type->kind == RZ_TYPE_KIND_ARRAY) {
|
|
ptr = type->array.type;
|
|
}
|
|
|
|
rz_return_val_if_fail(ptr, false);
|
|
if (could_be_callable(ptr->kind)) {
|
|
return rz_type_is_callable_ptr_nested(ptr);
|
|
}
|
|
|
|
if (ptr->kind == RZ_TYPE_KIND_CALLABLE) {
|
|
/* If we did encounter a callable, then the parent kind should necessarily be a pointer. */
|
|
rz_return_val_if_fail(type->kind == RZ_TYPE_KIND_POINTER, false);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static const RzCallable *callable_ptr_unwrap(RZ_NONNULL const RzType *type, RZ_NONNULL RzVector /*<st64>*/ *wrapper_type_infos) {
|
|
rz_return_val_if_fail(type && wrapper_type_infos, NULL);
|
|
|
|
if (type->kind == RZ_TYPE_KIND_POINTER) {
|
|
st64 array_len = -1;
|
|
rz_vector_push(wrapper_type_infos, &array_len);
|
|
|
|
return callable_ptr_unwrap(type->pointer.type, wrapper_type_infos);
|
|
} else if (type->kind == RZ_TYPE_KIND_ARRAY) {
|
|
st64 array_len = type->array.count;
|
|
rz_vector_push(wrapper_type_infos, &array_len);
|
|
|
|
return callable_ptr_unwrap(type->array.type, wrapper_type_infos);
|
|
}
|
|
|
|
return type->kind == RZ_TYPE_KIND_CALLABLE ? type->callable : NULL;
|
|
}
|
|
|
|
static inline char *callable_name_or_ptr(RZ_NONNULL const RzCallable *callable, RZ_NONNULL RzVector /*<st64>*/ *wrapper_type_infos, bool zero_vla) {
|
|
rz_return_val_if_fail(callable && wrapper_type_infos, NULL);
|
|
|
|
if (rz_vector_empty(wrapper_type_infos)) {
|
|
return rz_str_dup(rz_str_get(callable->name));
|
|
} else {
|
|
RzStrBuf *buf = rz_strbuf_new(callable->name);
|
|
rz_return_val_if_fail(buf, NULL);
|
|
|
|
RzTypeKind last_kind = RZ_TYPE_KIND_IDENTIFIER;
|
|
bool innermost_wrapper = true;
|
|
|
|
st64 *it;
|
|
rz_vector_foreach (wrapper_type_infos, it) {
|
|
st64 array_len = *it;
|
|
|
|
RzTypeKind current_kind;
|
|
if (array_len < 0) {
|
|
current_kind = RZ_TYPE_KIND_POINTER;
|
|
} else {
|
|
current_kind = RZ_TYPE_KIND_ARRAY;
|
|
}
|
|
|
|
/* No need for parens for the innermost wrapper, since they would be superfluous. */
|
|
if (!innermost_wrapper && last_kind != current_kind) {
|
|
/* Change in the wrapping type's kind */
|
|
rz_strbuf_prepend(buf, "(");
|
|
rz_strbuf_append(buf, ")");
|
|
}
|
|
last_kind = current_kind;
|
|
|
|
innermost_wrapper = false;
|
|
|
|
if (current_kind == RZ_TYPE_KIND_POINTER) {
|
|
rz_strbuf_prepend(buf, "*");
|
|
} else if (current_kind == RZ_TYPE_KIND_ARRAY) {
|
|
char *array_str;
|
|
|
|
if (array_len == 0) {
|
|
array_str = rz_str_newf("[%s]", zero_vla ? "0" : "");
|
|
} else {
|
|
array_str = rz_str_newf("[%" PFMT64d "]", array_len);
|
|
}
|
|
|
|
rz_return_val_if_fail(array_str, NULL);
|
|
rz_strbuf_append(buf, array_str);
|
|
|
|
RZ_FREE(array_str);
|
|
} else {
|
|
rz_return_val_if_reached(NULL);
|
|
}
|
|
}
|
|
|
|
rz_strbuf_prepend(buf, "(");
|
|
rz_strbuf_append(buf, ")");
|
|
|
|
return rz_strbuf_drain(buf);
|
|
}
|
|
}
|
|
|
|
static bool callable_as_string(RzStrBuf *buf, const RzTypeDB *typedb, RZ_NONNULL const RzCallable *callable, RZ_NONNULL RzVector /*<st64>*/ *wrapper_type_infos, bool zero_vla) {
|
|
rz_return_val_if_fail(buf && typedb && callable && wrapper_type_infos, false);
|
|
|
|
if (callable->noret) {
|
|
rz_strbuf_append(buf, "__attribute__((noreturn)) ");
|
|
}
|
|
char *ret_str = callable->ret ? rz_type_as_string(typedb, callable->ret) : NULL;
|
|
char *callable_name = callable_name_or_ptr(callable, wrapper_type_infos, zero_vla);
|
|
rz_strbuf_appendf(buf, "%s %s(", ret_str ? ret_str : "void", callable_name);
|
|
free(ret_str);
|
|
free(callable_name);
|
|
void **it;
|
|
bool first = true;
|
|
rz_pvector_foreach (callable->args, it) {
|
|
RzCallableArg *arg = *it;
|
|
if (arg) {
|
|
char *argstr = rz_type_identifier_declaration_as_string(typedb, arg->type, rz_str_get(arg->name));
|
|
const char *comma = first ? "" : ", ";
|
|
rz_strbuf_appendf(buf, "%s%s", comma, argstr);
|
|
first = false;
|
|
free(argstr);
|
|
}
|
|
}
|
|
if (callable->has_unspecified_parameters) {
|
|
if (rz_pvector_len(callable->args) >= 1) {
|
|
rz_strbuf_append(buf, ", ");
|
|
}
|
|
rz_strbuf_append(buf, "...");
|
|
}
|
|
rz_strbuf_append(buf, ")");
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the callable pointer C representation
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param callable RzCallable instance
|
|
*/
|
|
RZ_API RZ_OWN char *rz_type_callable_ptr_as_string(const RzTypeDB *typedb, RZ_NONNULL const RzType *type, bool zero_vla) {
|
|
rz_return_val_if_fail(typedb && type, NULL);
|
|
rz_return_val_if_fail(could_be_callable(type->kind), NULL);
|
|
|
|
RzVector *wrapper_type_infos = rz_vector_new(sizeof(st64), NULL, free);
|
|
|
|
const RzCallable *callable = callable_ptr_unwrap(type, wrapper_type_infos);
|
|
if (!callable) {
|
|
rz_vector_free(wrapper_type_infos);
|
|
return NULL;
|
|
}
|
|
RzStrBuf *buf = rz_strbuf_new("");
|
|
if (!callable_as_string(buf, typedb, callable, wrapper_type_infos, zero_vla)) {
|
|
rz_vector_free(wrapper_type_infos);
|
|
rz_strbuf_free(buf);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rz_vector_free(wrapper_type_infos);
|
|
return rz_strbuf_drain(buf);
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the callable C representation
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param callable RzCallable instance
|
|
*/
|
|
RZ_API RZ_OWN char *rz_type_callable_as_string(const RzTypeDB *typedb, RZ_NONNULL const RzCallable *callable) {
|
|
rz_return_val_if_fail(typedb && callable, NULL);
|
|
RzStrBuf *buf = rz_strbuf_new("");
|
|
|
|
RzVector *wrapper_type_infos = rz_vector_new(sizeof(st64), NULL, free);
|
|
if (!callable_as_string(buf, typedb, callable, wrapper_type_infos, false)) {
|
|
rz_strbuf_free(buf);
|
|
rz_vector_free(wrapper_type_infos);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rz_vector_free(wrapper_type_infos);
|
|
return rz_strbuf_drain(buf);
|
|
}
|
|
|
|
/**
|
|
* \brief Checks if the RzCallable type is defined as "noreturn"
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param name Name of the RzCallable type
|
|
*/
|
|
RZ_API bool rz_type_func_is_noreturn(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, false);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return false;
|
|
}
|
|
return callable->noret;
|
|
}
|
|
|
|
/**
|
|
* \brief Adds the "noreturn" attribute to the RzCallable type
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param name Name of the RzCallable type
|
|
*/
|
|
RZ_API bool rz_type_func_noreturn_add(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, false);
|
|
// If the function exists with the specified name already, we set the noreturn flag for it
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (callable) {
|
|
callable->noret = true;
|
|
} else {
|
|
// If it doesn't - we create a new dummy RzCallable for it
|
|
// The return type is default and no arguments
|
|
callable = rz_type_func_new(typedb, name, NULL);
|
|
if (!callable) {
|
|
return false;
|
|
}
|
|
callable->noret = true;
|
|
rz_type_func_save(typedb, callable);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Drops the "noreturn" attribute from the RzCallable type
|
|
*
|
|
* \param typedb Types Database instance
|
|
* \param name Name of the RzCallable type
|
|
*/
|
|
RZ_API bool rz_type_func_noreturn_drop(RzTypeDB *typedb, RZ_NONNULL const char *name) {
|
|
rz_return_val_if_fail(typedb && name, false);
|
|
RzCallable *callable = rz_type_func_get(typedb, name);
|
|
if (!callable) {
|
|
return false;
|
|
}
|
|
callable->noret = false;
|
|
return true;
|
|
}
|
|
|
|
// Listing function types
|
|
|
|
static bool function_names_collect_cb(void *user, RZ_UNUSED const char *k, const void *v) {
|
|
RzList *l = (RzList *)user;
|
|
RzCallable *callable = (RzCallable *)v;
|
|
rz_list_append(l, rz_str_dup(callable->name));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the list of all function type names
|
|
*
|
|
* \param typedb Types Database instance
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<char *>*/ *rz_type_function_names(RzTypeDB *typedb) {
|
|
rz_return_val_if_fail(typedb, NULL);
|
|
RzList *result = rz_list_newf(free);
|
|
ht_sp_foreach(typedb->callables, function_names_collect_cb, result);
|
|
return result;
|
|
}
|
|
|
|
static bool noreturn_function_names_collect_cb(void *user, RZ_UNUSED const char *k, const void *v) {
|
|
RzList *l = (RzList *)user;
|
|
RzCallable *callable = (RzCallable *)v;
|
|
if (callable->noret) {
|
|
rz_list_append(l, rz_str_dup(callable->name));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the list of all noreturn function type names
|
|
*
|
|
* \param typedb Types Database instance
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<char *>*/ *rz_type_noreturn_function_names(RzTypeDB *typedb) {
|
|
rz_return_val_if_fail(typedb, NULL);
|
|
RzList *noretl = rz_list_newf(free);
|
|
ht_sp_foreach(typedb->callables, noreturn_function_names_collect_cb, noretl);
|
|
return noretl;
|
|
}
|