From 77228b52e670fd91ee7ae9c030632f30bd79cd22 Mon Sep 17 00:00:00 2001
From: Rob Shearman <robertshearman@gmail.com>
Date: Tue, 6 Jan 2009 23:20:59 +0000
Subject: [PATCH] widl: Determine the type of an array entirely at code
 generation time instead of at parse time.

Previously, this was done partially (for fixed array types only).
---
 tools/widl/header.c   | 18 ++++-----
 tools/widl/parser.y   | 80 ++++++++++++++++++------------------
 tools/widl/typegen.c  | 94 ++++++++++++++++++++++++++++++++-----------
 tools/widl/typelib.c  |  5 +++
 tools/widl/typetree.c | 14 +++++++
 tools/widl/typetree.h |  2 +
 6 files changed, 138 insertions(+), 75 deletions(-)

diff --git a/tools/widl/header.c b/tools/widl/header.c
index fc09e63feb0..a3d225078b7 100644
--- a/tools/widl/header.c
+++ b/tools/widl/header.c
@@ -116,9 +116,7 @@ int is_void(const type_t *t)
 
 int is_conformant_array(const type_t *t)
 {
-    return t->type == RPC_FC_CARRAY
-        || t->type == RPC_FC_CVARRAY
-        || (t->type == RPC_FC_BOGUS_ARRAY && type_array_has_conformance(t));
+    return is_array(t) && type_array_has_conformance(t);
 }
 
 void write_guid(FILE *f, const char *guid_prefix, const char *name, const UUID *uuid)
@@ -186,7 +184,7 @@ static void write_enums(FILE *h, var_list_t *enums)
 int needs_space_after(type_t *t)
 {
   return (type_is_alias(t) ||
-          (!is_ptr(t) && (!is_conformant_array(t) || t->declarray)));
+          (!is_ptr(t) && (!is_conformant_array(t) || t->declarray || (is_array(t) && t->name))));
 }
 
 void write_type_left(FILE *h, type_t *t, int declonly)
@@ -202,7 +200,11 @@ void write_type_left(FILE *h, type_t *t, int declonly)
   else {
     if (t->sign > 0) fprintf(h, "signed ");
     else if (t->sign < 0) fprintf(h, "unsigned ");
-    switch (t->type) {
+
+    if (is_array(t) && !t->name) {
+      write_type_left(h, type_array_get_element(t), declonly);
+      fprintf(h, "%s*", needs_space_after(type_array_get_element(t)) ? " " : "");
+    } else switch (t->type) {
       case RPC_FC_ENUM16:
       case RPC_FC_ENUM32:
         if (!declonly && t->defined && !t->written) {
@@ -257,12 +259,6 @@ void write_type_left(FILE *h, type_t *t, int declonly)
         fprintf(h, "%s*", needs_space_after(type_pointer_get_ref(t)) ? " " : "");
         if (is_attr(t->attrs, ATTR_CONST)) fprintf(h, "const ");
         break;
-      case RPC_FC_CARRAY:
-      case RPC_FC_CVARRAY:
-      case RPC_FC_BOGUS_ARRAY:
-        write_type_left(h, type_array_get_element(t), declonly);
-        fprintf(h, "%s*", needs_space_after(type_array_get_element(t)) ? " " : "");
-        break;
       default:
         fprintf(h, "%s", t->name);
     }
diff --git a/tools/widl/parser.y b/tools/widl/parser.y
index 3593043df52..084b5b3d2a0 100644
--- a/tools/widl/parser.y
+++ b/tools/widl/parser.y
@@ -1404,9 +1404,9 @@ static void set_type(var_t *v, decl_spec_t *decl_spec, const declarator_t *decl,
 {
   expr_list_t *sizes = get_attrp(v->attrs, ATTR_SIZEIS);
   expr_list_t *lengs = get_attrp(v->attrs, ATTR_LENGTHIS);
-  int sizeless, has_varconf;
+  int sizeless;
   expr_t *dim;
-  type_t *atype, **ptype;
+  type_t **ptype;
   array_dims_t *arr = decl ? decl->array : NULL;
   type_t *func_type = decl ? decl->func_type : NULL;
   type_t *type = decl_spec->type;
@@ -1469,6 +1469,7 @@ static void set_type(var_t *v, decl_spec_t *decl_spec, const declarator_t *decl,
       error_loc("'%s': [v1_enum] attribute applied to non-enum type\n", v->name);
   }
 
+  ptype = &v->type;
   sizeless = FALSE;
   if (arr) LIST_FOR_EACH_ENTRY_REV(dim, arr, expr_t, entry)
   {
@@ -1477,35 +1478,46 @@ static void set_type(var_t *v, decl_spec_t *decl_spec, const declarator_t *decl,
 
     if (dim->is_const)
     {
-      v->type = make_type(RPC_FC_LGFARRAY, v->type);
+      if (dim->cval <= 0)
+        error_loc("%s: array dimension must be positive\n", v->name);
+
+      /* FIXME: should use a type_memsize that allows us to pass in a pointer size */
+      if (0)
+      {
+        unsigned int align = 0;
+        size_t size = type_memsize(v->type, &align);
+
+        if (0xffffffffuL / size < (unsigned long) dim->cval)
+          error_loc("%s: total array size is too large\n", v->name);
+      }
     }
     else
-    {
       sizeless = TRUE;
-      v->type = make_type(RPC_FC_CARRAY, v->type);
-    }
 
-    v->type->declarray = TRUE;
-    v->type->details.array.dim = dim->cval;
+    *ptype = type_new_array(NULL, *ptype, TRUE,
+                            dim->is_const ? dim->cval : 0,
+                            dim->is_const ? NULL : dim, NULL);
   }
 
   ptype = &v->type;
-  has_varconf = FALSE;
   if (sizes) LIST_FOR_EACH_ENTRY(dim, sizes, expr_t, entry)
   {
     if (dim->type != EXPR_VOID)
     {
-      has_varconf = TRUE;
-      atype = *ptype = duptype(*ptype, 0);
-
-      if (atype->type == RPC_FC_SMFARRAY || atype->type == RPC_FC_LGFARRAY)
-        error_loc("%s: cannot specify size_is for a fixed sized array\n", v->name);
-
-      if (atype->type != RPC_FC_CARRAY && !is_ptr(atype))
+      if (is_array(*ptype))
+      {
+        if (type_array_get_conformance(*ptype)->is_const)
+          error_loc("%s: cannot specify size_is for a fixed sized array\n", v->name);
+        else
+          *ptype = type_new_array((*ptype)->name,
+                                  type_array_get_element(*ptype), TRUE,
+                                  0, dim, NULL);
+      }
+      else if (is_ptr(*ptype))
+        *ptype = type_new_array((*ptype)->name, type_pointer_get_ref(*ptype), FALSE,
+                                0, dim, NULL);
+      else
         error_loc("%s: size_is attribute applied to illegal type\n", v->name);
-
-      atype->type = RPC_FC_CARRAY;
-      atype->details.array.size_is = dim;
     }
 
     ptype = &(*ptype)->ref;
@@ -1518,19 +1530,17 @@ static void set_type(var_t *v, decl_spec_t *decl_spec, const declarator_t *decl,
   {
     if (dim->type != EXPR_VOID)
     {
-      has_varconf = TRUE;
-      atype = *ptype = duptype(*ptype, 0);
-
-      if (atype->type == RPC_FC_SMFARRAY)
-        atype->type = RPC_FC_SMVARRAY;
-      else if (atype->type == RPC_FC_LGFARRAY)
-        atype->type = RPC_FC_LGVARRAY;
-      else if (atype->type == RPC_FC_CARRAY)
-        atype->type = RPC_FC_CVARRAY;
+      if (is_array(*ptype))
+      {
+        *ptype = type_new_array((*ptype)->name,
+                                type_array_get_element(*ptype),
+                                (*ptype)->declarray,
+                                type_array_get_dim(*ptype),
+                                type_array_get_conformance(*ptype),
+                                dim);
+      }
       else
         error_loc("%s: length_is attribute applied to illegal type\n", v->name);
-
-      atype->details.array.length_is = dim;
     }
 
     ptype = &(*ptype)->ref;
@@ -1538,16 +1548,6 @@ static void set_type(var_t *v, decl_spec_t *decl_spec, const declarator_t *decl,
       error_loc("%s: too many expressions in length_is attribute\n", v->name);
   }
 
-  if (has_varconf && !last_array(v->type))
-  {
-    ptype = &v->type;
-    for (ptype = &v->type; is_array(*ptype); ptype = &(*ptype)->ref)
-    {
-      *ptype = duptype(*ptype, 0);
-      (*ptype)->type = RPC_FC_BOGUS_ARRAY;
-    }
-  }
-
   /* 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 */
diff --git a/tools/widl/typegen.c b/tools/widl/typegen.c
index 2436f40b8c1..dfcb31e13e7 100644
--- a/tools/widl/typegen.c
+++ b/tools/widl/typegen.c
@@ -120,6 +120,15 @@ const char *string_of_type(unsigned char type)
     }
 }
 
+static unsigned char get_pointer_fc(const type_t *type)
+{
+    assert(is_ptr(type));
+    /* FIXME: see corresponding hack in set_type - we shouldn't be getting
+     * the pointer type from an alias, rather determining it from the
+     * position */
+    return type->type;
+}
+
 static int get_struct_type(const type_t *type)
 {
   int has_pointer = 0;
@@ -210,6 +219,10 @@ static int get_struct_type(const type_t *type)
       has_pointer = 1;
       break;
 
+    case RPC_FC_SMFARRAY:
+    case RPC_FC_LGFARRAY:
+    case RPC_FC_SMVARRAY:
+    case RPC_FC_LGVARRAY:
     case RPC_FC_CARRAY:
     case RPC_FC_CVARRAY:
     case RPC_FC_BOGUS_ARRAY:
@@ -281,37 +294,70 @@ static int get_struct_type(const type_t *type)
   return RPC_FC_STRUCT;
 }
 
-static int get_array_type(const type_t *type)
+static unsigned char get_array_type(const type_t *type)
 {
-    if (is_array(type))
+    unsigned char fc;
+    const expr_t *size_is;
+    const type_t *elem_type;
+
+    if (!is_array(type))
+        return type->type;
+
+    elem_type = type_array_get_element(type);
+    size_is = type_array_get_conformance(type);
+
+    if (!size_is)
     {
-        const type_t *rt = type_array_get_element(type);
-        if (is_user_type(rt))
-            return RPC_FC_BOGUS_ARRAY;
-        switch (get_struct_type(rt))
+        unsigned int align = 0;
+        size_t size = type_memsize(elem_type, &align);
+        if (size * type_array_get_dim(type) > 0xffffuL)
+            fc = RPC_FC_LGFARRAY;
+        else
+            fc = RPC_FC_SMFARRAY;
+    }
+    else
+        fc = RPC_FC_CARRAY;
+
+    if (type_array_has_variance(type))
+    {
+        if (fc == RPC_FC_SMFARRAY)
+            fc = RPC_FC_SMVARRAY;
+        else if (fc == RPC_FC_LGFARRAY)
+            fc = RPC_FC_LGVARRAY;
+        else if (fc == RPC_FC_CARRAY)
+            fc = RPC_FC_CVARRAY;
+    }
+
+    if (is_user_type(elem_type))
+        fc = RPC_FC_BOGUS_ARRAY;
+    else if (is_struct(elem_type->type))
+    {
+        switch (get_struct_type(elem_type))
         {
         case RPC_FC_BOGUS_STRUCT:
-        case RPC_FC_NON_ENCAPSULATED_UNION:
-        case RPC_FC_ENCAPSULATED_UNION:
-        case RPC_FC_ENUM16:
-            return RPC_FC_BOGUS_ARRAY;
-            /* FC_RP should be above, but widl overuses these, and will break things.  */
-        case RPC_FC_UP:
-        case RPC_FC_RP:
-            if (type_pointer_get_ref(rt)->type == RPC_FC_IP)
-                return RPC_FC_BOGUS_ARRAY;
+            fc = RPC_FC_BOGUS_ARRAY;
             break;
         }
-
-        if (type->type == RPC_FC_LGFARRAY || type->type == RPC_FC_LGVARRAY)
-        {
-            unsigned int align = 0;
-            size_t size = type_memsize(type, &align);
-            if (size * type_array_get_dim(type) <= 0xffff)
-                return (type->type == RPC_FC_LGFARRAY) ? RPC_FC_SMFARRAY : RPC_FC_SMVARRAY;
-        }
     }
-    return type->type;
+    else if (elem_type->type == RPC_FC_ENUM16)
+    {
+        /* is 16-bit enum - if so, wire size differs from mem size and so
+         * the array cannot be block copied, which means the array is complex */
+        fc = RPC_FC_BOGUS_ARRAY;
+    }
+    else if (is_union(elem_type->type))
+        fc = RPC_FC_BOGUS_ARRAY;
+    else if (is_ptr(elem_type))
+    {
+        /* ref pointers cannot just be block copied. unique pointers to
+         * interfaces need special treatment. either case means the array is
+         * complex */
+        if (get_pointer_fc(elem_type) == RPC_FC_RP ||
+            type_pointer_get_ref(elem_type)->type == RPC_FC_IP)
+            fc = RPC_FC_BOGUS_ARRAY;
+    }
+
+    return fc;
 }
 
 int is_struct(unsigned char type)
diff --git a/tools/widl/typelib.c b/tools/widl/typelib.c
index d8ccd0ed151..55a2e22c4fa 100644
--- a/tools/widl/typelib.c
+++ b/tools/widl/typelib.c
@@ -190,8 +190,13 @@ unsigned short get_type_vt(type_t *t)
   case RPC_FC_UP:
   case RPC_FC_OP:
   case RPC_FC_FP:
+  case RPC_FC_SMFARRAY:
+  case RPC_FC_LGFARRAY:
+  case RPC_FC_SMVARRAY:
+  case RPC_FC_LGVARRAY:
   case RPC_FC_CARRAY:
   case RPC_FC_CVARRAY:
+  case RPC_FC_BOGUS_ARRAY:
     if(t->ref)
     {
       if (match(t->ref->name, "SAFEARRAY"))
diff --git a/tools/widl/typetree.c b/tools/widl/typetree.c
index d90fef1fa7e..5591ce6de21 100644
--- a/tools/widl/typetree.c
+++ b/tools/widl/typetree.c
@@ -78,6 +78,20 @@ type_t *type_new_module(char *name)
     return type;
 }
 
+type_t *type_new_array(const char *name, type_t *element, int declarray,
+                       unsigned long dim, expr_t *size_is, expr_t *length_is)
+{
+    type_t *t = make_type(RPC_FC_LGFARRAY, element);
+    if (name) t->name = xstrdup(name);
+    t->declarray = declarray;
+    t->details.array.length_is = length_is;
+    if (size_is)
+        t->details.array.size_is = size_is;
+    else
+        t->details.array.dim = dim;
+    return t;
+}
+
 static int compute_method_indexes(type_t *iface)
 {
     int idx;
diff --git a/tools/widl/typetree.h b/tools/widl/typetree.h
index 1538541197e..8efc62bd996 100644
--- a/tools/widl/typetree.h
+++ b/tools/widl/typetree.h
@@ -28,6 +28,8 @@ type_t *type_new_function(var_list_t *args);
 type_t *type_new_pointer(type_t *ref, attr_list_t *attrs);
 type_t *type_new_alias(type_t *t, const char *name);
 type_t *type_new_module(char *name);
+type_t *type_new_array(const char *name, type_t *element, int declarray,
+                       unsigned long dim, expr_t *size_is, expr_t *length_is);
 void type_interface_define(type_t *iface, type_t *inherit, statement_list_t *stmts);
 void type_dispinterface_define(type_t *iface, var_list_t *props, func_list_t *methods);
 void type_dispinterface_define_from_iface(type_t *dispiface, type_t *iface);
-- 
GitLab