--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -1235,187 +1235,135 @@ var DirectoryIterator = function Directo
* Open the iterator on the worker thread
*
* @type {Promise}
* @resolves {*} A message accepted by the methods of DirectoryIterator
* in the worker thread
* @rejects {StopIteration} If all entries have already been visited
* or the iterator has been closed.
*/
- this.__itmsg = Scheduler.post(
+ this._itmsg = Scheduler.post(
"new_DirectoryIterator", [Type.path.toMsg(path), options],
path
);
this._isClosed = false;
};
DirectoryIterator.prototype = {
- iterator: function () this,
- __iterator__: function () this,
-
- // Once close() is called, _itmsg should reject with a
- // StopIteration. However, we don't want to create the promise until
- // it's needed because it might never be used. In that case, we
- // would get a warning on the console.
- get _itmsg() {
- if (!this.__itmsg) {
- this.__itmsg = Promise.reject(StopIteration);
- }
- return this.__itmsg;
- },
-
/**
* Determine whether the directory exists.
*
* @resolves {boolean}
*/
exists: function exists() {
return this._itmsg.then(
function onSuccess(iterator) {
return Scheduler.post("DirectoryIterator_prototype_exists", [iterator]);
}
);
},
/**
* Get the next entry in the directory.
*
- * @return {Promise}
- * @resolves {OS.File.Entry}
- * @rejects {StopIteration} If all entries have already been visited.
+ * @return {Promise} // FIXME
*/
next: function next() {
- let self = this;
- let promise = this._itmsg;
-
- // Get the iterator, call _next
- promise = promise.then(
- function withIterator(iterator) {
- return self._next(iterator);
- });
-
- return promise;
+ return this._itmsg.then(iterator => this._next(iterator));
},
/**
* Get several entries at once.
*
* @param {number=} length If specified, the number of entries
* to return. If unspecified, return all remaining entries.
* @return {Promise}
* @resolves {Array} An array containing the |length| next entries.
*/
- nextBatch: function nextBatch(size) {
+ nextBatch: Task.async(function*() {
if (this._isClosed) {
- return Promise.resolve([]);
+ return [];
}
- let promise = this._itmsg;
- promise = promise.then(
- function withIterator(iterator) {
- return Scheduler.post("DirectoryIterator_prototype_nextBatch", [iterator, size]);
- });
- promise = promise.then(
- function withEntries(array) {
- return array.map(DirectoryIterator.Entry.fromMsg);
- });
- return promise;
- },
+ let iterator = yield this._itmsg;
+ let array = yield Scheduler.post("DirectoryIterator_prototype_nextBatch", [iterator, size]);
+ return array.map(DirectoryIterator.Entry.fromMsg);
+ }),
+
/**
* Apply a function to all elements of the directory sequentially.
*
* @param {Function} cb This function will be applied to all entries
* of the directory. It receives as arguments
* - the OS.File.Entry corresponding to the entry;
* - the index of the entry in the enumeration;
* - the iterator itself - return |iterator.close()| to stop the loop.
*
* If the callback returns a promise, iteration waits until the
* promise is resolved before proceeding.
*
* @return {Promise} A promise resolved once the loop has reached
* its end.
*/
- forEach: function forEach(cb, options) {
+ forEach: Task.async(function*(cb, options) {
if (this._isClosed) {
- return Promise.resolve();
+ return;
}
-
- let self = this;
let position = 0;
- let iterator;
-
- // Grab iterator
- let promise = this._itmsg.then(
- function(aIterator) {
- iterator = aIterator;
+ let iterator = yield this._itmsg;
+ while (true) {
+ let {value, done} = yield this._next(iterator);
+ if (done) {
+ return;
}
- );
- // Then iterate
- let loop = function loop() {
- if (self._isClosed) {
- return Promise.resolve();
- }
- return self._next(iterator).then(
- function onSuccess(value) {
- return Promise.resolve(cb(value, position++, self)).then(loop);
- },
- function onFailure(reason) {
- if (reason == StopIteration) {
- return;
- }
- throw reason;
- }
- );
- };
-
- return promise.then(loop);
- },
+ dump(`osfile_async_front: forEach: ${value}, ${Object.keys(value)}\n`);
+ dump(`=> ${value._name}\n`);
+ yield cb(value, position++, this);
+ }
+ }),
/**
* Auxiliary method: fetch the next item
*
* @rejects {StopIteration} If all entries have already been visited
- * or the iterator has been closed.
+ * or the iterator has been closed. // FIXME
*/
- _next: function _next(iterator) {
+ _next: Task.async(function*(iterator) {
if (this._isClosed) {
- return this._itmsg;
+ return { value: undefined, done: true };
}
- let self = this;
- let promise = Scheduler.post("DirectoryIterator_prototype_next", [iterator]);
- promise = promise.then(
- DirectoryIterator.Entry.fromMsg,
- function onReject(reason) {
- if (reason == StopIteration) {
- self.close();
- throw StopIteration;
- }
- throw reason;
- });
- return promise;
- },
+ let {value, done} = yield Scheduler.post("DirectoryIterator_prototype_next", [iterator]);
+ if (done) {
+ this.close();
+ return { value: undefined, done: true };
+ }
+ return { value: DirectoryIterator.Entry.fromMsg(value), done: false };
+ }),
/**
* Close the iterator
*/
- close: function close() {
+ close: Task.async(function*() {
if (this._isClosed) {
- return Promise.resolve();
+ return;
}
this._isClosed = true;
- let self = this;
- return this._itmsg.then(
- function withIterator(iterator) {
- // Set __itmsg to null so that the _itmsg getter returns a
- // rejected StopIteration promise if it's ever used.
- self.__itmsg = null;
- return Scheduler.post("DirectoryIterator_prototype_close", [iterator]);
- }
- );
- }
+ let iterator = yield this._itmsg;
+ this._itmsg = null;
+ return Scheduler.post("DirectoryIterator_prototype_close", [iterator]);
+ }),
};
+/**
+ * Allow iterating with |for|
+ */
+DirectoryIterator.prototype[Symbol.iterator] = function() {
+ return { value: this.next(), done: false };
+};
+DirectoryIterator.prototype.iterator = DirectoryIterator.prototype[Symbol.iterator];
+
DirectoryIterator.Entry = function Entry(value) {
- return value;
+ for (let k of Object.keys(value)) {
+ this[k] = value[k];
+ }
};
DirectoryIterator.Entry.prototype = Object.create(SysAll.AbstractEntry.prototype);
DirectoryIterator.Entry.fromMsg = function fromMsg(value) {
return new DirectoryIterator.Entry(value);
};
File.resetWorker = function() {
--- a/toolkit/components/osfile/modules/osfile_async_worker.js
+++ b/toolkit/components/osfile/modules/osfile_async_worker.js
@@ -354,24 +354,21 @@ if (this.Components) {
function do_flush() {
return this.flush();
});
},
// Methods of OS.File.DirectoryIterator
DirectoryIterator_prototype_next: function next(dir) {
return withDir(dir,
function do_next() {
- try {
- return File.DirectoryIterator.Entry.toMsg(this.next());
- } catch (x) {
- if (x == StopIteration) {
- OpenedDirectoryIterators.remove(dir);
- }
- throw x;
+ let next = this.next();
+ if (next.done) {
+ OpenedDirectoryIterators.remove(dir);
}
+ return next;
}, false);
},
DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) {
return withDir(dir,
function do_nextBatch() {
let result;
try {
result = this.nextBatch(size);
--- a/toolkit/components/osfile/modules/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_shared_front.jsm
@@ -175,34 +175,28 @@ AbstractFile.openUnique = function openU
/**
* Code shared by iterators.
*/
AbstractFile.AbstractIterator = function AbstractIterator() {
};
AbstractFile.AbstractIterator.prototype = {
/**
- * Allow iterating with |for|
- */
- __iterator__: function __iterator__() {
- return this;
- },
- /**
* Apply a function to all elements of the directory sequentially.
*
* @param {Function} cb This function will be applied to all entries
* of the directory. It receives as arguments
* - the OS.File.Entry corresponding to the entry;
* - the index of the entry in the enumeration;
* - the iterator itself - calling |close| on the iterator stops
* the loop.
*/
forEach: function forEach(cb) {
let index = 0;
- for (let entry in this) {
+ for (let entry of this) {
cb(entry, index++, this);
}
},
/**
* Return several entries at once.
*
* Entries are returned in the same order as a walk with |forEach| or
* |for(...)|.
@@ -221,16 +215,25 @@ AbstractFile.AbstractIterator.prototype
return array;
}
}
return array;
}
};
/**
+ * Allow iterating with |for|
+ */
+AbstractFile.AbstractIterator.prototype[Symbol.iterator] = function() {
+ return {
+ next: () => this.next()
+ }
+};
+
+/**
* Utility function shared by implementations of |OS.File.open|:
* extract read/write/trunc/create/existing flags from a |mode|
* object.
*
* @param {*=} mode An object that may contain fields |read|,
* |write|, |truncate|, |create|, |existing|. These fields
* are interpreted only if true-ish.
* @return {{read:bool, write:bool, trunc:bool, create:bool,
@@ -466,45 +469,53 @@ AbstractFile.writeAtomic =
};
/**
* This function is used by removeDir to avoid calling lstat for each
* files under the specified directory. External callers should not call
* this function directly.
*/
AbstractFile.removeRecursive = function(path, options = {}) {
+ dump(`removeRecursive 1\n`);
let iterator = new OS.File.DirectoryIterator(path);
if (!iterator.exists()) {
if (!("ignoreAbsent" in options) || options.ignoreAbsent) {
return;
}
}
+ dump(`removeRecursive 2\n`);
try {
- for (let entry in iterator) {
+ for (let entry of iterator) {
+ dump(`removeRecursive 3: ${entry}\n`);
if (entry.isDir) {
if (entry.isLink) {
// Unlike Unix symlinks, NTFS junctions or NTFS symlinks to
// directories are directories themselves. OS.File.remove()
// will not work for them.
OS.File.removeEmptyDir(entry.path, options);
+ dump(`removeRecursive 4\n`);
} else {
// Normal directories.
AbstractFile.removeRecursive(entry.path, options);
+ dump(`removeRecursive 5\n`);
}
} else {
// NTFS symlinks to files, Unix symlinks, or regular files.
OS.File.remove(entry.path, options);
+ dump(`removeRecursive 6\n`);
}
}
+ dump(`removeRecursive 7\n`);
} finally {
iterator.close();
}
-
+ dump(`removeRecursive 8\n`);
OS.File.removeEmptyDir(path);
+ dump(`removeRecursive 9\n`);
};
/**
* Create a directory and, optionally, its parent directories.
*
* @param {string} path The name of the directory.
* @param {*=} options Additional options.
*
--- a/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_allthreads.jsm
@@ -81,17 +81,17 @@ libc.declareLazy(LazyBindings, "strerror
*/
var OSError = function OSError(operation = "unknown operation",
errno = ctypes.errno, path = "") {
SharedAll.OSError.call(this, operation, path);
this.unixErrno = errno;
};
OSError.prototype = Object.create(SharedAll.OSError.prototype);
OSError.prototype.toString = function toString() {
- return "Unix error " + this.unixErrno +
+ return "OSError: Unix error " + this.unixErrno +
" during operation " + this.operation +
(this.path? " on file " + this.path : "") +
" (" + LazyBindings.strerror(this.unixErrno).readString() + ")";
};
OSError.prototype.toMsg = function toMsg() {
return OSError.toMsg(this);
};
@@ -279,21 +279,20 @@ exports.AbstractInfo = AbstractInfo;
/**
* Code shared by implementations of File.DirectoryIterator.Entry on Unix
*
* @constructor
*/
var AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) {
this._isDir = isDir;
- this._isSymlLink = isSymLink;
+ this._isSymLink = isSymLink;
this._name = name;
this._path = path;
};
-
AbstractEntry.prototype = {
/**
* |true| if the entry is a directory, |false| otherwise
*/
get isDir() {
return this._isDir;
},
/**
--- a/toolkit/components/osfile/modules/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm
@@ -746,26 +746,27 @@
File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
/**
* Return the next entry in the directory, if any such entry is
* available.
*
* Skip special directories "." and "..".
*
- * @return {File.Entry} The next entry in the directory.
- * @throws {StopIteration} Once all files in the directory have been
- * encountered.
+ * @return By definition of the iterator protocol, either
+ * `{value: {File.Entry}, done: false}` if there is an unvisited
+ * entry in the directory, or `{value: undefined, done: true}`,
+ * otherwise.
*/
File.DirectoryIterator.prototype.next = function next() {
if (!this._exists) {
throw File.Error.noSuchFile("DirectoryIterator.prototype.next", this._path);
}
if (this._closed) {
- throw StopIteration;
+ return {value: undefined, done: true};
}
for (let entry = UnixFile.readdir(this._dir);
entry != null && !entry.isNull();
entry = UnixFile.readdir(this._dir)) {
let contents = entry.contents;
let name = contents.d_name.readString();
if (name == "." || name == "..") {
continue;
@@ -778,20 +779,20 @@
throw_on_negative("lstat", UnixFile.lstat(path, gStatDataPtr), this._path);
isDir = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFDIR;
isSymLink = (gStatData.st_mode & Const.S_IFMT) == Const.S_IFLNK;
} else {
isDir = contents.d_type == Const.DT_DIR;
isSymLink = contents.d_type == Const.DT_LNK;
}
- return new File.DirectoryIterator.Entry(isDir, isSymLink, name, this._path);
+ return {value: new File.DirectoryIterator.Entry(isDir, isSymLink, name, this._path), done: false}
}
this.close();
- throw StopIteration;
+ return {value: undefined, done: true};
};
/**
* Close the iterator and recover all resources.
* You should call this once you have finished iterating on a directory.
*/
File.DirectoryIterator.prototype.close = function close() {
if (this._closed) return;
--- a/toolkit/components/osfile/modules/osfile_win_allthreads.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_allthreads.jsm
@@ -104,17 +104,17 @@ OSError.prototype.toString = function to
/* Minimum size of buffer */ 1024,
/* Format args*/ null
);
if (!result) {
buf = "additional error " +
ctypes.winLastError +
" while fetching system error message";
}
- return "Win error " + this.winLastError + " during operation "
+ return "OSError: Win error " + this.winLastError + " during operation "
+ this.operation + (this.path? " on file " + this.path : "") +
" (" + buf.readString() + ")";
};
OSError.prototype.toMsg = function toMsg() {
return OSError.toMsg(this);
};
/**
--- a/toolkit/components/osfile/modules/osfile_win_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_win_front.jsm
@@ -794,32 +794,36 @@
},
/**
* Return the next entry in the directory, if any such entry is
* available.
*
* Skip special directories "." and "..".
*
- * @return {File.Entry} The next entry in the directory.
- * @throws {StopIteration} Once all files in the directory have been
- * encountered.
+ * @return By definition of the iterator protocol, either
+ * `{value: {File.Entry}, done: false}` if there is an unvisited
+ * entry in the directory, or `{value: undefined, done: true}`,
+ * otherwise.
*/
File.DirectoryIterator.prototype.next = function next() {
// FIXME: If we start supporting "\\?\"-prefixed paths, do not forget
// that "." and ".." are absolutely normal file names if _path starts
// with such prefix
for (let entry = this._next(); entry != null; entry = this._next()) {
let name = entry.cFileName.readString();
if (name == "." || name == "..") {
continue;
}
- return new File.DirectoryIterator.Entry(entry, this._path);
+ return {
+ value: new File.DirectoryIterator.Entry(entry, this._path),
+ done: false
+ };
}
- throw StopIteration;
+ return { value: undefined, done: true };
};
File.DirectoryIterator.prototype.close = function close() {
if (this._closed) {
return;
}
this._closed = true;
if (this._handle) {
--- a/toolkit/components/osfile/tests/xpcshell/test_removeDir.js
+++ b/toolkit/components/osfile/tests/xpcshell/test_removeDir.js
@@ -13,83 +13,74 @@ do_register_cleanup(function() {
});
function run_test() {
Services.prefs.setBoolPref("toolkit.osfile.log", true);
run_next_test();
}
-add_task(function() {
+add_task(function*() {
// Set up profile. We create the directory in the profile, because the profile
// is removed after every test run.
do_get_profile();
let file = OS.Path.join(OS.Constants.Path.profileDir, "file");
let dir = OS.Path.join(OS.Constants.Path.profileDir, "directory");
let file1 = OS.Path.join(dir, "file1");
let file2 = OS.Path.join(dir, "file2");
let subDir = OS.Path.join(dir, "subdir");
let fileInSubDir = OS.Path.join(subDir, "file");
// Sanity checking for the test
- do_check_false((yield OS.File.exists(dir)));
+ Assert.ok(!(yield OS.File.exists(dir)), "The directory initially doesn't exist");
// Remove non-existent directory
- let exception = null;
- try {
- yield OS.File.removeDir(dir, {ignoreAbsent: false});
- } catch (ex) {
- exception = ex;
- }
-
- do_check_true(!!exception);
- do_check_true(exception instanceof OS.File.Error);
+ yield Assert.rejects(OS.File.removeDir(dir, {ignoreAbsent: false}), /OSError/);
// Remove non-existent directory with ignoreAbsent
yield OS.File.removeDir(dir, {ignoreAbsent: true});
yield OS.File.removeDir(dir);
// Remove file with ignoreAbsent: false
yield OS.File.writeAtomic(file, "content", { tmpPath: file + ".tmp" });
- exception = null;
- try {
- yield OS.File.removeDir(file, {ignoreAbsent: false});
- } catch (ex) {
- exception = ex;
- }
-
- do_check_true(!!exception);
- do_check_true(exception instanceof OS.File.Error);
+ Assert.rejects(OS.File.removeDir(file, {ignoreAbsent: false}), /OSError/);
// Remove empty directory
yield OS.File.makeDir(dir);
yield OS.File.removeDir(dir);
- do_check_false((yield OS.File.exists(dir)));
+ Assert.ok(!(yield OS.File.exists(dir)), "Directory has been removed");
// Remove directory that contains one file
yield OS.File.makeDir(dir);
+ do_print("Created directory");
+
yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
+ do_print("Wrote something in the directory");
+
yield OS.File.removeDir(dir);
- do_check_false((yield OS.File.exists(dir)));
+ do_print("Removed directory");
+
+ Assert.ok(!(yield OS.File.exists(dir)), "Directory has been removed 2");
// Remove directory that contains multiple files
yield OS.File.makeDir(dir);
yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
yield OS.File.writeAtomic(file2, "content", { tmpPath: file2 + ".tmp" });
yield OS.File.removeDir(dir);
- do_check_false((yield OS.File.exists(dir)));
+ Assert.ok(!(yield OS.File.exists(dir)), "Directory has been removed 3");
// Remove directory that contains a file and a directory
yield OS.File.makeDir(dir);
yield OS.File.writeAtomic(file1, "content", { tmpPath: file1 + ".tmp" });
yield OS.File.makeDir(subDir);
yield OS.File.writeAtomic(fileInSubDir, "content", { tmpPath: fileInSubDir + ".tmp" });
yield OS.File.removeDir(dir);
- do_check_false((yield OS.File.exists(dir)));
+ Assert.ok(!(yield OS.File.exists(dir)), "Directory has been removed 4");
+
});
add_task(function* test_unix_symlink() {
// Windows does not implement OS.File.unixSymLink()
if (OS.Constants.Win) {
return;
}