diff options
author | Josh Matthews <josh@joshmatthews.net> | 2013-10-31 17:25:44 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2013-11-05 12:57:02 -0500 |
commit | 06b1db8818c09201989b017434eef105f4d99e51 (patch) | |
tree | 0e4e0271c873a79034c476665f10bc3eac5843c7 /src/components/script/dom/bindings/codegen | |
parent | d00736a9c059f08c8b411b3aada795e8bb9e2ea3 (diff) | |
download | servo-06b1db8818c09201989b017434eef105f4d99e51.tar.gz servo-06b1db8818c09201989b017434eef105f4d99e51.zip |
Import unmodified callbacks-related codegen source from Gecko.
Diffstat (limited to 'src/components/script/dom/bindings/codegen')
3 files changed, 1734 insertions, 23 deletions
diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 5734df036c6..0d012d2b1c8 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -9,7 +9,7 @@ import string import operator from WebIDL import * -from Configuration import NoSuchDescriptorError +from Configuration import NoSuchDescriptorError, Descriptor AUTOGENERATED_WARNING_COMMENT = \ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" @@ -445,7 +445,10 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, treatNullAs="Default", treatUndefinedAs="Default", isEnforceRange=False, - isClamp=False): + isClamp=False, + exceptionCode=None, + isCallbackReturnValue=False, + sourceDescription="value"): """ Get a template for converting a JS value to a native object based on the given type and descriptor. If failureCode is given, then we're actually @@ -517,16 +520,40 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, # Also, we should not have a defaultValue if we know we're an object assert(not isDefinitelyObject or defaultValue is None) + # If exceptionCode is not set, we'll just rethrow the exception we got. + # Note that we can't just set failureCode to exceptionCode, because setting + # failureCode will prevent pending exceptions from being set in cases when + # they really should be! + if exceptionCode is None: + exceptionCode = "return false;" + # We often want exceptionCode to be indented, since it often appears in an + # if body. + exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode)) + + # Unfortunately, .capitalize() on a string will lowercase things inside the + # string, which we do not want. + def firstCap(string): + return string[0].upper() + string[1:] + + # Helper functions for dealing with failures due to the JS value being the + # wrong type of value # Helper functions for dealing with failures due to the JS value being the # wrong type of value def onFailureNotAnObject(failureCode): - return CGWrapper(CGGeneric( + return CGWrapper( + CGGeneric( failureCode or - 'return 0; //XXXjdm return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n") + ('//XXXjdm ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' + '%s' % (firstCap(sourceDescription), exceptionCode))), + post="\n") def onFailureBadType(failureCode, typeName): - return CGWrapper(CGGeneric( + return CGWrapper( + CGGeneric( failureCode or - 'return 0; //XXXjdm return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n") + ('//XXXjdm ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s")\n;' + '%s' % (firstCap(sourceDescription), typeName, + exceptionCode))), + post="\n") # A helper function for handling default values. Takes a template # body and the C++ code to set the default value and wraps the @@ -886,6 +913,22 @@ for (uint32_t i = 0; i < length; ++i) { descriptor = descriptorProvider.getDescriptor( type.unroll().inner.identifier.name) + + if descriptor.interface.isCallback(): + name = descriptor.interface.identifier.name + if type.nullable() or isCallbackReturnValue: + declType = CGGeneric("nsRefPtr<%s>" % name); + else: + declType = CGGeneric("OwningNonNull<%s>" % name) + conversion = ( + " ${declName} = new %s(&${val}.toObject());\n" % name) + + template = wrapObjectTemplate(conversion, type, + "${declName} = nullptr", + failureCode) + return JSToNativeConversionInfo(template, declType=declType, + dealWithOptional=isOptional) + # This is an interface that we implement as a concrete class # or an XPCOM interface. @@ -1218,6 +1261,12 @@ for (uint32_t i = 0; i < length; ++i) { return (template, declType, None, False, None) + if type.isVoid(): + assert not isOptional + # This one only happens for return values, and its easy: Just + # ignore the jsval. + return JSToNativeConversionInfo("") + if not type.isPrimitive(): raise TypeError("Need conversion for argument type '%s'" % str(type)) @@ -1422,7 +1471,7 @@ class CGArgumentConverter(CGThing): self.argcAndIndex).define() def getWrapTemplateForType(type, descriptorProvider, result, successCode, - isCreator): + isCreator, exceptionCode): """ Reflect a C++ value stored in "result", of IDL type "type" into JS. The "successCode" is the code to run once we have successfully done the @@ -1435,6 +1484,10 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, if not haveSuccessCode: successCode = "return 1;" + # We often want exceptionCode to be indented, since it often appears in an + # if body. + exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode)) + def setValue(value, callWrapValue=False): """ Returns the code to set the jsval to value. If "callWrapValue" is true @@ -1478,7 +1531,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, # Nullable sequences are Nullable< nsTArray<T> > (recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider, "%s.Value()" % result, successCode, - isCreator) + isCreator, exceptionCode) return (""" if (%s.IsNull()) { %s @@ -1611,7 +1664,7 @@ if %(resultStr)s.is_null() { if type.nullable(): (recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider, "%s.Value()" % result, successCode, - isCreator) + isCreator, exceptionCode) return ("if (%s.IsNull()) {\n" % result + CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" + "}\n" + recTemplate, recInfal) @@ -1661,7 +1714,9 @@ def wrapForType(type, descriptorProvider, templateValues): wrap = getWrapTemplateForType(type, descriptorProvider, templateValues.get('result', 'result'), templateValues.get('successCode', None), - templateValues.get('isCreator', False))[0] + templateValues.get('isCreator', False), + templateValues.get('exceptionCode', + "return 0;"),)[0] defaultValues = {'obj': 'obj'} return string.Template(wrap).substitute(defaultValues, **templateValues) @@ -2363,11 +2418,17 @@ class Argument(): """ A class for outputting the type and name of an argument """ - def __init__(self, argType, name): + def __init__(self, argType, name, default=None): self.argType = argType self.name = name - def __str__(self): - return self.name + ': ' + self.argType + self.default = default + def declare(self): + string = self.argType + ' ' + self.name + if self.default is not None: + string += " = " + self.default + return string + def define(self): + return self.argType + ' ' + self.name class CGAbstractMethod(CGThing): """ @@ -2408,8 +2469,8 @@ class CGAbstractMethod(CGThing): self.templateArgs = templateArgs self.pub = pub; self.unsafe = unsafe - def _argstring(self): - return ', '.join([str(a) for a in self.args]) + def _argstring(self, declare): + return ', '.join([a.declare() if declare else a.define() for a in self.args]) def _template(self): if self.templateArgs is None: return '' @@ -2450,13 +2511,13 @@ class CGAbstractMethod(CGThing): # self._argstring(), self._returnType()) return "" - def _define(self): - return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue() + def _define(self, fromDeclare=False): + return self.definition_prologue(fromDeclare) + "\n" + self.definition_body() + self.definition_epilogue() def define(self): return "" if self.inline else self._define() - def definition_prologue(self): + def definition_prologue(self, fromDeclare): return "%sfn %s%s(%s)%s {%s" % (self._decorators(), self.name, self._template(), - self._argstring(), self._returnType(), self._unsafe_open()) + self._argstring(fromDeclare), self._returnType(), self._unsafe_open()) def definition_epilogue(self): return "%s}\n" % self._unsafe_close() def definition_body(self): @@ -2885,6 +2946,21 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): *aEnabled = true; return %s(aCx, global, aReceiver).is_not_null();""" % (getter)) +def isResultAlreadyAddRefed(extendedAttributes): + return not 'resultNotAddRefed' in extendedAttributes + +def needCx(returnType, arguments, extendedAttributes, considerTypes): + return (considerTypes and + (typeNeedsCx(returnType, True) or + any(typeNeedsCx(a.type) for a in arguments)) or + 'implicitJSContext' in extendedAttributes) + +def needScopeObject(returnType, arguments, extendedAttributes, + isWrapperCached, considerTypes): + return (considerTypes and not isWrapperCached and + (typeNeedsScopeObject(returnType, True) or + any(typeNeedsScopeObject(a.type) for a in arguments))) + class CGCallGenerator(CGThing): """ A class to generate an actual call to a C++ object. Assumes that the C++ @@ -3386,7 +3462,7 @@ def infallibleForMember(member, type, descriptorProvider): failure conditions. """ return getWrapTemplateForType(type, descriptorProvider, 'result', None,\ - memberIsCreator(member))[1] + memberIsCreator(member), "return false;",)[1] class CGMemberJITInfo(CGThing): """ @@ -3488,6 +3564,543 @@ class CGEnum(CGThing): """ % (",\n ".join(map(getEnumValueName, self.enum.values())), ",\n ".join(['EnumEntry {value: &"' + val + '", length: ' + str(len(val)) + '}' for val in self.enum.values()])) +class ClassItem: + """ Use with CGClass """ + def __init__(self, name, visibility): + self.name = name + self.visibility = visibility + def declare(self, cgClass): + assert False + def define(self, cgClass): + assert False + +class ClassBase(ClassItem): + def __init__(self, name, visibility='public'): + ClassItem.__init__(self, name, visibility) + def declare(self, cgClass): + return '%s %s' % (self.visibility, self.name) + def define(self, cgClass): + # Only in the header + return '' + +class ClassMethod(ClassItem): + def __init__(self, name, returnType, args, inline=False, static=False, + virtual=False, const=False, bodyInHeader=False, + templateArgs=None, visibility='public', body=None, + breakAfterReturnDecl="\n", + breakAfterSelf="\n", override=False): + """ + override indicates whether to flag the method as MOZ_OVERRIDE + """ + assert not override or virtual + self.returnType = returnType + self.args = args + self.inline = inline or bodyInHeader + self.static = static + self.virtual = virtual + self.const = const + self.bodyInHeader = bodyInHeader + self.templateArgs = templateArgs + self.body = body + self.breakAfterReturnDecl = breakAfterReturnDecl + self.breakAfterSelf = breakAfterSelf + self.override = override + ClassItem.__init__(self, name, visibility) + + def getDecorators(self, declaring): + decorators = [] + if self.inline: + decorators.append('inline') + if declaring: + if self.static: + decorators.append('static') + if self.virtual: + decorators.append('virtual') + if decorators: + return ' '.join(decorators) + ' ' + return '' + + def getBody(self): + # Override me or pass a string to constructor + assert self.body is not None + return self.body + + def declare(self, cgClass): + templateClause = 'template <%s>\n' % ', '.join(self.templateArgs) \ + if self.bodyInHeader and self.templateArgs else '' + args = ', '.join([a.declare() for a in self.args]) + if self.bodyInHeader: + body = CGIndenter(CGGeneric(self.getBody())).define() + body = '\n{\n' + body + '\n}' + else: + body = ';' + + return string.Template("${templateClause}${decorators}${returnType}%s" + "${name}(${args})${const}${override}${body}%s" % + (self.breakAfterReturnDecl, self.breakAfterSelf) + ).substitute({ + 'templateClause': templateClause, + 'decorators': self.getDecorators(True), + 'returnType': self.returnType, + 'name': self.name, + 'const': ' const' if self.const else '', + 'override': ' MOZ_OVERRIDE' if self.override else '', + 'args': args, + 'body': body + }) + + def define(self, cgClass): + if self.bodyInHeader: + return '' + + templateArgs = cgClass.templateArgs + if templateArgs: + if cgClass.templateSpecialization: + templateArgs = \ + templateArgs[len(cgClass.templateSpecialization):] + + if templateArgs: + templateClause = \ + 'template <%s>\n' % ', '.join([str(a) for a in templateArgs]) + else: + templateClause = '' + + args = ', '.join([a.define() for a in self.args]) + + body = CGIndenter(CGGeneric(self.getBody())).define() + + return string.Template("""${templateClause}${decorators}${returnType} +${className}::${name}(${args})${const} +{ +${body} +} +""").substitute({ 'templateClause': templateClause, + 'decorators': self.getDecorators(False), + 'returnType': self.returnType, + 'className': cgClass.getNameString(), + 'name': self.name, + 'args': args, + 'const': ' const' if self.const else '', + 'body': body }) + +class ClassUsingDeclaration(ClassItem): + """" + Used for importing a name from a base class into a CGClass + + baseClass is the name of the base class to import the name from + + name is the name to import + + visibility determines the visibility of the name (public, + protected, private), defaults to public. + """ + def __init__(self, baseClass, name, visibility='public'): + self.baseClass = baseClass + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + return string.Template("""using ${baseClass}::${name}; +""").substitute({ 'baseClass': self.baseClass, + 'name': self.name }) + + def define(self, cgClass): + return '' + +class ClassConstructor(ClassItem): + """ + Used for adding a constructor to a CGClass. + + args is a list of Argument objects that are the arguments taken by the + constructor. + + inline should be True if the constructor should be marked inline. + + bodyInHeader should be True if the body should be placed in the class + declaration in the header. + + visibility determines the visibility of the constructor (public, + protected, private), defaults to private. + + explicit should be True if the constructor should be marked explicit. + + baseConstructors is a list of strings containing calls to base constructors, + defaults to None. + + body contains a string with the code for the constructor, defaults to empty. + """ + def __init__(self, args, inline=False, bodyInHeader=False, + visibility="private", explicit=False, baseConstructors=None, + body=""): + self.args = args + self.inline = inline or bodyInHeader + self.bodyInHeader = bodyInHeader + self.explicit = explicit + self.baseConstructors = baseConstructors or [] + self.body = body + ClassItem.__init__(self, None, visibility) + + def getDecorators(self, declaring): + decorators = [] + if self.explicit: + decorators.append('explicit') + if self.inline and declaring: + decorators.append('inline') + if decorators: + return ' '.join(decorators) + ' ' + return '' + + def getInitializationList(self, cgClass): + items = [str(c) for c in self.baseConstructors] + for m in cgClass.members: + if not m.static: + initialize = m.body + if initialize: + items.append(m.name + "(" + initialize + ")") + + if len(items) > 0: + return '\n : ' + ',\n '.join(items) + return '' + + def getBody(self): + return self.body + + def declare(self, cgClass): + args = ', '.join([a.declare() for a in self.args]) + if self.bodyInHeader: + body = ' ' + self.getBody(); + body = stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + body = self.getInitializationList(cgClass) + '\n{\n' + body + '}' + else: + body = ';' + + return string.Template("""${decorators}${className}(${args})${body} +""").substitute({ 'decorators': self.getDecorators(True), + 'className': cgClass.getNameString(), + 'args': args, + 'body': body }) + + def define(self, cgClass): + if self.bodyInHeader: + return '' + + args = ', '.join([a.define() for a in self.args]) + + body = ' ' + self.getBody() + body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + + return string.Template("""${decorators} +${className}::${className}(${args})${initializationList} +{${body}} +""").substitute({ 'decorators': self.getDecorators(False), + 'className': cgClass.getNameString(), + 'args': args, + 'initializationList': self.getInitializationList(cgClass), + 'body': body }) + +class ClassDestructor(ClassItem): + """ + Used for adding a destructor to a CGClass. + + inline should be True if the destructor should be marked inline. + + bodyInHeader should be True if the body should be placed in the class + declaration in the header. + + visibility determines the visibility of the destructor (public, + protected, private), defaults to private. + + body contains a string with the code for the destructor, defaults to empty. + + virtual determines whether the destructor is virtual, defaults to False. + """ + def __init__(self, inline=False, bodyInHeader=False, + visibility="private", body='', virtual=False): + self.inline = inline or bodyInHeader + self.bodyInHeader = bodyInHeader + self.body = body + self.virtual = virtual + ClassItem.__init__(self, None, visibility) + + def getDecorators(self, declaring): + decorators = [] + if self.virtual and declaring: + decorators.append('virtual') + if self.inline and declaring: + decorators.append('inline') + if decorators: + return ' '.join(decorators) + ' ' + return '' + + def getBody(self): + return self.body + + def declare(self, cgClass): + if self.bodyInHeader: + body = ' ' + self.getBody(); + body = stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + body = '\n{\n' + body + '}' + else: + body = ';' + + return string.Template("""${decorators}~${className}()${body} +""").substitute({ 'decorators': self.getDecorators(True), + 'className': cgClass.getNameString(), + 'body': body }) + + def define(self, cgClass): + if self.bodyInHeader: + return '' + + body = ' ' + self.getBody() + body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n ')) + if len(body) > 0: + body += '\n' + + return string.Template("""${decorators} +${className}::~${className}() +{${body}} +""").substitute({ 'decorators': self.getDecorators(False), + 'className': cgClass.getNameString(), + 'body': body }) + +class ClassMember(ClassItem): + def __init__(self, name, type, visibility="private", static=False, + body=None): + self.type = type; + self.static = static + self.body = body + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + return '%s%s %s;\n' % ('static ' if self.static else '', self.type, + self.name) + + def define(self, cgClass): + if not self.static: + return '' + if self.body: + body = " = " + self.body + else: + body = "" + return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(), + self.name, body) + +class ClassTypedef(ClassItem): + def __init__(self, name, type, visibility="public"): + self.type = type + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + return 'typedef %s %s;\n' % (self.type, self.name) + + def define(self, cgClass): + # Only goes in the header + return '' + +class ClassEnum(ClassItem): + def __init__(self, name, entries, values=None, visibility="public"): + self.entries = entries + self.values = values + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + entries = [] + for i in range(0, len(self.entries)): + if not self.values or i >= len(self.values): + entry = '%s' % self.entries[i] + else: + entry = '%s = %s' % (self.entries[i], self.values[i]) + entries.append(entry) + name = '' if not self.name else ' ' + self.name + return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries)) + + def define(self, cgClass): + # Only goes in the header + return '' + +class ClassUnion(ClassItem): + def __init__(self, name, entries, visibility="public"): + self.entries = [entry + ";" for entry in entries] + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + return 'union %s\n{\n %s\n};\n' % (self.name, '\n '.join(self.entries)) + + def define(self, cgClass): + # Only goes in the header + return '' + +class CGClass(CGThing): + def __init__(self, name, bases=[], members=[], constructors=[], + destructor=None, methods=[], + typedefs = [], enums=[], unions=[], templateArgs=[], + templateSpecialization=[], isStruct=False, + disallowCopyConstruction=False, indent='', + decorators='', + extradeclarations='', + extradefinitions=''): + CGThing.__init__(self) + self.name = name + self.bases = bases + self.members = members + self.constructors = constructors + # We store our single destructor in a list, since all of our + # code wants lists of members. + self.destructors = [destructor] if destructor else [] + self.methods = methods + self.typedefs = typedefs + self.enums = enums + self.unions = unions + self.templateArgs = templateArgs + self.templateSpecialization = templateSpecialization + self.isStruct = isStruct + self.disallowCopyConstruction = disallowCopyConstruction + self.indent = indent + self.defaultVisibility ='public' if isStruct else 'private' + self.decorators = decorators + self.extradeclarations = extradeclarations + self.extradefinitions = extradefinitions + + def getNameString(self): + className = self.name + if self.templateSpecialization: + className = className + \ + '<%s>' % ', '.join([str(a) for a + in self.templateSpecialization]) + return className + + def declare(self): + result = '' + if self.templateArgs: + templateArgs = [a.declare() for a in self.templateArgs] + templateArgs = templateArgs[len(self.templateSpecialization):] + result = result + self.indent + 'template <%s>\n' \ + % ','.join([str(a) for a in templateArgs]) + + type = 'struct' if self.isStruct else 'class' + + if self.templateSpecialization: + specialization = \ + '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) + else: + specialization = '' + + myself = '%s%s %s%s' % (self.indent, type, self.name, specialization) + if self.decorators != '': + myself += " " + self.decorators + result += myself + + if self.bases: + inherit = ' : ' + result += inherit + # Grab our first base + baseItems = [CGGeneric(b.declare(self)) for b in self.bases] + bases = baseItems[:1] + # Indent the rest + bases.extend(CGIndenter(b, len(myself) + len(inherit)) for + b in baseItems[1:]) + result += ",\n".join(b.define() for b in bases) + + result = result + '\n%s{\n' % self.indent + + result += CGIndenter(CGGeneric(self.extradeclarations), + len(self.indent)).define() + + def declareMembers(cgClass, memberList, defaultVisibility, itemCount, + separator=''): + members = { 'private': [], 'protected': [], 'public': [] } + + for member in memberList: + members[member.visibility].append(member) + + + if defaultVisibility == 'public': + order = [ 'public', 'protected', 'private' ] + else: + order = [ 'private', 'protected', 'public' ] + + result = '' + + lastVisibility = defaultVisibility + for visibility in order: + list = members[visibility] + if list: + if visibility != lastVisibility: + if itemCount: + result = result + '\n' + result = result + visibility + ':\n' + itemCount = 0 + for member in list: + if itemCount != 0: + result = result + separator + declaration = member.declare(cgClass) + declaration = CGIndenter(CGGeneric(declaration)).define() + result = result + declaration + itemCount = itemCount + 1 + lastVisibility = visibility + return (result, lastVisibility, itemCount) + + if self.disallowCopyConstruction: + class DisallowedCopyConstructor(object): + def __init__(self): + self.visibility = "private" + def declare(self, cgClass): + name = cgClass.getNameString() + return ("%s(const %s&) MOZ_DELETE;\n" + "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name)) + disallowedCopyConstructors = [DisallowedCopyConstructor()] + else: + disallowedCopyConstructors = [] + + order = [(self.enums, ''), (self.unions, ''), + (self.typedefs, ''), (self.members, ''), + (self.constructors + disallowedCopyConstructors, '\n'), + (self.destructors, '\n'), (self.methods, '\n')] + + lastVisibility = self.defaultVisibility + itemCount = 0 + for (memberList, separator) in order: + (memberString, lastVisibility, itemCount) = \ + declareMembers(self, memberList, lastVisibility, itemCount, + separator) + if self.indent: + memberString = CGIndenter(CGGeneric(memberString), + len(self.indent)).define() + result = result + memberString + + result = result + self.indent + '};\n' + return result + + def define(self): + def defineMembers(cgClass, memberList, itemCount, separator=''): + result = '' + for member in memberList: + if itemCount != 0: + result = result + separator + definition = member.define(cgClass) + if definition: + # Member variables would only produce empty lines here. + result += definition + itemCount += 1 + return (result, itemCount) + + order = [(self.members, ''), (self.constructors, '\n'), + (self.destructors, '\n'), (self.methods, '\n')] + + result = self.extradefinitions + itemCount = 0 + for (memberList, separator) in order: + (memberString, itemCount) = defineMembers(self, memberList, + itemCount, separator) + result = result + memberString + return result + class CGXrayHelper(CGAbstractExternMethod): def __init__(self, descriptor, name, args, properties): CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) @@ -4610,10 +5223,15 @@ class CGBindingRoot(CGThing): def __init__(self, config, prefix, webIDLFile): descriptors = config.getDescriptors(webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True) - dictionaries = config.getDictionaries(webIDLFile) + dictionaries = config.getDictionaries(webIDLFile=webIDLFile) cgthings = [] + mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile, + workers=False) + callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile, + isCallback=True) + # Do codegen for all the enums def makeEnum(e): return CGNamespace.build([e.identifier.name + "Values"], @@ -4654,9 +5272,17 @@ class CGBindingRoot(CGThing): cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False)) for d in dictionaries]) + # Do codegen for all the callbacks. + cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False)) + for c in mainCallbacks) + # Do codegen for all the descriptors cgthings.extend([CGDescriptor(x) for x in descriptors]) + # Do codegen for all the callback interfaces. Skip worker callbacks. + cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors if + not x.workers]) + # And make sure we have the right number of newlines at the end curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") @@ -4705,6 +5331,896 @@ class CGBindingRoot(CGThing): def define(self): return stripTrailingWhitespace(self.root.define()) +class CGNativeMember(ClassMethod): + def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, + breakAfter=True, passJSBitsAsNeeded=True, visibility="public", + jsObjectsArePtr=False, variadicIsSequence=False): + """ + If jsObjectsArePtr is true, typed arrays and "object" will be + passed as JSObject*. + + If passJSBitsAsNeeded is false, we don't automatically pass in a + JSContext* or a JSObject* based on the return and argument types. We + can still pass it based on 'implicitJSContext' annotations. + """ + self.descriptorProvider = descriptorProvider + self.member = member + self.extendedAttrs = extendedAttrs + self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.extendedAttrs) + self.passJSBitsAsNeeded = passJSBitsAsNeeded + self.jsObjectsArePtr = jsObjectsArePtr + self.variadicIsSequence = variadicIsSequence + breakAfterSelf = "\n" if breakAfter else "" + ClassMethod.__init__(self, name, + self.getReturnType(signature[0], False), + self.getArgs(signature[0], signature[1]), + static=member.isStatic(), + # Mark our getters, which are attrs that + # have a non-void return type, as const. + const=(not member.isStatic() and member.isAttr() and + not signature[0].isVoid()), + breakAfterReturnDecl=" ", + breakAfterSelf=breakAfterSelf, + visibility=visibility) + + def getReturnType(self, type, isMember): + return self.getRetvalInfo(type, isMember)[0] + + def getRetvalInfo(self, type, isMember): + """ + Returns a tuple: + + The first element is the type declaration for the retval + + The second element is a default value that can be used on error returns. + For cases whose behavior depends on isMember, the second element will be + None if isMember is true. + + The third element is a template for actually returning a value stored in + "${declName}" and "${holderName}". This means actually returning it if + we're not outparam, else assigning to the "retval" outparam. If + isMember is true, this can be None, since in that case the caller will + never examine this value. + """ + if type.isVoid(): + return "void", "", "" + if type.isPrimitive() and type.tag() in builtinNames: + result = CGGeneric(builtinNames[type.tag()]) + defaultReturnArg = "0" + if type.nullable(): + result = CGTemplatedType("Nullable", result) + defaultReturnArg = "" + return (result.define(), + "%s(%s)" % (result.define(), defaultReturnArg), + "return ${declName};") + if type.isDOMString(): + if isMember: + # No need for a third element in the isMember case + return "nsString", None, None + # Outparam + return "void", "", "retval = ${declName};" + if type.isByteString(): + if isMember: + # No need for a third element in the isMember case + return "nsCString", None, None + # Outparam + return "void", "", "retval = ${declName};" + if type.isEnum(): + enumName = type.unroll().inner.identifier.name + if type.nullable(): + enumName = CGTemplatedType("Nullable", + CGGeneric(enumName)).define() + defaultValue = "%s()" % enumName + else: + defaultValue = "%s(0)" % enumName + return enumName, defaultValue, "return ${declName};" + if type.isGeckoInterface(): + iface = type.unroll().inner; + nativeType = self.descriptorProvider.getDescriptor( + iface.identifier.name).nativeType + # Now trim off unnecessary namespaces + nativeType = nativeType.split("::") + if nativeType[0] == "mozilla": + nativeType.pop(0) + if nativeType[0] == "dom": + nativeType.pop(0) + result = CGGeneric("::".join(nativeType)) + if self.resultAlreadyAddRefed: + if isMember: + holder = "nsRefPtr" + else: + holder = "already_AddRefed" + if memberReturnsNewObject(self.member): + warning = "" + else: + warning = "// Mark this as resultNotAddRefed to return raw pointers\n" + result = CGWrapper(result, + pre=("%s%s<" % (warning, holder)), + post=">") + else: + result = CGWrapper(result, post="*") + # Since we always force an owning type for callback return values, + # our ${declName} is an OwningNonNull or nsRefPtr. So we can just + # .forget() to get our already_AddRefed. + return result.define(), "nullptr", "return ${declName}.forget();" + if type.isCallback(): + return ("already_AddRefed<%s>" % type.unroll().identifier.name, + "nullptr", "return ${declName}.forget();") + if type.isAny(): + return "JS::Value", "JS::UndefinedValue()", "return ${declName};" + if type.isObject(): + return "JSObject*", "nullptr", "return ${declName};" + if type.isSpiderMonkeyInterface(): + if type.nullable(): + returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();" + else: + returnCode = "return ${declName}.Obj();" + return "JSObject*", "nullptr", returnCode + if type.isSequence(): + # If we want to handle sequence-of-sequences return values, we're + # going to need to fix example codegen to not produce nsTArray<void> + # for the relevant argument... + assert not isMember + # Outparam. + if type.nullable(): + returnCode = ("if (${declName}.IsNull()) {\n" + " retval.SetNull();\n" + "} else {\n" + " retval.SetValue().SwapElements(${declName}.Value());\n" + "}") + else: + returnCode = "retval.SwapElements(${declName});" + return "void", "", returnCode + if type.isDate(): + result = CGGeneric("Date") + if type.nullable(): + result = CGTemplatedType("Nullable", result) + return (result.define(), "%s()" % result.define(), + "return ${declName};") + raise TypeError("Don't know how to declare return value for %s" % + type) + + def getArgs(self, returnType, argList): + args = [self.getArg(arg) for arg in argList] + # Now the outparams + if returnType.isDOMString(): + args.append(Argument("nsString&", "retval")) + if returnType.isByteString(): + args.append(Argument("nsCString&", "retval")) + elif returnType.isSequence(): + nullable = returnType.nullable() + if nullable: + returnType = returnType.inner + # And now the actual underlying type + elementDecl = self.getReturnType(returnType.inner, True) + type = CGTemplatedType("nsTArray", CGGeneric(elementDecl)) + if nullable: + type = CGTemplatedType("Nullable", type) + args.append(Argument("%s&" % type.define(), "retval")) + # And the ErrorResult + if not 'infallible' in self.extendedAttrs: + # Use aRv so it won't conflict with local vars named "rv" + args.append(Argument("ErrorResult&", "aRv")) + # The legacycaller thisval + if self.member.isMethod() and self.member.isLegacycaller(): + # If it has an identifier, we can't deal with it yet + assert self.member.isIdentifierLess() + args.insert(0, Argument("JS::Value", "aThisVal")) + # And jscontext bits. + if needCx(returnType, argList, self.extendedAttrs, + self.passJSBitsAsNeeded): + args.insert(0, Argument("JSContext*", "cx")) + if needScopeObject(returnType, argList, self.extendedAttrs, + self.descriptorProvider, + self.passJSBitsAsNeeded): + args.insert(1, Argument("JS::Handle<JSObject*>", "obj")) + # And if we're static, a global + if self.member.isStatic(): + args.insert(0, Argument("const GlobalObject&", "global")) + return args + + def doGetArgType(self, type, optional, isMember): + """ + The main work of getArgType. Returns a string type decl, whether this + is a const ref, as well as whether the type should be wrapped in + Nullable as needed. + + isMember can be false or one of the strings "Sequence" or "Variadic" + """ + if type.isArray(): + raise TypeError("Can't handle array arguments yet") + + if type.isSequence(): + nullable = type.nullable() + if nullable: + type = type.inner + elementType = type.inner + argType = self.getArgType(elementType, False, "Sequence")[0] + decl = CGTemplatedType("Sequence", argType) + return decl.define(), True, True + + if type.isUnion(): + if type.nullable(): + type = type.inner + return str(type), True, True + + if type.isGeckoInterface() and not type.isCallbackInterface(): + iface = type.unroll().inner + argIsPointer = type.nullable() or iface.isExternal() + forceOwningType = iface.isCallback() or isMember + if argIsPointer: + if (optional or isMember) and forceOwningType: + typeDecl = "nsRefPtr<%s>" + else: + typeDecl = "%s*" + else: + if optional or isMember: + if forceOwningType: + typeDecl = "OwningNonNull<%s>" + else: + typeDecl = "NonNull<%s>" + else: + typeDecl = "%s&" + return ((typeDecl % + self.descriptorProvider.getDescriptor(iface.identifier.name).nativeType), + False, False) + + if type.isSpiderMonkeyInterface(): + if self.jsObjectsArePtr: + return "JSObject*", False, False + + return type.name, True, True + + if type.isDOMString(): + if isMember: + declType = "nsString" + else: + declType = "nsAString" + return declType, True, False + + if type.isByteString(): + declType = "nsCString" + return declType, True, False + + if type.isEnum(): + return type.unroll().inner.identifier.name, False, True + + if type.isCallback() or type.isCallbackInterface(): + forceOwningType = optional or isMember + if type.nullable(): + if forceOwningType: + declType = "nsRefPtr<%s>" + else: + declType = "%s*" + else: + if forceOwningType: + declType = "OwningNonNull<%s>" + else: + declType = "%s&" + if type.isCallback(): + name = type.unroll().identifier.name + else: + name = type.unroll().inner.identifier.name + return declType % name, False, False + + if type.isAny(): + # Don't do the rooting stuff for variadics for now + if isMember: + declType = "JS::Value" + else: + declType = "JS::Handle<JS::Value>" + return declType, False, False + + if type.isObject(): + if isMember: + declType = "JSObject*" + else: + declType = "JS::Handle<JSObject*>" + return declType, False, False + + if type.isDictionary(): + typeName = CGDictionary.makeDictionaryName(type.inner) + return typeName, True, True + + if type.isDate(): + return "Date", False, True + + assert type.isPrimitive() + + return builtinNames[type.tag()], False, True + + def getArgType(self, type, optional, isMember): + """ + Get the type of an argument declaration. Returns the type CGThing, and + whether this should be a const ref. + + isMember can be False, "Sequence", or "Variadic" + """ + (decl, ref, handleNullable) = self.doGetArgType(type, optional, + isMember) + decl = CGGeneric(decl) + if handleNullable and type.nullable(): + decl = CGTemplatedType("Nullable", decl) + ref = True + if isMember == "Variadic": + arrayType = "Sequence" if self.variadicIsSequence else "nsTArray" + decl = CGTemplatedType(arrayType, decl) + ref = True + elif optional: + # Note: All variadic args claim to be optional, but we can just use + # empty arrays to represent them not being present. + decl = CGTemplatedType("Optional", decl) + ref = True + return (decl, ref) + + def getArg(self, arg): + """ + Get the full argument declaration for an argument + """ + (decl, ref) = self.getArgType(arg.type, + arg.optional and not arg.defaultValue, + "Variadic" if arg.variadic else False) + if ref: + decl = CGWrapper(decl, pre="const ", post="&") + + return Argument(decl.define(), arg.identifier.name) + +def isJSImplementedDescriptor(descriptorProvider): + return (isinstance(descriptorProvider, Descriptor) and + descriptorProvider.interface.isJSImplemented()) + +class CGCallback(CGClass): + def __init__(self, idlObject, descriptorProvider, baseName, methods, + getters=[], setters=[]): + self.baseName = baseName + self._deps = idlObject.getDeps() + name = idlObject.identifier.name + if isJSImplementedDescriptor(descriptorProvider): + name = jsImplName(name) + # 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( + [Argument("JSObject*", "aCallback")], + bodyInHeader=True, + visibility="public", + explicit=True, + baseConstructors=[ + "%s(aCallback)" % self.baseName + ])] + + def getMethodImpls(self, method): + assert method.needThisHandling + args = list(method.args) + # Strip out the JSContext*/JSObject* args + # that got added. + assert args[0].name == "cx" and args[0].argType == "JSContext*" + assert args[1].name == "aThisObj" and args[1].argType == "JS::Handle<JSObject*>" + args = args[2:] + # Record the names of all the arguments, so we can use them when we call + # the private method. + argnames = [arg.name for arg in args] + argnamesWithThis = ["s.GetContext()", "thisObjJS"] + argnames + argnamesWithoutThis = ["s.GetContext()", "JS::NullPtr()"] + argnames + # Now that we've recorded the argnames for our call to our private + # method, insert our optional argument for deciding whether the + # CallSetup should re-throw exceptions on aRv. + args.append(Argument("ExceptionHandling", "aExceptionHandling", + "eReportExceptions")) + # And now insert our template argument. + argsWithoutThis = list(args) + args.insert(0, Argument("const T&", "thisObj")) + + setupCall = ("CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n" + "if (!s.GetContext()) {\n" + " aRv.Throw(NS_ERROR_UNEXPECTED);\n" + " return${errorReturn};\n" + "}\n") + + bodyWithThis = string.Template( + setupCall+ + "JS::Rooted<JSObject*> thisObjJS(s.GetContext(),\n" + " WrapCallThisObject(s.GetContext(), CallbackPreserveColor(), thisObj));\n" + "if (!thisObjJS) {\n" + " aRv.Throw(NS_ERROR_FAILURE);\n" + " return${errorReturn};\n" + "}\n" + "return ${methodName}(${callArgs});").substitute({ + "errorReturn" : method.getDefaultRetval(), + "callArgs" : ", ".join(argnamesWithThis), + "methodName": method.name, + }) + bodyWithoutThis = string.Template( + setupCall + + "return ${methodName}(${callArgs});").substitute({ + "errorReturn" : method.getDefaultRetval(), + "callArgs" : ", ".join(argnamesWithoutThis), + "methodName": method.name, + }) + return [ClassMethod(method.name, method.returnType, args, + bodyInHeader=True, + templateArgs=["typename T"], + body=bodyWithThis), + ClassMethod(method.name, method.returnType, argsWithoutThis, + bodyInHeader=True, + body=bodyWithoutThis), + method] + + def deps(self): + return self._deps + +class CGCallbackFunction(CGCallback): + def __init__(self, callback, descriptorProvider): + CGCallback.__init__(self, callback, descriptorProvider, + "CallbackFunction", + methods=[CallCallback(callback, descriptorProvider)]) + + def getConstructors(self): + return CGCallback.getConstructors(self) + [ + ClassConstructor( + [Argument("CallbackFunction*", "aOther")], + bodyInHeader=True, + visibility="public", + explicit=True, + baseConstructors=[ + "CallbackFunction(aOther)" + ])] + +# We're always fallible +def callbackGetterName(attr): + return "Get" + MakeNativeName(attr.identifier.name) + +def callbackSetterName(attr): + return "Set" + MakeNativeName(attr.identifier.name) + +class CGCallbackFunction(CGCallback): + def __init__(self, callback, descriptorProvider): + CGCallback.__init__(self, callback, descriptorProvider, + "CallbackFunction", + methods=[CallCallback(callback, descriptorProvider)]) + + def getConstructors(self): + return CGCallback.getConstructors(self) + [ + ClassConstructor( + [Argument("CallbackFunction*", "aOther")], + bodyInHeader=True, + visibility="public", + explicit=True, + baseConstructors=[ + "CallbackFunction(aOther)" + ])] + +class CGCallbackInterface(CGCallback): + def __init__(self, descriptor): + iface = descriptor.interface + attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] + getters = [CallbackGetter(a, descriptor) for a in attrs] + setters = [CallbackSetter(a, descriptor) for a in attrs + if not a.readonly] + methods = [m for m in iface.members + if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] + methods = [CallbackOperation(m, sig, descriptor) for m in methods + for sig in m.signatures()] + if iface.isJSImplemented() and iface.ctor(): + sigs = descriptor.interface.ctor().signatures() + if len(sigs) != 1: + raise TypeError("We only handle one constructor. See bug 869268.") + methods.append(CGJSImplInitOperation(sigs[0], descriptor)) + CGCallback.__init__(self, iface, descriptor, "CallbackInterface", + methods, getters=getters, setters=setters) + +class FakeMember(): + def __init__(self): + self.treatUndefinedAs = self.treatNullAs = "Default" + def isStatic(self): + return False + def isAttr(self): + return False + def isMethod(self): + return False + def getExtendedAttribute(self, name): + # Claim to be a [NewObject] so we can avoid the "mark this + # resultNotAddRefed" comments CGNativeMember codegen would + # otherwise stick in. + if name == "NewObject": + return True + return None + +class CallbackMember(CGNativeMember): + def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): + """ + needThisHandling is True if we need to be able to accept a specified + thisObj, False otherwise. + """ + assert not rethrowContentException or not needThisHandling + + self.retvalType = sig[0] + self.originalSig = sig + args = sig[1] + self.argCount = len(args) + if self.argCount > 0: + # Check for variadic arguments + lastArg = args[self.argCount-1] + if lastArg.variadic: + self.argCountStr = ( + "(%d - 1) + %s.Length()" % (self.argCount, + lastArg.identifier.name)) + else: + self.argCountStr = "%d" % self.argCount + self.needThisHandling = needThisHandling + # If needThisHandling, we generate ourselves as private and the caller + # will handle generating public versions that handle the "this" stuff. + visibility = "private" if needThisHandling else "public" + self.rethrowContentException = rethrowContentException + # We don't care, for callback codegen, whether our original member was + # a method or attribute or whatnot. Just always pass FakeMember() + # here. + CGNativeMember.__init__(self, descriptorProvider, FakeMember(), + name, (self.retvalType, args), + extendedAttrs={}, + passJSBitsAsNeeded=False, + visibility=visibility, + jsObjectsArePtr=True) + # We have to do all the generation of our body now, because + # the caller relies on us throwing if we can't manage it. + self.exceptionCode=("aRv.Throw(NS_ERROR_UNEXPECTED);\n" + "return%s;" % self.getDefaultRetval()) + self.body = self.getImpl() + + def getImpl(self): + replacements = { + "declRval": self.getRvalDecl(), + "errorReturn" : self.getDefaultRetval(), + "returnResult": self.getResultConversion(), + "convertArgs": self.getArgConversions(), + "doCall": self.getCall(), + "setupCall": self.getCallSetup(), + } + if self.argCount > 0: + replacements["argCount"] = self.argCountStr + replacements["argvDecl"] = string.Template( + "JS::AutoValueVector argv(cx);\n" + "if (!argv.resize(${argCount})) {\n" + " aRv.Throw(NS_ERROR_OUT_OF_MEMORY);\n" + " return${errorReturn};\n" + "}\n" + ).substitute(replacements) + else: + # Avoid weird 0-sized arrays + replacements["argvDecl"] = "" + + return string.Template( + # Newlines and semicolons are in the values + "${setupCall}" + "${declRval}" + "${argvDecl}" + "${convertArgs}" + "${doCall}" + "${returnResult}").substitute(replacements) + + def getResultConversion(self): + replacements = { + "val": "rval", + "mutableVal": "&rval", + "holderName" : "rvalHolder", + "declName" : "rvalDecl", + # We actually want to pass in a null scope object here, because + # wrapping things into our current compartment (that of mCallback) + # is what we want. + "obj": "nullptr" + } + + if isJSImplementedDescriptor(self.descriptorProvider): + isCallbackReturnValue = "JSImpl" + else: + isCallbackReturnValue = "Callback" + convertType = instantiateJSToNativeConversion( + getJSToNativeConversionInfo(self.retvalType, + self.descriptorProvider, + exceptionCode=self.exceptionCode, + isCallbackReturnValue=isCallbackReturnValue, + # XXXbz we should try to do better here + sourceDescription="return value"), + replacements) + assignRetval = string.Template( + self.getRetvalInfo(self.retvalType, + False)[2]).substitute(replacements) + return convertType.define() + "\n" + assignRetval + + def getArgConversions(self): + # Just reget the arglist from self.originalSig, because our superclasses + # just have way to many members they like to clobber, so I can't find a + # safe member name to store it in. + argConversions = [self.getArgConversion(i, arg) for (i, arg) + in enumerate(self.originalSig[1])] + # Do them back to front, so our argc modifications will work + # correctly, because we examine trailing arguments first. + argConversions.reverse(); + # Wrap each one in a scope so that any locals it has don't leak out, and + # also so that we can just "break;" for our successCode. + argConversions = [CGWrapper(CGIndenter(CGGeneric(c)), + pre="do {\n", + post="\n} while (0);") + for c in argConversions] + if self.argCount > 0: + argConversions.insert(0, self.getArgcDecl()) + # And slap them together. + return CGList(argConversions, "\n\n").define() + "\n\n" + + def getArgConversion(self, i, arg): + argval = arg.identifier.name + + if arg.variadic: + argval = argval + "[idx]" + jsvalIndex = "%d + idx" % i + else: + jsvalIndex = "%d" % i + if arg.optional and not arg.defaultValue: + argval += ".Value()" + if arg.type.isDOMString(): + # XPConnect string-to-JS conversion wants to mutate the string. So + # let's give it a string it can mutate + # XXXbz if we try to do a sequence of strings, this will kinda fail. + result = "mutableStr" + prepend = "nsString mutableStr(%s);\n" % argval + else: + result = argval + prepend = "" + + conversion = prepend + wrapForType( + arg.type, self.descriptorProvider, + { + 'result' : result, + 'successCode' : "continue;" if arg.variadic else "break;", + 'jsvalRef' : "argv.handleAt(%s)" % jsvalIndex, + 'jsvalHandle' : "argv.handleAt(%s)" % jsvalIndex, + # XXXbz we don't have anything better to use for 'obj', + # really... It's OK to use CallbackPreserveColor because + # CallSetup already handled the unmark-gray bits for us. + 'obj' : 'CallbackPreserveColor()', + 'returnsNewObject': False, + 'exceptionCode' : self.exceptionCode + }) + if arg.variadic: + conversion = string.Template( + "for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {\n" + + CGIndenter(CGGeneric(conversion)).define() + "\n" + "}\n" + "break;").substitute({ "arg": arg.identifier.name }) + elif arg.optional and not arg.defaultValue: + conversion = ( + CGIfWrapper(CGGeneric(conversion), + "%s.WasPassed()" % arg.identifier.name).define() + + " else if (argc == %d) {\n" + " // This is our current trailing argument; reduce argc\n" + " --argc;\n" + "} else {\n" + " argv[%d] = JS::UndefinedValue();\n" + "}" % (i+1, i)) + return conversion + + def getDefaultRetval(self): + default = self.getRetvalInfo(self.retvalType, False)[1] + if len(default) != 0: + default = " " + default + return default + + def getArgs(self, returnType, argList): + args = CGNativeMember.getArgs(self, returnType, argList) + if not self.needThisHandling: + # Since we don't need this handling, we're the actual method that + # will be called, so we need an aRethrowExceptions argument. + if self.rethrowContentException: + args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) + else: + args.append(Argument("ExceptionHandling", "aExceptionHandling", + "eReportExceptions")) + return args + # We want to allow the caller to pass in a "this" object, as + # well as a JSContext. + return [Argument("JSContext*", "cx"), + Argument("JS::Handle<JSObject*>", "aThisObj")] + args + + def getCallSetup(self): + if self.needThisHandling: + # It's been done for us already + return "" + callSetup = "CallSetup s(CallbackPreserveColor(), aRv" + if self.rethrowContentException: + # getArgs doesn't add the aExceptionHandling argument but does add + # aCompartment for us. + callSetup += ", eRethrowContentExceptions, aCompartment" + else: + callSetup += ", aExceptionHandling" + callSetup += ");" + return string.Template( + "${callSetup}\n" + "JSContext* cx = s.GetContext();\n" + "if (!cx) {\n" + " aRv.Throw(NS_ERROR_UNEXPECTED);\n" + " return${errorReturn};\n" + "}\n").substitute({ + "callSetup": callSetup, + "errorReturn" : self.getDefaultRetval(), + }) + + def getArgcDecl(self): + return CGGeneric("unsigned argc = %s;" % self.argCountStr); + + @staticmethod + def ensureASCIIName(idlObject): + type = "attribute" if idlObject.isAttr() else "operation" + if re.match("[^\x20-\x7E]", idlObject.identifier.name): + raise SyntaxError('Callback %s name "%s" contains non-ASCII ' + "characters. We can't handle that. %s" % + (type, idlObject.identifier.name, + idlObject.location)) + if re.match('"', idlObject.identifier.name): + raise SyntaxError("Callback %s name '%s' contains " + "double-quote character. We can't handle " + "that. %s" % + (type, idlObject.identifier.name, + idlObject.location)) + +class CallbackMethod(CallbackMember): + def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): + CallbackMember.__init__(self, sig, name, descriptorProvider, + needThisHandling, rethrowContentException) + def getRvalDecl(self): + return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" + + def getCall(self): + replacements = { + "errorReturn" : self.getDefaultRetval(), + "thisObj": self.getThisObj(), + "getCallable": self.getCallableDecl() + } + if self.argCount > 0: + replacements["argv"] = "argv.begin()" + replacements["argc"] = "argc" + else: + replacements["argv"] = "nullptr" + replacements["argc"] = "0" + return string.Template("${getCallable}" + "if (!JS_CallFunctionValue(cx, ${thisObj}, callable,\n" + " ${argc}, ${argv}, rval.address())) {\n" + " aRv.Throw(NS_ERROR_UNEXPECTED);\n" + " return${errorReturn};\n" + "}\n").substitute(replacements) + +class CallCallback(CallbackMethod): + def __init__(self, callback, descriptorProvider): + CallbackMethod.__init__(self, callback.signatures()[0], "Call", + descriptorProvider, needThisHandling=True) + + def getThisObj(self): + return "aThisObj" + + def getCallableDecl(self): + return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n" + +class CallbackOperationBase(CallbackMethod): + """ + Common class for implementing various callback operations. + """ + def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False): + self.singleOperation = singleOperation + self.methodName = jsName + CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException) + + def getThisObj(self): + if not self.singleOperation: + return "mCallback" + # This relies on getCallableDecl declaring a boolean + # isCallable in the case when we're a single-operation + # interface. + return "isCallable ? aThisObj.get() : mCallback" + + def getCallableDecl(self): + replacements = { + "errorReturn" : self.getDefaultRetval(), + "methodName": self.methodName + } + getCallableFromProp = string.Template( + 'if (!GetCallableProperty(cx, "${methodName}", &callable)) {\n' + ' aRv.Throw(NS_ERROR_UNEXPECTED);\n' + ' return${errorReturn};\n' + '}\n').substitute(replacements) + if not self.singleOperation: + return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp + return ( + 'bool isCallable = JS_ObjectIsCallable(cx, mCallback);\n' + 'JS::Rooted<JS::Value> callable(cx);\n' + 'if (isCallable) {\n' + ' callable = JS::ObjectValue(*mCallback);\n' + '} else {\n' + '%s' + '}\n' % CGIndenter(CGGeneric(getCallableFromProp)).define()) + +class CallbackOperation(CallbackOperationBase): + """ + Codegen actual WebIDL operations on callback interfaces. + """ + def __init__(self, method, signature, descriptor): + self.ensureASCIIName(method) + jsName = method.identifier.name + CallbackOperationBase.__init__(self, signature, + jsName, MakeNativeName(jsName), + descriptor, descriptor.interface.isSingleOperationInterface(), + rethrowContentException=descriptor.interface.isJSImplemented()) + +class CallbackGetter(CallbackMember): + def __init__(self, attr, descriptor): + self.ensureASCIIName(attr) + self.attrName = attr.identifier.name + CallbackMember.__init__(self, + (attr.type, []), + callbackGetterName(attr), + descriptor, + needThisHandling=False, + rethrowContentException=descriptor.interface.isJSImplemented()) + + def getRvalDecl(self): + return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n" + + def getCall(self): + replacements = { + "errorReturn" : self.getDefaultRetval(), + "attrName": self.attrName + } + return string.Template( + 'if (!JS_GetProperty(cx, mCallback, "${attrName}", &rval)) {\n' + ' aRv.Throw(NS_ERROR_UNEXPECTED);\n' + ' return${errorReturn};\n' + '}\n').substitute(replacements); + +class CallbackSetter(CallbackMember): + def __init__(self, attr, descriptor): + self.ensureASCIIName(attr) + self.attrName = attr.identifier.name + CallbackMember.__init__(self, + (BuiltinTypes[IDLBuiltinType.Types.void], + [FakeArgument(attr.type, attr)]), + callbackSetterName(attr), + descriptor, + needThisHandling=False, + rethrowContentException=descriptor.interface.isJSImplemented()) + + def getRvalDecl(self): + # We don't need an rval + return "" + + def getCall(self): + replacements = { + "errorReturn" : self.getDefaultRetval(), + "attrName": self.attrName, + "argv": "argv.handleAt(0)", + } + return string.Template( + 'MOZ_ASSERT(argv.length() == 1);\n' + 'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n' + ' aRv.Throw(NS_ERROR_UNEXPECTED);\n' + ' return${errorReturn};\n' + '}\n').substitute(replacements) + + def getArgcDecl(self): + return None + class GlobalGenRoots(): """ Roots for global codegen. diff --git a/src/components/script/dom/bindings/codegen/Configuration.py b/src/components/script/dom/bindings/codegen/Configuration.py index 32918d272b3..0c87ead0259 100644 --- a/src/components/script/dom/bindings/codegen/Configuration.py +++ b/src/components/script/dom/bindings/codegen/Configuration.py @@ -42,6 +42,8 @@ class Configuration: self.enums = [e for e in parseData if e.isEnum()] self.dictionaries = [d for d in parseData if d.isDictionary()] + self.callbacks = [c for c in parseData if + c.isCallback() and not c.isInterface()] # Keep the descriptor list sorted for determinism. self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) @@ -66,14 +68,34 @@ class Configuration: getter = lambda x: x.interface.isCallback() elif key == 'isExternal': getter = lambda x: x.interface.isExternal() + elif key == 'isJSImplemented': + getter = lambda x: x.interface.isJSImplemented() else: getter = lambda x: getattr(x, key) curr = filter(lambda x: getter(x) == val, curr) return curr def getEnums(self, webIDLFile): return filter(lambda e: e.filename() == webIDLFile, self.enums) - def getDictionaries(self, webIDLFile): - return filter(lambda d: d.filename() == webIDLFile, self.dictionaries) + + @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 diff --git a/src/components/script/dom/bindings/codegen/parser/WebIDL.py b/src/components/script/dom/bindings/codegen/parser/WebIDL.py index 1aab3debc6a..046b8130dff 100644 --- a/src/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/src/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -146,6 +146,23 @@ class IDLObject(object): def isCallback(self): return False + def isSingleOperationInterface(self): + assert self.isCallback() or self.isJSImplemented() + return ( + # JS-implemented things should never need the + # this-handling weirdness of single-operation interfaces. + not self.isJSImplemented() and + # Not inheriting from another interface + not self.parent and + # No consequential interfaces + len(self.getConsequentialInterfaces()) == 0 and + # No attributes of any kinds + not any(m.isAttr() for m in self.members) and + # There is at least one regular operation, and all regular + # operations have the same identifier + len(set(m.identifier.name for m in self.members if + m.isMethod() and not m.isStatic())) == 1) + def isType(self): return False @@ -167,6 +184,38 @@ class IDLObject(object): def handleExtendedAttribute(self, attr): assert False # Override me! + def _getDependentObjects(self): + assert False # Override me! + + def getDeps(self, visited=None): + """ Return a set of files that this object depends on. If any of + these files are changed the parser needs to be rerun to regenerate + a new IDLObject. + + The visited argument is a set of all the objects already visited. + We must test to see if we are in it, and if so, do nothing. This + prevents infinite recursion.""" + + # NB: We can't use visited=set() above because the default value is + # evaluated when the def statement is evaluated, not when the function + # is executed, so there would be one set for all invocations. + if visited == None: + visited = set() + + if self in visited: + return set() + + visited.add(self) + + deps = set() + if self.filename() != "<builtin>": + deps.add(self.filename()) + + for d in self._getDependentObjects(): + deps = deps.union(d.getDeps(visited)) + + return deps + class IDLScope(IDLObject): def __init__(self, location, parentScope, identifier): IDLObject.__init__(self, location) @@ -428,6 +477,15 @@ class IDLExternalInterface(IDLObjectWithIdentifier): def resolve(self, parentScope): pass + def getJSImplementation(self): + return None + + def isJSImplemented(self): + return False + + def _getDependentObjects(self): + return set() + class IDLInterface(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members, isPartial): @@ -777,6 +835,24 @@ class IDLInterface(IDLObjectWithScope): # Put the new members at the beginning self.members = members + self.members + def getJSImplementation(self): + classId = self.getExtendedAttribute("JSImplementation") + if not classId: + return classId + assert isinstance(classId, list) + assert len(classId) == 1 + return classId[0] + + def isJSImplemented(self): + return bool(self.getJSImplementation()) + + def _getDependentObjects(self): + deps = set(self.members) + deps.union(self.implementedInterfaces) + if self.parent: + deps.add(self.parent) + return deps + class IDLDictionary(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members): assert isinstance(parentScope, IDLScope) @@ -847,6 +923,11 @@ class IDLDictionary(IDLObjectWithScope): def addExtendedAttributes(self, attrs): assert len(attrs) == 0 + def _getDependentObjects(self): + deps = set(self.members) + if (self.parent): + deps.add(self.parent) + return deps class IDLEnum(IDLObjectWithIdentifier): def __init__(self, location, parentScope, name, values): @@ -875,6 +956,9 @@ class IDLEnum(IDLObjectWithIdentifier): def addExtendedAttributes(self, attrs): assert len(attrs) == 0 + def _getDependentObjects(self): + return set() + class IDLType(IDLObject): Tags = enum( # The integer types @@ -893,6 +977,7 @@ class IDLType(IDLObject): # Other types 'any', 'domstring', + 'bytestring', 'object', 'date', 'void', @@ -930,6 +1015,12 @@ class IDLType(IDLObject): def isString(self): return False + def isByteString(self): + return False + + def isDOMString(self): + return False + def isVoid(self): return self.name == "Void" @@ -1075,6 +1166,12 @@ class IDLNullableType(IDLType): def isString(self): return self.inner.isString() + def isByteString(self): + return self.inner.isByteString() + + def isDOMString(self): + return self.inner.isDOMString() + def isFloat(self): return self.inner.isFloat() @@ -1163,6 +1260,9 @@ class IDLNullableType(IDLType): return False return self.inner.isDistinguishableFrom(other) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLSequenceType(IDLType): def __init__(self, location, parameterType): assert not parameterType.isVoid() @@ -1231,6 +1331,9 @@ class IDLSequenceType(IDLType): other.isDictionary() or other.isDate() or other.isNonCallbackInterface()) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLUnionType(IDLType): def __init__(self, location, memberTypes): IDLType.__init__(self, location, "") @@ -1317,6 +1420,9 @@ class IDLUnionType(IDLType): return False return True + def _getDependentObjects(self): + return set(self.memberTypes) + class IDLArrayType(IDLType): def __init__(self, location, parameterType): assert not parameterType.isVoid() @@ -1393,6 +1499,9 @@ class IDLArrayType(IDLType): other.isDictionary() or other.isDate() or other.isNonCallbackInterface()) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def __init__(self, location, innerType, name): IDLType.__init__(self, location, innerType.name) @@ -1478,6 +1587,9 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): def isDistinguishableFrom(self, other): return self.inner.isDistinguishableFrom(other) + def _getDependentObjects(self): + return self.inner._getDependentObjects() + class IDLWrapperType(IDLType): def __init__(self, location, inner): IDLType.__init__(self, location, inner.identifier.name) @@ -1583,6 +1695,23 @@ class IDLWrapperType(IDLType): assert other.isObject() return False + def _getDependentObjects(self): + # NB: The codegen for an interface type depends on + # a) That the identifier is in fact an interface (as opposed to + # a dictionary or something else). + # b) The native type of the interface. + # If we depend on the interface object we will also depend on + # anything the interface depends on which is undesirable. We + # considered implementing a dependency just on the interface type + # file, but then every modification to an interface would cause this + # to be regenerated which is still undesirable. We decided not to + # depend on anything, reasoning that: + # 1) Changing the concrete type of the interface requires modifying + # Bindings.conf, which is still a global dependency. + # 2) Changing an interface to a dictionary (or vice versa) with the + # same identifier should be incredibly rare. + return set() + class IDLBuiltinType(IDLType): Types = enum( @@ -1602,6 +1731,7 @@ class IDLBuiltinType(IDLType): # Other types 'any', 'domstring', + 'bytestring', 'object', 'date', 'void', @@ -1633,6 +1763,7 @@ class IDLBuiltinType(IDLType): Types.double: IDLType.Tags.double, Types.any: IDLType.Tags.any, Types.domstring: IDLType.Tags.domstring, + Types.bytestring: IDLType.Tags.bytestring, Types.object: IDLType.Tags.object, Types.date: IDLType.Tags.date, Types.void: IDLType.Tags.void, @@ -1658,6 +1789,13 @@ class IDLBuiltinType(IDLType): return self._typeTag <= IDLBuiltinType.Types.double def isString(self): + return self._typeTag == IDLBuiltinType.Types.domstring or \ + self._typeTag == IDLBuiltinType.Types.bytestring + + def isByteString(self): + return self._typeTag == IDLBuiltinType.Types.bytestring + + def isDOMString(self): return self._typeTag == IDLBuiltinType.Types.domstring def isInteger(self): @@ -1733,6 +1871,9 @@ class IDLBuiltinType(IDLType): (self.isTypedArray() and not other.isArrayBufferView() and not (other.isTypedArray() and other.name == self.name))))) + def _getDependentObjects(self): + return set() + BuiltinTypes = { IDLBuiltinType.Types.byte: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte", @@ -1877,6 +2018,9 @@ class IDLValue(IDLObject): raise WebIDLError("Cannot coerce type %s to type %s." % (self.type, type), [location]) + def _getDependentObjects(self): + return set() + class IDLNullValue(IDLObject): def __init__(self, location): IDLObject.__init__(self, location) @@ -1895,6 +2039,9 @@ class IDLNullValue(IDLObject): nullValue.type = type return nullValue + def _getDependentObjects(self): + return set() + class IDLInterfaceMember(IDLObjectWithIdentifier): @@ -1966,6 +2113,9 @@ class IDLConst(IDLInterfaceMember): def validate(self): pass + def _getDependentObjects(self): + return set([self.type, self.value]) + class IDLAttribute(IDLInterfaceMember): def __init__(self, location, identifier, type, readonly, inherit, static=False): @@ -2052,6 +2202,9 @@ class IDLAttribute(IDLInterfaceMember): def hasLenientThis(self): return self.lenientThis + def _getDependentObjects(self): + return set([self.type]) + class IDLArgument(IDLObjectWithIdentifier): def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False): IDLObjectWithIdentifier.__init__(self, location, None, identifier) @@ -2124,6 +2277,12 @@ class IDLArgument(IDLObjectWithIdentifier): self.location) assert self.defaultValue + def _getDependentObjects(self): + deps = set([self.type]) + if self.defaultValue: + deps.add(self.defaultValue) + return deps + class IDLCallbackType(IDLType, IDLObjectWithScope): def __init__(self, location, parentScope, identifier, returnType, arguments): assert isinstance(returnType, IDLType) @@ -2179,6 +2338,9 @@ class IDLCallbackType(IDLType, IDLObjectWithScope): return (other.isPrimitive() or other.isString() or other.isEnum() or other.isNonCallbackInterface() or other.isDate()) + def _getDependentObjects(self): + return set([self._returnType] + self._arguments) + class IDLMethodOverload: """ A class that represents a single overload of a WebIDL method. This is not @@ -2194,6 +2356,11 @@ class IDLMethodOverload: self.arguments = list(arguments) self.location = location + def _getDependentObjects(self): + deps = set(self.arguments) + deps.add(self.returnType) + return deps + class IDLMethod(IDLInterfaceMember, IDLScope): Special = enum( @@ -2494,6 +2661,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope): [attr.location, self.location]) IDLInterfaceMember.handleExtendedAttribute(self, attr) + def _getDependentObjects(self): + deps = set() + for overload in self._overloads: + deps.union(overload._getDependentObjects()) + return deps + class IDLImplementsStatement(IDLObject): def __init__(self, location, implementor, implementee): IDLObject.__init__(self, location) |