the following snippet is the hello-world.cc
example script from the v8 embedding guide with a few minor tweaks. I can confirm that it compiles and produces the expected result.
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);`
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'", v8::NewStringType::kNormal).ToLocalChecked();
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8(isolate, result);
std::cout << *utf8 << std::endl;
}
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
return 0;
}
I wanted to refactor this script such that the initialisation is repeatable, as I will be needing multiple isolates. I also wanted to separate the execution of the code. I refactored the script to look like this:
#include <iostream>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
v8::Local<v8::Value> execute(v8::Isolate* isolate, const char* input) {
v8::HandleScope handle_scope(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, input).ToLocalChecked();
v8::Local<v8::Script> src = v8::Script::Compile(context, source).ToLocalChecked();
v8::Local<v8::Value> result = src->Run(context).ToLocalChecked();
return result;
}
v8::Isolate* init(std::string data) {
v8::V8::InitializeICUDefaultLocation(data.c_str());
v8::V8::InitializeExternalStartupData(data.c_str());
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
return isolate;
}
int main(int args, char* argv[]) {
v8::Isolate* isolate = init(argv[0]);
auto input = "1 + 2";
auto result = execute(isolate, input);
v8::String::Utf8Value utf8(isolate, result);
std::cout << *utf8 << std::endl;
return 0;
}
The program compiles flawlessly, except produces a segfault when run. The reason for this is baffling to me. In the example script, the execution code is placed in its own scope, and fails without it. My thinking therefore is that a function should have the same effect, however moving either pieces of code into different functions also causes the program to fail.
I've also had the honour of noticing that by moving the creation of the Context
v8::HandleScope handle_scope(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
outside of the execute function causes the program to fail with the following message:
#
# Fatal error in v8::HandleScope::CreateHandle()
# Cannot create a handle without a HandleScope
#
I've seen many threads about this, all of which are answered by upgrading the NodeJS version, or using different functions to do the same thing. Unfortunately, this isn't applicable here, as I'm not using NodeJS.
I would also like to clarify that I'm not particularly eloquent with C/C++, so do excuse any noobishness. I'm simply hoping for some pointers, and to explain why/how this code breaks, and what is needed to fix it.
Thanks
All v8::Local
s that you create while a given HandleScope
is active will become invalid when that HandleScope
goes out of scope. To return a v8::Local
from a function that sets up (and destroys) its own HandleScope
, use an EscapableHandleScope
. More details: https://v8.dev/docs/embed (search for "HandleScope").