Bug 961665 - Native implementation of OS.File.read, js code. r=froydnj

This commit is contained in:
David Rajchenbach-Teller 2014-03-13 09:51:50 -04:00
parent c5a137c09e
commit bdd8e6a2a6
7 changed files with 165 additions and 10 deletions

View File

@ -10,6 +10,7 @@ EXTRA_JS_MODULES += [
'_PromiseWorker.jsm', '_PromiseWorker.jsm',
'osfile_async_front.jsm', 'osfile_async_front.jsm',
'osfile_async_worker.js', 'osfile_async_worker.js',
'osfile_native.jsm',
'osfile_shared_allthreads.jsm', 'osfile_shared_allthreads.jsm',
'osfile_shared_front.jsm', 'osfile_shared_front.jsm',
'osfile_unix_allthreads.jsm', 'osfile_unix_allthreads.jsm',

View File

@ -58,6 +58,7 @@ Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this); Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
Cu.import("resource://gre/modules/AsyncShutdown.jsm", this); Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
let Native = Cu.import("resource://gre/modules/osfile/osfile_native.jsm", {});
/** /**
* Constructors for decoding standard exceptions * Constructors for decoding standard exceptions
@ -348,7 +349,7 @@ const PREF_OSFILE_LOG_REDIRECT = "toolkit.osfile.log.redirect";
* @param bool oldPref * @param bool oldPref
* An optional value that the DEBUG flag was set to previously. * An optional value that the DEBUG flag was set to previously.
*/ */
let readDebugPref = function readDebugPref(prefName, oldPref = false) { function readDebugPref(prefName, oldPref = false) {
let pref = oldPref; let pref = oldPref;
try { try {
pref = Services.prefs.getBoolPref(prefName); pref = Services.prefs.getBoolPref(prefName);
@ -379,6 +380,19 @@ Services.prefs.addObserver(PREF_OSFILE_LOG_REDIRECT,
}, false); }, false);
SharedAll.Config.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, false); SharedAll.Config.TEST = readDebugPref(PREF_OSFILE_LOG_REDIRECT, false);
/**
* If |true|, use the native implementaiton of OS.File methods
* whenever possible. Otherwise, force the use of the JS version.
*/
let nativeWheneverAvailable = true;
const PREF_OSFILE_NATIVE = "toolkit.osfile.native";
Services.prefs.addObserver(PREF_OSFILE_NATIVE,
function prefObserver(aSubject, aTopic, aData) {
nativeWheneverAvailable = readDebugPref(PREF_OSFILE_NATIVE, nativeWheneverAvailable);
}, false);
// Update worker's DEBUG flag if it's true. // Update worker's DEBUG flag if it's true.
// Don't start the worker just for this, though. // Don't start the worker just for this, though.
if (SharedAll.Config.DEBUG && Scheduler.launched) { if (SharedAll.Config.DEBUG && Scheduler.launched) {
@ -913,12 +927,32 @@ File.makeDir = function makeDir(path, options) {
* read from the file. * read from the file.
*/ */
File.read = function read(path, bytes, options = {}) { File.read = function read(path, bytes, options = {}) {
if (typeof bytes == "object") {
// Passing |bytes| as an argument is deprecated.
// We should now be passing it as a field of |options|.
options = bytes || {};
} else {
options = clone(options, ["outExecutionDuration"]);
if (typeof bytes != "undefined") {
options.bytes = bytes;
}
}
if (options.compression || !nativeWheneverAvailable) {
// We need to use the JS implementation.
let promise = Scheduler.post("read", let promise = Scheduler.post("read",
[Type.path.toMsg(path), bytes, options], path); [Type.path.toMsg(path), bytes, options], path);
return promise.then( return promise.then(
function onSuccess(data) { function onSuccess(data) {
if (typeof data == "string") {
return data;
}
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
}); });
}
// Otherwise, use the native implementation.
return Scheduler.push(() => Native.read(path, options));
}; };
/** /**

View File

@ -362,6 +362,9 @@ const EXCEPTION_NAMES = {
}, },
read: function read(path, bytes, options) { read: function read(path, bytes, options) {
let data = File.read(Type.path.fromMsg(path), bytes, options); let data = File.read(Type.path.fromMsg(path), bytes, options);
if (typeof data == "string") {
return data;
}
return new Meta({ return new Meta({
buffer: data.buffer, buffer: data.buffer,
byteOffset: data.byteOffset, byteOffset: data.byteOffset,

View File

@ -0,0 +1,70 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Native (xpcom) implementation of key OS.File functions
*/
"use strict";
this.EXPORTED_SYMBOLS = ["read"];
let {results: Cr, utils: Cu, interfaces: Ci} = Components;
let SharedAll = Cu.import("resource://gre/modules/osfile/osfile_shared_allthreads.jsm", {});
let SysAll = {};
if (SharedAll.Constants.Win) {
Cu.import("resource://gre/modules/osfile/osfile_win_allthreads.jsm", SysAll);
} else if (SharedAll.Constants.libc) {
Cu.import("resource://gre/modules/osfile/osfile_unix_allthreads.jsm", SysAll);
} else {
throw new Error("I am neither under Windows nor under a Posix system");
}
let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
/**
* The native service holding the implementation of the functions.
*/
XPCOMUtils.defineLazyServiceGetter(this,
"Internals",
"@mozilla.org/toolkit/osfile/native-internals;1",
"nsINativeOSFileInternalsService");
/**
* Native implementation of OS.File.read
*
* This implementation does not handle option |compression|.
*/
this.read = function(path, options = {}) {
// Sanity check on types of options
if ("encoding" in options && typeof options.encoding != "string") {
return Promise.reject(new TypeError("Invalid type for option encoding"));
}
if ("compression" in options && typeof options.compression != "string") {
return Promise.reject(new TypeError("Invalid type for option compression"));
}
if ("bytes" in options && typeof options.bytes != "number") {
return Promise.reject(new TypeError("Invalid type for option bytes"));
}
let deferred = Promise.defer();
Internals.read(path,
options,
function onSuccess(success) {
success.QueryInterface(Ci.nsINativeOSFileResult);
if ("outExecutionDuration" in options) {
options.outExecutionDuration =
success.executionDurationMS +
(options.outExecutionDuration || 0);
}
deferred.resolve(success.result);
},
function onError(operation, oserror) {
deferred.reject(new SysAll.Error(operation, oserror, path));
}
);
return deferred.promise;
};

View File

@ -331,14 +331,35 @@ AbstractFile.read = function read(path, bytes, options = {}) {
options = bytes; options = bytes;
bytes = options.bytes || null; bytes = options.bytes || null;
} }
if ("encoding" in options && typeof options.encoding != "string") {
throw new TypeError("Invalid type for option encoding");
}
if ("compression" in options && typeof options.compression != "string") {
throw new TypeError("Invalid type for option compression: " + options.compression);
}
if ("bytes" in options && typeof options.bytes != "number") {
throw new TypeError("Invalid type for option bytes");
}
let file = exports.OS.File.open(path); let file = exports.OS.File.open(path);
try { try {
let buffer = file.read(bytes, options); let buffer = file.read(bytes, options);
if ("compression" in options && options.compression == "lz4") { if ("compression" in options) {
return Lz4.decompressFileContent(buffer, options); if (options.compression == "lz4") {
buffer = Lz4.decompressFileContent(buffer, options);
} else { } else {
throw OS.File.Error.invalidArgument("Compression");
}
}
if (!("encoding" in options)) {
return buffer; return buffer;
} }
let decoder;
try {
decoder = new TextDecoder(options.encoding);
} catch (ex if ex instanceof TypeError) {
throw OS.File.Error.invalidArgument("Decode");
}
return decoder.decode(buffer);
} finally { } finally {
file.close(); file.close();
} }

View File

@ -139,6 +139,15 @@ Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
return this.unixErrno == Const.EACCES; return this.unixErrno == Const.EACCES;
} }
}); });
/**
* |true| if the error was raised because some invalid argument was passed,
* |false| otherwise.
*/
Object.defineProperty(OSError.prototype, "becauseInvalidArgument", {
get: function becauseInvalidArgument() {
return this.unixErrno == Const.EINVAL;
}
});
/** /**
* Serialize an instance of OSError to something that can be * Serialize an instance of OSError to something that can be
@ -331,6 +340,10 @@ OSError.noSuchFile = function noSuchFile(operation, path) {
return new OSError(operation, Const.ENOENT, path); return new OSError(operation, Const.ENOENT, path);
}; };
OSError.invalidArgument = function invalidArgument(operation) {
return new OSError(operation, Const.EINVAL);
};
let EXPORTED_SYMBOLS = [ let EXPORTED_SYMBOLS = [
"declareFFI", "declareFFI",
"libc", "libc",

View File

@ -161,6 +161,15 @@ Object.defineProperty(OSError.prototype, "becauseAccessDenied", {
return this.winLastError == Const.ERROR_ACCESS_DENIED; return this.winLastError == Const.ERROR_ACCESS_DENIED;
} }
}); });
/**
* |true| if the error was raised because some invalid argument was passed,
* |false| otherwise.
*/
Object.defineProperty(OSError.prototype, "becauseInvalidArgument", {
get: function becauseInvalidArgument() {
return this.winLastError == Const.ERROR_NOT_SUPPORTED;
}
});
/** /**
* Serialize an instance of OSError to something that can be * Serialize an instance of OSError to something that can be
@ -368,6 +377,10 @@ OSError.noSuchFile = function noSuchFile(operation, path) {
return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path); return new OSError(operation, Const.ERROR_FILE_NOT_FOUND, path);
}; };
OSError.invalidArgument = function invalidArgument(operation) {
return new OSError(operation, Const.ERROR_NOT_SUPPORTED);
};
let EXPORTED_SYMBOLS = [ let EXPORTED_SYMBOLS = [
"declareFFI", "declareFFI",
"libc", "libc",