Bug 1146235 - Part 1: Add support for an [Alias] extended attribute on IDL operations. r=peterv
authorCameron McCormack <cam@mcc.id.au>
Sat, 28 Mar 2015 11:44:39 +1100
changeset 265196 d135a7b7864fdd9cf5043e1a70cca339aea5fa55
parent 265195 8183a5fa421db468925be3b4dedc53afd5c6d3dd
child 265197 665c141c2479169301d8a2a967fb20f3d2e2152c
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1146235
milestone39.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 1146235 - Part 1: Add support for an [Alias] extended attribute on IDL operations. r=peterv
dom/bindings/Codegen.py
dom/bindings/parser/WebIDL.py
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2153,17 +2153,18 @@ class MethodDefiner(PropertyDefiner):
                 "returnsPromise": m.returnsPromise()
             }
             if isChromeOnly(m):
                 self.chrome.append(method)
             else:
                 self.regular.append(method)
 
         # FIXME Check for an existing iterator on the interface first.
-        if any(m.isGetter() and m.isIndexed() for m in methods):
+        if (any(m.isGetter() and m.isIndexed() for m in methods) and
+            not any("@@iterator" in m.aliases for m in methods)):
             self.regular.append({
                 "name": "@@iterator",
                 "methodInfo": False,
                 "selfHostedName": "ArrayValues",
                 "length": 0,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": MemberCondition(None, None)
             })
@@ -2763,19 +2764,75 @@ class CGCreateInterfaceObjectsMethod(CGA
                 if (*protoCache) {
                   js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
                                       JS::ObjectValue(*unforgeableHolder));
                 }
                 """,
                 name=self.descriptor.name))
         else:
             setUnforgeableHolder = None
+
+        aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
+        if aliasedMembers:
+            assert needInterfacePrototypeObject
+
+            def defineAlias(alias):
+                if alias == "@@iterator":
+                    symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))"
+                    getSymbolJSID = CGGeneric(fill("JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});",
+                                                   symbolJSID=symbolJSID))
+                    defineFn = "JS_DefinePropertyById"
+                    prop = "iteratorId"
+                elif alias.startswith("@@"):
+                    raise TypeError("Can't handle any well-known Symbol other than @@iterator")
+                else:
+                    getSymbolJSID = None
+                    defineFn = "JS_DefineProperty"
+                    prop = '"%s"' % alias
+                return CGList([
+                    getSymbolJSID,
+                    # XXX If we ever create non-enumerate properties that can be
+                    #     aliased, we should consider making the aliases match
+                    #     the enumerability of the property being aliased.
+                    CGGeneric(fill("""
+                    if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, JSPROP_ENUMERATE)) {
+                      return;
+                    }
+                    """,
+                    defineFn=defineFn,
+                    prop=prop))
+                ], "\n")
+
+            def defineAliasesFor(m):
+                return CGList([
+                    CGGeneric(fill("""
+                        if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
+                          return;
+                        }
+                        """,
+                        prop=m.identifier.name))
+                ] + [defineAlias(alias) for alias in sorted(m.aliases)])
+
+            defineAliases = CGList([
+                CGGeneric(dedent("""
+                    // Set up aliases on the interface prototype object we just created.
+                    JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx, aGlobal);
+                    if (!proto) {
+                      return;
+                    }
+
+                    """)),
+                CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n")
+            ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
+        else:
+            defineAliases = None
+
         return CGList(
             [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
-             prefCache, CGGeneric(call), createUnforgeableHolder, setUnforgeableHolder],
+             prefCache, CGGeneric(call), defineAliases, createUnforgeableHolder, setUnforgeableHolder],
             "\n").define()
 
 
 class CGGetPerInterfaceObject(CGAbstractMethod):
     """
     A method for getting a per-interface object (a prototype object or interface
     constructor object).
     """
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1058,16 +1058,55 @@ class IDLInterface(IDLObjectWithScope, I
                                           "missing attribute %s" %
                               (attr.identifier.name, iface, putForwards),
                               [attr.location])
 
                     iface = forwardIface
                     attr = fowardAttr
                     putForwards = attr.getExtendedAttribute("PutForwards")
 
+            # Check that the name of an [Alias] doesn't conflict with an
+            # interface member.
+            if member.isMethod():
+                for alias in member.aliases:
+                    if self.isOnGlobalProtoChain():
+                        raise WebIDLError("[Alias] must not be used on a "
+                                          "[Global] interface operation",
+                                          [member.location])
+                    if (member.getExtendedAttribute("Exposed") or
+                        member.getExtendedAttribute("ChromeOnly") or
+                        member.getExtendedAttribute("Pref") or
+                        member.getExtendedAttribute("Func") or
+                        member.getExtendedAttribute("AvailableIn") or
+                        member.getExtendedAttribute("CheckPermissions")):
+                        raise WebIDLError("[Alias] must not be used on a "
+                                          "conditionally exposed operation",
+                                          [member.location])
+                    if member.isStatic():
+                        raise WebIDLError("[Alias] must not be used on a "
+                                          "static operation",
+                                          [member.location])
+                    if member.isIdentifierLess():
+                        raise WebIDLError("[Alias] must not be used on an "
+                                          "identifierless operation",
+                                          [member.location])
+                    if member.isUnforgeable():
+                        raise WebIDLError("[Alias] must not be used on an "
+                                          "[Unforgeable] operation",
+                                          [member.location])
+                    for m in self.members:
+                        if m.identifier.name == alias:
+                            raise WebIDLError("[Alias=%s] has same name as "
+                                              "interface member" % alias,
+                                              [member.location, m.location])
+                        if m.isMethod() and alias in m.aliases:
+                            raise WebIDLError("duplicate [Alias=%s] definitions" %
+                                              alias,
+                                              [member.location, m.location])
+
         if (self.getExtendedAttribute("Pref") and
             self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
             raise WebIDLError("[Pref] used on an interface that is not %s-only" %
                               self.parentScope.primaryGlobalName,
                               [self.location])
 
         if (self.getExtendedAttribute("CheckPermissions") and
             self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
@@ -3149,16 +3188,22 @@ class IDLInterfaceMember(IDLObjectWithId
             raise WebIDLError("Trying to specify multiple different Affects, "
                               "Pure, or Constant extended attributes for "
                               "attribute", [self.location])
         if affects not in IDLInterfaceMember.AffectsValues:
             raise WebIDLError("Invalid [Affects=%s] on attribute" % dependsOn,
                               [self.location])
         self.affects = affects
 
+    def _addAlias(self, alias):
+        if alias in self.aliases:
+            raise WebIDLError("Duplicate [Alias=%s] on attribute" % alias,
+                              [self.location])
+        self.aliases.append(alias)
+
 class IDLConst(IDLInterfaceMember):
     def __init__(self, location, identifier, type, value):
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Const)
 
         assert isinstance(type, IDLType)
         if type.isDictionary():
             raise WebIDLError("A constant cannot be of a dictionary type",
@@ -3745,16 +3790,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert isinstance(stringifier, bool)
         self._stringifier = stringifier
         assert isinstance(jsonifier, bool)
         self._jsonifier = jsonifier
         self._specialType = specialType
         self._unforgeable = False
         self.dependsOn = "Everything"
         self.affects = "Everything"
+        self.aliases = []
 
         if static and identifier.name == "prototype":
             raise WebIDLError("The identifier of a static operation must not be 'prototype'",
                               [location])
 
         self.assertSignatureConstraints()
 
     def __str__(self):
@@ -4078,16 +4124,21 @@ class IDLMethod(IDLInterfaceMember, IDLS
                 raise WebIDLError("[Affects] takes an identifier",
                                   [attr.location])
             self._setAffects(attr.value())
         elif identifier == "DependsOn":
             if not attr.hasValue():
                 raise WebIDLError("[DependsOn] takes an identifier",
                                   [attr.location])
             self._setDependsOn(attr.value())
+        elif identifier == "Alias":
+            if not attr.hasValue():
+                raise WebIDLError("[Alias] takes an identifier or string",
+                                  [attr.location])
+            self._addAlias(attr.value())
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Func" or
               identifier == "AvailableIn" or
               identifier == "CheckPermissions" or