Bug 938704 - Make OS.File support modern iterators. r=florian,Yoric

MozReview-Commit-ID: 8F1DtgakxM3

--HG--
extra : rebase_source : 05c42a3236ad55356a9149d8107e4a569fc06cd0
This commit is contained in:
Masatoshi Kimura 2017-08-19 15:04:13 +09:00
parent 7c7d5a22d4
commit b3d2965807
11 changed files with 103 additions and 159 deletions

View File

@ -372,7 +372,7 @@ var Agent = {
if (!iterator.exists()) {
return;
}
for (let entry in iterator) {
for (let entry of iterator) {
if (entry.isDir) {
continue;
}

View File

@ -14,7 +14,7 @@ function gatherFiles(path, fileRegex) {
const iterator = new OS.File.DirectoryIterator(path);
try {
for (let child in iterator) {
for (let child of iterator) {
// Don't descend into test directories. Saves us some time and
// there's no reason to.
if (child.isDir && !child.path.endsWith("/test")) {

View File

@ -1245,64 +1245,45 @@ var DirectoryIterator = function DirectoryIterator(path, options) {
* @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() {
return this;
},
__iterator__() {
[Symbol.asyncIterator]() {
return 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;
},
_itmsg: null,
/**
* Determine whether the directory exists.
*
* @resolves {boolean}
*/
exists: function exists() {
return this._itmsg.then(
function onSuccess(iterator) {
return Scheduler.post("DirectoryIterator_prototype_exists", [iterator]);
}
);
async exists() {
if (this._isClosed) {
return Promise.resolve(false);
}
let iterator = await this._itmsg;
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.
* @resolves By definition of the async iterator protocol, either
* `{value: {File.Entry}, done: false}` if there is an unvisited entry
* in the directory, or `{value: undefined, done: true}`, otherwise.
*/
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;
async next() {
if (this._isClosed) {
return {value: undefined, done: true};
}
return this._next(await this._itmsg);
},
/**
* Get several entries at once.
@ -1312,20 +1293,13 @@ DirectoryIterator.prototype = {
* @return {Promise}
* @resolves {Array} An array containing the |length| next entries.
*/
nextBatch: function nextBatch(size) {
async nextBatch(size) {
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 = await this._itmsg;
let array = await Scheduler.post("DirectoryIterator_prototype_nextBatch", [iterator, size]);
return array.map(DirectoryIterator.Entry.fromMsg);
},
/**
* Apply a function to all elements of the directory sequentially.
@ -1342,82 +1316,51 @@ DirectoryIterator.prototype = {
* @return {Promise} A promise resolved once the loop has reached
* its end.
*/
forEach: function forEach(cb, options) {
async forEach(cb, options) {
if (this._isClosed) {
return Promise.resolve();
return undefined;
}
let self = this;
let position = 0;
let iterator;
// Grab iterator
let promise = this._itmsg.then(
function(aIterator) {
iterator = aIterator;
let iterator = await this._itmsg;
while (true) {
if (this._isClosed) {
return undefined;
}
);
// Then iterate
let loop = function loop() {
if (self._isClosed) {
return Promise.resolve();
let {value, done} = await this._next(iterator);
if (done) {
return undefined;
}
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);
await 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.
* @resolves `{value: undefined, done: true}` If all entries have already
* been visited or the iterator has been closed.
*/
_next: function _next(iterator) {
async _next(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} = await 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() {
async close() {
if (this._isClosed) {
return Promise.resolve();
return undefined;
}
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 = this._itmsg;
this._itmsg = null;
return Scheduler.post("DirectoryIterator_prototype_close", [iterator]);
}
};

View File

@ -356,14 +356,12 @@ if (this.Components) {
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 {value, done} = this.next();
if (done) {
OpenedDirectoryIterators.remove(dir);
return {value: undefined, done: true};
}
return {value: File.DirectoryIterator.Entry.toMsg(value), done: false};
}, false);
},
DirectoryIterator_prototype_nextBatch: function nextBatch(dir, size) {

View File

@ -190,9 +190,9 @@ AbstractFile.AbstractIterator = function AbstractIterator() {
};
AbstractFile.AbstractIterator.prototype = {
/**
* Allow iterating with |for|
* Allow iterating with |for-of|
*/
__iterator__: function __iterator__() {
[Symbol.iterator]() {
return this;
},
/**
@ -207,7 +207,7 @@ AbstractFile.AbstractIterator.prototype = {
*/
forEach: function forEach(cb) {
let index = 0;
for (let entry in this) {
for (let entry of this) {
cb(entry, index++, this);
}
},
@ -225,7 +225,7 @@ AbstractFile.AbstractIterator.prototype = {
nextBatch: function nextBatch(length) {
let array = [];
let i = 0;
for (let entry in this) {
for (let entry of this) {
array.push(entry);
if (++i >= length) {
return array;
@ -503,7 +503,7 @@ AbstractFile.removeRecursive = function(path, options = {}) {
}
try {
for (let entry in iterator) {
for (let entry of iterator) {
if (entry.isDir) {
if (entry.isLink) {
// Unlike Unix symlinks, NTFS junctions or NTFS symlinks to

View File

@ -732,16 +732,16 @@
*
* 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();
@ -764,10 +764,13 @@
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};
};
/**

View File

@ -811,9 +811,9 @@
*
* 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
@ -824,9 +824,12 @@
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() {

View File

@ -215,7 +215,7 @@ function test_iter_dir() {
let iterator = new OS.File.DirectoryIterator(parent);
info("test_iter_dir: iterator created");
let encountered_tmp_file = false;
for (let entry in iterator) {
for (let entry of iterator) {
// Checking that |name| can be decoded properly
info("test_iter_dir: encountering entry " + entry.name);
@ -266,7 +266,7 @@ function test_iter_dir() {
// Testing nextBatch()
iterator = new OS.File.DirectoryIterator(parent);
let allentries = [];
for (let x in iterator) {
for (let x of iterator) {
allentries.push(x);
}
iterator.close();

View File

@ -2973,12 +2973,9 @@ SearchService.prototype = {
{ winPattern: "*.xml" });
try {
// Add dir to distDirs if it contains any files.
await checkForSyncCompletion(iterator.next());
distDirs.push(dir);
} catch (ex) {
// Catch for StopIteration exception.
if (ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) {
throw ex;
let {done} = await checkForSyncCompletion(iterator.next());
if (!done) {
distDirs.push(dir);
}
} finally {
iterator.close();
@ -2998,12 +2995,9 @@ SearchService.prototype = {
{ winPattern: "*.xml" });
try {
// Add dir to otherDirs if it contains any files.
await checkForSyncCompletion(iterator.next());
otherDirs.push(dir);
} catch (ex) {
// Catch for StopIteration exception.
if (ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) {
throw ex;
let {done} = await checkForSyncCompletion(iterator.next());
if (!done) {
otherDirs.push(dir);
}
} finally {
iterator.close();

View File

@ -89,7 +89,7 @@ var Agent = {
let skip = new Set(skipFiles);
let entries = [];
for (let entry in iter) {
for (let entry of iter) {
if (!entry.isDir && !entry.isSymLink && !skip.has(entry.name)) {
entries.push(entry);
}
@ -108,7 +108,7 @@ var Agent = {
}
let iter = new OS.File.DirectoryIterator(pathFrom);
if (iter.exists()) {
for (let entry in iter) {
for (let entry of iter) {
if (entry.isDir || entry.isSymLink) {
continue;
}
@ -153,7 +153,7 @@ var Agent = {
wipe: function Agent_wipe(path) {
let iterator = new File.DirectoryIterator(path);
try {
for (let entry in iterator) {
for (let entry of iterator) {
try {
File.remove(entry.path);
} catch (ex) {

View File

@ -6567,8 +6567,11 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
}
try {
for (let promise in iterator) {
let entry = await promise;
for (;;) {
let {value: entry, done} = await iterator.next();
if (done) {
break;
}
// Skip the directory currently in use
if (this._directory && this._directory.path == entry.path) {
@ -6581,16 +6584,16 @@ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
}
if (entry.isDir) {
await OS.File.removeDir(entry.path, {
ignoreAbsent: true,
ignorePermissions: true,
});
} else {
await OS.File.remove(entry.path, {
ignoreAbsent: true,
});
}
}
await OS.File.removeDir(entry.path, {
ignoreAbsent: true,
ignorePermissions: true,
});
} else {
await OS.File.remove(entry.path, {
ignoreAbsent: true,
});
}
}
} catch (e) {
logger.error("Failed to clean updated system add-ons directories.", e);