// SPDX-FileCopyrightText: 2021 RizinOrg // SPDX-FileCopyrightText: 2021 deroad // SPDX-FileCopyrightText: 2021 heersin // SPDX-License-Identifier: LGPL-3.0-only #include #include #include /** * 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, NULL); 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 /**/ *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"; } }