mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Revert "Split NAPI tests out of the huge C++ file"
This reverts commit 9d10df2501.
This commit is contained in:
@@ -1,194 +0,0 @@
|
||||
#include "async_tests.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
|
||||
namespace napitests {
|
||||
|
||||
struct AsyncWorkData {
|
||||
int result;
|
||||
napi_deferred deferred;
|
||||
napi_async_work work;
|
||||
bool do_throw;
|
||||
|
||||
AsyncWorkData()
|
||||
: result(0), deferred(nullptr), work(nullptr), do_throw(false) {}
|
||||
|
||||
static void execute(napi_env env, void *data) {
|
||||
AsyncWorkData *async_work_data = reinterpret_cast<AsyncWorkData *>(data);
|
||||
async_work_data->result = 42;
|
||||
}
|
||||
|
||||
static void complete(napi_env c_env, napi_status status, void *data) {
|
||||
Napi::Env env(c_env);
|
||||
AsyncWorkData *async_work_data = reinterpret_cast<AsyncWorkData *>(data);
|
||||
NODE_API_ASSERT_CUSTOM_RETURN(env, void(), status == napi_ok);
|
||||
|
||||
if (async_work_data->do_throw) {
|
||||
// still have to resolve/reject otherwise the process times out
|
||||
// we should not see the resolution as our unhandled exception handler
|
||||
// exits the process before that can happen
|
||||
napi_value result = env.Undefined();
|
||||
NODE_API_CALL_CUSTOM_RETURN(
|
||||
env, void(),
|
||||
napi_resolve_deferred(env, async_work_data->deferred, result));
|
||||
|
||||
Napi::Error::New(env, "error from napi").ThrowAsJavaScriptException();
|
||||
} else {
|
||||
char buf[64] = {0};
|
||||
snprintf(buf, sizeof(buf), "the number is %d", async_work_data->result);
|
||||
napi_value result = Napi::String::New(env, buf);
|
||||
NODE_API_CALL_CUSTOM_RETURN(
|
||||
env, void(),
|
||||
napi_resolve_deferred(env, async_work_data->deferred, result));
|
||||
}
|
||||
|
||||
NODE_API_CALL_CUSTOM_RETURN(
|
||||
env, void(), napi_delete_async_work(env, async_work_data->work));
|
||||
delete async_work_data;
|
||||
}
|
||||
};
|
||||
|
||||
// create_promise(void *unused_run_gc_callback, bool do_throw): makes a promise
|
||||
// using napi_Async_work that either resolves or throws in the complete callback
|
||||
static napi_value create_promise(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
auto *data = new AsyncWorkData();
|
||||
// info[0] is a callback to run the GC
|
||||
data->do_throw = info[1].As<Napi::Boolean>();
|
||||
|
||||
napi_value promise;
|
||||
NODE_API_CALL(env, napi_create_promise(env, &data->deferred, &promise));
|
||||
|
||||
napi_value resource_name =
|
||||
Napi::String::New(env, "napitests__create_promise");
|
||||
NODE_API_CALL(
|
||||
env, napi_create_async_work(env, /* async resource */ nullptr,
|
||||
resource_name, AsyncWorkData::execute,
|
||||
AsyncWorkData::complete, data, &data->work));
|
||||
NODE_API_CALL(env, napi_queue_async_work(env, data->work));
|
||||
return promise;
|
||||
}
|
||||
|
||||
class EchoWorker : public Napi::AsyncWorker {
|
||||
public:
|
||||
EchoWorker(Napi::Env env, Napi::Promise::Deferred deferred,
|
||||
const std::string &&echo)
|
||||
: Napi::AsyncWorker(env), m_echo(echo), m_deferred(deferred) {}
|
||||
~EchoWorker() override {}
|
||||
|
||||
void Execute() override {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
void OnOK() override { m_deferred.Resolve(Napi::String::New(Env(), m_echo)); }
|
||||
|
||||
private:
|
||||
std::string m_echo;
|
||||
Napi::Promise::Deferred m_deferred;
|
||||
};
|
||||
|
||||
static Napi::Value
|
||||
create_promise_with_napi_cpp(const Napi::CallbackInfo &info) {
|
||||
auto deferred = Napi::Promise::Deferred::New(info.Env());
|
||||
auto *work = new EchoWorker(info.Env(), deferred, "hello world");
|
||||
work->Queue();
|
||||
return deferred.Promise();
|
||||
}
|
||||
|
||||
struct ThreadsafeFunctionData {
|
||||
napi_threadsafe_function tsfn;
|
||||
napi_deferred deferred;
|
||||
|
||||
static void thread_entry(ThreadsafeFunctionData *data) {
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(10ms);
|
||||
// nonblocking means it will return an error if the threadsafe function's
|
||||
// queue is full, which it should never do because we only use it once and
|
||||
// we init with a capacity of 1
|
||||
assert(napi_call_threadsafe_function(data->tsfn, nullptr,
|
||||
napi_tsfn_nonblocking) == napi_ok);
|
||||
}
|
||||
|
||||
static void tsfn_finalize_callback(napi_env env, void *finalize_data,
|
||||
void *finalize_hint) {
|
||||
printf("tsfn_finalize_callback\n");
|
||||
ThreadsafeFunctionData *data =
|
||||
reinterpret_cast<ThreadsafeFunctionData *>(finalize_data);
|
||||
delete data;
|
||||
}
|
||||
|
||||
static void tsfn_callback(napi_env c_env, napi_value js_callback,
|
||||
void *context, void *data) {
|
||||
// context == ThreadsafeFunctionData pointer
|
||||
// data == nullptr
|
||||
printf("tsfn_callback\n");
|
||||
ThreadsafeFunctionData *tsfn_data =
|
||||
reinterpret_cast<ThreadsafeFunctionData *>(context);
|
||||
Napi::Env env(c_env);
|
||||
|
||||
napi_value recv = env.Undefined();
|
||||
|
||||
// call our JS function with undefined for this and no arguments
|
||||
napi_value js_result;
|
||||
napi_status call_result =
|
||||
napi_call_function(env, recv, js_callback, 0, nullptr, &js_result);
|
||||
NODE_API_ASSERT_CUSTOM_RETURN(env, void(),
|
||||
call_result == napi_ok ||
|
||||
call_result == napi_pending_exception);
|
||||
|
||||
if (call_result == napi_ok) {
|
||||
// only resolve if js_callback did not return an error
|
||||
// resolve the promise with the return value of the JS function
|
||||
NODE_API_CALL_CUSTOM_RETURN(
|
||||
env, void(),
|
||||
napi_resolve_deferred(env, tsfn_data->deferred, js_result));
|
||||
}
|
||||
|
||||
// clean up the threadsafe function
|
||||
NODE_API_CALL_CUSTOM_RETURN(
|
||||
env, void(),
|
||||
napi_release_threadsafe_function(tsfn_data->tsfn, napi_tsfn_abort));
|
||||
}
|
||||
};
|
||||
|
||||
napi_value
|
||||
create_promise_with_threadsafe_function(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
ThreadsafeFunctionData *tsfn_data = new ThreadsafeFunctionData;
|
||||
|
||||
napi_value async_resource_name = Napi::String::New(
|
||||
env, "napitests::create_promise_with_threadsafe_function");
|
||||
|
||||
// this is called directly, without the GC callback, so argument 0 is a JS
|
||||
// callback used to resolve the promise
|
||||
NODE_API_CALL(env,
|
||||
napi_create_threadsafe_function(
|
||||
env, info[0], nullptr, async_resource_name,
|
||||
// max_queue_size, initial_thread_count
|
||||
1, 1,
|
||||
// thread_finalize_data, thread_finalize_cb
|
||||
tsfn_data, ThreadsafeFunctionData::tsfn_finalize_callback,
|
||||
// context
|
||||
tsfn_data, ThreadsafeFunctionData::tsfn_callback,
|
||||
&tsfn_data->tsfn));
|
||||
// create a promise we can return to JS and put the deferred counterpart in
|
||||
// tsfn_data
|
||||
napi_value promise;
|
||||
NODE_API_CALL(env, napi_create_promise(env, &tsfn_data->deferred, &promise));
|
||||
|
||||
// spawn and release std::thread
|
||||
std::thread secondary_thread(ThreadsafeFunctionData::thread_entry, tsfn_data);
|
||||
secondary_thread.detach();
|
||||
// return the promise to javascript
|
||||
return promise;
|
||||
}
|
||||
|
||||
void register_async_tests(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, create_promise);
|
||||
REGISTER_FUNCTION(env, exports, create_promise_with_napi_cpp);
|
||||
REGISTER_FUNCTION(env, exports, create_promise_with_threadsafe_function);
|
||||
}
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Tests that use napi_async_work or napi_deferred
|
||||
|
||||
#include "napi_with_version.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
void register_async_tests(Napi::Env env, Napi::Object exports);
|
||||
|
||||
} // namespace napitests
|
||||
@@ -10,7 +10,7 @@
|
||||
"AdditionalOptions": ["/std:c++20"],
|
||||
},
|
||||
},
|
||||
"sources": ["main.cpp", "async_tests.cpp", "class_test.cpp", "conversion_tests.cpp", "js_test_helpers.cpp", "standalone_tests.cpp", "wrap_tests.cpp"],
|
||||
"sources": ["main.cpp", "wrap_tests.cpp"],
|
||||
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
|
||||
"libraries": [],
|
||||
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "class_test.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
static napi_value constructor(napi_env env, napi_callback_info info) {
|
||||
napi_value this_value;
|
||||
void *data;
|
||||
NODE_API_CALL(
|
||||
env, napi_get_cb_info(env, info, nullptr, nullptr, &this_value, &data));
|
||||
|
||||
printf("in constructor, data = \"%s\"\n",
|
||||
reinterpret_cast<const char *>(data));
|
||||
|
||||
napi_value new_target;
|
||||
NODE_API_CALL(env, napi_get_new_target(env, info, &new_target));
|
||||
printf("typeof new.target = %s\n",
|
||||
new_target ? napi_valuetype_to_string(get_typeof(env, new_target))
|
||||
: "[nullptr]");
|
||||
|
||||
printf("typeof this = %s\n",
|
||||
napi_valuetype_to_string(get_typeof(env, this_value)));
|
||||
|
||||
napi_value global;
|
||||
NODE_API_CALL(env, napi_get_global(env, &global));
|
||||
bool equal;
|
||||
NODE_API_CALL(env, napi_strict_equals(env, this_value, global, &equal));
|
||||
printf("this == global = %s\n", equal ? "true" : "false");
|
||||
|
||||
napi_value property_value = Napi::String::New(env, "meow");
|
||||
napi_set_named_property(env, this_value, "foo", property_value);
|
||||
|
||||
napi_value undefined;
|
||||
NODE_API_CALL(env, napi_get_undefined(env, &undefined));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static napi_value getData_callback(napi_env env, napi_callback_info info) {
|
||||
void *data;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data));
|
||||
const char *str_data = reinterpret_cast<const char *>(data);
|
||||
|
||||
napi_value ret;
|
||||
NODE_API_CALL(env,
|
||||
napi_create_string_utf8(env, str_data, NAPI_AUTO_LENGTH, &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static napi_value get_class_with_constructor(const Napi::CallbackInfo &info) {
|
||||
static char constructor_data[] = "constructor data";
|
||||
static char method_data[] = "method data";
|
||||
static char wrap_data[] = "wrap data";
|
||||
|
||||
napi_env env = info.Env();
|
||||
napi_value napi_class;
|
||||
|
||||
const napi_property_descriptor properties[] = {{
|
||||
.utf8name = "getData",
|
||||
.name = nullptr,
|
||||
.method = getData_callback,
|
||||
.getter = nullptr,
|
||||
.setter = nullptr,
|
||||
.value = nullptr,
|
||||
.attributes = napi_default_method,
|
||||
.data = reinterpret_cast<void *>(method_data),
|
||||
}};
|
||||
|
||||
NODE_API_CALL(
|
||||
env, napi_define_class(env, "NapiClass", NAPI_AUTO_LENGTH, constructor,
|
||||
reinterpret_cast<void *>(constructor_data), 1,
|
||||
properties, &napi_class));
|
||||
NODE_API_CALL(env,
|
||||
napi_wrap(env, napi_class, reinterpret_cast<void *>(wrap_data),
|
||||
nullptr, nullptr, nullptr));
|
||||
return napi_class;
|
||||
}
|
||||
|
||||
void register_class_test(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, get_class_with_constructor);
|
||||
}
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Functions exported to JS that make a class available with some interesting
|
||||
// properties and methods
|
||||
|
||||
#include <napi.h>
|
||||
|
||||
namespace napitests {
|
||||
|
||||
void register_class_test(Napi::Env env, Napi::Object exports);
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,169 +0,0 @@
|
||||
#include "conversion_tests.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
// double_to_i32(any): number|undefined
|
||||
static napi_value double_to_i32(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value input = info[0];
|
||||
|
||||
int32_t integer;
|
||||
napi_value result;
|
||||
napi_status status = napi_get_value_int32(env, input, &integer);
|
||||
if (status == napi_ok) {
|
||||
NODE_API_CALL(env, napi_create_int32(env, integer, &result));
|
||||
} else {
|
||||
NODE_API_ASSERT(env, status == napi_number_expected);
|
||||
NODE_API_CALL(env, napi_get_undefined(env, &result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// double_to_u32(any): number|undefined
|
||||
static napi_value double_to_u32(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value input = info[0];
|
||||
|
||||
uint32_t integer;
|
||||
napi_value result;
|
||||
napi_status status = napi_get_value_uint32(env, input, &integer);
|
||||
if (status == napi_ok) {
|
||||
NODE_API_CALL(env, napi_create_uint32(env, integer, &result));
|
||||
} else {
|
||||
NODE_API_ASSERT(env, status == napi_number_expected);
|
||||
NODE_API_CALL(env, napi_get_undefined(env, &result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// double_to_i64(any): number|undefined
|
||||
static napi_value double_to_i64(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value input = info[0];
|
||||
|
||||
int64_t integer;
|
||||
napi_value result;
|
||||
napi_status status = napi_get_value_int64(env, input, &integer);
|
||||
if (status == napi_ok) {
|
||||
NODE_API_CALL(env, napi_create_int64(env, integer, &result));
|
||||
} else {
|
||||
NODE_API_ASSERT(env, status == napi_number_expected);
|
||||
NODE_API_CALL(env, napi_get_undefined(env, &result));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// test from the C++ side
|
||||
static napi_value
|
||||
test_number_integer_conversions(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
using f64_limits = std::numeric_limits<double>;
|
||||
using i32_limits = std::numeric_limits<int32_t>;
|
||||
using u32_limits = std::numeric_limits<uint32_t>;
|
||||
using i64_limits = std::numeric_limits<int64_t>;
|
||||
|
||||
std::array<std::pair<double, int32_t>, 14> i32_cases{{
|
||||
// special values
|
||||
{f64_limits::infinity(), 0},
|
||||
{-f64_limits::infinity(), 0},
|
||||
{f64_limits::quiet_NaN(), 0},
|
||||
// normal
|
||||
{0.0, 0},
|
||||
{1.0, 1},
|
||||
{-1.0, -1},
|
||||
// truncation
|
||||
{1.25, 1},
|
||||
{-1.25, -1},
|
||||
// limits
|
||||
{i32_limits::min(), i32_limits::min()},
|
||||
{i32_limits::max(), i32_limits::max()},
|
||||
// wrap around
|
||||
{static_cast<double>(i32_limits::min()) - 1.0, i32_limits::max()},
|
||||
{static_cast<double>(i32_limits::max()) + 1.0, i32_limits::min()},
|
||||
{static_cast<double>(i32_limits::min()) - 2.0, i32_limits::max() - 1},
|
||||
{static_cast<double>(i32_limits::max()) + 2.0, i32_limits::min() + 1},
|
||||
}};
|
||||
|
||||
for (const auto &[in, expected_out] : i32_cases) {
|
||||
napi_value js_in;
|
||||
NODE_API_CALL(env, napi_create_double(env, in, &js_in));
|
||||
int32_t out_from_napi;
|
||||
NODE_API_CALL(env, napi_get_value_int32(env, js_in, &out_from_napi));
|
||||
NODE_API_ASSERT(env, out_from_napi == expected_out);
|
||||
}
|
||||
|
||||
std::array<std::pair<double, uint32_t>, 12> u32_cases{{
|
||||
// special values
|
||||
{f64_limits::infinity(), 0},
|
||||
{-f64_limits::infinity(), 0},
|
||||
{f64_limits::quiet_NaN(), 0},
|
||||
// normal
|
||||
{0.0, 0},
|
||||
{1.0, 1},
|
||||
// truncation
|
||||
{1.25, 1},
|
||||
{-1.25, u32_limits::max()},
|
||||
// limits
|
||||
{u32_limits::max(), u32_limits::max()},
|
||||
// wrap around
|
||||
{-1.0, u32_limits::max()},
|
||||
{static_cast<double>(u32_limits::max()) + 1.0, 0},
|
||||
{-2.0, u32_limits::max() - 1},
|
||||
{static_cast<double>(u32_limits::max()) + 2.0, 1},
|
||||
|
||||
}};
|
||||
|
||||
for (const auto &[in, expected_out] : u32_cases) {
|
||||
napi_value js_in;
|
||||
NODE_API_CALL(env, napi_create_double(env, in, &js_in));
|
||||
uint32_t out_from_napi;
|
||||
NODE_API_CALL(env, napi_get_value_uint32(env, js_in, &out_from_napi));
|
||||
NODE_API_ASSERT(env, out_from_napi == expected_out);
|
||||
}
|
||||
|
||||
std::array<std::pair<double, int64_t>, 12> i64_cases{
|
||||
{// special values
|
||||
{f64_limits::infinity(), 0},
|
||||
{-f64_limits::infinity(), 0},
|
||||
{f64_limits::quiet_NaN(), 0},
|
||||
// normal
|
||||
{0.0, 0},
|
||||
{1.0, 1},
|
||||
{-1.0, -1},
|
||||
// truncation
|
||||
{1.25, 1},
|
||||
{-1.25, -1},
|
||||
// limits
|
||||
// i64 max can't be precisely represented as double so it would round to
|
||||
// 1 + i64 max, which would clamp and we don't want that yet. so we test
|
||||
// the largest double smaller than i64 max instead (which is i64 max -
|
||||
// 1024)
|
||||
{i64_limits::min(), i64_limits::min()},
|
||||
{std::nextafter(static_cast<double>(i64_limits::max()), 0.0),
|
||||
static_cast<int64_t>(
|
||||
std::nextafter(static_cast<double>(i64_limits::max()), 0.0))},
|
||||
// clamp
|
||||
{i64_limits::min() - 4096.0, i64_limits::min()},
|
||||
{i64_limits::max() + 4096.0, i64_limits::max()}}};
|
||||
|
||||
for (const auto &[in, expected_out] : i64_cases) {
|
||||
napi_value js_in;
|
||||
NODE_API_CALL(env, napi_create_double(env, in, &js_in));
|
||||
int64_t out_from_napi;
|
||||
NODE_API_CALL(env, napi_get_value_int64(env, js_in, &out_from_napi));
|
||||
NODE_API_ASSERT(env, out_from_napi == expected_out);
|
||||
}
|
||||
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
void register_conversion_tests(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, double_to_i32);
|
||||
REGISTER_FUNCTION(env, exports, double_to_u32);
|
||||
REGISTER_FUNCTION(env, exports, double_to_i64);
|
||||
REGISTER_FUNCTION(env, exports, test_number_integer_conversions);
|
||||
}
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Includes both some callbacks for module.js to use, and a long pure-C++ test
|
||||
// of Node-API conversion functions
|
||||
|
||||
#include "napi_with_version.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
void register_conversion_tests(Napi::Env env, Napi::Object exports);
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,226 +0,0 @@
|
||||
#include "js_test_helpers.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace napitests {
|
||||
|
||||
static bool finalize_called = false;
|
||||
|
||||
static void finalize_cb(napi_env env, void *finalize_data,
|
||||
void *finalize_hint) {
|
||||
// only do this in bun
|
||||
bool &create_handle_scope = *reinterpret_cast<bool *>(finalize_hint);
|
||||
if (create_handle_scope) {
|
||||
napi_handle_scope hs;
|
||||
NODE_API_CALL_CUSTOM_RETURN(env, void(), napi_open_handle_scope(env, &hs));
|
||||
NODE_API_CALL_CUSTOM_RETURN(env, void(), napi_close_handle_scope(env, hs));
|
||||
}
|
||||
delete &create_handle_scope;
|
||||
finalize_called = true;
|
||||
}
|
||||
|
||||
static napi_value create_ref_with_finalizer(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value create_handle_scope_in_finalizer = info[0];
|
||||
|
||||
napi_value object;
|
||||
NODE_API_CALL(env, napi_create_object(env, &object));
|
||||
|
||||
bool *finalize_hint = new bool;
|
||||
NODE_API_CALL(env, napi_get_value_bool(env, create_handle_scope_in_finalizer,
|
||||
finalize_hint));
|
||||
|
||||
napi_ref ref;
|
||||
|
||||
NODE_API_CALL(env, napi_wrap(env, object, nullptr, finalize_cb,
|
||||
reinterpret_cast<bool *>(finalize_hint), &ref));
|
||||
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_value was_finalize_called(const Napi::CallbackInfo &info) {
|
||||
napi_value ret;
|
||||
NODE_API_CALL(info.Env(),
|
||||
napi_get_boolean(info.Env(), finalize_called, &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// calls a function (the sole argument) which must throw. catches and returns
|
||||
// the thrown error
|
||||
static napi_value call_and_get_exception(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value fn = info[0];
|
||||
napi_value undefined;
|
||||
NODE_API_CALL(env, napi_get_undefined(env, &undefined));
|
||||
|
||||
NODE_API_ASSERT(env, napi_call_function(env, undefined, fn, 0, nullptr,
|
||||
nullptr) == napi_pending_exception);
|
||||
|
||||
bool is_pending;
|
||||
NODE_API_CALL(env, napi_is_exception_pending(env, &is_pending));
|
||||
NODE_API_ASSERT(env, is_pending);
|
||||
|
||||
napi_value exception;
|
||||
NODE_API_CALL(env, napi_get_and_clear_last_exception(env, &exception));
|
||||
|
||||
napi_valuetype type = get_typeof(env, exception);
|
||||
printf("typeof thrown exception = %s\n", napi_valuetype_to_string(type));
|
||||
|
||||
NODE_API_CALL(env, napi_is_exception_pending(env, &is_pending));
|
||||
NODE_API_ASSERT(env, !is_pending);
|
||||
|
||||
return exception;
|
||||
}
|
||||
|
||||
// throw_error(code: string|undefined, msg: string|undefined,
|
||||
// error_kind: 'error'|'type_error'|'range_error'|'syntax_error')
|
||||
// if code and msg are JS undefined then change them to nullptr
|
||||
static napi_value throw_error(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
|
||||
Napi::Value js_code = info[0];
|
||||
Napi::Value js_msg = info[1];
|
||||
std::string error_kind = info[2].As<Napi::String>().Utf8Value();
|
||||
|
||||
// these are optional
|
||||
const char *code = nullptr;
|
||||
std::string code_str;
|
||||
const char *msg = nullptr;
|
||||
std::string msg_str;
|
||||
|
||||
if (js_code.IsString()) {
|
||||
code_str = js_code.As<Napi::String>().Utf8Value();
|
||||
code = code_str.c_str();
|
||||
}
|
||||
if (js_msg.IsString()) {
|
||||
msg_str = js_msg.As<Napi::String>().Utf8Value();
|
||||
msg = msg_str.c_str();
|
||||
}
|
||||
|
||||
using ThrowFunction =
|
||||
napi_status (*)(napi_env, const char *code, const char *msg);
|
||||
std::map<std::string, ThrowFunction> functions{
|
||||
{"error", napi_throw_error},
|
||||
{"type_error", napi_throw_type_error},
|
||||
{"range_error", napi_throw_range_error},
|
||||
{"syntax_error", node_api_throw_syntax_error}};
|
||||
|
||||
auto throw_function = functions[error_kind];
|
||||
|
||||
if (msg == nullptr) {
|
||||
NODE_API_ASSERT(env, throw_function(env, code, msg) == napi_invalid_arg);
|
||||
return ok(env);
|
||||
} else {
|
||||
NODE_API_ASSERT(env, throw_function(env, code, msg) == napi_ok);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// create_and_throw_error(code: any, msg: any,
|
||||
// error_kind: 'error'|'type_error'|'range_error'|'syntax_error')
|
||||
// if code and msg are JS null then change them to nullptr
|
||||
static napi_value create_and_throw_error(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
|
||||
napi_value js_code = info[0];
|
||||
napi_value js_msg = info[1];
|
||||
std::string error_kind = info[2].As<Napi::String>();
|
||||
|
||||
if (get_typeof(env, js_code) == napi_null) {
|
||||
js_code = nullptr;
|
||||
}
|
||||
if (get_typeof(env, js_msg) == napi_null) {
|
||||
js_msg = nullptr;
|
||||
}
|
||||
|
||||
using CreateErrorFunction = napi_status (*)(
|
||||
napi_env, napi_value code, napi_value msg, napi_value *result);
|
||||
std::map<std::string, CreateErrorFunction> functions{
|
||||
{"error", napi_create_error},
|
||||
{"type_error", napi_create_type_error},
|
||||
{"range_error", napi_create_range_error},
|
||||
{"syntax_error", node_api_create_syntax_error}};
|
||||
|
||||
auto create_error_function = functions[error_kind];
|
||||
|
||||
napi_value err;
|
||||
napi_status create_status = create_error_function(env, js_code, js_msg, &err);
|
||||
// cases that should fail:
|
||||
// - js_msg is nullptr
|
||||
// - js_msg is not a string
|
||||
// - js_code is not nullptr and not a string
|
||||
// also we need to make sure not to call get_typeof with nullptr, since it
|
||||
// asserts that napi_typeof succeeded
|
||||
if (!js_msg || get_typeof(env, js_msg) != napi_string ||
|
||||
(js_code && get_typeof(env, js_code) != napi_string)) {
|
||||
// bun and node may return different errors here depending on in what order
|
||||
// the parameters are checked, but what's important is that there is an
|
||||
// error
|
||||
NODE_API_ASSERT(env, create_status == napi_string_expected ||
|
||||
create_status == napi_invalid_arg);
|
||||
return ok(env);
|
||||
} else {
|
||||
NODE_API_ASSERT(env, create_status == napi_ok);
|
||||
NODE_API_CALL(env, napi_throw(env, err));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// perform_get(object, key)
|
||||
static napi_value perform_get(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value obj = info[0];
|
||||
napi_value key = info[1];
|
||||
napi_status status;
|
||||
napi_value value;
|
||||
|
||||
// if key is a string, try napi_get_named_property
|
||||
napi_valuetype type = get_typeof(env, key);
|
||||
if (type == napi_string) {
|
||||
char buf[1024];
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_utf8(env, key, buf, 1024, nullptr));
|
||||
status = napi_get_named_property(env, obj, buf, &value);
|
||||
if (status == napi_ok) {
|
||||
NODE_API_ASSERT(env, value != nullptr);
|
||||
printf("value type = %d\n", get_typeof(env, value));
|
||||
} else {
|
||||
NODE_API_ASSERT(env, status == napi_pending_exception);
|
||||
return ok(env);
|
||||
}
|
||||
}
|
||||
|
||||
status = napi_get_property(env, obj, key, &value);
|
||||
NODE_API_ASSERT(env, status == napi_pending_exception);
|
||||
if (status == napi_ok) {
|
||||
NODE_API_ASSERT(env, value != nullptr);
|
||||
printf("value type = %d\n", get_typeof(env, value));
|
||||
return value;
|
||||
} else {
|
||||
return ok(env);
|
||||
}
|
||||
}
|
||||
|
||||
static napi_value make_empty_array(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value js_size = info[0];
|
||||
uint32_t size;
|
||||
NODE_API_CALL(env, napi_get_value_uint32(env, js_size, &size));
|
||||
napi_value array;
|
||||
NODE_API_CALL(env, napi_create_array_with_length(env, size, &array));
|
||||
return array;
|
||||
}
|
||||
|
||||
void register_js_test_helpers(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, create_ref_with_finalizer);
|
||||
REGISTER_FUNCTION(env, exports, was_finalize_called);
|
||||
REGISTER_FUNCTION(env, exports, call_and_get_exception);
|
||||
REGISTER_FUNCTION(env, exports, perform_get);
|
||||
REGISTER_FUNCTION(env, exports, throw_error);
|
||||
REGISTER_FUNCTION(env, exports, create_and_throw_error);
|
||||
REGISTER_FUNCTION(env, exports, make_empty_array);
|
||||
}
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Functions that are used by tests implemented in module.js, rather than
|
||||
// directly used by napi.test.ts, but are not complex enough or do not cleanly
|
||||
// fit into a category to go in a separate C++ file
|
||||
|
||||
#include "napi_with_version.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
void register_js_test_helpers(Napi::Env env, Napi::Object exports);
|
||||
|
||||
} // namespace napitests
|
||||
File diff suppressed because it is too large
Load Diff
@@ -238,9 +238,9 @@ nativeTests.test_create_array_with_length = () => {
|
||||
};
|
||||
|
||||
nativeTests.test_throw_functions_exhaustive = () => {
|
||||
for (const code of [undefined, "", "error code"]) {
|
||||
for (const msg of [undefined, "", "error message"]) {
|
||||
for (const errorKind of ["error", "type_error", "range_error", "syntax_error"]) {
|
||||
for (const errorKind of ["error", "type_error", "range_error", "syntax_error"]) {
|
||||
for (const code of [undefined, "", "error code"]) {
|
||||
for (const msg of [undefined, "", "error message"]) {
|
||||
try {
|
||||
nativeTests.throw_error(code, msg, errorKind);
|
||||
console.log(`napi_throw_${errorKind}(${code ?? "nullptr"}, ${msg ?? "nullptr"}) did not throw`);
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
#include "standalone_tests.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/7685
|
||||
static napi_value test_issue_7685(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env(info.Env());
|
||||
Napi::HandleScope scope(env);
|
||||
// info[0] is a function to run the GC
|
||||
NODE_API_ASSERT(env, info[1].IsNumber());
|
||||
NODE_API_ASSERT(env, info[2].IsNumber());
|
||||
NODE_API_ASSERT(env, info[3].IsNumber());
|
||||
NODE_API_ASSERT(env, info[4].IsNumber());
|
||||
NODE_API_ASSERT(env, info[5].IsNumber());
|
||||
NODE_API_ASSERT(env, info[6].IsNumber());
|
||||
NODE_API_ASSERT(env, info[7].IsNumber());
|
||||
NODE_API_ASSERT(env, info[8].IsNumber());
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_threadsafe_function tsfn_11949 = nullptr;
|
||||
|
||||
static void test_issue_11949_callback(napi_env env, napi_value js_callback,
|
||||
void *opaque_context, void *opaque_data) {
|
||||
int *context = reinterpret_cast<int *>(opaque_context);
|
||||
int *data = reinterpret_cast<int *>(opaque_data);
|
||||
printf("data = %d, context = %d\n", *data, *context);
|
||||
delete context;
|
||||
delete data;
|
||||
napi_unref_threadsafe_function(env, tsfn_11949);
|
||||
tsfn_11949 = nullptr;
|
||||
}
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/11949
|
||||
static napi_value test_issue_11949(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
Napi::HandleScope scope(env);
|
||||
napi_value name = Napi::String::New(env, "TSFN");
|
||||
|
||||
int *context = new int(42);
|
||||
int *data = new int(1234);
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_create_threadsafe_function(
|
||||
env, /* JavaScript function */ nullptr,
|
||||
/* async resource */ nullptr, name,
|
||||
/* max queue size (unlimited) */ 0,
|
||||
/* initial thread count */ 1, /* finalize data */ nullptr,
|
||||
/* finalize callback */ nullptr, context,
|
||||
&test_issue_11949_callback, &tsfn_11949));
|
||||
NODE_API_CALL(env, napi_call_threadsafe_function(tsfn_11949, data,
|
||||
napi_tsfn_nonblocking));
|
||||
return env.Undefined();
|
||||
}
|
||||
|
||||
static void noop_callback(napi_env env, napi_value js_callback, void *context,
|
||||
void *data) {}
|
||||
|
||||
static napi_value test_napi_threadsafe_function_does_not_hang_after_finalize(
|
||||
const Napi::CallbackInfo &info) {
|
||||
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
napi_value resource_name = Napi::String::New(env, "simple");
|
||||
|
||||
napi_threadsafe_function cb;
|
||||
NODE_API_CALL(env,
|
||||
napi_create_threadsafe_function(
|
||||
env, /* JavaScript function */ nullptr,
|
||||
/* async resource */ nullptr, resource_name,
|
||||
/* max queue size (unlimited) */ 0,
|
||||
/* initial thread count */ 1, /* finalize data */ nullptr,
|
||||
/* finalize callback */ nullptr, /* context */ nullptr,
|
||||
&noop_callback, &cb));
|
||||
|
||||
NODE_API_CALL(env, napi_release_threadsafe_function(cb, napi_tsfn_release));
|
||||
printf("success!\n");
|
||||
return env.Undefined();
|
||||
}
|
||||
|
||||
static napi_value
|
||||
test_napi_get_value_string_utf8_with_buffer(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
// info[0] is a function to run the GC
|
||||
napi_value string_js = info[1];
|
||||
// get how many chars we need to copy
|
||||
size_t len = info[2].As<Napi::Number>().Uint32Value();
|
||||
|
||||
if (len == 424242) {
|
||||
len = NAPI_AUTO_LENGTH;
|
||||
} else {
|
||||
NODE_API_ASSERT(env, len <= 29);
|
||||
}
|
||||
|
||||
size_t copied;
|
||||
const size_t BUF_SIZE = 30;
|
||||
char buf[BUF_SIZE];
|
||||
memset(buf, '*', BUF_SIZE);
|
||||
buf[BUF_SIZE - 1] = '\0';
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_utf8(env, string_js, buf, len, &copied));
|
||||
|
||||
std::cout << "Chars to copy: " << len << std::endl;
|
||||
std::cout << "Copied chars: " << copied << std::endl;
|
||||
std::cout << "Buffer: ";
|
||||
for (size_t i = 0; i < BUF_SIZE; i++) {
|
||||
std::cout << (int)buf[i] << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << "Value str: " << buf << std::endl;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_value
|
||||
test_napi_handle_scope_string(const Napi::CallbackInfo &info) {
|
||||
// this is mostly a copy of test_handle_scope_gc from
|
||||
// test/v8/v8-module/main.cpp -- see comments there for explanation
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
constexpr size_t num_small_strings = 10000;
|
||||
|
||||
auto *small_strings = new napi_value[num_small_strings];
|
||||
|
||||
for (size_t i = 0; i < num_small_strings; i++) {
|
||||
std::string cpp_str = std::to_string(i);
|
||||
NODE_API_CALL(env,
|
||||
napi_create_string_utf8(env, cpp_str.c_str(), cpp_str.size(),
|
||||
&small_strings[i]));
|
||||
}
|
||||
|
||||
run_gc(info);
|
||||
|
||||
for (size_t j = 0; j < num_small_strings; j++) {
|
||||
char buf[16];
|
||||
size_t result;
|
||||
NODE_API_CALL(env, napi_get_value_string_utf8(env, small_strings[j], buf,
|
||||
sizeof buf, &result));
|
||||
NODE_API_ASSERT(env, atoi(buf) == (int)j);
|
||||
}
|
||||
|
||||
delete[] small_strings;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_value
|
||||
test_napi_handle_scope_bigint(const Napi::CallbackInfo &info) {
|
||||
// this is mostly a copy of test_handle_scope_gc from
|
||||
// test/v8/v8-module/main.cpp -- see comments there for explanation
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
constexpr size_t num_small_ints = 10000;
|
||||
constexpr size_t small_int_size = 100;
|
||||
|
||||
auto *small_ints = new napi_value[num_small_ints];
|
||||
|
||||
for (size_t i = 0; i < num_small_ints; i++) {
|
||||
std::array<uint64_t, small_int_size> words;
|
||||
words.fill(i + 1);
|
||||
NODE_API_CALL(env, napi_create_bigint_words(env, 0, small_int_size,
|
||||
words.data(), &small_ints[i]));
|
||||
}
|
||||
|
||||
run_gc(info);
|
||||
|
||||
for (size_t j = 0; j < num_small_ints; j++) {
|
||||
std::array<uint64_t, small_int_size> words;
|
||||
int sign;
|
||||
size_t word_count = words.size();
|
||||
NODE_API_CALL(env, napi_get_value_bigint_words(env, small_ints[j], &sign,
|
||||
&word_count, words.data()));
|
||||
printf("%d, %zu\n", sign, word_count);
|
||||
NODE_API_ASSERT(env, sign == 0 && word_count == words.size());
|
||||
NODE_API_ASSERT(env,
|
||||
std::all_of(words.begin(), words.end(),
|
||||
[j](const uint64_t &w) { return w == j + 1; }));
|
||||
}
|
||||
|
||||
delete[] small_ints;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_value test_napi_delete_property(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
// info[0] is a function to run the GC
|
||||
napi_value object = info[1];
|
||||
napi_valuetype type = get_typeof(env, object);
|
||||
NODE_API_ASSERT(env, type == napi_object);
|
||||
|
||||
napi_value key = Napi::String::New(env, "foo");
|
||||
|
||||
napi_value non_configurable_key = Napi::String::New(env, "bar");
|
||||
|
||||
napi_value val;
|
||||
NODE_API_CALL(env, napi_create_int32(env, 42, &val));
|
||||
|
||||
bool delete_result;
|
||||
NODE_API_CALL(env, napi_delete_property(env, object, non_configurable_key,
|
||||
&delete_result));
|
||||
NODE_API_ASSERT(env, delete_result == false);
|
||||
|
||||
NODE_API_CALL(env, napi_delete_property(env, object, key, &delete_result));
|
||||
NODE_API_ASSERT(env, delete_result == true);
|
||||
|
||||
bool has_property;
|
||||
NODE_API_CALL(env, napi_has_property(env, object, key, &has_property));
|
||||
NODE_API_ASSERT(env, has_property == false);
|
||||
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
// Returns false if any napi function failed
|
||||
static bool store_escaped_handle(napi_env env, napi_value *out,
|
||||
const char *str) {
|
||||
// Allocate these values on the heap so they cannot be seen by stack scanning
|
||||
// after this function returns. An earlier version tried putting them on the
|
||||
// stack and using volatile stores to set them to nullptr, but that wasn't
|
||||
// effective when the NAPI module was built in release mode as extra copies of
|
||||
// the pointers would still be left in uninitialized stack memory.
|
||||
napi_escapable_handle_scope *ehs = new napi_escapable_handle_scope;
|
||||
napi_value *s = new napi_value;
|
||||
napi_value *escaped = new napi_value;
|
||||
NODE_API_CALL_CUSTOM_RETURN(env, false,
|
||||
napi_open_escapable_handle_scope(env, ehs));
|
||||
NODE_API_CALL_CUSTOM_RETURN(
|
||||
env, false, napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, s));
|
||||
NODE_API_CALL_CUSTOM_RETURN(env, false,
|
||||
napi_escape_handle(env, *ehs, *s, escaped));
|
||||
// can't call a second time
|
||||
NODE_API_ASSERT_CUSTOM_RETURN(env, false,
|
||||
napi_escape_handle(env, *ehs, *s, escaped) ==
|
||||
napi_escape_called_twice);
|
||||
NODE_API_CALL_CUSTOM_RETURN(env, false,
|
||||
napi_close_escapable_handle_scope(env, *ehs));
|
||||
*out = *escaped;
|
||||
|
||||
delete escaped;
|
||||
delete s;
|
||||
delete ehs;
|
||||
return true;
|
||||
}
|
||||
|
||||
static napi_value
|
||||
test_napi_escapable_handle_scope(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
// allocate space for a napi_value on the heap
|
||||
// use store_escaped_handle to put the value into it
|
||||
// trigger GC
|
||||
// the napi_value should still be valid even though it can't be found on the
|
||||
// stack, because it escaped into the current handle scope
|
||||
|
||||
constexpr const char *str = "this is a long string meow meow meow";
|
||||
|
||||
napi_value *hidden = new napi_value;
|
||||
NODE_API_ASSERT(env, store_escaped_handle(env, hidden, str));
|
||||
|
||||
run_gc(info);
|
||||
|
||||
char buf[64];
|
||||
size_t len;
|
||||
NODE_API_CALL(
|
||||
env, napi_get_value_string_utf8(env, *hidden, buf, sizeof(buf), &len));
|
||||
NODE_API_ASSERT(env, len == strlen(str));
|
||||
NODE_API_ASSERT(env, strcmp(buf, str) == 0);
|
||||
|
||||
delete hidden;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_value
|
||||
test_napi_handle_scope_nesting(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
constexpr const char *str = "this is a long string meow meow meow";
|
||||
|
||||
// Create an outer handle scope, hidden on the heap (the one created in
|
||||
// NAPIFunction::call is still on the stack
|
||||
napi_handle_scope *outer_hs = new napi_handle_scope;
|
||||
NODE_API_CALL(env, napi_open_handle_scope(env, outer_hs));
|
||||
|
||||
// Make a handle in the outer scope, on the heap so stack scanning can't see
|
||||
// it
|
||||
napi_value *outer_scope_handle = new napi_value;
|
||||
NODE_API_CALL(env, napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH,
|
||||
outer_scope_handle));
|
||||
|
||||
// Make a new handle scope on the heap so that the outer handle scope isn't
|
||||
// active anymore
|
||||
napi_handle_scope *inner_hs = new napi_handle_scope;
|
||||
NODE_API_CALL(env, napi_open_handle_scope(env, inner_hs));
|
||||
|
||||
// Force GC
|
||||
run_gc(info);
|
||||
|
||||
// Try to read our first handle. Did the outer handle scope get
|
||||
// collected now that it's not on the global object? The inner handle scope
|
||||
// should be keeping it alive even though it's not on the stack.
|
||||
char buf[64];
|
||||
size_t len;
|
||||
NODE_API_CALL(env, napi_get_value_string_utf8(env, *outer_scope_handle, buf,
|
||||
sizeof(buf), &len));
|
||||
NODE_API_ASSERT(env, len == strlen(str));
|
||||
NODE_API_ASSERT(env, strcmp(buf, str) == 0);
|
||||
|
||||
// Clean up
|
||||
NODE_API_CALL(env, napi_close_handle_scope(env, *inner_hs));
|
||||
delete inner_hs;
|
||||
NODE_API_CALL(env, napi_close_handle_scope(env, *outer_hs));
|
||||
delete outer_hs;
|
||||
delete outer_scope_handle;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
// call this with a bunch (>10) of string arguments representing increasing
|
||||
// decimal numbers. ensures that the runtime does not let these arguments be
|
||||
// freed.
|
||||
//
|
||||
// test_napi_handle_scope_many_args(() => gc(), '1', '2', '3', ...)
|
||||
static napi_value
|
||||
test_napi_handle_scope_many_args(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
run_gc(info);
|
||||
// now if bun is broken a bunch of our args are dead, because node-addon-api
|
||||
// uses a heap array for >6 args
|
||||
for (size_t i = 1; i < info.Length(); i++) {
|
||||
Napi::String s = info[i].As<Napi::String>();
|
||||
NODE_API_ASSERT(env, s.Utf8Value() == std::to_string(i));
|
||||
}
|
||||
return env.Undefined();
|
||||
}
|
||||
|
||||
static napi_value test_napi_ref(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
|
||||
napi_value object;
|
||||
NODE_API_CALL(env, napi_create_object(env, &object));
|
||||
|
||||
napi_ref ref;
|
||||
NODE_API_CALL(env, napi_create_reference(env, object, 0, &ref));
|
||||
|
||||
napi_value from_ref;
|
||||
NODE_API_CALL(env, napi_get_reference_value(env, ref, &from_ref));
|
||||
NODE_API_ASSERT(env, from_ref != nullptr);
|
||||
napi_valuetype typeof_result = get_typeof(env, from_ref);
|
||||
NODE_API_ASSERT(env, typeof_result == napi_object);
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
static napi_value test_napi_run_script(const Napi::CallbackInfo &info) {
|
||||
napi_value ret = nullptr;
|
||||
// info[0] is the GC callback
|
||||
(void)napi_run_script(info.Env(), info[1], &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void register_standalone_tests(Napi::Env env, Napi::Object exports) {
|
||||
REGISTER_FUNCTION(env, exports, test_issue_7685);
|
||||
REGISTER_FUNCTION(env, exports, test_issue_11949);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_get_value_string_utf8_with_buffer);
|
||||
REGISTER_FUNCTION(env, exports,
|
||||
test_napi_threadsafe_function_does_not_hang_after_finalize);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_handle_scope_string);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_handle_scope_bigint);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_delete_property);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_escapable_handle_scope);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_handle_scope_nesting);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_handle_scope_many_args);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_ref);
|
||||
REGISTER_FUNCTION(env, exports, test_napi_run_script);
|
||||
}
|
||||
|
||||
} // namespace napitests
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Functions that are run as the entire test by napi.test.ts
|
||||
|
||||
#include "napi_with_version.h"
|
||||
|
||||
namespace napitests {
|
||||
|
||||
void register_standalone_tests(Napi::Env env, Napi::Object exports);
|
||||
|
||||
} // namespace napitests
|
||||
@@ -8,7 +8,6 @@ namespace napitests {
|
||||
static napi_ref ref_to_wrapped_object = nullptr;
|
||||
static bool wrap_finalize_called = false;
|
||||
|
||||
// TODO: this needs https://github.com/oven-sh/bun/pulls/14501 to work
|
||||
// static void delete_the_ref(napi_env env, void *_data, void *_hint) {
|
||||
// printf("delete_the_ref\n");
|
||||
// // not using NODE_API_ASSERT as this runs in a finalizer where allocating
|
||||
@@ -67,7 +66,7 @@ static napi_value create_wrap(const Napi::CallbackInfo &info) {
|
||||
return js_object;
|
||||
}
|
||||
|
||||
// get_wrap_data(js_object: object): number|undefined
|
||||
// get_wrap_data(js_object: object): number
|
||||
static napi_value get_wrap_data(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value js_object = info[0];
|
||||
@@ -87,7 +86,7 @@ static napi_value get_wrap_data(const Napi::CallbackInfo &info) {
|
||||
return js_number;
|
||||
}
|
||||
|
||||
// get_object_from_ref(): object|undefined
|
||||
// get_object_from_ref(): object
|
||||
static napi_value get_object_from_ref(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ describe("napi", () => {
|
||||
describe("issue_11949", () => {
|
||||
it("napi_call_threadsafe_function should accept null", () => {
|
||||
const result = checkSameOutput("test_issue_11949", []);
|
||||
expect(result).toStartWith("data = 1234, context = 42");
|
||||
expect(result).toStartWith("data: nullptr");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -263,18 +263,18 @@ describe("napi", () => {
|
||||
|
||||
describe("napi_run_script", () => {
|
||||
it("evaluates a basic expression", () => {
|
||||
checkSameOutput("test_napi_run_script", ["5 * (1 + 2)"]);
|
||||
checkSameOutput("eval_wrapper", ["5 * (1 + 2)"]);
|
||||
});
|
||||
it("provides the right this value", () => {
|
||||
checkSameOutput("test_napi_run_script", ["this === global"]);
|
||||
checkSameOutput("eval_wrapper", ["this === global"]);
|
||||
});
|
||||
it("propagates exceptions", () => {
|
||||
checkSameOutput("test_napi_run_script", ["(()=>{ throw new TypeError('oops'); })()"]);
|
||||
checkSameOutput("eval_wrapper", ["(()=>{ throw new TypeError('oops'); })()"]);
|
||||
});
|
||||
it("cannot see locals from around its invocation", () => {
|
||||
// variable should_not_exist is declared on main.js:18, but it should not be in scope for the eval'd code
|
||||
// this doesn't use checkSameOutput because V8 and JSC use different error messages for a missing variable
|
||||
let bunResult = runOn(bunExe(), "test_napi_run_script", ["shouldNotExist"]);
|
||||
let bunResult = runOn(bunExe(), "eval_wrapper", ["shouldNotExist"]);
|
||||
// remove all debug logs
|
||||
bunResult = bunResult.replaceAll(/^\[\w+\].+$/gm, "").trim();
|
||||
expect(bunResult).toBe(
|
||||
|
||||
Reference in New Issue
Block a user