Bug 1588194 part 1. Factor out the missing property use counter code into helper functions. r=peterv

This incidentally reduces the codesize, while allowing us to call those
functions from elsewhere.  The performance impact of the extra out-of-line call
is not measurable (that is, is within the noise level) on the testcase at
<https://bugzilla.mozilla.org/attachment.cgi?id=9096814>.

Differential Revision: https://phabricator.services.mozilla.com/D49044

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Boris Zbarsky 2019-10-29 16:18:44 +00:00
parent 458e8baf26
commit 89b4cac9a3
2 changed files with 135 additions and 74 deletions

View File

@ -11811,11 +11811,43 @@ class CGProxyUnwrap(CGAbstractMethod):
MISSING_PROP_PREF = "dom.missing_prop_counters.enabled"
def missingPropUseCountersForDescriptor(desc):
instrumentedProps = desc.instrumentedProps
if not instrumentedProps:
if not desc.needsMissingPropUseCounters:
return ""
def gen_switch(switchDecriptor):
return fill(
"""
if (StaticPrefs::${pref}() && JSID_IS_ATOM(id)) {
CountMaybeMissingProperty(proxy, id);
}
""",
pref=prefIdentifier(MISSING_PROP_PREF))
def findAncestorWithInstrumentedProps(desc):
"""
Find an ancestor of desc.interface (not including desc.interface
itself) that has instrumented properties on it. May return None
if there is no such ancestor.
"""
ancestor = desc.interface.parent
while ancestor:
if ancestor.getExtendedAttribute("InstrumentedProps"):
return ancestor
ancestor = ancestor.parent
return None
class CGCountMaybeMissingProperty(CGAbstractMethod):
def __init__(self, descriptor):
"""
Returns whether we counted the property involved.
"""
CGAbstractMethod.__init__(self, descriptor, "CountMaybeMissingProperty",
"bool",
[Argument("JS::Handle<JSObject*>", "proxy"),
Argument("JS::Handle<jsid>", "id")])
def gen_switch(self, switchDecriptor):
"""
Generate a switch from the switch descriptor. The descriptor
dictionary must have the following properties:
@ -11831,7 +11863,7 @@ def missingPropUseCountersForDescriptor(desc):
cases = []
for label, body in sorted(switchDecriptor['cases'].iteritems()):
if isinstance(body, dict):
body = gen_switch(body)
body = self.gen_switch(body)
cases.append(fill(
"""
case ${label}: {
@ -11852,7 +11884,7 @@ def missingPropUseCountersForDescriptor(desc):
condition=switchDecriptor['condition'],
cases="".join(cases))
def charSwitch(props, charIndex):
def charSwitch(self, props, charIndex):
"""
Create a switch for the given props, based on the first char where
they start to differ at index charIndex or more. Each prop is a tuple
@ -11868,8 +11900,8 @@ def missingPropUseCountersForDescriptor(desc):
counter.emplace(eUseCounter_${iface}_${name});
}
""",
iface=props[0][0],
name=props[0][1])
iface=self.descriptor.name,
name=props[0])
switch = dict()
if charIndex == 0:
@ -11879,7 +11911,7 @@ def missingPropUseCountersForDescriptor(desc):
switch['precondition'] = ""
# Find the first place where we might actually have a difference.
while all(prop[1][charIndex] == props[0][1][charIndex]
while all(prop[charIndex] == props[0][charIndex]
for prop in props):
charIndex += 1
@ -11889,47 +11921,70 @@ def missingPropUseCountersForDescriptor(desc):
curChar = None
idx = 0
while idx < len(props):
nextChar = "'%s'" % props[idx][1][charIndex]
nextChar = "'%s'" % props[idx][charIndex]
if nextChar != curChar:
if curChar:
switch['cases'][curChar] = charSwitch(current_props,
charIndex + 1)
switch['cases'][curChar] = self.charSwitch(current_props,
charIndex + 1)
current_props = []
curChar = nextChar
current_props.append(props[idx])
idx += 1
switch['cases'][curChar] = charSwitch(current_props, charIndex + 1)
switch['cases'][curChar] = self.charSwitch(current_props, charIndex + 1)
return switch
lengths = set(len(prop[1]) for prop in instrumentedProps)
switchDesc = { 'condition': 'js::GetLinearStringLength(str)',
'precondition': '' }
switchDesc['cases'] = dict()
for length in sorted(lengths):
switchDesc['cases'][str(length)] = charSwitch(
list(sorted(prop for prop in instrumentedProps
if len(prop[1]) == length)),
0)
def definition_body(self):
ancestor = findAncestorWithInstrumentedProps(self.descriptor)
return fill(
"""
if (StaticPrefs::${pref}() && JSID_IS_ATOM(id)) {
Maybe<UseCounter> counter;
{
// Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
JS::AutoCheckCannotGC nogc;
JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
// Don't waste time fetching the chars until we've done the length switch.
$*{switch}
}
if (counter) {
SetUseCounter(proxy, *counter);
}
}
if ancestor:
body = fill(
"""
if (${ancestor}_Binding::CountMaybeMissingProperty(proxy, id)) {
return true;
}
""",
pref=prefIdentifier(MISSING_PROP_PREF),
switch=gen_switch(switchDesc))
""",
ancestor=ancestor.identifier.name)
else:
body = ""
instrumentedProps = self.descriptor.instrumentedProps
if not instrumentedProps:
return body + dedent(
"""
return false;
""")
lengths = set(len(prop) for prop in instrumentedProps)
switchDesc = { 'condition': 'js::GetLinearStringLength(str)',
'precondition': '' }
switchDesc['cases'] = dict()
for length in sorted(lengths):
switchDesc['cases'][str(length)] = self.charSwitch(
list(sorted(prop for prop in instrumentedProps
if len(prop) == length)),
0)
return body + fill(
"""
MOZ_ASSERT(StaticPrefs::${pref}() && JSID_IS_ATOM(id));
Maybe<UseCounter> counter;
{
// Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
JS::AutoCheckCannotGC nogc;
JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id));
// Don't waste time fetching the chars until we've done the length switch.
$*{switch}
}
if (counter) {
SetUseCounter(proxy, *counter);
return true;
}
return false;
""",
pref=prefIdentifier(MISSING_PROP_PREF),
switch=self.gen_switch(switchDesc))
class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
@ -12559,8 +12614,8 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
"Should not have a XrayWrapper here");
$*{maybeCrossOrigin}
$*{indexed}
$*{missingPropUseCounters}
$*{missingPropUseCounters}
JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
if (expando) {
bool b = true;
@ -13450,6 +13505,9 @@ class CGDescriptor(CGThing):
"non-leaf interfaces like %s" %
descriptor.interface.identifier.name)
if descriptor.needsMissingPropUseCounters:
cgThings.append(CGCountMaybeMissingProperty(descriptor))
if descriptor.concrete:
if descriptor.interface.isSerializable():
cgThings.append(CGSerializer(descriptor))
@ -15101,6 +15159,11 @@ class CGBindingRoot(CGThing):
bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = any(
d.interface.isSerializable() for d in descriptors)
for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors):
if not ancestor:
continue
bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
cgthings.extend(traverseMethods)
cgthings.extend(unlinkMethods)

View File

@ -438,9 +438,24 @@ class Descriptor(DescriptorProvider):
if m.isMethod() and m.isDefaultToJSON():
self.hasDefaultToJSON = True
# We keep track of instrumente props for all non-external interfaces.
self.instrumentedProps = []
instrumentedProps = self.interface.getExtendedAttribute("InstrumentedProps")
if instrumentedProps:
# It's actually a one-element list, with the list
# we want as the only element.
self.instrumentedProps = instrumentedProps[0]
# Check that we don't have duplicated instrumented props.
uniqueInstrumentedProps = set(self.instrumentedProps)
if len(uniqueInstrumentedProps) != len(self.instrumentedProps):
duplicates = [p for p in uniqueInstrumentedProps if
self.instrumentedProps.count(p) > 1]
raise TypeError("Duplicated instrumented properties: %s.\n%s" %
(duplicates, self.interface.location))
if self.concrete:
self.proxy = False
self.instrumentedProps = []
iface = self.interface
for m in iface.members:
# Don't worry about inheriting legacycallers either: in
@ -453,14 +468,8 @@ class Descriptor(DescriptorProvider):
raise TypeError("We don't support overloaded "
"legacycaller.\n%s" % m.location)
addOperation('LegacyCaller', m)
while iface:
instrumentedProps = iface.getExtendedAttribute("InstrumentedProps")
if instrumentedProps:
# It's actually a one-element list, with the list
# we want as the only element.
for prop in instrumentedProps[0]:
self.instrumentedProps.append((iface.identifier.name,
prop))
for m in iface.members:
if not m.isMethod():
continue
@ -487,21 +496,6 @@ class Descriptor(DescriptorProvider):
iface.setUserData('hasConcreteDescendant', True)
iface = iface.parent
# Check that we don't have duplicated instrumented props.
uniqueInstrumentedProps = set(prop[1] for prop in self.instrumentedProps)
if len(uniqueInstrumentedProps) != len(self.instrumentedProps):
for prop in self.instrumentedProps:
name = prop[1]
if name in uniqueInstrumentedProps:
uniqueInstrumentedProps.remove(name)
else:
ifaces = list(
entry[0] for entry in self.instrumentedProps if
entry[1] == name)
raise TypeError(
"Duplicated instrumented property '%s' defined on "
"these interfaces: %s." % (name, str(ifaces)))
self.proxy = (self.supportsIndexedProperties() or
(self.supportsNamedProperties() and
not self.hasNamedPropertiesObject) or
@ -593,18 +587,22 @@ class Descriptor(DescriptorProvider):
if ctor:
maybeAddBinaryName(ctor)
# Some default binary names for cases when nothing else got set.
self._binaryNames.setdefault('__legacycaller', 'LegacyCall')
self._binaryNames.setdefault('__stringifier', 'Stringify')
# Some default binary names for cases when nothing else got set.
self._binaryNames.setdefault('__legacycaller', 'LegacyCall')
self._binaryNames.setdefault('__stringifier', 'Stringify')
# Build the prototype chain.
self.prototypeChain = []
parent = interface
while parent:
self.prototypeChain.insert(0, parent.identifier.name)
parent = parent.parent
config.maxProtoChainLength = max(config.maxProtoChainLength,
len(self.prototypeChain))
# Build the prototype chain.
self.prototypeChain = []
self.needsMissingPropUseCounters = False
parent = interface
while parent:
self.needsMissingPropUseCounters = (
self.needsMissingPropUseCounters or
parent.getExtendedAttribute("InstrumentedProps"))
self.prototypeChain.insert(0, parent.identifier.name)
parent = parent.parent
config.maxProtoChainLength = max(config.maxProtoChainLength,
len(self.prototypeChain))
def binaryNameFor(self, name):
return self._binaryNames.get(name, name)