Bug 1895261 - Only generate an equality operator for WebIDL dictionaries when they have a [GenerateEqualityOperator] extended attribute. r=farre

This also extends the check for supported types to the types of inherited
members, and automatically generates an equality operator in the base classes if
needed. It also deletes the equality operator if a dictionary doesn't need
one but its parent does.

Differential Revision: https://phabricator.services.mozilla.com/D210846
This commit is contained in:
Peter Van der Beken 2024-05-21 14:20:35 +00:00
parent 4f5ef67dd3
commit b6e624e324
4 changed files with 159 additions and 14 deletions

View file

@ -13629,12 +13629,14 @@ class ClassMethod(ClassItem):
override=False,
canRunScript=False,
noDiscard=False,
delete=False,
):
"""
override indicates whether to flag the method as override
"""
assert not override or virtual
assert not (override and static)
assert not (delete and body)
self.returnType = returnType
self.args = args
self.inline = inline or bodyInHeader
@ -13649,6 +13651,7 @@ class ClassMethod(ClassItem):
self.override = override
self.canRunScript = canRunScript
self.noDiscard = noDiscard
self.delete = delete
ClassItem.__init__(self, name, visibility)
def getDecorators(self, declaring):
@ -13680,7 +13683,9 @@ class ClassMethod(ClassItem):
else ""
)
args = ", ".join([a.declare() for a in self.args])
if self.bodyInHeader:
if self.delete:
body = " = delete;\n"
elif self.bodyInHeader:
body = indent(self.getBody())
body = "\n{\n" + body + "}\n"
else:
@ -13703,7 +13708,7 @@ class ClassMethod(ClassItem):
)
def define(self, cgClass):
if self.bodyInHeader:
if self.delete or self.bodyInHeader:
return ""
templateArgs = cgClass.templateArgs
@ -17691,23 +17696,48 @@ class CGDictionary(CGThing):
body=body.define(),
)
def canHaveEqualsOperator(self):
return all(
m.type.isString() or m.type.isPrimitive() for (m, _) in self.memberInfo
)
def equalityOperator(self):
# For now we only allow equality operators if our members have a string
# type, a primitive type or an enum type.
if not all(
m.type.isString() or m.type.isPrimitive() or m.type.isEnum()
for m in self.dictionary.members
):
err = (
"[GenerateEqualityOperator] set on %s, but it"
% self.dictionary.needsEqualityOperator.identifier.name
)
if self.dictionary.needsEqualityOperator != self.dictionary:
err += "s ancestor %s" % self.dictionary.identifier.name
err += " contains types other than string, primitive or enum types."
raise TypeError(err)
def equalsOperator(self):
body = CGList([])
if self.dictionary.parent:
# If we have a parent dictionary we have to call its equals
# operator.
parentTest = CGGeneric(
fill(
"""
if (!${base}::operator==(aOther)) {
return false;
}
""",
base=self.makeClassName(self.dictionary.parent),
)
)
body.append(parentTest)
for m, _ in self.memberInfo:
memberName = self.makeMemberName(m.identifier.name)
memberTest = CGGeneric(
fill(
"""
if (${memberName} != aOther.${memberName}) {
return false;
}
""",
if (${memberName} != aOther.${memberName}) {
return false;
}
""",
memberName=memberName,
)
)
@ -17829,8 +17859,22 @@ class CGDictionary(CGThing):
else:
disallowCopyConstruction = True
if self.canHaveEqualsOperator():
methods.append(self.equalsOperator())
if d.needsEqualityOperator:
methods.append(self.equalityOperator())
elif d.parent and d.parent.needsEqualityOperator:
methods.append(
ClassMethod(
"operator==",
"bool",
[
Argument(
"const %s&" % self.makeClassName(self.dictionary), "aOther"
)
],
visibility="public",
delete=True,
)
)
struct = CGClass(
selfName,

View file

@ -2232,6 +2232,7 @@ class IDLDictionary(IDLObjectWithScope):
"_extendedAttrDict",
"needsConversionToJS",
"needsConversionFromJS",
"needsEqualityOperator",
)
def __init__(self, location, parentScope, name, parent, members):
@ -2246,6 +2247,7 @@ class IDLDictionary(IDLObjectWithScope):
self._extendedAttrDict = {}
self.needsConversionToJS = False
self.needsConversionFromJS = False
self.needsEqualityOperator = None
IDLObjectWithScope.__init__(self, location, parentScope, name)
@ -2310,6 +2312,14 @@ class IDLDictionary(IDLObjectWithScope):
[self.identifier.location],
)
inheritedMembers.extend(ancestor.members)
if (
self.getExtendedAttribute("GenerateEqualityOperator")
and ancestor.needsEqualityOperator is None
):
# Store the dictionary that has the [GenerateEqualityOperator]
# extended attribute, so we can use it when generating error
# messages.
ancestor.needsEqualityOperator = self
ancestor = ancestor.parent
# Catch name duplication
@ -2435,6 +2445,13 @@ class IDLDictionary(IDLObjectWithScope):
# implement ToJSON by converting to a JS object and
# then using JSON.stringify.
self.needsConversionToJS = True
elif identifier == "GenerateEqualityOperator":
if not attr.noArguments():
raise WebIDLError(
"[GenerateEqualityOperator] must take no arguments",
[attr.location],
)
self.needsEqualityOperator = self
elif identifier == "Unsorted":
if not attr.noArguments():
raise WebIDLError(

View file

@ -933,7 +933,7 @@ dictionary ParentProcInfoDictionary {
* serialization, deserialization, and inheritance.
* (3) Update the methods on mozilla::OriginAttributesPattern, including matching.
*/
[GenerateInitFromJSON]
[GenerateInitFromJSON, GenerateEqualityOperator]
dictionary OriginAttributesDictionary {
unsigned long userContextId = 0;
unsigned long privateBrowsingId = 0;

View file

@ -1903,6 +1903,90 @@ worker involved is a `ChromeWorker` or not. At the moment the only
possible caller types are `System` (representing system-principal
callers) and `NonSystem`.
### `[GenerateInit]`
When set on a dictionary it will add two `Init` methods to the generated C++
class with the following signatures:
``` cpp
bool Init(BindingCallContext& cx, JS::Handle<JS::Value> val, const char* sourceDescription="Value", bool passedToJSImpl=false);
bool Init(JSContext* cx_, JS::Handle<JS::Value> val, const char* sourceDescription="Value", bool passedToJSImpl=false);
```
These methods will initialize the dictionary from `val` by following WebIDL's
[JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary).
### `[GenerateInitFromJSON]`
When set on a dictionary it will add an `Init` method to the generated C++
class with the following signature:
``` cpp
bool Init(const nsAString& aJSON);
```
This extended attribute will only have an effect if all of the types of the
dictionary's members are representable in JSON (they are a string type, a
primitive type that's not an unrestricted float/double, a void type, or a
sequence, union, dictionary or record containing these types).
The method is expected to be called with a JSON string as input. The JSON string
will be parsed into a JavaScript value, and then the dictionary is initialized
with this value by following WebIDL's
[JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary).
Note: As a side-effect of how this is implemented it will also add the two
`Init` methods that would be added by a [`[GenerateInit]`](#generateinit)
extended attribute.
### `[GenerateToJSON]`
When set on a dictionary it will add a `ToJSON` method to the generated C++
class with the following signature:
``` cpp
bool ToJSON(nsAString& aJSON);
```
The method will generate a JSON representation of the dictionary members' values
in `aJSON` by converting the dictionary to a JavaScript object by following
WebIDL's [JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary)
and then converting that object to a JSON string.
The same restrictions on types applies as on
[`[GenerateInitFromJSON]`](#generateinitfromjson).
Note: As a side-effect of how this is implemented it will also add the
`ToObjectInternal` method that would be added by a
[`[GenerateConversionToJS]`](#generateconversiontojs) extended attribute.
### `[GenerateConversionToJS]`
When set on a dictionary it will add a `ToObjectInternal` method to the
generated C++ class with the following signature:
``` cpp
bool ToObjectInternal(JSContext* cx, JS::MutableHandle<JS::Value> rval);
```
The method will create a JavaScript object by following WebIDL's
[JavaScript type mapping](https://webidl.spec.whatwg.org/#js-dictionary).
### `[GenerateEqualityOperator]`
When set on a dictionary it will add an equality operator to the generated C++
class.
This is only allowed on dictionaries who only have members (own or inherited)
with string, primitive or enum types.
### `[Unsorted]`
When set on a dictionary the dictionary's members will not be sorted in
lexicographic order (which is specified by WebIDL).
This should only ever be used on internal APIs that are not exposed to the Web!
## Helper objects
The C++ side of the bindings uses a number of helper objects.