tor-browser

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

utility.js (13621B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
      6 
      7 "use strict";
      8 
      9 loadRelativeToScript('dumpCFG.js');
     10 
     11 // Attribute bits - each call edge may carry a set of 'attrs' bits, saying eg
     12 // that the edge takes place within a scope where GC is suppressed, for
     13 // example.
     14 var ATTR_GC_SUPPRESSED     = 1 << 0;
     15 var ATTR_CANSCRIPT_BOUNDED = 1 << 1; // Unimplemented
     16 var ATTR_DOM_ITERATING     = 1 << 2; // Unimplemented
     17 var ATTR_NONRELEASING      = 1 << 3; // ~RefPtr of value whose refcount will not go to zero
     18 var ATTR_REPLACED          = 1 << 4; // Ignore edge, it was replaced by zero or more better edges.
     19 var ATTR_SYNTHETIC         = 1 << 5; // Call was manufactured in some way.
     20 
     21 var ATTR_LAST              = 1 << 5;
     22 var ATTRS_NONE             = 0;
     23 var ATTRS_ALL              = (ATTR_LAST << 1) - 1; // All possible bits set
     24 
     25 // The traversal algorithms we run will recurse into children if you change any
     26 // attrs bit to zero. Use all bits set to maximally attributed, including
     27 // additional bits that all just mean "unvisited", so that the first time we
     28 // see a node with this attrs, we're guaranteed to turn at least one bit off
     29 // and thereby keep going.
     30 var ATTRS_UNVISITED = 0xffff;
     31 
     32 // gcc appends this to mangled function names for "not in charge"
     33 // constructors/destructors.
     34 var internalMarker = " *INTERNAL* ";
     35 
     36 if (! Set.prototype.hasOwnProperty("update")) {
     37    Object.defineProperty(Set.prototype, "update", {
     38        value: function (collection) {
     39            for (let elt of collection)
     40                this.add(elt);
     41        }
     42    });
     43 }
     44 
     45 function assert(x, msg)
     46 {
     47    if (x)
     48        return;
     49    debugger;
     50    if (msg)
     51        throw new Error("assertion failed: " + msg + "\n");
     52    else
     53        throw new Error("assertion failed");
     54 }
     55 
     56 function defined(x) {
     57    return x !== undefined;
     58 }
     59 
     60 function xprint(x, padding)
     61 {
     62    if (!padding)
     63        padding = "";
     64    if (x instanceof Array) {
     65        print(padding + "[");
     66        for (var elem of x)
     67            xprint(elem, padding + " ");
     68        print(padding + "]");
     69    } else if (x instanceof Object) {
     70        print(padding + "{");
     71        for (var prop in x) {
     72            print(padding + " " + prop + ":");
     73            xprint(x[prop], padding + "  ");
     74        }
     75        print(padding + "}");
     76    } else {
     77        print(padding + x);
     78    }
     79 }
     80 
     81 // Command-line argument parser.
     82 //
     83 // `parameters` is a dict of parameters specs, each of which is a dict with keys:
     84 //
     85 //   - name: name of option, prefixed with "--" if it is named (otherwise, it
     86 //     is interpreted as a positional parameter.)
     87 //   - dest: key to store the result in, defaulting to the parameter name without
     88 //     any leading "--"" and with dashes replaced with underscores.
     89 //   - default: value of option if no value is given. Positional parameters with
     90 //     a default value are optional. If no default is given, the parameter's name
     91 //     is not included in the return value.
     92 //   - type: `bool` if it takes no argument, otherwise an argument is required.
     93 //     Named arguments default to 'bool', positional arguments to 'string'.
     94 //   - nargs: the only supported value is `+`, which means to grab all following
     95 //     arguments, up to the next named option, and store them as a list.
     96 //
     97 // The command line is parsed for `--foo=value` and `--bar` arguments.
     98 //
     99 // Return value is a dict of parameter values, keyed off of `dest` as determined
    100 // above. An extra option named "rest" will be set to the list of all remaining
    101 // arguments passed in.
    102 //
    103 function parse_options(parameters, inArgs = scriptArgs) {
    104    const options = {};
    105 
    106    const named = {};
    107    const positional = [];
    108    for (const param of parameters) {
    109        if (param.name.startsWith("-")) {
    110            named[param.name] = param;
    111            if (!param.dest) {
    112                if (!param.name.startsWith("--")) {
    113                    throw new Error(`parameter '${param.name}' requires param.dest to be set`);
    114                }
    115                param.dest = param.name.substring(2).replace("-", "_");
    116            }
    117        } else {
    118            if (!('default' in param) && positional.length > 0 && ('default' in positional.at(-1))) {
    119                throw new Error(`required parameter '${param.name}' follows optional parameter`);
    120            }
    121            param.positional = true;
    122            positional.push(param);
    123            param.dest = param.dest || param.name.replace("-", "_");
    124        }
    125 
    126        if (!param.type) {
    127            if (param.nargs === "+") {
    128                param.type = "list";
    129            } else if (param.positional) {
    130                param.type = "string";
    131            } else {
    132                param.type = "bool";
    133            }
    134        }
    135 
    136        if ('default' in param) {
    137            options[param.dest] = param.default;
    138        }
    139    }
    140 
    141    options.rest = [];
    142    const args = [...inArgs];
    143    let grabbing_into = undefined;
    144    while (args.length > 0) {
    145        let arg = args.shift();
    146        let param;
    147        if (arg.startsWith("-") && arg in named) {
    148            param = named[arg];
    149            if (param.type !== 'bool') {
    150                if (args.length == 0) {
    151                    throw(new Error(`${param.name} requires an argument`));
    152                }
    153                arg = args.shift();
    154            }
    155        } else {
    156            const pos = arg.indexOf("=");
    157            if (pos != -1) {
    158                const name = arg.substring(0, pos);
    159                param = named[name];
    160                if (!param) {
    161                    throw(new Error(`Unknown option '${name}'`));
    162                } else if (param.type === 'bool') {
    163                    throw(new Error(`--${param.name} does not take an argument`));
    164                }
    165                arg = arg.substring(pos + 1);
    166            }
    167        }
    168 
    169        // If this isn't a --named param, and we're not accumulating into a nargs="+" param, then
    170        // use the next positional.
    171        if (!param && !grabbing_into && positional.length > 0) {
    172            param = positional.shift();
    173        }
    174 
    175        // If a parameter was identified, then any old accumulator is done and we might start a new one.
    176        if (param) {
    177            if (param.type === 'list') {
    178                grabbing_into = options[param.dest] = options[param.dest] || [];
    179            } else {
    180                grabbing_into = undefined;
    181            }
    182        }
    183 
    184        if (grabbing_into) {
    185            grabbing_into.push(arg);
    186        } else if (param) {
    187            if (param.type === 'bool') {
    188                options[param.dest] = true;
    189            } else {
    190                options[param.dest] = arg;
    191            }
    192        } else {
    193            options.rest.push(arg);
    194        }
    195    }
    196 
    197    for (const param of positional) {
    198        if (!('default' in param)) {
    199            throw(new Error(`'${param.name}' option is required`));
    200        }
    201    }
    202 
    203    for (const param of parameters) {
    204        if (param.nargs === '+' && options[param.dest].length == 0) {
    205            throw(new Error(`at least one value required for option '${param.name}'`));
    206        }
    207    }
    208 
    209    return options;
    210 }
    211 
    212 function sameBlockId(id0, id1)
    213 {
    214    if (id0.Kind != id1.Kind)
    215        return false;
    216    if (!sameVariable(id0.Variable, id1.Variable))
    217        return false;
    218    if (id0.Kind == "Loop" && id0.Loop != id1.Loop)
    219        return false;
    220    return true;
    221 }
    222 
    223 function sameVariable(var0, var1)
    224 {
    225    assert("Name" in var0 || var0.Kind == "This" || var0.Kind == "Return");
    226    assert("Name" in var1 || var1.Kind == "This" || var1.Kind == "Return");
    227    if ("Name" in var0)
    228        return "Name" in var1 && var0.Name[0] == var1.Name[0];
    229    return var0.Kind == var1.Kind;
    230 }
    231 
    232 function blockIdentifier(body)
    233 {
    234    if (body.BlockId.Kind == "Loop")
    235        return body.BlockId.Loop;
    236    assert(body.BlockId.Kind == "Function", "body.Kind should be Function, not " + body.BlockId.Kind);
    237    return body.BlockId.Variable.Name[0];
    238 }
    239 
    240 function collectBodyEdges(body)
    241 {
    242    body.predecessors = [];
    243    body.successors = [];
    244    if (!("PEdge" in body))
    245        return;
    246 
    247    for (var edge of body.PEdge) {
    248        var [ source, target ] = edge.Index;
    249        if (!(target in body.predecessors))
    250            body.predecessors[target] = [];
    251        body.predecessors[target].push(edge);
    252        if (!(source in body.successors))
    253            body.successors[source] = [];
    254        body.successors[source].push(edge);
    255    }
    256 }
    257 
    258 function getPredecessors(body)
    259 {
    260    if (!('predecessors' in body))
    261        collectBodyEdges(body);
    262    return body.predecessors;
    263 }
    264 
    265 function getSuccessors(body)
    266 {
    267    if (!('successors' in body))
    268        collectBodyEdges(body);
    269    return body.successors;
    270 }
    271 
    272 // Split apart a function from sixgill into its mangled and unmangled name. If
    273 // no mangled name was given, use the unmangled name as its mangled name
    274 function splitFunction(func)
    275 {
    276    var split = func.indexOf("$");
    277    if (split != -1)
    278        return [ func.substr(0, split), func.substr(split+1) ];
    279    split = func.indexOf("|");
    280    if (split != -1)
    281        return [ func.substr(0, split), func.substr(split+1) ];
    282    return [ func, func ];
    283 }
    284 
    285 function mangled(fullname)
    286 {
    287    var [ mangled, unmangled ] = splitFunction(fullname);
    288    return mangled;
    289 }
    290 
    291 function readable(fullname)
    292 {
    293    var [ mangled, unmangled ] = splitFunction(fullname);
    294    return unmangled;
    295 }
    296 
    297 function xdbLibrary()
    298 {
    299    var lib = ctypes.open(os.getenv('XDB'));
    300    var api = {
    301        open: lib.declare("xdb_open", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr),
    302        min_data_stream: lib.declare("xdb_min_data_stream", ctypes.default_abi, ctypes.int),
    303        max_data_stream: lib.declare("xdb_max_data_stream", ctypes.default_abi, ctypes.int),
    304        read_key: lib.declare("xdb_read_key", ctypes.default_abi, ctypes.char.ptr, ctypes.int),
    305        read_entry: lib.declare("xdb_read_entry", ctypes.default_abi, ctypes.char.ptr, ctypes.char.ptr),
    306        free_string: lib.declare("xdb_free", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr)
    307    };
    308    try {
    309        api.lookup_key = lib.declare("xdb_lookup_key", ctypes.default_abi, ctypes.int, ctypes.char.ptr);
    310    } catch (e) {
    311        // lookup_key is for development use only and is not strictly necessary.
    312    }
    313    return api;
    314 }
    315 
    316 function openLibrary(names) {
    317    for (const name of names) {
    318        try {
    319            return ctypes.open(name);
    320        } catch(e) {
    321        }
    322    }
    323    return undefined;
    324 }
    325 
    326 function cLibrary()
    327 {
    328    const lib = openLibrary(['libc.so.6', 'libc.so', 'libc.dylib']);
    329    if (!lib) {
    330        throw new Error("Unable to open libc");
    331    }
    332 
    333    if (getBuildConfiguration("moz-memory")) {
    334        throw new Error("cannot use libc functions with --enable-jemalloc, since they will be routed " +
    335                        "through jemalloc, but calling libc.free() directly will bypass it and the " +
    336                        "malloc/free will be mismatched");
    337    }
    338 
    339    return {
    340        fopen: lib.declare("fopen", ctypes.default_abi, ctypes.void_t.ptr, ctypes.char.ptr, ctypes.char.ptr),
    341        getline: lib.declare("getline", ctypes.default_abi, ctypes.ssize_t, ctypes.char.ptr.ptr, ctypes.size_t.ptr, ctypes.void_t.ptr),
    342        fclose: lib.declare("fclose", ctypes.default_abi, ctypes.int, ctypes.void_t.ptr),
    343        free: lib.declare("free", ctypes.default_abi, ctypes.void_t, ctypes.void_t.ptr),
    344    };
    345 }
    346 
    347 function* readFileLines_gen(filename)
    348 {
    349    var libc = cLibrary();
    350    var linebuf = ctypes.char.ptr();
    351    var bufsize = ctypes.size_t(0);
    352    var fp = libc.fopen(filename, "r");
    353    if (fp.isNull())
    354        throw new Error("Unable to open '" + filename + "'");
    355 
    356    while (libc.getline(linebuf.address(), bufsize.address(), fp) > 0)
    357        yield linebuf.readString();
    358    libc.fclose(fp);
    359    libc.free(ctypes.void_t.ptr(linebuf));
    360 }
    361 
    362 function addToKeyedList(collection, key, entry)
    363 {
    364    if (!(key in collection))
    365        collection[key] = [];
    366    collection[key].push(entry);
    367    return collection[key];
    368 }
    369 
    370 function addToMappedList(map, key, entry)
    371 {
    372    if (!map.has(key))
    373        map.set(key, []);
    374    map.get(key).push(entry);
    375    return map.get(key);
    376 }
    377 
    378 function loadTypeInfo(filename)
    379 {
    380    return JSON.parse(os.file.readFile(filename));
    381 }
    382 
    383 // Given the range `first` .. `last`, break it down into `count` batches and
    384 // return the start of the (1-based) `num` batch.
    385 function batchStart(num, count, first, last) {
    386  const N = (last - first) + 1;
    387  return Math.floor((num - 1) / count * N) + first;
    388 }
    389 
    390 // As above, but return the last value in the (1-based) `num` batch.
    391 function batchLast(num, count, first, last) {
    392  const N = (last - first) + 1;
    393  return Math.floor(num / count * N) + first - 1;
    394 }
    395 
    396 // Debugging tool. See usage below.
    397 function PropertyTracer(traced_prop, check) {
    398    return {
    399        matches(prop, value) {
    400            if (prop != traced_prop)
    401                return false;
    402            if ('value' in check)
    403                return value == check.value;
    404            return true;
    405        },
    406 
    407        // Also called when defining a property.
    408        set(obj, prop, value) {
    409            if (this.matches(prop, value))
    410                debugger;
    411            return Reflect.set(...arguments);
    412        },
    413    };
    414 }
    415 
    416 // Usage: var myobj = traced({}, 'name', {value: 'Bob'})
    417 //
    418 // This will execute a `debugger;` statement when myobj['name'] is defined or
    419 // set to 'Bob'.
    420 function traced(obj, traced_prop, check) {
    421  return new Proxy(obj, PropertyTracer(traced_prop, check));
    422 }