Created
July 28, 2016 12:05
-
-
Save abhi-bit/d3a2910ec54a47ce8294c2cc0c4bef93 to your computer and use it in GitHub Desktop.
Trying to expose nested map from C++ to JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cassert> | |
#include <cstdio> | |
#include <cstring> | |
#include <fstream> | |
#include <iostream> | |
#include <map> | |
#include <string> | |
#include <vector> | |
#include <include/v8.h> | |
#include <include/libplatform/libplatform.h> | |
using namespace std; | |
using namespace v8; | |
class HTTPResponse { | |
public: | |
HTTPResponse(Isolate* isolate); | |
~HTTPResponse(); | |
Local<Object> WrapHTTPResponseMap(); | |
Isolate* GetIsolate() { return isolate_; } | |
Global<ObjectTemplate> http_response_map_template_; | |
map<string, string> http_response; | |
private: | |
static Local<ObjectTemplate> MakeHTTPResponseMapTemplate(Isolate* isolate); | |
static void HTTPResponseSet(Local<Name> name, Local<Value> value, | |
const PropertyCallbackInfo<Value>& info); | |
Isolate* isolate_; | |
}; | |
class HTTPBody { | |
public: | |
HTTPBody(Isolate* isolate); | |
~HTTPBody(); | |
Local<Object> WrapHTTPBodyMap(); | |
Isolate* GetIsolate() { return isolate_; } | |
map<string, string> http_body; | |
private: | |
static void HTTPBodySet(Local<Name> name, Local<Value> value, | |
const PropertyCallbackInfo<Value>& info); | |
Isolate* isolate_; | |
}; | |
// ================================================================ | |
map<string, string>* UnwrapMap(Local<Object> obj) { | |
Local<External> field = Local<External>::Cast(obj->GetInternalField(0)); | |
void* ptr = field->Value(); | |
return static_cast<map<string, string>*>(ptr); | |
} | |
string ObjectToString(Local<Value> value) { | |
String::Utf8Value utf8_value(value); | |
return string(*utf8_value); | |
} | |
string ToString(Isolate* isolate, Handle<Value> object) { | |
HandleScope handle_scope(isolate); | |
Local<Context> context = isolate->GetCurrentContext(); | |
Local<Object> global = context->Global(); | |
Local<Object> JSON = global->Get(String::NewFromUtf8(isolate, "JSON"))->ToObject(); | |
Local<Function> JSON_stringify = | |
Local<Function>::Cast(JSON->Get( | |
String::NewFromUtf8(isolate, "stringify"))); | |
Local<Value> result; | |
Local<Value> args[1]; | |
args[0] = { object }; | |
result = JSON_stringify->Call(context->Global(), 1, args); | |
return ObjectToString(result); | |
} | |
// ================================================================ | |
HTTPBody::HTTPBody(Isolate* isolate) { | |
isolate_ = isolate; | |
} | |
HTTPBody::~HTTPBody() { } | |
void HTTPBody::HTTPBodySet(Local<Name> name, Local<Value> value_obj, | |
const PropertyCallbackInfo<Value>& info) { | |
if (name->IsSymbol()) return; | |
string key = ObjectToString(Local<String>::Cast(name)); | |
string value = ToString(info.GetIsolate(), value_obj); | |
map<string, string>* body = UnwrapMap(info.Holder()); | |
(*body)[key] = value; | |
cout << "ABHI: body field " << value << endl; | |
info.GetReturnValue().Set(value_obj); | |
} | |
Local<Object> HTTPBody::WrapHTTPBodyMap() { | |
EscapableHandleScope handle_scope(GetIsolate()); | |
Local<FunctionTemplate> body_map_template = FunctionTemplate::New(GetIsolate()); | |
body_map_template->InstanceTemplate()->SetHandler( | |
NamedPropertyHandlerConfiguration(NULL, HTTPBodySet)); | |
body_map_template->InstanceTemplate()->SetInternalFieldCount(1); | |
Local<Object> result = body_map_template->GetFunction()->NewInstance(); | |
Local<External> map_ptr = External::New(GetIsolate(), &http_body); | |
result->SetInternalField(0, map_ptr); | |
return handle_scope.Escape(result); | |
} | |
HTTPResponse::HTTPResponse(Isolate* isolate) { | |
isolate_ = isolate; | |
} | |
HTTPResponse::~HTTPResponse() { } | |
void HTTPResponse::HTTPResponseSet(Local<Name> name, Local<Value> value_obj, | |
const PropertyCallbackInfo<Value>& info) { | |
if (name->IsSymbol()) return; | |
string key = ObjectToString(Local<String>::Cast(name)); | |
string value = ToString(info.GetIsolate(), value_obj); | |
map<string, string>* response = UnwrapMap(info.Holder()); | |
(*response)[key] = value; | |
cout << "key: " << key << " value: " << value << endl; | |
Local<External> field = Local<External>::Cast(info.Holder()); | |
Isolate* isolate = static_cast<Isolate*>(field->Value()); | |
cout << __LINE__ << endl; | |
HTTPBody* body = new HTTPBody(isolate); | |
cout << __LINE__ << endl; | |
Local<Object> body_map = body->WrapHTTPBodyMap(); | |
cout << __LINE__ << endl; | |
info.GetReturnValue().Set(body_map); | |
} | |
Local<ObjectTemplate> HTTPResponse::MakeHTTPResponseMapTemplate( | |
Isolate* isolate) { | |
EscapableHandleScope handle_scope(isolate); | |
Local<ObjectTemplate> result = ObjectTemplate::New(isolate); | |
result->SetInternalFieldCount(2); | |
result->SetHandler(NamedPropertyHandlerConfiguration(NULL, HTTPResponseSet)); | |
return handle_scope.Escape(result); | |
} | |
Local<Object> HTTPResponse::WrapHTTPResponseMap() { | |
EscapableHandleScope handle_scope(GetIsolate()); | |
if (http_response_map_template_.IsEmpty()) { | |
Local<ObjectTemplate> raw_template = MakeHTTPResponseMapTemplate(GetIsolate()); | |
http_response_map_template_.Reset(GetIsolate(), raw_template); | |
} | |
Local<ObjectTemplate> templ = | |
Local<ObjectTemplate>::New(GetIsolate(), http_response_map_template_); | |
Local<Object> result = | |
templ->NewInstance(GetIsolate()->GetCurrentContext()).ToLocalChecked(); | |
Local<External> map_ptr = External::New(GetIsolate(), &http_response); | |
Local<External> isolate_ptr = External::New(GetIsolate(), isolate_); | |
result->SetInternalField(0, map_ptr); | |
result->SetInternalField(0, isolate_ptr); | |
return handle_scope.Escape(result); | |
} | |
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { | |
public: | |
virtual void* Allocate(size_t length) { | |
void* data = AllocateUninitialized(length); | |
return data == NULL ? data : memset(data, 0, length); | |
} | |
virtual void* AllocateUninitialized(size_t length) { return malloc(length); } | |
virtual void Free(void* data, size_t) { free(data); } | |
}; | |
const char* js_function = | |
"function OnHTTPGet(req, res) {" | |
" res.body.query_result = 100;" | |
" res.body.row_count = 1;" | |
"}"; | |
int main(int argc, char* argv[]) { | |
V8::InitializeICU(); | |
V8::InitializeExternalStartupData(argv[0]); | |
Platform* platform = platform::CreateDefaultPlatform(); | |
V8::InitializePlatform(platform); | |
V8::Initialize(); | |
ArrayBufferAllocator allocator; | |
Isolate::CreateParams create_params; | |
create_params.array_buffer_allocator = &allocator; | |
Isolate* isolate = Isolate::New(create_params); | |
{ | |
Isolate::Scope isolate_scope(isolate); | |
HandleScope handle_scope(isolate); | |
Local<Context> context = Context::New(isolate); | |
Context::Scope context_scope(context); | |
Local<String> source = | |
String::NewFromUtf8(isolate, js_function, | |
NewStringType::kNormal).ToLocalChecked(); | |
Local<Script> script = Script::Compile(context, source).ToLocalChecked(); | |
Local<Value> result = script->Run(context).ToLocalChecked(); | |
const char* http_req = "{\"path\": \"/root\"}"; | |
HTTPResponse* response = new HTTPResponse(isolate); | |
Handle<Value> args[2]; | |
args[0] = JSON::Parse(String::NewFromUtf8(isolate, http_req)); | |
args[1] = response->WrapHTTPResponseMap(); | |
Local<String> on_http_get = | |
String::NewFromUtf8(isolate, "OnHTTPGet", NewStringType::kNormal) | |
.ToLocalChecked(); | |
Local<Value> on_http_get_val; | |
if(!context->Global()->Get(context, on_http_get).ToLocal(&on_http_get_val)) | |
cout << "Failed to grab OnHTTPGet function " << endl; | |
if(on_http_get_val->IsFunction()) | |
cout << "Yes it's a function" << endl; | |
Local<Function> on_http_get_fun = Local<Function>::Cast(on_http_get_val); | |
cout << __LINE__ << endl; | |
on_http_get_fun->Call(context->Global(), 2, args); | |
cout << __LINE__ << endl; | |
} | |
cout << __LINE__ << endl; | |
isolate->Dispose(); | |
V8::Dispose(); | |
V8::ShutdownPlatform(); | |
delete platform; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I line 147/148 you're writing two different values to the same internal field. I think storing the isolate pointer is not necessary, as you can just get it via info.GetIsolate() in the handler.
The actual bug is that response needs a getter, not a setter. "res.body.query_result" first gets body from res and then sets query_result on body.