Bug 802534 - [OS.File] Main thread versions of OS.File.Info and OS.File.DirectoryIterator.Entry should have nice prototypes; r=yoric
authorEduard Neculaesi <eduardnem@gmail.com>
Thu, 10 Jan 2013 10:40:46 -0800
changeset 118449 fdfa7133f2079cb8897925d0bdc49d8f13eb59d3
parent 118448 e6c4fd2603f95b7b6a746af9150f26a2444a1d19
child 118450 5b042b636671dd61e242c8f396ad1f35b56d493f
push id24166
push userMs2ger@gmail.com
push dateFri, 11 Jan 2013 13:57:41 +0000
treeherdermozilla-central@63c4b0f66a0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyoric
bugs802534
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 802534 - [OS.File] Main thread versions of OS.File.Info and OS.File.DirectoryIterator.Entry should have nice prototypes; r=yoric
toolkit/components/osfile/osfile_async_front.jsm
toolkit/components/osfile/osfile_unix_allthreads.jsm
toolkit/components/osfile/osfile_unix_front.jsm
toolkit/components/osfile/osfile_win_allthreads.jsm
toolkit/components/osfile/osfile_win_front.jsm
toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
--- a/toolkit/components/osfile/osfile_async_front.jsm
+++ b/toolkit/components/osfile/osfile_async_front.jsm
@@ -538,16 +538,24 @@ File.writeAtomic = function writeAtomic(
  * Information on a file, as returned by OS.File.stat or
  * OS.File.prototype.stat
  *
  * @constructor
  */
 File.Info = function Info(value) {
   return value;
 };
+if (OS.Constants.Win) {
+  File.Info.prototype = Object.create(OS.Shared.Win.AbstractInfo.prototype);
+} else if (OS.Constants.libc) {
+  File.Info.prototype = Object.create(OS.Shared.Unix.AbstractInfo.prototype);
+} else {
+  throw new Error("I am neither under Windows nor under a Posix system");
+}
+
 File.Info.fromMsg = function fromMsg(value) {
   return new File.Info(value);
 };
 
 /**
  * Iterate asynchronously through a directory
  *
  * @constructor
@@ -705,16 +713,24 @@ DirectoryIterator.prototype = {
       }
     );
   }
 };
 
 DirectoryIterator.Entry = function Entry(value) {
   return value;
 };
+if (OS.Constants.Win) {
+  DirectoryIterator.Entry.prototype = Object.create(OS.Shared.Win.AbstractEntry.prototype);
+} else if (OS.Constants.libc) {
+  DirectoryIterator.Entry.prototype = Object.create(OS.Shared.Unix.AbstractEntry.prototype);
+} else {
+  throw new Error("I am neither under Windows nor under a Posix system");
+}
+
 DirectoryIterator.Entry.fromMsg = function fromMsg(value) {
   return new DirectoryIterator.Entry(value);
 };
 
 // Constants
 Object.defineProperty(File, "POS_START", {value: OS.Shared.POS_START});
 Object.defineProperty(File, "POS_CURRENT", {value: OS.Shared.POS_CURRENT});
 Object.defineProperty(File, "POS_END", {value: OS.Shared.POS_END});
--- a/toolkit/components/osfile/osfile_unix_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_unix_allthreads.jsm
@@ -155,16 +155,146 @@ if (typeof Components != "undefined") {
    * Deserialize a message back to an instance of OSError
    */
   OSError.fromMsg = function fromMsg(msg) {
     return new OSError(msg.operation, msg.unixErrno);
   };
 
   exports.OS.Shared.Unix.Error = OSError;
 
+  /**
+   * Code shared by implementations of File.Info on Unix
+   *
+   * @constructor
+  */
+  let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, lastAccessDate,
+                                           lastModificationDate, unixLastStatusChangeDate,
+                                           unixOwner, unixGroup, unixMode) {
+    this._isDir = isDir;
+    this._isSymlLink = isSymLink;
+    this._size = size;
+    this._lastAccessDate = lastAccessDate;
+    this._lastModificationDate = lastModificationDate;
+    this._unixLastStatusChangeDate = unixLastStatusChangeDate;
+    this._unixOwner = unixOwner;
+    this._unixGroup = unixGroup;
+    this._unixMode = unixMode;
+  };
+
+  AbstractInfo.prototype = {
+    /**
+     * |true| if this file is a directory, |false| otherwise
+     */
+    get isDir() {
+      return this._isDir;
+    },
+    /**
+     * |true| if this file is a symbolink link, |false| otherwise
+     */
+    get isSymLink() {
+      return this._isSymlLink;
+    },
+    /**
+     * The size of the file, in bytes.
+     *
+     * Note that the result may be |NaN| if the size of the file cannot be
+     * represented in JavaScript.
+     *
+     * @type {number}
+     */
+    get size() {
+      return this._size;
+    },
+    /**
+     * The date of last access to this file.
+     *
+     * Note that the definition of last access may depend on the
+     * underlying operating system and file system.
+     *
+     * @type {Date}
+     */
+    get lastAccessDate() {
+      return this._lastAccessDate;
+    },
+    /**
+     * Return the date of last modification of this file.
+     */
+    get lastModificationDate() {
+      return this._lastModificationDate;
+    },
+    /**
+     * Return the date at which the status of this file was last modified
+     * (this is the date of the latest write/renaming/mode change/...
+     * of the file)
+     */
+    get unixLastStatusChangeDate() {
+      return this._unixLastStatusChangeDate;
+    },
+    /*
+     * Return the Unix owner of this file
+     */
+    get unixOwner() {
+      return this._unixOwner;
+    },
+    /*
+     * Return the Unix group of this file
+     */
+    get unixGroup() {
+      return this._unixGroup;
+    },
+    /*
+     * Return the Unix group of this file
+     */
+    get unixMode() {
+      return this._unixMode;
+    }
+  };
+  exports.OS.Shared.Unix.AbstractInfo = AbstractInfo;
+
+  /**
+   * Code shared by implementations of File.DirectoryIterator.Entry on Unix
+   *
+   * @constructor
+  */
+  let AbstractEntry = function AbstractEntry(isDir, isSymLink, name, path) {
+    this._isDir = isDir;
+    this._isSymlLink = isSymLink;
+    this._name = name;
+    this._path = path;
+  };
+
+  AbstractEntry.prototype = {
+    /**
+     * |true| if the entry is a directory, |false| otherwise
+     */
+    get isDir() {
+      return this._isDir;
+    },
+    /**
+     * |true| if the entry is a directory, |false| otherwise
+     */
+    get isSymLink() {
+      return this._isSymlLink;
+    },
+    /**
+     * The name of the entry
+     * @type {string}
+     */
+    get name() {
+      return this._name;
+    },
+    /**
+     * The full path to the entry
+     */
+    get path() {
+      return this._path;
+    }
+  };
+  exports.OS.Shared.Unix.AbstractEntry = AbstractEntry;
+
   // Special constants that need to be defined on all platforms
 
    Object.defineProperty(exports.OS.Shared, "POS_START", { value: exports.OS.Constants.libc.SEEK_SET });
    Object.defineProperty(exports.OS.Shared, "POS_CURRENT", { value: exports.OS.Constants.libc.SEEK_CUR });
    Object.defineProperty(exports.OS.Shared, "POS_END", { value: exports.OS.Constants.libc.SEEK_END });
 
   // Special types that need to be defined for communication
   // between threads
--- a/toolkit/components/osfile/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/osfile_unix_front.jsm
@@ -662,53 +662,25 @@
      };
 
      /**
       * An entry in a directory.
       */
      File.DirectoryIterator.Entry = function Entry(unix_entry, parent) {
        // Copy the relevant part of |unix_entry| to ensure that
        // our data is not overwritten prematurely.
-       this._d_type = unix_entry.d_type;
-       this._name = unix_entry.d_name.readString();
+       let isDir = unix_entry.d_type == OS.Constants.libc.DT_DIR;
+       let isSymLink = unix_entry.d_type == OS.Constants.libc.DT_LNK;
+       let name = unix_entry.d_name.readString();
        this._parent = parent;
-     };
-     File.DirectoryIterator.Entry.prototype = {
-       /**
-        * |true| if the entry is a directory, |false| otherwise
-        */
-       get isDir() {
-         return this._d_type == OS.Constants.libc.DT_DIR;
-       },
-
-       /**
-        * |true| if the entry is a symbolic link, |false| otherwise
-        */
-       get isSymLink() {
-         return this._d_type == OS.Constants.libc.DT_LNK;
-       },
+       let path = OS.Unix.Path.join(this._parent, name);
 
-       /**
-        * The name of the entry.
-        * @type {string}
-        */
-       get name() {
-         return this._name;
-       },
-
-       /**
-        * The full path to the entry.
-        */
-       get path() {
-         delete this.path;
-         let path = OS.Unix.Path.join(this._parent, this.name);
-         Object.defineProperty(this, "path", {value: path});
-         return path;
-       }
+       exports.OS.Shared.Unix.AbstractEntry.call(this, isDir, isSymLink, name, path);
      };
+     File.DirectoryIterator.Entry.prototype = Object.create(exports.OS.Shared.Unix.AbstractEntry.prototype);
 
      /**
       * Return a version of an instance of
       * File.DirectoryIterator.Entry that can be sent from a worker
       * thread to the main thread. Note that deserialization is
       * asymmetric and returns an object with a different
       * implementation.
       */
@@ -724,138 +696,59 @@
        }
        return serialized;
      };
 
      let gStatData = new OS.Shared.Type.stat.implementation();
      let gStatDataPtr = gStatData.address();
      let MODE_MASK = 4095 /*= 07777*/;
      File.Info = function Info(stat) {
-       this._st_mode = stat.st_mode;
-       this._st_uid = stat.st_uid;
-       this._st_gid = stat.st_gid;
-       this._st_atime = stat.st_atime;
-       this._st_mtime = stat.st_mtime;
-       this._st_ctime = stat.st_ctime;
+       let isDir = (stat.st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFDIR;
+       let isSymLink = (stat.st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.S_IFLNK;
+       let size = exports.OS.Shared.Type.size_t.importFromC(stat.st_size);
+
+       let lastAccessDate = new Date(stat.st_atime * 1000);
+       let lastModificationDate = new Date(stat.st_mtime * 1000);
+       let unixLastStatusChangeDate = new Date(stat.st_ctime * 1000);
+
+       let unixOwner = exports.OS.Shared.Type.uid_t.importFromC(stat.st_uid);
+       let unixGroup = exports.OS.Shared.Type.gid_t.importFromC(stat.st_gid);
+       let unixMode = exports.OS.Shared.Type.mode_t.importFromC(stat.st_mode & MODE_MASK);
+
+       exports.OS.Shared.Unix.AbstractInfo.call(this, isDir, isSymLink, size, lastAccessDate,
+                                                lastModificationDate, unixLastStatusChangeDate,
+                                                unixOwner, unixGroup, unixMode);
+
        // Some platforms (e.g. MacOS X, some BSDs) store a file creation date
        if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
-         this._st_birthtime = stat.st_birthtime;
-       }
-       this._st_size = stat.st_size;
-     };
-     File.Info.prototype = {
-       /**
-        * |true| if this file is a directory, |false| otherwise
-        */
-       get isDir() {
-         return (this._st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFDIR;
-       },
-       /**
-        * |true| if this file is a symbolink link, |false| otherwise
-        */
-       get isSymLink() {
-         return (this._st_mode & OS.Constants.libc.S_IFMT) == OS.Constants.libc.S_IFLNK;
-       },
-       /**
-        * The size of the file, in bytes.
-        *
-        * Note that the result may be |NaN| if the size of the file cannot be
-        * represented in JavaScript.
-        *
-        * @type {number}
-        */
-       get size() {
-         return exports.OS.Shared.Type.size_t.importFromC(this._st_size);
-       },
-       // Deprecated, use macBirthDate/winBirthDate instead
-       get creationDate() {
-         // On the Macintosh, returns the birth date if available.
-         // On other Unix, as the birth date is not available,
-         // returns the epoch.
-         return this.macBirthDate || new Date(0);
-       },
-       /**
-        * The date of last access to this file.
-        *
-        * Note that the definition of last access may depend on the
-        * underlying operating system and file system.
-        *
-        * @type {Date}
-        */
-       get lastAccessDate() {
-         delete this.lastAccessDate;
-         let date = new Date(this._st_atime * 1000);
-         Object.defineProperty(this, "lastAccessDate", {value: date});
-         return date;
-       },
-       /**
-        * Return the date of last modification of this file.
-        */
-       get lastModificationDate() {
-         delete this.lastModificationDate;
-         let date = new Date(this._st_mtime * 1000);
-         Object.defineProperty(this, "lastModificationDate", {value: date});
-         return date;
-       },
-       /**
-        * Return the date at which the status of this file was last modified
-        * (this is the date of the latest write/renaming/mode change/...
-        * of the file)
-        */
-       get unixLastStatusChangeDate() {
-         delete this.unixLastStatusChangeDate;
-         let date = new Date(this._st_ctime * 1000);
-         Object.defineProperty(this, "unixLastStatusChangeDate", {value: date});
-         return date;
-       },
-       /**
-        * Return the Unix owner of this file.
-        */
-       get unixOwner() {
-         return exports.OS.Shared.Type.uid_t.importFromC(this._st_uid);
-       },
-       /**
-        * Return the Unix group of this file.
-        */
-       get unixGroup() {
-         return exports.OS.Shared.Type.gid_t.importFromC(this._st_gid);
-       },
-       /**
-        * Return the Unix mode of this file.
-        */
-       get unixMode() {
-         return exports.OS.Shared.Type.mode_t.importFromC(this._st_mode & MODE_MASK);
+         let date = new Date(stat.st_birthtime * 1000);
+
+        /**
+         * The date of creation of this file.
+         *
+         * Note that the date returned by this method is not always
+         * reliable. Not all file systems are able to provide this
+         * information.
+         *
+         * @type {Date}
+         */
+         this.macBirthDate = date;
        }
      };
+     File.Info.prototype = Object.create(exports.OS.Shared.Unix.AbstractInfo.prototype);
 
-    /**
-     * The date of creation of this file.
-     *
-     * Note that the date returned by this method is not always
-     * reliable. Not all file systems are able to provide this
-     * information.
-     *
-     * @type {Date}
-     */
-     if ("OSFILE_OFFSETOF_STAT_ST_BIRTHTIME" in OS.Constants.libc) {
-       Object.defineProperty(
-         File.Info.prototype,
-         "macBirthDate",
-         {
-           get: function macBirthDate() {
-             delete this.macBirthDate;
-             let time;
-             time = this._st_birthtime;
-             let date = new Date(time * 1000);
-             Object.defineProperty(this, "macBirthDate", { value: date });
-             return date;
-           }
-         }
-       );
-     }
+     // Deprecated, use macBirthDate/winBirthDate instead
+     Object.defineProperty(File.Info.prototype, "creationDate", {
+      get: function creationDate() {
+        // On the Macintosh, returns the birth date if available.
+        // On other Unix, as the birth date is not available,
+        // returns the epoch.
+        return this.macBirthDate || new Date(0);
+      }
+     });
 
      /**
       * Return a version of an instance of File.Info that can be sent
       * from a worker thread to the main thread. Note that deserialization
       * is asymmetric and returns an object with a different implementation.
       */
      File.Info.toMsg = function toMsg(stat) {
        if (!stat instanceof File.Info) {
--- a/toolkit/components/osfile/osfile_win_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_win_allthreads.jsm
@@ -163,16 +163,160 @@ if (typeof Components != "undefined") {
    * Deserialize a message back to an instance of OSError
    */
   OSError.fromMsg = function fromMsg(msg) {
     return new OSError(msg.operation, msg.winLastError);
   };
 
   exports.OS.Shared.Win.Error = OSError;
 
+  /**
+   * Code shared by implementation of File.Info on Windows
+   *
+   * @constructor
+   */
+  let AbstractInfo = function AbstractInfo(isDir, isSymLink, size, winBirthDate,
+                                           lastAccessDate) {
+    this._isDir = isDir;
+    this._isSymLink = isSymLink;
+    this._size = size;
+    this._winBirthDate = winBirthDate;
+    this._lastAccessDate = lastAccessDate;
+    this._lastModificationDate = lastAccessDate;
+  };
+
+  AbstractInfo.prototype = {
+    /**
+     * |true| if this file is a directory, |false| otherwise
+     */
+    get isDir() {
+      return this._isDir;
+    },
+    /**
+     * |true| if this file is a symbolic link, |false| otherwise
+     */
+    get isSymLink() {
+      return this._isSymLink;
+    },
+    /**
+     * The size of the file, in bytes.
+     *
+     * Note that the result may be |NaN| if the size of the file cannot be
+     * represented in JavaScript.
+     *
+     * @type {number}
+     */
+    get size() {
+      return this._size;
+    },
+    // Deprecated
+    get creationDate() {
+      return this._winBirthDate;
+    },
+    /**
+     * The date of creation of this file.
+     *
+     * @type {Date}
+     */
+    get winBirthDate() {
+      return this._winBirthDate;
+    },
+    /**
+     * The date of last access to this file.
+     *
+     * Note that the definition of last access may depend on the underlying
+     * operating system and file system.
+     *
+     * @type {Date}
+     */
+    get lastAccessDate() {
+      return this._lastAccessDate;
+    },
+    /**
+     * The date of last modification of this file.
+     *
+     * Note that the definition of last access may depend on the underlying
+     * operating system and file system.
+     *
+     * @type {Date}
+     */
+    get lastModificationDate() {
+      return this._lastModificationDate;
+    }
+  };
+  exports.OS.Shared.Win.AbstractInfo = AbstractInfo;
+
+  /**
+   * Code shared by implementation of File.DirectoryIterator.Entry on Windows
+   *
+   * @constructor
+   */
+  let AbstractEntry = function AbstractEntry(isDir, isSymLink, name,
+                                             winCreationDate, winLastWriteDate,
+                                             winLastAccessDate, path) {
+    this._isDir = isDir;
+    this._isSymLink = isSymLink;
+    this._name = name;
+    this._winCreationDate = winCreationDate;
+    this._winLastWriteDate = winLastWriteDate;
+    this._winLastAccessDate = winLastAccessDate;
+    this._path = path;
+  };
+
+  AbstractEntry.prototype = {
+    /**
+     * |true| if the entry is a directory, |false| otherwise
+     */
+    get isDir() {
+      return this._isDir;
+    },
+    /**
+     * |true| if the entry is a symbolic link, |false| otherwise
+     */
+    get isSymLink() {
+      return this._isSymLink;
+    },
+    /**
+     * The name of the entry.
+     * @type {string}
+     */
+    get name() {
+      return this._name;
+    },
+    /**
+     * The creation time of this file.
+     * @type {Date}
+     */
+    get winCreationDate() {
+      return this._winCreationDate;
+    },
+    /**
+     * The last modification time of this file.
+     * @type {Date}
+     */
+    get winLastWriteDate() {
+      return this._winLastWriteDate;
+    },
+    /**
+     * The last access time of this file.
+     * @type {Date}
+     */
+    get winLastAccessDate() {
+      return this._winLastAccessDate;
+    },
+    /**
+     * The full path of the entry
+     * @type {string}
+     */
+    get path() {
+      return this._path;
+    }
+  };
+  exports.OS.Shared.Win.AbstractEntry = AbstractEntry;
+
   // Special constants that need to be defined on all platforms
 
   Object.defineProperty(exports.OS.Shared, "POS_START", { value: exports.OS.Constants.Win.FILE_BEGIN });
   Object.defineProperty(exports.OS.Shared, "POS_CURRENT", { value: exports.OS.Constants.Win.FILE_CURRENT });
   Object.defineProperty(exports.OS.Shared, "POS_END", { value: exports.OS.Constants.Win.FILE_END });
 
   // Special types that need to be defined for communication
   // between threads
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -592,104 +592,46 @@
          // before being used.
          throw_on_zero("FindClose",
            WinFile.FindClose(this._handle));
          this._handle = null;
        }
      };
 
      File.DirectoryIterator.Entry = function Entry(win_entry, parent) {
+       if (!win_entry.dwFileAttributes || !win_entry.ftCreationTime ||
+           !win_entry.ftLastAccessTime || !win_entry.ftLastWriteTime)
+        throw new TypeError();
+
        // Copy the relevant part of |win_entry| to ensure that
        // our data is not overwritten prematurely.
-       if (!win_entry.dwFileAttributes) {
-         throw new TypeError();
-       }
-       this._dwFileAttributes = win_entry.dwFileAttributes;
-       this._name = win_entry.cFileName.readString();
-       if (!this._name) {
+       let isDir = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
+       let isSymLink = !!(win_entry.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
+
+       let winCreationDate = FILETIME_to_Date(win_entry.ftCreationTime);
+       let winLastWriteDate = FILETIME_to_Date(win_entry.ftLastWriteTime);
+       let winLastAccessDate = FILETIME_to_Date(win_entry.ftLastAccessTime);
+
+       let name = win_entry.cFileName.readString();
+       if (!name) {
          throw new TypeError("Empty name");
        }
-       this._ftCreationTime = win_entry.ftCreationTime;
-       if (!win_entry.ftCreationTime) {
-         throw new TypeError();
-       }
-       this._ftLastAccessTime = win_entry.ftLastAccessTime;
-       if (!win_entry.ftLastAccessTime) {
-         throw new TypeError();
-       }
-       this._ftLastWriteTime = win_entry.ftLastWriteTime;
-       if (!win_entry.ftLastWriteTime) {
-         throw new TypeError();
-       }
+
        if (!parent) {
          throw new TypeError("Empty parent");
        }
        this._parent = parent;
+
+       let path = OS.Win.Path.join(this._parent, name);
+
+       exports.OS.Shared.Win.AbstractEntry.call(this, isDir, isSymLink, name,
+                                                winCreationDate, winLastWriteDate,
+                                                winLastAccessDate, path);
      };
-     File.DirectoryIterator.Entry.prototype = {
-       /**
-        * |true| if the entry is a directory, |false| otherwise
-        */
-       get isDir() {
-         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
-       },
-       /**
-        * |true| if the entry is a symbolic link, |false| otherwise
-        */
-       get isSymLink() {
-         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
-       },
-       /**
-        * The name of the entry.
-        * @type {string}
-        */
-       get name() {
-         return this._name;
-       },
-       /**
-        * The creation time of this file.
-        * @type {Date}
-        */
-       get winCreationDate() {
-         let date = FILETIME_to_Date(this._ftCreationTime);
-         delete this.winCreationDate;
-         Object.defineProperty(this, "winCreationDate", {value: date});
-         return date;
-       },
-       /**
-        * The last modification time of this file.
-        * @type {Date}
-        */
-       get winLastWriteDate() {
-         let date = FILETIME_to_Date(this._ftLastWriteTime);
-         delete this.winLastWriteDate;
-         Object.defineProperty(this, "winLastWriteDate", {value: date});
-         return date;
-       },
-       /**
-        * The last access time of this file.
-        * @type {Date}
-        */
-       get winLastAccessDate() {
-         let date = FILETIME_to_Date(this._ftLastAccessTime);
-         delete this.winLastAccessDate;
-         Object.defineProperty(this, "winLastAccessDate", {value: date});
-         return date;
-       },
-       /**
-        * The full path to the entry.
-        * @type {string}
-        */
-       get path() {
-         delete this.path;
-         let path = OS.Win.Path.join(this._parent, this.name);
-         Object.defineProperty(this, "path", {value: path});
-         return path;
-       }
-     };
+     File.DirectoryIterator.Entry.prototype = Object.create(exports.OS.Shared.Win.AbstractEntry.prototype);
 
      /**
       * Return a version of an instance of
       * File.DirectoryIterator.Entry that can be sent from a worker
       * thread to the main thread. Note that deserialization is
       * asymmetric and returns an object with a different
       * implementation.
       */
@@ -712,92 +654,29 @@
       *
       * To obtain the latest information on a file, use |File.stat|
       * (for an unopened file) or |File.prototype.stat| (for an
       * already opened file).
       *
       * @constructor
       */
      File.Info = function Info(stat) {
-       this._dwFileAttributes = stat.dwFileAttributes;
-       this._ftCreationTime = stat.ftCreationTime;
-       this._ftLastAccessTime = stat.ftLastAccessTime;
-       this._ftLastWriteTime = stat.ftLastAccessTime;
-       this._nFileSizeHigh = stat.nFileSizeHigh;
-       this._nFileSizeLow = stat.nFileSizeLow;
+       let isDir = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
+       let isSymLink = !!(stat.dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
+       
+       let winBirthDate = FILETIME_to_Date(stat.ftCreationTime);
+       let lastAccessDate = FILETIME_to_Date(stat.ftLastAccessTime);
+
+       let value = ctypes.UInt64.join(stat.nFileSizeHigh, stat.nFileSizeLow);
+       let size = exports.OS.Shared.Type.uint64_t.importFromC(value);
+
+       exports.OS.Shared.Win.AbstractInfo.call(this, isDir, isSymLink, size,
+                                               winBirthDate, lastAccessDate);
      };
-     File.Info.prototype = {
-       /**
-        * |true| if this file is a directory, |false| otherwise
-        */
-       get isDir() {
-         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_DIRECTORY);
-       },
-       /**
-        * |true| if this file is a symbolink link, |false| otherwise
-        */
-       get isSymLink() {
-         return !!(this._dwFileAttributes & Const.FILE_ATTRIBUTE_REPARSE_POINT);
-       },
-       /**
-        * The size of the file, in bytes.
-        *
-        * Note that the result may be |NaN| if the size of the file cannot be
-        * represented in JavaScript.
-        *
-        * @type {number}
-        */
-       get size() {
-         let value = ctypes.UInt64.join(this._nFileSizeHigh, this._nFileSizeLow);
-         return exports.OS.Shared.Type.uint64_t.importFromC(value);
-       },
-       // Deprecated
-       get creationDate() {
-         return this.winBirthDate;
-       },
-       /**
-        * The date of creation of this file.
-        *
-        * @type {Date}
-        */
-       get winBirthDate() {
-         delete this.winBirthDate;
-         let date = FILETIME_to_Date(this._ftCreationTime);
-         Object.defineProperty(this, "winBirthDate", { value: date });
-         return date;
-       },
-       /**
-        * The date of last access to this file.
-        *
-        * Note that the definition of last access may depend on the
-        * underlying operating system and file system.
-        *
-        * @type {Date}
-        */
-       get lastAccessDate() {
-         delete this.lastAccess;
-         let date = FILETIME_to_Date(this._ftLastAccessTime);
-         Object.defineProperty(this, "lastAccessDate", { value: date });
-         return date;
-       },
-       /**
-        * Return the date of last modification of this file.
-        *
-        * Note that the definition of last access may depend on the
-        * underlying operating system and file system.
-        *
-        * @type {Date}
-        */
-       get lastModificationDate() {
-         delete this.lastModification;
-         let date = FILETIME_to_Date(this._ftLastWriteTime);
-         Object.defineProperty(this, "lastModificationDate", { value: date });
-         return date;
-       }
-     };
+     File.Info.prototype = Object.create(exports.OS.Shared.Win.AbstractInfo.prototype);
 
      /**
       * Return a version of an instance of File.Info that can be sent
       * from a worker thread to the main thread. Note that deserialization
       * is asymmetric and returns an object with a different implementation.
       */
      File.Info.toMsg = function toMsg(stat) {
        if (!stat instanceof File.Info) {
--- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
+++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
@@ -126,16 +126,17 @@ let reference_compare_files = function r
 
 let test = maketest("Main", function main(test) {
   return Task.spawn(function() {
     SimpleTest.waitForExplicitFinish();
     yield test_constants();
     yield test_path();
     yield test_open();
     yield test_stat();
+    yield test_info_features_detect();
     yield test_read_write();
     yield test_read_write_all();
     yield test_position();
     yield test_copy();
     yield test_mkdir();
     yield test_iter();
     yield test_exists();
     info("Test is over");
@@ -243,16 +244,39 @@ let test_stat = maketest("stat", functio
     test.ok(stat2, "stat 2 is not empty");
     for (let key in stat2) {
       test.is("" + stat1[key], "" + stat2[key], "Stat field " + key + "is the same");
     }
   });
 });
 
 /**
+ * Test feature detection using OS.File.Info.prototype on main thread
+ */
+let test_info_features_detect = maketest("features_detect", function features_detect(test) {
+  return Task.spawn(function() {
+    if (OS.Constants.Win) {
+      // see if winBirthDate is defined
+      if ("winBirthDate" in OS.File.Info.prototype) {
+        test.ok(true, "winBirthDate is defined");
+      } else {
+        test.fail("winBirthDate not defined though we are under Windows");
+      }
+    } else if (OS.Constants.libc) {
+      // see if unixGroup is defined
+      if ("unixGroup" in OS.File.Info.prototype) {
+        test.ok(true, "unixGroup is defined");
+      } else {
+        test.fail("unixGroup is not defined though we are under Unix");
+      }
+    }
+  });
+});
+
+/**
  * Test OS.File.prototype.{read, readTo, write}
  */
 let test_read_write = maketest("read_write", function read_write(test) {
   return Task.spawn(function() {
     // Test readTo/write
     let currentDir = yield OS.File.getCurrentDirectory();
     let pathSource = OS.Path.join(currentDir, EXISTING_FILE);
     let pathDest = OS.Path.join(OS.Constants.Path.tmpDir,