mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Implement napi_handle_scope and napi_escapable_handle_scope (#13756)
This commit is contained in:
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "AsyncContextFrame.h"
|
||||
|
||||
#include "napi_handle_scope.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
@@ -406,6 +408,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
|
||||
return JSC::JSValue::encode(JSC::JSValue {});
|
||||
}
|
||||
|
||||
NapiHandleScope handleScope(globalObject);
|
||||
|
||||
EncodedJSValue exportsValue = JSC::JSValue::encode(exports);
|
||||
JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(globalObject, exportsValue));
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
#include "libusockets.h"
|
||||
#include "ModuleLoader.h"
|
||||
#include "napi_external.h"
|
||||
#include "napi_handle_scope.h"
|
||||
#include "napi.h"
|
||||
#include "NodeHTTP.h"
|
||||
#include "NodeVM.h"
|
||||
@@ -2901,6 +2902,10 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
Bun::NapiPrototype::createStructure(init.vm, init.owner, init.owner->objectPrototype()));
|
||||
});
|
||||
|
||||
m_NapiHandleScopeImplStructure.initLater([](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
init.set(Bun::NapiHandleScopeImpl::createStructure(init.vm, init.owner));
|
||||
});
|
||||
|
||||
m_cachedNodeVMGlobalObjectStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
init.set(WebCore::createNodeVMGlobalObjectStructure(init.vm));
|
||||
@@ -3582,6 +3587,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
visitor.append(thisObject->m_pendingNapiModuleAndExports[0]);
|
||||
visitor.append(thisObject->m_pendingNapiModuleAndExports[1]);
|
||||
|
||||
visitor.append(thisObject->m_currentNapiHandleScopeImpl);
|
||||
|
||||
thisObject->m_asyncBoundFunctionStructure.visit(visitor);
|
||||
thisObject->m_bunObject.visit(visitor);
|
||||
thisObject->m_cachedNodeVMGlobalObjectStructure.visit(visitor);
|
||||
@@ -3620,6 +3627,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
thisObject->m_NapiExternalStructure.visit(visitor);
|
||||
thisObject->m_NAPIFunctionStructure.visit(visitor);
|
||||
thisObject->m_NapiPrototypeStructure.visit(visitor);
|
||||
thisObject->m_NapiHandleScopeImplStructure.visit(visitor);
|
||||
thisObject->m_nativeMicrotaskTrampoline.visit(visitor);
|
||||
thisObject->m_navigatorObject.visit(visitor);
|
||||
thisObject->m_NodeVMScriptClassStructure.visit(visitor);
|
||||
|
||||
@@ -29,6 +29,7 @@ class Performance;
|
||||
|
||||
namespace Bun {
|
||||
class InternalModuleRegistry;
|
||||
class NapiHandleScopeImpl;
|
||||
} // namespace Bun
|
||||
|
||||
namespace v8 {
|
||||
@@ -283,6 +284,7 @@ public:
|
||||
Structure* NapiExternalStructure() const { return m_NapiExternalStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NapiPrototypeStructure() const { return m_NapiPrototypeStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NAPIFunctionStructure() const { return m_NAPIFunctionStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NapiHandleScopeImplStructure() const { return m_NapiHandleScopeImplStructure.getInitializedOnMainThread(this); }
|
||||
|
||||
Structure* JSSQLStatementStructure() const { return m_JSSQLStatementStructure.getInitializedOnMainThread(this); }
|
||||
|
||||
@@ -405,6 +407,11 @@ public:
|
||||
// When a napi module initializes on dlopen, we need to know what the value is
|
||||
mutable JSC::WriteBarrier<Unknown> m_pendingNapiModuleAndExports[2];
|
||||
|
||||
// The handle scope where all new NAPI values will be created. You must not pass any napi_values
|
||||
// back to a NAPI function without putting them in the handle scope, as the NAPI function may
|
||||
// move them off the stack which will cause them to get collected if not in the handle scope.
|
||||
JSC::WriteBarrier<Bun::NapiHandleScopeImpl> m_currentNapiHandleScopeImpl;
|
||||
|
||||
// The original, unmodified Error.prepareStackTrace.
|
||||
//
|
||||
// We set a default value for this to mimick Node.js behavior It is a
|
||||
@@ -564,6 +571,8 @@ public:
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiExternalStructure;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiPrototypeStructure;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NAPIFunctionStructure;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiHandleScopeImplStructure;
|
||||
|
||||
LazyProperty<JSGlobalObject, Structure> m_JSSQLStatementStructure;
|
||||
LazyProperty<JSGlobalObject, v8::GlobalInternals> m_V8GlobalInternals;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "JavaScriptCore/JSGlobalObject.h"
|
||||
#include "JavaScriptCore/SourceCode.h"
|
||||
#include "js_native_api_types.h"
|
||||
#include "v8/V8HandleScope.h"
|
||||
#include "napi_handle_scope.h"
|
||||
|
||||
#include "helpers.h"
|
||||
#include <JavaScriptCore/JSObjectInlines.h>
|
||||
@@ -148,7 +148,7 @@ void NapiFinalizer::call(JSC::JSGlobalObject* globalObject, void* data)
|
||||
{
|
||||
if (this->finalize_cb) {
|
||||
NAPI_PREMABLE
|
||||
this->finalize_cb(reinterpret_cast<napi_env>(globalObject), data, this->finalize_hint);
|
||||
this->finalize_cb(toNapi(globalObject), data, this->finalize_hint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,10 +375,10 @@ public:
|
||||
// and receives the actual count of args.
|
||||
napi_value* argv, // [out] Array of values
|
||||
napi_value* this_arg, // [out] Receives the JS 'this' arg for the call
|
||||
void** data)
|
||||
void** data, Zig::GlobalObject* globalObject)
|
||||
{
|
||||
if (this_arg != nullptr) {
|
||||
*this_arg = toNapi(callframe.thisValue());
|
||||
*this_arg = toNapi(callframe.thisValue(), globalObject);
|
||||
}
|
||||
|
||||
if (data != nullptr) {
|
||||
@@ -404,7 +404,7 @@ public:
|
||||
|
||||
if (overflow > 0) {
|
||||
while (overflow--) {
|
||||
*argv = toNapi(jsUndefined());
|
||||
*argv = toNapi(jsUndefined(), globalObject);
|
||||
argv++;
|
||||
}
|
||||
}
|
||||
@@ -442,7 +442,7 @@ public:
|
||||
NAPICallFrame frame(JSC::ArgList(args), function->m_dataPtr);
|
||||
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
v8::HandleScope handleScope(v8::Isolate::fromGlobalObject(static_cast<Zig::GlobalObject*>(globalObject)));
|
||||
Bun::NapiHandleScope handleScope(jsCast<Zig::GlobalObject*>(globalObject));
|
||||
|
||||
auto result = callback(env, NAPICallFrame::toNapiCallbackInfo(frame));
|
||||
|
||||
@@ -701,7 +701,7 @@ extern "C" napi_status napi_get_property(napi_env env, napi_value object,
|
||||
auto keyProp = toJS(key);
|
||||
JSC::EnsureStillAliveScope ensureAlive2(keyProp);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
*result = toNapi(target->getIfPropertyExists(globalObject, keyProp.toPropertyKey(globalObject)));
|
||||
*result = toNapi(target->getIfPropertyExists(globalObject, keyProp.toPropertyKey(globalObject)), globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
scope.clearException();
|
||||
@@ -727,7 +727,7 @@ extern "C" napi_status napi_delete_property(napi_env env, napi_value object,
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
if (LIKELY(result)) {
|
||||
*result = toNapi(deleteResult);
|
||||
*result = deleteResult;
|
||||
}
|
||||
|
||||
scope.clearException();
|
||||
@@ -752,7 +752,7 @@ extern "C" napi_status napi_has_own_property(napi_env env, napi_value object,
|
||||
|
||||
auto keyProp = toJS(key);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
*result = toNapi(target->hasOwnProperty(globalObject, JSC::PropertyName(keyProp.toPropertyKey(globalObject))));
|
||||
*result = target->hasOwnProperty(globalObject, JSC::PropertyName(keyProp.toPropertyKey(globalObject)));
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
scope.clearException();
|
||||
@@ -799,7 +799,7 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env,
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSC::JSGlobalObject* globalObject = toJS(env);
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
if (UNLIKELY(!globalObject || !result)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -822,7 +822,7 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env,
|
||||
if (LIKELY(data && jsArrayBuffer->impl())) {
|
||||
*data = jsArrayBuffer->impl()->data();
|
||||
}
|
||||
*result = toNapi(jsArrayBuffer);
|
||||
*result = toNapi(jsArrayBuffer, globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -886,7 +886,7 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object,
|
||||
PROPERTY_NAME_FROM_UTF8(name);
|
||||
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
*result = toNapi(target->getIfPropertyExists(globalObject, name));
|
||||
*result = toNapi(target->getIfPropertyExists(globalObject, name), globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
scope.clearException();
|
||||
@@ -916,14 +916,14 @@ node_api_create_external_string_latin1(napi_env env,
|
||||
#if NAPI_VERBOSE
|
||||
printf("[napi] string finalize_callback\n");
|
||||
#endif
|
||||
finalize_callback(reinterpret_cast<napi_env>(defaultGlobalObject()), nullptr, hint);
|
||||
finalize_callback(toNapi(defaultGlobalObject()), nullptr, hint);
|
||||
}
|
||||
});
|
||||
JSGlobalObject* globalObject = defaultGlobalObject(env);
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl));
|
||||
ensureStillAliveHere(out);
|
||||
*result = toNapi(out);
|
||||
*result = toNapi(out, globalObject);
|
||||
ensureStillAliveHere(out);
|
||||
|
||||
return napi_ok;
|
||||
@@ -954,14 +954,14 @@ node_api_create_external_string_utf16(napi_env env,
|
||||
#endif
|
||||
|
||||
if (finalize_callback) {
|
||||
finalize_callback(reinterpret_cast<napi_env>(defaultGlobalObject()), nullptr, hint);
|
||||
finalize_callback(toNapi(defaultGlobalObject()), nullptr, hint);
|
||||
}
|
||||
});
|
||||
JSGlobalObject* globalObject = defaultGlobalObject(env);
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl));
|
||||
ensureStillAliveHere(out);
|
||||
*result = toNapi(out);
|
||||
*result = toNapi(out, globalObject);
|
||||
ensureStillAliveHere(out);
|
||||
|
||||
return napi_ok;
|
||||
@@ -997,7 +997,8 @@ extern "C" void napi_module_register(napi_module* mod)
|
||||
|
||||
JSC::Strong<JSC::JSObject> strongObject = { vm, object };
|
||||
|
||||
JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object)));
|
||||
Bun::NapiHandleScope handleScope(globalObject);
|
||||
JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object, globalObject)));
|
||||
|
||||
RETURN_IF_EXCEPTION(scope, void());
|
||||
|
||||
@@ -1170,7 +1171,7 @@ extern "C" napi_status napi_create_function(napi_env env, const char* utf8name,
|
||||
auto* function = NAPIFunction::create(vm, globalObject, length, name, method, data);
|
||||
|
||||
ASSERT(function->isCallable());
|
||||
*result = toNapi(JSC::JSValue(function));
|
||||
*result = toNapi(JSC::JSValue(function), globalObject);
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -1187,9 +1188,10 @@ extern "C" napi_status napi_get_cb_info(
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSC::CallFrame* callFrame = reinterpret_cast<JSC::CallFrame*>(cbinfo);
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) {
|
||||
NAPICallFrame::extract(*frame, argc, argv, this_arg, data);
|
||||
NAPICallFrame::extract(*frame, argc, argv, this_arg, data, globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1204,14 +1206,14 @@ extern "C" napi_status napi_get_cb_info(
|
||||
memcpy(argv, callFrame->addressOfArgumentsStart(), argsToCopy * sizeof(JSC::JSValue));
|
||||
|
||||
for (size_t i = outputArgsCount; i < inputArgsCount; i++) {
|
||||
argv[i] = toNapi(JSC::jsUndefined());
|
||||
argv[i] = toNapi(JSC::jsUndefined(), globalObject);
|
||||
}
|
||||
}
|
||||
|
||||
JSC::JSValue thisValue = callFrame->thisValue();
|
||||
|
||||
if (this_arg != nullptr) {
|
||||
*this_arg = toNapi(thisValue);
|
||||
*this_arg = toNapi(thisValue, globalObject);
|
||||
}
|
||||
|
||||
if (data != nullptr) {
|
||||
@@ -1456,7 +1458,7 @@ extern "C" napi_status napi_get_reference_value(napi_env env, napi_ref ref,
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
*result = toNapi(napiRef->value());
|
||||
*result = toNapi(napiRef->value(), toJS(env));
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -1582,7 +1584,7 @@ extern "C" napi_status napi_get_and_clear_last_exception(napi_env env,
|
||||
}
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
*result = toNapi(JSC::JSValue(globalObject->vm().lastException()));
|
||||
*result = toNapi(JSC::JSValue(globalObject->vm().lastException()), globalObject);
|
||||
globalObject->vm().clearLastException();
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -1632,7 +1634,7 @@ extern "C" napi_status node_api_symbol_for(napi_env env,
|
||||
}
|
||||
|
||||
auto description = WTF::String::fromUTF8({ utf8description, length == NAPI_AUTO_LENGTH ? strlen(utf8description) : length });
|
||||
*result = toNapi(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(description)));
|
||||
*result = toNapi(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(description)), globalObject);
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -1655,7 +1657,7 @@ extern "C" napi_status node_api_create_syntax_error(napi_env env,
|
||||
return napi_generic_failure;
|
||||
}
|
||||
|
||||
*result = toNapi(err);
|
||||
*result = toNapi(err, toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1707,7 +1709,7 @@ extern "C" napi_status napi_create_type_error(napi_env env, napi_value code,
|
||||
return napi_generic_failure;
|
||||
}
|
||||
|
||||
*result = toNapi(err);
|
||||
*result = toNapi(err, toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1733,7 +1735,7 @@ extern "C" napi_status napi_create_error(napi_env env, napi_value code,
|
||||
return napi_generic_failure;
|
||||
}
|
||||
|
||||
*result = toNapi(err);
|
||||
*result = toNapi(err, toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
extern "C" napi_status napi_throw_range_error(napi_env env, const char* code,
|
||||
@@ -1760,8 +1762,7 @@ extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value)
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::EncodedJSValue encodedValue = reinterpret_cast<JSC::EncodedJSValue>(object_value);
|
||||
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
||||
JSC::JSValue value = toJS(object_value);
|
||||
if (!value.isObject()) {
|
||||
return NAPI_OBJECT_EXPECTED;
|
||||
}
|
||||
@@ -1780,8 +1781,7 @@ extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value)
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::EncodedJSValue encodedValue = reinterpret_cast<JSC::EncodedJSValue>(object_value);
|
||||
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
||||
JSC::JSValue value = toJS(object_value);
|
||||
|
||||
if (UNLIKELY(!value.isObject())) {
|
||||
return NAPI_OBJECT_EXPECTED;
|
||||
@@ -1804,7 +1804,7 @@ extern "C" napi_status napi_get_global(napi_env env, napi_value* result)
|
||||
}
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
*result = reinterpret_cast<napi_value>(globalObject->globalThis());
|
||||
*result = toNapi(globalObject->globalThis(), globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1829,7 +1829,7 @@ extern "C" napi_status napi_create_range_error(napi_env env, napi_value code,
|
||||
if (UNLIKELY(!err)) {
|
||||
return napi_generic_failure;
|
||||
}
|
||||
*result = toNapi(err);
|
||||
*result = toNapi(err, toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1848,12 +1848,12 @@ extern "C" napi_status napi_get_new_target(napi_env env,
|
||||
CallFrame* callFrame = reinterpret_cast<JSC::CallFrame*>(cbinfo);
|
||||
|
||||
if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) {
|
||||
*result = toNapi(frame->newTarget);
|
||||
*result = toNapi(frame->newTarget, toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
JSC::JSValue newTarget = callFrame->newTarget();
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(newTarget));
|
||||
*result = toNapi(newTarget, toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1872,14 +1872,14 @@ extern "C" napi_status napi_create_dataview(napi_env env, size_t length,
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::EncodedJSValue encodedArraybuffer = reinterpret_cast<JSC::EncodedJSValue>(arraybuffer);
|
||||
auto arraybufferValue = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(JSC::JSValue::decode(encodedArraybuffer));
|
||||
if (!arraybufferValue) {
|
||||
JSC::JSValue arraybufferValue = toJS(arraybuffer);
|
||||
auto arraybufferPtr = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(arraybufferValue);
|
||||
if (!arraybufferPtr) {
|
||||
return napi_arraybuffer_expected;
|
||||
}
|
||||
auto dataView = JSC::DataView::create(arraybufferValue->impl(), byte_offset, length);
|
||||
auto dataView = JSC::DataView::create(arraybufferPtr->impl(), byte_offset, length);
|
||||
|
||||
*result = reinterpret_cast<napi_value>(dataView->wrap(globalObject, globalObject));
|
||||
*result = toNapi(dataView->wrap(globalObject, globalObject), globalObject);
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -1932,6 +1932,7 @@ JSC_DEFINE_HOST_FUNCTION(NapiClass_ConstructorFunction,
|
||||
});
|
||||
NAPICallFrame frame(JSC::ArgList(args), nullptr);
|
||||
frame.newTarget = newTarget;
|
||||
Bun::NapiHandleScope handleScope(jsCast<Zig::GlobalObject*>(globalObject));
|
||||
|
||||
napi->constructor()(globalObject, reinterpret_cast<JSC::CallFrame*>(NAPICallFrame::toNapiCallbackInfo(frame)));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
@@ -2019,7 +2020,7 @@ extern "C" napi_status napi_get_all_property_names(
|
||||
|
||||
JSC::JSArray* exportKeys = ownPropertyKeys(globalObject, object, jsc_property_mode, jsc_key_mode);
|
||||
// TODO: filter
|
||||
*result = toNapi(JSC::JSValue::encode(exportKeys));
|
||||
*result = toNapi(JSC::JSValue(exportKeys), globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2091,7 +2092,7 @@ extern "C" napi_status napi_define_class(napi_env env,
|
||||
napiClass->dataPtr = data;
|
||||
}
|
||||
|
||||
*result = toNapi(value);
|
||||
*result = toNapi(value, globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2113,10 +2114,10 @@ extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value,
|
||||
// .toString() can throw
|
||||
JSC::JSValue resultValue = JSC::JSValue(jsValue.toString(globalObject));
|
||||
JSC::EnsureStillAliveScope ensureStillAlive1(resultValue);
|
||||
*result = toNapi(resultValue);
|
||||
*result = toNapi(resultValue, globalObject);
|
||||
|
||||
if (UNLIKELY(scope.exception())) {
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(JSC::jsUndefined()));
|
||||
*result = toNapi(JSC::jsUndefined(), globalObject);
|
||||
return napi_generic_failure;
|
||||
}
|
||||
scope.clearException();
|
||||
@@ -2144,13 +2145,13 @@ extern "C" napi_status napi_get_property_names(napi_env env, napi_value object,
|
||||
JSC::EnsureStillAliveScope ensureStillAlive(jsValue);
|
||||
JSC::JSValue value = JSC::ownPropertyKeys(globalObject, jsValue.getObject(), PropertyNameMode::Strings, DontEnumPropertiesMode::Include);
|
||||
if (UNLIKELY(scope.exception())) {
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(JSC::jsUndefined()));
|
||||
*result = toNapi(JSC::jsUndefined(), globalObject);
|
||||
return napi_generic_failure;
|
||||
}
|
||||
scope.clearException();
|
||||
JSC::EnsureStillAliveScope ensureStillAlive1(value);
|
||||
|
||||
*result = toNapi(value);
|
||||
*result = toNapi(value, globalObject);
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -2180,7 +2181,7 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
|
||||
|
||||
auto* buffer = JSC::JSUint8Array::create(globalObject, subclassStructure, WTFMove(arrayBuffer), 0, length);
|
||||
|
||||
*result = toNapi(buffer);
|
||||
*result = toNapi(buffer, globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2207,7 +2208,7 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte
|
||||
|
||||
auto* buffer = JSC::JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(ArrayBufferSharingMode::Shared), WTFMove(arrayBuffer));
|
||||
|
||||
*result = toNapi(buffer);
|
||||
*result = toNapi(buffer, globalObject);
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -2220,7 +2221,7 @@ extern "C" napi_status napi_create_double(napi_env env, double value,
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
|
||||
*result = toNapi(jsDoubleNumber(value));
|
||||
*result = toNapi(jsDoubleNumber(value), toJS(env));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2336,7 +2337,7 @@ extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue,
|
||||
JSValue element = object->getIndex(toJS(env), index);
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
*result = toNapi(element);
|
||||
*result = toNapi(element, toJS(env));
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -2375,7 +2376,7 @@ extern "C" napi_status napi_create_object(napi_env env, napi_value* result)
|
||||
|
||||
JSValue value = JSValue(NapiPrototype::create(vm, globalObject->NapiPrototypeStructure()));
|
||||
|
||||
*result = toNapi(value);
|
||||
*result = toNapi(value, globalObject);
|
||||
JSC::EnsureStillAliveScope ensureStillAlive(value);
|
||||
|
||||
return napi_ok;
|
||||
@@ -2396,7 +2397,7 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
|
||||
auto* structure = globalObject->NapiExternalStructure();
|
||||
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, reinterpret_cast<void*>(finalize_cb));
|
||||
JSC::EnsureStillAliveScope ensureStillAlive(value);
|
||||
*result = toNapi(value);
|
||||
*result = toNapi(value, globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2571,7 +2572,7 @@ extern "C" napi_status napi_run_script(napi_env env, napi_value script,
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSC::JSGlobalObject* globalObject = toJS(env);
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -2599,7 +2600,7 @@ extern "C" napi_status napi_run_script(napi_env env, napi_value script,
|
||||
}
|
||||
|
||||
if (result != nullptr) {
|
||||
*result = toNapi(value);
|
||||
*result = toNapi(value, globalObject);
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, napi_ok);
|
||||
@@ -2652,7 +2653,7 @@ extern "C" napi_status napi_create_bigint_words(napi_env env,
|
||||
}
|
||||
}
|
||||
|
||||
*result = toNapi(bigint);
|
||||
*result = toNapi(bigint, globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2680,12 +2681,13 @@ extern "C" napi_status napi_create_symbol(napi_env env, napi_value description,
|
||||
}
|
||||
|
||||
if (descriptionString->length() > 0) {
|
||||
*result = toNapi(JSC::Symbol::createWithDescription(vm, descriptionString->value(globalObject)));
|
||||
*result = toNapi(JSC::Symbol::createWithDescription(vm, descriptionString->value(globalObject)),
|
||||
globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
}
|
||||
|
||||
*result = toNapi(JSC::Symbol::create(vm));
|
||||
*result = toNapi(JSC::Symbol::create(vm), globalObject);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -2721,7 +2723,7 @@ extern "C" napi_status napi_new_instance(napi_env env, napi_value constructor,
|
||||
|
||||
auto value = construct(globalObject, constructorObject, constructData, args);
|
||||
RETURN_IF_EXCEPTION(throwScope, napi_pending_exception);
|
||||
*result = toNapi(value);
|
||||
*result = toNapi(value, globalObject);
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, napi_ok);
|
||||
}
|
||||
@@ -2761,9 +2763,9 @@ extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi,
|
||||
|
||||
if (result_ptr) {
|
||||
if (result.isEmpty()) {
|
||||
*result_ptr = toNapi(JSC::jsUndefined());
|
||||
*result_ptr = toNapi(JSC::jsUndefined(), globalObject);
|
||||
} else {
|
||||
*result_ptr = toNapi(result);
|
||||
*result_ptr = toNapi(result, globalObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace Zig {
|
||||
class GlobalObject;
|
||||
}
|
||||
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/JSFunction.h>
|
||||
#include <JavaScriptCore/VM.h>
|
||||
@@ -14,6 +10,8 @@ class GlobalObject;
|
||||
#include "js_native_api_types.h"
|
||||
#include <JavaScriptCore/JSWeakValue.h>
|
||||
#include "JSFFIFunction.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "napi_handle_scope.h"
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
@@ -38,14 +36,12 @@ static inline Zig::GlobalObject* toJS(napi_env val)
|
||||
return reinterpret_cast<Zig::GlobalObject*>(val);
|
||||
}
|
||||
|
||||
static inline napi_value toNapi(JSC::EncodedJSValue val)
|
||||
static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObject)
|
||||
{
|
||||
return reinterpret_cast<napi_value>(val);
|
||||
}
|
||||
|
||||
static inline napi_value toNapi(JSC::JSValue val)
|
||||
{
|
||||
return toNapi(JSC::JSValue::encode(val));
|
||||
if (val.isCell()) {
|
||||
globalObject->m_currentNapiHandleScopeImpl.get()->append(val);
|
||||
}
|
||||
return reinterpret_cast<napi_value>(JSC::JSValue::encode(val));
|
||||
}
|
||||
|
||||
static inline napi_env toNapi(JSC::JSGlobalObject* val)
|
||||
@@ -61,7 +57,6 @@ public:
|
||||
void call(JSC::JSGlobalObject* globalObject, void* data);
|
||||
};
|
||||
|
||||
|
||||
// This is essentially JSC::JSWeakValue, except with a JSCell* instead of a
|
||||
// JSObject*. Sometimes, a napi embedder might want to store a JSC::Exception, a
|
||||
// JSC::HeapBigInt, JSC::Symbol, etc inside of a NapiRef. So we can't limit it
|
||||
@@ -313,4 +308,4 @@ static inline NapiRef* toJS(napi_ref val)
|
||||
|
||||
Structure* createNAPIFunctionStructure(VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
133
src/bun.js/bindings/napi_handle_scope.cpp
Normal file
133
src/bun.js/bindings/napi_handle_scope.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "napi_handle_scope.h"
|
||||
|
||||
#include "ZigGlobalObject.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// for CREATE_METHOD_TABLE
|
||||
namespace JSCastingHelpers = JSC::JSCastingHelpers;
|
||||
|
||||
const JSC::ClassInfo NapiHandleScopeImpl::s_info = {
|
||||
"NapiHandleScopeImpl"_s,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
CREATE_METHOD_TABLE(NapiHandleScopeImpl)
|
||||
};
|
||||
|
||||
NapiHandleScopeImpl::NapiHandleScopeImpl(JSC::VM& vm, JSC::Structure* structure, NapiHandleScopeImpl* parent, bool escapable)
|
||||
: Base(vm, structure)
|
||||
, m_parent(parent)
|
||||
, m_escapeSlot(nullptr)
|
||||
{
|
||||
if (escapable) {
|
||||
m_escapeSlot = parent->reserveSlot();
|
||||
}
|
||||
}
|
||||
|
||||
NapiHandleScopeImpl* NapiHandleScopeImpl::create(JSC::VM& vm,
|
||||
JSC::Structure* structure,
|
||||
NapiHandleScopeImpl* parent,
|
||||
bool escapable)
|
||||
{
|
||||
NapiHandleScopeImpl* buffer = new (NotNull, JSC::allocateCell<NapiHandleScopeImpl>(vm))
|
||||
NapiHandleScopeImpl(vm, structure, parent, escapable);
|
||||
buffer->finishCreation(vm);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void NapiHandleScopeImpl::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
NapiHandleScopeImpl* thisObject = jsCast<NapiHandleScopeImpl*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
|
||||
WTF::Locker locker { thisObject->cellLock() };
|
||||
|
||||
for (auto& handle : thisObject->m_storage) {
|
||||
visitor.append(handle);
|
||||
}
|
||||
|
||||
if (thisObject->m_parent) {
|
||||
visitor.appendUnbarriered(thisObject->m_parent);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(NapiHandleScopeImpl);
|
||||
|
||||
void NapiHandleScopeImpl::append(JSC::JSValue val)
|
||||
{
|
||||
m_storage.append(Slot(vm(), this, val));
|
||||
}
|
||||
|
||||
bool NapiHandleScopeImpl::escape(JSC::JSValue val)
|
||||
{
|
||||
if (!m_escapeSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_escapeSlot->set(vm(), m_parent, val);
|
||||
m_escapeSlot = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
NapiHandleScopeImpl::Slot* NapiHandleScopeImpl::reserveSlot()
|
||||
{
|
||||
m_storage.append(Slot());
|
||||
return &m_storage.last();
|
||||
}
|
||||
|
||||
NapiHandleScopeImpl* NapiHandleScope::push(Zig::GlobalObject* globalObject, bool escapable)
|
||||
{
|
||||
auto* impl = NapiHandleScopeImpl::create(globalObject->vm(),
|
||||
globalObject->NapiHandleScopeImplStructure(),
|
||||
globalObject->m_currentNapiHandleScopeImpl.get(),
|
||||
escapable);
|
||||
globalObject->m_currentNapiHandleScopeImpl.set(globalObject->vm(), globalObject, impl);
|
||||
return impl;
|
||||
}
|
||||
|
||||
void NapiHandleScope::pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current)
|
||||
{
|
||||
RELEASE_ASSERT_WITH_MESSAGE(current == globalObject->m_currentNapiHandleScopeImpl.get(),
|
||||
"Unbalanced napi_handle_scope opens and closes");
|
||||
if (auto* parent = current->parent()) {
|
||||
globalObject->m_currentNapiHandleScopeImpl.set(globalObject->vm(), globalObject, parent);
|
||||
} else {
|
||||
globalObject->m_currentNapiHandleScopeImpl.clear();
|
||||
}
|
||||
}
|
||||
|
||||
NapiHandleScope::NapiHandleScope(Zig::GlobalObject* globalObject)
|
||||
: m_globalObject(globalObject)
|
||||
, m_impl(NapiHandleScope::push(globalObject, false))
|
||||
{
|
||||
}
|
||||
|
||||
NapiHandleScope::~NapiHandleScope()
|
||||
{
|
||||
NapiHandleScope::pop(m_globalObject, m_impl);
|
||||
}
|
||||
|
||||
extern "C" NapiHandleScopeImpl* NapiHandleScope__push(Zig::GlobalObject* globalObject, bool escapable)
|
||||
{
|
||||
return NapiHandleScope::push(globalObject, escapable);
|
||||
}
|
||||
|
||||
extern "C" void NapiHandleScope__pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current)
|
||||
{
|
||||
return NapiHandleScope::pop(globalObject, current);
|
||||
}
|
||||
|
||||
extern "C" void NapiHandleScope__append(Zig::GlobalObject* globalObject, JSC::EncodedJSValue value)
|
||||
{
|
||||
globalObject->m_currentNapiHandleScopeImpl.get()->append(JSC::JSValue::decode(value));
|
||||
}
|
||||
|
||||
extern "C" bool NapiHandleScope__escape(NapiHandleScopeImpl* handleScope, JSC::EncodedJSValue value)
|
||||
{
|
||||
return handleScope->escape(JSC::JSValue::decode(value));
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
97
src/bun.js/bindings/napi_handle_scope.h
Normal file
97
src/bun.js/bindings/napi_handle_scope.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include "BunClientData.h"
|
||||
#include "root.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// An array of write barriers (so that newly-added objects are not lost by GC) to JSValues. Unlike
|
||||
// the V8 version, pointer stability is not required (because napi_values don't point into this
|
||||
// structure) so we can use a regular WTF::Vector
|
||||
//
|
||||
// Don't use this directly, use NapiHandleScope. Most NAPI functions won't even need to use that as
|
||||
// a handle scope is created before calling a native function.
|
||||
class NapiHandleScopeImpl : public JSC::JSCell {
|
||||
public:
|
||||
using Base = JSC::JSCell;
|
||||
|
||||
static NapiHandleScopeImpl* create(
|
||||
JSC::VM& vm,
|
||||
JSC::Structure* structure,
|
||||
NapiHandleScopeImpl* parent,
|
||||
bool escapable = false);
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, JSC::jsNull(), JSC::TypeInfo(JSC::CellType, StructureFlags), info(), 0, 0);
|
||||
}
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<NapiHandleScopeImpl, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForNapiHandleScopeImpl.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNapiHandleScopeImpl = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForNapiHandleScopeImpl.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForNapiHandleScopeImpl = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
// Store val in the handle scope
|
||||
void append(JSC::JSValue val);
|
||||
NapiHandleScopeImpl* parent() const { return m_parent; }
|
||||
// Returns false if this handle scope is not escapable or if it is but escape() has already
|
||||
// been called
|
||||
bool escape(JSC::JSValue val);
|
||||
|
||||
private:
|
||||
using Slot = JSC::WriteBarrier<JSC::Unknown>;
|
||||
|
||||
NapiHandleScopeImpl* m_parent;
|
||||
WTF::Vector<Slot, 16> m_storage;
|
||||
Slot* m_escapeSlot;
|
||||
|
||||
Slot* reserveSlot();
|
||||
|
||||
NapiHandleScopeImpl(JSC::VM& vm, JSC::Structure* structure, NapiHandleScopeImpl* parent, bool escapable);
|
||||
};
|
||||
|
||||
// Wrapper class used to push a new handle scope and pop it when this instance goes out of scope
|
||||
class NapiHandleScope {
|
||||
public:
|
||||
NapiHandleScope(Zig::GlobalObject* globalObject);
|
||||
~NapiHandleScope();
|
||||
|
||||
// Create a new handle scope in the given environment
|
||||
static NapiHandleScopeImpl* push(Zig::GlobalObject* globalObject, bool escapable);
|
||||
|
||||
// Pop the most recently created handle scope in the given environment and restore the old one.
|
||||
// Asserts that `current` is the active handle scope.
|
||||
static void pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current);
|
||||
|
||||
private:
|
||||
NapiHandleScopeImpl* m_impl;
|
||||
Zig::GlobalObject* m_globalObject;
|
||||
};
|
||||
|
||||
// Create a new handle scope in the given environment
|
||||
extern "C" NapiHandleScopeImpl* NapiHandleScope__push(Zig::GlobalObject* globalObject, bool escapable);
|
||||
|
||||
// Pop the most recently created handle scope in the given environment and restore the old one.
|
||||
// Asserts that `current` is the active handle scope.
|
||||
extern "C" void NapiHandleScope__pop(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current);
|
||||
|
||||
// Store a value in the active handle scope in the given environment
|
||||
extern "C" void NapiHandleScope__append(Zig::GlobalObject* globalObject, JSC::EncodedJSValue value);
|
||||
|
||||
// Put a value from the current handle scope into its escape slot reserved in the outer handle
|
||||
// scope. Returns false if the current handle scope is not escapable or if escape has already been
|
||||
// called on it.
|
||||
extern "C" bool NapiHandleScope__escape(NapiHandleScopeImpl* handle_scope, JSC::EncodedJSValue value);
|
||||
|
||||
} // namespace Bun
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNextTickQueue;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNAPIFunction;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTTYWrapObject;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNapiHandleScopeImpl;
|
||||
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
|
||||
/* --- bun --- */
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSNextTickQueue;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNAPIFunction;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForTTYWrapObject;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNapiHandleScopeImpl;
|
||||
|
||||
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
|
||||
/*-- BUN --*/
|
||||
|
||||
@@ -64,12 +64,60 @@ pub const Ref = opaque {
|
||||
extern fn napi_delete_reference_internal(ref: *Ref) void;
|
||||
extern fn napi_set_ref(ref: *Ref, value: JSC.JSValue) void;
|
||||
};
|
||||
pub const napi_handle_scope = napi_env;
|
||||
pub const napi_escapable_handle_scope = napi_env;
|
||||
pub const NapiHandleScope = opaque {
|
||||
extern fn NapiHandleScope__push(globalObject: *JSC.JSGlobalObject, escapable: bool) *NapiHandleScope;
|
||||
extern fn NapiHandleScope__pop(globalObject: *JSC.JSGlobalObject, current: *NapiHandleScope) void;
|
||||
extern fn NapiHandleScope__append(globalObject: *JSC.JSGlobalObject, value: JSC.JSValueReprInt) void;
|
||||
extern fn NapiHandleScope__escape(handleScope: *NapiHandleScope, value: JSC.JSValueReprInt) bool;
|
||||
|
||||
pub fn push(env: napi_env, escapable: bool) *NapiHandleScope {
|
||||
return NapiHandleScope__push(env, escapable);
|
||||
}
|
||||
|
||||
pub fn pop(self: *NapiHandleScope, env: napi_env) void {
|
||||
NapiHandleScope__pop(env, self);
|
||||
}
|
||||
|
||||
pub fn append(env: napi_env, value: JSC.JSValue) void {
|
||||
NapiHandleScope__append(env, @intFromEnum(value));
|
||||
}
|
||||
|
||||
pub fn escape(self: *NapiHandleScope, value: JSC.JSValue) error{EscapeCalledTwice}!void {
|
||||
if (!NapiHandleScope__escape(self, @intFromEnum(value))) {
|
||||
return error.EscapeCalledTwice;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const napi_handle_scope = *NapiHandleScope;
|
||||
pub const napi_escapable_handle_scope = *NapiHandleScope;
|
||||
pub const napi_callback_info = *JSC.CallFrame;
|
||||
pub const napi_deferred = *JSC.JSPromise.Strong;
|
||||
|
||||
pub const napi_value = JSC.JSValue;
|
||||
/// To ensure napi_values are not collected prematurely after being returned into a native module,
|
||||
/// you must use these functions rather than convert between napi_value and JSC.JSValue directly
|
||||
pub const napi_value = enum(JSC.JSValueReprInt) {
|
||||
_,
|
||||
|
||||
pub fn set(
|
||||
self: *napi_value,
|
||||
env: napi_env,
|
||||
val: JSC.JSValue,
|
||||
) void {
|
||||
NapiHandleScope.append(env, val);
|
||||
self.* = @enumFromInt(@intFromEnum(val));
|
||||
}
|
||||
|
||||
pub fn get(self: *const napi_value) JSC.JSValue {
|
||||
return @enumFromInt(@intFromEnum(self.*));
|
||||
}
|
||||
|
||||
pub fn create(env: napi_env, val: JSC.JSValue) napi_value {
|
||||
NapiHandleScope.append(env, val);
|
||||
return @enumFromInt(@intFromEnum(val));
|
||||
}
|
||||
};
|
||||
|
||||
pub const struct_napi_escapable_handle_scope__ = opaque {};
|
||||
|
||||
const char16_t = u16;
|
||||
@@ -205,29 +253,29 @@ pub const napi_type_tag = extern struct {
|
||||
upper: u64,
|
||||
};
|
||||
pub extern fn napi_get_last_error_info(env: napi_env, result: [*c][*c]const napi_extended_error_info) napi_status;
|
||||
pub export fn napi_get_undefined(_: napi_env, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_get_undefined(env: napi_env, result_: ?*napi_value) napi_status {
|
||||
log("napi_get_undefined", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsUndefined();
|
||||
result.set(env, JSValue.jsUndefined());
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_null(_: napi_env, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_get_null(env: napi_env, result_: ?*napi_value) napi_status {
|
||||
log("napi_get_null", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsNull();
|
||||
result.set(env, JSValue.jsNull());
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_get_global(env: napi_env, result: *napi_value) napi_status;
|
||||
pub export fn napi_get_boolean(_: napi_env, value: bool, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_get_boolean(env: napi_env, value: bool, result_: ?*napi_value) napi_status {
|
||||
log("napi_get_boolean", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsBoolean(value);
|
||||
result.set(env, JSValue.jsBoolean(value));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_array(env: napi_env, result_: ?*napi_value) napi_status {
|
||||
@@ -235,7 +283,7 @@ pub export fn napi_create_array(env: napi_env, result_: ?*napi_value) napi_statu
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.createEmptyArray(env, 0);
|
||||
result.set(env, JSValue.createEmptyArray(env, 0));
|
||||
return .ok;
|
||||
}
|
||||
const prefilled_undefined_args_array: [128]JSC.JSValue = brk: {
|
||||
@@ -262,38 +310,34 @@ pub export fn napi_create_array_with_length(env: napi_env, length: usize, result
|
||||
}
|
||||
|
||||
array.ensureStillAlive();
|
||||
result.* = array;
|
||||
result.set(env, array);
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_create_double(_: napi_env, value: f64, result: *napi_value) napi_status;
|
||||
pub export fn napi_create_int32(_: napi_env, value: i32, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_create_int32(env: napi_env, value: i32, result_: ?*napi_value) napi_status {
|
||||
log("napi_create_int32", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsNumber(value);
|
||||
result.set(env, JSValue.jsNumber(value));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_uint32(_: napi_env, value: u32, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_create_uint32(env: napi_env, value: u32, result_: ?*napi_value) napi_status {
|
||||
log("napi_create_uint32", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsNumber(value);
|
||||
result.set(env, JSValue.jsNumber(value));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_int64(_: napi_env, value: i64, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_create_int64(env: napi_env, value: i64, result_: ?*napi_value) napi_status {
|
||||
log("napi_create_int64", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsNumber(value);
|
||||
result.set(env, JSValue.jsNumber(value));
|
||||
return .ok;
|
||||
}
|
||||
inline fn setNapiValue(result: *napi_value, value: JSValue) void {
|
||||
value.ensureStillAlive();
|
||||
result.* = value;
|
||||
}
|
||||
pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status {
|
||||
const result: *napi_value = result_ orelse {
|
||||
return invalidArg();
|
||||
@@ -315,7 +359,7 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length
|
||||
log("napi_create_string_latin1: {s}", .{slice});
|
||||
|
||||
if (slice.len == 0) {
|
||||
setNapiValue(result, bun.String.empty.toJS(env));
|
||||
result.set(env, bun.String.empty.toJS(env));
|
||||
return .ok;
|
||||
}
|
||||
|
||||
@@ -324,7 +368,7 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length
|
||||
|
||||
@memcpy(bytes, slice);
|
||||
|
||||
setNapiValue(result, string.toJS(env));
|
||||
result.set(env, string.toJS(env));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status {
|
||||
@@ -352,7 +396,7 @@ pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length:
|
||||
}
|
||||
|
||||
defer string.deref();
|
||||
setNapiValue(result, string.toJS(env));
|
||||
result.set(env, string.toJS(env));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, length: usize, result_: ?*napi_value) napi_status {
|
||||
@@ -377,7 +421,7 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l
|
||||
log("napi_create_string_utf16: {d} {any}", .{ slice.len, bun.fmt.FormatUTF16{ .buf = slice[0..@min(slice.len, 512)] } });
|
||||
|
||||
if (slice.len == 0) {
|
||||
setNapiValue(result, bun.String.empty.toJS(env));
|
||||
result.set(env, bun.String.empty.toJS(env));
|
||||
}
|
||||
|
||||
var string, const chars = bun.String.createUninitialized(.utf16, slice.len);
|
||||
@@ -385,7 +429,7 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l
|
||||
|
||||
@memcpy(chars, slice);
|
||||
|
||||
setNapiValue(result, string.toJS(env));
|
||||
result.set(env, string.toJS(env));
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_create_symbol(env: napi_env, description: napi_value, result: *napi_value) napi_status;
|
||||
@@ -394,44 +438,48 @@ pub extern fn napi_create_type_error(env: napi_env, code: napi_value, msg: napi_
|
||||
pub extern fn napi_create_range_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status;
|
||||
pub extern fn napi_typeof(env: napi_env, value: napi_value, result: *napi_valuetype) napi_status;
|
||||
pub extern fn napi_get_value_double(env: napi_env, value: napi_value, result: *f64) napi_status;
|
||||
pub export fn napi_get_value_int32(_: napi_env, value: napi_value, result_: ?*i32) napi_status {
|
||||
pub export fn napi_get_value_int32(_: napi_env, value_: napi_value, result_: ?*i32) napi_status {
|
||||
log("napi_get_value_int32", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
if (!value.isNumber()) {
|
||||
return .number_expected;
|
||||
}
|
||||
result.* = value.to(i32);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_value_uint32(_: napi_env, value: napi_value, result_: ?*u32) napi_status {
|
||||
pub export fn napi_get_value_uint32(_: napi_env, value_: napi_value, result_: ?*u32) napi_status {
|
||||
log("napi_get_value_uint32", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
if (!value.isNumber()) {
|
||||
return .number_expected;
|
||||
}
|
||||
result.* = value.to(u32);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_value_int64(_: napi_env, value: napi_value, result_: ?*i64) napi_status {
|
||||
pub export fn napi_get_value_int64(_: napi_env, value_: napi_value, result_: ?*i64) napi_status {
|
||||
log("napi_get_value_int64", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
if (!value.isNumber()) {
|
||||
return .number_expected;
|
||||
}
|
||||
result.* = value.to(i64);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_value_bool(_: napi_env, value: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_get_value_bool(_: napi_env, value_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_get_value_bool", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
|
||||
result.* = value.to(bool);
|
||||
return .ok;
|
||||
@@ -441,8 +489,9 @@ inline fn maybeAppendNull(ptr: anytype, doit: bool) void {
|
||||
ptr.* = 0;
|
||||
}
|
||||
}
|
||||
pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf_ptr_: ?[*:0]c_char, bufsize: usize, result_ptr: ?*usize) napi_status {
|
||||
pub export fn napi_get_value_string_latin1(env: napi_env, value_: napi_value, buf_ptr_: ?[*:0]c_char, bufsize: usize, result_ptr: ?*usize) napi_status {
|
||||
log("napi_get_value_string_latin1", .{});
|
||||
const value = value_.get();
|
||||
defer value.ensureStillAlive();
|
||||
const buf_ptr = @as(?[*:0]u8, @ptrCast(buf_ptr_));
|
||||
|
||||
@@ -498,8 +547,9 @@ pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf
|
||||
/// via the result parameter.
|
||||
/// The result argument is optional unless buf is NULL.
|
||||
pub extern fn napi_get_value_string_utf8(env: napi_env, value: napi_value, buf_ptr: [*c]u8, bufsize: usize, result_ptr: ?*usize) napi_status;
|
||||
pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_ptr: ?[*]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status {
|
||||
pub export fn napi_get_value_string_utf16(env: napi_env, value_: napi_value, buf_ptr: ?[*]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status {
|
||||
log("napi_get_value_string_utf16", .{});
|
||||
const value = value_.get();
|
||||
defer value.ensureStillAlive();
|
||||
const str = value.toBunString(env);
|
||||
defer str.deref();
|
||||
@@ -546,40 +596,44 @@ pub export fn napi_get_value_string_utf16(env: napi_env, value: napi_value, buf_
|
||||
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_coerce_to_bool(env: napi_env, value: napi_value, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_coerce_to_bool(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status {
|
||||
log("napi_coerce_to_bool", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.jsBoolean(value.coerce(bool, env));
|
||||
const value = value_.get();
|
||||
result.set(env, JSValue.jsBoolean(value.coerce(bool, env)));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_coerce_to_number(env: napi_env, value: napi_value, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_coerce_to_number(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status {
|
||||
log("napi_coerce_to_number", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSC.JSValue.jsNumber(JSC.C.JSValueToNumber(env.ref(), value.asObjectRef(), TODO_EXCEPTION));
|
||||
const value = value_.get();
|
||||
result.set(env, JSC.JSValue.jsNumber(JSC.C.JSValueToNumber(env.ref(), value.asObjectRef(), TODO_EXCEPTION)));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_coerce_to_object(env: napi_env, value: napi_value, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_coerce_to_object(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status {
|
||||
log("napi_coerce_to_object", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.c(JSC.C.JSValueToObject(env.ref(), value.asObjectRef(), TODO_EXCEPTION));
|
||||
const value = value_.get();
|
||||
result.set(env, JSValue.c(JSC.C.JSValueToObject(env.ref(), value.asObjectRef(), TODO_EXCEPTION)));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_prototype(env: napi_env, object: napi_value, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_get_prototype(env: napi_env, object_: napi_value, result_: ?*napi_value) napi_status {
|
||||
log("napi_get_prototype", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const object = object_.get();
|
||||
if (!object.isObject()) {
|
||||
return .object_expected;
|
||||
}
|
||||
|
||||
result.* = JSValue.c(JSC.C.JSObjectGetPrototype(env.ref(), object.asObjectRef()));
|
||||
result.set(env, JSValue.c(JSC.C.JSObjectGetPrototype(env.ref(), object.asObjectRef())));
|
||||
return .ok;
|
||||
}
|
||||
// TODO: bind JSC::ownKeys
|
||||
@@ -591,8 +645,10 @@ pub export fn napi_get_prototype(env: napi_env, object: napi_value, result_: ?*n
|
||||
|
||||
// result.* =
|
||||
// }
|
||||
pub export fn napi_set_element(env: napi_env, object: napi_value, index: c_uint, value: napi_value) napi_status {
|
||||
pub export fn napi_set_element(env: napi_env, object_: napi_value, index: c_uint, value_: napi_value) napi_status {
|
||||
log("napi_set_element", .{});
|
||||
const object = object_.get();
|
||||
const value = value_.get();
|
||||
if (!object.jsType().isIndexable()) {
|
||||
return .array_expected;
|
||||
}
|
||||
@@ -601,11 +657,12 @@ pub export fn napi_set_element(env: napi_env, object: napi_value, index: c_uint,
|
||||
JSC.C.JSObjectSetPropertyAtIndex(env.ref(), object.asObjectRef(), index, value.asObjectRef(), TODO_EXCEPTION);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_has_element(env: napi_env, object: napi_value, index: c_uint, result_: ?*bool) napi_status {
|
||||
pub export fn napi_has_element(env: napi_env, object_: napi_value, index: c_uint, result_: ?*bool) napi_status {
|
||||
log("napi_has_element", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const object = object_.get();
|
||||
|
||||
if (!object.jsType().isIndexable()) {
|
||||
return .array_expected;
|
||||
@@ -617,19 +674,21 @@ pub export fn napi_has_element(env: napi_env, object: napi_value, index: c_uint,
|
||||
pub extern fn napi_get_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status;
|
||||
pub extern fn napi_delete_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status;
|
||||
pub extern fn napi_define_properties(env: napi_env, object: napi_value, property_count: usize, properties: [*c]const napi_property_descriptor) napi_status;
|
||||
pub export fn napi_is_array(_: napi_env, value: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_is_array(_: napi_env, value_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_is_array", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
result.* = value.jsType().isArray();
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_array_length(env: napi_env, value: napi_value, result_: [*c]u32) napi_status {
|
||||
pub export fn napi_get_array_length(env: napi_env, value_: napi_value, result_: [*c]u32) napi_status {
|
||||
log("napi_get_array_length", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
|
||||
if (!value.jsType().isArray()) {
|
||||
return .array_expected;
|
||||
@@ -638,22 +697,24 @@ pub export fn napi_get_array_length(env: napi_env, value: napi_value, result_: [
|
||||
result.* = @as(u32, @truncate(value.getLength(env)));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_strict_equals(env: napi_env, lhs: napi_value, rhs: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_strict_equals(env: napi_env, lhs_: napi_value, rhs_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_strict_equals", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const lhs, const rhs = .{ lhs_.get(), rhs_.get() };
|
||||
// there is some nuance with NaN here i'm not sure about
|
||||
result.* = lhs.isSameValue(rhs, env);
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_call_function(env: napi_env, recv: napi_value, func: napi_value, argc: usize, argv: [*c]const napi_value, result: *napi_value) napi_status;
|
||||
pub extern fn napi_new_instance(env: napi_env, constructor: napi_value, argc: usize, argv: [*c]const napi_value, result_: ?*napi_value) napi_status;
|
||||
pub export fn napi_instanceof(env: napi_env, object: napi_value, constructor: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_instanceof(env: napi_env, object_: napi_value, constructor_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_instanceof", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const object, const constructor = .{ object_.get(), constructor_.get() };
|
||||
// TODO: does this throw object_expected in node?
|
||||
result.* = object.isObject() and object.isInstanceOf(env, constructor);
|
||||
return .ok;
|
||||
@@ -683,20 +744,18 @@ pub extern fn napi_reference_unref(env: napi_env, ref: *Ref, result: [*c]u32) na
|
||||
pub extern fn napi_get_reference_value(env: napi_env, ref: *Ref, result: *napi_value) napi_status;
|
||||
pub extern fn napi_get_reference_value_internal(ref: *Ref) JSC.JSValue;
|
||||
|
||||
// JSC scans the stack
|
||||
// we don't need this
|
||||
pub export fn napi_open_handle_scope(env: napi_env, result_: ?*napi_handle_scope) napi_status {
|
||||
log("napi_open_handle_scope", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = env;
|
||||
result.* = NapiHandleScope.push(env, false);
|
||||
return .ok;
|
||||
}
|
||||
// JSC scans the stack
|
||||
// we don't need this
|
||||
pub export fn napi_close_handle_scope(_: napi_env, _: napi_handle_scope) napi_status {
|
||||
|
||||
pub export fn napi_close_handle_scope(env: napi_env, handle_scope: napi_handle_scope) napi_status {
|
||||
log("napi_close_handle_scope", .{});
|
||||
handle_scope.pop(env);
|
||||
return .ok;
|
||||
}
|
||||
|
||||
@@ -714,8 +773,9 @@ pub export fn napi_async_destroy(_: napi_env, _: *anyopaque) napi_status {
|
||||
}
|
||||
|
||||
// this is just a regular function call
|
||||
pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv: napi_value, func: napi_value, arg_count: usize, args: ?[*]const napi_value, result: ?*napi_value) napi_status {
|
||||
pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv_: napi_value, func_: napi_value, arg_count: usize, args: ?[*]const napi_value, maybe_result: ?*napi_value) napi_status {
|
||||
log("napi_make_callback", .{});
|
||||
const recv, const func = .{ recv_.get(), func_.get() };
|
||||
if (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.vm())) {
|
||||
return .function_expected;
|
||||
}
|
||||
@@ -732,8 +792,8 @@ pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv: napi_value,
|
||||
&.{},
|
||||
);
|
||||
|
||||
if (result) |result_| {
|
||||
result_.* = res;
|
||||
if (maybe_result) |result| {
|
||||
result.set(env, res);
|
||||
}
|
||||
|
||||
// TODO: this is likely incorrect
|
||||
@@ -761,26 +821,26 @@ fn notImplementedYet(comptime name: []const u8) void {
|
||||
);
|
||||
}
|
||||
|
||||
// JSC stack scanning will handle this
|
||||
pub export fn napi_open_escapable_handle_scope(env: napi_env, handle_: ?*napi_escapable_handle_scope) napi_status {
|
||||
pub export fn napi_open_escapable_handle_scope(env: napi_env, result_: ?*napi_escapable_handle_scope) napi_status {
|
||||
log("napi_open_escapable_handle_scope", .{});
|
||||
const handle = handle_ orelse {
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
handle.* = env;
|
||||
result.* = NapiHandleScope.push(env, true);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_close_escapable_handle_scope(_: napi_env, _: napi_escapable_handle_scope) napi_status {
|
||||
pub export fn napi_close_escapable_handle_scope(env: napi_env, scope: napi_escapable_handle_scope) napi_status {
|
||||
log("napi_close_escapable_handle_scope", .{});
|
||||
scope.pop(env);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_escape_handle(_: napi_env, _: napi_escapable_handle_scope, value: napi_value, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_escape_handle(_: napi_env, scope: napi_escapable_handle_scope, escapee: napi_value, result_: ?*napi_value) napi_status {
|
||||
log("napi_escape_handle", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
value.ensureStillAlive();
|
||||
result.* = value;
|
||||
scope.escape(escapee.get()) catch return .escape_called_twice;
|
||||
result.* = escapee;
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_type_tag_object(_: napi_env, _: napi_value, _: [*c]const napi_type_tag) napi_status {
|
||||
@@ -807,18 +867,20 @@ pub extern fn napi_throw(env: napi_env, @"error": napi_value) napi_status;
|
||||
pub extern fn napi_throw_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status;
|
||||
pub extern fn napi_throw_type_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status;
|
||||
pub extern fn napi_throw_range_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status;
|
||||
pub export fn napi_is_error(_: napi_env, value: napi_value, result: *bool) napi_status {
|
||||
pub export fn napi_is_error(_: napi_env, value_: napi_value, result: *bool) napi_status {
|
||||
log("napi_is_error", .{});
|
||||
const value = value_.get();
|
||||
result.* = value.isAnyError();
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_is_exception_pending(env: napi_env, result: *bool) napi_status;
|
||||
pub extern fn napi_get_and_clear_last_exception(env: napi_env, result: *napi_value) napi_status;
|
||||
pub export fn napi_is_arraybuffer(_: napi_env, value: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_is_arraybuffer(_: napi_env, value_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_is_arraybuffer", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
result.* = !value.isNumber() and value.jsTypeLoose() == .ArrayBuffer;
|
||||
return .ok;
|
||||
}
|
||||
@@ -826,8 +888,9 @@ pub extern fn napi_create_arraybuffer(env: napi_env, byte_length: usize, data: [
|
||||
|
||||
pub extern fn napi_create_external_arraybuffer(env: napi_env, external_data: ?*anyopaque, byte_length: usize, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status;
|
||||
|
||||
pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer: napi_value, data: ?*[*]u8, byte_length: ?*usize) napi_status {
|
||||
pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer_: napi_value, data: ?*[*]u8, byte_length: ?*usize) napi_status {
|
||||
log("napi_get_arraybuffer_info", .{});
|
||||
const arraybuffer = arraybuffer_.get();
|
||||
const array_buffer = arraybuffer.asArrayBuffer(env) orelse return .arraybuffer_expected;
|
||||
const slice = array_buffer.slice();
|
||||
if (data) |dat|
|
||||
@@ -836,18 +899,20 @@ pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer: napi_value,
|
||||
len.* = slice.len;
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_is_typedarray(_: napi_env, value: napi_value, result: ?*bool) napi_status {
|
||||
pub export fn napi_is_typedarray(_: napi_env, value_: napi_value, result: ?*bool) napi_status {
|
||||
log("napi_is_typedarray", .{});
|
||||
const value = value_.get();
|
||||
if (result != null)
|
||||
result.?.* = value.jsTypeLoose().isTypedArray();
|
||||
return if (result != null) .ok else invalidArg();
|
||||
}
|
||||
pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_type, length: usize, arraybuffer: napi_value, byte_offset: usize, result_: ?*napi_value) napi_status {
|
||||
pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_type, length: usize, arraybuffer_: napi_value, byte_offset: usize, result_: ?*napi_value) napi_status {
|
||||
log("napi_create_typedarray", .{});
|
||||
const arraybuffer = arraybuffer_.get();
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSValue.c(
|
||||
result.set(env, JSValue.c(
|
||||
JSC.C.JSObjectMakeTypedArrayWithArrayBufferAndOffset(
|
||||
env.ref(),
|
||||
@"type".toC(),
|
||||
@@ -856,64 +921,74 @@ pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_typ
|
||||
length,
|
||||
TODO_EXCEPTION,
|
||||
),
|
||||
);
|
||||
));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_typedarray_info(
|
||||
env: napi_env,
|
||||
typedarray: napi_value,
|
||||
@"type": ?*napi_typedarray_type,
|
||||
length: ?*usize,
|
||||
data: ?*[*]u8,
|
||||
arraybuffer: ?*napi_value,
|
||||
byte_offset: ?*usize,
|
||||
typedarray_: napi_value,
|
||||
maybe_type: ?*napi_typedarray_type,
|
||||
maybe_length: ?*usize,
|
||||
maybe_data: ?*[*]u8,
|
||||
maybe_arraybuffer: ?*napi_value,
|
||||
maybe_byte_offset: ?*usize,
|
||||
) napi_status {
|
||||
log("napi_get_typedarray_info", .{});
|
||||
const typedarray = typedarray_.get();
|
||||
if (typedarray.isEmptyOrUndefinedOrNull())
|
||||
return invalidArg();
|
||||
defer typedarray.ensureStillAlive();
|
||||
|
||||
const array_buffer = typedarray.asArrayBuffer(env) orelse return invalidArg();
|
||||
if (@"type" != null)
|
||||
@"type".?.* = napi_typedarray_type.fromJSType(array_buffer.typed_array_type) orelse return invalidArg();
|
||||
if (maybe_type) |@"type"|
|
||||
@"type".* = napi_typedarray_type.fromJSType(array_buffer.typed_array_type) orelse return invalidArg();
|
||||
|
||||
// TODO: handle detached
|
||||
if (data != null)
|
||||
data.?.* = array_buffer.ptr;
|
||||
if (maybe_data) |data|
|
||||
data.* = array_buffer.ptr;
|
||||
|
||||
if (length != null)
|
||||
length.?.* = array_buffer.len;
|
||||
if (maybe_length) |length|
|
||||
length.* = array_buffer.len;
|
||||
|
||||
if (arraybuffer != null)
|
||||
arraybuffer.?.* = JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), typedarray.asObjectRef(), null));
|
||||
if (maybe_arraybuffer) |arraybuffer|
|
||||
arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), typedarray.asObjectRef(), null)));
|
||||
|
||||
if (byte_offset != null)
|
||||
byte_offset.?.* = array_buffer.offset;
|
||||
if (maybe_byte_offset) |byte_offset|
|
||||
byte_offset.* = array_buffer.offset;
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_create_dataview(env: napi_env, length: usize, arraybuffer: napi_value, byte_offset: usize, result: *napi_value) napi_status;
|
||||
pub export fn napi_is_dataview(_: napi_env, value: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_is_dataview(_: napi_env, value_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_is_dataview", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
result.* = !value.isEmptyOrUndefinedOrNull() and value.jsTypeLoose() == .DataView;
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_dataview_info(env: napi_env, dataview: napi_value, bytelength: ?*usize, data: ?*[*]u8, arraybuffer: ?*napi_value, byte_offset: ?*usize) napi_status {
|
||||
pub export fn napi_get_dataview_info(
|
||||
env: napi_env,
|
||||
dataview_: napi_value,
|
||||
maybe_bytelength: ?*usize,
|
||||
maybe_data: ?*[*]u8,
|
||||
maybe_arraybuffer: ?*napi_value,
|
||||
maybe_byte_offset: ?*usize,
|
||||
) napi_status {
|
||||
log("napi_get_dataview_info", .{});
|
||||
const dataview = dataview_.get();
|
||||
const array_buffer = dataview.asArrayBuffer(env) orelse return .object_expected;
|
||||
if (bytelength != null)
|
||||
bytelength.?.* = array_buffer.byte_len;
|
||||
if (maybe_bytelength) |bytelength|
|
||||
bytelength.* = array_buffer.byte_len;
|
||||
|
||||
if (data != null)
|
||||
data.?.* = array_buffer.ptr;
|
||||
if (maybe_data) |data|
|
||||
data.* = array_buffer.ptr;
|
||||
|
||||
if (arraybuffer != null)
|
||||
arraybuffer.?.* = JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), dataview.asObjectRef(), null));
|
||||
if (maybe_arraybuffer) |arraybuffer|
|
||||
arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), dataview.asObjectRef(), null)));
|
||||
|
||||
if (byte_offset != null)
|
||||
byte_offset.?.* = array_buffer.offset;
|
||||
if (maybe_byte_offset) |byte_offset|
|
||||
byte_offset.* = array_buffer.offset;
|
||||
|
||||
return .ok;
|
||||
}
|
||||
@@ -935,27 +1010,30 @@ pub export fn napi_create_promise(env: napi_env, deferred_: ?*napi_deferred, pro
|
||||
};
|
||||
deferred.* = bun.default_allocator.create(JSC.JSPromise.Strong) catch @panic("failed to allocate napi_deferred");
|
||||
deferred.*.* = JSC.JSPromise.Strong.init(env);
|
||||
promise.* = deferred.*.get().asValue(env);
|
||||
promise.set(env, deferred.*.get().asValue(env));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_resolve_deferred(env: napi_env, deferred: napi_deferred, resolution: napi_value) napi_status {
|
||||
pub export fn napi_resolve_deferred(env: napi_env, deferred: napi_deferred, resolution_: napi_value) napi_status {
|
||||
log("napi_resolve_deferred", .{});
|
||||
const resolution = resolution_.get();
|
||||
var prom = deferred.get();
|
||||
prom.resolve(env, resolution);
|
||||
deferred.deinit();
|
||||
bun.default_allocator.destroy(deferred);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_reject_deferred(env: napi_env, deferred: napi_deferred, rejection: napi_value) napi_status {
|
||||
pub export fn napi_reject_deferred(env: napi_env, deferred: napi_deferred, rejection_: napi_value) napi_status {
|
||||
log("napi_reject_deferred", .{});
|
||||
const rejection = rejection_.get();
|
||||
var prom = deferred.get();
|
||||
prom.reject(env, rejection);
|
||||
deferred.deinit();
|
||||
bun.default_allocator.destroy(deferred);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_is_promise(_: napi_env, value: napi_value, is_promise_: ?*bool) napi_status {
|
||||
pub export fn napi_is_promise(_: napi_env, value_: napi_value, is_promise_: ?*bool) napi_status {
|
||||
log("napi_is_promise", .{});
|
||||
const value = value_.get();
|
||||
const is_promise = is_promise_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
@@ -975,14 +1053,15 @@ pub export fn napi_create_date(env: napi_env, time: f64, result_: ?*napi_value)
|
||||
return invalidArg();
|
||||
};
|
||||
var args = [_]JSC.C.JSValueRef{JSC.JSValue.jsNumber(time).asObjectRef()};
|
||||
result.* = JSValue.c(JSC.C.JSObjectMakeDate(env.ref(), 1, &args, TODO_EXCEPTION));
|
||||
result.set(env, JSValue.c(JSC.C.JSObjectMakeDate(env.ref(), 1, &args, TODO_EXCEPTION)));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_is_date(_: napi_env, value: napi_value, is_date_: ?*bool) napi_status {
|
||||
pub export fn napi_is_date(_: napi_env, value_: napi_value, is_date_: ?*bool) napi_status {
|
||||
log("napi_is_date", .{});
|
||||
const is_date = is_date_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
is_date.* = value.jsTypeLoose() == .JSDate;
|
||||
return .ok;
|
||||
}
|
||||
@@ -993,7 +1072,7 @@ pub export fn napi_create_bigint_int64(env: napi_env, value: i64, result_: ?*nap
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSC.JSValue.fromInt64NoTruncate(env, value);
|
||||
result.set(env, JSC.JSValue.fromInt64NoTruncate(env, value));
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*napi_value) napi_status {
|
||||
@@ -1001,25 +1080,27 @@ pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*na
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
result.* = JSC.JSValue.fromUInt64NoTruncate(env, value);
|
||||
result.set(env, JSC.JSValue.fromUInt64NoTruncate(env, value));
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_create_bigint_words(env: napi_env, sign_bit: c_int, word_count: usize, words: [*c]const u64, result: *napi_value) napi_status;
|
||||
// TODO: lossless
|
||||
pub export fn napi_get_value_bigint_int64(_: napi_env, value: napi_value, result_: ?*i64, _: *bool) napi_status {
|
||||
pub export fn napi_get_value_bigint_int64(_: napi_env, value_: napi_value, result_: ?*i64, _: *bool) napi_status {
|
||||
log("napi_get_value_bigint_int64", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
result.* = value.toInt64();
|
||||
return .ok;
|
||||
}
|
||||
// TODO: lossless
|
||||
pub export fn napi_get_value_bigint_uint64(_: napi_env, value: napi_value, result_: ?*u64, _: *bool) napi_status {
|
||||
pub export fn napi_get_value_bigint_uint64(_: napi_env, value_: napi_value, result_: ?*u64, _: *bool) napi_status {
|
||||
log("napi_get_value_bigint_uint64", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
result.* = value.toUInt64NoTruncate();
|
||||
return .ok;
|
||||
}
|
||||
@@ -1113,6 +1194,8 @@ pub const napi_async_work = struct {
|
||||
}
|
||||
|
||||
pub fn runFromJS(this: *napi_async_work) void {
|
||||
const handle_scope = NapiHandleScope.push(this.global, false);
|
||||
defer handle_scope.pop(this.global);
|
||||
this.complete.?(
|
||||
this.global,
|
||||
if (this.status.load(.seq_cst) == @intFromEnum(Status.cancelled))
|
||||
@@ -1196,7 +1279,7 @@ pub export fn napi_create_buffer(env: napi_env, length: usize, data: ?**anyopaqu
|
||||
ptr.* = buffer.asArrayBuffer(env).?.ptr;
|
||||
}
|
||||
}
|
||||
result.* = buffer;
|
||||
result.set(env, buffer);
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_create_external_buffer(env: napi_env, length: usize, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status;
|
||||
@@ -1215,20 +1298,22 @@ pub export fn napi_create_buffer_copy(env: napi_env, length: usize, data: [*]u8,
|
||||
}
|
||||
}
|
||||
|
||||
result.* = buffer;
|
||||
result.set(env, buffer);
|
||||
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_is_buffer(env: napi_env, value: napi_value, result_: ?*bool) napi_status {
|
||||
pub export fn napi_is_buffer(env: napi_env, value_: napi_value, result_: ?*bool) napi_status {
|
||||
log("napi_is_buffer", .{});
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const value = value_.get();
|
||||
result.* = value.isBuffer(env);
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_get_buffer_info(env: napi_env, value: napi_value, data: ?*[*]u8, length: ?*usize) napi_status {
|
||||
pub export fn napi_get_buffer_info(env: napi_env, value_: napi_value, data: ?*[*]u8, length: ?*usize) napi_status {
|
||||
log("napi_get_buffer_info", .{});
|
||||
const value = value_.get();
|
||||
const array_buf = value.asArrayBuffer(env) orelse {
|
||||
// TODO: is invalid_arg what to return here?
|
||||
return .arraybuffer_expected;
|
||||
@@ -1491,7 +1576,9 @@ pub const ThreadSafeFunction = struct {
|
||||
log("call() {}", .{str});
|
||||
}
|
||||
|
||||
cb.napi_threadsafe_function_call_js(globalObject, cb.js, this.ctx, task);
|
||||
const handle_scope = NapiHandleScope.push(globalObject, false);
|
||||
defer handle_scope.pop(globalObject);
|
||||
cb.napi_threadsafe_function_call_js(globalObject, napi_value.create(globalObject, cb.js), this.ctx, task);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1572,7 +1659,7 @@ pub const ThreadSafeFunction = struct {
|
||||
|
||||
pub export fn napi_create_threadsafe_function(
|
||||
env: napi_env,
|
||||
func: napi_value,
|
||||
func_: napi_value,
|
||||
_: napi_value,
|
||||
_: napi_value,
|
||||
max_queue_size: usize,
|
||||
@@ -1587,6 +1674,7 @@ pub export fn napi_create_threadsafe_function(
|
||||
const result = result_ orelse {
|
||||
return invalidArg();
|
||||
};
|
||||
const func = func_.get();
|
||||
|
||||
if (call_js_cb == null and (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.vm()))) {
|
||||
return napi_status.function_expected;
|
||||
|
||||
@@ -46,6 +46,7 @@ napi_value test_issue_7685(const Napi::CallbackInfo &info) {
|
||||
napi_assert(info[5].IsNumber());
|
||||
napi_assert(info[6].IsNumber());
|
||||
napi_assert(info[7].IsNumber());
|
||||
#undef napi_assert
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
@@ -146,6 +147,290 @@ test_napi_get_value_string_utf8_with_buffer(const Napi::CallbackInfo &info) {
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
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;
|
||||
constexpr size_t num_large_strings = 100;
|
||||
constexpr size_t large_string_size = 20'000'000;
|
||||
|
||||
auto *small_strings = new napi_value[num_small_strings];
|
||||
auto *large_strings = new napi_value[num_large_strings];
|
||||
auto *string_data = new char[large_string_size];
|
||||
string_data[large_string_size - 1] = 0;
|
||||
|
||||
for (size_t i = 0; i < num_small_strings; i++) {
|
||||
std::string cpp_str = std::to_string(i);
|
||||
assert(napi_create_string_utf8(env, cpp_str.c_str(), cpp_str.size(),
|
||||
&small_strings[i]) == napi_ok);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_large_strings; i++) {
|
||||
memset(string_data, i + 1, large_string_size);
|
||||
assert(napi_create_string_utf8(env, string_data, large_string_size,
|
||||
&large_strings[i]) == napi_ok);
|
||||
|
||||
for (size_t j = 0; j < num_small_strings; j++) {
|
||||
char buf[16];
|
||||
size_t result;
|
||||
assert(napi_get_value_string_utf8(env, small_strings[j], buf, sizeof buf,
|
||||
&result) == napi_ok);
|
||||
printf("%s\n", buf);
|
||||
assert(atoi(buf) == (int)j);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] small_strings;
|
||||
delete[] large_strings;
|
||||
delete[] string_data;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
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 = 100;
|
||||
constexpr size_t num_large_ints = 10000;
|
||||
constexpr size_t small_int_size = 16;
|
||||
// JSC bigint size limit = 1<<20 bits
|
||||
constexpr size_t large_int_size = (1 << 20) / 64;
|
||||
|
||||
auto *small_ints = new napi_value[num_small_ints];
|
||||
auto *large_ints = new napi_value[num_large_ints];
|
||||
std::vector<uint64_t> int_words(large_int_size);
|
||||
|
||||
for (size_t i = 0; i < num_small_ints; i++) {
|
||||
std::array<uint64_t, small_int_size> words;
|
||||
words.fill(i + 1);
|
||||
assert(napi_create_bigint_words(env, 0, small_int_size, words.data(),
|
||||
&small_ints[i]) == napi_ok);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_large_ints; i++) {
|
||||
std::fill(int_words.begin(), int_words.end(), i + 1);
|
||||
assert(napi_create_bigint_words(env, 0, large_int_size, int_words.data(),
|
||||
&large_ints[i]) == napi_ok);
|
||||
|
||||
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();
|
||||
assert(napi_get_value_bigint_words(env, small_ints[j], &sign, &word_count,
|
||||
words.data()) == napi_ok);
|
||||
printf("%d, %zu\n", sign, word_count);
|
||||
assert(sign == 0 && word_count == words.size());
|
||||
assert(std::all_of(words.begin(), words.end(),
|
||||
[j](const uint64_t &w) { return w == j + 1; }));
|
||||
}
|
||||
}
|
||||
|
||||
delete[] small_ints;
|
||||
delete[] large_ints;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
napi_value test_napi_delete_property(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
napi_value object = info[0];
|
||||
napi_valuetype type;
|
||||
assert(napi_typeof(env, object, &type) == napi_ok);
|
||||
assert(type == napi_object);
|
||||
|
||||
napi_value key;
|
||||
assert(napi_create_string_utf8(env, "foo", 3, &key) == napi_ok);
|
||||
|
||||
napi_value non_configurable_key;
|
||||
assert(napi_create_string_utf8(env, "bar", 3, &non_configurable_key) ==
|
||||
napi_ok);
|
||||
|
||||
napi_value val;
|
||||
assert(napi_create_int32(env, 42, &val) == napi_ok);
|
||||
|
||||
bool delete_result;
|
||||
assert(napi_delete_property(env, object, non_configurable_key,
|
||||
&delete_result) == napi_ok);
|
||||
assert(delete_result == false);
|
||||
|
||||
assert(napi_delete_property(env, object, key, &delete_result) == napi_ok);
|
||||
assert(delete_result == true);
|
||||
|
||||
bool has_property;
|
||||
assert(napi_has_property(env, object, key, &has_property) == napi_ok);
|
||||
assert(has_property == false);
|
||||
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
void store_escaped_handle(napi_env env, napi_value *out, const char *str) {
|
||||
napi_escapable_handle_scope ehs;
|
||||
assert(napi_open_escapable_handle_scope(env, &ehs) == napi_ok);
|
||||
napi_value s;
|
||||
assert(napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, &s) == napi_ok);
|
||||
napi_value escaped;
|
||||
assert(napi_escape_handle(env, ehs, s, &escaped) == napi_ok);
|
||||
// can't call a second time
|
||||
assert(napi_escape_handle(env, ehs, s, &escaped) == napi_escape_called_twice);
|
||||
assert(napi_close_escapable_handle_scope(env, ehs) == napi_ok);
|
||||
*out = escaped;
|
||||
|
||||
// try to defeat stack scanning
|
||||
*(volatile napi_value *)(&s) = nullptr;
|
||||
*(volatile napi_value *)(&escaped) = nullptr;
|
||||
}
|
||||
|
||||
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
|
||||
// allocate some big objects to 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;
|
||||
store_escaped_handle(env, hidden, str);
|
||||
|
||||
constexpr size_t big_string_length = 20'000'000;
|
||||
auto *string_data = new char[big_string_length];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
napi_value s;
|
||||
memset(string_data, i + 1, big_string_length);
|
||||
assert(napi_create_string_utf8(env, string_data, big_string_length, &s) ==
|
||||
napi_ok);
|
||||
}
|
||||
delete[] string_data;
|
||||
|
||||
char buf[64];
|
||||
size_t len;
|
||||
assert(napi_get_value_string_utf8(env, *hidden, buf, sizeof(buf), &len) ==
|
||||
napi_ok);
|
||||
assert(len == strlen(str));
|
||||
assert(strcmp(buf, str) == 0);
|
||||
|
||||
delete hidden;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
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;
|
||||
assert(napi_open_handle_scope(env, outer_hs) == napi_ok);
|
||||
|
||||
// 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;
|
||||
assert(napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH,
|
||||
outer_scope_handle) == napi_ok);
|
||||
|
||||
// Make a new handle scope on the heap
|
||||
napi_handle_scope *inner_hs = new napi_handle_scope;
|
||||
assert(napi_open_handle_scope(env, inner_hs) == napi_ok);
|
||||
|
||||
// Allocate lots of memory to force GC
|
||||
constexpr size_t big_string_length = 20'000'000;
|
||||
auto *string_data = new char[big_string_length];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
napi_value s;
|
||||
memset(string_data, i + 1, big_string_length);
|
||||
assert(napi_create_string_utf8(env, string_data, big_string_length, &s) ==
|
||||
napi_ok);
|
||||
}
|
||||
delete[] string_data;
|
||||
|
||||
// Try to read our first handle. Did the outer handle scope get
|
||||
// collected now that it's not on the global object?
|
||||
char buf[64];
|
||||
size_t len;
|
||||
assert(napi_get_value_string_utf8(env, *outer_scope_handle, buf, sizeof(buf),
|
||||
&len) == napi_ok);
|
||||
assert(len == strlen(str));
|
||||
assert(strcmp(buf, str) == 0);
|
||||
|
||||
// Clean up
|
||||
assert(napi_close_handle_scope(env, *inner_hs) == napi_ok);
|
||||
delete inner_hs;
|
||||
assert(napi_close_handle_scope(env, *outer_hs) == napi_ok);
|
||||
delete outer_hs;
|
||||
delete outer_scope_handle;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
napi_value constructor(napi_env env, napi_callback_info info) {
|
||||
napi_value this_value;
|
||||
assert(napi_get_cb_info(env, info, nullptr, nullptr, &this_value, nullptr) ==
|
||||
napi_ok);
|
||||
napi_value property_value;
|
||||
assert(napi_create_string_utf8(env, "meow", NAPI_AUTO_LENGTH,
|
||||
&property_value) == napi_ok);
|
||||
assert(napi_set_named_property(env, this_value, "foo", property_value) ==
|
||||
napi_ok);
|
||||
napi_value undefined;
|
||||
assert(napi_get_undefined(env, &undefined) == napi_ok);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
napi_value get_class_with_constructor(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
napi_value napi_class;
|
||||
assert(napi_define_class(env, "NapiClass", NAPI_AUTO_LENGTH, constructor,
|
||||
nullptr, 0, nullptr, &napi_class) == napi_ok);
|
||||
return napi_class;
|
||||
}
|
||||
|
||||
struct AsyncWorkData {
|
||||
int result;
|
||||
napi_deferred deferred;
|
||||
napi_async_work work;
|
||||
|
||||
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 env, napi_status status, void *data) {
|
||||
AsyncWorkData *async_work_data = reinterpret_cast<AsyncWorkData *>(data);
|
||||
assert(status == napi_ok);
|
||||
|
||||
napi_value result;
|
||||
char buf[64] = {0};
|
||||
snprintf(buf, sizeof(buf), "the number is %d", async_work_data->result);
|
||||
assert(napi_create_string_utf8(env, buf, NAPI_AUTO_LENGTH, &result) ==
|
||||
napi_ok);
|
||||
assert(napi_resolve_deferred(env, async_work_data->deferred, result) ==
|
||||
napi_ok);
|
||||
assert(napi_delete_async_work(env, async_work_data->work) == napi_ok);
|
||||
delete async_work_data;
|
||||
}
|
||||
};
|
||||
|
||||
napi_value create_promise(const Napi::CallbackInfo &info) {
|
||||
napi_env env = info.Env();
|
||||
auto *data = new AsyncWorkData;
|
||||
napi_value promise;
|
||||
|
||||
assert(napi_create_promise(env, &data->deferred, &promise) == napi_ok);
|
||||
|
||||
napi_value resource_name;
|
||||
assert(napi_create_string_utf8(env, "napitests::create_promise",
|
||||
NAPI_AUTO_LENGTH, &resource_name) == napi_ok);
|
||||
assert(napi_create_async_work(env, nullptr, resource_name,
|
||||
AsyncWorkData::execute, AsyncWorkData::complete,
|
||||
data, &data->work) == napi_ok);
|
||||
assert(napi_queue_async_work(env, data->work) == napi_ok);
|
||||
return promise;
|
||||
}
|
||||
|
||||
Napi::Value RunCallback(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
Napi::Function cb = info[0].As<Napi::Function>();
|
||||
@@ -174,6 +459,19 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports1) {
|
||||
"test_napi_threadsafe_function_does_not_hang_after_finalize",
|
||||
Napi::Function::New(
|
||||
env, test_napi_threadsafe_function_does_not_hang_after_finalize));
|
||||
exports.Set("test_napi_handle_scope_string",
|
||||
Napi::Function::New(env, test_napi_handle_scope_string));
|
||||
exports.Set("test_napi_handle_scope_bigint",
|
||||
Napi::Function::New(env, test_napi_handle_scope_bigint));
|
||||
exports.Set("test_napi_delete_property",
|
||||
Napi::Function::New(env, test_napi_delete_property));
|
||||
exports.Set("test_napi_escapable_handle_scope",
|
||||
Napi::Function::New(env, test_napi_escapable_handle_scope));
|
||||
exports.Set("test_napi_handle_scope_nesting",
|
||||
Napi::Function::New(env, test_napi_handle_scope_nesting));
|
||||
exports.Set("get_class_with_constructor",
|
||||
Napi::Function::New(env, get_class_with_constructor));
|
||||
exports.Set("create_promise", Napi::Function::New(env, create_promise));
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const tests = require("./build/Release/napitests.node");
|
||||
const tests = require("./module");
|
||||
if (process.argv[2] === "self") {
|
||||
console.log(
|
||||
tests(function (str) {
|
||||
@@ -11,7 +11,9 @@ const fn = tests[process.argv[2]];
|
||||
if (typeof fn !== "function") {
|
||||
throw new Error("Unknown test:", process.argv[2]);
|
||||
}
|
||||
const result = fn.apply(null, JSON.parse(process.argv[3] ?? "[]"));
|
||||
if (result) {
|
||||
const result = fn.apply(null, eval(process.argv[3] ?? "[]"));
|
||||
if (result instanceof Promise) {
|
||||
result.then(x => console.log("resolved to", x));
|
||||
} else if (result) {
|
||||
throw new Error(result);
|
||||
}
|
||||
|
||||
9
test/napi/napi-app/module.js
Normal file
9
test/napi/napi-app/module.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const nativeTests = require("./build/Release/napitests.node");
|
||||
|
||||
nativeTests.test_napi_class_constructor_handle_scope = () => {
|
||||
const NapiClass = nativeTests.get_class_with_constructor();
|
||||
const x = new NapiClass();
|
||||
console.log("x.foo =", x.foo);
|
||||
};
|
||||
|
||||
module.exports = nativeTests;
|
||||
@@ -69,9 +69,56 @@ describe("napi", () => {
|
||||
const result = checkSameOutput("self", []);
|
||||
expect(result).toBe("hello world!");
|
||||
});
|
||||
|
||||
describe("handle_scope", () => {
|
||||
it("keeps strings alive", () => {
|
||||
checkSameOutput("test_napi_handle_scope_string", []);
|
||||
});
|
||||
it("keeps bigints alive", () => {
|
||||
checkSameOutput("test_napi_handle_scope_bigint", []);
|
||||
}, 10000);
|
||||
it("keeps the parent handle scope alive", () => {
|
||||
checkSameOutput("test_napi_handle_scope_nesting", []);
|
||||
});
|
||||
it("exists when calling a napi constructor", () => {
|
||||
checkSameOutput("test_napi_class_constructor_handle_scope", []);
|
||||
});
|
||||
it("exists while calling a napi_async_complete_callback", () => {
|
||||
checkSameOutput("create_promise", []);
|
||||
});
|
||||
});
|
||||
|
||||
describe("escapable_handle_scope", () => {
|
||||
it("keeps the escaped value alive in the outer scope", () => {
|
||||
checkSameOutput("test_napi_escapable_handle_scope", []);
|
||||
});
|
||||
});
|
||||
|
||||
describe("napi_delete_property", () => {
|
||||
it("returns a valid boolean", () => {
|
||||
checkSameOutput(
|
||||
"test_napi_delete_property",
|
||||
// generate a string representing an array around an IIFE which main.js will eval
|
||||
// we do this as the napi_delete_property test needs an object with an own non-configurable
|
||||
// property
|
||||
"[(" +
|
||||
function () {
|
||||
const object = { foo: 42 };
|
||||
Object.defineProperty(object, "bar", {
|
||||
get() {
|
||||
return 1;
|
||||
},
|
||||
configurable: false,
|
||||
});
|
||||
return object;
|
||||
}.toString() +
|
||||
")()]",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function checkSameOutput(test: string, args: any[]) {
|
||||
function checkSameOutput(test: string, args: any[] | string) {
|
||||
const nodeResult = runOn("node", test, args).trim();
|
||||
let bunResult = runOn(bunExe(), test, args);
|
||||
// remove all debug logs
|
||||
@@ -80,9 +127,9 @@ function checkSameOutput(test: string, args: any[]) {
|
||||
return nodeResult;
|
||||
}
|
||||
|
||||
function runOn(executable: string, test: string, args: any[]) {
|
||||
function runOn(executable: string, test: string, args: any[] | string) {
|
||||
const exec = spawnSync({
|
||||
cmd: [executable, join(__dirname, "napi-app/main.js"), test, JSON.stringify(args)],
|
||||
cmd: [executable, join(__dirname, "napi-app/main.js"), test, typeof args == "string" ? args : JSON.stringify(args)],
|
||||
env: bunEnv,
|
||||
});
|
||||
const errs = exec.stderr.toString();
|
||||
|
||||
Reference in New Issue
Block a user