[PATCH 2/4] widl: Support WinRT parameterized interface type.

Rémi Bernon rbernon at codeweavers.com
Mon Feb 15 04:09:44 CST 2021


This allows parameterized interfaces to be instanciated in declare
blocks, in the same way MIDL does, generating a new interface to the
header from the parameterized type template, replacing its parameters
with the given types.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 include/windows.media.speechsynthesis.idl |  24 +++
 tools/widl/header.c                       |   7 +-
 tools/widl/parser.l                       |   1 +
 tools/widl/parser.y                       |  63 +++++-
 tools/widl/typetree.c                     | 238 ++++++++++++++++++++++
 tools/widl/typetree.h                     |   2 +
 6 files changed, 330 insertions(+), 5 deletions(-)

diff --git a/include/windows.media.speechsynthesis.idl b/include/windows.media.speechsynthesis.idl
index 87497678f30..af4466681dc 100644
--- a/include/windows.media.speechsynthesis.idl
+++ b/include/windows.media.speechsynthesis.idl
@@ -35,11 +35,24 @@ namespace Windows {
             interface ISpeechSynthesizer;
             interface ISpeechSynthesizer2;
             interface IVoiceInformation;
+            runtimeclass SpeechSynthesizer;
             runtimeclass VoiceInformation;
         }
     }
 }
 
+namespace Windows {
+    namespace Media {
+        namespace SpeechSynthesis {
+            declare {
+                interface Windows.Foundation.Collections.IIterator<Windows.Media.SpeechSynthesis.VoiceInformation*>;
+                interface Windows.Foundation.Collections.IIterable<Windows.Media.SpeechSynthesis.VoiceInformation*>;
+                interface Windows.Foundation.Collections.IVectorView<Windows.Media.SpeechSynthesis.VoiceInformation*>;
+            }
+        }
+    }
+}
+
 namespace Windows {
     namespace Media {
         namespace SpeechSynthesis {
@@ -64,6 +77,17 @@ namespace Windows {
                 [propget] HRESULT Gender([out] [retval] VoiceGender* value);
             }
 
+            [
+                contract(Windows.Foundation.UniversalApiContract, 1.0),
+                exclusiveto(Windows.Media.SpeechSynthesis.SpeechSynthesizer),
+                uuid(7d526ecc-7533-4c3f-85be-888c2baeebdc)
+            ]
+            interface IInstalledVoicesStatic : IInspectable
+            {
+                [propget] HRESULT AllVoices([out, retval] Windows.Foundation.Collections.IVectorView<VoiceInformation*>** value);
+                [propget] HRESULT DefaultVoice([out, retval] VoiceInformation** value);
+            }
+
             [
                 contract(Windows.Foundation.UniversalApiContract, 1.0),
                 marshaling_behavior(agile)
diff --git a/tools/widl/header.c b/tools/widl/header.c
index 8423756e060..a4f1db56a01 100644
--- a/tools/widl/header.c
+++ b/tools/widl/header.c
@@ -1479,7 +1479,8 @@ static void write_forward(FILE *header, type_t *iface)
   fprintf(header, "typedef interface %s %s;\n", iface->c_name, iface->c_name);
   fprintf(header, "#ifdef __cplusplus\n");
   write_namespace_start(header, iface->namespace);
-  write_line(header, 0, "interface %s;", iface->name);
+  if (strchr(iface->name, '<')) write_line(header, 0, "template<> struct %s;", iface->name);
+  else write_line(header, 0, "interface %s;", iface->name);
   write_namespace_end(header, iface->namespace);
   fprintf(header, "#endif /* __cplusplus */\n");
   fprintf(header, "#endif\n\n" );
@@ -1548,11 +1549,13 @@ static void write_com_interface_end(FILE *header, type_t *iface)
       write_namespace_start(header, iface->namespace);
   }
   if (uuid) {
+      if (strchr(iface->name, '<')) write_line(header, 0, "template<>");
       write_line(header, 0, "MIDL_INTERFACE(\"%s\")", uuid_string(uuid));
       indent(header, 0);
   }else {
       indent(header, 0);
-      fprintf(header, "interface ");
+      if (strchr(iface->name, '<')) fprintf(header, "template<> struct ");
+      else fprintf(header, "interface ");
   }
   if (type_iface_get_inherit(iface))
   {
diff --git a/tools/widl/parser.l b/tools/widl/parser.l
index 946dba84cd6..d319954edd3 100644
--- a/tools/widl/parser.l
+++ b/tools/widl/parser.l
@@ -276,6 +276,7 @@ static const struct keyword keywords[] = {
 	{"coclass",         tCOCLASS,        0},
 	{"const",           tCONST,          0},
 	{"cpp_quote",       tCPPQUOTE,       0},
+	{"declare",         tDECLARE,        1},
 	{"default",         tDEFAULT,        0},
 	{"dispinterface",   tDISPINTERFACE,  0},
 	{"double",          tDOUBLE,         0},
diff --git a/tools/widl/parser.y b/tools/widl/parser.y
index 0725332a736..9f5c12fd922 100644
--- a/tools/widl/parser.y
+++ b/tools/widl/parser.y
@@ -81,6 +81,7 @@ static void pop_namespace(const char *name);
 static void push_parameters_namespace(const char *name);
 static void pop_parameters_namespace(const char *name);
 
+static statement_list_t *append_parameterized_type_stmts(statement_list_t *stmts);
 static void check_arg_attrs(const var_t *arg);
 static void check_statements(const statement_list_t *stmts, int is_inside_library);
 static void check_all_user_types(const statement_list_t *stmts);
@@ -108,6 +109,7 @@ static statement_t *make_statement_importlib(const char *str);
 static statement_t *make_statement_module(type_t *type);
 static statement_t *make_statement_typedef(var_list_t *names, int declonly);
 static statement_t *make_statement_import(const char *str);
+static statement_t *make_statement_parameterized_type(type_t *type, type_list_t *params);
 static statement_list_t *append_statement(statement_list_t *list, statement_t *stmt);
 static statement_list_t *append_statements(statement_list_t *, statement_list_t *);
 static attr_list_t *append_attribs(attr_list_t *, attr_list_t *);
@@ -118,6 +120,7 @@ static struct namespace global_namespace = {
 
 static struct namespace *current_namespace = &global_namespace;
 static struct namespace *parameters_namespace = NULL;
+static statement_list_t *parameterized_type_stmts = NULL;
 
 static typelib_t *current_typelib;
 
@@ -180,6 +183,7 @@ static typelib_t *current_typelib;
 %token tCONTRACTVERSION
 %token tCONTROL tCPPQUOTE
 %token tCUSTOM
+%token tDECLARE
 %token tDECODE tDEFAULT tDEFAULTBIND
 %token tDEFAULTCOLLELEM
 %token tDEFAULTVALUE
@@ -320,6 +324,8 @@ static typelib_t *current_typelib;
 %type <typelib> library_start librarydef
 %type <statement> statement typedef pragma_warning
 %type <stmt_list> gbl_statements imp_statements int_statements
+%type <stmt_list> decl_block decl_statements
+%type <stmt_list> imp_decl_block imp_decl_statements
 %type <warning_list> warnings
 %type <num> allocate_option_list allocate_option
 %type <namespace> namespace_pfx
@@ -343,7 +349,8 @@ static typelib_t *current_typelib;
 
 %%
 
-input: gbl_statements m_acf			{ check_statements($1, FALSE);
+input: gbl_statements m_acf			{ $1 = append_parameterized_type_stmts($1);
+						  check_statements($1, FALSE);
 						  check_all_user_types($1);
 						  write_header($1);
 						  write_id_data($1);
@@ -359,6 +366,22 @@ input: gbl_statements m_acf			{ check_statements($1, FALSE);
 
 m_acf: /* empty */ | aACF acf_statements
 
+decl_statements:				{ $$ = NULL; }
+	| decl_statements tINTERFACE qualified_type '<' parameterized_type_args '>' ';'
+						{ parameterized_type_stmts = append_statement(parameterized_type_stmts, make_statement_parameterized_type($3, $5));
+						  $$ = append_statement($1, make_statement_reference(type_parameterized_type_specialize_declare($3, $5)));
+						}
+	;
+
+decl_block: tDECLARE '{' decl_statements '}' { $$ = $3; }
+
+imp_decl_statements:				{ $$ = NULL; }
+	| imp_decl_statements tINTERFACE qualified_type '<' parameterized_type_args '>' ';'
+						{ $$ = append_statement($1, make_statement_reference(type_parameterized_type_specialize_declare($3, $5))); }
+	;
+
+imp_decl_block: tDECLARE '{' imp_decl_statements '}' { $$ = $3; }
+
 gbl_statements:					{ $$ = NULL; }
 	| gbl_statements namespacedef '{' { push_namespace($2); } gbl_statements '}'
 						{ pop_namespace($2); $$ = append_statements($1, $5); }
@@ -380,6 +403,7 @@ gbl_statements:					{ $$ = NULL; }
 	| gbl_statements moduledef		{ $$ = append_statement($1, make_statement_module($2)); }
 	| gbl_statements librarydef		{ $$ = append_statement($1, make_statement_library($2)); }
 	| gbl_statements statement		{ $$ = append_statement($1, $2); }
+	| gbl_statements decl_block		{ $$ = append_statements($1, $2); }
 	;
 
 imp_statements:					{ $$ = NULL; }
@@ -402,6 +426,7 @@ imp_statements:					{ $$ = NULL; }
 	| imp_statements statement		{ $$ = append_statement($1, $2); }
 	| imp_statements importlib		{ $$ = append_statement($1, make_statement_importlib($2)); }
 	| imp_statements librarydef		{ $$ = append_statement($1, make_statement_library($2)); }
+	| imp_statements imp_decl_block		{ $$ = append_statements($1, $2); }
 	;
 
 int_statements:					{ $$ = NULL; }
@@ -3084,6 +3109,27 @@ static void check_async_uuid(type_t *iface)
     iface->details.iface->async_iface = async_iface->details.iface->async_iface = async_iface;
 }
 
+static statement_list_t *append_parameterized_type_stmts(statement_list_t *stmts)
+{
+    statement_t *stmt, *next;
+    if (stmts && parameterized_type_stmts) LIST_FOR_EACH_ENTRY_SAFE(stmt, next, parameterized_type_stmts, statement_t, entry)
+    {
+        switch(stmt->type) {
+        case STMT_TYPE:
+            stmt->u.type = type_parameterized_type_specialize_define(stmt->u.type_list->type, stmt->u.type_list->next);
+            stmt->declonly = FALSE;
+            list_remove(&stmt->entry);
+            stmts = append_statement(stmts, stmt);
+            break;
+        default:
+            assert(0); /* should not be there */
+            break;
+        }
+    }
+
+    return stmts;
+}
+
 static void check_statements(const statement_list_t *stmts, int is_inside_library)
 {
     const statement_t *stmt;
@@ -3265,6 +3311,15 @@ static statement_t *make_statement_typedef(declarator_list_t *decls, int declonl
     return stmt;
 }
 
+static statement_t *make_statement_parameterized_type(type_t *type, type_list_t *params)
+{
+    statement_t *stmt = make_statement(STMT_TYPE);
+    stmt->u.type_list = xmalloc(sizeof(type_list_t));
+    stmt->u.type_list->type = type;
+    stmt->u.type_list->next = params;
+    return stmt;
+}
+
 static statement_list_t *append_statements(statement_list_t *l1, statement_list_t *l2)
 {
     if (!l2) return l1;
@@ -3309,8 +3364,10 @@ type_t *find_parameterized_type(type_t *type, type_list_t *params)
         assert(type->type_type == TYPE_PARAMETERIZED_TYPE);
         type = type_parameterized_type_specialize_partial(type, params);
     }
-    /* FIXME: If not in another parameterized type, we'll have to look for the declared specialization. */
-    else error_loc("parameterized type '%s' not declared\n", name);
+    else if ((type = find_type(name, type->namespace, 0)))
+        assert(type->type_type != TYPE_PARAMETERIZED_TYPE);
+    else
+        error_loc("parameterized type '%s' not declared\n", name);
 
     free(name);
     return type;
diff --git a/tools/widl/typetree.c b/tools/widl/typetree.c
index 22c75d32161..58b019cc090 100644
--- a/tools/widl/typetree.c
+++ b/tools/widl/typetree.c
@@ -137,6 +137,41 @@ char *format_parameterized_type_name(type_t *type, type_list_t *params)
     return buf;
 }
 
+static char const *parameterized_type_shorthands[][2] = {
+    {"Windows_CFoundation_CCollections_C", "__F"},
+    {"Windows_CFoundation_C", "__F"},
+};
+
+static char *format_parameterized_type_c_name(type_t *type, type_list_t *params)
+{
+    size_t len = 0, pos = 0;
+    char *buf = NULL, *tmp;
+    type_list_t *entry;
+    int i, count = 0;
+
+    pos += append_namespaces(&buf, &len, pos, type->namespace, "__x_", "_C", type->name, use_abi_namespace ? "ABI" : NULL);
+    for (entry = params; entry; entry = entry->next) count++;
+    pos += strappend(&buf, &len, pos, "_%d", count);
+    for (entry = params; entry; entry = entry->next)
+    {
+        for (type = entry->type; type->type_type == TYPE_POINTER; type = type_pointer_get_ref_type(type)) {}
+        pos += append_namespaces(&buf, &len, pos, type->namespace, "_", "__C", type->name, NULL);
+    }
+
+    for (i = 0; i < ARRAY_SIZE(parameterized_type_shorthands); ++i)
+    {
+        if ((tmp = strstr(buf, parameterized_type_shorthands[i][0])) &&
+            (tmp - buf) == strlen(use_abi_namespace ? "__x_ABI_C" : "__x_C"))
+        {
+           tmp += strlen(parameterized_type_shorthands[i][0]);
+           strcpy(buf, parameterized_type_shorthands[i][1]);
+           memmove(buf + 3, tmp, len - (tmp - buf) + 1);
+        }
+    }
+
+    return buf;
+}
+
 type_t *type_new_function(var_list_t *args)
 {
     var_t *arg;
@@ -677,6 +712,209 @@ type_t *type_parameterized_type_specialize_partial(type_t *type, type_list_t *pa
     return new_type;
 }
 
+static type_t *replace_type_parameters_in_type(type_t *type, type_list_t *orig, type_list_t *repl);
+
+static type_list_t *replace_type_parameters_in_type_list(type_list_t *type_list, type_list_t *orig, type_list_t *repl)
+{
+    type_list_t *entry, *new_entry, **next, *first = NULL;
+
+    if (!type_list) return type_list;
+
+    next = &first;
+    for (entry = type_list; entry; entry = entry->next)
+    {
+        new_entry = xmalloc(sizeof(*new_entry));
+        new_entry->type = replace_type_parameters_in_type(entry->type, orig, repl);
+        new_entry->next = NULL;
+        *next = new_entry;
+        next = &new_entry->next;
+    }
+
+    return first;
+}
+
+static var_t *replace_type_parameters_in_var(var_t *var, type_list_t *orig, type_list_t *repl)
+{
+    var_t *new_var = xmalloc(sizeof(*new_var));
+    *new_var = *var;
+    list_init(&new_var->entry);
+    new_var->declspec.type = replace_type_parameters_in_type(var->declspec.type, orig, repl);
+    return new_var;
+}
+
+static var_list_t *replace_type_parameters_in_var_list(var_list_t *var_list, type_list_t *orig, type_list_t *repl)
+{
+    var_list_t *new_var_list;
+    var_t *var, *new_var;
+
+    if (!var_list) return var_list;
+
+    new_var_list = xmalloc(sizeof(*new_var_list));
+    list_init(new_var_list);
+
+    LIST_FOR_EACH_ENTRY(var, var_list, var_t, entry)
+    {
+        new_var = replace_type_parameters_in_var(var, orig, repl);
+        list_add_tail(new_var_list, &new_var->entry);
+    }
+
+    return new_var_list;
+}
+
+static statement_t *replace_type_parameters_in_statement(statement_t *stmt, type_list_t *orig, type_list_t *repl)
+{
+    statement_t *new_stmt = xmalloc(sizeof(*new_stmt));
+    *new_stmt = *stmt;
+    list_init(&new_stmt->entry);
+
+    switch (stmt->type)
+    {
+    case STMT_DECLARATION:
+        new_stmt->u.var = replace_type_parameters_in_var(stmt->u.var, orig, repl);
+        break;
+    case STMT_LIBRARY:
+    case STMT_TYPE:
+    case STMT_TYPEREF:
+    case STMT_MODULE:
+    case STMT_TYPEDEF:
+        new_stmt->u.type_list = replace_type_parameters_in_type_list(stmt->u.type_list, orig, repl);
+        break;
+    case STMT_IMPORT:
+    case STMT_IMPORTLIB:
+    case STMT_PRAGMA:
+    case STMT_CPPQUOTE:
+        fprintf(stderr, "%d\n", stmt->type);
+        assert(0);
+        break;
+    }
+
+    return new_stmt;
+}
+
+static statement_list_t *replace_type_parameters_in_statement_list(statement_list_t *stmt_list, type_list_t *orig, type_list_t *repl)
+{
+    statement_list_t *new_stmt_list;
+    statement_t *stmt, *new_stmt;
+
+    if (!stmt_list) return stmt_list;
+
+    new_stmt_list = xmalloc(sizeof(*new_stmt_list));
+    list_init(new_stmt_list);
+
+    LIST_FOR_EACH_ENTRY(stmt, stmt_list, statement_t, entry)
+    {
+        new_stmt = replace_type_parameters_in_statement(stmt, orig, repl);
+        list_add_tail(new_stmt_list, &new_stmt->entry);
+    }
+
+    return new_stmt_list;
+}
+
+static type_t *replace_type_parameters_in_type(type_t *type, type_list_t *orig, type_list_t *repl)
+{
+    type_list_t *o, *r;
+    type_t *t;
+
+    if (!type) return type;
+    switch (type->type_type)
+    {
+    case TYPE_VOID:
+    case TYPE_BASIC:
+    case TYPE_ENUM:
+    case TYPE_BITFIELD:
+    case TYPE_INTERFACE:
+    case TYPE_RUNTIMECLASS:
+        return type;
+    case TYPE_PARAMETER:
+        for (o = orig, r = repl; o && r; o = o->next, r = r->next)
+            if (type == o->type) return r->type;
+        return type;
+    case TYPE_POINTER:
+        t = replace_type_parameters_in_type(type->details.pointer.ref.type, orig, repl);
+        if (t == type->details.pointer.ref.type) return type;
+        type = duptype(type, 0);
+        type->details.pointer.ref.type = t;
+        return type;
+    case TYPE_ALIAS:
+        t = replace_type_parameters_in_type(type->details.alias.aliasee.type, orig, repl);
+        if (t == type->details.alias.aliasee.type) return type;
+        type = duptype(type, 0);
+        type->details.alias.aliasee.type = t;
+        return type;
+    case TYPE_ARRAY:
+        t = replace_type_parameters_in_type(type->details.array.elem.type, orig, repl);
+        if (t == t->details.array.elem.type) return type;
+        type = duptype(type, 0);
+        t->details.array.elem.type = t;
+        return type;
+    case TYPE_FUNCTION:
+        t = duptype(type, 0);
+        t->details.function = xmalloc(sizeof(*t->details.function));
+        t->details.function->args = replace_type_parameters_in_var_list(type->details.function->args, orig, repl);
+        t->details.function->retval = replace_type_parameters_in_var(type->details.function->retval, orig, repl);
+        return t;
+    case TYPE_PARAMETERIZED_TYPE:
+        t = type->details.parameterized.type;
+        if (t->type_type != TYPE_PARAMETERIZED_TYPE) return find_parameterized_type(type, repl);
+        repl = replace_type_parameters_in_type_list(type->details.parameterized.params, orig, repl);
+        return replace_type_parameters_in_type(t, t->details.parameterized.params, repl);
+    case TYPE_STRUCT:
+    case TYPE_ENCAPSULATED_UNION:
+    case TYPE_UNION:
+    case TYPE_MODULE:
+    case TYPE_COCLASS:
+    case TYPE_APICONTRACT:
+        assert(0); /* FIXME: implement when needed */
+        break;
+    }
+
+    return type;
+}
+
+static void type_parameterized_interface_specialize(type_t *tmpl, type_t *iface, type_list_t *orig, type_list_t *repl)
+{
+    iface->details.iface = xmalloc(sizeof(*iface->details.iface));
+    iface->details.iface->disp_methods = NULL;
+    iface->details.iface->disp_props = NULL;
+    iface->details.iface->stmts = replace_type_parameters_in_statement_list(tmpl->details.iface->stmts, orig, repl);
+    iface->details.iface->inherit = replace_type_parameters_in_type(tmpl->details.iface->inherit, orig, repl);
+    iface->details.iface->disp_inherit = NULL;
+    iface->details.iface->async_iface = NULL;
+    iface->details.iface->requires = NULL;
+}
+
+type_t *type_parameterized_type_specialize_declare(type_t *type, type_list_t *params)
+{
+    type_t *tmpl = type->details.parameterized.type;
+    type_t *new_type = duptype(tmpl, 0);
+
+    new_type->namespace = type->namespace;
+    new_type->name = format_parameterized_type_name(type, params);
+    reg_type(new_type, new_type->name, new_type->namespace, 0);
+    new_type->c_name = format_parameterized_type_c_name(type, params);
+
+    return new_type;
+}
+
+type_t *type_parameterized_type_specialize_define(type_t *type, type_list_t *params)
+{
+    type_list_t *orig = type->details.parameterized.params;
+    type_t *tmpl = type->details.parameterized.type;
+    type_t *iface = find_parameterized_type(type, params);
+
+    if (tmpl->type_type == TYPE_INTERFACE)
+        type_parameterized_interface_specialize(tmpl, iface, orig, params);
+    else
+    {
+        error_loc("Unsupported parameterized type template %d\n", tmpl->type_type);
+        return NULL;
+    }
+
+    iface->defined = TRUE;
+    compute_method_indexes(iface);
+    return iface;
+}
+
 int type_is_equal(const type_t *type1, const type_t *type2)
 {
     if (type1 == type2)
diff --git a/tools/widl/typetree.h b/tools/widl/typetree.h
index 52ae2ec8677..0dc75dff62e 100644
--- a/tools/widl/typetree.h
+++ b/tools/widl/typetree.h
@@ -67,6 +67,8 @@ type_t *type_apicontract_define(type_t *apicontract, attr_list_t *attrs);
 type_t *type_parameterized_interface_declare(char *name, struct namespace *namespace, type_list_t *params);
 type_t *type_parameterized_interface_define(type_t *type, attr_list_t *attrs, type_t *inherit, statement_list_t *stmts, ifref_list_t *requires);
 type_t *type_parameterized_type_specialize_partial(type_t *type, type_list_t *params);
+type_t *type_parameterized_type_specialize_declare(type_t *type, type_list_t *params);
+type_t *type_parameterized_type_specialize_define(type_t *type, type_list_t *params);
 int type_is_equal(const type_t *type1, const type_t *type2);
 const char *type_get_name(const type_t *type, enum name_type name_type);
 char *gen_name(void);
-- 
2.30.0




More information about the wine-devel mailing list