rizin/librz/il/definitions/variable.c
2023-05-09 15:42:21 +08:00

192 lines
4.8 KiB
C

// SPDX-FileCopyrightText: 2021 RizinOrg <info@rizin.re>
// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
// SPDX-FileCopyrightText: 2021 heersin <teablearcher@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_il/definitions/variable.h>
#include <string.h>
#include <stdlib.h>
/**
* Create a new variable description
*/
RZ_API RZ_OWN RzILVar *rz_il_variable_new(RZ_NONNULL const char *name, RzILSortPure sort) {
rz_return_val_if_fail(name, NULL);
RzILVar *ret = RZ_NEW0(RzILVar);
if (!ret) {
return NULL;
}
ret->name = strdup(name);
if (!ret->name) {
free(ret);
return NULL;
}
ret->sort = sort;
return ret;
}
/**
* Free variable
* \param var RzILVar, pointer to RzILVar
*/
RZ_API void rz_il_variable_free(RZ_NULLABLE RzILVar *var) {
if (!var) {
return;
}
free(var->name);
free(var);
}
// Variable Set
static void var_ht_free(HtPPKv *kv) {
free(kv->key);
rz_il_variable_free(kv->value);
}
static void val_ht_free(HtPPKv *kv) {
free(kv->key);
rz_il_value_free(kv->value);
}
/**
* Initialize \p vs as an empty variable set
*
* This makes sure that if a failure occurs, the contents are still zeroed out,
* so it is safe (but not required) to call rz_il_var_set_fini(), even if the init failed.
*
* \return whether the initialization succeeded
*/
RZ_API bool rz_il_var_set_init(RzILVarSet *vs) {
rz_return_val_if_fail(vs, false);
memset(vs, 0, sizeof(*vs));
vs->vars = ht_pp_new(NULL, var_ht_free, NULL);
if (!vs->vars) {
return false;
}
vs->contents = ht_pp_new(NULL, val_ht_free, NULL);
if (!vs->contents) {
ht_pp_free(vs->vars);
vs->vars = NULL;
return false;
}
return true;
}
RZ_API void rz_il_var_set_fini(RzILVarSet *vs) {
ht_pp_free(vs->vars);
ht_pp_free(vs->contents);
}
RZ_API void rz_il_var_set_reset(RzILVarSet *vs) {
rz_il_var_set_fini(vs);
rz_il_var_set_init(vs);
}
/**
* Create a new variable of the given name and sort.
* If a variable of this name already exists, nothing happens.
*/
RZ_API RZ_BORROW RzILVar *rz_il_var_set_create_var(RzILVarSet *vs, const char *name, RzILSortPure sort) {
rz_return_val_if_fail(vs && name, NULL);
if (ht_pp_find(vs->vars, name, NULL)) {
return NULL;
}
RzILVar *var = rz_il_variable_new(name, sort);
if (!var) {
return NULL;
}
ht_pp_insert(vs->vars, name, var);
return var;
}
/**
* Remove a variable, if it exists
* \return the variable's variable, to be freed by the caller
*/
RZ_API RZ_OWN RZ_NULLABLE RzILVal *rz_il_var_set_remove_var(RzILVarSet *vs, const char *name) {
rz_return_val_if_fail(vs && name, NULL);
ht_pp_delete(vs->vars, name);
HtPPKv *kv = ht_pp_find_kv(vs->contents, name, NULL);
if (!kv) {
return NULL;
}
RzILVal *r = kv->value;
kv->value = NULL;
ht_pp_delete(vs->contents, name);
return r;
}
/**
* Set the contents of the variable called \p name to \p val
*
* In order for this to succeed, a variable called \p name must already exist
* and the sort of \p val must match the variable's sort. Checking this is done
* inside this function, so calling it with invalid args in that sense is fine.
*
* \return whether the value was successfully bound
*/
RZ_API bool rz_il_var_set_bind(RzILVarSet *vs, const char *name, RZ_OWN RzILVal *val) {
rz_return_val_if_fail(vs && name && val, false);
RzILVar *var = ht_pp_find(vs->vars, name, NULL);
if (!var || !rz_il_sort_pure_eq(var->sort, rz_il_value_get_sort(val))) {
if (!var) {
RZ_LOG_ERROR("Attempted to bind value to non-existent variable \"%s\"\n", name);
} else {
RZ_LOG_ERROR("Attempted to bind mis-sorted value to variable \"%s\"\n", name);
}
rz_il_value_free(val);
return false;
}
ht_pp_update(vs->contents, name, val);
return true;
}
/**
* Get the definition of the variable called \p name
*/
RZ_API RZ_BORROW RzILVar *rz_il_var_set_get(RzILVarSet *vs, const char *name) {
return ht_pp_find(vs->vars, name, NULL);
}
static bool vars_collect_cb(void *user, const void *k, const void *v) {
rz_pvector_push(user, (void *)v);
return true;
}
/**
* Get a list of all variable definitions in the given set
*/
RZ_API RZ_OWN RzPVector /*<RzILVar *>*/ *rz_il_var_set_get_all(RzILVarSet *vs) {
rz_return_val_if_fail(vs, NULL);
RzPVector *r = rz_pvector_new(NULL);
if (!r) {
return NULL;
}
ht_pp_foreach(vs->vars, vars_collect_cb, r);
return r;
}
/**
* Get the current value of the variable called \p name
*/
RZ_API RZ_BORROW RzILVal *rz_il_var_set_get_value(RzILVarSet *vs, const char *name) {
rz_return_val_if_fail(vs && name, NULL);
return ht_pp_find(vs->contents, name, NULL);
}
/**
* Get a readable string representation of \p kind
*/
const char *rz_il_var_kind_name(RzILVarKind kind) {
switch (kind) {
case RZ_IL_VAR_KIND_GLOBAL:
return "global";
case RZ_IL_VAR_KIND_LOCAL:
return "local";
case RZ_IL_VAR_KIND_LOCAL_PURE:
return "local pure";
default:
return "invalid";
}
}