addon-sdk/source/lib/sdk/util/sequence.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     4  */
     5 "use strict";
     7 module.metadata = {
     8   "stability": "experimental"
     9 };
    11 // Disclamer:
    12 // In this module we'll have some common argument / variable names
    13 // to hint their type or behavior.
    14 //
    15 // - `f` stands for "function" that is intended to be side effect
    16 //   free.
    17 // - `p` stands for "predicate" that is function which returns logical
    18 //   true or false and is intended to be side effect free.
    19 // - `x` / `y` single item of the sequence.
    20 // - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies
    21 //    type of the items in sequence, so sequence is not of the same item.
    22 // - `_` used for argument(s) or variable(s) who's values are ignored.
    24 const { complement, flip, identity } = require("../lang/functional");
    25 const { iteratorSymbol } = require("../util/iteration");
    26 const { isArray, isArguments, isMap, isSet,
    27         isString, isBoolean, isNumber } = require("../lang/type");
    29 const Sequence = function Sequence(iterator) {
    30   if (iterator.isGenerator && iterator.isGenerator())
    31     this[iteratorSymbol] = iterator;
    32   else
    33     throw TypeError("Expected generator argument");
    34 };
    35 exports.Sequence = Sequence;
    37 const polymorphic = dispatch => x =>
    38   x === null ? dispatch.null(null) :
    39   x === void(0) ? dispatch.void(void(0)) :
    40   isArray(x) ? (dispatch.array || dispatch.indexed)(x) :
    41   isString(x) ? (dispatch.string || dispatch.indexed)(x) :
    42   isArguments(x) ? (dispatch.arguments || dispatch.indexed)(x) :
    43   isMap(x) ? dispatch.map(x) :
    44   isSet(x) ? dispatch.set(x) :
    45   isNumber(x) ? dispatch.number(x) :
    46   isBoolean(x) ? dispatch.boolean(x) :
    47   dispatch.default(x);
    49 const nogen = function*() {};
    50 const empty = () => new Sequence(nogen);
    51 exports.empty = empty;
    53 const seq = polymorphic({
    54   null: empty,
    55   void: empty,
    56   array: identity,
    57   string: identity,
    58   arguments: identity,
    59   map: identity,
    60   set: identity,
    61   default: x => x instanceof Sequence ? x : new Sequence(x)
    62 });
    63 exports.seq = seq;
    68 // Function to cast seq to string.
    69 const string = (...etc) => "".concat(...etc);
    70 exports.string = string;
    72 // Function for casting seq to plain object.
    73 const object = (...pairs) => {
    74   let result = {};
    75   for (let [key, value] of pairs)
    76     result[key] = value;
    78   return result;
    79 };
    80 exports.object = object;
    82 // Takes `getEnumerator` function that returns `nsISimpleEnumerator`
    83 // and creates lazy sequence of it's items. Note that function does
    84 // not take `nsISimpleEnumerator` itslef because that would allow
    85 // single iteration, which would not be consistent with rest of the
    86 // lazy sequences.
    87 const fromEnumerator = getEnumerator => seq(function* () {
    88   const enumerator = getEnumerator();
    89   while (enumerator.hasMoreElements())
    90    yield enumerator.getNext();
    91 });
    92 exports.fromEnumerator = fromEnumerator;
    94 // Takes `object` and returns lazy sequence of own `[key, value]`
    95 // pairs (does not include inherited and non enumerable keys).
    96 const pairs = polymorphic({
    97   null: empty,
    98   void: empty,
    99   map: identity,
   100   indexed: indexed => seq(function* () {
   101     const count = indexed.length;
   102     let index = 0;
   103     while (index < count) {
   104       yield [index, indexed[index]];
   105       index = index + 1;
   106     }
   107   }),
   108   default: object => seq(function* () {
   109     for (let key of Object.keys(object))
   110       yield [key, object[key]];
   111   })
   112 });
   113 exports.pairs = pairs;
   116 const keys = polymorphic({
   117   null: empty,
   118   void: empty,
   119   indexed: indexed => seq(function* () {
   120     const count = indexed.length;
   121     let index = 0;
   122     while (index < count) {
   123       yield index;
   124       index = index + 1;
   125     }
   126   }),
   127   map: map => seq(function* () {
   128     for (let [key, _] of map)
   129       yield key;
   130   }),
   131   default: object => seq(function* () {
   132     for (let key of Object.keys(object))
   133       yield key;
   134   })
   135 });
   136 exports.keys = keys;
   139 const values = polymorphic({
   140   null: empty,
   141   void: empty,
   142   set: identity,
   143   indexed: indexed => seq(function* () {
   144     const count = indexed.length;
   145     let index = 0;
   146     while (index < count) {
   147       yield indexed[index];
   148       index = index + 1;
   149     }
   150   }),
   151   map: map => seq(function* () {
   152     for (let [_, value] of map) yield value;
   153   }),
   154   default: object => seq(function* () {
   155     for (let key of Object.keys(object)) yield object[key];
   156   })
   157 });
   158 exports.values = values;
   162 // Returns a lazy sequence of `x`, `f(x)`, `f(f(x))` etc.
   163 // `f` must be free of side-effects. Note that returned
   164 // sequence is infinite so it must be consumed partially.
   165 //
   166 // Implements clojure iterate:
   167 // http://clojuredocs.org/clojure_core/clojure.core/iterate
   168 const iterate = (f, x) => seq(function* () {
   169   let state = x;
   170   while (true) {
   171     yield state;
   172     state = f(state);
   173   }
   174 });
   175 exports.iterate = iterate;
   177 // Returns a lazy sequence of the items in sequence for which `p(item)`
   178 // returns `true`. `p` must be free of side-effects.
   179 //
   180 // Implements clojure filter:
   181 // http://clojuredocs.org/clojure_core/clojure.core/filter
   182 const filter = (p, sequence) => seq(function* () {
   183   if (sequence !== null && sequence !== void(0)) {
   184     for (let item of sequence) {
   185       if (p(item))
   186         yield item;
   187     }
   188   }
   189 });
   190 exports.filter = filter;
   192 // Returns a lazy sequence consisting of the result of applying `f` to the
   193 // set of first items of each sequence, followed by applying f to the set
   194 // of second items in each sequence, until any one of the sequences is
   195 // exhausted. Any remaining items in other sequences are ignored. Function
   196 // `f` should accept number-of-sequences arguments.
   197 //
   198 // Implements clojure map:
   199 // http://clojuredocs.org/clojure_core/clojure.core/map
   200 const map = (f, ...sequences) => seq(function* () {
   201   const count = sequences.length;
   202   // Optimize a single sequence case
   203   if (count === 1) {
   204     let [sequence] = sequences;
   205     if (sequence !== null && sequence !== void(0)) {
   206       for (let item of sequence)
   207         yield f(item);
   208     }
   209   }
   210   else {
   211     // define args array that will be recycled on each
   212     // step to aggregate arguments to be passed to `f`.
   213     let args = [];
   214     // define inputs to contain started generators.
   215     let inputs = [];
   217     let index = 0;
   218     while (index < count) {
   219       inputs[index] = sequences[index][iteratorSymbol]();
   220       index = index + 1;
   221     }
   223     // Run loop yielding of applying `f` to the set of
   224     // items at each step until one of the `inputs` is
   225     // exhausted.
   226     let done = false;
   227     while (!done) {
   228       let index = 0;
   229       let value = void(0);
   230       while (index < count && !done) {
   231         ({ done, value }) = inputs[index].next();
   233         // If input is not exhausted yet store value in args.
   234         if (!done) {
   235           args[index] = value;
   236           index = index + 1;
   237         }
   238       }
   240       // If none of the inputs is exhasted yet, `args` contain items
   241       // from each input so we yield application of `f` over them.
   242       if (!done)
   243         yield f(...args);
   244     }
   245   }
   246 });
   247 exports.map = map;
   249 // Returns a lazy sequence of the intermediate values of the reduction (as
   250 // per reduce) of sequence by `f`, starting with `initial` value if provided.
   251 //
   252 // Implements clojure reductions:
   253 // http://clojuredocs.org/clojure_core/clojure.core/reductions
   254 const reductions = (...params) => {
   255   const count = params.length;
   256   let hasInitial = false;
   257   let f, initial, source;
   258   if (count === 2) {
   259     ([f, source]) = params;
   260   }
   261   else if (count === 3) {
   262     ([f, initial, source]) = params;
   263     hasInitial = true;
   264   }
   265   else {
   266     throw Error("Invoked with wrong number of arguments: " + count);
   267   }
   269   const sequence = seq(source);
   271   return seq(function* () {
   272     let started = hasInitial;
   273     let result = void(0);
   275     // If initial is present yield it.
   276     if (hasInitial)
   277       yield (result = initial);
   279     // For each item of the sequence accumulate new result.
   280     for (let item of sequence) {
   281       // If nothing has being yield yet set result to first
   282       // item and yield it.
   283       if (!started) {
   284         started = true;
   285         yield (result = item);
   286       }
   287       // Otherwise accumulate new result and yield it.
   288       else {
   289         yield (result = f(result, item));
   290       }
   291     }
   293     // If nothing has being yield yet it's empty sequence and no
   294     // `initial` was provided in which case we need to yield `f()`.
   295     if (!started)
   296       yield f();
   297   });
   298 };
   299 exports.reductions = reductions;
   301 // `f` should be a function of 2 arguments. If `initial` is not supplied,
   302 // returns the result of applying `f` to the first 2 items in sequence, then
   303 // applying `f` to that result and the 3rd item, etc. If sequence contains no
   304 // items, `f` must accept no arguments as well, and reduce returns the
   305 // result of calling f with no arguments. If sequence has only 1 item, it
   306 // is returned and `f` is not called. If `initial` is supplied, returns the
   307 // result of applying `f` to `initial` and the first item in  sequence, then
   308 // applying `f` to that result and the 2nd item, etc. If sequence contains no
   309 // items, returns `initial` and `f` is not called.
   310 //
   311 // Implements clojure reduce:
   312 // http://clojuredocs.org/clojure_core/clojure.core/reduce
   313 const reduce = (...args) => {
   314   const xs = reductions(...args);
   315   let x;
   316   for (x of xs) void(0);
   317   return x;
   318 };
   319 exports.reduce = reduce;
   321 const each = (f, sequence) => {
   322   for (let x of seq(sequence)) void(f(x));
   323 };
   324 exports.each = each;
   327 const inc = x => x + 1;
   328 // Returns the number of items in the sequence. `count(null)` && `count()`
   329 // returns `0`. Also works on strings, arrays, Maps & Sets.
   331 // Implements clojure count:
   332 // http://clojuredocs.org/clojure_core/clojure.core/count
   333 const count = polymorphic({
   334   null: _ => 0,
   335   void: _ => 0,
   336   indexed: indexed => indexed.length,
   337   map: map => map.size,
   338   set: set => set.size,
   339   default: xs => reduce(inc, 0, xs)
   340 });
   341 exports.count = count;
   343 // Returns `true` if sequence has no items.
   345 // Implements clojure empty?:
   346 // http://clojuredocs.org/clojure_core/clojure.core/empty_q
   347 const isEmpty = sequence => {
   348   // Treat `null` and `undefined` as empty sequences.
   349   if (sequence === null || sequence === void(0))
   350     return true;
   352   // If contains any item non empty so return `false`.
   353   for (let _ of sequence)
   354     return false;
   356   // If has not returned yet, there was nothing to iterate
   357   // so it's empty.
   358   return true;
   359 };
   360 exports.isEmpty = isEmpty;
   362 const and = (a, b) => a && b;
   364 // Returns true if `p(x)` is logical `true` for every `x` in sequence, else
   365 // `false`.
   366 //
   367 // Implements clojure every?:
   368 // http://clojuredocs.org/clojure_core/clojure.core/every_q
   369 const isEvery = (p, sequence) => {
   370   if (sequence !== null && sequence !== void(0)) {
   371     for (let item of sequence) {
   372       if (!p(item))
   373         return false;
   374     }
   375   }
   376   return true;
   377 };
   378 exports.isEvery = isEvery;
   380 // Returns the first logical true value of (p x) for any x in sequence,
   381 // else `null`.
   382 //
   383 // Implements clojure some:
   384 // http://clojuredocs.org/clojure_core/clojure.core/some
   385 const some = (p, sequence) => {
   386   if (sequence !== null && sequence !== void(0)) {
   387     for (let item of sequence) {
   388       if (p(item))
   389         return true;
   390     }
   391   }
   392   return null;
   393 };
   394 exports.some = some;
   396 // Returns a lazy sequence of the first `n` items in sequence, or all items if
   397 // there are fewer than `n`.
   398 //
   399 // Implements clojure take:
   400 // http://clojuredocs.org/clojure_core/clojure.core/take
   401 const take = (n, sequence) => n <= 0 ? empty() : seq(function* () {
   402   let count = n;
   403   for (let item of sequence) {
   404     yield item;
   405     count = count - 1;
   406     if (count === 0) break;
   407   }
   408 });
   409 exports.take = take;
   411 // Returns a lazy sequence of successive items from sequence while
   412 // `p(item)` returns `true`. `p` must be free of side-effects.
   413 //
   414 // Implements clojure take-while:
   415 // http://clojuredocs.org/clojure_core/clojure.core/take-while
   416 const takeWhile = (p, sequence) => seq(function* () {
   417   for (let item of sequence) {
   418     if (!p(item))
   419       break;
   421     yield item;
   422   }
   423 });
   424 exports.takeWhile = takeWhile;
   426 // Returns a lazy sequence of all but the first `n` items in
   427 // sequence.
   428 //
   429 // Implements clojure drop:
   430 // http://clojuredocs.org/clojure_core/clojure.core/drop
   431 const drop = (n, sequence) => seq(function* () {
   432   if (sequence !== null && sequence !== void(0)) {
   433     let count = n;
   434     for (let item of sequence) {
   435       if (count > 0)
   436         count = count - 1;
   437       else
   438         yield item;
   439     }
   440   }
   441 });
   442 exports.drop = drop;
   444 // Returns a lazy sequence of the items in sequence starting from the
   445 // first item for which `p(item)` returns falsy value.
   446 //
   447 // Implements clojure drop-while:
   448 // http://clojuredocs.org/clojure_core/clojure.core/drop-while
   449 const dropWhile = (p, sequence) => seq(function* () {
   450   let keep = false;
   451   for (let item of sequence) {
   452     keep = keep || !p(item);
   453     if (keep) yield item;
   454   }
   455 });
   456 exports.dropWhile = dropWhile;
   458 // Returns a lazy sequence representing the concatenation of the
   459 // suplied sequences.
   460 //
   461 // Implements clojure conact:
   462 // http://clojuredocs.org/clojure_core/clojure.core/concat
   463 const concat = (...sequences) => seq(function* () {
   464   for (let sequence of sequences)
   465     for (let item of sequence)
   466       yield item;
   467 });
   468 exports.concat = concat;
   470 // Returns the first item in the sequence.
   471 //
   472 // Implements clojure first:
   473 // http://clojuredocs.org/clojure_core/clojure.core/first
   474 const first = sequence => {
   475   if (sequence !== null && sequence !== void(0)) {
   476     for (let item of sequence)
   477       return item;
   478   }
   479   return null;
   480 };
   481 exports.first = first;
   483 // Returns a possibly empty sequence of the items after the first.
   484 //
   485 // Implements clojure rest:
   486 // http://clojuredocs.org/clojure_core/clojure.core/rest
   487 const rest = sequence => drop(1, sequence);
   488 exports.rest = rest;
   490 // Returns the value at the index. Returns `notFound` or `undefined`
   491 // if index is out of bounds.
   492 const nth = (xs, n, notFound) => {
   493   if (n >= 0) {
   494     if (isArray(xs) || isArguments(xs) || isString(xs)) {
   495       return n < xs.length ? xs[n] : notFound;
   496     }
   497     else if (xs !== null && xs !== void(0)) {
   498       let count = n;
   499       for (let x of xs) {
   500         if (count <= 0)
   501           return x;
   503         count = count - 1;
   504       }
   505     }
   506   }
   507   return notFound;
   508 };
   509 exports.nth = nth;
   511 // Return the last item in sequence, in linear time.
   512 // If `sequence` is an array or string or arguments
   513 // returns in constant time.
   514 // Implements clojure last:
   515 // http://clojuredocs.org/clojure_core/clojure.core/last
   516 const last = polymorphic({
   517   null: _ => null,
   518   void: _ => null,
   519   indexed: indexed => indexed[indexed.length - 1],
   520   map: xs => reduce((_, x) => x, xs),
   521   set: xs => reduce((_, x) => x, xs),
   522   default: xs => reduce((_, x) => x, xs)
   523 });
   524 exports.last = last;
   526 // Return a lazy sequence of all but the last `n` (default 1) items
   527 // from the give `xs`.
   528 //
   529 // Implements clojure drop-last:
   530 // http://clojuredocs.org/clojure_core/clojure.core/drop-last
   531 const dropLast = flip((xs, n=1) => seq(function* () {
   532   let ys = [];
   533   for (let x of xs) {
   534     ys.push(x);
   535     if (ys.length > n)
   536       yield ys.shift();
   537   }
   538 }));
   539 exports.dropLast = dropLast;
   541 // Returns a lazy sequence of the elements of `xs` with duplicates
   542 // removed
   543 //
   544 // Implements clojure distinct
   545 // http://clojuredocs.org/clojure_core/clojure.core/distinct
   546 const distinct = sequence => seq(function* () {
   547   let items = new Set();
   548   for (let item of sequence) {
   549     if (!items.has(item)) {
   550       items.add(item);
   551       yield item;
   552     }
   553   }
   554 });
   555 exports.distinct = distinct;
   557 // Returns a lazy sequence of the items in `xs` for which
   558 // `p(x)` returns false. `p` must be free of side-effects.
   559 //
   560 // Implements clojure remove
   561 // http://clojuredocs.org/clojure_core/clojure.core/remove
   562 const remove = (p, xs) => filter(complement(p), xs);
   563 exports.remove = remove;
   565 // Returns the result of applying concat to the result of
   566 // `map(f, xs)`. Thus function `f` should return a sequence.
   567 //
   568 // Implements clojure mapcat
   569 // http://clojuredocs.org/clojure_core/clojure.core/mapcat
   570 const mapcat = (f, sequence) => seq(function* () {
   571   const sequences = map(f, sequence);
   572   for (let sequence of sequences)
   573     for (let item of sequence)
   574       yield item;
   575 });
   576 exports.mapcat = mapcat;

mercurial