Fix for bug 785277 (Implement PutForwards in the new DOM bindings). r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Mon, 13 Aug 2012 14:20:49 +0200
changeset 111806 461978f9a844c3418db9d639d27739951be433e6
parent 111805 acfb2d79235b74f959fafbef0513881365dc1d1b
child 111807 1413ce44a30fc597cb1affa9b60f3cc32bbadbba
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbz
bugs785277
milestone19.0a1
Fix for bug 785277 (Implement PutForwards in the new DOM bindings). r=bz.
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
dom/bindings/parser/tests/test_putForwards.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/bindings/test/TestExampleGen.webidl
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1113,17 +1113,17 @@ class AttrDefiner(PropertyDefiner):
         def getter(attr):
             native = ("genericLenientGetter" if attr.hasLenientThis()
                       else "genericGetter")
             return ("{(JSPropertyOp)%(native)s, &%(name)s_getterinfo}"
                     % {"name" : attr.identifier.name,
                        "native" : native})
 
         def setter(attr):
-            if attr.readonly:
+            if attr.readonly and attr.getExtendedAttribute("PutForwards") is None:
                 return "JSOP_NULLWRAPPER"
             native = ("genericLenientSetter" if attr.hasLenientThis()
                       else "genericSetter")
             return ("{(JSStrictPropertyOp)%(native)s, &%(name)s_setterinfo}"
                     % {"name" : attr.identifier.name,
                        "native" : native})
 
         def specData(attr):
@@ -3863,16 +3863,41 @@ class CGSpecializedSetter(CGAbstractStat
         return CGIndenter(CGSetterCall(self.attr.type, nativeName,
                                        self.descriptor, self.attr)).define()
 
     @staticmethod
     def makeNativeName(descriptor, attr):
         name = attr.identifier.name
         return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
 
+class CGSpecializedForwardingSetter(CGSpecializedSetter):
+    """
+    A class for generating the code for a specialized attribute setter with
+    PutForwards that the JIT can call with lower overhead.
+    """
+    def __init__(self, descriptor, attr):
+        CGSpecializedSetter.__init__(self, descriptor, attr)
+
+    def definition_body(self):
+        attrName = self.attr.identifier.name
+        forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
+        # JS_GetProperty and JS_SetProperty can only deal with ASCII
+        assert all(ord(c) < 128 for c in attrName)
+        assert all(ord(c) < 128 for c in forwardToAttrName)
+        return CGIndenter(CGGeneric("""js::RootedValue v(cx);
+if (!JS_GetProperty(cx, obj, "%s", v.address())) {
+  return false;
+}
+
+if (!v.isObject()) {
+  return ThrowErrorMessage(cx, MSG_NOT_OBJECT);
+}
+
+return JS_SetProperty(cx, &v.toObject(), "%s", argv);""" % (attrName, forwardToAttrName))).define()
+
 def memberIsCreator(member):
     return member.getExtendedAttribute("Creator") is not None
 
 class CGMemberJITInfo(CGThing):
     """
     A class for generating the JITInfo for a property that points to
     our specialized getter and setter.
     """
@@ -3898,17 +3923,17 @@ class CGMemberJITInfo(CGThing):
 
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" % self.member.identifier.name)
             getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
             getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
             result = self.defineJitInfo(getterinfo, getter, getterinfal)
-            if not self.member.readonly:
+            if not self.member.readonly or self.member.getExtendedAttribute("PutForwards") is not None:
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
                 result += self.defineJitInfo(setterinfo, setter, False)
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
@@ -5315,16 +5340,19 @@ class CGDescriptor(CGThing):
                     else:
                         hasGetter = True
                     if not m.readonly:
                         cgThings.append(CGSpecializedSetter(descriptor, m))
                         if m.hasLenientThis():
                             hasLenientSetter = True
                         else:
                             hasSetter = True
+                    elif m.getExtendedAttribute("PutForwards"):
+                        cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
+                        hasSetter = True
                     cgThings.append(CGMemberJITInfo(descriptor, m))
             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))
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -591,17 +591,17 @@ class IDLInterface(IDLObjectWithScope):
             ancestor.interfacesBasedOnSelf.add(self)
             for ancestorConsequential in ancestor.getConsequentialInterfaces():
                 ancestorConsequential.interfacesBasedOnSelf.add(self)
 
         # Ensure that there's at most one of each {named,indexed}
         # {getter,setter,creator,deleter}.
         specialMembersSeen = set()
         for member in self.members:
-            if member.tag != IDLInterfaceMember.Tags.Method:
+            if not member.isMethod():
                 continue
 
             if member.isGetter():
                 memberType = "getters"
             elif member.isSetter():
                 memberType = "setters"
             elif member.isCreator():
                 memberType = "creators"
@@ -622,16 +622,56 @@ class IDLInterface(IDLObjectWithScope):
                                    [self.location])
 
             specialMembersSeen.add(memberType)
 
     def validate(self):
         for member in self.members:
             member.validate()
 
+            # Check that PutForwards refers to another attribute and that no
+            # cycles exist in forwarded assignments.
+            if member.isAttr():
+                iface = self
+                attr = member
+                putForwards = attr.getExtendedAttribute("PutForwards")
+                if putForwards and self.isCallback():
+                    raise WebIDLError("[PutForwards] used on an attribute "
+                                      "on interface %s which is a callback "
+                                      "interface" % self.identifier.name,
+                                      [self.location, member.location])
+
+                while putForwards is not None:
+                    forwardIface = attr.type.unroll().inner
+                    fowardAttr = None
+
+                    for forwardedMember in forwardIface.members:
+                        if (not forwardedMember.isAttr() or
+                            forwardedMember.identifier.name != putForwards[0]):
+                            continue
+                        if forwardedMember == member:
+                            raise WebIDLError("Cycle detected in forwarded "
+                                              "assignments for attribute %s on "
+                                              "%s" %
+                                              (member.identifier.name, self),
+                                              [member.location])
+                        fowardAttr = forwardedMember
+                        break
+
+                    if fowardAttr is None:
+                        raise WebIDLError("Attribute %s on %s forwards to "
+                                          "missing attribute %s" %
+                              (attr.identifier.name, iface, putForwards),
+                              [attr.location])
+
+                    iface = forwardIface
+                    attr = fowardAttr
+                    putForwards = attr.getExtendedAttribute("PutForwards")
+
+
     def isInterface(self):
         return True
 
     def isExternal(self):
         return False
 
     def setIsConsequentialInterfaceOf(self, other):
         self._consequential = True
@@ -1998,16 +2038,19 @@ class IDLAttribute(IDLInterfaceMember):
                                       "types, and so on) is a dictionary "
                                       "type", [self.location, f.location])
                 if f.isSequence():
                     raise WebIDLError("An attribute cannot be of a union "
                                       "type if one of its member types (or "
                                       "one of its member types's member "
                                       "types, and so on) is a sequence "
                                       "type", [self.location, f.location])
+        if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
+            raise WebIDLError("An attribute with [PutForwards] must have an "
+                              "interface type as its type", [self.location])
 
     def validate(self):
         pass
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
         if identifier == "TreatNonCallableAsNull":
             raise WebIDLError("TreatNonCallableAsNull cannot be specified on attributes",
@@ -2024,16 +2067,35 @@ class IDLAttribute(IDLInterfaceMember):
                 raise WebIDLError("[LenientThis] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             self.lenientThis = True
         elif identifier == "Unforgeable":
             if not self.readonly:
                 raise WebIDLError("[Unforgeable] is only allowed on readonly "
                                   "attributes", [attr.location, self.location])
             self._unforgeable = True
+        elif identifier == "PutForwards":
+            if not self.readonly:
+                raise WebIDLError("[PutForwards] is only allowed on readonly "
+                                  "attributes", [attr.location, self.location])
+            if self.isStatic():
+                raise WebIDLError("[PutForwards] is only allowed on non-static "
+                                  "attributes", [attr.location, self.location])
+            if self.getExtendedAttribute("Replaceable") is not None:
+                raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+                                  "appear on the same attribute",
+                                  [attr.location, self.location])
+            if not attr.hasValue():
+                raise WebIDLError("[PutForwards] takes an identifier",
+                                  [attr.location, self.location])
+        elif identifier == "Replaceable":
+            if self.getExtendedAttribute("PutForwards") is not None:
+                raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+                                  "appear on the same attribute",
+                                  [attr.location, self.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
     def resolve(self, parentScope):
         assert isinstance(parentScope, IDLScope)
         self.type.resolveType(parentScope)
         IDLObjectWithIdentifier.resolve(self, parentScope)
 
     def addExtendedAttributes(self, attrs):
@@ -2497,16 +2559,19 @@ class IDLMethod(IDLInterfaceMember, IDLS
         elif identifier == "SetterInfallible":
             raise WebIDLError("Methods must not be flagged as "
                               "[SetterInfallible]",
                               [attr.location, self.location])
         elif identifier == "Unforgeable":
             raise WebIDLError("Methods must not be flagged as "
                               "[Unforgeable]",
                               [attr.location, self.location])
+        elif identifier == "PutForwards":
+            raise WebIDLError("Only attributes support [PutForwards]",
+                              [attr.location, self.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
 class IDLImplementsStatement(IDLObject):
     def __init__(self, location, implementor, implementee):
         IDLObject.__init__(self, location)
         self.implementor = implementor;
         self.implementee = implementee
 
new file mode 100644
--- /dev/null
+++ b/dom/bindings/parser/tests/test_putForwards.py
@@ -0,0 +1,107 @@
+def WebIDLTest(parser, harness):
+    threw = False
+    try:
+        parser.parse("""
+            interface I {
+              [PutForwards=B] readonly attribute long A;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface I {
+              [PutForwards=B] readonly attribute J A;
+            };
+            interface J {
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface I {
+              [PutForwards=B] attribute J A;
+            };
+            interface J {
+              attribute long B;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface I {
+              [PutForwards=B] static readonly attribute J A;
+            };
+            interface J {
+              attribute long B;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            callback interface I {
+              [PutForwards=B] readonly attribute J A;
+            };
+            interface J {
+              attribute long B;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface I {
+              [PutForwards=C] readonly attribute J A;
+              [PutForwards=C] readonly attribute J B;
+            };
+            interface J {
+              [PutForwards=D] readonly attribute K C;
+            };
+            interface K {
+              [PutForwards=A] readonly attribute I D;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown.")
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -429,16 +429,19 @@ public:
   void ExerciseTypedefInterfaces3(TestInterface&);
 
   // Miscellania
   int32_t AttrWithLenientThis();
   void SetAttrWithLenientThis(int32_t);
   uint32_t UnforgeableAttr();
   uint32_t UnforgeableAttr2();
   void PassRenamedInterface(nsRenamedInterface&);
+  TestInterface* PutForwardsAttr();
+  TestInterface* PutForwardsAttr2();
+  TestInterface* PutForwardsAttr3();
 
   // Methods and properties imported via "implements"
   bool ImplementedProperty();
   void SetImplementedProperty(bool);
   void ImplementedMethod();
   bool ImplementedParentProperty();
   void SetImplementedParentProperty(bool);
   void ImplementedParentMethod();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -334,16 +334,19 @@ interface TestInterface {
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   void passRenamedInterface(TestRenamedInterface arg);
+  [PutForwards=writableByte] readonly attribute TestInterface putForwardsAttr;
+  [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
+  [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
 
   // If you add things here, add them to TestExampleGen as well
 };
 
 interface TestNonWrapperCacheInterface {
 };
 
 interface ImplementedInterfaceParent {
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -301,11 +301,14 @@ interface TestExampleInterface {
   AnotherNameForTestInterface exerciseTypedefInterfaces2(NullableTestInterface arg);
   void exerciseTypedefInterfaces3(YetAnotherNameForTestInterface arg);
 
   // Miscellania
   [LenientThis] attribute long attrWithLenientThis;
   [Unforgeable] readonly attribute long unforgeableAttr;
   [Unforgeable, ChromeOnly] readonly attribute long unforgeableAttr2;
   void passRenamedInterface(TestRenamedInterface arg);
+  [PutForwards=writableByte] readonly attribute TestExampleInterface putForwardsAttr;
+  [PutForwards=writableByte, LenientThis] readonly attribute TestExampleInterface putForwardsAttr2;
+  [PutForwards=writableByte, ChromeOnly] readonly attribute TestExampleInterface putForwardsAttr3;
 
   // If you add things here, add them to TestCodeGen as well
 };