Bug 1803752 - Make CSS2Properties getters and setters use a common generated implementation. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D181106
This commit is contained in:
Peter Van der Beken 2023-06-20 16:32:06 +00:00
parent 87e05a8a95
commit f1c25edf31
8 changed files with 722 additions and 105 deletions

View File

@ -2125,3 +2125,31 @@ addExternalIface('nsISessionStoreRestoreData',
headerFile='nsISessionStoreRestoreData.h', notflattened=True)
addExternalIface('nsIScreen', nativeType='nsIScreen',
headerFile='nsIScreen.h', notflattened=True)
# The TemplatedAttributes dictionary has the interface name where the template
# should be generated as the key. The values are lists of dictionaries, where
# each dictionary corresponds to one template. The dictionary contains:
#
# template the template's name
# getter the name for the native getter to call
# setter the name for the native setter to call
# argument a tuple for the additional argument that should be passed to the
# native getter and setter, containing the type for the argument
# and a name for the argument. The value will be supplied by the
# [BindingTemplate] extended attribute.
# attrName a string which in the generated C++ code would yield a
# |const char*| that contains the attribute's name
TemplatedAttributes = {
'CSS2Properties': [
{
'template': 'CSS2Property',
'getter': 'GetPropertyValue',
'setter': 'SetPropertyValue',
'argument': ('nsCSSPropertyID', 'id'),
'attrName': 'nsCSSProps::PropertyIDLName(id)',
},
],
}

View File

@ -1927,7 +1927,7 @@ class CGAbstractMethod(CGThing):
prologue += indent(
fill(
"""
BindingCallContext ${cxname}(cx_, "${label}");
BindingCallContext ${cxname}(cx_, ${label});
""",
cxname=cxname,
label=error_reporting_label,
@ -8989,6 +8989,14 @@ class CGPerSignatureCall(CGThing):
dontSetSlot should be set to True if the value should not be cached in a
slot (even if the attribute is marked as StoreInSlot or Cached in the
WebIDL).
errorReportingLabel can contain a custom label to use for error reporting.
It will be inserted as is in the code, so if it needs to be a literal
string in C++ it should be quoted.
additionalArgsPre contains additional arguments that are added after the
arguments that CGPerSignatureCall itself adds (JSContext, global, ), and
before the actual arguments.
"""
# XXXbz For now each entry in the argument list is either an
@ -9013,6 +9021,8 @@ class CGPerSignatureCall(CGThing):
objectName="obj",
dontSetSlot=False,
extendedAttributes=None,
errorReportingLabel=None,
additionalArgsPre=[],
):
assert idlNode.isMethod() == (not getter and not setter)
assert idlNode.isAttr() == (getter or setter)
@ -9322,14 +9332,17 @@ class CGPerSignatureCall(CGThing):
assert setter
cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode))
else:
context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
if getter:
context = context + " getter"
elif setter:
context = context + " setter"
# Callee expects a quoted string for the context if
# there's a context.
context = '"%s"' % context
if errorReportingLabel is None:
context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
if getter:
context = context + " getter"
elif setter:
context = context + " setter"
# Callee expects a quoted string for the context if
# there's a context.
context = '"%s"' % context
else:
context = errorReportingLabel
if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"):
[
@ -9346,7 +9359,7 @@ class CGPerSignatureCall(CGThing):
needsCallerType(idlNode),
isChromeOnly(idlNode),
args,
argsPre,
argsPre + additionalArgsPre,
returnType,
self.extendedAttributes,
descriptor,
@ -10225,6 +10238,8 @@ class CGGetterCall(CGPerSignatureCall):
nativeMethodName,
descriptor,
attr,
errorReportingLabel=None,
argsPre=[],
dontSetSlot=False,
extendedAttributes=None,
):
@ -10249,6 +10264,8 @@ class CGGetterCall(CGPerSignatureCall):
useCounterName=useCounterName,
dontSetSlot=dontSetSlot,
extendedAttributes=extendedAttributes,
errorReportingLabel=errorReportingLabel,
additionalArgsPre=argsPre,
)
@ -10285,7 +10302,15 @@ class CGSetterCall(CGPerSignatureCall):
setter.
"""
def __init__(self, argType, nativeMethodName, descriptor, attr):
def __init__(
self,
argType,
nativeMethodName,
descriptor,
attr,
errorReportingLabel=None,
argsPre=[],
):
if attr.getExtendedAttribute("UseCounter"):
useCounterName = "%s_%s_setter" % (
descriptor.interface.identifier.name,
@ -10305,6 +10330,8 @@ class CGSetterCall(CGPerSignatureCall):
attr,
setter=True,
useCounterName=useCounterName,
errorReportingLabel=errorReportingLabel,
additionalArgsPre=argsPre,
)
def wrap_return_value(self):
@ -10586,7 +10613,7 @@ class CGSpecializedMethod(CGAbstractStaticMethod):
descriptor, idlMethod
):
return None
return GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
def error_reporting_label(self):
return CGSpecializedMethod.error_reporting_label_helper(
@ -10596,7 +10623,9 @@ class CGSpecializedMethod(CGAbstractStaticMethod):
@staticmethod
def makeNativeName(descriptor, method):
if method.underlyingAttr:
return CGSpecializedGetter.makeNativeName(descriptor, method.underlyingAttr)
return CGSpecializedGetterCommon.makeNativeName(
descriptor, method.underlyingAttr
)
name = method.identifier.name
return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic()))
@ -10989,21 +11018,25 @@ class CGStaticMethod(CGAbstractStaticBindingMethod):
)
class CGSpecializedGetter(CGAbstractStaticMethod):
class CGSpecializedGetterCommon(CGAbstractStaticMethod):
"""
A class for generating the code for a specialized attribute getter
that the JIT can call with lower overhead.
"""
def __init__(self, descriptor, attr):
self.attr = attr
name = "get_" + IDLToCIdentifier(attr.identifier.name)
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitGetterCallArgs", "args"),
]
def __init__(
self,
descriptor,
name,
nativeName,
attr,
args,
errorReportingLabel=None,
additionalArg=None,
):
self.nativeName = nativeName
self.errorReportingLabel = errorReportingLabel
self.additionalArgs = [] if additionalArg is None else [additionalArg]
# StoreInSlot attributes have their getters called from Wrap(). We
# really hope they can't run script, and don't want to annotate Wrap()
# methods as doing that anyway, so let's not annotate them as
@ -11013,7 +11046,7 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
descriptor,
name,
"bool",
args,
args + self.additionalArgs,
canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
)
@ -11041,7 +11074,13 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
# backing object from the slot, this requires its own generator.
return prefix + getObservableArrayGetterBody(self.descriptor, self.attr)
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr)
if self.nativeName is None:
nativeName = CGSpecializedGetterCommon.makeNativeName(
self.descriptor, self.attr
)
else:
nativeName = self.nativeName
type = self.attr.type
if self.attr.getExtendedAttribute("CrossOriginReadable"):
remoteType = type
@ -11074,6 +11113,8 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
nativeName,
self.descriptor,
self.attr,
self.errorReportingLabel,
argsPre=[a.name for a in self.additionalArgs],
dontSetSlot=True,
extendedAttributes=extendedAttributes,
).define(),
@ -11144,21 +11185,30 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
)
return (
prefix + CGGetterCall(type, nativeName, self.descriptor, self.attr).define()
prefix
+ CGGetterCall(
type,
nativeName,
self.descriptor,
self.attr,
self.errorReportingLabel,
argsPre=[a.name for a in self.additionalArgs],
).define()
)
def auto_profiler_label(self):
def auto_profiler_label(self, profilerLabel=None):
if profilerLabel is None:
profilerLabel = '"' + self.attr.identifier.name + '"'
interface_name = self.descriptor.interface.identifier.name
attr_name = self.attr.identifier.name
return fill(
"""
AUTO_PROFILER_LABEL_DYNAMIC_FAST(
"${interface_name}", "${attr_name}", DOM, cx,
"${interface_name}", ${attr_name}, DOM, cx,
uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
""",
interface_name=interface_name,
attr_name=attr_name,
attr_name=profilerLabel,
)
def error_reporting_label(self):
@ -11177,6 +11227,112 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
return nativeName
class CGSpecializedGetter(CGSpecializedGetterCommon):
"""
A class for generating the code for a specialized attribute getter
that the JIT can call with lower overhead.
"""
def __init__(self, descriptor, attr):
self.attr = attr
name = "get_" + IDLToCIdentifier(attr.identifier.name)
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitGetterCallArgs", "args"),
]
CGSpecializedGetterCommon.__init__(self, descriptor, name, None, attr, args)
class CGTemplateForSpecializedGetter(CGSpecializedGetterCommon):
"""
A class for generating the code for a specialized attribute getter
that can be used as the common getter that templated attribute
getters can forward to.
"""
def __init__(self, descriptor, template):
self.attr = template.attr
self.attrNameString = template.attrNameString
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitGetterCallArgs", "args"),
]
errorDescription = (
'ErrorDescriptionFor<ErrorFor::getter>{ "%s", attrName }'
% descriptor.interface.identifier.name
)
CGSpecializedGetterCommon.__init__(
self,
descriptor,
template.getter,
template.getter,
self.attr,
args,
errorReportingLabel=errorDescription,
additionalArg=Argument(template.argument.type, template.argument.name),
)
def auto_profiler_label(self):
return (
fill(
"""
const char* attrName = ${attrNameString};
""",
attrNameString=self.attrNameString,
)
+ CGSpecializedGetterCommon.auto_profiler_label(self, "attrName")
)
class CGSpecializedTemplatedGetter(CGAbstractStaticMethod):
"""
A class for generating the code for a specialized templated attribute
getter that forwards to a common template getter.
"""
def __init__(self, descriptor, attr, template, additionalArg):
self.attr = attr
self.template = template
self.additionalArg = additionalArg
name = "get_" + IDLToCIdentifier(attr.identifier.name)
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitGetterCallArgs", "args"),
]
assert not attr.getExtendedAttribute("StoreInSlot")
CGAbstractStaticMethod.__init__(
self,
descriptor,
name,
"bool",
args,
canRunScript=True,
)
def definition_body(self):
if self.additionalArg is None:
additionalArg = self.attr.identifier.name
else:
additionalArg = self.additionalArg
return fill(
"""
return ${namespace}::${getter}(cx, obj, void_self, args, ${additionalArg});
""",
namespace=toBindingNamespace(
self.template.descriptor.interface.identifier.name
),
getter=self.template.getter,
additionalArg=additionalArg,
)
class CGGetterPromiseWrapper(CGAbstractStaticMethod):
"""
A class for generating a wrapper around another getter that will
@ -11220,7 +11376,9 @@ class CGStaticGetter(CGAbstractStaticBindingMethod):
CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
def generate_code(self):
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr)
nativeName = CGSpecializedGetterCommon.makeNativeName(
self.descriptor, self.attr
)
return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
def auto_profiler_label(self):
@ -11242,29 +11400,44 @@ class CGStaticGetter(CGAbstractStaticBindingMethod):
return None
class CGSpecializedSetter(CGAbstractStaticMethod):
class CGSpecializedSetterCommon(CGAbstractStaticMethod):
"""
A class for generating the code for a specialized attribute setter
that the JIT can call with lower overhead.
"""
def __init__(self, descriptor, attr):
self.attr = attr
name = "set_" + IDLToCIdentifier(attr.identifier.name)
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitSetterCallArgs", "args"),
]
def __init__(
self,
descriptor,
name,
nativeName,
attr,
args,
errorReportingLabel=None,
additionalArg=None,
):
self.nativeName = nativeName
self.errorReportingLabel = errorReportingLabel
self.additionalArgs = [] if additionalArg is None else [additionalArg]
CGAbstractStaticMethod.__init__(
self, descriptor, name, "bool", args, canRunScript=True
self,
descriptor,
name,
"bool",
args + self.additionalArgs,
canRunScript=True,
)
def definition_body(self):
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr)
type = self.attr.type
call = CGSetterCall(type, nativeName, self.descriptor, self.attr).define()
call = CGSetterCall(
type,
self.nativeName,
self.descriptor,
self.attr,
self.errorReportingLabel,
[a.name for a in self.additionalArgs],
).define()
prefix = ""
if self.attr.getExtendedAttribute("CrossOriginWritable"):
if type.isGeckoInterface() and not type.unroll().inner.isExternal():
@ -11298,18 +11471,19 @@ class CGSpecializedSetter(CGAbstractStaticMethod):
call=call,
)
def auto_profiler_label(self):
def auto_profiler_label(self, profilerLabel=None):
interface_name = self.descriptor.interface.identifier.name
attr_name = self.attr.identifier.name
if profilerLabel is None:
profilerLabel = '"' + self.attr.identifier.name + '"'
return fill(
"""
AUTO_PROFILER_LABEL_DYNAMIC_FAST(
"${interface_name}", "${attr_name}", DOM, cx,
"${interface_name}", ${attr_name}, DOM, cx,
uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
""",
interface_name=interface_name,
attr_name=attr_name,
attr_name=profilerLabel,
)
@staticmethod
@ -11320,14 +11494,19 @@ class CGSpecializedSetter(CGAbstractStaticMethod):
attr.type, descriptor, allowTreatNonCallableAsNull=True
):
return None
return (
return '"%s"' % (
GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
)
def error_reporting_label(self):
return CGSpecializedSetter.error_reporting_label_helper(
errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper(
self.descriptor, self.attr
)
if errorReportingLabel is None:
return None
if self.errorReportingLabel:
return self.errorReportingLabel
return errorReportingLabel
@staticmethod
def makeNativeName(descriptor, attr):
@ -11335,6 +11514,114 @@ class CGSpecializedSetter(CGAbstractStaticMethod):
return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
class CGSpecializedSetter(CGSpecializedSetterCommon):
"""
A class for generating the code for a specialized attribute setter
that the JIT can call with lower overhead.
"""
def __init__(self, descriptor, attr):
self.attr = attr
name = "set_" + IDLToCIdentifier(attr.identifier.name)
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitSetterCallArgs", "args"),
]
CGSpecializedSetterCommon.__init__(
self,
descriptor,
name,
CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
attr,
args,
)
class CGTemplateForSpecializedSetter(CGSpecializedSetterCommon):
"""
A class for generating the code for a specialized attribute setter
that can be used as the common setter that templated attribute
setters can forward to.
"""
def __init__(self, descriptor, template):
self.attr = template.attr
self.attrNameString = template.attrNameString
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitSetterCallArgs", "args"),
]
errorDescription = (
'ErrorDescriptionFor<ErrorFor::setter>{ "%s", attrName }'
% descriptor.interface.identifier.name
)
CGSpecializedSetterCommon.__init__(
self,
descriptor,
template.setter,
template.setter,
self.attr,
args,
errorReportingLabel=errorDescription,
additionalArg=Argument(template.argument.type, template.argument.name),
)
def auto_profiler_label(self):
return (
fill(
"""
const char* attrName = ${attrNameString};
""",
attrNameString=self.attrNameString,
)
+ CGSpecializedSetterCommon.auto_profiler_label(self, "attrName")
)
class CGSpecializedTemplatedSetter(CGAbstractStaticMethod):
"""
A class for generating the code for a specialized templated attribute
setter that forwards to a common template setter.
"""
def __init__(self, descriptor, attr, template, additionalArg):
self.attr = attr
self.template = template
self.additionalArg = additionalArg
name = "set_" + IDLToCIdentifier(attr.identifier.name)
args = [
Argument("JSContext*", "cx"),
Argument("JS::Handle<JSObject*>", "obj"),
Argument("void*", "void_self"),
Argument("JSJitSetterCallArgs", "args"),
]
CGAbstractStaticMethod.__init__(
self, descriptor, name, "bool", args, canRunScript=True
)
def definition_body(self):
additionalArgs = []
if self.additionalArg is None:
additionalArgs.append(self.attr.identifier.name)
else:
additionalArgs.append(self.additionalArg)
return fill(
"""
return ${namespace}::${setter}(cx, obj, void_self, args, ${additionalArgs});
""",
namespace=toBindingNamespace(
self.template.descriptor.interface.identifier.name
),
setter=self.template.setter,
additionalArgs=", ".join(additionalArgs),
)
class CGStaticSetter(CGAbstractStaticBindingMethod):
"""
A class for generating the C++ code for an IDL static attribute setter.
@ -11346,7 +11633,9 @@ class CGStaticSetter(CGAbstractStaticBindingMethod):
CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
def generate_code(self):
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr)
nativeName = CGSpecializedSetterCommon.makeNativeName(
self.descriptor, self.attr
)
checkForArg = CGGeneric(
fill(
"""
@ -11375,7 +11664,7 @@ class CGStaticSetter(CGAbstractStaticBindingMethod):
)
def error_reporting_label(self):
return CGSpecializedSetter.error_reporting_label_helper(
return CGSpecializedSetterCommon.error_reporting_label_helper(
self.descriptor, self.attr
)
@ -11416,7 +11705,7 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter):
def error_reporting_label(self):
# We always need to be able to throw.
return (
return '"%s"' % (
GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
+ " setter"
)
@ -14825,7 +15114,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
if error_label:
cxDecl = fill(
"""
BindingCallContext cx(cx_, "${error_label}");
BindingCallContext cx(cx_, ${error_label});
""",
error_label=error_label,
)
@ -14877,7 +15166,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
if error_label:
set += fill(
"""
BindingCallContext cx(cx_, "${error_label}");
BindingCallContext cx(cx_, ${error_label});
""",
error_label=error_label,
)
@ -15638,7 +15927,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod):
if error_label:
cxDecl = fill(
"""
BindingCallContext cx(cx_, "${error_label}");
BindingCallContext cx(cx_, ${error_label});
""",
error_label=error_label,
)
@ -15671,7 +15960,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod):
if error_label:
cxDecl = fill(
"""
BindingCallContext cx(cx_, "${error_label}");
BindingCallContext cx(cx_, ${error_label});
""",
error_label=error_label,
)
@ -16305,7 +16594,7 @@ def memberProperties(m, descriptor):
class CGDescriptor(CGThing):
def __init__(self, descriptor):
def __init__(self, descriptor, attributeTemplates):
CGThing.__init__(self)
assert (
@ -16365,10 +16654,23 @@ class CGDescriptor(CGThing):
defaultToJSONMethod = None
needCrossOriginPropertyArrays = False
unscopableNames = list()
for n in descriptor.interface.legacyFactoryFunctions:
cgThings.append(
CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n))
)
if descriptor.attributeTemplates is not None:
for template in descriptor.attributeTemplates:
if template.getter is not None:
cgThings.append(
CGTemplateForSpecializedGetter(descriptor, template)
)
if template.setter is not None:
cgThings.append(
CGTemplateForSpecializedSetter(descriptor, template)
)
for m in descriptor.interface.members:
if m.isMethod() and m.identifier.name == "QueryInterface":
continue
@ -16417,7 +16719,28 @@ class CGDescriptor(CGThing):
assert descriptor.interface.hasInterfaceObject()
cgThings.append(CGStaticGetter(descriptor, m))
elif descriptor.interface.hasInterfacePrototypeObject():
specializedGetter = CGSpecializedGetter(descriptor, m)
template = m.getExtendedAttribute("BindingTemplate")
if template is not None:
templateName = template[0][0]
additionalArg = template[0][1]
if not (m.type.isPrimitive() or m.type.isString()):
raise TypeError(
"We only support primitives or strings on templated attributes. "
"Attribute '%s' on interface '%s' has type '%s' but tries to "
"use template '%s'"
% (
m.identifier.name,
descriptor.interface.identifier.name,
m.type,
templateName,
)
)
template = attributeTemplates.get(templateName)
specializedGetter = CGSpecializedTemplatedGetter(
descriptor, m, template, additionalArg
)
else:
specializedGetter = CGSpecializedGetter(descriptor, m)
cgThings.append(specializedGetter)
if m.type.isPromise():
cgThings.append(
@ -16430,7 +16753,21 @@ class CGDescriptor(CGThing):
assert descriptor.interface.hasInterfaceObject()
cgThings.append(CGStaticSetter(descriptor, m))
elif descriptor.interface.hasInterfacePrototypeObject():
cgThings.append(CGSpecializedSetter(descriptor, m))
template = m.getExtendedAttribute("BindingTemplate")
if template is not None:
if isinstance(template[0], list):
templateName = template[0][0]
additionalArg = template[0][1]
else:
templateName = template[0]
additionalArg = None
template = attributeTemplates.get(templateName)
specializedSetter = CGSpecializedTemplatedSetter(
descriptor, m, template, additionalArg
)
else:
specializedSetter = CGSpecializedSetter(descriptor, m)
cgThings.append(specializedSetter)
if props.isCrossOriginSetter:
needCrossOriginPropertyArrays = True
elif m.getExtendedAttribute("PutForwards"):
@ -18708,7 +19045,9 @@ class CGBindingRoot(CGThing):
cgthings.append(CGNamespace("binding_detail", CGFastCallback(t)))
# Do codegen for all the descriptors
cgthings.extend([CGDescriptor(x) for x in descriptors])
cgthings.extend(
[CGDescriptor(x, config.attributeTemplates) for x in descriptors]
)
# Do codegen for all the callback interfaces.
cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
@ -19324,7 +19663,7 @@ class CGExampleGetter(CGNativeMember):
self,
descriptor,
attr,
CGSpecializedGetter.makeNativeName(descriptor, attr),
CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
(attr.type, []),
descriptor.getExtendedAttributes(attr, getter=True),
)
@ -19349,7 +19688,7 @@ class CGExampleSetter(CGNativeMember):
self,
descriptor,
attr,
CGSpecializedSetter.makeNativeName(descriptor, attr),
CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
(
BuiltinTypes[IDLBuiltinType.Types.undefined],
[FakeArgument(attr.type)],
@ -19472,7 +19811,7 @@ class CGBindingImplClass(CGClass):
m
for m in iface.members
if m.isAttr()
and CGSpecializedGetter.makeNativeName(descriptor, m) == "Length"
and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length"
)
if not haveLengthAttr:
self.methodDecls.append(
@ -20023,7 +20362,7 @@ class CGJSImplGetter(CGJSImplMember):
self,
descriptor,
attr,
CGSpecializedGetter.makeNativeName(descriptor, attr),
CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
(attr.type, []),
descriptor.getExtendedAttributes(attr, getter=True),
passJSBitsAsNeeded=False,
@ -20048,7 +20387,7 @@ class CGJSImplSetter(CGJSImplMember):
self,
descriptor,
attr,
CGSpecializedSetter.makeNativeName(descriptor, attr),
CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
(
BuiltinTypes[IDLBuiltinType.Types.undefined],
[FakeArgument(attr.type)],
@ -23592,7 +23931,7 @@ class CGEventGetter(CGNativeMember):
self,
descriptor,
attr,
CGSpecializedGetter.makeNativeName(descriptor, attr),
CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
(attr.type, []),
ea,
resultNotAddRefed=not attr.type.isSequence(),
@ -23932,7 +24271,7 @@ class CGEventClass(CGBindingImplClass):
# either.
extraMethods.append(
ClassMethod(
CGSpecializedGetter.makeNativeName(descriptor, m),
CGSpecializedGetterCommon.makeNativeName(descriptor, m),
"void",
[Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
const=True,

View File

@ -52,6 +52,31 @@ class Configuration(DescriptorProvider):
exec(io.open(filename, encoding="utf-8").read(), glbl)
config = glbl["DOMInterfaces"]
class IDLAttrGetterOrSetterTemplate:
def __init__(self, template, getter, setter, argument, attrName):
class TemplateAdditionalArg:
def __init__(self, type, name, value=None):
self.type = type
self.name = name
self.value = value
self.descriptor = None
self.usedInOtherInterfaces = False
self.getter = getter
self.setter = setter
self.argument = TemplateAdditionalArg(*argument)
self.attrNameString = attrName
self.attr = None
self.attributeTemplates = dict()
attributeTemplatesByInterface = dict()
for interface, templates in glbl["TemplatedAttributes"].items():
for template in templates:
name = template.get("template")
t = IDLAttrGetterOrSetterTemplate(**template)
self.attributeTemplates[name] = t
attributeTemplatesByInterface.setdefault(interface, list()).append(t)
webRoots = tuple(map(os.path.normpath, webRoots))
def isInWebIDLRoot(path):
@ -137,7 +162,12 @@ class Configuration(DescriptorProvider):
entry = config.get(iface.identifier.name, {})
assert not isinstance(entry, list)
desc = Descriptor(self, iface, entry)
desc = Descriptor(
self,
iface,
entry,
attributeTemplatesByInterface.get(iface.identifier.name),
)
self.descriptors.append(desc)
# Setting up descriptorsByName while iterating through interfaces
# means we can get the nativeType of iterable interfaces without
@ -274,6 +304,183 @@ class Configuration(DescriptorProvider):
offsets = accumulate(map(lambda n: len(n) + 1, names), initial=0)
self.namesStringOffsets = list(zip(names, offsets))
allTemplatedAttributes = (
(m, d)
for d in self.descriptors
if not d.interface.isExternal()
for m in d.interface.members
if m.isAttr() and m.getExtendedAttribute("BindingTemplate") is not None
)
# attributesPerTemplate will have the template names as keys, and a
# list of tuples as values. Every tuple contains an IDLAttribute and a
# descriptor.
attributesPerTemplate = dict()
for m, d in allTemplatedAttributes:
t = m.getExtendedAttribute("BindingTemplate")
if isinstance(t[0], list):
t = t[0]
l = attributesPerTemplate.setdefault(t[0], list())
# We want the readonly attributes last, because we use the first
# attribute in the list as the canonical attribute for the
# template, and if there are any writable attributes the
# template should have support for that.
if not m.readonly:
l.insert(0, (m, d))
else:
l.append((m, d))
for name, attributes in attributesPerTemplate.items():
# We use the first attribute to generate a canonical implementation
# of getter and setter.
firstAttribute, firstDescriptor = attributes[0]
template = self.attributeTemplates.get(name)
if template is None:
raise TypeError(
"Unknown BindingTemplate with name %s for %s on %s"
% (
name,
firstAttribute.identifier.name,
firstDescriptor.interface.identifier.name,
)
)
# This mimics a real IDL attribute for templated bindings.
class TemplateIDLAttribute:
def __init__(self, attr):
assert attr.isAttr()
assert not attr.isMaplikeOrSetlikeAttr()
assert not attr.slotIndices
self.identifier = attr.identifier
self.type = attr.type
self.extendedAttributes = attr.getExtendedAttributes()
self.slotIndices = None
def getExtendedAttribute(self, name):
return self.extendedAttributes.get(name)
def isAttr(self):
return True
def isMaplikeOrSetlikeAttr(self):
return False
def isMethod(self):
return False
def isStatic(self):
return False
template.attr = TemplateIDLAttribute(firstAttribute)
def filterExtendedAttributes(extendedAttributes):
# These are the extended attributes that we allow to have
# different values among all atributes that use the same
# template.
ignoredAttributes = {
"BindingTemplate",
"BindingAlias",
"Pure",
"Pref",
"Func",
"Throws",
"GetterThrows",
"SetterThrows",
}
return dict(
filter(
lambda i: i[0] not in ignoredAttributes,
extendedAttributes.items(),
)
)
firstExtAttrs = filterExtendedAttributes(
firstAttribute.getExtendedAttributes()
)
for a, d in attributes:
# We want to make sure all getters or setters grouped by a
# template have the same WebIDL signatures, so make sure
# their types are the same.
if template.attr.type != a.type:
raise TypeError(
"%s on %s and %s on %s have different type, but they're using the same template %s."
% (
firstAttribute.identifier.name,
firstDescriptor.interface.identifier.name,
a.identifier.name,
d.interface.identifier.name,
name,
)
)
extAttrs = filterExtendedAttributes(a.getExtendedAttributes())
if template.attr.extendedAttributes != extAttrs:
for k in extAttrs.keys() - firstExtAttrs.keys():
raise TypeError(
"%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
% (
a.identifier.name,
d.interface.identifier.name,
k,
firstAttribute.identifier.name,
firstDescriptor.interface.identifier.name,
name,
)
)
for k in firstExtAttrs.keys() - extAttrs.keys():
raise TypeError(
"%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
% (
firstAttribute.identifier.name,
firstDescriptor.interface.identifier.name,
k,
a.identifier.name,
d.interface.identifier.name,
name,
)
)
for (k, v) in firstExtAttrs.items():
if extAttrs[k] != v:
raise TypeError(
"%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s."
% (
firstAttribute.identifier.name,
firstDescriptor.interface.identifier.name,
a.identifier.name,
d.interface.identifier.name,
k,
name,
)
)
def sameThrows(getter=False, setter=False):
extAttrs1 = firstDescriptor.getExtendedAttributes(
firstAttribute, getter=getter, setter=setter
)
extAttrs2 = d.getExtendedAttributes(a, getter=getter, setter=setter)
return ("needsErrorResult" in extAttrs1) == (
"needsErrorResult" in extAttrs2
)
if not sameThrows(getter=True) or (
not a.readonly and not sameThrows(setter=True)
):
raise TypeError(
"%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s."
% (
firstAttribute.identifier.name,
firstDescriptor.interface.identifier.name,
a.identifier.name,
d.interface.identifier.name,
name,
)
)
for name, template in self.attributeTemplates.items():
if template.attr is None:
raise TypeError("Template %s is unused, please remove it." % name)
def getInterface(self, ifname):
return self.interfaces[ifname]
@ -427,10 +634,14 @@ class Descriptor(DescriptorProvider):
Represents a single descriptor for an interface. See Bindings.conf.
"""
def __init__(self, config, interface, desc):
def __init__(self, config, interface, desc, attributeTemplates):
DescriptorProvider.__init__(self)
self.config = config
self.interface = interface
self.attributeTemplates = attributeTemplates
if self.attributeTemplates is not None:
for t in self.attributeTemplates:
t.descriptor = self
self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow()

View File

@ -464,6 +464,11 @@ class TErrorResult {
// hopefully it's all temporary until we sort out the EME bits.
friend class dom::Promise;
// Implementation of MaybeSetPendingException for the case when we're a
// failure result. See documentation of MaybeSetPendingException for the
// "context" argument.
void SetPendingException(JSContext* cx, const char* context);
private:
#ifdef DEBUG
enum UnionState {
@ -567,11 +572,6 @@ class TErrorResult {
// touching the union anymore.
void ClearUnionData();
// Implementation of MaybeSetPendingException for the case when we're a
// failure result. See documentation of MaybeSetPendingException for the
// "context" argument.
void SetPendingException(JSContext* cx, const char* context);
// Methods for setting various specific kinds of pending exceptions. See
// documentation of MaybeSetPendingException for the "context" argument.
void SetPendingExceptionWithMessage(JSContext* cx, const char* context);
@ -828,13 +828,47 @@ class CopyableErrorResult
inline ErrorResult::ErrorResult(CopyableErrorResult&& aRHS)
: ErrorResult(reinterpret_cast<ErrorResult&&>(aRHS)) {}
namespace dom {
namespace binding_detail {
namespace dom::binding_detail {
enum class ErrorFor {
getter,
setter,
};
template <ErrorFor ErrorType>
struct ErrorDescriptionFor {
const char* mInterface;
const char* mMember;
};
class FastErrorResult : public mozilla::binding_danger::TErrorResult<
mozilla::binding_danger::JustAssertCleanupPolicy> {
public:
using TErrorResult::MaybeSetPendingException;
template <ErrorFor ErrorType>
[[nodiscard]] bool MaybeSetPendingException(
JSContext* aCx, const ErrorDescriptionFor<ErrorType>& aDescription) {
WouldReportJSException();
if (!Failed()) {
return false;
}
nsAutoCString description(aDescription.mInterface);
description.Append('.');
description.Append(aDescription.mMember);
if constexpr (ErrorType == ErrorFor::getter) {
description.AppendLiteral(" getter");
} else {
static_assert(ErrorType == ErrorFor::setter);
description.AppendLiteral(" setter");
}
SetPendingException(aCx, description.get());
return true;
}
};
} // namespace binding_detail
} // namespace dom
} // namespace dom::binding_detail
// We want an OOMReporter class that has the following properties:
//

View File

@ -35,9 +35,13 @@ def generate(output, idlFilename, dataFile):
# We already added this as a BindingAlias for the original prop.
continue
propId = p.prop_id
else:
propId = p.id
# Unfortunately, even some of the getters here are fallible
# (e.g. on nsComputedDOMStyle).
extendedAttrs = [
"BindingTemplate=(CSS2Property, eCSSProperty_%s)" % propId,
"CEReactions",
"Throws",
"SetterNeedsSubjectPrincipal=NonSystem",

View File

@ -5725,6 +5725,7 @@ class IDLAttribute(IDLInterfaceMember):
or identifier == "ReturnValueNeedsContainsHack"
or identifier == "BinaryName"
or identifier == "NonEnumerable"
or identifier == "BindingTemplate"
):
# Known attributes that we don't need to do anything with here
pass
@ -5735,6 +5736,9 @@ class IDLAttribute(IDLInterfaceMember):
)
IDLInterfaceMember.handleExtendedAttribute(self, attr)
def getExtendedAttributes(self):
return self._extendedAttrDict
def resolve(self, parentScope):
assert isinstance(parentScope, IDLScope)
self.type.resolveType(parentScope)

View File

@ -1390,6 +1390,25 @@ implementing `MyInterface`.
Multiple `[BindingAlias]` extended attributes can be used on a single
attribute.
### `[BindingTemplate=(name, value)]`
This extended attribute can be specified on an attribute, and causes the getter
and setter for this attribute to forward to a common generated implementation,
shared with all other attributes that have a `[BindingTemplate]` with the same
value for the `name` argument. The `TemplatedAttributes` dictionary in
Bindings.conf needs to contain a definition for the template with the name
`name`. The `value` will be passed as an argument when calling the common
generated implementation.
This is aimed at very specialized use cases where an interface has a
large number of attributes that all have the same type, and for which we have a
native implementation that's common to all these attributes, and typically uses
some id based on the attribute's name in the implementation. All the attributes
that use the same template need to mostly have the same extended attributes,
except form a small number that are allowed to differ (`[BindingTemplate]`,
`[BindingAlias]`, `[Pure]`, [`Pref`] and [`Func`], and the annotations for
whether the getter and setter throws exceptions).
### `[ChromeOnly]`
This extended attribute can be specified on any method, attribute, or

View File

@ -65,6 +65,10 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration {
*/
virtual nsresult GetPropertyValue(const nsCSSPropertyID aPropID,
nsACString& aValue);
void GetPropertyValue(const nsCSSPropertyID aPropID, nsACString& aValue,
mozilla::ErrorResult& aRv) {
aRv = GetPropertyValue(aPropID, aValue);
}
/**
* Method analogous to CSSStyleDeclaration::SetProperty. This
@ -93,32 +97,6 @@ class nsDOMCSSDeclaration : public nsICSSDeclaration {
uint32_t Length() override;
// WebIDL interface for CSS2Properties
#define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) publicname_
#define CSS_PROP(id_, method_) \
void Get##method_(nsACString& aValue, mozilla::ErrorResult& rv) { \
rv = GetPropertyValue(eCSSProperty_##id_, aValue); \
} \
\
void Set##method_(const nsACString& aValue, nsIPrincipal* aSubjectPrincipal, \
mozilla::ErrorResult& aRv) { \
SetPropertyValue(eCSSProperty_##id_, aValue, aSubjectPrincipal, aRv); \
}
#define CSS_PROP_LIST_EXCLUDE_INTERNAL
#define CSS_PROP_LIST_EXCLUDE_NOT_IN_STYLE
#define CSS_PROP_LONGHAND(name_, id_, method_, ...) CSS_PROP(id_, method_)
#define CSS_PROP_SHORTHAND(name_, id_, method_, ...) CSS_PROP(id_, method_)
#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, ...) \
CSS_PROP(id_, method_)
#include "mozilla/ServoCSSPropList.h"
#undef CSS_PROP_ALIAS
#undef CSS_PROP_SHORTHAND
#undef CSS_PROP_LONGHAND
#undef CSS_PROP_LIST_EXCLUDE_INTERNAL
#undef CSS_PROP_LIST_EXCLUDE_NOT_IN_STYLE
#undef CSS_PROP
#undef CSS_PROP_PUBLIC_OR_PRIVATE
virtual void IndexedGetter(uint32_t aIndex, bool& aFound,
nsACString& aPropName) override;