servo: Merge #17254 - Update the WebIDL parser (from servo:webidl); r=jdm

Source-Repo: https://github.com/servo/servo
Source-Revision: 0c929ad3fd902f4314844787868fe891033d3b01

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 39a79ac6b1aefd9222663d328bb2b133f2455b98
This commit is contained in:
Anthony Ramine 2017-06-12 07:37:49 -07:00
parent 3a2e1cf12d
commit 0279918943
18 changed files with 1183 additions and 318 deletions

View File

@ -17,11 +17,12 @@ import functools
from WebIDL import (
BuiltinTypes,
IDLBuiltinType,
IDLNullValue,
IDLNullableType,
IDLObject,
IDLType,
IDLInterfaceMember,
IDLNullableType,
IDLNullValue,
IDLObject,
IDLPromiseType,
IDLType,
IDLUndefinedValue,
IDLWrapperType,
)
@ -94,14 +95,14 @@ def stripTrailingWhitespace(text):
def innerContainerType(type):
assert type.isSequence() or type.isMozMap()
assert type.isSequence() or type.isRecord()
return type.inner.inner if type.nullable() else type.inner
def wrapInNativeContainerType(type, inner):
if type.isSequence():
containerType = "Vec"
elif type.isMozMap():
elif type.isRecord():
containerType = "MozMap"
else:
raise TypeError("Unexpected container type %s", type)
@ -697,7 +698,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not (isEnforceRange and isClamp) # These are mutually exclusive
if type.isSequence() or type.isMozMap():
if type.isSequence() or type.isRecord():
innerInfo = getJSToNativeConversionInfo(innerContainerType(type),
descriptorProvider,
isMember=isMember)
@ -754,6 +755,56 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return handleOptional(templateBody, declType, default)
if type.isPromise():
assert not type.nullable()
# Per spec, what we're supposed to do is take the original
# Promise.resolve and call it with the original Promise as this
# value to make a Promise out of whatever value we actually have
# here. The question is which global we should use. There are
# a couple cases to consider:
#
# 1) Normal call to API with a Promise argument. This is a case the
# spec covers, and we should be using the current Realm's
# Promise. That means the current compartment.
# 2) Promise return value from a callback or callback interface.
# This is in theory a case the spec covers but in practice it
# really doesn't define behavior here because it doesn't define
# what Realm we're in after the callback returns, which is when
# the argument conversion happens. We will use the current
# compartment, which is the compartment of the callable (which
# may itself be a cross-compartment wrapper itself), which makes
# as much sense as anything else. In practice, such an API would
# once again be providing a Promise to signal completion of an
# operation, which would then not be exposed to anyone other than
# our own implementation code.
templateBody = fill(
"""
{ // Scope for our JSAutoCompartment.
rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
rooted!(in(cx) let mut valueToResolve = $${val}.get());
if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
$*{exceptionCode}
}
match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
Ok(value) => value,
Err(error) => {
throw_dom_exception(cx, &promiseGlobal, error);
$*{exceptionCode}
}
}
}
""",
exceptionCode=exceptionCode)
if isArgument:
declType = CGGeneric("&Promise")
else:
declType = CGGeneric("Rc<Promise>")
return handleOptional(templateBody, declType, handleDefaultNull("None"))
if type.isGeckoInterface():
assert not isEnforceRange and not isClamp
@ -780,79 +831,34 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
elif isArgument:
descriptorType = descriptor.argumentType
templateBody = ""
isPromise = descriptor.interface.identifier.name == "Promise"
if isPromise:
# Per spec, what we're supposed to do is take the original
# Promise.resolve and call it with the original Promise as this
# value to make a Promise out of whatever value we actually have
# here. The question is which global we should use. There are
# a couple cases to consider:
#
# 1) Normal call to API with a Promise argument. This is a case the
# spec covers, and we should be using the current Realm's
# Promise. That means the current compartment.
# 2) Promise return value from a callback or callback interface.
# This is in theory a case the spec covers but in practice it
# really doesn't define behavior here because it doesn't define
# what Realm we're in after the callback returns, which is when
# the argument conversion happens. We will use the current
# compartment, which is the compartment of the callable (which
# may itself be a cross-compartment wrapper itself), which makes
# as much sense as anything else. In practice, such an API would
# once again be providing a Promise to signal completion of an
# operation, which would then not be exposed to anyone other than
# our own implementation code.
templateBody = fill(
"""
{ // Scope for our JSAutoCompartment.
if descriptor.interface.isConsequential():
raise TypeError("Consequential interface %s being used as an "
"argument" % descriptor.interface.identifier.name)
rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
rooted!(in(cx) let mut valueToResolve = $${val}.get());
if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
$*{exceptionCode}
}
match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
Ok(value) => value,
Err(error) => {
throw_dom_exception(cx, &promiseGlobal, error);
$*{exceptionCode}
}
}
}
""",
exceptionCode=exceptionCode)
if failureCode is None:
substitutions = {
"sourceDescription": sourceDescription,
"interface": descriptor.interface.identifier.name,
"exceptionCode": exceptionCode,
}
unwrapFailureCode = string.Template(
'throw_type_error(cx, "${sourceDescription} does not '
'implement interface ${interface}.");\n'
'${exceptionCode}').substitute(substitutions)
else:
if descriptor.interface.isConsequential():
raise TypeError("Consequential interface %s being used as an "
"argument" % descriptor.interface.identifier.name)
unwrapFailureCode = failureCode
if failureCode is None:
substitutions = {
"sourceDescription": sourceDescription,
"interface": descriptor.interface.identifier.name,
"exceptionCode": exceptionCode,
templateBody = fill(
"""
match ${function}($${val}) {
Ok(val) => val,
Err(()) => {
$*{failureCode}
}
unwrapFailureCode = string.Template(
'throw_type_error(cx, "${sourceDescription} does not '
'implement interface ${interface}.");\n'
'${exceptionCode}').substitute(substitutions)
else:
unwrapFailureCode = failureCode
templateBody = fill(
"""
match ${function}($${val}) {
Ok(val) => val,
Err(()) => {
$*{failureCode}
}
}
""",
failureCode=unwrapFailureCode + "\n",
function=conversionFunction)
}
""",
failureCode=unwrapFailureCode + "\n",
function=conversionFunction)
declType = CGGeneric(descriptorType)
if type.nullable():
@ -1323,7 +1329,7 @@ def typeNeedsCx(type, retVal=False):
# Returns a conversion behavior suitable for a type
def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs):
if type.isSequence() or type.isMozMap():
if type.isSequence() or type.isRecord():
return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs)
if type.isDOMString():
assert not isEnforceRange and not isClamp
@ -1381,6 +1387,9 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
if returnType.isPromise():
assert not returnType.nullable()
return CGGeneric("Rc<Promise>")
if returnType.isGeckoInterface():
descriptor = descriptorProvider.getDescriptor(
returnType.unroll().inner.identifier.name)
@ -1408,7 +1417,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
if returnType.isSequence() or returnType.isMozMap():
if returnType.isSequence() or returnType.isRecord():
result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider)
result = wrapInNativeContainerType(returnType, result)
if returnType.nullable():
@ -1946,8 +1955,10 @@ class CGImports(CGWrapper):
if parentName:
descriptor = descriptorProvider.getDescriptor(parentName)
extras += [descriptor.path, descriptor.bindingPath]
elif t.isType() and t.isMozMap():
elif t.isType() and t.isRecord():
extras += ['dom::bindings::mozmap::MozMap']
elif isinstance(t, IDLPromiseType):
extras += ["dom::promise::Promise"]
else:
if t.isEnum():
extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
@ -3819,7 +3830,9 @@ class CGMemberJITInfo(CGThing):
return "JSVAL_TYPE_UNDEFINED"
if t.isSequence():
return "JSVAL_TYPE_OBJECT"
if t.isMozMap():
if t.isRecord():
return "JSVAL_TYPE_OBJECT"
if t.isPromise():
return "JSVAL_TYPE_OBJECT"
if t.isGeckoInterface():
return "JSVAL_TYPE_OBJECT"
@ -4055,7 +4068,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
elif type.isDictionary():
name = type.name
typeName = name
elif type.isSequence() or type.isMozMap():
elif type.isSequence() or type.isRecord():
name = type.name
inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider)
typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define()
@ -4208,7 +4221,7 @@ class CGUnionConversionStruct(CGThing):
else:
object = None
mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
mozMapMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
if len(mozMapMemberTypes) > 0:
assert len(mozMapMemberTypes) == 1
typeName = mozMapMemberTypes[0].name
@ -6870,6 +6883,8 @@ def process_arg(expr, arg):
expr += ".r()"
else:
expr = "&" + expr
elif isinstance(arg.type, IDLPromiseType):
expr = "&" + expr
return expr

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,162 @@
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, 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, 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] creator boolean (DOMString name, boolean value);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [CEReactions] used on a named creator")
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")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] jsonifier;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier")

View File

@ -13,7 +13,7 @@ def WebIDLTest(parser, harness):
def checkMethod(method, QName, name, signatures,
static=True, getter=False, setter=False, creator=False,
deleter=False, legacycaller=False, stringifier=False,
chromeOnly=False):
chromeOnly=False, htmlConstructor=False):
harness.ok(isinstance(method, WebIDL.IDLMethod),
"Should be an IDLMethod")
harness.ok(method.isMethod(), "Method is a method")
@ -29,6 +29,7 @@ def WebIDLTest(parser, harness):
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")
sigpairs = zip(method.signatures(), signatures)
@ -93,6 +94,21 @@ def WebIDLTest(parser, harness):
"constructor", [("TestChromeConstructor (Wrapper)", [])],
chromeOnly=True)
parser = parser.reset()
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructor {
};
""")
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:
@ -107,3 +123,151 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
# Test HTMLConstructor with argument
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor(DOMString a)]
interface TestHTMLConstructorWithArgs {
};
""")
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("""
[HTMLConstructor]
callback interface TestHTMLConstructorOnCallbackInterface {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
# Test HTMLConstructor and Constructor
parser = parser.reset()
threw = False
try:
parser.parse("""
[Constructor,
HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
};
""")
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("""
[HTMLConstructor,
Constructor]
interface TestHTMLConstructorAndConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
Constructor(DOMString a)]
interface TestHTMLConstructorAndConstructor {
};
""")
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Constructor(DOMString a),
HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
};
""")
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
# Test HTMLConstructor and ChromeConstructor
parser = parser.reset()
threw = False
try:
parser.parse("""
[ChromeConstructor,
HTMLConstructor]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
ChromeConstructor]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[ChromeConstructor(DOMString a),
HTMLConstructor]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
ChromeConstructor(DOMString a)]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")

View File

@ -34,3 +34,36 @@ def WebIDLTest(parser, harness):
interface TestNamedConstructorNoInterfaceObject {
};
""")
# Test HTMLConstructor and NoInterfaceObject
parser = parser.reset()
threw = False
try:
parser.parse("""
[NoInterfaceObject, HTMLConstructor]
interface TestHTMLConstructorNoInterfaceObject {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor, NoInterfaceObject]
interface TestHTMLConstructorNoInterfaceObject {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@ -158,21 +158,27 @@ def WebIDLTest(parser, harness):
"CallbackInterface?", "CallbackInterface2",
"object", "Callback", "Callback2", "optional Dict",
"optional Dict2", "sequence<long>", "sequence<short>",
"MozMap<object>", "MozMap<Dict>", "MozMap<long>",
"record<DOMString, object>",
"record<USVString, Dict>",
"record<ByteString, 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.
"Uint8Array", "Uint16Array",
"(long or Callback)", "optional (long or Dict)",
]
# When we can parse Date, we need to add it here.
# XXXbz we can, and should really do that...
# 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)", "optional (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" ]
nonStrings = allBut(argTypes, strings)
@ -182,16 +188,18 @@ def WebIDLTest(parser, harness):
sharedBufferSourceTypes = ["SharedArrayBuffer"]
interfaces = [ "Interface", "Interface?", "AncestorInterface",
"UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes
nullables = ["long?", "short?", "boolean?", "Interface?",
"CallbackInterface?", "optional Dict", "optional Dict2",
"Date?", "any", "Promise<any>?"]
nullables = (["long?", "short?", "boolean?", "Interface?",
"CallbackInterface?", "optional Dict", "optional Dict2",
"Date?", "any", "Promise<any>?"] +
allBut(unions, [ "(long or Callback)" ]))
dates = [ "Date", "Date?" ]
sequences = [ "sequence<long>", "sequence<short>" ]
nonUserObjects = nonObjects + interfaces + dates + sequences
otherObjects = allBut(argTypes, nonUserObjects + ["object"])
notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes)
mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ]
records = [ "record<DOMString, object>", "record<USVString, Dict>",
"record<ByteString, long>" ]
# Build a representation of the distinguishability table as a dict
# of dicts, holding True values where needed, holes elsewhere.
@ -231,9 +239,9 @@ def WebIDLTest(parser, harness):
allBut(argTypes, sequences + ["object"]))
setDistinguishable("sequence<short>",
allBut(argTypes, sequences + ["object"]))
setDistinguishable("MozMap<object>", nonUserObjects)
setDistinguishable("MozMap<Dict>", nonUserObjects)
setDistinguishable("MozMap<long>", nonUserObjects)
setDistinguishable("record<DOMString, object>", nonUserObjects)
setDistinguishable("record<USVString, Dict>", nonUserObjects)
setDistinguishable("record<ByteString, long>", nonUserObjects)
setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))
setDistinguishable("any", [])
@ -244,6 +252,10 @@ def WebIDLTest(parser, harness):
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("optional (long or Dict)",
allBut(nonUserObjects, numerics + nullables))
def areDistinguishable(type1, type2):
return data[type1].get(type2, False)
@ -263,7 +275,6 @@ def WebIDLTest(parser, harness):
callback Callback2 = long(short arg);
dictionary Dict {};
dictionary Dict2 {};
interface _Promise {};
interface TestInterface {%s
};
"""

View File

@ -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, 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, 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, e:
harness.ok("Multiple unresolvable definitions" in e.message,
"Should have name collision for dictionary")

View File

@ -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,52 +101,147 @@ 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("Maplike (readwrite)",
shouldPass("Iterable (key and value) inheriting from parent",
"""
interface Foo1 {
maplike<long, long>;
interface Foo1 : Foo2 {
iterable<long, long>;
};
""", mapRWMembers)
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)
""", mapRWMembers + unrelatedMembers)
shouldPass("Maplike (readwrite) inheriting from parent",
"""
interface Foo1 : Foo2 {
maplike<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""", 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>;
};
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)
""", mapROMembers + unrelatedMembers)
shouldPass("Maplike (readonly) inheriting from parent",
"""
interface Foo1 : Foo2 {
readonly maplike<long, long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""", 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("Setlike (readonly) inheriting from parent",
"""
interface Foo1 : Foo2 {
readonly setlike<long>;
};
interface Foo2 {
attribute long unrelatedAttribute;
long unrelatedMethod();
};
""", setROMembers, numProductions=2)
shouldPass("Inheritance of maplike/setlike",
"""

View File

@ -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]")

View File

@ -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();
@ -48,9 +45,36 @@ def WebIDLTest(parser, harness):
"Should not allow overloads which have both Promise and "
"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.")

View File

@ -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:
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,x:
threw = True
harness.ok(threw,
"Should have thrown on dictionary containing itself via record.")

View File

@ -6,15 +6,14 @@ def WebIDLTest(parser, harness):
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);
@ -49,25 +48,39 @@ def WebIDLTest(parser, harness):
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[3], "::SpecialMethods::__namedgetter", "__namedgetter",
getter=True)
checkMethod(iface.members[5], "::SpecialMethods::__namedsetter", "__namedsetter",
checkMethod(iface.members[4], "::SpecialMethods::__namedsetter", "__namedsetter",
setter=True)
checkMethod(iface.members[6], "::SpecialMethods::__namedcreator", "__namedcreator",
checkMethod(iface.members[5], "::SpecialMethods::__namedcreator", "__namedcreator",
creator=True)
checkMethod(iface.members[7], "::SpecialMethods::__nameddeleter", "__nameddeleter",
checkMethod(iface.members[6], "::SpecialMethods::__nameddeleter", "__nameddeleter",
deleter=True)
iface = results[1]
harness.check(len(iface.members), 4, "Expect 4 members")
harness.check(len(iface.members), 3, "Expect 3 members")
checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedgetterdeleter",
"__indexedgetterdeleter", getter=True, deleter=True)
checkMethod(iface.members[1], "::SpecialMethodsCombination::__indexedsettercreator",
checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedsettercreator",
"__indexedsettercreator", setter=True, creator=True)
checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedgetterdeleter",
checkMethod(iface.members[1], "::SpecialMethodsCombination::__namedgetterdeleter",
"__namedgetterdeleter", getter=True, deleter=True)
checkMethod(iface.members[3], "::SpecialMethodsCombination::__namedsettercreator",
checkMethod(iface.members[2], "::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")

View File

@ -194,7 +194,7 @@ impl Headers {
}
Ok(())
},
Some(HeadersInit::ByteStringMozMap(m)) => {
Some(HeadersInit::StringByteStringRecord(m)) => {
for (key, value) in m.iter() {
let key_vec = key.as_ref().to_string().into();
let headers_key = ByteString::new(key_vec);

View File

@ -311,9 +311,9 @@ impl Request {
try!(headers_copy.fill(Some(
HeadersInit::ByteStringSequenceSequence(init_sequence.clone()))));
},
&HeadersInit::ByteStringMozMap(ref init_map) => {
&HeadersInit::StringByteStringRecord(ref init_map) => {
try!(headers_copy.fill(Some(
HeadersInit::ByteStringMozMap(init_map.clone()))));
HeadersInit::StringByteStringRecord(init_map.clone()))));
},
}
}
@ -880,8 +880,8 @@ impl Clone for HeadersInit {
HeadersInit::Headers(h.clone()),
&HeadersInit::ByteStringSequenceSequence(ref b) =>
HeadersInit::ByteStringSequenceSequence(b.clone()),
&HeadersInit::ByteStringMozMap(ref m) =>
HeadersInit::ByteStringMozMap(m.clone()),
&HeadersInit::StringByteStringRecord(ref m) =>
HeadersInit::StringByteStringRecord(m.clone()),
}
}
}

View File

@ -662,9 +662,9 @@ impl TestBindingMethods for TestBinding {
fn PassStringMozMap(&self, _: MozMap<DOMString>) {}
fn PassByteStringMozMap(&self, _: MozMap<ByteString>) {}
fn PassMozMapOfMozMaps(&self, _: MozMap<MozMap<i32>>) {}
fn PassMozMapUnion(&self, _: UnionTypes::LongOrByteStringMozMap) {}
fn PassMozMapUnion2(&self, _: UnionTypes::TestBindingOrByteStringMozMap) {}
fn PassMozMapUnion3(&self, _: UnionTypes::TestBindingOrByteStringSequenceSequenceOrByteStringMozMap) {}
fn PassMozMapUnion(&self, _: UnionTypes::LongOrStringByteStringRecord) {}
fn PassMozMapUnion2(&self, _: UnionTypes::TestBindingOrStringByteStringRecord) {}
fn PassMozMapUnion3(&self, _: UnionTypes::TestBindingOrByteStringSequenceSequenceOrStringByteStringRecord) {}
fn ReceiveMozMap(&self) -> MozMap<i32> { MozMap::new() }
fn ReceiveNullableMozMap(&self) -> Option<MozMap<i32>> { Some(MozMap::new()) }
fn ReceiveMozMapOfNullableInts(&self) -> MozMap<Option<i32>> { MozMap::new() }
@ -750,9 +750,6 @@ impl TestBindingMethods for TestBinding {
fn AcceptPromise(&self, _promise: &Promise) {
}
fn AcceptNullablePromise(&self, _promise: Option<&Promise>) {
}
fn PassSequenceSequence(&self, _seq: Vec<Vec<i32>>) {}
fn ReturnSequenceSequence(&self) -> Vec<Vec<i32>> { vec![] }
fn PassUnionSequenceSequence(&self, seq: LongOrLongSequenceSequence) {

View File

@ -16,9 +16,9 @@ dictionary BluetoothLEScanFilterInit {
DOMString name;
DOMString namePrefix;
// Maps unsigned shorts to BluetoothDataFilters.
MozMap<BluetoothDataFilterInit> manufacturerData;
record<DOMString, BluetoothDataFilterInit> manufacturerData;
// Maps BluetoothServiceUUIDs to BluetoothDataFilters.
MozMap<BluetoothDataFilterInit> serviceData;
record<DOMString, BluetoothDataFilterInit> serviceData;
};
dictionary RequestDeviceOptions {

View File

@ -4,7 +4,7 @@
// https://fetch.spec.whatwg.org/#headers-class
typedef (Headers or sequence<sequence<ByteString>> or MozMap<ByteString>) HeadersInit;
typedef (Headers or sequence<sequence<ByteString>> or record<DOMString, ByteString>) HeadersInit;
[Constructor(optional HeadersInit init),
Exposed=(Window,Worker)]

View File

@ -434,33 +434,33 @@ interface TestBinding {
sequence<sequence<long>> returnSequenceSequence();
void passUnionSequenceSequence((long or sequence<sequence<long>>) seq);
void passMozMap(MozMap<long> arg);
void passNullableMozMap(MozMap<long>? arg);
void passMozMapOfNullableInts(MozMap<long?> arg);
void passOptionalMozMapOfNullableInts(optional MozMap<long?> arg);
void passOptionalNullableMozMapOfNullableInts(optional MozMap<long?>? arg);
void passCastableObjectMozMap(MozMap<TestBinding> arg);
void passNullableCastableObjectMozMap(MozMap<TestBinding?> arg);
void passCastableObjectNullableMozMap(MozMap<TestBinding>? arg);
void passNullableCastableObjectNullableMozMap(MozMap<TestBinding?>? arg);
void passOptionalMozMap(optional MozMap<long> arg);
void passOptionalNullableMozMap(optional MozMap<long>? arg);
void passOptionalNullableMozMapWithDefaultValue(optional MozMap<long>? arg = null);
void passOptionalObjectMozMap(optional MozMap<TestBinding> arg);
void passStringMozMap(MozMap<DOMString> arg);
void passByteStringMozMap(MozMap<ByteString> arg);
void passMozMapOfMozMaps(MozMap<MozMap<long>> arg);
void passMozMap(record<DOMString, long> arg);
void passNullableMozMap(record<DOMString, long>? arg);
void passMozMapOfNullableInts(record<DOMString, long?> arg);
void passOptionalMozMapOfNullableInts(optional record<DOMString, long?> arg);
void passOptionalNullableMozMapOfNullableInts(optional record<DOMString, long?>? arg);
void passCastableObjectMozMap(record<DOMString, TestBinding> arg);
void passNullableCastableObjectMozMap(record<DOMString, TestBinding?> arg);
void passCastableObjectNullableMozMap(record<DOMString, TestBinding>? arg);
void passNullableCastableObjectNullableMozMap(record<DOMString, TestBinding?>? arg);
void passOptionalMozMap(optional record<DOMString, long> arg);
void passOptionalNullableMozMap(optional record<DOMString, long>? arg);
void passOptionalNullableMozMapWithDefaultValue(optional record<DOMString, long>? arg = null);
void passOptionalObjectMozMap(optional record<DOMString, TestBinding> arg);
void passStringMozMap(record<DOMString, DOMString> arg);
void passByteStringMozMap(record<DOMString, ByteString> arg);
void passMozMapOfMozMaps(record<DOMString, record<DOMString, long>> arg);
void passMozMapUnion((long or MozMap<ByteString>) init);
void passMozMapUnion2((TestBinding or MozMap<ByteString>) init);
void passMozMapUnion3((TestBinding or sequence<sequence<ByteString>> or MozMap<ByteString>) init);
void passMozMapUnion((long or record<DOMString, ByteString>) init);
void passMozMapUnion2((TestBinding or record<DOMString, ByteString>) init);
void passMozMapUnion3((TestBinding or sequence<sequence<ByteString>> or record<DOMString, ByteString>) init);
MozMap<long> receiveMozMap();
MozMap<long>? receiveNullableMozMap();
MozMap<long?> receiveMozMapOfNullableInts();
MozMap<long?>? receiveNullableMozMapOfNullableInts();
MozMap<MozMap<long>> receiveMozMapOfMozMaps();
MozMap<any> receiveAnyMozMap();
record<DOMString, long> receiveMozMap();
record<DOMString, long>? receiveNullableMozMap();
record<DOMString, long?> receiveMozMapOfNullableInts();
record<DOMString, long?>? receiveNullableMozMapOfNullableInts();
record<DOMString, record<DOMString, long>> receiveMozMapOfMozMaps();
record<DOMString, any> receiveAnyMozMap();
static attribute boolean booleanAttributeStatic;
static void receiveVoidStatic();
@ -519,7 +519,6 @@ interface TestBinding {
Promise<any> returnRejectedPromise(any value);
readonly attribute Promise<boolean> promiseAttribute;
void acceptPromise(Promise<DOMString> string);
void acceptNullablePromise(Promise<DOMString>? string);
Promise<any> promiseNativeHandler(SimpleCallback? resolve, SimpleCallback? reject);
void promiseResolveNative(Promise<any> p, any value);
void promiseRejectNative(Promise<any> p, any value);