Bug 760851 - Add jsonifier WebIDL declaration and add toJSON to performance.timing. r=bz
authorMina Almasry <almasry.mina@gmail.com>
Fri, 26 Jul 2013 12:00:49 -0400
changeset 140175 e26410b337b59d41e88c952bceef0ac437d455b9
parent 140174 bcc709f93afb905dada3e188a42964e57c746cc5
child 140176 7ff04bb944aa4239f0f5d61204b16cd0da585e90
push id1945
push userryanvm@gmail.com
push dateSat, 27 Jul 2013 02:27:26 +0000
treeherderfx-team@4874fa438b1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs760851
milestone25.0a1
Bug 760851 - Add jsonifier WebIDL declaration and add toJSON to performance.timing. r=bz This patch adds a jsonifier declaration to WebIDL's. The declaration adds an autogenerated method toJSON() on the given webidl. This patch also adds jsonifier and toJSON() to PerformanceTiming.webidl, and performance.timing, respectively.
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/parser/WebIDL.py
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
dom/bindings/test/TestJSImplGen.webidl
dom/tests/mochitest/bugs/Makefile.in
dom/tests/mochitest/bugs/test_toJSON.html
dom/webidl/Performance.webidl
dom/webidl/PerformanceNavigation.webidl
dom/webidl/PerformanceTiming.webidl
js/src/jsfriendapi.h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1433,16 +1433,27 @@ class MethodDefiner(PropertyDefiner):
                                  "nativeName": stringifier.identifier.name,
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
                                  "condition": PropertyDefiner.getControllingCondition(stringifier) }
                 if isChromeOnly(stringifier):
                     self.chrome.append(toStringDesc)
                 else:
                     self.regular.append(toStringDesc)
+            jsonifier = descriptor.operations['Jsonifier']
+            if jsonifier:
+                toJSONDesc = { "name": "toJSON",
+                               "nativeName": jsonifier.identifier.name,
+                               "length": 0,
+                               "flags": "JSPROP_ENUMERATE",
+                               "condition": PropertyDefiner.getControllingCondition(jsonifier) }
+                if isChromeOnly(jsonifier):
+                    self.chrome.append(toJSONDesc)
+                else:
+                    self.regular.append(toJSONDesc)
         elif (descriptor.interface.isJSImplemented() and
               descriptor.interface.hasInterfaceObject()):
             self.chrome.append({"name": '_create',
                                 "nativeName": ("%s::_Create" %
                                                descriptor.name),
                                 "methodInfo": False,
                                 "length": 2,
                                 "flags": "0",
@@ -5202,16 +5213,42 @@ class CGSpecializedMethod(CGAbstractStat
         return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
                             self.method).define()
 
     @staticmethod
     def makeNativeName(descriptor, method):
         name = method.identifier.name
         return MakeNativeName(descriptor.binaryNames.get(name, name))
 
+class CGJsonifierMethod(CGSpecializedMethod):
+    def __init__(self, descriptor, method):
+        assert method.isJsonifier()
+        CGSpecializedMethod.__init__(self, descriptor, method)
+
+    def definition_body(self):
+        ret = ('JS::Rooted<JSObject*> result(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));\n'
+               'if (!result) {\n'
+               '  return false;\n'
+               '}\n')
+        for m in self.descriptor.interface.members:
+          if m.isAttr() and not m.isStatic():
+              ret += ('{ // scope for "temp"\n'
+                      '  JS::Rooted<JS::Value> temp(cx);\n'
+                      '  if (!get_%s(cx, obj, self, JSJitGetterCallArgs(&temp))) {\n'
+                      '    return false;\n'
+                      '  }\n'
+                      '  if (!JS_DefineProperty(cx, result, "%s", temp, nullptr, nullptr, JSPROP_ENUMERATE)) {\n'
+                      '    return false;\n'
+                      '  }\n'
+                      '}\n' % (m.identifier.name, m.identifier.name))
+
+        ret += ('args.rval().setObject(*result);\n'
+                'return true;')
+        return CGIndenter(CGGeneric(ret)).define()
+
 class CGLegacyCallHook(CGAbstractBindingMethod):
     """
     Call hook for our object
     """
     def __init__(self, descriptor):
         self._legacycaller = descriptor.operations["LegacyCaller"]
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
@@ -7539,25 +7576,29 @@ class CGDescriptor(CGThing):
            descriptor.interface.hasChildInterfaces() or
            descriptor.interface.parent):
            raise TypeError("Owned interface cannot have a parent or children")
 
         self._deps = descriptor.interface.getDeps()
 
         cgThings = []
         # These are set to true if at least one non-static
-        # method/getter/setter exist on the interface.
-        (hasMethod, hasGetter, hasLenientGetter,
-         hasSetter, hasLenientSetter) = False, False, False, False, False
+        # method/getter/setter or jsonifier exist on the interface.
+        (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasJsonifier,
+            hasLenientSetter) = False, False, False, False, False, False
         for n in descriptor.interface.namedConstructors:
             cgThings.append(CGClassConstructor(descriptor, n,
                                                NamedConstructorName(n)))
         for m in descriptor.interface.members:
-            if (m.isMethod() and
-                (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])):
+            if (m.isMethod() and m == descriptor.operations['Jsonifier']):
+                hasJsonifier = True
+                hasMethod = True
+                jsonifierMethod = m
+            elif (m.isMethod() and
+                  (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])):
                 if m.isStatic():
                     assert descriptor.interface.hasInterfaceObject
                     cgThings.append(CGStaticMethod(descriptor, m))
                 elif descriptor.interface.hasInterfacePrototypeObject():
                     cgThings.append(CGSpecializedMethod(descriptor, m))
                     cgThings.append(CGMemberJITInfo(descriptor, m))
                     hasMethod = True
             elif m.isAttr():
@@ -7581,16 +7622,19 @@ class CGDescriptor(CGThing):
                         else:
                             hasSetter = True
                 elif m.getExtendedAttribute("PutForwards"):
                     cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
                     hasSetter = True
                 if (not m.isStatic() and
                     descriptor.interface.hasInterfacePrototypeObject()):
                     cgThings.append(CGMemberJITInfo(descriptor, m))
+        if hasJsonifier:
+            cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
+            cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
         if hasMethod: cgThings.append(CGGenericMethod(descriptor))
         if hasGetter: cgThings.append(CGGenericGetter(descriptor))
         if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
                                                              lenientThis=True))
         if hasSetter: cgThings.append(CGGenericSetter(descriptor))
         if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
                                                              lenientThis=True))
 
@@ -8984,16 +9028,19 @@ class CGBindingImplClass(CGClass):
                     return
             if name == "LegacyCaller":
                 if op.isIdentifierLess():
                     # XXXbz I wish we were consistent about our renaming here.
                     name = "LegacyCall"
                 else:
                     # We already added this method
                     return
+            if name == "Jsonifier":
+                # We already added this method
+                return
             self.methodDecls.append(
                 CGNativeMember(descriptor, op,
                                name,
                                (returnType, args),
                                descriptor.getExtendedAttributes(op)))
         # Sort things by name so we get stable ordering in the output.
         ops = descriptor.operations.items()
         ops.sort(key=lambda x: x[0])
@@ -9549,17 +9596,17 @@ class CGCallbackFunction(CGCallback):
 class CGCallbackInterface(CGCallback):
     def __init__(self, descriptor):
         iface = descriptor.interface
         attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
         getters = [CallbackGetter(a, descriptor) for a in attrs]
         setters = [CallbackSetter(a, descriptor) for a in attrs
                    if not a.readonly]
         methods = [m for m in iface.members
-                   if m.isMethod() and not m.isStatic()]
+                   if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
         methods = [CallbackOperation(m, sig, descriptor) for m in methods
                    for sig in m.signatures()]
         if iface.isJSImplemented() and iface.ctor():
             sigs = descriptor.interface.ctor().signatures()
             if len(sigs) != 1:
                 raise TypeError("We only handle one constructor.  See bug 869268.")
             methods.append(CGJSImplInitOperation(sigs[0], descriptor))
         CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -272,29 +272,32 @@ class Descriptor(DescriptorProvider):
             'IndexedSetter': None,
             'IndexedCreator': None,
             'IndexedDeleter': None,
             'NamedGetter': None,
             'NamedSetter': None,
             'NamedCreator': None,
             'NamedDeleter': None,
             'Stringifier': None,
-            'LegacyCaller': None
+            'LegacyCaller': None,
+            'Jsonifier': None
             }
         if self.concrete:
             self.proxy = False
             iface = self.interface
             def addOperation(operation, m):
                 if not operations[operation]:
                     operations[operation] = m
             # Since stringifiers go on the prototype, we only need to worry
             # about our own stringifier, not those of our ancestor interfaces.
             for m in iface.members:
                 if m.isMethod() and m.isStringifier():
                     addOperation('Stringifier', m)
+                if m.isMethod() and m.isJsonifier():
+                    addOperation('Jsonifier', m)
                 # Don't worry about inheriting legacycallers either: in
                 # practice these are on most-derived prototypes.
                 if m.isMethod() and m.isLegacycaller():
                     if not m.isIdentifierLess():
                         raise TypeError("We don't support legacycaller with "
                                         "identifier.\n%s" % m.location);
                     if len(m.signatures()) != 1:
                         raise TypeError("We don't support overloaded "
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -713,22 +713,25 @@ class IDLInterface(IDLObjectWithScope):
             elif member.isSetter():
                 memberType = "setters"
             elif member.isCreator():
                 memberType = "creators"
             elif member.isDeleter():
                 memberType = "deleters"
             elif member.isStringifier():
                 memberType = "stringifiers"
+            elif member.isJsonifier():
+                memberType = "jsonifiers"
             elif member.isLegacycaller():
                 memberType = "legacycallers"
             else:
                 continue
 
-            if memberType != "stringifiers" and memberType != "legacycallers":
+            if (memberType != "stringifiers" and memberType != "legacycallers" and
+                memberType != "jsonifiers"):
                 if member.isNamed():
                     memberType = "named " + memberType
                 else:
                     assert member.isIndexed()
                     memberType = "indexed " + memberType
 
             if memberType in specialMembersSeen:
                 raise WebIDLError("Multiple " + memberType + " on %s" % (self),
@@ -2807,17 +2810,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
         'Neither',
         'Named',
         'Indexed'
     )
 
     def __init__(self, location, identifier, returnType, arguments,
                  static=False, getter=False, setter=False, creator=False,
                  deleter=False, specialType=NamedOrIndexed.Neither,
-                 legacycaller=False, stringifier=False):
+                 legacycaller=False, stringifier=False, jsonifier=False):
         # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Method)
 
         self._hasOverloads = False
 
         assert isinstance(returnType, IDLType)
 
@@ -2833,16 +2836,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert isinstance(creator, bool)
         self._creator = creator
         assert isinstance(deleter, bool)
         self._deleter = deleter
         assert isinstance(legacycaller, bool)
         self._legacycaller = legacycaller
         assert isinstance(stringifier, bool)
         self._stringifier = stringifier
+        assert isinstance(jsonifier, bool)
+        self._jsonifier = jsonifier
         self._specialType = specialType
 
         if static and identifier.name == "prototype":
             raise WebIDLError("The identifier of a static operation must not be 'prototype'",
                               [location])
 
         self.assertSignatureConstraints()
 
@@ -2870,16 +2875,22 @@ class IDLMethod(IDLInterfaceMember, IDLS
             assert not arguments[1].optional and not arguments[1].variadic
 
         if self._stringifier:
             assert len(self._overloads) == 1
             overload = self._overloads[0]
             assert len(overload.arguments) == 0
             assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
 
+        if self._jsonifier:
+            assert len(self._overloads) == 1
+            overload = self._overloads[0]
+            assert len(overload.arguments) == 0
+            assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
+
     def isStatic(self):
         return self._static
 
     def isGetter(self):
         return self._getter
 
     def isSetter(self):
         return self._setter
@@ -2901,16 +2912,19 @@ class IDLMethod(IDLInterfaceMember, IDLS
         return self._specialType == IDLMethod.NamedOrIndexed.Indexed
 
     def isLegacycaller(self):
         return self._legacycaller
 
     def isStringifier(self):
         return self._stringifier
 
+    def isJsonifier(self):
+        return self._jsonifier
+
     def hasOverloads(self):
         return self._hasOverloads
 
     def isIdentifierLess(self):
         return self.identifier.name[:2] == "__"
 
     def resolve(self, parentScope):
         assert isinstance(parentScope, IDLScope)
@@ -2946,16 +2960,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert not self.isSetter()
         assert not method.isSetter()
         assert not self.isCreator()
         assert not method.isCreator()
         assert not self.isDeleter()
         assert not method.isDeleter()
         assert not self.isStringifier()
         assert not method.isStringifier()
+        assert not self.isJsonifier()
+        assert not method.isJsonifier()
 
         return self
 
     def signatures(self):
         return [(overload.returnType, overload.arguments) for overload in
                 self._overloads]
 
     def finish(self, scope):
@@ -3279,16 +3295,17 @@ class Tokenizer(object):
         "typedef": "TYPEDEF",
         "implements": "IMPLEMENTS",
         "const": "CONST",
         "null": "NULL",
         "true": "TRUE",
         "false": "FALSE",
         "serializer": "SERIALIZER",
         "stringifier": "STRINGIFIER",
+        "jsonifier": "JSONIFIER",
         "unrestricted": "UNRESTRICTED",
         "attribute": "ATTRIBUTE",
         "readonly": "READONLY",
         "inherit": "INHERIT",
         "static": "STATIC",
         "getter": "GETTER",
         "setter": "SETTER",
         "creator": "CREATOR",
@@ -3909,16 +3926,29 @@ class Parser(Tokenizer):
                                              allowDoubleUnderscore=True)
         method = IDLMethod(self.getLocation(p, 1),
                            identifier,
                            returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
                            arguments=[],
                            stringifier=True)
         p[0] = method
 
+    def p_Jsonifier(self, p):
+        """
+            Operation : JSONIFIER SEMICOLON
+        """
+        identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+                                             "__jsonifier", allowDoubleUnderscore=True)
+        method = IDLMethod(self.getLocation(p, 1),
+                           identifier,
+                           returnType=BuiltinTypes[IDLBuiltinType.Types.object],
+                           arguments=[],
+                           jsonifier=True)
+        p[0] = method
+
     def p_QualifierStatic(self, p):
         """
             Qualifier : STATIC
         """
         p[0] = [IDLInterfaceMember.Special.Static]
 
     def p_QualifierStringifier(self, p):
         """
@@ -4061,16 +4091,17 @@ class Parser(Tokenizer):
                          | INHERIT
                          | INTERFACE
                          | LEGACYCALLER
                          | PARTIAL
                          | SERIALIZER
                          | SETTER
                          | STATIC
                          | STRINGIFIER
+                         | JSONIFIER
                          | TYPEDEF
                          | UNRESTRICTED
         """
         p[0] = p[1]
 
     def p_Optional(self, p):
         """
             Optional : OPTIONAL
@@ -4190,16 +4221,17 @@ class Parser(Tokenizer):
                   | OBJECT
                   | OCTET
                   | OPTIONAL
                   | SEQUENCE
                   | SETTER
                   | SHORT
                   | STATIC
                   | STRINGIFIER
+                  | JSONIFIER
                   | TRUE
                   | TYPEDEF
                   | UNSIGNED
                   | VOID
         """
         pass
 
     def p_OtherOrComma(self, p):
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -570,16 +570,17 @@ interface TestInterface {
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
+  jsonifier;
 
   // If you add things here, add them to TestExampleGen and TestJSImplGen as well
 };
 
 interface TestParentInterface {
 };
 
 interface TestChildInterface : TestParentInterface {
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -467,16 +467,17 @@ interface TestExampleInterface {
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
+  jsonifier;
 
   // If you add things here, add them to TestCodeGen and TestJSImplGen as well
 };
 
 interface TestExampleProxyInterface {
   getter long longIndexedGetter(unsigned long ix);
   deleter void (unsigned long ix);
   setter creator void longIndexedSetter(unsigned long y, long z);
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -459,16 +459,17 @@ interface TestJSImplInterface {
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
   // legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
+  jsonifier;
 
   // If you add things here, add them to TestCodeGen as well
 };
 
 [NavigatorProperty="TestNavigator", JSImplementation="@mozilla.org/test;1"]
 interface TestNavigator {
 };
 
--- a/dom/tests/mochitest/bugs/Makefile.in
+++ b/dom/tests/mochitest/bugs/Makefile.in
@@ -141,11 +141,12 @@ MOCHITEST_FILES	= \
 		test_bug817476.html \
 		test_bug823173.html \
 		test_bug850517.html \
 		test_bug848088.html \
 		test_resize_move_windows.html \
 		test_bug862540.html \
 		test_bug857555.html \
 		test_bug876098.html \
+		test_toJSON.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_toJSON.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=760851
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 760851</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 Bug 760851 **/
+  SimpleTest.waitForExplicitFinish();
+
+  // We need to skip all the interface constants.
+  var keysToSkip = ["TYPE_NAVIGATE", "TYPE_RELOAD", "TYPE_RESERVED",
+                    "TYPE_BACK_FORWARD"];
+
+  // Testing window.performance is sufficient, because checkAttributesMatch is
+  // recursive, so window.performance.navigation and window.performance.timing
+  // get tested as well.
+  var toTest = [window.performance];
+
+  function checkAttributesMatch(obj, jsonObj) {
+    if (typeof(obj) !== "object") {
+      throw "Expected obj to be of type \"object\". Test failed.";
+    }
+    if (typeof(jsonObj) !== "object") {
+      is(false, "Expected object " + jsonObj + " to be of type object, but gotten otherwise");
+    }
+    for (key in obj) {
+      if (typeof(obj[key]) === "function" || keysToSkip.indexOf(key) > -1)
+        continue;
+      if (typeof(obj[key]) === "object") {
+        checkAttributesMatch(obj[key], jsonObj[key]);
+        continue;
+      }
+      is(jsonObj[key], obj[key], "values for " + obj + " key " + key + " should match");
+    }
+  }
+
+  function runTest() {
+    toTest.forEach(function(testObj) {
+      var jsonCopy = JSON.parse(JSON.stringify(testObj));
+      checkAttributesMatch(testObj, jsonCopy);
+    });
+    SimpleTest.finish();
+  }
+
+  </script>
+</head>
+<body onload="runTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=760851">Mozilla Bug 760851</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <p></p>
+  <p></p>
+  <p></p>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -14,9 +14,11 @@ typedef double DOMHighResTimeStamp;
 
 interface Performance {
   DOMHighResTimeStamp now();
 
   [Constant]
   readonly attribute PerformanceTiming timing;
   [Constant]
   readonly attribute PerformanceNavigation navigation;
+
+  jsonifier;
 };
--- a/dom/webidl/PerformanceNavigation.webidl
+++ b/dom/webidl/PerformanceNavigation.webidl
@@ -13,9 +13,11 @@
 interface PerformanceNavigation {
   const unsigned short TYPE_NAVIGATE = 0;
   const unsigned short TYPE_RELOAD = 1;
   const unsigned short TYPE_BACK_FORWARD = 2;
   const unsigned short TYPE_RESERVED = 255;
 
   readonly attribute unsigned short type;
   readonly attribute unsigned short redirectCount;
+
+  jsonifier;
 };
--- a/dom/webidl/PerformanceTiming.webidl
+++ b/dom/webidl/PerformanceTiming.webidl
@@ -28,9 +28,11 @@ interface PerformanceTiming {
   readonly attribute unsigned long long responseEnd;
   readonly attribute unsigned long long domLoading;
   readonly attribute unsigned long long domInteractive;
   readonly attribute unsigned long long domContentLoadedEventStart;
   readonly attribute unsigned long long domContentLoadedEventEnd;
   readonly attribute unsigned long long domComplete;
   readonly attribute unsigned long long loadEventStart;
   readonly attribute unsigned long long loadEventEnd;
+
+  jsonifier;
 };
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1470,16 +1470,20 @@ JS_GetDataViewData(JSObject *obj);
  */
 class JSJitGetterCallArgs : protected JS::MutableHandleValue
 {
   public:
     explicit JSJitGetterCallArgs(const JS::CallArgs& args)
       : JS::MutableHandleValue(args.rval())
     {}
 
+    explicit JSJitGetterCallArgs(JS::Rooted<JS::Value>* rooted)
+      : JS::MutableHandleValue(rooted)
+    {}
+
     JS::MutableHandleValue rval() {
         return *this;
     }
 };
 
 /*
  * A class, expected to be passed by value, which represents the CallArgs for a
  * JSJitSetterOp.