diff options
Diffstat (limited to 'components/script/dom/bindings/codegen/parser')
54 files changed, 4700 insertions, 1948 deletions
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index dc9fa036141..d74278c3e0c 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -4,13 +4,15 @@ """ A WebIDL parser. """ + from ply import lex, yacc import re import os import traceback import math import string -from collections import defaultdict +from collections import defaultdict, OrderedDict +from itertools import chain # Machinery @@ -40,32 +42,22 @@ def parseInt(literal): return value * sign -# Magic for creating enums -def M_add_class_attribs(attribs, start): - def foo(name, bases, dict_): - for v, k in enumerate(attribs): - dict_[k] = start + v - assert 'length' not in dict_ - dict_['length'] = start + len(attribs) - return type(name, bases, dict_) - return foo - - def enum(*names, **kw): - if len(kw) == 1: - base = kw['base'].__class__ - start = base.length - else: - assert len(kw) == 0 - base = object - start = 0 - - class Foo(base): - __metaclass__ = M_add_class_attribs(names, start) - + class Foo(object): + attrs = OrderedDict() + def __init__(self, names): + for v, k in enumerate(names): + self.attrs[k] = v + def __getattr__(self, attr): + if attr in self.attrs: + return self.attrs[attr] + raise AttributeError def __setattr__(self, name, value): # this makes it read-only raise NotImplementedError - return Foo() + + if "base" not in kw: + return Foo(names) + return Foo(chain(list(kw["base"].attrs.keys()), names)) class WebIDLError(Exception): @@ -132,6 +124,9 @@ class BuiltinLocation(object): return (isinstance(other, BuiltinLocation) and self.msg == other.msg) + def __hash__(self): + return hash(self.msg) + def filename(self): return '<builtin>' @@ -162,6 +157,9 @@ class IDLObject(object): def isNamespace(self): return False + def isInterfaceMixin(self): + return False + def isEnum(self): return False @@ -241,15 +239,19 @@ class IDLScope(IDLObject): # A mapping from global name to the set of global interfaces # that have that global name. self.globalNameMapping = defaultdict(set) - self.primaryGlobalAttr = None - self.primaryGlobalName = None def __str__(self): return self.QName() def QName(self): - if self._name: - return self._name.QName() + "::" + # It's possible for us to be called before __init__ has been called, for + # the IDLObjectWithScope case. In that case, self._name won't be set yet. + if hasattr(self, "_name"): + name = self._name + else: + name = None + if name: + return name.QName() + "::" return "::" def ensureUnique(self, identifier, object): @@ -305,8 +307,8 @@ class IDLScope(IDLObject): # because we need to merge overloads of NamedConstructors and we need to # detect conflicts in those across interfaces. See also the comment in # IDLInterface.addExtendedAttributes for "NamedConstructor". - if (originalObject.tag == IDLInterfaceMember.Tags.Method and - newObject.tag == IDLInterfaceMember.Tags.Method): + if (isinstance(originalObject, IDLMethod) and + isinstance(newObject, IDLMethod)): return originalObject.addOverload(newObject) # Default to throwing, derived classes can override. @@ -316,7 +318,7 @@ class IDLScope(IDLObject): newObject.location) raise WebIDLError( - "Multiple unresolvable definitions of identifier '%s' in scope '%s%s" + "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s" % (identifier.name, str(self), conflictdesc), []) def _lookupIdentifier(self, identifier): @@ -327,6 +329,13 @@ class IDLScope(IDLObject): assert identifier.scope == self return self._lookupIdentifier(identifier) + def addIfaceGlobalNames(self, interfaceName, globalNames): + """Record the global names (from |globalNames|) that can be used in + [Exposed] to expose things in a global named |interfaceName|""" + self.globalNames.update(globalNames) + for name in globalNames: + self.globalNameMapping[name].add(interfaceName) + class IDLIdentifier(IDLObject): def __init__(self, location, scope, name): @@ -367,8 +376,6 @@ class IDLUnresolvedIdentifier(IDLObject): [location]) if name[0] == '_' and not allowDoubleUnderscore: name = name[1:] - # TODO: Bug 872377, Restore "toJSON" to below list. - # We sometimes need custom serialization, so allow toJSON for now. if (name in ["constructor", "toString"] and not allowForbidden): raise WebIDLError("Cannot use reserved identifier '%s'" % (name), @@ -409,48 +416,11 @@ class IDLObjectWithIdentifier(IDLObject): if parentScope: self.resolve(parentScope) - self.treatNullAs = "Default" - def resolve(self, parentScope): assert isinstance(parentScope, IDLScope) assert isinstance(self.identifier, IDLUnresolvedIdentifier) self.identifier.resolve(parentScope, self) - def checkForStringHandlingExtendedAttributes(self, attrs, - isDictionaryMember=False, - isOptional=False): - """ - A helper function to deal with TreatNullAs. Returns the list - of attrs it didn't handle itself. - """ - assert isinstance(self, IDLArgument) or isinstance(self, IDLAttribute) - unhandledAttrs = list() - for attr in attrs: - if not attr.hasValue(): - unhandledAttrs.append(attr) - continue - - identifier = attr.identifier() - value = attr.value() - if identifier == "TreatNullAs": - if not self.type.isDOMString() or self.type.nullable(): - raise WebIDLError("[TreatNullAs] is only allowed on " - "arguments or attributes whose type is " - "DOMString", - [self.location]) - if isDictionaryMember: - raise WebIDLError("[TreatNullAs] is not allowed for " - "dictionary members", [self.location]) - if value != 'EmptyString': - raise WebIDLError("[TreatNullAs] must take the identifier " - "'EmptyString', not '%s'" % value, - [self.location]) - self.treatNullAs = value - else: - unhandledAttrs.append(attr) - - return unhandledAttrs - class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope): def __init__(self, location, parentScope, identifier): @@ -496,8 +466,17 @@ class IDLExposureMixins(): raise WebIDLError("Unknown [Exposed] value %s" % globalName, [self._location]) - if len(self._exposureGlobalNames) == 0: - self._exposureGlobalNames.add(scope.primaryGlobalName) + # Verify that we are exposed _somwhere_ if we have some place to be + # exposed. We don't want to assert that we're definitely exposed + # because a lot of our parser tests have small-enough IDL snippets that + # they don't include any globals, and we don't really want to go through + # and add global interfaces and [Exposed] annotations to all those + # tests. + if len(scope.globalNames) != 0: + if (len(self._exposureGlobalNames) == 0): + raise WebIDLError(("'%s' is not exposed anywhere even though we have " + "globals to be exposed to") % self, + [self.location]) globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposureSet) @@ -505,18 +484,14 @@ class IDLExposureMixins(): def isExposedInWindow(self): return 'Window' in self.exposureSet - def isExposedOnMainThread(self): - return (self.isExposedInWindow() or - self.isExposedInSystemGlobals()) - def isExposedInAnyWorker(self): return len(self.getWorkerExposureSet()) > 0 def isExposedInWorkerDebugger(self): return len(self.getWorkerDebuggerExposureSet()) > 0 - def isExposedInSystemGlobals(self): - return 'BackstagePass' in self.exposureSet + def isExposedInAnyWorklet(self): + return len(self.getWorkletExposureSet()) > 0 def isExposedInSomeButNotAllWorkers(self): """ @@ -534,22 +509,24 @@ class IDLExposureMixins(): workerScopes = self._globalScope.globalNameMapping["Worker"] return workerScopes.intersection(self.exposureSet) + def getWorkletExposureSet(self): + workletScopes = self._globalScope.globalNameMapping["Worklet"] + return workletScopes.intersection(self.exposureSet) + def getWorkerDebuggerExposureSet(self): workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"] return workerDebuggerScopes.intersection(self.exposureSet) -class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins): +class IDLExternalInterface(IDLObjectWithIdentifier): def __init__(self, location, parentScope, identifier): assert isinstance(identifier, IDLUnresolvedIdentifier) assert isinstance(parentScope, IDLScope) self.parent = None IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) - IDLExposureMixins.__init__(self, location) IDLObjectWithIdentifier.resolve(self, parentScope) def finish(self, scope): - IDLExposureMixins.finish(self, scope) pass def validate(self): @@ -564,11 +541,11 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins): def isInterface(self): return True - def isConsequential(self): - return False - def addExtendedAttributes(self, attrs): - assert len(attrs) == 0 + if len(attrs) != 0: + raise WebIDLError("There are no extended attributes that are " + "allowed on external interfaces", + [attrs[0].location, self.location]) def resolve(self, parentScope): pass @@ -579,16 +556,41 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins): def isJSImplemented(self): return False - def isProbablyShortLivingObject(self): - return False - - def isNavigatorProperty(self): + def hasProbablyShortLivingWrapper(self): return False def _getDependentObjects(self): return set() +class IDLPartialDictionary(IDLObject): + def __init__(self, location, name, members, nonPartialDictionary): + assert isinstance(name, IDLUnresolvedIdentifier) + + IDLObject.__init__(self, location) + self.identifier = name + self.members = members + self._nonPartialDictionary = nonPartialDictionary + self._finished = False + nonPartialDictionary.addPartialDictionary(self) + + def addExtendedAttributes(self, attrs): + pass + + def finish(self, scope): + if self._finished: + return + self._finished = True + + # Need to make sure our non-partial dictionary gets + # finished so it can report cases when we only have partial + # dictionaries. + self._nonPartialDictionary.finish(scope) + + def validate(self): + pass + + class IDLPartialInterfaceOrNamespace(IDLObject): def __init__(self, location, name, members, nonPartialInterfaceOrNamespace): assert isinstance(name, IDLUnresolvedIdentifier) @@ -602,13 +604,13 @@ class IDLPartialInterfaceOrNamespace(IDLObject): self._haveSecureContextExtendedAttribute = False self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace self._finished = False - nonPartialInterfaceOrNamespace.addPartialInterface(self) + nonPartialInterfaceOrNamespace.addPartial(self) def addExtendedAttributes(self, attrs): for attr in attrs: identifier = attr.identifier() - if identifier in ["Constructor", "NamedConstructor"]: + if identifier == "NamedConstructor": self.propagatedExtendedAttrs.append(attr) elif identifier == "SecureContext": self._haveSecureContextExtendedAttribute = True @@ -673,36 +675,225 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet): for name in nameSet: exposureSet.update(globalScope.globalNameMapping[name]) - -class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): - def __init__(self, location, parentScope, name, parent, members, - isKnownNonPartial): +class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins): + def __init__(self, location, parentScope, name): assert isinstance(parentScope, IDLScope) assert isinstance(name, IDLUnresolvedIdentifier) + + self._finished = False + self.members = [] + self._partials = [] + self._extendedAttrDict = {} + self._isKnownNonPartial = False + + IDLObjectWithScope.__init__(self, location, parentScope, name) + IDLExposureMixins.__init__(self, location) + + def finish(self, scope): + if not self._isKnownNonPartial: + raise WebIDLError("%s does not have a non-partial declaration" % + str(self), [self.location]) + + IDLExposureMixins.finish(self, scope) + + # Now go ahead and merge in our partials. + for partial in self._partials: + partial.finish(scope) + self.addExtendedAttributes(partial.propagatedExtendedAttrs) + self.members.extend(partial.members) + + def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): + assert isinstance(scope, IDLScope) + assert isinstance(originalObject, IDLInterfaceMember) + assert isinstance(newObject, IDLInterfaceMember) + + retval = IDLScope.resolveIdentifierConflict(self, scope, identifier, + originalObject, newObject) + + # Might be a ctor, which isn't in self.members + if newObject in self.members: + self.members.remove(newObject) + return retval + + def typeName(self): + if self.isInterface(): + return "interface" + if self.isNamespace(): + return "namespace" + assert self.isInterfaceMixin() + return "interface mixin" + + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) + + def setNonPartial(self, location, members): + if self._isKnownNonPartial: + raise WebIDLError("Two non-partial definitions for the " + "same %s" % self.typeName(), + [location, self.location]) + self._isKnownNonPartial = True + # Now make it look like we were parsed at this new location, since + # that's the place where the interface is "really" defined + self.location = location + # Put the new members at the beginning + self.members = members + self.members + + def addPartial(self, partial): + assert self.identifier.name == partial.identifier.name + self._partials.append(partial) + + def getPartials(self): + # Don't let people mutate our guts. + return list(self._partials) + + def finishMembers(self, scope): + # Assuming we've merged in our partials, set the _exposureGlobalNames on + # any members that don't have it set yet. Note that any partial + # interfaces that had [Exposed] set have already set up + # _exposureGlobalNames on all the members coming from them, so this is + # just implementing the "members default to interface or interface mixin + # that defined them" and "partial interfaces or interface mixins default + # to interface or interface mixin they're a partial for" rules from the + # spec. + for m in self.members: + # If m, or the partial m came from, had [Exposed] + # specified, it already has a nonempty exposure global names set. + if len(m._exposureGlobalNames) == 0: + m._exposureGlobalNames.update(self._exposureGlobalNames) + if m.isAttr() and m.stringifier: + m.expand(self.members) + + # resolve() will modify self.members, so we need to iterate + # over a copy of the member list here. + for member in list(self.members): + member.resolve(self) + + for member in self.members: + member.finish(scope) + + # Now that we've finished our members, which has updated their exposure + # sets, make sure they aren't exposed in places where we are not. + for member in self.members: + if not member.exposureSet.issubset(self.exposureSet): + raise WebIDLError("Interface or interface mixin member has " + "larger exposure set than its container", + [member.location, self.location]) + + def isExternal(self): + return False + + +class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace): + def __init__(self, location, parentScope, name, members, isKnownNonPartial): + self.actualExposureGlobalNames = set() + + assert isKnownNonPartial or len(members) == 0 + IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name) + + if isKnownNonPartial: + self.setNonPartial(location, members) + + def __str__(self): + return "Interface mixin '%s'" % self.identifier.name + + def isInterfaceMixin(self): + return True + + def finish(self, scope): + if self._finished: + return + self._finished = True + + # Expose to the globals of interfaces that includes this mixin if this + # mixin has no explicit [Exposed] so that its members can be exposed + # based on the base interface exposure set. + # + # Make sure this is done before IDLExposureMixins.finish call, since + # that converts our set of exposure global names to an actual exposure + # set. + hasImplicitExposure = len(self._exposureGlobalNames) == 0 + if hasImplicitExposure: + self._exposureGlobalNames.update(self.actualExposureGlobalNames) + + IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope) + + self.finishMembers(scope) + + def validate(self): + for member in self.members: + + if member.isAttr(): + if member.inherit: + raise WebIDLError("Interface mixin member cannot include " + "an inherited attribute", + [member.location, self.location]) + if member.isStatic(): + raise WebIDLError("Interface mixin member cannot include " + "a static member", + [member.location, self.location]) + + if member.isMethod(): + if member.isStatic(): + raise WebIDLError("Interface mixin member cannot include " + "a static operation", + [member.location, self.location]) + if (member.isGetter() or + member.isSetter() or + member.isDeleter() or + member.isLegacycaller()): + raise WebIDLError("Interface mixin member cannot include a " + "special operation", + [member.location, self.location]) + + def addExtendedAttributes(self, attrs): + for attr in attrs: + identifier = attr.identifier() + + if identifier == "SecureContext": + if not attr.noArguments(): + raise WebIDLError("[%s] must take no arguments" % identifier, + [attr.location]) + # This gets propagated to all our members. + for member in self.members: + if member.getExtendedAttribute("SecureContext"): + raise WebIDLError("[SecureContext] specified on both " + "an interface mixin member and on" + "the interface mixin itself", + [member.location, attr.location]) + member.addExtendedAttributes([attr]) + elif identifier == "Exposed": + convertExposedAttrToGlobalNameSet(attr, + self._exposureGlobalNames) + else: + raise WebIDLError("Unknown extended attribute %s on interface" % identifier, + [attr.location]) + + attrlist = attr.listValue() + self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True + + def _getDependentObjects(self): + return set(self.members) + + +class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace): + def __init__(self, location, parentScope, name, parent, members, + isKnownNonPartial, toStringTag): assert isKnownNonPartial or not parent assert isKnownNonPartial or len(members) == 0 self.parent = None self._callback = False - self._finished = False - self.members = [] self.maplikeOrSetlikeOrIterable = None - self._partialInterfaces = [] - self._extendedAttrDict = {} # namedConstructors needs deterministic ordering because bindings code # outputs the constructs in the order that namedConstructors enumerates # them. self.namedConstructors = list() - self.implementedInterfaces = set() - self._consequential = False - self._isKnownNonPartial = False + self.legacyWindowAliases = [] + self.includedMixins = set() # self.interfacesBasedOnSelf is the set of interfaces that inherit from - # self or have self as a consequential interface, including self itself. + # self, including self itself. # Used for distinguishability checking. self.interfacesBasedOnSelf = set([self]) - # self.interfacesImplementingSelf is the set of interfaces that directly - # have self as a consequential interface - self.interfacesImplementingSelf = set() self._hasChildInterfaces = False self._isOnGlobalProtoChain = False # Tracking of the number of reserved slots we need for our @@ -713,9 +904,14 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): # If this is an iterator interface, we need to know what iterable # interface we're iterating for in order to get its nativeType. self.iterableInterface = None + # True if we have cross-origin members. + self.hasCrossOriginMembers = False + # True if some descendant (including ourselves) has cross-origin members + self.hasDescendantWithCrossOriginMembers = False - IDLObjectWithScope.__init__(self, location, parentScope, name) - IDLExposureMixins.__init__(self, location) + self.toStringTag = toStringTag + + IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name) if isKnownNonPartial: self.setNonPartial(location, parent, members) @@ -735,37 +931,23 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): def isIteratorInterface(self): return self.iterableInterface is not None - def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): - assert isinstance(scope, IDLScope) - assert isinstance(originalObject, IDLInterfaceMember) - assert isinstance(newObject, IDLInterfaceMember) - - retval = IDLScope.resolveIdentifierConflict(self, scope, identifier, - originalObject, newObject) - - # Might be a ctor, which isn't in self.members - if newObject in self.members: - self.members.remove(newObject) - return retval - def finish(self, scope): if self._finished: return self._finished = True - if not self._isKnownNonPartial: - raise WebIDLError("Interface %s does not have a non-partial " - "declaration" % self.identifier.name, - [self.location]) - - IDLExposureMixins.finish(self, scope) + IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope) - # Now go ahead and merge in our partial interfaces. - for partial in self._partialInterfaces: - partial.finish(scope) - self.addExtendedAttributes(partial.propagatedExtendedAttrs) - self.members.extend(partial.members) + if len(self.legacyWindowAliases) > 0: + if not self.hasInterfaceObject(): + raise WebIDLError("Interface %s unexpectedly has [LegacyWindowAlias] " + "and [NoInterfaceObject] together" % self.identifier.name, + [self.location]) + if not self.isExposedInWindow(): + raise WebIDLError("Interface %s has [LegacyWindowAlias] " + "but not exposed in Window" % self.identifier.name, + [self.location]) # Generate maplike/setlike interface members. Since generated members # need to be treated like regular interface members, do this before @@ -788,19 +970,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): # our required methods in Codegen. Generate members now. self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented()) - # Now that we've merged in our partial interfaces, set the - # _exposureGlobalNames on any members that don't have it set yet. Note - # that any partial interfaces that had [Exposed] set have already set up - # _exposureGlobalNames on all the members coming from them, so this is - # just implementing the "members default to interface that defined them" - # and "partial interfaces default to interface they're a partial for" - # rules from the spec. - for m in self.members: - # If m, or the partial interface m came from, had [Exposed] - # specified, it already has a nonempty exposure global names set. - if len(m._exposureGlobalNames) == 0: - m._exposureGlobalNames.update(self._exposureGlobalNames) - assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder) parent = self.parent.finish(scope) if self.parent else None if parent and isinstance(parent, IDLExternalInterface): @@ -809,7 +978,11 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): (self.identifier.name, self.parent.identifier.name), [self.location]) - assert not parent or isinstance(parent, IDLInterface) + if parent and not isinstance(parent, IDLInterface): + raise WebIDLError("%s inherits from %s which is not an interface " % + (self.identifier.name, + self.parent.identifier.name), + [self.location, parent.location]) self.parent = parent @@ -835,10 +1008,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): self.totalMembersInSlots = self.parent.totalMembersInSlots - # Interfaces with [Global] or [PrimaryGlobal] must not - # have anything inherit from them - if (self.parent.getExtendedAttribute("Global") or - self.parent.getExtendedAttribute("PrimaryGlobal")): + # Interfaces with [Global] must not have anything inherit from them + if self.parent.getExtendedAttribute("Global"): # Note: This is not a self.parent.isOnGlobalProtoChain() check # because ancestors of a [Global] interface can have other # descendants. @@ -854,10 +1025,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): self.parent.identifier.name), [self.location, self.parent.location]) - # Callbacks must not inherit from non-callbacks or inherit from - # anything that has consequential interfaces. + # Callbacks must not inherit from non-callbacks. # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending. - # XXXbz Can callbacks have consequential interfaces? Spec issue pending if self.isCallback(): if not self.parent.isCallback(): raise WebIDLError("Callback interface %s inheriting from " @@ -894,48 +1063,40 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): self.parent.identifier.name), [self.location, self.parent.location]) - for iface in self.implementedInterfaces: - iface.finish(scope) + for mixin in self.includedMixins: + mixin.finish(scope) cycleInGraph = self.findInterfaceLoopPoint(self) if cycleInGraph: - raise WebIDLError("Interface %s has itself as ancestor or " - "implemented interface" % self.identifier.name, - [self.location, cycleInGraph.location]) - - if self.isCallback(): - # "implements" should have made sure we have no - # consequential interfaces. - assert len(self.getConsequentialInterfaces()) == 0 - # And that we're not consequential. - assert not self.isConsequential() - - # Now resolve() and finish() our members before importing the - # ones from our implemented interfaces. - - # resolve() will modify self.members, so we need to iterate - # over a copy of the member list here. - for member in list(self.members): - member.resolve(self) + raise WebIDLError( + "Interface %s has itself as ancestor" % self.identifier.name, + [self.location, cycleInGraph.location]) - for member in self.members: - member.finish(scope) - - # Now that we've finished our members, which has updated their exposure - # sets, make sure they aren't exposed in places where we are not. - for member in self.members: - if not member.exposureSet.issubset(self.exposureSet): - raise WebIDLError("Interface member has larger exposure set " - "than the interface itself", - [member.location, self.location]) + self.finishMembers(scope) ctor = self.ctor() if ctor is not None: - assert len(ctor._exposureGlobalNames) == 0 + if not self.hasInterfaceObject(): + raise WebIDLError( + "Can't have both a constructor and [NoInterfaceObject]", + [self.location, ctor.location]) + + if self.globalNames: + raise WebIDLError( + "Can't have both a constructor and [Global]", + [self.location, ctor.location]) + + assert(ctor._exposureGlobalNames == self._exposureGlobalNames) ctor._exposureGlobalNames.update(self._exposureGlobalNames) - ctor.finish(scope) + # Remove the constructor operation from our member list so + # it doesn't get in the way later. + self.members.remove(ctor) for ctor in self.namedConstructors: + if self.globalNames: + raise WebIDLError( + "Can't have both a named constructor and [Global]", + [self.location, ctor.location]) assert len(ctor._exposureGlobalNames) == 0 ctor._exposureGlobalNames.update(self._exposureGlobalNames) ctor.finish(scope) @@ -945,41 +1106,16 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): # admixed. self.originalMembers = list(self.members) - # Import everything from our consequential interfaces into - # self.members. Sort our consequential interfaces by name - # just so we have a consistent order. - for iface in sorted(self.getConsequentialInterfaces(), - cmp=cmp, + for mixin in sorted(self.includedMixins, key=lambda x: x.identifier.name): - # Flag the interface as being someone's consequential interface - iface.setIsConsequentialInterfaceOf(self) - # Verify that we're not exposed somewhere where iface is not exposed - if not self.exposureSet.issubset(iface.exposureSet): - raise WebIDLError("Interface %s is exposed in globals where its " - "consequential interface %s is not exposed." % - (self.identifier.name, iface.identifier.name), - [self.location, iface.location]) - - # If we have a maplike or setlike, and the consequential interface - # also does, throw an error. - if iface.maplikeOrSetlikeOrIterable and self.maplikeOrSetlikeOrIterable: - raise WebIDLError("Maplike/setlike/iterable interface %s cannot have " - "maplike/setlike/iterable interface %s as a " - "consequential interface" % - (self.identifier.name, - iface.identifier.name), - [self.maplikeOrSetlikeOrIterable.location, - iface.maplikeOrSetlikeOrIterable.location]) - additionalMembers = iface.originalMembers - for additionalMember in additionalMembers: + for mixinMember in mixin.members: for member in self.members: - if additionalMember.identifier.name == member.identifier.name: + if mixinMember.identifier.name == member.identifier.name: raise WebIDLError( - "Multiple definitions of %s on %s coming from 'implements' statements" % + "Multiple definitions of %s on %s coming from 'includes' statements" % (member.identifier.name, self), - [additionalMember.location, member.location]) - self.members.extend(additionalMembers) - iface.interfacesImplementingSelf.add(self) + [mixinMember.location, member.location]) + self.members.extend(mixin.members) for ancestor in self.getInheritedInterfaces(): ancestor.interfacesBasedOnSelf.add(self) @@ -992,8 +1128,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): ancestor.identifier.name), [self.maplikeOrSetlikeOrIterable.location, ancestor.maplikeOrSetlikeOrIterable.location]) - for ancestorConsequential in ancestor.getConsequentialInterfaces(): - ancestorConsequential.interfacesBasedOnSelf.add(self) # Deal with interfaces marked [Unforgeable], now that we have our full # member list, except unforgeables pulled in from parents. We want to @@ -1009,10 +1143,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): [self.location]) for m in self.members: - if ((m.isMethod() and m.isJsonifier()) or - m.identifier.name == "toJSON"): + if m.identifier.name == "toJSON": raise WebIDLError("Unforgeable interface %s has a " - "jsonifier so we won't be able to add " + "toJSON so we won't be able to add " "one ourselves" % self.identifier.name, [self.location, m.location]) @@ -1028,6 +1161,21 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): not hasattr(member, "originatingInterface")): member.originatingInterface = self + for member in self.members: + if ((member.isMethod() and + member.getExtendedAttribute("CrossOriginCallable")) or + (member.isAttr() and + (member.getExtendedAttribute("CrossOriginReadable") or + member.getExtendedAttribute("CrossOriginWritable")))): + self.hasCrossOriginMembers = True + break + + if self.hasCrossOriginMembers: + parent = self + while parent: + parent.hasDescendantWithCrossOriginMembers = True + parent = parent.parent + # Compute slot indices for our members before we pull in unforgeable # members from our parent. Also, maplike/setlike declarations get a # slot to hold their backing object. @@ -1036,6 +1184,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): (member.getExtendedAttribute("StoreInSlot") or member.getExtendedAttribute("Cached"))) or member.isMaplikeOrSetlike()): + if self.isJSImplemented() and not member.isMaplikeOrSetlike(): + raise WebIDLError("Interface %s is JS-implemented and we " + "don't support [Cached] or [StoreInSlot] " + "on JS-implemented interfaces" % + self.identifier.name, + [self.location, member.location]) if member.slotIndices is None: member.slotIndices = dict() member.slotIndices[self.identifier.name] = self.totalMembersInSlots @@ -1044,13 +1198,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): self._ownMembersInSlots += 1 if self.parent: - # Make sure we don't shadow any of the [Unforgeable] attributes on - # our ancestor interfaces. We don't have to worry about - # consequential interfaces here, because those have already been - # imported into the relevant .members lists. And we don't have to - # worry about anything other than our parent, because it has already - # imported its ancestors unforgeable attributes into its member - # list. + # Make sure we don't shadow any of the [Unforgeable] attributes on our + # ancestor interfaces. We don't have to worry about mixins here, because + # those have already been imported into the relevant .members lists. And + # we don't have to worry about anything other than our parent, because it + # has already imported its ancestors' unforgeable attributes into its + # member list. for unforgeableMember in (member for member in self.parent.members if (member.isAttr() or member.isMethod()) and member.isUnforgeable()): @@ -1087,10 +1240,13 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): testInterface = testInterface.parent # Ensure that there's at most one of each {named,indexed} - # {getter,setter,creator,deleter}, at most one stringifier, + # {getter,setter,deleter}, at most one stringifier, # and at most one legacycaller. Note that this last is not # quite per spec, but in practice no one overloads - # legacycallers. + # legacycallers. Also note that in practice we disallow + # indexed deleters, but it simplifies some other code to + # treat deleter analogously to getter/setter by + # prefixing it with "named". specialMembersSeen = {} for member in self.members: if not member.isMethod(): @@ -1100,21 +1256,16 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): memberType = "getters" elif member.isSetter(): memberType = "setters" - elif member.isCreator(): - memberType = "creators" elif member.isDeleter(): memberType = "deleters" elif member.isStringifier(): memberType = "stringifiers" - elif member.isJsonifier(): - memberType = "jsonifiers" elif member.isLegacycaller(): memberType = "legacycallers" else: continue - if (memberType != "stringifiers" and memberType != "legacycallers" and - memberType != "jsonifiers"): + if (memberType != "stringifiers" and memberType != "legacycallers"): if member.isNamed(): memberType = "named " + memberType else: @@ -1147,8 +1298,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): ancestor = ancestor.parent if self._isOnGlobalProtoChain: - # Make sure we have no named setters, creators, or deleters - for memberType in ["setter", "creator", "deleter"]: + # Make sure we have no named setters or deleters + for memberType in ["setter", "deleter"]: memberId = "named " + memberType + "s" if memberId in specialMembersSeen: raise WebIDLError("Interface with [Global] has a named %s" % @@ -1172,16 +1323,21 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): parent = parent.parent def validate(self): - # We don't support consequential unforgeable interfaces. Need to check - # this here, because in finish() an interface might not know yet that - # it's consequential. - if self.getExtendedAttribute("Unforgeable") and self.isConsequential(): - raise WebIDLError( - "%s is an unforgeable consequential interface" % - self.identifier.name, - [self.location] + - list(i.location for i in - (self.interfacesBasedOnSelf - {self}))) + + def checkDuplicateNames(member, name, attributeName): + for m in self.members: + if m.identifier.name == name: + raise WebIDLError("[%s=%s] has same name as interface member" % + (attributeName, name), + [member.location, m.location]) + if m.isMethod() and m != member and name in m.aliases: + raise WebIDLError("conflicting [%s=%s] definitions" % + (attributeName, name), + [member.location, m.location]) + if m.isAttr() and m != member and name in m.bindingAliases: + raise WebIDLError("conflicting [%s=%s] definitions" % + (attributeName, name), + [member.location, m.location]) # We also don't support inheriting from unforgeable interfaces. if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces(): @@ -1192,6 +1348,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): self.identifier.name, locations) + ctor = self.ctor() + if ctor is not None: + ctor.validate() + for namedCtor in self.namedConstructors: + namedCtor.validate() + indexedGetter = None hasLengthAttribute = False for member in self.members: @@ -1278,23 +1440,22 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): raise WebIDLError("[Alias] must not be used on an " "[Unforgeable] operation", [member.location]) - for m in self.members: - if m.identifier.name == alias: - raise WebIDLError("[Alias=%s] has same name as " - "interface member" % alias, - [member.location, m.location]) - if m.isMethod() and m != member and alias in m.aliases: - raise WebIDLError("duplicate [Alias=%s] definitions" % - alias, - [member.location, m.location]) + + checkDuplicateNames(member, alias, "Alias") + + # Check that the name of a [BindingAlias] doesn't conflict with an + # interface member. + if member.isAttr(): + for bindingAlias in member.bindingAliases: + checkDuplicateNames(member, bindingAlias, "BindingAlias") + # Conditional exposure makes no sense for interfaces with no - # interface object, unless they're navigator properties. + # interface object. # And SecureContext makes sense for interfaces with no interface object, # since it is also propagated to interface members. if (self.isExposedConditionally(exclusions=["SecureContext"]) and - not self.hasInterfaceObject() and - not self.isNavigatorProperty()): + not self.hasInterfaceObject()): raise WebIDLError("Interface with no interface object is " "exposed conditionally", [self.location]) @@ -1308,7 +1469,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): if not indexedGetter: raise WebIDLError("Interface with value iterator does not " "support indexed properties", - [self.location]) + [self.location, iterableDecl.location]) if iterableDecl.valueType != indexedGetter.signatures()[0][0]: raise WebIDLError("Iterable type does not match indexed " @@ -1319,7 +1480,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): if not hasLengthAttribute: raise WebIDLError('Interface with value iterator does not ' 'have an integer-typed "length" attribute', - [self.location]) + [self.location, iterableDecl.location]) else: assert iterableDecl.isPairIterator() if indexedGetter: @@ -1328,15 +1489,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): [self.location, iterableDecl.location, indexedGetter.location]) - def isExternal(self): - return False - - def setIsConsequentialInterfaceOf(self, other): - self._consequential = True - self.interfacesBasedOnSelf.add(other) - - def isConsequential(self): - return self._consequential + if indexedGetter and not hasLengthAttribute: + raise WebIDLError('Interface with an indexed getter does not have ' + 'an integer-typed "length" attribute', + [self.location, indexedGetter.location]) def setCallback(self, value): self._callback = value @@ -1352,8 +1508,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): 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 @@ -1381,9 +1535,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): return (not self.isCallback() and not self.isNamespace() and self.getUserData('hasConcreteDescendant', False)) - def addImplementedInterface(self, implementedInterface): - assert(isinstance(implementedInterface, IDLInterface)) - self.implementedInterfaces.add(implementedInterface) + def addIncludedMixin(self, includedMixin): + assert(isinstance(includedMixin, IDLInterfaceMixin)) + self.includedMixins.add(includedMixin) def getInheritedInterfaces(self): """ @@ -1398,27 +1552,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): parentInterfaces.insert(0, self.parent) return parentInterfaces - def getConsequentialInterfaces(self): - assert(self._finished) - # The interfaces we implement directly - consequentialInterfaces = set(self.implementedInterfaces) - - # And their inherited interfaces - for iface in self.implementedInterfaces: - consequentialInterfaces |= set(iface.getInheritedInterfaces()) - - # And now collect up the consequential interfaces of all of those - temp = set() - for iface in consequentialInterfaces: - temp |= iface.getConsequentialInterfaces() - - return consequentialInterfaces | temp - def findInterfaceLoopPoint(self, otherInterface): """ - Finds an interface, amongst our ancestors and consequential interfaces, - that inherits from otherInterface or implements otherInterface - directly. If there is no such interface, returns None. + Finds an interface amongst our ancestors that inherits from otherInterface. + If there is no such interface, returns None. """ if self.parent: if self.parent == otherInterface: @@ -1426,37 +1563,13 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): loopPoint = self.parent.findInterfaceLoopPoint(otherInterface) if loopPoint: return loopPoint - if otherInterface in self.implementedInterfaces: - return self - for iface in self.implementedInterfaces: - loopPoint = iface.findInterfaceLoopPoint(otherInterface) - if loopPoint: - return loopPoint return None - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - def setNonPartial(self, location, parent, members): assert not parent or isinstance(parent, IDLIdentifierPlaceholder) - if self._isKnownNonPartial: - raise WebIDLError("Two non-partial definitions for the " - "same %s" % - ("interface" if self.isInterface() - else "namespace"), - [location, self.location]) - self._isKnownNonPartial = True - # Now make it look like we were parsed at this new location, since - # that's the place where the interface is "really" defined - self.location = location + IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members) assert not self.parent self.parent = parent - # Put the new members at the beginning - self.members = members + self.members - - def addPartialInterface(self, partial): - assert self.identifier.name == partial.identifier.name - self._partialInterfaces.append(partial) def getJSImplementation(self): classId = self.getExtendedAttribute("JSImplementation") @@ -1469,47 +1582,14 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): def isJSImplemented(self): return bool(self.getJSImplementation()) - def isProbablyShortLivingObject(self): + def hasProbablyShortLivingWrapper(self): current = self while current: - if current.getExtendedAttribute("ProbablyShortLivingObject"): + if current.getExtendedAttribute("ProbablyShortLivingWrapper"): return True current = current.parent return False - def isNavigatorProperty(self): - naviProp = self.getExtendedAttribute("NavigatorProperty") - if not naviProp: - return False - assert len(naviProp) == 1 - assert isinstance(naviProp, list) - assert len(naviProp[0]) != 0 - return True - - def getNavigatorProperty(self): - naviProp = self.getExtendedAttribute("NavigatorProperty") - if not naviProp: - return None - assert len(naviProp) == 1 - assert isinstance(naviProp, list) - assert len(naviProp[0]) != 0 - conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes - attr = IDLAttribute(self.location, - IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]), - IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)), - True, - extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes }, - navigatorObjectGetter=True) - attr._exposureGlobalNames = self._exposureGlobalNames - # We're abusing Constant a little bit here, because we need Cached. The - # getter will create a new object every time, but we're never going to - # clear the cached value. - extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )), - IDLExtendedAttribute(self.location, ("Cached", )), - IDLExtendedAttribute(self.location, ("Constant", )) ] - attr.addExtendedAttributes(extendedAttrs) - return attr - def hasChildInterfaces(self): return self._hasChildInterfaces @@ -1518,7 +1598,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): def _getDependentObjects(self): deps = set(self.members) - deps.update(self.implementedInterfaces) + deps.update(self.includedMixins) if self.parent: deps.add(self.parent) return deps @@ -1526,18 +1606,19 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): def hasMembersInSlots(self): return self._ownMembersInSlots != 0 - conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn", - "SecureContext", - "CheckAnyPermissions", - "CheckAllPermissions" ] + conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", + "SecureContext" ] def isExposedConditionally(self, exclusions=[]): return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes) class IDLInterface(IDLInterfaceOrNamespace): def __init__(self, location, parentScope, name, parent, members, - isKnownNonPartial): + isKnownNonPartial, classNameOverride=None, + toStringTag=None): IDLInterfaceOrNamespace.__init__(self, location, parentScope, name, - parent, members, isKnownNonPartial) + parent, members, isKnownNonPartial, + toStringTag) + self.classNameOverride = classNameOverride def __str__(self): return "Interface '%s'" % self.identifier.name @@ -1545,6 +1626,11 @@ class IDLInterface(IDLInterfaceOrNamespace): def isInterface(self): return True + def getClassName(self): + if self.classNameOverride: + return self.classNameOverride + return self.identifier.name + def addExtendedAttributes(self, attrs): for attr in attrs: identifier = attr.identifier() @@ -1561,86 +1647,46 @@ class IDLInterface(IDLInterfaceOrNamespace): raise WebIDLError("[NoInterfaceObject] must take no arguments", [attr.location]) - if self.ctor(): - raise WebIDLError("Constructor and NoInterfaceObject are incompatible", - [self.location]) - self._noInterfaceObject = True - elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor": - if identifier == "Constructor" and not self.hasInterfaceObject(): - raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", - [self.location]) - - if identifier == "NamedConstructor" and not attr.hasValue(): + elif identifier == "NamedConstructor": + if not attr.hasValue(): raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list", [attr.location]) - if identifier == "ChromeConstructor" and not self.hasInterfaceObject(): - raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", - [self.location]) args = attr.args() if attr.hasArgs() else [] - if self.identifier.name == "Promise": - promiseType = BuiltinTypes[IDLBuiltinType.Types.any] - else: - promiseType = None - retType = IDLWrapperType(self.location, self, promiseType) + retType = IDLWrapperType(self.location, self) - if identifier == "Constructor" or identifier == "ChromeConstructor": - name = "constructor" - allowForbidden = True - else: - name = attr.value() - allowForbidden = False - - methodIdentifier = IDLUnresolvedIdentifier(self.location, name, - allowForbidden=allowForbidden) - - method = IDLMethod(self.location, methodIdentifier, retType, - args, static=True) - # Constructors are always NewObject and are always - # assumed to be able to throw (since there's no way to - # indicate otherwise) and never have any other - # extended attributes. + method = IDLConstructor(attr.location, args, attr.value()) + method.reallyInit(self) + + # Named constructors are always assumed to be able to + # throw (since there's no way to indicate otherwise). method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("NewObject",)), - IDLExtendedAttribute(self.location, ("Throws",))]) - if identifier == "ChromeConstructor": - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("ChromeOnly",))]) - - if identifier == "Constructor" or identifier == "ChromeConstructor": - method.resolve(self) - else: - # We need to detect conflicts for NamedConstructors across - # interfaces. We first call resolve on the parentScope, - # which will merge all NamedConstructors with the same - # identifier accross interfaces as overloads. - method.resolve(self.parentScope) - - # Then we look up the identifier on the parentScope. If the - # result is the same as the method we're adding then it - # hasn't been added as an overload and it's the first time - # we've encountered a NamedConstructor with that identifier. - # If the result is not the same as the method we're adding - # then it has been added as an overload and we need to check - # whether the result is actually one of our existing - # NamedConstructors. - newMethod = self.parentScope.lookupIdentifier(method.identifier) - if newMethod == method: - self.namedConstructors.append(method) - elif newMethod not in self.namedConstructors: - raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface", - [method.location, newMethod.location]) - elif (identifier == "ArrayClass"): - if not attr.noArguments(): - raise WebIDLError("[ArrayClass] must take no arguments", - [attr.location]) - if self.parent: - raise WebIDLError("[ArrayClass] must not be specified on " - "an interface with inherited interfaces", - [attr.location, self.location]) + [IDLExtendedAttribute(self.location, ("Throws",))]) + + # We need to detect conflicts for NamedConstructors across + # interfaces. We first call resolve on the parentScope, + # which will merge all NamedConstructors with the same + # identifier accross interfaces as overloads. + method.resolve(self.parentScope) + + # Then we look up the identifier on the parentScope. If the + # result is the same as the method we're adding then it + # hasn't been added as an overload and it's the first time + # we've encountered a NamedConstructor with that identifier. + # If the result is not the same as the method we're adding + # then it has been added as an overload and we need to check + # whether the result is actually one of our existing + # NamedConstructors. + newMethod = self.parentScope.lookupIdentifier(method.identifier) + if newMethod == method: + self.namedConstructors.append(method) + elif newMethod not in self.namedConstructors: + raise WebIDLError("NamedConstructor conflicts with a " + "NamedConstructor of a different interface", + [method.location, newMethod.location]) elif (identifier == "ExceptionClass"): if not attr.noArguments(): raise WebIDLError("[ExceptionClass] must take no arguments", @@ -1656,24 +1702,21 @@ class IDLInterface(IDLInterfaceOrNamespace): self.globalNames = attr.args() else: self.globalNames = [self.identifier.name] - self.parentScope.globalNames.update(self.globalNames) - for globalName in self.globalNames: - self.parentScope.globalNameMapping[globalName].add(self.identifier.name) + self.parentScope.addIfaceGlobalNames(self.identifier.name, + self.globalNames) self._isOnGlobalProtoChain = True - elif identifier == "PrimaryGlobal": - if not attr.noArguments(): - raise WebIDLError("[PrimaryGlobal] must take no arguments", + elif identifier == "LegacyWindowAlias": + if attr.hasValue(): + self.legacyWindowAliases = [attr.value()] + elif attr.hasArgs(): + self.legacyWindowAliases = attr.args() + else: + raise WebIDLError("[%s] must either take an identifier " + "or take an identifier list" % identifier, [attr.location]) - if self.parentScope.primaryGlobalAttr is not None: - raise WebIDLError( - "[PrimaryGlobal] specified twice", - [attr.location, - self.parentScope.primaryGlobalAttr.location]) - self.parentScope.primaryGlobalAttr = attr - self.parentScope.primaryGlobalName = self.identifier.name - self.parentScope.globalNames.add(self.identifier.name) - self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name) - self._isOnGlobalProtoChain = True + for alias in self.legacyWindowAliases: + unresolved = IDLUnresolvedIdentifier(attr.location, alias) + IDLObjectWithIdentifier(attr.location, self.parentScope, unresolved) elif identifier == "SecureContext": if not attr.noArguments(): raise WebIDLError("[%s] must take no arguments" % identifier, @@ -1690,11 +1733,12 @@ class IDLInterface(IDLInterfaceOrNamespace): identifier == "OverrideBuiltins" or identifier == "ChromeOnly" or identifier == "Unforgeable" or - identifier == "UnsafeInPrerendering" or identifier == "LegacyEventInit" or - identifier == "ProbablyShortLivingObject" or + identifier == "ProbablyShortLivingWrapper" or identifier == "LegacyUnenumerableNamedProperties" or - identifier == "NonOrdinaryGetPrototypeOf" or + identifier == "RunConstructorInCallerCompartment" or + identifier == "WantsEventListenerHooks" or + identifier == "Serializable" or identifier == "Abstract" or identifier == "Inline"): # Known extended attributes that do not take values @@ -1707,13 +1751,17 @@ class IDLInterface(IDLInterfaceOrNamespace): elif (identifier == "Pref" or identifier == "JSImplementation" or identifier == "HeaderFile" or - identifier == "NavigatorProperty" or identifier == "Func" or identifier == "Deprecated"): # Known extended attributes that take a string value if not attr.hasValue(): raise WebIDLError("[%s] must have a value" % identifier, [attr.location]) + elif identifier == "InstrumentedProps": + # Known extended attributes that take a list + if not attr.hasArgs(): + raise WebIDLError("[%s] must have arguments" % identifier, + [attr.location]) else: raise WebIDLError("Unknown extended attribute %s on interface" % identifier, [attr.location]) @@ -1721,11 +1769,36 @@ class IDLInterface(IDLInterfaceOrNamespace): attrlist = attr.listValue() self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True + def validate(self): + IDLInterfaceOrNamespace.validate(self) + if self.parent and self.isSerializable() and not self.parent.isSerializable(): + raise WebIDLError( + "Serializable interface inherits from non-serializable " + "interface. Per spec, that means the object should not be " + "serializable, so chances are someone made a mistake here " + "somewhere.", + [self.location, self.parent.location]) + + def isSerializable(self): + return self.getExtendedAttribute("Serializable") + + def setNonPartial(self, location, parent, members): + # Before we do anything else, finish initializing any constructors that + # might be in "members", so we don't have partially-initialized objects + # hanging around. We couldn't do it before now because we needed to have + # to have the IDLInterface on hand to properly set the return type. + for member in members: + if isinstance(member, IDLConstructor): + member.reallyInit(self) + + IDLInterfaceOrNamespace.setNonPartial(self, location, parent, members) + class IDLNamespace(IDLInterfaceOrNamespace): def __init__(self, location, parentScope, name, members, isKnownNonPartial): IDLInterfaceOrNamespace.__init__(self, location, parentScope, name, - None, members, isKnownNonPartial) + None, members, isKnownNonPartial, + toStringTag=None) def __str__(self): return "Namespace '%s'" % self.identifier.name @@ -1750,10 +1823,18 @@ class IDLNamespace(IDLInterfaceOrNamespace): if not attr.hasValue(): raise WebIDLError("[%s] must have a value" % identifier, [attr.location]) - elif identifier == "ProtoObjectHack": + elif (identifier == "ProtoObjectHack" or + identifier == "ChromeOnly"): if not attr.noArguments(): raise WebIDLError("[%s] must not have arguments" % identifier, [attr.location]) + elif (identifier == "Pref" or + identifier == "HeaderFile" or + identifier == "Func"): + # Known extended attributes that take a string value + if not attr.hasValue(): + raise WebIDLError("[%s] must have a value" % identifier, + [attr.location]) else: raise WebIDLError("Unknown extended attribute %s on namespace" % identifier, @@ -1762,6 +1843,9 @@ class IDLNamespace(IDLInterfaceOrNamespace): attrlist = attr.listValue() self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True + def isSerializable(self): + return False + class IDLDictionary(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members): @@ -1772,6 +1856,10 @@ class IDLDictionary(IDLObjectWithScope): self.parent = parent self._finished = False self.members = list(members) + self._partialDictionaries = [] + self._extendedAttrDict = {} + self.needsConversionToJS = False + self.needsConversionFromJS = False IDLObjectWithScope.__init__(self, location, parentScope, name) @@ -1808,6 +1896,11 @@ class IDLDictionary(IDLObjectWithScope): # looking at them. self.parent.finish(scope) + # Now go ahead and merge in our partial dictionaries. + for partial in self._partialDictionaries: + partial.finish(scope) + self.members.extend(partial.members) + for member in self.members: member.resolve(self) if not member.isComplete(): @@ -1815,7 +1908,7 @@ class IDLDictionary(IDLObjectWithScope): assert member.type.isComplete() # Members of a dictionary are sorted in lexicographic order - self.members.sort(cmp=cmp, key=lambda x: x.identifier.name) + self.members.sort(key=lambda x: x.identifier.name) inheritedMembers = [] ancestor = self.parent @@ -1853,7 +1946,7 @@ class IDLDictionary(IDLObjectWithScope): if (memberType.nullable() or memberType.isSequence() or - memberType.isMozMap()): + memberType.isRecord()): return typeContainsDictionary(memberType.inner, dictionary) if memberType.isDictionary(): @@ -1900,8 +1993,34 @@ class IDLDictionary(IDLObjectWithScope): self.identifier.name, [member.location] + locations) + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) + def addExtendedAttributes(self, attrs): - assert len(attrs) == 0 + for attr in attrs: + identifier = attr.identifier() + + if (identifier == "GenerateInitFromJSON" or + identifier == "GenerateInit"): + if not attr.noArguments(): + raise WebIDLError("[%s] must not have arguments" % identifier, + [attr.location]) + self.needsConversionFromJS = True + elif (identifier == "GenerateConversionToJS" or + identifier == "GenerateToJSON"): + if not attr.noArguments(): + raise WebIDLError("[%s] must not have arguments" % identifier, + [attr.location]) + # ToJSON methods require to-JS conversion, because we + # implement ToJSON by converting to a JS object and + # then using JSON.stringify. + self.needsConversionToJS = True + else: + raise WebIDLError("[%s] extended attribute not allowed on " + "dictionaries" % identifier, + [attr.location]) + + self._extendedAttrDict[identifier] = True def _getDependentObjects(self): deps = set(self.members) @@ -1909,6 +2028,10 @@ class IDLDictionary(IDLObjectWithScope): deps.add(self.parent) return deps + def addPartialDictionary(self, partial): + assert self.identifier.name == partial.identifier.name + self._partialDictionaries.append(partial) + class IDLEnum(IDLObjectWithIdentifier): def __init__(self, location, parentScope, name, values): @@ -1935,7 +2058,10 @@ class IDLEnum(IDLObjectWithIdentifier): return True def addExtendedAttributes(self, attrs): - assert len(attrs) == 0 + if len(attrs) != 0: + raise WebIDLError("There are no extended attributes that are " + "allowed on enums", + [attrs[0].location, self.location]) def _getDependentObjects(self): return set() @@ -1964,8 +2090,9 @@ class IDLType(IDLObject): 'domstring', 'bytestring', 'usvstring', + 'utf8string', + 'jsstring', 'object', - 'date', 'void', # Funny stuff 'interface', @@ -1974,16 +2101,25 @@ class IDLType(IDLObject): 'callback', 'union', 'sequence', - 'mozmap' + 'record', + 'promise', ) def __init__(self, location, name): IDLObject.__init__(self, location) self.name = name self.builtin = False + self.treatNullAsEmpty = False + self._clamp = False + self._enforceRange = False + self._allowShared = False + self._extendedAttrDict = {} def __eq__(self, other): - return other and self.builtin == other.builtin and self.name == other.name + return (other and self.builtin == other.builtin and self.name == other.name and + self._clamp == other.hasClamp() and self._enforceRange == other.hasEnforceRange() and + self.treatNullAsEmpty == other.treatNullAsEmpty and + self._allowShared == other.hasAllowShared()) def __ne__(self, other): return not self == other @@ -1991,6 +2127,14 @@ class IDLType(IDLObject): def __str__(self): return str(self.name) + def prettyName(self): + """ + A name that looks like what this type is named in the IDL spec. By default + this is just our .name, but types that have more interesting spec + representations should override this. + """ + return str(self.name) + def isType(self): return True @@ -2018,27 +2162,36 @@ class IDLType(IDLObject): def isUSVString(self): return False + def isUTF8String(self): + return False + + def isJSString(self): + return False + def isVoid(self): return self.name == "Void" def isSequence(self): return False - def isMozMap(self): + def isRecord(self): return False - def isArrayBuffer(self): + def isReadableStream(self): return False - def isArrayBufferView(self): + def isArrayBuffer(self): return False - def isSharedArrayBuffer(self): + def isArrayBufferView(self): return False def isTypedArray(self): return False + def isBufferSource(self): + return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray() + def isCallbackInterface(self): return False @@ -2054,12 +2207,9 @@ class IDLType(IDLObject): def isSpiderMonkeyInterface(self): """ Returns a boolean indicating whether this type is an 'interface' - type that is implemented in Spidermonkey. At the moment, this - only returns true for the types from the TypedArray spec. """ - return self.isInterface() and (self.isArrayBuffer() or - self.isArrayBufferView() or - self.isSharedArrayBuffer() or - self.isTypedArray()) + type that is implemented in SpiderMonkey. """ + return self.isInterface() and (self.isBufferSource() or + self.isReadableStream()) def isDictionary(self): return False @@ -2070,9 +2220,6 @@ class IDLType(IDLObject): def isAny(self): return self.tag() == IDLType.Tags.any - def isDate(self): - return self.tag() == IDLType.Tags.date - def isObject(self): return self.tag() == IDLType.Tags.object @@ -2092,9 +2239,18 @@ class IDLType(IDLObject): # Should only call this on float types assert self.isFloat() - def isSerializable(self): + def isJSONType(self): return False + def hasClamp(self): + return self._clamp + + def hasEnforceRange(self): + return self._enforceRange + + def hasAllowShared(self): + return self._allowShared + def tag(self): assert False # Override me! @@ -2106,8 +2262,14 @@ class IDLType(IDLObject): assert self.tag() == IDLType.Tags.callback return self.nullable() and self.inner.callback._treatNonObjectAsNull - def addExtendedAttributes(self, attrs): - assert len(attrs) == 0 + def withExtendedAttributes(self, attrs): + if len(attrs) > 0: + raise WebIDLError("Extended attributes on types only supported for builtins", + [attrs[0].location, self.location]) + return self + + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) def resolveType(self, parentScope): pass @@ -2128,9 +2290,9 @@ class IDLUnresolvedType(IDLType): Unresolved types are interface types """ - def __init__(self, location, name, promiseInnerType=None): + def __init__(self, location, name, attrs=[]): IDLType.__init__(self, location, name) - self._promiseInnerType = promiseInnerType + self.extraTypeAttributes = attrs def isComplete(self): return False @@ -2145,30 +2307,30 @@ class IDLUnresolvedType(IDLType): assert obj if obj.isType(): - print obj + print(obj) assert not obj.isType() if obj.isTypedef(): assert self.name.name == obj.identifier.name typedefType = IDLTypedefType(self.location, obj.innerType, obj.identifier) assert not typedefType.isComplete() - return typedefType.complete(scope) + return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes) elif obj.isCallback() and not obj.isInterface(): assert self.name.name == obj.identifier.name return IDLCallbackType(obj.location, obj) - if self._promiseInnerType and not self._promiseInnerType.isComplete(): - self._promiseInnerType = self._promiseInnerType.complete(scope) - name = self.name.resolve(scope, None) - return IDLWrapperType(self.location, obj, self._promiseInnerType) + return IDLWrapperType(self.location, obj) + + def withExtendedAttributes(self, attrs): + return IDLUnresolvedType(self.location, self.name, attrs) def isDistinguishableFrom(self, other): raise TypeError("Can't tell whether an unresolved type is or is not " "distinguishable from other things") -class IDLParameterizedType(IDLType): +class IDLParametrizedType(IDLType): def __init__(self, location, name, innerType): IDLType.__init__(self, location, name) self.builtin = False @@ -2191,22 +2353,25 @@ class IDLParameterizedType(IDLType): return self.inner._getDependentObjects() -class IDLNullableType(IDLParameterizedType): +class IDLNullableType(IDLParametrizedType): def __init__(self, location, innerType): assert not innerType.isVoid() assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] - name = innerType.name - if innerType.isComplete(): - name += "OrNull" - IDLParameterizedType.__init__(self, location, name, innerType) + IDLParametrizedType.__init__(self, location, None, innerType) def __eq__(self, other): return isinstance(other, IDLNullableType) and self.inner == other.inner + def __hash__(self): + return hash(self.inner) + def __str__(self): return self.inner.__str__() + "OrNull" + def prettyName(self): + return self.inner.prettyName() + "?" + def nullable(self): return True @@ -2234,6 +2399,12 @@ class IDLNullableType(IDLParameterizedType): def isUSVString(self): return self.inner.isUSVString() + def isUTF8String(self): + return self.inner.isUTF8String() + + def isJSString(self): + return self.inner.isJSString() + def isFloat(self): return self.inner.isFloat() @@ -2249,8 +2420,11 @@ class IDLNullableType(IDLParameterizedType): def isSequence(self): return self.inner.isSequence() - def isMozMap(self): - return self.inner.isMozMap() + def isRecord(self): + return self.inner.isRecord() + + def isReadableStream(self): + return self.inner.isReadableStream() def isArrayBuffer(self): return self.inner.isArrayBuffer() @@ -2258,9 +2432,6 @@ class IDLNullableType(IDLParameterizedType): def isArrayBufferView(self): return self.inner.isArrayBufferView() - def isSharedArrayBuffer(self): - return self.inner.isSharedArrayBuffer() - def isTypedArray(self): return self.inner.isTypedArray() @@ -2271,7 +2442,9 @@ class IDLNullableType(IDLParameterizedType): return self.inner.isInterface() def isPromise(self): - return self.inner.isPromise() + # There is no such thing as a nullable Promise. + assert not self.inner.isPromise() + return False def isCallbackInterface(self): return self.inner.isCallbackInterface() @@ -2285,14 +2458,29 @@ class IDLNullableType(IDLParameterizedType): def isUnion(self): return self.inner.isUnion() - def isSerializable(self): - return self.inner.isSerializable() + def isJSONType(self): + return self.inner.isJSONType() + + def hasClamp(self): + return self.inner.hasClamp() + + def hasEnforceRange(self): + return self.inner.hasEnforceRange() + + def hasAllowShared(self): + return self.inner.hasAllowShared() + + def isComplete(self): + return self.name is not None def tag(self): return self.inner.tag() def complete(self, scope): - self.inner = self.inner.complete(scope) + if not self.inner.isComplete(): + self.inner = self.inner.complete(scope) + assert self.inner.isComplete() + if self.inner.nullable(): raise WebIDLError("The inner type of a nullable type must not be " "a nullable type", @@ -2302,23 +2490,36 @@ class IDLNullableType(IDLParameterizedType): raise WebIDLError("The inner type of a nullable type must not " "be a union type that itself has a nullable " "type as a member type", [self.location]) + if self.inner.isDOMString(): + if self.inner.treatNullAsEmpty: + raise WebIDLError("[TreatNullAs] not allowed on a nullable DOMString", + [self.location, self.inner.location]) self.name = self.inner.name + "OrNull" return self def isDistinguishableFrom(self, other): - if (other.nullable() or (other.isUnion() and other.hasNullableType) or - other.isDictionary()): + if (other.nullable() or + other.isDictionary() or + (other.isUnion() and + (other.hasNullableType or other.hasDictionaryType()))): # Can't tell which type null should become return False return self.inner.isDistinguishableFrom(other) + def withExtendedAttributes(self, attrs): + # See https://github.com/heycam/webidl/issues/827#issuecomment-565131350 + # Allowing extended attributes to apply to a nullable type is an intermediate solution. + # A potential longer term solution is to introduce a null type and get rid of nullables. + # For example, we could do `([Clamp] long or null) foo` in the future. + return IDLNullableType(self.location, self.inner.withExtendedAttributes(attrs)) -class IDLSequenceType(IDLParameterizedType): + +class IDLSequenceType(IDLParametrizedType): def __init__(self, location, parameterType): assert not parameterType.isVoid() - IDLParameterizedType.__init__(self, location, parameterType.name, parameterType) + IDLParametrizedType.__init__(self, location, parameterType.name, parameterType) # Need to set self.name up front if our inner type is already complete, # since in that case our .complete() won't be called. if self.inner.isComplete(): @@ -2327,9 +2528,15 @@ class IDLSequenceType(IDLParameterizedType): def __eq__(self, other): return isinstance(other, IDLSequenceType) and self.inner == other.inner + def __hash__(self): + return hash(self.inner) + def __str__(self): return self.inner.__str__() + "Sequence" + def prettyName(self): + return "sequence<%s>" % self.inner.prettyName() + def nullable(self): return False @@ -2348,6 +2555,12 @@ class IDLSequenceType(IDLParameterizedType): def isUSVString(self): return False + def isUTF8String(self): + return False + + def isJSString(self): + return False + def isVoid(self): return False @@ -2363,8 +2576,8 @@ class IDLSequenceType(IDLParameterizedType): def isEnum(self): return False - def isSerializable(self): - return self.inner.isSerializable() + def isJSONType(self): + return self.inner.isJSONType() def tag(self): return IDLType.Tags.sequence @@ -2381,36 +2594,45 @@ class IDLSequenceType(IDLParameterizedType): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isDate() or other.isInterface() or - other.isDictionary() or - other.isCallback() or other.isMozMap()) + other.isInterface() or other.isDictionary() or + other.isCallback() or other.isRecord()) -class IDLMozMapType(IDLParameterizedType): - def __init__(self, location, parameterType): - assert not parameterType.isVoid() +class IDLRecordType(IDLParametrizedType): + def __init__(self, location, keyType, valueType): + assert keyType.isString() + assert keyType.isComplete() + assert not valueType.isVoid() + + IDLParametrizedType.__init__(self, location, valueType.name, valueType) + self.keyType = keyType - IDLParameterizedType.__init__(self, location, parameterType.name, parameterType) # Need to set self.name up front if our inner type is already complete, # since in that case our .complete() won't be called. if self.inner.isComplete(): - self.name = self.inner.name + "MozMap" + self.name = self.keyType.name + self.inner.name + "Record" def __eq__(self, other): - return isinstance(other, IDLMozMapType) and self.inner == other.inner + return isinstance(other, IDLRecordType) and self.inner == other.inner def __str__(self): - return self.inner.__str__() + "MozMap" + return self.keyType.__str__() + self.inner.__str__() + "Record" - def isMozMap(self): + def prettyName(self): + return "record<%s, %s>" % (self.keyType.prettyName(), self.inner.prettyName()) + + def isRecord(self): return True + def isJSONType(self): + return self.inner.isJSONType() + def tag(self): - return IDLType.Tags.mozmap + return IDLType.Tags.record def complete(self, scope): self.inner = self.inner.complete(scope) - self.name = self.inner.name + "MozMap" + self.name = self.keyType.name + self.inner.name + "Record" return self def unroll(self): @@ -2426,7 +2648,7 @@ class IDLMozMapType(IDLParameterizedType): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isDate() or other.isNonCallbackInterface() or other.isSequence()) + other.isNonCallbackInterface() or other.isSequence()) def isExposedInAllOf(self, exposureSet): return self.inner.unroll().isExposedInAllOf(exposureSet) @@ -2448,14 +2670,17 @@ class IDLUnionType(IDLType): assert self.isComplete() return self.name.__hash__() + def prettyName(self): + return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")" + def isVoid(self): return False def isUnion(self): return True - def isSerializable(self): - return all(m.isSerializable() for m in self.memberTypes) + def isJSONType(self): + return all(m.isJSONType() for m in self.memberTypes) def includesRestrictedFloat(self): return any(t.includesRestrictedFloat() for t in self.memberTypes) @@ -2479,6 +2704,9 @@ class IDLUnionType(IDLType): return typeName(type._identifier.object()) if isinstance(type, IDLObjectWithIdentifier): return typeName(type.identifier) + if isinstance(type, IDLBuiltinType) and type.hasAllowShared(): + assert type.isBufferSource() + return "MaybeShared" + type.name return type.name for (i, type) in enumerate(self.memberTypes): @@ -2602,14 +2830,26 @@ class IDLTypedefType(IDLType): def isUSVString(self): return self.inner.isUSVString() + def isUTF8String(self): + return self.inner.isUTF8String() + + def isJSString(self): + return self.inner.isJSString() + def isVoid(self): return self.inner.isVoid() + def isJSONType(self): + return self.inner.isJSONType() + def isSequence(self): return self.inner.isSequence() - def isMozMap(self): - return self.inner.isMozMap() + def isRecord(self): + return self.inner.isRecord() + + def isReadableStream(self): + return self.inner.isReadableStream() def isDictionary(self): return self.inner.isDictionary() @@ -2620,9 +2860,6 @@ class IDLTypedefType(IDLType): def isArrayBufferView(self): return self.inner.isArrayBufferView() - def isSharedArrayBuffer(self): - return self.inner.isSharedArrayBuffer() - def isTypedArray(self): return self.inner.isTypedArray() @@ -2658,12 +2895,17 @@ class IDLTypedefType(IDLType): def _getDependentObjects(self): return self.inner._getDependentObjects() + def withExtendedAttributes(self, attrs): + return IDLTypedefType(self.location, self.inner.withExtendedAttributes(attrs), self.name) + class IDLTypedef(IDLObjectWithIdentifier): def __init__(self, location, parentScope, innerType, name): + # Set self.innerType first, because IDLObjectWithIdentifier.__init__ + # will call our __str__, which wants to use it. + self.innerType = innerType identifier = IDLUnresolvedIdentifier(location, name) IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) - self.innerType = innerType def __str__(self): return "Typedef %s %s" % (self.identifier.name, self.innerType) @@ -2679,26 +2921,30 @@ class IDLTypedef(IDLObjectWithIdentifier): return True def addExtendedAttributes(self, attrs): - assert len(attrs) == 0 + if len(attrs) != 0: + raise WebIDLError("There are no extended attributes that are " + "allowed on typedefs", + [attrs[0].location, self.location]) def _getDependentObjects(self): return self.innerType._getDependentObjects() class IDLWrapperType(IDLType): - def __init__(self, location, inner, promiseInnerType=None): + def __init__(self, location, inner): IDLType.__init__(self, location, inner.identifier.name) self.inner = inner self._identifier = inner.identifier self.builtin = False - assert not promiseInnerType or inner.identifier.name == "Promise" - self._promiseInnerType = promiseInnerType def __eq__(self, other): return (isinstance(other, IDLWrapperType) and self._identifier == other._identifier and self.builtin == other.builtin) + def __hash__(self): + return hash((self._identifier, self.builtin)) + def __str__(self): return str(self.name) + " (Wrapper)" @@ -2720,6 +2966,12 @@ class IDLWrapperType(IDLType): def isUSVString(self): return False + def isUTF8String(self): + return False + + def isJSString(self): + return False + def isVoid(self): return False @@ -2742,23 +2994,25 @@ class IDLWrapperType(IDLType): def isEnum(self): return isinstance(self.inner, IDLEnum) - def isPromise(self): - return (isinstance(self.inner, IDLInterface) and - self.inner.identifier.name == "Promise") - - def promiseInnerType(self): - assert self.isPromise() - return self._promiseInnerType - - def isSerializable(self): + def isJSONType(self): if self.isInterface(): if self.inner.isExternal(): return False - return any(m.isMethod() and m.isJsonifier() for m in self.inner.members) + iface = self.inner + while iface: + if any(m.isMethod() and m.isToJSON() for m in iface.members): + return True + iface = iface.parent + return False elif self.isEnum(): return True elif self.isDictionary(): - return all(m.type.isSerializable() for m in self.inner.members) + dictionary = self.inner + while dictionary: + if not all(m.type.isJSONType() for m in dictionary.members): + return False + dictionary = dictionary.parent + return True else: raise WebIDLError("IDLWrapperType wraps type %s that we don't know if " "is serializable" % type(self.inner), [self.location]) @@ -2781,8 +3035,6 @@ class IDLWrapperType(IDLType): assert False def isDistinguishableFrom(self, other): - if self.isPromise(): - return False if other.isPromise(): return False if other.isUnion(): @@ -2792,11 +3044,11 @@ class IDLWrapperType(IDLType): if self.isEnum(): return (other.isPrimitive() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isMozMap() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isDictionary() and other.nullable(): return False if (other.isPrimitive() or other.isString() or other.isEnum() or - other.isDate() or other.isSequence()): + other.isSequence()): return True if self.isDictionary(): return other.isNonCallbackInterface() @@ -2814,7 +3066,7 @@ class IDLWrapperType(IDLType): (self.isNonCallbackInterface() or other.isNonCallbackInterface())) if (other.isDictionary() or other.isCallback() or - other.isMozMap()): + other.isRecord()): return self.isNonCallbackInterface() # Not much else |other| can be @@ -2826,13 +3078,10 @@ class IDLWrapperType(IDLType): return True iface = self.inner if iface.isExternal(): - # Let's say true, though ideally we'd only do this when - # exposureSet contains the primary global's name. + # Let's say true, so we don't have to implement exposure mixins on + # external interfaces and sprinkle [Exposed=Window] on every single + # external interface declaration. return True - if (self.isPromise() and - # Check the internal type - not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)): - return False return iface.exposureSet.issuperset(exposureSet) def _getDependentObjects(self): @@ -2860,6 +3109,48 @@ class IDLWrapperType(IDLType): return set() +class IDLPromiseType(IDLParametrizedType): + def __init__(self, location, innerType): + IDLParametrizedType.__init__(self, location, "Promise", innerType) + + def __eq__(self, other): + return (isinstance(other, IDLPromiseType) and + self.promiseInnerType() == other.promiseInnerType()) + + def __str__(self): + return self.inner.__str__() + "Promise" + + def prettyName(self): + return "Promise<%s>" % self.inner.prettyName() + + def isPromise(self): + return True + + def promiseInnerType(self): + return self.inner + + def tag(self): + return IDLType.Tags.promise + + def complete(self, scope): + self.inner = self.promiseInnerType().complete(scope) + return self + + def unroll(self): + # We do not unroll our inner. Just stop at ourselves. That + # lets us add headers for both ourselves and our inner as + # needed. + return self + + def isDistinguishableFrom(self, other): + # Promises are not distinguishable from anything. + return False + + def isExposedInAllOf(self, exposureSet): + # Check the internal type + return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet) + + class IDLBuiltinType(IDLType): Types = enum( @@ -2884,13 +3175,13 @@ class IDLBuiltinType(IDLType): 'domstring', 'bytestring', 'usvstring', + 'utf8string', + 'jsstring', 'object', - 'date', 'void', # Funny stuff 'ArrayBuffer', 'ArrayBufferView', - 'SharedArrayBuffer', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', @@ -2899,7 +3190,8 @@ class IDLBuiltinType(IDLType): 'Int32Array', 'Uint32Array', 'Float32Array', - 'Float64Array' + 'Float64Array', + 'ReadableStream', ) TagLookup = { @@ -2920,12 +3212,12 @@ class IDLBuiltinType(IDLType): Types.domstring: IDLType.Tags.domstring, Types.bytestring: IDLType.Tags.bytestring, Types.usvstring: IDLType.Tags.usvstring, + Types.utf8string: IDLType.Tags.utf8string, + Types.jsstring: IDLType.Tags.jsstring, Types.object: IDLType.Tags.object, - Types.date: IDLType.Tags.date, Types.void: IDLType.Tags.void, Types.ArrayBuffer: IDLType.Tags.interface, Types.ArrayBufferView: IDLType.Tags.interface, - Types.SharedArrayBuffer: IDLType.Tags.interface, Types.Int8Array: IDLType.Tags.interface, Types.Uint8Array: IDLType.Tags.interface, Types.Uint8ClampedArray: IDLType.Tags.interface, @@ -2934,13 +3226,129 @@ class IDLBuiltinType(IDLType): Types.Int32Array: IDLType.Tags.interface, Types.Uint32Array: IDLType.Tags.interface, Types.Float32Array: IDLType.Tags.interface, - Types.Float64Array: IDLType.Tags.interface + Types.Float64Array: IDLType.Tags.interface, + Types.ReadableStream: IDLType.Tags.interface, + } + + PrettyNames = { + Types.byte: "byte", + Types.octet: "octet", + Types.short: "short", + Types.unsigned_short: "unsigned short", + Types.long: "long", + Types.unsigned_long: "unsigned long", + Types.long_long: "long long", + Types.unsigned_long_long: "unsigned long long", + Types.boolean: "boolean", + Types.unrestricted_float: "unrestricted float", + Types.float: "float", + Types.unrestricted_double: "unrestricted double", + Types.double: "double", + Types.any: "any", + Types.domstring: "DOMString", + Types.bytestring: "ByteString", + Types.usvstring: "USVString", + Types.utf8string: "USVString", # That's what it is in spec terms + Types.jsstring: "USVString", # Again, that's what it is in spec terms + Types.object: "object", + Types.void: "void", + Types.ArrayBuffer: "ArrayBuffer", + Types.ArrayBufferView: "ArrayBufferView", + Types.Int8Array: "Int8Array", + Types.Uint8Array: "Uint8Array", + Types.Uint8ClampedArray: "Uint8ClampedArray", + Types.Int16Array: "Int16Array", + Types.Uint16Array: "Uint16Array", + Types.Int32Array: "Int32Array", + Types.Uint32Array: "Uint32Array", + Types.Float32Array: "Float32Array", + Types.Float64Array: "Float64Array", + Types.ReadableStream: "ReadableStream", } - def __init__(self, location, name, type): + def __init__(self, location, name, type, clamp=False, enforceRange=False, treatNullAsEmpty=False, + allowShared=False, attrLocation=[]): + """ + The mutually exclusive clamp/enforceRange/treatNullAsEmpty/allowShared arguments are used + to create instances of this type with the appropriate attributes attached. Use .clamped(), + .rangeEnforced(), .withTreatNullAs() and .withAllowShared(). + + attrLocation is an array of source locations of these attributes for error reporting. + """ IDLType.__init__(self, location, name) self.builtin = True self._typeTag = type + self._clamped = None + self._rangeEnforced = None + self._withTreatNullAs = None + self._withAllowShared = None; + if self.isInteger(): + if clamp: + self._clamp = True + self.name = "Clamped" + self.name + self._extendedAttrDict["Clamp"] = True + elif enforceRange: + self._enforceRange = True + self.name = "RangeEnforced" + self.name + self._extendedAttrDict["EnforceRange"] = True + elif clamp or enforceRange: + raise WebIDLError("Non-integer types cannot be [Clamp] or [EnforceRange]", attrLocation) + if self.isDOMString() or self.isUTF8String(): + if treatNullAsEmpty: + self.treatNullAsEmpty = True + self.name = "NullIsEmpty" + self.name + self._extendedAttrDict["TreatNullAs"] = ["EmptyString"] + elif treatNullAsEmpty: + raise WebIDLError("Non-string types cannot be [TreatNullAs]", attrLocation) + if self.isBufferSource(): + if allowShared: + self._allowShared = True + self._extendedAttrDict["AllowShared"] = True + elif allowShared: + raise WebIDLError("Types that are not buffer source types cannot be [AllowShared]", attrLocation) + + def __str__(self): + if self._allowShared: + assert self.isBufferSource() + return "MaybeShared" + str(self.name) + return str(self.name) + + def __eq__(self, other): + return other and self.location == other.location and self.name == other.name and self._typeTag == other._typeTag + + def __hash__(self): + return hash((self.location, self.name, self._typeTag)) + + def prettyName(self): + return IDLBuiltinType.PrettyNames[self._typeTag] + + def clamped(self, attrLocation): + if not self._clamped: + self._clamped = IDLBuiltinType(self.location, self.name, + self._typeTag, clamp=True, + attrLocation=attrLocation) + return self._clamped + + def rangeEnforced(self, attrLocation): + if not self._rangeEnforced: + self._rangeEnforced = IDLBuiltinType(self.location, self.name, + self._typeTag, enforceRange=True, + attrLocation=attrLocation) + return self._rangeEnforced + + def withTreatNullAs(self, attrLocation): + if not self._withTreatNullAs: + self._withTreatNullAs = IDLBuiltinType(self.location, self.name, + self._typeTag, treatNullAsEmpty=True, + attrLocation=attrLocation) + return self._withTreatNullAs + + def withAllowShared(self, attrLocation): + if not self._withAllowShared: + self._withAllowShared = IDLBuiltinType(self.location, self.name, + self._typeTag, allowShared=True, + attrLocation=attrLocation) + return self._withAllowShared def isPrimitive(self): return self._typeTag <= IDLBuiltinType.Types.double @@ -2954,7 +3362,9 @@ class IDLBuiltinType(IDLType): def isString(self): return (self._typeTag == IDLBuiltinType.Types.domstring or self._typeTag == IDLBuiltinType.Types.bytestring or - self._typeTag == IDLBuiltinType.Types.usvstring) + self._typeTag == IDLBuiltinType.Types.usvstring or + self._typeTag == IDLBuiltinType.Types.utf8string or + self._typeTag == IDLBuiltinType.Types.jsstring) def isByteString(self): return self._typeTag == IDLBuiltinType.Types.bytestring @@ -2965,6 +3375,12 @@ class IDLBuiltinType(IDLType): def isUSVString(self): return self._typeTag == IDLBuiltinType.Types.usvstring + def isUTF8String(self): + return self._typeTag == IDLBuiltinType.Types.utf8string + + def isJSString(self): + return self._typeTag == IDLBuiltinType.Types.jsstring + def isInteger(self): return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long @@ -2974,21 +3390,21 @@ class IDLBuiltinType(IDLType): def isArrayBufferView(self): return self._typeTag == IDLBuiltinType.Types.ArrayBufferView - def isSharedArrayBuffer(self): - return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer - def isTypedArray(self): return (self._typeTag >= IDLBuiltinType.Types.Int8Array and self._typeTag <= IDLBuiltinType.Types.Float64Array) + def isReadableStream(self): + return self._typeTag == IDLBuiltinType.Types.ReadableStream + def isInterface(self): # TypedArray things are interface types per the TypedArray spec, # but we handle them as builtins because SpiderMonkey implements # all of it internally. return (self.isArrayBuffer() or self.isArrayBufferView() or - self.isSharedArrayBuffer() or - self.isTypedArray()) + self.isTypedArray() or + self.isReadableStream()) def isNonCallbackInterface(self): # All the interfaces we can be are non-callback @@ -3005,8 +3421,8 @@ class IDLBuiltinType(IDLType): return (self._typeTag == IDLBuiltinType.Types.unrestricted_float or self._typeTag == IDLBuiltinType.Types.unrestricted_double) - def isSerializable(self): - return self.isPrimitive() or self.isString() or self.isDate() + def isJSONType(self): + return self.isPrimitive() or self.isString() or self.isObject() def includesRestrictedFloat(self): return self.isFloat() and not self.isUnrestricted() @@ -3024,27 +3440,22 @@ class IDLBuiltinType(IDLType): return (other.isNumeric() or other.isString() or other.isEnum() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isMozMap() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isNumeric(): return (other.isBoolean() or other.isString() or other.isEnum() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isMozMap() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isString(): return (other.isPrimitive() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isMozMap() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isAny(): # Can't tell "any" apart from anything return False if self.isObject(): return other.isPrimitive() or other.isString() or other.isEnum() - if self.isDate(): - return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isInterface() or other.isCallback() or - other.isDictionary() or other.isSequence() or - other.isMozMap()) if self.isVoid(): return not other.isVoid() # Not much else we could be! @@ -3052,12 +3463,12 @@ class IDLBuiltinType(IDLType): # Like interfaces, but we know we're not a callback return (other.isPrimitive() or other.isString() or other.isEnum() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isMozMap() or other.isDate() or + other.isSequence() or other.isRecord() or (other.isInterface() and ( # ArrayBuffer is distinguishable from everything # that's not an ArrayBuffer or a callback interface (self.isArrayBuffer() and not other.isArrayBuffer()) or - (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or + (self.isReadableStream() and not other.isReadableStream()) or # ArrayBufferView is distinguishable from everything # that's not an ArrayBufferView or typed array. (self.isArrayBufferView() and not other.isArrayBufferView() and @@ -3071,6 +3482,54 @@ class IDLBuiltinType(IDLType): def _getDependentObjects(self): return set() + def withExtendedAttributes(self, attrs): + ret = self + for attribute in attrs: + identifier = attribute.identifier() + if identifier == "Clamp": + if not attribute.noArguments(): + raise WebIDLError("[Clamp] must take no arguments", + [attribute.location]) + if ret.hasEnforceRange() or self._enforceRange: + raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", + [self.location, attribute.location]) + ret = self.clamped([self.location, attribute.location]) + elif identifier == "EnforceRange": + if not attribute.noArguments(): + raise WebIDLError("[EnforceRange] must take no arguments", + [attribute.location]) + if ret.hasClamp() or self._clamp: + raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", + [self.location, attribute.location]) + ret = self.rangeEnforced([self.location, attribute.location]) + elif identifier == "TreatNullAs": + if not (self.isDOMString() or self.isUTF8String()): + raise WebIDLError("[TreatNullAs] only allowed on DOMStrings and UTF8Strings", + [self.location, attribute.location]) + assert not self.nullable() + if not attribute.hasValue(): + raise WebIDLError("[TreatNullAs] must take an identifier argument", + [attribute.location]) + value = attribute.value() + if value != 'EmptyString': + raise WebIDLError("[TreatNullAs] must take the identifier " + "'EmptyString', not '%s'" % value, + [attribute.location]) + ret = self.withTreatNullAs([self.location, attribute.location]) + elif identifier == "AllowShared": + if not attribute.noArguments(): + raise WebIDLError("[AllowShared] must take no arguments", + [attribute.location]) + if not self.isBufferSource(): + raise WebIDLError("[AllowShared] only allowed on buffer source types", + [self.location, attribute.location]) + ret = self.withAllowShared([self.location, attribute.location]) + + else: + raise WebIDLError("Unhandled extended attribute on type", + [self.location, attribute.location]) + return ret + BuiltinTypes = { IDLBuiltinType.Types.byte: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte", @@ -3123,12 +3582,15 @@ BuiltinTypes = { IDLBuiltinType.Types.usvstring: IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString", IDLBuiltinType.Types.usvstring), + IDLBuiltinType.Types.utf8string: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "UTF8String", + IDLBuiltinType.Types.utf8string), + IDLBuiltinType.Types.jsstring: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "JSString", + IDLBuiltinType.Types.jsstring), IDLBuiltinType.Types.object: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object", IDLBuiltinType.Types.object), - IDLBuiltinType.Types.date: - IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date", - IDLBuiltinType.Types.date), IDLBuiltinType.Types.void: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void", IDLBuiltinType.Types.void), @@ -3138,9 +3600,6 @@ BuiltinTypes = { IDLBuiltinType.Types.ArrayBufferView: IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView", IDLBuiltinType.Types.ArrayBufferView), - IDLBuiltinType.Types.SharedArrayBuffer: - IDLBuiltinType(BuiltinLocation("<builtin type>"), "SharedArrayBuffer", - IDLBuiltinType.Types.SharedArrayBuffer), IDLBuiltinType.Types.Int8Array: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array", IDLBuiltinType.Types.Int8Array), @@ -3167,7 +3626,10 @@ BuiltinTypes = { IDLBuiltinType.Types.Float32Array), IDLBuiltinType.Types.Float64Array: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array", - IDLBuiltinType.Types.Float64Array) + IDLBuiltinType.Types.Float64Array), + IDLBuiltinType.Types.ReadableStream: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "ReadableStream", + IDLBuiltinType.Types.ReadableStream), } @@ -3184,7 +3646,7 @@ integerTypeSizes = { def matchIntegerValueToType(value): - for type, extremes in integerTypeSizes.items(): + for type, extremes in list(integerTypeSizes.items()): (min, max) = extremes if value <= max and value >= min: return BuiltinTypes[type] @@ -3263,7 +3725,7 @@ class IDLValue(IDLObject): elif self.type.isString() and type.isEnum(): # Just keep our string, but make sure it's a valid value for this enum enum = type.unroll().inner - if self.value not in enum.values(): + if self.value not in list(enum.values()): raise WebIDLError("'%s' is not a valid default value for enum %s" % (self.value, enum.identifier.name), [location, enum.location]) @@ -3282,8 +3744,13 @@ class IDLValue(IDLObject): # extra normalization step. assert self.type.isDOMString() return self - elif self.type.isString() and type.isByteString(): - # Allow ByteStrings to use a default value like DOMString. + elif self.type.isDOMString() and type.treatNullAsEmpty: + # TreatNullAsEmpty is a different type for resolution reasons, + # however once you have a value it doesn't matter + return self + elif self.type.isString() and (type.isByteString() or type.isJSString() or type.isUTF8String()): + # Allow ByteStrings, UTF8String, and JSStrings to use a default + # value like DOMString. # No coercion is required as Codegen.py will handle the # extra steps. We want to make sure that our string contains # only valid characters, so we check that here. @@ -3312,8 +3779,6 @@ class IDLNullValue(IDLObject): def coerceToType(self, type, location): if (not isinstance(type, IDLNullableType) and not (type.isUnion() and type.hasNullableType) and - not (type.isUnion() and type.hasDictionaryType()) and - not type.isDictionary() and not type.isAny()): raise WebIDLError("Cannot coerce null value to type %s." % type, [location]) @@ -3362,6 +3827,35 @@ class IDLEmptySequenceValue(IDLObject): return set() +class IDLDefaultDictionaryValue(IDLObject): + def __init__(self, location): + IDLObject.__init__(self, location) + self.type = None + self.value = None + + def coerceToType(self, type, location): + if type.isUnion(): + # We use the flat member types here, because if we have a nullable + # member type, or a nested union, we want the type the value + # actually coerces to, not the nullable or nested union type. + for subtype in type.unroll().flatMemberTypes: + try: + return self.coerceToType(subtype, location) + except: + pass + + if not type.isDictionary(): + raise WebIDLError("Cannot coerce default dictionary value to type %s." % type, + [location]) + + defaultDictionaryValue = IDLDefaultDictionaryValue(self.location) + defaultDictionaryValue.type = type + return defaultDictionaryValue + + def _getDependentObjects(self): + return set() + + class IDLUndefinedValue(IDLObject): def __init__(self, location): IDLObject.__init__(self, location) @@ -3437,10 +3931,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): return self._extendedAttrDict.get(name, None) def finish(self, scope): - # We better be exposed _somewhere_. - if (len(self._exposureGlobalNames) == 0): - print self.identifier.name - assert len(self._exposureGlobalNames) != 0 IDLExposureMixins.finish(self, scope) def validate(self): @@ -3456,6 +3946,14 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): raise WebIDLError("A [NewObject] method is not idempotent, " "so it has to depend on something other than DOM state.", [self.location]) + if (self.getExtendedAttribute("Cached") or + self.getExtendedAttribute("StoreInSlot")): + raise WebIDLError("A [NewObject] attribute shouldnt be " + "[Cached] or [StoreInSlot], since the point " + "of those is to keep returning the same " + "thing across multiple calls, which is not " + "what [NewObject] does.", + [self.location]) def _setDependsOn(self, dependsOn): if self.dependsOn != "Everything": @@ -3483,6 +3981,11 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): [self.location]) self.aliases.append(alias) + def _addBindingAlias(self, bindingAlias): + if bindingAlias in self.bindingAliases: + raise WebIDLError("Duplicate [BindingAlias=%s] on attribute" % bindingAlias, + [self.location]) + self.bindingAliases.append(bindingAlias) class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): @@ -3527,8 +4030,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): (member.identifier.name, self.maplikeOrSetlikeOrIterableType), [self.location, member.location]) - # Check that there are no disallowed non-method members - if (isAncestor or (member.isAttr() or member.isConst()) and + # Check that there are no disallowed non-method members. + # Ancestor members are always disallowed here; own members + # are disallowed only if they're non-methods. + if ((isAncestor or member.isAttr() or member.isConst()) and member.identifier.name in self.disallowedNonMethodNames): raise WebIDLError("Member '%s' conflicts " "with reserved %s method." % @@ -3601,6 +4106,11 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): if isIteratorAlias: method.addExtendedAttributes( [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))]) + # Methods generated for iterables should be enumerable, but the ones for + # maplike/setlike should not be. + if not self.isIterable(): + method.addExtendedAttributes( + [IDLExtendedAttribute(self.location, ("NonEnumerable",))]) members.append(method) def resolve(self, parentScope): @@ -3724,12 +4234,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase): specification during parsing. """ # Both maplike and setlike have a size attribute - members.append(IDLAttribute(self.location, - IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"), - BuiltinTypes[IDLBuiltinType.Types.unsigned_long], - True, - maplikeOrSetlike=self)) + sizeAttr = IDLAttribute(self.location, + IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"), + BuiltinTypes[IDLBuiltinType.Types.unsigned_long], + True, + maplikeOrSetlike=self) + # This should be non-enumerable. + sizeAttr.addExtendedAttributes( + [IDLExtendedAttribute(self.location, ("NonEnumerable",))]) + members.append(sizeAttr) self.reserved_ro_names = ["size"] + self.disallowedMemberNames.append("size") # object entries() self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object], @@ -3820,6 +4335,9 @@ class IDLConst(IDLInterfaceMember): if type.isDictionary(): raise WebIDLError("A constant cannot be of a dictionary type", [self.location]) + if type.isRecord(): + raise WebIDLError("A constant cannot be of a record type", + [self.location]) self.type = type self.value = value @@ -3860,7 +4378,9 @@ class IDLConst(IDLInterfaceMember): elif (identifier == "Pref" or identifier == "ChromeOnly" or identifier == "Func" or - identifier == "SecureContext"): + identifier == "SecureContext" or + identifier == "NonEnumerable" or + identifier == "NeedsWindowsUndef"): # Known attributes that we don't need to do anything with here pass else: @@ -3875,7 +4395,7 @@ class IDLConst(IDLInterfaceMember): class IDLAttribute(IDLInterfaceMember): def __init__(self, location, identifier, type, readonly, inherit=False, static=False, stringifier=False, maplikeOrSetlike=None, - extendedAttrDict=None, navigatorObjectGetter=False): + extendedAttrDict=None): IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.Tags.Attr, extendedAttrDict=extendedAttrDict) @@ -3888,14 +4408,12 @@ class IDLAttribute(IDLInterfaceMember): self.lenientThis = False self._unforgeable = False self.stringifier = stringifier - self.enforceRange = False - self.clamp = False self.slotIndices = None assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike) self.maplikeOrSetlike = maplikeOrSetlike self.dependsOn = "Everything" self.affects = "Everything" - self.navigatorObjectGetter = navigatorObjectGetter + self.bindingAliases = [] if static and identifier.name == "prototype": raise WebIDLError("The identifier of a static attribute must not be 'prototype'", @@ -3925,14 +4443,18 @@ class IDLAttribute(IDLInterfaceMember): assert not isinstance(t.name, IDLUnresolvedIdentifier) self.type = t + if self.readonly and (self.type.hasClamp() or self.type.hasEnforceRange() or + self.type.hasAllowShared() or self.type.treatNullAsEmpty): + raise WebIDLError("A readonly attribute cannot be [Clamp] or [EnforceRange] or [AllowShared]", + [self.location]) if self.type.isDictionary() and not self.getExtendedAttribute("Cached"): raise WebIDLError("An attribute cannot be of a dictionary type", [self.location]) if self.type.isSequence() and not self.getExtendedAttribute("Cached"): raise WebIDLError("A non-cached attribute cannot be of a sequence " "type", [self.location]) - if self.type.isMozMap() and not self.getExtendedAttribute("Cached"): - raise WebIDLError("A non-cached attribute cannot be of a MozMap " + if self.type.isRecord() and not self.getExtendedAttribute("Cached"): + raise WebIDLError("A non-cached attribute cannot be of a record " "type", [self.location]) if self.type.isUnion(): for f in self.type.unroll().flatMemberTypes: @@ -3948,25 +4470,30 @@ class IDLAttribute(IDLInterfaceMember): "one of its member types's member " "types, and so on) is a sequence " "type", [self.location, f.location]) - if f.isMozMap(): + if f.isRecord(): raise WebIDLError("An attribute cannot be of a union " "type if one of its member types (or " "one of its member types's member " - "types, and so on) is a MozMap " + "types, and so on) is a record " "type", [self.location, f.location]) if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"): raise WebIDLError("An attribute with [PutForwards] must have an " "interface type as its type", [self.location]) - if not self.type.isInterface() and self.getExtendedAttribute("SameObject"): + if (not self.type.isInterface() and + self.getExtendedAttribute("SameObject")): raise WebIDLError("An attribute with [SameObject] must have an " "interface type as its type", [self.location]) + if self.type.isPromise() and not self.readonly: + raise WebIDLError("Promise-returning attributes must be readonly", + [self.location]) + def validate(self): def typeContainsChromeOnlyDictionaryMember(type): if (type.nullable() or type.isSequence() or - type.isMozMap()): + type.isRecord()): return typeContainsChromeOnlyDictionaryMember(type.inner) if type.isUnion(): @@ -4012,27 +4539,42 @@ class IDLAttribute(IDLInterfaceMember): [self.location, location]) if self.getExtendedAttribute("Frozen"): if (not self.type.isSequence() and not self.type.isDictionary() and - not self.type.isMozMap()): + not self.type.isRecord()): raise WebIDLError("[Frozen] is only allowed on " "sequence-valued, dictionary-valued, and " - "MozMap-valued attributes", + "record-valued attributes", [self.location]) if not self.type.unroll().isExposedInAllOf(self.exposureSet): raise WebIDLError("Attribute returns a type that is not exposed " "everywhere where the attribute is exposed", [self.location]) + if self.getExtendedAttribute("CEReactions"): + if self.readonly: + raise WebIDLError("[CEReactions] is not allowed on " + "readonly attributes", + [self.location]) def handleExtendedAttribute(self, attr): identifier = attr.identifier() - if identifier == "SetterThrows" and self.readonly: + if ((identifier == "SetterThrows" or identifier == "SetterCanOOM" or + identifier == "SetterNeedsSubjectPrincipal") + and self.readonly): raise WebIDLError("Readonly attributes must not be flagged as " - "[SetterThrows]", + "[%s]" % identifier, [self.location]) - elif (((identifier == "Throws" or identifier == "GetterThrows") and + elif identifier == "BindingAlias": + if not attr.hasValue(): + raise WebIDLError("[BindingAlias] takes an identifier or string", + [attr.location]) + self._addBindingAlias(attr.value()) + elif (((identifier == "Throws" or identifier == "GetterThrows" or + identifier == "CanOOM" or identifier == "GetterCanOOM") and self.getExtendedAttribute("StoreInSlot")) or (identifier == "StoreInSlot" and (self.getExtendedAttribute("Throws") or - self.getExtendedAttribute("GetterThrows")))): + self.getExtendedAttribute("GetterThrows") or + self.getExtendedAttribute("CanOOM") or + self.getExtendedAttribute("GetterCanOOM")))): raise WebIDLError("Throwing things can't be [StoreInSlot]", [attr.location]) elif identifier == "LenientThis": @@ -4066,6 +4608,10 @@ class IDLAttribute(IDLInterfaceMember): if not self.readonly: raise WebIDLError("[PutForwards] is only allowed on readonly " "attributes", [attr.location, self.location]) + if self.type.isPromise(): + raise WebIDLError("[PutForwards] is not allowed on " + "Promise-typed attributes", + [attr.location, self.location]) if self.isStatic(): raise WebIDLError("[PutForwards] is only allowed on non-static " "attributes", [attr.location, self.location]) @@ -4083,6 +4629,10 @@ class IDLAttribute(IDLInterfaceMember): if not self.readonly: raise WebIDLError("[Replaceable] is only allowed on readonly " "attributes", [attr.location, self.location]) + if self.type.isPromise(): + raise WebIDLError("[Replaceable] is not allowed on " + "Promise-typed attributes", + [attr.location, self.location]) if self.isStatic(): raise WebIDLError("[Replaceable] is only allowed on non-static " "attributes", [attr.location, self.location]) @@ -4097,6 +4647,10 @@ class IDLAttribute(IDLInterfaceMember): if not self.readonly: raise WebIDLError("[LenientSetter] is only allowed on readonly " "attributes", [attr.location, self.location]) + if self.type.isPromise(): + raise WebIDLError("[LenientSetter] is not allowed on " + "Promise-typed attributes", + [attr.location, self.location]) if self.isStatic(): raise WebIDLError("[LenientSetter] is only allowed on non-static " "attributes", [attr.location, self.location]) @@ -4116,16 +4670,6 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("[LenientFloat] used on an attribute with a " "non-restricted-float type", [attr.location, self.location]) - elif identifier == "EnforceRange": - if self.readonly: - raise WebIDLError("[EnforceRange] used on a readonly attribute", - [attr.location, self.location]) - self.enforceRange = True - elif identifier == "Clamp": - if self.readonly: - raise WebIDLError("[Clamp] used on a readonly attribute", - [attr.location, self.location]) - self.clamp = True elif identifier == "StoreInSlot": if self.getExtendedAttribute("Cached"): raise WebIDLError("[StoreInSlot] and [Cached] must not be " @@ -4138,7 +4682,7 @@ class IDLAttribute(IDLInterfaceMember): [attr.location, self.location]) elif (identifier == "CrossOriginReadable" or identifier == "CrossOriginWritable"): - if not attr.noArguments() and identifier == "CrossOriginReadable": + if not attr.noArguments(): raise WebIDLError("[%s] must take no arguments" % identifier, [attr.location]) if self.isStatic(): @@ -4191,18 +4735,30 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("[Unscopable] is only allowed on non-static " "attributes and operations", [attr.location, self.location]) + elif identifier == "CEReactions": + if not attr.noArguments(): + raise WebIDLError("[CEReactions] must take no arguments", + [attr.location]) elif (identifier == "Pref" or identifier == "Deprecated" or identifier == "SetterThrows" or identifier == "Throws" or identifier == "GetterThrows" or + identifier == "SetterCanOOM" or + identifier == "CanOOM" or + identifier == "GetterCanOOM" or identifier == "ChromeOnly" or identifier == "Func" or identifier == "SecureContext" or identifier == "Frozen" or identifier == "NewObject" or - identifier == "UnsafeInPrerendering" or - identifier == "BinaryName"): + identifier == "NeedsSubjectPrincipal" or + identifier == "SetterNeedsSubjectPrincipal" or + identifier == "GetterNeedsSubjectPrincipal" or + identifier == "NeedsCallerType" or + identifier == "ReturnValueNeedsContainsHack" or + identifier == "BinaryName" or + identifier == "NonEnumerable"): # Known attributes that we don't need to do anything with here pass else: @@ -4215,10 +4771,6 @@ class IDLAttribute(IDLInterfaceMember): self.type.resolveType(parentScope) IDLObjectWithIdentifier.resolve(self, parentScope) - def addExtendedAttributes(self, attrs): - attrs = self.checkForStringHandlingExtendedAttributes(attrs) - IDLInterfaceMember.addExtendedAttributes(self, attrs) - def hasLenientThis(self): return self.lenientThis @@ -4236,9 +4788,43 @@ class IDLAttribute(IDLInterfaceMember): def _getDependentObjects(self): return set([self.type]) + def expand(self, members): + assert self.stringifier + if not self.type.isDOMString() and not self.type.isUSVString(): + raise WebIDLError("The type of a stringifer attribute must be " + "either DOMString or USVString", + [self.location]) + identifier = IDLUnresolvedIdentifier(self.location, "__stringifier", + allowDoubleUnderscore=True) + method = IDLMethod(self.location, + identifier, + returnType=self.type, arguments=[], + stringifier=True, underlyingAttr=self) + allowedExtAttrs = ["Throws", "NeedsSubjectPrincipal", "Pure"] + # Safe to ignore these as they are only meaningful for attributes + attributeOnlyExtAttrs = [ + "CEReactions", + "CrossOriginWritable", + "SetterThrows", + ] + for (key, value) in list(self._extendedAttrDict.items()): + if key in allowedExtAttrs: + if value is not True: + raise WebIDLError("[%s] with a value is currently " + "unsupported in stringifier attributes, " + "please file a bug to add support" % key, + [self.location]) + method.addExtendedAttributes([IDLExtendedAttribute(self.location, (key,))]) + elif not key in attributeOnlyExtAttrs: + raise WebIDLError("[%s] is currently unsupported in " + "stringifier attributes, please file a bug " + "to add support" % key, + [self.location]) + members.append(method) + class IDLArgument(IDLObjectWithIdentifier): - def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False): + def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False, allowTypeAttributes=False): IDLObjectWithIdentifier.__init__(self, location, None, identifier) assert isinstance(type, IDLType) @@ -4249,41 +4835,25 @@ class IDLArgument(IDLObjectWithIdentifier): self.variadic = variadic self.dictionaryMember = dictionaryMember self._isComplete = False - self.enforceRange = False - self.clamp = False self._allowTreatNonCallableAsNull = False self._extendedAttrDict = {} + self.allowTypeAttributes = allowTypeAttributes assert not variadic or optional assert not variadic or not defaultValue def addExtendedAttributes(self, attrs): - attrs = self.checkForStringHandlingExtendedAttributes( - attrs, - isDictionaryMember=self.dictionaryMember, - isOptional=self.optional) for attribute in attrs: identifier = attribute.identifier() - if identifier == "Clamp": - if not attribute.noArguments(): - raise WebIDLError("[Clamp] must take no arguments", - [attribute.location]) - if self.enforceRange: - raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", - [self.location]) - self.clamp = True - elif identifier == "EnforceRange": - if not attribute.noArguments(): - raise WebIDLError("[EnforceRange] must take no arguments", - [attribute.location]) - if self.clamp: - raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", - [self.location]) - self.enforceRange = True + if self.allowTypeAttributes and (identifier == "EnforceRange" or identifier == "Clamp" or + identifier == "TreatNullAs" or identifier == "AllowShared"): + self.type = self.type.withExtendedAttributes([attribute]) elif identifier == "TreatNonCallableAsNull": self._allowTreatNonCallableAsNull = True elif (self.dictionaryMember and - (identifier == "ChromeOnly" or identifier == "Func")): + (identifier == "ChromeOnly" or + identifier == "Func" or + identifier == "Pref")): if not self.optional: raise WebIDLError("[%s] must not be used on a required " "dictionary member" % identifier, @@ -4315,13 +4885,7 @@ class IDLArgument(IDLObjectWithIdentifier): assert not isinstance(type.name, IDLUnresolvedIdentifier) self.type = type - if ((self.type.isDictionary() or - self.type.isUnion() and self.type.unroll().hasDictionaryType()) and - self.optional and not self.defaultValue and not self.variadic): - # Default optional non-variadic dictionaries to null, - # for simplicity, so the codegen doesn't have to special-case this. - self.defaultValue = IDLNullValue(self.location) - elif self.type.isAny(): + if self.type.isAny(): assert (self.defaultValue is None or isinstance(self.defaultValue, IDLNullValue)) # optional 'any' values always have a default value @@ -4330,6 +4894,8 @@ class IDLArgument(IDLObjectWithIdentifier): # codegen doesn't have to special-case this. self.defaultValue = IDLUndefinedValue(self.location) + if self.dictionaryMember and self.type.treatNullAsEmpty: + raise WebIDLError("Dictionary members cannot be [TreatNullAs]", [self.location]) # Now do the coercing thing; this needs to happen after the # above creation of a default value. if self.defaultValue: @@ -4351,7 +4917,7 @@ class IDLArgument(IDLObjectWithIdentifier): class IDLCallback(IDLObjectWithScope): - def __init__(self, location, parentScope, identifier, returnType, arguments): + def __init__(self, location, parentScope, identifier, returnType, arguments, isConstructor): assert isinstance(returnType, IDLType) self._returnType = returnType @@ -4366,10 +4932,15 @@ class IDLCallback(IDLObjectWithScope): self._treatNonCallableAsNull = False self._treatNonObjectAsNull = False + self._isRunScriptBoundary = False + self._isConstructor = isConstructor def isCallback(self): return True + def isConstructor(self): + return self._isConstructor + def signatures(self): return [(self._returnType, self._arguments)] @@ -4402,7 +4973,16 @@ class IDLCallback(IDLObjectWithScope): if attr.identifier() == "TreatNonCallableAsNull": self._treatNonCallableAsNull = True elif attr.identifier() == "TreatNonObjectAsNull": + if self._isConstructor: + raise WebIDLError("[TreatNonObjectAsNull] is not supported " + "on constructors", [self.location]) self._treatNonObjectAsNull = True + elif attr.identifier() == "MOZ_CAN_RUN_SCRIPT_BOUNDARY": + if self._isConstructor: + raise WebIDLError("[MOZ_CAN_RUN_SCRIPT_BOUNDARY] is not " + "permitted on constructors", + [self.location]) + self._isRunScriptBoundary = True else: unhandledAttrs.append(attr) if self._treatNonCallableAsNull and self._treatNonObjectAsNull: @@ -4414,6 +4994,9 @@ class IDLCallback(IDLObjectWithScope): def _getDependentObjects(self): return set([self._returnType] + self._arguments) + def isRunScriptBoundary(self): + return self._isRunScriptBoundary; + class IDLCallbackType(IDLType): def __init__(self, location, callback): @@ -4433,8 +5016,7 @@ class IDLCallbackType(IDLType): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isNonCallbackInterface() or other.isDate() or - other.isSequence()) + other.isNonCallbackInterface() or other.isSequence()) def _getDependentObjects(self): return self.callback._getDependentObjects() @@ -4460,13 +5042,15 @@ class IDLMethodOverload: deps.add(self.returnType) return deps + def includesRestrictedFloatArgument(self): + return any(arg.type.includesRestrictedFloat() for arg in self.arguments) + class IDLMethod(IDLInterfaceMember, IDLScope): Special = enum( 'Getter', 'Setter', - 'Creator', 'Deleter', 'LegacyCaller', base=IDLInterfaceMember.Special @@ -4479,10 +5063,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope): ) def __init__(self, location, identifier, returnType, arguments, - static=False, getter=False, setter=False, creator=False, + static=False, getter=False, setter=False, deleter=False, specialType=NamedOrIndexed.Neither, - legacycaller=False, stringifier=False, jsonifier=False, - maplikeOrSetlikeOrIterable=None): + legacycaller=False, stringifier=False, + maplikeOrSetlikeOrIterable=None, + underlyingAttr=None): # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.Tags.Method) @@ -4500,18 +5085,16 @@ class IDLMethod(IDLInterfaceMember, IDLScope): self._getter = getter assert isinstance(setter, bool) self._setter = setter - assert isinstance(creator, bool) - self._creator = creator assert isinstance(deleter, bool) self._deleter = deleter assert isinstance(legacycaller, bool) self._legacycaller = legacycaller assert isinstance(stringifier, bool) self._stringifier = stringifier - assert isinstance(jsonifier, bool) - self._jsonifier = jsonifier assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase) self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable + self._htmlConstructor = False + self.underlyingAttr = underlyingAttr self._specialType = specialType self._unforgeable = False self.dependsOn = "Everything" @@ -4538,7 +5121,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert not arguments[0].optional and not arguments[0].variadic assert not self._getter or not overload.returnType.isVoid() - if self._setter or self._creator: + if self._setter: assert len(self._overloads) == 1 arguments = self._overloads[0].arguments assert len(arguments) == 2 @@ -4551,13 +5134,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert len(self._overloads) == 1 overload = self._overloads[0] assert len(overload.arguments) == 0 - assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring] - - if self._jsonifier: - assert len(self._overloads) == 1 - overload = self._overloads[0] - assert len(overload.arguments) == 0 - assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object] + if not self.underlyingAttr: + assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring] def isStatic(self): return self._static @@ -4571,9 +5149,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def isSetter(self): return self._setter - def isCreator(self): - return self._creator - def isDeleter(self): return self._deleter @@ -4593,8 +5168,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def isStringifier(self): return self._stringifier - def isJsonifier(self): - return self._jsonifier + def isToJSON(self): + return self.identifier.name == "toJSON" + + def isDefaultToJSON(self): + return self.isToJSON() and self.getExtendedAttribute("Default") def isMaplikeOrSetlikeOrIterableMethod(self): """ @@ -4606,11 +5184,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def isSpecial(self): return (self.isGetter() or self.isSetter() or - self.isCreator() or self.isDeleter() or self.isLegacycaller() or - self.isStringifier() or - self.isJsonifier()) + self.isStringifier()) + + def isHTMLConstructor(self): + return self._htmlConstructor def hasOverloads(self): return self._hasOverloads @@ -4637,10 +5216,25 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def addOverload(self, method): assert len(method._overloads) == 1 - if self._extendedAttrDict != method ._extendedAttrDict: - raise WebIDLError("Extended attributes differ on different " - "overloads of %s" % method.identifier, - [self.location, method.location]) + if self._extendedAttrDict != method._extendedAttrDict: + extendedAttrDiff = set(self._extendedAttrDict.keys()) ^ set(method._extendedAttrDict.keys()) + + if extendedAttrDiff == { "LenientFloat" }: + if "LenientFloat" not in self._extendedAttrDict: + for overload in self._overloads: + if overload.includesRestrictedFloatArgument(): + raise WebIDLError("Restricted float behavior differs on different " + "overloads of %s" % method.identifier, + [overload.location, method.location]) + self._extendedAttrDict["LenientFloat"] = method._extendedAttrDict["LenientFloat"] + elif method._overloads[0].includesRestrictedFloatArgument(): + raise WebIDLError("Restricted float behavior differs on different " + "overloads of %s" % method.identifier, + [self.location, method.location]) + else: + raise WebIDLError("Extended attributes differ on different " + "overloads of %s" % method.identifier, + [self.location, method.location]) self._overloads.extend(method._overloads) @@ -4659,14 +5253,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert not method.isGetter() assert not self.isSetter() assert not method.isSetter() - assert not self.isCreator() - assert not method.isCreator() assert not self.isDeleter() assert not method.isDeleter() assert not self.isStringifier() assert not method.isStringifier() - assert not self.isJsonifier() - assert not method.isJsonifier() + assert not self.isHTMLConstructor() + assert not method.isHTMLConstructor() return self @@ -4735,7 +5327,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): assert argument.type.isComplete() if ((argument.type.isDictionary() and - argument.type.inner.canBeEmpty())or + argument.type.unroll().inner.canBeEmpty()) or (argument.type.isUnion() and argument.type.unroll().hasPossiblyEmptyDictionaryType())): # Optional dictionaries and unions containing optional @@ -4743,19 +5335,33 @@ class IDLMethod(IDLInterfaceMember, IDLScope): # optional arguments must be optional. if (not argument.optional and all(arg.optional for arg in arguments[idx+1:])): - raise WebIDLError("Dictionary argument or union " - "argument containing a dictionary " - "not followed by a required argument " + raise WebIDLError("Dictionary argument without any " + "required fields or union argument " + "containing such dictionary not " + "followed by a required argument " "must be optional", [argument.location]) - # An argument cannot be a Nullable Dictionary - if argument.type.nullable(): - raise WebIDLError("An argument cannot be a nullable " - "dictionary or nullable union " - "containing a dictionary", + if (not argument.defaultValue and + all(arg.optional for arg in arguments[idx+1:])): + raise WebIDLError("Dictionary argument without any " + "required fields or union argument " + "containing such dictionary not " + "followed by a required argument " + "must have a default value", [argument.location]) + # An argument cannot be a nullable dictionary or a + # nullable union containing a dictionary. + if (argument.type.nullable() and + (argument.type.isDictionary() or + (argument.type.isUnion() and + argument.type.unroll().hasDictionaryType()))): + raise WebIDLError("An argument cannot be a nullable " + "dictionary or nullable union " + "containing a dictionary", + [argument.location]) + # Only the last argument can be variadic if variadicArgument: raise WebIDLError("Variadic argument is not last argument", @@ -4786,6 +5392,19 @@ class IDLMethod(IDLInterfaceMember, IDLScope): " methods on JS-implemented classes only.", [self.location]) + # Ensure that toJSON methods satisfy the spec constraints on them. + if self.identifier.name == "toJSON": + if len(self.signatures()) != 1: + raise WebIDLError("toJSON method has multiple overloads", + [self._overloads[0].location, + self._overloads[1].location]) + if len(self.signatures()[0][1]) != 0: + raise WebIDLError("toJSON method has arguments", + [self.location]) + if not self.signatures()[0][0].isJSONType(): + raise WebIDLError("toJSON method has non-JSON return type", + [self.location]) + def overloadsForArgCount(self, argc): return [overload for overload in self._overloads if len(overload.arguments) == argc or @@ -4831,13 +5450,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def handleExtendedAttribute(self, attr): identifier = attr.identifier() - if identifier == "GetterThrows": + if (identifier == "GetterThrows" or + identifier == "SetterThrows" or + identifier == "GetterCanOOM" or + identifier == "SetterCanOOM" or + identifier == "SetterNeedsSubjectPrincipal" or + identifier == "GetterNeedsSubjectPrincipal"): raise WebIDLError("Methods must not be flagged as " - "[GetterThrows]", - [attr.location, self.location]) - elif identifier == "SetterThrows": - raise WebIDLError("Methods must not be flagged as " - "[SetterThrows]", + "[%s]" % identifier, [attr.location, self.location]) elif identifier == "Unforgeable": if self.isStatic(): @@ -4858,12 +5478,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope): [attr.location, self.location]) elif identifier == "LenientFloat": # This is called before we've done overload resolution - assert len(self.signatures()) == 1 - sig = self.signatures()[0] - if not sig[0].isVoid(): + overloads = self._overloads + assert len(overloads) == 1 + if not overloads[0].returnType.isVoid(): raise WebIDLError("[LenientFloat] used on a non-void method", [attr.location, self.location]) - if not any(arg.type.includesRestrictedFloat() for arg in sig[1]): + if not overloads[0].includesRestrictedFloatArgument(): raise WebIDLError("[LenientFloat] used on an operation with no " "restricted float type arguments", [attr.location, self.location]) @@ -4875,6 +5495,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope): if not attr.noArguments(): raise WebIDLError("[%s] must take no arguments" % identifier, [attr.location]) + if identifier == "CrossOriginCallable" and self.isStatic(): + raise WebIDLError("[CrossOriginCallable] is only allowed on non-static " + "attributes", + [attr.location, self.location]) elif identifier == "Pure": if not attr.noArguments(): raise WebIDLError("[Pure] must take no arguments", @@ -4909,16 +5533,42 @@ class IDLMethod(IDLInterfaceMember, IDLScope): raise WebIDLError("[Unscopable] is only allowed on non-static " "attributes and operations", [attr.location, self.location]) + elif identifier == "CEReactions": + if not attr.noArguments(): + raise WebIDLError("[CEReactions] must take no arguments", + [attr.location]) + + if self.isSpecial() and not self.isSetter() and not self.isDeleter(): + raise WebIDLError("[CEReactions] is only allowed on operation, " + "attribute, setter, and deleter", + [attr.location, self.location]) + elif identifier == "Default": + if not attr.noArguments(): + raise WebIDLError("[Default] must take no arguments", + [attr.location]) + + if not self.isToJSON(): + raise WebIDLError("[Default] is only allowed on toJSON operations", + [attr.location, self.location]) + + if self.signatures()[0][0] != BuiltinTypes[IDLBuiltinType.Types.object]: + raise WebIDLError("The return type of the default toJSON " + "operation must be 'object'", + [attr.location, self.location]) elif (identifier == "Throws" or + identifier == "CanOOM" or identifier == "NewObject" or identifier == "ChromeOnly" or - identifier == "UnsafeInPrerendering" or identifier == "Pref" or identifier == "Deprecated" or identifier == "Func" or identifier == "SecureContext" or identifier == "BinaryName" or - identifier == "StaticClassOverride"): + identifier == "NeedsSubjectPrincipal" or + identifier == "NeedsCallerType" or + identifier == "StaticClassOverride" or + identifier == "NonEnumerable" or + identifier == "Unexposed"): # Known attributes that we don't need to do anything with here pass else: @@ -4939,49 +5589,110 @@ class IDLMethod(IDLInterfaceMember, IDLScope): return deps -class IDLImplementsStatement(IDLObject): - def __init__(self, location, implementor, implementee): +class IDLConstructor(IDLMethod): + def __init__(self, location, args, name): + # We can't actually init our IDLMethod yet, because we do not know the + # return type yet. Just save the info we have for now and we will init + # it later. + self._initLocation = location + self._initArgs = args + self._initName = name + self._inited = False + self._initExtendedAttrs = [] + + def addExtendedAttributes(self, attrs): + if self._inited: + return IDLMethod.addExtendedAttributes(self, attrs) + self._initExtendedAttrs.extend(attrs) + + def handleExtendedAttribute(self, attr): + identifier = attr.identifier() + if (identifier == "BinaryName" or + identifier == "ChromeOnly" or + identifier == "NewObject" or + identifier == "SecureContext" or + identifier == "Throws" or + identifier == "Func" or + identifier == "Pref"): + IDLMethod.handleExtendedAttribute(self, attr) + elif identifier == "HTMLConstructor": + if not attr.noArguments(): + raise WebIDLError("[HTMLConstructor] must take no arguments", + [attr.location]) + # We shouldn't end up here for named constructors. + assert(self.identifier.name == "constructor") + + if any(len(sig[1]) != 0 for sig in self.signatures()): + raise WebIDLError("[HTMLConstructor] must not be applied to a " + "constructor operation that has arguments.", + [attr.location]) + self._htmlConstructor = True + else: + raise WebIDLError("Unknown extended attribute %s on method" % identifier, + [attr.location]) + + def reallyInit(self, parentInterface): + name = self._initName + location = self._initLocation + identifier = IDLUnresolvedIdentifier(location, name, allowForbidden=True) + retType = IDLWrapperType(parentInterface.location, parentInterface) + IDLMethod.__init__(self, location, identifier, retType, self._initArgs, + static=True) + self._inited = True; + # Propagate through whatever extended attributes we already had + self.addExtendedAttributes(self._initExtendedAttrs) + self._initExtendedAttrs = [] + # Constructors are always NewObject. Whether they throw or not is + # indicated by [Throws] annotations in the usual way. + self.addExtendedAttributes( + [IDLExtendedAttribute(self.location, ("NewObject",))]) + + +class IDLIncludesStatement(IDLObject): + def __init__(self, location, interface, mixin): IDLObject.__init__(self, location) - self.implementor = implementor - self.implementee = implementee + self.interface = interface + self.mixin = mixin self._finished = False def finish(self, scope): if self._finished: return - assert(isinstance(self.implementor, IDLIdentifierPlaceholder)) - assert(isinstance(self.implementee, IDLIdentifierPlaceholder)) - implementor = self.implementor.finish(scope) - implementee = self.implementee.finish(scope) - # NOTE: we depend on not setting self.implementor and - # self.implementee here to keep track of the original + self._finished = True + assert(isinstance(self.interface, IDLIdentifierPlaceholder)) + assert(isinstance(self.mixin, IDLIdentifierPlaceholder)) + interface = self.interface.finish(scope) + mixin = self.mixin.finish(scope) + # NOTE: we depend on not setting self.interface and + # self.mixin here to keep track of the original # locations. - if not isinstance(implementor, IDLInterface): - raise WebIDLError("Left-hand side of 'implements' is not an " - "interface", - [self.implementor.location]) - if implementor.isCallback(): - raise WebIDLError("Left-hand side of 'implements' is a callback " - "interface", - [self.implementor.location]) - if not isinstance(implementee, IDLInterface): - raise WebIDLError("Right-hand side of 'implements' is not an " + if not isinstance(interface, IDLInterface): + raise WebIDLError("Left-hand side of 'includes' is not an " "interface", - [self.implementee.location]) - if implementee.isCallback(): - raise WebIDLError("Right-hand side of 'implements' is a callback " + [self.interface.location, interface.location]) + if interface.isCallback(): + raise WebIDLError("Left-hand side of 'includes' is a callback " "interface", - [self.implementee.location]) - implementor.addImplementedInterface(implementee) - self.implementor = implementor - self.implementee = implementee + [self.interface.location, interface.location]) + if not isinstance(mixin, IDLInterfaceMixin): + raise WebIDLError("Right-hand side of 'includes' is not an " + "interface mixin", + [self.mixin.location, mixin.location]) + + mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames) + + interface.addIncludedMixin(mixin) + self.interface = interface + self.mixin = mixin def validate(self): pass def addExtendedAttributes(self, attrs): - assert len(attrs) == 0 - + if len(attrs) != 0: + raise WebIDLError("There are no extended attributes that are " + "allowed on includes statements", + [attrs[0].location, self.location]) class IDLExtendedAttribute(IDLObject): """ @@ -5028,6 +5739,7 @@ class Tokenizer(object): "FLOATLITERAL", "IDENTIFIER", "STRING", + "COMMENTS", "WHITESPACE", "OTHER" ] @@ -5051,7 +5763,7 @@ class Tokenizer(object): return t def t_IDENTIFIER(self, t): - r'[A-Z_a-z][0-9A-Z_a-z-]*' + r'[_-]?[A-Za-z][0-9A-Z_a-z-]*' t.type = self.keywords.get(t.value, 'IDENTIFIER') return t @@ -5060,8 +5772,12 @@ class Tokenizer(object): t.value = t.value[1:-1] return t + def t_COMMENTS(self, t): + r'(\/\*(.|\n)*?\*\/)|(\/\/.*)' + pass + def t_WHITESPACE(self, t): - r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+' + r'[\t\n\r ]+' pass def t_ELLIPSIS(self, t): @@ -5075,22 +5791,21 @@ class Tokenizer(object): return t keywords = { - "module": "MODULE", "interface": "INTERFACE", "partial": "PARTIAL", + "mixin": "MIXIN", "dictionary": "DICTIONARY", "exception": "EXCEPTION", "enum": "ENUM", "callback": "CALLBACK", "typedef": "TYPEDEF", - "implements": "IMPLEMENTS", + "includes": "INCLUDES", "const": "CONST", "null": "NULL", "true": "TRUE", "false": "FALSE", "serializer": "SERIALIZER", "stringifier": "STRINGIFIER", - "jsonifier": "JSONIFIER", "unrestricted": "UNRESTRICTED", "attribute": "ATTRIBUTE", "readonly": "READONLY", @@ -5098,16 +5813,16 @@ class Tokenizer(object): "static": "STATIC", "getter": "GETTER", "setter": "SETTER", - "creator": "CREATOR", "deleter": "DELETER", "legacycaller": "LEGACYCALLER", "optional": "OPTIONAL", "...": "ELLIPSIS", "::": "SCOPE", - "Date": "DATE", "DOMString": "DOMSTRING", "ByteString": "BYTESTRING", "USVString": "USVSTRING", + "JSString": "JSSTRING", + "UTF8String": "UTF8STRING", "any": "ANY", "boolean": "BOOLEAN", "byte": "BYTE", @@ -5119,7 +5834,7 @@ class Tokenizer(object): "Promise": "PROMISE", "required": "REQUIRED", "sequence": "SEQUENCE", - "MozMap": "MOZMAP", + "record": "RECORD", "short": "SHORT", "unsigned": "UNSIGNED", "void": "VOID", @@ -5137,15 +5852,18 @@ class Tokenizer(object): "<": "LT", ">": "GT", "ArrayBuffer": "ARRAYBUFFER", - "SharedArrayBuffer": "SHAREDARRAYBUFFER", "or": "OR", "maplike": "MAPLIKE", "setlike": "SETLIKE", "iterable": "ITERABLE", - "namespace": "NAMESPACE" + "namespace": "NAMESPACE", + "ReadableStream": "READABLESTREAM", + "constructor": "CONSTRUCTOR", + "symbol": "SYMBOL", + "async": "ASYNC", } - tokens.extend(keywords.values()) + tokens.extend(list(keywords.values())) def t_error(self, t): raise WebIDLError("Unrecognized Input", @@ -5154,23 +5872,21 @@ class Tokenizer(object): lexpos=self.lexer.lexpos, filename=self.filename)]) - def __init__(self, outputdir, lexer=None): + def __init__(self, lexer=None): if lexer: self.lexer = lexer else: - self.lexer = lex.lex(object=self, - outputdir=outputdir, - lextab='webidllex', - reflags=re.DOTALL) + self.lexer = lex.lex(object=self) class SqueakyCleanLogger(object): errorWhitelist = [ - # Web IDL defines the WHITESPACE token, but doesn't actually + # Web IDL defines the WHITESPACE and COMMENTS token, but doesn't actually # use it ... so far. "Token 'WHITESPACE' defined, but not used", - # And that means we have an unused token - "There is 1 unused token", + "Token 'COMMENTS' defined, but not used", + # And that means we have unused tokens + "There are 2 unused tokens", # Web IDL defines a OtherOrComma rule that's only used in # ExtendedAttributeInner, which we don't use yet. "Rule 'OtherOrComma' defined, but not used", @@ -5238,21 +5954,21 @@ class Parser(Tokenizer): def p_Definition(self, p): """ - Definition : CallbackOrInterface + Definition : CallbackOrInterfaceOrMixin | Namespace | Partial | Dictionary | Exception | Enum | Typedef - | ImplementsStatement + | IncludesStatement """ p[0] = p[1] assert p[1] # We might not have implemented something ... - def p_CallbackOrInterfaceCallback(self, p): + def p_CallbackOrInterfaceOrMixinCallback(self, p): """ - CallbackOrInterface : CALLBACK CallbackRestOrInterface + CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface """ if p[2].isInterface(): assert isinstance(p[2], IDLInterface) @@ -5260,16 +5976,17 @@ class Parser(Tokenizer): p[0] = p[2] - def p_CallbackOrInterfaceInterface(self, p): + def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p): """ - CallbackOrInterface : Interface + CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin """ - p[0] = p[1] + p[0] = p[2] def p_CallbackRestOrInterface(self, p): """ CallbackRestOrInterface : CallbackRest - | Interface + | CallbackConstructorRest + | CallbackInterface """ assert p[1] p[0] = p[1] @@ -5277,9 +5994,10 @@ class Parser(Tokenizer): def handleNonPartialObject(self, location, identifier, constructor, constructorArgs, nonPartialArgs): """ - This handles non-partial objects (interfaces and namespaces) by - checking for an existing partial object, and promoting it to - non-partial as needed. The return value is the non-partial object. + This handles non-partial objects (interfaces, namespaces and + dictionaries) by checking for an existing partial object, and promoting + it to non-partial as needed. The return value is the non-partial + object. constructorArgs are all the args for the constructor except the last one: isKnownNonPartial. @@ -5301,7 +6019,7 @@ class Parser(Tokenizer): [location, existingObj.location]) existingObj.setNonPartial(*nonPartialArgs) return existingObj - except Exception, ex: + except Exception as ex: if isinstance(ex, WebIDLError): raise ex pass @@ -5309,14 +6027,27 @@ class Parser(Tokenizer): # True for isKnownNonPartial return constructor(*(constructorArgs + [True])) - def p_Interface(self, p): + def p_InterfaceOrMixin(self, p): + """ + InterfaceOrMixin : InterfaceRest + | MixinRest + """ + p[0] = p[1] + + def p_CallbackInterface(self, p): + """ + CallbackInterface : INTERFACE InterfaceRest """ - Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON + p[0] = p[2] + + def p_InterfaceRest(self, p): + """ + InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON """ location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[5] - parent = p[3] + identifier = IDLUnresolvedIdentifier(location, p[1]) + members = p[4] + parent = p[2] p[0] = self.handleNonPartialObject( location, identifier, IDLInterface, @@ -5325,10 +6056,10 @@ class Parser(Tokenizer): def p_InterfaceForwardDecl(self, p): """ - Interface : INTERFACE IDENTIFIER SEMICOLON + InterfaceRest : IDENTIFIER SEMICOLON """ location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + identifier = IDLUnresolvedIdentifier(location, p[1]) try: if self.globalScope()._lookupIdentifier(identifier): @@ -5339,13 +6070,26 @@ class Parser(Tokenizer): "%s and %s" % (identifier.name, p[0]), [location, p[0].location]) return - except Exception, ex: + except Exception as ex: if isinstance(ex, WebIDLError): raise ex pass p[0] = IDLExternalInterface(location, self.globalScope(), identifier) + def p_MixinRest(self, p): + """ + MixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[4] + + p[0] = self.handleNonPartialObject( + location, identifier, IDLInterfaceMixin, + [location, self.globalScope(), identifier, members], + [location, members]) + def p_Namespace(self, p): """ Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON @@ -5365,10 +6109,16 @@ class Parser(Tokenizer): """ p[0] = p[2] + def p_PartialDefinitionInterface(self, p): + """ + PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin + """ + p[0] = p[2] + def p_PartialDefinition(self, p): """ - PartialDefinition : PartialInterface - | PartialNamespace + PartialDefinition : PartialNamespace + | PartialDictionary """ p[0] = p[1] @@ -5376,17 +6126,17 @@ class Parser(Tokenizer): nonPartialConstructorArgs, partialConstructorArgs): """ - This handles partial objects (interfaces and namespaces) by checking for - an existing non-partial object, and adding ourselves to it as needed. - The return value is our partial object. For now we just use - IDLPartialInterfaceOrNamespace for partial objects. + This handles partial objects (interfaces, namespaces and dictionaries) + by checking for an existing non-partial object, and adding ourselves to + it as needed. The return value is our partial object. We use + IDLPartialInterfaceOrNamespace for partial interfaces or namespaces, + and IDLPartialDictionary for partial dictionaries. nonPartialConstructorArgs are all the args for the non-partial constructor except the last two: members and isKnownNonPartial. - partialConstructorArgs are the arguments for the - IDLPartialInterfaceOrNamespace constructor, except the last one (the - non-partial object). + partialConstructorArgs are the arguments for the partial object + constructor, except the last one (the non-partial object). """ # The name of the class starts with "IDL", so strip that off. # Also, starts with a capital letter after that, so nix that @@ -5402,7 +6152,7 @@ class Parser(Tokenizer): "non-%s object" % (prettyname, prettyname), [location, nonPartialObject.location]) - except Exception, ex: + except Exception as ex: if isinstance(ex, WebIDLError): raise ex pass @@ -5410,24 +6160,55 @@ class Parser(Tokenizer): if not nonPartialObject: nonPartialObject = nonPartialConstructor( # No members, False for isKnownNonPartial - *(nonPartialConstructorArgs + [[], False])) - partialInterface = IDLPartialInterfaceOrNamespace( - *(partialConstructorArgs + [nonPartialObject])) - return partialInterface + *(nonPartialConstructorArgs), members=[], isKnownNonPartial=False) + + partialObject = None + if isinstance(nonPartialObject, IDLDictionary): + partialObject = IDLPartialDictionary( + *(partialConstructorArgs + [nonPartialObject])) + elif isinstance(nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace)): + partialObject = IDLPartialInterfaceOrNamespace( + *(partialConstructorArgs + [nonPartialObject])) + else: + raise WebIDLError("Unknown partial object type %s" % + type(partialObject), + [location]) + + return partialObject - def p_PartialInterface(self, p): + def p_PartialInterfaceOrPartialMixin(self, p): """ - PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON + PartialInterfaceOrPartialMixin : PartialInterfaceRest + | PartialMixinRest + """ + p[0] = p[1] + + def p_PartialInterfaceRest(self, p): + """ + PartialInterfaceRest : IDENTIFIER LBRACE PartialInterfaceMembers RBRACE SEMICOLON """ location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[4] + identifier = IDLUnresolvedIdentifier(location, p[1]) + members = p[3] p[0] = self.handlePartialObject( location, identifier, IDLInterface, [location, self.globalScope(), identifier, None], [location, identifier, members]) + def p_PartialMixinRest(self, p): + """ + PartialMixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[4] + + p[0] = self.handlePartialObject( + location, identifier, IDLInterfaceMixin, + [location, self.globalScope(), identifier], + [location, identifier, members]) + def p_PartialNamespace(self, p): """ PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON @@ -5441,6 +6222,19 @@ class Parser(Tokenizer): [location, self.globalScope(), identifier], [location, identifier, members]) + def p_PartialDictionary(self, p): + """ + PartialDictionary : DICTIONARY IDENTIFIER LBRACE DictionaryMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[4] + + p[0] = self.handlePartialObject( + location, identifier, IDLDictionary, + [location, self.globalScope(), identifier], + [location, identifier, members]) + def p_Inheritance(self, p): """ Inheritance : COLON ScopedName @@ -5457,7 +6251,7 @@ class Parser(Tokenizer): """ InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers """ - p[0] = [p[2]] if p[2] else [] + p[0] = [p[2]] assert not p[1] or p[2] p[2].addExtendedAttributes(p[1]) @@ -5472,8 +6266,64 @@ class Parser(Tokenizer): def p_InterfaceMember(self, p): """ - InterfaceMember : Const - | AttributeOrOperationOrMaplikeOrSetlikeOrIterable + InterfaceMember : PartialInterfaceMember + | Constructor + """ + p[0] = p[1] + + def p_Constructor(self, p): + """ + Constructor : CONSTRUCTOR LPAREN ArgumentList RPAREN SEMICOLON + """ + p[0] = IDLConstructor(self.getLocation(p, 1), p[3], "constructor") + + def p_PartialInterfaceMembers(self, p): + """ + PartialInterfaceMembers : ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers + """ + p[0] = [p[2]] + + assert not p[1] or p[2] + p[2].addExtendedAttributes(p[1]) + + p[0].extend(p[3]) + + def p_PartialInterfaceMembersEmpty(self, p): + """ + PartialInterfaceMembers : + """ + p[0] = [] + + def p_PartialInterfaceMember(self, p): + """ + PartialInterfaceMember : Const + | AttributeOrOperationOrMaplikeOrSetlikeOrIterable + """ + p[0] = p[1] + + + def p_MixinMembersEmpty(self, p): + """ + MixinMembers : + """ + p[0] = [] + + def p_MixinMembers(self, p): + """ + MixinMembers : ExtendedAttributeList MixinMember MixinMembers + """ + p[0] = [p[2]] + + assert not p[1] or p[2] + p[2].addExtendedAttributes(p[1]) + + p[0].extend(p[3]) + + def p_MixinMember(self, p): + """ + MixinMember : Const + | Attribute + | Operation """ p[0] = p[1] @@ -5495,31 +6345,42 @@ class Parser(Tokenizer): # We're at the end of the list p[0] = [] return - # Add our extended attributes p[2].addExtendedAttributes(p[1]) p[0] = [p[2]] p[0].extend(p[3]) - def p_DictionaryMember(self, p): + def p_DictionaryMemberRequired(self, p): """ - DictionaryMember : Required Type IDENTIFIER Default SEMICOLON + DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON """ - # These quack a lot like optional arguments, so just treat them that way. + # These quack a lot like required arguments, so just treat them that way. t = p[2] assert isinstance(t, IDLType) identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) - defaultValue = p[4] - optional = not p[1] - - if not optional and defaultValue: - raise WebIDLError("Required dictionary members can't have a default value.", - [self.getLocation(p, 4)]) p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, - optional=optional, - defaultValue=defaultValue, variadic=False, + optional=False, + defaultValue=None, variadic=False, dictionaryMember=True) + def p_DictionaryMember(self, p): + """ + DictionaryMember : Type IDENTIFIER Default SEMICOLON + """ + # These quack a lot like optional arguments, so just treat them that way. + t = p[1] + assert isinstance(t, IDLType) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + defaultValue = p[3] + + # Any attributes that precede this may apply to the type, so + # we configure the argument to forward type attributes down instead of producing + # a parse error + p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, + optional=True, + defaultValue=defaultValue, variadic=False, + dictionaryMember=True, allowTypeAttributes=True) + def p_Default(self, p): """ Default : EQUALS DefaultValue @@ -5534,12 +6395,23 @@ class Parser(Tokenizer): """ DefaultValue : ConstValue | LBRACKET RBRACKET + | LBRACE RBRACE """ if len(p) == 2: p[0] = p[1] else: - assert len(p) == 3 # Must be [] - p[0] = IDLEmptySequenceValue(self.getLocation(p, 1)) + assert len(p) == 3 # Must be [] or {} + if p[1] == "[": + p[0] = IDLEmptySequenceValue(self.getLocation(p, 1)) + else: + assert p[1] == "{" + p[0] = IDLDefaultDictionaryValue(self.getLocation(p, 1)) + + def p_DefaultValueNull(self, p): + """ + DefaultValue : NULL + """ + p[0] = IDLNullValue(self.getLocation(p, 1)) def p_Exception(self, p): """ @@ -5596,7 +6468,15 @@ class Parser(Tokenizer): """ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) p[0] = IDLCallback(self.getLocation(p, 1), self.globalScope(), - identifier, p[3], p[5]) + identifier, p[3], p[5], isConstructor=False) + + def p_CallbackConstructorRest(self, p): + """ + CallbackConstructorRest : CONSTRUCTOR IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON + """ + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + p[0] = IDLCallback(self.getLocation(p, 2), self.globalScope(), + identifier, p[4], p[6], isConstructor=True) def p_ExceptionMembers(self, p): """ @@ -5607,21 +6487,20 @@ class Parser(Tokenizer): def p_Typedef(self, p): """ - Typedef : TYPEDEF Type IDENTIFIER SEMICOLON + Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON """ typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(), p[2], p[3]) p[0] = typedef - def p_ImplementsStatement(self, p): + def p_IncludesStatement(self, p): """ - ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON + IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON """ - assert(p[2] == "implements") - implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1]) - implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3]) - p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor, - implementee) + assert(p[2] == "includes") + interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1]) + mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3]) + p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin) def p_Const(self, p): """ @@ -5670,12 +6549,6 @@ class Parser(Tokenizer): stringType = BuiltinTypes[IDLBuiltinType.Types.domstring] p[0] = IDLValue(location, stringType, p[1]) - def p_ConstValueNull(self, p): - """ - ConstValue : NULL - """ - p[0] = IDLNullValue(self.getLocation(p, 1)) - def p_BooleanLiteralTrue(self, p): """ BooleanLiteral : TRUE @@ -5700,8 +6573,8 @@ class Parser(Tokenizer): def p_Iterable(self, p): """ - Iterable : ITERABLE LT Type GT SEMICOLON - | ITERABLE LT Type COMMA Type GT SEMICOLON + Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON + | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON """ location = self.getLocation(p, 2) identifier = IDLUnresolvedIdentifier(location, "__iterable", @@ -5717,7 +6590,7 @@ class Parser(Tokenizer): def p_Setlike(self, p): """ - Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON + Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON """ readonly = p[1] maplikeOrSetlikeType = p[2] @@ -5731,7 +6604,7 @@ class Parser(Tokenizer): def p_Maplike(self, p): """ - Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON + Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON """ readonly = p[1] maplikeOrSetlikeType = p[2] @@ -5769,7 +6642,7 @@ class Parser(Tokenizer): def p_AttributeRest(self, p): """ - AttributeRest : ReadOnly ATTRIBUTE Type AttributeName SEMICOLON + AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON """ location = self.getLocation(p, 2) readonly = p[1] @@ -5812,13 +6685,12 @@ class Parser(Tokenizer): getter = True if IDLMethod.Special.Getter in p[1] else False setter = True if IDLMethod.Special.Setter in p[1] else False - creator = True if IDLMethod.Special.Creator in p[1] else False deleter = True if IDLMethod.Special.Deleter in p[1] else False legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False if getter or deleter: - if setter or creator: - raise WebIDLError("getter and deleter are incompatible with setter and creator", + if setter: + raise WebIDLError("getter and deleter are incompatible with setter", [self.getLocation(p, 1)]) (returnType, identifier, arguments) = p[2] @@ -5837,6 +6709,9 @@ class Parser(Tokenizer): specialType = IDLMethod.NamedOrIndexed.Named elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: specialType = IDLMethod.NamedOrIndexed.Indexed + if deleter: + raise WebIDLError("There is no such thing as an indexed deleter.", + [self.getLocation(p, 1)]) else: raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" % ("getter" if getter else "deleter"), @@ -5850,10 +6725,9 @@ class Parser(Tokenizer): if returnType.isVoid(): raise WebIDLError("getter cannot have void return type", [self.getLocation(p, 2)]) - if setter or creator: + if setter: if len(arguments) != 2: - raise WebIDLError("%s has wrong number of arguments" % - ("setter" if setter else "creator"), + raise WebIDLError("setter has wrong number of arguments", [self.getLocation(p, 2)]) argType = arguments[0].type if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: @@ -5861,18 +6735,15 @@ class Parser(Tokenizer): elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: specialType = IDLMethod.NamedOrIndexed.Indexed else: - raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" % - ("setter" if setter else "creator"), + raise WebIDLError("settter has wrong argument type (must be DOMString or UnsignedLong)", [arguments[0].location]) if arguments[0].optional or arguments[0].variadic: - raise WebIDLError("%s cannot have %s argument" % - ("setter" if setter else "creator", - "optional" if arguments[0].optional else "variadic"), + raise WebIDLError("setter cannot have %s argument" % + ("optional" if arguments[0].optional else "variadic"), [arguments[0].location]) if arguments[1].optional or arguments[1].variadic: - raise WebIDLError("%s cannot have %s argument" % - ("setter" if setter else "creator", - "optional" if arguments[1].optional else "variadic"), + raise WebIDLError("setter cannot have %s argument" % + ("optional" if arguments[1].optional else "variadic"), [arguments[1].location]) if stringifier: @@ -5885,7 +6756,7 @@ class Parser(Tokenizer): # identifier might be None. This is only permitted for special methods. if not identifier: - if (not getter and not setter and not creator and + if (not getter and not setter and not deleter and not legacycaller and not stringifier): raise WebIDLError("Identifier required for non-special methods", [self.getLocation(p, 2)]) @@ -5893,19 +6764,18 @@ class Parser(Tokenizer): location = BuiltinLocation("<auto-generated-identifier>") identifier = IDLUnresolvedIdentifier( location, - "__%s%s%s%s%s%s%s" % + "__%s%s%s%s%s%s" % ("named" if specialType == IDLMethod.NamedOrIndexed.Named else "indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "", "getter" if getter else "", "setter" if setter else "", "deleter" if deleter else "", - "creator" if creator else "", "legacycaller" if legacycaller else "", "stringifier" if stringifier else ""), allowDoubleUnderscore=True) method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments, - static=static, getter=getter, setter=setter, creator=creator, + static=static, getter=getter, setter=setter, deleter=deleter, specialType=specialType, legacycaller=legacycaller, stringifier=stringifier) p[0] = method @@ -5924,19 +6794,6 @@ class Parser(Tokenizer): stringifier=True) p[0] = method - def p_Jsonifier(self, p): - """ - Operation : JSONIFIER SEMICOLON - """ - identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), - "__jsonifier", allowDoubleUnderscore=True) - method = IDLMethod(self.getLocation(p, 1), - identifier, - returnType=BuiltinTypes[IDLBuiltinType.Types.object], - arguments=[], - jsonifier=True) - p[0] = method - def p_QualifierStatic(self, p): """ Qualifier : STATIC @@ -5981,12 +6838,6 @@ class Parser(Tokenizer): """ p[0] = IDLMethod.Special.Setter - def p_SpecialCreator(self, p): - """ - Special : CREATOR - """ - p[0] = IDLMethod.Special.Creator - def p_SpecialDeleter(self, p): """ Special : DELETER @@ -6045,95 +6896,105 @@ class Parser(Tokenizer): def p_Argument(self, p): """ - Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default + Argument : ExtendedAttributeList ArgumentRest """ - t = p[3] + p[0] = p[2] + p[0].addExtendedAttributes(p[1]) + + def p_ArgumentRestOptional(self, p): + """ + ArgumentRest : OPTIONAL TypeWithExtendedAttributes ArgumentName Default + """ + t = p[2] assert isinstance(t, IDLType) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5]) + # Arg names can be reserved identifiers + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3], + allowForbidden=True) - optional = p[2] - variadic = p[4] - defaultValue = p[6] + defaultValue = p[4] - if not optional and defaultValue: - raise WebIDLError("Mandatory arguments can't have a default value.", - [self.getLocation(p, 6)]) # We can't test t.isAny() here and give it a default value as needed, # since at this point t is not a fully resolved type yet (e.g. it might # be a typedef). We'll handle the 'any' case in IDLArgument.complete. - if variadic: - if optional: - raise WebIDLError("Variadic arguments should not be marked optional.", - [self.getLocation(p, 2)]) - optional = variadic + p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, True, defaultValue, False) - p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic) - p[0].addExtendedAttributes(p[1]) + def p_ArgumentRest(self, p): + """ + ArgumentRest : Type Ellipsis ArgumentName + """ + t = p[1] + assert isinstance(t, IDLType) + # Arg names can be reserved identifiers + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3], + allowForbidden=True) + + variadic = p[2] + + # We can't test t.isAny() here and give it a default value as needed, + # since at this point t is not a fully resolved type yet (e.g. it might + # be a typedef). We'll handle the 'any' case in IDLArgument.complete. + + # variadic implies optional + # Any attributes that precede this may apply to the type, so + # we configure the argument to forward type attributes down instead of producing + # a parse error + p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, variadic, None, variadic, allowTypeAttributes=True) def p_ArgumentName(self, p): """ ArgumentName : IDENTIFIER - | ATTRIBUTE - | CALLBACK - | CONST - | CREATOR - | DELETER - | DICTIONARY - | ENUM - | EXCEPTION - | GETTER - | IMPLEMENTS - | INHERIT - | INTERFACE - | ITERABLE - | LEGACYCALLER - | MAPLIKE - | PARTIAL - | REQUIRED - | SERIALIZER - | SETLIKE - | SETTER - | STATIC - | STRINGIFIER - | JSONIFIER - | TYPEDEF - | UNRESTRICTED - | NAMESPACE + | ArgumentNameKeyword """ p[0] = p[1] - def p_AttributeName(self, p): - """ - AttributeName : IDENTIFIER - | REQUIRED + def p_ArgumentNameKeyword(self, p): + """ + ArgumentNameKeyword : ASYNC + | ATTRIBUTE + | CALLBACK + | CONST + | CONSTRUCTOR + | DELETER + | DICTIONARY + | ENUM + | EXCEPTION + | GETTER + | INCLUDES + | INHERIT + | INTERFACE + | ITERABLE + | LEGACYCALLER + | MAPLIKE + | MIXIN + | NAMESPACE + | PARTIAL + | READONLY + | REQUIRED + | SERIALIZER + | SETLIKE + | SETTER + | STATIC + | STRINGIFIER + | TYPEDEF + | UNRESTRICTED """ p[0] = p[1] - def p_Optional(self, p): - """ - Optional : OPTIONAL - """ - p[0] = True - - def p_OptionalEmpty(self, p): - """ - Optional : - """ - p[0] = False - - def p_Required(self, p): + def p_AttributeName(self, p): """ - Required : REQUIRED + AttributeName : IDENTIFIER + | AttributeNameKeyword """ - p[0] = True + p[0] = p[1] - def p_RequiredEmpty(self, p): + def p_AttributeNameKeyword(self, p): """ - Required : + AttributeNameKeyword : ASYNC + | REQUIRED """ - p[0] = False + p[0] = p[1] def p_Ellipsis(self, p): """ @@ -6218,43 +7079,32 @@ class Parser(Tokenizer): | EQUALS | GT | QUESTIONMARK - | DATE | DOMSTRING | BYTESTRING | USVSTRING + | UTF8STRING + | JSSTRING + | PROMISE | ANY - | ATTRIBUTE | BOOLEAN | BYTE - | LEGACYCALLER - | CONST - | CREATOR - | DELETER | DOUBLE - | EXCEPTION | FALSE | FLOAT - | GETTER - | IMPLEMENTS - | INHERIT - | INTERFACE | LONG - | MODULE | NULL | OBJECT | OCTET + | OR | OPTIONAL + | RECORD | SEQUENCE - | MOZMAP - | SETTER | SHORT - | STATIC - | STRINGIFIER - | JSONIFIER + | SYMBOL | TRUE - | TYPEDEF | UNSIGNED | VOID + | ArgumentNameKeyword """ pass @@ -6277,9 +7127,15 @@ class Parser(Tokenizer): """ p[0] = self.handleNullable(p[1], p[2]) - def p_SingleTypeNonAnyType(self, p): + def p_TypeWithExtendedAttributes(self, p): """ - SingleType : NonAnyType + TypeWithExtendedAttributes : ExtendedAttributeList Type + """ + p[0] = p[2].withExtendedAttributes(p[1]) + + def p_SingleTypeDistinguishableType(self, p): + """ + SingleType : DistinguishableType """ p[0] = p[1] @@ -6289,6 +7145,14 @@ class Parser(Tokenizer): """ p[0] = BuiltinTypes[IDLBuiltinType.Types.any] + # Note: Promise<void> is allowed, so we want to parametrize on ReturnType, + # not Type. Promise types can't be null, hence no "Null" in there. + def p_SingleTypePromiseType(self, p): + """ + SingleType : PROMISE LT ReturnType GT + """ + p[0] = IDLPromiseType(self.getLocation(p, 1), p[3]) + def p_UnionType(self, p): """ UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN @@ -6297,11 +7161,11 @@ class Parser(Tokenizer): types.extend(p[5]) p[0] = IDLUnionType(self.getLocation(p, 1), types) - def p_UnionMemberTypeNonAnyType(self, p): + def p_UnionMemberTypeDistinguishableType(self, p): """ - UnionMemberType : NonAnyType + UnionMemberType : ExtendedAttributeList DistinguishableType """ - p[0] = p[1] + p[0] = p[2].withExtendedAttributes(p[1]) def p_UnionMemberType(self, p): """ @@ -6322,55 +7186,50 @@ class Parser(Tokenizer): """ p[0] = [] - def p_NonAnyType(self, p): + def p_DistinguishableType(self, p): """ - NonAnyType : PrimitiveOrStringType Null - | ARRAYBUFFER Null - | SHAREDARRAYBUFFER Null - | OBJECT Null + DistinguishableType : PrimitiveType Null + | ARRAYBUFFER Null + | READABLESTREAM Null + | OBJECT Null """ if p[1] == "object": type = BuiltinTypes[IDLBuiltinType.Types.object] elif p[1] == "ArrayBuffer": type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] - elif p[1] == "SharedArrayBuffer": - type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer] + elif p[1] == "ReadableStream": + type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream] else: type = BuiltinTypes[p[1]] p[0] = self.handleNullable(type, p[2]) - def p_NonAnyTypeSequenceType(self, p): + def p_DistinguishableTypeStringType(self, p): """ - NonAnyType : SEQUENCE LT Type GT Null + DistinguishableType : StringType Null """ - innerType = p[3] - type = IDLSequenceType(self.getLocation(p, 1), innerType) - p[0] = self.handleNullable(type, p[5]) + p[0] = self.handleNullable(p[1], p[2]) - # Note: Promise<void> is allowed, so we want to parametrize on - # ReturnType, not Type. Also, we want this to end up picking up - # the Promise interface for now, hence the games with IDLUnresolvedType. - def p_NonAnyTypePromiseType(self, p): + def p_DistinguishableTypeSequenceType(self, p): """ - NonAnyType : PROMISE LT ReturnType GT Null + DistinguishableType : SEQUENCE LT TypeWithExtendedAttributes GT Null """ innerType = p[3] - promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise") - type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3]) + type = IDLSequenceType(self.getLocation(p, 1), innerType) p[0] = self.handleNullable(type, p[5]) - def p_NonAnyTypeMozMapType(self, p): + def p_DistinguishableTypeRecordType(self, p): """ - NonAnyType : MOZMAP LT Type GT Null + DistinguishableType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null """ - innerType = p[3] - type = IDLMozMapType(self.getLocation(p, 1), innerType) - p[0] = self.handleNullable(type, p[5]) + keyType = p[3] + valueType = p[5] + type = IDLRecordType(self.getLocation(p, 1), keyType, valueType) + p[0] = self.handleNullable(type, p[7]) - def p_NonAnyTypeScopedName(self, p): + def p_DistinguishableTypeScopedName(self, p): """ - NonAnyType : ScopedName Null + DistinguishableType : ScopedName Null """ assert isinstance(p[1], IDLUnresolvedIdentifier) @@ -6400,95 +7259,104 @@ class Parser(Tokenizer): type = IDLUnresolvedType(self.getLocation(p, 1), p[1]) p[0] = self.handleNullable(type, p[2]) - def p_NonAnyTypeDate(self, p): - """ - NonAnyType : DATE Null - """ - p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date], - p[2]) - def p_ConstType(self, p): """ - ConstType : PrimitiveOrStringType Null + ConstType : PrimitiveType """ - type = BuiltinTypes[p[1]] - p[0] = self.handleNullable(type, p[2]) + p[0] = BuiltinTypes[p[1]] def p_ConstTypeIdentifier(self, p): """ - ConstType : IDENTIFIER Null + ConstType : IDENTIFIER """ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) - type = IDLUnresolvedType(self.getLocation(p, 1), identifier) - p[0] = self.handleNullable(type, p[2]) + p[0] = IDLUnresolvedType(self.getLocation(p, 1), identifier) - def p_PrimitiveOrStringTypeUint(self, p): + def p_PrimitiveTypeUint(self, p): """ - PrimitiveOrStringType : UnsignedIntegerType + PrimitiveType : UnsignedIntegerType """ p[0] = p[1] - def p_PrimitiveOrStringTypeBoolean(self, p): + def p_PrimitiveTypeBoolean(self, p): """ - PrimitiveOrStringType : BOOLEAN + PrimitiveType : BOOLEAN """ p[0] = IDLBuiltinType.Types.boolean - def p_PrimitiveOrStringTypeByte(self, p): + def p_PrimitiveTypeByte(self, p): """ - PrimitiveOrStringType : BYTE + PrimitiveType : BYTE """ p[0] = IDLBuiltinType.Types.byte - def p_PrimitiveOrStringTypeOctet(self, p): + def p_PrimitiveTypeOctet(self, p): """ - PrimitiveOrStringType : OCTET + PrimitiveType : OCTET """ p[0] = IDLBuiltinType.Types.octet - def p_PrimitiveOrStringTypeFloat(self, p): + def p_PrimitiveTypeFloat(self, p): """ - PrimitiveOrStringType : FLOAT + PrimitiveType : FLOAT """ p[0] = IDLBuiltinType.Types.float - def p_PrimitiveOrStringTypeUnrestictedFloat(self, p): + def p_PrimitiveTypeUnrestictedFloat(self, p): """ - PrimitiveOrStringType : UNRESTRICTED FLOAT + PrimitiveType : UNRESTRICTED FLOAT """ p[0] = IDLBuiltinType.Types.unrestricted_float - def p_PrimitiveOrStringTypeDouble(self, p): + def p_PrimitiveTypeDouble(self, p): """ - PrimitiveOrStringType : DOUBLE + PrimitiveType : DOUBLE """ p[0] = IDLBuiltinType.Types.double - def p_PrimitiveOrStringTypeUnrestictedDouble(self, p): + def p_PrimitiveTypeUnrestictedDouble(self, p): """ - PrimitiveOrStringType : UNRESTRICTED DOUBLE + PrimitiveType : UNRESTRICTED DOUBLE """ p[0] = IDLBuiltinType.Types.unrestricted_double - def p_PrimitiveOrStringTypeDOMString(self, p): + def p_StringType(self, p): + """ + StringType : BuiltinStringType + """ + p[0] = BuiltinTypes[p[1]] + + def p_BuiltinStringTypeDOMString(self, p): """ - PrimitiveOrStringType : DOMSTRING + BuiltinStringType : DOMSTRING """ p[0] = IDLBuiltinType.Types.domstring - def p_PrimitiveOrStringTypeBytestring(self, p): + def p_BuiltinStringTypeBytestring(self, p): """ - PrimitiveOrStringType : BYTESTRING + BuiltinStringType : BYTESTRING """ p[0] = IDLBuiltinType.Types.bytestring - def p_PrimitiveOrStringTypeUSVString(self, p): + def p_BuiltinStringTypeUSVString(self, p): """ - PrimitiveOrStringType : USVSTRING + BuiltinStringType : USVSTRING """ p[0] = IDLBuiltinType.Types.usvstring + def p_BuiltinStringTypeUTF8String(self, p): + """ + BuiltinStringType : UTF8STRING + """ + p[0] = IDLBuiltinType.Types.utf8string + + def p_BuiltinStringTypeJSString(self, p): + """ + BuiltinStringType : JSSTRING + """ + p[0] = IDLBuiltinType.Types.jsstring + def p_UnsignedIntegerTypeUnsigned(self, p): """ UnsignedIntegerType : UNSIGNED IntegerType @@ -6622,7 +7490,13 @@ class Parser(Tokenizer): IdentifierList : IDENTIFIER Identifiers """ idents = list(p[2]) - idents.insert(0, p[1]) + # This is only used for identifier-list-valued extended attributes, and if + # we're going to restrict to IDENTIFIER here we should at least allow + # escaping with leading '_' as usual for identifiers. + ident = p[1] + if ident[0] == '_': + ident = ident[1:] + idents.insert(0, ident) p[0] = idents def p_IdentifiersList(self, p): @@ -6630,7 +7504,13 @@ class Parser(Tokenizer): Identifiers : COMMA IDENTIFIER Identifiers """ idents = list(p[3]) - idents.insert(0, p[2]) + # This is only used for identifier-list-valued extended attributes, and if + # we're going to restrict to IDENTIFIER here we should at least allow + # escaping with leading '_' as usual for identifiers. + ident = p[2] + if ident[0] == '_': + ident = ident[1:] + idents.insert(0, ident) p[0] = idents def p_IdentifiersEmpty(self, p): @@ -6647,36 +7527,16 @@ class Parser(Tokenizer): raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)]) def __init__(self, outputdir='', lexer=None): - Tokenizer.__init__(self, outputdir, lexer) + Tokenizer.__init__(self, lexer) logger = SqueakyCleanLogger() try: - self.parser = yacc.yacc(module=self, - outputdir=outputdir, - tabmodule='webidlyacc', - errorlog=logger, - debug=False - # Pickling the grammar is a speedup in - # some cases (older Python?) but a - # significant slowdown in others. - # We're not pickling for now, until it - # becomes a speedup again. - # , picklefile='WebIDLGrammar.pkl' - ) + self.parser = yacc.yacc(module=self, errorlog=logger, debug=False) finally: logger.reportGrammarErrors() self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None) - # To make our test harness work, pretend like we have a primary global already. - # Note that we _don't_ set _globalScope.primaryGlobalAttr, - # so we'll still be able to detect multiple PrimaryGlobal extended attributes. - self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal" - self._globalScope.globalNames.add("FakeTestPrimaryGlobal") - self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal") - # And we add the special-cased "System" global name, which - # doesn't have any corresponding interfaces. - self._globalScope.globalNames.add("System") - self._globalScope.globalNameMapping["System"].add("BackstagePass") + self._installBuiltins(self._globalScope) self._productions = [] @@ -6689,8 +7549,8 @@ class Parser(Tokenizer): def _installBuiltins(self, scope): assert isinstance(scope, IDLScope) - # xrange omits the last value. - for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1): + # range omits the last value. + for x in range(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1): builtin = BuiltinTypes[x] name = builtin.name typedef = IDLTypedef(BuiltinLocation("<builtin type>"), scope, builtin, name) @@ -6703,12 +7563,12 @@ class Parser(Tokenizer): return type def parse(self, t, filename=None): - self.lexer.input(t) + self._filename = filename + self.lexer.input(t.decode(encoding = 'utf-8')) # for tok in iter(self.lexer.token, None): # print tok - self._filename = filename self._productions.extend(self.parser.parse(lexer=self.lexer, tracking=True)) self._filename = None @@ -6719,23 +7579,9 @@ class Parser(Tokenizer): for p in self._productions: if isinstance(p, IDLInterface): interfaceStatements.append(p) - if p.identifier.name == "Navigator": - navigatorInterface = p iterableIteratorIface = None for iface in interfaceStatements: - navigatorProperty = iface.getNavigatorProperty() - if navigatorProperty: - # We're generating a partial interface to add a readonly - # property to the Navigator interface for every interface - # annotated with NavigatorProperty. - partialInterface = IDLPartialInterfaceOrNamespace( - iface.location, - IDLUnresolvedIdentifier(iface.location, "Navigator"), - [ navigatorProperty ], - navigatorInterface) - self._productions.append(partialInterface) - iterable = None # We haven't run finish() on the interface yet, so we don't know # whether our interface is maplike/setlike/iterable or not. This @@ -6755,9 +7601,12 @@ class Parser(Tokenizer): nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")]) itr_ident = IDLUnresolvedIdentifier(iface.location, iface.identifier.name + "Iterator") + toStringTag = iface.identifier.name + " Iterator" itr_iface = IDLInterface(iface.location, self.globalScope(), itr_ident, None, [nextMethod], - isKnownNonPartial=True) + isKnownNonPartial=True, + classNameOverride=toStringTag, + toStringTag=toStringTag) itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")]) # Make sure the exposure set for the iterator interface is the # same as the exposure set for the iterable interface, because @@ -6771,14 +7620,14 @@ class Parser(Tokenizer): self._productions.append(itr_iface) iterable.iteratorType = IDLWrapperType(iface.location, itr_iface) - # Then, finish all the IDLImplementsStatements. In particular, we - # have to make sure we do those before we do the IDLInterfaces. + # Make sure we finish IDLIncludesStatements before we finish the + # IDLInterfaces. # XXX khuey hates this bit and wants to nuke it from orbit. - implementsStatements = [p for p in self._productions if - isinstance(p, IDLImplementsStatement)] + includesStatements = [p for p in self._productions if + isinstance(p, IDLIncludesStatement)] otherStatements = [p for p in self._productions if - not isinstance(p, IDLImplementsStatement)] - for production in implementsStatements: + not isinstance(p, IDLIncludesStatement)] + for production in includesStatements: production.finish(self.globalScope()) for production in otherStatements: production.finish(self.globalScope()) @@ -6831,14 +7680,14 @@ def main(): f = open(fullPath, 'rb') lines = f.readlines() f.close() - print fullPath + print(fullPath) parser.parse(''.join(lines), fullPath) parser.finish() - except WebIDLError, e: + except WebIDLError as e: if options.verbose_errors: traceback.print_exc() else: - print e + print(e) if __name__ == '__main__': main() diff --git a/components/script/dom/bindings/codegen/parser/abstract.patch b/components/script/dom/bindings/codegen/parser/abstract.patch index a8e2ddcf759..180e345b61b 100644 --- a/components/script/dom/bindings/codegen/parser/abstract.patch +++ b/components/script/dom/bindings/codegen/parser/abstract.patch @@ -1,11 +1,11 @@ --- WebIDL.py +++ WebIDL.py -@@ -1416,7 +1416,8 @@ - identifier == "LegacyEventInit" or - identifier == "ProbablyShortLivingObject" or +@@ -1883,7 +1883,8 @@ class IDLInterface(IDLInterfaceOrNamespace): identifier == "LegacyUnenumerableNamedProperties" or -- identifier == "NonOrdinaryGetPrototypeOf"): -+ identifier == "NonOrdinaryGetPrototypeOf" or + identifier == "RunConstructorInCallerCompartment" or + identifier == "WantsEventListenerHooks" or +- identifier == "Serializable"): ++ identifier == "Serializable" or + identifier == "Abstract"): # Known extended attributes that do not take values if not attr.noArguments(): diff --git a/components/script/dom/bindings/codegen/parser/callback-location.patch b/components/script/dom/bindings/codegen/parser/callback-location.patch index fac5d035801..b7a308df631 100644 --- a/components/script/dom/bindings/codegen/parser/callback-location.patch +++ b/components/script/dom/bindings/codegen/parser/callback-location.patch @@ -1,17 +1,15 @@ -diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py -index da32340..81c52b7 100644 --- WebIDL.py +++ WebIDL.py -@@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType): - return typedefType.complete(scope) +@@ -2283,7 +2283,7 @@ class IDLUnresolvedType(IDLType): + return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes) elif obj.isCallback() and not obj.isInterface(): assert self.name.name == obj.identifier.name - return IDLCallbackType(self.location, obj) + return IDLCallbackType(obj.location, obj) - - if self._promiseInnerType and not self._promiseInnerType.isComplete(): - self._promiseInnerType = self._promiseInnerType.complete(scope) -@@ -6521,7 +6521,7 @@ class Parser(Tokenizer): + + name = self.name.resolve(scope, None) + return IDLWrapperType(self.location, obj) +@@ -6854,7 +6854,7 @@ class Parser(Tokenizer): type = IDLTypedefType(self.getLocation(p, 1), obj.innerType, obj.identifier.name) elif obj.isCallback() and not obj.isInterface(): @@ -19,4 +17,4 @@ index da32340..81c52b7 100644 + type = IDLCallbackType(obj.location, obj) else: type = IDLWrapperType(self.getLocation(p, 1), p[1]) - p[0] = self.handleModifiers(type, p[2]) + p[0] = self.handleNullable(type, p[2]) diff --git a/components/script/dom/bindings/codegen/parser/debug.patch b/components/script/dom/bindings/codegen/parser/debug.patch index ca391c38273..a4f8739000d 100644 --- a/components/script/dom/bindings/codegen/parser/debug.patch +++ b/components/script/dom/bindings/codegen/parser/debug.patch @@ -1,6 +1,6 @@ --- WebIDL.py +++ WebIDL.py -@@ -6823,7 +6823,8 @@ class Parser(Tokenizer): +@@ -7382,7 +7382,8 @@ class Parser(Tokenizer): self.parser = yacc.yacc(module=self, outputdir=outputdir, tabmodule='webidlyacc', diff --git a/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch b/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch new file mode 100644 index 00000000000..210134d8ca6 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch @@ -0,0 +1,11 @@ +--- WebIDL.py ++++ WebIDL.py +@@ -3490,7 +3490,7 @@ class IDLBuiltinType(IDLType): + [self.location, attribute.location]) + assert not self.nullable() + if not attribute.hasValue(): +- raise WebIDLError("[TreatNullAs] must take an identifier argument" ++ raise WebIDLError("[TreatNullAs] must take an identifier argument", + [attribute.location]) + value = attribute.value() + if value != 'EmptyString': diff --git a/components/script/dom/bindings/codegen/parser/inline.patch b/components/script/dom/bindings/codegen/parser/inline.patch index 5d1056d2b58..46971ce5067 100644 --- a/components/script/dom/bindings/codegen/parser/inline.patch +++ b/components/script/dom/bindings/codegen/parser/inline.patch @@ -1,12 +1,12 @@ --- WebIDL.py +++ WebIDL.py -@@ -1695,7 +1695,8 @@ class IDLInterface(IDLInterfaceOrNamespace): - identifier == "ProbablyShortLivingObject" or - identifier == "LegacyUnenumerableNamedProperties" or - identifier == "NonOrdinaryGetPrototypeOf" or +@@ -1884,7 +1884,8 @@ class IDLInterface(IDLInterfaceOrNamespace): + identifier == "RunConstructorInCallerCompartment" or + identifier == "WantsEventListenerHooks" or + identifier == "Serializable" or - identifier == "Abstract"): + identifier == "Abstract" or + identifier == "Inline"): # Known extended attributes that do not take values if not attr.noArguments(): - raise WebIDLError("[%s] must take no arguments" % identifier,
\ No newline at end of file + raise WebIDLError("[%s] must take no arguments" % identifier, diff --git a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch b/components/script/dom/bindings/codegen/parser/pref-main-thread.patch deleted file mode 100644 index 7be2dcbfc5e..00000000000 --- a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch +++ /dev/null @@ -1,28 +0,0 @@ ---- WebIDL.py -+++ WebIDL.py -@@ -1239,12 +1239,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): - alias, - [member.location, m.location]) - -- if (self.getExtendedAttribute("Pref") and -- self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): -- raise WebIDLError("[Pref] used on an interface that is not %s-only" % -- self.parentScope.primaryGlobalName, -- [self.location]) -- - # Conditional exposure makes no sense for interfaces with no - # interface object, unless they're navigator properties. - # And SecureContext makes sense for interfaces with no interface object, -@@ -3459,12 +3453,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): - IDLExposureMixins.finish(self, scope) - - def validate(self): -- if (self.getExtendedAttribute("Pref") and -- self.exposureSet != set([self._globalScope.primaryGlobalName])): -- raise WebIDLError("[Pref] used on an interface member that is not " -- "%s-only" % self._globalScope.primaryGlobalName, -- [self.location]) -- - if self.isAttr() or self.isMethod(): - if self.affects == "Everything" and self.dependsOn != "Everything": - raise WebIDLError("Interface member is flagged as affecting " diff --git a/components/script/dom/bindings/codegen/parser/runtests.py b/components/script/dom/bindings/codegen/parser/runtests.py index b8d45ef31b5..0599bf55fec 100644 --- a/components/script/dom/bindings/codegen/parser/runtests.py +++ b/components/script/dom/bindings/codegen/parser/runtests.py @@ -1,6 +1,6 @@ # 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/. +# file, You can obtain one at https://mozilla.org/MPL/2.0/. import os, sys import glob @@ -62,7 +62,7 @@ def run_tests(tests, verbose): harness.start() try: _test.WebIDLTest.__call__(WebIDL.Parser(), harness) - except Exception, ex: + except Exception as ex: print("TEST-UNEXPECTED-FAIL | Unhandled exception in test %s: %s" % (testpath, ex)) traceback.print_exc() finally: diff --git a/components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py b/components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py new file mode 100644 index 00000000000..e190f617e26 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_argument_keywords.py @@ -0,0 +1,17 @@ +def WebIDLTest(parser, harness): + parser.parse(""" + interface Foo { + void foo(object constructor); + }; + """) + + results = parser.finish() + harness.check(len(results), 1, "Should have an interface"); + iface = results[0]; + harness.check(len(iface.members), 1, "Should have an operation"); + operation = iface.members[0]; + harness.check(len(operation.signatures()), 1, "Should have one signature"); + (retval, args) = operation.signatures()[0]; + harness.check(len(args), 1, "Should have an argument"); + harness.check(args[0].identifier.name, "constructor", + "Should have an identifier named 'constructor'"); diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attr.py b/components/script/dom/bindings/codegen/parser/tests/test_attr.py index ad7aabc1918..35f680aaa82 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_attr.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_attr.py @@ -133,7 +133,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should not allow [SetterThrows] on readonly attributes") @@ -146,7 +146,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should spell [Throws] correctly") @@ -159,7 +159,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should not allow [SameObject] on attributes not of interface type") @@ -172,6 +172,6 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(not threw, "Should allow [SameObject] on attributes of interface type") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py new file mode 100644 index 00000000000..ff08791d16f --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py @@ -0,0 +1,415 @@ +# Import the WebIDL module, so we can do isinstance checks and whatnot +import WebIDL + +def WebIDLTest(parser, harness): + # Basic functionality + threw = False + try: + parser.parse(""" + typedef [EnforceRange] long Foo; + typedef [Clamp] long Bar; + typedef [TreatNullAs=EmptyString] DOMString Baz; + dictionary A { + required [EnforceRange] long a; + required [Clamp] long b; + [ChromeOnly, EnforceRange] long c; + Foo d; + }; + interface B { + attribute Foo typedefFoo; + attribute [EnforceRange] long foo; + attribute [Clamp] long bar; + attribute [TreatNullAs=EmptyString] DOMString baz; + void method([EnforceRange] long foo, [Clamp] long bar, + [TreatNullAs=EmptyString] DOMString baz); + void method2(optional [EnforceRange] long foo, optional [Clamp] long bar, + optional [TreatNullAs=EmptyString] DOMString baz); + }; + interface C { + attribute [EnforceRange] long? foo; + attribute [Clamp] long? bar; + void method([EnforceRange] long? foo, [Clamp] long? bar); + void method2(optional [EnforceRange] long? foo, optional [Clamp] long? bar); + }; + interface Setlike { + setlike<[Clamp] long>; + }; + interface Maplike { + maplike<[Clamp] long, [EnforceRange] long>; + }; + interface Iterable { + iterable<[Clamp] long, [EnforceRange] long>; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(not threw, "Should not have thrown on parsing normal") + if not threw: + harness.check(results[0].innerType.hasEnforceRange(), True, "Foo is [EnforceRange]") + harness.check(results[1].innerType.hasClamp(), True, "Bar is [Clamp]") + harness.check(results[2].innerType.treatNullAsEmpty, True, "Baz is [TreatNullAs=EmptyString]") + A = results[3] + harness.check(A.members[0].type.hasEnforceRange(), True, "A.a is [EnforceRange]") + harness.check(A.members[1].type.hasClamp(), True, "A.b is [Clamp]") + harness.check(A.members[2].type.hasEnforceRange(), True, "A.c is [EnforceRange]") + harness.check(A.members[3].type.hasEnforceRange(), True, "A.d is [EnforceRange]") + B = results[4] + harness.check(B.members[0].type.hasEnforceRange(), True, "B.typedefFoo is [EnforceRange]") + harness.check(B.members[1].type.hasEnforceRange(), True, "B.foo is [EnforceRange]") + harness.check(B.members[2].type.hasClamp(), True, "B.bar is [Clamp]") + harness.check(B.members[3].type.treatNullAsEmpty, True, "B.baz is [TreatNullAs=EmptyString]") + method = B.members[4].signatures()[0][1] + harness.check(method[0].type.hasEnforceRange(), True, "foo argument of method is [EnforceRange]") + harness.check(method[1].type.hasClamp(), True, "bar argument of method is [Clamp]") + harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method is [TreatNullAs=EmptyString]") + method2 = B.members[5].signatures()[0][1] + harness.check(method[0].type.hasEnforceRange(), True, "foo argument of method2 is [EnforceRange]") + harness.check(method[1].type.hasClamp(), True, "bar argument of method2 is [Clamp]") + harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method2 is [TreatNullAs=EmptyString]") + C = results[5] + harness.ok(C.members[0].type.nullable(), "C.foo is nullable") + harness.ok(C.members[0].type.hasEnforceRange(), "C.foo has [EnforceRange]") + harness.ok(C.members[1].type.nullable(), "C.bar is nullable") + harness.ok(C.members[1].type.hasClamp(), "C.bar has [Clamp]") + method = C.members[2].signatures()[0][1] + harness.ok(method[0].type.nullable(), "foo argument of method is nullable") + harness.ok(method[0].type.hasEnforceRange(), "foo argument of method has [EnforceRange]") + harness.ok(method[1].type.nullable(), "bar argument of method is nullable") + harness.ok(method[1].type.hasClamp(), "bar argument of method has [Clamp]") + method2 = C.members[3].signatures()[0][1] + harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable") + harness.ok(method2[0].type.hasEnforceRange(), "foo argument of method2 has [EnforceRange]") + harness.ok(method2[1].type.nullable(), "bar argument of method2 is nullable") + harness.ok(method2[1].type.hasClamp(), "bar argument of method2 has [Clamp]") + + # Test [AllowShared] + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [AllowShared] ArrayBufferView Foo; + dictionary A { + required [AllowShared] ArrayBufferView a; + [ChromeOnly, AllowShared] ArrayBufferView b; + Foo c; + }; + interface B { + attribute Foo typedefFoo; + attribute [AllowShared] ArrayBufferView foo; + void method([AllowShared] ArrayBufferView foo); + void method2(optional [AllowShared] ArrayBufferView foo); + }; + interface C { + attribute [AllowShared] ArrayBufferView? foo; + void method([AllowShared] ArrayBufferView? foo); + void method2(optional [AllowShared] ArrayBufferView? foo); + }; + interface Setlike { + setlike<[AllowShared] ArrayBufferView>; + }; + interface Maplike { + maplike<[Clamp] long, [AllowShared] ArrayBufferView>; + }; + interface Iterable { + iterable<[Clamp] long, [AllowShared] ArrayBufferView>; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(not threw, "Should not have thrown on parsing normal") + if not threw: + harness.ok(results[0].innerType.hasAllowShared(), "Foo is [AllowShared]") + A = results[1] + harness.ok(A.members[0].type.hasAllowShared(), "A.a is [AllowShared]") + harness.ok(A.members[1].type.hasAllowShared(), "A.b is [AllowShared]") + harness.ok(A.members[2].type.hasAllowShared(), "A.c is [AllowShared]") + B = results[2] + harness.ok(B.members[0].type.hasAllowShared(), "B.typedefFoo is [AllowShared]") + harness.ok(B.members[1].type.hasAllowShared(), "B.foo is [AllowShared]") + method = B.members[2].signatures()[0][1] + harness.ok(method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]") + method2 = B.members[3].signatures()[0][1] + harness.ok(method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]") + C = results[3] + harness.ok(C.members[0].type.nullable(), "C.foo is nullable") + harness.ok(C.members[0].type.hasAllowShared(), "C.foo is [AllowShared]") + method = C.members[1].signatures()[0][1] + harness.ok(method[0].type.nullable(), "foo argument of method is nullable") + harness.ok(method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]") + method2 = C.members[2].signatures()[0][1] + harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable") + harness.ok(method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]") + + ATTRIBUTES = [("[Clamp]", "long"), ("[EnforceRange]", "long"), + ("[TreatNullAs=EmptyString]", "DOMString"), ("[AllowShared]", "ArrayBufferView")] + TEMPLATES = [ + ("required dictionary members", """ + dictionary Foo { + %s required %s foo; + }; + """), + ("optional arguments", """ + interface Foo { + void foo(%s optional %s foo); + }; + """), + ("typedefs", """ + %s typedef %s foo; + """), + ("attributes", """ + interface Foo { + %s attribute %s foo; + }; + """), + ("readonly attributes", """ + interface Foo { + readonly attribute %s %s foo; + }; + """), + ("readonly unresolved attributes", """ + interface Foo { + readonly attribute Bar baz; + }; + typedef %s %s Bar; + """), + ("method", """ + interface Foo { + %s %s foo(); + }; + """), + ("interface",""" + %s + interface Foo { + attribute %s foo; + }; + """), + ("partial interface",""" + interface Foo { + void foo(); + }; + %s + partial interface Foo { + attribute %s bar; + }; + """), + ("interface mixin",""" + %s + interface mixin Foo { + attribute %s foo; + }; + """), + ("namespace",""" + %s + namespace Foo { + attribute %s foo; + }; + """), + ("partial namespace",""" + namespace Foo { + void foo(); + }; + %s + partial namespace Foo { + attribute %s bar; + }; + """), + ("dictionary",""" + %s + dictionary Foo { + %s foo; + }; + """) + ]; + + for (name, template) in TEMPLATES: + parser = parser.reset() + threw = False + try: + parser.parse(template % ("", "long")) + parser.finish() + except: + threw = True + harness.ok(not threw, "Template for %s parses without attributes" % name) + for (attribute, type) in ATTRIBUTES: + parser = parser.reset() + threw = False + try: + parser.parse(template % (attribute, type)) + parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow %s on %s" % (attribute, name)) + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp, EnforceRange] long Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange, Clamp] long Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]") + + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp] long Foo; + typedef [EnforceRange] Foo bar; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange] long Foo; + typedef [Clamp] Foo bar; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs") + + TYPES = ["DOMString", "unrestricted float", "float", "unrestricted double", "double"] + + for type in TYPES: + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp] %s Foo; + """ % type) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [Clamp] on %s" % type) + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange] %s Foo; + """ % type) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [EnforceRange] on %s" % type) + + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [TreatNullAs=EmptyString] long Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [TreatNullAs] on long") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [TreatNullAs=EmptyString] JSString Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [TreatNullAs] on JSString") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [TreatNullAs=EmptyString] DOMString? Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [TreatNullAs] on nullable DOMString") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [AllowShared] DOMString Foo; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[AllowShared] only allowed on buffer source types") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [AllowShared=something] ArrayBufferView Foo; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[AllowShared] must take no arguments") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + void foo([Clamp] Bar arg); + }; + typedef long Bar; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow type attributes on unresolved types") + harness.check(results[0].members[0].signatures()[0][1][0].type.hasClamp(), True, + "Unresolved types with type attributes should correctly resolve with attributes") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + void foo(Bar arg); + }; + typedef [Clamp] long Bar; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow type attributes on typedefs") + harness.check(results[0].members[0].signatures()[0][1][0].type.hasClamp(), True, + "Unresolved types that resolve to typedefs with attributes should correctly resolve with attributes") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback.py b/components/script/dom/bindings/codegen/parser/tests/test_callback.py index 4dfda1c3c76..c304d085ce5 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_callback.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_callback.py @@ -32,3 +32,6 @@ def WebIDLTest(parser, harness): harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type") harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type") harness.ok(t.isCallback(), "Attr has the right type") + + callback = results[1] + harness.ok(not callback.isConstructor(), "callback is not constructor") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py new file mode 100644 index 00000000000..4999deef623 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_callback_constructor.py @@ -0,0 +1,63 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + interface TestCallbackConstructor { + attribute CallbackConstructorType? constructorAttribute; + }; + + callback constructor CallbackConstructorType = TestCallbackConstructor (unsigned long arg); + """) + + results = parser.finish() + + harness.ok(True, "TestCallbackConstructor interface parsed without error.") + harness.check(len(results), 2, "Should be two productions.") + iface = results[0] + harness.ok(isinstance(iface, WebIDL.IDLInterface), + "Should be an IDLInterface") + harness.check(iface.identifier.QName(), "::TestCallbackConstructor", "Interface has the right QName") + harness.check(iface.identifier.name, "TestCallbackConstructor", "Interface has the right name") + harness.check(len(iface.members), 1, "Expect %s members" % 1) + + attr = iface.members[0] + harness.ok(isinstance(attr, WebIDL.IDLAttribute), + "Should be an IDLAttribute") + harness.ok(attr.isAttr(), "Should be an attribute") + harness.ok(not attr.isMethod(), "Attr is not an method") + harness.ok(not attr.isConst(), "Attr is not a const") + harness.check(attr.identifier.QName(), "::TestCallbackConstructor::constructorAttribute", "Attr has the right QName") + harness.check(attr.identifier.name, "constructorAttribute", "Attr has the right name") + t = attr.type + harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type") + harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type") + harness.ok(t.isCallback(), "Attr has the right type") + + callback = results[1] + harness.ok(callback.isConstructor(), "Callback is constructor") + + parser.reset() + threw = False + try: + parser.parse(""" + [TreatNonObjectAsNull] + callback constructor CallbackConstructorType = object (); + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should throw on TreatNonObjectAsNull callback constructors") + + parser.reset() + threw = False + try: + parser.parse(""" + [MOZ_CAN_RUN_SCRIPT_BOUNDARY] + callback constructor CallbackConstructorType = object (); + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not permit MOZ_CAN_RUN_SCRIPT_BOUNDARY callback constructors") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py b/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py new file mode 100644 index 00000000000..f726907c2fc --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py @@ -0,0 +1,133 @@ +def WebIDLTest(parser, harness): + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions(DOMString a)] void foo(boolean arg2); + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] with an argument") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions(DOMString b)] readonly attribute boolean bar; + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] with an argument") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] attribute boolean bar; + }; + """) + + results = parser.finish() + except Exception as e: + harness.ok(False, "Shouldn't have thrown for [CEReactions] used on writable attribute. %s" % e) + threw = True + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] void foo(boolean arg2); + }; + """) + + results = parser.finish() + except Exception as e: + harness.ok(False, "Shouldn't have thrown for [CEReactions] used on regular operations. %s" % e) + threw = True + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] readonly attribute boolean A; + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] used on a readonly attribute") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [CEReactions] + interface Foo { + } + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown for [CEReactions] used on a interface") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] getter any(DOMString name); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a named getter") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] legacycaller double compute(double x); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a legacycaller") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + [CEReactions] stringifier DOMString (); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Should have thrown for [CEReactions] used on a stringifier") + diff --git a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py index 433b7e501a4..8420f2ee4e0 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py @@ -44,8 +44,8 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, exception: - pass + except Exception as e: + exception = e harness.ok(exception, "Should have thrown.") harness.check(exception.message, @@ -70,8 +70,8 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, exception: - pass + except Exception as e: + exception = e harness.ok(exception, "Should have thrown (2).") harness.check(exception.message, @@ -100,8 +100,8 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, exception: - pass + except Exception as e: + exception = e harness.ok(exception, "Should have thrown (3).") harness.check(exception.message, diff --git a/components/script/dom/bindings/codegen/parser/tests/test_const.py b/components/script/dom/bindings/codegen/parser/tests/test_const.py index 80b6fb0e9c8..918f284a226 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_const.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_const.py @@ -12,9 +12,6 @@ expected = [ ("::TestConsts::ll", "ll", "LongLong", -8), ("::TestConsts::t", "t", "Boolean", True), ("::TestConsts::f", "f", "Boolean", False), - ("::TestConsts::n", "n", "BooleanOrNull", None), - ("::TestConsts::nt", "nt", "BooleanOrNull", True), - ("::TestConsts::nf", "nf", "BooleanOrNull", False), ("::TestConsts::fl", "fl", "Float", 0.2), ("::TestConsts::db", "db", "Double", 0.2), ("::TestConsts::ufl", "ufl", "UnrestrictedFloat", 0.2), @@ -39,9 +36,6 @@ def WebIDLTest(parser, harness): const long long ll = -010; const boolean t = true; const boolean f = false; - const boolean? n = null; - const boolean? nt = true; - const boolean? nf = false; const float fl = 0.2; const double db = 0.2; const unrestricted float ufl = 0.2; @@ -78,3 +72,16 @@ def WebIDLTest(parser, harness): "Const's value has the same type as the type") harness.check(const.value.value, value, "Const value has the right value.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestConsts { + const boolean? zero = 0; + }; + """) + parser.finish() + except: + threw = True + harness.ok(threw, "Nullable types are not allowed for consts.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py index 348204c7dc1..83e1f4fc34f 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py @@ -11,9 +11,9 @@ def WebIDLTest(parser, harness): harness.check(argument.variadic, variadic, "Argument has the right variadic value") def checkMethod(method, QName, name, signatures, - static=True, getter=False, setter=False, creator=False, - deleter=False, legacycaller=False, stringifier=False, - chromeOnly=False): + static=True, getter=False, setter=False, deleter=False, + legacycaller=False, stringifier=False, chromeOnly=False, + htmlConstructor=False, secureContext=False, pref=None, func=None): harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod") harness.ok(method.isMethod(), "Method is a method") @@ -24,12 +24,15 @@ def WebIDLTest(parser, harness): harness.check(method.isStatic(), static, "Method has the correct static value") harness.check(method.isGetter(), getter, "Method has the correct getter value") harness.check(method.isSetter(), setter, "Method has the correct setter value") - harness.check(method.isCreator(), creator, "Method has the correct creator value") harness.check(method.isDeleter(), deleter, "Method has the correct deleter value") harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value") harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value") harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly") + harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value") harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures") + harness.check(method.getExtendedAttribute("Pref"), pref, "Method has the correct pref value") + harness.check(method.getExtendedAttribute("Func"), func, "Method has the correct func value") + harness.check(method.getExtendedAttribute("SecureContext") is not None, secureContext, "Method has the correct SecureContext value") sigpairs = zip(method.signatures(), signatures) for (gotSignature, expectedSignature) in sigpairs: @@ -43,45 +46,116 @@ def WebIDLTest(parser, harness): (QName, name, type, optional, variadic) = expectedArgs[i] checkArgument(gotArgs[i], QName, name, type, optional, variadic) + def checkResults(results): + harness.check(len(results), 3, "Should be three productions") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + harness.ok(isinstance(results[1], WebIDL.IDLInterface), + "Should be an IDLInterface") + harness.ok(isinstance(results[2], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor", + "constructor", [("TestConstructorNoArgs (Wrapper)", [])]) + harness.check(len(results[0].members), 0, + "TestConstructorNoArgs should not have members") + checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor", + "constructor", + [("TestConstructorWithArgs (Wrapper)", + [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])]) + harness.check(len(results[1].members), 0, + "TestConstructorWithArgs should not have members") + checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor", + "constructor", + [("TestConstructorOverloads (Wrapper)", + [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]), + ("TestConstructorOverloads (Wrapper)", + [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])]) + harness.check(len(results[2].members), 0, + "TestConstructorOverloads should not have members") + parser.parse(""" - [Constructor] interface TestConstructorNoArgs { + constructor(); }; - [Constructor(DOMString name)] interface TestConstructorWithArgs { + constructor(DOMString name); }; - [Constructor(object foo), Constructor(boolean bar)] interface TestConstructorOverloads { + constructor(object foo); + constructor(boolean bar); + }; + """) + results = parser.finish() + checkResults(results) + + parser = parser.reset() + parser.parse(""" + interface TestPrefConstructor { + [Pref="dom.webidl.test1"] constructor(); }; """) results = parser.finish() - harness.check(len(results), 3, "Should be three productions") + harness.check(len(results), 1, "Should be one production") harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface") - harness.ok(isinstance(results[1], WebIDL.IDLInterface), + + checkMethod(results[0].ctor(), "::TestPrefConstructor::constructor", + "constructor", [("TestPrefConstructor (Wrapper)", [])], + pref=["dom.webidl.test1"]) + + parser = parser.reset() + parser.parse(""" + interface TestChromeOnlyConstructor { + [ChromeOnly] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestChromeOnlyConstructor::constructor", + "constructor", [("TestChromeOnlyConstructor (Wrapper)", [])], + chromeOnly=True) + + parser = parser.reset() + parser.parse(""" + interface TestSCConstructor { + [SecureContext] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface") - harness.ok(isinstance(results[2], WebIDL.IDLInterface), + + checkMethod(results[0].ctor(), "::TestSCConstructor::constructor", + "constructor", [("TestSCConstructor (Wrapper)", [])], + secureContext=True) + + parser = parser.reset() + parser.parse(""" + interface TestFuncConstructor { + [Func="Document::IsWebAnimationsEnabled"] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface") - checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor", - "constructor", [("TestConstructorNoArgs (Wrapper)", [])]) - checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor", - "constructor", - [("TestConstructorWithArgs (Wrapper)", - [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])]) - checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor", - "constructor", - [("TestConstructorOverloads (Wrapper)", - [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]), - ("TestConstructorOverloads (Wrapper)", - [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])]) + checkMethod(results[0].ctor(), "::TestFuncConstructor::constructor", + "constructor", [("TestFuncConstructor (Wrapper)", [])], + func=["Document::IsWebAnimationsEnabled"]) parser = parser.reset() parser.parse(""" - [ChromeConstructor()] - interface TestChromeConstructor { + interface TestPrefChromeOnlySCFuncConstructor { + [ChromeOnly, Pref="dom.webidl.test1", SecureContext, Func="Document::IsWebAnimationsEnabled"] + constructor(); }; """) results = parser.finish() @@ -89,21 +163,262 @@ def WebIDLTest(parser, harness): harness.ok(isinstance(results[0], WebIDL.IDLInterface), "Should be an IDLInterface") - checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor", - "constructor", [("TestChromeConstructor (Wrapper)", [])], - chromeOnly=True) + checkMethod(results[0].ctor(), "::TestPrefChromeOnlySCFuncConstructor::constructor", + "constructor", [("TestPrefChromeOnlySCFuncConstructor (Wrapper)", [])], + func=["Document::IsWebAnimationsEnabled"], pref=["dom.webidl.test1"], + chromeOnly=True, secureContext=True) + + parser = parser.reset() + parser.parse(""" + interface TestHTMLConstructor { + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor", + "constructor", [("TestHTMLConstructor (Wrapper)", [])], + htmlConstructor=True) parser = parser.reset() threw = False try: parser.parse(""" - [Constructor(), - ChromeConstructor(DOMString a)] - interface TestChromeConstructor { + interface TestChromeOnlyConstructor { + constructor() + [ChromeOnly] constructor(DOMString a); }; """) results = parser.finish() except: threw = True - harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor") + harness.ok(threw, "Can't have both a constructor and a ChromeOnly constructor") + + # Test HTMLConstructor with argument + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorWithArgs { + [HTMLConstructor] constructor(DOMString a); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "HTMLConstructor should take no argument") + + # Test HTMLConstructor on a callback interface + parser = parser.reset() + threw = False + try: + parser.parse(""" + callback interface TestHTMLConstructorOnCallbackInterface { + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "HTMLConstructor can't be used on a callback interface") + + # Test HTMLConstructor and constructor operation + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + constructor(); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Can't have both a constructor and a HTMLConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + [Throws] + constructor(); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a throwing constructor and a HTMLConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + constructor(DOMString a); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a HTMLConstructor and a constructor operation") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + [Throws] + constructor(DOMString a); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a HTMLConstructor and a throwing constructor " + "operation") + + # Test HTMLConstructor and [ChromeOnly] constructor operation + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + [ChromeOnly] + constructor(); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a ChromeOnly constructor and a HTMLConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + [Throws, ChromeOnly] + constructor(); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a throwing chromeonly constructor and a " + "HTMLConstructor") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + [ChromeOnly] + constructor(DOMString a); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a HTMLConstructor and a chromeonly constructor " + "operation") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestHTMLConstructorAndConstructor { + [Throws, ChromeOnly] + constructor(DOMString a); + [HTMLConstructor] constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have both a HTMLConstructor and a throwing chromeonly " + "constructor operation") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [NoInterfaceObject] + interface InterfaceWithoutInterfaceObject { + constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have a constructor operation on a [NoInterfaceObject] " + "interface") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface InterfaceWithPartial { + }; + + partial interface InterfaceWithPartial { + constructor(); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have a constructor operation on a partial interface") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface InterfaceWithMixin { + }; + + interface mixin Mixin { + constructor(); + }; + + InterfaceWithMixin includes Mixin + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Can't have a constructor operation on a mixin") + diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py new file mode 100644 index 00000000000..b7eabb1e35b --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor_global.py @@ -0,0 +1,63 @@ +import traceback + +def WebIDLTest(parser, harness): + threw = False + try: + parser.parse(""" + [Global, Exposed=TestConstructorGlobal] + interface TestConstructorGlobal { + constructor(); + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Global, Exposed=TestNamedConstructorGlobal, + NamedConstructor=FooBar] + interface TestNamedConstructorGlobal { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [NamedConstructor=FooBar, Global, + Exposed=TestNamedConstructorGlobal] + interface TestNamedConstructorGlobal { + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Global, Exposed=TestHTMLConstructorGlobal] + interface TestHTMLConstructorGlobal { + [HTMLConstructor] constructor(); + }; + """) + + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should have thrown.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py index 2b09ae71e69..24cc36066cd 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor_no_interface_object.py @@ -2,8 +2,9 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Constructor, NoInterfaceObject] + [NoInterfaceObject] interface TestConstructorNoInterfaceObject { + constructor(); }; """) @@ -13,11 +14,23 @@ def WebIDLTest(parser, harness): harness.ok(threw, "Should have thrown.") + parser = parser.reset() + + parser.parse(""" + [NoInterfaceObject, NamedConstructor=FooBar] + interface TestNamedConstructorNoInterfaceObject { + }; + """) + + # Test HTMLConstructor and NoInterfaceObject + parser = parser.reset() + threw = False try: parser.parse(""" - [NoInterfaceObject, Constructor] - interface TestConstructorNoInterfaceObject { + [NoInterfaceObject] + interface TestHTMLConstructorNoInterfaceObject { + [HTMLConstructor] constructor(); }; """) @@ -26,11 +39,3 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "Should have thrown.") - - parser = parser.reset() - - parser.parse(""" - [NoInterfaceObject, NamedConstructor=FooBar] - interface TestNamedConstructorNoInterfaceObject { - }; - """) diff --git a/components/script/dom/bindings/codegen/parser/tests/test_date.py b/components/script/dom/bindings/codegen/parser/tests/test_date.py deleted file mode 100644 index 2bdfc95e14f..00000000000 --- a/components/script/dom/bindings/codegen/parser/tests/test_date.py +++ /dev/null @@ -1,15 +0,0 @@ -def WebIDLTest(parser, harness): - parser.parse(""" - interface WithDates { - attribute Date foo; - void bar(Date arg); - void baz(sequence<Date> arg); - }; - """) - - results = parser.finish() - harness.ok(results[0].members[0].type.isDate(), "Should have Date") - harness.ok(results[0].members[1].signatures()[0][1][0].type.isDate(), - "Should have Date argument") - harness.ok(not results[0].members[2].signatures()[0][1][0].type.isDate(), - "Should have non-Date argument") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py index 2c0fa61239d..3cad3022389 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_dictionary.py @@ -26,6 +26,31 @@ def WebIDLTest(parser, harness): harness.check(dict2.members[1].identifier.name, "child", "'a' really comes before 'c'") + # Test partial dictionary. + parser = parser.reset(); + parser.parse(""" + dictionary A { + long c; + long g; + }; + partial dictionary A { + long h; + long d; + }; + """) + results = parser.finish() + + dict1 = results[0]; + harness.check(len(dict1.members), 4, "Dict1 has four members") + harness.check(dict1.members[0].identifier.name, "c", + "c should be first") + harness.check(dict1.members[1].identifier.name, "d", + "d should come after c") + harness.check(dict1.members[2].identifier.name, "g", + "g should come after d") + harness.check(dict1.members[3].identifier.name, "h", + "h should be last") + # Now reset our parser parser = parser.reset() threw = False @@ -42,6 +67,24 @@ def WebIDLTest(parser, harness): harness.ok(threw, "Should not allow name duplication in a dictionary") + # Test no name duplication across normal and partial dictionary. + parser = parser.reset(); + threw = False + try: + parser.parse(""" + dictionary A { + long prop = 5; + }; + partial dictionary A { + long prop; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow name duplication across normal and partial dictionary") + # Now reset our parser again parser = parser.reset() threw = False @@ -131,6 +174,22 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { + void doFoo(optional A arg); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Trailing dictionary arg must have a default value") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + }; + interface X { void doFoo((A or DOMString) arg); }; """) @@ -148,6 +207,23 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { + void doFoo(optional (A or DOMString) arg); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Trailing union arg containing a dictionary must have a default value") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + }; + interface X { void doFoo(A arg1, optional long arg2); }; """) @@ -164,6 +240,22 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { + void doFoo(optional A arg1, optional long arg2); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Dictionary arg followed by optional arg must have default value") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + }; + interface X { void doFoo(A arg1, optional long arg2, long arg3); }; """) @@ -193,6 +285,24 @@ def WebIDLTest(parser, harness): "be optional") parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + }; + interface X { + void doFoo(optional (A or DOMString) arg1, optional long arg2); + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, + "Union arg containing dictionary followed by optional arg must " + "have a default value") + + parser = parser.reset() parser.parse(""" dictionary A { }; @@ -210,14 +320,75 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { - void doFoo(optional A? arg1); + void doFoo(optional A? arg1 = {}); }; """) results = parser.finish() - except: - threw = True + except Exception as x: + threw = x + + harness.ok(threw, "Optional dictionary arg must not be nullable") + harness.ok("nullable" in str(threw), + "Must have the expected exception for optional nullable dictionary arg") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + required long x; + }; + interface X { + void doFoo(A? arg1); + }; + """) + results = parser.finish() + except Exception as x: + threw = x + + harness.ok(threw, "Required dictionary arg must not be nullable") + harness.ok("nullable" in str(threw), + "Must have the expected exception for required nullable " + "dictionary arg") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + }; + interface X { + void doFoo(optional (A or long)? arg1 = {}); + }; + """) + results = parser.finish() + except Exception as x: + threw = x + + harness.ok(threw, "Dictionary arg must not be in an optional nullable union") + harness.ok("nullable" in str(threw), + "Must have the expected exception for optional nullable union " + "arg containing dictionary") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + required long x; + }; + interface X { + void doFoo((A or long)? arg1); + }; + """) + results = parser.finish() + except Exception as x: + threw = x - harness.ok(threw, "Dictionary arg must not be nullable") + harness.ok(threw, "Dictionary arg must not be in a required nullable union") + harness.ok("nullable" in str(threw), + "Must have the expected exception for required nullable union " + "arg containing dictionary") parser = parser.reset() threw = False @@ -226,14 +397,15 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { - void doFoo(optional (A or long)? arg1); + void doFoo(sequence<A?> arg1); }; """) results = parser.finish() except: threw = True - harness.ok(threw, "Dictionary arg must not be in a nullable union") + harness.ok(not threw, + "Nullable union should be allowed in a sequence argument") parser = parser.reset() threw = False @@ -283,7 +455,7 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { - void doFoo(optional A arg); + void doFoo(optional A arg = {}); }; """) results = parser.finish() @@ -294,13 +466,24 @@ def WebIDLTest(parser, harness): dictionary A { }; interface X { - void doFoo(optional (A or DOMString) arg); + void doFoo(optional (A or DOMString) arg = {}); }; """) results = parser.finish() harness.ok(True, "Union arg containing a dictionary should actually parse") parser = parser.reset() + parser.parse(""" + dictionary A { + }; + interface X { + void doFoo(optional (A or DOMString) arg = "abc"); + }; + """) + results = parser.finish() + harness.ok(True, "Union arg containing a dictionary with string default should actually parse") + + parser = parser.reset() threw = False try: parser.parse(""" @@ -553,3 +736,17 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "Only unrestricted values can be initialized to NaN") + + parser = parser.reset(); + threw = False + try: + parser.parse(""" + dictionary Foo { + long module; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(not threw, "Should be able to use 'module' as a dictionary member name") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py index d7780c1ffa1..505b36468d6 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py @@ -3,13 +3,16 @@ def firstArgType(method): def WebIDLTest(parser, harness): parser.parse(""" + // Give our dictionary a required member so we don't need to + // mess with optional and default values. dictionary Dict { + required long member; }; callback interface Foo { }; interface Bar { // Bit of a pain to get things that have dictionary types - void passDict(optional Dict arg); + void passDict(Dict arg); void passFoo(Foo arg); void passNullableUnion((object? or DOMString) arg); void passNullable(Foo? arg); @@ -56,8 +59,6 @@ def WebIDLTest(parser, harness): void passKid(Kid arg); void passParent(Parent arg); void passGrandparent(Grandparent arg); - void passImplemented(Implemented arg); - void passImplementedParent(ImplementedParent arg); void passUnrelated1(Unrelated1 arg); void passUnrelated2(Unrelated2 arg); void passArrayBuffer(ArrayBuffer arg); @@ -67,9 +68,6 @@ def WebIDLTest(parser, harness): interface Kid : Parent {}; interface Parent : Grandparent {}; interface Grandparent {}; - interface Implemented : ImplementedParent {}; - Parent implements Implemented; - interface ImplementedParent {}; interface Unrelated1 {}; interface Unrelated2 {}; """) @@ -151,47 +149,50 @@ def WebIDLTest(parser, harness): # Now let's test our whole distinguishability table argTypes = [ "long", "short", "long?", "short?", "boolean", - "boolean?", "DOMString", "ByteString", "Enum", "Enum2", + "boolean?", "DOMString", "ByteString", "UTF8String", "Enum", "Enum2", "Interface", "Interface?", - "AncestorInterface", "UnrelatedInterface", - "ImplementedInterface", "CallbackInterface", + "AncestorInterface", "UnrelatedInterface", "CallbackInterface", "CallbackInterface?", "CallbackInterface2", - "object", "Callback", "Callback2", "optional Dict", - "optional Dict2", "sequence<long>", "sequence<short>", - "MozMap<object>", "MozMap<Dict>", "MozMap<long>", - "Date", "Date?", "any", - "Promise<any>", "Promise<any>?", - "USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer", - "Uint8Array", "Uint16Array" ] - # When we can parse Date and RegExp, we need to add them here. + "object", "Callback", "Callback2", "Dict", + "Dict2", "sequence<long>", "sequence<short>", + "record<DOMString, object>", + "record<USVString, Dict>", + "record<ByteString, long>", + "record<UTF8String, long>", + "any", "Promise<any>", "Promise<any>?", + "USVString", "JSString", "ArrayBuffer", "ArrayBufferView", + "Uint8Array", "Uint16Array", + "(long or Callback)", "(long or Dict)", + ] # Try to categorize things a bit to keep list lengths down def allBut(list1, list2): return [a for a in list1 if a not in list2 and (a != "any" and a != "Promise<any>" and a != "Promise<any>?")] + unions = [ "(long or Callback)", "(long or Dict)" ] numerics = [ "long", "short", "long?", "short?" ] booleans = [ "boolean", "boolean?" ] primitives = numerics + booleans - nonNumerics = allBut(argTypes, numerics) + nonNumerics = allBut(argTypes, numerics + unions) nonBooleans = allBut(argTypes, booleans) - strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString" ] + strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString", "JSString", "UTF8String" ] nonStrings = allBut(argTypes, strings) nonObjects = primitives + strings objects = allBut(argTypes, nonObjects ) bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"] - sharedBufferSourceTypes = ["SharedArrayBuffer"] interfaces = [ "Interface", "Interface?", "AncestorInterface", - "UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes - nullables = ["long?", "short?", "boolean?", "Interface?", - "CallbackInterface?", "optional Dict", "optional Dict2", - "Date?", "any", "Promise<any>?"] - dates = [ "Date", "Date?" ] + "UnrelatedInterface" ] + bufferSourceTypes + nullables = (["long?", "short?", "boolean?", "Interface?", + "CallbackInterface?", "Dict", "Dict2", + "Date?", "any", "Promise<any>?"] + + allBut(unions, [ "(long or Callback)" ])) sequences = [ "sequence<long>", "sequence<short>" ] - nonUserObjects = nonObjects + interfaces + dates + sequences + nonUserObjects = nonObjects + interfaces + sequences otherObjects = allBut(argTypes, nonUserObjects + ["object"]) notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] + - otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes) - mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ] + otherObjects + sequences + bufferSourceTypes) + records = [ "record<DOMString, object>", "record<USVString, Dict>", + "record<ByteString, long>", "record<UTF8String, long>" ] # JSString not supported in records # Build a representation of the distinguishability table as a dict # of dicts, holding True values where needed, holes elsewhere. @@ -210,7 +211,9 @@ def WebIDLTest(parser, harness): setDistinguishable("boolean?", allBut(nonBooleans, nullables)) setDistinguishable("DOMString", nonStrings) setDistinguishable("ByteString", nonStrings) + setDistinguishable("UTF8String", nonStrings) setDistinguishable("USVString", nonStrings) + setDistinguishable("JSString", nonStrings) setDistinguishable("Enum", nonStrings) setDistinguishable("Enum2", nonStrings) setDistinguishable("Interface", notRelatedInterfaces) @@ -218,24 +221,23 @@ def WebIDLTest(parser, harness): setDistinguishable("AncestorInterface", notRelatedInterfaces) setDistinguishable("UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"])) - setDistinguishable("ImplementedInterface", notRelatedInterfaces) setDistinguishable("CallbackInterface", nonUserObjects) setDistinguishable("CallbackInterface?", allBut(nonUserObjects, nullables)) setDistinguishable("CallbackInterface2", nonUserObjects) setDistinguishable("object", nonObjects) setDistinguishable("Callback", nonUserObjects) setDistinguishable("Callback2", nonUserObjects) - setDistinguishable("optional Dict", allBut(nonUserObjects, nullables)) - setDistinguishable("optional Dict2", allBut(nonUserObjects, nullables)) + setDistinguishable("Dict", allBut(nonUserObjects, nullables)) + setDistinguishable("Dict2", allBut(nonUserObjects, nullables)) setDistinguishable("sequence<long>", allBut(argTypes, sequences + ["object"])) setDistinguishable("sequence<short>", allBut(argTypes, sequences + ["object"])) - setDistinguishable("MozMap<object>", nonUserObjects) - setDistinguishable("MozMap<Dict>", nonUserObjects) - setDistinguishable("MozMap<long>", nonUserObjects) - setDistinguishable("Date", allBut(argTypes, dates + ["object"])) - setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"])) + setDistinguishable("record<DOMString, object>", nonUserObjects) + setDistinguishable("record<USVString, Dict>", nonUserObjects) + # JSString not supported in records + setDistinguishable("record<ByteString, long>", nonUserObjects) + setDistinguishable("record<UTF8String, long>", nonUserObjects) setDistinguishable("any", []) setDistinguishable("Promise<any>", []) setDistinguishable("Promise<any>?", []) @@ -243,7 +245,10 @@ def WebIDLTest(parser, harness): setDistinguishable("ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"])) setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"])) setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"])) - setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"])) + setDistinguishable("(long or Callback)", + allBut(nonUserObjects, numerics)) + setDistinguishable("(long or Dict)", + allBut(nonUserObjects, numerics + nullables)) def areDistinguishable(type1, type2): return data[type1].get(type2, False) @@ -255,15 +260,14 @@ def WebIDLTest(parser, harness): interface Interface : AncestorInterface {}; interface AncestorInterface {}; interface UnrelatedInterface {}; - interface ImplementedInterface {}; - Interface implements ImplementedInterface; callback interface CallbackInterface {}; callback interface CallbackInterface2 {}; callback Callback = any(); callback Callback2 = long(short arg); - dictionary Dict {}; - dictionary Dict2 {}; - interface _Promise {}; + // Give our dictionaries required members so we don't need to + // mess with optional and default values. + dictionary Dict { required long member; }; + dictionary Dict2 { required long member; }; interface TestInterface {%s }; """ diff --git a/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py index 799f2e0e0ed..4874b3aafe6 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_duplicate_qualifiers.py @@ -30,20 +30,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface DuplicateQualifiers3 { - creator creator byte foo(unsigned long index, byte value); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" interface DuplicateQualifiers4 { deleter deleter byte foo(unsigned long index); }; @@ -68,17 +54,3 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "Should have thrown.") - - threw = False - try: - results = parser.parse(""" - interface DuplicateQualifiers6 { - creator setter creator byte foo(unsigned long index, byte value); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py b/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py index 350ae72f022..a713266c88e 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_empty_sequence_default_value.py @@ -10,7 +10,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Constant cannot have [] as a default value") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py index ca0674aec04..7afd15513c6 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_error_colno.py @@ -8,7 +8,7 @@ def WebIDLTest(parser, harness): try: parser.parse(input) results = parser.finish() - except WebIDL.WebIDLError, e: + except WebIDL.WebIDLError as e: threw = True lines = str(e).split('\n') diff --git a/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py index f11222e7a4d..70bb1883682 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_error_lineno.py @@ -14,7 +14,7 @@ interface ?""" try: parser.parse(input) results = parser.finish() - except WebIDL.WebIDLError, e: + except WebIDL.WebIDLError as e: threw = True lines = str(e).split('\n') diff --git a/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py b/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py index 48957098bfe..e0241a56426 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_exposed_extended_attribute.py @@ -2,9 +2,9 @@ import WebIDL def WebIDLTest(parser, harness): parser.parse(""" - [PrimaryGlobal] interface Foo {}; - [Global=(Bar1,Bar2)] interface Bar {}; - [Global=Baz2] interface Baz {}; + [Global, Exposed=Foo] interface Foo {}; + [Global=(Bar, Bar1,Bar2), Exposed=Bar] interface Bar {}; + [Global=(Baz, Baz2), Exposed=Baz] interface Baz {}; [Exposed=(Foo,Bar1)] interface Iface { @@ -51,10 +51,11 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" - [PrimaryGlobal] interface Foo {}; - [Global=(Bar1,Bar2)] interface Bar {}; - [Global=Baz2] interface Baz {}; + [Global, Exposed=Foo] interface Foo {}; + [Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {}; + [Global=(Baz, Baz2), Exposed=Baz] interface Baz {}; + [Exposed=Foo] interface Iface2 { void method3(); }; @@ -80,9 +81,9 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" - [PrimaryGlobal] interface Foo {}; - [Global=(Bar1,Bar2)] interface Bar {}; - [Global=Baz2] interface Baz {}; + [Global, Exposed=Foo] interface Foo {}; + [Global=(Bar, Bar1, Bar2), Exposed=Bar] interface Bar {}; + [Global=(Baz, Baz2), Exposed=Baz] interface Baz {}; [Exposed=Foo] interface Iface3 { @@ -90,11 +91,11 @@ def WebIDLTest(parser, harness): }; [Exposed=(Foo,Bar1)] - interface Mixin { + interface mixin Mixin { void method5(); }; - Iface3 implements Mixin; + Iface3 includes Mixin; """) results = parser.finish() harness.check(len(results), 6, "Should know about six things"); @@ -124,7 +125,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown on invalid Exposed value on interface.") @@ -140,7 +141,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.") @@ -156,7 +157,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown on invalid Exposed value on operation.") @@ -172,7 +173,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown on invalid Exposed value on constant.") @@ -181,8 +182,8 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Global] interface Foo {}; - [Global] interface Bar {}; + [Global, Exposed=Foo] interface Foo {}; + [Global, Exposed=Bar] interface Bar {}; [Exposed=Foo] interface Baz { @@ -192,31 +193,46 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown on member exposed where its interface is not.") parser = parser.reset() - threw = False - try: - parser.parse(""" - [Global] interface Foo {}; - [Global] interface Bar {}; + parser.parse(""" + [Global, Exposed=Foo] interface Foo {}; + [Global, Exposed=Bar] interface Bar {}; - [Exposed=Foo] - interface Baz { - void method(); - }; + [Exposed=Foo] + interface Baz { + void method(); + }; - [Exposed=Bar] - interface Mixin {}; + [Exposed=Bar] + interface mixin Mixin { + void otherMethod(); + }; - Baz implements Mixin; - """) + Baz includes Mixin; + """) + + results = parser.finish() + + harness.check(len(results), 5, "Should know about five things"); + iface = results[2] + harness.ok(isinstance(iface, WebIDL.IDLInterface), + "Should have an interface here"); + members = iface.members + harness.check(len(members), 2, "Should have two members") + + harness.ok(members[0].exposureSet == set(["Foo"]), + "method should have the right exposure set") + harness.ok(members[0]._exposureGlobalNames == set(["Foo"]), + "method should have the right exposure global names") + + harness.ok(members[1].exposureSet == set(["Bar"]), + "otherMethod should have the right exposure set") + harness.ok(members[1]._exposureGlobalNames == set(["Bar"]), + "otherMethod should have the right exposure global names") - results = parser.finish() - except Exception,x: - threw = True - harness.ok(threw, "Should have thrown on LHS of implements being exposed where RHS is not.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py index 85a70d98f2c..144c945bc10 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py @@ -56,9 +56,9 @@ def WebIDLTest(parser, harness): results = parser.finish() # Pull out the first argument out of the arglist of the first (and # only) signature. - harness.ok(results[0].members[0].signatures()[0][1][0].clamp, + harness.ok(results[0].members[0].signatures()[0][1][0].type.hasClamp(), "Should be clamped") - harness.ok(not results[0].members[1].signatures()[0][1][0].clamp, + harness.ok(not results[0].members[1].signatures()[0][1][0].type.hasClamp(), "Should not be clamped") parser = parser.reset() @@ -86,9 +86,9 @@ def WebIDLTest(parser, harness): results = parser.finish() # Pull out the first argument out of the arglist of the first (and # only) signature. - harness.ok(results[0].members[0].signatures()[0][1][0].enforceRange, + harness.ok(results[0].members[0].signatures()[0][1][0].type.hasEnforceRange(), "Should be enforceRange") - harness.ok(not results[0].members[1].signatures()[0][1][0].enforceRange, + harness.ok(not results[0].members[1].signatures()[0][1][0].type.hasEnforceRange(), "Should not be enforceRange") parser = parser.reset() diff --git a/components/script/dom/bindings/codegen/parser/tests/test_float_types.py b/components/script/dom/bindings/codegen/parser/tests/test_float_types.py index 718f09c114b..b7325cf9d26 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_float_types.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_float_types.py @@ -68,7 +68,7 @@ def WebIDLTest(parser, harness): long m(float arg); }; """) - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "[LenientFloat] only allowed on void methods") @@ -81,7 +81,7 @@ def WebIDLTest(parser, harness): void m(unrestricted float arg); }; """) - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args") @@ -94,7 +94,7 @@ def WebIDLTest(parser, harness): void m(sequence<unrestricted float> arg); }; """) - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (2)") @@ -107,7 +107,7 @@ def WebIDLTest(parser, harness): void m((unrestricted float or FloatTypes) arg); }; """) - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (3)") @@ -120,6 +120,6 @@ def WebIDLTest(parser, harness): readonly attribute float foo; }; """) - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "[LenientFloat] only allowed on writable attributes") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py b/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py index c752cecd298..28b79642d86 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_global_extended_attr.py @@ -1,9 +1,10 @@ def WebIDLTest(parser, harness): parser.parse(""" - [Global] + [Global, Exposed=Foo] interface Foo : Bar { getter any(DOMString name); }; + [Exposed=Foo] interface Bar {}; """) @@ -18,7 +19,7 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Global] + [Global, Exposed=Foo] interface Foo { getter any(DOMString name); setter void(DOMString name, any arg); @@ -36,25 +37,7 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Global] - interface Foo { - getter any(DOMString name); - creator void(DOMString name, any arg); - }; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, - "Should have thrown for [Global] used on an interface with a " - "named creator") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - [Global] + [Global, Exposed=Foo] interface Foo { getter any(DOMString name); deleter void(DOMString name); @@ -72,7 +55,7 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Global, OverrideBuiltins] + [Global, OverrideBuiltins, Exposed=Foo] interface Foo { }; """) @@ -88,10 +71,10 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Global] + [Global, Exposed=Foo] interface Foo : Bar { }; - [OverrideBuiltins] + [OverrideBuiltins, Exposed=Foo] interface Bar { }; """) @@ -107,9 +90,10 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - [Global] + [Global, Exposed=Foo] interface Foo { }; + [Exposed=Foo] interface Bar : Foo { }; """) diff --git a/components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py b/components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py new file mode 100644 index 00000000000..0e9a6654aa7 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_identifier_conflict.py @@ -0,0 +1,39 @@ +# Import the WebIDL module, so we can do isinstance checks and whatnot +import WebIDL + +def WebIDLTest(parser, harness): + try: + parser.parse(""" + enum Foo { "a" }; + interface Foo; + """) + results = parser.finish() + harness.ok(False, "Should fail to parse") + except Exception as e: + harness.ok("Name collision" in e.message, + "Should have name collision for interface") + + parser = parser.reset() + try: + parser.parse(""" + dictionary Foo { long x; }; + enum Foo { "a" }; + """) + results = parser.finish() + harness.ok(False, "Should fail to parse") + except Exception as e: + harness.ok("Name collision" in e.message, + "Should have name collision for dictionary") + + parser = parser.reset() + try: + parser.parse(""" + enum Foo { "a" }; + enum Foo { "b" }; + """) + results = parser.finish() + harness.ok(False, "Should fail to parse") + except Exception as e: + harness.ok("Multiple unresolvable definitions" in e.message, + "Should have name collision for dictionary") + diff --git a/components/script/dom/bindings/codegen/parser/tests/test_implements.py b/components/script/dom/bindings/codegen/parser/tests/test_implements.py deleted file mode 100644 index 04c47d92abe..00000000000 --- a/components/script/dom/bindings/codegen/parser/tests/test_implements.py +++ /dev/null @@ -1,216 +0,0 @@ -# Import the WebIDL module, so we can do isinstance checks and whatnot -import WebIDL - -def WebIDLTest(parser, harness): - # Basic functionality - threw = False - try: - parser.parse(""" - A implements B; - interface B { - attribute long x; - }; - interface A { - attribute long y; - }; - """) - results = parser.finish() - except: - threw = True - - harness.ok(not threw, "Should not have thrown on implements statement " - "before interfaces") - harness.check(len(results), 3, "We have three statements") - harness.ok(isinstance(results[1], WebIDL.IDLInterface), "B is an interface") - harness.check(len(results[1].members), 1, "B has one member") - A = results[2] - harness.ok(isinstance(A, WebIDL.IDLInterface), "A is an interface") - harness.check(len(A.members), 2, "A has two members") - harness.check(A.members[0].identifier.name, "y", "First member is 'y'") - harness.check(A.members[1].identifier.name, "x", "Second member is 'x'") - - # Duplicated member names not allowed - threw = False - try: - parser.parse(""" - C implements D; - interface D { - attribute long x; - }; - interface C { - attribute long x; - }; - """) - parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown on implemented interface duplicating " - "a name on base interface") - - # Same, but duplicated across implemented interfaces - threw = False - try: - parser.parse(""" - E implements F; - E implements G; - interface F { - attribute long x; - }; - interface G { - attribute long x; - }; - interface E {}; - """) - parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown on implemented interfaces " - "duplicating each other's member names") - - # Same, but duplicated across indirectly implemented interfaces - threw = False - try: - parser.parse(""" - H implements I; - H implements J; - I implements K; - interface K { - attribute long x; - }; - interface L { - attribute long x; - }; - interface I {}; - interface J : L {}; - interface H {}; - """) - parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown on indirectly implemented interfaces " - "duplicating each other's member names") - - # Same, but duplicated across an implemented interface and its parent - threw = False - try: - parser.parse(""" - M implements N; - interface O { - attribute long x; - }; - interface N : O { - attribute long x; - }; - interface M {}; - """) - parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown on implemented interface and its " - "ancestor duplicating member names") - - # Reset the parser so we can actually find things where we expect - # them in the list - parser = parser.reset() - - # Diamonds should be allowed - threw = False - try: - parser.parse(""" - P implements Q; - P implements R; - Q implements S; - R implements S; - interface Q {}; - interface R {}; - interface S { - attribute long x; - }; - interface P {}; - """) - results = parser.finish() - except: - threw = True - - harness.ok(not threw, "Diamond inheritance is fine") - harness.check(results[6].identifier.name, "S", "We should be looking at 'S'") - harness.check(len(results[6].members), 1, "S should have one member") - harness.check(results[6].members[0].identifier.name, "x", - "S's member should be 'x'") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface TestInterface { - }; - callback interface TestCallbackInterface { - }; - TestInterface implements TestCallbackInterface; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, - "Should not allow callback interfaces on the right-hand side " - "of 'implements'") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface TestInterface { - }; - callback interface TestCallbackInterface { - }; - TestCallbackInterface implements TestInterface; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, - "Should not allow callback interfaces on the left-hand side of " - "'implements'") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface TestInterface { - }; - dictionary Dict { - }; - Dict implements TestInterface; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, - "Should not allow non-interfaces on the left-hand side " - "of 'implements'") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface TestInterface { - }; - dictionary Dict { - }; - TestInterface implements Dict; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, - "Should not allow non-interfaces on the right-hand side " - "of 'implements'") - diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface.py b/components/script/dom/bindings/codegen/parser/tests/test_interface.py index e8ed67b54b3..47db3ae4cc9 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_interface.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_interface.py @@ -84,100 +84,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface A {}; - interface B {}; - A implements B; - B implements A; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should not allow cycles via implements") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface A {}; - interface C {}; - interface B {}; - A implements C; - C implements B; - B implements A; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should not allow indirect cycles via implements") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface A : B {}; - interface B {}; - B implements A; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should not allow inheriting from an interface that implements us") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface A : B {}; - interface B {}; - interface C {}; - B implements C; - C implements A; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should not allow inheriting from an interface that indirectly implements us") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface A : B {}; - interface B : C {}; - interface C {}; - C implements A; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should not allow indirectly inheriting from an interface that implements us") - - parser = parser.reset() - threw = False - try: - parser.parse(""" - interface A : B {}; - interface B : C {}; - interface C {}; - interface D {}; - C implements D; - D implements A; - """) - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should not allow indirectly inheriting from an interface that indirectly implements us") - - parser = parser.reset() - threw = False - try: - parser.parse(""" interface A; interface B : A {}; """) @@ -189,12 +95,12 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" - [Constructor(long arg)] interface A { + constructor(); + constructor(long arg); readonly attribute boolean x; void foo(); }; - [Constructor] partial interface A { readonly attribute boolean y; void foo(long arg); @@ -219,13 +125,13 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" - [Constructor] partial interface A { readonly attribute boolean y; void foo(long arg); }; - [Constructor(long arg)] interface A { + constructor(); + constructor(long arg); readonly attribute boolean x; void foo(); }; @@ -376,30 +282,89 @@ def WebIDLTest(parser, harness): "Should not allow unknown extended attributes on interfaces") parser = parser.reset() + parser.parse(""" + [Global, Exposed=Window] interface Window {}; + [Exposed=Window, LegacyWindowAlias=A] + interface B {}; + [Exposed=Window, LegacyWindowAlias=(C, D)] + interface E {}; + """); + results = parser.finish(); + harness.check(results[1].legacyWindowAliases, ["A"], + "Should support a single identifier") + harness.check(results[2].legacyWindowAliases, ["C", "D"], + "Should support an identifier list") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [LegacyWindowAlias] + interface A {}; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow [LegacyWindowAlias] with no value") + + parser = parser.reset() threw = False try: parser.parse(""" + [Exposed=Worker, LegacyWindowAlias=B] + interface A {}; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow [LegacyWindowAlias] without Window exposure") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Global, Exposed=Window] interface Window {}; + [Exposed=Window] + interface A {}; + [Exposed=Window, LegacyWindowAlias=A] interface B {}; - [ArrayClass] - interface A : B { - }; """) results = parser.finish() except: threw = True harness.ok(threw, - "Should not allow [ArrayClass] on interfaces with parents") + "Should not allow [LegacyWindowAlias] to conflict with other identifiers") parser = parser.reset() threw = False try: parser.parse(""" - [ArrayClass] - interface A { - }; + [Global, Exposed=Window] interface Window {}; + [Exposed=Window, LegacyWindowAlias=A] + interface B {}; + [Exposed=Window] + interface A {}; """) results = parser.finish() except: threw = True - harness.ok(not threw, - "Should allow [ArrayClass] on interfaces without parents") + harness.ok(threw, + "Should not allow [LegacyWindowAlias] to conflict with other identifiers") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Global, Exposed=Window] interface Window {}; + [Exposed=Window, LegacyWindowAlias=A] + interface B {}; + [Exposed=Window, LegacyWindowAlias=A] + interface C {}; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow [LegacyWindowAlias] to conflict with other identifiers") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py b/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py index 159b50f84ff..e070adee7e6 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_interface_maplikesetlikeiterable.py @@ -37,10 +37,10 @@ def WebIDLTest(parser, harness): p.finish() harness.ok(False, prefix + " - Interface passed when should've failed") - except WebIDL.WebIDLError, e: + except WebIDL.WebIDLError as e: harness.ok(True, prefix + " - Interface failed as expected") - except Exception, e: + except Exception as e: harness.ok(False, prefix + " - Interface failed but not as a WebIDLError exception: %s" % e) @@ -88,6 +88,8 @@ def WebIDLTest(parser, harness): disallowedNonMethodNames = ["clear", "delete"] mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames + unrelatedMembers = [("unrelatedAttribute", WebIDL.IDLAttribute), + ("unrelatedMethod", WebIDL.IDLMethod)] # # Simple Usage Tests @@ -99,86 +101,171 @@ def WebIDLTest(parser, harness): iterable<long>; readonly attribute unsigned long length; getter long(unsigned long index); + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, valueIterableMembers) + """, valueIterableMembers + unrelatedMembers) + + shouldPass("Iterable (key only) inheriting from parent", + """ + interface Foo1 : Foo2 { + iterable<long>; + readonly attribute unsigned long length; + getter long(unsigned long index); + }; + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, valueIterableMembers, numProductions=2) shouldPass("Iterable (key and value)", """ interface Foo1 { iterable<long, long>; + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, iterableMembers, + """, iterableMembers + unrelatedMembers, # numProductions == 2 because of the generated iterator iface, numProductions=2) + shouldPass("Iterable (key and value) inheriting from parent", + """ + interface Foo1 : Foo2 { + iterable<long, long>; + }; + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, iterableMembers, + # numProductions == 3 because of the generated iterator iface, + numProductions=3) + shouldPass("Maplike (readwrite)", """ interface Foo1 { maplike<long, long>; + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, mapRWMembers + unrelatedMembers) + + shouldPass("Maplike (readwrite) inheriting from parent", + """ + interface Foo1 : Foo2 { + maplike<long, long>; + }; + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, mapRWMembers) + """, mapRWMembers, numProductions=2) shouldPass("Maplike (readwrite)", """ interface Foo1 { maplike<long, long>; + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, mapRWMembers + unrelatedMembers) + + shouldPass("Maplike (readwrite) inheriting from parent", + """ + interface Foo1 : Foo2 { + maplike<long, long>; }; - """, mapRWMembers) + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, mapRWMembers, numProductions=2) shouldPass("Maplike (readonly)", """ interface Foo1 { readonly maplike<long, long>; + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, mapROMembers + unrelatedMembers) + + shouldPass("Maplike (readonly) inheriting from parent", + """ + interface Foo1 : Foo2 { + readonly maplike<long, long>; + }; + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, mapROMembers) + """, mapROMembers, numProductions=2) shouldPass("Setlike (readwrite)", """ interface Foo1 { setlike<long>; + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, setRWMembers) + """, setRWMembers + unrelatedMembers) + + shouldPass("Setlike (readwrite) inheriting from parent", + """ + interface Foo1 : Foo2 { + setlike<long>; + }; + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); + }; + """, setRWMembers, numProductions=2) shouldPass("Setlike (readonly)", """ interface Foo1 { readonly setlike<long>; + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, setROMembers) + """, setROMembers + unrelatedMembers) - shouldPass("Inheritance of maplike/setlike", + shouldPass("Setlike (readonly) inheriting from parent", """ - interface Foo1 { - maplike<long, long>; + interface Foo1 : Foo2 { + readonly setlike<long>; }; - interface Foo2 : Foo1 { + interface Foo2 { + attribute long unrelatedAttribute; + long unrelatedMethod(); }; - """, mapRWMembers, numProductions=2) + """, setROMembers, numProductions=2) - shouldPass("Implements with maplike/setlike", + shouldPass("Inheritance of maplike/setlike", """ interface Foo1 { maplike<long, long>; }; - interface Foo2 { + interface Foo2 : Foo1 { }; - Foo2 implements Foo1; - """, mapRWMembers, numProductions=3) + """, mapRWMembers, numProductions=2) shouldPass("JS Implemented maplike interface", """ - [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1", - Constructor()] + [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"] interface Foo1 { + constructor(); setlike<long>; }; """, setRWChromeMembers) shouldPass("JS Implemented maplike interface", """ - [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1", - Constructor()] + [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"] interface Foo1 { + constructor(); maplike<long, long>; }; """, mapRWChromeMembers) @@ -253,31 +340,6 @@ def WebIDLTest(parser, harness): }; """) - shouldFail("Consequential interface with conflicting maplike/setlike", - """ - interface Foo1 { - maplike<long, long>; - }; - interface Foo2 { - setlike<long>; - }; - Foo2 implements Foo1; - """) - - shouldFail("Consequential interfaces with conflicting maplike/setlike", - """ - interface Foo1 { - maplike<long, long>; - }; - interface Foo2 { - setlike<long>; - }; - interface Foo3 { - }; - Foo3 implements Foo1; - Foo3 implements Foo2; - """) - # # Member name collision tests # @@ -380,52 +442,28 @@ def WebIDLTest(parser, harness): }; """, mapRWMembers, numProductions=3) - shouldFail("Interface with consequential maplike/setlike interface member collision", - """ - interface Foo1 { - void entries(); - }; - interface Foo2 { - maplike<long, long>; - }; - Foo1 implements Foo2; - """) - - shouldFail("Maplike interface with consequential interface member collision", + shouldFail("Maplike interface with mixin member collision", """ interface Foo1 { maplike<long, long>; }; - interface Foo2 { + interface mixin Foo2 { void entries(); }; - Foo1 implements Foo2; + Foo1 includes Foo2; """) - shouldPass("Consequential Maplike interface with inherited interface member collision", - """ - interface Foo1 { - maplike<long, long>; - }; - interface Foo2 { - void entries(); - }; - interface Foo3 : Foo2 { - }; - Foo3 implements Foo1; - """, mapRWMembers, numProductions=4) - shouldPass("Inherited Maplike interface with consequential interface member collision", """ interface Foo1 { maplike<long, long>; }; - interface Foo2 { + interface mixin Foo2 { void entries(); }; interface Foo3 : Foo1 { }; - Foo3 implements Foo2; + Foo3 includes Foo2; """, mapRWMembers, numProductions=4) shouldFail("Inheritance of name collision with child maplike/setlike", @@ -548,7 +586,7 @@ def WebIDLTest(parser, harness): }; """) - shouldPass("Implemented interface with readonly allowable overrides", + shouldPass("Interface with readonly allowable overrides", """ interface Foo1 { readonly setlike<long>; @@ -558,9 +596,9 @@ def WebIDLTest(parser, harness): shouldPass("JS Implemented read-only interface with readonly allowable overrides", """ - [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1", - Constructor()] + [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"] interface Foo1 { + constructor(); readonly setlike<long>; readonly attribute boolean clear; }; @@ -568,9 +606,9 @@ def WebIDLTest(parser, harness): shouldFail("JS Implemented read-write interface with non-readwrite allowable overrides", """ - [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1", - Constructor()] + [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"] interface Foo1 { + constructor(); setlike<long>; readonly attribute boolean clear; }; diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py b/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py new file mode 100644 index 00000000000..477a9f37799 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py @@ -0,0 +1,437 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse("interface mixin Foo { };") + results = parser.finish() + harness.ok(True, "Empty interface mixin parsed without error.") + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin), + "Should be an IDLInterfaceMixin") + mixin = results[0] + harness.check(mixin.identifier.QName(), "::Foo", "Interface mixin has the right QName") + harness.check(mixin.identifier.name, "Foo", "Interface mixin has the right name") + + parser = parser.reset() + parser.parse(""" + interface mixin QNameBase { + const long foo = 3; + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one productions") + harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin), + "Should be an IDLInterfaceMixin") + harness.check(len(results[0].members), 1, "Expect 1 productions") + mixin = results[0] + harness.check(mixin.members[0].identifier.QName(), "::QNameBase::foo", + "Member has the right QName") + + parser = parser.reset() + parser.parse(""" + interface mixin A { + readonly attribute boolean x; + void foo(); + }; + partial interface mixin A { + readonly attribute boolean y; + void foo(long arg); + }; + """) + results = parser.finish() + harness.check(len(results), 2, + "Should have two results with partial interface mixin") + mixin = results[0] + harness.check(len(mixin.members), 3, + "Should have three members with partial interface mixin") + harness.check(mixin.members[0].identifier.name, "x", + "First member should be x with partial interface mixin") + harness.check(mixin.members[1].identifier.name, "foo", + "Second member should be foo with partial interface mixin") + harness.check(len(mixin.members[1].signatures()), 2, + "Should have two foo signatures with partial interface mixin") + harness.check(mixin.members[2].identifier.name, "y", + "Third member should be y with partial interface mixin") + + parser = parser.reset() + parser.parse(""" + partial interface mixin A { + readonly attribute boolean y; + void foo(long arg); + }; + interface mixin A { + readonly attribute boolean x; + void foo(); + }; + """) + results = parser.finish() + harness.check(len(results), 2, + "Should have two results with reversed partial interface mixin") + mixin = results[1] + harness.check(len(mixin.members), 3, + "Should have three members with reversed partial interface mixin") + harness.check(mixin.members[0].identifier.name, "x", + "First member should be x with reversed partial interface mixin") + harness.check(mixin.members[1].identifier.name, "foo", + "Second member should be foo with reversed partial interface mixin") + harness.check(len(mixin.members[1].signatures()), 2, + "Should have two foo signatures with reversed partial interface mixin") + harness.check(mixin.members[2].identifier.name, "y", + "Third member should be y with reversed partial interface mixin") + + parser = parser.reset() + parser.parse(""" + interface Interface {}; + interface mixin Mixin { + attribute short x; + }; + Interface includes Mixin; + """) + results = parser.finish() + iface = results[0] + harness.check(len(iface.members), 1, "Should merge members from mixins") + harness.check(iface.members[0].identifier.name, "x", + "Should merge members from mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + readonly attribute boolean x; + }; + interface mixin A { + readonly attribute boolean y; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow two non-partial interface mixins with the same name") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + partial interface mixin A { + readonly attribute boolean x; + }; + partial interface mixin A { + readonly attribute boolean y; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Must have a non-partial interface mixin for a given name") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + boolean x; + }; + partial interface mixin A { + readonly attribute boolean y; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow a name collision between partial interface " + "mixin and other object") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary A { + boolean x; + }; + interface mixin A { + readonly attribute boolean y; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow a name collision between interface mixin " + "and other object") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + readonly attribute boolean x; + }; + interface A; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow a name collision between external interface " + "and interface mixin") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [SomeRandomAnnotation] + interface mixin A { + readonly attribute boolean y; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow unknown extended attributes on interface mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + getter double (DOMString propertyName); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow getters on interface mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + setter void (DOMString propertyName, double propertyValue); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow setters on interface mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + deleter void (DOMString propertyName); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow deleters on interface mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + legacycaller double compute(double x); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow legacycallers on interface mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin A { + inherit attribute x; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow inherited attribute on interface mixins") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Interface {}; + interface NotMixin { + attribute short x; + }; + Interface includes NotMixin; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if the right side does not point an interface mixin") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin NotInterface {}; + interface mixin Mixin { + attribute short x; + }; + NotInterface includes Mixin; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if the left side does not point an interface") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin Mixin { + iterable<DOMString>; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if an interface mixin includes iterable") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin Mixin { + setlike<DOMString>; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if an interface mixin includes setlike") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface mixin Mixin { + maplike<DOMString, DOMString>; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if an interface mixin includes maplike") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Interface { + attribute short attr; + }; + interface mixin Mixin { + attribute short attr; + }; + Interface includes Mixin; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if the included mixin interface has duplicated member") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Interface {}; + interface mixin Mixin1 { + attribute short attr; + }; + interface mixin Mixin2 { + attribute short attr; + }; + Interface includes Mixin1; + Interface includes Mixin2; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, + "Should fail if the included mixin interfaces have duplicated member") + + parser = parser.reset() + parser.parse(""" + [Global, Exposed=Window] interface Window {}; + [Global, Exposed=Worker] interface Worker {}; + [Exposed=Window] + interface Base {}; + interface mixin Mixin { + Base returnSelf(); + }; + Base includes Mixin; + """) + results = parser.finish() + base = results[2] + attr = base.members[0] + harness.check(attr.exposureSet, set(["Window"]), + "Should expose on globals where the base interfaces are exposed") + + parser = parser.reset() + parser.parse(""" + [Global, Exposed=Window] interface Window {}; + [Global, Exposed=Worker] interface Worker {}; + [Exposed=Window] + interface Base {}; + [Exposed=Window] + interface mixin Mixin { + attribute short a; + }; + Base includes Mixin; + """) + results = parser.finish() + base = results[2] + attr = base.members[0] + harness.check(attr.exposureSet, set(["Window"]), + "Should follow [Exposed] on interface mixin") + + parser = parser.reset() + parser.parse(""" + [Global, Exposed=Window] interface Window {}; + [Global, Exposed=Worker] interface Worker {}; + [Exposed=Window] + interface Base1 {}; + [Exposed=Worker] + interface Base2 {}; + interface mixin Mixin { + attribute short a; + }; + Base1 includes Mixin; + Base2 includes Mixin; + """) + results = parser.finish() + base = results[2] + attr = base.members[0] + harness.check(attr.exposureSet, set(["Window", "Worker"]), + "Should expose on all globals where including interfaces are " + "exposed") + base = results[3] + attr = base.members[0] + harness.check(attr.exposureSet, set(["Window", "Worker"]), + "Should expose on all globals where including interfaces are " + "exposed") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_method.py b/components/script/dom/bindings/codegen/parser/tests/test_method.py index cf7f1b40d76..88ee874386c 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_method.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_method.py @@ -41,7 +41,7 @@ def WebIDLTest(parser, harness): harness.check(argument.variadic, variadic, "Argument has the right variadic value") def checkMethod(method, QName, name, signatures, - static=False, getter=False, setter=False, creator=False, + static=False, getter=False, setter=False, deleter=False, legacycaller=False, stringifier=False): harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod") @@ -53,7 +53,6 @@ def WebIDLTest(parser, harness): harness.check(method.isStatic(), static, "Method has the correct static value") harness.check(method.isGetter(), getter, "Method has the correct getter value") harness.check(method.isSetter(), setter, "Method has the correct setter value") - harness.check(method.isCreator(), creator, "Method has the correct creator value") harness.check(method.isDeleter(), deleter, "Method has the correct deleter value") harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value") harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value") @@ -121,7 +120,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(not threw, "Should allow integer to float type corecion") @@ -134,7 +133,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should not allow [GetterThrows] on methods") @@ -147,7 +146,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should not allow [SetterThrows] on methods") @@ -160,7 +159,7 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should spell [Throws] correctly on methods") @@ -173,6 +172,85 @@ def WebIDLTest(parser, harness): }; """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should not allow __noSuchMethod__ methods") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [Throws, LenientFloat] + void foo(float myFloat); + [Throws] + void foo(); + }; + """) + results = parser.finish() + except Exception as x: + threw = True + harness.ok(not threw, "Should allow LenientFloat to be only in a specific overload") + + parser = parser.reset() + parser.parse(""" + interface A { + [Throws] + void foo(); + [Throws, LenientFloat] + void foo(float myFloat); + }; + """) + results = parser.finish() + iface = results[0] + methods = iface.members + lenientFloat = methods[0].getExtendedAttribute("LenientFloat") + harness.ok(lenientFloat is not None, "LenientFloat in overloads must be added to the method") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [Throws, LenientFloat] + void foo(float myFloat); + [Throws] + void foo(float myFloat, float yourFloat); + }; + """) + results = parser.finish() + except Exception as x: + threw = True + harness.ok(threw, "Should prevent overloads from getting different restricted float behavior") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [Throws] + void foo(float myFloat, float yourFloat); + [Throws, LenientFloat] + void foo(float myFloat); + }; + """) + results = parser.finish() + except Exception as x: + threw = True + harness.ok(threw, "Should prevent overloads from getting different restricted float behavior (2)") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [Throws, LenientFloat] + void foo(float myFloat); + [Throws, LenientFloat] + void foo(short myShort); + }; + """) + results = parser.finish() + except Exception as x: + threw = True + harness.ok(threw, "Should prevent overloads from getting redundant [LenientFloat]") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_namespace.py b/components/script/dom/bindings/codegen/parser/tests/test_namespace.py index 74533a1770e..62edb270c63 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_namespace.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_namespace.py @@ -70,7 +70,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -85,7 +85,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -104,7 +104,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -123,7 +123,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -142,7 +142,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -161,7 +161,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -180,7 +180,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -199,7 +199,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -218,6 +218,6 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_newobject.py b/components/script/dom/bindings/codegen/parser/tests/test_newobject.py new file mode 100644 index 00000000000..26785c6a270 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_newobject.py @@ -0,0 +1,70 @@ +# Import the WebIDL module, so we can do isinstance checks and whatnot +import WebIDL + +def WebIDLTest(parser, harness): + # Basic functionality + parser.parse( + """ + interface Iface { + [NewObject] readonly attribute Iface attr; + [NewObject] Iface method(); + }; + """) + results = parser.finish() + harness.ok(results, "Should not have thrown on basic [NewObject] usage") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Iface { + [Pure, NewObject] readonly attribute Iface attr; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[NewObject] attributes must depend on something") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Iface { + [Pure, NewObject] Iface method(); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[NewObject] methods must depend on something") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Iface { + [Cached, NewObject, Affects=Nothing] readonly attribute Iface attr; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[NewObject] attributes must not be [Cached]") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Iface { + [StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py index 2b48b615dd4..8ba6771677a 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py @@ -80,7 +80,7 @@ def checkEquivalent(iface, harness): for attr in dir(type1): if attr.startswith('_') or \ attr in ['nullable', 'builtin', 'filename', 'location', - 'inner', 'QName', 'getDeps', 'name'] or \ + 'inner', 'QName', 'getDeps', 'name', 'prettyName'] or \ (hasattr(type(type1), attr) and not callable(getattr(type1, attr))): continue diff --git a/components/script/dom/bindings/codegen/parser/tests/test_promise.py b/components/script/dom/bindings/codegen/parser/tests/test_promise.py index 55bc0768092..43c74029dc5 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_promise.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_promise.py @@ -2,7 +2,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface _Promise {}; interface A { legacycaller Promise<any> foo(); }; @@ -18,7 +17,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface _Promise {}; interface A { Promise<any> foo(); long foo(long arg); @@ -35,7 +33,6 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface _Promise {}; interface A { long foo(long arg); Promise<any> foo(); @@ -49,8 +46,35 @@ def WebIDLTest(parser, harness): "non-Promise return types.") parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + Promise<any>? foo(); + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow nullable Promise return values.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + void foo(Promise<any>? arg); + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow nullable Promise arguments.") + + parser = parser.reset() parser.parse(""" - interface _Promise {}; interface A { Promise<any> foo(); Promise<any> foo(long arg); @@ -61,3 +85,73 @@ def WebIDLTest(parser, harness): harness.ok(True, "Should allow overloads which only have Promise and return " "types.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + attribute Promise<any> attr; + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow writable Promise-typed attributes.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [LenientSetter] readonly attribute Promise<any> attr; + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow [LenientSetter] Promise-typed attributes.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [PutForwards=bar] readonly attribute Promise<any> attr; + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow [PutForwards] Promise-typed attributes.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [Replaceable] readonly attribute Promise<any> attr; + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow [Replaceable] Promise-typed attributes.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface A { + [SameObject] readonly attribute Promise<any> attr; + }; + """) + results = parser.finish(); + except: + threw = True + harness.ok(threw, + "Should not allow [SameObject] Promise-typed attributes.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_mozmap.py b/components/script/dom/bindings/codegen/parser/tests/test_record.py index 1a36fdd62c4..d50572caf07 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_mozmap.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_record.py @@ -3,8 +3,8 @@ import WebIDL def WebIDLTest(parser, harness): parser.parse(""" dictionary Dict {}; - interface MozMapArg { - void foo(MozMap<Dict> arg); + interface RecordArg { + void foo(record<DOMString, Dict> arg); }; """) @@ -19,7 +19,7 @@ def WebIDLTest(parser, harness): signature = members[0].signatures()[0] args = signature[1] harness.check(len(args), 1, "Should have one arg") - harness.ok(args[0].type.isMozMap(), "Should have a MozMap type here") + harness.ok(args[0].type.isRecord(), "Should have a record type here") harness.ok(args[0].type.inner.isDictionary(), "Should have a dictionary inner type") @@ -27,13 +27,27 @@ def WebIDLTest(parser, harness): threw = False try: parser.parse(""" - interface MozMapVoidArg { - void foo(MozMap<void> arg); + interface RecordVoidArg { + void foo(record<DOMString, void> arg); }; """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True + harness.ok(threw, "Should have thrown because record can't have void as value type.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + dictionary Dict { + record<DOMString, Dict> val; + }; + """) - harness.ok(threw, "Should have thrown.") + results = parser.finish() + except Exception as x: + threw = True + harness.ok(threw, + "Should have thrown on dictionary containing itself via record.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py index 084f19fa7f5..442dba45d76 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py @@ -288,33 +288,32 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "[SecureContext] must appear on interfaces that inherit from another [SecureContext] interface") - # Test 'implements'. The behavior tested here may have to change depending - # on the resolution of https://github.com/heycam/webidl/issues/118 + # Test 'includes'. parser = parser.reset() parser.parse(""" [SecureContext] - interface TestSecureContextInterfaceThatImplementsNonSecureContextInterface { + interface TestSecureContextInterfaceThatIncludesNonSecureContextMixin { const octet TEST_CONSTANT = 0; }; - interface TestNonSecureContextInterface { + interface mixin TestNonSecureContextMixin { const octet TEST_CONSTANT_2 = 0; readonly attribute byte testAttribute2; void testMethod2(byte foo); }; - TestSecureContextInterfaceThatImplementsNonSecureContextInterface implements TestNonSecureContextInterface; + TestSecureContextInterfaceThatIncludesNonSecureContextMixin includes TestNonSecureContextMixin; """) results = parser.finish() - harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have two members") + harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have four members") harness.ok(results[0].getExtendedAttribute("SecureContext"), "Interface should have [SecureContext] extended attribute") harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"), "[SecureContext] should propagate from interface to constant members even when other members are copied from a non-[SecureContext] interface") harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None, - "Constants copied from non-[SecureContext] interface should not be [SecureContext]") + "Constants copied from non-[SecureContext] mixin should not be [SecureContext]") harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None, - "Attributes copied from non-[SecureContext] interface should not be [SecureContext]") + "Attributes copied from non-[SecureContext] mixin should not be [SecureContext]") harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None, - "Methods copied from non-[SecureContext] interface should not be [SecureContext]") + "Methods copied from non-[SecureContext] mixin should not be [SecureContext]") # Test SecureContext and NoInterfaceObject parser = parser.reset() diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py index 5ea1743d36a..52cfcb96817 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_special_method_signature_mismatch.py @@ -222,73 +222,3 @@ def WebIDLTest(parser, harness): threw = True harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" - interface SpecialMethodSignatureMismatch20 { - creator long long foo(long index, long long value); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" - interface SpecialMethodSignatureMismatch22 { - creator boolean foo(unsigned long index, boolean value, long long extraArg); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" - interface SpecialMethodSignatureMismatch23 { - creator boolean foo(unsigned long index, boolean... value); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" - interface SpecialMethodSignatureMismatch24 { - creator boolean foo(unsigned long index, optional boolean value); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" - interface SpecialMethodSignatureMismatch25 { - creator boolean foo(); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py index 695cfe4f250..7f911733b62 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods.py @@ -5,26 +5,21 @@ def WebIDLTest(parser, harness): interface SpecialMethods { getter long long (unsigned long index); setter long long (unsigned long index, long long value); - creator long long (unsigned long index, long long value); - deleter long long (unsigned long index); getter boolean (DOMString name); setter boolean (DOMString name, boolean value); - creator boolean (DOMString name, boolean value); deleter boolean (DOMString name); + readonly attribute unsigned long length; }; interface SpecialMethodsCombination { - getter deleter long long (unsigned long index); - setter creator long long (unsigned long index, long long value); getter deleter boolean (DOMString name); - setter creator boolean (DOMString name, boolean value); }; """) results = parser.finish() def checkMethod(method, QName, name, - static=False, getter=False, setter=False, creator=False, + static=False, getter=False, setter=False, deleter=False, legacycaller=False, stringifier=False): harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod") @@ -33,7 +28,6 @@ def WebIDLTest(parser, harness): harness.check(method.isStatic(), static, "Method has the correct static value") harness.check(method.isGetter(), getter, "Method has the correct getter value") harness.check(method.isSetter(), setter, "Method has the correct setter value") - harness.check(method.isCreator(), creator, "Method has the correct creator value") harness.check(method.isDeleter(), deleter, "Method has the correct deleter value") harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value") harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value") @@ -41,33 +35,39 @@ def WebIDLTest(parser, harness): harness.check(len(results), 2, "Expect 2 interfaces") iface = results[0] - harness.check(len(iface.members), 8, "Expect 8 members") + harness.check(len(iface.members), 6, "Expect 6 members") checkMethod(iface.members[0], "::SpecialMethods::__indexedgetter", "__indexedgetter", getter=True) checkMethod(iface.members[1], "::SpecialMethods::__indexedsetter", "__indexedsetter", setter=True) - checkMethod(iface.members[2], "::SpecialMethods::__indexedcreator", "__indexedcreator", - creator=True) - checkMethod(iface.members[3], "::SpecialMethods::__indexeddeleter", "__indexeddeleter", - deleter=True) - checkMethod(iface.members[4], "::SpecialMethods::__namedgetter", "__namedgetter", + checkMethod(iface.members[2], "::SpecialMethods::__namedgetter", "__namedgetter", getter=True) - checkMethod(iface.members[5], "::SpecialMethods::__namedsetter", "__namedsetter", + checkMethod(iface.members[3], "::SpecialMethods::__namedsetter", "__namedsetter", setter=True) - checkMethod(iface.members[6], "::SpecialMethods::__namedcreator", "__namedcreator", - creator=True) - checkMethod(iface.members[7], "::SpecialMethods::__nameddeleter", "__nameddeleter", + checkMethod(iface.members[4], "::SpecialMethods::__nameddeleter", "__nameddeleter", deleter=True) iface = results[1] - harness.check(len(iface.members), 4, "Expect 4 members") + harness.check(len(iface.members), 1, "Expect 1 member") - checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedgetterdeleter", - "__indexedgetterdeleter", getter=True, deleter=True) - checkMethod(iface.members[1], "::SpecialMethodsCombination::__indexedsettercreator", - "__indexedsettercreator", setter=True, creator=True) - checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedgetterdeleter", + checkMethod(iface.members[0], "::SpecialMethodsCombination::__namedgetterdeleter", "__namedgetterdeleter", getter=True, deleter=True) - checkMethod(iface.members[3], "::SpecialMethodsCombination::__namedsettercreator", - "__namedsettercreator", setter=True, creator=True) + + parser = parser.reset(); + + threw = False + try: + parser.parse( + """ + interface IndexedDeleter { + deleter void(unsigned long index); + }; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "There are no indexed deleters") + + diff --git a/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py index 42e2c5bb71b..9bf3d903463 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_special_methods_uniqueness.py @@ -35,23 +35,8 @@ def WebIDLTest(parser, harness): try: parser.parse(""" interface SpecialMethodUniqueness1 { - setter creator boolean (DOMString name); - creator boolean (DOMString name); - }; - """) - - results = parser.finish() - except: - threw = True - - harness.ok(threw, "Should have thrown.") - - threw = False - try: - parser.parse(""" - interface SpecialMethodUniqueness1 { setter boolean (DOMString name); - creator setter boolean (DOMString name); + setter boolean (DOMString name); }; """) diff --git a/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py b/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py index 14c2c5226fc..deabdc5ec81 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_stringifier.py @@ -44,3 +44,103 @@ def WebIDLTest(parser, harness): harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'") + parser = parser.reset() + parser.parse(""" + interface TestStringifier { + stringifier attribute DOMString foo; + }; + """) + results = parser.finish() + harness.ok(isinstance(results[0].members[0], WebIDL.IDLAttribute), + "Stringifier attribute should be an attribute") + stringifier = results[0].members[1] + harness.ok(isinstance(stringifier, WebIDL.IDLMethod), + "Stringifier attribute should insert a method") + harness.ok(stringifier.isStringifier(), + "Inserted method should be a stringifier") + + parser = parser.reset() + parser.parse(""" + interface TestStringifier {}; + interface mixin TestStringifierMixin { + stringifier attribute DOMString foo; + }; + TestStringifier includes TestStringifierMixin; + """) + results = parser.finish() + harness.ok(isinstance(results[0].members[0], WebIDL.IDLAttribute), + "Stringifier attribute should be an attribute") + stringifier = results[0].members[1] + harness.ok(isinstance(stringifier, WebIDL.IDLMethod), + "Stringifier attribute should insert a method") + harness.ok(stringifier.isStringifier(), + "Inserted method should be a stringifier") + + parser = parser.reset() + parser.parse(""" + interface TestStringifier { + stringifier attribute USVString foo; + }; + """) + results = parser.finish() + stringifier = results[0].members[1] + harness.ok(stringifier.signatures()[0][0].isUSVString(), + "Stringifier attributes should allow USVString") + + parser = parser.reset() + parser.parse(""" + interface TestStringifier { + [Throws, NeedsSubjectPrincipal] + stringifier attribute USVString foo; + }; + """) + results = parser.finish() + stringifier = results[0].members[1] + harness.ok(stringifier.getExtendedAttribute("Throws"), + "Stringifier attributes should support [Throws]") + harness.ok(stringifier.getExtendedAttribute("NeedsSubjectPrincipal"), + "Stringifier attributes should support [NeedsSubjectPrincipal]") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestStringifier { + stringifier attribute ByteString foo; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow ByteString") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestStringifier { + stringifier; + stringifier attribute DOMString foo; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow a 'stringifier;' and a stringifier attribute") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestStringifier { + stringifier attribute DOMString foo; + stringifier attribute DOMString bar; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow multiple stringifier attributes") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py b/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py new file mode 100644 index 00000000000..ad01330e65a --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py @@ -0,0 +1,195 @@ +def WebIDLTest(parser, harness): + threw = False + try: + parser.parse( + """ + interface Test { + object toJSON(); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow a toJSON method.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Test { + object toJSON(object arg); + object toJSON(long arg); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "Should not allow overloads of a toJSON method.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Test { + object toJSON(object arg); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "Should not allow a toJSON method with arguments.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Test { + long toJSON(); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow a toJSON method with 'long' as return type.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Test { + [Default] object toJSON(); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow a default toJSON method with 'object' as return type.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Test { + [Default] long toJSON(); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "Should not allow a default toJSON method with non-'object' as return type.") + + JsonTypes = [ "byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", + "unsigned long long", "float", "unrestricted float", "double", "unrestricted double", "boolean", + "DOMString", "ByteString", "UTF8String", "USVString", "Enum", "InterfaceWithToJSON", "object" ] + + nonJsonTypes = [ "InterfaceWithoutToJSON", "any", "Int8Array", "Int16Array", "Int32Array","Uint8Array", + "Uint16Array", "Uint32Array", "Uint8ClampedArray", "Float32Array", "Float64Array", "ArrayBuffer" ] + + def doTest(testIDL, shouldThrow, description): + p = parser.reset() + threw = False + try: + p.parse(testIDL + + """ + enum Enum { "a", "b", "c" }; + interface InterfaceWithToJSON { long toJSON(); }; + interface InterfaceWithoutToJSON {}; + """); + p.finish(); + except Exception as x: + threw = True + harness.ok(x.message == "toJSON method has non-JSON return type", x) + harness.check(threw, shouldThrow, description) + + + for type in JsonTypes: + doTest("interface Test { %s toJSON(); };" % type, False, + "%s should be a JSON type" % type) + + doTest("interface Test { sequence<%s> toJSON(); };" % type, False, + "sequence<%s> should be a JSON type" % type) + + doTest("dictionary Foo { %s foo; }; " + "interface Test { Foo toJSON(); }; " % type, False, + "dictionary containing only JSON type (%s) should be a JSON type" % type) + + doTest("dictionary Foo { %s foo; }; dictionary Bar : Foo { }; " + "interface Test { Bar toJSON(); }; " % type, False, + "dictionary whose ancestors only contain JSON types should be a JSON type") + + doTest("dictionary Foo { any foo; }; dictionary Bar : Foo { %s bar; };" + "interface Test { Bar toJSON(); };" % type, True, + "dictionary whose ancestors contain non-JSON types should not be a JSON type") + + doTest("interface Test { record<DOMString, %s> toJSON(); };" % type, False, + "record<DOMString, %s> should be a JSON type" % type) + + doTest("interface Test { record<ByteString, %s> toJSON(); };" % type, False, + "record<ByteString, %s> should be a JSON type" % type) + + doTest("interface Test { record<UTF8String, %s> toJSON(); };" % type, False, + "record<UTF8String, %s> should be a JSON type" % type) + + doTest("interface Test { record<USVString, %s> toJSON(); };" % type, False, + "record<USVString, %s> should be a JSON type" % type) + + otherUnionType = "Foo" if type != "object" else "long" + doTest("interface Foo { object toJSON(); };" + "interface Test { (%s or %s) toJSON(); };" % (otherUnionType, type), False, + "union containing only JSON types (%s or %s) should be a JSON type" %(otherUnionType, type)) + + doTest("interface test { %s? toJSON(); };" % type, False, + "Nullable type (%s) should be a JSON type" % type) + + doTest("interface Foo : InterfaceWithoutToJSON { %s toJSON(); };" + "interface Test { Foo toJSON(); };" % type, False, + "interface with toJSON should be a JSON type") + + doTest("interface Foo : InterfaceWithToJSON { };" + "interface Test { Foo toJSON(); };", False, + "inherited interface with toJSON should be a JSON type") + + for type in nonJsonTypes: + doTest("interface Test { %s toJSON(); };" % type, True, + "%s should not be a JSON type" % type) + + doTest("interface Test { sequence<%s> toJSON(); };" % type, True, + "sequence<%s> should not be a JSON type" % type) + + doTest("dictionary Foo { %s foo; }; " + "interface Test { Foo toJSON(); }; " % type, True, + "Dictionary containing a non-JSON type (%s) should not be a JSON type" % type) + + doTest("dictionary Foo { %s foo; }; dictionary Bar : Foo { }; " + "interface Test { Bar toJSON(); }; " % type, True, + "dictionary whose ancestors only contain non-JSON types should not be a JSON type") + + doTest("interface Test { record<DOMString, %s> toJSON(); };" % type, True, + "record<DOMString, %s> should not be a JSON type" % type) + + doTest("interface Test { record<ByteString, %s> toJSON(); };" % type, True, + "record<ByteString, %s> should not be a JSON type" % type) + + doTest("interface Test { record<USVString, %s> toJSON(); };" % type, True, + "record<USVString, %s> should not be a JSON type" % type) + + if type != "any": + doTest("interface Foo { object toJSON(); }; " + "interface Test { (Foo or %s) toJSON(); };" % type, True, + "union containing a non-JSON type (%s) should not be a JSON type" % type) + + doTest("interface test { %s? toJSON(); };" % type, True, + "Nullable type (%s) should not be a JSON type" % type) + + doTest("dictionary Foo { long foo; any bar; };" + "interface Test { Foo toJSON(); };", True, + "dictionary containing a non-JSON type should not be a JSON type") + + doTest("interface Foo : InterfaceWithoutToJSON { }; " + "interface Test { Foo toJSON(); };", True, + "interface without toJSON should not be a JSON type") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py index 8921985c5ca..b5fc1c68890 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_typedef.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef.py @@ -4,15 +4,15 @@ def WebIDLTest(parser, harness): typedef long? mynullablelong; interface Foo { const mylong X = 5; - const mynullablelong Y = 7; - const mynullablelong Z = null; - void foo(mylong arg); + void foo(optional mynullablelong arg = 7); + void bar(optional mynullablelong arg = null); + void baz(mylong arg); }; """) results = parser.finish() - harness.check(results[2].members[1].type.name, "LongOrNull", + harness.check(results[2].members[1].signatures()[0][1][0].type.name, "LongOrNull", "Should expand typedefs") parser = parser.reset() diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py new file mode 100644 index 00000000000..0ea38ce437b --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py @@ -0,0 +1,16 @@ +def WebIDLTest(parser, harness): + exception = None + try: + parser.parse( + """ + typedef long foo; + typedef long foo; + """) + + results = parser.finish() + except Exception as e: + exception = e + + harness.ok(exception, "Should have thrown.") + harness.ok("Multiple unresolvable definitions of identifier 'foo'" in str(exception), + "Should have a sane exception message") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py b/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py index d017d5ce092..d28cc1ec052 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_unenumerable_own_properties.py @@ -24,7 +24,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -39,7 +39,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") @@ -59,6 +59,6 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception, x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py b/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py index 3787e8c6af1..770a9d3736f 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_unforgeable.py @@ -111,7 +111,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown when shadowing unforgeable attribute on " @@ -130,7 +130,7 @@ def WebIDLTest(parser, harness): """) results = parser.finish() - except Exception,x: + except Exception as x: threw = True harness.ok(threw, "Should have thrown when shadowing unforgeable operation on " @@ -141,16 +141,16 @@ def WebIDLTest(parser, harness): interface Child : Parent { }; interface Parent {}; - interface Consequential { + interface mixin Mixin { [Unforgeable] readonly attribute long foo; }; - Parent implements Consequential; + Parent includes Mixin; """) results = parser.finish() harness.check(len(results), 4, "Should be able to inherit from an interface with a " - "consequential interface with [Unforgeable] properties.") + "mixin with [Unforgeable] properties.") parser = parser.reset(); threw = False @@ -160,10 +160,10 @@ def WebIDLTest(parser, harness): void foo(); }; interface Parent {}; - interface Consequential { + interface mixin Mixin { [Unforgeable] readonly attribute long foo; }; - Parent implements Consequential; + Parent includes Mixin; """) results = parser.finish() @@ -182,14 +182,14 @@ def WebIDLTest(parser, harness): }; interface Parent : GrandParent {}; interface GrandParent {}; - interface Consequential { + interface mixin Mixin { [Unforgeable] readonly attribute long foo; }; - GrandParent implements Consequential; - interface ChildConsequential { + GrandParent includes Mixin; + interface mixin ChildMixin { void foo(); }; - Child implements ChildConsequential; + Child includes ChildMixin; """) results = parser.finish() @@ -208,14 +208,14 @@ def WebIDLTest(parser, harness): }; interface Parent : GrandParent {}; interface GrandParent {}; - interface Consequential { + interface mixin Mixin { [Unforgeable] void foo(); }; - GrandParent implements Consequential; - interface ChildConsequential { + GrandParent includes Mixin; + interface mixin ChildMixin { void foo(); }; - Child implements ChildConsequential; + Child includes ChildMixin; """) results = parser.finish() diff --git a/components/script/dom/bindings/codegen/parser/tests/test_union.py b/components/script/dom/bindings/codegen/parser/tests/test_union.py index 9c4f2a56ab6..801314fd0bd 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_union.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_union.py @@ -17,7 +17,7 @@ def combinations(iterable, r): n = len(pool) if r > n: return - indices = range(r) + indices = list(range(r)) yield tuple(pool[i] for i in indices) while True: for i in reversed(range(r)): diff --git a/components/script/dom/bindings/codegen/parser/union-typedef.patch b/components/script/dom/bindings/codegen/parser/union-typedef.patch index 3021e14193f..20efea8e129 100644 --- a/components/script/dom/bindings/codegen/parser/union-typedef.patch +++ b/components/script/dom/bindings/codegen/parser/union-typedef.patch @@ -1,22 +1,22 @@ --- WebIDL.py +++ WebIDL.py -@@ -2481,10 +2481,18 @@ class IDLUnionType(IDLType): +@@ -2624,10 +2624,18 @@ class IDLUnionType(IDLType): return type.name - + for (i, type) in enumerate(self.memberTypes): +- if not type.isComplete(): + # Exclude typedefs because if given "typedef (B or C) test", + # we want AOrTest, not AOrBOrC + if not type.isComplete() and not isinstance(type, IDLTypedefType): -+ self.memberTypes[i] = type.complete(scope) -+ -+ self.name = "Or".join(typeName(type) for type in self.memberTypes) + self.memberTypes[i] = type.complete(scope) + + self.name = "Or".join(typeName(type) for type in self.memberTypes) + + # We do this again to complete the typedef types + for (i, type) in enumerate(self.memberTypes): - if not type.isComplete(): - self.memberTypes[i] = type.complete(scope) - -- self.name = "Or".join(typeName(type) for type in self.memberTypes) ++ if not type.isComplete(): ++ self.memberTypes[i] = type.complete(scope) ++ self.flatMemberTypes = list(self.memberTypes) i = 0 while i < len(self.flatMemberTypes): diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh index 213aba6df89..dd7803c940c 100755 --- a/components/script/dom/bindings/codegen/parser/update.sh +++ b/components/script/dom/bindings/codegen/parser/update.sh @@ -1,13 +1,12 @@ wget https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/bindings/parser/WebIDL.py -O WebIDL.py patch < abstract.patch patch < debug.patch -patch < pref-main-thread.patch patch < callback-location.patch patch < union-typedef.patch patch < inline.patch -wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz +wget https://hg.mozilla.org/mozilla-central/archive/tip.zip/dom/bindings/parser/tests/ -O tests.zip rm -r tests mkdir tests -tar xvpf tests.tar.gz -C tests --strip-components=5 -rm tests.tar.gz WebIDL.py.orig +unzip -d tests -j tests.zip +rm tests.zip WebIDL.py.orig |