def firstArgType(method): return method.signatures()[0][1][0].type 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 undefined passDict(Dict arg); undefined passFoo(Foo arg); undefined passNullableUnion((object? or DOMString) arg); undefined passNullable(Foo? arg); }; """ ) results = parser.finish() iface = results[2] harness.ok(iface.isInterface(), "Should have interface") dictMethod = iface.members[0] ifaceMethod = iface.members[1] nullableUnionMethod = iface.members[2] nullableIfaceMethod = iface.members[3] dictType = firstArgType(dictMethod) ifaceType = firstArgType(ifaceMethod) harness.ok(dictType.isDictionary(), "Should have dictionary type") harness.ok(ifaceType.isInterface(), "Should have interface type") harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type") harness.ok( not dictType.isDistinguishableFrom(ifaceType), "Dictionary not distinguishable from callback interface", ) harness.ok( not ifaceType.isDistinguishableFrom(dictType), "Callback interface not distinguishable from dictionary", ) nullableUnionType = firstArgType(nullableUnionMethod) nullableIfaceType = firstArgType(nullableIfaceMethod) harness.ok(nullableUnionType.isUnion(), "Should have union type") harness.ok(nullableIfaceType.isInterface(), "Should have interface type") harness.ok(nullableIfaceType.nullable(), "Should have nullable type") harness.ok( not nullableUnionType.isDistinguishableFrom(nullableIfaceType), "Nullable type not distinguishable from union with nullable " "member type", ) harness.ok( not nullableIfaceType.isDistinguishableFrom(nullableUnionType), "Union with nullable member type not distinguishable from " "nullable type", ) parser = parser.reset() parser.parse( """ interface TestIface { undefined passKid(Kid arg); undefined passParent(Parent arg); undefined passGrandparent(Grandparent arg); undefined passUnrelated1(Unrelated1 arg); undefined passUnrelated2(Unrelated2 arg); undefined passArrayBuffer(ArrayBuffer arg); undefined passArrayBuffer(ArrayBufferView arg); }; interface Kid : Parent {}; interface Parent : Grandparent {}; interface Grandparent {}; interface Unrelated1 {}; interface Unrelated2 {}; """ ) results = parser.finish() iface = results[0] harness.ok(iface.isInterface(), "Should have interface") argTypes = [firstArgType(method) for method in iface.members] unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]] for type1 in argTypes: for type2 in argTypes: distinguishable = type1 is not type2 and ( type1 in unrelatedTypes or type2 in unrelatedTypes ) harness.check( type1.isDistinguishableFrom(type2), distinguishable, "Type %s should %sbe distinguishable from type %s" % (type1, "" if distinguishable else "not ", type2), ) harness.check( type2.isDistinguishableFrom(type1), distinguishable, "Type %s should %sbe distinguishable from type %s" % (type2, "" if distinguishable else "not ", type1), ) parser = parser.reset() parser.parse( """ interface Dummy {}; interface TestIface { undefined method(long arg1, TestIface arg2); undefined method(long arg1, long arg2); undefined method(long arg1, Dummy arg2); undefined method(DOMString arg1, DOMString arg2, DOMString arg3); }; """ ) results = parser.finish() harness.check(len(results[1].members), 1, "Should look like we have one method") harness.check( len(results[1].members[0].signatures()), 4, "Should have four signatures" ) parser = parser.reset() threw = False try: parser.parse( """ interface Dummy {}; interface TestIface { undefined method(long arg1, TestIface arg2); undefined method(long arg1, long arg2); undefined method(any arg1, Dummy arg2); undefined method(DOMString arg1, DOMString arg2, DOMString arg3); }; """ ) parser.finish() except Exception: threw = True harness.ok( threw, "Should throw when args before the distinguishing arg are not " "all the same type", ) parser = parser.reset() threw = False try: parser.parse( """ interface Dummy {}; interface TestIface { undefined method(long arg1, TestIface arg2); undefined method(long arg1, long arg2); undefined method(any arg1, DOMString arg2); undefined method(DOMString arg1, DOMString arg2, DOMString arg3); }; """ ) parser.finish() except Exception: threw = True harness.ok(threw, "Should throw when there is no distinguishing index") # Now let's test our whole distinguishability table argTypes = [ "long", "short", "long?", "short?", "boolean", "boolean?", "undefined", "undefined?", "DOMString", "ByteString", "UTF8String", "Enum", "Enum2", "Interface", "Interface?", "AncestorInterface", "UnrelatedInterface", "CallbackInterface", "CallbackInterface?", "CallbackInterface2", "object", "Callback", "Callback2", "Dict", "Dict2", "sequence", "sequence", "record", "record", "record", "record", "any", "Promise", "Promise?", "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" and a != "Promise?") ] unions = ["(long or Callback)", "(long or Dict)"] numerics = ["long", "short", "long?", "short?"] booleans = ["boolean", "boolean?"] undefineds = ["undefined", "undefined?"] primitives = numerics + booleans nonNumerics = allBut(argTypes, numerics + unions) nonBooleans = allBut(argTypes, booleans) strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString", "JSString", "UTF8String", ] nonStrings = allBut(argTypes, strings) nonObjects = undefineds + primitives + strings bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"] interfaces = [ "Interface", "Interface?", "AncestorInterface", "UnrelatedInterface", ] + bufferSourceTypes nullables = [ "long?", "short?", "boolean?", "undefined?", "Interface?", "CallbackInterface?", "Dict", "Dict2", "Date?", "any", "Promise?", ] + allBut(unions, ["(long or Callback)"]) sequences = ["sequence", "sequence"] nonUserObjects = nonObjects + interfaces + sequences otherObjects = allBut(argTypes, nonUserObjects + ["object"]) notRelatedInterfaces = ( nonObjects + ["UnrelatedInterface"] + otherObjects + sequences + bufferSourceTypes ) records = [ "record", "record", "record", "record", ] # JSString not supported in records dictionaryLike = ( [ "Dict", "Dict2", "CallbackInterface", "CallbackInterface?", "CallbackInterface2", ] + records + allBut(unions, ["(long or Callback)"]) ) # Build a representation of the distinguishability table as a dict # of dicts, holding True values where needed, holes elsewhere. data = dict() for type in argTypes: data[type] = dict() def setDistinguishable(type, types): for other in types: data[type][other] = True setDistinguishable("long", nonNumerics) setDistinguishable("short", nonNumerics) setDistinguishable("long?", allBut(nonNumerics, nullables)) setDistinguishable("short?", allBut(nonNumerics, nullables)) setDistinguishable("boolean", nonBooleans) setDistinguishable("boolean?", allBut(nonBooleans, nullables)) setDistinguishable("undefined", allBut(argTypes, undefineds + dictionaryLike)) setDistinguishable( "undefined?", allBut(argTypes, undefineds + dictionaryLike + 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) setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables)) setDistinguishable("AncestorInterface", notRelatedInterfaces) setDistinguishable( "UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"]) ) setDistinguishable("CallbackInterface", allBut(nonUserObjects, undefineds)) setDistinguishable( "CallbackInterface?", allBut(nonUserObjects, nullables + undefineds) ) setDistinguishable("CallbackInterface2", allBut(nonUserObjects, undefineds)) setDistinguishable("object", nonObjects) setDistinguishable("Callback", nonUserObjects) setDistinguishable("Callback2", nonUserObjects) setDistinguishable("Dict", allBut(nonUserObjects, nullables + undefineds)) setDistinguishable("Dict2", allBut(nonUserObjects, nullables + undefineds)) setDistinguishable("sequence", allBut(argTypes, sequences + ["object"])) setDistinguishable("sequence", allBut(argTypes, sequences + ["object"])) setDistinguishable("record", allBut(nonUserObjects, undefineds)) setDistinguishable("record", allBut(nonUserObjects, undefineds)) # JSString not supported in records setDistinguishable("record", allBut(nonUserObjects, undefineds)) setDistinguishable("record", allBut(nonUserObjects, undefineds)) setDistinguishable("any", []) setDistinguishable("Promise", []) setDistinguishable("Promise?", []) setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"])) setDistinguishable( "ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]), ) setDistinguishable( "Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]) ) setDistinguishable( "Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]) ) setDistinguishable("(long or Callback)", allBut(nonUserObjects, numerics)) setDistinguishable( "(long or Dict)", allBut(nonUserObjects, numerics + nullables + undefineds) ) def areDistinguishable(type1, type2): return data[type1].get(type2, False) def checkDistinguishability(parser, type1, type2): idlTemplate = """ enum Enum { "a", "b" }; enum Enum2 { "c", "d" }; interface Interface : AncestorInterface {}; interface AncestorInterface {}; interface UnrelatedInterface {}; callback interface CallbackInterface {}; callback interface CallbackInterface2 {}; callback Callback = any(); callback Callback2 = long(short arg); // 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 }; """ if type1 in undefineds or type2 in undefineds: methods = """ (%s or %s) myMethod();""" % ( type1, type2, ) else: methodTemplate = """ undefined myMethod(%s arg);""" methods = (methodTemplate % type1) + (methodTemplate % type2) idl = idlTemplate % methods parser = parser.reset() threw = False try: parser.parse(idl) parser.finish() except Exception: threw = True if areDistinguishable(type1, type2): harness.ok( not threw, "Should not throw for '%s' and '%s' because they are distinguishable" % (type1, type2), ) else: harness.ok( threw, "Should throw for '%s' and '%s' because they are not distinguishable" % (type1, type2), ) # Enumerate over everything in both orders, since order matters in # terms of our implementation of distinguishability checks for type1 in argTypes: for type2 in argTypes: checkDistinguishability(parser, type1, type2)