author | Boris Zbarsky <bzbarsky@mit.edu> |
Tue, 12 Jun 2012 10:22:05 -0400 | |
changeset 96481 | 84536fdda9b7574c16d8c61d3693a19f87612761 |
parent 96480 | d82140dbb2a5cc2447547f8145f2fb0679120229 |
child 96482 | 16f1a804057cb1fc8f46cb260dd025e40460a4c4 |
push id | 22910 |
push user | mbrubeck@mozilla.com |
push date | Wed, 13 Jun 2012 01:26:32 +0000 |
treeherder | mozilla-central@964b11fea7f1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | peterv |
bugs | 742153 |
milestone | 16.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -566,34 +566,41 @@ WrapNativeParent(JSContext* cx, JSObject qsObjectHelper helper(GetParentPointer(p), cache); JS::Value v; return XPCOMObjectToJsval(cx, scope, helper, NULL, false, &v) ? JSVAL_TO_OBJECT(v) : NULL; } +static inline bool +InternJSString(JSContext* cx, jsid& id, const char* chars) +{ + if (JSString *str = ::JS_InternString(cx, chars)) { + id = INTERNED_STRING_TO_JSID(cx, str); + return true; + } + return false; +} + // Spec needs a name property template <typename Spec> static bool InitIds(JSContext* cx, Prefable<Spec>* prefableSpecs, jsid* ids) { MOZ_ASSERT(prefableSpecs); MOZ_ASSERT(prefableSpecs->specs); do { // We ignore whether the set of ids is enabled and just intern all the IDs, // because this is only done once per application runtime. Spec* spec = prefableSpecs->specs; do { - JSString *str = ::JS_InternString(cx, spec->name); - if (!str) { + if (!InternJSString(cx, *ids, spec->name)) { return false; } - - *ids = INTERNED_STRING_TO_JSID(cx, str); } while (++ids, (++spec)->name); // We ran out of ids for that pref. Put a JSID_VOID in on the id // corresponding to the list terminator for the pref. *ids = JSID_VOID; ++ids; } while ((++prefableSpecs)->specs);
--- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -308,33 +308,34 @@ class CGIncludeGuard(CGWrapper): CGWrapper.__init__(self, child, declarePre='#ifndef %s\n#define %s\n\n' % (define, define), declarePost='\n#endif // %s\n' % define) class CGHeaders(CGWrapper): """ Generates the appropriate include statements. """ - def __init__(self, descriptors, declareIncludes, defineIncludes, child): + def __init__(self, descriptors, dictionaries, declareIncludes, + defineIncludes, child): """ Builds a set of includes to cover |descriptors|. Also includes the files in |declareIncludes| in the header file and the files in |defineIncludes| in the .cpp. """ # Determine the filenames for which we need headers. interfaceDeps = [d.interface for d in descriptors] ancestors = [] for iface in interfaceDeps: while iface.parent: ancestors.append(iface.parent) iface = iface.parent interfaceDeps.extend(ancestors) - bindingIncludes = set(self.getInterfaceFilename(d) for d in interfaceDeps) + bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps) # Grab all the implementation declaration files we need. implementationIncludes = set(d.headerFile for d in descriptors) # Now find all the things we'll need as arguments because we # need to wrap or unwrap them. bindingHeaders = set() for d in descriptors: @@ -354,32 +355,40 @@ class CGHeaders(CGWrapper): if t.unroll().isInterface(): if t.unroll().isSpiderMonkeyInterface(): bindingHeaders.add("jsfriendapi.h") bindingHeaders.add("mozilla/dom/TypedArray.h") else: typeDesc = d.getDescriptor(t.unroll().inner.identifier.name) if typeDesc is not None: implementationIncludes.add(typeDesc.headerFile) - bindingHeaders.add(self.getInterfaceFilename(typeDesc.interface)) + bindingHeaders.add(self.getDeclarationFilename(typeDesc.interface)) + elif t.unroll().isDictionary(): + bindingHeaders.add(self.getDeclarationFilename(t.unroll().inner)) + + declareIncludes = set(declareIncludes) + for d in dictionaries: + if d.parent: + declareIncludes.add(self.getDeclarationFilename(d.parent)) + bindingHeaders.add(self.getDeclarationFilename(d)) # Let the machinery do its thing. def _includeString(includes): return ''.join(['#include "%s"\n' % i for i in includes]) + '\n' CGWrapper.__init__(self, child, - declarePre=_includeString(declareIncludes), + declarePre=_includeString(sorted(declareIncludes)), definePre=_includeString(sorted(set(defineIncludes) | bindingIncludes | bindingHeaders | implementationIncludes))) @staticmethod - def getInterfaceFilename(interface): + def getDeclarationFilename(decl): # Use our local version of the header, not the exported one, so that # test bindings, which don't export, will work correctly. - basename = os.path.basename(interface.filename()) + basename = os.path.basename(decl.filename()) return basename.replace('.webidl', 'Binding.h') class Argument(): """ A class for outputting the type and name of an argument """ def __init__(self, argType, name): self.argType = argType @@ -1693,16 +1702,45 @@ for (uint32_t i = 0; i < length; ++i) { "${declName} = NULL", descriptorProvider.workers, failureCode) if type.nullable(): declType = CGGeneric("JSObject*") else: declType = CGGeneric("NonNull<JSObject>") return (template, declType, None, isOptional) + if type.isDictionary(): + if failureCode is not None: + raise TypeError("Can't handle dictionaries when failureCode is not None") + + if type.nullable(): + typeName = type.inner.inner.identifier.name + declType = CGGeneric("Nullable<%s>" % typeName) + selfRef = "${declName}.Value()" + else: + typeName = type.inner.identifier.name + declType = CGGeneric(typeName) + selfRef = "${declName}" + # If we're optional or a member of something else, the const + # will come from the Optional or our container. + mutableTypeName = declType + if not isOptional and not isMember: + declType = CGWrapper(declType, pre="const ") + selfRef = "const_cast<%s&>(%s)" % (typeName, selfRef) + + template = wrapObjectTemplate("if (!%s.Init(cx, &${val}.toObject())) {\n" + " return false;\n" + "}" % selfRef, + isDefinitelyObject, type, + ("const_cast<%s&>(${declName}).SetNull()" % + mutableTypeName.define()), + descriptorProvider.workers, None) + + return (template, declType, None, isOptional) + if not type.isPrimitive(): raise TypeError("Need conversion for argument type '%s'" % type) # XXXbz need to add support for [EnforceRange] and [Clamp] typeName = builtinNames[type.tag()] if type.nullable(): return ("if (${val}.isNullOrUndefined()) {\n" " ${declName}.SetNull();\n" @@ -3374,16 +3412,189 @@ class CGNamespacedEnum(CGThing): # Save the result. self.node = curr def declare(self): return self.node.declare() def define(self): assert False # Only for headers. +class CGDictionary(CGThing): + def __init__(self, dictionary, workers): + 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 ] + + def declare(self): + d = self.dictionary + if d.parent: + inheritance = ": public %s " % self.makeClassName(d.parent) + else: + inheritance = "" + memberDecls = [" %s %s;" % + (self.getMemberType(m), m[0].identifier.name) + for m in self.memberInfo] + + return (string.Template( + "struct ${selfName} ${inheritance}{\n" + " ${selfName}() {}\n" + " bool Init(JSContext* cx, JSObject* obj);\n" + "\n" + + "\n".join(memberDecls) + "\n" + "private:\n" + " // Disallow copy-construction\n" + " ${selfName}(const ${selfName}&) MOZ_DELETE;\n" + " static bool InitIds(JSContext* cx);\n" + " static bool initedIds;\n" + + "\n".join(" static jsid " + + self.makeIdName(m.identifier.name) + ";" for + m in d.members) + "\n" + "};").substitute( { "selfName": self.makeClassName(d), + "inheritance": inheritance })) + + def define(self): + d = self.dictionary + if d.parent: + initParent = ("// Per spec, we init the parent's members first\n" + "if (!%s::Init(cx, obj)) {\n" + " return false;\n" + "}\n" % self.makeClassName(d.parent)) + else: + initParent = "" + + memberInits = [CGIndenter(self.getMemberConversion(m)).define() + for m in self.memberInfo] + idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' % + (m.identifier.name + "_id", m.identifier.name)) + for m in d.members] + idinit = CGList(idinit, " ||\n") + idinit = CGWrapper(idinit, pre="if (", + post=(") {\n" + " return false;\n" + "}"), + reindent=True) + + return string.Template( + "bool ${selfName}::initedIds = false;\n" + + "\n".join("jsid ${selfName}::%s = JSID_VOID;" % + self.makeIdName(m.identifier.name) + for m in d.members) + "\n" + "\n" + "bool\n" + "${selfName}::InitIds(JSContext* cx)\n" + "{\n" + " MOZ_ASSERT(!initedIds);\n" + "${idInit}\n" + " initedIds = true;\n" + " return true;\n" + "}\n" + "\n" + "bool\n" + "${selfName}::Init(JSContext* cx, JSObject* obj)\n" + "{\n" + " if (!initedIds && !InitIds(cx)) {\n" + " return false;\n" + " }\n" + "${initParent}" + " JSBool found;\n" + " JS::Value temp;\n" + "\n" + "${initMembers}\n" + " return true;\n" + "}").substitute({ + "selfName": self.makeClassName(d), + "initParent": CGIndenter(CGGeneric(initParent)).define(), + "initMembers": "\n\n".join(memberInits), + "idInit": CGIndenter(idinit).define() + }) + + def makeClassName(self, dictionary): + suffix = "Workers" if self.workers else "" + return dictionary.identifier.name + suffix + + def getMemberType(self, memberInfo): + (member, (templateBody, declType, + holderType, dealWithOptional)) = memberInfo + # We can't handle having a holderType here + assert holderType is None + if dealWithOptional: + declType = CGWrapper(declType, pre="Optional< ", post=" >") + return declType.define() + + def getMemberConversion(self, memberInfo): + # Fake a descriptorProvider + (member, (templateBody, declType, + holderType, dealWithOptional)) = memberInfo + replacements = { "val": "temp", + "valPtr": "&temp", + # Use this->%s to refer to members, because we don't + # control the member names and want to make sure we're + # talking about the member, not some local that + # shadows the member. Another option would be to move + # 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) } + # We can't handle having a holderType here + assert holderType is None + if dealWithOptional: + replacements["declName"] = "(" + replacements["declName"] + ".Value())" + + conversionReplacements = { + "propId" : self.makeIdName(member.identifier.name), + "prop": "(this->%s)" % member.identifier.name, + "convert": string.Template(templateBody).substitute(replacements) + } + conversion = ("if (!JS_HasPropertyById(cx, obj, ${propId}, &found)) {\n" + " return false;\n" + "}\n") + if member.defaultValue: + conversion += ( + "if (found) {\n" + " if (!JS_GetPropertyById(cx, obj, ${propId}, &temp)) {\n" + " return false;\n" + " }\n" + "} else {\n" + " temp = ${defaultVal};\n" + "}\n" + "${convert}") + conversionReplacements["defaultVal"] = ( + convertIDLDefaultValueToJSVal(member.defaultValue)) + else: + conversion += ( + "if (found) {\n" + " ${prop}.Construct();\n" + " if (!JS_GetPropertyById(cx, obj, ${propId}, &temp)) {\n" + " return false;\n" + " }\n" + "${convert}\n" + "}") + conversionReplacements["convert"] = CGIndenter( + CGGeneric(conversionReplacements["convert"])).define() + + return CGGeneric( + string.Template(conversion).substitute(conversionReplacements) + ) + + @staticmethod + def makeIdName(name): + return name + "_id" + class CGRegisterProtos(CGAbstractMethod): def __init__(self, config): CGAbstractMethod.__init__(self, None, 'Register', 'void', [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')]) self.config = config def _defineMacro(self): return """ @@ -3404,16 +3615,17 @@ class CGRegisterProtos(CGAbstractMethod) class CGBindingRoot(CGThing): """ Root codegen class for binding generation. Instantiate the class, and call declare or define to generate header or cpp code (respectively). """ def __init__(self, config, prefix, webIDLFile): descriptors = config.getDescriptors(webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True) + dictionaries = config.getDictionaries(webIDLFile) forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')] for x in descriptors: nativeType = x.nativeType components = x.nativeType.split('::') className = components[-1] # JSObject is a struct, not a class @@ -3441,40 +3653,63 @@ class CGBindingRoot(CGThing): traitsClasses = CGNamespace.build(['mozilla', 'dom'], CGWrapper(CGList(traitsClasses), declarePre='\n'), declareOnly=True) traitsClasses = CGWrapper(traitsClasses, declarePost='\n') else: traitsClasses = None - # Do codegen for all the descriptors and enums. + # Do codegen for all the enums def makeEnum(e): return CGNamespace.build([e.identifier.name + "Values"], CGEnum(e)) def makeEnumTypedef(e): return CGGeneric(declare=("typedef %sValues::valuelist %s;\n" % (e.identifier.name, e.identifier.name))) cgthings = [ fun(e) for e in config.getEnums(webIDLFile) for fun in [makeEnum, makeEnumTypedef] ] + + # Do codegen for all the dictionaries. We have to be a bit careful + # here, because we have to generate these in order from least derived to + # most derived so that class inheritance works out. + # + # XXXbz this will fail if we have two webidl files A and B such that A + # 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 = [] + while len(dictionaries) != 0: + toMove = [d for d in dictionaries if d.parent not in dictionaries] + dictionaries = [d for d in dictionaries if d.parent in dictionaries] + 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]) + + # Do codegen for all the descriptors cgthings.extend([CGDescriptor(x) for x in descriptors]) - curr = CGList(cgthings, "\n") + + # And make sure we have the right number of newlines at the end + curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") # Wrap all of that in our namespaces. curr = CGNamespace.build(['mozilla', 'dom'], CGWrapper(curr, pre="\n")) curr = CGList([forwardDeclares, CGWrapper(CGGeneric("using namespace mozilla::dom;"), defineOnly=True), traitsClasses, curr], "\n") # Add header includes. curr = CGHeaders(descriptors, + dictionaries, ['mozilla/dom/BindingUtils.h', 'mozilla/dom/DOMJSClass.h'], ['mozilla/dom/Nullable.h', 'PrimitiveConversions.h', 'XPCQuickStubs.h', 'nsDOMQS.h', 'AccessCheck.h', 'WorkerPrivate.h', @@ -3567,20 +3802,20 @@ struct PrototypeIDMap; curr = CGRegisterProtos(config) # Wrap all of that in our namespaces. curr = CGNamespace.build(['mozilla', 'dom'], CGWrapper(curr, post='\n')) curr = CGWrapper(curr, post='\n') # Add the includes - defineIncludes = [CGHeaders.getInterfaceFilename(desc.interface) + defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) for desc in config.getDescriptors(hasInterfaceObject=True, workers=False, register=True)] defineIncludes.append('nsScriptNameSpaceManager.h') - curr = CGHeaders([], [], defineIncludes, curr) + curr = CGHeaders([], [], [], defineIncludes, curr) # Add include guards. curr = CGIncludeGuard('RegisterBindings', curr) # Done. return curr
--- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -37,16 +37,17 @@ class Configuration: # an interface. for descriptor in self.descriptors: intefaceName = descriptor.interface.identifier.name otherDescriptors = [d for d in self.descriptors if d.interface.identifier.name == intefaceName] descriptor.uniqueImplementation = len(otherDescriptors) == 1 self.enums = [e for e in parseData if e.isEnum()] + self.dictionaries = [d for d in parseData if d.isDictionary()] # Keep the descriptor list sorted for determinism. self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) def getInterface(self, ifname): return self.interfaces[ifname] def getDescriptors(self, **filters): """Gets the descriptors that match the given filters.""" @@ -67,16 +68,18 @@ class Configuration: elif key == 'isExternal': getter = lambda x: x.interface.isExternal() 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) class Descriptor: """ Represents a single descriptor for an interface. See Bindings.conf. """ def __init__(self, config, interface, desc): self.config = config self.interface = interface
--- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -141,8 +141,14 @@ GARBAGE += \ $(binding_header_files) \ $(binding_cpp_files) \ $(all_webidl_files) \ $(globalgen_targets) \ ParserResults.pkl \ webidlyacc.py \ parser.out \ $(NULL) + +# Make sure all binding header files are created during the export stage, so we +# don't have issues with .cpp files being compiled before we've generated the +# headers they depend on. This is really only needed for the test files, since +# the non-test headers are all exported above anyway. +export:: $(binding_header_files) \ No newline at end of file
--- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -145,16 +145,19 @@ class IDLObject(object): return False def isCallback(self): return False def isType(self): return False + def isDictionary(self): + return False; + def getUserData(self, key, default): return self.userData.get(key, default) def setUserData(self, key, value): self.userData[key] = value def addExtendedAttributes(self, attrs): assert False # Override me! @@ -598,16 +601,19 @@ class IDLDictionary(IDLObjectWithScope): self._finished = False self.members = list(members) IDLObjectWithScope.__init__(self, location, parentScope, name) def __str__(self): return "Dictionary '%s'" % self.identifier.name + def isDictionary(self): + return True; + def finish(self, scope): if self._finished: return self._finished = True if self.parent: assert isinstance(self.parent, IDLIdentifierPlaceholder)
--- a/dom/bindings/test/TestBindingHeader.h +++ b/dom/bindings/test/TestBindingHeader.h @@ -289,16 +289,24 @@ public: // binaryNames tests void MethodRenamedTo(ErrorResult&); void MethodRenamedTo(int8_t, ErrorResult&); int8_t GetAttributeGetterRenamedTo(ErrorResult&); int8_t GetAttributeRenamedTo(ErrorResult&); void SetAttributeRenamedTo(int8_t, ErrorResult&); + // Dictionary tests + void PassDictionary(const Dict&, ErrorResult&); + void PassOptionalDictionary(const Optional<Dict>&, ErrorResult&); + void PassNullableDictionary(const Nullable<Dict>&, ErrorResult&); + void PassOptionalNullableDictionary(const Optional<Nullable<Dict> >&, ErrorResult&); + void PassOtherDictionary(const GrandparentDict&, ErrorResult&); + void PassSequenceOfDictionaries(const Sequence<Dict>&, ErrorResult&); + // Methods and properties imported via "implements" bool GetImplementedProperty(ErrorResult&); void SetImplementedProperty(bool, ErrorResult&); void ImplementedMethod(ErrorResult&); bool GetImplementedParentProperty(ErrorResult&); void SetImplementedParentProperty(bool, ErrorResult&); void ImplementedParentMethod(ErrorResult&); bool GetIndirectlyImplementedProperty(ErrorResult&);
--- a/dom/bindings/test/TestCodeGen.webidl +++ b/dom/bindings/test/TestCodeGen.webidl @@ -219,16 +219,23 @@ interface TestInterface { object receiveObject(); object? receiveNullableObject(); // binaryNames tests void methodRenamedFrom(); void methodRenamedFrom(byte argument); readonly attribute byte attributeGetterRenamedFrom; attribute byte attributeRenamedFrom; + + void passDictionary(Dict x); + void passOptionalDictionary(optional Dict x); + void passNullableDictionary(Dict? x); + void passOptionalNullableDictionary(optional Dict? x); + void passOtherDictionary(GrandparentDict x); + void passSequenceOfDictionaries(sequence<Dict> x); }; interface ImplementedInterfaceParent { void implementedParentMethod(); attribute boolean implementedParentProperty; const long implementedParentConstant = 8; }; @@ -261,8 +268,19 @@ interface DiamondBranch2A : DiamondImple interface DiamondBranch2B : DiamondImplements { }; TestInterface implements DiamondBranch1A; TestInterface implements DiamondBranch1B; TestInterface implements DiamondBranch2A; TestInterface implements DiamondBranch2B; DiamondBranch1A implements DiamondImplements; DiamondBranch1B implements DiamondImplements; + +dictionary Dict : ParentDict { + long x; + long a; + long b = 8; + long z = 9; +}; + +dictionary ParentDict : GrandparentDict { + long c = 5; +};
new file mode 100644 --- /dev/null +++ b/dom/bindings/test/TestDictionary.webidl @@ -0,0 +1,9 @@ +/* -*- 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/. + */ + +dictionary GrandparentDict { + double someNum; +}; \ No newline at end of file
--- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -10,13 +10,16 @@ webidl_files = \ EventTarget.webidl \ XMLHttpRequest.webidl \ XMLHttpRequestEventTarget.webidl \ XMLHttpRequestUpload.webidl \ WebGLRenderingContext.webidl \ $(NULL) ifdef ENABLE_TESTS -test_webidl_files := TestCodeGen.webidl +test_webidl_files := \ + TestCodeGen.webidl \ + TestDictionary.webidl \ + $(NULL) else test_webidl_files := $(NULL) endif