X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=common%2Fmodules%2Fbase.jsm;h=b581186ea703f0418ef196f4562d0a5bffdf4abd;hb=refs%2Fheads%2Fupstream;hp=e0f0eaad1bff85036755b1c98f512ce5bc81e733;hpb=354a049cce8415487552ce405cce167b7071fe1f;p=dactyl.git diff --git a/common/modules/base.jsm b/common/modules/base.jsm index e0f0eaa..b581186 100644 --- a/common/modules/base.jsm +++ b/common/modules/base.jsm @@ -1,4 +1,4 @@ -// Copyright (c) 2009-2013 Kris Maglione +// Copyright (c) 2009-2014 Kris Maglione // // This work is licensed for reuse under an MIT license. Details are // given in the LICENSE.txt file included with this file. @@ -6,10 +6,19 @@ var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); +var Cs = new Proxy(Components.stack, { + get: function Cs_get(target, prop) Components.stack.caller[prop] +}); + +function module(url) { + let obj = {}; + Cu.import(url, obj); + return obj; +} + +var { XPCOMUtils } = module("resource://gre/modules/XPCOMUtils.jsm"); try { - var ctypes; - Cu.import("resource://gre/modules/ctypes.jsm"); + var ctypes = module("resource://gre/modules/ctypes.jsm"); } catch (e) {} @@ -17,15 +26,41 @@ let objproto = Object.prototype; let { __lookupGetter__, __lookupSetter__, __defineGetter__, __defineSetter__, hasOwnProperty, propertyIsEnumerable } = objproto; -if (typeof XPCSafeJSObjectWrapper === "undefined") - this.XPCSafeJSObjectWrapper = XPCNativeWrapper; +hasOwnProperty = Function.call.bind(hasOwnProperty); +propertyIsEnumerable = Function.call.bind(propertyIsEnumerable); -let getGlobalForObject = Cu.getGlobalForObject || (obj => obj.__parent__); +// Gecko 24. +if (!("find" in Array.prototype)) + Object.defineProperty(Array.prototype, "find", { + configurable: true, + writable: true, + value: function Array_find(pred, self) { + for (let [i, elem] in Iterator(this)) + if (pred.call(self, elem, i, this)) + return elem; + } + }); -function require(module, target) JSMLoader.load(module, target); +if (!("findIndex" in Array.prototype)) + Object.defineProperty(Array.prototype, "findIndex", { + configurable: true, + writable: true, + value: function Array_findIndex(pred, self) { + for (let [i, elem] in Iterator(this)) + if (pred.call(self, elem, i, this)) + return i; + return -1; + } + }); + +function require(module_, target) { + if (/^[A-Za-z0-9]+:/.test(module_)) + return module(module_); + return JSMLoader.load(module_, target); +} function lazyRequire(module, names, target) { - for each (let name in names) + for (let name of names) memoize(target || this, name, name => require(module)[name]); } @@ -133,41 +168,78 @@ function require_(obj, name, from, targetName) { defineModule("base", { // sed -n 's/^(const|var|function) ([a-zA-Z0-9_]+).*/ "\2",/p' base.jsm | sort | fmt exports: [ - "ErrorBase", "Cc", "Ci", "Class", "Cr", "Cu", "Finished", "Module", "JSMLoader", - "Set", "Struct", "StructBase", "Timer", "UTF8", "XPCOM", "XPCOMShim", "XPCOMUtils", - "XPCSafeJSObjectWrapper", "array", "bind", "call", "callable", "ctypes", "curry", - "debuggerProperties", "defineModule", "deprecated", "endModule", "forEach", "isArray", - "isGenerator", "isinstance", "isObject", "isString", "isSubclass", "isXML", "iter", - "iterAll", "iterOwnProperties", "keys", "literal", "memoize", "octal", "properties", - "require", "set", "update", "values", "update_" + "Cc", + "Ci", + "Class", + "Cr", + "Cs", + "Cu", + "ErrorBase", + "Finished", + "JSMLoader", + "Module", + "RealSet", + "Set", + "Struct", + "StructBase", + "Timer", + "UTF8", + "XPCOM", + "XPCOMShim", + "XPCOMUtils", + "array", + "bind", + "call", + "callable", + "ctypes", + "curry", + "defineModule", + "deprecated", + "endModule", + "forEach", + "hasOwnProperty", + "isArray", + "isGenerator", + "isObject", + "isString", + "isSubclass", + "isinstance", + "iter", + "iterAll", + "iterOwnProperties", + "keys", + "literal", + "memoize", + "modujle", + "octal", + "properties", + "require", + "set", + "update", + "values", ] }); this.lazyRequire("cache", ["cache"]); this.lazyRequire("config", ["config"]); this.lazyRequire("messages", ["_", "Messages"]); +this.lazyRequire("promises", ["Task", "promises"]); this.lazyRequire("services", ["services"]); this.lazyRequire("storage", ["File"]); this.lazyRequire("util", ["FailedAssertion", "util"]); +literal.files = {}; +literal.locations = {}; function literal(/* comment */) { let { caller } = Components.stack; while (caller && caller.language != 2) caller = caller.caller; let file = caller.filename.replace(/.* -> /, ""); - let key = "literal:" + file + ":" + caller.line; - - let source = File.readURL(file); - - let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" + - ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source); - return match[1]; - - // Later... - return cache.get(key, function () { - let source = cache.get("literal:" + file, - () => util.httpGet(file).responseText); + let key = "literal:" + file + ":" + caller.lineNumber; + return cache.get(key, function() { + let source = literal.files[file] || File.readURL(file); + literal.files[file] = source; let match = RegExp("(?:.*\\n){" + (caller.lineNumber - 1) + "}" + ".*literal\\(/\\*([^]*?)\\*/\\)").exec(source); @@ -175,25 +247,10 @@ function literal(/* comment */) { }); } -/** - * Returns a list of all of the top-level properties of an object, by - * way of the debugger. - * - * @param {object} obj - * @returns [jsdIProperty] - */ -function debuggerProperties(obj) { - if (loaded.services && services.debugger.isOn) { - let res = {}; - services.debugger.wrapValue(obj).getProperties(res, {}); - return res.value; - } -} - /** * Iterates over the names of all of the top-level properties of an * object or, if prototypes is given, all of the properties in the - * prototype chain below the top. Uses the debugger if possible. + * prototype chain below the top. * * @param {object} obj The object to inspect. * @param {boolean} properties Whether to inspect the prototype chain @@ -205,14 +262,14 @@ function prototype(obj) XPCNativeWrapper.unwrap(obj).__proto__ || Object.getPrototypeOf(XPCNativeWrapper.unwrap(obj)); -function properties(obj, prototypes, debugger_) { +function properties(obj, prototypes) { let orig = obj; - let seen = { dactylPropertyNames: true }; + let seen = RealSet(["dactylPropertyNames"]); try { if ("dactylPropertyNames" in obj && !prototypes) for (let key in values(obj.dactylPropertyNames)) - if (key in obj && !Set.add(seen, key)) + if (key in obj && !seen.add(key)) yield key; } catch (e) {} @@ -250,16 +307,8 @@ function properties(obj, prototypes, debugger_) { } for (; obj; obj = prototypes && prototype(obj)) { - try { - if (!debugger_ || !services.debugger.isOn) - var iter = (v for each (v in props(obj))); - } - catch (e) {} - if (!iter) - iter = (prop.name.stringValue for (prop in values(debuggerProperties(obj)))); - - for (let key in iter) - if (!prototypes || !Set.add(seen, key) && obj != orig) + for (let key of props(obj)) + if (!prototypes || !seen.add(key) && obj != orig) yield key; } } @@ -271,10 +320,14 @@ function iterOwnProperties(obj) { function deprecated(alternative, fn) { if (isObject(fn)) - return Class.Property(iter(fn).map(([k, v]) => [k, callable(v) ? deprecated(alternative, v) : v]) + return Class.Property(iter(fn).map(([k, v]) => [k, + callable(v) ? deprecated(alternative, v) + : v]) .toObject()); - let name, func = callable(fn) ? fn : function () this[fn].apply(this, arguments); + let name, + func = callable(fn) ? fn + : function () this[fn].apply(this, arguments); function deprecatedMethod() { let obj = !this ? "" : @@ -282,9 +335,13 @@ function deprecated(alternative, fn) { this.constructor.className ? this.constructor.className + "#" : ""; - deprecated.warn(func, obj + (fn.name || name), alternative); + deprecated.warn(func, + obj + (fn.realName || fn.name || name || "").replace(/__/g, "."), + alternative); return func.apply(this, arguments); } + if (func.name) + deprecatedMethod.realName = func.name; return callable(fn) ? deprecatedMethod : Class.Property({ get: function () deprecatedMethod, @@ -293,14 +350,20 @@ function deprecated(alternative, fn) { } deprecated.warn = function warn(func, name, alternative, frame) { if (!func.seenCaller) - func.seenCaller = Set([ + func.seenCaller = RealSet([ "resource://dactyl/javascript.jsm", "resource://dactyl/util.jsm" ]); + if (!(loaded.util && util && loaded.config && config.protocolLoaded)) { + dump("DACTYL: deprecated method called too early [" + [name, alternative] + "]:\n" + Error().stack + "\n\n"); + return; + } + frame = frame || Components.stack.caller.caller; + let filename = util.fixURI(frame.filename || "unknown"); - if (!Set.add(func.seenCaller, filename)) + if (!func.seenCaller.add(filename)) util.dactyl(func).warn([util.urlPath(filename), frame.lineNumber, " "].join(":") + _("warn.deprecated", name, alternative)); } @@ -313,9 +376,13 @@ deprecated.warn = function warn(func, name, alternative, frame) { * @returns {Generator} */ function keys(obj) iter(function keys() { - for (var k in obj) - if (hasOwnProperty.call(obj, k)) + if (isinstance(obj, ["Map"])) + for (let [k, v] of obj) yield k; + else + for (var k in obj) + if (hasOwnProperty(obj, k)) + yield k; }()); /** @@ -326,18 +393,47 @@ function keys(obj) iter(function keys() { * @returns {Generator} */ function values(obj) iter(function values() { - if (isinstance(obj, ["Generator", "Iterator", Iter])) + if (isinstance(obj, ["Map"])) + for (let [k, v] of obj) + yield v; + else if (isinstance(obj, ["Generator", "Iterator", Iter])) for (let k in obj) yield k; + else if (iter.iteratorProp in obj) + for (let v of obj) + yield v; else for (var k in obj) - if (hasOwnProperty.call(obj, k)) + if (hasOwnProperty(obj, k)) yield obj[k]; }()); var forEach = deprecated("iter.forEach", function forEach() iter.forEach.apply(iter, arguments)); var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments)); +var RealSet = Set; +let Set_add = RealSet.prototype.add; +RealSet.prototype.add = function RealSet_add(val) { + let res = this.has(val); + Set_add.apply(this, arguments); + return res; +}; + +RealSet.prototype.difference = function RealSet_difference(set) { + return RealSet(i for (i of this) if (!set.has(i))); +}; + +RealSet.prototype.intersection = function RealSet_intersection(set) { + return RealSet(i for (i of this) if (set.has(i))); +}; + +RealSet.prototype.union = function RealSet_union(set) { + let res = RealSet(this); + for (let item of set) + res.add(item); + return res; +}; + /** * Utility for managing sets of strings. Given an array, returns an * object with one key for each value thereof. @@ -345,13 +441,13 @@ var iterAll = deprecated("iter", function iterAll() iter.apply(null, arguments)) * @param {[string]} ary @optional * @returns {object} */ -function Set(ary) { +this.Set = deprecated("RealSet", function Set(ary) { let obj = {}; if (ary) for (let val in values(ary)) obj[val] = true; return obj; -} +}); /** * Adds an element to a set and returns true if the element was * previously contained. @@ -360,11 +456,18 @@ function Set(ary) { * @param {string} key The key to add. * @returns boolean */ -Set.add = curry(function set_add(set, key) { - let res = this.has(set, key); - set[key] = true; - return res; -}); +Set.add = deprecated("RealSet#add", + curry(function Set__add(set, key) { + if (isinstance(set, ["Set"])) { + let res = set.has(key); + set.add(key); + return res; + } + + let res = this.has(set, key); + set[key] = true; + return res; + })); /** * Returns true if the given set contains the given key. * @@ -372,8 +475,14 @@ Set.add = curry(function set_add(set, key) { * @param {string} key The key to check. * @returns {boolean} */ -Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) && - propertyIsEnumerable.call(set, key)); +Set.has = deprecated("hasOwnProperty or Set#has", + curry(function Set__has(set, key) { + if (isinstance(set, ["Set"])) + return set.has(key); + + return hasOwnProperty(set, key) && + propertyIsEnumerable(set, key); + })); /** * Returns a new set containing the members of the first argument which * do not exist in any of the other given arguments. @@ -381,13 +490,15 @@ Set.has = curry(function set_has(set, key) hasOwnProperty.call(set, key) && * @param {object} set The set. * @returns {object} */ -Set.subtract = function set_subtract(set) { - set = update({}, set); - for (let i = 1; i < arguments.length; i++) - for (let k in keys(arguments[i])) - delete set[k]; - return set; -}; +Set.subtract = deprecated("RealSet#difference", + function set_subtract(set) { + set = update({}, set); + for (let i = 1; i < arguments.length; i++) + for (let k in keys(arguments[i])) + delete set[k]; + return set; + }); + /** * Removes an element from a set and returns true if the element was * previously contained. @@ -396,11 +507,15 @@ Set.subtract = function set_subtract(set) { * @param {string} key The key to remove. * @returns boolean */ -Set.remove = curry(function set_remove(set, key) { - let res = set.has(set, key); - delete set[key]; - return res; -}); +Set.remove = deprecated("RealSet#delete", + curry(function Set__remove(set, key) { + if (isinstance(set, ["Set"])) + return set.delete(key); + + let res = set.has(set, key); + delete set[key]; + return res; + })); function set() { deprecated.warn(set, "set", "Set"); @@ -446,7 +561,7 @@ function curry(fn, length, self, acc) { if (acc == null) acc = []; - return function curried(...args) { + function curried(...args) { // The curried result should preserve 'this' if (args.length == 0) return close(self || this, curried); @@ -458,18 +573,13 @@ function curry(fn, length, self, acc) { return curry(fn, length, self || this, args); }; + curried.realName = fn.realName || fn.name; + return curried; } -if (curry.bind) - var bind = function bind(meth, self, ...args) let (func = callable(meth) ? meth : self[meth]) +var bind = function bind(meth, self, ...args) + let (func = callable(meth) ? meth : self[meth]) func.bind.apply(func, [self].concat(args)); -else - var bind = function bind(func, self, ...args) { - if (!callable(func)) - func = self[func]; - - return function bound(...args2) func.apply(self, args.concat(args2)); - }; /** * Returns true if both arguments are functions and @@ -532,12 +642,6 @@ function isinstance(object, interfaces) { */ function isObject(obj) typeof obj === "object" && obj != null || obj instanceof Ci.nsISupports; -/** - * Returns true if obje is an E4X XML object. - * @deprecated - */ -function isXML(obj) typeof obj === "xml"; - /** * Returns true if and only if its sole argument is an * instance of the builtin Array type. The array may come from @@ -545,10 +649,8 @@ function isXML(obj) typeof obj === "xml"; * is not the case when using (obj instanceof Array). */ var isArray = - Array.isArray - // This is bloody stupid. - ? function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array" - : function isArray(val) objproto.toString.call(val) == "[object Array]"; + // This is bloody stupid. + function isArray(val) Array.isArray(val) || val && val.constructor && val.constructor.name === "Array"; /** * Returns true if and only if its sole argument is an @@ -589,7 +691,7 @@ function call(fn, self, ...args) { function memoize(obj, key, getter) { if (arguments.length == 1) { let res = update(Object.create(obj), obj); - for each (let prop in Object.getOwnPropertyNames(obj)) { + for (let prop of Object.getOwnPropertyNames(obj)) { let get = __lookupGetter__.call(obj, prop); if (get) memoize(res, prop, get); @@ -670,33 +772,6 @@ function update(target) { } return target; } -function update_(target) { - for (let i = 1; i < arguments.length; i++) { - let src = arguments[i]; - Object.getOwnPropertyNames(src || {}).forEach(function (k) { - let desc = Object.getOwnPropertyDescriptor(src, k); - if (desc.value instanceof Class.Property) - desc = desc.value.init(k, target) || desc.value; - - try { - if (typeof desc.value === "function" && target.__proto__ && !(desc.value instanceof Ci.nsIDOMElement /* wtf? */)) { - let func = desc.value.wrapped || desc.value; - if (!func.superapply) { - func.__defineGetter__("super", function get_super_() Object.getPrototypeOf(target)[k]); - func.superapply = function super_apply(self, args) - let (meth = Object.getPrototypeOf(target)[k]) - meth && meth.apply(self, args); - func.supercall = function super_call(self, ...args) - func.superapply(self, args); - } - } - Object.defineProperty(target, k, desc); - } - catch (e) {} - }); - } - return target; -} /** * @constructor Class @@ -729,34 +804,23 @@ function Class(...args) { if (callable(args[0])) superclass = args.shift(); - if (loaded.config && (config.haveGecko("5.*", "6.0") || config.haveGecko("6.*"))) // Bug 657418. - var Constructor = function Constructor() { - var self = Object.create(Constructor.prototype); - self.instance = self; - self.globalInstance = self; - - if ("_metaInit_" in self && self._metaInit_) - self._metaInit_.apply(self, arguments); - - var res = self.init.apply(self, arguments); - return res !== undefined ? res : self; - }; - else - var Constructor = eval(String.replace('\n\ - (function constructor(PARAMS) { \n\ - var self = Object.create(Constructor.prototype); \n\ - self.instance = self; \n\ - self.globalInstance = self; \n\ - \n\ - if ("_metaInit_" in self && self._metaInit_) \n\ - self._metaInit_.apply(self, arguments); \n\ - \n\ - var res = self.init.apply(self, arguments); \n\ - return res !== undefined ? res : self; \n\ - })', - "constructor", (name || superclass.className).replace(/\W/g, "_")) - .replace("PARAMS", /^function .*?\((.*?)\)/.exec(args[0] && args[0].init || Class.prototype.init)[1] - .replace(/\b(self|res|Constructor)\b/g, "$1_"))); + var Constructor = eval(String.replace('\n\ + (function constructor(PARAMS) { \n\ + var self = Object.create(Constructor.prototype); \n\ + self.instance = self; \n\ + self.globalInstance = self; \n\ + \n\ + if ("_metaInit_" in self && self._metaInit_) \n\ + self._metaInit_.apply(self, arguments); \n\ + \n\ + var res = self.init.apply(self, arguments); \n\ + return res !== undefined ? res : self; \n\ + })', + "constructor", (name || superclass.className).replace(/\W/g, "_")) + .replace("PARAMS", + /^function .*?\((.*?)\)/ + .exec(args[0] && args[0].init || Class.prototype.init)[1] + .replace(/\b(self|res|Constructor)\b/g, "$1_"))); Constructor.className = name || superclass.className || superclass.name; @@ -772,7 +836,10 @@ function Class(...args) { } Class.extend(Constructor, superclass, args[0]); - memoize(Constructor, "closure", Class.makeClosure); + memoize(Constructor, "bound", Class.makeClosure); + if (Iter && array) // Hack. :/ + Object.defineProperty(Constructor, "closure", + deprecated("bound", { get: function closure() this.bound })); update(Constructor, args[1]); Constructor.__proto__ = superclass; @@ -785,21 +852,14 @@ function Class(...args) { return Constructor; } -if (Cu.getGlobalForObject) - Class.objectGlobal = function (object) { - try { - return Cu.getGlobalForObject(object); - } - catch (e) { - return null; - } - }; -else - Class.objectGlobal = function (object) { - while (object.__parent__) - object = object.__parent__; - return object; - }; +Class.objectGlobal = function (object) { + try { + return Cu.getGlobalForObject(object); + } + catch (e) { + return null; + } +}; /** * @class Class.Property @@ -863,16 +923,16 @@ Class.Memoize = function Memoize(getter, wait) } }); - util.yieldable(function () { + Task.spawn(function () { let wait; for (var res in getter.call(obj)) { if (wait !== undefined) - yield wait; + yield promises.sleep(wait); wait = res; } Class.replaceProperty(obj, key, res); done = true; - })(); + }); return this[key]; }; @@ -956,8 +1016,21 @@ Class.prototype = { util.rehashing && !isinstance(Cu.getGlobalForObject(callback), ["BackstagePass"])) return; this.timeouts.splice(this.timeouts.indexOf(timer), 1); - util.trapErrors(callback, this); + try { + callback.call(this); + } + catch (e) { + try { + util.dump("Error invoking timer callback registered at " + + [frame.filename, frame.lineNumber, ""].join(":")); + util.reportError(e); + } + catch (e) { + Cu.reportError(e); + } + } }; + let frame = Components.stack.caller; let timer = services.Timer(timeout_notify, timeout || 0, services.Timer.TYPE_ONE_SHOT); this.timeouts.push(timer); return timer; @@ -996,7 +1069,7 @@ Class.prototype = { } try { - if ("value" in desc && (k in this.localizedProperties || k in this.magicalProperties)) + if ("value" in desc && (this.localizedProperties.has(k) || this.magicalProperties.has(k))) this[k] = desc.value; else Object.defineProperty(this, k, desc); @@ -1007,8 +1080,8 @@ Class.prototype = { return this; }, - localizedProperties: {}, - magicalProperties: {} + localizedProperties: RealSet(), + magicalProperties: RealSet() }; for (let name in properties(Class.prototype)) { let desc = Object.getOwnPropertyDescriptor(Class.prototype, name); @@ -1016,37 +1089,39 @@ for (let name in properties(Class.prototype)) { Object.defineProperty(Class.prototype, name, desc); } -Class.makeClosure = function makeClosure() { - const self = this; - function closure(fn) { - function _closure() { - try { - return fn.apply(self, arguments); - } - catch (e if !(e instanceof FailedAssertion)) { - util.reportError(e); - throw e.stack ? e : Error(e); - } +var closureHooks = { + get: function closure_get(target, prop) { + if (hasOwnProperty(target._closureCache, prop)) + return target._closureCache[prop]; + + let p = target[prop] + if (callable(p)) + return target._closureCache[prop] = p.bind(target); + return p; + } + + /* + getOwnPropertyNames: function getOwnPropertyNames(target) { + return [k for (k in properties(target, true))]; + }, + + getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, prop) { + let self = this; + return { + configurable: false, + writable: false, + get value() self.get(target, prop) } - _closure.wrapped = fn; - return _closure; } + */ +}; - iter(properties(this), properties(this, true)).forEach(function (k) { - if (!__lookupGetter__.call(this, k) && callable(this[k])) - closure[k] = closure(this[k]); - else if (!(k in closure)) - Object.defineProperty(closure, k, { - configurable: true, - enumerable: true, - get: function get_proxy() self[k], - set: function set_proxy(val) self[k] = val, - }); - }, this); - - return closure; +Class.makeClosure = function makeClosure() { + this._closureCache = {}; + + return new Proxy(this, closureHooks); }; -memoize(Class.prototype, "closure", Class.makeClosure); +memoize(Class.prototype, "bound", Class.makeClosure); /** * A base class generator for classes which implement XPCOM interfaces. @@ -1094,7 +1169,7 @@ let stub = Class.Property({ */ var ErrorBase = Class("ErrorBase", Error, { level: 2, - init: function EB_init(message, level = 0) { + init: function EB_init(message, level=0) { let error = Error(message); update(this, error); this.stack = error.stack; @@ -1132,7 +1207,7 @@ function Module(name, prototype, ...args) { let proto = callable(prototype) ? args[0] : prototype; proto._metaInit_ = function () { - delete module.prototype._metaInit_; + module.prototype._metaInit_ = null; currentModule[name.toLowerCase()] = this; }; @@ -1223,6 +1298,7 @@ var StructBase = Class("StructBase", Array, { clone: function struct_clone() this.constructor.apply(null, this.slice()), + bound: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "bound")), closure: Class.Property(Object.getOwnPropertyDescriptor(Class.prototype, "closure")), get: function struct_get(key, val) this[this.members[key]], @@ -1268,7 +1344,7 @@ var StructBase = Class("StructBase", Array, { }); var Timer = Class("Timer", { - init: function init(minInterval, maxInterval, callback, self = this) { + init: function init(minInterval, maxInterval, callback, self=this) { this._timer = services.Timer(); this.callback = callback; this.self = self; @@ -1381,6 +1457,7 @@ function octal(decimal) parseInt(decimal, 8); * @param {nsIJSIID} iface The interface to which to query all elements. * @returns {Generator} */ +iter.iteratorProp = "@@iterator" in [] ? "@@iterator" : "iterator"; function iter(obj, iface) { if (arguments.length == 2 && iface instanceof Ci.nsIJSIID) return iter(obj).map(item => item.QueryInterface(iface)); @@ -1394,8 +1471,12 @@ function iter(obj, iface) { for (let j in iter(args[i])) yield j; })(); - else if (isinstance(obj, ["Iterator", "Generator"])) + else if (isinstance(obj, ["Iterator", "Generator", "Array"])) ; + else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList])) + res = array.iterItems(obj); + else if (iter.iteratorProp in obj && callable(obj[iter.iteratorProp]) && !("__iterator__" in obj)) + res = (x for (x of obj)); else if (ctypes && ctypes.CData && obj instanceof ctypes.CData) { while (obj.constructor instanceof ctypes.PointerType) obj = obj.contents; @@ -1409,8 +1490,6 @@ function iter(obj, iface) { else return iter({}); } - else if (isinstance(obj, [Ci.nsIDOMHTMLCollection, Ci.nsIDOMNodeList])) - res = array.iterItems(obj); else if (Ci.nsIDOMNamedNodeMap && obj instanceof Ci.nsIDOMNamedNodeMap || Ci.nsIDOMMozNamedAttrMap && obj instanceof Ci.nsIDOMMozNamedAttrMap) res = (function () { @@ -1460,21 +1539,21 @@ update(iter, { every: function every(iter, pred, self) { pred = pred || util.identity; - for (let elem in iter) + for (let elem of iter) if (!pred.call(self, elem)) return false; return true; }, some: function every(iter, pred, self) { pred = pred || util.identity; - for (let elem in iter) + for (let elem of iter) if (pred.call(self, elem)) return true; return false; }, filter: function filter(iter, pred, self) { - for (let elem in iter) + for (let elem of iter) if (pred.call(self, elem)) yield elem; }, @@ -1488,13 +1567,13 @@ update(iter, { * @param {object} self The this object for *fn*. */ forEach: function forEach(iter, func, self) { - for (let val in iter) + for (let val of iter) func.call(self, val); }, indexOf: function indexOf(iter, elem) { let i = 0; - for (let item in iter) { + for (let item of iter) { if (item == elem) return i; i++; @@ -1510,7 +1589,7 @@ update(iter, { * @returns {Array} */ map: function map(iter, func, self) { - for (let i in iter) + for (let i of iter) yield func.call(self, i); }, @@ -1522,19 +1601,30 @@ update(iter, { if (typeof pred === "number") [pred, n] = [() => true, pred]; // Hack. - for (let elem in iter) + for (let elem of iter) if (pred.call(self, elem) && n-- === 0) return elem; return undefined; }, + /** + * Analog of Array.find method. Returns the first item in the + * iterator for which `pred` returns true. + */ + find: function find(iter, pred, self) { + for (let elem of iter) + if (pred.call(self, elem)) + return elem; + return undefined; + }, + sort: function sort(iter, fn, self) array(this.toArray(iter).sort(fn, self)), uniq: function uniq(iter) { - let seen = {}; - for (let item in iter) - if (!Set.add(seen, item)) + let seen = RealSet(); + for (let item of iter) + if (!seen.add(item)) yield item; }, @@ -1570,6 +1660,20 @@ const Iter = Class("Iter", { __iterator__: function () this.iter }); +iter.Iter = Iter; + +function arrayWrap(fn) { + function wrapper() { + let res = fn.apply(this, arguments); + if (isArray(res)) + return array(res); + if (isinstance(res, ["Iterator", "Generator"])) + return iter(res); + return res; + } + wrapper.wrapped = fn; + return wrapper; +} /** * Array utility methods. @@ -1581,23 +1685,25 @@ var array = Class("array", Array, { else if (ary.length) ary = Array.slice(ary); - return { - __proto__: ary, - __iterator__: function () this.iterItems(), - __noSuchMethod__: function (meth, args) { - var res = array[meth].apply(null, [this.array].concat(args)); - if (isArray(res)) - return array(res); - if (isinstance(res, ["Iterator", "Generator"])) - return iter(res); - return res; - }, - array: ary, - toString: function () this.array.toString(), - concat: function (...args) this.__noSuchMethod__("concat", args), - filter: function (...args) this.__noSuchMethod__("filter", args), - map: function (...args) this.__noSuchMethod__("map", args) - }; + let self = this; + return new Proxy(ary, { + get: function array_get(target, prop) { + if (prop in array && callable(array[prop])) + return arrayWrap(array[prop].bind(array, target)); + + if (prop == "array") + return target; + + let p = target[prop]; + if (!/^\d+$/.test(prop) && + prop != "toString" && + prop != "toSource" && + callable(p)) + return arrayWrap(p); + + return p; + } + }); } }, { /** @@ -1760,6 +1866,9 @@ Object.getOwnPropertyNames(Array.prototype).forEach(function (k) { }; }); +Object.defineProperty(Class.prototype, "closure", + deprecated("bound", { get: function closure() this.bound })); + endModule(); // catch(e){dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack);}