[2/2] widl: Lay framework for unions with simple unions working

Dan Hipschman dsh at linux.ucla.edu
Fri May 18 18:53:27 CDT 2007


This patch lays the framework for unions and gets the simplest case of
unions working.  The next oaidl.idl problem is a complex structure which
contains a union, although this patch doesn't add quite enough function-
ality to fix that yet.  This is about the least I could do to be able to
write a test case that passed, so there are a few holes left in that need
to be filled out (actually, padding has been so far ignored, so in that
case holes need to be put in - sorry for the horrible pun :-)).  I'll
work more on completing unions and complex structures next week.  Tests
pass on XP.

---
 dlls/rpcrt4/tests/server.c         |   34 +++++
 dlls/rpcrt4/tests/server.idl       |   14 ++
 dlls/rpcrt4/tests/server_defines.h |   25 ++++
 tools/widl/parser.y                |    1 +
 tools/widl/typegen.c               |  266 ++++++++++++++++++++++++++++++++++--
 tools/widl/widltypes.h             |    1 +
 6 files changed, 332 insertions(+), 9 deletions(-)
 create mode 100644 dlls/rpcrt4/tests/server_defines.h

diff --git a/dlls/rpcrt4/tests/server.c b/dlls/rpcrt4/tests/server.c
index ae84a41..da2bf5e 100644
--- a/dlls/rpcrt4/tests/server.c
+++ b/dlls/rpcrt4/tests/server.c
@@ -21,6 +21,7 @@
 #include <windows.h>
 #include "wine/test.h"
 #include "server.h"
+#include "server_defines.h"
 
 #include <stdio.h>
 
@@ -138,6 +139,19 @@ s_sum_sp(sp_t *sp)
   return sp->x + sp->s->x;
 }
 
+double
+s_square_sun(sun_t *sun)
+{
+  switch (sun->s)
+  {
+  case SUN_I: return sun->u.i * sun->u.i;
+  case SUN_F1:
+  case SUN_F2: return sun->u.f * sun->u.f;
+  default:
+    return 0.0;
+  }
+}
+
 void
 s_stop(void)
 {
@@ -253,6 +267,24 @@ basic_tests(void)
 }
 
 static void
+union_tests(void)
+{
+  sun_t sun;
+
+  sun.s = SUN_I;
+  sun.u.i = 9;
+  ok(square_sun(&sun) == 81.0, "RPC square_sun\n");
+
+  sun.s = SUN_F1;
+  sun.u.f = 5.0;
+  ok(square_sun(&sun) == 25.0, "RPC square_sun\n");
+
+  sun.s = SUN_I;
+  sun.u.i = -2.0;
+  ok(square_sun(&sun) == 4.0, "RPC square_sun\n");
+}
+
+static void
 client(const char *test)
 {
   if (strcmp(test, "tcp_basic") == 0)
@@ -266,6 +298,7 @@ client(const char *test)
     ok(RPC_S_OK == RpcBindingFromStringBinding(binding, &IServer_IfHandle), "RpcBindingFromStringBinding\n");
 
     basic_tests();
+    union_tests();
 
     ok(RPC_S_OK == RpcStringFree(&binding), "RpcStringFree\n");
     ok(RPC_S_OK == RpcBindingFree(&IServer_IfHandle), "RpcBindingFree\n");
@@ -281,6 +314,7 @@ client(const char *test)
     ok(RPC_S_OK == RpcBindingFromStringBinding(binding, &IServer_IfHandle), "RpcBindingFromStringBinding\n");
 
     basic_tests();
+    union_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 2d671ab..e6f27e7 100644
--- a/dlls/rpcrt4/tests/server.idl
+++ b/dlls/rpcrt4/tests/server.idl
@@ -18,6 +18,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "server_defines.h"
+
 typedef struct tag_vector
 {
   int x;
@@ -53,6 +55,17 @@ interface IServer
     vector_t **pv;
   } pvectors_t;
 
+  typedef struct
+  {
+    [switch_is(s)] union
+    {
+      [case(SUN_I)] int i;
+      [case(SUN_F1, SUN_F2)] float f;
+    } u;
+
+    int s;
+  } sun_t;
+
   int int_return(void);
   int square(int x);
   int sum(int x, int y);
@@ -81,5 +94,6 @@ interface IServer
   } sp_t;
 
   int sum_sp(sp_t *sp);
+  double square_sun(sun_t *sun);
   void stop(void);
 }
diff --git a/dlls/rpcrt4/tests/server_defines.h b/dlls/rpcrt4/tests/server_defines.h
new file mode 100644
index 0000000..72c76fd
--- /dev/null
+++ b/dlls/rpcrt4/tests/server_defines.h
@@ -0,0 +1,25 @@
+/*
+ * Constants shared between server.c and server.idl
+ *
+ * Copyright (C) Google 2007 (Dan Hipschman)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/* sun_t case values */
+#define SUN_I 10
+#define SUN_F1 -2
+#define SUN_F2 7
+
diff --git a/tools/widl/parser.y b/tools/widl/parser.y
index a74aab1..b70dab8 100644
--- a/tools/widl/parser.y
+++ b/tools/widl/parser.y
@@ -1260,6 +1260,7 @@ static var_t *make_var(char *name)
   v->attrs = NULL;
   v->array = NULL;
   v->eval = NULL;
+  v->corrdesc = 0;
   return v;
 }
 
diff --git a/tools/widl/typegen.c b/tools/widl/typegen.c
index 3aca92f..31f50e3 100644
--- a/tools/widl/typegen.c
+++ b/tools/widl/typegen.c
@@ -61,8 +61,9 @@ struct expr_eval_routine
 
 static size_t type_memsize(const type_t *t, const array_dims_t *array, unsigned int *align);
 static size_t fields_memsize(const var_list_t *fields, unsigned int *align);
-static size_t write_struct_tfs(FILE *file, type_t *type, const char *name,
-                               unsigned int *typestring_offset);
+static size_t write_struct_tfs(FILE *file, type_t *type, const char *name, unsigned int *tfsoff);
+static void write_embedded_types(FILE *file, const type_t *type, size_t *tfsoff);
+
 const char *string_of_type(unsigned char type)
 {
     switch (type)
@@ -87,6 +88,14 @@ const char *string_of_type(unsigned char type)
     case RPC_FC_UP: return "FC_UP";
     case RPC_FC_OP: return "FC_OP";
     case RPC_FC_FP: return "FC_FP";
+    case RPC_FC_ENCAPSULATED_UNION: return "FC_ENCAPSULATED_UNION";
+    case RPC_FC_NON_ENCAPSULATED_UNION: return "FC_NON_ENCAPSULATED_UNION";
+    case RPC_FC_STRUCT: return "FC_STRUCT";
+    case RPC_FC_PSTRUCT: return "FC_PSTRUCT";
+    case RPC_FC_CSTRUCT: return "FC_CSTRUCT";
+    case RPC_FC_CPSTRUCT: return "FC_CPSTRUCT";
+    case RPC_FC_CVSTRUCT: return "FC_CVSTRUCT";
+    case RPC_FC_BOGUS_STRUCT: return "FC_BOGUS_STRUCT";
     default:
         error("string_of_type: unknown type 0x%02x\n", type);
         return NULL;
@@ -109,6 +118,49 @@ static int is_struct(unsigned char type)
     }
 }
 
+static int is_union(unsigned char type)
+{
+    switch (type)
+    {
+    case RPC_FC_ENCAPSULATED_UNION:
+    case RPC_FC_NON_ENCAPSULATED_UNION:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+static int is_embedded_complex(const type_t *type)
+{
+    return is_struct(type->type) || is_union(type->type);
+}
+
+static long field_offset(const type_t *strct, const char *name, var_t **pfield)
+{
+    long offset = 0;
+    var_list_t *fields = strct->fields;
+    var_t *f;
+
+    if (fields) LIST_FOR_EACH_ENTRY(f, fields, var_t, entry)
+    {
+        unsigned int align = 0;
+
+        if (f->name != NULL && strcmp(name, f->name) == 0)
+        {
+            if (pfield) *pfield = f;
+            return offset;
+        }
+        else
+        {
+            /* FIXME: handle possible padding */
+            offset += type_memsize(f->type, f->array, &align);
+        }
+    }
+
+    if (pfield) *pfield = NULL;
+    return -1;
+}
+
 static int compare_expr(const expr_t *a, const expr_t *b)
 {
     int ret;
@@ -612,6 +664,26 @@ static size_t fields_memsize(const var_list_t *fields, unsigned int *align)
     return size;
 }
 
+static size_t union_memsize(const var_list_t *fields, unsigned int *pmaxa)
+{
+    size_t size, maxs = 0;
+    unsigned int align = *pmaxa;
+    const var_t *v;
+
+    if (fields) LIST_FOR_EACH_ENTRY( v, fields, const var_t, entry )
+    {
+        /* we could have an empty default field with NULL type */
+        if (v->type)
+        {
+            size = type_memsize(v->type, v->array, &align);
+            if (maxs < size) maxs = size;
+            if (*pmaxa < align) *pmaxa = align;
+        }
+    }
+
+    return size;
+}
+
 static size_t get_array_size( const array_dims_t *array )
 {
     size_t size = 1;
@@ -672,9 +744,11 @@ static size_t type_memsize(const type_t *t, const array_dims_t *array, unsigned
     case RPC_FC_CSTRUCT:
     case RPC_FC_PSTRUCT:
     case RPC_FC_BOGUS_STRUCT:
+        size = fields_memsize(t->fields, align);
+        break;
     case RPC_FC_ENCAPSULATED_UNION:
     case RPC_FC_NON_ENCAPSULATED_UNION:
-        size = fields_memsize(t->fields, align);
+        size = union_memsize(t->fields, align);
         break;
     default:
         error("type_memsize: Unknown type %d\n", t->type);
@@ -1167,6 +1241,20 @@ static void write_struct_members(FILE *file, const type_t *type, unsigned int *t
             print_file( file, 2, "0x8,\t/* FC_LONG */\n" );
             *typestring_offset += 1;
         }
+        else if (is_embedded_complex(field->type))
+        {
+            size_t absoff = (field->corrdesc
+                             ? field->corrdesc
+                             : field->type->typestring_offset);
+            short reloff = absoff - (*typestring_offset + 2);
+
+            print_file(file, 2, "0x4c,\t/* FC_EMBEDDED_COMPLEX */\n");
+            /* FIXME: actually compute necessary padding */
+            print_file(file, 2, "0x0,\t/* FIXME: padding */\n");
+            print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%lu) */\n",
+                       reloff, reloff, absoff);
+            *typestring_offset += 4;
+        }
         else if (!write_base_type( file, field->type, typestring_offset ))
             error("Unsupported member type 0x%x\n", rtype);
     }
@@ -1327,6 +1415,32 @@ static size_t write_struct_tfs(FILE *file, type_t *type,
         *typestring_offset += 1;
 
         return start_offset;
+
+    case RPC_FC_BOGUS_STRUCT:
+        total_size = type_memsize(type, NULL, &align);
+        if (total_size > USHRT_MAX)
+            error("structure size for %s exceeds %d bytes by %d bytes\n",
+                  name, USHRT_MAX, total_size - USHRT_MAX);
+
+        write_embedded_types(file, type, typestring_offset);
+
+        start_offset = *typestring_offset;
+        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);
+        print_file(file, 2, "NdrFcShort(0x%x),\t/* %d */\n", total_size, total_size);
+
+        /* do conformant array stuff */
+        print_file(file, 2, "NdrFcShort(0x0),\t/* FIXME: conformant array stuff */\n");
+
+        /* do pointer stuff here */
+        print_file(file, 2, "NdrFcShort(0x0),\t/* FIXME: pointer stuff */\n");
+
+        *typestring_offset += 8;
+        write_struct_members(file, type, typestring_offset);
+
+        return start_offset;
+
     default:
         error("write_struct_tfs: Unimplemented for type 0x%x\n", type->type);
         return *typestring_offset;
@@ -1366,12 +1480,91 @@ static size_t write_pointer_only_tfs(FILE *file, const attr_list_t *attrs, int p
     return start_offset;
 }
 
-static size_t write_union_tfs(FILE *file, const attr_list_t *attrs,
-                              const type_t *type, const char *name,
-                              unsigned int *typeformat_offset)
+static void write_branch_type(FILE *file, const type_t *t, size_t *tfsoff)
 {
-    error("write_union_tfs: Unimplemented\n");
-    return *typeformat_offset;
+    if (is_base_type(t->type))
+    {
+        print_file(file, 2, "NdrFcShort(0x80%02x),\t/* Simple arm type: %s */\n",
+                   t->type, string_of_type(t->type));
+    }
+    else if (t->typestring_offset)
+    {
+        short reloff = t->typestring_offset - (*tfsoff + 2);
+        print_file(file, 2, "NdrFcShort(0x%x),\t/* Offset= %d (%d) */\n",
+                   reloff, reloff, t->typestring_offset);
+    }
+    else
+        error("write_branch_type: type unimplemented (0x%x)\n", t->type);
+
+    *tfsoff += 2;
+}
+
+static size_t write_union_tfs(FILE *file, type_t *type, size_t *tfsoff)
+{
+    size_t align = 0;
+    size_t start_offset;
+    size_t size = type_memsize(type, NULL, &align);
+    var_list_t *fields = type->fields;
+    size_t nbranch = 0;
+    type_t *deftype = NULL;
+    short nodeftype = 0xffff;
+    var_t *f;
+
+    if (fields) LIST_FOR_EACH_ENTRY(f, fields, var_t, entry)
+    {
+        expr_list_t *cases = get_attrp(f->attrs, ATTR_CASE);
+        if (cases)
+            nbranch += list_count(cases);
+    }
+
+    start_offset = *tfsoff;
+    type->typestring_offset = start_offset;
+    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);
+    *tfsoff += 4;
+
+    if (fields) LIST_FOR_EACH_ENTRY(f, fields, var_t, entry)
+    {
+        type_t *ft = f->type;
+        expr_list_t *cases = get_attrp(f->attrs, ATTR_CASE);
+        int deflt = is_attr(f->attrs, ATTR_DEFAULT);
+        expr_t *c;
+
+        if (cases == NULL && !deflt)
+            error("union field %s with neither case nor default attribute\n", f->name);
+
+        if (cases) LIST_FOR_EACH_ENTRY(c, cases, expr_t, entry)
+        {
+            /* MIDL doesn't check for duplicate cases, even though that seems
+               like a reasonable thing to do, it just dumps them to the TFS
+               like we're going to do here.  */
+            print_file(file, 2, "NdrFcLong(0x%x),\t/* %d */\n", c->cval, c->cval);
+            *tfsoff += 4;
+            write_branch_type(file, ft, tfsoff);
+        }
+
+        /* MIDL allows multiple default branches, even though that seems
+           illogical, it just chooses the last one, which is what we will
+           do.  */
+        if (deflt)
+        {
+            deftype = ft;
+            nodeftype = 0;
+        }
+    }
+
+    if (deftype)
+    {
+        write_branch_type(file, deftype, tfsoff);
+    }
+    else
+    {
+        print_file(file, 2, "NdrFcShort(0x%x),\n", nodeftype);
+        *tfsoff += 2;
+    }
+
+    return start_offset;
 }
 
 static size_t write_ip_tfs(FILE *file, const func_t *func, const type_t *type, const var_t *var,
@@ -1481,7 +1674,7 @@ static size_t write_typeformatstring_var(FILE *file, int indent, const func_t *f
             return write_struct_tfs(file, type, var->name, typeformat_offset);
         case RPC_FC_ENCAPSULATED_UNION:
         case RPC_FC_NON_ENCAPSULATED_UNION:
-            return write_union_tfs(file, var->attrs, type, var->name, typeformat_offset);
+            return write_union_tfs(file, type, typeformat_offset);
         case RPC_FC_IGNORE:
         case RPC_FC_BIND_PRIMITIVE:
             /* nothing to do */
@@ -1550,6 +1743,61 @@ static void clear_tfsoff(type_t *type)
     }
 }
 
+static void write_embedded_types(FILE *file, const type_t *type, size_t *tfsoff)
+{
+    var_list_t *fields = type->fields;
+    size_t offset = 0;
+    var_t *f;
+
+    if (fields) LIST_FOR_EACH_ENTRY(f, fields, var_t, entry)
+    {
+        unsigned int align = 0;
+        type_t *ft = f->type;
+        size_t corroff;
+
+        if (ft->type == RPC_FC_NON_ENCAPSULATED_UNION)
+        {
+            expr_t *swexp = get_attrp(f->attrs, ATTR_SWITCHIS);
+            const char *swname;
+            var_t *swvar;
+            unsigned char corrdesc, op = 0;
+            short creloff, ureloff;
+
+            if (swexp == NULL)
+                error("union %s needs a switch_is attribute\n", f->name);
+            if (swexp->type != EXPR_IDENTIFIER)
+                error("%s: only identifiers are supported for switch_is at this time\n",
+                      f->name);
+
+            if (ft->typestring_offset == 0)
+                write_union_tfs(file, ft, tfsoff);
+
+            swname = swexp->u.sval;
+            corroff = field_offset(type, swname, &swvar);
+            corrdesc = swvar->type->type;
+            creloff = corroff - offset;
+
+            f->corrdesc = *tfsoff;
+            ureloff = ft->typestring_offset - (f->corrdesc + 6);
+            print_file(file, 0, "/* %d */\n", f->corrdesc);
+            print_file(file, 2, "0x%x,\t/* %s */\n", ft->type, string_of_type(ft->type));
+            print_file(file, 2, "0x8,\t/* FIXME: support other switch types */\n");
+            print_file(file, 2, "0x%x,\t/* Corr desc: %s */\n",
+                       corrdesc, string_of_type(corrdesc & 0xf));
+            print_file(file, 2, "0x%x,\n", op);
+            print_file(file, 2, "NdrFcShort(0x%hx),\t/* %hd */\n", creloff, creloff);
+            print_file(file, 2, "NdrFcShort(0x%hx),\t/* Offset= %hd (%lu) */\n",
+                       ureloff, ureloff, ft->typestring_offset);
+            *tfsoff += 8;
+        }
+        else if (!is_base_type(ft->type))
+            error("write_embedded_types: unknown type (0x%x)\n", ft->type);
+
+        /* FIXME: this doesn't take alignment/padding into account */
+        offset += type_memsize(ft, NULL, &align);
+    }
+}
+
 static void clear_all_tfsoffs(const ifref_list_t *ifaces)
 {
     const ifref_t * iface;
diff --git a/tools/widl/widltypes.h b/tools/widl/widltypes.h
index 18a976a..946581a 100644
--- a/tools/widl/widltypes.h
+++ b/tools/widl/widltypes.h
@@ -220,6 +220,7 @@ struct _var_t {
   var_list_t *args;  /* for function pointers */
   attr_list_t *attrs;
   expr_t *eval;
+  size_t corrdesc;  /* offset to correlation descriptor (e.g., for unions) */
 
   /* parser-internal */
   struct list entry;



More information about the wine-patches mailing list