Bug 788225. Implement WebIDL deleters. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 05 Nov 2012 11:58:02 -0500
changeset 112321 b67a1dba5690052f4c4c0785732321c6a3c05fa9
parent 112320 6f08c574fb7968845fb886de1e1726f6886c7b1d
child 112322 6184b17f8abd6c1166d3e1c629900a4483638308
push id23812
push useremorley@mozilla.com
push dateTue, 06 Nov 2012 14:01:34 +0000
treeherdermozilla-central@f4aeed115e54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs788225
milestone19.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 788225. Implement WebIDL deleters. r=peterv
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -591,16 +591,41 @@ DOMInterfaces = {
         },
 
 'TestRenamedInterface' : {
         'headerFile': 'TestBindingHeader.h',
         'register': False,
         'nativeType': 'nsRenamedInterface'
         },
 
+'TestIndexedDeleterInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False
+        },
+
+'TestIndexedDeleterWithRetvalInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False
+        },
+
+'TestNamedDeleterInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False
+        },
+
+'TestNamedDeleterWithRetvalInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False
+        },
+
+'TestIndexedAndNamedDeleterInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False
+        },
+
 'TestCppKeywordNamedMethodsInterface' : {
         'headerFile': 'TestBindingHeader.h',
         'register': False
         },
 
 'TestExampleInterface' : {
         # Keep this in sync with TestInterface
         'headerFile': 'TestExampleInterface-example.h',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5028,22 +5028,22 @@ class CGProxySpecialOperation(CGPerSigna
                                                        treatUndefinedAs=argument.treatUndefinedAs)
             templateValues = {
                 "declName": argument.identifier.name,
                 "holderName": argument.identifier.name + "_holder",
                 "val": "desc->value",
                 "valPtr": "&desc->value"
             }
             self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
-        elif operation.isGetter():
+        elif operation.isGetter() or operation.isDeleter():
             self.cgRoot.prepend(CGGeneric("bool found;"))
 
     def getArguments(self):
         args = [(a, a.identifier.name) for a in self.arguments]
-        if self.idlNode.isGetter():
+        if self.idlNode.isGetter() or self.idlNode.isDeleter():
             args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
                                       self.idlNode),
                          "found"))
         return args
 
     def wrap_return_value(self):
         if not self.idlNode.isGetter() or self.templateValues is None:
             return ""
@@ -5079,16 +5079,30 @@ class CGProxyNamedGetter(CGProxySpecialO
 
 class CGProxyNamedSetter(CGProxySpecialOperation):
     """
     Class to generate a call to a named setter.
     """
     def __init__(self, descriptor):
         CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
 
+class CGProxyIndexedDeleter(CGProxySpecialOperation):
+    """
+    Class to generate a call to an indexed deleter.
+    """
+    def __init__(self, descriptor):
+        CGProxySpecialOperation.__init__(self, descriptor, 'IndexedDeleter')
+
+class CGProxyNamedDeleter(CGProxySpecialOperation):
+    """
+    Class to generate a call to a named deleter.
+    """
+    def __init__(self, descriptor):
+        CGProxySpecialOperation.__init__(self, descriptor, 'NamedDeleter')
+
 class CGProxyIsProxy(CGAbstractMethod):
     def __init__(self, descriptor):
         args = [Argument('JSObject*', 'obj')]
         CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
     def declare(self):
         return ""
     def definition_body(self):
         return "  return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();"
@@ -5269,16 +5283,92 @@ class CGDOMJSProxyHandler_defineProperty
                     "  %s* self = UnwrapProxy(proxy);\n" +
                     CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
                     "  if (found) {\n"
                     "    return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
                     "  }\n" +
                     "}\n") % (self.descriptor.nativeType, self.descriptor.name)
         return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
 
+class CGDOMJSProxyHandler_delete(ClassMethod):
+    def __init__(self, descriptor):
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
+                Argument('jsid', 'id'),
+                Argument('bool*', 'bp')]
+        ClassMethod.__init__(self, "delete_", "bool", args)
+        self.descriptor = descriptor
+
+    def getBody(self):
+        def getDeleterBody(type):
+            """
+            type should be "Named" or "Indexed"
+            """
+            assert type is "Named" or type is "Indexed"
+            deleter = self.descriptor.operations[type + 'Deleter']
+            if deleter:
+                if (not deleter.signatures()[0][0].isPrimitive() or
+                    deleter.signatures()[0][0].nullable() or
+                    deleter.signatures()[0][0].tag() != IDLType.Tags.bool):
+                    setBp = "*bp = true;"
+                else:
+                    setBp = ("if (found) {\n"
+                             "  // XXXbz we should throw as needed if Throw is true\n"
+                             "  *bp = result;\n"
+                             "} else {\n"
+                             "  *bp = true;\n"
+                             "}")
+                body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() +
+                        setBp)
+            elif self.descriptor.operations[type + 'Getter']:
+                # XXXbz: We should be doing this for cases when we have a
+                # creator or setter too, but we have no way to find out
+                # whether the property name is supported in those cases!
+                body = (eval("CGProxy%sGetter" % type)(self.descriptor).define() +
+                        "if (found) {\n"
+                        "  // XXXbz we should throw if Throw is true!\n"
+                        "  *bp = false;\n"
+                        "} else {\n"
+                        "  *bp = true;\n"
+                        "}")
+            else:
+                body = None
+            return body
+
+        delete = ""
+
+        indexedBody = getDeleterBody("Indexed")
+        if indexedBody is not None:
+            delete += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
+                       "if (index >= 0) {\n" +
+                       "  %s* self = UnwrapProxy(proxy);\n" +
+                       CGIndenter(CGGeneric(indexedBody)).define() + "\n"
+                       "  // We always return here, even if the property was not found\n"
+                       "  return true;\n" +
+                       "}\n") % self.descriptor.nativeType
+
+        namedBody = getDeleterBody("Named")
+        if namedBody is not None:
+            delete += ("if (JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n"
+                       "  jsval nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+                       "  FakeDependentString name;\n"
+                       "  if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+                       "                              eStringify, eStringify, name)) {\n" +
+                       "    return false;\n" +
+                       "  }\n" +
+                       "  %s* self = UnwrapProxy(proxy);\n" +
+                       CGIndenter(CGGeneric(namedBody)).define() + "\n"
+                       "  if (found) {\n"
+                       "    return true;\n"
+                       "  }\n"
+                       "}\n") % self.descriptor.nativeType
+
+        delete += "return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);";
+
+        return delete
+
 class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'proxy'),
                 Argument('JS::AutoIdVector&', 'props')]
         ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args)
         self.descriptor = descriptor
     def getBody(self):
         indexedGetter = self.descriptor.operations['IndexedGetter']
@@ -5514,17 +5604,18 @@ class CGDOMJSProxyHandler(CGClass):
         if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
             methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
         methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
                         CGDOMJSProxyHandler_hasOwn(descriptor),
                         CGDOMJSProxyHandler_get(descriptor),
                         CGDOMJSProxyHandler_obj_toString(descriptor),
                         CGDOMJSProxyHandler_finalize(descriptor),
                         CGDOMJSProxyHandler_getElementIfPresent(descriptor),
-                        CGDOMJSProxyHandler_getInstance()])
+                        CGDOMJSProxyHandler_getInstance(),
+                        CGDOMJSProxyHandler_delete(descriptor)])
         CGClass.__init__(self, 'DOMProxyHandler',
                          bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
                          constructors=constructors,
                          methods=methods)
 
 def stripTrailingWhitespace(text):
     tail = '\n' if text.endswith('\n') else ''
     lines = text.splitlines()
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -229,19 +229,16 @@ class Descriptor(DescriptorProvider):
                     if m.isGetter():
                         addIndexedOrNamedOperation('Getter', m)
                     if m.isSetter():
                         addIndexedOrNamedOperation('Setter', m)
                     if m.isCreator():
                         addIndexedOrNamedOperation('Creator', m)
                     if m.isDeleter():
                         addIndexedOrNamedOperation('Deleter', m)
-                        raise TypeError("deleter specified on %s but we "
-                                        "don't support deleters yet" %
-                                        self.interface.identifier.name)
 
                 iface.setUserData('hasConcreteDescendant', True)
                 iface = iface.parent
 
             if self.proxy:
                 iface = self.interface
                 while iface:
                     iface.setUserData('hasProxyDescendant', True)
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -691,12 +691,86 @@ public:
   // We need a GetParentObject to make binding codegen happy
   virtual nsISupports* GetParentObject();
 
   bool Continue();
   bool Delete();
   int32_t Volatile();
 };
 
+class TestIndexedDeleterInterface : public nsISupports,
+                                    public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // We need a GetParentObject to make binding codegen happy
+  virtual nsISupports* GetParentObject();
+
+  void IndexedDeleter(uint32_t, bool&);
+  void IndexedDeleter(uint32_t) MOZ_DELETE;
+  void DelItem(uint32_t);
+  void DelItem(uint32_t, bool&) MOZ_DELETE;
+};
+
+class TestIndexedDeleterWithRetvalInterface : public nsISupports,
+                                              public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // We need a GetParentObject to make binding codegen happy
+  virtual nsISupports* GetParentObject();
+
+  bool IndexedDeleter(uint32_t, bool&);
+  bool IndexedDeleter(uint32_t) MOZ_DELETE;
+  bool DelItem(uint32_t);
+  bool DelItem(uint32_t, bool&) MOZ_DELETE;
+};
+
+class TestNamedDeleterInterface : public nsISupports,
+                                  public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // We need a GetParentObject to make binding codegen happy
+  virtual nsISupports* GetParentObject();
+
+  void NamedDeleter(const nsAString&, bool&);
+};
+
+class TestNamedDeleterWithRetvalInterface : public nsISupports,
+                                            public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // We need a GetParentObject to make binding codegen happy
+  virtual nsISupports* GetParentObject();
+
+  bool NamedDeleter(const nsAString&, bool&);
+  bool NamedDeleter(const nsAString&) MOZ_DELETE;
+  bool DelNamedItem(const nsAString&);
+  bool DelNamedItem(const nsAString&, bool&) MOZ_DELETE;
+};
+
+class TestIndexedAndNamedDeleterInterface : public nsISupports,
+                                            public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // We need a GetParentObject to make binding codegen happy
+  virtual nsISupports* GetParentObject();
+
+  void IndexedDeleter(uint32_t, bool&);
+
+  void NamedDeleter(const nsAString&, bool&);
+  void NamedDeleter(const nsAString&) MOZ_DELETE;
+  void DelNamedItem(const nsAString&);
+  void DelNamedItem(const nsAString&, bool&) MOZ_DELETE;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* TestBindingHeader_h */
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -458,14 +458,35 @@ interface TestIndexedAndNamedGetterAndSe
   setter creator void (unsigned long index, long item);
   setter creator void (DOMString name, DOMString item);
   [Infallible]
   stringifier DOMString ();
   [Infallible]
   readonly attribute unsigned long length;
 };
 
+interface TestIndexedDeleterInterface {
+  deleter void delItem(unsigned long index);
+};
+
+interface TestIndexedDeleterWithRetvalInterface {
+  deleter boolean delItem(unsigned long index);
+};
+
+interface TestNamedDeleterInterface {
+  deleter void (DOMString name);
+};
+
+interface TestNamedDeleterWithRetvalInterface {
+  deleter boolean delNamedItem(DOMString name);
+};
+
+interface TestIndexedAndNamedDeleterInterface {
+  deleter void (unsigned long index);
+  deleter void delNamedItem(DOMString name);
+};
+
 interface TestCppKeywordNamedMethodsInterface {
   boolean continue();
   boolean delete();
   long volatile();
 };