Bug 1447849 - Eliminate the anonymous union from XPTTypeDescriptor. r=njn draft
authorAndrew McCreight <continuation@gmail.com>
Wed, 21 Mar 2018 16:35:55 -0700
changeset 770947 c44c4aa3c4e2a9a7f503bddfc947e83d28e17daa
parent 769595 c8dbb4ed05f38f40ef3607a6e36545bd95b8a287
push id103543
push userbmo:continuation@gmail.com
push dateThu, 22 Mar 2018 05:20:13 +0000
reviewersnjn
bugs1447849, 1438688
milestone61.0a1
Bug 1447849 - Eliminate the anonymous union from XPTTypeDescriptor. r=njn For bug 1438688, I need to statically initialize all of the data types in xpt_struct.h. It turns out that the approach I was using for unions is C99 and not C++. While for some reason Clang and GCC are okay with that, MSVC is not. One of the unions is a gnarly anonymous union in XPTTypeDescriptor. However, every arm of this union is either 1 or 2 uint8_t, so I think it is reasonable to eliminate the union and replace it with two fields, mData1 and mData2. I've fixed up the places that read this data to use accessor methods with nice asserts about the tag on the variant, so if anything I think that part is nicer. The code in xpt_struct.cpp that writes to this data is maybe less nice, but I'm deleting it in bug 1438688 so I think that's okay. We also lose some detail in the comments about the relationship between what is stored in memory compared to what is stored on disk, but I think that's okay, too. MozReview-Commit-ID: DGi19f8HnMi
xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
xpcom/typelib/xpt/xpt_struct.cpp
xpcom/typelib/xpt/xpt_struct.h
--- a/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
@@ -296,25 +296,24 @@ xptiInterfaceEntry::GetInterfaceIndexFor
     {
         NS_ERROR("bad param");
         return NS_ERROR_INVALID_ARG;
     }
 
     const XPTTypeDescriptor *td = &param->mType;
 
     while (td->Tag() == TD_ARRAY) {
-        td = &mDescriptor->mAdditionalTypes[td->u.mArray.mAdditionalType];
+        td = td->ArrayElementType(mDescriptor);
     }
 
     if (td->Tag() != TD_INTERFACE_TYPE) {
         NS_ERROR("not an interface");
         return NS_ERROR_INVALID_ARG;
     }
-
-    *interfaceIndex = (td->u.mIface.mIfaceHi8 << 8) | td->u.mIface.mIfaceLo8;
+    *interfaceIndex = td->InterfaceIndex();
     return NS_OK;
 }
 
 nsresult
 xptiInterfaceEntry::GetEntryForParam(uint16_t methodIndex,
                                      const nsXPTParamInfo * param,
                                      xptiInterfaceEntry** entry)
 {
@@ -431,25 +430,23 @@ xptiInterfaceEntry::GetIIDForParamNoAllo
 nsresult
 xptiInterfaceEntry::GetTypeInArray(const nsXPTParamInfo* param,
                                   uint16_t dimension,
                                   const XPTTypeDescriptor** type)
 {
     NS_ASSERTION(IsFullyResolved(), "bad state");
 
     const XPTTypeDescriptor *td = &param->mType;
-    const XPTTypeDescriptor *additional_types =
-                mDescriptor->mAdditionalTypes;
 
     for (uint16_t i = 0; i < dimension; i++) {
         if (td->Tag() != TD_ARRAY) {
             NS_ERROR("bad dimension");
             return NS_ERROR_INVALID_ARG;
         }
-        td = &additional_types[td->u.mArray.mAdditionalType];
+        td = td->ArrayElementType(mDescriptor);
     }
 
     *type = td;
     return NS_OK;
 }
 
 nsresult
 xptiInterfaceEntry::GetTypeForParam(uint16_t methodIndex,
@@ -513,21 +510,19 @@ xptiInterfaceEntry::GetSizeIsArgNumberFo
             return rv;
     }
     else
         td = &param->mType;
 
     // verify that this is a type that has size_is
     switch (td->Tag()) {
       case TD_ARRAY:
-        *argnum = td->u.mArray.mArgNum;
-        break;
       case TD_PSTRING_SIZE_IS:
       case TD_PWSTRING_SIZE_IS:
-        *argnum = td->u.mPStringIs.mArgNum;
+        *argnum = td->ArgNum();
         break;
       default:
         NS_ERROR("not a size_is");
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
@@ -549,25 +544,25 @@ xptiInterfaceEntry::GetInterfaceIsArgNum
     {
         NS_ERROR("bad index");
         return NS_ERROR_INVALID_ARG;
     }
 
     const XPTTypeDescriptor *td = &param->mType;
 
     while (td->Tag() == TD_ARRAY) {
-        td = &mDescriptor->mAdditionalTypes[td->u.mArray.mAdditionalType];
+        td = td->ArrayElementType(mDescriptor);
     }
 
     if (td->Tag() != TD_INTERFACE_IS_TYPE) {
         NS_ERROR("not an iid_is");
         return NS_ERROR_INVALID_ARG;
     }
 
-    *argnum = td->u.mInterfaceIs.mArgNum;
+    *argnum = td->ArgNum();
     return NS_OK;
 }
 
 nsresult
 xptiInterfaceEntry::IsIID(const nsIID * iid, bool *_retval)
 {
     // It is not necessary to Resolve because this info is read from manifest.
     *_retval = mIID.Equals(*iid);
--- a/xpcom/typelib/xpt/xpt_struct.cpp
+++ b/xpcom/typelib/xpt/xpt_struct.cpp
@@ -406,44 +406,44 @@ DoTypeDescriptor(XPTArena *arena, NotNul
         return false;
     }
 
     switch (td->Tag()) {
       case TD_INTERFACE_TYPE:
         uint16_t iface;
         if (!XPT_Do16(cursor, &iface))
             return false;
-        td->u.mIface.mIfaceHi8 = (iface >> 8) & 0xff;
-        td->u.mIface.mIfaceLo8 = iface & 0xff;
+        td->mData1 = (iface >> 8) & 0xff;
+        td->mData2 = iface & 0xff;
         break;
       case TD_INTERFACE_IS_TYPE:
-        if (!XPT_Do8(cursor, &td->u.mInterfaceIs.mArgNum))
+        if (!XPT_Do8(cursor, &td->mData1))
             return false;
         break;
       case TD_ARRAY: {
         // argnum2 appears in the on-disk format but it isn't used.
         uint8_t argnum2 = 0;
-        if (!XPT_Do8(cursor, &td->u.mArray.mArgNum) ||
+        if (!XPT_Do8(cursor, &td->mData1) ||
             !XPT_Do8(cursor, &argnum2))
             return false;
 
         XPTTypeDescriptor elementTypeDescriptor;
         if (!DoTypeDescriptor(arena, cursor, &elementTypeDescriptor, id))
             return false;
         if (!InterfaceDescriptorAddType(arena, id, &elementTypeDescriptor))
             return false;
-        td->u.mArray.mAdditionalType = id->mNumAdditionalTypes - 1;
+        td->mData2 = id->mNumAdditionalTypes - 1;
 
         break;
       }
       case TD_PSTRING_SIZE_IS:
       case TD_PWSTRING_SIZE_IS: {
         // argnum2 appears in the on-disk format but it isn't used.
         uint8_t argnum2 = 0;
-        if (!XPT_Do8(cursor, &td->u.mPStringIs.mArgNum) ||
+        if (!XPT_Do8(cursor, &td->mData1) ||
             !XPT_Do8(cursor, &argnum2))
             return false;
         break;
       }
       default:
         /* nothing special */
         break;
     }
--- a/xpcom/typelib/xpt/xpt_struct.h
+++ b/xpcom/typelib/xpt/xpt_struct.h
@@ -9,16 +9,17 @@
  * http://www.mozilla.org/scriptable/typelib_file.html
  */
 
 #ifndef xpt_struct_h
 #define xpt_struct_h
 
 #include "nsID.h"
 #include <stdint.h>
+#include "mozilla/Assertions.h"
 
 /*
  * Originally, I was going to have structures that exactly matched the on-disk
  * representation, but that proved difficult: different compilers can pack
  * their structs differently, and that makes overlaying them atop a
  * read-from-disk byte buffer troublesome.  So now I just have some structures
  * that are used in memory, and we're going to write a nice XDR library to
  * write them to disk and stuff.  It is pure joy. -- shaver
@@ -189,49 +190,46 @@ enum XPTTypeDescriptorTags {
   TD_JSVAL             = 26
 };
 
 struct XPTTypeDescriptor {
   uint8_t Tag() const {
     return mPrefix.TagPart();
   }
 
+  uint8_t ArgNum() const {
+    MOZ_ASSERT(Tag() == TD_INTERFACE_IS_TYPE ||
+               Tag() == TD_PSTRING_SIZE_IS ||
+               Tag() == TD_PWSTRING_SIZE_IS ||
+               Tag() == TD_ARRAY);
+    return mData1;
+  }
+
+  const XPTTypeDescriptor* ArrayElementType(const XPTInterfaceDescriptor* aDescriptor) const {
+    MOZ_ASSERT(Tag() == TD_ARRAY);
+    return &aDescriptor->mAdditionalTypes[mData2];
+  }
+
+  // We store the 16-bit iface value as two 8-bit values in order to
+  // avoid 16-bit alignment requirements for XPTTypeDescriptor, which
+  // reduces its size and also the size of XPTParamDescriptor.
+  uint16_t InterfaceIndex() const {
+    MOZ_ASSERT(Tag() == TD_INTERFACE_TYPE);
+    return (mData1 << 8) | mData2;
+  }
+
   XPTTypeDescriptorPrefix mPrefix;
 
-  // The memory layout here doesn't exactly match (for the appropriate types)
-  // the on-disk format. This is to save memory.
-  union {
-    // Used for TD_INTERFACE_IS_TYPE.
-    struct {
-      uint8_t mArgNum;
-    } mInterfaceIs;
-
-    // Used for TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS.
-    struct {
-      uint8_t mArgNum;
-      //uint8_t mArgNum2;         // Present on disk, omitted here.
-    } mPStringIs;
-
-    // Used for TD_ARRAY.
-    struct {
-      uint8_t mArgNum;
-      //uint8_t mArgNum2;         // Present on disk, omitted here.
-      uint8_t mAdditionalType;    // uint16_t on disk, uint8_t here;
-                                  // in practice it never exceeds 20.
-    } mArray;
-
-    // Used for TD_INTERFACE_TYPE.
-    struct {
-      // We store the 16-bit iface value as two 8-bit values in order to
-      // avoid 16-bit alignment requirements for XPTTypeDescriptor, which
-      // reduces its size and also the size of XPTParamDescriptor.
-      uint8_t mIfaceHi8;
-      uint8_t mIfaceLo8;
-    } mIface;
-  } u;
+  // The data for the different variants is stored in these two data fields.
+  // These should only be accessed via the getter methods above, which will
+  // assert if the tag is invalid. The memory layout here doesn't exactly match
+  // the on-disk format. This is to save memory. Some fields for some cases are
+  // smaller than they are on disk or omitted entirely.
+  uint8_t mData1;
+  uint8_t mData2;
 };
 
 /*
  * A ConstDescriptor is a variable-size record that records the name and
  * value of a scoped interface constant. This is allowed only for a subset
  * of types.
  *
  * The type (and thus the size) of the value record is determined by the