Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Large refactor of file code.

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.
  • Loading branch information...
commit d1b0ce6d37accf5e0007df8f975d487f173b72fa 1 parent f3af6c6
@ry ry authored
Showing with 421 additions and 515 deletions.
  1. +294 −379 src/file.cc
  2. +1 −43 src/file.h
  3. +123 −92 src/file.js
  4. +2 −0  src/node.h
  5. +1 −1  src/node.js
View
673 src/file.cc
@@ -12,490 +12,405 @@
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);
}
-
View
44 src/file.h
@@ -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
View
215 src/file.js
@@ -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);
View
2  src/node.h
@@ -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 { \
View
2  src/node.js
@@ -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);
Please sign in to comment.
Something went wrong with that request. Please try again.