From 719c7bf0305407041965c5d6c2309030a89ecc41 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Mon, 28 Jan 2013 23:30:17 -0500 Subject: [PATCH] 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 --- dom/bindings/Bindings.conf | 11 + dom/bindings/Codegen.py | 208 ++++++------------ dom/bindings/Configuration.py | 130 ++++++++++- dom/bindings/parser/WebIDL.py | 6 - dom/webidl/DummyBinding.webidl | 18 ++ dom/webidl/WebIDL.mk | 1 + .../src/peerconnection/PeerConnectionImpl.cpp | 2 + 7 files changed, 224 insertions(+), 152 deletions(-) create mode 100644 dom/webidl/DummyBinding.webidl diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 932d7e080589..39a8ea13106d 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -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' ], diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index dc439058d499..13c801825610 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -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)]) diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 6f5376cae319..e5d3fb0ff231 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -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) diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 754789e15b4a..825eb83e4dce 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -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: diff --git a/dom/webidl/DummyBinding.webidl b/dom/webidl/DummyBinding.webidl new file mode 100644 index 000000000000..9027d0904d41 --- /dev/null +++ b/dom/webidl/DummyBinding.webidl @@ -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(); +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index a177d6ef5644..a42731e2cd9f 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -40,6 +40,7 @@ webidl_files = \ DOMStringMap.webidl \ DOMTokenList.webidl \ DOMTransaction.webidl \ + DummyBinding.webidl \ DynamicsCompressorNode.webidl \ Element.webidl \ EventHandler.webidl \ diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index f31db4cb131e..68149afb28ae 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -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;