Bug 1444991 - Part 3: Generate DOMObject info for xptinfo, r=mccr8
authorNika Layzell <nika@thelayzells.com>
Fri, 06 Apr 2018 18:28:42 -0400
changeset 467735 ae4da56bcf71aeb41efdd4cd1a7a76d59cfcf1cc
parent 467734 c59b5be67ba225f8864219c8b8d589446d1d8459
child 467736 667b0dbdc19019818ee3c42654ff18b2acaa36f6
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1444991
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1444991 - Part 3: Generate DOMObject info for xptinfo, r=mccr8 Unlike the other lists in xptinfo, this list contains relocations. Each DOMObject has 3 functions generated for it, `Wrap`, `Unwrap` and `Cleanup`, which perform the necessary actions. These are stored as function pointers. Wrap gets the DOMObject wrapper using the DOM binding code, Unwrap gets the underlying C++ object, and addrefs it (as XPCOM methods return native types via getter_AddRefs), and Cleanup releases a reference to the underlying C++ object, for when the unwrapped object is used as a temporary during a call. To generate the code, we need to have the declaration of the native C++ type in scope, so we also emit #include-s for the headerFiles.
xpcom/reflect/xptinfo/xptcodegen.py
xpcom/reflect/xptinfo/xptinfo.h
--- a/xpcom/reflect/xptinfo/xptcodegen.py
+++ b/xpcom/reflect/xptinfo/xptcodegen.py
@@ -86,16 +86,25 @@ nsXPTMethodInfo = mkstruct(
     "mOptArgc",
     "mContext",
     "mHasRetval",
 )
 
 ##########################################################
 # Ensure these fields are in the same order as xptinfo.h #
 ##########################################################
+nsXPTDOMObjectInfo = mkstruct(
+    "mUnwrap",
+    "mWrap",
+    "mCleanup",
+)
+
+##########################################################
+# Ensure these fields are in the same order as xptinfo.h #
+##########################################################
 ConstInfo = mkstruct(
     "mName",
     "mSigned",
     "mValue",
 )
 
 
 # Helper functions for dealing with IIDs.
@@ -168,21 +177,42 @@ def link_to_cpp(interfaces, fd):
     types = []
     type_cache = {}
     ifaces = []
     params = []
     param_cache = {}
     methods = []
     consts = []
     prophooks = []
+    domobjects = []
+    domobject_cache = {}
     strings = OrderedDict()
 
     def lower_uuid(uuid):
         return "{0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}}" % split_iid(uuid)
 
+    def lower_domobject(do):
+        assert do['tag'] == 'TD_DOMOBJECT'
+
+        idx = domobject_cache.get(do['name'])
+        if idx is None:
+            idx = domobject_cache[do['name']] = len(domobjects)
+
+            includes.add(do['headerFile'])
+            domobjects.append(nsXPTDOMObjectInfo(
+                "%d = %s" % (idx, do['name']),
+                # These methods are defined at the top of the generated file.
+                mUnwrap="UnwrapDOMObject<mozilla::dom::prototypes::id::%s, %s>" %
+                    (do['name'], do['native']),
+                mWrap="WrapDOMObject<%s>" % do['native'],
+                mCleanup="CleanupDOMObject<%s>" % do['native'],
+            ))
+
+        return idx
+
     def lower_string(s):
         if s in strings:
             # We've already seen this string.
             return strings[s]
         elif len(strings):
             # Get the last string we inserted (should be O(1) on OrderedDict).
             last_s = next(reversed(strings))
             strings[s] = strings[last_s] + len(last_s) + 1
@@ -190,17 +220,17 @@ def link_to_cpp(interfaces, fd):
             strings[s] = 0
         return strings[s]
 
     def describe_type(type): # Create the type's documentation comment.
         tag = type['tag'][3:].lower()
         if tag == 'array':
             return '%s[size_is=%d]' % (
                 describe_type(type['element']), type['size_is'])
-        elif tag == 'interface_type':
+        elif tag == 'interface_type' or tag == 'domobject':
             return type['name']
         elif tag == 'interface_is_type':
             return 'iid_is(%d)' % type['iid_is']
         elif tag.endswith('_size_is'):
             return '%s(size_is=%d)' % (tag, type['size_is'])
         return tag
 
     def lower_type(type, in_=False, out=False, optional=False):
@@ -218,16 +248,19 @@ def link_to_cpp(interfaces, fd):
                 types.append(lower_type(type['element']))
 
         elif tag == 'TD_INTERFACE_TYPE':
             d1, d2 = splitint(interface_idx(type['name']))
 
         elif tag == 'TD_INTERFACE_IS_TYPE':
             d1 = type['iid_is']
 
+        elif tag == 'TD_DOMOBJECT':
+            d1, d2 = splitint(lower_domobject(type))
+
         elif tag.endswith('_SIZE_IS'):
             d1 = type['size_is']
 
         assert d1 < 256 and d2 < 256, "Data values too large"
         return nsXPTType(
             describe_type(type),
             mTag=tag,
             mData1=d1,
@@ -388,30 +421,54 @@ def link_to_cpp(interfaces, fd):
     # Include any bindings files which we need to include due to XPT shims.
     for include in includes:
         fd.write('#include "%s"\n' % include)
 
     # Write out our header
     fd.write("""
 #include "xptinfo.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/dom/BindingUtils.h"
+
+// These template methods are specialized to be used in the sDOMObjects table.
+template<mozilla::dom::prototypes::ID PrototypeID, typename T>
+static nsresult UnwrapDOMObject(JS::HandleValue aHandle, void** aObj)
+{
+  RefPtr<T> p;
+  nsresult rv = mozilla::dom::UnwrapObject<PrototypeID, T>(aHandle, p);
+  p.forget(aObj);
+  return rv;
+}
+
+template<typename T>
+static bool WrapDOMObject(JSContext* aCx, void* aObj, JS::MutableHandleValue aHandle)
+{
+  return mozilla::dom::GetOrCreateDOMReflector(aCx, reinterpret_cast<T*>(aObj), aHandle);
+}
+
+template<typename T>
+static void CleanupDOMObject(void* aObj)
+{
+  RefPtr<T> p = already_AddRefed<T>(reinterpret_cast<T*>(aObj));
+}
 
 namespace xpt {
 namespace detail {
 
 """)
 
     # Static data arrays
     def array(ty, name, els):
         fd.write("const %s %s[] = {%s\n};\n\n" %
             (ty, name, ','.join(indented('\n' + str(e)) for e in els)))
     array("nsXPTInterfaceInfo", "sInterfaces", ifaces)
     array("nsXPTType", "sTypes", types)
     array("nsXPTParamInfo", "sParams", params)
     array("nsXPTMethodInfo", "sMethods", methods)
+    array("nsXPTDOMObjectInfo", "sDOMObjects", domobjects)
     array("ConstInfo", "sConsts", consts)
     array("mozilla::dom::NativePropertyHooks*", "sPropHooks", prophooks)
 
     # The strings array. We write out individual characters to avoid MSVC restrictions.
     fd.write("const char sStrings[] = {\n")
     for s, off in strings.iteritems():
         fd.write("  // %d = %s\n  '%s','\\0',\n" % (off, s, "','".join(s)))
     fd.write("};\n\n")
--- a/xpcom/reflect/xptinfo/xptinfo.h
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -24,25 +24,27 @@ namespace dom {
 struct NativePropertyHooks;
 } // namespace dom
 } // namespace mozilla
 
 struct nsXPTInterfaceInfo;
 struct nsXPTType;
 struct nsXPTParamInfo;
 struct nsXPTMethodInfo;
+struct nsXPTDOMObjectInfo;
 
 // Internal helper methods.
 namespace xpt {
 namespace detail {
 
 inline const nsXPTInterfaceInfo* GetInterface(uint16_t aIndex);
 inline const nsXPTType& GetType(uint16_t aIndex);
 inline const nsXPTParamInfo& GetParam(uint16_t aIndex);
 inline const nsXPTMethodInfo& GetMethod(uint16_t aIndex);
+inline const nsXPTDOMObjectInfo& GetDOMObjectInfo(uint16_t aIndex);
 inline const char* GetString(uint32_t aIndex);
 
 extern const uint16_t sInterfacesSize;
 
 } // namespace detail
 } // namespace xpt
 
 
@@ -182,17 +184,18 @@ enum nsXPTTypeTag : uint8_t
   TD_INTERFACE_TYPE    = 18,
   TD_INTERFACE_IS_TYPE = 19,
   TD_ARRAY             = 20,
   TD_PSTRING_SIZE_IS   = 21,
   TD_PWSTRING_SIZE_IS  = 22,
   TD_UTF8STRING        = 23,
   TD_CSTRING           = 24,
   TD_ASTRING           = 25,
-  TD_JSVAL             = 26
+  TD_JSVAL             = 26,
+  TD_DOMOBJECT         = 27
 };
 
 
 /*
  * A nsXPTType is a union used to identify the type of a method argument or
  * return value. The internal data is stored as an 5-bit tag, and two 8-bit
  * integers, to keep alignment requirements low.
  *
@@ -210,23 +213,31 @@ struct nsXPTType
     return mData1;
   }
 
   const nsXPTType& ArrayElementType() const {
     MOZ_ASSERT(Tag() == TD_ARRAY);
     return xpt::detail::GetType(mData2);
   }
 
+private:
+  uint16_t Data16() const { return ((uint16_t)mData1 << 8) | mData2; }
+
+public:
   // 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.
   const nsXPTInterfaceInfo* GetInterface() const {
     MOZ_ASSERT(Tag() == TD_INTERFACE_TYPE);
-    uint16_t index = ((uint16_t)mData1 << 8) | mData2;
-    return xpt::detail::GetInterface(index);
+    return xpt::detail::GetInterface(Data16());
+  }
+
+  const nsXPTDOMObjectInfo& GetDOMObjectInfo() const {
+    MOZ_ASSERT(Tag() == TD_DOMOBJECT);
+    return xpt::detail::GetDOMObjectInfo(Data16());
   }
 
   // 'Arithmetic' here roughly means that the value is self-contained and
   // doesn't depend on anything else in memory (ie: not a pointer, not an
   // XPCOM object, not a jsval, etc).
   //
   // Supposedly this terminology comes from Harbison/Steele, but it's still
   // a rather crappy name. We'd change it if it wasn't used all over the
@@ -289,17 +300,18 @@ struct nsXPTType
     T_INTERFACE         = TD_INTERFACE_TYPE   ,
     T_INTERFACE_IS      = TD_INTERFACE_IS_TYPE,
     T_ARRAY             = TD_ARRAY            ,
     T_PSTRING_SIZE_IS   = TD_PSTRING_SIZE_IS  ,
     T_PWSTRING_SIZE_IS  = TD_PWSTRING_SIZE_IS ,
     T_UTF8STRING        = TD_UTF8STRING       ,
     T_CSTRING           = TD_CSTRING          ,
     T_ASTRING           = TD_ASTRING          ,
-    T_JSVAL             = TD_JSVAL
+    T_JSVAL             = TD_JSVAL            ,
+    T_DOMOBJECT         = TD_DOMOBJECT
   };
 
   ////////////////////////////////////////////////////////////////
   // Ensure these fields are in the same order as xptcodegen.py //
   ////////////////////////////////////////////////////////////////
 
   uint8_t mTag : 5;
 
@@ -422,16 +434,44 @@ struct nsXPTMethodInfo
   uint8_t mContext : 1;
   uint8_t mHasRetval : 1;
   // uint8_t unused : 1;
 };
 
 // The fields in nsXPTMethodInfo were carefully ordered to minimize size.
 static_assert(sizeof(nsXPTMethodInfo) == 8, "wrong size");
 
+/**
+ * Object representing the information required to wrap and unwrap DOMObjects.
+ *
+ * This object will not live in rodata as it contains relocations.
+ */
+struct nsXPTDOMObjectInfo
+{
+  nsresult Unwrap(JS::HandleValue aHandle, void** aObj) const {
+    return mUnwrap(aHandle, aObj);
+  }
+
+  bool Wrap(JSContext* aCx, void* aObj, JS::MutableHandleValue aHandle) const {
+    return mWrap(aCx, aObj, aHandle);
+  }
+
+  void Cleanup(void* aObj) const {
+    return mCleanup(aObj);
+  }
+
+  ////////////////////////////////////////////////////////////////
+  // Ensure these fields are in the same order as xptcodegen.py //
+  ////////////////////////////////////////////////////////////////
+
+  nsresult (*mUnwrap) (JS::HandleValue aHandle, void** aObj);
+  bool (*mWrap) (JSContext* aCx, void* aObj, JS::MutableHandleValue aHandle);
+  void (*mCleanup) (void* aObj);
+};
+
 
 namespace xpt {
 namespace detail {
 
 /**
  * The compressed representation of constants from XPT. Not part of the public
  * interface, as we also need to support Shim interfaces.
  */
@@ -456,16 +496,17 @@ static_assert(sizeof(ConstInfo) == 8, "w
 // Raw typelib data stored in const statics //
 //////////////////////////////////////////////
 
 // XPIDL information
 extern const nsXPTInterfaceInfo sInterfaces[];
 extern const nsXPTType sTypes[];
 extern const nsXPTParamInfo sParams[];
 extern const nsXPTMethodInfo sMethods[];
+extern const nsXPTDOMObjectInfo sDOMObjects[];
 
 extern const char sStrings[];
 extern const ConstInfo sConsts[];
 
 // shim constant information
 extern const mozilla::dom::NativePropertyHooks* sPropHooks[];
 
 // Perfect Hash Function backing data
@@ -501,16 +542,22 @@ GetParam(uint16_t aIndex)
 }
 
 inline const nsXPTMethodInfo&
 GetMethod(uint16_t aIndex)
 {
   return sMethods[aIndex];
 }
 
+inline const nsXPTDOMObjectInfo&
+GetDOMObjectInfo(uint16_t aIndex)
+{
+  return sDOMObjects[aIndex];
+}
+
 inline const char*
 GetString(uint32_t aIndex)
 {
   return &sStrings[aIndex];
 }
 
 } // namespace detail
 } // namespace xpt