Currently toplevel protocols inherit from both IProtocolManager<IProtocol>
and IToplevelProtocol. This change makes IToplevelProtocol inherit from
IProtocol, so now toplevel protocols only inherit from
IToplevelProtocol.
Currently all our protocols inherit from IProtocolManager<IProtocol>. I have
no idea why. This patch switches everything over to IProtocol, without any
templates. I had to move ReadActor to the .cpp file to avoid redefinition
errors.
This patch moves FatalError to IProtocol. I had to make a few changes.
- I added a ProtocolName() method to find the name of the protocol.
- I gave the two-argument version of FatalError its own name. Otherwise
C++ doesn't like there to be two virtual methods with the same name
in cases where one is overridden and the other isn't (as happens
in IToplevelProtocol).
This moves some of the generated methods in subprotocols that simply defer
to the parent protocol to IProtocol. These methods are still overridden in
the toplevel protocol.
This patch stores mManager in IProtocol rather than in each individual
PFoo. It also adds a generic accessor for that field. Note that each
individual protocol still defines a Manager() function that returns
PFooParent or whatever. I tried to get rid of that but it was a lot
of work.
With this change, MessageChannel stores mListener as an IToplevelProtocol
rather than a MessageListener (which isn't really a useful concept on
its own). The MessageListener methods are split out to IProtocol and
IToplevelProtocol. MessageListener gets deleted. Some of the inline
functions in MessageChannel had to be moved to MessageChannel.cpp since
IToplevelProtocol isn't defined in MessageChannel.h.
Currently toplevel protocols inherit from both IProtocolManager<IProtocol>
and IToplevelProtocol. This change makes IToplevelProtocol inherit from
IProtocol, so now toplevel protocols only inherit from
IToplevelProtocol.
Currently all our protocols inherit from IProtocolManager<IProtocol>. I have
no idea why. This patch switches everything over to IProtocol, without any
templates. I had to move ReadActor to the .cpp file to avoid redefinition
errors.
The only thing we needed opened actor tracking for was the ability to
clone all the actors. But now that we no longer have support for
cloning actors, we no longer need to track the actors that we've cloned,
which makes a number of things significantly simpler.
CloneOpenedToplevels, which is never called, is the only interesting
caller of CloneToplevel. And CloneToplevel, in turn, is the only
interesting caller of CloneManagees. Which means we can ditch all this
code for a decent amount of space savings, both in code and writable
static data (no more useless virtual function entries in vtables).
We construct Trigger structures for every IPDL send and recv to run
the (currently defunct) IPDL state machine for each protocol. These
structures are 64 bits to hold an enum that could be represented in 1
bit and an IPDL message enum that can easily be represented in 31 bits.
Therefore, we can make the Trigger structure smaller by storing the
members as bitfields, rather than full-width integers. (The message
enums are formed from a 16-bit protocol enum and a 16-bit
protocol-specific message enum; since the protocol enum goes in the
upper bits, we'd need 32768 protocols to overflow the bitfield we're
using in Trigger...a number that we're not going to hit anytime soon.)
This change saves ~15K of space on x86-64 Linux.
There's no reason to generate identical code for every kind of managed
protocol; we can have a single instance that operates on void* and cast
our way to victory. This change saves ~60K of text on x86-64 Linux.
Actor reading from IPC message is codegen'd with a lot of repeated code.
We can improve that by moving the core actor reading code out of
subclasses into IProtocolmanager. While we still need to codegen a bit
of code to cast the read actor to the proper type, the code overall is
smaller. The lone downside is that if we do encounter an error reading
the actor id out of the message, the precision of our crash messages is
reduced somewhat: we no longer have the protocol name doing the reading,
nor do we get crash report annotations, since we can't tell whether
we're in the parent or child process.
IProtocolManager is templated over some listener type. In our IPDL
code, that type is always IProtocol, which is a subclass of
MessageListener. It's also important to note that IProtocol uses
protected inheritance from MessageListener; the generated code takes
advantage of this inheritance structure when it reads actors:
// ChannelListener is typedef'd to MessageListener
// Lookup here is IProtocolManager::Lookup
ChannelListener* listener = Lookup(id);
Lookup returns a pointer to the type over which IProtocolManager is
templated. As mentioned above, that type is always IProtocol. But
thanks to the containing class inheriting from *both* IProtocolManager
and IProtocol, the returned pointer can be silently upcasted to
MessageListener thanks to C++ visibility rules.
It's not clear that this restricted inheritance structure is actually
benefitting anybody, or that the inheritance hierarchy of protocol
classes is the best way to do things. This particular implementation
detail is getting in the way for the next improvement, so let's make the
protected inheritance public instead.
We do this for the same reasons outlined in part 1: calls to
NS_RUNTIMEABORT are rather large and we generate a lot of them (~1000
left after part 1). This patch reduces .text size by ~20K on x86-64
Linux.
We don't do anything with it in terms of error reporting, we pass in 0
in the child process, and if you're in a debugger, presumably you can
figure out the other process's PID yourself.
The first step to eliminating all the generated Message subclasses the
IPDL compiler spits out is to move the functionality of their Log
methods someplace else. In addition to eliminating the need for the Log
methods, this change has the welcome effect of moving a bunch of code
that would be generated hundreds of times into a single place, which
should reduce code size a bit (debug builds only). We don't actually
remove the generation of the Log methods; that change will be done for a
future patch.
The bulk of this commit was generated with a script, executed at the top
level of a typical source code checkout. The only non-machine-generated
part was modifying MFBT's moz.build to reflect the new naming.
CLOSED TREE makes big refactorings like this a piece of cake.
# The main substitution.
find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \
xargs perl -p -i -e '
s/nsRefPtr\.h/RefPtr\.h/g; # handle includes
s/nsRefPtr ?</RefPtr</g; # handle declarations and variables
'
# Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h.
perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h
# Handle nsRefPtr.h itself, a couple places that define constructors
# from nsRefPtr, and code generators specially. We do this here, rather
# than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename
# things like nsRefPtrHashtable.
perl -p -i -e 's/nsRefPtr/RefPtr/g' \
mfbt/nsRefPtr.h \
xpcom/glue/nsCOMPtr.h \
xpcom/base/OwningNonNull.h \
ipc/ipdl/ipdl/lower.py \
ipc/ipdl/ipdl/builtin.py \
dom/bindings/Codegen.py \
python/lldbutils/lldbutils/utils.py
# In our indiscriminate substitution above, we renamed
# nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up.
find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \
xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g'
if [ -d .git ]; then
git mv mfbt/nsRefPtr.h mfbt/RefPtr.h
else
hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h
fi
--HG--
rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
A lot of existing code has variations on:
if (ManagedPFooChild().Length()) {
...(ManagedPFooChild()[0])...
}
// Do something with nullptr, or some other action.
It's pretty reasonable to repeat this code when the managed protocols
are stored in an array; the code gets much less nice when managed
protocols are stored in a hashtable. Let's write a small utility
function to handle those details for us. Then when we change the
underlying storage, we only need to update this function, rather than a
bunch of callsites.
ProtocolUtils.h is included by all the generated IPDL headers, so
LoneManagedOrNull should be available everywhere the above pattern would
be encountered.