Bug 787070 - Expandos on the xray of DOM prototypes should have effect on xrays of DOM nodes, cache named properties objects. r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Mon, 15 Sep 2014 16:51:33 +0200
changeset 207614 3ef7fe194800
parent 207613 d7cb38405fc2
child 207615 ee4fe6bc73be
push id49735
push userpvanderbeken@mozilla.com
push dateMon, 29 Sep 2014 07:58:28 +0000
treeherdermozilla-inbound@090b62fdfd21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs787070
milestone35.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 787070 - Expandos on the xray of DOM prototypes should have effect on xrays of DOM nodes, cache named properties objects. r=bz.
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -286,23 +286,27 @@ IsConvertibleToDictionary(JSContext* cx,
 }
 
 MOZ_ALWAYS_INLINE bool
 IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj)
 {
   return IsNotDateOrRegExp(cx, obj);
 }
 
-// The items in the protoAndIfaceCache are indexed by the prototypes::id::ID and
-// constructors::id::ID enums, in that order. The end of the prototype objects
-// should be the start of the interface objects.
+// The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
+// constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
+// The end of the prototype objects should be the start of the interface
+// objects, and the end of the interface objects should be the start of the
+// named properties objects.
 static_assert((size_t)constructors::id::_ID_Start ==
-              (size_t)prototypes::id::_ID_Count,
+              (size_t)prototypes::id::_ID_Count &&
+              (size_t)namedpropertiesobjects::id::_ID_Start ==
+              (size_t)constructors::id::_ID_Count,
               "Overlapping or discontiguous indexes.");
-const size_t kProtoAndIfaceCacheCount = constructors::id::_ID_Count;
+const size_t kProtoAndIfaceCacheCount = namedpropertiesobjects::id::_ID_Count;
 
 class ProtoAndIfaceCache
 {
   // The caching strategy we use depends on what sort of global we're dealing
   // with.  For a window-like global, we want everything to be as fast as
   // possible, so we use a flat array, indexed by prototype/constructor ID.
   // For everything else (e.g. globals for JSMs), space is more important than
   // speed, so we use a two-level lookup table.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2576,18 +2576,18 @@ class CGCreateInterfaceObjectsMethod(CGA
                 Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
                 Argument('bool', 'aDefineOnGlobal')]
         CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
         self.properties = properties
 
     def definition_body(self):
         parentProtoName = self.descriptor.parentPrototypeName
         if self.descriptor.hasNamedPropertiesObject:
-            parentProtoType = "Rooted"
-            getParentProto = "aCx, GetNamedPropertiesObject(aCx, aGlobal)"
+            parentProtoType = "Handle"
+            getParentProto = "GetNamedPropertiesObject(aCx, aGlobal)"
         elif parentProtoName is None:
             parentProtoType = "Rooted"
             if self.descriptor.interface.getExtendedAttribute("ArrayClass"):
                 getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)"
             elif self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
                 getParentProto = "aCx, JS_GetErrorPrototype(aCx)"
             else:
                 getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)"
@@ -2851,39 +2851,52 @@ class CGGetConstructorObjectMethod(CGGet
 
 
 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aGlobal')]
         CGAbstractStaticMethod.__init__(self, descriptor,
                                         'GetNamedPropertiesObject',
-                                        'JSObject*', args)
+                                        'JS::Handle<JSObject*>', args)
 
     def definition_body(self):
         parentProtoName = self.descriptor.parentPrototypeName
         if parentProtoName is None:
             getParentProto = ""
             parentProto = "nullptr"
         else:
             getParentProto = fill(
                 """
                 JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObject(aCx, aGlobal));
                 if (!parentProto) {
-                  return nullptr;
+                  return js::NullPtr();
                 }
                 """,
                 parent=toBindingNamespace(parentProtoName))
             parentProto = "parentProto"
         return fill(
             """
-            $*{getParentProto}
-            return ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
+            /* Make sure our global is sane.  Hopefully we can remove this sometime */
+            if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
+              return JS::NullPtr();
+            }
+
+            /* Check to see whether the named properties object has already been created */
+            ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
+
+            JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
+            if (!namedPropertiesObject) {
+              $*{getParentProto}
+              namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
+            }
+            return JS::Handle<JSObject*>::fromMarkedLocation(namedPropertiesObject.address());
             """,
             getParentProto=getParentProto,
+            ifaceName=self.descriptor.name,
             parentProto=parentProto,
             nativeType=self.descriptor.nativeType)
 
 
 class CGDefineDOMInterfaceMethod(CGAbstractMethod):
     """
     A method for resolve hooks to try to lazily define the interface object for
     a given interface.
@@ -14204,16 +14217,28 @@ class GlobalGenRoots():
 
         # Wrap all of that in our namespaces.
         idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
                                    CGWrapper(idEnum, pre='\n'))
         idEnum = CGWrapper(idEnum, post='\n')
 
         curr.append(idEnum)
 
+        # Named properties object enum.
+        namedPropertiesObjects = [d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)]
+        idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + namedPropertiesObjects,
+                                  ['constructors::id::_ID_Count', '_ID_Start'])
+
+        # Wrap all of that in our namespaces.
+        idEnum = CGNamespace.build(['mozilla', 'dom', 'namedpropertiesobjects'],
+                                   CGWrapper(idEnum, pre='\n'))
+        idEnum = CGWrapper(idEnum, post='\n')
+
+        curr.append(idEnum)
+
         traitsDecls = [CGGeneric(declare=dedent("""
             template <prototypes::ID PrototypeID>
             struct PrototypeTraits;
             """))]
         traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
 
         ifaceNamesWithProto = [d.interface.identifier.name
                                for d in descriptorsWithPrototype]