]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/base.jsm
Imported Upstream version 1.1+hg7904
[dactyl.git] / common / modules / base.jsm
1 // Copyright (c) 2009-2014 Kris Maglione <maglione.k@gmail.com>
2 //
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
5 "use strict";
6
7 var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
8
9 var Cs = new Proxy(Components.stack, {
10     get: function Cs_get(target, prop) Components.stack.caller[prop]
11 });
12
13 function module(url) {
14     let obj = {};
15     Cu.import(url, obj);
16     return obj;
17 }
18
19 var { XPCOMUtils } = module("resource://gre/modules/XPCOMUtils.jsm");
20 try {
21     var ctypes = module("resource://gre/modules/ctypes.jsm");
22 }
23 catch (e) {}
24
25 let objproto = Object.prototype;
26 let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__,
27       hasOwnProperty, propertyIsEnumerable } = objproto;
28
29 hasOwnProperty = Function.call.bind(hasOwnProperty);
30 propertyIsEnumerable = Function.call.bind(propertyIsEnumerable);
31
32 // Gecko 24.
33 if (!("find" in Array.prototype))
34     Object.defineProperty(Array.prototype, "find", {
35         configurable: true,
36         writable: true,
37         value: function Array_find(pred, self) {
38             for (let [i, elem] in Iterator(this))
39                 if (pred.call(self, elem, i, this))
40                     return elem;
41         }
42     });
43
44 if (!("findIndex" in Array.prototype))
45     Object.defineProperty(Array.prototype, "findIndex", {
46         configurable: true,
47         writable: true,
48         value: function Array_findIndex(pred, self) {
49             for (let [i, elem] in Iterator(this))
50                 if (pred.call(self, elem, i, this))
51                     return i;
52             return -1;
53         }
54     });
55
56 function require(module_, target) {
57     if (/^[A-Za-z0-9]+:/.test(module_))
58         return module(module_);
59     return JSMLoader.load(module_, target);
60 }
61
62 function lazyRequire(module, names, target) {
63     for (let name of names)
64         memoize(target || this, name, name => require(module)[name]);
65 }
66
67 let jsmodules = { lazyRequire: lazyRequire };
68 jsmodules.jsmodules = jsmodules;
69
70 function toString() "[module-global " + this.NAME + "]";
71
72 let use = {};
73 let loaded = {};
74 let currentModule;
75 let global = this;
76 function defineModule(name, params, module) {
77     if (!module)
78         module = this;
79
80     module.NAME = name;
81     module.EXPORTED_SYMBOLS = params.exports || [];
82     if (!~module.EXPORTED_SYMBOLS.indexOf("File"))
83         delete module.File;
84
85     defineModule.loadLog.push("[Begin " + name + "]");
86     defineModule.prefix += "  ";
87
88     for (let [, mod] in Iterator(params.require || []))
89         require(mod, module);
90
91     module._lastModule = currentModule;
92     currentModule = module;
93     module.startTime = Date.now();
94 }
95
96 defineModule.loadLog = [];
97 Object.defineProperty(defineModule.loadLog, "push", {
98     value: function (val) {
99         val = defineModule.prefix + val;
100         if (true)
101             defineModule.dump(val + "\n");
102         this[this.length] = Date.now() + " " + val;
103     }
104 });
105 defineModule.prefix = "";
106 defineModule.dump = function dump_(...args) {
107     let msg = args.map(function (msg) {
108         if (loaded.util && typeof msg == "object")
109             msg = util.objectToString(msg);
110         return msg;
111     }).join(", ");
112     dump(String.replace(msg, /\n?$/, "\n")
113                .replace(/^./gm, JSMLoader.name + ": $&"));
114 }
115 defineModule.modules = [];
116 defineModule.time = function time(major, minor, func, self, ...args) {
117     let time = Date.now();
118     if (typeof func !== "function")
119         func = self[func];
120
121     try {
122         var res = func.apply(self, args);
123     }
124     catch (e) {
125         loaded.util && util.reportError(e);
126     }
127
128     JSMLoader.times.add(major, minor, Date.now() - time);
129     return res;
130 }
131
132 function endModule() {
133     defineModule.prefix = defineModule.prefix.slice(0, -2);
134     defineModule.loadLog.push("(End   " + currentModule.NAME + ")");
135
136     loaded[currentModule.NAME] = 1;
137     require(currentModule.NAME, jsmodules);
138     currentModule = currentModule._lastModule;
139 }
140
141 function require_(obj, name, from, targetName) {
142     try {
143         if (arguments.length === 1)
144             [obj, name] = [{}, obj];
145
146         let caller = Components.stack.caller;
147
148         if (!loaded[name])
149             defineModule.loadLog.push((from || "require") + ": loading " + name +
150                                       " into " + (targetName || obj.NAME || caller.filename + ":" + caller.lineNumber));
151
152         JSMLoader.load(name + ".jsm", obj);
153
154         if (!loaded[name] && obj != jsmodules)
155             JSMLoader.load(name + ".jsm", jsmodules);
156
157         return obj;
158     }
159     catch (e) {
160         defineModule.dump("loading " + String.quote(name + ".jsm") + "\n");
161         if (loaded.util)
162             util.reportError(e);
163         else
164             defineModule.dump("    " + (e.filename || e.fileName) + ":" + e.lineNumber + ": " + e + "\n");
165     }
166 }
167
168 defineModule("base", {
169     // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/       "\2",/p' base.jsm | sort | fmt
170     exports: [
171         "Cc",
172         "Ci",
173         "Class",
174         "Cr",
175         "Cs",
176         "Cu",
177         "ErrorBase",
178         "Finished",
179         "JSMLoader",
180         "Module",
181         "RealSet",
182         "Set",
183         "Struct",
184         "StructBase",
185         "Timer",
186         "UTF8",
187         "XPCOM",
188         "XPCOMShim",
189         "XPCOMUtils",
190         "array",
191         "bind",
192         "call",
193         "callable",
194         "ctypes",
195         "curry",
196         "defineModule",
197         "deprecated",
198         "endModule",
199         "forEach",
200         "hasOwnProperty",
201         "isArray",
202         "isGenerator",
203         "isObject",
204         "isString",
205         "isSubclass",
206         "isinstance",
207         "iter",
208         "iterAll",
209         "iterOwnProperties",
210         "keys",
211         "literal",
212         "memoize",
213         "modujle",
214         "octal",
215         "properties",
216         "require",
217         "set",
218         "update",
219         "values",
220     ]
221 });
222
223 this.lazyRequire("cache", ["cache"]);
224 this.lazyRequire("config", ["config"]);
225 this.lazyRequire("messages", ["_", "Messages"]);
226 this.lazyRequire("promises", ["Task", "promises"]);
227 this.lazyRequire("services", ["services"]);
228 this.lazyRequire("storage", ["File"]);
229 this.lazyRequire("util", ["FailedAssertion", "util"]);
230
231 literal.files = {};
232 literal.locations = {};
233 function literal(/* comment */) {
234     let { caller } = Components.stack;
235     while (caller && caller.language != 2)
236         caller = caller.caller;
237
238     let file = caller.filename.replace(/.* -> /, "");
239     let key = "literal:" + file + ":" + caller.lineNumber;
240     return cache.get(key, function() {
241         let source = literal.files[file] || File.readURL(file);
242         literal.files[file] = source;
243
244         let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" +
245                            ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source);
246         return match[1];
247     });
248 }
249
250 /**
251  * Iterates over the names of all of the top-level properties of an
252  * object or, if prototypes is given, all of the properties in the
253  * prototype chain below the top.
254  *
255  * @param {object} obj The object to inspect.
256  * @param {boolean} properties Whether to inspect the prototype chain
257  * @default false
258  * @returns {Generator}
259  */
260 function prototype(obj)
261     obj.__proto__ || Object.getPrototypeOf(obj) ||
262     XPCNativeWrapper.unwrap(obj).__proto__ ||
263     Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj));
264
265 function properties(obj, prototypes) {
266     let orig = obj;
267     let seen = RealSet(["dactylPropertyNames"]);
268
269     try {
270         if ("dactylPropertyNames" in obj && !prototypes)
271             for (let key in values(obj.dactylPropertyNames))
272                 if (key in obj && !seen.add(key))
273                     yield key;
274     }
275     catch (e) {}
276
277     function props(obj) {
278         // Grr.
279         try {
280             return Object.getOwnPropertyNames(obj);
281         }
282         catch (e) {
283             if (e.result === Cr.NS_ERROR_FAILURE) {
284                 // This is being thrown for PDF.js content windows,
285                 // currently.
286                 let filter = function filter(prop) {
287                     try {
288                         return prop in obj;
289                     }
290                     catch (e) {
291                         util.reportError("Filtering properties for " +
292                                          String.quote(obj) + ", " +
293                                          "error checking presence of " +
294                                          String.quote(prop) + ": " + e);
295                     }
296                     return false;
297                 };
298                 return array.uniq([k for (k in obj)].concat(
299                     Object.getOwnPropertyNames(
300                               XPCNativeWrapper.unwrap(obj))
301                           .filter(filter)));
302             }
303             else if (!e.stack) {
304                 throw Error(e);
305             }
306         }
307     }
308
309     for (; obj; obj = prototypes && prototype(obj)) {
310         for (let key of props(obj))
311             if (!prototypes || !seen.add(key) && obj != orig)
312                 yield key;
313     }
314 }
315
316 function iterOwnProperties(obj) {
317     for (let prop in properties(obj))
318         yield [prop, Object.getOwnPropertyDescriptor(obj, prop)];
319 }
320
321 function deprecated(alternative, fn) {
322     if (isObject(fn))
323         return Class.Property(iter(fn).map(([k, v]) => [k,
324                                                         callable(v) ? deprecated(alternative, v)
325                                                                     : v])
326                                       .toObject());
327
328     let name,
329         func = callable(fn) ? fn
330                             : function () this[fn].apply(this, arguments);
331
332     function deprecatedMethod() {
333         let obj = !this                      ? "" :
334                   this.className             ? this.className + "#" :
335                   this.constructor.className ? this.constructor.className + "#" :
336                       "";
337
338         deprecated.warn(func,
339                         obj + (fn.realName || fn.name || name || "").replace(/__/g, "."),
340                         alternative);
341         return func.apply(this, arguments);
342     }
343     if (func.name)
344         deprecatedMethod.realName = func.name;
345
346     return callable(fn) ? deprecatedMethod : Class.Property({
347         get: function () deprecatedMethod,
348         init: function (prop) { name = prop; }
349     });
350 }
351 deprecated.warn = function warn(func, name, alternative, frame) {
352     if (!func.seenCaller)
353         func.seenCaller = RealSet([
354             "resource://dactyl/javascript.jsm",
355             "resource://dactyl/util.jsm"
356         ]);
357
358     if (!(loaded.util && util && loaded.config && config.protocolLoaded)) {
359         dump("DACTYL: deprecated method called too early [" + [name, alternative] + "]:\n" + Error().stack + "\n\n");
360         return;
361     }
362
363     frame = frame || Components.stack.caller.caller;
364
365     let filename = util.fixURI(frame.filename || "unknown");
366     if (!func.seenCaller.add(filename))
367         util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":")
368                                    + _("warn.deprecated", name, alternative));
369 }
370
371 /**
372  * Iterates over all of the top-level, iterable property names of an
373  * object.
374  *
375  * @param {object} obj The object to inspect.
376  * @returns {Generator}
377  */
378 function keys(obj) iter(function keys() {
379     if (isinstance(obj, ["Map"]))
380         for (let [k, v] of obj)
381             yield k;
382     else
383         for (var k in obj)
384             if (hasOwnProperty(obj, k))
385                 yield k;
386 }());
387
388 /**
389  * Iterates over all of the top-level, iterable property values of an
390  * object.
391  *
392  * @param {object} obj The object to inspect.
393  * @returns {Generator}
394  */
395 function values(obj) iter(function values() {
396     if (isinstance(obj, ["Map"]))
397         for (let [k, v] of obj)
398             yield v;
399     else if (isinstance(obj, ["Generator", "Iterator", Iter]))
400         for (let k in obj)
401             yield k;
402     else if (iter.iteratorProp in obj)
403         for (let v of obj)
404             yield v;
405     else
406         for (var k in obj)
407             if (hasOwnProperty(obj, k))
408                 yield obj[k];
409 }());
410
411 var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments));
412 var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments));
413
414 var RealSet = Set;
415 let Set_add = RealSet.prototype.add;
416 RealSet.prototype.add = function RealSet_add(val) {
417     let res = this.has(val);
418     Set_add.apply(this, arguments);
419     return res;
420 };
421
422 RealSet.prototype.difference = function RealSet_difference(set) {
423     return RealSet(i for (i of this) if (!set.has(i)));
424 };
425
426 RealSet.prototype.intersection = function RealSet_intersection(set) {
427     return RealSet(i for (i of this) if (set.has(i)));
428 };
429
430 RealSet.prototype.union = function RealSet_union(set) {
431     let res = RealSet(this);
432     for (let item of set)
433         res.add(item);
434     return res;
435 };
436
437 /**
438  * Utility for managing sets of strings. Given an array, returns an
439  * object with one key for each value thereof.
440  *
441  * @param {[string]} ary @optional
442  * @returns {object}
443  */
444 this.Set = deprecated("RealSet", function Set(ary) {
445     let obj = {};
446     if (ary)
447         for (let val in values(ary))
448             obj[val] = true;
449     return obj;
450 });
451 /**
452  * Adds an element to a set and returns true if the element was
453  * previously contained.
454  *
455  * @param {object} set The set.
456  * @param {string} key The key to add.
457  * @returns boolean
458  */
459 Set.add = deprecated("RealSet#add",
460     curry(function Set__add(set, key) {
461         if (isinstance(set, ["Set"])) {
462             let res = set.has(key);
463             set.add(key);
464             return res;
465         }
466
467         let res = this.has(set, key);
468         set[key] = true;
469         return res;
470     }));
471 /**
472  * Returns true if the given set contains the given key.
473  *
474  * @param {object} set The set.
475  * @param {string} key The key to check.
476  * @returns {boolean}
477  */
478 Set.has = deprecated("hasOwnProperty or Set#has",
479     curry(function Set__has(set, key) {
480         if (isinstance(set, ["Set"]))
481             return set.has(key);
482
483         return hasOwnProperty(set, key) &&
484                propertyIsEnumerable(set, key);
485     }));
486 /**
487  * Returns a new set containing the members of the first argument which
488  * do not exist in any of the other given arguments.
489  *
490  * @param {object} set The set.
491  * @returns {object}
492  */
493 Set.subtract = deprecated("RealSet#difference",
494     function set_subtract(set) {
495         set = update({}, set);
496         for (let i = 1; i < arguments.length; i++)
497             for (let k in keys(arguments[i]))
498                 delete set[k];
499         return set;
500     });
501
502 /**
503  * Removes an element from a set and returns true if the element was
504  * previously contained.
505  *
506  * @param {object} set The set.
507  * @param {string} key The key to remove.
508  * @returns boolean
509  */
510 Set.remove = deprecated("RealSet#delete",
511     curry(function Set__remove(set, key) {
512         if (isinstance(set, ["Set"]))
513             return set.delete(key);
514
515         let res = set.has(set, key);
516         delete set[key];
517         return res;
518     }));
519
520 function set() {
521     deprecated.warn(set, "set", "Set");
522     return Set.apply(this, arguments);
523 }
524 Object.keys(Set).forEach(function (meth) {
525     set[meth] = function proxy() {
526         deprecated.warn(proxy, "set." + meth, "Set." + meth);
527         return Set[meth].apply(Set, arguments);
528     };
529 });
530
531 /**
532  * Curries a function to the given number of arguments. Each
533  * call of the resulting function returns a new function. When
534  * a call does not contain enough arguments to satisfy the
535  * required number, the resulting function is another curried
536  * function with previous arguments accumulated.
537  *
538  *     function foo(a, b, c) [a, b, c].join(" ");
539  *     curry(foo)(1, 2, 3) -> "1 2 3";
540  *     curry(foo)(4)(5, 6) -> "4 5 6";
541  *     curry(foo)(7)(8)(9) -> "7 8 9";
542  *
543  * @param {function} fn The function to curry.
544  * @param {integer} length The number of arguments expected.
545  *     @default fn.length
546  *     @optional
547  * @param {object} self The 'this' value for the returned function. When
548  *     omitted, the value of 'this' from the first call to the function is
549  *     preserved.
550  *     @optional
551  */
552 function curry(fn, length, self, acc) {
553     if (length == null)
554         length = fn.length;
555     if (length == 0)
556         return fn;
557
558     // Close over function with 'this'
559     function close(self, fn) (...args) => fn.apply(self, args);
560
561     if (acc == null)
562         acc = [];
563
564     function curried(...args) {
565         // The curried result should preserve 'this'
566         if (args.length == 0)
567             return close(self || this, curried);
568
569         let args = acc.concat(args);
570
571         if (args.length >= length)
572             return fn.apply(self || this, args);
573
574         return curry(fn, length, self || this, args);
575     };
576     curried.realName = fn.realName || fn.name;
577     return curried;
578 }
579
580 var bind = function bind(meth, self, ...args)
581     let (func = callable(meth) ? meth : self[meth])
582         func.bind.apply(func, [self].concat(args));
583
584 /**
585  * Returns true if both arguments are functions and
586  * (targ() instanceof src) would also return true.
587  *
588  * @param {function} targ
589  * @param {function} src
590  * @returns {boolean}
591  */
592 function isSubclass(targ, src) {
593     return src === targ ||
594         targ && typeof targ === "function" && targ.prototype instanceof src;
595 }
596
597 /**
598  * Returns true if *object* is an instance of *interfaces*. If *interfaces* is
599  * an array, returns true if *object* is an instance of any element of
600  * *interfaces*. If *interfaces* is the object form of a primitive type,
601  * returns true if *object* is a non-boxed version of the type, i.e., if
602  * (typeof object == "string"), isinstance(object, String) is true. Finally, if
603  * *interfaces* is a string, returns true if ({}.toString.call(object) ==
604  * "[object <interfaces>]").
605  *
606  * @param {object} object The object to check.
607  * @param {constructor|[constructor|string]} interfaces The types to check *object* against.
608  * @returns {boolean}
609  */
610 var isinstance_types = {
611     boolean: Boolean,
612     string: String,
613     function: Function,
614     number: Number
615 };
616 function isinstance(object, interfaces) {
617     if (object == null)
618         return false;
619
620     return Array.concat(interfaces).some(function isinstance_some(iface) {
621         if (typeof iface === "string") {
622             if (objproto.toString.call(object) === "[object " + iface + "]")
623                 return true;
624         }
625         else if (typeof object === "object" && "isinstance" in object && object.isinstance !== isinstance) {
626             if (object.isinstance(iface))
627                 return true;
628         }
629         else {
630             if (object instanceof iface)
631                 return true;
632             var type = isinstance_types[typeof object];
633             if (type && isSubclass(iface, type))
634                 return true;
635         }
636         return false;
637     });
638 }
639
640 /**
641  * Returns true if obj is a non-null object.
642  */
643 function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports;
644
645 /**
646  * Returns true if and only if its sole argument is an
647  * instance of the builtin Array type. The array may come from
648  * any window, frame, namespace, or execution context, which
649  * is not the case when using (obj instanceof Array).
650  */
651 var isArray =
652     // This is bloody stupid.
653     function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array";
654
655 /**
656  * Returns true if and only if its sole argument is an
657  * instance of the builtin Generator type. This includes
658  * functions containing the 'yield' statement and generator
659  * statements such as (x for (x in obj)).
660  */
661 function isGenerator(val) objproto.toString.call(val) == "[object Generator]";
662
663 /**
664  * Returns true if and only if its sole argument is a String,
665  * as defined by the builtin type. May be constructed via
666  * String(foo) or new String(foo) from any window, frame,
667  * namespace, or execution context, which is not the case when
668  * using (obj instanceof String) or (typeof obj == "string").
669  */
670 function isString(val) objproto.toString.call(val) == "[object String]";
671
672 /**
673  * Returns true if and only if its sole argument may be called
674  * as a function. This includes classes and function objects.
675  */
676 function callable(val) typeof val === "function" && !(val instanceof Ci.nsIDOMElement);
677
678 function call(fn, self, ...args) {
679     fn.apply(self, args);
680     return fn;
681 }
682
683 /**
684  * Memoizes an object property value.
685  *
686  * @param {object} obj The object to add the property to.
687  * @param {string} key The property name.
688  * @param {function} getter The function which will return the initial
689  * value of the property.
690  */
691 function memoize(obj, key, getter) {
692     if (arguments.length == 1) {
693         let res = update(Object.create(obj), obj);
694         for (let prop of Object.getOwnPropertyNames(obj)) {
695             let get = __lookupGetter__.call(obj, prop);
696             if (get)
697                 memoize(res, prop, get);
698         }
699         return res;
700     }
701
702     try {
703         Object.defineProperty(obj, key, {
704             configurable: true,
705             enumerable: true,
706
707             get: function g_replaceProperty() {
708                 try {
709                     Class.replaceProperty(this.instance || this, key, null);
710                     return Class.replaceProperty(this.instance || this, key, getter.call(this, key));
711                 }
712                 catch (e) {
713                     util.reportError(e);
714                 }
715             },
716
717             set: function s_replaceProperty(val)
718                 Class.replaceProperty(this.instance || this, key, val)
719         });
720     }
721     catch (e) {
722         obj[key] = getter.call(obj, key);
723     }
724 }
725
726 let sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance());
727 sandbox.__proto__ = this;
728
729 /**
730  * Updates an object with the properties of another object. Getters
731  * and setters are copied as expected. Moreover, any function
732  * properties receive new 'supercall' and 'superapply' properties,
733  * which will call the identically named function in target's
734  * prototype.
735  *
736  *    let a = { foo: function (arg) "bar " + arg }
737  *    let b = { __proto__: a }
738  *    update(b, { foo: function foo() foo.supercall(this, "baz") });
739  *
740  *    a.foo("foo") -> "bar foo"
741  *    b.foo()      -> "bar baz"
742  *
743  * @param {Object} target The object to update.
744  * @param {Object} src The source object from which to update target.
745  *    May be provided multiple times.
746  * @returns {Object} Returns its updated first argument.
747  */
748 function update(target) {
749     for (let i = 1; i < arguments.length; i++) {
750         let src = arguments[i];
751         Object.getOwnPropertyNames(src || {}).forEach(function (k) {
752             let desc = Object.getOwnPropertyDescriptor(src, k);
753             if (desc.value instanceof Class.Property)
754                 desc = desc.value.init(k, target) || desc.value;
755
756             try {
757                 if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) {
758                     let func = desc.value.wrapped || desc.value;
759                     if (!func.superapply) {
760                         func.__defineGetter__("super", function get_super() Object.getPrototypeOf(target)[k]);
761                         func.superapply = function superapply(self, args)
762                             let (meth = Object.getPrototypeOf(target)[k])
763                                 meth && meth.apply(self, args);
764                         func.supercall = function supercall(self, ...args)
765                             func.superapply(self, args);
766                     }
767                 }
768                 Object.defineProperty(target, k, desc);
769             }
770             catch (e) {}
771         });
772     }
773     return target;
774 }
775
776 /**
777  * @constructor Class
778  *
779  * Constructs a new Class. Arguments marked as optional must be
780  * either entirely elided, or they must have the exact type
781  * specified.
782  *
783  * @param {string} name The class's as it will appear when toString
784  *     is called, as well as in stack traces.
785  *     @optional
786  * @param {function} base The base class for this module. May be any
787  *     callable object.
788  *     @optional
789  *     @default Class
790  * @param {Object} prototype The prototype for instances of this
791  *     object. The object itself is copied and not used as a prototype
792  *     directly.
793  * @param {Object} classProperties The class properties for the new
794  *     module constructor. More than one may be provided.
795  *     @optional
796  *
797  * @returns {function} The constructor for the resulting class.
798  */
799 function Class(...args) {
800
801     if (isString(args[0]))
802         var name = args.shift();
803     var superclass = Class;
804     if (callable(args[0]))
805         superclass = args.shift();
806
807     var Constructor = eval(String.replace('\n\
808         (function constructor(PARAMS) {                      \n\
809             var self = Object.create(Constructor.prototype); \n\
810             self.instance = self;                            \n\
811             self.globalInstance = self;                      \n\
812                                                              \n\
813             if ("_metaInit_" in self && self._metaInit_)     \n\
814                 self._metaInit_.apply(self, arguments);      \n\
815                                                              \n\
816             var res = self.init.apply(self, arguments);      \n\
817             return res !== undefined ? res : self;           \n\
818         })',
819         "constructor", (name || superclass.className).replace(/\W/g, "_"))
820             .replace("PARAMS",
821                      /^function .*?\((.*?)\)/
822                         .exec(args[0] && args[0].init || Class.prototype.init)[1]
823                         .replace(/\b(self|res|Constructor)\b/g, "$1_")));
824
825     Constructor.className = name || superclass.className || superclass.name;
826
827     if ("init" in superclass.prototype)
828         Constructor.__proto__ = superclass;
829     else {
830         let superc = superclass;
831         superclass = function Shim() {};
832         Class.extend(superclass, superc, {
833             init: superc
834         });
835         superclass.__proto__ = superc;
836     }
837
838     Class.extend(Constructor, superclass, args[0]);
839     memoize(Constructor, "bound", Class.makeClosure);
840     if (Iter && array) // Hack. :/
841         Object.defineProperty(Constructor, "closure",
842                               deprecated("bound", { get: function closure() this.bound }));
843     update(Constructor, args[1]);
844
845     Constructor.__proto__ = superclass;
846
847     args.slice(2).forEach(function (obj) {
848         if (callable(obj))
849             obj = obj.prototype;
850         update(Constructor.prototype, obj);
851     });
852     return Constructor;
853 }
854
855 Class.objectGlobal = function (object) {
856     try {
857         return Cu.getGlobalForObject(object);
858     }
859     catch (e) {
860         return null;
861     }
862 };
863
864 /**
865  * @class Class.Property
866  * A class which, when assigned to a property in a Class's prototype
867  * or class property object, defines that property's descriptor
868  * rather than its value. If the desc object has an init property, it
869  * will be called with the property's name before the descriptor is
870  * assigned.
871  *
872  * @param {Object} desc The property descriptor.
873  */
874 Class.Property = function Property(desc) update(
875     Object.create(Property.prototype), desc || { configurable: true, writable: true });
876 Class.Property.prototype.init = function () {};
877 /**
878  * Extends a subclass with a superclass. The subclass's
879  * prototype is replaced with a new object, which inherits
880  * from the superclass's prototype, {@see update}d with the
881  * members of *overrides*.
882  *
883  * @param {function} subclass
884  * @param {function} superclass
885  * @param {Object} overrides @optional
886  */
887 Class.extend = function extend(subclass, superclass, overrides) {
888     subclass.superclass = superclass;
889
890     subclass.prototype = Object.create(superclass.prototype);
891     update(subclass.prototype, overrides);
892     subclass.prototype.constructor = subclass;
893     subclass.prototype._class_ = subclass;
894
895     if (superclass.prototype.constructor === objproto.constructor)
896         superclass.prototype.constructor = superclass;
897 }
898
899 /**
900  * Memoizes the value of a class property to the value returned by
901  * the passed function the first time the property is accessed.
902  *
903  * @param {function(string)} getter The function which returns the
904  *      property's value.
905  * @returns {Class.Property}
906  */
907 Class.Memoize = function Memoize(getter, wait)
908     Class.Property({
909         configurable: true,
910         enumerable: true,
911         init: function (key) {
912             let done = false;
913
914             if (wait)
915                 // Crazy, yeah, I know. -- Kris
916                 this.get = function replace() {
917                     let obj = this.instance || this;
918                     Object.defineProperty(obj, key,  {
919                         configurable: true, enumerable: false,
920                         get: function get() {
921                             util.waitFor(() => done);
922                             return this[key];
923                         }
924                     });
925
926                     Task.spawn(function () {
927                         let wait;
928                         for (var res in getter.call(obj)) {
929                             if (wait !== undefined)
930                                 yield promises.sleep(wait);
931                             wait = res;
932                         }
933                         Class.replaceProperty(obj, key, res);
934                         done = true;
935                     });
936
937                     return this[key];
938                 };
939             else
940                 this.get = function g_Memoize() {
941                     let obj = this.instance || this;
942                     try {
943                         Class.replaceProperty(obj, key, null);
944                         return Class.replaceProperty(obj, key, getter.call(this, key));
945                     }
946                     catch (e) {
947                         util.reportError(e);
948                     }
949                 };
950
951             this.set = function s_Memoize(val) Class.replaceProperty(this.instance || this, key, val);
952         }
953     });
954
955 Class.memoize = deprecated("Class.Memoize", function memoize() Class.Memoize.apply(this, arguments));
956
957 /**
958  * Updates the given object with the object in the target class's
959  * prototype.
960  */
961 Class.Update = function Update(obj)
962     Class.Property({
963         configurable: true,
964         enumerable: true,
965         writable: true,
966         init: function (key, target) {
967             this.value = update({}, target[key], obj);
968         }
969     });
970
971 Class.replaceProperty = function replaceProperty(obj, prop, value) {
972     Object.defineProperty(obj, prop, { configurable: true, enumerable: true, value: value, writable: true });
973     return value;
974 };
975 Class.toString = function toString() "[class " + this.className + "]";
976 Class.prototype = {
977     /**
978      * Initializes new instances of this class. Called automatically
979      * when new instances are created.
980      */
981     init: function c_init() {},
982
983     get instance() ({}),
984     set instance(val) Class.replaceProperty(this, "instance", val),
985
986     withSavedValues: function withSavedValues(names, callback, self) {
987         let vals = names.map(name => this[name]);
988         try {
989             return callback.call(self || this);
990         }
991         finally {
992             names.forEach((name, i) => { this[name] = vals[i]; });
993         }
994     },
995
996     toString: function C_toString() {
997         if (this.toStringParams)
998             var params = "(" + this.toStringParams.map(m => (isArray(m)  ? "[" + m + "]" :
999                                                              isString(m) ? m.quote() : String(m)))
1000                                    .join(", ") + ")";
1001         return "[instance " + this.constructor.className + (params || "") + "]";
1002     },
1003
1004     /**
1005      * Executes *callback* after *timeout* milliseconds. The value of
1006      * 'this' is preserved in the invocation of *callback*.
1007      *
1008      * @param {function} callback The function to call after *timeout*
1009      * @param {number} timeout The time, in milliseconds, to wait
1010      *     before calling *callback*.
1011      * @returns {nsITimer} The timer which backs this timeout.
1012      */
1013     timeout: function timeout(callback, timeout) {
1014         let timeout_notify = (timer) => {
1015             if (this.stale ||
1016                     util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"]))
1017                 return;
1018             this.timeouts.splice(this.timeouts.indexOf(timer), 1);
1019             try {
1020                 callback.call(this);
1021             }
1022             catch (e) {
1023                 try {
1024                     util.dump("Error invoking timer callback registered at " +
1025                               [frame.filename, frame.lineNumber, ""].join(":"));
1026                     util.reportError(e);
1027                 }
1028                 catch (e) {
1029                     Cu.reportError(e);
1030                 }
1031             }
1032         };
1033         let frame = Components.stack.caller;
1034         let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT);
1035         this.timeouts.push(timer);
1036         return timer;
1037     },
1038     timeouts: [],
1039
1040     /**
1041      * Updates this instance with the properties of the given objects.
1042      * Like the update function, but with special semantics for
1043      * localized properties.
1044      */
1045     update: function update() {
1046         // XXX: Duplication.
1047
1048         for (let i = 0; i < arguments.length; i++) {
1049             let src = arguments[i];
1050             Object.getOwnPropertyNames(src || {}).forEach((k) => {
1051                 let desc = Object.getOwnPropertyDescriptor(src, k);
1052                 if (desc.value instanceof Class.Property)
1053                     desc = desc.value.init(k, this) || desc.value;
1054
1055                 if (typeof desc.value === "function") {
1056                     let func = desc.value.wrapped || desc.value;
1057                     if (!func.superapply) {
1058                         func.__defineGetter__("super", () => Object.getPrototypeOf(this)[k]);
1059
1060                         func.superapply = function superapply(self, args) {
1061                             let meth = Object.getPrototypeOf(self)[k];
1062                             return meth && meth.apply(self, args);
1063                         };
1064
1065                         func.supercall = function supercall(self, ...args) {
1066                             return func.superapply(self, args);
1067                         }
1068                     }
1069                 }
1070
1071                 try {
1072                     if ("value" in desc && (this.localizedProperties.has(k) || this.magicalProperties.has(k)))
1073                         this[k] = desc.value;
1074                     else
1075                         Object.defineProperty(this, k, desc);
1076                 }
1077                 catch (e) {}
1078             }, this);
1079         }
1080         return this;
1081     },
1082
1083     localizedProperties: RealSet(),
1084     magicalProperties: RealSet()
1085 };
1086 for (let name in properties(Class.prototype)) {
1087     let desc = Object.getOwnPropertyDescriptor(Class.prototype, name);
1088     desc.enumerable = false;
1089     Object.defineProperty(Class.prototype, name, desc);
1090 }
1091
1092 var closureHooks = {
1093     get: function closure_get(target, prop) {
1094         if (hasOwnProperty(target._closureCache, prop))
1095             return target._closureCache[prop];
1096
1097         let p = target[prop]
1098         if (callable(p))
1099             return target._closureCache[prop] = p.bind(target);
1100         return p;
1101     }
1102
1103     /*
1104     getOwnPropertyNames: function getOwnPropertyNames(target) {
1105         return [k for (k in properties(target, true))];
1106     },
1107
1108     getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, prop) {
1109         let self = this;
1110         return {
1111             configurable: false,
1112             writable: false,
1113             get value() self.get(target, prop)
1114         }
1115     }
1116     */
1117 };
1118
1119 Class.makeClosure = function makeClosure() {
1120     this._closureCache = {};
1121
1122     return new Proxy(this, closureHooks);
1123 };
1124 memoize(Class.prototype, "bound", Class.makeClosure);
1125
1126 /**
1127  * A base class generator for classes which implement XPCOM interfaces.
1128  *
1129  * @param {nsIIID|[nsIJSIID]} interfaces The interfaces which the class
1130  *      implements.
1131  * @param {Class} superClass A super class. @optional
1132  * @returns {Class}
1133  */
1134 function XPCOM(interfaces, superClass) {
1135     interfaces = Array.concat(interfaces);
1136
1137     let shim = XPCOMShim(interfaces);
1138
1139     let res = Class("XPCOM(" + interfaces + ")", superClass || Class,
1140         update(iter([k,
1141                      v === undefined || callable(v) ? stub : v]
1142                      for ([k, v] in Iterator(shim))).toObject(),
1143                { QueryInterface: XPCOMUtils.generateQI(interfaces) }));
1144
1145     return res;
1146 }
1147 function XPCOMShim(interfaces) {
1148     let ip = services.InterfacePointer({
1149         QueryInterface: function (iid) {
1150             if (iid.equals(Ci.nsISecurityCheckedComponent))
1151                 throw Cr.NS_ERROR_NO_INTERFACE;
1152             return this;
1153         },
1154         getHelperForLanguage: function () null,
1155         getInterfaces: function (count) { count.value = 0; }
1156     });
1157     return (interfaces || []).reduce((shim, iface) => shim.QueryInterface(Ci[iface]),
1158                                      ip.data);
1159 };
1160 let stub = Class.Property({
1161     configurable: true,
1162     enumerable: false,
1163     value: function stub() null,
1164     writable: true
1165 });
1166
1167 /**
1168  * An abstract base class for classes that wish to inherit from Error.
1169  */
1170 var ErrorBase = Class("ErrorBase", Error, {
1171     level: 2,
1172     init: function EB_init(message, level=0) {
1173         let error = Error(message);
1174         update(this, error);
1175         this.stack = error.stack;
1176         this.message = message;
1177
1178         let frame = Components.stack;
1179         for (let i = 0; i < this.level + level; i++) {
1180             frame = frame.caller;
1181             this.stack = this.stack.replace(/^.*\n/, "");
1182         }
1183         this.fileName = frame.filename;
1184         this.lineNumber = frame.lineNumber;
1185     },
1186     toString: function () String(this.message)
1187 });
1188
1189 /**
1190  * An Error subclass to throw in order to stop sourcing a plugin without
1191  * printing a stack trace.
1192  */
1193 var Finished = Class("Finished", ErrorBase);
1194
1195 /**
1196  * Constructs a new Module class and instantiates an instance into the current
1197  * module global object.
1198  *
1199  * @param {string} name The name of the instance.
1200  * @param {Object} prototype The instance prototype.
1201  * @param {Object} classProperties Properties to be applied to the class constructor.
1202  * @returns {Class}
1203  */
1204 function Module(name, prototype, ...args) {
1205     try {
1206         let init = callable(prototype) ? 2 : 1;
1207         let proto = callable(prototype) ? args[0] : prototype;
1208
1209         proto._metaInit_ = function () {
1210             module.prototype._metaInit_ = null;
1211             currentModule[name.toLowerCase()] = this;
1212         };
1213
1214         const module = Class.apply(Class, [name, prototype, ...args.slice(0, init)]);
1215         let instance = module();
1216         module.className = name.toLowerCase();
1217
1218         instance.INIT = update(Object.create(Module.INIT),
1219                                args[init] || {});
1220
1221         currentModule[module.className] = instance;
1222         defineModule.modules.push(instance);
1223         return module;
1224     }
1225     catch (e) {
1226         if (typeof e === "string")
1227             e = Error(e);
1228
1229         dump(e.fileName + ":" + e.lineNumber + ": " + e + "\n" + (e.stack || Error().stack));
1230     }
1231 }
1232 Module.INIT = {
1233     init: function Module_INIT_init(dactyl, modules, window) {
1234         let args = arguments;
1235
1236         let locals = [];
1237         for (let local = this.Local; local; local = local.super)
1238             locals.push(local);
1239
1240         if (locals.length) {
1241             let module = this, objs = {};
1242             for (let i in locals) {
1243                 module = objs[i] = Object.create(module);
1244                 module.modules = modules;
1245             }
1246             module.isLocalModule = true;
1247
1248             modules.jsmodules[this.constructor.className] = module;
1249             locals.reverse().forEach((fn, i) => { update(objs[i], fn.apply(module, args)); });
1250
1251             memoize(module, "closure", Class.makeClosure);
1252             module.instance = module;
1253             module.init();
1254
1255             if (module.signals)
1256                 modules.dactyl.registerObservers(module);
1257         }
1258     }
1259 }
1260
1261 /**
1262  * @class Struct
1263  *
1264  * Creates a new Struct constructor, used for creating objects with
1265  * a fixed set of named members. Each argument should be the name of
1266  * a member in the resulting objects. These names will correspond to
1267  * the arguments passed to the resultant constructor. Instances of
1268  * the new struct may be treated very much like arrays, and provide
1269  * many of the same methods.
1270  *
1271  *     const Point = Struct("x", "y", "z");
1272  *     let p1 = Point(x, y, z);
1273  *
1274  * @returns {function} The constructor for the new Struct.
1275  */
1276 function Struct(...args) {
1277     if (/^[A-Z]/.test(args[0]))
1278         var className = args.shift();
1279
1280     const Struct = Class(className || "Struct", StructBase, {
1281         length: args.length,
1282         members: array.toObject(args.map((v, k) => [v, k]))
1283     });
1284     args.forEach(function (name, i) {
1285         Struct.prototype.__defineGetter__(name, function () this[i]);
1286         Struct.prototype.__defineSetter__(name, function (val) { this[i] = val; });
1287     });
1288     return Struct;
1289 }
1290 var StructBase = Class("StructBase", Array, {
1291     init: function struct_init() {
1292         for (let i = 0; i < arguments.length; i++)
1293             if (arguments[i] != undefined)
1294                 this[i] = arguments[i];
1295     },
1296
1297     get toStringParams() this,
1298
1299     clone: function struct_clone() this.constructor.apply(null, this.slice()),
1300
1301     bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")),
1302     closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")),
1303
1304     get: function struct_get(key, val) this[this.members[key]],
1305     set: function struct_set(key, val) this[this.members[key]] = val,
1306
1307     toString: function struct_toString() Class.prototype.toString.apply(this, arguments),
1308
1309     // Iterator over our named members
1310     __iterator__: function struct__iterator__() {
1311         let self = this;
1312         return ([k, self[k]] for (k in keys(self.members)))
1313     }
1314 }, {
1315     fromArray: function fromArray(ary) {
1316         if (!(ary instanceof this))
1317             ary.__proto__ = this.prototype;
1318         return ary;
1319     },
1320
1321     /**
1322      * Sets a lazily constructed default value for a member of
1323      * the struct. The value is constructed once, the first time
1324      * it is accessed and memoized thereafter.
1325      *
1326      * @param {string} key The name of the member for which to
1327      *     provide the default value.
1328      * @param {function} val The function which is to generate
1329      *     the default value.
1330      */
1331     defaultValue: function defaultValue(key, val) {
1332         let i = this.prototype.members[key];
1333         this.prototype.__defineGetter__(i, function () (this[i] = val.call(this)));
1334         this.prototype.__defineSetter__(i, function (value)
1335             Class.replaceProperty(this, i, value));
1336         return this;
1337     },
1338
1339     localize: function localize(key, defaultValue) {
1340         let i = this.prototype.members[key];
1341         Object.defineProperty(this.prototype, i, Messages.Localized(defaultValue).init(key, this.prototype));
1342         return this;
1343     }
1344 });
1345
1346 var Timer = Class("Timer", {
1347     init: function init(minInterval, maxInterval, callback, self=this) {
1348         this._timer = services.Timer();
1349         this.callback = callback;
1350         this.self = self;
1351         this.minInterval = minInterval;
1352         this.maxInterval = maxInterval;
1353         this.doneAt = 0;
1354         this.latest = 0;
1355     },
1356
1357     notify: function notify(timer, force) {
1358         try {
1359             if (!loaded || loaded.util && util.rehashing || typeof util === "undefined" || !force && this.doneAt == 0)
1360                 return;
1361
1362             this._timer.cancel();
1363             this.latest = 0;
1364             // minInterval is the time between the completion of the command and the next firing
1365             this.doneAt = Date.now() + this.minInterval;
1366
1367             this.callback.call(this.self, this.arg);
1368         }
1369         catch (e) {
1370             if (typeof util === "undefined")
1371                 dump(JSMLoader.name + ": " + e + "\n" + (e.stack || Error().stack));
1372             else
1373                 util.reportError(e);
1374         }
1375         finally {
1376             this.doneAt = Date.now() + this.minInterval;
1377         }
1378     },
1379
1380     tell: function tell(arg) {
1381         if (arguments.length > 0)
1382             this.arg = arg;
1383
1384         let now = Date.now();
1385         if (this.doneAt == -1)
1386             this._timer.cancel();
1387
1388         let timeout = this.minInterval;
1389         if (now > this.doneAt && this.doneAt > -1)
1390             timeout = 0;
1391         else if (this.latest)
1392             timeout = Math.min(timeout, this.latest - now);
1393         else
1394             this.latest = now + this.maxInterval;
1395
1396         this._timer.initWithCallback(this, Math.max(timeout, 0), this._timer.TYPE_ONE_SHOT);
1397         this.doneAt = -1;
1398     },
1399
1400     reset: function reset() {
1401         this._timer.cancel();
1402         this.doneAt = 0;
1403     },
1404
1405     flush: function flush(force) {
1406         if (this.doneAt == -1 || force)
1407             this.notify(null, true);
1408     }
1409 });
1410
1411 /**
1412  * Idempotent function which returns the UTF-8 encoded value of an
1413  * improperly-decoded string.
1414  *
1415  * @param {string} str
1416  * @returns {string}
1417  */
1418 function UTF8(str) {
1419     try {
1420         return decodeURIComponent(escape(str));
1421     }
1422     catch (e) {
1423         return str;
1424     }
1425 }
1426
1427 function octal(decimal) parseInt(decimal, 8);
1428
1429 /**
1430  * Iterates over an arbitrary object. The following iterator types are
1431  * supported, and work as a user would expect:
1432  *
1433  *  â€¢ nsIDOMNodeIterator
1434  *  â€¢ mozIStorageStatement
1435  *
1436  * Additionally, the following array-like objects yield a tuple of the
1437  * form [index, element] for each contained element:
1438  *
1439  *  â€¢ nsIDOMHTMLCollection
1440  *  â€¢ nsIDOMNodeList
1441  *
1442  * and the following likewise yield one element of the form
1443  * [name, element] for each contained element:
1444  *
1445  *  â€¢ nsIDOMNamedNodeMap
1446  *
1447  * Duck typing is implemented for any other type. If the object
1448  * contains the "enumerator" property, iter is called on that. If the
1449  * property is a function, it is called first. If it contains the
1450  * property "getNext" along with either "hasMoreItems" or "hasMore", it
1451  * is iterated over appropriately.
1452  *
1453  * For all other cases, this function behaves exactly like the Iterator
1454  * function.
1455  *
1456  * @param {object} obj
1457  * @param {nsIJSIID} iface The interface to which to query all elements.
1458  * @returns {Generator}
1459  */
1460 iter.iteratorProp = "@@iterator" in [] ? "@@iterator" : "iterator";
1461 function iter(obj, iface) {
1462     if (arguments.length == 2 && iface instanceof Ci.nsIJSIID)
1463         return iter(obj).map(item => item.QueryInterface(iface));
1464
1465     let args = arguments;
1466     let res = Iterator(obj);
1467
1468     if (args.length > 1)
1469         res = (function () {
1470             for (let i = 0; i < args.length; i++)
1471                 for (let j in iter(args[i]))
1472                     yield j;
1473         })();
1474     else if (isinstance(obj, ["Iterator", "Generator", "Array"]))
1475         ;
1476     else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList]))
1477         res = array.iterItems(obj);
1478     else if (iter.iteratorProp in obj && callable(obj[iter.iteratorProp]) && !("__iterator__" in obj))
1479         res = (x for (x of obj));
1480     else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) {
1481         while (obj.constructor instanceof ctypes.PointerType)
1482             obj = obj.contents;
1483         if (obj.constructor instanceof ctypes.ArrayType)
1484             res = array.iterItems(obj);
1485         else if (obj.constructor instanceof ctypes.StructType)
1486             res = (function () {
1487                 for (let prop in values(obj.constructor.fields))
1488                     yield let ([name, type] = Iterator(prop).next()) [name, obj[name]];
1489             })();
1490         else
1491             return iter({});
1492     }
1493     else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap ||
1494              Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap)
1495         res = (function () {
1496             for (let i = 0; i < obj.length; i++)
1497                 yield [obj.name, obj];
1498         })();
1499     else if (obj instanceof Ci.mozIStorageStatement)
1500         res = (function (obj) {
1501             while (obj.executeStep())
1502                 yield obj.row;
1503             obj.reset();
1504         })(obj);
1505     else if ("getNext" in obj) {
1506         if ("hasMoreElements" in obj)
1507             res = (function () {
1508                 while (obj.hasMoreElements())
1509                     yield obj.getNext();
1510             })();
1511         else if ("hasMore" in obj)
1512             res = (function () {
1513                 while (obj.hasMore())
1514                     yield obj.getNext();
1515             })();
1516     }
1517     else if ("enumerator" in obj) {
1518         if (callable(obj.enumerator))
1519             return iter(obj.enumerator());
1520         return iter(obj.enumerator);
1521     }
1522     return Iter(res);
1523 }
1524 update(iter, {
1525     toArray: function toArray(iter) array(iter).array,
1526
1527     // See array.prototype for API docs.
1528     toObject: function toObject(iter) {
1529         let obj = {};
1530         for (let [k, v] in iter)
1531             if (v instanceof Class.Property)
1532                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1533             else
1534                 obj[k] = v;
1535         return obj;
1536     },
1537
1538     compact: function compact(iter) (item for (item in iter) if (item != null)),
1539
1540     every: function every(iter, pred, self) {
1541         pred = pred || util.identity;
1542         for (let elem of iter)
1543             if (!pred.call(self, elem))
1544                 return false;
1545         return true;
1546     },
1547     some: function every(iter, pred, self) {
1548         pred = pred || util.identity;
1549         for (let elem of iter)
1550             if (pred.call(self, elem))
1551                 return true;
1552         return false;
1553     },
1554
1555     filter: function filter(iter, pred, self) {
1556         for (let elem of iter)
1557             if (pred.call(self, elem))
1558                 yield elem;
1559     },
1560
1561     /**
1562      * Iterates over an iterable object and calls a callback for each
1563      * element.
1564      *
1565      * @param {object} iter The iterator.
1566      * @param {function} fn The callback.
1567      * @param {object} self The this object for *fn*.
1568      */
1569     forEach: function forEach(iter, func, self) {
1570         for (let val of iter)
1571             func.call(self, val);
1572     },
1573
1574     indexOf: function indexOf(iter, elem) {
1575         let i = 0;
1576         for (let item of iter) {
1577             if (item == elem)
1578                 return i;
1579             i++;
1580         }
1581     },
1582
1583     /**
1584      * Returns the array that results from applying *func* to each property of
1585      * *obj*.
1586      *
1587      * @param {Object} obj
1588      * @param {function} func
1589      * @returns {Array}
1590      */
1591     map: function map(iter, func, self) {
1592         for (let i of iter)
1593             yield func.call(self, i);
1594     },
1595
1596     /**
1597      * Returns the nth member of the given array that matches the
1598      * given predicate.
1599      */
1600     nth: function nth(iter, pred, n, self) {
1601         if (typeof pred === "number")
1602             [pred, n] = [() => true, pred]; // Hack.
1603
1604         for (let elem of iter)
1605             if (pred.call(self, elem) && n-- === 0)
1606                 return elem;
1607         return undefined;
1608     },
1609
1610     /**
1611      * Analog of Array.find method. Returns the first item in the
1612      * iterator for which `pred` returns true.
1613      */
1614     find: function find(iter, pred, self) {
1615         for (let elem of iter)
1616             if (pred.call(self, elem))
1617                 return elem;
1618         return undefined;
1619     },
1620
1621     sort: function sort(iter, fn, self)
1622         array(this.toArray(iter).sort(fn, self)),
1623
1624     uniq: function uniq(iter) {
1625         let seen = RealSet();
1626         for (let item of iter)
1627             if (!seen.add(item))
1628                 yield item;
1629     },
1630
1631     /**
1632      * Zips the contents of two arrays. The resulting array is the length of
1633      * ary1, with any shortcomings of ary2 replaced with null strings.
1634      *
1635      * @param {Array} ary1
1636      * @param {Array} ary2
1637      * @returns {Array}
1638      */
1639     zip: function zip(iter1, iter2) {
1640         try {
1641             yield [iter1.next(), iter2.next()];
1642         }
1643         catch (e if e instanceof StopIteration) {}
1644     }
1645 });
1646
1647 const Iter = Class("Iter", {
1648     init: function init(iter) {
1649         this.iter = iter;
1650         if ("__iterator__" in iter)
1651             this.iter = iter.__iterator__();
1652
1653         if (this.iter.finalize)
1654             this.finalize = function finalize() this.iter.finalize.apply(this.iter, arguments);
1655     },
1656
1657     next: function next() this.iter.next(),
1658
1659     send: function send() this.iter.send.apply(this.iter, arguments),
1660
1661     __iterator__: function () this.iter
1662 });
1663 iter.Iter = Iter;
1664
1665 function arrayWrap(fn) {
1666     function wrapper() {
1667         let res = fn.apply(this, arguments);
1668         if (isArray(res))
1669             return array(res);
1670         if (isinstance(res, ["Iterator", "Generator"]))
1671             return iter(res);
1672         return res;
1673     }
1674     wrapper.wrapped = fn;
1675     return wrapper;
1676 }
1677
1678 /**
1679  * Array utility methods.
1680  */
1681 var array = Class("array", Array, {
1682     init: function (ary) {
1683         if (isinstance(ary, ["Iterator", "Generator"]) || "__iterator__" in ary)
1684             ary = [k for (k in ary)];
1685         else if (ary.length)
1686             ary = Array.slice(ary);
1687
1688         let self = this;
1689         return new Proxy(ary, {
1690             get: function array_get(target, prop) {
1691                 if (prop in array && callable(array[prop]))
1692                     return arrayWrap(array[prop].bind(array, target));
1693
1694                 if (prop == "array")
1695                     return target;
1696
1697                 let p = target[prop];
1698                 if (!/^\d+$/.test(prop) &&
1699                     prop != "toString" &&
1700                     prop != "toSource" &&
1701                     callable(p))
1702                     return arrayWrap(p);
1703
1704                 return p;
1705             }
1706         });
1707     }
1708 }, {
1709     /**
1710      * Converts an array to an object. As in lisp, an assoc is an
1711      * array of key-value pairs, which maps directly to an object,
1712      * as such:
1713      *    [["a", "b"], ["c", "d"]] -> { a: "b", c: "d" }
1714      *
1715      * @param {[Array]} assoc
1716      * @... {string} 0 - Key
1717      * @...          1 - Value
1718      */
1719     toObject: function toObject(assoc) {
1720         let obj = {};
1721         assoc.forEach(function ([k, v]) {
1722             if (v instanceof Class.Property)
1723                 Object.defineProperty(obj, k, v.init(k, obj) || v);
1724             else
1725                 obj[k] = v;
1726         });
1727         return obj;
1728     },
1729
1730     /**
1731      * Compacts an array, removing all elements that are null or undefined:
1732      *    ["foo", null, "bar", undefined] -> ["foo", "bar"]
1733      *
1734      * @param {Array} ary
1735      * @returns {Array}
1736      */
1737     compact: function compact(ary) ary.filter(item => item != null),
1738
1739     /**
1740      * Returns true if each element of ary1 is equal to the
1741      * corresponding element in ary2.
1742      *
1743      * @param {Array} ary1
1744      * @param {Array} ary2
1745      * @returns {boolean}
1746      */
1747     equals: function (ary1, ary2)
1748         ary1.length === ary2.length && Array.every(ary1, (e, i) => e === ary2[i]),
1749
1750     /**
1751      * Flattens an array, such that all elements of the array are
1752      * joined into a single array:
1753      *    [["foo", ["bar"]], ["baz"], "quux"] -> ["foo", ["bar"], "baz", "quux"]
1754      *
1755      * @param {Array} ary
1756      * @returns {Array}
1757      */
1758     flatten: function flatten(ary) ary.length ? Array.prototype.concat.apply([], ary) : [],
1759
1760     /**
1761      * Returns an Iterator for an array's values.
1762      *
1763      * @param {Array} ary
1764      * @returns {Iterator(Object)}
1765      */
1766     iterValues: function iterValues(ary) {
1767         for (let i = 0; i < ary.length; i++)
1768             yield ary[i];
1769     },
1770
1771     /**
1772      * Returns an Iterator for an array's indices and values.
1773      *
1774      * @param {Array} ary
1775      * @returns {Iterator([{number}, {Object}])}
1776      */
1777     iterItems: function iterItems(ary) {
1778         let length = ary.length;
1779         for (let i = 0; i < length; i++)
1780             yield [i, ary[i]];
1781     },
1782
1783     /**
1784      * Returns the nth member of the given array that matches the
1785      * given predicate.
1786      */
1787     nth: function nth(ary, pred, n, self) {
1788         for (let elem in values(ary))
1789             if (pred.call(self, elem) && n-- === 0)
1790                 return elem;
1791         return undefined;
1792     },
1793
1794     /**
1795      * Filters out all duplicates from an array. If *unsorted* is false, the
1796      * array is sorted before duplicates are removed.
1797      *
1798      * @param {Array} ary
1799      * @param {boolean} unsorted
1800      * @returns {Array}
1801      */
1802     uniq: function uniq(ary, unsorted) {
1803         let res = [];
1804         if (unsorted) {
1805             for (let item in values(ary))
1806                 if (res.indexOf(item) == -1)
1807                     res.push(item);
1808         }
1809         else {
1810             for (let [, item] in Iterator(ary.sort())) {
1811                 if (item != last || !res.length)
1812                     res.push(item);
1813                 var last = item;
1814             }
1815         }
1816         return res;
1817     },
1818
1819     /**
1820      * Zips the contents of two arrays. The resulting array is the length of
1821      * ary1, with any shortcomings of ary2 replaced with null strings.
1822      *
1823      * @param {Array} ary1
1824      * @param {Array} ary2
1825      * @returns {Array}
1826      */
1827     zip: function zip(ary1, ary2) {
1828         let res = [];
1829         for (let [i, item] in Iterator(ary1))
1830             res.push([item, i in ary2 ? ary2[i] : ""]);
1831         return res;
1832     }
1833 });
1834
1835 /* Make Minefield not explode, because Minefield exploding is not fun. */
1836 let iterProto = Iter.prototype;
1837 Object.keys(iter).forEach(function (k) {
1838     iterProto[k] = function (...args) {
1839         let res = iter[k].apply(iter, [this].concat(args));
1840         if (isinstance(res, ["Iterator", "Generator"]))
1841             return Iter(res);
1842         return res;
1843     };
1844 });
1845
1846 Object.keys(array).forEach(function (k) {
1847     if (!(k in iterProto))
1848         iterProto[k] = function (...args) {
1849             let res = array[k].apply(array, [this.toArray()].concat(args));
1850             if (isinstance(res, ["Iterator", "Generator"]))
1851                 return Iter(res);
1852             if (isArray(res))
1853                 return array(res);
1854             return res;
1855         };
1856 });
1857
1858 Object.getOwnPropertyNames(Array.prototype).forEach(function (k) {
1859     if (!(k in iterProto) && callable(Array.prototype[k]))
1860         iterProto[k] = function () {
1861             let ary = iter(this).toArray();
1862             let res = ary[k].apply(ary, arguments);
1863             if (isArray(res))
1864                 return array(res);
1865             return res;
1866         };
1867 });
1868
1869 Object.defineProperty(Class.prototype, "closure",
1870                       deprecated("bound", { get: function closure() this.bound }));
1871
1872 endModule();
1873
1874 // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}
1875
1876 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: