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 <anna@addaleax.net> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
1 parent 646e057 commit 44fdf95

File tree

6 files changed

+95
-3
lines changed

6 files changed

+95
-3
lines changed

src/node.h

+5-3
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
#define NODE_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+
struct fn##_ { \
834+
fn##_() { fn(); }; \
835+
} fn##_v_; \
836+
} \
835837
NODE_CTOR_PREFIX void __cdecl fn(void)
836838
#else
837839
#define NODE_C_CTOR(fn) \

src/node_api.h

+17
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,29 @@ typedef struct napi_module {
4444
#define NAPI_MODULE_VERSION 1
4545

4646
#if defined(_MSC_VER)
47+
#if defined(__cplusplus)
48+
#define NAPI_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
#define NAPI_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
#define NAPI_C_CTOR(fn) \
5572
static void fn(void) __attribute__((constructor)); \

test/js-native-api/common.h

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
#define DECLARE_NODE_API_GETTER(name, func) \
6363
{ (name), NULL, NULL, (func), NULL, NULL, napi_default, NULL }
6464

65+
#define DECLARE_NODE_API_PROPERTY_VALUE(name, value) \
66+
{ (name), NULL, NULL, NULL, NULL, (value), napi_default, NULL }
67+
6568
void add_returned_status(napi_env env,
6669
const char* key,
6770
napi_value object,
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+
}

test/node-api/test_init_order/test.js

+10
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+
const common = require('../../common');
6+
const test_init_order = require(`./build/${common.buildType}/test_init_order`);
7+
const assert = require('assert');
8+
9+
assert.strictEqual(test_init_order.cppIntValue, 42);
10+
assert.strictEqual(test_init_order.cppStringValue, '123');
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+
struct MyClass {
19+
static const std::unique_ptr<int> valueHolder;
20+
static const std::string testString;
21+
};
22+
23+
const std::unique_ptr<int> MyClass::valueHolder =
24+
std::unique_ptr<int>(new int(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)