Large refactor of file code.
authorRyan <ry@tinyclouds.org>
Tue, 26 May 2009 01:30:51 +0000 (03:30 +0200)
committerRyan <ry@tinyclouds.org>
Tue, 26 May 2009 01:30:51 +0000 (03:30 +0200)
All the c++ code is now reduced to simple wrappers. The node.fs.File object
is defined entirely in javascript now. As is the actionQueue methods.

This makes the boundaries much cleaner. There is still some thought that
needs to go into how exactly the API should behave but this simplification
is a first step.

src/file.cc
src/file.h
src/file.js
src/node.h
src/node.js

index c8f6fae..d5f261d 100644 (file)
 using namespace v8;
 using namespace node;
 
-#define FD_SYMBOL           String::NewSymbol("fd")
-#define ACTION_QUEUE_SYMBOL String::NewSymbol("_actionQueue")
-#define ENCODING_SYMBOL     String::NewSymbol("encoding")
-#define CALLBACK_SYMBOL     String::NewSymbol("callbaccallback")
-#define POLL_ACTIONS_SYMBOL String::NewSymbol("_pollActions")
-
-#define UTF8_SYMBOL         String::NewSymbol("utf8")
-#define RAW_SYMBOL          String::NewSymbol("raw")
-
-void
-File::Initialize (Handle<Object> target)
+#define DEV_SYMBOL         String::NewSymbol("dev")
+#define INO_SYMBOL         String::NewSymbol("ino")
+#define MODE_SYMBOL        String::NewSymbol("mode")
+#define NLINK_SYMBOL       String::NewSymbol("nlink")
+#define UID_SYMBOL         String::NewSymbol("uid")
+#define GID_SYMBOL         String::NewSymbol("gid")
+#define RDEV_SYMBOL        String::NewSymbol("rdev")
+#define SIZE_SYMBOL        String::NewSymbol("size")
+#define BLKSIZE_SYMBOL     String::NewSymbol("blksize")
+#define BLOCKS_SYMBOL      String::NewSymbol("blocks")
+#define ATIME_SYMBOL       String::NewSymbol("atime")
+#define MTIME_SYMBOL       String::NewSymbol("mtime")
+#define CTIME_SYMBOL       String::NewSymbol("ctime")
+#define BAD_ARGUMENTS      String::New("Bad argument")
+
+#define MAKE_CALLBACK_PTR                                             \
+  Persistent<Function> *callback = NULL;                              \
+  Local<Value> last_arg = args[args.Length()-1];                      \
+  if (last_arg->IsFunction()) {                                       \
+    Local<Function> l = Local<Function>::Cast(last_arg);              \
+    callback = new Persistent<Function>();                            \
+    *callback = Persistent<Function>::New(l);                         \
+  }                                                                   \
+  node::eio_warmup();                                                 \
+
+#define CALL_CALLBACK_PTR(req, argc, argv)                            \
+do {                                                                  \
+  if (req->data) {                                                    \
+    Persistent<Function> *callback =                                  \
+      reinterpret_cast<Persistent<Function>*>(req->data);             \
+    TryCatch try_catch;                                               \
+    (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);   \
+    if(try_catch.HasCaught())                                         \
+      node::fatal_exception(try_catch);                               \
+    free(callback);                                                   \
+  }                                                                   \
+} while(0)
+
+#define DEFINE_SIMPLE_CB(name)                                        \
+static int After##name (eio_req *req)                                 \
+{                                                                     \
+  HandleScope scope;                                                  \
+  Local<Value> argv[] = { Integer::New(req->errorno) };               \
+  CALL_CALLBACK_PTR(req, 1, argv);                                    \
+  return 0;                                                           \
+}                                                                     \
+
+DEFINE_SIMPLE_CB(Close)
+static Handle<Value> Close (const Arguments& args)
 {
+  if (args.Length() < 1 || !args[0]->IsInt32())
+    return ThrowException(BAD_ARGUMENTS);
   HandleScope scope;
-
-  // file system methods
-  NODE_SET_METHOD(target, "rename",   FileSystem::Rename);
-  NODE_SET_METHOD(target, "stat",     FileSystem::Stat);
-  NODE_SET_METHOD(target, "strerror", FileSystem::StrError);
-
-  target->Set(String::NewSymbol("STDIN_FILENO"), Integer::New(STDIN_FILENO));
-  target->Set(String::NewSymbol("STDOUT_FILENO"), Integer::New(STDOUT_FILENO));
-  target->Set(String::NewSymbol("STDERR_FILENO"), Integer::New(STDERR_FILENO));
-
-
-  Local<FunctionTemplate> file_template = FunctionTemplate::New(File::New);
-  file_template->InstanceTemplate()->SetInternalFieldCount(1);
-
-  // file methods
-  NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_open", File::Open);
-  NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_close", File::Close);
-  NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_write", File::Write);
-  NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_read", File::Read);
-  file_template->InstanceTemplate()->SetAccessor(ENCODING_SYMBOL, File::GetEncoding, File::SetEncoding);
-  target->Set(String::NewSymbol("File"), file_template->GetFunction());
-}
-
-Handle<Value>
-File::GetEncoding (Local<String> property, const AccessorInfo& info) 
-{
-  File *file = NODE_UNWRAP(File, info.This());
-
-  if (file->encoding_ == UTF8)
-    return UTF8_SYMBOL;
-  else
-    return RAW_SYMBOL;
-}
-    
-void
-File::SetEncoding (Local<String> property, Local<Value> value, const AccessorInfo& info) 
-{
-  File *file = NODE_UNWRAP(File, info.This());
-
-  if (value->IsString()) {
-    Local<String> encoding_string = value->ToString();
-    char buf[5]; // need enough room for "utf8" or "raw"
-    encoding_string->WriteAscii(buf, 0, 4);
-    buf[4] = '\0';
-    file->encoding_ = strcasecmp(buf, "utf8") == 0 ? UTF8 : RAW;
-  } else {
-    file->encoding_ = RAW;
-  }
-}
-
-static void
-CallTopCallback (Handle<Object> handle, const int argc, Handle<Value> argv[])
-{
-  HandleScope scope;
-
-  // poll_actions
-  Local<Value> poll_actions_value = handle->Get(POLL_ACTIONS_SYMBOL);
-  assert(poll_actions_value->IsFunction());  
-  Handle<Function> poll_actions = Handle<Function>::Cast(poll_actions_value);
-
-  TryCatch try_catch;
-  poll_actions->Call(handle, argc, argv);
-  if(try_catch.HasCaught())
-    node::fatal_exception(try_catch);
+  int fd = args[0]->Int32Value();
+  MAKE_CALLBACK_PTR
+  eio_close(fd, EIO_PRI_DEFAULT, AfterClose, callback);
+  return Undefined();
 }
 
-Handle<Value>
-FileSystem::Rename (const Arguments& args)
+DEFINE_SIMPLE_CB(Rename)
+static Handle<Value> Rename (const Arguments& args)
 {
-  if (args.Length() < 2)
-    return Undefined();
-
+  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());
-
-  Persistent<Function> *callback = NULL;
-  if (args[2]->IsFunction()) {
-    Local<Function> l = Local<Function>::Cast(args[2]);
-    callback = new Persistent<Function>();
-    *callback = Persistent<Function>::New(l);
-  }
-
-  node::eio_warmup();
-  eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL);
-
+  MAKE_CALLBACK_PTR
+  eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, callback);
   return Undefined();
 }
 
-int
-FileSystem::AfterRename (eio_req *req)
+static int
+AfterOpen (eio_req *req)
 {
   HandleScope scope;
-  const int argc = 1;
+  const int argc = 2;
   Local<Value> argv[argc];
   argv[0] = Integer::New(req->errorno);
-
-  if (req->data) {
-    Persistent<Function> *callback = reinterpret_cast<Persistent<Function>*>(req->data);
-
-    TryCatch try_catch;
-    (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
-    if(try_catch.HasCaught())
-      node::fatal_exception(try_catch);
-
-    free(callback);
-  }
-
+  argv[1] = Integer::New(req->result);
+  CALL_CALLBACK_PTR(req, argc, argv);
   return 0;
 }
 
-Handle<Value>
-FileSystem::Stat (const Arguments& args)
+static Handle<Value>
+Open (const Arguments& args)
 {
-  if (args.Length() < 1)
-    return v8::Undefined();
+  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());
 
-  Persistent<Function> *callback = NULL;
-  if (args[1]->IsFunction()) {
-    Local<Function> l = Local<Function>::Cast(args[1]);
-    callback = new Persistent<Function>();
-    *callback = Persistent<Function>::New(l);
-  }
-
-  node::eio_warmup();
-  eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback);
+  MAKE_CALLBACK_PTR
 
+  eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, callback);
   return Undefined();
 }
 
-int
-FileSystem::AfterStat (eio_req *req)
+static int
+AfterWrite (eio_req *req)
 {
   HandleScope scope;
 
+  free(req->ptr2);
+
+  ssize_t written = req->result;
+
   const int argc = 2;
   Local<Value> argv[argc];
   argv[0] = Integer::New(req->errorno);
+  argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
 
-  Local<Object> stats = Object::New();
-  argv[1] = stats;
-
-  if (req->result == 0) {
-    struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
-
-    /* ID of device containing file */
-    stats->Set(NODE_SYMBOL("dev"), Integer::New(s->st_dev));
-    /* inode number */
-    stats->Set(NODE_SYMBOL("ino"), Integer::New(s->st_ino));
-    /* protection */
-    stats->Set(NODE_SYMBOL("mode"), Integer::New(s->st_mode));
-    /* number of hard links */
-    stats->Set(NODE_SYMBOL("nlink"), Integer::New(s->st_nlink));
-    /* user ID of owner */
-    stats->Set(NODE_SYMBOL("uid"), Integer::New(s->st_uid));
-    /* group ID of owner */
-    stats->Set(NODE_SYMBOL("gid"), Integer::New(s->st_gid));
-    /* device ID (if special file) */
-    stats->Set(NODE_SYMBOL("rdev"), Integer::New(s->st_rdev));
-    /* total size, in bytes */
-    stats->Set(NODE_SYMBOL("size"), Integer::New(s->st_size));
-    /* blocksize for filesystem I/O */
-    stats->Set(NODE_SYMBOL("blksize"), Integer::New(s->st_blksize));
-    /* number of blocks allocated */
-    stats->Set(NODE_SYMBOL("blocks"), Integer::New(s->st_blocks));
-    /* time of last access */
-    stats->Set(NODE_SYMBOL("atime"), Date::New(1000*static_cast<double>(s->st_atime)));
-    /* time of last modification */
-    stats->Set(NODE_SYMBOL("mtime"), Date::New(1000*static_cast<double>(s->st_mtime)));
-    /* time of last status change */
-    stats->Set(NODE_SYMBOL("ctime"), Date::New(1000*static_cast<double>(s->st_ctime)));
-  }
-
-  if (req->data) {
-    Persistent<Function> *callback = reinterpret_cast<Persistent<Function>*>(req->data);
-
-    TryCatch try_catch;
-    (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
-    if(try_catch.HasCaught())
-      node::fatal_exception(try_catch);
-
-    free(callback);
-  }
-
+  CALL_CALLBACK_PTR(req, argc, argv);
   return 0;
 }
 
-Handle<Value>
-FileSystem::StrError (const Arguments& args)
+
+/* node.fs.write(fd, data, position, callback)
+ * Wrapper for write(2). 
+ *
+ * 0 fd        integer. file descriptor
+ * 1 data      the data to write 
+ * 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() < 1) return v8::Undefined();
-  if (!args[0]->IsNumber()) return v8::Undefined();
+  if ( args.Length() < 3 
+    || !args[0]->IsInt32() 
+     ) return ThrowException(BAD_ARGUMENTS);
 
   HandleScope scope;
 
-  int errorno = args[0]->IntegerValue();
-
-  Local<String> message = String::New(strerror(errorno));
-
-  return scope.Close(message);
-}
-
-///////////////////// FILE ///////////////////// 
+  int fd = args[0]->Int32Value();
+  off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
 
-File::File (Handle<Object> handle)
-  : ObjectWrap(handle)
-{
-  HandleScope scope;
-  encoding_ = RAW;
-  handle->Set(ACTION_QUEUE_SYMBOL, Array::New());
-}
+  char *buf = NULL; 
+  size_t len = 0;
 
-File::~File ()
-{
-  ; // XXX call close?
-}
+  if (args[1]->IsString()) {
+    // utf8 encoding
+    Local<String> string = args[1]->ToString();
+    len = string->Utf8Length();
+    buf = reinterpret_cast<char*>(malloc(len));
+    string->WriteUtf8(buf, len);
+    
+  } else if (args[1]->IsArray()) {
+    // raw encoding
+    Local<Array> array = Local<Array>::Cast(args[1]);
+    len = array->Length();
+    buf = reinterpret_cast<char*>(malloc(len));
+    for (unsigned int i = 0; i < len; i++) {
+      Local<Value> int_value = array->Get(Integer::New(i));
+      buf[i] = int_value->Int32Value();
+    }
 
-bool
-File::HasUtf8Encoding (void)
-{
-  return false;
-}
+  } else {
+    return ThrowException(BAD_ARGUMENTS);
+  }
 
-int
-File::GetFD (void)
-{
-  Handle<Value> fd_value = handle_->Get(FD_SYMBOL);
-  int fd = fd_value->IntegerValue();
-  return fd;
+  MAKE_CALLBACK_PTR
+  eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, callback);
+  return Undefined();
 }
 
-Handle<Value>
-File::Close (const Arguments& args) 
+static int
+AfterUtf8Read (eio_req *req)
 {
   HandleScope scope;
 
-  File *file = NODE_UNWRAP(File, args.Holder());
-
-  int fd = file->GetFD();
-
-  node::eio_warmup();
-  eio_close (fd, EIO_PRI_DEFAULT, File::AfterClose, file);
-  file->Attach();
-  return Undefined();
-}
+  const int argc = 2;
+  Local<Value> argv[argc];
+  argv[0] = Integer::New(req->errorno);
 
-int
-File::AfterClose (eio_req *req)
-{
-  File *file = reinterpret_cast<File*>(req->data);
+  char *buf = reinterpret_cast<char*>(req->ptr2);
 
-  if (req->result == 0) {
-    file->handle_->Delete(FD_SYMBOL);
+  if (req->result == 0) { 
+    // eof 
+    argv[1] = Local<Value>::New(Null());
+  } else {
+    size_t len = req->result;
+    argv[1] = String::New(buf, len);
   }
 
-  const int argc = 1;
-  Local<Value> argv[argc];
-  argv[0] = Integer::New(req->errorno);
-  CallTopCallback(file->handle_, argc, argv);
-  file->Detach();
+  CALL_CALLBACK_PTR(req, argc, argv);
   return 0;
 }
 
-Handle<Value>
-File::Open (const Arguments& args)
+static int
+AfterRawRead(eio_req *req)
 {
-  /* check arguments */
-  if (args.Length() < 1) return Undefined();
-  if (!args[0]->IsString()) return Undefined();
-
   HandleScope scope;
 
-  File *file = NODE_UNWRAP(File, args.Holder());
-
-  // make sure that we don't already have a pending open
-  if (file->handle_->Has(FD_SYMBOL)) {
-    return ThrowException(String::New("File object is opened."));
-  }
+  const int argc = 2;
+  Local<Value> argv[argc];
+  argv[0] = Integer::New(req->errorno);
 
-  String::Utf8Value path(args[0]->ToString());
+  char *buf = reinterpret_cast<char*>(req->ptr2);
 
-  int flags = O_RDONLY; // default
-  if (args[1]->IsString()) {
-    String::AsciiValue mode_v(args[1]->ToString());
-    char *mode = *mode_v;
-    // XXX is this interpretation of the mode correct?
-    // I don't want to to use fopen() directly because eio doesn't support it.
-    switch(mode[0]) {
-      case 'r':
-        flags = (mode[1] == '+' ? O_RDWR : O_RDONLY); 
-        break;
-      case 'w':
-        flags = O_CREAT | O_TRUNC | (mode[1] == '+' ? O_RDWR : O_WRONLY); 
-        break;
-      case 'a':
-        flags = O_APPEND | O_CREAT | (mode[1] == '+' ? O_RDWR : O_WRONLY); 
-        break;
+  if (req->result == 0) { 
+    // eof 
+    argv[1] = Local<Value>::New(Null());
+  } else {
+    // raw encoding
+    size_t len = req->result;
+    Local<Array> array = Array::New(len);
+    for (unsigned int i = 0; i < len; i++) {
+      array->Set(Integer::New(i), Integer::New(buf[i]));
     }
+    argv[1] = array;
   }
 
-  // TODO how should the mode be set?
-  node::eio_warmup();
-  eio_open (*path, flags, 0666, EIO_PRI_DEFAULT, File::AfterOpen, file);
-  file->Attach();
-  return Undefined();
-}
-
-int
-File::AfterOpen (eio_req *req)
-{
-  File *file = reinterpret_cast<File*>(req->data);
-  HandleScope scope;
-
-  if(req->result >= 0) {
-    file->handle_->Set(FD_SYMBOL, Integer::New(req->result));
-  }
-
-  const int argc = 1;
-  Handle<Value> argv[argc];
-  argv[0] = Integer::New(req->errorno);
-  CallTopCallback(file->handle_, argc, argv);
-
-  file->Detach();
+  CALL_CALLBACK_PTR(req, argc, argv);
   return 0;
 }
 
-Handle<Value>
-File::Write (const Arguments& args)
+/* node.fs.read(fd, length, position, encoding, callback)
+ * 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)
+ */
+static Handle<Value>
+Read (const Arguments& args)
 {
-  if (args.Length() < 2) return Undefined();
-  if (!args[1]->IsNumber()) return Undefined();
+  if ( args.Length() < 3 
+    || !args[0]->IsInt32()   // fd
+    || !args[1]->IsNumber()  // len
+     ) return ThrowException(BAD_ARGUMENTS);
 
   HandleScope scope;
 
-  File *file = NODE_UNWRAP(File, args.Holder());
+  int fd = args[0]->Int32Value();
+  size_t len = args[1]->IntegerValue();
+  off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
 
-  off_t pos = args[1]->IntegerValue();
-
-  char *buf = NULL; 
-  size_t length = 0;
-
-  if (args[0]->IsString()) {
-    // utf8 encoding
-    Local<String> string = args[0]->ToString();
-    length = string->Utf8Length();
-    buf = reinterpret_cast<char*>(malloc(length));
-    string->WriteUtf8(buf, length);
-    
-  } else if (args[0]->IsArray()) {
-    // raw encoding
-    Local<Array> array = Local<Array>::Cast(args[0]);
-    length = array->Length();
-    buf = reinterpret_cast<char*>(malloc(length));
-    for (unsigned int i = 0; i < length; i++) {
-      Local<Value> int_value = array->Get(Integer::New(i));
-      buf[i] = int_value->Int32Value();
-    }
-
-  } else {
-    // bad arguments. raise error?
-    return Undefined();
+  enum encoding encoding = RAW;
+  if (args[3]->IsInt32()) {
+    encoding = static_cast<enum encoding>(args[3]->Int32Value());
   }
 
-  if (file->handle_->Has(FD_SYMBOL) == false) {
-    printf("trying to write to a bad fd!\n");  
-    return Undefined();
-  }
-
-  int fd = file->GetFD();
-
-  node::eio_warmup();
-  eio_write(fd, buf, length, pos, EIO_PRI_DEFAULT, File::AfterWrite, file);
-
-  file->Attach();
+  MAKE_CALLBACK_PTR
+  // NOTE: 2nd param: NULL pointer tells eio to allocate it itself
+  eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, 
+      encoding == UTF8 ? AfterUtf8Read : AfterRawRead, callback);
   return Undefined();
 }
 
-int
-File::AfterWrite (eio_req *req)
+static int
+AfterStat (eio_req *req)
 {
-  File *file = reinterpret_cast<File*>(req->data);
-
-  //char *buf = reinterpret_cast<char*>(req->ptr2);
-  free(req->ptr2);
-  ssize_t written = req->result;
-
   HandleScope scope;
 
   const int argc = 2;
   Local<Value> argv[argc];
   argv[0] = Integer::New(req->errorno);
-  argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
-  CallTopCallback(file->handle_, argc, argv);
 
-  file->Detach();
+  Local<Object> stats = Object::New();
+  argv[1] = stats;
+
+  if (req->result == 0) {
+    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)));
+  }
+  CALL_CALLBACK_PTR(req, argc, argv);                                    \
   return 0;
 }
 
-Handle<Value>
-File::Read (const Arguments& args)
+static Handle<Value>
+Stat (const Arguments& args)
 {
-  HandleScope scope;
+  if (args.Length() < 1 || !args[0]->IsString())
+    return ThrowException(BAD_ARGUMENTS);
 
-  if (args.Length() < 1 || !args[0]->IsNumber() || !args[1]->IsNumber()) {
-    return ThrowException(String::New("Bad parameter for _ffi_read()"));
-  }
+  HandleScope scope;
 
-  File *file = NODE_UNWRAP(File, args.Holder());
-  size_t len = args[0]->IntegerValue();
-  off_t pos  = args[1]->IntegerValue();
+  String::Utf8Value path(args[0]->ToString());
 
-  int fd = file->GetFD();
-  assert(fd >= 0);
+  MAKE_CALLBACK_PTR
 
-  // NOTE: 2nd param: NULL pointer tells eio to allocate it itself
-  node::eio_warmup();
-  eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, File::AfterRead, file);
+  eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback);
 
-  file->Attach();
   return Undefined();
 }
 
-int
-File::AfterRead (eio_req *req)
+static Handle<Value>
+StrError (const Arguments& args)
 {
-  File *file = reinterpret_cast<File*>(req->data);
-  HandleScope scope;
-
-  const int argc = 2;
-  Local<Value> argv[argc];
-  argv[0] = Integer::New(req->errorno);
+  if (args.Length() < 1) return v8::Undefined();
+  if (!args[0]->IsNumber()) return v8::Undefined();
 
-  char *buf = reinterpret_cast<char*>(req->ptr2);
+  HandleScope scope;
 
-  if (req->result == 0) { 
-    // eof 
-    argv[1] = Local<Value>::New(Null());
-  } else {
-    size_t len = req->result;
-    if (file->encoding_ == UTF8) {
-      // utf8 encoding
-      argv[1] = String::New(buf, req->result);
-    } else {
-      // raw encoding
-      Local<Array> array = Array::New(len);
-      for (unsigned int i = 0; i < len; i++) {
-        array->Set(Integer::New(i), Integer::New(buf[i]));
-      }
-      argv[1] = array;
-    }
-  }
+  int errorno = args[0]->IntegerValue();
 
-  CallTopCallback(file->handle_, argc, argv);
+  Local<String> message = String::New(strerror(errorno));
 
-  file->Detach();
-  return 0;
+  return scope.Close(message);
 }
 
-Handle<Value>
-File::New(const Arguments& args)
+void
+File::Initialize (Handle<Object> target)
 {
   HandleScope scope;
 
-  File *f = new File(args.Holder());
-  ObjectWrap::InformV8ofAllocation(f);
-
-  return args.This();
+  // file system methods
+  NODE_SET_METHOD(target, "rename", Rename);
+  NODE_SET_METHOD(target, "stat", Stat);
+  NODE_SET_METHOD(target, "close", Close);
+  NODE_SET_METHOD(target, "open", Open);
+  NODE_SET_METHOD(target, "write", Write);
+  NODE_SET_METHOD(target, "read", Read);
+
+  NODE_SET_METHOD(target, "strerror",  StrError);
+
+  NODE_DEFINE_CONSTANT(target, RAW);
+  NODE_DEFINE_CONSTANT(target, UTF8);
+
+  NODE_DEFINE_CONSTANT(target, STDIN_FILENO);
+  NODE_DEFINE_CONSTANT(target, STDOUT_FILENO);
+  NODE_DEFINE_CONSTANT(target, STDERR_FILENO);
+  // file access modes
+  NODE_DEFINE_CONSTANT(target, O_RDONLY);
+  NODE_DEFINE_CONSTANT(target, O_WRONLY);
+  NODE_DEFINE_CONSTANT(target, O_RDWR);
+  // file creation flags
+  NODE_DEFINE_CONSTANT(target, O_CREAT);
+  NODE_DEFINE_CONSTANT(target, O_EXCL);
+  NODE_DEFINE_CONSTANT(target, O_NOCTTY);
+  NODE_DEFINE_CONSTANT(target, O_TRUNC);
+  // file status flags
+  NODE_DEFINE_CONSTANT(target, O_APPEND);
+//  NODE_DEFINE_CONSTANT(target, O_ASYNC); 
+  NODE_DEFINE_CONSTANT(target, O_CLOEXEC);
+  NODE_DEFINE_CONSTANT(target, O_DIRECT);
+  NODE_DEFINE_CONSTANT(target, O_DIRECTORY);
+  NODE_DEFINE_CONSTANT(target, O_EXCL);
+  NODE_DEFINE_CONSTANT(target, O_LARGEFILE);
+  NODE_DEFINE_CONSTANT(target, O_NOATIME);
+  NODE_DEFINE_CONSTANT(target, O_NOFOLLOW);
+//  NODE_DEFINE_CONSTANT(target, O_NONBLOCK);
+//  NODE_DEFINE_CONSTANT(target, O_SYNC);
+  NODE_DEFINE_CONSTANT(target, O_SYNC);
+
+  NODE_DEFINE_CONSTANT(target, S_IRWXU);
+
+  NODE_DEFINE_CONSTANT(target, S_IRUSR);
+  NODE_DEFINE_CONSTANT(target, S_IWUSR);
+  NODE_DEFINE_CONSTANT(target, S_IXUSR);
+
+  NODE_DEFINE_CONSTANT(target, S_IRWXG);
+
+  NODE_DEFINE_CONSTANT(target, S_IRGRP);
+  NODE_DEFINE_CONSTANT(target, S_IWGRP);
+  NODE_DEFINE_CONSTANT(target, S_IXGRP);
+
+  NODE_DEFINE_CONSTANT(target, S_IRWXO);
+
+  NODE_DEFINE_CONSTANT(target, S_IROTH);
+  NODE_DEFINE_CONSTANT(target, S_IWOTH);
+  NODE_DEFINE_CONSTANT(target, S_IXOTH);
 }
-
index b42d1ae..e48694c 100644 (file)
@@ -5,51 +5,9 @@
 
 namespace node {
 
-class FileSystem {
-public:
-  static v8::Handle<v8::Value> Rename (const v8::Arguments& args);
-  static int AfterRename (eio_req *req);
-
-  static v8::Handle<v8::Value> Stat (const v8::Arguments& args);
-  static int AfterStat (eio_req *req);
-
-  static v8::Handle<v8::Value> StrError (const v8::Arguments& args);
-};
-
-class File : ObjectWrap {
+class File {
 public:
   static void Initialize (v8::Handle<v8::Object> target);
-
-  virtual size_t size (void) { return sizeof(File); } 
-
-protected:
-  File (v8::Handle<v8::Object> handle);
-  ~File ();
-
-  static v8::Handle<v8::Value> New (const v8::Arguments& args);
-
-  static v8::Handle<v8::Value> Open (const v8::Arguments& args);
-  static int AfterOpen (eio_req *req);
-
-  static v8::Handle<v8::Value> Close (const v8::Arguments& args); 
-  static int AfterClose (eio_req *req);
-
-  static v8::Handle<v8::Value> Write (const v8::Arguments& args);
-  static int AfterWrite (eio_req *req);
-
-  static v8::Handle<v8::Value> Read (const v8::Arguments& args);
-  static int AfterRead (eio_req *req);
-
-  static v8::Handle<v8::Value> GetEncoding (v8::Local<v8::String> property, 
-                                            const v8::AccessorInfo& info);
-  static void SetEncoding (v8::Local<v8::String> property, 
-                            v8::Local<v8::Value> value, 
-                            const v8::AccessorInfo& info);
-
-  bool HasUtf8Encoding (void);
-  int GetFD (void);
-
-  enum encoding encoding_;
 };
 
 } // namespace node
index a4caafc..66879b2 100644 (file)
@@ -5,14 +5,13 @@ node.fs.exists = function (path, callback) {
 }
 
 node.fs.cat = function (path, encoding, callback) {
-  var file = new node.fs.File();
-  file.encoding = encoding;
+  var file = new node.fs.File({encoding: encoding});
 
   file.onError = function (method, errno, msg) {
     callback(-1);
   };
 
-  var content = (file.encoding == "raw" ? "" : []);
+  var content = (encoding == node.fs.UTF8 ? "" : []);
   var pos = 0;
   var chunkSize = 10*1024;
 
@@ -38,112 +37,144 @@ node.fs.cat = function (path, encoding, callback) {
   });
 }
 
-node.fs.File.prototype.open = function (path, mode, callback) {
-  this._addAction("open", [path, mode], callback);
-};
+node.fs.File = function (options) {
+  var self = this;
+  options = options || {};
+
+  if (options.encoding === undefined)
+    self.encoding = node.fs.RAW;
+  else
+    self.encoding = options.encoding
+
+  //node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding);
+  self.fd = options.fd || null;
+
+  var actionQueue = [];
+  
+  // Adds a method to the queue. 
+  function addAction (method, args, callback) {
+    var action = { method: method 
+                 , callback: callback
+                 , args: args
+                 };
+    //node.debug("add action: " + JSON.stringify(action));
+    actionQueue.push(action);
+
+    // If the queue was empty, immediately call the method.
+    if (actionQueue.length == 1) act();
+  }
 
-node.fs.File.prototype.close = function (callback) {
-  this._addAction("close", [], callback);
-};
+  // called after each action finishes (when it returns from the thread pool)
+  function poll () {
+    var action = actionQueue[0];
 
-node.fs.File.prototype.read = function (length, pos, callback) {
-  this._addAction("read", [length, pos], callback);
-};
+    var errno = arguments[0]; 
 
-node.fs.File.prototype.write = function (buf, pos, callback) {
-  this._addAction("write", [buf, pos], callback);
-};
+    if (errno < 0) {
+      if (self.onError)
+        self.onError(action.method, errno, node.fs.strerror(errno));
+      actionQueue = []; // empty the queue.
+      return;
+    }
 
-node.fs.File.prototype.print = function (data, callback) {
-  this.write(data, -1, callback);
-};
+    var rest = [];
+    for (var i = 1; i < arguments.length; i++)
+      rest.push(arguments[i]);
 
-node.fs.File.prototype.puts = function (data, callback) {
-  this.write(data + "\n", -1, callback);
-};
+    //node.debug("poll action: " + JSON.stringify(action));
+    //node.debug("poll rest: " + JSON.stringify(rest));
 
-// Some explanation of the File binding.
-//
-// All file operations are blocking. To get around this they are executed
-// in a thread pool in C++ (libeio). 
-//
-// The ordering of method calls to a file should be preserved, so they are
-// only executed one at a time. A queue, called _actionQueue is employed. 
-//
-// The constructor File() is implemented in C++. It initlizes 
-// the member _actionQueue = []
-//
-// Any of the methods called on a file are put into this queue. When they
-// reach the head of the queue they will be executed. C++ calls the
-// method _pollActions each time it becomes idle. If there is no action
-// currently being executed then _pollActions will not be called. When
-// actions are added to an empty _actionQueue, they will be immediately
-// executed.
-//
-// When an action has completed, the C++ side is looks at the first
-// element of _actionQueue in order to get a handle on the callback
-// function. Only after that completion callback has been made can the
-// action be shifted out of the queue.
-// 
-// See File::CallTopCallback() in file.cc for the other side of the binding.
-
-
-node.fs.File.prototype._addAction = function (method, args, callback) {
-  // This adds a method to the queue. 
-  var action = { method: method 
-               , callback: callback
-               , args: args
-               };
-  this._actionQueue.push(action);
-
-  // If the queue was empty, immediately call the method.
-  if (this._actionQueue.length == 1) this._act();
-};
+    if (action.callback)
+      action.callback.apply(this, rest);
 
-node.fs.File.prototype._act = function () {
-  // peek at the head of the queue
-  var action = this._actionQueue[0];
-  if (action) {
-    // execute the c++ version of the method. the c++ version 
-    // is gotten by appending "_ffi_" to the method name.
-    this["_ffi_" + action.method].apply(this, action.args);
+    actionQueue.shift();
+    act();
   }
-};
-
-// called from C++ after each action finishes
-// (i.e. when it returns from the thread pool)
-node.fs.File.prototype._pollActions = function () {
-  var action = this._actionQueue[0];
-
-  var errno = arguments[0]; 
 
-  if (errno < 0) {
-    if (this.onError)
-      this.onError(action.method, errno, node.fs.strerror(errno));
-    this._actionQueue = []; // empty the queue.
-    return;
+  function act () {
+    var action = actionQueue[0]; // peek at the head of the queue
+    if (action) {
+      internal_methods[action.method].apply(this, action.args);
+    }
   }
 
-  var rest = [];
-  for (var i = 1; i < arguments.length; i++)
-    rest.push(arguments[i]);
+  var internal_methods = {
+    open: function (path, mode) {
+      var m = node.fs.O_RDONLY;
+      switch (mode) {
+        case "r":
+          m = node.fs.O_RDONLY;
+          break;
+        case "r+":
+          m = node.fs.O_RDWR;
+          break;
+        case "w":
+          m = O_CREAT | O_TRUNC | O_WRONLY;
+          break;
+        case "w+":
+          m = O_CREAT | O_TRUNC | O_RDWR;
+          break;
+        case "a":
+          m = O_APPEND | O_CREAT | O_WRONLY; 
+          break;
+        case "a+":
+          m = O_APPEND | O_CREAT | O_RDWR; 
+          break;
+        default:
+          throw "Unknown mode";
+      }
 
-  if (action.callback)
-    action.callback.apply(this, rest);
+      node.fs.open(path, m, 0666, function (status, fd) {
+        self.fd = fd;
+        poll(status, fd);
+      });
+    },
+
+    close: function ( ) {
+      node.fs.close(self.fd, function (status) {
+        self.fd = null;
+        poll(status);
+      });
+    }, 
+
+    read: function (length, position) {
+      //node.debug("encoding: " + self.encoding);
+      node.fs.read(self.fd, length, position, self.encoding, poll);
+    },
+
+    write: function (data, position) {
+      node.fs.write(self.fd, data, position, poll);
+    }
+  };
 
-  this._actionQueue.shift();
+  self.open = function (path, mode, callback) {
+    addAction("open", [path, mode], callback);
+  };
+   
+  self.close = function (callback) {
+    addAction("close", [], callback);
+  };
 
-  this._act();
-};
+  self.read = function (length, pos, callback) {
+    addAction("read", [length, pos], callback);
+  };
 
-stdout = new node.fs.File();
-stdout.fd = node.fs.File.STDOUT_FILENO;
+  self.write = function (buf, pos, callback) {
+    addAction("write", [buf, pos], callback);
+  };
+
+  self.print = function (data, callback) {
+    return self.write(data, null, callback);
+  };
 
-stderr = new node.fs.File();
-stderr.fd = node.fs.File.STDERR_FILENO;
+  self.puts = function (data, callback) {
+    return self.write(data + "\n", null, callback);
+  };
+};
 
-stdin = new node.fs.File();
-stdin.fd = node.fs.File.STDIN_FILENO;
+stdout = new node.fs.File({ fd: node.fs.STDOUT_FILENO });
+stderr = new node.fs.File({ fd: node.fs.STDERR_FILENO });
+stdin = new node.fs.File({ fd: node.fs.STDIN_FILENO });
 
 puts = function (data, callback) {
   stdout.puts(data, callback);
index d6db49d..7d1d296 100644 (file)
@@ -12,6 +12,8 @@ namespace node {
 #define NODE_SET_METHOD(obj, name, callback) \
   obj->Set(NODE_SYMBOL(name), v8::FunctionTemplate::New(callback)->GetFunction())
 #define NODE_UNWRAP(type, value) static_cast<type*>(node::ObjectWrap::Unwrap(value))
+#define NODE_DEFINE_CONSTANT(target, constant) \
+  (target)->Set(v8::String::NewSymbol(#constant), v8::Integer::New(constant))
 
 #define NODE_SET_PROTOTYPE_METHOD(templ, name, callback)                  \
 do {                                                                      \
index e3d9d16..6bdef37 100644 (file)
@@ -134,7 +134,7 @@ clearInterval = clearTimeout;
   }
 
   function loadScript (filename, target, callback) {
-    node.fs.cat(filename, "utf8", function (status, content) {
+    node.fs.cat(filename, node.fs.UTF8, function (status, content) {
       if (status != 0) {
         stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status));
         node.exit(1);