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::Locals 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").
Related
I would like to be able to create C++ classes and expose them into the V8 JavaScript engine. I'm using the v8pp library to do this, and by following their examples as well as the Google V8 Hello World Script, I have come to this code
main.cpp file
TestClass1.h - whose class I'd like to expose to JavaScript
CMakeList.txt file - so you can see how I've linked V8 if this is important):
// main.cpp
#define V8_COMPRESS_POINTERS
#include <v8.h>
#include <libplatform.h>
#include <v8pp/module.hpp>
#include <v8pp/class.hpp>
#include "src/TestClass1.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);
v8pp::module window(isolate);
v8pp::class_<TestClass1> TestClass1_V8(isolate);
TestClass1_V8
.ctor<int, int>()
.set("a", v8pp::property(&TestClass1::getA, &TestClass1::setA))
.set("b", v8pp::property(&TestClass1::getB, &TestClass1::setB))
//.set_static("c", 5, true)
.set("methodA", &TestClass1::testMethod);
window.set("TestClass1", TestClass1_V8);
isolate->GetCurrentContext()->Global()->Set(isolate->GetCurrentContext(), v8pp::to_v8(isolate, "window"), window.new_instance());
v8::Context::Scope context_scope(context);
{
v8::Local<v8::String> source = v8::String::NewFromUtf8Literal(isolate, "(function() {let t = new window.TestClass1(); t.a = 5; return t.a})()");
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);
printf("%s\n", *utf8);
}
}
return 0;
}
// src/TestClass1.h
#ifndef V8PP_TESTCLASS1_H
#define V8PP_TESTCLASS1_H
class TestClass1 {
friend int main(int argc, char* argv[]);
public:
static int m_c;
TestClass1(int a, int b) {
m_a = a;
m_b = b;
}
int testMethod(int c) {
return m_a + m_b + c;
}
private:
int m_a;
int m_b;
int getA() {
return m_a;
}
void setA(int a) {
m_a = 2 * a;
}
int getB() {
return m_b;
}
void setB(int b) {
m_b = 3 + b;
}
};
#endif //V8PP_TESTCLASS1_H
# CMakeList.txt
cmake_minimum_required(VERSION 3.19)
project(V8PP)
set(CMAKE_CXX_STANDARD 20)
### V8 ### [MANUAL]
set(CMAKE_CXX_FLAGS "/MT")
set(CMAKE_C_FLAGS "/MT")
if(MSVC)
add_compile_options(
$<$<CONFIG:>:/MT> #---------|
$<$<CONFIG:Debug>:/MTd> #---|-- Statically link the runtime libraries
$<$<CONFIG:Release>:/MT> #--|
)
endif()
include_directories(E:/V8/depot_tools/v8/include)
include_directories(E:/V8/depot_tools/v8/include/libplatform)
include_directories(${CMAKE_SOURCE_DIR}/v8pp-master/v8pp-master)
link_directories(E:/V8/depot_tools/v8/out.gn/x64.release/obj/)
link_directories(E:/V8/depot_tools/v8/out.gn/x64.release/obj/third_party)
link_directories(E:/V8/depot_tools/v8/out.gn/x64.release/obj/third_party/icu)
link_libraries(
v8_libbase
v8_libplatform
v8_monolith
icuuc
icui18n
)
link_libraries(winmm.lib)
link_libraries(dbghelp.lib)
link_libraries(shlwapi.lib)
### V8 ###
add_executable(V8PP main.cpp)
I've isolated the error down to the line
isolate->GetCurrentContext()->Global()->Set(isolate->GetCurrentContext(), v8pp::to_v8(isolate, "window"), window.new_instance());
Specifically window.new_instance(). Going into the v8pp source code (file module.hpp), the only line in the method is
return obj_->NewInstance(isolate_->GetCurrentContext()).ToLocalChecked();
I separated out the different statements onto separate lines, and the error is coming from the obj_->NewInstance(), where obj_ is a v8::Local<v8::ObjectTemplate>, created in the initializer list of the constructor of the module object. This function call is part of v8 itself, but I only have access to the header files of v8, so I don't know what has caused the error.
The code builds fine, but when it's run, there isn't a traceback, just:
Process finished with exit code -1073741819 (0xC0000005)
implying a memory access error (maybe to do with pointers?)
Does anyone know how to add a new instance of a v8pp module into the v8 engine without this crash occurring?
Edit
Using:
Windows 10
C++ 20
CMake (on CLion)
MSVC 2019 64-bit
I found the issue: firstly, I had to move the line of code
v8::Context::Scope context_scope(context);
to directly under the line
v8::Local<v8::Context> context = v8::Context::New(isolate);
This did create another error
#
# Fatal error in v8::ToLocalChecked
# Empty MaybeLocal.
#
<unknown>:21: Uncaught argument count does not match function definition
which was because when I called the constructor in JavaScript, I forgot to add the arguments, so changing the JavaScript code to
(function() {let t = new window.TestClass1(); t.a = 5; return t.a;})()
and everything works.
Trying to compile V8 samples/hello-world.cc on ubuntu
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
int main(int argc, char* argv[]) {
// Initialize V8.
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();
// Create a new Isolate and make it the current one.
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);
// Create a stack-allocated handle scope.
v8::HandleScope handle_scope(isolate);
// Create a new context.
v8::Local<v8::Context> context = v8::Context::New(isolate);
// Enter the context for compiling and running the hello world script.
v8::Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source =
v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'",
v8::NewStringType::kNormal)
.ToLocalChecked();
// Compile the source code.
v8::Local<v8::Script> script =
v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
v8::String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
return 0;
}
with following command:
g++ -I. -Iinclude samples/hello-world.cc -o hello-world -Wl,--start-group \
out.gn/x64.release/libicui18n.so out.gn/x64.release/libicuuc.so\
out.gn/x64.release/libv8_libbase.so\
out.gn/x64.release/libv8_libplatform.so\ out.gn/x64.release/libv8.so\
out.gn/x64.release/libc++.so\
-Wl,--end-group -lrt -ldl -pthread -std=c++0x
And getting an Error:
hello-world.cc:(.text+0x75): undefined reference to
v8::platform::NewDefaultPlatform(int, v8::platform::IdleTaskSupport,
v8::platform::InProcessStackDumping,
std::unique_ptr<v8::TracingController,
std::default_delete<v8::TracingController> >)
Note that I built V8 with GN args:
"is_debug = false, is_official_build = true, is_component_build = true, is_cfi = false, is_clang = false, v8_use_external_startup_data = false, treat_warnings_as_errors = false, use_custom_libcxx = false, use_sysroot = false, use_gold = false"
Am I doing something wrong with V8 built or compilation of samples/hello-world.cc
In your compiler command line, try removing the \ after out.gn/x64.release/libv8_libplatform.so. \ is an "escape" character, so it changes how the following space is interpreted -- specifically, it makes it part of the file name rather than a file name separator, resulting in a filename that does not exist.
Does that help?
Is V8 Broken or am I?
I want to add a JS Date to a Object available in the global object. This works with a older version (4.9.385.28) but fails with (5.0.71.33)... see output
g++ -I/usr/local core.c -o testCore -ldl -pthread -std=c++0x -lv8 -lv8_libplatform -lv8_libbase
core.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
#include "time.h"
using namespace v8;
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); }
};
int main(int argc, char* argv[])
{
// Initialize V8.
V8::InitializeICU();
V8::InitializeExternalStartupData(argv[0]);
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);
Local<Context> context = Context::New(isolate,NULL,ObjectTemplate::New(isolate));
Context::Scope context_scope(context);
Local<ObjectTemplate> activity = ObjectTemplate::New(isolate);
// ################################################################################################################################
// ************************ This line breaks the NewInstance() call (5.0.71.33) works in (4.9.385.28) *****************************
// ################################################################################################################################
activity->Set (String::NewFromUtf8(isolate, "createddate", NewStringType::kNormal).ToLocalChecked(), Date::New(isolate,time(NULL)*1000.0));
Local<Object> activityInst = activity->NewInstance();
context->Global()->Set(String::NewFromUtf8(isolate, "activity", NewStringType::kNormal).ToLocalChecked(), activityInst);
return 0;
}
output:
#
# Fatal error in ../src/heap/heap.cc, line 3564
# Check failed: map->instance_type() == JS_REGEXP_TYPE || map->instance_type() == JS_OBJECT_TYPE || map->instance_type() == JS_ARRAY_TYPE.
#
==== C stack trace ===============================
1: V8_Fatal
2: v8::internal::Heap::CopyJSObject(v8::internal::JSObject*, v8::internal::AllocationSite*)
3: v8::internal::Factory::CopyJSObjectWithAllocationSite(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::AllocationSite>)
4: v8::internal::JSObjectWalkVisitor<v8::internal::DummyContextObject, (v8::internal::BoilerplateKind)1>::StructureWalk(v8::internal::Handle<v8::internal::JSObject>)
5: v8::internal::JSObjectWalkVisitor<v8::internal::DummyContextObject, (v8::internal::BoilerplateKind)1>::StructureWalk(v8::internal::Handle<v8::internal::JSObject>)
6: v8::internal::JSObject::DeepCopyApiBoilerplate(v8::internal::Handle<v8::internal::JSObject>)
7: v8::internal::(anonymous namespace)::InstantiateObject(v8::internal::Isolate*, v8::internal::Handle<v8::internal::ObjectTemplateInfo>, bool)
8: v8::internal::ApiNatives::InstantiateObject(v8::internal::Handle<v8::internal::ObjectTemplateInfo>)
9: v8::ObjectTemplate::NewInstance(v8::Local<v8::Context>)
10: main
11: start
12: 0x1
Illegal instruction
You cannot add javascript objects to ObjectTemplates. You can only add templates. This changed between the versions you mention.
The idea, I think, is that a template will be instantiated many times and associating the same object with each of them doesn't make sense. Whereas associated templates will also be re-instantiated giving different sub-objects for each parent object.
You're free to associate anything with the resulting object after NewInstance() is called, though.
I am currently working on ubuntu 12.04 . I have simple hello world program in c++ with v8 binding as follows :
#include "include/v8.h"
#include "include/libplatform/libplatform.h"
using namespace v8;
int main(int argc, char* argv[]) {
// Initialize V8.
V8::InitializeICU();
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
// Create a new Isolate and make it the current one.
//Isolate* isolate = Isolate::GetCurrent();
Isolate* isolate = Isolate::New();
{
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);
// 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, "'Hello' + ', World!'");
// Compile the source code.
Local<Script> script = Script::Compile(source);
// Run the script to get the result.
Local<Value> result = script->Run();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
return 0;
}
I just want
compilation( Local<Script> script = Script::Compile(source);)
and
execution(Local<Value> result = script->Run();)
in different functions. Is it possible? if yes , how? Can anybody help
I am trying to use v8 in my C++ application. I am stuck on the helloworld itself!
The helloworld at https://developers.google.com/v8/get_started works just fine. Now I am trying to catch exceptions/error in the code. So I used TryCatch trycatch;.
int main(int argc, char *argv[]) {
HandleScope handle_scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
TryCatch trycatch; /* TO CATCH EXCETIONS/ERRORS */
Handle<String> source = String::New("xyz();");
Handle<Script> script = Script::Compile(source);
Handle<Value> result = script->Run();
if (result.IsEmpty()) {
fprintf(stderr, "Exception: %s\n",
*String::AsciiValue(trycatch.Exception()));
return -1;
}
String::AsciiValue ascii(result);
printf("%s\n", *ascii);
context.Dispose();
return 0;
}
The exceptions are caught fine but the program does not terminate properly. It generates a segmentation fault. What am I doing wrong?
Turned out to be a foolish thing. I had long back installed libv8-dev and forgotten about it. And now I installed V8 from source. So I had two versions of V8 on my system. I uninstalled libv8-dev and the problem has been solved.