git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3245 ed5197a5-7fde-0310-b194-c3ffbd925b24
829 lines
32 KiB
C++
829 lines
32 KiB
C++
#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 <algorithm>
|
|
#include <assert.h>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <libplatform/libplatform.h>
|
|
#include <map>
|
|
#include <sys/types.h>
|
|
#include <uv.h>
|
|
#include <v8.h>
|
|
#include <v8-platform.h>
|
|
|
|
#ifdef _WIN32
|
|
static const int STDIN_FILENO = 0;
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
extern v8::Platform* gPlatform;
|
|
int gNextTaskId = 1;
|
|
|
|
int Task::_count;
|
|
|
|
struct ExportRecord {
|
|
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _persistent;
|
|
int _useCount;
|
|
|
|
ExportRecord(v8::Isolate* isolate, v8::Handle<v8::Function> function)
|
|
: _persistent(isolate, function),
|
|
_useCount(0) {
|
|
}
|
|
|
|
void ref() {
|
|
++_useCount;
|
|
}
|
|
|
|
bool release() {
|
|
return --_useCount == 0;
|
|
}
|
|
};
|
|
|
|
struct ImportRecord {
|
|
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _persistent;
|
|
exportid_t _export;
|
|
taskid_t _task;
|
|
Task* _owner;
|
|
int _useCount;
|
|
|
|
ImportRecord(v8::Isolate* isolate, v8::Handle<v8::Function> 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<v8::Function, ImportRecord >& 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<v8::Context> Task::getContext() {
|
|
return v8::Local<v8::Context>::New(_isolate, _context);
|
|
}
|
|
|
|
void Task::run() {
|
|
{
|
|
v8::Isolate::Scope isolateScope(_isolate);
|
|
v8::HandleScope handleScope(_isolate);
|
|
v8::Context::Scope contextScope(v8::Local<v8::Context>::New(_isolate, _context));
|
|
uv_run(_loop, UV_RUN_DEFAULT);
|
|
}
|
|
_promises.clear();
|
|
_exports.clear();
|
|
_imports.clear();
|
|
}
|
|
|
|
v8::Handle<v8::String> Task::loadFile(v8::Isolate* isolate, const char* fileName) {
|
|
v8::Handle<v8::String> 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<const uint8_t*>(buffer), v8::String::kNormalString, fileSize);
|
|
delete[] buffer;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void Task::activate() {
|
|
v8::Isolate::Scope isolateScope(_isolate);
|
|
v8::HandleScope handleScope(_isolate);
|
|
|
|
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
|
|
|
|
if (!_importObject.IsEmpty()) {
|
|
v8::Local<v8::Object> imports(_importObject.Get(_isolate));
|
|
v8::Handle<v8::Array> keys = imports->GetOwnPropertyNames();
|
|
for (size_t i = 0; i < keys->Length(); ++i) {
|
|
global->SetAccessor(keys->Get(i).As<v8::String>(), 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<v8::Context> context = v8::Context::New(_isolate, 0, global);
|
|
_context = v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context> >(_isolate, context);
|
|
|
|
v8::Context::Scope contextScope(v8::Local<v8::Context>::New(_isolate, _context));
|
|
|
|
v8::Local<v8::Object> exportObject = v8::Object::New(_isolate);
|
|
_exportObject = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(_isolate, exportObject);
|
|
}
|
|
|
|
void Task::activate(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
Task* task = Task::get(args.GetIsolate());
|
|
task->activate();
|
|
}
|
|
|
|
void Task::print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
v8::HandleScope scope(args.GetIsolate());
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
v8::Handle<v8::Object> json = context->Global()->Get(v8::String::NewFromUtf8(args.GetIsolate(), "JSON"))->ToObject();
|
|
v8::Handle<v8::Function> stringify = v8::Handle<v8::Function>::Cast(json->Get(v8::String::NewFromUtf8(args.GetIsolate(), "stringify")));
|
|
Task* task = reinterpret_cast<Task*>(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<v8::Value> 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<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _callback;
|
|
};
|
|
|
|
void Task::setTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
v8::HandleScope scope(args.GetIsolate());
|
|
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
|
|
|
TimeoutData* timeout = new TimeoutData();
|
|
timeout->_task = task;
|
|
|
|
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > function(args.GetIsolate(), v8::Handle<v8::Function>::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<uint64_t>(args[1].As<v8::Number>()->Value()), 0);
|
|
}
|
|
|
|
void Task::timeoutCallback(uv_timer_t* handle) {
|
|
TimeoutData* timeout = reinterpret_cast<TimeoutData*>(handle->data);
|
|
TaskTryCatch tryCatch(timeout->_task);
|
|
v8::HandleScope scope(timeout->_task->_isolate);
|
|
v8::Handle<v8::Function> function = v8::Local<v8::Function>::New(timeout->_task->_isolate, timeout->_callback);
|
|
function->Call(v8::Undefined(timeout->_task->_isolate), 0, 0);
|
|
delete timeout;
|
|
}
|
|
|
|
void Task::utf8Length(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
|
TaskTryCatch tryCatch(task);
|
|
v8::HandleScope scope(task->_isolate);
|
|
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), args[0].As<v8::String>()->Utf8Length()));
|
|
}
|
|
|
|
void Task::exit(const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Context>::New(_isolate, _context));
|
|
|
|
v8::Handle<v8::String> name = v8::String::NewFromUtf8(_isolate, fileName);
|
|
|
|
v8::Handle<v8::String> 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<v8::Script> 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<v8::Value>& args) {
|
|
Task* sender = Task::get(args.GetIsolate());
|
|
v8::Handle<v8::Object> data = v8::Handle<v8::Object>::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<v8::Array> 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<v8::Value> Task::invokeExport(TaskStub* from, Task* to, exportid_t exportId, const std::vector<char>& buffer) {
|
|
v8::Handle<v8::Value> result;
|
|
if (to->_exports[exportId]) {
|
|
v8::Handle<v8::Array> arguments = v8::Handle<v8::Array>::Cast(Serialize::load(to, from, buffer));
|
|
std::vector<v8::Handle<v8::Value> > argumentArray;
|
|
for (size_t i = 1; i < arguments->Length(); ++i) {
|
|
argumentArray.push_back(arguments->Get(i));
|
|
}
|
|
v8::Handle<v8::Function> function = v8::Local<v8::Function>::New(to->_isolate, to->_exports[exportId]->_persistent);
|
|
v8::Handle<v8::Value>* argumentPointer = 0;
|
|
if (argumentArray.size()) {
|
|
argumentPointer = &*argumentArray.begin();
|
|
}
|
|
result = function->Call(v8::Handle<v8::Object>::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<char*>(&exportId), sizeof(exportId));
|
|
return result;
|
|
}
|
|
|
|
void Task::sendPromiseResolve(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result) {
|
|
if (!result.IsEmpty() && result->IsPromise()) {
|
|
// We're not going to serialize/deserialize a promise...
|
|
v8::Handle<v8::Object> 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<v8::Promise> promise = v8::Handle<v8::Promise>::Cast(result);
|
|
v8::Handle<v8::Function> then = v8::Function::New(from->_isolate, invokeThen, data);
|
|
promise->Then(then);
|
|
v8::Handle<v8::Function> 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<v8::Value> result) {
|
|
if (!result.IsEmpty() && result->IsPromise()) {
|
|
// We're not going to serialize/deserialize a promise...
|
|
v8::Handle<v8::Object> 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<v8::Promise> promise = v8::Handle<v8::Promise>::Cast(result);
|
|
v8::Handle<v8::Function> then = v8::Function::New(from->_isolate, invokeThen, data);
|
|
promise->Then(then);
|
|
v8::Handle<v8::Function> 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<v8::Value> result) {
|
|
if (to) {
|
|
std::vector<char> buffer;
|
|
buffer.insert(buffer.end(), reinterpret_cast<char*>(&promise), reinterpret_cast<char*>(&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<v8::Value> result) {
|
|
std::vector<char> buffer;
|
|
buffer.insert(buffer.end(), reinterpret_cast<char*>(&promise), reinterpret_cast<char*>(&promise) + sizeof(promise));
|
|
buffer.insert(buffer.end(), reinterpret_cast<char*>(&exportId), reinterpret_cast<char*>(&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<v8::Value>& args) {
|
|
Task* from = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
|
v8::Handle<v8::Object> data = v8::Handle<v8::Object>::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<v8::Value>& args) {
|
|
Task* from = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
|
v8::Handle<v8::Object> data = v8::Handle<v8::Object>::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<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
|
Task* task = reinterpret_cast<Task*>(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<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
|
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
|
args.GetReturnValue().Set(v8::String::NewFromUtf8(task->_isolate, v8::V8::GetVersion()));
|
|
}
|
|
|
|
void Task::getImportProperty(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
|
v8::Local<v8::Object> imports = Task::get(args.GetIsolate())->_importObject.Get(args.GetIsolate());
|
|
args.GetReturnValue().Set(imports->Get(property));
|
|
}
|
|
|
|
void Task::getImports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
|
args.GetReturnValue().Set(v8::Local<v8::Object>::New(args.GetIsolate(), Task::get(args.GetIsolate())->_importObject));
|
|
}
|
|
|
|
void Task::getExports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
|
args.GetReturnValue().Set(v8::Local<v8::Object>::New(args.GetIsolate(), Task::get(args.GetIsolate())->_exportObject));
|
|
}
|
|
|
|
void Task::setExports(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args) {
|
|
Task::get(args.GetIsolate())->_exportObject = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(args.GetIsolate(), v8::Handle<v8::Object>::Cast(value));
|
|
}
|
|
|
|
Task* Task::get(v8::Isolate* isolate) {
|
|
return reinterpret_cast<Task*>(isolate->GetData(0));
|
|
}
|
|
|
|
promiseid_t Task::allocatePromise() {
|
|
promiseid_t promiseId;
|
|
do {
|
|
promiseId = _nextPromise++;
|
|
} while (_promises.find(promiseId) != _promises.end());
|
|
v8::Persistent<v8::Promise::Resolver, v8::NonCopyablePersistentTraits<v8::Promise::Resolver> > promise(_isolate, v8::Promise::Resolver::New(_isolate));
|
|
_promises[promiseId] = promise;
|
|
return promiseId;
|
|
}
|
|
|
|
v8::Handle<v8::Promise::Resolver> Task::getPromise(promiseid_t promise) {
|
|
v8::Handle<v8::Promise::Resolver> result;
|
|
if (!_promises[promise].IsEmpty()) {
|
|
result = v8::Local<v8::Promise::Resolver>::New(_isolate, _promises[promise]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void Task::resolvePromise(promiseid_t promise, v8::Handle<v8::Value> value) {
|
|
TaskTryCatch tryCatch(this);
|
|
if (!_promises[promise].IsEmpty()) {
|
|
v8::HandleScope handleScope(_isolate);
|
|
v8::Handle<v8::Promise::Resolver> resolver = v8::Local<v8::Promise::Resolver>::New(_isolate, _promises[promise]);
|
|
resolver->Resolve(value);
|
|
_isolate->RunMicrotasks();
|
|
_promises[promise].Reset();
|
|
_promises.erase(promise);
|
|
}
|
|
}
|
|
|
|
void Task::rejectPromise(promiseid_t promise, v8::Handle<v8::Value> value) {
|
|
TaskTryCatch tryCatch(this);
|
|
if (!_promises[promise].IsEmpty()) {
|
|
v8::HandleScope handleScope(_isolate);
|
|
v8::Handle<v8::Promise::Resolver> resolver = v8::Local<v8::Promise::Resolver>::New(_isolate, _promises[promise]);
|
|
resolver->Reject(value);
|
|
_isolate->RunMicrotasks();
|
|
_promises[promise].Reset();
|
|
_promises.erase(promise);
|
|
}
|
|
}
|
|
|
|
exportid_t Task::exportFunction(v8::Handle<v8::Function> function) {
|
|
exportid_t exportId = -1;
|
|
v8::Handle<v8::String> exportName = v8::String::NewFromUtf8(_isolate, "export");
|
|
|
|
v8::Local<v8::Value> 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<char> buffer;
|
|
buffer.insert(buffer.end(), reinterpret_cast<char*>(&exportId), reinterpret_cast<char*>(&exportId) + sizeof(exportId));
|
|
task->getStream().send(kReleaseExport, &*buffer.begin(), buffer.size());
|
|
}
|
|
}
|
|
|
|
v8::Handle<v8::Function> Task::addImport(taskid_t taskId, exportid_t exportId) {
|
|
v8::Local<v8::Object> 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<v8::Function> 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<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
|
|
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
|
|
args.GetReturnValue().Set(task->getStatistics());
|
|
}
|
|
|
|
v8::Handle<v8::Object> Task::getStatistics() {
|
|
v8::Handle<v8::Object> 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<TaskStub*>(userData);
|
|
TaskStub* from = stub;
|
|
Task* to = stub->getOwner();
|
|
|
|
TaskTryCatch tryCatch(to);
|
|
v8::HandleScope scope(to->_isolate);
|
|
|
|
switch (static_cast<MessageType>(packetType)) {
|
|
case kStatistics:
|
|
{
|
|
promiseid_t promise;
|
|
std::memcpy(&promise, begin, sizeof(promise));
|
|
v8::Handle<v8::Value> 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<v8::Value> result = invokeExport(from, to, exportId, std::vector<char>(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<v8::Value> arg;
|
|
promiseid_t promise;
|
|
std::memcpy(&promise, begin, sizeof(promiseid_t));
|
|
if (length > sizeof(promiseid_t)) {
|
|
arg = Serialize::load(to, from, std::vector<char>(begin + sizeof(promiseid_t), begin + length));
|
|
}
|
|
else {
|
|
arg = v8::Undefined(to->_isolate);
|
|
}
|
|
if (static_cast<MessageType>(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<v8::Object> result = v8::Handle<v8::Object>::Cast(Serialize::load(to, from, std::vector<char>(begin, begin + length)));
|
|
to->_sourceObject = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(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<v8::Value> value = Serialize::load(to, from, std::vector<char>(begin + sizeof(promiseid_t), begin + length));
|
|
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
|
|
v8::Handle<v8::String> source = v8::Handle<v8::String>::Cast(object->Get(v8::String::NewFromUtf8(to->_isolate, "source")));
|
|
v8::Handle<v8::String> name = v8::Handle<v8::String>::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<v8::Object> result = v8::Handle<v8::Object>::Cast(Serialize::load(to, from, std::vector<char>(begin, begin + length)));
|
|
to->_importObject = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(to->_isolate, result);
|
|
}
|
|
break;
|
|
case kGetExports:
|
|
promiseid_t promise;
|
|
assert(length == sizeof(promise));
|
|
std::memcpy(&promise, begin, sizeof(promiseid_t));
|
|
v8::Handle<v8::Object> result = v8::Local<v8::Object>::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<v8::Value>& 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<v8::Object> exports = v8::Local<v8::Object>::New(args.GetIsolate(), it->second);
|
|
args.GetReturnValue().Set(exports);
|
|
} else {
|
|
v8::Handle<v8::Object> exports = v8::Object::New(args.GetIsolate());
|
|
task->_scriptExports[path] = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(args.GetIsolate(), exports);
|
|
|
|
v8::Handle<v8::String> name = v8::String::NewFromUtf8(args.GetIsolate(), path.c_str());
|
|
v8::Handle<v8::String> source = loadFile(args.GetIsolate(), path.c_str());
|
|
std::cout << "Requiring script " << path << "\n";
|
|
if (!source.IsEmpty()) {
|
|
v8::Handle<v8::Object> global = args.GetIsolate()->GetCurrentContext()->Global();
|
|
v8::Handle<v8::Value> oldExports = global->Get(v8::String::NewFromUtf8(args.GetIsolate(), "exports"));
|
|
global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), exports);
|
|
v8::Handle<v8::Script> 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<v8::Value> Task::executeSource(v8::Handle<v8::String>& source, v8::Handle<v8::String>& name) {
|
|
v8::Isolate::Scope isolateScope(_isolate);
|
|
v8::HandleScope handleScope(_isolate);
|
|
v8::Context::Scope contextScope(v8::Local<v8::Context>::New(_isolate, _context));
|
|
|
|
v8::Handle<v8::Value> result;
|
|
v8::String::Utf8Value nameValue(name);
|
|
if (!source.IsEmpty()) {
|
|
v8::Handle<v8::Script> 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<v8::Value>& args) {
|
|
v8::HandleScope scope(args.GetIsolate());
|
|
Task* task = Task::get(args.GetIsolate());
|
|
|
|
v8::Handle<v8::Object> requiresObject = v8::Local<v8::Object>::New(args.GetIsolate(), task->_sourceObject);
|
|
if (!requiresObject.IsEmpty()) {
|
|
v8::Handle<v8::String> name = args[0]->ToString();
|
|
v8::String::Utf8Value nameValue(name);
|
|
ScriptExportMap::iterator it = task->_scriptExports.find(*nameValue);
|
|
if (it != task->_scriptExports.end()) {
|
|
v8::Handle<v8::Object> exports = v8::Local<v8::Object>::New(args.GetIsolate(), it->second);
|
|
args.GetReturnValue().Set(exports);
|
|
} else {
|
|
v8::Handle<v8::Object> exports = v8::Object::New(args.GetIsolate());
|
|
v8::Handle<v8::String> source = v8::Handle<v8::String>::Cast(requiresObject->Get(args[0]));
|
|
if (!source.IsEmpty()) {
|
|
v8::Handle<v8::Object> global = args.GetIsolate()->GetCurrentContext()->Global();
|
|
v8::Handle<v8::Value> oldExports = global->Get(v8::String::NewFromUtf8(args.GetIsolate(), "exports"));
|
|
global->Set(v8::String::NewFromUtf8(args.GetIsolate(), "exports"), exports);
|
|
v8::Handle<v8::Script> 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()))));
|
|
}
|
|
}
|
|
}
|
|
}
|