Skip to content

Commit 44fdf95

Browse files
vmorozmhdawson
authored andcommitted
node-api,src: fix module registration in MSVC C++
PR-URL: #42459 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Gerhard Stöbich <[email protected]>
1 parent 646e057 commit 44fdf95

File tree

6 files changed

+95
-3
lines changed

6 files changed

+95
-3
lines changed

‎src/node.h‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -827,11 +827,13 @@ extern "C" NODE_EXTERN void node_module_register(void* mod);
827827
#endif
828828

829829
#if defined(_MSC_VER)
830-
#pragma section(".CRT$XCU", read)
831830
#defineNODE_C_CTOR(fn) \
832831
NODE_CTOR_PREFIX void __cdecl fn(void); \
833-
__declspec(dllexport, allocate(".CRT$XCU")) \
834-
void (__cdecl*fn ## _)(void) = fn; \
832+
namespace{\
833+
structfn##_{\
834+
fn##_(){fn()}; \
835+
} fn##_v_; \
836+
} \
835837
NODE_CTOR_PREFIX void __cdecl fn(void)
836838
#else
837839
#defineNODE_C_CTOR(fn) \

‎src/node_api.h‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,29 @@ typedef struct napi_module{
4444
#defineNAPI_MODULE_VERSION 1
4545

4646
#if defined(_MSC_VER)
47+
#if defined(__cplusplus)
48+
#defineNAPI_C_CTOR(fn) \
49+
static void __cdecl fn(void); \
50+
namespace{\
51+
struct fn##_{\
52+
fn##_(){fn()} \
53+
} fn##_v_; \
54+
} \
55+
static void __cdecl fn(void)
56+
#else// !defined(__cplusplus)
4757
#pragma section(".CRT$XCU", read)
58+
// The NAPI_C_CTOR macro defines a function fn that is called during CRT
59+
// initialization.
60+
// C does not support dynamic initialization of static variables and this code
61+
// simulates C++ behavior. Exporting the function pointer prevents it from being
62+
// optimized. See for details:
63+
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
4864
#defineNAPI_C_CTOR(fn) \
4965
static void __cdecl fn(void); \
5066
__declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \
5167
fn; \
5268
static void __cdecl fn(void)
69+
#endif// defined(__cplusplus)
5370
#else
5471
#defineNAPI_C_CTOR(fn) \
5572
static void fn(void) __attribute__((constructor)); \

‎test/js-native-api/common.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
#defineDECLARE_NODE_API_GETTER(name, func) \
6363
{(name), NULL, NULL, (func), NULL, NULL, napi_default, NULL }
6464

65+
#defineDECLARE_NODE_API_PROPERTY_VALUE(name, value) \
66+
{(name), NULL, NULL, NULL, NULL, (value), napi_default, NULL }
67+
6568
voidadd_returned_status(napi_envenv,
6669
constchar*key,
6770
napi_valueobject,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "test_init_order",
5+
"sources": [ "test_init_order.cc" ]
6+
}
7+
]
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict';
2+
3+
// This test verifies that C++ static variable dynamic initialization is called
4+
// correctly and does not interfere with the module initialization.
5+
constcommon=require('../../common');
6+
consttest_init_order=require(`./build/${common.buildType}/test_init_order`);
7+
constassert=require('assert');
8+
9+
assert.strictEqual(test_init_order.cppIntValue,42);
10+
assert.strictEqual(test_init_order.cppStringValue,'123');
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include<node_api.h>
2+
#include<memory>
3+
#include<string>
4+
#include"../../js-native-api/common.h"
5+
6+
// This test verifies that use of the NAPI_MODULE in C++ code does not
7+
// interfere with the C++ dynamic static initializers.
8+
9+
namespace{
10+
11+
// This class uses dynamic static initializers for the test.
12+
// In production code developers must avoid dynamic static initializers because
13+
// they affect the start up time. They must prefer static initialization such as
14+
// use of constexpr functions or classes with constexpr constructors. E.g.
15+
// instead of using std::string, it is preferrable to use const char[], or
16+
// constexpr std::string_view starting with C++17, or even constexpr
17+
// std::string starting with C++20.
18+
structMyClass{
19+
staticconst std::unique_ptr<int> valueHolder;
20+
staticconst std::string testString;
21+
};
22+
23+
const std::unique_ptr<int> MyClass::valueHolder =
24+
std::unique_ptr<int>(newint(42));
25+
// NOLINTNEXTLINE(runtime/string)
26+
const std::string MyClass::testString = std::string("123");
27+
28+
} // namespace
29+
30+
EXTERN_C_START
31+
napi_value Init(napi_env env, napi_value exports){
32+
napi_value cppIntValue, cppStringValue;
33+
NODE_API_CALL(env,
34+
napi_create_int32(env, *MyClass::valueHolder, &cppIntValue));
35+
NODE_API_CALL(
36+
env,
37+
napi_create_string_utf8(
38+
env, MyClass::testString.c_str(), NAPI_AUTO_LENGTH, &cppStringValue));
39+
40+
napi_property_descriptor descriptors[] ={
41+
DECLARE_NODE_API_PROPERTY_VALUE("cppIntValue", cppIntValue),
42+
DECLARE_NODE_API_PROPERTY_VALUE("cppStringValue", cppStringValue)};
43+
44+
NODE_API_CALL(env,
45+
napi_define_properties(
46+
env, exports, std::size(descriptors), descriptors));
47+
48+
return exports;
49+
}
50+
EXTERN_C_END
51+
52+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

0 commit comments

Comments
(0)