node/test/cctest/test_linked_binding.cc
Anna Henningsen cd43073ce2
src: fix multiple AddLinkedBinding() calls
Singly-linked lists are extended at their tail, not their head.
This fixes using more than 2 linked addons at a time.

PR-URL: https://github.com/nodejs/node/pull/39012
Reviewed-By: Shelley Vohr <codebytere@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Richard Lau <rlau@redhat.com>
2021-06-14 13:30:50 +02:00

225 lines
7.7 KiB
C++

#include "node_test_fixture.h"
#include "node_api.h"
void InitializeBinding(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
void* priv) {
v8::Isolate* isolate = context->GetIsolate();
exports->Set(
context,
v8::String::NewFromOneByte(isolate,
reinterpret_cast<const uint8_t*>("key"))
.ToLocalChecked(),
v8::String::NewFromOneByte(isolate,
reinterpret_cast<const uint8_t*>("value"))
.ToLocalChecked())
.FromJust();
}
NODE_MODULE_LINKED(cctest_linkedbinding, InitializeBinding)
class LinkedBindingTest : public EnvironmentTestFixture {};
TEST_F(LinkedBindingTest, SimpleTest) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
Env test_env {handle_scope, argv};
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
const char* run_script =
"process._linkedBinding('cctest_linkedbinding').key";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8val(isolate_, completion_value);
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "value"), 0);
}
void InitializeLocalBinding(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
void* priv) {
++*static_cast<int*>(priv);
v8::Isolate* isolate = context->GetIsolate();
exports->Set(
context,
v8::String::NewFromOneByte(isolate,
reinterpret_cast<const uint8_t*>("key"))
.ToLocalChecked(),
v8::String::NewFromOneByte(isolate,
reinterpret_cast<const uint8_t*>("value"))
.ToLocalChecked())
.FromJust();
}
TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingTest) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
Env test_env {handle_scope, argv};
int calls = 0;
AddLinkedBinding(*test_env, "local_linked", InitializeLocalBinding, &calls);
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
const char* run_script =
"process._linkedBinding('local_linked').key";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8val(isolate_, completion_value);
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "value"), 0);
CHECK_EQ(calls, 1);
}
napi_value InitializeLocalNapiBinding(napi_env env, napi_value exports) {
napi_value key, value;
CHECK_EQ(
napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key), napi_ok);
CHECK_EQ(
napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &value), napi_ok);
CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
return nullptr;
}
static napi_module local_linked_napi = {
NAPI_MODULE_VERSION,
node::ModuleFlags::kLinked,
nullptr,
InitializeLocalNapiBinding,
"local_linked_napi",
nullptr,
{0},
};
TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiTest) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
Env test_env {handle_scope, argv};
AddLinkedBinding(*test_env, local_linked_napi);
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
const char* run_script =
"process._linkedBinding('local_linked_napi').hello";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8val(isolate_, completion_value);
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "world"), 0);
}
napi_value NapiLinkedWithInstanceData(napi_env env, napi_value exports) {
int* instance_data = new int(0);
CHECK_EQ(
napi_set_instance_data(
env,
instance_data,
[](napi_env env, void* data, void* hint) {
++*static_cast<int*>(data);
}, nullptr),
napi_ok);
napi_value key, value;
CHECK_EQ(
napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &key), napi_ok);
CHECK_EQ(
napi_create_external(env, instance_data, nullptr, nullptr, &value),
napi_ok);
CHECK_EQ(napi_set_property(env, exports, key, value), napi_ok);
return nullptr;
}
static napi_module local_linked_napi_id = {
NAPI_MODULE_VERSION,
node::ModuleFlags::kLinked,
nullptr,
NapiLinkedWithInstanceData,
"local_linked_napi_id",
nullptr,
{0},
};
TEST_F(LinkedBindingTest, LocallyDefinedLinkedBindingNapiInstanceDataTest) {
const v8::HandleScope handle_scope(isolate_);
int* instance_data = nullptr;
{
const Argv argv;
Env test_env {handle_scope, argv};
AddLinkedBinding(*test_env, local_linked_napi_id);
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
const char* run_script =
"process._linkedBinding('local_linked_napi_id').hello";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value =
script->Run(context).ToLocalChecked();
CHECK(completion_value->IsExternal());
instance_data = static_cast<int*>(
completion_value.As<v8::External>()->Value());
CHECK_NE(instance_data, nullptr);
CHECK_EQ(*instance_data, 0);
}
CHECK_EQ(*instance_data, 1);
delete instance_data;
}
TEST_F(LinkedBindingTest, ManyBindingsTest) {
const v8::HandleScope handle_scope(isolate_);
const Argv argv;
Env test_env {handle_scope, argv};
int calls = 0;
AddLinkedBinding(*test_env, "local_linked1", InitializeLocalBinding, &calls);
AddLinkedBinding(*test_env, "local_linked2", InitializeLocalBinding, &calls);
AddLinkedBinding(*test_env, "local_linked3", InitializeLocalBinding, &calls);
AddLinkedBinding(*test_env, local_linked_napi); // Add a N-API addon as well.
AddLinkedBinding(*test_env, "local_linked4", InitializeLocalBinding, &calls);
AddLinkedBinding(*test_env, "local_linked5", InitializeLocalBinding, &calls);
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
const char* run_script =
"for (let i = 1; i <= 5; i++)process._linkedBinding(`local_linked${i}`);"
"process._linkedBinding('local_linked_napi').hello";
v8::Local<v8::Script> script = v8::Script::Compile(
context,
v8::String::NewFromOneByte(isolate_,
reinterpret_cast<const uint8_t*>(run_script))
.ToLocalChecked())
.ToLocalChecked();
v8::Local<v8::Value> completion_value = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8val(isolate_, completion_value);
CHECK_NOT_NULL(*utf8val);
CHECK_EQ(strcmp(*utf8val, "world"), 0);
CHECK_EQ(calls, 5);
}