Use reference counting for types.
This commit is contained in:
parent
264f420186
commit
3ada78a557
|
@ -31,7 +31,7 @@ struct libab_behavior_s {
|
||||||
/**
|
/**
|
||||||
* The type of the function.
|
* The type of the function.
|
||||||
*/
|
*/
|
||||||
libab_parsetype* type;
|
libab_ref type;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* A struct that holds informatiion
|
* A struct that holds informatiion
|
||||||
|
|
|
@ -33,7 +33,7 @@ void libab_parser_init(libab_parser* parser, libab_table* table);
|
||||||
libab_result libab_parser_parse(libab_parser* parser, ll* tokens,
|
libab_result libab_parser_parse(libab_parser* parser, ll* tokens,
|
||||||
const char* string, libab_tree** store_into);
|
const char* string, libab_tree** store_into);
|
||||||
libab_result libab_parser_parse_type(libab_parser* parser, ll* tokens,
|
libab_result libab_parser_parse_type(libab_parser* parser, ll* tokens,
|
||||||
const char* string, libab_parsetype** store_into);
|
const char* string, libab_ref* store_into);
|
||||||
/**
|
/**
|
||||||
* Releases the resources allocated by the parser.
|
* Releases the resources allocated by the parser.
|
||||||
* @param parser the parser to release.
|
* @param parser the parser to release.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define LIBABACUS_PARSETYPE_H
|
#define LIBABACUS_PARSETYPE_H
|
||||||
|
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
#include "vec.h"
|
#include "ref_vec.h"
|
||||||
#include "basetype.h"
|
#include "basetype.h"
|
||||||
|
|
||||||
#define LIBABACUS_TYPE_F_PARENT (1)
|
#define LIBABACUS_TYPE_F_PARENT (1)
|
||||||
|
@ -10,13 +10,7 @@
|
||||||
#define LIBABACUS_TYPE_F_RESOLVED (1 << 2)
|
#define LIBABACUS_TYPE_F_RESOLVED (1 << 2)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A parse type.
|
* A type, either parsed or resolved.
|
||||||
* A parse type is a type as it was parsed, not
|
|
||||||
* resolved. Effectively, it doesn't have to be valid,
|
|
||||||
* or it can contain references to types whose adresses
|
|
||||||
* are not yet known. The parse type is recursive,
|
|
||||||
* with PT_STRING being the "base case", and PT_PARENT
|
|
||||||
* meaning an initialized children vector with the sub-parse types.
|
|
||||||
*/
|
*/
|
||||||
struct libab_parsetype_s {
|
struct libab_parsetype_s {
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +34,7 @@ struct libab_parsetype_s {
|
||||||
* A vector of children that this parse type contains.
|
* A vector of children that this parse type contains.
|
||||||
* The children are effectively type parameters.
|
* The children are effectively type parameters.
|
||||||
*/
|
*/
|
||||||
vec children;
|
libab_ref_vec children;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct libab_parsetype_s libab_parsetype;
|
typedef struct libab_parsetype_s libab_parsetype;
|
||||||
|
@ -51,11 +45,5 @@ typedef struct libab_parsetype_s libab_parsetype;
|
||||||
* @param type the type to free.
|
* @param type the type to free.
|
||||||
*/
|
*/
|
||||||
void libab_parsetype_free(libab_parsetype* type);
|
void libab_parsetype_free(libab_parsetype* type);
|
||||||
/**
|
|
||||||
* Recursively frees the given parse type, calling free
|
|
||||||
* on every single type (including the one passed in).
|
|
||||||
* @param type the type to free.
|
|
||||||
*/
|
|
||||||
void libab_parsetype_free_recursive(libab_parsetype* type);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct libab_tree_s {
|
||||||
/**
|
/**
|
||||||
* The parse type of this node, if applicable.
|
* The parse type of this node, if applicable.
|
||||||
*/
|
*/
|
||||||
libab_parsetype* parse_type;
|
libab_ref type;
|
||||||
/**
|
/**
|
||||||
* The variant of tree node.
|
* The variant of tree node.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "custom.h"
|
#include "custom.h"
|
||||||
|
|
||||||
void libab_behavior_free(libab_behavior* behavior) {
|
void libab_behavior_free(libab_behavior* behavior) {
|
||||||
libab_parsetype_free_recursive(behavior->type);
|
libab_ref_free(&behavior->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void libab_operator_free(libab_operator* op) {
|
void libab_operator_free(libab_operator* op) {
|
||||||
|
|
|
@ -55,7 +55,7 @@ libab_result _register_operator(libab* ab, const char* op, libab_operator_varian
|
||||||
libab_table_entry* new_entry;
|
libab_table_entry* new_entry;
|
||||||
if((new_entry = malloc(sizeof(*new_entry)))) {
|
if((new_entry = malloc(sizeof(*new_entry)))) {
|
||||||
new_entry->variant = ENTRY_OP;
|
new_entry->variant = ENTRY_OP;
|
||||||
new_entry->data_u.op.behavior.type = NULL;
|
libab_ref_null(&new_entry->data_u.op.behavior.type);
|
||||||
new_entry->data_u.op.precedence = precedence;
|
new_entry->data_u.op.precedence = precedence;
|
||||||
new_entry->data_u.op.associativity = associativity;
|
new_entry->data_u.op.associativity = associativity;
|
||||||
new_entry->data_u.op.type = token_type;
|
new_entry->data_u.op.type = token_type;
|
||||||
|
@ -64,6 +64,7 @@ libab_result _register_operator(libab* ab, const char* op, libab_operator_varian
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
|
libab_ref_free(&new_entry->data_u.op.behavior.type);
|
||||||
result = _initialize_behavior(ab, &(new_entry->data_u.op.behavior), type, func);
|
result = _initialize_behavior(ab, &(new_entry->data_u.op.behavior), type, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,8 +78,8 @@ libab_result _register_operator(libab* ab, const char* op, libab_operator_varian
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != LIBAB_SUCCESS) {
|
if(result != LIBAB_SUCCESS) {
|
||||||
if(new_entry && new_entry->data_u.op.behavior.type)
|
if(new_entry)
|
||||||
libab_parsetype_free_recursive(new_entry->data_u.op.behavior.type);
|
libab_ref_free(&new_entry->data_u.op.behavior.type);
|
||||||
eval_config_remove(&ab->lexer.config, op, TOKEN_OP);
|
eval_config_remove(&ab->lexer.config, op, TOKEN_OP);
|
||||||
free(new_entry);
|
free(new_entry);
|
||||||
}
|
}
|
||||||
|
@ -103,12 +104,13 @@ libab_result libab_register_function(libab* ab, const char* name, const char* ty
|
||||||
libab_table_entry* new_entry;
|
libab_table_entry* new_entry;
|
||||||
if((new_entry = malloc(sizeof(*new_entry)))) {
|
if((new_entry = malloc(sizeof(*new_entry)))) {
|
||||||
new_entry->variant = ENTRY_FUN;
|
new_entry->variant = ENTRY_FUN;
|
||||||
new_entry->data_u.function.behavior.type = NULL;
|
libab_ref_null(&new_entry->data_u.function.behavior.type);
|
||||||
} else {
|
} else {
|
||||||
result = LIBAB_MALLOC;
|
result = LIBAB_MALLOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
|
libab_ref_free(&new_entry->data_u.function.behavior.type);
|
||||||
result = _initialize_behavior(ab,
|
result = _initialize_behavior(ab,
|
||||||
&(new_entry->data_u.function.behavior), type, func);
|
&(new_entry->data_u.function.behavior), type, func);
|
||||||
}
|
}
|
||||||
|
@ -118,8 +120,8 @@ libab_result libab_register_function(libab* ab, const char* name, const char* ty
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != LIBAB_SUCCESS) {
|
if(result != LIBAB_SUCCESS) {
|
||||||
if(new_entry && new_entry->data_u.function.behavior.type)
|
if(new_entry)
|
||||||
libab_parsetype_free_recursive(new_entry->data_u.function.behavior.type);
|
libab_ref_free(&new_entry->data_u.function.behavior.type);
|
||||||
free(new_entry);
|
free(new_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
62
src/parser.c
62
src/parser.c
|
@ -113,7 +113,7 @@ libab_result _parser_consume_type(struct parser_state* state,
|
||||||
|
|
||||||
libab_result _parse_block(struct parser_state*, libab_tree**, int);
|
libab_result _parse_block(struct parser_state*, libab_tree**, int);
|
||||||
libab_result _parse_expression(struct parser_state* state, libab_tree** store_into);
|
libab_result _parse_expression(struct parser_state* state, libab_tree** store_into);
|
||||||
libab_result _parse_type(struct parser_state* state, libab_parsetype** into);
|
libab_result _parse_type(struct parser_state* state, libab_ref* ref);
|
||||||
|
|
||||||
libab_result _parse_braced_block(struct parser_state* state, libab_tree** store_into) {
|
libab_result _parse_braced_block(struct parser_state* state, libab_tree** store_into) {
|
||||||
return _parse_block(state, store_into, 1);
|
return _parse_block(state, store_into, 1);
|
||||||
|
@ -135,20 +135,18 @@ libab_result _parser_allocate_type(libab_parsetype** into, const char* source, s
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
libab_result _parser_append_type(struct parser_state* state, vec* into) {
|
libab_result _parser_append_type(struct parser_state* state, libab_ref_vec* into) {
|
||||||
libab_result result = LIBAB_SUCCESS;
|
libab_result result = LIBAB_SUCCESS;
|
||||||
libab_parsetype* temp = NULL;
|
libab_ref temp;
|
||||||
result = _parse_type(state, &temp);
|
result = _parse_type(state, &temp);
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
result = libab_convert_ds_result(vec_add(into, temp));
|
result = libab_ref_vec_insert(into, &temp);
|
||||||
if(result != LIBAB_SUCCESS) {
|
libab_ref_free(&temp);
|
||||||
libab_parsetype_free_recursive(temp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
libab_result _parse_type_list(struct parser_state* state, vec* into, char end_char) {
|
libab_result _parse_type_list(struct parser_state* state, libab_ref_vec* into, char end_char) {
|
||||||
libab_result result = LIBAB_SUCCESS;
|
libab_result result = LIBAB_SUCCESS;
|
||||||
int is_parenth, is_comma;
|
int is_parenth, is_comma;
|
||||||
while(result == LIBAB_SUCCESS && !_parser_eof(state) && !_parser_is_char(state, end_char)) {
|
while(result == LIBAB_SUCCESS && !_parser_eof(state) && !_parser_is_char(state, end_char)) {
|
||||||
|
@ -198,7 +196,7 @@ libab_result _parse_type_id(struct parser_state* state, libab_parsetype** into)
|
||||||
if(placeholder_flag) {
|
if(placeholder_flag) {
|
||||||
result = LIBAB_UNEXPECTED;
|
result = LIBAB_UNEXPECTED;
|
||||||
} else {
|
} else {
|
||||||
result = libab_convert_ds_result(vec_init(&(*into)->children));
|
result = libab_ref_vec_init(&(*into)->children);
|
||||||
if(result != LIBAB_SUCCESS) {
|
if(result != LIBAB_SUCCESS) {
|
||||||
free((*into)->data_u.name);
|
free((*into)->data_u.name);
|
||||||
free(*into);
|
free(*into);
|
||||||
|
@ -212,7 +210,7 @@ libab_result _parse_type_id(struct parser_state* state, libab_parsetype** into)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != LIBAB_SUCCESS && *into) {
|
if(result != LIBAB_SUCCESS && *into) {
|
||||||
libab_parsetype_free_recursive(*into);
|
libab_parsetype_free(*into);
|
||||||
*into = NULL;
|
*into = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +222,7 @@ libab_result _parse_type_function(struct parser_state* state,
|
||||||
libab_result result = _parser_allocate_type(into, "function", 0, 8);
|
libab_result result = _parser_allocate_type(into, "function", 0, 8);
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
(*into)->variant |= LIBABACUS_TYPE_F_PARENT;
|
(*into)->variant |= LIBABACUS_TYPE_F_PARENT;
|
||||||
result = libab_convert_ds_result(vec_init(&(*into)->children));
|
result = libab_ref_vec_init(&(*into)->children);
|
||||||
if(result != LIBAB_SUCCESS) {
|
if(result != LIBAB_SUCCESS) {
|
||||||
free((*into)->data_u.name);
|
free((*into)->data_u.name);
|
||||||
free(*into);
|
free(*into);
|
||||||
|
@ -247,7 +245,7 @@ libab_result _parse_type_function(struct parser_state* state,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != LIBAB_SUCCESS && *into) {
|
if(result != LIBAB_SUCCESS && *into) {
|
||||||
libab_parsetype_free_recursive(*into);
|
libab_parsetype_free(*into);
|
||||||
*into = NULL;
|
*into = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +257,7 @@ libab_result _parse_type_array(struct parser_state* state,
|
||||||
libab_result result = _parser_allocate_type(into, "array", 0, 5);
|
libab_result result = _parser_allocate_type(into, "array", 0, 5);
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
(*into)->variant |= LIBABACUS_TYPE_F_PARENT;
|
(*into)->variant |= LIBABACUS_TYPE_F_PARENT;
|
||||||
result = libab_convert_ds_result(vec_init(&(*into)->children));
|
result = libab_ref_vec_init(&(*into)->children);
|
||||||
if(result != LIBAB_SUCCESS) {
|
if(result != LIBAB_SUCCESS) {
|
||||||
free((*into)->data_u.name);
|
free((*into)->data_u.name);
|
||||||
free(*into);
|
free(*into);
|
||||||
|
@ -277,14 +275,14 @@ libab_result _parse_type_array(struct parser_state* state,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != LIBAB_SUCCESS && *into) {
|
if(result != LIBAB_SUCCESS && *into) {
|
||||||
libab_parsetype_free_recursive(*into);
|
libab_parsetype_free(*into);
|
||||||
*into = NULL;
|
*into = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
libab_result _parse_type(struct parser_state* state, libab_parsetype** into) {
|
libab_result _parse_type_raw(struct parser_state* state, libab_parsetype** into) {
|
||||||
libab_result result;
|
libab_result result;
|
||||||
if(_parser_is_type(state, TOKEN_ID) || _parser_is_char(state, '\'')) {
|
if(_parser_is_type(state, TOKEN_ID) || _parser_is_char(state, '\'')) {
|
||||||
result = _parse_type_id(state, into);
|
result = _parse_type_id(state, into);
|
||||||
|
@ -299,6 +297,23 @@ libab_result _parse_type(struct parser_state* state, libab_parsetype** into) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _parse_type_free(void* data) {
|
||||||
|
libab_parsetype_free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
libab_result _parse_type(struct parser_state* state, libab_ref* into) {
|
||||||
|
libab_parsetype* store_into;
|
||||||
|
libab_result result = _parse_type_raw(state, &store_into);
|
||||||
|
if(result == LIBAB_SUCCESS) {
|
||||||
|
result = libab_ref_new(into, store_into, _parse_type_free);
|
||||||
|
if(result != LIBAB_SUCCESS) {
|
||||||
|
libab_parsetype_free(store_into);
|
||||||
|
libab_ref_null(into);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
libab_result _parser_allocate_node(libab_lexer_match* match, libab_tree** into) {
|
libab_result _parser_allocate_node(libab_lexer_match* match, libab_tree** into) {
|
||||||
libab_result result = LIBAB_SUCCESS;
|
libab_result result = LIBAB_SUCCESS;
|
||||||
if(((*into) = malloc(sizeof(**into))) == NULL) {
|
if(((*into) = malloc(sizeof(**into))) == NULL) {
|
||||||
|
@ -429,12 +444,13 @@ libab_result _parse_fun_param(struct parser_state* state, libab_tree** store_int
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
_parser_state_step(state);
|
_parser_state_step(state);
|
||||||
(*store_into)->variant = TREE_FUN_PARAM;
|
(*store_into)->variant = TREE_FUN_PARAM;
|
||||||
(*store_into)->parse_type = NULL;
|
libab_ref_null(&(*store_into)->type);
|
||||||
result = _parser_consume_char(state, ':');
|
result = _parser_consume_char(state, ':');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
result = _parse_type(state, &(*store_into)->parse_type);
|
libab_ref_free(&(*store_into)->type);
|
||||||
|
result = _parse_type(state, &(*store_into)->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result != LIBAB_SUCCESS && *store_into) {
|
if(result != LIBAB_SUCCESS && *store_into) {
|
||||||
|
@ -460,13 +476,14 @@ libab_result _parse_def_fun(struct parser_state* state, libab_tree** store_into)
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
_parser_state_step(state);
|
_parser_state_step(state);
|
||||||
(*store_into)->parse_type = NULL;
|
libab_ref_null(&(*store_into)->type);
|
||||||
(*store_into)->variant = TREE_FUN;
|
(*store_into)->variant = TREE_FUN;
|
||||||
result = _parser_consume_char(state, ':');
|
result = _parser_consume_char(state, ':');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
result = _parse_type(state, &(*store_into)->parse_type);
|
libab_ref_free(&(*store_into)->type);
|
||||||
|
result = _parse_type(state, &(*store_into)->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
|
@ -499,7 +516,7 @@ libab_result _parse_fun(struct parser_state* state, libab_tree** store_into) {
|
||||||
}
|
}
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
_parser_state_step(state);
|
_parser_state_step(state);
|
||||||
(*store_into)->parse_type = NULL;
|
libab_ref_null(&(*store_into)->type);
|
||||||
(*store_into)->variant = TREE_FUN;
|
(*store_into)->variant = TREE_FUN;
|
||||||
result = _parser_consume_char(state, '(');
|
result = _parser_consume_char(state, '(');
|
||||||
}
|
}
|
||||||
|
@ -526,7 +543,8 @@ libab_result _parse_fun(struct parser_state* state, libab_tree** store_into) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
result = _parse_type(state, &(*store_into)->parse_type);
|
libab_ref_free(&(*store_into)->type);
|
||||||
|
result = _parse_type(state, &(*store_into)->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == LIBAB_SUCCESS) {
|
if(result == LIBAB_SUCCESS) {
|
||||||
|
@ -1072,7 +1090,7 @@ libab_result libab_parser_parse(libab_parser* parser, ll* tokens,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
libab_result libab_parser_parse_type(libab_parser* parser, ll* tokens,
|
libab_result libab_parser_parse_type(libab_parser* parser, ll* tokens,
|
||||||
const char* string, libab_parsetype** store_into) {
|
const char* string, libab_ref* store_into) {
|
||||||
struct parser_state state;
|
struct parser_state state;
|
||||||
_parser_state_init(&state, tokens, string, parser->base_table);
|
_parser_state_init(&state, tokens, string, parser->base_table);
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,11 @@
|
||||||
#include "parsetype.h"
|
#include "parsetype.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int _foreach_free_child(void* data, va_list args) {
|
|
||||||
libab_parsetype_free_recursive(data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
void libab_parsetype_free(libab_parsetype* type) {
|
void libab_parsetype_free(libab_parsetype* type) {
|
||||||
if(!(type->variant & LIBABACUS_TYPE_F_RESOLVED)) {
|
if(!(type->variant & LIBABACUS_TYPE_F_RESOLVED)) {
|
||||||
free(type->data_u.name);
|
free(type->data_u.name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void libab_parsetype_free_recursive(libab_parsetype* type) {
|
|
||||||
if(type->variant & LIBABACUS_TYPE_F_PARENT) {
|
if(type->variant & LIBABACUS_TYPE_F_PARENT) {
|
||||||
vec_foreach(&(type->children), NULL, compare_always, _foreach_free_child);
|
libab_ref_vec_free(&(type->children));
|
||||||
vec_free(&(type->children));
|
|
||||||
}
|
}
|
||||||
libab_parsetype_free(type);
|
|
||||||
free(type);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,8 @@ void libab_tree_free(libab_tree* tree) {
|
||||||
free_type = libab_tree_has_type(tree->variant);
|
free_type = libab_tree_has_type(tree->variant);
|
||||||
if(free_string) free(tree->string_value);
|
if(free_string) free(tree->string_value);
|
||||||
if(free_vector) vec_free(&tree->children);
|
if(free_vector) vec_free(&tree->children);
|
||||||
if(free_type && tree->parse_type)
|
if(free_type)
|
||||||
libab_parsetype_free_recursive(tree->parse_type);
|
libab_ref_free(&tree->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
int _tree_foreach_free(void* data, va_list args) {
|
int _tree_foreach_free(void* data, va_list args) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user