Bug 963382, part 2 - Add chrome JS function for clearing cache. r=bz
authorAndrew McCreight <continuation@gmail.com>
Thu, 21 Aug 2014 13:56:11 -0700
changeset 201076 019faa34f1948b0f8f5ba18a3a82b056177e58ee
parent 201075 c9ce133b845e6ea0973d9a65349e4ef9a1b3b47a
child 201077 1cbd24c6e684972b6a3c6bb01466c8e30002b555
push id27362
push userkwierso@gmail.com
push dateFri, 22 Aug 2014 23:47:54 +0000
treeherdermozilla-central@64c4ec2df3d4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs963382
milestone34.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 963382, part 2 - Add chrome JS function for clearing cache. r=bz
dom/bindings/Codegen.py
dom/bindings/test/TestInterfaceJS.js
dom/bindings/test/mochitest.ini
dom/bindings/test/test_bug963382.html
dom/webidl/TestInterfaceJS.webidl
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2032,16 +2032,31 @@ def methodLength(method):
     return min(overloadLength(arguments) for retType, arguments in signatures)
 
 
 def isMaybeExposedIn(member, descriptor):
     # All we can say for sure is that if this is a worker descriptor
     # and member is only exposed in windows, then it's not exposed.
     return not descriptor.workers or member.exposureSet != set(["Window"])
 
+def clearableCachedAttrs(descriptor):
+    return (m for m in descriptor.interface.members if
+            m.isAttr() and
+            # Constants should never need clearing!
+            not m.getExtendedAttribute("Constant") and
+            not m.getExtendedAttribute("SameObject") and
+            m.slotIndex is not None)
+
+def MakeClearCachedValueNativeName(member):
+    return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
+
+def MakeJSImplClearCachedValueNativeName(member):
+    return "_" + MakeClearCachedValueNativeName(member)
+
+
 class MethodDefiner(PropertyDefiner):
     """
     A class for defining methods on a prototype object.
     """
     def __init__(self, descriptor, name, static, unforgeable=False):
         assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
 
@@ -2172,16 +2187,27 @@ class MethodDefiner(PropertyDefiner):
                 self.chrome.append({
                     "name": '_create',
                     "nativeName": ("%s::_Create" % descriptor.name),
                     "methodInfo": False,
                     "length": 2,
                     "flags": "0",
                     "condition": MemberCondition(None, None)
                 })
+            else:
+                for m in clearableCachedAttrs(descriptor):
+                    attrName = MakeNativeName(m.identifier.name)
+                    self.chrome.append({
+                        "name": "_clearCached%sValue" % attrName,
+                        "nativeName": MakeJSImplClearCachedValueNativeName(m),
+                        "methodInfo": False,
+                        "length": "0",
+                        "flags": "0",
+                        "condition": MemberCondition(None, None)
+                    })
 
         self.unforgeable = unforgeable
 
         if static:
             if not descriptor.interface.hasInterfaceObject():
                 # static methods go on the interface object
                 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
         else:
@@ -3244,17 +3270,17 @@ class CGClearCachedValueMethod(CGAbstrac
         # If we're StoreInSlot, we'll need to call the getter
         if member.getExtendedAttribute("StoreInSlot"):
             args = [Argument('JSContext*', 'aCx')]
             returnType = 'bool'
         else:
             args = []
             returnType = 'void'
         args.append(Argument(descriptor.nativeType + '*', 'aObject'))
-        name = ("ClearCached%sValue" % MakeNativeName(member.identifier.name))
+        name = MakeClearCachedValueNativeName(member)
         CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
 
     def definition_body(self):
         slotIndex = memberReservedSlot(self.member)
         if self.member.getExtendedAttribute("StoreInSlot"):
             # We have to root things and save the old value in case
             # regetting fails, so we can restore it.
             declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
@@ -7080,18 +7106,18 @@ class CGSetterCall(CGPerSignatureCall):
 
     def wrap_return_value(self):
         attr = self.idlNode
         if self.descriptor.wrapperCache and attr.slotIndex is not None:
             if attr.getExtendedAttribute("StoreInSlot"):
                 args = "cx, self"
             else:
                 args = "self"
-            clearSlot = ("ClearCached%sValue(%s);\n" %
-                         (MakeNativeName(self.idlNode.identifier.name), args))
+            clearSlot = ("%s(%s);\n" %
+                         (MakeClearCachedValueNativeName(self.idlNode), args))
         else:
             clearSlot = ""
 
         # We have no return value
         return ("\n"
                 "%s"
                 "return true;\n" % clearSlot)
 
@@ -10836,16 +10862,21 @@ class CGDescriptor(CGThing):
             for (k, v) in sorted(descriptor.permissions.items()):
                 perms = CGList((CGGeneric('"%s",' % p) for p in k), joiner="\n")
                 perms.append(CGGeneric("nullptr"))
                 cgThings.append(CGWrapper(CGIndenter(perms),
                                           pre="static const char* const permissions_%i[] = {\n" % v,
                                           post="\n};\n",
                                           defineOnly=True))
 
+        # Generate the _ClearCachedFooValue methods before the property arrays that use them.
+        if descriptor.interface.isJSImplemented():
+            for m in clearableCachedAttrs(descriptor):
+                cgThings.append(CGJSImplClearCachedValueMethod(descriptor, m))
+
         properties = PropertyArrays(descriptor)
         cgThings.append(CGGeneric(define=str(properties)))
         cgThings.append(CGNativeProperties(descriptor, properties))
 
         # Set up our Xray callbacks as needed.  Note that we don't need to do
         # it in workers.
         if not descriptor.workers and descriptor.concrete and descriptor.proxy:
             cgThings.append(CGResolveOwnProperty(descriptor))
@@ -10927,22 +10958,17 @@ class CGDescriptor(CGThing):
             else:
                 cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
                                                             properties))
 
         # If we're not wrappercached, we don't know how to clear our
         # cached values, since we can't get at the JSObject.
         if descriptor.wrapperCache:
             cgThings.extend(CGClearCachedValueMethod(descriptor, m) for
-                            m in descriptor.interface.members if
-                            m.isAttr() and
-                            # Constants should never need clearing!
-                            not m.getExtendedAttribute("Constant") and
-                            not m.getExtendedAttribute("SameObject") and
-                            m.slotIndex is not None)
+                            m in clearableCachedAttrs(descriptor))
 
         # CGCreateInterfaceObjectsMethod needs to come after our
         # CGDOMJSClass, if any.
         cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
 
         # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
         # to come after CGCreateInterfaceObjectsMethod.
         if descriptor.interface.hasInterfacePrototypeObject():
@@ -12956,16 +12982,41 @@ def callbackGetterName(attr, descriptor)
         descriptor.binaryNameFor(attr.identifier.name))
 
 
 def callbackSetterName(attr, descriptor):
     return "Set" + MakeNativeName(
         descriptor.binaryNameFor(attr.identifier.name))
 
 
+class CGJSImplClearCachedValueMethod(CGAbstractBindingMethod):
+    def __init__(self, descriptor, attr):
+        if attr.getExtendedAttribute("StoreInSlot"):
+            raise TypeError("[StoreInSlot] is not supported for JS-implemented WebIDL. See bug 1056325.")
+
+        args = [Argument("JSContext*", "cx"),
+                Argument("unsigned", "argc"),
+                Argument("JS::Value*", "vp")]
+
+        CGAbstractBindingMethod.__init__(self, descriptor,
+                                         MakeJSImplClearCachedValueNativeName(attr),
+                                         args)
+        self.attr = attr
+
+    def generate_code(self):
+        return CGGeneric(fill(
+            """
+            ${bindingNamespace}::${fnName}(self);
+            args.rval().setUndefined();
+            return true;
+            """,
+            bindingNamespace=toBindingNamespace(self.descriptor.interface.identifier.name),
+            fnName=MakeClearCachedValueNativeName(self.attr)))
+
+
 class CGJSImplGetter(CGJSImplMember):
     """
     Class for generating code for the getters of attributes for a JS-implemented
     WebIDL interface.
     """
     def __init__(self, descriptor, attr):
         CGJSImplMember.__init__(self, descriptor, attr,
                                 CGSpecializedGetter.makeNativeName(descriptor,
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -17,16 +17,17 @@ TestInterfaceJS.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
   __init: function (anyArg, objectArg, dictionaryArg) {
     this._anyAttr = undefined;
     this._objectAttr = null;
     this._anyArg = anyArg;
     this._objectArg = objectArg;
     this._dictionaryArg = dictionaryArg;
+    this._cachedAttr = 15;
   },
 
   get anyArg() { return this._anyArg; },
   get objectArg() { return this._objectArg; },
   get dictionaryArg() { return this._dictionaryArg; },
   get anyAttr() { return this._anyAttr; },
   set anyAttr(val) { this._anyAttr = val; },
   get objectAttr() { return this._objectAttr; },
@@ -45,12 +46,16 @@ TestInterfaceJS.prototype = {
 
   getCallerPrincipal: function() { return Cu.getWebIDLCallerPrincipal().origin; },
 
   convertSVS: function(svs) { return svs; },
 
   pingPongUnion: function(x) { return x; },
   pingPongUnionContainingNull: function(x) { return x; },
   pingPongNullableUnion: function(x) { return x; },
-  returnBadUnion: function(x) { return 3; }
+  returnBadUnion: function(x) { return 3; },
+
+  get cachedAttr() { return this._cachedAttr; },
+  setCachedAttr: function(n) { this._cachedAttr = n; },
+  clearCachedAttrCache: function () { this.__DOM_IMPL__._clearCachedCachedAttrValue(); },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -14,16 +14,18 @@ support-files =
 [test_bug742191.html]
 [test_bug759621.html]
 [test_bug773326.html]
 [test_bug788369.html]
 [test_bug852846.html]
 [test_bug862092.html]
 [test_bug1036214.html]
 skip-if = debug == false
+[test_bug963382.html]
+skip-if = debug == false
 [test_bug1041646.html]
 [test_barewordGetsWindow.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_bug963382.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=963382
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 963382</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for clearing cache attributes in JS-implemented WebIDL implementations. **/
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+
+  function go() {
+    var t = new TestInterfaceJS();
+
+    // Test [Cached] attribute clearing.
+    is(t.cachedAttr, 15, "Initial value of number");
+
+    t.setCachedAttr(3);
+    is(t.cachedAttr, 15, "Setting the number on the inner JS object should not affect cached value");
+
+    t.clearCachedAttrCache();
+    is(t.cachedAttr, 3, "Setting the number on the inner JS object should affect cached value after clearing the cache.");
+
+    SimpleTest.finish();
+  }
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=963382">Mozilla Bug 963382</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/webidl/TestInterfaceJS.webidl
+++ b/dom/webidl/TestInterfaceJS.webidl
@@ -32,9 +32,14 @@ interface TestInterfaceJS {
   DOMString getCallerPrincipal();
 
   DOMString convertSVS(ScalarValueString svs);
 
   (TestInterfaceJS or long) pingPongUnion((TestInterfaceJS or long) something);
   (DOMString or TestInterfaceJS?) pingPongUnionContainingNull((TestInterfaceJS? or DOMString) something);
   (TestInterfaceJS or long)? pingPongNullableUnion((TestInterfaceJS or long)? something);
   (Location or TestInterfaceJS) returnBadUnion();
+
+  [Cached, Pure]
+  readonly attribute short cachedAttr;
+  void setCachedAttr(short n);
+  void clearCachedAttrCache();
 };