#define CTIME_SYMBOL String::NewSymbol("ctime")
#define BAD_ARGUMENTS Exception::TypeError(String::New("Bad argument"))
-static int
-AfterClose (eio_req *req)
+void
+EIOPromise::Attach (void)
{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
- if (req->result == 0) {
- promise->EmitSuccess(0, NULL);
- } else {
- promise->EmitError(0, NULL);
- }
- return 0;
+ ev_ref(EV_DEFAULT_UC);
+ Promise::Attach();
}
-static Handle<Value>
-Close (const Arguments& args)
+void
+EIOPromise::Detach (void)
+{
+ Promise::Detach();
+ ev_unref(EV_DEFAULT_UC);
+}
+
+EIOPromise*
+EIOPromise::Create (void)
{
- if (args.Length() < 1 || !args[0]->IsInt32())
- return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
- int fd = args[0]->Int32Value();
- Promise *promise = Promise::Create(true);
+ Local<Object> handle =
+ Promise::constructor_template->GetFunction()->NewInstance();
- eio_close(fd, EIO_PRI_DEFAULT, AfterClose, promise);
- return scope.Close(promise->Handle());
+ EIOPromise *promise = new EIOPromise();
+ promise->Wrap(handle);
+
+ promise->Attach();
+
+ return promise;
}
-static int
-AfterRename (eio_req *req)
+#define NODE_UNIXTIME(t) v8::Date::New(1000*static_cast<double>(t))
+int
+EIOPromise::After (eio_req *req)
{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
- if (req->result == 0) {
- promise->EmitSuccess(0, NULL);
- } else {
- promise->EmitError(0, NULL);
+ HandleScope scope;
+
+ EIOPromise *promise = reinterpret_cast<EIOPromise*>(req->data);
+ assert(req == promise->req_);
+
+ if (req->errorno != 0) {
+ Local<Value> exception = Exception::Error(
+ String::NewSymbol(strerror(req->errorno)));
+ promise->EmitError(1, &exception);
+ return 0;
+ }
+
+ int argc = 0;
+ Local<Value> argv[5]; // 5 is the maximum number of args
+
+ switch (req->type) {
+ case EIO_CLOSE:
+ case EIO_RENAME:
+ case EIO_UNLINK:
+ case EIO_RMDIR:
+ argc = 0;
+ break;
+
+ case EIO_OPEN:
+ case EIO_WRITE:
+ argc = 1;
+ argv[0] = Integer::New(req->result);
+ break;
+
+ case EIO_STAT:
+ {
+ Local<Object> stats = Object::New();
+ struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
+ stats->Set(DEV_SYMBOL, Integer::New(s->st_dev)); /* ID of device containing file */
+ stats->Set(INO_SYMBOL, Integer::New(s->st_ino)); /* inode number */
+ stats->Set(MODE_SYMBOL, Integer::New(s->st_mode)); /* protection */
+ stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink)); /* number of hard links */
+ stats->Set(UID_SYMBOL, Integer::New(s->st_uid)); /* user ID of owner */
+ stats->Set(GID_SYMBOL, Integer::New(s->st_gid)); /* group ID of owner */
+ stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev)); /* device ID (if special file) */
+ stats->Set(SIZE_SYMBOL, Integer::New(s->st_size)); /* total size, in bytes */
+ stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize)); /* blocksize for filesystem I/O */
+ stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks)); /* number of blocks allocated */
+ stats->Set(ATIME_SYMBOL, NODE_UNIXTIME(s->st_atime)); /* time of last access */
+ stats->Set(MTIME_SYMBOL, NODE_UNIXTIME(s->st_mtime)); /* time of last modification */
+ stats->Set(CTIME_SYMBOL, NODE_UNIXTIME(s->st_ctime)); /* time of last status change */
+ argc = 1;
+ argv[0] = stats;
+ break;
+ }
+
+ case EIO_READ:
+ {
+ argc = 2;
+ // FIXME the following is really ugly!
+ if (promise->encoding_ == RAW) {
+ if (req->result == 0) {
+ argv[0] = Local<Value>::New(Null());
+ argv[1] = Integer::New(0);
+ } else {
+ char *buf = reinterpret_cast<char*>(req->ptr2);
+ size_t len = req->result;
+ Local<Array> array = Array::New(len);
+ for (unsigned int i = 0; i < len; i++) {
+ unsigned char val = reinterpret_cast<const unsigned char*>(buf)[i];
+ array->Set(Integer::New(i), Integer::New(val));
+ }
+ argv[0] = array;
+ argv[1] = Integer::New(req->result);
+ }
+ } else {
+ // UTF8
+ if (req->result == 0) {
+ // eof
+ argv[0] = Local<Value>::New(Null());
+ argv[1] = Integer::New(0);
+ } else {
+ char *buf = reinterpret_cast<char*>(req->ptr2);
+ argv[0] = String::New(buf, req->result);
+ argv[1] = Integer::New(req->result);
+ }
+ }
+ break;
+ }
+
+ default:
+ assert(0 && "Unhandled eio response");
}
+
+ promise->EmitSuccess(argc, argv);
+
return 0;
}
-static Handle<Value> Rename (const Arguments& args)
+static Handle<Value>
+Close (const Arguments& args)
{
- if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString())
- return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
- String::Utf8Value path(args[0]->ToString());
- String::Utf8Value new_path(args[1]->ToString());
- Promise *promise = Promise::Create(true);
+ if (args.Length() < 1 || !args[0]->IsInt32()) {
+ return ThrowException(BAD_ARGUMENTS);
+ }
- eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, promise);
- return scope.Close(promise->Handle());
+ int fd = args[0]->Int32Value();
+
+ return scope.Close(EIOPromise::Close(fd));
}
-static int
-AfterUnlink (eio_req *req)
+static Handle<Value>
+Stat (const Arguments& args)
{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
- if (req->result == 0) {
- promise->EmitSuccess(0, NULL);
- } else {
- promise->EmitError(0, NULL);
+ HandleScope scope;
+
+ if (args.Length() < 1 || !args[0]->IsString()) {
+ return ThrowException(BAD_ARGUMENTS);
}
- return 0;
+
+ String::Utf8Value path(args[0]->ToString());
+
+ return scope.Close(EIOPromise::Stat(*path));
}
-static Handle<Value> Unlink (const Arguments& args)
+static Handle<Value>
+Rename (const Arguments& args)
{
- if (args.Length() < 1 || !args[0]->IsString())
- return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
+
+ if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) {
+ return ThrowException(BAD_ARGUMENTS);
+ }
+
String::Utf8Value path(args[0]->ToString());
- Promise *promise = Promise::Create(true);
- eio_unlink(*path, EIO_PRI_DEFAULT, AfterUnlink, promise);
+ String::Utf8Value new_path(args[1]->ToString());
+
+ return scope.Close(EIOPromise::Rename(*path, *new_path));
+ Promise *promise = EIOPromise::Create();
+
return scope.Close(promise->Handle());
}
-static int
-AfterRMDir (eio_req *req)
+static Handle<Value> Unlink (const Arguments& args)
{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
- if (req->result == 0) {
- promise->EmitSuccess(0, NULL);
- } else {
- promise->EmitError(0, NULL);
- }
- return 0;
-}
+ HandleScope scope;
-static Handle<Value> RMDir (const Arguments& args)
-{
- if (args.Length() < 1 || !args[0]->IsString())
+ if (args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(BAD_ARGUMENTS);
- HandleScope scope;
+ }
+
String::Utf8Value path(args[0]->ToString());
- Promise *promise = Promise::Create(true);
- eio_rmdir(*path, EIO_PRI_DEFAULT, AfterRMDir, promise);
- return scope.Close(promise->Handle());
+ return scope.Close(EIOPromise::Unlink(*path));
}
-static int
-AfterOpen (eio_req *req)
+static Handle<Value>
+RMDir (const Arguments& args)
{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
+ HandleScope scope;
- if (req->result < 0) {
- promise->EmitError(0, NULL);
- return 0;
+ if (args.Length() < 1 || !args[0]->IsString()) {
+ return ThrowException(BAD_ARGUMENTS);
}
- HandleScope scope;
- Local<Value> argv[1] = { Integer::New(req->result) };
- promise->EmitSuccess(1, argv);
- return 0;
+ String::Utf8Value path(args[0]->ToString());
+ return scope.Close(EIOPromise::RMDir(*path));
}
static Handle<Value>
Open (const Arguments& args)
{
+ HandleScope scope;
+
if ( args.Length() < 3
|| !args[0]->IsString()
|| !args[1]->IsInt32()
|| !args[2]->IsInt32()
) return ThrowException(BAD_ARGUMENTS);
- HandleScope scope;
String::Utf8Value path(args[0]->ToString());
int flags = args[1]->Int32Value();
mode_t mode = static_cast<mode_t>(args[2]->Int32Value());
- Promise *promise = Promise::Create(true);
-
- eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, promise);
- return scope.Close(promise->Handle());
-}
-
-static int
-AfterWrite (eio_req *req)
-{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
-
- if (req->result < 0) {
- promise->EmitError(0, NULL);
- return 0;
- }
-
- HandleScope scope;
-
- free(req->ptr2);
-
- ssize_t written = req->result;
- Local<Value> argv[1];
- argv[0] = written >= 0 ? Integer::New(written) : Integer::New(0);
-
- promise->EmitSuccess(1, argv);
- return 0;
+ return scope.Close(EIOPromise::Open(*path, flags, mode));
}
-
-/* node.fs.write(fd, data, position, callback)
+/* node.fs.write(fd, data, position=null)
* Wrapper for write(2).
*
* 0 fd integer. file descriptor
* 1 data the data to write (string = utf8, array = raw)
* 2 position if integer, position to write at in the file.
* if null, write from the current position
- *
- * 3 callback(errorno, written)
*/
static Handle<Value>
Write (const Arguments& args)
{
- if ( args.Length() < 3
- || !args[0]->IsInt32()
- ) return ThrowException(BAD_ARGUMENTS);
-
HandleScope scope;
+ if (args.Length() < 2 || !args[0]->IsInt32()) {
+ return ThrowException(BAD_ARGUMENTS);
+ }
+
int fd = args[0]->Int32Value();
- off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
+ off_t offset = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
char *buf = NULL;
size_t len = 0;
return ThrowException(BAD_ARGUMENTS);
}
- Promise *promise = Promise::Create(true);
- eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, promise);
- return scope.Close(promise->Handle());
-}
-
-static int
-AfterUtf8Read (eio_req *req)
-{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
-
- if (req->result < 0) {
- promise->EmitError(0, NULL);
- return 0;
- }
-
- HandleScope scope;
-
- Local<Value> argv[2];
-
- if (req->result == 0) {
- // eof
- argv[0] = Local<Value>::New(Null());
- argv[1] = Integer::New(0);
- } else {
- char *buf = reinterpret_cast<char*>(req->ptr2);
- argv[0] = String::New(buf, req->result);
- argv[1] = Integer::New(req->result);
- }
-
- promise->EmitSuccess(2, argv);
- return 0;
-}
-
-static int
-AfterRawRead(eio_req *req)
-{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
-
- if (req->result < 0) {
- promise->EmitError(0, NULL);
- return 0;
- }
-
- HandleScope scope;
- Local<Value> argv[2];
-
- if (req->result == 0) {
- argv[0] = Local<Value>::New(Null());
- argv[1] = Integer::New(0);
- } else {
- char *buf = reinterpret_cast<char*>(req->ptr2);
- size_t len = req->result;
- Local<Array> array = Array::New(len);
- for (unsigned int i = 0; i < len; i++) {
- unsigned char val = reinterpret_cast<const unsigned char*>(buf)[i];
- array->Set(Integer::New(i), Integer::New(val));
- }
- argv[0] = array;
- argv[1] = Integer::New(req->result);
- }
-
- promise->EmitSuccess(2, argv);
- return 0;
+ return scope.Close(EIOPromise::Write(fd, buf, len, offset));
}
-/* node.fs.read(fd, length, position, encoding, callback)
+/* node.fs.read(fd, length, position, encoding)
* Wrapper for read(2).
*
* 0 fd integer. file descriptor
* 1 length integer. length to read
* 2 position if integer, position to read from in the file.
* if null, read from the current position
- * 3 encoding either node.fs.UTF8 or node.fs.RAW
- *
- * 4 callback(errorno, data)
+ * 3 encoding either node.UTF8 or node.RAW
*/
static Handle<Value>
Read (const Arguments& args)
{
- if ( args.Length() < 2
- || !args[0]->IsInt32() // fd
- || !args[1]->IsNumber() // len
- ) return ThrowException(BAD_ARGUMENTS);
-
HandleScope scope;
+ if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsNumber()) {
+ return ThrowException(BAD_ARGUMENTS);
+ }
+
int fd = args[0]->Int32Value();
size_t len = args[1]->IntegerValue();
off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
encoding = static_cast<enum encoding>(args[3]->Int32Value());
}
- Promise *promise = Promise::Create(true);
-
- // NOTE: 2nd param: NULL pointer tells eio to allocate it itself
- eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT,
- encoding == UTF8 ? AfterUtf8Read : AfterRawRead, promise);
-
- return scope.Close(promise->Handle());
-}
-
-static int
-AfterStat (eio_req *req)
-{
- Promise *promise = reinterpret_cast<Promise*>(req->data);
-
- if (req->result < 0) {
- promise->EmitError(0, NULL);
- return 0;
- }
-
- HandleScope scope;
-
- Local<Object> stats = Object::New();
-
- struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
-
- /* ID of device containing file */
- stats->Set(DEV_SYMBOL, Integer::New(s->st_dev));
- /* inode number */
- stats->Set(INO_SYMBOL, Integer::New(s->st_ino));
- /* protection */
- stats->Set(MODE_SYMBOL, Integer::New(s->st_mode));
- /* number of hard links */
- stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink));
- /* user ID of owner */
- stats->Set(UID_SYMBOL, Integer::New(s->st_uid));
- /* group ID of owner */
- stats->Set(GID_SYMBOL, Integer::New(s->st_gid));
- /* device ID (if special file) */
- stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev));
- /* total size, in bytes */
- stats->Set(SIZE_SYMBOL, Integer::New(s->st_size));
- /* blocksize for filesystem I/O */
- stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize));
- /* number of blocks allocated */
- stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks));
- /* time of last access */
- stats->Set(ATIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_atime)));
- /* time of last modification */
- stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_mtime)));
- /* time of last status change */
- stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_ctime)));
-
- Local<Value> argv[1] = { stats };
- promise->EmitSuccess(1, argv);
-
- return 0;
-}
-
-static Handle<Value>
-Stat (const Arguments& args)
-{
- if (args.Length() < 1 || !args[0]->IsString())
- return ThrowException(BAD_ARGUMENTS);
-
- HandleScope scope;
-
- String::Utf8Value path(args[0]->ToString());
-
- Promise *promise = Promise::Create(true);
-
- eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, promise);
-
- return scope.Close(promise->Handle());
-}
-
-static Handle<Value>
-StrError (const Arguments& args)
-{
- if (args.Length() < 1) return v8::Undefined();
- if (!args[0]->IsNumber()) return v8::Undefined();
-
- HandleScope scope;
-
- int errorno = args[0]->IntegerValue();
-
- Local<String> message = String::New(strerror(errorno));
-
- return scope.Close(message);
+ return scope.Close(EIOPromise::Read(fd, len, pos, encoding));
}
void
{
HandleScope scope;
- // POSIX Wrappers
NODE_SET_METHOD(target, "close", Close);
NODE_SET_METHOD(target, "open", Open);
NODE_SET_METHOD(target, "read", Read);
NODE_SET_METHOD(target, "stat", Stat);
NODE_SET_METHOD(target, "unlink", Unlink);
NODE_SET_METHOD(target, "write", Write);
-
- NODE_SET_METHOD(target, "strerror", StrError);
}
#ifndef node_file_h
#define node_file_h
+#include "node.h"
+#include "events.h"
#include <v8.h>
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<v8::Value> name (const v8::Arguments& args)
+
class File {
public:
static void Initialize (v8::Handle<v8::Object> target);
};
+class EIOPromise : public Promise {
+ public:
+ static v8::Handle<v8::Object>
+ 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<v8::Object>
+ Close (int fd)
+ {
+ EIOPromise *p = Create();
+ p->req_ = eio_close(fd, EIO_PRI_DEFAULT, After, p);
+ return p->handle_;
+ }
+
+ static v8::Handle<v8::Object>
+ 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<v8::Object>
+ 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<v8::Object>
+ Stat (const char *path)
+ {
+ EIOPromise *p = Create();
+ p->req_ = eio_stat(path, EIO_PRI_DEFAULT, After, p);
+ return p->handle_;
+ }
+
+ static v8::Handle<v8::Object>
+ 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<v8::Object>
+ Unlink (const char *path)
+ {
+ EIOPromise *p = Create();
+ p->req_ = eio_unlink(path, EIO_PRI_DEFAULT, After, p);
+ return p->handle_;
+ }
+
+ static v8::Handle<v8::Object>
+ RMDir (const char *path)
+ {
+ EIOPromise *p = Create();
+ p->req_ = eio_rmdir(path, EIO_PRI_DEFAULT, After, p);
+ return p->handle_;
+ }
+
+ static EIOPromise* Create (void);
+
+ protected:
+
+ void Attach (void);
+ void Detach (void);
+
+ EIOPromise () : Promise() { }
+
+ private:
+
+ static int After (eio_req *req);
+
+ eio_req *req_;
+ enum encoding encoding_;
+};
+
} // namespace node
#endif // node_file_h