test_distinguishability.py (15272B)
1 import WebIDL 2 3 4 def firstArgType(method): 5 return method.signatures()[0][1][0].type 6 7 8 def WebIDLTest(parser, harness): 9 parser.parse( 10 """ 11 // Give our dictionary a required member so we don't need to 12 // mess with optional and default values. 13 dictionary Dict { 14 required long member; 15 }; 16 callback interface Foo { 17 }; 18 interface Bar { 19 // Bit of a pain to get things that have dictionary types 20 undefined passDict(Dict arg); 21 undefined passFoo(Foo arg); 22 undefined passNullableUnion((object? or DOMString) arg); 23 undefined passNullable(Foo? arg); 24 }; 25 """ 26 ) 27 results = parser.finish() 28 29 iface = results[2] 30 harness.ok(iface.isInterface(), "Should have interface") 31 dictMethod = iface.members[0] 32 ifaceMethod = iface.members[1] 33 nullableUnionMethod = iface.members[2] 34 nullableIfaceMethod = iface.members[3] 35 36 dictType = firstArgType(dictMethod) 37 ifaceType = firstArgType(ifaceMethod) 38 39 harness.ok(dictType.isDictionary(), "Should have dictionary type") 40 harness.ok(ifaceType.isInterface(), "Should have interface type") 41 harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type") 42 43 harness.ok( 44 not dictType.isDistinguishableFrom(ifaceType), 45 "Dictionary not distinguishable from callback interface", 46 ) 47 harness.ok( 48 not ifaceType.isDistinguishableFrom(dictType), 49 "Callback interface not distinguishable from dictionary", 50 ) 51 52 nullableUnionType = firstArgType(nullableUnionMethod) 53 nullableIfaceType = firstArgType(nullableIfaceMethod) 54 55 harness.ok(nullableUnionType.isUnion(), "Should have union type") 56 harness.ok(nullableIfaceType.isInterface(), "Should have interface type") 57 harness.ok(nullableIfaceType.nullable(), "Should have nullable type") 58 59 harness.ok( 60 not nullableUnionType.isDistinguishableFrom(nullableIfaceType), 61 "Nullable type not distinguishable from union with nullable member type", 62 ) 63 harness.ok( 64 not nullableIfaceType.isDistinguishableFrom(nullableUnionType), 65 "Union with nullable member type not distinguishable from nullable type", 66 ) 67 68 parser = parser.reset() 69 parser.parse( 70 """ 71 interface TestIface { 72 undefined passKid(Kid arg); 73 undefined passParent(Parent arg); 74 undefined passGrandparent(Grandparent arg); 75 undefined passUnrelated1(Unrelated1 arg); 76 undefined passUnrelated2(Unrelated2 arg); 77 undefined passArrayBuffer(ArrayBuffer arg); 78 undefined passArrayBuffer(ArrayBufferView arg); 79 }; 80 81 interface Kid : Parent {}; 82 interface Parent : Grandparent {}; 83 interface Grandparent {}; 84 interface Unrelated1 {}; 85 interface Unrelated2 {}; 86 """ 87 ) 88 results = parser.finish() 89 90 iface = results[0] 91 harness.ok(iface.isInterface(), "Should have interface") 92 argTypes = [firstArgType(method) for method in iface.members] 93 unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]] 94 95 for type1 in argTypes: 96 for type2 in argTypes: 97 distinguishable = type1 is not type2 and ( 98 type1 in unrelatedTypes or type2 in unrelatedTypes 99 ) 100 101 harness.check( 102 type1.isDistinguishableFrom(type2), 103 distinguishable, 104 "Type %s should %sbe distinguishable from type %s" 105 % (type1, "" if distinguishable else "not ", type2), 106 ) 107 harness.check( 108 type2.isDistinguishableFrom(type1), 109 distinguishable, 110 "Type %s should %sbe distinguishable from type %s" 111 % (type2, "" if distinguishable else "not ", type1), 112 ) 113 114 parser = parser.reset() 115 parser.parse( 116 """ 117 interface Dummy {}; 118 interface TestIface { 119 undefined method(long arg1, TestIface arg2); 120 undefined method(long arg1, long arg2); 121 undefined method(long arg1, Dummy arg2); 122 undefined method(DOMString arg1, DOMString arg2, DOMString arg3); 123 }; 124 """ 125 ) 126 results = parser.finish() 127 harness.check(len(results[1].members), 1, "Should look like we have one method") 128 harness.check( 129 len(results[1].members[0].signatures()), 4, "Should have four signatures" 130 ) 131 132 parser = parser.reset() 133 threw = False 134 try: 135 parser.parse( 136 """ 137 interface Dummy {}; 138 interface TestIface { 139 undefined method(long arg1, TestIface arg2); 140 undefined method(long arg1, long arg2); 141 undefined method(any arg1, Dummy arg2); 142 undefined method(DOMString arg1, DOMString arg2, DOMString arg3); 143 }; 144 """ 145 ) 146 parser.finish() 147 except WebIDL.WebIDLError: 148 threw = True 149 150 harness.ok( 151 threw, 152 "Should throw when args before the distinguishing arg are not " 153 "all the same type", 154 ) 155 156 parser = parser.reset() 157 threw = False 158 try: 159 parser.parse( 160 """ 161 interface Dummy {}; 162 interface TestIface { 163 undefined method(long arg1, TestIface arg2); 164 undefined method(long arg1, long arg2); 165 undefined method(any arg1, DOMString arg2); 166 undefined method(DOMString arg1, DOMString arg2, DOMString arg3); 167 }; 168 """ 169 ) 170 parser.finish() 171 except WebIDL.WebIDLError: 172 threw = True 173 174 harness.ok(threw, "Should throw when there is no distinguishing index") 175 176 # Now let's test our whole distinguishability table 177 argTypes = [ 178 "long", 179 "short", 180 "long?", 181 "short?", 182 "boolean", 183 "boolean?", 184 "undefined", 185 "undefined?", 186 "DOMString", 187 "ByteString", 188 "UTF8String", 189 "Enum", 190 "Enum2", 191 "Interface", 192 "Interface?", 193 "AncestorInterface", 194 "UnrelatedInterface", 195 "CallbackInterface", 196 "CallbackInterface?", 197 "CallbackInterface2", 198 "object", 199 "Callback", 200 "Callback2", 201 "Dict", 202 "Dict2", 203 "sequence<long>", 204 "sequence<short>", 205 "record<DOMString, object>", 206 "record<USVString, Dict>", 207 "record<ByteString, long>", 208 "record<UTF8String, long>", 209 "any", 210 "Promise<any>", 211 "Promise<any>?", 212 "USVString", 213 "JSString", 214 "ArrayBuffer", 215 "ArrayBufferView", 216 "Uint8Array", 217 "Uint16Array", 218 "(long or Callback)", 219 "(long or Dict)", 220 ] 221 222 # Try to categorize things a bit to keep list lengths down 223 def allBut(list1, list2): 224 return [ 225 a 226 for a in list1 227 if a not in list2 and a not in {"any", "Promise<any>", "Promise<any>?"} 228 ] 229 230 unionsWithCallback = ["(long or Callback)"] 231 unionsNoCallback = ["(long or Dict)"] 232 unions = unionsWithCallback + unionsNoCallback 233 numerics = ["long", "short", "long?", "short?"] 234 booleans = ["boolean", "boolean?"] 235 undefineds = ["undefined", "undefined?"] 236 primitives = numerics + booleans 237 nonNumerics = allBut(argTypes, numerics + unions) 238 nonBooleans = allBut(argTypes, booleans) 239 strings = [ 240 "DOMString", 241 "ByteString", 242 "Enum", 243 "Enum2", 244 "USVString", 245 "JSString", 246 "UTF8String", 247 ] 248 nonStrings = allBut(argTypes, strings) 249 nonObjects = undefineds + primitives + strings 250 bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"] 251 interfaces = [ 252 "Interface", 253 "Interface?", 254 "AncestorInterface", 255 "UnrelatedInterface", 256 ] + bufferSourceTypes 257 nullables = [ 258 "long?", 259 "short?", 260 "boolean?", 261 "undefined?", 262 "Interface?", 263 "CallbackInterface?", 264 "Dict", 265 "Dict2", 266 "Date?", 267 "any", 268 "Promise<any>?", 269 ] + unionsNoCallback 270 sequences = ["sequence<long>", "sequence<short>"] 271 nonUserObjects = nonObjects + interfaces + sequences 272 otherObjects = allBut(argTypes, nonUserObjects + ["object"]) 273 notRelatedInterfaces = ( 274 nonObjects 275 + ["UnrelatedInterface"] 276 + otherObjects 277 + sequences 278 + bufferSourceTypes 279 ) 280 records = [ 281 "record<DOMString, object>", 282 "record<USVString, Dict>", 283 "record<ByteString, long>", 284 "record<UTF8String, long>", 285 ] # JSString not supported in records 286 dicts = ["Dict", "Dict2"] 287 callbacks = ["Callback", "Callback2"] 288 callbackInterfaces = [ 289 "CallbackInterface", 290 "CallbackInterface?", 291 "CallbackInterface2", 292 ] 293 dictionaryLike = dicts + callbackInterfaces + records + unionsNoCallback 294 295 # Build a representation of the distinguishability table as a dict 296 # of dicts, holding True values where needed, holes elsewhere. 297 data = dict() 298 for type in argTypes: 299 data[type] = dict() 300 301 def setDistinguishable(type, types): 302 for other in types: 303 data[type][other] = True 304 305 setDistinguishable("long", nonNumerics) 306 setDistinguishable("short", nonNumerics) 307 setDistinguishable("long?", allBut(nonNumerics, nullables)) 308 setDistinguishable("short?", allBut(nonNumerics, nullables)) 309 setDistinguishable("boolean", nonBooleans) 310 setDistinguishable("boolean?", allBut(nonBooleans, nullables)) 311 setDistinguishable("undefined", allBut(argTypes, undefineds + dictionaryLike)) 312 setDistinguishable( 313 "undefined?", allBut(argTypes, undefineds + dictionaryLike + nullables) 314 ) 315 setDistinguishable("DOMString", nonStrings) 316 setDistinguishable("ByteString", nonStrings) 317 setDistinguishable("UTF8String", nonStrings) 318 setDistinguishable("USVString", nonStrings) 319 setDistinguishable("JSString", nonStrings) 320 setDistinguishable("Enum", nonStrings) 321 setDistinguishable("Enum2", nonStrings) 322 setDistinguishable("Interface", notRelatedInterfaces) 323 setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables)) 324 setDistinguishable("AncestorInterface", notRelatedInterfaces) 325 setDistinguishable( 326 "UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"]) 327 ) 328 setDistinguishable( 329 "CallbackInterface", 330 allBut(nonUserObjects + callbacks + unionsWithCallback, undefineds), 331 ) 332 setDistinguishable( 333 "CallbackInterface?", 334 allBut(nonUserObjects + callbacks + unionsWithCallback, nullables + undefineds), 335 ) 336 setDistinguishable( 337 "CallbackInterface2", 338 allBut(nonUserObjects + callbacks + unionsWithCallback, undefineds), 339 ) 340 setDistinguishable("object", nonObjects) 341 setDistinguishable( 342 "Callback", 343 nonUserObjects + unionsNoCallback + dicts + records + callbackInterfaces, 344 ) 345 setDistinguishable( 346 "Callback2", 347 nonUserObjects + unionsNoCallback + dicts + records + callbackInterfaces, 348 ) 349 setDistinguishable( 350 "Dict", 351 allBut(nonUserObjects + unionsWithCallback + callbacks, nullables + undefineds), 352 ) 353 setDistinguishable( 354 "Dict2", 355 allBut(nonUserObjects + unionsWithCallback + callbacks, nullables + undefineds), 356 ) 357 setDistinguishable( 358 "sequence<long>", 359 allBut(argTypes, sequences + ["object"]), 360 ) 361 setDistinguishable( 362 "sequence<short>", 363 allBut(argTypes, sequences + ["object"]), 364 ) 365 setDistinguishable( 366 "record<DOMString, object>", 367 allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), 368 ) 369 setDistinguishable( 370 "record<USVString, Dict>", 371 allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), 372 ) 373 # JSString not supported in records 374 setDistinguishable( 375 "record<ByteString, long>", 376 allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), 377 ) 378 setDistinguishable( 379 "record<UTF8String, long>", 380 allBut(nonUserObjects + unionsWithCallback + callbacks, undefineds), 381 ) 382 setDistinguishable("any", []) 383 setDistinguishable("Promise<any>", []) 384 setDistinguishable("Promise<any>?", []) 385 setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"])) 386 setDistinguishable( 387 "ArrayBufferView", 388 allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]), 389 ) 390 setDistinguishable( 391 "Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]) 392 ) 393 setDistinguishable( 394 "Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]) 395 ) 396 setDistinguishable( 397 "(long or Callback)", 398 allBut(nonUserObjects + dicts + records + callbackInterfaces, numerics), 399 ) 400 setDistinguishable( 401 "(long or Dict)", 402 allBut(nonUserObjects + callbacks, numerics + nullables + undefineds), 403 ) 404 405 def areDistinguishable(type1, type2): 406 return data[type1].get(type2, False) 407 408 def checkDistinguishability(parser, type1, type2): 409 idlTemplate = """ 410 enum Enum { "a", "b" }; 411 enum Enum2 { "c", "d" }; 412 interface Interface : AncestorInterface {}; 413 interface AncestorInterface {}; 414 interface UnrelatedInterface {}; 415 callback interface CallbackInterface {}; 416 callback interface CallbackInterface2 {}; 417 callback Callback = any(); 418 callback Callback2 = long(short arg); 419 [LegacyTreatNonObjectAsNull] callback LegacyCallback1 = any(); 420 // Give our dictionaries required members so we don't need to 421 // mess with optional and default values. 422 dictionary Dict { required long member; }; 423 dictionary Dict2 { required long member; }; 424 interface TestInterface {%s 425 }; 426 """ 427 if type1 in undefineds or type2 in undefineds: 428 methods = """ 429 (%s or %s) myMethod();""" % ( 430 type1, 431 type2, 432 ) 433 else: 434 methodTemplate = """ 435 undefined myMethod(%s arg);""" 436 methods = (methodTemplate % type1) + (methodTemplate % type2) 437 idl = idlTemplate % methods 438 439 parser = parser.reset() 440 threw = False 441 try: 442 parser.parse(idl) 443 parser.finish() 444 except WebIDL.WebIDLError: 445 threw = True 446 447 if areDistinguishable(type1, type2): 448 harness.ok( 449 not threw, 450 "Should not throw for '%s' and '%s' because they are distinguishable" 451 % (type1, type2), 452 ) 453 else: 454 harness.ok( 455 threw, 456 "Should throw for '%s' and '%s' because they are not distinguishable" 457 % (type1, type2), 458 ) 459 460 # Enumerate over everything in both orders, since order matters in 461 # terms of our implementation of distinguishability checks 462 for type1 in argTypes: 463 for type2 in argTypes: 464 checkDistinguishability(parser, type1, type2)