diff --git a/core/core.js b/core/core.js index 969e935e..d163630d 100644 --- a/core/core.js +++ b/core/core.js @@ -105,7 +105,7 @@ function databaseGetAll() { return getDatabase(this).getAll(); } -function getPackages() { +async function getPackages() { var packages = []; var packageOwners = File.readDirectory("packages/"); for (var i = 0; i < packageOwners.length; i++) { @@ -116,7 +116,7 @@ function getPackages() { packages.push({ owner: packageOwners[i], name: packageNames[j], - manifest: getManifest("packages/" + packageOwners[i] + "/" + packageNames[j] + "/" + packageNames[j] + ".js"), + manifest: await getManifest("packages/" + packageOwners[i] + "/" + packageNames[j] + "/" + packageNames[j] + ".js"), }); } } @@ -195,7 +195,17 @@ function readFileUtf8(fileName) { return new TextDecoder("UTF-8").decode(File.readFile(fileName)); } -function getManifest(fileName) { +let gManifestCache = {}; + +async function getManifest(fileName) { + let oldEntry = gManifestCache[fileName]; + let stat = await File.stat(fileName); + if (oldEntry) { + if (oldEntry.stat.mtime == stat.mtime && oldEntry.stat.size == stat.size) { + return oldEntry.manifest; + } + } + let manifest = []; let lines = readFileUtf8(fileName).split("\n").map(x => x.trimRight()); for (let i = 0; i < lines.length; i++) { @@ -212,6 +222,12 @@ function getManifest(fileName) { print("ERROR: getManifest(" + fileName + "): ", error); // Oh well. No manifest. } + + gManifestCache[fileName] = { + stat: stat, + manifest: result, + }; + return result; } @@ -220,7 +236,7 @@ function packageNameToPath(name) { return "packages/" + process.packageOwner + "/" + name + "/"; } -function getProcess(packageOwner, packageName, key, options) { +async function getProcess(packageOwner, packageName, key, options) { var process = gProcesses[key]; if (!process && !(options && "create" in options && !options.create) @@ -229,7 +245,7 @@ function getProcess(packageOwner, packageName, key, options) { try { print("Creating task for " + packageName + " " + key); var fileName = "packages/" + packageOwner + "/" + packageName + "/" + packageName + ".js"; - var manifest = getManifest(fileName); + var manifest = await getManifest(fileName); process = {}; process.key = key; process.index = gProcessIndex++; diff --git a/core/terminal.js b/core/terminal.js index 700f2654..3b6dcc82 100644 --- a/core/terminal.js +++ b/core/terminal.js @@ -143,7 +143,7 @@ function socket(request, response, client) { } options.credentials = credentials; - response.onMessage = function(event) { + response.onMessage = async function(event) { if (event.opCode == 0x1 || event.opCode == 0x2) { var message; try { @@ -164,7 +164,7 @@ function socket(request, response, client) { response.send(JSON.stringify({lines: [{action: "session", sessionId: sessionId, credentials: credentials}]}), 0x1); options.terminalApi = message.terminalApi || []; - process = getSessionProcess(packageOwner, packageName, sessionId, options); + process = await getSessionProcess(packageOwner, packageName, sessionId, options); process.terminal.readOutput(function(message) { response.send(JSON.stringify(message), 0x1); }); @@ -226,7 +226,7 @@ function socket(request, response, client) { } } -function handler(request, response, packageOwner, packageName, uri) { +async function handler(request, response, packageOwner, packageName, uri) { var found = false; if (badName(packageOwner) || badName(packageName)) { @@ -292,7 +292,7 @@ function handler(request, response, packageOwner, packageName, uri) { } } } else if (uri === "/submit") { - var process = getServiceProcess(packageOwner, packageName, "submit"); + var process = await getServiceProcess(packageOwner, packageName, "submit"); process.lastActive = Date.now(); return process.ready.then(function() { var payload = form.decodeForm(request.body, form.decodeForm(request.query)); @@ -308,7 +308,7 @@ function handler(request, response, packageOwner, packageName, uri) { }); }); } else if (uri === "/atom") { - var process = getServiceProcess(packageOwner, packageName, "atom"); + var process = await getServiceProcess(packageOwner, packageName, "atom"); process.lastActive = Date.now(); return process.ready.then(function() { var payload = form.decodeForm(request.body, form.decodeForm(request.query)); diff --git a/src/File.cpp b/src/File.cpp index b40f2f7a..297dd32e 100644 --- a/src/File.cpp +++ b/src/File.cpp @@ -1,6 +1,7 @@ #include "File.h" #include "Task.h" +#include "TaskTryCatch.h" #include #include @@ -14,6 +15,14 @@ #include #endif +double timeSpecToDouble(uv_timespec_t& timeSpec); + +struct FileStatData { + Task* _task; + promiseid_t _promise; + uv_fs_t _request; +}; + void File::configure(v8::Isolate* isolate, v8::Handle global) { v8::Local fileTemplate = v8::ObjectTemplate::New(isolate); fileTemplate->Set(v8::String::NewFromUtf8(isolate, "readFile"), v8::FunctionTemplate::New(isolate, readFile)); @@ -22,6 +31,7 @@ void File::configure(v8::Isolate* isolate, v8::Handle global fileTemplate->Set(v8::String::NewFromUtf8(isolate, "writeFile"), v8::FunctionTemplate::New(isolate, writeFile)); fileTemplate->Set(v8::String::NewFromUtf8(isolate, "renameFile"), v8::FunctionTemplate::New(isolate, renameFile)); fileTemplate->Set(v8::String::NewFromUtf8(isolate, "unlinkFile"), v8::FunctionTemplate::New(isolate, unlinkFile)); + fileTemplate->Set(v8::String::NewFromUtf8(isolate, "stat"), v8::FunctionTemplate::New(isolate, stat)); global->Set(v8::String::NewFromUtf8(isolate, "File"), fileTemplate); } @@ -134,3 +144,48 @@ void File::makeDirectory(const v8::FunctionCallbackInfo& args) { int result = uv_fs_mkdir(task->getLoop(), &req, *v8::String::Utf8Value(directory), 0755, 0); args.GetReturnValue().Set(result); } + +void File::stat(const v8::FunctionCallbackInfo& args) { + if (Task* task = Task::get(args.GetIsolate())) { + v8::HandleScope scope(args.GetIsolate()); + v8::Handle path = args[0]->ToString(); + + promiseid_t promise = task->allocatePromise(); + + FileStatData* data = new FileStatData; + data->_task = task; + data->_promise = promise; + data->_request.data = data; + + int result = uv_fs_stat(task->getLoop(), &data->_request, *v8::String::Utf8Value(path), onStatComplete); + if (result) { + task->resolvePromise(promise, v8::Number::New(args.GetIsolate(), result)); + delete data; + } + args.GetReturnValue().Set(task->getPromise(promise)); + } +} + +double timeSpecToDouble(uv_timespec_t& timeSpec) { + return timeSpec.tv_sec + static_cast(timeSpec.tv_nsec) / 1e9; +} + +void File::onStatComplete(uv_fs_t* request) { + FileStatData* data = reinterpret_cast(request->data); + v8::EscapableHandleScope scope(data->_task->getIsolate()); + TaskTryCatch tryCatch(data->_task); + v8::Isolate* isolate = data->_task->getIsolate(); + v8::Context::Scope contextScope(v8::Local::New(isolate, data->_task->getContext())); + + if (request->result) { + data->_task->rejectPromise(data->_promise, v8::Number::New(data->_task->getIsolate(), request->result)); + } else { + v8::Handle result = v8::Object::New(isolate); + result->Set(v8::String::NewFromUtf8(isolate, "mtime"), v8::Number::New(isolate, timeSpecToDouble(request->statbuf.st_mtim))); + result->Set(v8::String::NewFromUtf8(isolate, "ctime"), v8::Number::New(isolate, timeSpecToDouble(request->statbuf.st_ctim))); + result->Set(v8::String::NewFromUtf8(isolate, "atime"), v8::Number::New(isolate, timeSpecToDouble(request->statbuf.st_atim))); + result->Set(v8::String::NewFromUtf8(isolate, "size"), v8::Number::New(isolate, request->statbuf.st_size)); + data->_task->resolvePromise(data->_promise, result); + } + delete data; +} diff --git a/src/File.h b/src/File.h index 7b693eb1..f09d58c9 100644 --- a/src/File.h +++ b/src/File.h @@ -3,6 +3,8 @@ #include +typedef struct uv_fs_s uv_fs_t; + class File { public: static void configure(v8::Isolate* isolate, v8::Handle global); @@ -14,6 +16,9 @@ private: static void makeDirectory(const v8::FunctionCallbackInfo& args); static void unlinkFile(const v8::FunctionCallbackInfo& args); static void renameFile(const v8::FunctionCallbackInfo& args); + static void stat(const v8::FunctionCallbackInfo& args); + + static void onStatComplete(uv_fs_t* request); }; #endif