sandboxos => tildefriends

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3157 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
2016-03-12 18:50:43 +00:00
commit 7c6a377c0b
94 changed files with 27121 additions and 0 deletions

190
src/Database.cpp Normal file
View File

@ -0,0 +1,190 @@
#include "Database.h"
#include "Task.h"
#include <assert.h>
#include <sstream>
int Database::_count = 0;
Database::Database(Task* task) {
++_count;
_task = task;
v8::Handle<v8::External> data = v8::External::New(task->getIsolate(), this);
v8::Local<v8::ObjectTemplate> databaseTemplate = v8::ObjectTemplate::New(task->getIsolate());
databaseTemplate->SetInternalFieldCount(1);
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "get"), v8::FunctionTemplate::New(task->getIsolate(), get, data));
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "set"), v8::FunctionTemplate::New(task->getIsolate(), set, data));
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "remove"), v8::FunctionTemplate::New(task->getIsolate(), remove, data));
databaseTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "getAll"), v8::FunctionTemplate::New(task->getIsolate(), getAll, data));
v8::Local<v8::Object> databaseObject = databaseTemplate->NewInstance();
databaseObject->SetInternalField(0, v8::External::New(task->getIsolate(), this));
_object = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(task->getIsolate(), databaseObject);
}
Database::~Database() {
--_count;
}
void Database::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope handleScope(args.GetIsolate());
if (Database* database = new Database(Task::get(args.GetIsolate()))) {
if (database->open(args.GetIsolate(), *v8::String::Utf8Value(args[0].As<v8::String>()))) {
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), database->_object);
args.GetReturnValue().Set(result);
}
database->release();
}
}
bool Database::checkError(const char* command, int result) {
bool isError = false;
if (result != MDB_SUCCESS) {
isError = true;
std::ostringstream buffer;
buffer << command << " failed (" << result << "): " << mdb_strerror(result);
_task->getIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(_task->getIsolate(), buffer.str().c_str())));
}
return isError;
}
bool Database::open(v8::Isolate* isolate, const char* path) {
int result = mdb_env_create(&_environment);
if (checkError("mdb_env_create", result)) {
return false;
}
result = mdb_env_set_maxdbs(_environment, 10);
checkError("mdb_env_set_maxdbs", result);
result = mdb_env_open(_environment, path, 0, 0644);
if (!checkError("mdb_env_open", result)) {
result = mdb_txn_begin(_environment, 0, 0, &_transaction);
if (!checkError("mdb_txn_begin", result)) {
result = mdb_dbi_open(_transaction, NULL, MDB_CREATE, &_database);
if (!checkError("mdb_dbi_open", result)) {
result = mdb_txn_commit(_transaction);
checkError("mdb_txn_commit", result);
}
}
if (result != MDB_SUCCESS) {
mdb_txn_abort(_transaction);
}
}
if (result != MDB_SUCCESS) {
mdb_env_close(_environment);
}
return result == MDB_SUCCESS;
}
void Database::get(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Database* database = Database::get(args.Data())) {
int result = mdb_txn_begin(database->_environment, 0, MDB_RDONLY, &database->_transaction);
if (!database->checkError("mdb_txn_begin", result)) {
MDB_val key;
MDB_val value;
v8::String::Utf8Value keyString(args[0].As<v8::String>());
key.mv_data = *keyString;
key.mv_size = keyString.length();
if (mdb_get(database->_transaction, database->_database, &key, &value) == MDB_SUCCESS) {
args.GetReturnValue().Set(v8::String::NewFromUtf8(args.GetIsolate(), reinterpret_cast<const char*>(value.mv_data), v8::String::kNormalString, value.mv_size));
}
mdb_txn_reset(database->_transaction);
}
}
}
void Database::set(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Database* database = Database::get(args.Data())) {
int result = mdb_txn_begin(database->_environment, 0, 0, &database->_transaction);
if (!database->checkError("mdb_txn_begin", result)) {
MDB_val key;
MDB_val data;
v8::String::Utf8Value keyString(args[0].As<v8::String>());
key.mv_data = *keyString;
key.mv_size = keyString.length();
v8::String::Utf8Value valueString(args[1]->ToString(args.GetIsolate()));
data.mv_data = *valueString;
data.mv_size = valueString.length();
result = mdb_put(database->_transaction, database->_database, &key, &data, 0);
database->checkError("mdb_put", result);
mdb_txn_commit(database->_transaction);
}
}
}
void Database::remove(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Database* database = Database::get(args.Data())) {
int result = mdb_txn_begin(database->_environment, 0, 0, &database->_transaction);
if (!database->checkError("mdb_txn_begin", result)) {
MDB_val key;
v8::String::Utf8Value keyString(args[0].As<v8::String>());
key.mv_data = *keyString;
key.mv_size = keyString.length();
result = mdb_del(database->_transaction, database->_database, &key, 0);
database->checkError("mdb_del", result);
mdb_txn_commit(database->_transaction);
}
}
}
void Database::getAll(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Database* database = Database::get(args.Data())) {
int result = mdb_txn_begin(database->_environment, 0, MDB_RDONLY, &database->_transaction);
if (!database->checkError("mdb_txn_begin", result)) {
MDB_cursor* cursor;
result = mdb_cursor_open(database->_transaction, database->_database, &cursor);
if (!database->checkError("mdb_cursor_open", result)) {
int expectedCount = 0;
MDB_stat statistics;
if (mdb_stat(database->_transaction, database->_database, &statistics) == 0) {
expectedCount = statistics.ms_entries;
}
v8::Local<v8::Array> array = v8::Array::New(args.GetIsolate(), expectedCount);
MDB_val key;
int index = 0;
while ((result = mdb_cursor_get(cursor, &key, 0, MDB_NEXT)) == 0) {
array->Set(index++, v8::String::NewFromUtf8(args.GetIsolate(), reinterpret_cast<const char*>(key.mv_data), v8::String::kNormalString, key.mv_size));
}
if (result == MDB_NOTFOUND) {
args.GetReturnValue().Set(array);
} else {
database->checkError("mdb_cursor_get", result);
}
mdb_cursor_close(cursor);
}
mdb_txn_reset(database->_transaction);
}
}
}
void Database::onRelease(const v8::WeakCallbackData<v8::Object, Database>& data) {
data.GetParameter()->_object.Reset();
delete data.GetParameter();
}
void Database::ref() {
if (++_refCount == 1) {
_object.ClearWeak();
}
}
void Database::release() {
assert(_refCount >= 1);
if (--_refCount == 0) {
_object.SetWeak(this, onRelease);
}
}
Database* Database::get(v8::Handle<v8::Value> databaseObject) {
return reinterpret_cast<Database*>(v8::Handle<v8::External>::Cast(databaseObject)->Value());
}

44
src/Database.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef INCLUDED_Database
#define INCLUDED_Database
#include <lmdb.h>
#include <v8.h>
class Task;
class Database {
public:
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
static int getCount() { return _count; }
private:
Database(Task* task);
~Database();
Task* _task;
int _refCount = 1;
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _object;
MDB_env* _environment;
MDB_dbi _database;
MDB_txn* _transaction;
static int _count;
static Database* get(v8::Handle<v8::Value> databaseObject);
static void onRelease(const v8::WeakCallbackData<v8::Object, Database>& data);
static void get(const v8::FunctionCallbackInfo<v8::Value>& args);
static void set(const v8::FunctionCallbackInfo<v8::Value>& args);
static void remove(const v8::FunctionCallbackInfo<v8::Value>& args);
static void getAll(const v8::FunctionCallbackInfo<v8::Value>& args);
bool open(v8::Isolate* isolate, const char* path);
bool checkError(const char* command, int result);
void ref();
void release();
};
#endif

129
src/File.cpp Normal file
View File

@ -0,0 +1,129 @@
#include "File.h"
#include "Task.h"
#include <cstring>
#include <fstream>
#include <iostream>
#include <uv.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dirent.h>
#include <unistd.h>
#endif
void File::configure(v8::Isolate* isolate, v8::Handle<v8::ObjectTemplate> global) {
v8::Local<v8::ObjectTemplate> fileTemplate = v8::ObjectTemplate::New(isolate);
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "readFile"), v8::FunctionTemplate::New(isolate, readFile));
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "readDirectory"), v8::FunctionTemplate::New(isolate, readDirectory));
fileTemplate->Set(v8::String::NewFromUtf8(isolate, "makeDirectory"), v8::FunctionTemplate::New(isolate, makeDirectory));
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));
global->Set(v8::String::NewFromUtf8(isolate, "File"), fileTemplate);
}
void File::readFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
v8::Handle<v8::String> fileName = args[0]->ToString();
v8::String::Utf8Value utf8FileName(fileName);
std::ifstream file(*utf8FileName, std::ios_base::in | std::ios_base::binary | std::ios_base::ate);
std::streampos fileSize = file.tellg();
if (fileSize >= 0 && fileSize < 4 * 1024 * 1024) {
file.seekg(0, std::ios_base::beg);
char* buffer = new char[fileSize];
file.read(buffer, fileSize);
std::string contents(buffer, buffer + fileSize);
args.GetReturnValue().Set(v8::String::NewFromOneByte(args.GetIsolate(), reinterpret_cast<const uint8_t*>(buffer), v8::String::kNormalString, fileSize));
delete[] buffer;
}
}
void File::writeFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
v8::Handle<v8::String> fileName = args[0]->ToString();
v8::Handle<v8::String> contents = args[1]->ToString();
v8::String::Utf8Value utf8FileName(fileName);
std::ofstream file(*utf8FileName, std::ios_base::out | std::ios_base::binary);
if (contents->ContainsOnlyOneByte()) {
std::vector<uint8_t> bytes(contents->Length());
contents->WriteOneByte(bytes.data(), 0, bytes.size(), v8::String::NO_NULL_TERMINATION);
if (!file.write(reinterpret_cast<const char*>(bytes.data()), bytes.size())) {
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), -1));
}
} else {
v8::String::Utf8Value utf8Contents(contents);
if (!file.write(*utf8Contents, utf8Contents.length())) {
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), -1));
}
}
}
void File::renameFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
v8::HandleScope scope(args.GetIsolate());
v8::String::Utf8Value oldName(args[0]->ToString());
v8::String::Utf8Value newName(args[1]->ToString());
uv_fs_t req;
int result = uv_fs_rename(task->getLoop(), &req, *oldName, *newName, 0);
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), result));
}
void File::unlinkFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
Task* task = reinterpret_cast<Task*>(args.GetIsolate()->GetData(0));
v8::HandleScope scope(args.GetIsolate());
v8::String::Utf8Value fileName(args[0]->ToString());
uv_fs_t req;
int result = uv_fs_unlink(task->getLoop(), &req, *fileName, 0);
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), result));
}
void File::readDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope scope(args.GetIsolate());
v8::Handle<v8::String> directory = args[0]->ToString();
v8::Handle<v8::Array> array = v8::Array::New(args.GetIsolate(), 0);
#ifdef _WIN32
WIN32_FIND_DATA find;
std::string pattern = *v8::String::Utf8Value(directory);
pattern += "\\*";
HANDLE handle = FindFirstFile(pattern.c_str(), &find);
if (handle != INVALID_HANDLE_VALUE) {
int index = 0;
do {
array->Set(v8::Integer::New(args.GetIsolate(), index++), v8::String::NewFromUtf8(args.GetIsolate(), find.cFileName));
} while (FindNextFile(handle, &find) != 0);
FindClose(handle);
}
#else
if (DIR* dir = opendir(*v8::String::Utf8Value(directory))) {
int index = 0;
while (struct dirent* entry = readdir(dir)) {
array->Set(v8::Integer::New(args.GetIsolate(), index++), v8::String::NewFromUtf8(args.GetIsolate(), entry->d_name));
}
closedir(dir);
}
#endif
args.GetReturnValue().Set(array);
}
void File::makeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Task* task = Task::get(args.GetIsolate());
v8::HandleScope scope(args.GetIsolate());
v8::Handle<v8::String> directory = args[0]->ToString();
uv_fs_t req;
int result = uv_fs_mkdir(task->getLoop(), &req, *v8::String::Utf8Value(directory), 0755, 0);
args.GetReturnValue().Set(result);
}

19
src/File.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef INCLUDED_File
#define INCLUDED_File
#include <v8.h>
class File {
public:
static void configure(v8::Isolate* isolate, v8::Handle<v8::ObjectTemplate> global);
private:
static void readFile(const v8::FunctionCallbackInfo<v8::Value>& args);
static void writeFile(const v8::FunctionCallbackInfo<v8::Value>& args);
static void readDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
static void makeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args);
static void unlinkFile(const v8::FunctionCallbackInfo<v8::Value>& args);
static void renameFile(const v8::FunctionCallbackInfo<v8::Value>& args);
};
#endif

32
src/Mutex.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "Mutex.h"
#include <iostream>
#include <assert.h>
Mutex::Mutex() {
int result = uv_mutex_init(&_mutex);
if (result != 0) {
assert("Mutex lock failed.");
}
}
Mutex::~Mutex() {
uv_mutex_destroy(&_mutex);
}
void Mutex::lock() {
uv_mutex_lock(&_mutex);
}
void Mutex::unlock() {
uv_mutex_unlock(&_mutex);
}
Lock::Lock(Mutex& mutex)
: _mutex(mutex) {
_mutex.lock();
}
Lock::~Lock() {
_mutex.unlock();
}

26
src/Mutex.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef INCLUDED_Mutex
#define INCLUDED_Mutex
#include <uv.h>
class Mutex {
public:
Mutex();
~Mutex();
void lock();
void unlock();
private:
uv_mutex_t _mutex;
};
class Lock {
public:
Lock(Mutex& mutex);
~Lock();
private:
Mutex& _mutex;
};
#endif

85
src/PacketStream.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "PacketStream.h"
#include <cstring>
#include <iostream>
PacketStream::PacketStream()
: _onReceive(0),
_onReceiveUserData(0) {
}
PacketStream::~PacketStream() {
_onReceive = 0;
_onReceiveUserData = 0;
close();
}
void PacketStream::close() {
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&_stream))) {
uv_close(reinterpret_cast<uv_handle_t*>(&_stream), 0);
}
}
void PacketStream::start() {
_stream.data = this;
uv_read_start(reinterpret_cast<uv_stream_t*>(&_stream), onAllocate, onRead);
}
void PacketStream::send(int packetType, char* begin, size_t length) {
size_t bufferLength = sizeof(uv_write_t) + sizeof(packetType) + sizeof(length) + length;
char* buffer = new char[bufferLength];
uv_write_t* request = reinterpret_cast<uv_write_t*>(buffer);
buffer += sizeof(uv_write_t);
memcpy(buffer, &packetType, sizeof(packetType));
memcpy(buffer + sizeof(packetType), &length, sizeof(length));
memcpy(buffer + sizeof(packetType) + sizeof(length), begin, length);
uv_buf_t writeBuffer;
writeBuffer.base = buffer;
writeBuffer.len = sizeof(packetType) + sizeof(length) + length;
uv_write(request, reinterpret_cast<uv_stream_t*>(&_stream), &writeBuffer, 1, onWrite);
}
void PacketStream::setOnReceive(OnReceive* onReceiveCallback, void* userData) {
_onReceive = onReceiveCallback;
_onReceiveUserData = userData;
}
void PacketStream::onWrite(uv_write_t* request, int status) {
delete[] reinterpret_cast<char*>(request);
}
void PacketStream::onAllocate(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer) {
buffer->base = new char[suggestedSize];
buffer->len = suggestedSize;
}
void PacketStream::onRead(uv_stream_t* handle, ssize_t count, const uv_buf_t* buffer) {
PacketStream* owner = reinterpret_cast<PacketStream*>(handle->data);
if (count >= 0) {
if (count > 0) {
owner->_buffer.insert(owner->_buffer.end(), buffer->base, buffer->base + count);
owner->processMessages();
}
delete[] reinterpret_cast<char*>(buffer->base);
} else {
owner->close();
}
}
void PacketStream::processMessages() {
int packetType = 0;
size_t length = 0;
while (_buffer.size() >= sizeof(packetType) + sizeof(length)) {
memcpy(&packetType, &*_buffer.begin(), sizeof(packetType));
memcpy(&length, &*_buffer.begin() + sizeof(packetType), sizeof(length));
if (_buffer.size() >= sizeof(packetType) + sizeof(length) + length) {
if (_onReceive) {
_onReceive(packetType, &*_buffer.begin() + sizeof(length) + sizeof(packetType), length, _onReceiveUserData);
}
_buffer.erase(_buffer.begin(), _buffer.begin() + sizeof(length) + sizeof(packetType) + length);
} else {
break;
}
}
}

34
src/PacketStream.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef INCLUDED_PacketStream
#define INCLUDED_PacketStream
#include <uv.h>
#include <vector>
class PacketStream {
public:
PacketStream();
~PacketStream();
void start();
typedef void (OnReceive)(int packetType, const char* begin, size_t length, void* userData);
void send(int packetType, char* begin, size_t length);
void setOnReceive(OnReceive* onReceiveCallback, void* userData);
void close();
uv_pipe_t& getStream() { return _stream; }
private:
OnReceive* _onReceive;
void* _onReceiveUserData;
uv_pipe_t _stream;
std::vector<char> _buffer;
void processMessages();
static void onAllocate(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer);
static void onRead(uv_stream_t* handle, ssize_t count, const uv_buf_t* buffer);
static void onWrite(uv_write_t* request, int status);
};
#endif

210
src/Serialize.cpp Normal file
View File

@ -0,0 +1,210 @@
#include "Serialize.h"
#include "Task.h"
#include "TaskStub.h"
#include <cstring>
void Serialize::writeInt8(std::vector<char>& buffer, int8_t value) {
buffer.insert(buffer.end(), value);
}
void Serialize::writeInt32(std::vector<char>& buffer, int32_t value) {
const char* p = reinterpret_cast<char*>(&value);
buffer.insert(buffer.end(), p, p + sizeof(value));
}
void Serialize::writeUint32(std::vector<char>& buffer, uint32_t value) {
const char* p = reinterpret_cast<char*>(&value);
buffer.insert(buffer.end(), p, p + sizeof(value));
}
void Serialize::writeDouble(std::vector<char>& buffer, double value) {
const char* p = reinterpret_cast<char*>(&value);
buffer.insert(buffer.end(), p, p + sizeof(value));
}
int8_t Serialize::readInt8(const std::vector<char>& buffer, int& offset) {
int8_t result;
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
offset += sizeof(result);
return result;
}
int32_t Serialize::readInt32(const std::vector<char>& buffer, int& offset) {
int32_t result;
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
offset += sizeof(result);
return result;
}
uint32_t Serialize::readUint32(const std::vector<char>& buffer, int& offset) {
uint32_t result;
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
offset += sizeof(result);
return result;
}
double Serialize::readDouble(const std::vector<char>& buffer, int& offset) {
double result;
std::memcpy(&result, &*buffer.begin() + offset, sizeof(result));
offset += sizeof(result);
return result;
}
bool Serialize::store(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value) {
return storeInternal(task, buffer, value, 0);
}
bool Serialize::storeInternal(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value, int depth) {
if (value.IsEmpty()) {
return false;
} else if (value->IsUndefined()) {
writeInt32(buffer, kUndefined);
} else if (value->IsNull()) {
writeInt32(buffer, kNull);
} else if (value->IsBoolean()) {
writeInt32(buffer, kBoolean);
writeInt8(buffer, value->IsTrue() ? 1 : 0);
} else if (value->IsInt32()) {
writeInt32(buffer, kInt32);
writeInt32(buffer, value->Int32Value());
} else if (value->IsUint32()) {
writeInt32(buffer, kUint32);
writeInt32(buffer, value->Uint32Value());
} else if (value->IsNumber()) {
writeInt32(buffer, kNumber);
writeDouble(buffer, value->NumberValue());
} else if (value->IsString()) {
writeInt32(buffer, kString);
v8::String::Utf8Value utf8(value->ToString());
writeInt32(buffer, utf8.length());
buffer.insert(buffer.end(), *utf8, *utf8 + utf8.length());
} else if (value->IsArray()) {
writeInt32(buffer, kArray);
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
writeInt32(buffer, array->Length());
for (size_t i = 0; i < array->Length(); ++i) {
storeInternal(task, buffer, array->Get(i), depth + 1);
}
} else if (value->IsFunction()) {
writeInt32(buffer, kFunction);
exportid_t exportId = task->exportFunction(v8::Handle<v8::Function>::Cast(value));
writeInt32(buffer, exportId);
} else if (value->IsNativeError()) {
storeInternal(task, buffer, storeMessage(task, v8::Exception::CreateMessage(value)), depth);
} else if (value->IsObject()) {
writeInt32(buffer, kObject);
v8::Handle<v8::Object> object = value->ToObject();
// XXX: For some reason IsNativeError isn't working reliably. Catch an
// object that still looks like an error object and treat it as such.
if (object->GetOwnPropertyNames()->Length() == 0
&& !object->Get(v8::String::NewFromUtf8(task->getIsolate(), "stackTrace")).IsEmpty()) {
object = v8::Handle<v8::Object>::Cast(storeMessage(task, v8::Exception::CreateMessage(value)));
}
v8::Handle<v8::Array> keys = object->GetOwnPropertyNames();
writeInt32(buffer, keys->Length());
for (size_t i = 0; i < keys->Length(); ++i) {
v8::Handle<v8::Value> key = keys->Get(i);
storeInternal(task, buffer, key, depth + 1);
storeInternal(task, buffer, object->Get(key), depth + 1);
}
} else {
writeInt32(buffer, kString);
v8::String::Utf8Value utf8(value->ToString());
writeInt32(buffer, utf8.length());
buffer.insert(buffer.end(), *utf8, *utf8 + utf8.length());
}
return true;
}
v8::Handle<v8::Value> Serialize::store(Task* task, v8::TryCatch& tryCatch) {
return storeMessage(task, tryCatch.Message());
}
v8::Handle<v8::Object> Serialize::storeMessage(Task* task, v8::Handle<v8::Message> message) {
v8::Handle<v8::Object> error = v8::Object::New(task->getIsolate());
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "message"), message->Get());
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "fileName"), message->GetScriptResourceName());
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "lineNumber"), v8::Integer::New(task->getIsolate(), message->GetLineNumber()));
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "sourceLine"), message->GetSourceLine());
if (!message->GetStackTrace().IsEmpty()) {
error->Set(v8::String::NewFromUtf8(task->getIsolate(), "stackTrace"), message->GetStackTrace()->AsArray());
}
return error;
}
v8::Handle<v8::Value> Serialize::load(Task* task, TaskStub* from, const std::vector<char>& buffer) {
int offset = 0;
return loadInternal(task, from, buffer, offset, 0);
}
v8::Handle<v8::Value> Serialize::loadInternal(Task* task, TaskStub* from, const std::vector<char>& buffer, int& offset, int depth) {
if (static_cast<size_t>(offset) >= buffer.size()) {
return v8::Undefined(task->getIsolate());
} else {
int32_t type = readInt32(buffer, offset);
v8::Handle<v8::Value> result;
switch (type) {
case kUndefined:
result = v8::Undefined(task->getIsolate());
break;
case kNull:
result = v8::Null(task->getIsolate());
break;
case kBoolean:
result = v8::Boolean::New(task->getIsolate(), readInt8(buffer, offset) != 0);
break;
case kInt32:
result = v8::Int32::New(task->getIsolate(), readInt32(buffer, offset));
break;
case kUint32:
result = v8::Uint32::New(task->getIsolate(), readUint32(buffer, offset));
break;
case kNumber:
result = v8::Number::New(task->getIsolate(), readDouble(buffer, offset));
break;
case kString:
{
int32_t length = readInt32(buffer, offset);
result = v8::String::NewFromUtf8(task->getIsolate(), &*buffer.begin() + offset, v8::String::kNormalString, length);
offset += length;
}
break;
case kArray:
{
int32_t length = readInt32(buffer, offset);
v8::Handle<v8::Array> array = v8::Array::New(task->getIsolate());
for (int i = 0; i < length; ++i) {
v8::Handle<v8::Value> value = loadInternal(task, from, buffer, offset, depth + 1);
array->Set(i, value);
}
result = array;
}
break;
case kFunction:
{
exportid_t exportId = readInt32(buffer, offset);
result = task->addImport(from->getId(), exportId);
}
break;
case kObject:
{
int32_t length = readInt32(buffer, offset);
v8::Handle<v8::Object> object = v8::Object::New(task->getIsolate());
for (int i = 0; i < length; ++i) {
v8::Handle<v8::Value> key = loadInternal(task, from, buffer, offset, depth + 1);
v8::Handle<v8::Value> value = loadInternal(task, from, buffer, offset, depth + 1);
object->Set(key, value);
}
result = object;
}
break;
}
return result;
}
}

47
src/Serialize.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef INCLUDED_Serialize
#define INCLUDED_Serialize
#include <v8.h>
#include <vector>
class Task;
class TaskStub;
class Serialize {
public:
static bool store(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value);
static v8::Handle<v8::Value> load(Task* task, TaskStub* from, const std::vector<char>& buffer);
static v8::Handle<v8::Value> store(Task* task, v8::TryCatch& tryCatch);
static v8::Handle<v8::Object> storeMessage(Task* task, v8::Handle<v8::Message> message);
private:
static bool storeInternal(Task* task, std::vector<char>& buffer, v8::Handle<v8::Value> value, int depth);
static v8::Handle<v8::Value> loadInternal(Task* task, TaskStub* from, const std::vector<char>& buffer, int& offse, int deptht);
static void writeInt8(std::vector<char>& buffer, int8_t value);
static void writeInt32(std::vector<char>& buffer, int32_t value);
static void writeUint32(std::vector<char>& buffer, uint32_t value);
static void writeDouble(std::vector<char>& buffer, double value);
static int8_t readInt8(const std::vector<char>& buffer, int& offset);
static int32_t readInt32(const std::vector<char>& buffer, int& offset);
static uint32_t readUint32(const std::vector<char>& buffer, int& offset);
static double readDouble(const std::vector<char>& buffer, int& offset);
enum Types {
kUndefined,
kNull,
kBoolean,
kInt32,
kUint32,
kNumber,
kString,
kArray,
kObject,
kFunction,
kError,
};
};
#endif

653
src/Socket.cpp Normal file
View File

@ -0,0 +1,653 @@
#include "Socket.h"
#include "Task.h"
#include "TaskTryCatch.h"
#include "Tls.h"
#include "TlsContextWrapper.h"
#include <assert.h>
#include <cstring>
#include <uv.h>
int Socket::_count = 0;
int Socket::_openCount = 0;
TlsContext* Socket::_defaultTlsContext = 0;
struct SocketResolveData {
uv_getaddrinfo_t resolver;
Socket* socket;
promiseid_t promise;
};
Socket::Socket(Task* task) {
v8::HandleScope scope(task->getIsolate());
++_count;
v8::Handle<v8::External> data = v8::External::New(task->getIsolate(), this);
v8::Local<v8::ObjectTemplate> socketTemplate = v8::ObjectTemplate::New(task->getIsolate());
socketTemplate->SetInternalFieldCount(1);
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "bind"), v8::FunctionTemplate::New(task->getIsolate(), bind, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "connect"), v8::FunctionTemplate::New(task->getIsolate(), connect, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "listen"), v8::FunctionTemplate::New(task->getIsolate(), listen, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "accept"), v8::FunctionTemplate::New(task->getIsolate(), accept, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "startTls"), v8::FunctionTemplate::New(task->getIsolate(), startTls, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "stopTls"), v8::FunctionTemplate::New(task->getIsolate(), stopTls, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "shutdown"), v8::FunctionTemplate::New(task->getIsolate(), shutdown, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "close"), v8::FunctionTemplate::New(task->getIsolate(), close, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "read"), v8::FunctionTemplate::New(task->getIsolate(), read, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "onError"), v8::FunctionTemplate::New(task->getIsolate(), onError, data));
socketTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "write"), v8::FunctionTemplate::New(task->getIsolate(), write, data));
socketTemplate->SetAccessor(v8::String::NewFromUtf8(task->getIsolate(), "peerName"), getPeerName, 0, data);
socketTemplate->SetAccessor(v8::String::NewFromUtf8(task->getIsolate(), "peerCertificate"), getPeerCertificate, 0, data);
socketTemplate->SetAccessor(v8::String::NewFromUtf8(task->getIsolate(), "isConnected"), isConnected, 0, data);
v8::Local<v8::Object> socketObject = socketTemplate->NewInstance();
socketObject->SetInternalField(0, v8::External::New(task->getIsolate(), this));
_object = v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> >(task->getIsolate(), socketObject);
uv_tcp_init(task->getLoop(), &_socket);
++_openCount;
_socket.data = this;
_task = task;
}
Socket::~Socket() {
if (_tls) {
delete _tls;
_tls = 0;
}
--_count;
}
void Socket::close() {
if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(&_socket))) {
if (!_onRead.IsEmpty()) {
_onRead.Reset();
}
uv_close(reinterpret_cast<uv_handle_t*>(&_socket), onClose);
}
}
void Socket::reportError(const char* error) {
v8::Handle<v8::Value> exception = v8::Exception::Error(v8::String::NewFromUtf8(_task->getIsolate(), error));
if (!_onError.IsEmpty()) {
v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(_task->getIsolate(), _onError);
callback->Call(callback, 1, &exception);
} else {
_task->getIsolate()->ThrowException(exception);
}
}
void Socket::reportTlsErrors() {
char buffer[4096];
while (_tls && _tls->getError(buffer, sizeof(buffer))) {
reportError(buffer);
}
}
void Socket::startTls(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
if (!socket->_tls) {
TlsContext* context = 0;
if (args.Length() > 0 && !args[0].IsEmpty() && !args[0]->IsUndefined()) {
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args[0])) {
context = wrapper->getContext();
}
} else {
if (!_defaultTlsContext) {
_defaultTlsContext = TlsContext::create();
}
context = _defaultTlsContext;
}
if (context) {
socket->_tls = context->createSession();
}
if (socket->_tls) {
socket->_tls->setHostname(socket->_peerName.c_str());
if (socket->_direction == kAccept) {
socket->_tls->startAccept();
} else if (socket->_direction == kConnect) {
socket->_tls->startConnect();
}
socket->_startTlsPromise = socket->_task->allocatePromise();
socket->processOutgoingTls();
args.GetReturnValue().Set(socket->_task->getPromise(socket->_startTlsPromise));
} else {
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "Failed to get TLS context")));
}
} else {
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "startTls with TLS already started")));
}
}
}
void Socket::stopTls(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
if (socket->_tls) {
socket->processOutgoingTls();
delete socket->_tls;
socket->_tls = 0;
} else {
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "stopTls with TLS already stopped")));
}
}
}
bool Socket::processSomeOutgoingTls(promiseid_t promise, uv_write_cb callback) {
char buffer[8192];
int result = _tls->readEncrypted(buffer, sizeof(buffer));
if (result > 0) {
char* rawBuffer = new char[sizeof(uv_write_t) + result];
uv_write_t* request = reinterpret_cast<uv_write_t*>(rawBuffer);
std::memset(request, 0, sizeof(*request));
request->data = reinterpret_cast<void*>(promise);
rawBuffer += sizeof(uv_write_t);
std::memcpy(rawBuffer, buffer, result);
uv_buf_t writeBuffer;
writeBuffer.base = rawBuffer;
writeBuffer.len = result;
int writeResult = uv_write(request, reinterpret_cast<uv_stream_t*>(&_socket), &writeBuffer, 1, callback);
if (writeResult != 0) {
std::string error = "uv_write: " + std::string(uv_strerror(writeResult));
reportError(error.c_str());
}
} else {
reportTlsErrors();
}
return result > 0;
}
void Socket::processOutgoingTls() {
while (processSomeOutgoingTls(-1, onWrite)) {}
}
void Socket::bind(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
v8::String::Utf8Value node(args[0]->ToString());
v8::String::Utf8Value port(args[1]->ToString());
SocketResolveData* data = new SocketResolveData();
std::memset(data, 0, sizeof(*data));
struct addrinfo hints;
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = 0;
data->resolver.data = data;
data->socket = socket;
data->promise = socket->_task->allocatePromise();
int result = uv_getaddrinfo(socket->_task->getLoop(), &data->resolver, onResolvedForBind, *node, *port, &hints);
if (result != 0) {
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(result));
socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), error.c_str())));
delete data;
}
args.GetReturnValue().Set(socket->_task->getPromise(data->promise));
}
}
void Socket::onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) {
SocketResolveData* data = reinterpret_cast<SocketResolveData*>(resolver->data);
if (status != 0) {
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(status));
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
} else {
int bindResult = uv_tcp_bind(&data->socket->_socket, result->ai_addr, 0);
if (bindResult != 0) {
std::string error = "uv_tcp_bind: " + std::string(uv_strerror(bindResult));
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
} else {
data->socket->_task->resolvePromise(data->promise, v8::Undefined(data->socket->_task->getIsolate()));
}
}
delete data;
}
void Socket::connect(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
socket->_direction = kConnect;
v8::String::Utf8Value node(args[0]->ToString());
v8::String::Utf8Value port(args[1]->ToString());
socket->_peerName = *node;
promiseid_t promise = socket->_task->allocatePromise();
SocketResolveData* data = new SocketResolveData();
std::memset(data, 0, sizeof(*data));
struct addrinfo hints;
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = 0;
data->resolver.data = data;
data->socket = socket;
data->promise = promise;
int result = uv_getaddrinfo(socket->_task->getLoop(), &data->resolver, onResolvedForConnect, *node, *port, &hints);
if (result != 0) {
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(result));
socket->_task->rejectPromise(promise, v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), error.c_str())));
delete data;
}
args.GetReturnValue().Set(socket->_task->getPromise(promise));
}
}
void Socket::onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) {
SocketResolveData* data = reinterpret_cast<SocketResolveData*>(resolver->data);
if (status != 0) {
std::string error = "uv_getaddrinfo: " + std::string(uv_strerror(status));
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
} else {
uv_connect_t* request = new uv_connect_t();
std::memset(request, 0, sizeof(*request));
request->data = reinterpret_cast<void*>(data->promise);
int connectResult = uv_tcp_connect(request, &data->socket->_socket, result->ai_addr, onConnect);
if (connectResult != 0) {
std::string error("uv_tcp_connect: " + std::string(uv_strerror(connectResult)));
data->socket->_task->rejectPromise(data->promise, v8::Exception::Error(v8::String::NewFromUtf8(data->socket->_task->getIsolate(), error.c_str())));
}
}
delete data;
}
void Socket::onConnect(uv_connect_t* request, int status) {
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
if (promise != -1) {
Socket* socket = reinterpret_cast<Socket*>(request->handle->data);
if (status == 0) {
socket->_connected = true;
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), status));
} else {
std::string error("uv_tcp_connect: " + std::string(uv_strerror(status)));
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str()));
}
}
}
void Socket::listen(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
int backlog = args[0]->ToInteger()->Value();
if (socket->_onConnect.IsEmpty()) {
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > callback(args.GetIsolate(), args[1].As<v8::Function>());
socket->_onConnect = callback;
int result = uv_listen(reinterpret_cast<uv_stream_t*>(&socket->_socket), backlog, onNewConnection);
if (result != 0) {
std::string error = "uv_listen: " + std::string(uv_strerror(result));
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), v8::String::kNormalString, error.size())));
}
args.GetReturnValue().Set(v8::Integer::New(args.GetIsolate(), result));
} else {
args.GetIsolate()->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(args.GetIsolate(), "listen: Already listening.")));
}
}
}
void Socket::onNewConnection(uv_stream_t* server, int status) {
if (Socket* socket = reinterpret_cast<Socket*>(server->data)) {
v8::HandleScope handleScope(socket->_task->getIsolate());
TaskTryCatch tryCatch(socket->_task);
if (!socket->_onConnect.IsEmpty()) {
v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onConnect);
callback->Call(callback, 0, 0);
}
}
}
void Socket::accept(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
v8::HandleScope handleScope(args.GetIsolate());
Socket* client = new Socket(socket->_task);
client->_direction = kAccept;
promiseid_t promise = socket->_task->allocatePromise();
v8::Handle<v8::Value> promiseObject = socket->_task->getPromise(promise);
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), client->_object);
int status = uv_accept(reinterpret_cast<uv_stream_t*>(&socket->_socket), reinterpret_cast<uv_stream_t*>(&client->_socket));
if (status == 0) {
client->_connected = true;
socket->_task->resolvePromise(promise, result);
} else {
std::string error = "uv_accept: " + std::string(uv_strerror(status));
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
}
args.GetReturnValue().Set(promiseObject);
client->release();
}
}
void Socket::close(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
if (socket->_closePromise == -1) {
socket->_closePromise = socket->_task->allocatePromise();
args.GetReturnValue().Set(socket->_task->getPromise(socket->_closePromise));
socket->close();
}
}
}
void Socket::shutdown(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
if (socket->_tls) {
promiseid_t promise = socket->_task->allocatePromise();
socket->processTlsShutdown(promise);
args.GetReturnValue().Set(socket->_task->getPromise(promise));
} else {
promiseid_t promise = socket->_task->allocatePromise();
socket->shutdownInternal(promise);
args.GetReturnValue().Set(socket->_task->getPromise(promise));
}
}
}
void Socket::shutdownInternal(promiseid_t promise) {
uv_shutdown_t* request = new uv_shutdown_t();
std::memset(request, 0, sizeof(*request));
request->data = reinterpret_cast<void*>(promise);
int result = uv_shutdown(request, reinterpret_cast<uv_stream_t*>(&_socket), onShutdown);
if (result != 0) {
std::string error = "uv_shutdown: " + std::string(uv_strerror(result));
_task->rejectPromise(promise, v8::Exception::Error(v8::String::NewFromUtf8(_task->getIsolate(), error.c_str())));
delete request;
}
}
void Socket::processTlsShutdown(promiseid_t promise) {
_tls->shutdown();
if (!processSomeOutgoingTls(promise, onTlsShutdown)) {
shutdownInternal(promise);
}
}
void Socket::onTlsShutdown(uv_write_t* request, int status) {
if (Socket* socket = reinterpret_cast<Socket*>(request->handle->data)) {
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
socket->processTlsShutdown(promise);
}
}
void Socket::onError(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > callback(args.GetIsolate(), args[0].As<v8::Function>());
socket->_onError = callback;
}
}
void Socket::read(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > callback(args.GetIsolate(), args[0].As<v8::Function>());
socket->_onRead = callback;
int result = uv_read_start(reinterpret_cast<uv_stream_t*>(&socket->_socket), allocateBuffer, onRead);
promiseid_t promise = socket->_task->allocatePromise();
if (result != 0) {
std::string error = "uv_read_start: " + std::string(uv_strerror(result));
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
} else {
socket->_task->resolvePromise(promise, v8::Undefined(socket->_task->getIsolate()));
}
}
}
void Socket::allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) {
*buf = uv_buf_init(new char[suggestedSize], suggestedSize);
}
void Socket::onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer) {
if (Socket* socket = reinterpret_cast<Socket*>(stream->data)) {
v8::HandleScope handleScope(socket->_task->getIsolate());
TaskTryCatch tryCatch(socket->_task);
v8::Handle<v8::Value> data;
if (readSize <= 0) {
socket->_connected = false;
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
if (!callback.IsEmpty()) {
data = v8::Undefined(socket->_task->getIsolate());
callback->Call(callback, 1, &data);
}
socket->close();
} else {
if (socket->_tls) {
socket->reportTlsErrors();
socket->_tls->writeEncrypted(buffer->base, readSize);
if (socket->_startTlsPromise != -1) {
TlsSession::HandshakeResult result = socket->_tls->handshake();
if (result == TlsSession::kDone) {
promiseid_t promise = socket->_startTlsPromise;
socket->_startTlsPromise = -1;
socket->_task->resolvePromise(promise, v8::Undefined(socket->_task->getIsolate()));
} else if (result == TlsSession::kFailed) {
promiseid_t promise = socket->_startTlsPromise;
socket->_startTlsPromise = -1;
char buffer[8192];
if (socket->_tls->getError(buffer, sizeof(buffer))) {
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), buffer));
} else {
socket->_task->rejectPromise(promise, v8::Undefined(socket->_task->getIsolate()));
}
}
}
while (true) {
char plain[8192];
int result = socket->_tls->readPlain(plain, sizeof(plain));
if (result > 0) {
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
if (!callback.IsEmpty()) {
data = v8::String::NewFromOneByte(socket->_task->getIsolate(), reinterpret_cast<const uint8_t*>(plain), v8::String::kNormalString, result);
callback->Call(callback, 1, &data);
}
} else if (result == TlsSession::kReadFailed) {
socket->reportTlsErrors();
socket->close();
break;
} else if (result == TlsSession::kReadZero) {
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
if (!callback.IsEmpty()) {
data = v8::Undefined(socket->_task->getIsolate());
callback->Call(callback, 1, &data);
}
break;
} else {
break;
}
}
if (socket->_tls) {
socket->processOutgoingTls();
}
} else {
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
if (!callback.IsEmpty()) {
data = v8::String::NewFromOneByte(socket->_task->getIsolate(), reinterpret_cast<const uint8_t*>(buffer->base), v8::String::kNormalString, readSize);
callback->Call(callback, 1, &data);
}
}
}
}
delete[] buffer->base;
}
void Socket::write(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (Socket* socket = Socket::get(args.Data())) {
promiseid_t promise = socket->_task->allocatePromise();
args.GetReturnValue().Set(socket->_task->getPromise(promise));
v8::Handle<v8::String> value = args[0].As<v8::String>();
if (!value.IsEmpty() && value->IsString()) {
if (socket->_tls) {
socket->reportTlsErrors();
int result;
int length;
if (value->ContainsOnlyOneByte()) {
length = value->Length();
std::vector<uint8_t> bytes(length);
value->WriteOneByte(bytes.data(), 0, bytes.size(), v8::String::NO_NULL_TERMINATION);
result = socket->_tls->writePlain(reinterpret_cast<const char*>(bytes.data()), bytes.size());
} else {
v8::String::Utf8Value utf8(value);
length = utf8.length();
result = socket->_tls->writePlain(*utf8, utf8.length());
}
char buffer[8192];
if (result <= 0 && socket->_tls->getError(buffer, sizeof(buffer))) {
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(args.GetIsolate(), buffer));
} else if (result < length) {
socket->_task->rejectPromise(promise, v8::Integer::New(socket->_task->getIsolate(), result));
} else {
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), result));
}
socket->processOutgoingTls();
} else {
v8::String::Utf8Value utf8(value);
int length;
char* rawBuffer = 0;
if (value->ContainsOnlyOneByte()) {
length = value->Length();
rawBuffer = new char[sizeof(uv_write_t) + length];
value->WriteOneByte(reinterpret_cast<uint8_t*>(rawBuffer) + sizeof(uv_write_t), 0, length, v8::String::NO_NULL_TERMINATION);
} else {
v8::String::Utf8Value utf8(value);
length = utf8.length();
rawBuffer = new char[sizeof(uv_write_t) + length];
std::memcpy(rawBuffer + sizeof(uv_write_t), *utf8, length);
}
uv_write_t* request = reinterpret_cast<uv_write_t*>(rawBuffer);
uv_buf_t buffer;
buffer.base = rawBuffer + sizeof(uv_write_t);
buffer.len = length;
request->data = reinterpret_cast<void*>(promise);
int result = uv_write(request, reinterpret_cast<uv_stream_t*>(&socket->_socket), &buffer, 1, onWrite);
if (result != 0) {
std::string error = "uv_write: " + std::string(uv_strerror(result));
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
}
}
} else {
socket->_task->rejectPromise(promise, v8::Integer::New(args.GetIsolate(), -2));
}
}
}
void Socket::onWrite(uv_write_t* request, int status) {
if (Socket* socket = reinterpret_cast<Socket*>(request->handle->data)) {
v8::HandleScope handleScope(socket->_task->getIsolate());
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
if (promise != -1) {
if (status == 0) {
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), status));
} else {
std::string error = "uv_write: " + std::string(uv_strerror(status));
socket->_task->rejectPromise(promise, v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str(), v8::String::kNormalString, error.size()));
}
}
}
delete[] reinterpret_cast<char*>(request);
}
void Socket::onClose(uv_handle_t* handle) {
--_openCount;
if (Socket* socket = reinterpret_cast<Socket*>(handle->data)) {
if (socket->_closePromise != -1) {
v8::HandleScope scope(socket->_task->getIsolate());
promiseid_t promise = socket->_closePromise;
socket->_closePromise = -1;
socket->_connected = false;
socket->_task->resolvePromise(promise, v8::Integer::New(socket->_task->getIsolate(), 0));
}
if (socket->_object.IsEmpty()) {
delete socket;
}
}
}
void Socket::onShutdown(uv_shutdown_t* request, int status) {
if (Socket* socket = reinterpret_cast<Socket*>(request->handle->data)) {
promiseid_t promise = reinterpret_cast<intptr_t>(request->data);
if (status == 0) {
socket->_task->resolvePromise(promise, v8::Undefined(socket->_task->getIsolate()));
} else {
std::string error = "uv_shutdown: " + std::string(uv_strerror(status));
socket->_task->rejectPromise(promise, v8::Exception::Error(v8::String::NewFromUtf8(socket->_task->getIsolate(), error.c_str())));
}
}
delete request;
}
void Socket::getPeerName(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
if (Socket* socket = Socket::get(info.Data())) {
struct sockaddr_in6 addr;
int nameLength = sizeof(addr);
if (uv_tcp_getpeername(&socket->_socket, reinterpret_cast<sockaddr*>(&addr), &nameLength) == 0) {
char name[1024];
if (static_cast<size_t>(nameLength) > sizeof(struct sockaddr_in)) {
if (uv_ip6_name(&addr, name, sizeof(name)) == 0) {
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), name));
}
} else {
if (uv_ip4_name(reinterpret_cast<struct sockaddr_in*>(&addr), name, sizeof(name)) == 0) {
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), name));
}
}
}
}
}
void Socket::getPeerCertificate(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
if (Socket* socket = Socket::get(info.Data())) {
if (socket->_tls) {
std::vector<char> buffer(128 * 1024);
int result = socket->_tls->getPeerCertificate(buffer.data(), buffer.size());
if (result > 0) {
info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(), buffer.data(), v8::String::kNormalString, result));
}
}
}
}
void Socket::isConnected(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
if (Socket* socket = Socket::get(info.Data())) {
info.GetReturnValue().Set(v8::Boolean::New(socket->_task->getIsolate(), socket->_connected));
}
}
void Socket::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope handleScope(args.GetIsolate());
if (Socket* socket = new Socket(Task::get(args.GetIsolate()))) {
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), socket->_object);
args.GetReturnValue().Set(result);
socket->release();
}
}
Socket* Socket::get(v8::Handle<v8::Value> socketObject) {
return reinterpret_cast<Socket*>(v8::Handle<v8::External>::Cast(socketObject)->Value());
}
void Socket::ref() {
if (++_refCount == 1) {
_object.ClearWeak();
}
}
void Socket::release() {
assert(_refCount >= 1);
if (--_refCount == 0) {
_object.SetWeak(this, onRelease);
}
}
void Socket::onRelease(const v8::WeakCallbackData<v8::Object, Socket>& data) {
data.GetParameter()->_object.Reset();
data.GetParameter()->close();
}

90
src/Socket.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef INCLUDED_Socket
#define INCLUDED_Socket
#include <string>
#include <uv.h>
#include <v8.h>
typedef int promiseid_t;
class Task;
class TlsContext;
class TlsSession;
class Socket {
public:
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
void close();
static int getCount() { return _count; }
static int getOpenCount() { return _openCount; }
private:
Socket(Task* task);
~Socket();
Task* _task;
uv_tcp_t _socket;
TlsSession* _tls = 0;
promiseid_t _startTlsPromise = -1;
promiseid_t _closePromise = -1;
int _refCount = 1;
bool _connected = false;
std::string _peerName;
enum Direction { kUndetermined, kAccept, kConnect };
Direction _direction = kUndetermined;
static int _count;
static int _openCount;
static TlsContext* _defaultTlsContext;
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _object;
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onConnect;
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onRead;
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onError;
static void startTls(const v8::FunctionCallbackInfo<v8::Value>& args);
static void stopTls(const v8::FunctionCallbackInfo<v8::Value>& args);
static void bind(const v8::FunctionCallbackInfo<v8::Value>& args);
static void connect(const v8::FunctionCallbackInfo<v8::Value>& args);
static void listen(const v8::FunctionCallbackInfo<v8::Value>& args);
static void accept(const v8::FunctionCallbackInfo<v8::Value>& args);
static void close(const v8::FunctionCallbackInfo<v8::Value>& args);
static void shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
static void read(const v8::FunctionCallbackInfo<v8::Value>& args);
static void onError(const v8::FunctionCallbackInfo<v8::Value>& args);
static void write(const v8::FunctionCallbackInfo<v8::Value>& args);
static void getPeerName(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);
static void getPeerCertificate(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);
static void isConnected(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info);
static Socket* get(v8::Handle<v8::Value> socketObject);
static void onClose(uv_handle_t* handle);
static void onShutdown(uv_shutdown_t* request, int status);
static void onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result);
static void onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result);
static void onConnect(uv_connect_t* request, int status);
static void onNewConnection(uv_stream_t* server, int status);
static void allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer);
static void onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer);
static void onWrite(uv_write_t* request, int status);
static void onRelease(const v8::WeakCallbackData<v8::Object, Socket>& data);
void processTlsShutdown(promiseid_t promise);
static void onTlsShutdown(uv_write_t* request, int status);
void shutdownInternal(promiseid_t promise);
bool processSomeOutgoingTls(promiseid_t promise, uv_write_cb callback);
void processOutgoingTls();
void reportTlsErrors();
void reportError(const char* error);
void ref();
void release();
};
#endif

749
src/Task.cpp Normal file
View File

@ -0,0 +1,749 @@
#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() {
{
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->Set(v8::String::NewFromUtf8(_isolate, "require"), v8::FunctionTemplate::New(_isolate, require));
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, "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);
}
v8::Local<v8::Context> context = v8::Context::New(_isolate, 0, global);
_context = v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context> >(_isolate, context);
}
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);
}
}
void Task::execute(const char* fileName) {
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 (!source.IsEmpty()) {
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
if (!script.IsEmpty()) {
script->Run();
std::cout << "Script " << fileName << " completed\n";
} 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())));
}
}
void Task::invokeExport(const v8::FunctionCallbackInfo<v8::Value>& args) {
Task* sender = Task::get(args.GetIsolate());
TaskTryCatch tryCatch(sender);
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);
array->Set(0, args.This());
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 kSetTrusted:
{
assert(length == sizeof(bool));
bool trusted = false;
memcpy(&trusted, begin, sizeof(bool));
to->_trusted = trusted;
}
break;
case kActivate:
to->activate();
break;
case kExecute:
{
assert(length >= sizeof(promiseid_t));
v8::Handle<v8::Value> arg;
promiseid_t promise;
std::memcpy(&promise, begin, sizeof(promiseid_t));
arg = Serialize::load(to, from, std::vector<char>(begin + sizeof(promiseid_t), begin + length));
v8::TryCatch tryCatch(to->_isolate);
tryCatch.SetCaptureMessage(true);
tryCatch.SetVerbose(true);
to->execute(*v8::String::Utf8Value(arg));
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;
std::string path = _scriptName;
size_t position = path.rfind('/');
if (position != std::string::npos) {
path.resize(position + 1);
std::cout << "Looking in " << path << " for " << require << "\n";
if (require.find("..") == std::string::npos && require.find('/') == std::string::npos) {
result = path + require;
}
if (result.size() && require.rfind(".js") != require.size() - 3) {
result += ".js";
}
}
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 script.\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."))));
}
}

163
src/Task.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef INCLUDED_Task
#define INCLUDED_Task
#include "PacketStream.h"
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include <v8.h>
#include <v8-platform.h>
#include <vector>
struct ExportRecord;
struct ImportRecord;
class Task;
class TaskStub;
struct uv_loop_s;
typedef struct uv_loop_s uv_loop_t;
typedef int taskid_t;
typedef int promiseid_t;
typedef int exportid_t;
enum MessageType {
kResolvePromise,
kRejectPromise,
kInvokeExport,
kReleaseExport,
kReleaseImport,
kSetTrusted,
kActivate,
kExecute,
kKill,
kStatistics,
kSetImports,
kGetExports,
};
class NewArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
public:
void* Allocate(size_t length) {
char* bytes = new char[length];
std::memset(bytes, 0, length);
return bytes;
}
void* AllocateUninitialized(size_t length) {
return new char[length];
}
void Free(void* data, size_t length) {
delete[] reinterpret_cast<char*>(data);
}
};
class Task {
public:
Task();
~Task();
const std::string& getName() const { return _scriptName; }
v8::Isolate* getIsolate() { return _isolate; }
uv_loop_t* getLoop() { return _loop; }
v8::Handle<v8::Context> getContext();
void kill();
promiseid_t allocatePromise();
v8::Handle<v8::Promise::Resolver> getPromise(promiseid_t promise);
void resolvePromise(promiseid_t promise, v8::Handle<v8::Value> value);
void rejectPromise(promiseid_t promise, v8::Handle<v8::Value> value);
void configureFromStdin();
void setTrusted(bool trusted) { _trusted = trusted; }
void execute(const char* fileName);
void activate();
void run();
static int getCount() { return _count; }
static Task* get(v8::Isolate* isolate);
TaskStub* get(taskid_t taskId);
exportid_t exportFunction(v8::Handle<v8::Function> function);
static void invokeExport(const v8::FunctionCallbackInfo<v8::Value>& args);
v8::Handle<v8::Function> addImport(taskid_t taskId, exportid_t exportId);
void releaseExport(taskid_t taskId, exportid_t exportId);
private:
static int _count;
TaskStub* _stub = 0;
TaskStub* _parent = 0;
taskid_t _nextTask = 1;
static const taskid_t kParentId = 0;
std::map<taskid_t, TaskStub*> _children;
typedef std::map<std::string, v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > > ScriptExportMap;
ScriptExportMap _scriptExports;
bool _trusted = false;
bool _killed = false;
std::string _scriptName;
NewArrayBufferAllocator _allocator;
v8::Isolate* _isolate = 0;
std::map<promiseid_t, v8::Persistent<v8::Promise::Resolver, v8::CopyablePersistentTraits<v8::Promise::Resolver> > > _promises;
promiseid_t _nextPromise = 0;
uv_loop_t* _loop = 0;
std::map<exportid_t, ExportRecord*> _exports;
exportid_t _nextExport = 0;
v8::Persistent<v8::Context, v8::CopyablePersistentTraits<v8::Context> > _context;
std::vector<ImportRecord*> _imports;
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _importObject;
v8::Persistent<v8::Object, v8::CopyablePersistentTraits<v8::Object> > _exportObject;
v8::Handle<v8::Object> getStatistics();
std::string resolveRequire(const std::string& require);
static void activate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void exit(const v8::FunctionCallbackInfo<v8::Value>& args);
static void print(const v8::FunctionCallbackInfo<v8::Value>& args);
static void require(const v8::FunctionCallbackInfo<v8::Value>& args);
static void setTimeout(const v8::FunctionCallbackInfo<v8::Value>& args);
static void timeoutCallback(uv_timer_t* handle);
static void invokeThen(const v8::FunctionCallbackInfo<v8::Value>& args);
static void invokeCatch(const v8::FunctionCallbackInfo<v8::Value>& args);
static void parent(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void version(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void statistics(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void utf8Length(const v8::FunctionCallbackInfo<v8::Value>& args);
static void getImportProperty(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void getImports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void getExports(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void setExports(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args);
static v8::Handle<v8::Value> invokeExport(TaskStub* from, Task* to, exportid_t exportId, const std::vector<char>& buffer);
static void sendPromiseResolve(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result);
static void sendPromiseReject(Task* from, TaskStub* to, promiseid_t promise, v8::Handle<v8::Value> result);
static void onReceivePacket(int packetType, const char* begin, size_t length, void* userData);
static void sendPromiseMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promise, v8::Handle<v8::Value> result);
static void sendPromiseExportMessage(Task* from, TaskStub* to, MessageType messageType, promiseid_t promiseId, exportid_t exportId, v8::Handle<v8::Value> result);
static v8::Handle<v8::String> loadFile(v8::Isolate* isolate, const char* fileName);
friend struct ImportRecord;
friend class TaskStub;
};
#endif

254
src/TaskStub.cpp Normal file
View File

@ -0,0 +1,254 @@
#include "TaskStub.h"
#include "PacketStream.h"
#include "Serialize.h"
#include "Task.h"
#include "TaskTryCatch.h"
#include <cstring>
#ifdef _WIN32
#include <io.h>
#include <windows.h>
#include <ws2tcpip.h>
static const int STDIN_FILENO = 0;
static const int STDOUT_FILENO = 1;
static const int STDERR_FILENO = 2;
#else
#include <unistd.h>
#endif
bool TaskStub::_determinedExecutable = false;
char TaskStub::_executable[1024];
void TaskStub::initialize() {
if (!_determinedExecutable) {
size_t size = sizeof(_executable);
uv_exepath(_executable, &size);
_determinedExecutable = true;
}
}
TaskStub::TaskStub() {
initialize();
std::memset(&_process, 0, sizeof(_process));
}
void TaskStub::ref() {
if (++_refCount == 1) {
_taskObject.ClearWeak();
}
}
void TaskStub::release() {
if (--_refCount == 0) {
_taskObject.SetWeak(this, onRelease);
}
}
TaskStub* TaskStub::createParent(Task* task, uv_file file) {
v8::Isolate::Scope isolateScope(task->_isolate);
v8::HandleScope scope(task->_isolate);
v8::Local<v8::Context> context = v8::Context::New(task->_isolate, 0);
context->Enter();
v8::Handle<v8::ObjectTemplate> parentTemplate = v8::ObjectTemplate::New(task->_isolate);
parentTemplate->SetInternalFieldCount(1);
v8::Handle<v8::Object> parentObject = parentTemplate->NewInstance();
TaskStub* parentStub = new TaskStub();
parentStub->_taskObject.Reset(task->_isolate, v8::Local<v8::Object>::New(task->_isolate, parentObject));
parentObject->SetInternalField(0, v8::External::New(task->_isolate, parentStub));
parentStub->_owner = task;
parentStub->_id = Task::kParentId;
if (uv_pipe_init(task->_loop, &parentStub->_stream.getStream(), 1) != 0) {
std::cerr << "uv_pipe_init failed\n";
}
parentStub->_stream.setOnReceive(Task::onReceivePacket, parentStub);
if (uv_pipe_open(&parentStub->_stream.getStream(), file) != 0) {
std::cerr << "uv_pipe_open failed\n";
}
parentStub->_stream.start();
return parentStub;
}
void TaskStub::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
Task* parent = Task::get(args.GetIsolate());
v8::HandleScope scope(args.GetIsolate());
TaskStub* stub = new TaskStub();
v8::Handle<v8::External> data = v8::External::New(args.GetIsolate(), stub);
v8::Handle<v8::ObjectTemplate> taskTemplate = v8::ObjectTemplate::New(args.GetIsolate());
taskTemplate->SetAccessor(v8::String::NewFromUtf8(args.GetIsolate(), "trusted"), getTrusted, setTrusted, data);
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "setImports"), v8::FunctionTemplate::New(args.GetIsolate(), setImports, data));
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "getExports"), v8::FunctionTemplate::New(args.GetIsolate(), getExports, data));
taskTemplate->SetAccessor(v8::String::NewFromUtf8(args.GetIsolate(), "onExit"), getOnExit, setOnExit, data);
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "activate"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::activate, data));
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "execute"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::execute, data));
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "kill"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::kill, data));
taskTemplate->Set(v8::String::NewFromUtf8(args.GetIsolate(), "statistics"), v8::FunctionTemplate::New(args.GetIsolate(), TaskStub::statistics, data));
taskTemplate->SetInternalFieldCount(1);
v8::Handle<v8::Object> taskObject = taskTemplate->NewInstance();
stub->_taskObject.Reset(args.GetIsolate(), taskObject);
taskObject->SetInternalField(0, v8::External::New(args.GetIsolate(), stub));
stub->_owner = parent;
taskid_t id = 0;
if (parent) {
do {
id = parent->_nextTask++;
if (parent->_nextTask == Task::kParentId) {
++parent->_nextTask;
}
} while (parent->_children.find(id) != parent->_children.end());
parent->_children[id] = stub;
}
stub->_id = id;
char arg1[] = "--child";
char* argv[] = { _executable, arg1, 0 };
uv_pipe_t* pipe = reinterpret_cast<uv_pipe_t*>(&stub->_stream.getStream());
std::memset(pipe, 0, sizeof(*pipe));
if (uv_pipe_init(parent->getLoop(), pipe, 1) != 0) {
std::cerr << "uv_pipe_init failed\n";
}
uv_stdio_container_t io[3];
io[0].flags = static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE);
io[0].data.stream = reinterpret_cast<uv_stream_t*>(pipe);
io[1].flags = UV_INHERIT_FD;
io[1].data.fd = STDOUT_FILENO;
io[2].flags = UV_INHERIT_FD;
io[2].data.fd = STDERR_FILENO;
uv_process_options_t options = {0};
options.args = argv;
options.exit_cb = onProcessExit;
options.stdio = io;
options.stdio_count = sizeof(io) / sizeof(*io);
options.file = argv[0];
stub->_process.data = stub;
int result = uv_spawn(parent->getLoop(), &stub->_process, &options);
if (result == 0) {
stub->_stream.setOnReceive(Task::onReceivePacket, stub);
stub->_stream.start();
args.GetReturnValue().Set(taskObject);
} else {
std::cerr << "uv_spawn failed: " << uv_strerror(result) << "\n";
}
}
void TaskStub::onProcessExit(uv_process_t* process, int64_t status, int terminationSignal) {
TaskStub* stub = reinterpret_cast<TaskStub*>(process->data);
if (!stub->_onExit.IsEmpty()) {
TaskTryCatch tryCatch(stub->_owner);
v8::HandleScope scope(stub->_owner->_isolate);
v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(stub->_owner->_isolate, stub->_onExit);
v8::Handle<v8::Value> args[2];
args[0] = v8::Integer::New(stub->_owner->_isolate, status);
args[1] = v8::Integer::New(stub->_owner->_isolate, terminationSignal);
callback->Call(callback, 2, &args[0]);
}
stub->_stream.close();
stub->_owner->_children.erase(stub->_id);
uv_close(reinterpret_cast<uv_handle_t*>(process), 0);
}
void TaskStub::onRelease(const v8::WeakCallbackData<v8::Object, TaskStub>& data) {
}
void TaskStub::getTrusted(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(v8::Boolean::New(args.GetIsolate(), false));
}
void TaskStub::setTrusted(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
bool trusted = value->BooleanValue();
stub->_stream.send(kSetTrusted, reinterpret_cast<char*>(&trusted), sizeof(trusted));
}
}
void TaskStub::getExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
TaskTryCatch tryCatch(stub->_owner);
v8::HandleScope scope(args.GetIsolate());
promiseid_t promise = stub->_owner->allocatePromise();
Task::sendPromiseMessage(stub->_owner, stub, kGetExports, promise, v8::Undefined(args.GetIsolate()));
args.GetReturnValue().Set(stub->_owner->getPromise(promise));
}
}
void TaskStub::setImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
std::vector<char> buffer;
Serialize::store(Task::get(args.GetIsolate()), buffer, args[0]);
stub->_stream.send(kSetImports, &*buffer.begin(), buffer.size());
}
}
void TaskStub::getOnExit(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args) {
TaskTryCatch tryCatch(TaskStub::get(args.Data())->_owner);
v8::HandleScope scope(args.GetIsolate());
args.GetReturnValue().Set(v8::Local<v8::Function>::New(args.GetIsolate(), TaskStub::get(args.Data())->_onExit));
}
void TaskStub::setOnExit(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args) {
TaskTryCatch tryCatch(TaskStub::get(args.Data())->_owner);
v8::HandleScope scope(args.GetIsolate());
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > function(args.GetIsolate(), v8::Handle<v8::Function>::Cast(value));
TaskStub::get(args.Data())->_onExit = function;
}
TaskStub* TaskStub::get(v8::Handle<v8::Value> object) {
return reinterpret_cast<TaskStub*>(v8::Handle<v8::External>::Cast(object)->Value());
}
v8::Handle<v8::Object> TaskStub::getTaskObject() {
return v8::Local<v8::Object>::New(_owner->getIsolate(), _taskObject);
}
void TaskStub::activate(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
TaskTryCatch tryCatch(stub->_owner);
v8::HandleScope scope(args.GetIsolate());
v8::String::Utf8Value fileName(args[0]->ToString(args.GetIsolate()));
stub->_stream.send(kActivate, 0, 0);
}
}
void TaskStub::execute(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
TaskTryCatch tryCatch(stub->_owner);
v8::HandleScope scope(args.GetIsolate());
promiseid_t promise = stub->_owner->allocatePromise();
Task::sendPromiseMessage(stub->_owner, stub, kExecute, promise, args[0]);
args.GetReturnValue().Set(stub->_owner->getPromise(promise));
}
}
void TaskStub::kill(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
uv_process_kill(&stub->_process, SIGTERM);
}
}
void TaskStub::statistics(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TaskStub* stub = TaskStub::get(args.Data())) {
TaskTryCatch tryCatch(stub->_owner);
v8::HandleScope scope(args.GetIsolate());
promiseid_t promise = stub->_owner->allocatePromise();
Task::sendPromiseMessage(stub->_owner, stub, kStatistics, promise, v8::Undefined(args.GetIsolate()));
args.GetReturnValue().Set(stub->_owner->getPromise(promise));
}
}

63
src/TaskStub.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef INCLUDED_TaskStub
#define INCLUDED_TaskStub
#include "PacketStream.h"
#include <v8.h>
class Task;
typedef int taskid_t;
class TaskStub {
public:
void ref();
void release();
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
static TaskStub* createParent(Task* task, uv_file file);
static void initialize();
taskid_t getId() { return _id; }
Task* getOwner() { return _owner; }
v8::Handle<v8::Object> getTaskObject();
PacketStream& getStream() { return _stream; }
private:
v8::Persistent<v8::Object> _taskObject;
int _refCount = 1;
Task* _owner = 0;
PacketStream _stream;
taskid_t _id = -1;
uv_process_t _process;
v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function> > _onExit;
static bool _determinedExecutable;
static char _executable[1024];
TaskStub();
static TaskStub* get(v8::Handle<v8::Value> object);
static void getTrusted(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void setTrusted(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args);
static void getExports(const v8::FunctionCallbackInfo<v8::Value>& args);
static void setImports(const v8::FunctionCallbackInfo<v8::Value>& args);
static void getOnExit(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& args);
static void setOnExit(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& args);
static void activate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void execute(const v8::FunctionCallbackInfo<v8::Value>& args);
static void kill(const v8::FunctionCallbackInfo<v8::Value>& args);
static void statistics(const v8::FunctionCallbackInfo<v8::Value>& args);
static void onRelease(const v8::WeakCallbackData<v8::Object, TaskStub>& data);
static void onProcessExit(uv_process_t* process, int64_t status, int terminationSignal);
};
#endif

57
src/TaskTryCatch.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "TaskTryCatch.h"
#include "Task.h"
#include <iostream>
const char* TaskTryCatch::toString(const v8::String::Utf8Value& value) {
return *value ? *value : "(null)";
}
TaskTryCatch::TaskTryCatch(Task* task) {
_tryCatch.SetCaptureMessage(true);
_tryCatch.SetVerbose(true);
}
TaskTryCatch::~TaskTryCatch() {
if (_tryCatch.HasCaught()) {
if (v8::Isolate* isolate = v8::Isolate::GetCurrent()) {
if (Task* task = reinterpret_cast<Task*>(isolate->GetData(0))) {
std::cerr << "Task[" << task << ':' << task->getName() << "] ";
}
}
std::cerr << "Exception:\n";
v8::Handle<v8::Message> message(_tryCatch.Message());
if (!message.IsEmpty()) {
std::cerr
<< toString(v8::String::Utf8Value(message->GetScriptResourceName()))
<< ':'
<< message->GetLineNumber()
<< ": "
<< toString(v8::String::Utf8Value(_tryCatch.Exception()))
<< '\n';
std::cerr << toString(v8::String::Utf8Value(message->GetSourceLine())) << '\n';
for (int i = 0; i < message->GetStartColumn(); ++i) {
std::cerr << ' ';
}
for (int i = message->GetStartColumn(); i < message->GetEndColumn(); ++i) {
std::cerr << '^';
}
if (!message->GetStackTrace().IsEmpty()) {
for (int i = 0; i < message->GetStackTrace()->GetFrameCount(); ++i) {
std::cerr << "oops " << i << "\n";
}
}
std::cerr << '\n';
} else {
std::cerr << toString(v8::String::Utf8Value(_tryCatch.Exception())) << '\n';
}
v8::String::Utf8Value stackTrace(_tryCatch.StackTrace());
if (stackTrace.length() > 0) {
std::cerr << *stackTrace << '\n';
}
}
}

18
src/TaskTryCatch.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef INCLUDED_TaskTryCatch
#define INCLUDED_TaskTryCatch
#include <v8.h>
class Task;
class TaskTryCatch {
public:
TaskTryCatch(Task* task);
~TaskTryCatch();
private:
v8::TryCatch _tryCatch;
static const char* toString(const v8::String::Utf8Value& value);
};
#endif

1126
src/Tls.cpp Normal file

File diff suppressed because it is too large Load Diff

50
src/Tls.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef INCLUDED_Tls
#define INCLUDED_Tls
#include <cstddef>
class TlsSession;
class TlsContext {
public:
static TlsContext* create();
virtual ~TlsContext() {}
virtual TlsSession* createSession() { return 0; }
virtual bool setCertificate(const char* certificate) { return false; }
virtual bool setPrivateKey(const char* privateKey) { return false; }
virtual bool addTrustedCertificate(const char* certificate) { return false; }
};
class TlsSession {
public:
virtual ~TlsSession() {}
virtual void setHostname(const char* hostname) {}
virtual void startAccept() = 0;
virtual void startConnect() = 0;
virtual void shutdown() = 0;
virtual int getPeerCertificate(char* buffer, size_t bytes) { return -1; }
enum HandshakeResult {
kDone,
kMore,
kFailed,
};
virtual HandshakeResult handshake() = 0;
enum ReadResult {
kReadZero = -1,
kReadFailed = -2,
};
virtual int readPlain(char* buffer, size_t bytes) = 0;
virtual int writePlain(const char* buffer, size_t bytes) = 0;
virtual int readEncrypted(char* buffer, size_t bytes) = 0;
virtual int writeEncrypted(const char* buffer, size_t bytes) = 0;
virtual bool getError(char* buffer, size_t bytes) { return false; }
};
#endif

115
src/TlsContextWrapper.cpp Normal file
View File

@ -0,0 +1,115 @@
#include "TlsContextWrapper.h"
#include "Task.h"
#include "Tls.h"
#include <assert.h>
int TlsContextWrapper::_count = 0;
void TlsContextWrapper::create(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::HandleScope handleScope(args.GetIsolate());
if (TlsContextWrapper* wrapper = new TlsContextWrapper(Task::get(args.GetIsolate()))) {
v8::Handle<v8::Object> result = v8::Local<v8::Object>::New(args.GetIsolate(), wrapper->_object);
args.GetReturnValue().Set(result);
wrapper->release();
}
}
TlsContextWrapper::TlsContextWrapper(Task* task) {
++_count;
v8::HandleScope scope(task->getIsolate());
v8::Handle<v8::External> identifier = v8::External::New(task->getIsolate(), reinterpret_cast<void*>(&TlsContextWrapper::create));
v8::Handle<v8::External> data = v8::External::New(task->getIsolate(), this);
v8::Local<v8::ObjectTemplate> wrapperTemplate = v8::ObjectTemplate::New(task->getIsolate());
wrapperTemplate->SetInternalFieldCount(2);
wrapperTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "setCertificate"), v8::FunctionTemplate::New(task->getIsolate(), setCertificate, data));
wrapperTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "setPrivateKey"), v8::FunctionTemplate::New(task->getIsolate(), setPrivateKey, data));
wrapperTemplate->Set(v8::String::NewFromUtf8(task->getIsolate(), "addTrustedCertificate"), v8::FunctionTemplate::New(task->getIsolate(), addTrustedCertificate, data));
v8::Local<v8::Object> wrapperObject = wrapperTemplate->NewInstance();
wrapperObject->SetInternalField(0, identifier);
wrapperObject->SetInternalField(1, data);
_object.Reset(task->getIsolate(), wrapperObject);
_context = TlsContext::create();
_task = task;
}
TlsContextWrapper::~TlsContextWrapper() {
close();
--_count;
}
void TlsContextWrapper::close() {
if (_context) {
delete _context;
_context = 0;
}
}
void TlsContextWrapper::onRelease(const v8::WeakCallbackData<v8::Object, TlsContextWrapper>& data) {
data.GetParameter()->_object.Reset();
delete data.GetParameter();
}
TlsContextWrapper* TlsContextWrapper::get(v8::Handle<v8::Value> value) {
TlsContextWrapper* result = 0;
if (!value.IsEmpty()
&& value->IsObject())
{
v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(value);
if (object->InternalFieldCount() == 2
&& v8::Handle<v8::External>::Cast(object->GetInternalField(0))->Value() == &TlsContextWrapper::create)
{
result = reinterpret_cast<TlsContextWrapper*>(v8::Handle<v8::External>::Cast(object->GetInternalField(1))->Value());
}
}
return result;
}
TlsContextWrapper* TlsContextWrapper::get(const v8::FunctionCallbackInfo<v8::Value>& args) {
return reinterpret_cast<TlsContextWrapper*>(v8::Handle<v8::External>::Cast(args.Data())->Value());
}
void TlsContextWrapper::ref() {
if (++_refCount == 1) {
_object.ClearWeak();
}
}
void TlsContextWrapper::release() {
assert(_refCount >= 1);
if (--_refCount == 0) {
_object.SetWeak(this, onRelease);
}
}
void TlsContextWrapper::setCertificate(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args)) {
v8::String::Utf8Value value(args[0]->ToString(args.GetIsolate()));
wrapper->_context->setCertificate(*value);
}
}
void TlsContextWrapper::setPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args)) {
v8::String::Utf8Value value(args[0]->ToString(args.GetIsolate()));
wrapper->_context->setPrivateKey(*value);
}
}
void TlsContextWrapper::addTrustedCertificate(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (TlsContextWrapper* wrapper = TlsContextWrapper::get(args)) {
v8::String::Utf8Value value(args[0]->ToString(args.GetIsolate()));
wrapper->_context->addTrustedCertificate(*value);
}
}
int TlsContextWrapper::getCount()
{
return _count;
}

42
src/TlsContextWrapper.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef INCLUDED_TlsContextWrapper
#define INCLUDED_TlsContextWrapper
#include <v8.h>
class Task;
class TlsContext;
class TlsContextWrapper {
public:
static void create(const v8::FunctionCallbackInfo<v8::Value>& args);
void close();
static TlsContextWrapper* get(v8::Handle<v8::Value> value);
static void setCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void setPrivateKey(const v8::FunctionCallbackInfo<v8::Value>& args);
static void addTrustedCertificate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void onRelease(const v8::WeakCallbackData<v8::Object, TlsContextWrapper>& data);
TlsContext* getContext() { return _context; }
static int getCount();
private:
TlsContextWrapper(Task* task);
~TlsContextWrapper();
static TlsContextWrapper* get(const v8::FunctionCallbackInfo<v8::Value>& args);
TlsContext* _context = 0;
Task* _task = 0;
v8::Persistent<v8::Object> _object;
int _refCount = 1;
static int _count;
void ref();
void release();
};
#endif

75
src/main.cpp Normal file
View File

@ -0,0 +1,75 @@
#include "Task.h"
#include "TaskStub.h"
#include "TaskTryCatch.h"
#include <cstring>
#include <libplatform/libplatform.h>
#include <uv.h>
#include <v8.h>
#include <v8-platform.h>
#if !defined (_WIN32) && !defined (__MACH__)
#include <signal.h>
#include <sys/prctl.h>
#include <unistd.h>
#endif
v8::Platform* gPlatform = 0;
int main(int argc, char* argv[]) {
uv_setup_args(argc, argv);
TaskStub::initialize();
v8::V8::InitializeICU();
gPlatform = v8::platform::CreateDefaultPlatform();
v8::V8::InitializePlatform(gPlatform);
v8::V8::Initialize();
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
bool isChild = false;
const char* coreTask = "core/core.js";
for (int i = 1; i < argc; ++i) {
if (!std::strcmp(argv[i], "--child")) {
isChild = true;
} else {
coreTask = argv[i];
}
}
#if !defined (_WIN32)
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
perror("signal");
}
#endif
if (isChild) {
#if !defined (_WIN32) && !defined (__MACH__)
prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
Task task;
task.configureFromStdin();
task.activate();
task.run();
} else {
#if !defined (_WIN32) && !defined (__MACH__)
setpgid(0, 0);
#endif
Task task;
task.setTrusted(true);
task.activate();
{
v8::Isolate::Scope isolateScope(task.getIsolate());
v8::HandleScope handleScope(task.getIsolate());
v8::Context::Scope contextScope(task.getContext());
TaskTryCatch tryCatch(&task);
task.execute(coreTask);
}
task.run();
}
v8::V8::Dispose();
return 0;
}