From 9da3c377f32d64df345c8c4e28bba2f0c30ba266 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 21 Dec 2025 21:23:38 +0100 Subject: [PATCH] build,test: add tests for binary linked with shared libnode This adds tests to ensure the V8 parts (v8, libplatform, cppgc) in shared libnode works correctly. --- node.gyp | 55 ++++++++++++++++++++ test/common/index.js | 11 ++++ test/embedding/shared_embedtest.cc | 60 ++++++++++++++++++++++ test/embedding/test-embedding.js | 10 +--- test/embedding/test-shared-embedding-v8.js | 25 +++++++++ 5 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 test/embedding/shared_embedtest.cc create mode 100644 test/embedding/test-shared-embedding-v8.js diff --git a/node.gyp b/node.gyp index 6dfe2257b80e54..e67d463bfc6098 100644 --- a/node.gyp +++ b/node.gyp @@ -1400,6 +1400,61 @@ ], }, # embedtest + { + 'target_name': 'shared_embedtest', + 'type': 'executable', + + 'dependencies': [ + '<(node_lib_target_name)', + ], + + # Don't depend on node.gypi - it otherwise links to + # the static libraries and resolve symbols at build time. + 'include_dirs': [ + 'deps/v8/include', + ], + + 'sources': [ + 'test/embedding/shared_embedtest.cc', + ], + 'conditions': [ + [ 'node_shared=="true"', { + 'defines': [ + 'USING_V8_SHARED', + 'USING_V8_PLATFORM_SHARED', + ], + 'defines!': [ + 'BUILDING_V8_PLATFORM_SHARED=1', + 'BUILDING_V8_SHARED=1', + ], + }, { + # Only test shared embedding when Node is built as shared library. + 'type': 'none', + }], + # Only test platforms known to work. + ['OS not in "mac win linux"', { + 'type': 'none', + }], + ['OS=="win"', { + 'libraries': [ + 'Dbghelp.lib', + 'winmm.lib', + 'Ws2_32.lib', + ], + }], + ['OS=="mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ '-Wl,-rpath,@loader_path', ], + } + }], + ['OS=="linux"', { + 'ldflags': [ + '-Wl,-rpath,\\$$ORIGIN' + ], + }], + ], + }, # shared_embedtest + { 'target_name': 'overlapped-checker', 'type': 'executable', diff --git a/test/common/index.js b/test/common/index.js index 8056b7d434a8ab..e28fd6acd773b7 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -62,6 +62,8 @@ if (isMainThread) const noop = () => {}; +// Whether the executable is linked against the shared library i.e. libnode. +const usesSharedLibrary = process.config.variables.node_shared; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; @@ -950,6 +952,13 @@ function sleepSync(ms) { Atomics.wait(i32, 0, 0, ms); } +function resolveBuiltBinary(binary) { + if (isWindows) { + binary += '.exe'; + } + return path.join(path.dirname(process.execPath), binary); +} + const common = { allowGlobals, buildType, @@ -995,6 +1004,7 @@ const common = { printSkipMessage, pwdCommand, requireNoPackageJSONAbove, + resolveBuiltBinary, runWithInvalidFD, skip, skipIf32Bits, @@ -1003,6 +1013,7 @@ const common = { skipIfSQLiteMissing, spawnPromisified, sleepSync, + usesSharedLibrary, get enoughTestMem() { return require('os').totalmem() > 0x70000000; /* 1.75 Gb */ diff --git a/test/embedding/shared_embedtest.cc b/test/embedding/shared_embedtest.cc new file mode 100644 index 00000000000000..f071f43c5b76a2 --- /dev/null +++ b/test/embedding/shared_embedtest.cc @@ -0,0 +1,60 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +class Wrappable : public v8::Object::Wrappable { + public: + void Trace(cppgc::Visitor* visitor) const override { + v8::Object::Wrappable::Trace(visitor); + } +}; + +int main(int argc, char* argv[]) { + std::unique_ptr platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(platform.get()); + cppgc::InitializeProcess(platform->GetPageAllocator()); + v8::V8::Initialize(); + + auto heap = v8::CppHeap::Create(platform.get(), v8::CppHeapCreateParams{{}}); + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = + v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + create_params.cpp_heap = heap.release(); + + v8::Isolate* isolate = v8::Isolate::New(create_params); + { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + + v8::Local obj = v8::Object::New(isolate); + Wrappable* wrappable = cppgc::MakeGarbageCollected( + isolate->GetCppHeap()->GetAllocationHandle()); + v8::Object::Wrap( + isolate, obj, wrappable); + v8::Local source = + v8::String::NewFromUtf8Literal(isolate, "'Hello' + ', World!'"); + v8::Local script = + v8::Script::Compile(context, source).ToLocalChecked(); + v8::Local result = script->Run(context).ToLocalChecked(); + v8::String::Utf8Value utf8(isolate, result); + printf("%s\n", *utf8); + } + + isolate->Dispose(); + cppgc::ShutdownProcess(); + v8::V8::Dispose(); + v8::V8::DisposePlatform(); + delete create_params.array_buffer_allocator; + + return 0; +} diff --git a/test/embedding/test-embedding.js b/test/embedding/test-embedding.js index 49706079fd8b6a..79d79079b4b8c6 100644 --- a/test/embedding/test-embedding.js +++ b/test/embedding/test-embedding.js @@ -8,7 +8,6 @@ const { spawnSyncAndExit, spawnSyncAndExitWithoutError, } = require('../common/child_process'); -const path = require('path'); const fs = require('fs'); const os = require('os'); @@ -16,14 +15,7 @@ tmpdir.refresh(); common.allowGlobals(global.require); common.allowGlobals(global.embedVars); -function resolveBuiltBinary(binary) { - if (common.isWindows) { - binary += '.exe'; - } - return path.join(path.dirname(process.execPath), binary); -} - -const binary = resolveBuiltBinary('embedtest'); +const binary = common.resolveBuiltBinary('embedtest'); spawnSyncAndAssert( binary, diff --git a/test/embedding/test-shared-embedding-v8.js b/test/embedding/test-shared-embedding-v8.js new file mode 100644 index 00000000000000..4d03a9d4b480d6 --- /dev/null +++ b/test/embedding/test-shared-embedding-v8.js @@ -0,0 +1,25 @@ +'use strict'; + +// This tests the V8 parts in the shared library work correctly. +// TODO(joyeecheung): also test that the Node.js parts work correctly, +// which can be done in embedtest just built in shared library mode. + +const common = require('../common'); + +if (!common.usesSharedLibrary) { + common.skip('Only tests builds linking against Node.js shared library'); +} + +const { spawnSyncAndAssert } = require('../common/child_process'); +const fs = require('fs'); + +const binary = common.resolveBuiltBinary('shared_embedtest'); + +if (!fs.existsSync(binary)) { + common.skip('shared_embedtest binary not built'); +} + +spawnSyncAndAssert(binary, { + trim: true, + stdout: 'Hello, World!', +});