Files
bun.sh/test/napi/napi-app/leak_tests.cpp
2024-11-01 19:09:49 -07:00

162 lines
5.3 KiB
C++

#include "leak_tests.h"
#include "utils.h"
#include <cassert>
#include <vector>
namespace napitests {
static std::vector<Napi::Reference<Napi::Value>> global_weak_refs;
// add a weak reference to a global array
// this will cause extra memory usage for the ref, but it should not retain the
// JS object being referenced
Napi::Value add_weak_refs(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
for (int i = 0; i < 50; i++) {
global_weak_refs.emplace_back(
Napi::Reference<Napi::Value>::New(info[0], 0));
}
return env.Undefined();
}
// delete all the weak refs created by add_weak_ref
Napi::Value clear_weak_refs(const Napi::CallbackInfo &info) {
global_weak_refs.clear();
return info.Env().Undefined();
}
// create a strong reference to a JS value, and then delete it
Napi::Value create_and_delete_strong_ref(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
// strong reference
auto ref = Napi::Reference<Napi::Value>::New(info[0], 2);
// destructor will be called
return env.Undefined();
}
class WrappedObject {
public:
static napi_value factory(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
napi_value s = info[0];
bool supports_node_api_post_finalize = info[1].As<Napi::Boolean>();
size_t len = 0;
NODE_API_CALL(env, napi_get_value_string_utf8(env, s, nullptr, 0, &len));
char *string = new char[len + 1];
string[len] = 0;
NODE_API_CALL(env,
napi_get_value_string_utf8(env, s, string, len + 1, nullptr));
napi_value js_object;
NODE_API_CALL(env, napi_create_object(env, &js_object));
WrappedObject *native_object =
new WrappedObject(string, supports_node_api_post_finalize);
NODE_API_CALL(env, napi_wrap(env, js_object, native_object, basic_finalize,
nullptr, &native_object->m_ref));
napi_property_descriptor property = {
.utf8name = "get",
.name = nullptr,
.method = get,
.getter = nullptr,
.setter = nullptr,
.value = nullptr,
.attributes = napi_default_method,
.data = nullptr,
};
NODE_API_CALL(env, napi_define_properties(env, js_object, 1, &property));
return js_object;
}
static napi_value get(napi_env env, napi_callback_info info) {
napi_value js_this;
NODE_API_CALL(
env, napi_get_cb_info(env, info, nullptr, nullptr, &js_this, nullptr));
WrappedObject *native_object;
NODE_API_CALL(env, napi_unwrap(env, js_this,
reinterpret_cast<void **>(&native_object)));
return Napi::String::New(env, native_object->m_string);
}
private:
static constexpr size_t big_alloc_size = 5'000'000;
WrappedObject(char *string, bool supports_node_api_post_finalize)
: m_string(string), m_big_alloc(new char[big_alloc_size]),
m_supports_node_api_post_finalize(supports_node_api_post_finalize) {
memset(m_big_alloc, big_alloc_size, 'x');
}
~WrappedObject() {
delete[] m_string;
delete[] m_big_alloc;
}
static void delete_ref(napi_env env, void *data, void *hint) {
napi_delete_reference(env, reinterpret_cast<napi_ref>(data));
}
static void basic_finalize(node_api_basic_env env, void *data, void *hint) {
auto *native_object = reinterpret_cast<WrappedObject *>(data);
if (native_object->m_supports_node_api_post_finalize) {
node_api_post_finalizer(env, delete_ref,
reinterpret_cast<void *>(native_object->m_ref),
nullptr);
} else {
napi_delete_reference(env, native_object->m_ref);
}
delete native_object;
}
char *m_string;
char *m_big_alloc;
napi_ref m_ref = nullptr;
bool m_supports_node_api_post_finalize;
};
class ExternalObject {
public:
static napi_value factory(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
std::string s = info[0].As<Napi::String>();
auto *native_object = new ExternalObject(std::move(s));
napi_value js_external;
NODE_API_CALL(env, napi_create_external(env, native_object, basic_finalize,
nullptr, &js_external));
return js_external;
}
static napi_value get(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
napi_value v = info[0];
ExternalObject *native_object;
NODE_API_CALL(env, napi_get_value_external(
env, v, reinterpret_cast<void **>(&native_object)));
return Napi::String::New(env, native_object->m_string);
}
private:
ExternalObject(std::string &&string) : m_string(string) {}
static void basic_finalize(node_api_basic_env env, void *data, void *hint) {
auto *native_object = reinterpret_cast<ExternalObject *>(data);
delete native_object;
}
std::string m_string;
};
void register_leak_tests(Napi::Env env, Napi::Object exports) {
REGISTER_FUNCTION(env, exports, add_weak_refs);
REGISTER_FUNCTION(env, exports, clear_weak_refs);
REGISTER_FUNCTION(env, exports, create_and_delete_strong_ref);
exports.Set("wrapped_object_factory",
Napi::Function::New(env, WrappedObject::factory));
exports.Set("external_factory",
Napi::Function::New(env, ExternalObject::factory));
exports.Set("external_get", Napi::Function::New(env, ExternalObject::get));
}
} // namespace napitests