From dd691decd2845de001c61c82db4c8faf0b1a3c22 Mon Sep 17 00:00:00 2001 From: Ryan Date: Fri, 17 Apr 2009 18:54:29 +0200 Subject: [PATCH] file system methods to be queued. --- src/file.cc | 349 ++++++++++++++++++++++++++------------------------------- src/file.js | 60 +++++----- src/main.js | 7 +- test/simple.js | 14 ++- 4 files changed, 209 insertions(+), 221 deletions(-) diff --git a/src/file.cc b/src/file.cc index 6d4ff15..832c63e 100644 --- a/src/file.cc +++ b/src/file.cc @@ -13,57 +13,87 @@ using namespace v8; #define FD_SYMBOL v8::String::NewSymbol("fd") #define ACTION_QUEUE_SYMBOL v8::String::NewSymbol("_actionQueue") -class File; -class Callback { - public: - Callback(Handle v); - ~Callback(); - Local Call(Handle recv, int argc, Handle argv[]); - File *file; - private: - Persistent handle_; -}; +// This is the file system object which contains methods +// for accessing the file system (like rename, mkdir, etC). +// In javascript it is called "File". +static Persistent fs; -Callback::Callback (Handle v) +static void +CallTopCallback (Handle handle, const int argc, Handle argv[]) { HandleScope scope; - Handle f = Handle::Cast(v); - handle_ = Persistent::New(f); -} -Callback::~Callback () -{ - handle_.Dispose(); - handle_.Clear(); // necessary? -} + Local queue_value = handle->Get(ACTION_QUEUE_SYMBOL); + assert(queue_value->IsArray()); -Local -Callback::Call (Handle recv, int argc, Handle argv[]) -{ - HandleScope scope; - Local r = handle_->Call(recv, argc, argv); - return scope.Close(r); + Local queue = Local::Cast(queue_value); + Local top_value = queue->Get(Integer::New(0)); + if (top_value->IsObject()) { + Local top = top_value->ToObject(); + Local callback_value = top->Get(String::NewSymbol("callback")); + if (callback_value->IsFunction()) { + Handle callback = Handle::Cast(callback_value); + + TryCatch try_catch; + callback->Call(handle, argc, argv); + if(try_catch.HasCaught()) { + node_fatal_exception(try_catch); + return; + } + } + } + + // poll_actions + Local poll_actions_value = handle->Get(String::NewSymbol("_pollActions")); + assert(poll_actions_value->IsFunction()); + Handle poll_actions = Handle::Cast(poll_actions_value); + + poll_actions->Call(handle, 0, NULL); } -static int -AfterRename (eio_req *req) +static void +InitActionQueue (Handle handle) { - Callback *callback = static_cast(req->data); - if (callback != NULL) { - HandleScope scope; - const int argc = 2; - Local argv[argc]; - - argv[0] = Integer::New(req->errorno); - argv[1] = String::New(strerror(req->errorno)); - - callback->Call(Context::GetCurrent()->Global(), argc, argv); - delete callback; - } - return 0; + handle->Set(ACTION_QUEUE_SYMBOL, Array::New()); } -JS_METHOD(rename) +class File { +public: + File (Handle handle); + ~File (); + + static File* Unwrap (Handle handle); + + static Handle Open (const Arguments& args); + static int AfterOpen (eio_req *req); + + static Handle Close (const Arguments& args); + static int AfterClose (eio_req *req); + + static Handle Write (const Arguments& args); + static int AfterWrite (eio_req *req); + + static Handle Read (const Arguments& args); + static int AfterRead (eio_req *req); + +private: + bool HasUtf8Encoding (void); + Persistent handle_; + int GetFD (void); + static void MakeWeak (Persistent _, void *data); +}; + +class FileSystem { +public: + static Handle Rename (const Arguments& args); + static int AfterRename (eio_req *req); + + static Handle Stat (const Arguments& args); + static int AfterStat (eio_req *req); +}; + +Handle +FileSystem::Rename (const Arguments& args) { if (args.Length() < 2) return Undefined(); @@ -73,67 +103,25 @@ JS_METHOD(rename) String::Utf8Value path(args[0]->ToString()); String::Utf8Value new_path(args[1]->ToString()); - Callback *callback = NULL; - if (!args[2]->IsUndefined()) callback = new Callback(args[2]); - - eio_req *req = eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, callback); + eio_req *req = eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL); node_eio_submit(req); return Undefined(); } -static int -AfterStat (eio_req *req) +int +FileSystem::AfterRename (eio_req *req) { - Callback *callback = static_cast(req->data); - if (callback != NULL) { - HandleScope scope; - const int argc = 3; - Local argv[argc]; - - Local stats = Object::New(); - argv[0] = stats; - argv[1] = Integer::New(req->errorno); - argv[2] = String::New(strerror(req->errorno)); - - if (req->result == 0) { - struct stat *s = static_cast(req->ptr2); - - /* ID of device containing file */ - stats->Set(JS_SYMBOL("dev"), Integer::New(s->st_dev)); - /* inode number */ - stats->Set(JS_SYMBOL("ino"), Integer::New(s->st_ino)); - /* protection */ - stats->Set(JS_SYMBOL("mode"), Integer::New(s->st_mode)); - /* number of hard links */ - stats->Set(JS_SYMBOL("nlink"), Integer::New(s->st_nlink)); - /* user ID of owner */ - stats->Set(JS_SYMBOL("uid"), Integer::New(s->st_uid)); - /* group ID of owner */ - stats->Set(JS_SYMBOL("gid"), Integer::New(s->st_gid)); - /* device ID (if special file) */ - stats->Set(JS_SYMBOL("rdev"), Integer::New(s->st_rdev)); - /* total size, in bytes */ - stats->Set(JS_SYMBOL("size"), Integer::New(s->st_size)); - /* blocksize for filesystem I/O */ - stats->Set(JS_SYMBOL("blksize"), Integer::New(s->st_blksize)); - /* number of blocks allocated */ - stats->Set(JS_SYMBOL("blocks"), Integer::New(s->st_blocks)); - /* time of last access */ - stats->Set(JS_SYMBOL("atime"), Date::New(1000*static_cast(s->st_atime))); - /* time of last modification */ - stats->Set(JS_SYMBOL("mtime"), Date::New(1000*static_cast(s->st_mtime))); - /* time of last status change */ - stats->Set(JS_SYMBOL("ctime"), Date::New(1000*static_cast(s->st_ctime))); - } - - callback->Call(Context::GetCurrent()->Global(), argc, argv); - delete callback; - } + HandleScope scope; + const int argc = 1; + Local argv[argc]; + argv[0] = Integer::New(req->errorno); + CallTopCallback(fs, argc, argv); return 0; } -JS_METHOD(stat) +Handle +FileSystem::Stat (const Arguments& args) { if (args.Length() < 1) return v8::Undefined(); @@ -142,54 +130,72 @@ JS_METHOD(stat) String::Utf8Value path(args[0]->ToString()); - Callback *callback = NULL; - if (!args[1]->IsUndefined()) callback = new Callback(args[1]); - - eio_req *req = eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback); + eio_req *req = eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, NULL); node_eio_submit(req); return Undefined(); } -///////////////////// FILE ///////////////////// - -class File { -public: - File (Handle handle); - ~File (); - - static File* Unwrap (Handle handle); +int +FileSystem::AfterStat (eio_req *req) +{ + HandleScope scope; - static Handle Open (const Arguments& args); - static int AfterOpen (eio_req *req); + const int argc = 2; + Local argv[argc]; + argv[0] = Integer::New(req->errorno); - static Handle Close (const Arguments& args); - static int AfterClose (eio_req *req); + Local stats = Object::New(); + argv[1] = stats; - static Handle Write (const Arguments& args); - static int AfterWrite (eio_req *req); + if (req->result == 0) { + struct stat *s = static_cast(req->ptr2); + + /* ID of device containing file */ + stats->Set(JS_SYMBOL("dev"), Integer::New(s->st_dev)); + /* inode number */ + stats->Set(JS_SYMBOL("ino"), Integer::New(s->st_ino)); + /* protection */ + stats->Set(JS_SYMBOL("mode"), Integer::New(s->st_mode)); + /* number of hard links */ + stats->Set(JS_SYMBOL("nlink"), Integer::New(s->st_nlink)); + /* user ID of owner */ + stats->Set(JS_SYMBOL("uid"), Integer::New(s->st_uid)); + /* group ID of owner */ + stats->Set(JS_SYMBOL("gid"), Integer::New(s->st_gid)); + /* device ID (if special file) */ + stats->Set(JS_SYMBOL("rdev"), Integer::New(s->st_rdev)); + /* total size, in bytes */ + stats->Set(JS_SYMBOL("size"), Integer::New(s->st_size)); + /* blocksize for filesystem I/O */ + stats->Set(JS_SYMBOL("blksize"), Integer::New(s->st_blksize)); + /* number of blocks allocated */ + stats->Set(JS_SYMBOL("blocks"), Integer::New(s->st_blocks)); + /* time of last access */ + stats->Set(JS_SYMBOL("atime"), Date::New(1000*static_cast(s->st_atime))); + /* time of last modification */ + stats->Set(JS_SYMBOL("mtime"), Date::New(1000*static_cast(s->st_mtime))); + /* time of last status change */ + stats->Set(JS_SYMBOL("ctime"), Date::New(1000*static_cast(s->st_ctime))); + } - static Handle Read (const Arguments& args); - static int AfterRead (eio_req *req); + CallTopCallback(fs, argc, argv); + + return 0; +} -private: - bool HasUtf8Encoding (void); - int GetFD (void); - static void MakeWeak (Persistent _, void *data); - void CallTopCallback (const int argc, Handle argv[]); - Persistent handle_; -}; +///////////////////// FILE ///////////////////// File::File (Handle handle) { HandleScope scope; handle_ = Persistent::New(handle); + InitActionQueue(handle); + Handle external = External::New(this); handle_->SetInternalField(0, external); handle_.MakeWeak(this, File::MakeWeak); - - handle_->Set(ACTION_QUEUE_SYMBOL, Array::New()); } File::~File () @@ -222,38 +228,6 @@ File::GetFD (void) int fd = fd_value->IntegerValue(); } -void -File::CallTopCallback (const int argc, Handle argv[]) -{ - HandleScope scope; - - Local queue_value = handle_->Get(ACTION_QUEUE_SYMBOL); - assert(queue_value->IsArray()); - - Local queue = Local::Cast(queue_value); - Local top_value = queue->Get(Integer::New(0)); - if (top_value->IsObject()) { - Local top = top_value->ToObject(); - Local callback_value = top->Get(String::NewSymbol("callback")); - if (callback_value->IsFunction()) { - Handle callback = Handle::Cast(callback_value); - - TryCatch try_catch; - callback->Call(handle_, argc, argv); - if(try_catch.HasCaught()) { - node_fatal_exception(try_catch); - return; - } - } - } - - // poll_actions - Local poll_actions_value = handle_->Get(String::NewSymbol("_pollActions")); - assert(poll_actions_value->IsFunction()); - Handle poll_actions = Handle::Cast(poll_actions_value); - - poll_actions->Call(handle_, 0, NULL); -} void File::MakeWeak (Persistent _, void *data) @@ -289,7 +263,7 @@ File::AfterClose (eio_req *req) const int argc = 1; Local argv[argc]; argv[0] = Integer::New(req->errorno); - file->CallTopCallback(argc, argv); + CallTopCallback(file->handle_, argc, argv); return 0; } @@ -331,11 +305,8 @@ File::Open (const Arguments& args) } } - // Get the current umask - mode_t mask = umask(0); - umask(mask); - - eio_req *req = eio_open (*path, flags, mask, EIO_PRI_DEFAULT, File::AfterOpen, file); + // TODO how should the mode be set? + eio_req *req = eio_open (*path, flags, 0666, EIO_PRI_DEFAULT, File::AfterOpen, file); node_eio_submit(req); return Undefined(); @@ -354,7 +325,7 @@ File::AfterOpen (eio_req *req) const int argc = 1; Handle argv[argc]; argv[0] = Integer::New(req->errorno); - file->CallTopCallback(argc, argv); + CallTopCallback(file->handle_, argc, argv); return 0; } @@ -423,7 +394,7 @@ File::AfterWrite (eio_req *req) Local argv[argc]; argv[0] = Integer::New(req->errorno); argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0); - file->CallTopCallback(argc, argv); + CallTopCallback(file->handle_, argc, argv); return 0; } @@ -458,23 +429,26 @@ File::AfterRead (eio_req *req) Local argv[argc]; argv[0] = Integer::New(req->errorno); - Local buffer; - size_t length = req->result; char *buf = static_cast(req->ptr2); - if (file->HasUtf8Encoding()) { - argv[1] = String::New(buf, req->result); + if(req->result == 0) { + // eof + argv[1] = Local::New(Null()); } else { - // raw encoding - Local array = Array::New(length); - for (int i = 0; i < length; i++) { - array->Set(Integer::New(i), Integer::New(buf[i])); + size_t length = req->result; + if (file->HasUtf8Encoding()) { + // utf8 encoding + argv[1] = String::New(buf, req->result); + } else { + // raw encoding + Local array = Array::New(length); + for (int i = 0; i < length; i++) { + array->Set(Integer::New(i), Integer::New(buf[i])); + } + argv[1] = array; } - argv[1] = array; } - - file->CallTopCallback(argc, argv); - + CallTopCallback(file->handle_, argc, argv); return 0; } @@ -492,28 +466,27 @@ NewFile (const Arguments& args) void NodeInit_file (Handle target) { - HandleScope scope; + if (!fs.IsEmpty()) + return; - Local fs = Object::New(); - target->Set(String::NewSymbol("fs"), fs); - - JS_SET_METHOD(fs, "rename", rename); - JS_SET_METHOD(fs, "stat", stat); + HandleScope scope; Local file_template = FunctionTemplate::New(NewFile); file_template->InstanceTemplate()->SetInternalFieldCount(1); - target->Set(String::NewSymbol("File"), file_template->GetFunction()); - // class methods for File - file_template->GetFunction()->Set(String::NewSymbol("STDIN_FILENO"), - Integer::New(STDIN_FILENO)); + fs = Persistent::New(file_template->GetFunction()); + InitActionQueue(fs); - file_template->GetFunction()->Set(String::NewSymbol("STDOUT_FILENO"), - Integer::New(STDOUT_FILENO)); + target->Set(String::NewSymbol("File"), fs); - file_template->GetFunction()->Set(String::NewSymbol("STDERR_FILENO"), - Integer::New(STDERR_FILENO)); + // file system methods + JS_SET_METHOD(fs, "_ffi_rename", FileSystem::Rename); + JS_SET_METHOD(fs, "_ffi_stat", FileSystem::Stat); + fs->Set(String::NewSymbol("STDIN_FILENO"), Integer::New(STDIN_FILENO)); + fs->Set(String::NewSymbol("STDOUT_FILENO"), Integer::New(STDOUT_FILENO)); + fs->Set(String::NewSymbol("STDERR_FILENO"), Integer::New(STDERR_FILENO)); + // file methods JS_SET_METHOD(file_template->InstanceTemplate(), "_ffi_open", File::Open); JS_SET_METHOD(file_template->InstanceTemplate(), "_ffi_close", File::Close); JS_SET_METHOD(file_template->InstanceTemplate(), "_ffi_write", File::Write); diff --git a/src/file.js b/src/file.js index b291fc7..513bd75 100644 --- a/src/file.js +++ b/src/file.js @@ -1,3 +1,27 @@ +File.rename = function (file1, file2, callback) { + this._addAction("rename", [file1, file2], callback); +}; + +File.prototype.puts = function (data, callback) { + this.write(data + "\n", callback); +}; + +File.prototype.open = function (path, mode, callback) { + this._addAction("open", [path, mode], callback); +}; + +File.prototype.close = function (callback) { + this._addAction("close", [], callback); +}; + +File.prototype.write = function (buf, callback) { + this._addAction("write", [buf], callback); +}; + +File.prototype.read = function (length, callback) { + this._addAction("read", [length], callback); +}; + // Some explanation of the File binding. // // All file operations are blocking. To get around this they are executed @@ -20,28 +44,10 @@ // 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. - -File.prototype.puts = function (data, callback) { - this.write(data + "\n", callback); -}; - -File.prototype.open = function (path, mode, callback) { - this._addAction("open", [path, mode], callback); -}; - -File.prototype.close = function (callback) { - this._addAction("close", [], callback); -}; - -File.prototype.write = function (buf, callback) { - this._addAction("write", [buf], callback); -}; - -File.prototype.read = function (length, callback) { - this._addAction("read", [length], callback); -}; - -File.prototype._addAction = function (method, args, callback) { +// +// See File::CallTopCallback() in file.cc to see the other side of the +// binding. +File._addAction = File.prototype._addAction = function (method, args, callback) { this._actionQueue.push({ method: method , callback: callback , args: args @@ -49,7 +55,7 @@ File.prototype._addAction = function (method, args, callback) { if (this._actionQueue.length == 1) this._act(); } -File.prototype._act = function () { +File._act = File.prototype._act = function () { var action = this._actionQueue[0]; if (action) this["_ffi_" + action.method].apply(this, action.args); @@ -57,7 +63,7 @@ File.prototype._act = function () { // called from C++ after each action finishes // (i.e. when it returns from the thread pool) -File.prototype._pollActions = function () { +File._pollActions = File.prototype._pollActions = function () { this._actionQueue.shift(); this._act(); }; @@ -68,8 +74,6 @@ stdout.fd = File.STDOUT_FILENO; var stderr = new File(); stderr.fd = File.STDERR_FILENO; +var stdin = new File(); +stdin.fd = File.STDIN_FILENO; - -Array.prototype.encodeUtf8 = function () { - return String.fromCharCode.apply(String, this); -} diff --git a/src/main.js b/src/main.js index 964968d..2a74003 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,11 @@ // module search paths node.includes = ["."]; +// This is useful for dealing with raw encodings. +Array.prototype.encodeUtf8 = function () { + return String.fromCharCode.apply(String, this); +} + node.path = new function () { this.join = function () { var joined = ""; @@ -42,7 +47,7 @@ function __require (path, loading_file) { } else { //filename = node.path.join(node.path.dirname(loading_file), path); } - stdout.puts("require: " + filename); + //stdout.puts("require: " + filename); var source = node.blocking.cat(filename); diff --git a/test/simple.js b/test/simple.js index 7179d6c..b1da2bf 100644 --- a/test/simple.js +++ b/test/simple.js @@ -1,18 +1,24 @@ var f = new File; -f.open("/tmp/world", "r+", function (status) { +f.open("/tmp/world", "w+", function (status) { if (status == 0) { stdout.puts("file open"); - f.write("hello world\n") + f.write("hello world\n"); f.write("something else.\n", function () { stdout.puts("written. "); }); f.read(100, function (status, buf) { - stdout.puts("read: >>" + buf.encodeUtf8() + "<<"); + if (buf) + stdout.puts("read: >>" + buf.encodeUtf8() + "<<"); }); } else { stdout.puts("file open failed: " + status.toString()); } - f.close(function () { stdout.puts("closed.") }); + f.close(function (status) { + stdout.puts("closed: " + status.toString()); + File.rename("/tmp/world", "/tmp/hello", function (status) { + stdout.puts("rename: " + status.toString()); + }); + }); }); -- 2.7.4