/ This function returns a new array with three elements, x, y, and z.
Local<Array> NewPointArray(int x, int y, int z) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
// We will be creating temporary handles so we use a handle scope.
EscapableHandleScope handle_scope(isolate);
// Create a new empty array.
Local<Array> array = Array::New(isolate, 3);
// Return an empty result if there was an error creating the array.
if (array.IsEmpty())
return Local<Array>();
// Fill out the values
array->Set(0, Integer::New(isolate, x));
array->Set(1, Integer::New(isolate, y));
array->Set(2, Integer::New(isolate, z));
// Return the value through Escape.
return handle_scope.Escape(array);
}
All V8 code should work. Moreover, v8pp has a converter std::vector<T>
<-> v8::Array
, so you can return a std::vector from a wrapped function and to use Javascript array as an argument for a wrapped C++ function.
Fundamental types and user classes wrapped with v8pp::class_
would be converted from/to V8 array items. And there is an overloaded template function v8pp::to_v8(Iterator begin, Iterator end)
to convert a pair of iterators into v8::Array
I was wondering if this was the correct way or best way to return a node that would be created by a javascript call. ex. getElementById would be called somewhere in javascript then the function would create a new node to return.
class dom_node
{
public:
v8::Handle<v8::Object> getElementById(char const* name)
{
...
return v8pp::class_<dom_node>::import_external(v8::Isolate::GetCurrent(),
new dom_node(node.select_node(sz_cmd.c_str()).node()));
}
}
This works, but because it's returning a v8::handle the function would be specific to v8 and wouldn't really be that useful to other c++ code. Like would it be possible to just return a pointer ex. dom_node getElementById(char const name){return new dom_node}
class dom_node
{
public:
dom_node* getElementById(char const* name)
{
...
dom_node* result = new dom_node(node.select_node(sz_cmd.c_str()).node());
v8pp::class_<dom_node>::import_external(v8::Isolate::GetCurrent(), result);
return result;
}
};
figured out how to use an object as an array. Simply giving the object an index sets the array accessor.
v8pp::class_<location> loc_class(_d->context->isolate());
loc_class
.set("href", v8pp::property(&location::get_url, &location::java_load_url))
.set("pathname", v8pp::property(&location::get_path_name, &location::java_load_url))
.set("hostname", v8pp::property(&location::get_path_name, &location::java_load_url))
.set("search", &location::get_query);
v8::Handle<v8::Object> location_obj =
v8pp::class_<location>::import_external(_d->context->isolate(), new location(_d->browser));
location_obj->Set(0, v8pp::to_v8(_d->context->isolate(), 10));
so location[0] would return 10 and location.href would return the url. A lot easier to integrate with v8pp lol
I didn't catch about default get/set method. do you need to have a default conversion to string for a wrapped C++ object in JS? As I remember, there should be a toString()
function in object prototype on JS side:
struct Some
{
std::string to_string() const { return "I am Some"; }
};
v8pp:class_<Some> some_js_class(isolate);
some_js_class.set("toString", &some::to_string);
In javascript it should work as:
var some = new Some();
var s = "" + some; // implicit call some.toString()
s == "I am Some"
using namespace v8;
V8::InitializeICU();
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
// Create a new Isolate and make it the current one.
ArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::New(create_params);
{
Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
// Create a new context.
Local<Context> context = Context::New(isolate);
context->SetSecurityToken(v8pp::to_v8(isolate, "test"));
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Local<String> source =
String::NewFromUtf8(isolate, "var x = 'hello'; x;",
NewStringType::kNormal).ToLocalChecked();
// Compile the source code.
Local<Script> script = Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
Local<Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
//Context::New()
Local<Context> context2 = Context::New(isolate);
Context::Scope context_scope2(context2);
context2->Enter();
context2->SetSecurityToken(context->GetSecurityToken());
context2->Global()->Set(v8pp::to_v8(isolate, "window"), context->Global());
Local<String> source2 =
String::NewFromUtf8(isolate, "window.x;",
NewStringType::kNormal).ToLocalChecked();
Local<Script> script2 = Script::Compile(context2, source2).ToLocalChecked();
v8::TryCatch try_catch(isolate);
v8::Local<v8::Value> result2;
// Run the script to get the result.
if (!script2->Run(context2).ToLocal(&result2)) {
assert(try_catch.HasCaught());
// Print errors that happened during execution.
ReportException(isolate, &try_catch);
return false;
}
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf82(result2);
printf("%s\n", *utf82);
}
You can create multiple v8pp::context
instances in the same v8::Isolate
. Constructor of v8pp::context
may accept existing v8::Isolate objects to re-use it.
Probably you may add another optional parameter for security token in v8pp::context
ctor to call v8::Conext::SetSecurityToken()
with it. So the code above would look like this:
// V8 init boilerplate
using namespace v8;
V8::InitializeICU();
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
Isolate* isolate = nullptr; // would be created in first context
char const* const security_token = "test";
v8pp::context context(nullptr, security_token); // create Isolate, context, enter there
isolate = context.isolate();
Local<Value> result =context.run_script("var x = 'hello'; x").ToLocalChecked();
// create and enttr to another context with the same isolate and security token
v8pp::context context2(context.isolate(), security_token);
context2.set("window", context.global()); // TODO: add v8pp::context.global() to return the Global object
Local<Value> result2 =context.run_script("window.x").ToLocalChecked();
v8::HandleScope
allows to cleanup local handles on exit from the scope, documentation: https://developers.google.com/v8/embed#handles-and-garbage-collection
v8::Context
and v8::Isolate
have scope classes for automatic call Enter()
and Exit()
functions. This is the common way in C++ named RAII: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
ah okay. I was thinking of just changing the v8pp context code to not enter a child context automatically and then just save a variable saying that it's a child and that would enter/exit when needed. Probably also add enter/exit functions so that it can be entered when doing a group of similar calls? or maybe that is not even needed because in the v8.h documentation it says that the enter function would only be used for compiling and running code. You wouldn't happen to know if anything else besides compiling/running requires a context to be entered?
anyways I'm going to try that and hope everything works lol. also I tried to delete the .sdf file to stop the crashing, but it didn't work :( I'm using vs 2013
I removed the get_external_data and set_external_data functions from function.hpp and that stopped it from crashing. or maybe just disabled the problem further down the line. I couldn't see anything wrong with the functions. so no idea why it's crashing for me.
got it to work using this code
v8pp::context test_context(nullptr);
test_context.set_security_token("test");
v8::HandleScope handle_scope(test_context.isolate());
v8pp::context child_context(test_context.isolate(), "test");
child_context.set_security_token(test_context);
std::string script = "var x = 'hello'; x;";
Local<Value> result = test_context.run_script(script);
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
v8pp::context child_context2(test_context.isolate(), "test");
child_context2.set_security_token(test_context);
child_context.set("window", test_context);
script = "window.x;";
result = child_context.run_script(script);
String::Utf8Value utf82(result);
printf("%s\n", *utf82);
child_context2.set("window", test_context);
script = "window.x;";
result = child_context2.run_script(script);
String::Utf8Value utf83(result);
printf("%s\n", *utf83);
so set security token creates a new handle<value> to set as the token then there's an overloaded function that will transfer the security token. I couldn't get it to work with just using the same string as the value. I guess it needs to use the same handle<value>?
then I added a new set overload that adds another context global object to the calling contexts global object.
and added a check to see if it has its own isolate in run script and enter/exit if it doesnt.
I thought security token has a string type. Since it's a v8::Value
you might store security token inv8pp::context
as a persistent handle, either to get it from v8::context:
Here's possible implementation:
v8::Handle<v8::Object> v8pp::context::global()
{
/* Note the comment from v8.h:
Returns the global proxy object.
*
* Global proxy object is a thin wrapper whose prototype points to actual
* context's global object with the properties like Object, etc. This is done
* that way for security reasons (for more details see
* https://wiki.mozilla.org/Gecko:SplitWindow).
*
* Please note that changes to global proxy object prototype most probably
* would break VM---v8 expects only global object as a prototype of global
* proxy object.
*/
return v8pp::to_local(isolate_, impl_)->Global();
}
v8::Handle<v8::Value> v8pp::context::security_token()
{
return v8pp::to_local(isolate_, impl_)->GetSecurityToken();
}
void v8pp::context::set_security_token(v8::Handle<v8::Value> value)
{
return v8pp::to_local(isolate_, impl_)->SetSecurityToken(value);
}
If you need to have a context clone, maybe you need to use a copy constructor:
v8pp::context::context(context const& src)
: own_isolate_(false)
, isolate_(src)
{
v8::HandleScope scope(isolate_);
v8::Local<v8::ObjectTemplate> src_templ = to_local(isolate_, src.templ_);
v8::Local<v8::Object> src_global = to_local(isolate_, src.impl_)->Global();
v8::Local<v8::Context> impl = v8::Context::New(isolate_, nullptr, src_templ, src_global);
impl->Enter();
impl_.Reset(isolate_, impl);
}
It requires to store an ObjectTemplate used to create v8::Context
as a persistent handle in v8pp::context
instance.
Having copy ctor you may quik create many contexts (but they will be in initial state):
v8pp::context parent; // create, enter to parent
{
v8pp::context child = parent; // copy initial global from parent, enter to child context
{
v8pp::context child2 = child;
...
} // exit from child 2, enter to child
} // exit from child, enter to parent
Copy ctor maybe not so goo idea, because v8pp::context becomes copy constructible with side effect (enterin into the V8 context). I'd rather would have a static function, say static v8pp::context v8pp::context::clone_and_enter(v8pp::context& src)