tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)