#include "Task.h" #include "Database.h" #include "File.h" #include "Serialize.h" #include "Socket.h" #include "TaskStub.h" #include "TaskTryCatch.h" #include "TlsContextWrapper.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 static const int STDIN_FILENO = 0; #else #include #endif extern v8::Platform* gPlatform; int gNextTaskId = 1; int Task::_count; struct ExportRecord { v8::Persistent > _persistent; int _useCount; ExportRecord(v8::Isolate* isolate, v8::Handle function) : _persistent(isolate, function), _useCount(0) { } void ref() { ++_useCount; } bool release() { return --_useCount == 0; } }; struct ImportRecord { v8::Persistent > _persistent; exportid_t _export; taskid_t _task; Task* _owner; int _useCount; ImportRecord(v8::Isolate* isolate, v8::Handle function, exportid_t exportId, taskid_t taskId, Task* owner) : _persistent(isolate, function), _export(exportId), _task(taskId), _owner(owner), _useCount(0) { _persistent.SetWeak(this, ImportRecord::onRelease); } void ref() { if (_useCount++ == 0) { // Make a strong ref again until an in-flight function call is finished. _persistent.ClearWeak(); } } void release() { if (--_useCount == 0) { // All in-flight calls are finished. Make weak. _persistent.SetWeak(this, ImportRecord::onRelease); } } static void onRelease(const v8::WeakCallbackData& data) { ImportRecord* import = data.GetParameter(); import->_owner->releaseExport(import->_task, import->_export); for (size_t i = 0; i < import->_owner->_imports.size(); ++i) { if (import->_owner->_imports[i] == import) { import->_owner->_imports.erase(import->_owner->_imports.begin() + i); break; } } import->_persistent.Reset(); delete import; } }; Task::Task() { _loop = uv_loop_new(); ++_count; v8::Isolate::CreateParams options; options.array_buffer_allocator = &_allocator; _isolate = v8::Isolate::New(options); _isolate->SetData(0, this); _isolate->SetCaptureStackTraceForUncaughtExceptions(true, 16); } Task::~Task() { _exportObject.Reset(); _sourceObject.Reset(); { v8::Isolate::Scope isolateScope(_isolate); v8::HandleScope handleScope(_isolate); _context.Reset(); } _isolate->Dispose(); _isolate = 0; uv_loop_delete(_loop); --_count; } v8::Handle Task::getContext() { return v8::Local::New(_isolate, _context); } void Task::run() { { v8::Isolate::Scope isolateScope(_isolate); v8::HandleScope handleScope(_isolate); v8::Context::Scope contextScope(v8::Local::New(_isolate, _context)); uv_run(_loop, UV_RUN_DEFAULT); } _promises.clear(); _exports.clear(); _imports.clear(); } v8::Handle Task::loadFile(v8::Isolate* isolate, const char* fileName) { v8::Handle value; std::ifstream file(fileName, std::ios_base::in | std::ios_base::binary | std::ios_base::ate); std::streampos fileSize = file.tellg(); if (fileSize >= 0) { file.seekg(0, std::ios_base::beg); char* buffer = new char[fileSize]; file.read(buffer, fileSize); std::string contents(buffer, buffer + fileSize); value = v8::String::NewFromOneByte(isolate, reinterpret_cast(buffer), v8::String::kNormalString, fileSize); delete[] buffer; } return value; } void Task::activate() { v8::Isolate::Scope isolateScope(_isolate); v8::HandleScope handleScope(_isolate); v8::Handle global = v8::ObjectTemplate::New(); if (!_importObject.IsEmpty()) { v8::Local imports(_importObject.Get(_isolate)); v8::Handle keys = imports->GetOwnPropertyNames(); for (size_t i = 0; i < keys->Length(); ++i) { global->SetAccessor(keys->Get(i).As(), getImportProperty); } } global->Set(v8::String::NewFromUtf8(_isolate, "print"), v8::FunctionTemplate::New(_isolate, print)); global->Set(v8::String::NewFromUtf8(_isolate, "setTimeout"), v8::FunctionTemplate::New(_isolate, setTimeout)); global->SetAccessor(v8::String::NewFromUtf8(_isolate, "parent"), parent); global->Set(v8::String::NewFromUtf8(_isolate, "exit"), v8::FunctionTemplate::New(_isolate, exit)); global->Set(v8::String::NewFromUtf8(_isolate, "utf8Length"), v8::FunctionTemplate::New(_isolate, utf8Length)); global->SetAccessor(v8::String::NewFromUtf8(_isolate, "exports"), getExports, setExports); global->SetAccessor(v8::String::NewFromUtf8(_isolate, "imports"), getImports); global->SetAccessor(v8::String::NewFromUtf8(_isolate, "version"), version); global->SetAccessor(v8::String::NewFromUtf8(_isolate, "statistics"), statistics); if (_trusted) { global->Set(v8::String::NewFromUtf8(_isolate, "require"), v8::FunctionTemplate::New(_isolate, require)); global->Set(v8::String::NewFromUtf8(_isolate, "Database"), v8::FunctionTemplate::New(_isolate, Database::create)); global->Set(v8::String::NewFromUtf8(_isolate, "Socket"), v8::FunctionTemplate::New(_isolate, Socket::create)); global->Set(v8::String::NewFromUtf8(_isolate, "Task"), v8::FunctionTemplate::New(_isolate, TaskStub::create)); global->Set(v8::String::NewFromUtf8(_isolate, "TlsContext"), v8::FunctionTemplate::New(_isolate, TlsContextWrapper::create)); File::configure(_isolate, global); } else { global->Set(v8::String::NewFromUtf8(_isolate, "require"), v8::FunctionTemplate::New(_isolate, childRequire)); } v8::Local context = v8::Context::New(_isolate, 0, global); _context = v8::Persistent >(_isolate, context); v8::Context::Scope contextScope(v8::Local::New(_isolate, _context)); v8::Local exportObject = v8::Object::New(_isolate); _exportObject = v8::Persistent >(_isolate, exportObject); } void Task::activate(const v8::FunctionCallbackInfo& args) { Task* task = Task::get(args.GetIsolate()); task->activate(); } void Task::print(const v8::FunctionCallbackInfo& args) { v8::HandleScope scope(args.GetIsolate()); v8::Local context = args.GetIsolate()->GetCurrentContext(); v8::Handle json = context->Global()->Get(v8::String::NewFromUtf8(args.GetIsolate(), "JSON"))->ToObject(); v8::Handle stringify = v8::Handle::Cast(json->Get(v8::String::NewFromUtf8(args.GetIsolate(), "stringify"))); Task* task = reinterpret_cast(args.GetIsolate()->GetData(0)); TaskTryCatch tryCatch(task); std::cout << "Task[" << task << ':' << task->_scriptName << "]>"; for (int i = 0; i < args.Length(); i++) { std::cout << ' '; v8::Handle arg = args[i]; if (arg->IsNativeError()) { arg = Serialize::storeMessage(task, v8::Exception::CreateMessage(arg)); } v8::String::Utf8Value value(stringify->Call(json, 1, &arg)); std::cout << (*value ? *value : "(null)"); } std::cout << '\n'; } struct TimeoutData { Task* _task; v8::Persistent > _callback; }; void Task::setTimeout(const v8::FunctionCallbackInfo& args) { v8::HandleScope scope(args.GetIsolate()); Task* task = reinterpret_cast(args.GetIsolate()->GetData(0)); TimeoutData* timeout = new TimeoutData(); timeout->_task = task; v8::Persistent > function(args.GetIsolate(), v8::Handle::Cast(args[0])); timeout->_callback = function; uv_timer_t* timer = new uv_timer_t(); uv_timer_init(task->_loop, timer); timer->data = timeout; uv_timer_start(timer, timeoutCallback, static_cast(args[1].As()->Value()), 0); } void Task::timeoutCallback(uv_timer_t* handle) { TimeoutData* timeout = reinterpret_cast(handle->data); TaskTryCatch tryCatch(timeout->_task); v8::HandleScope scope(timeout->_task->_isolate); v8::Handle function = v8::Local::New(timeout->_task->_isolate, timeout->_callback); function->Call(v8::Undefined(timeout->_task->_isolate), 0, 0); delete timeout; } void Task::utf8Length(const v8::FunctionCallbackInfo& args) { Task* task = reinterpret_cast(args.GetIsolate()->GetData(0)); TaskTryCatch tryCatch(task); v8::HandleScope scope(task->_isolate); args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), args[0].As()->Utf8Length())); } void Task::exit(const v8::FunctionCallbackInfo& args) { ::exit(args[0]->Int32Value()); } void Task::kill() { if (!_killed && _isolate) { _killed = true; v8::V8::TerminateExecution(_isolate); } } bool Task::execute(const char* fileName) { bool executed = false; v8::Isolate::Scope isolateScope(_isolate); v8::HandleScope handleScope(_isolate); v8::Context::Scope contextScope(v8::Local::New(_isolate, _context)); v8::Handle name = v8::String::NewFromUtf8(_isolate, fileName); v8::Handle source = loadFile(_isolate, fileName); std::cout << "Running script " << fileName << "\n"; if (!_scriptName.size()) { _scriptName = fileName; } if (!_path.size()) { std::string path = _scriptName; size_t position = path.rfind('/'); if (position != std::string::npos) { path.resize(position + 1); } else { path = "."; } _path.push_back(path); } if (!source.IsEmpty()) { v8::Handle script = v8::Script::Compile(source, name); if (!script.IsEmpty()) { script->Run(); std::cout << "Script " << fileName << " completed\n"; executed = true; } else { std::cerr << "Failed to compile: " << fileName << ".\n"; } } else { std::string message; message = "Failed to load file: "; message += fileName; _isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(_isolate, message.c_str()))); } return executed; } void Task::invokeExport(const v8::FunctionCallbackInfo& args) { Task* sender = Task::get(args.GetIsolate()); v8::Handle data = v8::Handle::Cast(args.Data()); exportid_t exportId = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "export"))->Int32Value(); taskid_t recipientId = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "task"))->Int32Value(); for (size_t i = 0; i < sender->_imports.size(); ++i) { if (sender->_imports[i]->_task == recipientId && sender->_imports[i]->_export == exportId) { sender->_imports[i]->ref(); break; } } v8::Local array = v8::Array::New(args.GetIsolate(), args.Length() + 1); for (int i = 0; i < args.Length(); ++i) { array->Set(i + 1, args[i]); } if (TaskStub* recipient = sender->get(recipientId)) { promiseid_t promise = sender->allocatePromise(); sendPromiseExportMessage(sender, recipient, kInvokeExport, promise, exportId, array); args.GetReturnValue().Set(sender->getPromise(promise)); } else { args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "Invoking a function on a nonexistant task.")))); } } v8::Handle Task::invokeExport(TaskStub* from, Task* to, exportid_t exportId, const std::vector& buffer) { v8::Handle result; if (to->_exports[exportId]) { v8::Handle arguments = v8::Handle::Cast(Serialize::load(to, from, buffer)); std::vector > argumentArray; for (size_t i = 1; i < arguments->Length(); ++i) { argumentArray.push_back(arguments->Get(i)); } v8::Handle function = v8::Local::New(to->_isolate, to->_exports[exportId]->_persistent); v8::Handle* argumentPointer = 0; if (argumentArray.size()) { argumentPointer = &*argumentArray.begin(); } result = function->Call(v8::Handle::Cast(arguments->Get(0)), argumentArray.size(), argumentPointer); } else { std::cout << to->_scriptName << ": That's not an export we have (exportId=" << exportId << ", exports = " << to->_exports.size() << ")\n"; } from->getStream().send(kReleaseImport, reinterpret_cast(&exportId), sizeof(exportId)); return result; } void Task::sendPromiseResolve(Task* from, TaskStub* to, promiseid_t promise, v8::Handle result) { if (!result.IsEmpty() && result->IsPromise()) { // We're not going to serialize/deserialize a promise... v8::Handle data = v8::Object::New(from->_isolate); data->Set(v8::String::NewFromUtf8(from->_isolate, "task"), v8::Int32::New(from->_isolate, to->getId())); data->Set(v8::String::NewFromUtf8(from->_isolate, "promise"), v8::Int32::New(from->_isolate, promise)); v8::Handle promise = v8::Handle::Cast(result); v8::Handle then = v8::Function::New(from->_isolate, invokeThen, data); promise->Then(then); v8::Handle catchCallback = v8::Function::New(from->_isolate, invokeCatch, data); promise->Catch(catchCallback); from->_isolate->RunMicrotasks(); } else { sendPromiseMessage(from, to, kResolvePromise, promise, result); } } void Task::sendPromiseReject(Task* from, TaskStub* to, promiseid_t promise, v8::Handle result) { if (!result.IsEmpty() && result->IsPromise()) { // We're not going to serialize/deserialize a promise... v8::Handle data = v8::Object::New(from->_isolate); data->Set(v8::String::NewFromUtf8(from->_isolate, "task"), v8::Int32::New(from->_isolate, to->getId())); data->Set(v8::String::NewFromUtf8(from->_isolate, "promise"), v8::Int32::New(from->_isolate, promise)); v8::Handle promise = v8::Handle::Cast(result); v8::Handle then = v8::Function::New(from->_isolate, invokeThen, data); promise->Then(then); v8::Handle catchCallback = v8::Function::New(from->_isolate, invokeCatch, data); promise->Catch(catchCallback); from->_isolate->RunMicrotasks(); } else { sendPromiseMessage(from, to, kRejectPromise, promise, result); } } void Task::sendPromiseMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promise, v8::Handle result) { if (to) { std::vector buffer; buffer.insert(buffer.end(), reinterpret_cast(&promise), reinterpret_cast(&promise) + sizeof(promise)); if (!result.IsEmpty() && !result->IsUndefined() && !result->IsNull()) { Serialize::store(from, buffer, result); } to->getStream().send(messageType, &*buffer.begin(), buffer.size()); } else { std::cerr << "Sending to a NULL task.\n"; } } void Task::sendPromiseExportMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promise, exportid_t exportId, v8::Handle result) { std::vector buffer; buffer.insert(buffer.end(), reinterpret_cast(&promise), reinterpret_cast(&promise) + sizeof(promise)); buffer.insert(buffer.end(), reinterpret_cast(&exportId), reinterpret_cast(&exportId) + sizeof(exportId)); if (!result.IsEmpty() && !result->IsUndefined() && !result->IsNull()) { Serialize::store(from, buffer, result); } to->getStream().send(messageType, &*buffer.begin(), buffer.size()); } TaskStub* Task::get(taskid_t taskId) { return taskId == kParentId ? _parent : _children[taskId]; } void Task::invokeThen(const v8::FunctionCallbackInfo& args) { Task* from = reinterpret_cast(args.GetIsolate()->GetData(0)); v8::Handle data = v8::Handle::Cast(args.Data()); TaskStub* to = from->get(data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "task"))->Int32Value()); promiseid_t promise = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "promise"))->Int32Value(); sendPromiseMessage(from, to, kResolvePromise, promise, args[0]); } void Task::invokeCatch(const v8::FunctionCallbackInfo& args) { Task* from = reinterpret_cast(args.GetIsolate()->GetData(0)); v8::Handle data = v8::Handle::Cast(args.Data()); TaskStub* to = from->get(data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "task"))->Int32Value()); promiseid_t promise = data->Get(v8::String::NewFromUtf8(args.GetIsolate(), "promise"))->Int32Value(); sendPromiseMessage(from, to, kRejectPromise, promise, args[0]); } void Task::parent(v8::Local property, const v8::PropertyCallbackInfo& args) { Task* task = reinterpret_cast(args.GetIsolate()->GetData(0)); if (task->_parent) { args.GetReturnValue().Set(task->_parent->getTaskObject()); } else { args.GetReturnValue().Set(v8::Undefined(task->_isolate)); } } void Task::version(v8::Local property, const v8::PropertyCallbackInfo& args) { Task* task = reinterpret_cast(args.GetIsolate()->GetData(0)); args.GetReturnValue().Set(v8::String::NewFromUtf8(task->_isolate, v8::V8::GetVersion())); } void Task::getImportProperty(v8::Local property, const v8::PropertyCallbackInfo& args) { v8::Local imports = Task::get(args.GetIsolate())->_importObject.Get(args.GetIsolate()); args.GetReturnValue().Set(imports->Get(property)); } void Task::getImports(v8::Local property, const v8::PropertyCallbackInfo& args) { args.GetReturnValue().Set(v8::Local::New(args.GetIsolate(), Task::get(args.GetIsolate())->_importObject)); } void Task::getExports(v8::Local property, const v8::PropertyCallbackInfo& args) { args.GetReturnValue().Set(v8::Local::New(args.GetIsolate(), Task::get(args.GetIsolate())->_exportObject)); } void Task::setExports(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& args) { Task::get(args.GetIsolate())->_exportObject = v8::Persistent >(args.GetIsolate(), v8::Handle::Cast(value)); } Task* Task::get(v8::Isolate* isolate) { return reinterpret_cast(isolate->GetData(0)); } promiseid_t Task::allocatePromise() { promiseid_t promiseId; do { promiseId = _nextPromise++; } while (_promises.find(promiseId) != _promises.end()); v8::Persistent > promise(_isolate, v8::Promise::Resolver::New(_isolate)); _promises[promiseId] = promise; return promiseId; } v8::Handle Task::getPromise(promiseid_t promise) { v8::Handle result; if (!_promises[promise].IsEmpty()) { result = v8::Local::New(_isolate, _promises[promise]); } return result; } void Task::resolvePromise(promiseid_t promise, v8::Handle value) { TaskTryCatch tryCatch(this); if (!_promises[promise].IsEmpty()) { v8::HandleScope handleScope(_isolate); v8::Handle resolver = v8::Local::New(_isolate, _promises[promise]); resolver->Resolve(value); _isolate->RunMicrotasks(); _promises[promise].Reset(); _promises.erase(promise); } } void Task::rejectPromise(promiseid_t promise, v8::Handle value) { TaskTryCatch tryCatch(this); if (!_promises[promise].IsEmpty()) { v8::HandleScope handleScope(_isolate); v8::Handle resolver = v8::Local::New(_isolate, _promises[promise]); resolver->Reject(value); _isolate->RunMicrotasks(); _promises[promise].Reset(); _promises.erase(promise); } } exportid_t Task::exportFunction(v8::Handle function) { exportid_t exportId = -1; v8::Handle exportName = v8::String::NewFromUtf8(_isolate, "export"); v8::Local value = function->GetHiddenValue(exportName); if (!value.IsEmpty() && value->IsNumber()) { exportid_t foundId = value->ToInteger(_isolate)->Int32Value(); if (_exports[foundId]) { exportId = foundId; } } if (exportId == -1) { do { exportId = _nextExport++; } while (_exports[_nextExport]); ExportRecord* record = new ExportRecord(_isolate, function); function->SetHiddenValue(exportName, v8::Integer::New(_isolate, exportId)); _exports[exportId] = record; } if (_exports[exportId]) { _exports[exportId]->ref(); } return exportId; } void Task::releaseExport(taskid_t taskId, exportid_t exportId) { if (TaskStub* task = get(taskId)) { std::vector buffer; buffer.insert(buffer.end(), reinterpret_cast(&exportId), reinterpret_cast(&exportId) + sizeof(exportId)); task->getStream().send(kReleaseExport, &*buffer.begin(), buffer.size()); } } v8::Handle Task::addImport(taskid_t taskId, exportid_t exportId) { v8::Local data = v8::Object::New(_isolate); data->Set(v8::String::NewFromUtf8(_isolate, "export"), v8::Int32::New(_isolate, exportId)); data->Set(v8::String::NewFromUtf8(_isolate, "task"), v8::Int32::New(_isolate, taskId)); v8::Local function = v8::Function::New(_isolate, Task::invokeExport, data); _imports.push_back(new ImportRecord(_isolate, function, exportId, taskId, this)); return function; } void Task::statistics(v8::Local property, const v8::PropertyCallbackInfo& args) { Task* task = reinterpret_cast(args.GetIsolate()->GetData(0)); args.GetReturnValue().Set(task->getStatistics()); } v8::Handle Task::getStatistics() { v8::Handle result = v8::Object::New(_isolate); result->Set(v8::String::NewFromUtf8(_isolate, "sockets"), v8::Integer::New(_isolate, Socket::getCount())); result->Set(v8::String::NewFromUtf8(_isolate, "openSockets"), v8::Integer::New(_isolate, Socket::getOpenCount())); result->Set(v8::String::NewFromUtf8(_isolate, "promises"), v8::Integer::New(_isolate, _promises.size())); result->Set(v8::String::NewFromUtf8(_isolate, "exports"), v8::Integer::New(_isolate, _exports.size())); result->Set(v8::String::NewFromUtf8(_isolate, "imports"), v8::Integer::New(_isolate, _imports.size())); result->Set(v8::String::NewFromUtf8(_isolate, "tlsContexts"), v8::Integer::New(_isolate, TlsContextWrapper::getCount())); uv_rusage_t usage; if (uv_getrusage(&usage) == 0) { result->Set(v8::String::NewFromUtf8(_isolate, "utime"), v8::Number::New(_isolate, usage.ru_utime.tv_sec + usage.ru_utime.tv_usec / 1000000.0)); result->Set(v8::String::NewFromUtf8(_isolate, "stime"), v8::Number::New(_isolate, usage.ru_stime.tv_sec + usage.ru_stime.tv_usec / 1000000.0)); result->Set(v8::String::NewFromUtf8(_isolate, "maxrss"), v8::Number::New(_isolate, usage.ru_maxrss)); } return result; } void Task::onReceivePacket(int packetType, const char* begin, size_t length, void* userData) { TaskStub* stub = reinterpret_cast(userData); TaskStub* from = stub; Task* to = stub->getOwner(); TaskTryCatch tryCatch(to); v8::HandleScope scope(to->_isolate); switch (static_cast(packetType)) { case kStatistics: { promiseid_t promise; std::memcpy(&promise, begin, sizeof(promise)); v8::Handle result = to->getStatistics(); sendPromiseResolve(to, from, promise, result); } break; case kInvokeExport: { promiseid_t promise; exportid_t exportId; std::memcpy(&promise, begin, sizeof(promise)); std::memcpy(&exportId, begin + sizeof(promise), sizeof(exportId)); v8::TryCatch tryCatch; v8::Handle result = invokeExport(from, to, exportId, std::vector(begin + sizeof(promiseid_t) + sizeof(exportid_t), begin + length)); if (tryCatch.HasCaught()) { sendPromiseReject(to, from, promise, Serialize::store(to, tryCatch)); } else { sendPromiseResolve(to, from, promise, result); } } break; case kResolvePromise: case kRejectPromise: { v8::Handle arg; promiseid_t promise; std::memcpy(&promise, begin, sizeof(promiseid_t)); if (length > sizeof(promiseid_t)) { arg = Serialize::load(to, from, std::vector(begin + sizeof(promiseid_t), begin + length)); } else { arg = v8::Undefined(to->_isolate); } if (static_cast(packetType) == kResolvePromise) { to->resolvePromise(promise, arg); } else { to->rejectPromise(promise, arg); } } break; case kReleaseExport: assert(length == sizeof(exportid_t)); exportid_t exportId; memcpy(&exportId, begin, sizeof(exportId)); if (to->_exports[exportId]) { if (to->_exports[exportId]->release()) { to->_exports.erase(exportId); } } break; case kReleaseImport: { assert(length == sizeof(exportid_t)); exportid_t exportId; memcpy(&exportId, begin, sizeof(exportId)); for (size_t i = 0; i < to->_imports.size(); ++i) { if (to->_imports[i]->_task == from->getId() && to->_imports[i]->_export == exportId) { to->_imports[i]->release(); break; } } } break; case kSetRequires: { v8::Handle result = v8::Handle::Cast(Serialize::load(to, from, std::vector(begin, begin + length))); to->_sourceObject = v8::Persistent >(to->_isolate, result); } break; case kActivate: to->activate(); break; case kExecute: { assert(length >= sizeof(promiseid_t)); promiseid_t promise; std::memcpy(&promise, begin, sizeof(promiseid_t)); v8::TryCatch tryCatch(to->_isolate); tryCatch.SetCaptureMessage(true); tryCatch.SetVerbose(true); v8::Handle value = Serialize::load(to, from, std::vector(begin + sizeof(promiseid_t), begin + length)); v8::Handle object = v8::Handle::Cast(value); v8::Handle source = v8::Handle::Cast(object->Get(v8::String::NewFromUtf8(to->_isolate, "source"))); v8::Handle name = v8::Handle::Cast(object->Get(v8::String::NewFromUtf8(to->_isolate, "name"))); to->executeSource(source, name); if (tryCatch.HasCaught()) { sendPromiseReject(to, from, promise, Serialize::store(to, tryCatch)); } else { sendPromiseResolve(to, from, promise, v8::Undefined(to->_isolate)); } } break; case kKill: ::exit(1); break; case kSetImports: { v8::Handle result = v8::Handle::Cast(Serialize::load(to, from, std::vector(begin, begin + length))); to->_importObject = v8::Persistent >(to->_isolate, result); } break; case kGetExports: promiseid_t promise; assert(length == sizeof(promise)); std::memcpy(&promise, begin, sizeof(promiseid_t)); v8::Handle result = v8::Local::New(to->_isolate, to->_exportObject); sendPromiseResolve(to, from, promise, result); break; } } void Task::configureFromStdin() { _parent = TaskStub::createParent(this, STDIN_FILENO); } std::string Task::resolveRequire(const std::string& require) { std::string result; for (size_t i = 0; i < _path.size(); ++i) { std::string& path = _path[i]; std::cout << "Looking in " << path << " for " << require << "\n"; std::string test; if (require.find("..") == std::string::npos && require.find('/') == std::string::npos) { test = path + require; } if (test.size() && (require.size() < 3 || require.rfind(".js") != require.size() - 3)) { test += ".js"; } uv_fs_t request; if (uv_fs_access(_loop, &request, test.c_str(), R_OK, 0) == 0) { result = test; break; } } return result; } void Task::require(const v8::FunctionCallbackInfo& args) { v8::HandleScope scope(args.GetIsolate()); Task* task = Task::get(args.GetIsolate()); v8::String::Utf8Value pathValue(args[0]); if (*pathValue) { std::string unresolved(*pathValue, *pathValue + pathValue.length()); std::string path = task->resolveRequire(unresolved); if (!path.size()) { args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), ("require(): Unable to resolve module: " + unresolved).c_str())))); } else { ScriptExportMap::iterator it = task->_scriptExports.find(path); if (it != task->_scriptExports.end()) { v8::Handle exports = v8::Local::New(args.GetIsolate(), it->second); args.GetReturnValue().Set(exports); } else { v8::Handle exports = v8::Object::New(args.GetIsolate()); task->_scriptExports[path] = v8::Persistent >(args.GetIsolate(), exports); v8::Handle name = v8::String::NewFromUtf8(args.GetIsolate(), path.c_str()); v8::Handle source = loadFile(args.GetIsolate(), path.c_str()); std::cout << "Requiring script " << path << "\n"; if (!source.IsEmpty()) { v8::Handle global = args.GetIsolate()->GetCurrentContext()->Global(); v8::Handle oldExports = global->Get(v8::String::NewFromUtf8(args.GetIsolate(), "exports")); global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), exports); v8::Handle script = v8::Script::Compile(source, name); if (!script.IsEmpty()) { script->Run(); std::cout << "Script " << path << " completed\n"; } else { std::cerr << "Failed to compile " << path << ".\n"; } global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), oldExports); args.GetReturnValue().Set(exports); } else { std::cerr << "Failed to load " << path << ".\n"; } } } } else { args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "require(): No module specified.")))); } } v8::Handle Task::executeSource(v8::Handle& source, v8::Handle& name) { v8::Isolate::Scope isolateScope(_isolate); v8::HandleScope handleScope(_isolate); v8::Context::Scope contextScope(v8::Local::New(_isolate, _context)); v8::Handle result; v8::String::Utf8Value nameValue(name); if (!source.IsEmpty()) { v8::Handle script = v8::Script::Compile(source, name); if (!script.IsEmpty()) { script->Run(); } else { std::cerr << "Failed to compile " << *nameValue << ".\n"; } } else { result = _isolate->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(_isolate, (std::string("Failed to load ") + *nameValue + ".").c_str()))); } return result; } void Task::childRequire(const v8::FunctionCallbackInfo& args) { v8::HandleScope scope(args.GetIsolate()); Task* task = Task::get(args.GetIsolate()); v8::Handle requiresObject = v8::Local::New(args.GetIsolate(), task->_sourceObject); if (!requiresObject.IsEmpty()) { v8::Handle name = args[0]->ToString(); v8::String::Utf8Value nameValue(name); ScriptExportMap::iterator it = task->_scriptExports.find(*nameValue); if (it != task->_scriptExports.end()) { v8::Handle exports = v8::Local::New(args.GetIsolate(), it->second); args.GetReturnValue().Set(exports); } else { v8::Handle exports = v8::Object::New(args.GetIsolate()); v8::Handle source = v8::Handle::Cast(requiresObject->Get(args[0])); if (!source.IsEmpty()) { v8::Handle global = args.GetIsolate()->GetCurrentContext()->Global(); v8::Handle oldExports = global->Get(v8::String::NewFromUtf8(args.GetIsolate(), "exports")); global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), exports); v8::Handle script = v8::Script::Compile(source, name); if (!script.IsEmpty()) { script->Run(); } else { std::cerr << "Failed to compile " << *nameValue << ".\n"; } global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), oldExports); args.GetReturnValue().Set(exports); } else { args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), (std::string("Failed to load ") + *nameValue + ".").c_str())))); } } } }