Bug 832014 part 2. Add codegen support for [Unforgeable] interfaces. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 11 Jul 2014 19:30:27 -0400
changeset 215624 d52ad359c13246173c00f3c085442f96d004805b
parent 215623 e437d7fa90a5dc06a399b432eadc79a02877b0ae
child 215625 b517ace62a49b55d85b809b5428e27b26a19bfa3
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs832014
milestone33.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 832014 part 2. Add codegen support for [Unforgeable] interfaces. r=peterv
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -905,16 +905,24 @@ GetInterfaceImpl(JSContext* aCx, nsIInte
   }
 
   if (!WrapObject(aCx, result, iid, aRetval)) {
     aError.Throw(NS_ERROR_FAILURE);
   }
 }
 
 bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  args.rval().set(args.thisv());
+  return true;
+}
+
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
 {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1696,16 +1696,19 @@ template<class T>
 void
 GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID,
              JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
   GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
 }
 
 bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
 // vp is allowed to be null; in that case no get will be attempted,
 // and *found will simply indicate whether the property exists.
 bool
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -484,20 +484,32 @@ def PrototypeIDAndDepth(descriptor):
             prototypeID += "_workers"
         depth = "PrototypeTraits<%s>::Depth" % prototypeID
     else:
         prototypeID += "_ID_Count"
         depth = "0"
     return (prototypeID, depth)
 
 
+def MemberIsUnforgeable(member, descriptor):
+    # Note: "or" and "and" return either their LHS or RHS, not
+    # necessarily booleans.  Make sure to return a boolean from this
+    # method, because callers will compare its return value to
+    # booleans.
+    return bool((member.isAttr() or member.isMethod()) and
+                not member.isStatic() and
+                (member.isUnforgeable() or
+                 descriptor.interface.getExtendedAttribute("Unforgeable")))
+
+
 def UseHolderForUnforgeable(descriptor):
     return (descriptor.concrete and
             descriptor.proxy and
-            any(m for m in descriptor.interface.members if (m.isAttr() or m.isMethod()) and m.isUnforgeable()))
+            any(m for m in descriptor.interface.members
+                if MemberIsUnforgeable(m, descriptor)))
 
 
 def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None,
                             useSharedRoot=False):
     """
     Generate the code to execute the code in "code" on an unforgeable holder if
     needed. code should be a string containing the code to execute. If it
     contains a ${holder} string parameter it will be replaced with the
@@ -1994,17 +2006,17 @@ class MethodDefiner(PropertyDefiner):
         # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
         #       We should be able to check for special operations without an
         #       identifier. For now we check if the name starts with __
 
         # Ignore non-static methods for interfaces without a proto object
         if descriptor.interface.hasInterfacePrototypeObject() or static:
             methods = [m for m in descriptor.interface.members if
                        m.isMethod() and m.isStatic() == static and
-                       m.isUnforgeable() == unforgeable and
+                       MemberIsUnforgeable(m, descriptor) == unforgeable and
                        not m.isIdentifierLess()]
         else:
             methods = []
         self.chrome = []
         self.regular = []
         for m in methods:
             if m.identifier.name == 'queryInterface':
                 if self.descriptor.workers:
@@ -2067,17 +2079,18 @@ class MethodDefiner(PropertyDefiner):
                 "selfHostedName": "ArrayValues",
                 "length": 0,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": MemberCondition(None, None)
             })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
-            if stringifier:
+            if (stringifier and
+                unforgeable == MemberIsUnforgeable(stringifier, descriptor)):
                 toStringDesc = {
                     "name": "toString",
                     "nativeName": stringifier.identifier.name,
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
                 }
                 if isChromeOnly(stringifier):
@@ -2092,16 +2105,28 @@ class MethodDefiner(PropertyDefiner):
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
                 }
                 if isChromeOnly(jsonifier):
                     self.chrome.append(toJSONDesc)
                 else:
                     self.regular.append(toJSONDesc)
+            if (unforgeable and
+                descriptor.interface.getExtendedAttribute("Unforgeable")):
+                # Synthesize our valueOf method
+                self.regular.append({
+                    "name": 'valueOf',
+                    "nativeName": "UnforgeableValueOf",
+                    "methodInfo": False,
+                    "length": 0,
+                    "flags": "JSPROP_ENUMERATE", # readonly/permanent added
+                                                 # automatically.
+                    "condition": MemberCondition(None, None)
+                })
         elif (descriptor.interface.isJSImplemented() and
               descriptor.interface.hasInterfaceObject()):
             self.chrome.append({
                 "name": '_create',
                 "nativeName": ("%s::_Create" % descriptor.name),
                 "methodInfo": False,
                 "length": 2,
                 "flags": "0",
@@ -2122,17 +2147,17 @@ class MethodDefiner(PropertyDefiner):
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def condition(m, d):
             return m["condition"]
 
         def flags(m):
-            unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
+            unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if self.unforgeable else ""
             return m["flags"] + unforgeable
 
         def specData(m):
             if "selfHostedName" in m:
                 selfHostedName = '"%s"' % m["selfHostedName"]
                 assert not m.get("methodInfo", True)
                 accessor = "nullptr"
                 jitinfo = "nullptr"
@@ -2179,17 +2204,17 @@ 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 interfaces without a proto object
         if descriptor.interface.hasInterfacePrototypeObject() or static:
             attributes = [m for m in descriptor.interface.members if
                           m.isAttr() and m.isStatic() == static and
-                          m.isUnforgeable() == unforgeable]
+                          MemberIsUnforgeable(m, descriptor) == unforgeable]
         else:
             attributes = []
         self.chrome = [m for m in attributes if isChromeOnly(m)]
         self.regular = [m for m in attributes if not isChromeOnly(m)]
         self.static = static
         self.unforgeable = unforgeable
 
         if static:
@@ -2781,45 +2806,63 @@ def CreateBindingJSObject(descriptor, pr
 
 
 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
     """
     properties is a PropertyArrays instance
     """
     unforgeables = []
 
+    if failureReturnValue:
+        failureReturnValue = " " + failureReturnValue
+    else:
+        failureReturnValue = ""
+
     defineUnforgeableAttrs = fill(
         """
         if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) {
           return${rv};
         }
         """,
         obj=obj,
-        rv=" " + failureReturnValue if failureReturnValue else "")
+        rv=failureReturnValue)
     defineUnforgeableMethods = fill(
         """
         if (!DefineUnforgeableMethods(aCx, ${obj}, %s)) {
           return${rv};
         }
         """,
         obj=obj,
-        rv=" " + failureReturnValue if failureReturnValue else "")
+        rv=failureReturnValue)
 
     unforgeableMembers = [
         (defineUnforgeableAttrs, properties.unforgeableAttrs),
         (defineUnforgeableMethods, properties.unforgeableMethods)
     ]
     for (template, array) in unforgeableMembers:
         if array.hasNonChromeOnly():
             unforgeables.append(CGGeneric(template % array.variableName(False)))
         if array.hasChromeOnly():
             unforgeables.append(
                 CGIfWrapper(CGGeneric(template % array.variableName(True)),
                             "nsContentUtils::ThreadsafeIsCallerChrome()"))
 
+    if descriptor.interface.getExtendedAttribute("Unforgeable"):
+        # We do our undefined toJSON here, not as a regular property
+        # because we don't have a concept of value props anywhere.
+        unforgeables.append(CGGeneric(fill(
+            """
+            if (!JS_DefineProperty(aCx, ${obj}, "toJSON", JS::UndefinedHandleValue,
+                                   JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
+              return${rv};
+            }
+            """,
+            obj=obj,
+            rv=failureReturnValue)))
+
     return CGList(unforgeables)
 
 
 def InitUnforgeableProperties(descriptor, properties):
     """
     properties is a PropertyArrays instance
     """
     unforgeableAttrs = properties.unforgeableAttrs