forked from cory/tildefriends
Cory McWilliams
4313cfbc4f
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3359 ed5197a5-7fde-0310-b194-c3ffbd925b24
227 lines
7.9 KiB
C++
227 lines
7.9 KiB
C++
#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->IsUint8Array()) {
|
|
writeInt32(buffer, kUint8Array);
|
|
v8::Handle<v8::Uint8Array> array = v8::Handle<v8::Uint8Array>::Cast(value);
|
|
char* data = reinterpret_cast<char*>(array->Buffer()->GetContents().Data());
|
|
size_t length = array->Buffer()->GetContents().ByteLength();
|
|
writeInt32(buffer, length);
|
|
buffer.insert(buffer.end(), data, data + 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 kUint8Array:
|
|
{
|
|
int32_t length = readInt32(buffer, offset);
|
|
v8::Handle<v8::ArrayBuffer> array = v8::ArrayBuffer::New(task->getIsolate(), length);
|
|
std::memcpy(array->GetContents().Data(), &*buffer.begin() + offset, length);
|
|
offset += length;
|
|
result = v8::Uint8Array::New(array, 0, 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;
|
|
}
|
|
}
|