Skip to content

Commit

Permalink
Add initial support for V8 backend on macOS (#41)
Browse files Browse the repository at this point in the history
Fixes: #11

<img width="627" alt="Screenshot 2024-02-22 at 17 55 23"
src="https://github.com/crossnx/includejs/assets/42526976/750dff68-435d-4956-b785-bacb39b3f17f">

Signed-off-by: Darshan Sen <raisinten@gmail.com>
  • Loading branch information
RaisinTen authored Feb 22, 2024
1 parent 546f7b1 commit ff33ef2
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ jobs:
cxx: clang++
type: shared
backend: JavaScriptCore
# TODO(RaisinTen): Support other OSs and static configuration for
# the V8 backend.
- os: macos-latest
cc: clang
cxx: clang++
type: shared
backend: V8
- os: ubuntu-latest
cc: clang
cxx: clang++
Expand Down
1 change: 1 addition & 0 deletions Brewfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
brew "cmake"
brew "clang-format"
brew "doxygen"
brew "v8"
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ CTEST = ctest
# Options
PRESET = Debug
SHARED = OFF
BACKEND = JavaScriptCore

all: configure compile test

configure: .always
$(CMAKE) -S . -B ./build \
-DCMAKE_BUILD_TYPE:STRING=$(PRESET) \
-DCMAKE_COMPILE_WARNING_AS_ERROR:BOOL=ON \
-DINCLUDEJS_BACKEND:STRING=JavaScriptCore \
-DINCLUDEJS_BACKEND:STRING=$(BACKEND) \
-DINCLUDEJS_ENGINE:BOOL=ON \
-DINCLUDEJS_TESTS:BOOL=ON \
-DINCLUDEJS_DOCS:BOOL=ON \
Expand Down
22 changes: 22 additions & 0 deletions cmake/FindV8.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
if(NOT V8_FOUND)
if(APPLE)
add_library(v8 INTERFACE IMPORTED)
# Since the "includejs" library is compiled with the `-isysroot` flag of
# Clang, it is unable to find the V8 library and headers which have been
# downloaded from Homebrew.
# Refs: https://github.com/Homebrew/homebrew-core/issues/45061#issuecomment-541420664
set(V8_VERSION "12.1.285.24")
target_include_directories(v8
INTERFACE "/usr/local/Cellar/v8/${V8_VERSION}/include")
target_link_directories(v8
INTERFACE "/usr/local/Cellar/v8/${V8_VERSION}/lib")
target_link_libraries(v8 INTERFACE "-lv8")
target_link_libraries(v8 INTERFACE "-lv8_libplatform")
target_compile_definitions(v8
INTERFACE V8_COMPRESS_POINTERS)
target_compile_definitions(v8
INTERFACE V8_ENABLE_SANDBOX)
add_library(V8::V8 ALIAS v8)
set(V8_FOUND ON)
endif()
endif()
13 changes: 13 additions & 0 deletions src/engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ if(INCLUDEJS_BACKEND STREQUAL "JavaScriptCore")
list(APPEND INCLUDEJS_ENGINE_SOURCES javascript_core/engine_context.cc)
list(APPEND INCLUDEJS_ENGINE_SOURCES javascript_core/engine_promise.cc)
list(APPEND INCLUDEJS_ENGINE_SOURCES javascript_core/engine_value.cc)
elseif(INCLUDEJS_BACKEND STREQUAL "V8")
list(APPEND INCLUDEJS_ENGINE_SOURCES v8/engine.cc)
else()
message(FATAL_ERROR "Unknown IncludeJS backend: ${INCLUDEJS_BACKEND}")
endif()
Expand All @@ -24,6 +26,11 @@ if(INCLUDEJS_INSTALL)
"${PROJECT_SOURCE_DIR}/cmake/FindJavaScriptCore.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/includejs"
COMPONENT includejs_dev)
elseif(INCLUDEJS_BACKEND STREQUAL "V8")
install(FILES
"${PROJECT_SOURCE_DIR}/cmake/FindV8.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/includejs"
COMPONENT includejs_dev)
endif()
endif()

Expand All @@ -35,6 +42,12 @@ if(INCLUDEJS_BACKEND STREQUAL "JavaScriptCore")
PRIVATE JavaScriptCore::JavaScriptCore)
target_compile_definitions(includejs_engine
PUBLIC SOURCEMETA_INCLUDEJS_ENGINE_JAVASCRIPT_CORE)
elseif(INCLUDEJS_BACKEND STREQUAL "V8")
find_package(V8 REQUIRED)
target_link_libraries(includejs_engine
PRIVATE V8::V8)
target_compile_definitions(includejs_engine
PUBLIC SOURCEMETA_INCLUDEJS_ENGINE_V8)
else()
message(FATAL_ERROR "Unknown IncludeJS backend: ${INCLUDEJS_BACKEND}")
endif()
4 changes: 4 additions & 0 deletions src/engine/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <utility> // std::move
#include <vector> // std::vector

#if !defined(SOURCEMETA_INCLUDEJS_ENGINE_V8)

namespace sourcemeta {
namespace includejs {

Expand Down Expand Up @@ -85,3 +87,5 @@ auto Engine::on_error(const Value &exception) -> void {

} // namespace includejs
} // namespace sourcemeta

#endif
4 changes: 4 additions & 0 deletions src/engine/common/engine_context.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include <includejs/engine_context.h>

#if !defined(SOURCEMETA_INCLUDEJS_ENGINE_V8)

namespace sourcemeta {
namespace includejs {

Expand All @@ -14,3 +16,5 @@ auto Context::from(const std::string &value) const -> Value {

} // namespace includejs
} // namespace sourcemeta

#endif
3 changes: 3 additions & 0 deletions src/engine/include/includejs/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ class INCLUDEJS_ENGINE_EXPORT Engine {
auto evaluate(std::ifstream &stream, const std::filesystem::path &path)
-> Value;

// TODO(RaisinTen): Add support for bind_function() to the V8 backend.
#if !defined(SOURCEMETA_INCLUDEJS_ENGINE_V8)
auto bind_function(std::initializer_list<std::string> location,
Function function) -> void;
#endif
auto bind_global(std::initializer_list<std::string> location, Value value)
-> void;
auto bind_global(std::initializer_list<std::string> location, bool value)
Expand Down
95 changes: 95 additions & 0 deletions src/engine/v8/engine.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <includejs/engine.h>

#include <iostream>

#include <libplatform/libplatform.h>
#include <v8.h>

namespace sourcemeta {
namespace includejs {

struct Engine::Internal {};

Engine::Engine() : internal{std::make_unique<Engine::Internal>()} {
// TODO(RaisinTen): This code is here to make it easy to verify if the V8
// integration is working.

// This example code has been taken from:
// https://chromium.googlesource.com/v8/v8/+/branch-heads/11.9/samples/hello-world.cc

// Initialize V8.
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::NewFromUtf8Literal(isolate, "'Hello' + ', World!'");
// 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);
}
{
// Use the JavaScript API to generate a WebAssembly module.
//
// |bytes| contains the binary format for the following module:
//
// (func (export "add") (param i32 i32) (result i32)
// get_local 0
// get_local 1
// i32.add)
//
const char csource[] = R"(
let bytes = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01,
0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07,
0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01,
0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b
]);
let module = new WebAssembly.Module(bytes);
let instance = new WebAssembly.Instance(module);
instance.exports.add(3, 4);
)";
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source =
v8::String::NewFromUtf8Literal(isolate, csource);
// 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 a uint32 and print it.
uint32_t number = result->Uint32Value(context).ToChecked();
printf("3 + 4 = %u\n", number);
}
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
v8::V8::Dispose();
v8::V8::DisposePlatform();
delete create_params.array_buffer_allocator;
}

Engine::~Engine() {}

} // namespace includejs
} // namespace sourcemeta
5 changes: 5 additions & 0 deletions test/engine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
if(INCLUDEJS_BACKEND STREQUAL "V8")
# TODO(RaisinTen): Start testing the V8 backend.
return()
endif()

add_executable(includejs_engine_unit
engine_binding_test.cc
engine_evaluate_test.cc
Expand Down
3 changes: 3 additions & 0 deletions test/packaging/find_package/hello.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

auto main() -> int {
sourcemeta::includejs::Engine engine;
// TODO(RaisinTen): Remove this once V8 supports this.
#if !defined(SOURCEMETA_INCLUDEJS_ENGINE_V8)
sourcemeta::includejs::Value result{engine.evaluate("1 + 3", "index.js")};
assert(result.is_number());
std::cout << result.to_number() << "\n";
#endif
return EXIT_SUCCESS;
}

0 comments on commit ff33ef2

Please sign in to comment.