Bug 838691 part 2. Add codegen support for calling a function to determine whether a property should be exposed in a WebIDL binding. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 19 Feb 2013 11:54:40 -0500
changeset 128770 35231869058aa71ac66801158f7a9fbe4b508313
parent 128769 83952a3e9b74a352497137c10e1c3927c92f7b31
child 128771 b5fa1cb51dc8f4385bf9e440536159f707e7e0cf
push id3582
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 20:50:56 +0000
treeherdermozilla-aurora@400370bbc9fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs838691
milestone21.0a1
Bug 838691 part 2. Add codegen support for calling a function to determine whether a property should be exposed in a WebIDL binding. r=peterv
dom/bindings/Codegen.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -994,16 +994,37 @@ class CGClassHasInstanceHook(CGAbstractS
     nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
   nsCOMPtr<nsIDOM%s> qiResult = do_QueryInterface(native);
   *bp = !!qiResult;
   return true;""" % self.descriptor.interface.identifier.name
 
 def isChromeOnly(m):
     return m.getExtendedAttribute("ChromeOnly")
 
+class MemberCondition:
+    """
+    An object representing the condition for a member to actually be
+    exposed.  Either pref or func or both can be None.  If not None,
+    they should be strings that have the pref name or function name.
+    """
+    def __init__(self, pref, func):
+        assert pref is None or isinstance(pref, str)
+        assert func is None or isinstance(func, str)
+        self.pref = pref
+        if func is None:
+            self.func = "nullptr"
+        else:
+            self.func = "&" + func
+
+    def __eq__(self, other):
+        return self.pref == other.pref and self.func == other.func
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
 class PropertyDefiner:
     """
     A common superclass for defining things on prototype objects.
 
     Subclasses should implement generateArray to generate the actual arrays of
     things we're defining.  They should also set self.chrome to the list of
     things only exposed to chrome and self.regular to the list of things exposed
     to both chrome and web pages.
@@ -1037,83 +1058,91 @@ class PropertyDefiner:
         str = self.generateArray(self.regular, self.variableName(False),
                                  self.usedForXrays())
         if self.hasChromeOnly():
             str += self.generateArray(self.chrome, self.variableName(True),
                                       self.usedForXrays())
         return str
 
     @staticmethod
-    def getControllingPref(interfaceMember):
-        prefName = interfaceMember.getExtendedAttribute("Pref")
-        if prefName is None:
+    def getStringAttr(member, name):
+        attr = member.getExtendedAttribute(name)
+        if attr is None:
             return None
         # It's a list of strings
-        assert(len(prefName) is 1)
-        assert(prefName[0] is not None)
-        return prefName[0]
+        assert(len(attr) is 1)
+        assert(attr[0] is not None)
+        return attr[0]
+
+    @staticmethod
+    def getControllingCondition(interfaceMember):
+        return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
+                                                             "Pref"),
+                               PropertyDefiner.getStringAttr(interfaceMember,
+                                                             "Func"))
 
     def generatePrefableArray(self, array, name, specTemplate, specTerminator,
-                              specType, getPref, getDataTuple, doIdArrays):
+                              specType, getCondition, getDataTuple, doIdArrays):
         """
         This method generates our various arrays.
 
         array is an array of interface members as passed to generateArray
 
         name is the name as passed to generateArray
 
         specTemplate is a template for each entry of the spec array
 
         specTerminator is a terminator for the spec array (inserted every time
           our controlling pref changes and at the end of the array)
 
         specType is the actual typename of our spec
 
-        getPref is a callback function that takes an array entry and returns
-          the corresponding pref value.
+        getCondition is a callback function that takes an array entry and
+          returns the corresponding MemberCondition.
 
         getDataTuple is a callback function that takes an array entry and
           returns a tuple suitable for substitution into specTemplate.
         """
 
         # We want to generate a single list of specs, but with specTerminator
         # inserted at every point where the pref name controlling the member
         # changes.  That will make sure the order of the properties as exposed
         # on the interface and interface prototype objects does not change when
         # pref control is added to members while still allowing us to define all
         # the members in the smallest number of JSAPI calls.
         assert(len(array) is not 0)
-        lastPref = getPref(array[0]) # So we won't put a specTerminator
-                                     # at the very front of the list.
+        lastCondition = getCondition(array[0]) # So we won't put a specTerminator
+                                               # at the very front of the list.
         specs = []
         prefableSpecs = []
 
-        prefableTemplate = '  { true, nullptr, &%s[%d] }'
+        prefableTemplate = '  { true, %s, &%s[%d] }'
         prefCacheTemplate = '&%s[%d].enabled'
-        def switchToPref(props, pref):
+        def switchToCondition(props, condition):
             # Remember the info about where our pref-controlled
             # booleans live.
-            if pref is not None:
+            if condition.pref is not None:
                 props.prefCacheData.append(
-                    (pref, prefCacheTemplate % (name, len(prefableSpecs)))
+                    (condition.pref,
+                     prefCacheTemplate % (name, len(prefableSpecs)))
                     )
             # Set up pointers to the new sets of specs inside prefableSpecs
             prefableSpecs.append(prefableTemplate %
-                                 (name + "_specs", len(specs)))
-
-        switchToPref(self, lastPref)
+                                 (condition.func, name + "_specs", len(specs)))
+
+        switchToCondition(self, lastCondition)
 
         for member in array:
-            curPref = getPref(member)
-            if lastPref != curPref:
+            curCondition = getCondition(member)
+            if lastCondition != curCondition:
                 # Terminate previous list
                 specs.append(specTerminator)
                 # And switch to our new pref
-                switchToPref(self, curPref)
-                lastPref = curPref
+                switchToCondition(self, curCondition)
+                lastCondition = curCondition
             # And the actual spec
             specs.append(specTemplate % getDataTuple(member))
         specs.append(specTerminator)
         prefableSpecs.append("  { false, NULL }");
 
         arrays = (("static %s %s_specs[] = {\n" +
                    ',\n'.join(specs) + "\n" +
                    "};\n\n" +
@@ -1157,48 +1186,48 @@ class MethodDefiner(PropertyDefiner):
             methods = []
         self.chrome = []
         self.regular = []
         for m in methods:
             method = { "name": m.identifier.name,
                        "methodInfo": not m.isStatic(),
                        "length": methodLength(m),
                        "flags": "JSPROP_ENUMERATE",
-                       "pref": PropertyDefiner.getControllingPref(m) }
+                       "condition": PropertyDefiner.getControllingCondition(m) }
             if isChromeOnly(m):
                 self.chrome.append(method)
             else:
                 self.regular.append(method)
 
         # FIXME Check for an existing iterator on the interface first.
         if any(m.isGetter() and m.isIndexed() for m in methods):
             self.regular.append({"name": 'iterator',
                                  "methodInfo": False,
                                  "nativeName": "JS_ArrayIterator",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
-                                 "pref": None })
+                                 "condition": MemberCondition(None, None) })
 
         if (not descriptor.interface.parent and not static and
             descriptor.nativeOwnership == 'nsisupports' and
             descriptor.interface.hasInterfacePrototypeObject()):
             self.chrome.append({"name": 'QueryInterface',
                                 "methodInfo": False,
                                 "length": 1,
                                 "flags": "0",
-                                "pref": None })
+                                "condition": MemberCondition(None, None) })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
             if stringifier:
                 toStringDesc = { "name": "toString",
                                  "nativeName": stringifier.identifier.name,
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
-                                 "pref": PropertyDefiner.getControllingPref(stringifier) }
+                                 "condition": PropertyDefiner.getControllingCondition(stringifier) }
                 if isChromeOnly(stringifier):
                     self.chrome.append(toStringDesc)
                 else:
                     self.regular.append(toStringDesc)
 
         if static:
             if not descriptor.interface.hasInterfaceObject():
                 # static methods go on the interface object
@@ -1207,34 +1236,34 @@ class MethodDefiner(PropertyDefiner):
             if not descriptor.interface.hasInterfacePrototypeObject():
                 # non-static methods go on the interface prototype object
                 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
 
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
-        def pref(m):
-            return m["pref"]
+        def condition(m):
+            return m["condition"]
 
         def specData(m):
             accessor = m.get("nativeName", m["name"])
             if m.get("methodInfo", True):
                 jitinfo = ("&%s_methodinfo" % accessor)
                 accessor = "genericMethod"
             else:
                 jitinfo = "nullptr"
             return (m["name"], accessor, jitinfo, m["length"], m["flags"])
 
         return self.generatePrefableArray(
             array, name,
             '  JS_FNINFO("%s", %s, %s, %s, %s)',
             '  JS_FS_END',
             'JSFunctionSpec',
-            pref, specData, doIdArrays)
+            condition, specData, doIdArrays)
 
 class AttrDefiner(PropertyDefiner):
     def __init__(self, descriptor, name, static, unforgeable=False):
         assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
         # Ignore non-static attributes for callback interfaces
         if not descriptor.interface.isCallback() or static:
@@ -1299,17 +1328,17 @@ class AttrDefiner(PropertyDefiner):
             return (attr.identifier.name, flags(attr), getter(attr),
                     setter(attr))
 
         return self.generatePrefableArray(
             array, name,
             '  { "%s", 0, %s, %s, %s}',
             '  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }',
             'JSPropertySpec',
-            PropertyDefiner.getControllingPref, specData, doIdArrays)
+            PropertyDefiner.getControllingCondition, specData, doIdArrays)
 
 class ConstDefiner(PropertyDefiner):
     """
     A class for definining constants on the interface object
     """
     def __init__(self, descriptor, name):
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
@@ -1325,17 +1354,17 @@ class ConstDefiner(PropertyDefiner):
             return (const.identifier.name,
                     convertConstIDLValueToJSVal(const.value))
 
         return self.generatePrefableArray(
             array, name,
             '  { "%s", %s }',
             '  { 0, JSVAL_VOID }',
             'ConstantSpec',
-            PropertyDefiner.getControllingPref, specData, doIdArrays)
+            PropertyDefiner.getControllingCondition, specData, doIdArrays)
 
 class PropertyArrays():
     def __init__(self, descriptor):
         self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
                                            static=True)
         self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
                                        static=True)
         self.methods = MethodDefiner(descriptor, "Methods", static=False)