eolian: support for setting a free function to values in eo files

This commit is contained in:
Daniel Kolesa 2014-08-19 15:55:31 +01:00
parent d788986fd8
commit ef380c56b6
9 changed files with 219 additions and 47 deletions

View File

@ -111,6 +111,8 @@ tests/eolian/data/extern.eo \
tests/eolian/data/struct.eo \
tests/eolian/data/var.eo \
tests/eolian/data/class_funcs.eo \
tests/eolian/data/enum.eo \
tests/eolian/data/free_func.eo \
tests/eolian/data/typedef_ref.c \
tests/eolian/data/struct_ref.c

View File

@ -1322,6 +1322,20 @@ EAPI Eina_Stringshare *eolian_type_full_name_get(const Eolian_Type *tp);
*/
EAPI Eina_Iterator *eolian_type_namespaces_get(const Eolian_Type *tp);
/*
* @brief Get the name of the function used to free this type.
*
* @param[in] tp the type.
* @return the free func name.
*
* For pointer types, this returns name of the func used to free the pointer.
* For struct and alias types, this returns name of the func used to free a
* pointer to that type. For other types, this returns NULL.
*
* @ingroup Eolian
*/
EAPI Eina_Stringshare *eolian_type_free_func_get(const Eolian_Type *tp);
/*
* @brief Evaluate an Eolian expression.
*

View File

@ -21,6 +21,7 @@ database_type_del(Eolian_Type *tp)
eina_stringshare_del(sp);
if (tp->comment) eina_stringshare_del(tp->comment);
if (tp->legacy) eina_stringshare_del(tp->legacy);
if (tp->freefunc) eina_stringshare_del(tp->freefunc);
free(tp);
}

View File

@ -306,3 +306,10 @@ eolian_type_namespaces_get(const Eolian_Type *tp)
if (!tp->namespaces) return NULL;
return eina_list_iterator_new(tp->namespaces);
}
EAPI Eina_Stringshare *
eolian_type_free_func_get(const Eolian_Type *tp)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(tp, NULL);
return tp->freefunc;
}

View File

@ -25,12 +25,13 @@ enum Tokens
KW(virtual), \
\
KW(abstract), KW(constructor), KW(constructors), KW(data), \
KW(destructor), KW(eo), KW(eo_prefix), KW(events), KW(func), KW(get), \
KW(implements), KW(interface), KW(keys), KW(legacy), KW(legacy_prefix), \
KW(methods), KW(mixin), KW(own), KW(params), KW(properties), KW(set), \
KW(type), KW(values), KW(var), KWAT(class), KWAT(const), \
KWAT(constructor), KWAT(extern), KWAT(in), KWAT(inout), KWAT(nonull), \
KWAT(out), KWAT(private), KWAT(protected), KWAT(warn_unused), \
KW(destructor), KW(eo), KW(eo_prefix), KW(events), KW(free), KW(func), \
KW(get), KW(implements), KW(interface), KW(keys), KW(legacy), \
KW(legacy_prefix), KW(methods), KW(mixin), KW(own), KW(params), \
KW(properties), KW(set), KW(type), KW(values), KW(var), KWAT(class), \
KWAT(const), KWAT(constructor), KWAT(extern), KWAT(free), KWAT(in), \
KWAT(inout), KWAT(nonull), KWAT(out), KWAT(private), KWAT(protected), \
KWAT(warn_unused), \
\
KW(byte), KW(ubyte), KW(char), KW(short), KW(ushort), KW(int), KW(uint), \
KW(long), KW(ulong), KW(llong), KW(ullong), \

View File

@ -563,7 +563,7 @@ _struct_field_free(Eolian_Struct_Field *def)
static Eolian_Type *
parse_struct(Eo_Lexer *ls, const char *name, Eina_Bool is_extern,
int line, int column)
int line, int column, const char *freefunc)
{
int bline = ls->line_number, bcolumn = ls->column;
Eolian_Type *def = push_type(ls);
@ -571,6 +571,7 @@ parse_struct(Eo_Lexer *ls, const char *name, Eina_Bool is_extern,
if (name) _fill_type_name(def, name);
def->type = EOLIAN_TYPE_STRUCT;
def->fields = eina_hash_string_small_new(EINA_FREE_CB(_struct_field_free));
def->freefunc = freefunc;
check_next(ls, '{');
if (ls->t.token == TOK_COMMENT)
{
@ -712,6 +713,49 @@ parse_enum(Eo_Lexer *ls, const char *name, Eina_Bool is_extern,
return def;
}
static void
parse_struct_attrs(Eo_Lexer *ls, Eina_Bool is_enum, Eina_Bool allow_named,
Eina_Bool *is_extern, const char **freefunc)
{
/* TODO: handle freefunc deref on error */
Eina_Bool has_extern = EINA_FALSE, has_free = EINA_FALSE;
*freefunc = NULL;
*is_extern = EINA_FALSE;
for (;;) switch (ls->t.kw)
{
case KW_at_extern:
CASE_LOCK(ls, extern, "@extern qualifier")
if (!allow_named)
{
if (is_enum)
eo_lexer_syntax_error(ls,
"only enum declarations can be extern");
else
eo_lexer_syntax_error(ls,
"only named structs can be extern");
}
eo_lexer_get(ls);
*is_extern = EINA_TRUE;
break;
case KW_at_free:
{
CASE_LOCK(ls, free, "@free qualifier")
if (is_enum)
eo_lexer_syntax_error(ls, "enums cannot have @free");
eo_lexer_get(ls);
int pline = ls->line_number, pcol = ls->column;
check_next(ls, '(');
check(ls, TOK_VALUE);
*freefunc = eina_stringshare_ref(ls->t.value.s);
eo_lexer_get(ls);
check_match(ls, ')', '(', pline, pcol);
break;
}
default:
return;
}
}
static Eolian_Type *
parse_type_named_void(Eo_Lexer *ls, Eina_Bool allow_named)
{
@ -759,31 +803,46 @@ parse_type_named_void(Eo_Lexer *ls, Eina_Bool allow_named)
check_match(ls, ')', '(', pline, pcolumn);
goto parse_ptr;
}
case KW_free:
{
int pline, pcolumn;
eo_lexer_get(ls);
pline = ls->line_number;
pcolumn = ls->column;
check_next(ls, '(');
eo_lexer_context_push(ls);
def = parse_type_void(ls);
if (def->type != EOLIAN_TYPE_POINTER)
{
eo_lexer_context_restore(ls);
eo_lexer_syntax_error(ls, "pointer type expected");
}
eo_lexer_context_pop(ls);
check_next(ls, ',');
check(ls, TOK_VALUE);
def->freefunc = eina_stringshare_ref(ls->t.value.s);
eo_lexer_get(ls);
def->base.file = eina_stringshare_ref(ls->filename);
def->base.line = line;
def->base.column = col;
check_match(ls, ')', '(', pline, pcolumn);
goto parse_ptr;
}
case KW_struct:
case KW_enum:
{
const char *freefunc;
Eina_Bool has_extern;
Eina_Bool is_enum = (ls->t.kw == KW_enum);
Eina_Bool is_extern = EINA_FALSE;
eo_lexer_get(ls);
if (ls->t.kw == KW_at_extern)
{
if (!allow_named)
{
if (is_enum)
eo_lexer_syntax_error(ls,
"only enum declarations can be extern");
else
eo_lexer_syntax_error(ls,
"only named structs can be extern");
}
is_extern = EINA_TRUE;
eo_lexer_get(ls);
}
parse_struct_attrs(ls, is_enum, allow_named, &has_extern, &freefunc);
if (freefunc && !allow_named)
check(ls, '{');
if (!is_enum && (ls->t.token == '{'))
{
if (is_extern)
if (has_extern)
eo_lexer_syntax_error(ls, "extern anonymous struct");
return parse_struct(ls, NULL, EINA_FALSE, line, col);
return parse_struct(ls, NULL, EINA_FALSE, line, col, freefunc);
}
buf = push_strbuf(ls);
eo_lexer_context_push(ls);
@ -793,7 +852,7 @@ parse_type_named_void(Eo_Lexer *ls, Eina_Bool allow_named)
sname = eina_stringshare_add(eina_strbuf_string_get(buf));
pop_strbuf(ls);
/* if we're extern and allow structs, gotta enforce it */
if (allow_named && is_extern)
if (allow_named && (has_extern || freefunc))
check(ls, '{');
if (allow_named && ls->t.token == '{')
{
@ -808,8 +867,8 @@ parse_type_named_void(Eo_Lexer *ls, Eina_Bool allow_named)
}
eo_lexer_context_pop(ls);
if (is_enum)
return parse_enum(ls, sname, is_extern, line, col);
return parse_struct(ls, sname, is_extern, line, col);
return parse_enum(ls, sname, has_extern, line, col);
return parse_struct(ls, sname, has_extern, line, col, freefunc);
}
eo_lexer_context_pop(ls);
def = push_type(ls);
@ -920,16 +979,14 @@ static Eolian_Type *
parse_typedef(Eo_Lexer *ls)
{
Eolian_Type *def = push_type(ls);
Eina_Bool is_extern = EINA_FALSE;
Eina_Bool has_extern;
const char *freefunc;
Eina_Strbuf *buf;
eo_lexer_get(ls);
if (ls->t.kw == KW_at_extern)
{
is_extern = EINA_TRUE;
eo_lexer_get(ls);
}
parse_struct_attrs(ls, EINA_FALSE, EINA_TRUE, &has_extern, &freefunc);
def->freefunc = freefunc;
def->type = EOLIAN_TYPE_ALIAS;
def->is_extern = is_extern;
def->is_extern = has_extern;
buf = push_strbuf(ls);
eo_lexer_context_push(ls);
def->base.file = eina_stringshare_ref(ls->filename);
@ -960,18 +1017,18 @@ static Eolian_Variable *
parse_variable(Eo_Lexer *ls, Eina_Bool global)
{
Eolian_Variable *def = push_var(ls);
Eina_Bool is_extern = EINA_FALSE;
Eina_Bool has_extern = EINA_FALSE;
Eina_Strbuf *buf;
eo_lexer_get(ls);
if (ls->t.kw == KW_at_extern)
{
if (!global)
eo_lexer_syntax_error(ls, "extern constant");
is_extern = EINA_TRUE;
has_extern = EINA_TRUE;
eo_lexer_get(ls);
}
def->type = global ? EOLIAN_VAR_GLOBAL : EOLIAN_VAR_CONSTANT;
def->is_extern = is_extern;
def->is_extern = has_extern;
buf = push_strbuf(ls);
eo_lexer_context_push(ls);
def->base.file = eina_stringshare_ref(ls->filename);
@ -982,7 +1039,7 @@ parse_variable(Eo_Lexer *ls, Eina_Bool global)
check_next(ls, ':');
def->base_type = parse_type(ls);
pop_type(ls);
if ((ls->t.token == '=') && !is_extern)
if ((ls->t.token == '=') && !has_extern)
{
ls->expr_mode = EINA_TRUE;
eo_lexer_get(ls);
@ -1766,14 +1823,11 @@ parse_unit(Eo_Lexer *ls, Eina_Bool eot)
const char *name;
int line, col;
Eolian_Type *tp;
Eina_Bool is_extern = EINA_FALSE;
Eina_Bool has_extern;
const char *freefunc;
Eina_Strbuf *buf;
eo_lexer_get(ls);
if (ls->t.kw == KW_at_extern)
{
is_extern = EINA_TRUE;
eo_lexer_get(ls);
}
parse_struct_attrs(ls, is_enum, EINA_TRUE, &has_extern, &freefunc);
buf = push_strbuf(ls);
eo_lexer_context_push(ls);
line = ls->line_number;
@ -1794,8 +1848,9 @@ parse_unit(Eo_Lexer *ls, Eina_Bool eot)
if (ls->t.token == ';')
{
Eolian_Type *def = push_type(ls);
def->is_extern = is_extern;
def->is_extern = has_extern;
def->type = EOLIAN_TYPE_STRUCT_OPAQUE;
def->freefunc = freefunc;
_fill_type_name(def, name);
eo_lexer_get(ls);
if (ls->t.token == TOK_COMMENT)
@ -1811,9 +1866,9 @@ parse_unit(Eo_Lexer *ls, Eina_Bool eot)
break;
}
if (is_enum)
parse_enum(ls, name, is_extern, line, col);
parse_enum(ls, name, has_extern, line, col);
else
parse_struct(ls, name, is_extern, line, col);
parse_struct(ls, name, has_extern, line, col, freefunc);
pop_type(ls);
break;
}

View File

@ -136,6 +136,7 @@ struct _Eolian_Type
Eina_List *field_names;
Eina_Stringshare *comment;
Eina_Stringshare *legacy;
Eina_Stringshare *freefunc;
};
};
Eina_Bool is_const :1;

View File

@ -0,0 +1,38 @@
/* regular struct */
struct Named1 {
field: int;
}
struct @free(test_free) Named2 {
field: int;
}
/* typedef */
type Typedef1: int;
type @free(def_free) Typedef2: int;
/* anon struct */
type Anon1: struct {
field: int;
};
type Anon2: struct @free(anon_free) {
field: int;
};
/* opaque */
struct Opaque1;
struct @free(opaque_free) Opaque2;
/* pointers */
type Pointer1: char *;
type Pointer2: free(char *, ptr_free);
class Free_Func {
methods {
foo {
params {
int idx;
}
return: own(char*);
}
}
}

View File

@ -823,6 +823,58 @@ START_TEST(eolian_class_funcs)
}
END_TEST
START_TEST(eolian_free_func)
{
const Eolian_Class *class;
const Eolian_Type *type;
eolian_init();
/* Parsing */
fail_if(!eolian_eo_file_parse(PACKAGE_DATA_DIR"/data/free_func.eo"));
/* Check that the class Dummy is still readable */
fail_if(!(class = eolian_class_get_by_name("Free_Func")));
fail_if(!eolian_class_function_get_by_name(class, "foo", EOLIAN_METHOD));
/* regular struct */
fail_if(!(type = eolian_type_struct_get_by_name("Named1")));
fail_if(eolian_type_free_func_get(type));
fail_if(!(type = eolian_type_struct_get_by_name("Named2")));
fail_if(strcmp(eolian_type_free_func_get(type), "test_free"));
/* typedef */
fail_if(!(type = eolian_type_alias_get_by_name("Typedef1")));
fail_if(eolian_type_free_func_get(type));
fail_if(!(type = eolian_type_alias_get_by_name("Typedef2")));
fail_if(strcmp(eolian_type_free_func_get(type), "def_free"));
/* anon struct */
fail_if(!(type = eolian_type_alias_get_by_name("Anon1")));
fail_if(!(type = eolian_type_base_type_get(type)));
fail_if(eolian_type_free_func_get(type));
fail_if(!(type = eolian_type_alias_get_by_name("Anon2")));
fail_if(!(type = eolian_type_base_type_get(type)));
fail_if(strcmp(eolian_type_free_func_get(type), "anon_free"));
/* opaque struct */
fail_if(!(type = eolian_type_struct_get_by_name("Opaque1")));
fail_if(eolian_type_free_func_get(type));
fail_if(!(type = eolian_type_struct_get_by_name("Opaque2")));
fail_if(strcmp(eolian_type_free_func_get(type), "opaque_free"));
/* pointer */
fail_if(!(type = eolian_type_alias_get_by_name("Pointer1")));
fail_if(!(type = eolian_type_base_type_get(type)));
fail_if(eolian_type_free_func_get(type));
fail_if(!(type = eolian_type_alias_get_by_name("Pointer2")));
fail_if(!(type = eolian_type_base_type_get(type)));
fail_if(strcmp(eolian_type_free_func_get(type), "ptr_free"));
eolian_shutdown();
}
END_TEST
void eolian_parsing_test(TCase *tc)
{
tcase_add_test(tc, eolian_simple_parsing);
@ -839,5 +891,6 @@ void eolian_parsing_test(TCase *tc)
tcase_add_test(tc, eolian_var);
tcase_add_test(tc, eolian_enum);
tcase_add_test(tc, eolian_class_funcs);
tcase_add_test(tc, eolian_free_func);
}