From ef380c56b64a68f01fb3039124ca9e24f46d303e Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Tue, 19 Aug 2014 15:55:31 +0100 Subject: [PATCH] eolian: support for setting a free function to values in eo files --- src/Makefile_Eolian.am | 2 + src/lib/eolian/Eolian.h | 14 +++ src/lib/eolian/database_type.c | 1 + src/lib/eolian/database_type_api.c | 7 ++ src/lib/eolian/eo_lexer.h | 13 +-- src/lib/eolian/eo_parser.c | 137 ++++++++++++++++++++--------- src/lib/eolian/eolian_database.h | 1 + src/tests/eolian/data/free_func.eo | 38 ++++++++ src/tests/eolian/eolian_parsing.c | 53 +++++++++++ 9 files changed, 219 insertions(+), 47 deletions(-) create mode 100644 src/tests/eolian/data/free_func.eo diff --git a/src/Makefile_Eolian.am b/src/Makefile_Eolian.am index 1d3a647d2f..cebdbda021 100644 --- a/src/Makefile_Eolian.am +++ b/src/Makefile_Eolian.am @@ -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 diff --git a/src/lib/eolian/Eolian.h b/src/lib/eolian/Eolian.h index e223729d26..58fc725a8d 100644 --- a/src/lib/eolian/Eolian.h +++ b/src/lib/eolian/Eolian.h @@ -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. * diff --git a/src/lib/eolian/database_type.c b/src/lib/eolian/database_type.c index 3810820d38..75191c2736 100644 --- a/src/lib/eolian/database_type.c +++ b/src/lib/eolian/database_type.c @@ -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); } diff --git a/src/lib/eolian/database_type_api.c b/src/lib/eolian/database_type_api.c index 0acd204680..597d5ebfad 100644 --- a/src/lib/eolian/database_type_api.c +++ b/src/lib/eolian/database_type_api.c @@ -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; +} diff --git a/src/lib/eolian/eo_lexer.h b/src/lib/eolian/eo_lexer.h index d595ecd8de..9d608ef873 100644 --- a/src/lib/eolian/eo_lexer.h +++ b/src/lib/eolian/eo_lexer.h @@ -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), \ diff --git a/src/lib/eolian/eo_parser.c b/src/lib/eolian/eo_parser.c index 6a4a481374..9bb588cdca 100644 --- a/src/lib/eolian/eo_parser.c +++ b/src/lib/eolian/eo_parser.c @@ -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; } diff --git a/src/lib/eolian/eolian_database.h b/src/lib/eolian/eolian_database.h index b7fd3ac59b..6f824f5001 100644 --- a/src/lib/eolian/eolian_database.h +++ b/src/lib/eolian/eolian_database.h @@ -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; diff --git a/src/tests/eolian/data/free_func.eo b/src/tests/eolian/data/free_func.eo new file mode 100644 index 0000000000..8944924ce1 --- /dev/null +++ b/src/tests/eolian/data/free_func.eo @@ -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*); + } + } +} diff --git a/src/tests/eolian/eolian_parsing.c b/src/tests/eolian/eolian_parsing.c index ea827d032e..2025ffebd6 100644 --- a/src/tests/eolian/eolian_parsing.c +++ b/src/tests/eolian/eolian_parsing.c @@ -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); }