This is actually broken in MIDL, even as recent as the version shipped with the Windows 10 SDK, but it makes our code simpler, so there's no reason not to do it right.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- v2: fix accidentally inverted condition
tools/widl/header.c | 109 ++++++++++++++++++++++------------------ tools/widl/header.h | 7 ++- tools/widl/parser.y | 111 +++++++++++++++++------------------------ tools/widl/typegen.c | 6 +-- tools/widl/typetree.c | 1 + tools/widl/widltypes.h | 1 - 6 files changed, 117 insertions(+), 118 deletions(-)
diff --git a/tools/widl/header.c b/tools/widl/header.c index 5bf5d09ef1..dd1186845b 100644 --- a/tools/widl/header.c +++ b/tools/widl/header.c @@ -285,16 +285,31 @@ int needs_space_after(type_t *t) (!is_ptr(t) && (!is_array(t) || !type_array_is_decl_as_ptr(t) || t->name))); }
+int decl_needs_parens(const type_t *t) +{ + if (type_is_alias(t)) + return FALSE; + if (is_array(t) && !type_array_is_decl_as_ptr(t)) + return TRUE; + return is_func(t); +} + static void write_pointer_left(FILE *h, type_t *ref) { if (needs_space_after(ref)) fprintf(h, " "); - if (!type_is_alias(ref) && is_array(ref) && !type_array_is_decl_as_ptr(ref)) + if (decl_needs_parens(ref)) fprintf(h, "("); + if (type_get_type_detect_alias(ref) == TYPE_FUNCTION) + { + const char *callconv = get_attrp(ref->attrs, ATTR_CALLCONV); + if (!callconv && is_object_interface) callconv = "STDMETHODCALLTYPE"; + if (callconv) fprintf(h, "%s ", callconv); + } fprintf(h, "*"); }
-void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, int declonly) +void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, int declonly, int write_callconv) { type_t *t = ds->type; const char *name; @@ -352,7 +367,7 @@ void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, i break; case TYPE_POINTER: { - write_type_left(h, type_pointer_get_ref(t), name_type, declonly); + write_type_left(h, type_pointer_get_ref(t), name_type, declonly, FALSE); write_pointer_left(h, type_pointer_get_ref_type(t)); if (is_attr(t->attrs, ATTR_CONST)) fprintf(h, "const "); break; @@ -362,11 +377,28 @@ void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, i fprintf(h, "%s", t->name); else { - write_type_left(h, type_array_get_element(t), name_type, declonly); + write_type_left(h, type_array_get_element(t), name_type, declonly, !type_array_is_decl_as_ptr(t)); if (type_array_is_decl_as_ptr(t)) write_pointer_left(h, type_array_get_element_type(t)); } break; + case TYPE_FUNCTION: + { + if (is_attr(t->attrs, ATTR_INLINE)) fprintf(h, "inline "); + write_type_left(h, type_function_get_ret(t), name_type, declonly, TRUE); + + /* A pointer to a function has to write the calling convention inside + * the parentheses. There's no way to handle that here, so we have to + * use an extra parameter to tell us whether to write the calling + * convention or not. */ + if (write_callconv) + { + const char *callconv = get_attrp(t->attrs, ATTR_CALLCONV); + if (!callconv && is_object_interface) callconv = "STDMETHODCALLTYPE"; + if (callconv) fprintf(h, " %s ", callconv); + } + break; + } case TYPE_BASIC: if (type_basic_get_type(t) != TYPE_BASIC_INT32 && type_basic_get_type(t) != TYPE_BASIC_INT64 && @@ -426,11 +458,10 @@ void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, i case TYPE_BITFIELD: { const decl_spec_t ds = {.type = type_bitfield_get_field(t)}; - write_type_left(h, &ds, name_type, declonly); + write_type_left(h, &ds, name_type, declonly, TRUE); break; } case TYPE_ALIAS: - case TYPE_FUNCTION: /* handled elsewhere */ assert(0); break; @@ -450,7 +481,7 @@ void write_type_right(FILE *h, type_t *t, int is_field) type_t *elem = type_array_get_element_type(t); if (type_array_is_decl_as_ptr(t)) { - if (!type_is_alias(elem) && is_array(elem) && !type_array_is_decl_as_ptr(elem)) + if (decl_needs_parens(elem)) fprintf(h, ")"); } else @@ -463,10 +494,22 @@ void write_type_right(FILE *h, type_t *t, int is_field) write_type_right(h, elem, FALSE); break; } + case TYPE_FUNCTION: + { + const var_list_t *args = type_function_get_args(t); + fputc('(', h); + if (args) + write_args(h, args, NULL, 0, FALSE); + else + fprintf(h, "void"); + fputc(')', h); + write_type_right(h, type_function_get_rettype(t), FALSE); + break; + } case TYPE_POINTER: { type_t *ref = type_pointer_get_ref_type(t); - if (!type_is_alias(ref) && is_array(ref) && !type_array_is_decl_as_ptr(ref)) + if (decl_needs_parens(ref)) fprintf(h, ")"); write_type_right(h, ref, FALSE); break; @@ -483,7 +526,6 @@ void write_type_right(FILE *h, type_t *t, int is_field) case TYPE_ALIAS: case TYPE_MODULE: case TYPE_COCLASS: - case TYPE_FUNCTION: case TYPE_INTERFACE: break; } @@ -491,46 +533,17 @@ void write_type_right(FILE *h, type_t *t, int is_field)
static void write_type_v(FILE *h, const decl_spec_t *ds, int is_field, int declonly, const char *name) { - type_t *t = ds->type, *pt = NULL; - int ptr_level = 0; + type_t *t = ds->type;
- if (!h) return; - - if (t) { - for (pt = t; is_ptr(pt); pt = type_pointer_get_ref_type(pt), ptr_level++) - ; - - if (type_get_type_detect_alias(pt) == TYPE_FUNCTION) { - int i; - const char *callconv = get_attrp(pt->attrs, ATTR_CALLCONV); - if (!callconv && is_object_interface) callconv = "STDMETHODCALLTYPE"; - if (is_attr(pt->attrs, ATTR_INLINE)) fprintf(h, "inline "); - write_type_left(h, type_function_get_ret(pt), NAME_DEFAULT, declonly); - fputc(' ', h); - if (ptr_level) fputc('(', h); - if (callconv) fprintf(h, "%s ", callconv); - for (i = 0; i < ptr_level; i++) - fputc('*', h); - } else - write_type_left(h, ds, NAME_DEFAULT, declonly); - } + if (!h) return;
- if (name) fprintf(h, "%s%s", !t || needs_space_after(t) ? " " : "", name ); + if (t) + write_type_left(h, ds, NAME_DEFAULT, declonly, TRUE);
- if (t) { - if (type_get_type_detect_alias(pt) == TYPE_FUNCTION) { - const var_list_t *args = type_function_get_args(pt); + if (name) fprintf(h, "%s%s", !t || needs_space_after(t) ? " " : "", name );
- if (ptr_level) fputc(')', h); - fputc('(', h); - if (args) - write_args(h, args, NULL, 0, FALSE); - else - fprintf(h, "void"); - fputc(')', h); - } else - write_type_right(h, t, is_field); - } + if (t) + write_type_right(h, t, is_field); }
static void write_type_def_or_decl(FILE *f, const decl_spec_t *t, int field, const char *name) @@ -550,14 +563,14 @@ static void write_type_definition(FILE *f, type_t *t) write_namespace_start(f, t->namespace); } indent(f, 0); - write_type_left(f, &ds, NAME_DEFAULT, FALSE); + write_type_left(f, &ds, NAME_DEFAULT, FALSE, TRUE); fprintf(f, ";\n"); if(in_namespace) { t->written = save_written; write_namespace_end(f, t->namespace); fprintf(f, "extern "C" {\n"); fprintf(f, "#else\n"); - write_type_left(f, &ds, NAME_C, FALSE); + write_type_left(f, &ds, NAME_C, FALSE, TRUE); fprintf(f, ";\n"); fprintf(f, "#endif\n\n"); } @@ -570,7 +583,7 @@ void write_type_decl(FILE *f, const decl_spec_t *t, const char *name)
void write_type_decl_left(FILE *f, const decl_spec_t *ds) { - write_type_left(f, ds, NAME_DEFAULT, TRUE); + write_type_left(f, ds, NAME_DEFAULT, TRUE, TRUE); }
static int user_type_registered(const char *name) diff --git a/tools/widl/header.h b/tools/widl/header.h index e047e4e2e4..cebfd83fe4 100644 --- a/tools/widl/header.h +++ b/tools/widl/header.h @@ -29,7 +29,7 @@ extern int is_attr(const attr_list_t *list, enum attr_type t); extern void *get_attrp(const attr_list_t *list, enum attr_type t); extern unsigned int get_attrv(const attr_list_t *list, enum attr_type t); extern const char* get_name(const var_t *v); -extern void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, int declonly); +extern void write_type_left(FILE *h, const decl_spec_t *ds, enum name_type name_type, int declonly, int write_callconv); extern void write_type_right(FILE *h, type_t *t, int is_field); extern void write_type_decl(FILE *f, const decl_spec_t *t, const char *name); extern void write_type_decl_left(FILE *f, const decl_spec_t *ds); @@ -66,6 +66,11 @@ static inline int is_array(const type_t *t) return type_get_type(t) == TYPE_ARRAY; }
+static inline int is_func(const type_t *t) +{ + return type_get_type(t) == TYPE_FUNCTION; +} + static inline int is_void(const type_t *t) { return type_get_type(t) == TYPE_VOID; diff --git a/tools/widl/parser.y b/tools/widl/parser.y index 038b40725c..9cfcd7f30f 100644 --- a/tools/widl/parser.y +++ b/tools/widl/parser.y @@ -75,6 +75,7 @@ static declarator_t *make_declarator(var_t *var); static type_t *make_safearray(type_t *type); static typelib_t *make_library(const char *name, const attr_list_t *attrs); static type_t *append_chain_type(type_t *chain, type_t *type); +static void append_chain_callconv(type_t *chain, char *callconv); static warning_list_t *append_warning(warning_list_t *, int);
static type_t *reg_typedefs(decl_spec_t *decl_spec, var_list_t *names, attr_list_t *attrs); @@ -975,8 +976,7 @@ decl_spec_no_type: declarator: '*' m_type_qual_list declarator %prec PPTR { $$ = $3; $$->type = append_chain_type($$->type, type_new_pointer(pointer_default, NULL, $2)); } - | callconv declarator { $$ = $2; if ($$->func_type) $$->func_type->attrs = append_attr($$->func_type->attrs, make_attrp(ATTR_CALLCONV, $1)); - else if ($$->type) $$->type->attrs = append_attr($$->type->attrs, make_attrp(ATTR_CALLCONV, $1)); } + | callconv declarator { $$ = $2; append_chain_callconv($$->type, $1); } | direct_declarator ;
@@ -984,18 +984,14 @@ direct_declarator: ident { $$ = make_declarator($1); } | '(' declarator ')' { $$ = $2; } | direct_declarator array { $$ = $1; $$->type = append_array($$->type, $2); } - | direct_declarator '(' m_args ')' { $$ = $1; - $$->func_type = append_chain_type($$->type, type_new_function($3)); - $$->type = NULL; - } + | direct_declarator '(' m_args ')' { $$ = $1; $$->type = append_chain_type($$->type, type_new_function($3)); } ;
/* abstract declarator */ abstract_declarator: '*' m_type_qual_list m_abstract_declarator %prec PPTR { $$ = $3; $$->type = append_chain_type($$->type, type_new_pointer(pointer_default, NULL, $2)); } - | callconv m_abstract_declarator { $$ = $2; if ($$->func_type) $$->func_type->attrs = append_attr($$->func_type->attrs, make_attrp(ATTR_CALLCONV, $1)); - else if ($$->type) $$->type->attrs = append_attr($$->type->attrs, make_attrp(ATTR_CALLCONV, $1)); } + | callconv m_abstract_declarator { $$ = $2; append_chain_callconv($$->type, $1); } | abstract_direct_declarator ;
@@ -1003,8 +999,7 @@ abstract_declarator: abstract_declarator_no_direct: '*' m_type_qual_list m_any_declarator %prec PPTR { $$ = $3; $$->type = append_chain_type($$->type, type_new_pointer(pointer_default, NULL, $2)); } - | callconv m_any_declarator { $$ = $2; if ($$->func_type) $$->func_type->attrs = append_attr($$->func_type->attrs, make_attrp(ATTR_CALLCONV, $1)); - else if ($$->type) $$->type->attrs = append_attr($$->type->attrs, make_attrp(ATTR_CALLCONV, $1)); } + | callconv m_any_declarator { $$ = $2; append_chain_callconv($$->type, $1); } ;
/* abstract declarator or empty */ @@ -1019,13 +1014,11 @@ abstract_direct_declarator: | array { $$ = make_declarator(NULL); $$->type = append_array($$->type, $1); } | '(' m_args ')' { $$ = make_declarator(NULL); - $$->func_type = append_chain_type($$->type, type_new_function($2)); - $$->type = NULL; + $$->type = append_chain_type($$->type, type_new_function($2)); } | abstract_direct_declarator '(' m_args ')' { $$ = $1; - $$->func_type = append_chain_type($$->type, type_new_function($3)); - $$->type = NULL; + $$->type = append_chain_type($$->type, type_new_function($3)); } ;
@@ -1033,7 +1026,7 @@ abstract_direct_declarator: any_declarator: '*' m_type_qual_list m_any_declarator %prec PPTR { $$ = $3; $$->type = append_chain_type($$->type, type_new_pointer(pointer_default, NULL, $2)); } - | callconv m_any_declarator { $$ = $2; $$->type->attrs = append_attr($$->type->attrs, make_attrp(ATTR_CALLCONV, $1)); } + | callconv m_any_declarator { $$ = $2; append_chain_callconv($$->type, $1); } | any_direct_declarator ;
@@ -1041,7 +1034,7 @@ any_declarator: any_declarator_no_direct: '*' m_type_qual_list m_any_declarator %prec PPTR { $$ = $3; $$->type = append_chain_type($$->type, type_new_pointer(pointer_default, NULL, $2)); } - | callconv m_any_declarator { $$ = $2; $$->type->attrs = append_attr($$->type->attrs, make_attrp(ATTR_CALLCONV, $1)); } + | callconv m_any_declarator { $$ = $2; append_chain_callconv($$->type, $1); } ;
/* abstract or non-abstract declarator or empty */ @@ -1059,13 +1052,11 @@ any_direct_declarator: | array { $$ = make_declarator(NULL); $$->type = append_array($$->type, $1); } | '(' m_args ')' { $$ = make_declarator(NULL); - $$->func_type = append_chain_type($$->type, type_new_function($2)); - $$->type = NULL; + $$->type = append_chain_type($$->type, type_new_function($2)); } | any_direct_declarator '(' m_args ')' { $$ = $1; - $$->func_type = append_chain_type($$->type, type_new_function($3)); - $$->type = NULL; + $$->type = append_chain_type($$->type, type_new_function($3)); } ;
@@ -1463,34 +1454,58 @@ static int is_allowed_range_type(const type_t *type) } }
-static type_t *get_array_or_ptr_ref(type_t *type) +static type_t *get_chain_ref(type_t *type) { if (is_ptr(type)) return type_pointer_get_ref_type(type); else if (is_array(type)) return type_array_get_element_type(type); + else if (is_func(type)) + return type_function_get_rettype(type); return NULL; }
+static type_t *get_chain_end(type_t *type) +{ + type_t *inner; + while ((inner = get_chain_ref(type))) + type = inner; + return type; +} + static type_t *append_chain_type(type_t *chain, type_t *type) { type_t *chain_type;
if (!chain) return type; - for (chain_type = chain; get_array_or_ptr_ref(chain_type); chain_type = get_array_or_ptr_ref(chain_type)) - ; + chain_type = get_chain_end(chain);
if (is_ptr(chain_type)) chain_type->details.pointer.ref.type = type; else if (is_array(chain_type)) chain_type->details.array.elem.type = type; + else if (is_func(chain_type)) + chain_type->details.function->retval->declspec.type = type; else assert(0);
+ if (!is_func(chain_type)) + type->attrs = move_attr(type->attrs, chain_type->attrs, ATTR_CALLCONV); + return chain; }
+static void append_chain_callconv(type_t *chain, char *callconv) +{ + type_t *chain_end; + + if (chain && (chain_end = get_chain_end(chain))) + chain_end->attrs = append_attr(chain_end->attrs, make_attrp(ATTR_CALLCONV, callconv)); + else + error_loc("calling convention applied to non-function type\n"); +} + static warning_list_t *append_warning(warning_list_t *list, int num) { warning_t *entry; @@ -1514,21 +1529,14 @@ static var_t *declare_var(attr_list_t *attrs, decl_spec_t *decl_spec, const decl expr_list_t *lengs = get_attrp(attrs, ATTR_LENGTHIS); expr_t *dim; type_t **ptype; - type_t *func_type = decl ? decl->func_type : NULL; type_t *type = decl_spec->type;
if (is_attr(type->attrs, ATTR_INLINE)) { - if (!func_type) + if (!decl || !is_func(decl->type)) error_loc("inline attribute applied to non-function type\n"); else - { - type_t *t; - /* move inline attribute from return type node to function node */ - for (t = func_type; is_ptr(t); t = type_pointer_get_ref_type(t)) - ; - t->attrs = move_attr(t->attrs, type->attrs, ATTR_INLINE); - } + decl->type->attrs = move_attr(decl->type->attrs, type->attrs, ATTR_INLINE); }
/* add type onto the end of the pointers in pident->type */ @@ -1536,16 +1544,16 @@ static var_t *declare_var(attr_list_t *attrs, decl_spec_t *decl_spec, const decl v->declspec.stgclass = decl_spec->stgclass; v->attrs = attrs;
+ if (is_attr(type->attrs, ATTR_CALLCONV) && !is_func(type)) + error_loc("calling convention applied to non-function type\n"); + /* check for pointer attribute being applied to non-pointer, non-array * type */ if (!is_array(v->declspec.type)) { int ptr_attr = get_attrv(v->attrs, ATTR_POINTERTYPE); const type_t *ptr = NULL; - /* pointer attributes on the left side of the type belong to the function - * pointer, if one is being declared */ - type_t **pt = func_type ? &func_type : &v->declspec.type; - for (ptr = *pt; ptr && !ptr_attr; ) + for (ptr = v->declspec.type; ptr && !ptr_attr; ) { ptr_attr = get_attrv(ptr->attrs, ATTR_POINTERTYPE); if (!ptr_attr && type_is_alias(ptr)) @@ -1560,13 +1568,13 @@ static var_t *declare_var(attr_list_t *attrs, decl_spec_t *decl_spec, const decl warning_loc_info(&v->loc_info, "%s: pointer attribute applied to interface " "pointer type has no effect\n", v->name); - if (!ptr_attr && top && type_pointer_get_default_fc(*pt) != FC_RP) + if (!ptr_attr && top && type_pointer_get_default_fc(v->declspec.type) != FC_RP) { /* FIXME: this is a horrible hack to cope with the issue that we * store an offset to the typeformat string in the type object, but * two typeformat strings may be written depending on whether the * pointer is a toplevel parameter or not */ - *pt = duptype(*pt, 1); + v->declspec.type = duptype(v->declspec.type, 1); } } else if (ptr_attr) @@ -1667,32 +1675,6 @@ static var_t *declare_var(attr_list_t *attrs, decl_spec_t *decl_spec, const decl error_loc("%s: too many expressions in length_is attribute\n", v->name); }
- /* v->type is currently pointing to the type on the left-side of the - * declaration, so we need to fix this up so that it is the return type of the - * function and make v->type point to the function side of the declaration */ - if (func_type) - { - type_t *ft, *t; - type_t *return_type = v->declspec.type; - v->declspec.type = func_type; - for (ft = v->declspec.type; is_ptr(ft); ft = type_pointer_get_ref_type(ft)) - ; - assert(type_get_type_detect_alias(ft) == TYPE_FUNCTION); - ft->details.function->retval = make_var(xstrdup("_RetVal")); - ft->details.function->retval->declspec.type = return_type; - /* move calling convention attribute, if present, from pointer nodes to - * function node */ - for (t = v->declspec.type; is_ptr(t); t = type_pointer_get_ref_type(t)) - ft->attrs = move_attr(ft->attrs, t->attrs, ATTR_CALLCONV); - } - else - { - type_t *t; - for (t = v->declspec.type; is_ptr(t); t = type_pointer_get_ref_type(t)) - if (is_attr(t->attrs, ATTR_CALLCONV)) - error_loc("calling convention applied to non-function-pointer type\n"); - } - if (decl->bits) v->declspec.type = type_new_bitfield(v->declspec.type, decl->bits);
@@ -1796,7 +1778,6 @@ static declarator_t *make_declarator(var_t *var) declarator_t *d = xmalloc(sizeof(*d)); d->var = var ? var : make_var(NULL); d->type = NULL; - d->func_type = NULL; d->bits = NULL; return d; } diff --git a/tools/widl/typegen.c b/tools/widl/typegen.c index 55abd0a40c..2e4045154f 100644 --- a/tools/widl/typegen.c +++ b/tools/widl/typegen.c @@ -4805,7 +4805,7 @@ void write_func_param_struct( FILE *file, const type_t *iface, const type_t *fun if (args) LIST_FOR_EACH_ENTRY( arg, args, const var_t, entry ) { print_file(file, 2, "%s", ""); - write_type_left( file, &arg->declspec, NAME_DEFAULT, TRUE ); + write_type_left( file, &arg->declspec, NAME_DEFAULT, TRUE, TRUE ); if (needs_space_after( arg->declspec.type )) fputc( ' ', file ); if (is_array( arg->declspec.type ) && !type_array_is_decl_as_ptr( arg->declspec.type )) fputc( '*', file );
@@ -4871,9 +4871,9 @@ int write_expr_eval_routines(FILE *file, const char *iface) { decl_spec_t ds = {.type = (type_t *)eval->cont_type}; print_file(file, 1, "%s", ""); - write_type_left(file, &ds, NAME_DEFAULT, TRUE); + write_type_left(file, &ds, NAME_DEFAULT, TRUE, TRUE); fprintf(file, " *%s = (", var_name); - write_type_left(file, &ds, NAME_DEFAULT, TRUE); + write_type_left(file, &ds, NAME_DEFAULT, TRUE, TRUE); fprintf(file, " *)(pStubMsg->StackTop - %u);\n", eval->baseoff); } print_file(file, 1, "pStubMsg->Offset = 0;\n"); /* FIXME */ diff --git a/tools/widl/typetree.c b/tools/widl/typetree.c index ef9a6699b2..615a758f4d 100644 --- a/tools/widl/typetree.c +++ b/tools/widl/typetree.c @@ -172,6 +172,7 @@ type_t *type_new_function(var_list_t *args) t = make_type(TYPE_FUNCTION); t->details.function = xmalloc(sizeof(*t->details.function)); t->details.function->args = args; + t->details.function->retval = make_var(xstrdup("_RetVal")); return t; }
diff --git a/tools/widl/widltypes.h b/tools/widl/widltypes.h index 3f2a719f2d..3f92192cee 100644 --- a/tools/widl/widltypes.h +++ b/tools/widl/widltypes.h @@ -478,7 +478,6 @@ struct _var_t { struct _declarator_t { var_t *var; type_t *type; - type_t *func_type; expr_t *bits;
/* parser-internal */