[4/4] widl: Allow types that reference themselves

Dan Hipschman dsh at linux.ucla.edu
Thu May 24 19:50:02 CDT 2007


This patch allows widl to handle types that reference themselves (like
a linked list node type, which contains a pointer to itself).  Without
this patch, widl will blow the stack on nonterminating recursive calls.
This is probably the simplest way to solve this problem.  Other soultions
might require abstracting the way the type format string is handled, and
would require more substantial changes.  Includes tests.

This gets oaidl.idl past another failure.  The next one involves
interface pointers in complex structures, which was easy.  I've already
got a patch for that, but I'm still working on the testcases.

---
 dlls/rpcrt4/tests/server.c         |   45 +++++++++++++++++++++++
 dlls/rpcrt4/tests/server.idl       |   12 ++++++
 dlls/rpcrt4/tests/server_defines.h |    4 ++
 tools/widl/parser.y                |    1 +
 tools/widl/typegen.c               |   69 +++++++++++++++++++++++++-----------
 tools/widl/widltypes.h             |    1 +
 6 files changed, 111 insertions(+), 21 deletions(-)

diff --git a/dlls/rpcrt4/tests/server.c b/dlls/rpcrt4/tests/server.c
index 0a628c1..9bbe853 100644
--- a/dlls/rpcrt4/tests/server.c
+++ b/dlls/rpcrt4/tests/server.c
@@ -154,6 +154,14 @@ s_square_sun(sun_t *sun)
   }
 }
 
+int
+s_test_list_length(test_list_t *list)
+{
+  return (list->t == TL_LIST
+          ? 1 + s_test_list_length(list->u.tail)
+          : 0);
+}
+
 void
 s_stop(void)
 {
@@ -292,6 +300,41 @@ union_tests(void)
   ok(square_sun(&sun) == 121.0, "RPC square_sun\n");
 }
 
+static test_list_t *
+null_list(void)
+{
+  test_list_t *n = HeapAlloc(GetProcessHeap(), 0, sizeof *n);
+  n->t = TL_NULL;
+  return n;
+}
+
+static test_list_t *
+make_list(test_list_t *tail)
+{
+  test_list_t *n = HeapAlloc(GetProcessHeap(), 0, sizeof *n);
+  n->t = TL_LIST;
+  n->u.tail = tail;
+  return n;
+}
+
+static void
+free_list(test_list_t *list)
+{
+  if (list->t == TL_LIST)
+    free_list(list->u.tail);
+  HeapFree(GetProcessHeap(), 0, list);
+}
+
+static void
+pointer_tests(void)
+{
+  test_list_t *list = make_list(make_list(make_list(null_list())));
+
+  ok(test_list_length(list) == 3, "RPC test_list_length\n");
+
+  free_list(list);
+}
+
 static void
 client(const char *test)
 {
@@ -307,6 +350,7 @@ client(const char *test)
 
     basic_tests();
     union_tests();
+    pointer_tests();
 
     ok(RPC_S_OK == RpcStringFree(&binding), "RpcStringFree\n");
     ok(RPC_S_OK == RpcBindingFree(&IServer_IfHandle), "RpcBindingFree\n");
@@ -323,6 +367,7 @@ client(const char *test)
 
     basic_tests();
     union_tests();
+    pointer_tests();
     stop();
 
     ok(RPC_S_OK == RpcStringFree(&binding), "RpcStringFree\n");
diff --git a/dlls/rpcrt4/tests/server.idl b/dlls/rpcrt4/tests/server.idl
index 064517e..b0ac434 100644
--- a/dlls/rpcrt4/tests/server.idl
+++ b/dlls/rpcrt4/tests/server.idl
@@ -96,5 +96,17 @@ interface IServer
 
   int sum_sp(sp_t *sp);
   double square_sun(sun_t *sun);
+
+  typedef struct test_list
+  {
+    int t;
+    [switch_is(t)] union
+    {
+      [case(TL_NULL)] int x;  /* end of list */
+      [case(TL_LIST)] struct test_list *tail;
+    } u;
+  } test_list_t;
+
+  int test_list_length(test_list_t *ls);
   void stop(void);
 }
diff --git a/dlls/rpcrt4/tests/server_defines.h b/dlls/rpcrt4/tests/server_defines.h
index ac94ef5..5794af2 100644
--- a/dlls/rpcrt4/tests/server_defines.h
+++ b/dlls/rpcrt4/tests/server_defines.h
@@ -23,3 +23,7 @@
 #define SUN_F1 -2
 #define SUN_F2 7
 #define SUN_PI 399
+
+/* test_list_t case values */
+#define TL_NULL 0
+#define TL_LIST 1
diff --git a/tools/widl/parser.y b/tools/widl/parser.y
index 60d7d14..27ac1c6 100644
--- a/tools/widl/parser.y
+++ b/tools/widl/parser.y
@@ -1206,6 +1206,7 @@ static type_t *make_type(unsigned char type, type_t *ref)
   t->defined = FALSE;
   t->written = FALSE;
   t->user_types_registered = FALSE;
+  t->tfswrite = FALSE;
   t->typelib_idx = -1;
   return t;
 }
diff --git a/tools/widl/typegen.c b/tools/widl/typegen.c
index 4c2833d..c6024e2 100644
--- a/tools/widl/typegen.c
+++ b/tools/widl/typegen.c
@@ -132,6 +132,23 @@ static int is_union(unsigned char type)
     }
 }
 
+static void update_tfsoff(type_t *type, unsigned int offset, FILE *file)
+{
+    type->typestring_offset = offset;
+    if (file) type->tfswrite = FALSE;
+}
+
+static void guard_rec(type_t *type)
+{
+    /* types that contain references to themselves (like a linked list),
+       need to be shielded from infinite recursion when writing embedded
+       types  */
+    if (type->typestring_offset)
+        type->tfswrite = FALSE;
+    else
+        type->typestring_offset = 1;
+}
+
 static int is_embedded_complex(const type_t *type)
 {
     return is_struct(type->type) || is_union(type->type);
@@ -789,7 +806,7 @@ static size_t write_pointer_tfs(FILE *file, type_t *type, unsigned int *typestri
     unsigned int offset = *typestring_offset;
 
     print_file(file, 0, "/* %d */\n", offset);
-    type->typestring_offset = offset;
+    update_tfsoff(type, offset, file);
 
     if (type->ref->typestring_offset)
         *typestring_offset += write_nonsimple_pointer(file, type, offset);
@@ -799,9 +816,9 @@ static size_t write_pointer_tfs(FILE *file, type_t *type, unsigned int *typestri
     return offset;
 }
 
-static int has_known_tfs(const type_t *type)
+static int processed(const type_t *type)
 {
-    return type->typestring_offset || is_base_type(type->type);
+    return type->typestring_offset && !type->tfswrite;
 }
 
 static size_t write_pointer_description(FILE *file, const attr_list_t *attrs,
@@ -827,7 +844,7 @@ static size_t write_pointer_description(FILE *file, const attr_list_t *attrs,
         print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", mem_offset, mem_offset);
         *typestring_offset += 6;
 
-        if (has_known_tfs(type->ref))
+        if (processed(type->ref) || is_base_type(type->ref->type))
             write_pointer_tfs(file, type, typestring_offset);
         else
             error("write_pointer_description: type format string unknown\n");
@@ -1224,6 +1241,8 @@ static size_t write_struct_tfs(FILE *file, type_t *type,
     int has_pointers;
     unsigned int align = 0;
 
+    guard_rec(type);
+
     switch (type->type)
     {
     case RPC_FC_STRUCT:
@@ -1238,7 +1257,7 @@ static size_t write_struct_tfs(FILE *file, type_t *type,
             write_embedded_types(file, NULL, type, name, NULL, 0, typestring_offset);
 
         start_offset = *typestring_offset;
-        type->typestring_offset = start_offset;
+        update_tfsoff(type, start_offset, file);
         if (type->type == RPC_FC_STRUCT)
             WRITE_FCTYPE(file, FC_STRUCT, *typestring_offset);
         else
@@ -1281,7 +1300,7 @@ static size_t write_struct_tfs(FILE *file, type_t *type,
             write_embedded_types(file, NULL, type, name, NULL, 0, typestring_offset);
 
         start_offset = *typestring_offset;
-        type->typestring_offset = start_offset;
+        update_tfsoff(type, start_offset, file);
         if (type->type == RPC_FC_CSTRUCT)
             WRITE_FCTYPE(file, FC_CSTRUCT, *typestring_offset);
         else
@@ -1334,7 +1353,7 @@ static size_t write_struct_tfs(FILE *file, type_t *type,
                                             typestring_offset);
 
         start_offset = *typestring_offset;
-        type->typestring_offset = start_offset;
+        update_tfsoff(type, start_offset, file);
         WRITE_FCTYPE(file, FC_CVSTRUCT, *typestring_offset);
         /* alignment */
         print_file(file, 2, "0x%02x,\n", align - 1);
@@ -1371,6 +1390,7 @@ static size_t write_struct_tfs(FILE *file, type_t *type,
         write_embedded_types(file, NULL, type, name, NULL, 0, typestring_offset);
 
         start_offset = *typestring_offset;
+        update_tfsoff(type, start_offset, file);
         print_file(file, 0, "/* %d */\n", start_offset);
         print_file(file, 2, "0x%x,\t/* %s */\n", type->type, string_of_type(type->type));
         print_file(file, 2, "0x%x,\t/* %d */\n", align - 1, align - 1);
@@ -1457,6 +1477,8 @@ static size_t write_union_tfs(FILE *file, type_t *type, const char *name,
     short nodeftype = 0xffff;
     var_t *f;
 
+    guard_rec(type);
+
     /* use a level of 1 so pointers always get written */
     write_embedded_types(file, NULL, type, name, NULL, 1, tfsoff);
 
@@ -1468,7 +1490,7 @@ static size_t write_union_tfs(FILE *file, type_t *type, const char *name,
     }
 
     start_offset = *tfsoff;
-    type->typestring_offset = start_offset;
+    update_tfsoff(type, start_offset, file);
     print_file(file, 0, "/* %d */\n", start_offset);
     print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", size, size);
     print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", nbranch, nbranch);
@@ -1669,11 +1691,11 @@ static size_t write_typeformatstring_var(FILE *file, int indent, const func_t *f
                            offset, typeformat_offset);
 }
 
-static void clear_tfsoff(type_t *type)
+static void set_tfswrite(type_t *type, int val)
 {
-    for (;;)
+    while (type->tfswrite != val)
     {
-        type->typestring_offset = 0;
+        type->tfswrite = val;
 
         if (type->kind == TKIND_ALIAS)
             type = type->orig;
@@ -1685,7 +1707,7 @@ static void clear_tfsoff(type_t *type)
             {
                 var_t *v;
                 LIST_FOR_EACH_ENTRY( v, type->fields, var_t, entry )
-                    clear_tfsoff(v->type);
+                    set_tfswrite(v->type, val);
             }
 
             return;
@@ -1707,7 +1729,8 @@ static int write_embedded_types(FILE *file, const attr_list_t *attrs, type_t *ty
         unsigned int align = 0;
         type_t *ft = f->type;
 
-        if (ft->type == RPC_FC_NON_ENCAPSULATED_UNION)
+        if (!ft) continue;
+        else if (ft->type == RPC_FC_NON_ENCAPSULATED_UNION)
         {
             expr_t *swexp = get_attrp(f->attrs, ATTR_SWITCHIS);
             const char *swname;
@@ -1722,7 +1745,7 @@ static int write_embedded_types(FILE *file, const attr_list_t *attrs, type_t *ty
                 error("%s: only identifiers are supported for switch_is at this time\n",
                       f->name);
 
-            if (ft->typestring_offset == 0)
+            if (!processed(ft))
                 write_union_tfs(file, ft, f->name, tfsoff);
 
             swname = swexp->u.sval;
@@ -1760,7 +1783,7 @@ static int write_embedded_types(FILE *file, const attr_list_t *attrs, type_t *ty
     {
         type_t *ref = type->ref;
 
-        if (!has_known_tfs(ref))
+        if (!processed(ref) && !is_base_type(ref->type))
         {
             if (is_ptr(ref))
             {
@@ -1791,7 +1814,7 @@ static int write_embedded_types(FILE *file, const attr_list_t *attrs, type_t *ty
     return retmask;
 }
 
-static void clear_all_tfsoffs(const ifref_list_t *ifaces)
+static void set_all_tfswrite(const ifref_list_t *ifaces, int val)
 {
     const ifref_t * iface;
     const func_t *func;
@@ -1803,7 +1826,7 @@ static void clear_all_tfsoffs(const ifref_list_t *ifaces)
                 LIST_FOR_EACH_ENTRY( func, iface->iface->funcs, const func_t, entry )
                     if (func->args)
                         LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry )
-                            clear_tfsoff(var->type);
+                            set_tfswrite(var->type, val);
 }
 
 static size_t process_tfs(FILE *file, const ifref_list_t *ifaces, int for_objects)
@@ -1827,9 +1850,12 @@ static size_t process_tfs(FILE *file, const ifref_list_t *ifaces, int for_object
                 current_func = func;
                 if (func->args)
                     LIST_FOR_EACH_ENTRY( var, func->args, const var_t, entry )
-                        var->type->typestring_offset
-                            = write_typeformatstring_var(file, 2, func, var->type,
-                                                         var, &typeformat_offset);
+                        update_tfsoff(
+                            var->type,
+                            write_typeformatstring_var(
+                                file, 2, func, var->type, var,
+                                &typeformat_offset),
+                            file);
             }
         }
     }
@@ -1850,7 +1876,7 @@ void write_typeformatstring(FILE *file, const ifref_list_t *ifaces, int for_obje
     indent++;
     print_file(file, indent, "NdrFcShort(0x0),\n");
 
-    clear_all_tfsoffs(ifaces);
+    set_all_tfswrite(ifaces, TRUE);
     process_tfs(file, ifaces, for_objects);
 
     print_file(file, indent, "0x0\n");
@@ -2417,6 +2443,7 @@ size_t get_size_procformatstring(const ifref_list_t *ifaces, int for_objects)
 
 size_t get_size_typeformatstring(const ifref_list_t *ifaces, int for_objects)
 {
+    set_all_tfswrite(ifaces, FALSE);
     return process_tfs(NULL, ifaces, for_objects);
 }
 
diff --git a/tools/widl/widltypes.h b/tools/widl/widltypes.h
index 4d8b6d4..69a1379 100644
--- a/tools/widl/widltypes.h
+++ b/tools/widl/widltypes.h
@@ -214,6 +214,7 @@ struct _type_t {
   unsigned int defined : 1;
   unsigned int written : 1;
   unsigned int user_types_registered : 1;
+  unsigned int tfswrite : 1;   /* if the type needs to be written to the TFS */
   int sign : 2;
 };
 



More information about the wine-patches mailing list