Bug 1364645 - Replace Task.async()/Task.spawn() with async function and yield with await. r=florian
authorJorg K <jorgk@jorgk.com>
Wed, 21 Nov 2018 13:16:01 +0100
changeset 33751 8429a12e23afdc625af4543a64113d995529e43a
parent 33750 b31bf692d3a11c36a1f25bd6798f9fd6eea2b618
child 33752 aa31b79e0b50b35696312451bb25664b3e998a81
push id388
push userclokep@gmail.com
push dateMon, 28 Jan 2019 20:54:56 +0000
reviewersflorian
bugs1364645
Bug 1364645 - Replace Task.async()/Task.spawn() with async function and yield with await. r=florian
chat/components/src/logger.js
chat/protocols/irc/ircCommands.jsm
mail/components/im/modules/index_im.jsm
--- a/chat/components/src/logger.js
+++ b/chat/components/src/logger.js
@@ -5,17 +5,16 @@
 var CC = Components.Constructor;
 
 ChromeUtils.import("resource:///modules/hiddenWindow.jsm");
 ChromeUtils.import("resource:///modules/imServices.jsm");
 ChromeUtils.import("resource:///modules/imXPCOMUtils.jsm");
 ChromeUtils.import("resource:///modules/jsProtoHelper.jsm");
 ChromeUtils.import("resource:///modules/ToLocaleFormat.jsm");
 
-ChromeUtils.import("resource://gre/modules/Task.jsm")
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "_", () =>
   l10nHelper("chrome://chat/locale/logger.properties")
 );
 
 var kLineBreak = "@mozilla.org/windows-registry-key;1" in Cc ? "\r\n" : "\n";
 
@@ -52,47 +51,47 @@ function queueFileOperation(aPath, aOper
  * the I/O operations reject, the returned promise will reject with the same reason.
  * We open the file, append, and close it immediately. The alternative is to keep
  * it open and append as required, but we want to make sure we don't open a file
  * for reading while it's already open for writing, so we close it every time
  * (opening a file multiple times concurrently may fail on Windows).
  * Note: This function creates parent directories if required.
  */
 function appendToFile(aPath, aEncodedString, aCreate) {
-  return queueFileOperation(aPath, Task.async(function* () {
-    yield OS.File.makeDir(OS.Path.dirname(aPath),
+  return queueFileOperation(aPath, async function () {
+    await OS.File.makeDir(OS.Path.dirname(aPath),
                           {ignoreExisting: true, from: OS.Constants.Path.profileDir});
-    let file = yield OS.File.open(aPath, {write: true, create: aCreate});
+    let file = await OS.File.open(aPath, {write: true, create: aCreate});
     try {
-      yield file.write(aEncodedString);
+      await file.write(aEncodedString);
     }
     finally {
       /*
        * If both the write() above and the close() below throw, and we don't
        * handle the close error here, the promise will be rejected with the close
        * error and the write error will be dropped. To avoid this, we log any
        * close error here so that any write error will be propagated.
        */
-      yield file.close().catch(Cu.reportError);
+      await file.close().catch(Cu.reportError);
     }
-  }));
+  });
 }
 
 OS.File.profileBeforeChange.addBlocker(
   "Chat logger: writing all pending messages",
-  Task.async(function* () {
+  async function () {
     for (let promise of gFilePromises.values()) {
       try {
-        yield promise;
+        await promise;
       }
       catch (aError) {
         // Ignore the error, whatever queued the operation will take care of it.
       }
     }
-  })
+  }
 );
 
 
 // This function checks names against OS naming conventions and alters them
 // accordingly so that they can be used as file/folder names.
 function encodeName(aName) {
   // Reserved device names by Windows (prefixing "%").
   let reservedNames = /^(CON|PRN|AUX|NUL|COM\d|LPT\d)$/i;
@@ -525,34 +524,34 @@ function Log(aEntries) {
   // quickly determine which directory a log file is from.  We'll use
   // the first file's path.
   this.path = aEntries[0].path;
 }
 Log.prototype = {
   __proto__: ClassInfo("imILog", "Log object"),
   _entryPaths: null,
   format: "json",
-  getConversation: Task.async(function* () {
+  async getConversation() {
     /*
      * Read the set of log files asynchronously and return a promise that
      * resolves to a LogConversation instance. Even if a file contains some
      * junk (invalid JSON), messages that are valid will be read. If the first
      * line of metadata is corrupt however, the data isn't useful and the
      * promise will resolve to null.
      */
     if (this.format != "json")
       return null;
     let messages = [];
     let properties = {};
     let firstFile = true;
     let decoder = new TextDecoder();
     for (let path of this._entryPaths) {
       let lines;
       try {
-        let contents = yield queueFileOperation(path, () => OS.File.read(path));
+        let contents = await queueFileOperation(path, () => OS.File.read(path));
         lines = decoder.decode(contents).split("\n");
       } catch (aError) {
         Cu.reportError("Error reading log file \"" + path + "\":\n" + aError);
         continue;
       }
       let nextLine = lines.shift();
       let filename = OS.Path.basename(path);
 
@@ -602,17 +601,17 @@ Log.prototype = {
         }
       }
     }
 
     if (firstFile) // All selected log files are invalid.
       return null;
 
     return new LogConversation(messages, properties);
-  })
+  }
 };
 
 
 /**
  * Log enumerators provide lists of log files ("entries"). aEntries is an array
  * of the OS.File.DirectoryIterator.Entry instances which represent the log
  * files to be parsed.
  *
@@ -691,35 +690,35 @@ LogEnumerator.prototype = {
   * [Symbol.iterator]() { while (this.hasMoreElements()) yield this.getNext(); }
 };
 
 
 function Logger() { }
 Logger.prototype = {
   // Returned Promise resolves to an array of entries for the
   // log folder if it exists, otherwise null.
-  _getLogArray: Task.async(function* (aAccount, aNormalizedName) {
+  async _getLogArray(aAccount, aNormalizedName) {
     let iterator, path;
     try {
       path = OS.Path.join(getLogFolderPathForAccount(aAccount),
                           encodeName(aNormalizedName));
-      if (yield queueFileOperation(path, () => OS.File.exists(path))) {
+      if (await queueFileOperation(path, () => OS.File.exists(path))) {
         iterator = new OS.File.DirectoryIterator(path);
-        let entries = yield iterator.nextBatch();
+        let entries = await iterator.nextBatch();
         iterator.close();
         return entries;
       }
     } catch (aError) {
       if (iterator)
         iterator.close();
       Cu.reportError("Error getting directory entries for \"" +
                      path + "\":\n" + aError);
     }
     return [];
-  }),
+  },
   getLogFromFile: function logger_getLogFromFile(aFilePath, aGroupByDay) {
     if (!aGroupByDay)
       return Promise.resolve(new Log(aFilePath));
     let [targetDate] = getDateFromFilename(OS.Path.basename(aFilePath));
     if (!targetDate)
       return null;
 
     targetDate = targetDate.toDateString();
@@ -749,79 +748,79 @@ Logger.prototype = {
     });
   },
   // Creates and returns the appropriate LogEnumerator for the given log array
   // depending on aGroupByDay, or an EmptyEnumerator if the input array is empty.
   _getEnumerator: function logger__getEnumerator(aLogArray, aGroupByDay) {
     let enumerator = aGroupByDay ? DailyLogEnumerator : LogEnumerator;
     return aLogArray.length ? new enumerator(aLogArray) : EmptyEnumerator;
   },
-  getLogPathsForConversation: Task.async(function* (aConversation) {
+  async getLogPathsForConversation(aConversation) {
     let writer = gLogWritersById.get(aConversation.id);
     // Resolve to null if we haven't created a LogWriter yet for this conv, or
     // if logging is disabled (paths will be null).
     if (!writer || !writer.paths)
       return null;
     let paths = writer.paths;
     // Wait for any pending file operations to finish, then resolve to the paths
     // regardless of whether these operations succeeded.
     for (let path of paths)
-      yield gFilePromises.get(path);
+      await gFilePromises.get(path);
     return paths;
-  }),
+  },
   getLogsForAccountAndName: function logger_getLogsForAccountAndName(aAccount,
                                        aNormalizedName, aGroupByDay) {
     return this._getLogArray(aAccount, aNormalizedName)
                .then(aEntries => this._getEnumerator(aEntries, aGroupByDay));
   },
   getLogsForAccountBuddy: function logger_getLogsForAccountBuddy(aAccountBuddy,
                                                                  aGroupByDay) {
     return this.getLogsForAccountAndName(aAccountBuddy.account,
                                          aAccountBuddy.normalizedName, aGroupByDay);
   },
-  getLogsForBuddy: Task.async(function* (aBuddy, aGroupByDay) {
+  async getLogsForBuddy(aBuddy, aGroupByDay) {
     let entries = [];
     for (let accountBuddy of aBuddy.getAccountBuddies()) {
-      entries = entries.concat(yield this._getLogArray(accountBuddy.account,
+      entries = entries.concat(await this._getLogArray(accountBuddy.account,
                                                        accountBuddy.normalizedName));
     }
     return this._getEnumerator(entries, aGroupByDay);
-  }),
-  getLogsForContact: Task.async(function* (aContact, aGroupByDay) {
+  },
+  async getLogsForContact(aContact, aGroupByDay) {
     let entries = [];
     for (let buddy of aContact.getBuddies()) {
       for (let accountBuddy of buddy.getAccountBuddies()) {
-        entries = entries.concat(yield this._getLogArray(accountBuddy.account,
+        entries = entries.concat(await this._getLogArray(accountBuddy.account,
                                                          accountBuddy.normalizedName));
       }
     }
     return this._getEnumerator(entries, aGroupByDay);
-  }),
+  },
   getLogsForConversation: function logger_getLogsForConversation(aConversation,
                                                                  aGroupByDay) {
     let name = aConversation.normalizedName;
     if (convIsRealMUC(aConversation))
       name += ".chat";
     return this.getLogsForAccountAndName(aConversation.account, name, aGroupByDay);
   },
   getSystemLogsForAccount: function logger_getSystemLogsForAccount(aAccount) {
     return this.getLogsForAccountAndName(aAccount, ".system");
   },
-  getSimilarLogs: Task.async(function* (aLog, aGroupByDay) {
+  async getSimilarLogs(aLog, aGroupByDay) {
     let iterator = new OS.File.DirectoryIterator(OS.Path.dirname(aLog.path));
     let entries;
     try {
-      entries = yield iterator.nextBatch();
+      entries = await iterator.nextBatch();
     } catch (aError) {
       Cu.reportError("Error getting similar logs for \"" +
                      aLog.path + "\":\n" + aError);
     }
     // If there was an error, this will return an EmptyEnumerator.
     return this._getEnumerator(entries, aGroupByDay);
-  }),
+  },
 
   getLogFolderPathForAccount: function(aAccount) {
     return getLogFolderPathForAccount(aAccount);
   },
 
   deleteLogFolderForAccount: function(aAccount) {
     if (!aAccount.disconnecting && !aAccount.disconnected)
       throw new Error("Account must be disconnected first before deleting logs.");
@@ -838,59 +837,59 @@ Logger.prototype = {
     }
     gFilePromises.forEach(checkLogFiles);
     // After all operations finish, remove the whole log folder.
     return Promise.all(pendingPromises)
                   .then(values => { OS.File.removeDir(logPath, { ignoreAbsent: true }); })
                   .catch(aError => Cu.reportError("Failed to remove log folders:\n" + aError));
   },
 
-  forEach: Task.async(function* (aCallback) {
-    let getAllSubdirs = Task.async(function* (aPaths, aErrorMsg) {
+  async forEach(aCallback) {
+    let getAllSubdirs = async function (aPaths, aErrorMsg) {
       let entries = [];
       for (let path of aPaths) {
         let iterator = new OS.File.DirectoryIterator(path);
         try {
-          entries = entries.concat(yield iterator.nextBatch());
+          entries = entries.concat(await iterator.nextBatch());
         } catch (aError) {
           if (aErrorMsg)
             Cu.reportError(aErrorMsg + "\n" + aError);
         } finally {
           iterator.close();
         }
       }
       entries = entries.filter(aEntry => aEntry.isDir)
                        .map(aEntry => aEntry.path);
       return entries;
-    });
+    }
 
     let logsPath = OS.Path.join(OS.Constants.Path.profileDir, "logs");
-    let prpls = yield getAllSubdirs([logsPath]);
+    let prpls = await getAllSubdirs([logsPath]);
     let accounts =
-      yield getAllSubdirs(prpls, "Error while sweeping prpl folder:");
+      await getAllSubdirs(prpls, "Error while sweeping prpl folder:");
     let logFolders =
-      yield getAllSubdirs(accounts, "Error while sweeping account folder:");
+      await getAllSubdirs(accounts, "Error while sweeping account folder:");
     for (let folder of logFolders) {
       let iterator = new OS.File.DirectoryIterator(folder);
       try {
-        yield iterator.forEach(aEntry => {
+        await iterator.forEach(aEntry => {
           if (aEntry.isDir || !aEntry.name.endsWith(".json"))
             return null;
           return aCallback.processLog(aEntry.path);
         });
       } catch (aError) {
         // If the callback threw, reject the promise and let the caller handle it.
         if (!(aError instanceof OS.File.Error))
           throw aError;
         Cu.reportError("Error sweeping log folder:\n" + aError);
       } finally {
         iterator.close();
       }
     }
-  }),
+  },
 
   observe: function logger_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
     case "profile-after-change":
       Services.obs.addObserver(this, "final-ui-startup");
       break;
     case "final-ui-startup":
       Services.obs.removeObserver(this, "final-ui-startup");
--- a/chat/protocols/irc/ircCommands.jsm
+++ b/chat/protocols/irc/ircCommands.jsm
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This is to be exported directly onto the IRC prplIProtocol object, directly
 // implementing the commands field before we register them.
 this.EXPORTED_SYMBOLS = ["commands"];
 
 ChromeUtils.import("resource:///modules/imXPCOMUtils.jsm");
 ChromeUtils.import("resource:///modules/ircUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 
 // Shortcut to get the JavaScript conversation object.
 function getConv(aConv) { return aConv.wrappedJSObject; }
 
 // Shortcut to get the JavaScript account object.
 function getAccount(aConv) { return getConv(aConv)._account; }
 
 // Trim leading and trailing spaces and split a string by any type of space.
@@ -272,32 +271,32 @@ var commands = [
     get helpString() { return _("command.list", "list"); },
     run: function(aMsg, aConv, aReturnedConv) {
       let account = getAccount(aConv);
       let serverName = account._currentServerName;
       let serverConv = account.getConversation(serverName);
       let pendingChats = [];
       account.requestRoomInfo({onRoomInfoAvailable: function(aRooms) {
         if (!pendingChats.length) {
-          Task.spawn(function*() {
+          (async function () {
             // pendingChats has no rooms added yet, so ensure we wait a tick.
             let t = 0;
             const kMaxBlockTime = 10; // Unblock every 10ms.
             do {
               if (Date.now() > t) {
-                yield Promise.resolve();
+                await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
                 t = Date.now() + kMaxBlockTime;
               }
               let name = pendingChats.pop();
               let roomInfo = account.getRoomInfo(name);
               serverConv.writeMessage(serverName,
                 name + " (" + roomInfo.participantCount + ") " + roomInfo.topic,
                 {incoming: true, noLog: true});
             } while (pendingChats.length);
-          });
+          })();
         }
         pendingChats = pendingChats.concat(aRooms);
       }}, true);
       if (aReturnedConv)
         aReturnedConv.value = serverConv;
       return true;
     }
   },
--- a/mail/components/im/modules/index_im.jsm
+++ b/mail/components/im/modules/index_im.jsm
@@ -18,17 +18,16 @@ ChromeUtils.import("resource:///modules/
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 const {
   clearTimeout,
   setTimeout,
 } = ChromeUtils.import("resource://gre/modules/Timer.jsm", null);
 
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
-ChromeUtils.defineModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 ChromeUtils.defineModuleGetter(this, "AsyncShutdown",
                                "resource://gre/modules/AsyncShutdown.jsm");
 ChromeUtils.defineModuleGetter(this, "GlodaDatastore",
                                "resource:///modules/gloda/datastore.js");
 
 var kCacheFileName = "indexedFiles.json";
 
 var FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
@@ -383,21 +382,21 @@ var GlodaIMIndexer = {
         id: convId,
         scheduledIndex: null,
         logFileCount: null,
         convObj: {},
       };
     }
 
     let conv = this._knownConversations[convId];
-    Task.spawn(function* () {
+    (async () => {
       // We need to get the log files every time, because a new log file might
       // have been started since we last got them.
       let logFiles =
-        yield Services.logs.getLogPathsForConversation(aConversation);
+        await Services.logs.getLogPathsForConversation(aConversation);
       if (!logFiles || !logFiles.length) {
         // No log files exist yet, nothing to do!
         return;
       }
 
       if (conv.logFileCount == undefined) {
         // We initialize the _knownFiles tree path for the current files below in
         // case it doesn't already exist.
@@ -428,37 +427,37 @@ var GlodaIMIndexer = {
       // conv.logFiles.length - 1, so we slice from there. This gives us all new
       // log files even if there are multiple new ones.
       let currentLogFiles = conv.logFileCount > 1 ?
                             logFiles.slice(conv.logFileCount - 1) :
                             logFiles;
       for (let logFile of currentLogFiles) {
         let fileName = OS.Path.basename(logFile);
         let lastModifiedTime =
-          (yield OS.File.stat(logFile)).lastModificationDate.valueOf();
+          (await OS.File.stat(logFile)).lastModificationDate.valueOf();
         if (Object.prototype.hasOwnProperty.call(conv.convObj, fileName) &&
             conv.convObj[fileName] == lastModifiedTime) {
           // The file hasn't changed since we last indexed it, so we're done.
           continue;
         }
 
         if (this._indexingJobPromise)
-          yield this._indexingJobPromise;
+          await this._indexingJobPromise;
         this._indexingJobPromise = new Promise(aResolve => {
           this._indexingJobCallbacks.set(convId, aResolve);
         });
 
         let job = new IndexingJob("indexIMConversation", null);
         job.conversation = conv;
         job.path = logFile;
         job.lastModifiedTime = lastModifiedTime;
         GlodaIndexer.indexJob(job);
       }
       conv.logFileCount = logFiles.length;
-    }.bind(this)).catch(Cu.reportError);
+    })().catch(Cu.reportError);
 
     // Now clear the job, so we can index in the future.
     this._knownConversations[convId].scheduledIndex = null;
   },
 
   observe: function logger_observe(aSubject, aTopic, aData) {
     if (aTopic == "new-ui-conversation") {
       // Add ourselves to the ui-conversation's list of observers for the
@@ -559,19 +558,19 @@ var GlodaIMIndexer = {
    * conversation in the database, so the caller dealing with ongoing
    * conversation has to provide the aGlodaConv parameter, while the caller
    * dealing with old conversations doesn't care.
    * The aCache parameter is an object mapping file names to their last
    * modified times at the time they were last indexed. The value for the file
    * currently being indexed is updated to the aLastModifiedTime parameter's
    * value once indexing is complete.
    * */
-  indexIMConversation: Task.async(function* (aCallbackHandle, aLogPath, aLastModifiedTime, aCache, aGlodaConv) {
-    let log = yield Services.logs.getLogFromFile(aLogPath);
-    let logConv = yield log.getConversation();
+  async indexIMConversation(aCallbackHandle, aLogPath, aLastModifiedTime, aCache, aGlodaConv) {
+    let log = await Services.logs.getLogFromFile(aLogPath);
+    let logConv = await log.getConversation();
 
     // Ignore corrupted log files.
     if (!logConv)
       return Gloda.kWorkDone;
 
     let fileName = OS.Path.basename(aLogPath);
     let content = logConv.getMessages()
                          // Some messages returned, e.g. sessionstart messages,
@@ -591,17 +590,17 @@ var GlodaIMIndexer = {
       glodaConv._content = content;
     } else {
       let relativePath = this._getRelativePath(aLogPath);
       glodaConv = new GlodaIMConversation(logConv.title, log.time, relativePath, content);
       // If we've indexed this file before, we need the id of the existing
       // gloda conversation so that the existing entry gets updated. This can
       // happen if the log sweep detects that the last messages in an open
       // chat were not in fact indexed before that session was shut down.
-      let id = yield this._getIdFromPath(relativePath);
+      let id = await this._getIdFromPath(relativePath);
       if (id)
         glodaConv.id = id;
       if (aGlodaConv)
         aGlodaConv.value = glodaConv;
     }
 
     if (!aCache)
       throw "indexIMConversation called without aCache parameter.";
@@ -610,17 +609,17 @@ var GlodaIMIndexer = {
       Gloda.grokNounItem(glodaConv, {}, true, isNew, aCallbackHandle));
 
     if (!aLastModifiedTime)
       Cu.reportError("indexIMConversation called without lastModifiedTime parameter.");
     aCache[fileName] = aLastModifiedTime || 1;
     this._scheduleCacheSave();
 
     return rv;
-  }),
+  },
 
   * _worker_indexIMConversation(aJob, aCallbackHandle) {
     let glodaConv = {};
     let existingGlodaConv = aJob.conversation.glodaConv;
     if (existingGlodaConv &&
         existingGlodaConv.path == this._getRelativePath(aJob.path))
       glodaConv.value = aJob.conversation.glodaConv;