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
fileTestClass1.h
- whose class I'd like to expose to JavaScriptCMakeList.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:
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.