From 3414eab2f3ec626be090e34cb7bf44ea03d38c55 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Sun, 6 Dec 2009 10:50:03 +0100 Subject: [PATCH] Refactor node_file.cc to not use Promises. At the same time implement synchronous wrappers of the POSIX functions. These will be undocumented until we settle on an API. Works like this // returns promise as before posix.mkdir("test").addCallback(function () { sys.puts("done"); }); // returns undefined, executed synchronously. posix.mkdirSync("test"); sys.puts("done"); This refactoring is a step towards allowing promises to be implemented purely in javascript. --- lib/posix.js | 37 ----- src/node.js | 235 ++++++++++++++++++++++++----- src/node_file.cc | 377 ++++++++++++++++++++++++++++------------------- src/node_file.h | 110 -------------- 4 files changed, 426 insertions(+), 333 deletions(-) delete mode 100644 lib/posix.js diff --git a/lib/posix.js b/lib/posix.js deleted file mode 100644 index 989532a560e..00000000000 --- a/lib/posix.js +++ /dev/null @@ -1,37 +0,0 @@ -process.Stats.prototype._checkModeProperty = function (property) { - return ((this.mode & property) === property); -}; - -process.Stats.prototype.isDirectory = function () { - return this._checkModeProperty(process.S_IFDIR); -}; - -process.Stats.prototype.isFile = function () { - return this._checkModeProperty(process.S_IFREG); -}; - -process.Stats.prototype.isBlockDevice = function () { - return this._checkModeProperty(process.S_IFBLK); -}; - -process.Stats.prototype.isCharacterDevice = function () { - return this._checkModeProperty(process.S_IFCHR); -}; - -process.Stats.prototype.isSymbolicLink = function () { - return this._checkModeProperty(process.S_IFLNK); -}; - -process.Stats.prototype.isFIFO = function () { - return this._checkModeProperty(process.S_IFIFO); -}; - -process.Stats.prototype.isSocket = function () { - return this._checkModeProperty(process.S_IFSOCK); -}; - -exports.Stats = process.Stats; - -for (var key in process.fs) { - if (process.fs.hasOwnProperty(key)) exports[key] = process.fs[key]; -} diff --git a/src/node.js b/src/node.js index 73645f0a945..1bdd65b5bfd 100644 --- a/src/node.js +++ b/src/node.js @@ -92,40 +92,6 @@ process.createChildProcess = function (file, args, env) { return child; }; -process.fs.cat = function (path, encoding) { - var promise = new process.Promise(); - - encoding = encoding || "utf8"; // default to utf8 - - process.fs.open(path, process.O_RDONLY, 0666).addCallback(function (fd) { - var content = "", pos = 0; - - function readChunk () { - process.fs.read(fd, 16*1024, pos, encoding).addCallback(function (chunk, bytes_read) { - if (chunk) { - if (chunk.constructor === String) { - content += chunk; - } else { - content = content.concat(chunk); - } - - pos += bytes_read; - readChunk(); - } else { - promise.emitSuccess(content); - process.fs.close(fd); - } - }).addErrback(function () { - promise.emitError(); - }); - } - readChunk(); - }).addErrback(function () { - promise.emitError(new Error("Could not open " + path)); - }); - return promise; -}; - process.assert = function (x, msg) { if (!(x)) throw new Error(msg || "assertion error"); }; @@ -374,6 +340,38 @@ process.unwatchFile = function (filename) { } }; +process.Stats.prototype._checkModeProperty = function (property) { + return ((this.mode & property) === property); +}; + +process.Stats.prototype.isDirectory = function () { + return this._checkModeProperty(process.S_IFDIR); +}; + +process.Stats.prototype.isFile = function () { + return this._checkModeProperty(process.S_IFREG); +}; + +process.Stats.prototype.isBlockDevice = function () { + return this._checkModeProperty(process.S_IFBLK); +}; + +process.Stats.prototype.isCharacterDevice = function () { + return this._checkModeProperty(process.S_IFCHR); +}; + +process.Stats.prototype.isSymbolicLink = function () { + return this._checkModeProperty(process.S_IFLNK); +}; + +process.Stats.prototype.isFIFO = function () { + return this._checkModeProperty(process.S_IFIFO); +}; + +process.Stats.prototype.isSocket = function () { + return this._checkModeProperty(process.S_IFSOCK); +}; + // Timers @@ -446,6 +444,171 @@ function createInternalModule (id, constructor) { return m; }; +var posixModule = createInternalModule("posix", function (exports) { + exports.Stats = process.Stats; + + function callback (promise) { + return function () { + if (arguments[0] instanceof Error) { + promise.emitError.apply(promise, arguments); + } else { + promise.emitSuccess.apply(promise, arguments); + } + } + } + + // Yes, the follow could be easily DRYed up but I provide the explicit + // list to make the arguments clear. + + exports.close = function (fd) { + var promise = new process.Promise() + process.fs.close(fd, callback(promise)); + return promise; + }; + + exports.closeSync = function (fd) { + return process.fs.close(fd); + }; + + exports.open = function (path, flags, mode) { + var promise = new process.Promise() + process.fs.open(path, flags, mode, callback(promise)); + return promise; + }; + + exports.openSync = function (path, flags, mode) { + return process.fs.open(path, flags, mode); + }; + + exports.read = function (fd, length, position, encoding) { + var promise = new process.Promise() + process.fs.read(fd, length, position, encoding, callback(promise)); + return promise; + }; + + exports.readSync = function (fd, length, position, encoding) { + return process.fs.read(fd, length, position, encoding); + }; + + exports.write = function (fd, data, position, encoding) { + var promise = new process.Promise() + process.fs.write(fd, data, position, encoding, callback(promise)); + return promise; + }; + + exports.writeSync = function (fd, data, position, encoding) { + return process.fs.write(fd, data, position, encoding); + }; + + exports.rename = function (oldPath, newPath) { + var promise = new process.Promise() + process.fs.rename(oldPath, newPath, callback(promise)); + return promise; + }; + + exports.renameSync = function (oldPath, newPath) { + return process.fs.rename(oldPath, newPath); + }; + + exports.rmdir = function (path) { + var promise = new process.Promise() + process.fs.rmdir(path, callback(promise)); + return promise; + }; + + exports.rmdirSync = function (path) { + return process.fs.rmdir(path); + }; + + exports.mkdir = function (path, mode) { + var promise = new process.Promise() + process.fs.mkdir(path, mode, callback(promise)); + return promise; + }; + + exports.mkdirSync = function (path, mode) { + return process.fs.mkdir(path, mode); + }; + + exports.sendfile = function (outFd, inFd, inOffset, length) { + var promise = new process.Promise() + process.fs.sendfile(outFd, inFd, inOffset, length, callback(promise)); + return promise; + }; + + exports.sendfileSync = function (outFd, inFd, inOffset, length) { + return process.fs.sendfile(outFd, inFd, inOffset, length); + }; + + exports.readdir = function (path) { + var promise = new process.Promise() + process.fs.readdir(path, callback(promise)); + return promise; + }; + + exports.readdirSync = function (path) { + return process.fs.readdir(path); + }; + + exports.stat = function (path) { + var promise = new process.Promise() + process.fs.stat(path, callback(promise)); + return promise; + }; + + exports.statSync = function (path) { + return process.fs.stat(path); + }; + + exports.unlink = function (path) { + var promise = new process.Promise() + process.fs.unlink(path, callback(promise)); + return promise; + }; + + exports.unlinkSync = function (path) { + return process.fs.unlink(path); + }; + + + exports.cat = function (path, encoding) { + var promise = new process.Promise(); + + encoding = encoding || "utf8"; // default to utf8 + + exports.open(path, process.O_RDONLY, 0666).addCallback(function (fd) { + var content = "", pos = 0; + + function readChunk () { + exports.read(fd, 16*1024, pos, encoding).addCallback(function (chunk, bytes_read) { + if (chunk) { + if (chunk.constructor === String) { + content += chunk; + } else { + content = content.concat(chunk); + } + + pos += bytes_read; + readChunk(); + } else { + promise.emitSuccess(content); + exports.close(fd); + } + }).addErrback(function () { + promise.emitError.call(arguments); + }); + } + readChunk(); + }).addErrback(function () { + promise.emitError.apply(promise, arguments); + }); + return promise; + }; +}); + +var posix = posixModule.exports; + + var pathModule = createInternalModule("path", function (exports) { exports.join = function () { var joined = ""; @@ -481,7 +644,7 @@ var pathModule = createInternalModule("path", function (exports) { }; exports.exists = function (path, callback) { - var p = process.fs.stat(path); + var p = posix.stat(path); p.addCallback(function () { callback(true); }); p.addErrback(function () { callback(false); }); }; @@ -637,7 +800,7 @@ function cat (id, loadPromise) { loadPromise.emitError(new Error("could not load core module \"http\"")); }); } else { - promise = process.fs.cat(id); + promise = posix.cat(id); } return promise; diff --git a/src/node_file.cc b/src/node_file.cc index efe3ab0fb45..3772ea4fc17 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -8,212 +8,258 @@ #include #include #include +#include namespace node { using namespace v8; -#define BAD_ARGUMENTS Exception::TypeError(String::New("Bad argument")) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define THROW_BAD_ARGS \ + ThrowException(Exception::TypeError(String::New("Bad argument"))) +#define ENCODING String::NewSymbol("node:encoding") -void EIOPromise::Attach(void) { - ev_ref(EV_DEFAULT_UC); - Promise::Attach(); +static inline Local errno_exception(int errorno) { + Local e = Exception::Error(String::NewSymbol(strerror(errorno))); + Local obj = e->ToObject(); + obj->Set(String::NewSymbol("errno"), Integer::New(errorno)); + return e; } -void EIOPromise::Detach(void) { - Promise::Detach(); +static int After(eio_req *req) { + HandleScope scope; + + Persistent *callback = + reinterpret_cast*>(req->data); + assert((*callback)->IsFunction()); + ev_unref(EV_DEFAULT_UC); -} - -Handle EIOPromise::New(const v8::Arguments& args) { - HandleScope scope; - - EIOPromise *promise = new EIOPromise(); - promise->Wrap(args.This()); - - promise->Attach(); - - return args.This(); -} - -EIOPromise* EIOPromise::Create() { - HandleScope scope; - - Local handle = - EIOPromise::constructor_template->GetFunction()->NewInstance(); - - return ObjectWrap::Unwrap(handle); -} - - -int EIOPromise::After(eio_req *req) { - HandleScope scope; - - EIOPromise *promise = reinterpret_cast(req->data); - assert(req == promise->req_); - - if (req->errorno != 0) { - Local exception = Exception::Error( - String::NewSymbol(strerror(req->errorno))); - promise->EmitError(1, &exception); - if (req->type == EIO_WRITE) { - assert(req->ptr2); - delete [] req->ptr2; - } - return 0; - } int argc = 0; Local argv[5]; // 5 is the maximum number of args - switch (req->type) { - case EIO_CLOSE: - case EIO_RENAME: - case EIO_UNLINK: - case EIO_RMDIR: - case EIO_MKDIR: - argc = 0; - break; + if (req->errorno != 0) { + argc = 1; + argv[0] = errno_exception(req->errorno); + } else { + switch (req->type) { + case EIO_CLOSE: + case EIO_RENAME: + case EIO_UNLINK: + case EIO_RMDIR: + case EIO_MKDIR: + argc = 0; + break; - case EIO_OPEN: - case EIO_SENDFILE: - argc = 1; - argv[0] = Integer::New(req->result); - break; + case EIO_OPEN: + case EIO_SENDFILE: + argc = 1; + argv[0] = Integer::New(req->result); + break; - case EIO_WRITE: - argc = 1; - argv[0] = Integer::New(req->result); - assert(req->ptr2); - delete [] req->ptr2; - break; + case EIO_WRITE: + argc = 1; + argv[0] = Integer::New(req->result); + break; - case EIO_STAT: - { - struct stat *s = reinterpret_cast(req->ptr2); - argc = 1; - argv[0] = BuildStatsObject(s); - break; - } - - case EIO_READ: - { - argc = 2; - argv[0] = Encode(req->ptr2, req->result, promise->encoding_); - argv[1] = Integer::New(req->result); - break; - } - - case EIO_READDIR: - { - char *namebuf = static_cast(req->ptr2); - int nnames = req->result; - - Local names = Array::New(nnames); - - for (int i = 0; i < nnames; i++) { - Local name = String::New(namebuf); - names->Set(Integer::New(i), name); -#ifndef NDEBUG - namebuf += strlen(namebuf); - assert(*namebuf == '\0'); - namebuf += 1; -#else - namebuf += strlen(namebuf) + 1; -#endif + case EIO_STAT: + { + struct stat *s = reinterpret_cast(req->ptr2); + argc = 1; + argv[0] = BuildStatsObject(s); + break; } - argc = 1; - argv[0] = names; - break; - } + case EIO_READ: + { + argc = 2; + Local obj = Local::New(*callback); + Local enc_val = obj->GetHiddenValue(ENCODING); + argv[0] = Encode(req->ptr2, req->result, ParseEncoding(enc_val)); + argv[1] = Integer::New(req->result); + break; + } - default: - assert(0 && "Unhandled eio response"); + case EIO_READDIR: + { + char *namebuf = static_cast(req->ptr2); + int nnames = req->result; + + Local names = Array::New(nnames); + + for (int i = 0; i < nnames; i++) { + Local name = String::New(namebuf); + names->Set(Integer::New(i), name); +#ifndef NDEBUG + namebuf += strlen(namebuf); + assert(*namebuf == '\0'); + namebuf += 1; +#else + namebuf += strlen(namebuf) + 1; +#endif + } + + argc = 1; + argv[0] = names; + break; + } + + default: + assert(0 && "Unhandled eio response"); + } } - promise->EmitSuccess(argc, argv); + if (req->type == EIO_WRITE) { + assert(req->ptr2); + delete [] reinterpret_cast(req->ptr2); + } + + TryCatch try_catch; + + (*callback)->Call(Context::GetCurrent()->Global(), argc, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + // Dispose of the persistent handle + callback->Dispose(); + delete callback; return 0; } +static Persistent* persistent_callback(const Local &v) { + Persistent *fn = new Persistent(); + *fn = Persistent::New(Local::Cast(v)); + return fn; +} + +#define ASYNC_CALL(func, callback, ...) \ + eio_req *req = eio_##func(__VA_ARGS__, EIO_PRI_DEFAULT, After, \ + persistent_callback(callback)); \ + assert(req); \ + ev_ref(EV_DEFAULT_UC); \ + return Undefined(); + static Handle Close(const Arguments& args) { HandleScope scope; if (args.Length() < 1 || !args[0]->IsInt32()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } int fd = args[0]->Int32Value(); - return scope.Close(EIOPromise::Close(fd)); + if (args[1]->IsFunction()) { + ASYNC_CALL(close, args[1], fd) + } else { + int ret = close(fd); + if (ret != 0) return ThrowException(errno_exception(errno)); + } } static Handle Stat(const Arguments& args) { HandleScope scope; if (args.Length() < 1 || !args[0]->IsString()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } String::Utf8Value path(args[0]->ToString()); - return scope.Close(EIOPromise::Stat(*path)); + if (args[1]->IsFunction()) { + ASYNC_CALL(stat, args[1], *path) + } else { + struct stat *s; + int ret = stat(*path, s); + if (ret != 0) return ThrowException(errno_exception(errno)); + return scope.Close(BuildStatsObject(s)); + } } static Handle Rename(const Arguments& args) { HandleScope scope; if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } - String::Utf8Value path(args[0]->ToString()); + String::Utf8Value old_path(args[0]->ToString()); String::Utf8Value new_path(args[1]->ToString()); - return scope.Close(EIOPromise::Rename(*path, *new_path)); + if (args[2]->IsFunction()) { + ASYNC_CALL(rename, args[2], *old_path, *new_path) + } else { + int ret = rename(*old_path, *new_path); + if (ret != 0) return ThrowException(errno_exception(errno)); + return Undefined(); + } } static Handle Unlink(const Arguments& args) { HandleScope scope; if (args.Length() < 1 || !args[0]->IsString()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } String::Utf8Value path(args[0]->ToString()); - return scope.Close(EIOPromise::Unlink(*path)); + + if (args[1]->IsFunction()) { + ASYNC_CALL(unlink, args[1], *path) + } else { + int ret = unlink(*path); + if (ret != 0) return ThrowException(errno_exception(errno)); + return Undefined(); + } } static Handle RMDir(const Arguments& args) { HandleScope scope; if (args.Length() < 1 || !args[0]->IsString()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } String::Utf8Value path(args[0]->ToString()); - return scope.Close(EIOPromise::RMDir(*path)); + + if (args[1]->IsFunction()) { + ASYNC_CALL(rmdir, args[1], *path) + } else { + int ret = rmdir(*path); + if (ret != 0) return ThrowException(errno_exception(errno)); + return Undefined(); + } } static Handle MKDir(const Arguments& args) { HandleScope scope; if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsInt32()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } String::Utf8Value path(args[0]->ToString()); mode_t mode = static_cast(args[1]->Int32Value()); - return scope.Close(EIOPromise::MKDir(*path, mode)); + if (args[2]->IsFunction()) { + ASYNC_CALL(mkdir, args[2], *path, mode) + } else { + int ret = mkdir(*path, mode); + if (ret != 0) return ThrowException(errno_exception(errno)); + return Undefined(); + } } static Handle SendFile(const Arguments& args) { HandleScope scope; - if (args.Length() < 4 || !args[0]->IsInt32() || !args[1]->IsInt32() || !args[3]->IsNumber()) { - return ThrowException(BAD_ARGUMENTS); + if (args.Length() < 4 || + !args[0]->IsInt32() || + !args[1]->IsInt32() || + !args[3]->IsNumber()) { + return THROW_BAD_ARGS; } int out_fd = args[0]->Int32Value(); @@ -221,37 +267,58 @@ static Handle SendFile(const Arguments& args) { off_t in_offset = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; size_t length = args[3]->IntegerValue(); - return scope.Close(EIOPromise::SendFile(out_fd, in_fd, in_offset, length)); + if (args[4]->IsFunction()) { + ASYNC_CALL(sendfile, args[4], out_fd, in_fd, in_offset, length) + } else { + ssize_t sent = eio_sendfile_sync (out_fd, in_fd, in_offset, length); + // XXX is this the right errno to use? + if (sent < 0) return ThrowException(errno_exception(errno)); + return Integer::New(sent); + } } static Handle ReadDir(const Arguments& args) { HandleScope scope; if (args.Length() < 1 || !args[0]->IsString()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } String::Utf8Value path(args[0]->ToString()); - return scope.Close(EIOPromise::ReadDir(*path)); + + if (args[1]->IsFunction()) { + ASYNC_CALL(readdir, args[1], *path, 0 /*flags*/) + } else { + // TODO + return ThrowException(Exception::Error( + String::New("synchronous readdir() not yet supported."))); + } } static Handle Open(const Arguments& args) { HandleScope scope; - if ( args.Length() < 3 - || !args[0]->IsString() - || !args[1]->IsInt32() - || !args[2]->IsInt32() - ) return ThrowException(BAD_ARGUMENTS); + if (args.Length() < 3 || + !args[0]->IsString() || + !args[1]->IsInt32() || + !args[2]->IsInt32()) { + return THROW_BAD_ARGS; + } String::Utf8Value path(args[0]->ToString()); int flags = args[1]->Int32Value(); mode_t mode = static_cast(args[2]->Int32Value()); - return scope.Close(EIOPromise::Open(*path, flags, mode)); + if (args[3]->IsFunction()) { + ASYNC_CALL(open, args[3], *path, flags, mode) + } else { + int fd = open(*path, flags, mode); + if (fd < 0) return ThrowException(errno_exception(errno)); + return scope.Close(Integer::New(fd)); + } } -/* node.fs.write(fd, data, position=null) +/* node.fs.write(fd, data, position, enc, callback) * Wrapper for write(2). * * 0 fd integer. file descriptor @@ -263,8 +330,8 @@ static Handle Open(const Arguments& args) { static Handle Write(const Arguments& args) { HandleScope scope; - if (args.Length() < 2 || !args[0]->IsInt32()) { - return ThrowException(BAD_ARGUMENTS); + if (args.Length() < 4 || !args[0]->IsInt32()) { + return THROW_BAD_ARGS; } int fd = args[0]->Int32Value(); @@ -281,7 +348,17 @@ static Handle Write(const Arguments& args) { ssize_t written = DecodeWrite(buf, len, args[1], enc); assert(written == len); - return scope.Close(EIOPromise::Write(fd, buf, len, offset)); + if (args[4]->IsFunction()) { + ASYNC_CALL(write, args[4], fd, buf, len, offset) + } else { + if (offset < 0) { + written = write(fd, buf, len); + } else { + written = pwrite(fd, buf, len, offset); + } + if (written < 0) return ThrowException(errno_exception(errno)); + return scope.Close(Integer::New(written)); + } } /* node.fs.read(fd, length, position, encoding) @@ -297,20 +374,32 @@ static Handle Read(const Arguments& args) { HandleScope scope; if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsNumber()) { - return ThrowException(BAD_ARGUMENTS); + return THROW_BAD_ARGS; } int fd = args[0]->Int32Value(); size_t len = args[1]->IntegerValue(); - off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; - + off_t offset = args[2]->IsNumber() ? args[2]->IntegerValue() : -1; enum encoding encoding = ParseEncoding(args[3]); - return scope.Close(EIOPromise::Read(fd, len, pos, encoding)); + if (args[4]->IsFunction()) { + Local obj = args[4]->ToObject(); + obj->SetHiddenValue(ENCODING, args[3]); + ASYNC_CALL(read, args[4], fd, NULL, len, offset) + } else { +#define READ_BUF_LEN (16*1024) + char *buf[READ_BUF_LEN]; + ssize_t ret; + if (offset < 0) { + ret = read(fd, buf, MIN(len, READ_BUF_LEN)); + } else { + ret = pread(fd, buf, MIN(len, READ_BUF_LEN), offset); + } + if (ret < 0) return ThrowException(errno_exception(errno)); + return scope.Close(Integer::New(ret)); + } } -Persistent EIOPromise::constructor_template; - void File::Initialize(Handle target) { HandleScope scope; @@ -325,18 +414,6 @@ void File::Initialize(Handle target) { NODE_SET_METHOD(target, "stat", Stat); NODE_SET_METHOD(target, "unlink", Unlink); NODE_SET_METHOD(target, "write", Write); - - - Local t2 = FunctionTemplate::New(EIOPromise::New); - EIOPromise::constructor_template = Persistent::New(t2); - EIOPromise::constructor_template->Inherit( - Promise::constructor_template); - EIOPromise::constructor_template->InstanceTemplate()-> - SetInternalFieldCount(1); - EIOPromise::constructor_template->SetClassName( - String::NewSymbol("EIOPromise")); - target->Set(String::NewSymbol("EIOPromise"), - EIOPromise::constructor_template->GetFunction()); } } // end namespace node diff --git a/src/node_file.h b/src/node_file.h index b968f287358..0ea0728dfe6 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -8,120 +8,10 @@ namespace node { -/* Are you missing your favorite POSIX function? It might be very easy to - * add a wrapper. Take a look in deps/libeio/eio.h at the list of wrapper - * functions. If your function is in that list, just follow the lead of - * EIOPromise::Open. You'll need to add two functions, one static function - * in EIOPromise, and one static function which interprets the javascript - * args in src/file.cc. Then just a reference to that function in - * File::Initialize() and you should be good to go. - * Don't forget to add a test to test/mjsunit. - */ - -#define NODE_V8_METHOD_DECLARE(name) \ - static v8::Handle name(const v8::Arguments& args) - class File { public: static void Initialize(v8::Handle target); }; -class EIOPromise : public Promise { - public: - static EIOPromise* Create(); - static v8::Persistent constructor_template; - static v8::Handle New(const v8::Arguments& args); - - static v8::Handle Open(const char *path, int flags, mode_t mode) { - EIOPromise *p = Create(); - p->req_ = eio_open(path, flags, mode, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle Close(int fd) { - EIOPromise *p = Create(); - p->req_ = eio_close(fd, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle Write(int fd, - void *buf, - size_t count, - off_t offset) { - EIOPromise *p = Create(); - p->req_ = eio_write(fd, buf, count, offset, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle Read(int fd, - size_t count, - off_t offset, - enum encoding encoding) { - EIOPromise *p = Create(); - p->encoding_ = encoding; - // NOTE: 2nd param: NULL pointer tells eio to allocate it itself - p->req_ = eio_read(fd, NULL, count, offset, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle Stat(const char *path) { - EIOPromise *p = Create(); - p->req_ = eio_stat(path, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle Rename(const char *path, const char *new_path) { - EIOPromise *p = Create(); - p->req_ = eio_rename(path, new_path, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle Unlink(const char *path) { - EIOPromise *p = Create(); - p->req_ = eio_unlink(path, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle RMDir(const char *path) { - EIOPromise *p = Create(); - p->req_ = eio_rmdir(path, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle MKDir(const char *path, mode_t mode) { - EIOPromise *p = Create(); - p->req_ = eio_mkdir(path, mode, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle ReadDir(const char *path) { - EIOPromise *p = Create(); - // By using EIO_READDIR_DENTS (or other flags), more complex results can - // be had from eio_readdir. Doesn't seem that we need that though. - p->req_ = eio_readdir(path, 0, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - static v8::Handle SendFile(int out_fd, int in_fd, off_t in_offset, size_t length) { - EIOPromise *p = Create(); - p->req_ = eio_sendfile(out_fd, in_fd, in_offset, length, EIO_PRI_DEFAULT, After, p); - return p->handle_; - } - - protected: - - void Attach(); - void Detach(); - - EIOPromise() : Promise() { } - - private: - - static int After(eio_req *req); - - eio_req *req_; - enum encoding encoding_; -}; - } // namespace node #endif // SRC_FILE_H_