Bug 830099. Flag WebIDL dictionaries and callbacks with some information indicating whether we need main-thread and worker codegen for them and then use that information to skip unneccessary codegen. r=peterv

This commit is contained in:
Boris Zbarsky 2013-01-28 23:30:17 -05:00
Родитель 8ccc3897f4
Коммит 719c7bf030
7 изменённых файлов: 224 добавлений и 152 удалений

Просмотреть файл

@ -248,6 +248,17 @@ DOMInterfaces = {
}
},
'DummyInterface': {
'skipGen': True,
'register': False,
},
'DummyInterfaceWorkers': {
'skipGen': True,
'register': False,
'workers': True
},
'DynamicsCompressorNode': {
'resultNotAddRefed': [ 'threshold', 'knee', 'ratio',
'reduction', 'attack', 'release' ],

Просмотреть файл

@ -10,7 +10,7 @@ import re
import string
from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType
from Configuration import NoSuchDescriptorError
from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback
AUTOGENERATED_WARNING_COMMENT = \
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
@ -389,45 +389,6 @@ class CGIncludeGuard(CGWrapper):
declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
declarePost='\n#endif // %s\n' % define)
def getTypesFromDescriptor(descriptor):
"""
Get all argument and return types for all members of the descriptor
"""
members = [m for m in descriptor.interface.members]
if descriptor.interface.ctor():
members.append(descriptor.interface.ctor())
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
types = []
for s in signatures:
assert len(s) == 2
(returnType, arguments) = s
types.append(returnType)
types.extend(a.type for a in arguments)
types.extend(a.type for a in members if a.isAttr())
return types
def getTypesFromDictionary(dictionary):
"""
Get all member types for this dictionary
"""
types = []
curDict = dictionary
while curDict:
types.extend([m.type for m in curDict.members])
curDict = curDict.parent
return types
def getTypesFromCallback(callback):
"""
Get the types this callback depends on: its return type and the
types of its arguments.
"""
sig = callback.signatures()[0]
types = [sig[0]] # Return type
types.extend(arg.type for arg in sig[1]) # Arguments
return types
def getRelevantProviders(descriptor, dictionary, config):
assert not descriptor or not dictionary
if descriptor is not None:
@ -6523,34 +6484,16 @@ class CGDictionary(CGThing):
self.descriptorProvider = descriptorProvider
self.workers = descriptorProvider.workers
self.needToInitIds = not self.workers and len(dictionary.members) > 0
if all(CGDictionary(d, descriptorProvider).generatable for
d in CGDictionary.getDictionaryDependencies(dictionary)):
self.generatable = True
else:
self.generatable = False
# Nothing else to do here
return
# Getting a conversion template for interface types can fail
# if we don't have a relevant descriptor when self.workers is True.
# If that happens, just mark ourselves as not being
# generatable and move on.
try:
self.memberInfo = [
(member,
getJSToNativeConversionTemplate(member.type,
descriptorProvider,
isMember="Dictionary",
isOptional=(not member.defaultValue),
defaultValue=member.defaultValue))
for member in dictionary.members ]
except NoSuchDescriptorError, err:
if not self.workers:
raise err
self.generatable = False
self.memberInfo = [
(member,
getJSToNativeConversionTemplate(member.type,
descriptorProvider,
isMember="Dictionary",
isOptional=(not member.defaultValue),
defaultValue=member.defaultValue))
for member in dictionary.members ]
def declare(self):
if not self.generatable:
return ""
d = self.dictionary
if d.parent:
inheritance = ": public %s " % self.makeClassName(d.parent)
@ -6599,8 +6542,6 @@ class CGDictionary(CGThing):
"inheritance": inheritance }))
def define(self):
if not self.generatable:
return ""
d = self.dictionary
if d.parent:
initParent = ("// Per spec, we init the parent's members first\n"
@ -6884,8 +6825,14 @@ class CGBindingRoot(CGThing):
descriptors = config.getDescriptors(webIDLFile=webIDLFile,
hasInterfaceOrInterfacePrototypeObject=True,
skipGen=False)
dictionaries = config.getDictionaries(webIDLFile)
callbacks = config.getCallbacks(webIDLFile)
mainDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
workers=False)
workerDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
workers=True)
mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
workers=False)
workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
workers=True)
callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
isCallback=True)
@ -6894,20 +6841,23 @@ class CGBindingRoot(CGThing):
descriptorsForForwardDeclaration = list(descriptors)
ifaces = []
workerIfaces = []
for dictionary in dictionaries:
dictionaryIfaces = [ type.unroll().inner
for type in getTypesFromDictionary(dictionary)
if type.unroll().isGeckoInterface() ]
ifaces.extend(dictionaryIfaces)
workerIfaces.extend(dictionaryIfaces)
def getInterfacesFromDictionary(d):
return [ type.unroll().inner
for type in getTypesFromDictionary(d)
if type.unroll().isGeckoInterface() ]
for dictionary in mainDictionaries:
ifaces.extend(getInterfacesFromDictionary(dictionary))
for dictionary in workerDictionaries:
workerIfaces.extend(getInterfacesFromDictionary(dictionary))
for callback in callbacks:
callbackIfaces = [ t.unroll().inner
for t in getTypesFromCallback(callback)
if t.unroll().isGeckoInterface() ]
workerIfaces.extend(callbackIfaces)
if not callback.isWorkerOnly():
ifaces.extend(callbackIfaces)
def getInterfacesFromCallback(c):
return [ t.unroll().inner
for t in getTypesFromCallback(c)
if t.unroll().isGeckoInterface() ]
for callback in mainCallbacks:
ifaces.extend(getInterfacesFromCallback(callback))
for callback in workerCallbacks:
workerIfaces.extend(getInterfacesFromCallback(callback))
for callbackDescriptor in callbackDescriptors:
callbackDescriptorIfaces = [
@ -6948,7 +6898,7 @@ class CGBindingRoot(CGThing):
# Now add the forward declarations we need for our union types
# and callback functions.
for callback in callbacks:
for callback in mainCallbacks + workerCallbacks:
forwardDeclares.extend(
declareNativeType("mozilla::dom::" + str(t.unroll()))
for t in getTypesFromCallback(callback)
@ -6961,7 +6911,7 @@ class CGBindingRoot(CGThing):
if t.unroll().isUnion() or t.unroll().isCallback())
# Forward declarations for callback functions used in dictionaries.
for dictionary in dictionaries:
for dictionary in mainDictionaries + workerDictionaries:
forwardDeclares.extend(
declareNativeType("mozilla::dom::" + str(t.unroll()))
for t in getTypesFromDictionary(dictionary)
@ -7007,30 +6957,31 @@ class CGBindingRoot(CGThing):
# declares a dictionary which inherits from a dictionary in B and B
# declares a dictionary (possibly a different one!) that inherits from a
# dictionary in A. The good news is that I expect this to never happen.
reSortedDictionaries = []
dictionaries = set(dictionaries)
while len(dictionaries) != 0:
# Find the dictionaries that don't depend on anything else anymore
# and move them over.
toMove = [d for d in dictionaries if
len(CGDictionary.getDictionaryDependencies(d) &
dictionaries) == 0]
if len(toMove) == 0:
raise TypeError("Loop in dictionary dependency graph")
dictionaries = dictionaries - set(toMove)
reSortedDictionaries.extend(toMove)
def sortDictionaries(dictionaries):
reSortedDictionaries = []
dictionaries = set(dictionaries)
while len(dictionaries) != 0:
# Find the dictionaries that don't depend on anything else
# anymore and move them over.
toMove = [d for d in dictionaries if
len(CGDictionary.getDictionaryDependencies(d) &
dictionaries) == 0]
if len(toMove) == 0:
raise TypeError("Loop in dictionary dependency graph")
dictionaries = dictionaries - set(toMove)
reSortedDictionaries.extend(toMove)
return reSortedDictionaries
dictionaries = reSortedDictionaries
cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True))
for d in dictionaries])
for d in sortDictionaries(workerDictionaries)])
cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
for d in dictionaries])
for d in sortDictionaries(mainDictionaries)])
# Do codegen for all the callbacks. Only do non-worker codegen for now,
# since we don't have a sane setup yet for invoking callbacks in workers
# and managing their lifetimes.
cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False))
for c in callbacks)
for c in mainCallbacks)
# Do codegen for all the descriptors
cgthings.extend([CGDescriptor(x) for x in descriptors])
@ -7053,8 +7004,8 @@ class CGBindingRoot(CGThing):
# Add header includes.
curr = CGHeaders(descriptors,
dictionaries,
callbacks,
mainDictionaries + workerDictionaries,
mainCallbacks + workerCallbacks,
callbackDescriptors,
['mozilla/dom/BindingDeclarations.h',
'mozilla/ErrorResult.h',
@ -7669,38 +7620,22 @@ class CGCallback(CGClass):
name = idlObject.identifier.name
if descriptorProvider.workers:
name += "Workers"
try:
# For our public methods that needThisHandling we want most of the
# same args and the same return type as what CallbackMember
# generates. So we want to take advantage of all its
# CGNativeMember infrastructure, but that infrastructure can't deal
# with templates and most especially template arguments. So just
# cheat and have CallbackMember compute all those things for us.
realMethods = []
for method in methods:
if not method.needThisHandling:
realMethods.append(method)
else:
realMethods.extend(self.getMethodImpls(method))
CGClass.__init__(self, name,
bases=[ClassBase(baseName)],
constructors=self.getConstructors(),
methods=realMethods+getters+setters)
self.generatable = True
except NoSuchDescriptorError, err:
if not descriptorProvider.workers:
raise err
self.generatable = False
def define(self):
if not self.generatable:
return ""
return CGClass.define(self)
def declare(self):
if not self.generatable:
return ""
return CGClass.declare(self)
# For our public methods that needThisHandling we want most of the
# same args and the same return type as what CallbackMember
# generates. So we want to take advantage of all its
# CGNativeMember infrastructure, but that infrastructure can't deal
# with templates and most especially template arguments. So just
# cheat and have CallbackMember compute all those things for us.
realMethods = []
for method in methods:
if not method.needThisHandling:
realMethods.append(method)
else:
realMethods.extend(self.getMethodImpls(method))
CGClass.__init__(self, name,
bases=[ClassBase(baseName)],
constructors=self.getConstructors(),
methods=realMethods+getters+setters)
def getConstructors(self):
return [ClassConstructor(
@ -7768,9 +7703,6 @@ class CGCallback(CGClass):
class CGCallbackFunction(CGCallback):
def __init__(self, callback, descriptorProvider):
if callback.isWorkerOnly() and not descriptorProvider.workers:
self.generatable = False
return
CGCallback.__init__(self, callback, descriptorProvider,
"CallbackFunction",
methods=[CallCallback(callback, descriptorProvider)])

Просмотреть файл

@ -62,10 +62,33 @@ class Configuration:
descriptor.uniqueImplementation = len(otherDescriptors) == 1
self.enums = [e for e in parseData if e.isEnum()]
# Figure out what our main-thread and worker dictionaries and callbacks
# are.
mainTypes = set()
for descriptor in self.getDescriptors(workers=False, isExternal=False):
mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
(mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes)
workerTypes = set();
for descriptor in self.getDescriptors(workers=True, isExternal=False):
workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
(workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes)
self.dictionaries = [d for d in parseData if d.isDictionary()]
self.callbacks = [c for c in parseData if
c.isCallback() and not c.isInterface()]
def flagWorkerOrMainThread(items, main, worker):
for item in items:
if item in main:
item.setUserData("mainThread", True)
if item in worker:
item.setUserData("workers", True)
flagWorkerOrMainThread(self.dictionaries, mainDictionaries,
workerDictionaries);
flagWorkerOrMainThread(self.callbacks, mainCallbacks, workerCallbacks)
# Keep the descriptor list sorted for determinism.
self.descriptors.sort(lambda x,y: cmp(x.name, y.name))
@ -95,14 +118,26 @@ class Configuration:
return curr
def getEnums(self, webIDLFile):
return filter(lambda e: e.filename() == webIDLFile, self.enums)
def getDictionaries(self, webIDLFile=None):
if not webIDLFile:
return self.dictionaries
return filter(lambda d: d.filename() == webIDLFile, self.dictionaries)
def getCallbacks(self, webIDLFile=None):
if not webIDLFile:
return self.callbacks
return filter(lambda d: d.filename() == webIDLFile, self.callbacks)
@staticmethod
def _filterForFileAndWorkers(items, filters):
"""Gets the items that match the given filters."""
for key, val in filters.iteritems():
if key == 'webIDLFile':
items = filter(lambda x: x.filename() == val, items)
elif key == 'workers':
if val:
items = filter(lambda x: x.getUserData("workers", False), items)
else:
items = filter(lambda x: x.getUserData("mainThread", False), items)
else:
assert(0) # Unknown key
return items
def getDictionaries(self, **filters):
return self._filterForFileAndWorkers(self.dictionaries, filters)
def getCallbacks(self, **filters):
return self._filterForFileAndWorkers(self.callbacks, filters)
def getDescriptor(self, interfaceName, workers):
"""
Gets the appropriate descriptor for the given interface name
@ -407,3 +442,82 @@ class Descriptor(DescriptorProvider):
def needsConstructHookHolder(self):
assert self.interface.hasInterfaceObject()
return not self.hasInstanceInterface and not self.interface.isCallback()
# Some utility methods
def getTypesFromDescriptor(descriptor):
"""
Get all argument and return types for all members of the descriptor
"""
members = [m for m in descriptor.interface.members]
if descriptor.interface.ctor():
members.append(descriptor.interface.ctor())
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
types = []
for s in signatures:
assert len(s) == 2
(returnType, arguments) = s
types.append(returnType)
types.extend(a.type for a in arguments)
types.extend(a.type for a in members if a.isAttr())
return types
def getFlatTypes(types):
retval = set()
for type in types:
type = type.unroll()
if type.isUnion():
retval |= set(type.flatMemberTypes)
else:
retval.add(type)
return retval
def getTypesFromDictionary(dictionary):
"""
Get all member types for this dictionary
"""
types = []
curDict = dictionary
while curDict:
types.extend([m.type for m in curDict.members])
curDict = curDict.parent
return types
def getTypesFromCallback(callback):
"""
Get the types this callback depends on: its return type and the
types of its arguments.
"""
sig = callback.signatures()[0]
types = [sig[0]] # Return type
types.extend(arg.type for arg in sig[1]) # Arguments
return types
def findCallbacksAndDictionaries(inputTypes):
"""
Ensure that all callbacks and dictionaries reachable from types end up in
the returned callbacks and dictionaries sets.
Note that we assume that our initial invocation already includes all types
reachable via descriptors in "types", so we only have to deal with things
that are themeselves reachable via callbacks and dictionaries.
"""
def doFindCallbacksAndDictionaries(types, callbacks, dictionaries):
unhandledTypes = set()
for type in types:
if type.isCallback() and type not in callbacks:
unhandledTypes |= getFlatTypes(getTypesFromCallback(type))
callbacks.add(type)
elif type.isDictionary() and type.inner not in dictionaries:
d = type.inner
unhandledTypes |= getFlatTypes(getTypesFromDictionary(d))
while d:
dictionaries.add(d)
d = d.parent
if len(unhandledTypes) != 0:
doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries)
retCallbacks = set()
retDictionaries = set()
doFindCallbacksAndDictionaries(inputTypes, retCallbacks, retDictionaries)
return (retCallbacks, retDictionaries)

Просмотреть файл

@ -2346,7 +2346,6 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
argument.resolve(self)
self._treatNonCallableAsNull = False
self._workerOnly = False
def isCallback(self):
return True
@ -2387,16 +2386,11 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isNonCallbackInterface() or other.isDate())
def isWorkerOnly(self):
return self._workerOnly
def addExtendedAttributes(self, attrs):
unhandledAttrs = []
for attr in attrs:
if attr.identifier() == "TreatNonCallableAsNull":
self._treatNonCallableAsNull = True
elif attr.identifier() == "WorkerOnly":
self._workerOnly = True
else:
unhandledAttrs.append(attr)
if len(unhandledAttrs) != 0:

Просмотреть файл

@ -0,0 +1,18 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// Dummy bindings that we need to force generation of things that
// aren't actually referenced anywhere in IDL yet but are used in C++.
interface DummyInterface {
readonly attribute OnErrorEventHandlerNonNull onErrorEventHandler;
FilePropertyBag fileBag();
RTCIceServer rtcIceServer();
};
interface DummyInterfaceWorkers {
BlobPropertyBag blobBag();
};

Просмотреть файл

@ -40,6 +40,7 @@ webidl_files = \
DOMStringMap.webidl \
DOMTokenList.webidl \
DOMTransaction.webidl \
DummyBinding.webidl \
DynamicsCompressorNode.webidl \
Element.webidl \
EventHandler.webidl \

Просмотреть файл

@ -351,6 +351,8 @@ PeerConnectionImpl::ConvertRTCConfiguration(const JS::Value& aSrc,
}
for (uint32_t i = 0; i < len; i++) {
nsresult rv;
// XXXbz once this moves to WebIDL, remove the RTCIceServer hack
// in DummyBinding.webidl.
RTCIceServer server;
{
JS::Value v;