From 03bc840cdd0c33f5ef43dee7beaf4c1574519eff Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 19 Jun 2012 12:09:37 -0400 Subject: [PATCH] Bug 763911. Add support for interface members of dictionaries. r=khuey --- dom/bindings/Codegen.py | 116 ++++++++++++++++++++------- dom/bindings/Configuration.py | 65 ++++++++++----- dom/bindings/parser/WebIDL.py | 5 ++ dom/bindings/test/TestCodeGen.webidl | 2 + 4 files changed, 137 insertions(+), 51 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 5d4ec10acacf..53888c753793 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -8,6 +8,7 @@ import os import string from WebIDL import * +from Configuration import NoSuchDescriptorError AUTOGENERATED_WARNING_COMMENT = \ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" @@ -351,6 +352,12 @@ class CGHeaders(CGWrapper): attrs = [a for a in members if a.isAttr()] types.extend([a.type for a in attrs]) + for dictionary in dictionaries: + curDict = dictionary + while curDict: + types.extend([m.type for m in curDict.members]) + curDict = curDict.parent + for t in types: if t.unroll().isInterface(): if t.unroll().isSpiderMonkeyInterface(): @@ -1556,15 +1563,15 @@ for (uint32_t i = 0; i < length; ++i) { elif descriptor.workers: templateBody += "${declName} = &${val}.toObject();" else: - # Either external, or new-binding non-castable. We always have a - # holder for these, because we don't actually know whether we have - # to addref when unwrapping or not. So we just pass an - # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release - # it'll put a non-null pointer in there. + # External interface. We always have a holder for these, + # because we don't actually know whether we have to addref + # when unwrapping or not. So we just pass an + # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need + # a release it'll put a non-null pointer in there. if forceOwningType: # Don't return a holderType in this case; our declName # will just own stuff. - templateBody += "nsRefPtr<" + typeName + "> ${holderName};" + templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n" else: holderType = "nsRefPtr<" + typeName + ">" templateBody += ( @@ -1756,11 +1763,13 @@ for (uint32_t i = 0; i < length; ++i) { raise TypeError("Can't handle dictionaries when failureCode is not None") if type.nullable(): - typeName = type.inner.inner.identifier.name + typeName = CGDictionary.makeDictionaryName(type.inner.inner, + descriptorProvider.workers) declType = CGGeneric("Nullable<%s>" % typeName) selfRef = "${declName}.Value()" else: - typeName = type.inner.identifier.name + typeName = CGDictionary.makeDictionaryName(type.inner, + descriptorProvider.workers) declType = CGGeneric(typeName) selfRef = "${declName}" # If we're optional or a member of something else, the const @@ -3490,24 +3499,37 @@ class CGNamespacedEnum(CGThing): assert False # Only for headers. class CGDictionary(CGThing): - def __init__(self, dictionary, workers): + def __init__(self, dictionary, descriptorProvider): self.dictionary = dictionary; - self.workers = workers - # Fake a descriptorProvider - # XXXbz this will fail for interface types! - for member in dictionary.members: - if member.type.unroll().isInterface(): - raise TypeError("No support for interface members of dictionaries: %s.%s" % - (dictionary.identifier.name, member.identifier.name)) - self.memberInfo = [ - (member, - getJSToNativeConversionTemplate(member.type, - { "workers": workers }, - isMember=True, - isOptional=(not member.defaultValue))) - for member in dictionary.members ] + self.workers = descriptorProvider.workers + if dictionary.parent: + parentCGThing = CGDictionary(dictionary.parent, descriptorProvider) + self.generatable = parentCGThing.generatable + if not self.generatable: + # Nothing else to do here + return + else: + self.generatable = True + # 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=True, + isOptional=(not member.defaultValue))) + for member in dictionary.members ] + except NoSuchDescriptorError, err: + if not self.workers: + raise err + self.generatable = False def declare(self): + if not self.generatable: + return "" d = self.dictionary if d.parent: inheritance = ": public %s " % self.makeClassName(d.parent) @@ -3535,6 +3557,8 @@ 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" @@ -3590,10 +3614,14 @@ class CGDictionary(CGThing): "idInit": CGIndenter(idinit).define() }) - def makeClassName(self, dictionary): - suffix = "Workers" if self.workers else "" + @staticmethod + def makeDictionaryName(dictionary, workers): + suffix = "Workers" if workers else "" return dictionary.identifier.name + suffix + def makeClassName(self, dictionary): + return self.makeDictionaryName(dictionary, self.workers) + def getMemberType(self, memberInfo): (member, (templateBody, declType, holderType, dealWithOptional)) = memberInfo @@ -3604,7 +3632,6 @@ class CGDictionary(CGThing): return declType.define() def getMemberConversion(self, memberInfo): - # Fake a descriptorProvider (member, (templateBody, declType, holderType, dealWithOptional)) = memberInfo replacements = { "val": "temp", @@ -3616,7 +3643,11 @@ class CGDictionary(CGThing): # the guts of init to a static method which is passed # an explicit reference to our dictionary object, so # we couldn't screw this up even if we wanted to.... - "declName": ("(this->%s)" % member.identifier.name) } + "declName": ("(this->%s)" % member.identifier.name), + # We need a holder name for external interfaces, but + # it's scoped down to the conversion so we can just use + # anything we want. + "holderName": "holder"} # We can't handle having a holderType here assert holderType is None if dealWithOptional: @@ -3696,7 +3727,30 @@ class CGBindingRoot(CGThing): forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')] - for x in descriptors: + descriptorsForForwardDeclaration = list(descriptors) + for dictionary in dictionaries: + curDict = dictionary + ifacemembers = [] + while curDict: + ifacemembers.extend([m.type.unroll().inner for m + in curDict.members + if m.type.unroll().isInterface()]) + curDict = curDict.parent + # Put in all the non-worker descriptors + descriptorsForForwardDeclaration.extend( + [config.getDescriptor(iface.identifier.name, False) for + iface in ifacemembers]) + # And now the worker ones. But these may not exist, so we + # have to be more careful. + for iface in ifacemembers: + try: + descriptorsForForwardDeclaration.append( + config.getDescriptor(iface.identifier.name, True)) + except NoSuchDescriptorError: + # just move along + pass + + for x in descriptorsForForwardDeclaration: nativeType = x.nativeType components = x.nativeType.split('::') className = components[-1] @@ -3755,8 +3809,10 @@ class CGBindingRoot(CGThing): reSortedDictionaries.extend(toMove) dictionaries = reSortedDictionaries - cgthings.extend([CGDictionary(d, workers=True) for d in dictionaries]) - cgthings.extend([CGDictionary(d, workers=False) for d in dictionaries]) + cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True)) + for d in dictionaries]) + cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False)) + for d in dictionaries]) # Do codegen for all the descriptors cgthings.extend([CGDescriptor(x) for x in descriptors]) diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 63c02bfb6cb1..6d478bf3b3b3 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -75,13 +75,55 @@ class Configuration: return filter(lambda e: e.filename() == webIDLFile, self.enums) def getDictionaries(self, webIDLFile): return filter(lambda d: d.filename() == webIDLFile, self.dictionaries) + def getDescriptor(self, interfaceName, workers): + """ + Gets the appropriate descriptor for the given interface name + and the given workers boolean. + """ + iface = self.getInterface(interfaceName) + descriptors = self.getDescriptors(interface=iface) -class Descriptor: + # The only filter we currently have is workers vs non-workers. + matches = filter(lambda x: x.workers is workers, descriptors) + + # After filtering, we should have exactly one result. + if len(matches) is not 1: + raise NoSuchDescriptorError("For " + interfaceName + " found " + + str(len(matches)) + " matches"); + return matches[0] + def getDescriptorProvider(self, workers): + """ + Gets a descriptor provider that can provide descriptors as needed, + for the given workers boolean + """ + return DescriptorProvider(self, workers) + +class NoSuchDescriptorError(TypeError): + def __init__(self, str): + TypeError.__init__(self, str) + +class DescriptorProvider: + """ + A way of getting descriptors for interface names + """ + def __init__(self, config, workers): + self.config = config + self.workers = workers + + def getDescriptor(self, interfaceName): + """ + Gets the appropriate descriptor for the given interface name given the + context of the current descriptor. This selects the appropriate + implementation for cases like workers. + """ + return self.config.getDescriptor(interfaceName, self.workers) + +class Descriptor(DescriptorProvider): """ Represents a single descriptor for an interface. See Bindings.conf. """ def __init__(self, config, interface, desc): - self.config = config + DescriptorProvider.__init__(self, config, desc.get('workers', False)) self.interface = interface # Read the desc, and fill in the relevant defaults. @@ -109,7 +151,6 @@ class Descriptor: self.prefable = desc.get('prefable', False) - self.workers = desc.get('workers', False) self.nativeIsISupports = not self.workers self.customTrace = desc.get('customTrace', self.workers) self.customFinalize = desc.get('customFinalize', self.workers) @@ -166,24 +207,6 @@ class Descriptor: return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject() - def getDescriptor(self, interfaceName): - """ - Gets the appropriate descriptor for the given interface name given the - context of the current descriptor. This selects the appropriate - implementation for cases like workers. - """ - iface = self.config.getInterface(interfaceName) - descriptors = self.config.getDescriptors(interface=iface) - - # The only filter we currently have is workers vs non-workers. - matches = filter(lambda x: x.workers is self.workers, descriptors) - - # After filtering, we should have exactly one result. - if len(matches) is not 1: - raise TypeError("For " + interfaceName + " found " + - str(len(matches)) + " matches"); - return matches[0] - def getExtendedAttributes(self, member, getter=False, setter=False): name = member.identifier.name if member.isMethod(): diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 649ebe52704b..6f4b6b5c7d89 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -634,6 +634,11 @@ class IDLDictionary(IDLObjectWithScope): for member in self.members: member.resolve(self) + if not member.type.isComplete(): + type = member.type.complete(scope) + assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type.name, IDLUnresolvedIdentifier) + member.type = type # Members of a dictionary are sorted in lexicographic order self.members.sort(cmp=cmp, key=lambda x: x.identifier.name) diff --git a/dom/bindings/test/TestCodeGen.webidl b/dom/bindings/test/TestCodeGen.webidl index dd3a080c2141..f1837543b99d 100644 --- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -304,4 +304,6 @@ dictionary Dict : ParentDict { dictionary ParentDict : GrandparentDict { long c = 5; + TestInterface someInterface; + TestExternalInterface someExternalInterface; };