Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
cc2417f
bump minimum supported V8 is 9.0+ (vcpkg version)
MangelSpec Feb 12, 2026
db7572d
fix script-reachable ToLocalChecked crashes
MangelSpec Feb 12, 2026
941c92d
add try_from_v8<T> exception-free conversion returning std::optional
MangelSpec Feb 12, 2026
7169dcc
fix V8PP_PRETTIFY_TYPENAMES config propagation
MangelSpec Feb 12, 2026
5521849
Add crash safety tests: from_v8 with invalid inputs, json
MangelSpec Feb 12, 2026
7d120d2
Add crash safety design fixes: unwrap_object depth limit, magic numbe…
MangelSpec Feb 12, 2026
1c7af9d
deduplicate object.hpp subobject traversal, replace SFINAE with C++20…
MangelSpec Feb 12, 2026
3a04d9d
add V8 performance hints: SideEffectType, internalized strings, NewFr…
MangelSpec Feb 12, 2026
e8ebabc
add convert extensions: BigInt, span, ArrayBuffer, set, pair, path, c…
MangelSpec Feb 12, 2026
929275b
add binding features: default parameters, function overloading, V8 Fa…
MangelSpec Feb 12, 2026
2211a97
add symbol protocol support, iterator protocol, and promise wrapper
MangelSpec Feb 12, 2026
61a92d4
add context_store for cross-context value persistence
MangelSpec Feb 12, 2026
664514d
add test suite expansion and fix constructor-without-new crash
MangelSpec Feb 12, 2026
a62e3d4
modernize CI workflow and improve reliability
MangelSpec Feb 12, 2026
7a8fd52
update README to reflect fork state and new features
MangelSpec Feb 12, 2026
b9a4845
improve CMake modernization and cleanup
MangelSpec Feb 12, 2026
4dd9ce0
add CLAUDE.md project guide
MangelSpec Feb 12, 2026
a67f55d
fix V8 12.9+ API compatibility for FastApiCallbackOptions and SetNati…
MangelSpec Feb 12, 2026
6c7993b
convert int64/uint64 to Number instead of BigInt for JS arithmetic co…
MangelSpec Feb 12, 2026
3bbf528
fix CI build failures across Ubuntu, Windows, and macOS
MangelSpec Feb 12, 2026
d83dcfe
fix CI failures: chrono precision, test cleanup, and compiler warnings
MangelSpec Feb 12, 2026
edc6336
add debug logging to test main and revert context-wrapping regression
MangelSpec Feb 12, 2026
39a34e2
fix alive_contexts thread safety and add Windows DLL diagnostics
MangelSpec Feb 12, 2026
a739cfa
fix NuGet cache missing V8 redist DLLs on Windows
MangelSpec Feb 12, 2026
cbcee36
add BUILD_BENCHMARKS option
MangelSpec Feb 12, 2026
e53d451
clean up debug printings from before
MangelSpec Feb 12, 2026
4afae20
add CUSTOM_V8_LIB_PATH support for custom V8 builds
MangelSpec Feb 12, 2026
fbee282
remove reference to none existing change analysis in README
MangelSpec Feb 12, 2026
4fb9b92
bump version to 3.0.0
MangelSpec Feb 12, 2026
e0596e0
fix: use proper UTF-8 conversion for filesystem::path in v8pp on Windows
MangelSpec Feb 12, 2026
548f85f
fix: define _AMD64_ before including stringapiset.h to fix Windows CI…
MangelSpec Feb 12, 2026
363bbed
fix: add _AMD64_ and NOMINMAX defines for stringapiset.h on Windows
MangelSpec Feb 12, 2026
4fe7a97
Merge branch 'master' into dev
MangelSpec Feb 12, 2026
f0d8ccd
include windows.h to setup all posibilities...
MangelSpec Feb 12, 2026
7fd12e3
add ctor extensions and fast property support
MangelSpec Feb 13, 2026
7af9c80
Add change detection job with dorny/paths-filter
MangelSpec Feb 13, 2026
690fda3
treat explicit undefined as "use the default" to match JS native beha…
MangelSpec Feb 13, 2026
38a0269
apply clang-format to all source files
MangelSpec Feb 13, 2026
85cb075
use clang-format-19
MangelSpec Feb 13, 2026
a51f028
fix fast_fn property inheritance by using PrototypeTemplate
MangelSpec Feb 13, 2026
2737efe
add mixed fast getter + slow setter property overload
MangelSpec Feb 13, 2026
f261849
extract Win32 UTF-8 string conversion out of convert.hpp
MangelSpec Feb 18, 2026
35fd76b
run clang-format
MangelSpec Feb 18, 2026
654220c
Merge branch 'master' into dev
MangelSpec Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions test/test_fast_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ void test_fast_api()
run_script<int>(context, "p.fast_xy = 77; p.x"), 77);
}

// --- Class property with fast getter + slow setter ---
{
struct Gauge
{
int32_t val = 0;
int32_t get_val() const { return val; }
void set_val(v8::Isolate*, int32_t v) { val = v; }
};

v8pp::class_<Gauge> gauge_class(isolate);
gauge_class
.ctor<>()
.property("mixed", v8pp::fast_fn<&Gauge::get_val>, &Gauge::set_val);
context.class_("Gauge", gauge_class);

check_eq("fast_api: mixed property get",
run_script<int>(context, "var g = new Gauge(); g.mixed"), 0);

check_eq("fast_api: mixed property set+get",
run_script<int>(context, "g.mixed = 42; g.mixed"), 42);
}

// --- Module property with fast API ---
{
static int32_t mod_value = 0;
Expand Down Expand Up @@ -188,4 +210,24 @@ void test_fast_api()
check_eq("fast_api: module read-only property is readonly",
run_script<int>(context, "fmod.fast_const = 999; fmod.fast_const"), 123);
}

// --- Module property with fast getter + slow setter ---
{
static int32_t mixed_val = 0;

struct MixedMod
{
static int32_t get_val() { return mixed_val; }
static void set_val(int32_t v) { mixed_val = v; }
};

v8pp::module m(isolate);
m.property("mixed", v8pp::fast_fn<&MixedMod::get_val>, &MixedMod::set_val);
context.module("mmod", m);

mixed_val = 0;
check_eq("fast_api: module mixed property set+get",
run_script<int>(context, "mmod.mixed = 33; mmod.mixed"), 33);
check_eq("fast_api: module mixed property updated C++ side", mixed_val, 33);
}
}
3 changes: 3 additions & 0 deletions v8pp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ set(V8PP_HEADERS
promise.hpp
property.hpp
ptr_traits.hpp
string_utils.hpp
throw_ex.hpp
type_info.hpp
utility.hpp
Expand All @@ -71,6 +72,7 @@ if(V8PP_HEADER_ONLY)
class.ipp
context_store.ipp
json.ipp
string_utils.ipp
throw_ex.ipp
version.ipp
)
Expand All @@ -81,6 +83,7 @@ else()
context.cpp
context_store.cpp
convert.cpp
string_utils.cpp
json.cpp
throw_ex.cpp
version.cpp
Expand Down
28 changes: 28 additions & 0 deletions v8pp/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,34 @@ class class_
return *this;
}

/// Set read/write class property with V8 Fast API getter and slow setter
template<auto GetPtr, typename SetFunction>
requires(!is_fast_function<std::decay_t<SetFunction>>::value)
class_& property(std::string_view name, fast_function<GetPtr>, SetFunction&& set)
{
using Setter = std::decay_t<SetFunction>;
static_assert(std::is_member_function_pointer_v<SetFunction> || detail::is_callable<Setter>::value, "SetFunction must be callable");

v8::HandleScope scope(isolate());

v8::Local<v8::Name> v8_name = v8pp::to_v8_name(isolate(), name);
auto getter_template = wrap_function_template<GetPtr, Traits>(
isolate(), fast_function<GetPtr>{},
v8::SideEffectType::kHasNoSideEffect);
auto setter_template = v8::FunctionTemplate::New(isolate(),
&detail::forward_function<Traits, Setter>,
detail::external_data::set(isolate(), Setter(std::forward<SetFunction>(set))),
v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasSideEffectToReceiver);

class_info_.js_function_template()->PrototypeTemplate()->SetAccessorProperty(
v8_name, getter_template, setter_template,
v8::PropertyAttribute(v8::DontDelete));

return *this;
}

/// Set read/write class property with V8 Fast API getter and setter
template<auto GetPtr, auto SetPtr>
class_& property(std::string_view name, fast_function<GetPtr>, fast_function<SetPtr>)
Expand Down
20 changes: 5 additions & 15 deletions v8pp/convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <optional>

#include "v8pp/ptr_traits.hpp"
#include "v8pp/string_utils.hpp"
#include "v8pp/utility.hpp"

namespace v8pp {
Expand Down Expand Up @@ -1093,10 +1094,7 @@ struct convert<std::filesystem::path, void>
{
std::string str = convert<std::string>::from_v8(isolate, value);
#ifdef WIN32
int sz = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0);
std::wstring w(sz, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), w.data(), sz);
return std::filesystem::path(w);
return std::filesystem::path(utf8_to_wide(str));
#else
return std::filesystem::path(str);
#endif
Expand All @@ -1106,14 +1104,10 @@ struct convert<std::filesystem::path, void>
{
if (auto str = convert<std::string>::try_from_v8(isolate, value))
{
std::string s = *std::move(str);
#ifdef WIN32
int sz = MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), nullptr, 0);
std::wstring w(sz, 0);
MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), w.data(), sz);
return std::filesystem::path(w);
return std::filesystem::path(utf8_to_wide(*std::move(str)));
#else
return std::filesystem::path(s);
return std::filesystem::path(*std::move(str));
#endif
}
return std::nullopt;
Expand All @@ -1122,11 +1116,7 @@ struct convert<std::filesystem::path, void>
static to_type to_v8(v8::Isolate* isolate, from_type const& value)
{
#ifdef WIN32
std::wstring w = value.generic_wstring();
int sz = WideCharToMultiByte(CP_UTF8, 0, w.data(), static_cast<int>(w.size()), nullptr, 0, nullptr, nullptr);
std::string utf8(sz, 0);
WideCharToMultiByte(CP_UTF8, 0, w.data(), static_cast<int>(w.size()), utf8.data(), sz, nullptr, nullptr);
return convert<std::string>::to_v8(isolate, utf8);
return convert<std::string>::to_v8(isolate, wide_to_utf8(value.generic_wstring()));
#else
return convert<std::string>::to_v8(isolate, value.string());
#endif
Expand Down
27 changes: 27 additions & 0 deletions v8pp/module.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,33 @@ class module
return *this;
}

/// Set read/write module property with V8 Fast API getter and slow setter
template<auto GetPtr, typename SetFunction>
requires(!is_fast_function<std::decay_t<SetFunction>>::value)
module& property(char const* name, fast_function<GetPtr>, SetFunction&& set)
{
using Setter = std::decay_t<SetFunction>;
static_assert(detail::is_callable<Setter>::value, "SetFunction must be callable");

v8::HandleScope scope(isolate_);

v8::Local<v8::Name> v8_name = v8pp::to_v8_name(isolate_, name);
auto getter_template = wrap_function_template<GetPtr>(
isolate_, fast_function<GetPtr>{},
v8::SideEffectType::kHasNoSideEffect);
auto setter_template = v8::FunctionTemplate::New(isolate_,
&detail::forward_function<raw_ptr_traits, Setter>,
detail::external_data::set(isolate_, Setter(std::forward<SetFunction>(set))),
v8::Local<v8::Signature>(), 0,
v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasSideEffectToReceiver);

obj_->SetAccessorProperty(v8_name, getter_template, setter_template,
v8::PropertyAttribute(v8::DontDelete));

return *this;
}

/// Set read/write module property with V8 Fast API getter and setter
template<auto GetPtr, auto SetPtr>
module& property(char const* name, fast_function<GetPtr>, fast_function<SetPtr>)
Expand Down
5 changes: 5 additions & 0 deletions v8pp/string_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "v8pp/config.hpp"

#if !V8PP_HEADER_ONLY
#include "v8pp/string_utils.ipp"
#endif
19 changes: 19 additions & 0 deletions v8pp/string_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <string>
#include <string_view>

#include "v8pp/config.hpp"

namespace v8pp {

#ifdef WIN32
std::wstring utf8_to_wide(std::string_view utf8);
std::string wide_to_utf8(std::wstring_view wide);
#endif

} // namespace v8pp

#if V8PP_HEADER_ONLY
#include "v8pp/string_utils.ipp"
#endif
33 changes: 33 additions & 0 deletions v8pp/string_utils.ipp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "v8pp/string_utils.hpp"

#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif

namespace v8pp {

#ifdef WIN32
V8PP_IMPL std::wstring utf8_to_wide(std::string_view utf8)
{
int sz = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), nullptr, 0);
std::wstring w(sz, 0);
MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast<int>(utf8.size()), w.data(), sz);
return w;
}

V8PP_IMPL std::string wide_to_utf8(std::wstring_view wide)
{
int sz = WideCharToMultiByte(CP_UTF8, 0, wide.data(), static_cast<int>(wide.size()), nullptr, 0, nullptr, nullptr);
std::string utf8(sz, 0);
WideCharToMultiByte(CP_UTF8, 0, wide.data(), static_cast<int>(wide.size()), utf8.data(), sz, nullptr, nullptr);
return utf8;
}
#endif

} // namespace v8pp