Commit ad4b9dcb9b for freeswitch.com
commit ad4b9dcb9bac1270fa60f55149c79c0152a994ba
Author: Andrey Volk <andywolk@gmail.com>
Date: Wed Oct 28 14:48:33 2026 +0300
[mod_v8, Build-System] Port to the V8 13.6 API of Node.js 24 libnode. Windows: Upgrade libnode to v24.22.1_1 and take it from windows-deps (#3249)
diff --git a/src/mod/languages/mod_v8/Makefile.am b/src/mod/languages/mod_v8/Makefile.am
index bb14928ae9..743d073448 100644
--- a/src/mod/languages/mod_v8/Makefile.am
+++ b/src/mod/languages/mod_v8/Makefile.am
@@ -5,7 +5,7 @@ AUTOMAKE_OPTIONS += foreign
MODNAME=mod_v8
AM_CFLAGS += -I. -I./include -I$(switch_srcdir)/src/mod/languages/mod_v8/include $(V8_CFLAGS)
-AM_CPPFLAGS += -I. -I./include -I$(switch_srcdir)/src/mod/languages/mod_v8/include $(V8_CFLAGS) -std=c++17
+AM_CPPFLAGS += -I. -I./include -I$(switch_srcdir)/src/mod/languages/mod_v8/include $(V8_CFLAGS) -std=c++20
AM_LDFLAGS += -avoid-version -module -no-undefined -shared
AM_LIBADD = $(switch_builddir)/libfreeswitch.la
diff --git a/src/mod/languages/mod_v8/include/javascript.hpp b/src/mod/languages/mod_v8/include/javascript.hpp
index 35d30ea570..769e2f7e68 100644
--- a/src/mod/languages/mod_v8/include/javascript.hpp
+++ b/src/mod/languages/mod_v8/include/javascript.hpp
@@ -205,7 +205,7 @@ static inline v8::Local<v8::StackFrame> js_stack_frame(v8::Local<v8::StackTrace>
/* Macro for easy V8 "get property" callback definition */
#define JS_GET_PROPERTY_DEF(method_name, class_name) \
- static void method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)\
+ static void method_name(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info)\
{\
JS_CHECK_SCRIPT_STATE();\
class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\
@@ -220,11 +220,11 @@ static inline v8::Local<v8::StackFrame> js_stack_frame(v8::Local<v8::StackTrace>
info.GetReturnValue().Set(false);\
}\
}\
- void method_name##Impl(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
+ void method_name##Impl(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info)
/* Macro for easy V8 "set property" callback definition */
#define JS_SET_PROPERTY_DEF(method_name, class_name) \
- static void method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)\
+ static void method_name(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)\
{\
JS_CHECK_SCRIPT_STATE();\
class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\
@@ -239,20 +239,20 @@ static inline v8::Local<v8::StackFrame> js_stack_frame(v8::Local<v8::StackTrace>
/* A property setter's ReturnValue is void; V8 7+ rejects Set() on it. */\
}\
}\
- void method_name##Impl(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
+ void method_name##Impl(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
/* Macro for easy V8 "function" callback definition */
#define JS_FUNCTION_DEF(method_name, class_name) \
static void method_name(const v8::FunctionCallbackInfo<v8::Value>& info)\
{\
JS_CHECK_SCRIPT_STATE();\
- class_name *obj = JSBase::GetInstance<class_name>(info.Holder());\
+ class_name *obj = JSBase::GetInstance<class_name>(info.This());\
if (obj) {\
obj->method_name##Impl(info);\
} else {\
int line;\
char *file = JSMain::GetStackInfo(info.GetIsolate(), &line);\
- JsUtf8Value str(info.Holder());\
+ JsUtf8Value str(info.This());\
switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, "mod_v8", line, NULL, SWITCH_LOG_DEBUG, "No valid internal data available for %s when calling %s\n", *str ? *str : "[unknown]", #class_name "::" #method_name "()");\
free(file);\
info.GetReturnValue().Set(false);\
@@ -261,18 +261,18 @@ static inline v8::Local<v8::StackFrame> js_stack_frame(v8::Local<v8::StackTrace>
void method_name##Impl(const v8::FunctionCallbackInfo<v8::Value>& info)
/* Macros for V8 callback implementations */
-#define JS_GET_PROPERTY_IMPL(method_name, class_name) void class_name::method_name##Impl(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
-#define JS_SET_PROPERTY_IMPL(method_name, class_name) void class_name::method_name##Impl(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
+#define JS_GET_PROPERTY_IMPL(method_name, class_name) void class_name::method_name##Impl(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info)
+#define JS_SET_PROPERTY_IMPL(method_name, class_name) void class_name::method_name##Impl(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
#define JS_FUNCTION_IMPL(method_name, class_name) void class_name::method_name##Impl(const v8::FunctionCallbackInfo<v8::Value>& info)
/* Macros for V8 callback definitions (class static version) */
-#define JS_GET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
-#define JS_SET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
+#define JS_GET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info)
+#define JS_SET_PROPERTY_DEF_STATIC(method_name) static void method_name(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
#define JS_FUNCTION_DEF_STATIC(method_name) static void method_name(const v8::FunctionCallbackInfo<v8::Value>& info)
/* Macros for V8 callback implementations (class static version) */
-#define JS_GET_PROPERTY_IMPL_STATIC(method_name, class_name) void class_name::method_name(v8::Local<v8::String> property, const v8::PropertyCallbackInfo<v8::Value>& info)
-#define JS_SET_PROPERTY_IMPL_STATIC(method_name, class_name) void class_name::method_name(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
+#define JS_GET_PROPERTY_IMPL_STATIC(method_name, class_name) void class_name::method_name(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info)
+#define JS_SET_PROPERTY_IMPL_STATIC(method_name, class_name) void class_name::method_name(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
#define JS_FUNCTION_IMPL_STATIC(method_name, class_name) void class_name::method_name(const v8::FunctionCallbackInfo<v8::Value>& info)
/* Macro for basic script state check (to know if the script is being terminated), should be called before calling any callback actual code */
@@ -321,8 +321,8 @@ typedef struct {
/* JS Property definition */
typedef struct {
const char *name; /* Name of the property */
- v8::AccessorGetterCallback get; /* The property getter */
- v8::AccessorSetterCallback set; /* The property setter */
+ v8::AccessorNameGetterCallback get; /* The property getter */
+ v8::AccessorNameSetterCallback set; /* The property setter */
} js_property_t;
/* JS Class definition */
@@ -395,13 +395,18 @@ public:
template <typename T> static T *GetInstance(const v8::FunctionCallbackInfo<v8::Value>& info)
{
v8::HandleScope scope(info.GetIsolate());
- return GetInstance<T>(info.Holder());
+ return GetInstance<T>(info.This());
}
/* Get a JSBase instance from a JavaScript object */
template <typename T> static T *GetInstance(const v8::Local<v8::Object>& self)
{
- v8::Local<v8::Value> val = self->GetInternalField(0);
+ /* Scripts can hand any object in here (as a receiver or an argument); only ours carry field 0 */
+ if (self.IsEmpty() || self->InternalFieldCount() < 1) {
+ return NULL;
+ }
+
+ v8::Local<v8::Value> val = self->GetInternalField(0).As<v8::Value>();
if (!val.IsEmpty() && val->IsExternal()) {
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(val);
@@ -416,7 +421,7 @@ public:
static v8::Local<v8::Function> GetFunctionFromArg(v8::Isolate *isolate, const v8::Local<v8::Value>& arg);
/* Default JS setter callback, to be used for read only values */
- static void DefaultSetProperty(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info);
+ static void DefaultSetProperty(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info);
/* Get the name of the JavaScript class - must be overridden by the actual implementation */
virtual std::string GetJSClassName() = 0;
diff --git a/src/mod/languages/mod_v8/src/fsevent.cpp b/src/mod/languages/mod_v8/src/fsevent.cpp
index 745e238913..09640992e3 100644
--- a/src/mod/languages/mod_v8/src/fsevent.cpp
+++ b/src/mod/languages/mod_v8/src/fsevent.cpp
@@ -348,7 +348,7 @@ JS_EVENT_FUNCTION_IMPL_STATIC(Fire)
{
JS_CHECK_SCRIPT_STATE();
HandleScope handle_scope(info.GetIsolate());
- FSEvent *obj = JSBase::GetInstance<FSEvent>(info.Holder());
+ FSEvent *obj = JSBase::GetInstance<FSEvent>(info.This());
if (obj && obj->_event) {
switch_event_fire(&obj->_event);
@@ -393,7 +393,7 @@ JS_EVENT_FUNCTION_IMPL_STATIC(Destroy)
{
JS_CHECK_SCRIPT_STATE();
HandleScope handle_scope(info.GetIsolate());
- FSEvent *obj = JSBase::GetInstance<FSEvent>(info.Holder());
+ FSEvent *obj = JSBase::GetInstance<FSEvent>(info.This());
if (obj) {
delete obj;
diff --git a/src/mod/languages/mod_v8/src/fseventhandler.cpp b/src/mod/languages/mod_v8/src/fseventhandler.cpp
index 2fa40bba8f..b7a0777ffd 100644
--- a/src/mod/languages/mod_v8/src/fseventhandler.cpp
+++ b/src/mod/languages/mod_v8/src/fseventhandler.cpp
@@ -572,7 +572,7 @@ JS_EVENTHANDLER_FUNCTION_IMPL_STATIC(Destroy)
{
JS_CHECK_SCRIPT_STATE();
- FSEventHandler *obj = JSBase::GetInstance<FSEventHandler>(info.Holder());
+ FSEventHandler *obj = JSBase::GetInstance<FSEventHandler>(info.This());
if (obj) {
delete obj;
diff --git a/src/mod/languages/mod_v8/src/fssession.cpp b/src/mod/languages/mod_v8/src/fssession.cpp
index c055cc3854..f478975507 100644
--- a/src/mod/languages/mod_v8/src/fssession.cpp
+++ b/src/mod/languages/mod_v8/src/fssession.cpp
@@ -1836,7 +1836,7 @@ JS_SESSION_FUNCTION_IMPL(Bridge)
cb_state.jss_a = this;
cb_state.jss_b = jss_b;
- cb_state.session_obj_a.Reset(info.GetIsolate(), info.Holder());
+ cb_state.session_obj_a.Reset(info.GetIsolate(), info.This());
cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b);
cb_state.session_state = this;
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
diff --git a/src/mod/languages/mod_v8/src/fsxml.cpp b/src/mod/languages/mod_v8/src/fsxml.cpp
index 93de0547da..76a379bf15 100644
--- a/src/mod/languages/mod_v8/src/fsxml.cpp
+++ b/src/mod/languages/mod_v8/src/fsxml.cpp
@@ -129,7 +129,7 @@ Local<Value> FSXML::GetJSObjFromXMLObj(const switch_xml_t xml, const v8::Functio
/* Create a new object if it wasn't found in the hash */
if ((newObj = new FSXML(info))) {
if (_rootJSObject.IsEmpty()) {
- newObj->_rootJSObject.Reset(info.GetIsolate(), info.Holder()); /* The caller is the owner */
+ newObj->_rootJSObject.Reset(info.GetIsolate(), info.This()); /* The caller is the owner */
} else {
newObj->_rootJSObject.Reset(info.GetIsolate(), _rootJSObject); /* The owner is stored in the persistent object */
}
@@ -354,7 +354,7 @@ JS_XML_IMPL_STATIC(Remove)
}
delete obj;
} else {
- JsUtf8Value str(info.Holder());
+ JsUtf8Value str(info.This());
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No valid internal data available for %s when calling FSXML::Remove()\n", *str ? *str : "[unknown]");
}
}
diff --git a/src/mod/languages/mod_v8/src/jsbase.cpp b/src/mod/languages/mod_v8/src/jsbase.cpp
index 85df1715b7..f4ae4e3451 100644
--- a/src/mod/languages/mod_v8/src/jsbase.cpp
+++ b/src/mod/languages/mod_v8/src/jsbase.cpp
@@ -217,7 +217,7 @@ void JSBase::Register(Isolate *isolate, const js_class_definition_t *desc)
// Add properties to the object
for (int i = 0;; i++) {
if (!desc->properties[i].get) break;
- function->InstanceTemplate()->SetAccessor(js_new_string(isolate, desc->properties[i].name), desc->properties[i].get, desc->properties[i].set);
+ function->InstanceTemplate()->SetNativeDataProperty(js_new_string(isolate, desc->properties[i].name), desc->properties[i].get, desc->properties[i].set);
}
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
@@ -287,7 +287,7 @@ Local<Function> JSBase::GetFunctionFromArg(Isolate *isolate, const Local<Value>&
}
}
-void JSBase::DefaultSetProperty(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
+void JSBase::DefaultSetProperty(v8::Local<v8::Name> property, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info)
{
info.GetIsolate()->ThrowException(js_new_string(info.GetIsolate(), "this property cannot be changed!"));
}
diff --git a/src/mod/languages/mod_v8/src/jsmain.cpp b/src/mod/languages/mod_v8/src/jsmain.cpp
index 93662f68eb..fc4a8bf5cf 100644
--- a/src/mod/languages/mod_v8/src/jsmain.cpp
+++ b/src/mod/languages/mod_v8/src/jsmain.cpp
@@ -173,7 +173,9 @@ JSMain::~JSMain(void)
if (forcedTerminationMessage) free(forcedTerminationMessage);
if (forcedTerminationScriptFile) free(forcedTerminationScriptFile);
- isolate->Dispose();
+ if (isolate) {
+ isolate->Dispose();
+ }
}
const string JSMain::GetExceptionInfo(Isolate* isolate, TryCatch* try_catch)
@@ -471,7 +473,6 @@ void JSMain::Dispose()
// Make sure to cleanup properly!
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
v8::Isolate::GetCurrent()->LowMemoryNotification();
- while (!v8::Isolate::GetCurrent()->IdleNotificationDeadline(0.500)) {}
V8::Dispose();
#if V8_MAJOR_VERSION >= 10
/* ShutdownPlatform() became a deprecated alias for DisposePlatform() in
diff --git a/w32/libnode-version.props b/w32/libnode-version.props
index 1204ab2b59..2a7565207a 100644
--- a/w32/libnode-version.props
+++ b/w32/libnode-version.props
@@ -4,7 +4,8 @@
<Import Project="basedir.props" Condition=" '$(BaseDirImported)' == ''"/>
</ImportGroup>
<PropertyGroup Label="UserMacros">
- <LibNodeVersion>20.19.2</LibNodeVersion>
+ <LibNodeVersion>24.22.1</LibNodeVersion>
+ <LibNodeBuildNumber>1</LibNodeBuildNumber>
</PropertyGroup>
<PropertyGroup>
<LibNodeVersionImported>true</LibNodeVersionImported>
diff --git a/w32/libnode.props b/w32/libnode.props
index 35971bace2..afe68a0fbe 100644
--- a/w32/libnode.props
+++ b/w32/libnode.props
@@ -6,21 +6,21 @@
</ImportGroup>
<PropertyGroup Label="UserMacros">
- <LibNodeDir>$(BaseDir)libs\libnode-$(LibNodeVersion)</LibNodeDir>
+ <LibNodeDir>$(BaseDir)libs\libnode-$(LibNodeVersion)_$(LibNodeBuildNumber)</LibNodeDir>
</PropertyGroup>
- <!--
+ <!--
Download Target.
- Name must be unique.
+ Name must be unique.
By design, targets are executed only once per project.
-
+
Usage:
-
+
package: URI
expectfileordirectory: Skips the download and extraction if exists
- outputfolder: Folder to store a downloaded file.
+ outputfolder: Folder to store a downloaded file.
By default "$(BaseDir)libs", if empty
outputfilename: If not empty, overrides filename from URI.
@@ -31,7 +31,7 @@
<Target Name="libnodeBinariesDownloadTarget" BeforeTargets="CustomBuild" DependsOnTargets="7za">
<DownloadPackageTask
- package="https://github.com/freeswitch/libnode-packaging/releases/download/libnode-v$(LibNodeVersion)/libnode-$(LibNodeVersion)-binaries-$(Platform.ToLower())-$(Configuration.ToLower()).zip"
+ package="https://github.com/freeswitch/windows-deps/releases/download/libnode-v$(LibNodeVersion)_$(LibNodeBuildNumber)/libnode-$(LibNodeVersion)_$(LibNodeBuildNumber)-binaries-$(Platform.ToLower())-$(Configuration.ToLower()).zip"
expectfileordirectory="$(LibNodeDir)\binaries\$(Platform)\$(Configuration)\libnode.dll"
outputfolder=""
outputfilename=""
@@ -40,7 +40,7 @@
</Target>
<Target Name="libnodeHeadersDownloadTarget" BeforeTargets="CustomBuild" DependsOnTargets="7za">
<DownloadPackageTask
- package="https://github.com/freeswitch/libnode-packaging/releases/download/libnode-v$(LibNodeVersion)/libnode-$(LibNodeVersion)-headers.zip"
+ package="https://github.com/freeswitch/windows-deps/releases/download/libnode-v$(LibNodeVersion)_$(LibNodeBuildNumber)/libnode-$(LibNodeVersion)_$(LibNodeBuildNumber)-headers.zip"
expectfileordirectory="$(LibNodeDir)\include\v8.h"
outputfolder=""
outputfilename=""
@@ -48,12 +48,15 @@
/>
</Target>
+ <!-- Copied whenever it differs, not only when absent: a libnode.dll of another
+ version left in the output folder would not match the libnode.lib mod_v8
+ links against. -->
<Target Name="libnodeCopyTarget" BeforeTargets="CustomBuild" DependsOnTargets="libnodeBinariesDownloadTarget">
<Message Text="Copying libnode.dll to the freeswitch output folder." Importance="High" />
<ItemGroup>
<libnodeFiles Include="$(LibNodeDir)\binaries\$(Platform)\$(Configuration)\*.dll"/>
</ItemGroup>
- <Copy Condition="!exists('$(BaseDir)\$(Platform)\$(Configuration)\libnode.dll')"
+ <Copy SkipUnchangedFiles="true"
SourceFiles="@(libnodeFiles)"
DestinationFiles="@(libnodeFiles->'$(BaseDir)\$(Platform)\$(Configuration)\%(Filename)%(Extension)')"
/>
@@ -68,11 +71,11 @@
`#if __cplusplus <= 201703L` fires even with /std:c++20. Set both here. -->
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>
- <AdditionalIncludeDirectories>$(SolutionDir)libs\libnode-$(LibNodeVersion)\include;$(SolutionDir)src\mod\languages\mod_v8;$(SolutionDir)src\mod\languages\mod_v8\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(LibNodeDir)\include;$(SolutionDir)src\mod\languages\mod_v8;$(SolutionDir)src\mod\languages\mod_v8\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>USING_V8_SHARED;USING_V8_PLATFORM_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
- <AdditionalLibraryDirectories>$(SolutionDir)libs\libnode-$(LibNodeVersion)\binaries\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalLibraryDirectories>$(LibNodeDir)\binaries\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>libnode.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>