mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 13:22:07 +00:00
NAPI fixes (#7765)
* napi fixes * Make bcrypt work * Always return this * Fixes #7685 * [autofix.ci] apply automated fixes * Update napi.cpp * Make it clearer what this is doing --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -3069,6 +3069,12 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
Bun::NapiExternal::createStructure(init.vm, init.owner, init.owner->objectPrototype()));
|
||||
});
|
||||
|
||||
m_NAPIFunctionStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
init.set(
|
||||
Zig::createNAPIFunctionStructure(init.vm, init.owner));
|
||||
});
|
||||
|
||||
m_NapiPrototypeStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
auto& global = *reinterpret_cast<Zig::GlobalObject*>(init.owner);
|
||||
@@ -3949,6 +3955,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
thisObject->m_cachedGlobalProxyStructure.visit(visitor);
|
||||
thisObject->m_NapiExternalStructure.visit(visitor);
|
||||
thisObject->m_NapiPrototypeStructure.visit(visitor);
|
||||
thisObject->m_NAPIFunctionStructure.visit(visitor);
|
||||
|
||||
thisObject->mockModule.mockFunctionStructure.visit(visitor);
|
||||
thisObject->mockModule.mockResultStructure.visit(visitor);
|
||||
|
||||
@@ -260,6 +260,7 @@ public:
|
||||
|
||||
Structure* NapiExternalStructure() { return m_NapiExternalStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NapiPrototypeStructure() { return m_NapiPrototypeStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NAPIFunctionStructure() { return m_NAPIFunctionStructure.getInitializedOnMainThread(this); }
|
||||
|
||||
bool hasProcessObject() const { return m_processObject.isInitialized(); }
|
||||
|
||||
@@ -521,6 +522,7 @@ public:
|
||||
LazyProperty<JSGlobalObject, Structure> m_JSCryptoKey;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiExternalStructure;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiPrototypeStructure;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NAPIFunctionStructure;
|
||||
|
||||
LazyProperty<JSGlobalObject, JSObject> m_bunObject;
|
||||
LazyProperty<JSGlobalObject, JSObject> m_cryptoObject;
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include <JavaScriptCore/JSSourceCode.h>
|
||||
#include "napi_external.h"
|
||||
#include <JavaScriptCore/JSArrayBuffer.h>
|
||||
#include <JavaScriptCore/FunctionPrototype.h>
|
||||
|
||||
// #include <iostream>
|
||||
using namespace JSC;
|
||||
@@ -64,7 +65,7 @@ using namespace Zig;
|
||||
#if NAPI_VERBOSE
|
||||
#include <stdio.h>
|
||||
#define NAPI_PREMABLE \
|
||||
printf("[napi] %s:%d:%s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
|
||||
printf("[napi.cpp:%d] %s\n", __LINE__, __PRETTY_FUNCTION__);
|
||||
#else
|
||||
|
||||
#endif // NAPI_VERBOSE
|
||||
@@ -110,7 +111,12 @@ public:
|
||||
void finalize(JSC::Handle<JSC::Unknown>, void* context) final
|
||||
{
|
||||
auto* weakValue = reinterpret_cast<NapiRef*>(context);
|
||||
weakValue->clear();
|
||||
|
||||
auto finalizer = weakValue->finalizer;
|
||||
if (finalizer.finalize_cb) {
|
||||
weakValue->finalizer.finalize_cb = nullptr;
|
||||
finalizer.call(weakValue->globalObject.get(), weakValue->data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,6 +129,7 @@ static NapiRefWeakHandleOwner& weakValueHandleOwner()
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -208,6 +215,184 @@ static uint32_t getPropertyAttributes(napi_property_descriptor prop)
|
||||
return result;
|
||||
}
|
||||
|
||||
class NAPICallFrame {
|
||||
public:
|
||||
NAPICallFrame(const JSC::ArgList args, void* dataPtr)
|
||||
: m_args(args)
|
||||
, m_dataPtr(dataPtr)
|
||||
{
|
||||
}
|
||||
|
||||
JSC::JSValue thisValue() const
|
||||
{
|
||||
return m_args.at(0);
|
||||
}
|
||||
|
||||
static constexpr uintptr_t NAPICallFramePtrTag = static_cast<uint64_t>(1) << 63;
|
||||
|
||||
static bool isNAPICallFramePtr(uintptr_t ptr)
|
||||
{
|
||||
return ptr & NAPICallFramePtrTag;
|
||||
}
|
||||
|
||||
static uintptr_t tagNAPICallFramePtr(uintptr_t ptr)
|
||||
{
|
||||
return ptr | NAPICallFramePtrTag;
|
||||
}
|
||||
|
||||
static napi_callback_info toNapiCallbackInfo(NAPICallFrame& frame)
|
||||
{
|
||||
return reinterpret_cast<napi_callback_info>(tagNAPICallFramePtr(reinterpret_cast<uintptr_t>(&frame)));
|
||||
}
|
||||
|
||||
static std::optional<NAPICallFrame*> get(JSC::CallFrame* callFrame)
|
||||
{
|
||||
uintptr_t ptr = reinterpret_cast<uintptr_t>(callFrame);
|
||||
if (!isNAPICallFramePtr(ptr)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ptr &= ~NAPICallFramePtrTag;
|
||||
return { reinterpret_cast<NAPICallFrame*>(ptr) };
|
||||
}
|
||||
|
||||
ALWAYS_INLINE const JSC::ArgList& args() const
|
||||
{
|
||||
return m_args;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void* dataPtr() const
|
||||
{
|
||||
return m_dataPtr;
|
||||
}
|
||||
|
||||
static void extract(NAPICallFrame& callframe, size_t* argc, // [in-out] Specifies the size of the provided argv array
|
||||
// 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)
|
||||
{
|
||||
if (this_arg != nullptr) {
|
||||
*this_arg = toNapi(callframe.thisValue());
|
||||
}
|
||||
|
||||
if (data != nullptr) {
|
||||
*data = callframe.dataPtr();
|
||||
}
|
||||
|
||||
size_t maxArgc = 0;
|
||||
if (argc != nullptr) {
|
||||
maxArgc = *argc;
|
||||
*argc = callframe.args().size() - 1;
|
||||
}
|
||||
|
||||
if (argv != nullptr) {
|
||||
size_t realArgCount = callframe.args().size() - 1;
|
||||
|
||||
size_t overflow = maxArgc > realArgCount ? maxArgc - realArgCount : 0;
|
||||
realArgCount = realArgCount < maxArgc ? realArgCount : maxArgc;
|
||||
|
||||
if (realArgCount > 0) {
|
||||
memcpy(argv, callframe.args().data() + 1, sizeof(napi_value) * realArgCount);
|
||||
argv += realArgCount;
|
||||
}
|
||||
|
||||
if (overflow > 0) {
|
||||
while (overflow--) {
|
||||
*argv = toNapi(jsUndefined());
|
||||
argv++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSC::JSValue newTarget;
|
||||
|
||||
private:
|
||||
const JSC::ArgList m_args;
|
||||
void* m_dataPtr;
|
||||
};
|
||||
|
||||
#define ADDRESS_OF_THIS_VALUE_IN_CALLFRAME(callframe) callframe->addressOfArgumentsStart() - 1
|
||||
|
||||
class NAPIFunction : public JSC::JSFunction {
|
||||
|
||||
public:
|
||||
using Base = JSC::JSFunction;
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
|
||||
static JSC::EncodedJSValue call(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe)
|
||||
{
|
||||
ASSERT(jsCast<NAPIFunction*>(callframe->jsCallee()));
|
||||
auto* function = static_cast<NAPIFunction*>(callframe->jsCallee());
|
||||
auto* env = toNapi(globalObject);
|
||||
auto* callback = reinterpret_cast<napi_callback>(function->m_method.get());
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
MarkedArgumentBufferWithSize<12> args;
|
||||
size_t argc = callframe->argumentCount() + 1;
|
||||
args.fill(vm, argc, [&](auto* slot) {
|
||||
memcpy(slot, ADDRESS_OF_THIS_VALUE_IN_CALLFRAME(callframe), sizeof(JSC::JSValue) * argc);
|
||||
});
|
||||
NAPICallFrame frame(JSC::ArgList(args), function->m_dataPtr);
|
||||
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
auto result = callback(env, NAPICallFrame::toNapiCallbackInfo(frame));
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(toJS(result)));
|
||||
}
|
||||
|
||||
NAPIFunction(JSC::VM& vm, JSC::NativeExecutable* exec, JSGlobalObject* globalObject, Structure* structure, JSC::NativeFunction method, void* dataPtr)
|
||||
: Base(vm, exec, globalObject, structure)
|
||||
, m_method(method)
|
||||
, m_dataPtr(dataPtr)
|
||||
{
|
||||
}
|
||||
|
||||
static NAPIFunction* create(JSC::VM& vm, Zig::GlobalObject* globalObject, unsigned length, const WTF::String& name, JSC::NativeFunction method, void* dataPtr)
|
||||
{
|
||||
|
||||
auto* structure = globalObject->NAPIFunctionStructure();
|
||||
NativeExecutable* executable = vm.getHostFunction(&NAPIFunction::call, ImplementationVisibility::Public, &NAPIFunction::call, name);
|
||||
NAPIFunction* functionObject = new (NotNull, JSC::allocateCell<NAPIFunction>(vm)) NAPIFunction(vm, executable, globalObject, structure, method, dataPtr);
|
||||
functionObject->finishCreation(vm, executable, length, name);
|
||||
return functionObject;
|
||||
}
|
||||
|
||||
void* m_dataPtr = nullptr;
|
||||
JSC::NativeFunction m_method = nullptr;
|
||||
|
||||
template<typename, SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<NAPIFunction, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForNAPIFunction.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNAPIFunction = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForNAPIFunction.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForNAPIFunction = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
|
||||
{
|
||||
ASSERT(globalObject);
|
||||
return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
|
||||
}
|
||||
};
|
||||
|
||||
const JSC::ClassInfo NAPIFunction::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NAPIFunction) };
|
||||
|
||||
Structure* Zig::createNAPIFunctionStructure(VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
ASSERT(globalObject);
|
||||
auto* prototype = globalObject->functionPrototype();
|
||||
return NAPIFunction::createStructure(vm, globalObject, prototype);
|
||||
}
|
||||
|
||||
static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* to, void* inheritedDataPtr, napi_property_descriptor property, bool isInstance, JSC::ThrowScope& scope)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -234,29 +419,16 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
|
||||
};
|
||||
|
||||
JSC::Identifier propertyName = getPropertyName();
|
||||
if (propertyName.isEmpty()) {
|
||||
if (!propertyName.isSymbol() && propertyName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (property.method) {
|
||||
JSC::JSValue value;
|
||||
auto method = reinterpret_cast<Zig::FFIFunction>(property.method);
|
||||
// if (!dataPtr) {
|
||||
// JSC::JSNativeStdFunction* func = JSC::JSNativeStdFunction::create(
|
||||
// globalObject->vm(), globalObject, 1, String(), [method](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
// JSC::MarkedArgumentBuffer values;
|
||||
// values.append(callFrame->thisValue());
|
||||
// for (int i = 0; i < callFrame->argumentCount(); i++) {
|
||||
// values.append(callFrame->argument(i));
|
||||
// }
|
||||
// return method(globalObject, callFrame);
|
||||
// });
|
||||
// value = JSC::JSValue(func);
|
||||
// } else {
|
||||
auto function = Zig::JSFFIFunction::create(vm, globalObject, 1, propertyName.isSymbol() ? String() : propertyName.string(), method);
|
||||
function->dataPtr = dataPtr;
|
||||
|
||||
auto* function = NAPIFunction::create(vm, globalObject, 1, propertyName.isSymbol() ? String() : propertyName.string(), method, dataPtr);
|
||||
value = JSC::JSValue(function);
|
||||
// }
|
||||
|
||||
to->putDirect(vm, propertyName, value, getPropertyAttributes(property));
|
||||
return;
|
||||
@@ -270,30 +442,17 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
|
||||
auto setterProperty = reinterpret_cast<FFIFunction>(property.setter);
|
||||
|
||||
if (getterProperty) {
|
||||
JSC::JSNativeStdFunction* getterFunction = JSC::JSNativeStdFunction::create(
|
||||
globalObject->vm(), globalObject, 0, String(), [getterProperty](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
JSC::MarkedArgumentBuffer values;
|
||||
values.append(callFrame->thisValue());
|
||||
return getterProperty(globalObject, callFrame);
|
||||
});
|
||||
getter = getterFunction;
|
||||
getter = NAPIFunction::create(vm, globalObject, 0, makeString("get "_s, propertyName.isSymbol() ? String() : propertyName.string()), getterProperty, dataPtr);
|
||||
} else {
|
||||
JSC::JSNativeStdFunction* getterFunction = JSC::JSNativeStdFunction::create(
|
||||
globalObject->vm(), globalObject, 0, String(), [](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
return JSC::JSValue::encode(JSC::jsUndefined());
|
||||
});
|
||||
setter = getterFunction;
|
||||
getter = getterFunction;
|
||||
}
|
||||
|
||||
if (setterProperty) {
|
||||
JSC::JSNativeStdFunction* setterFunction = JSC::JSNativeStdFunction::create(
|
||||
globalObject->vm(), globalObject, 1, String(), [setterProperty](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
JSC::MarkedArgumentBuffer values;
|
||||
values.append(callFrame->thisValue());
|
||||
values.append(callFrame->uncheckedArgument(0));
|
||||
return setterProperty(globalObject, callFrame);
|
||||
});
|
||||
setter = setterFunction;
|
||||
setter = NAPIFunction::create(vm, globalObject, 1, makeString("set "_s, propertyName.isSymbol() ? String() : propertyName.string()), setterProperty, dataPtr);
|
||||
} else {
|
||||
JSC::JSNativeStdFunction* setterFunction = JSC::JSNativeStdFunction::create(
|
||||
globalObject->vm(), globalObject, 1, String(), [](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
@@ -305,10 +464,10 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
|
||||
auto getterSetter = JSC::GetterSetter::create(vm, globalObject, getter, setter);
|
||||
to->putDirectAccessor(globalObject, propertyName, getterSetter, JSC::PropertyAttribute::Accessor | 0);
|
||||
} else {
|
||||
JSC::JSValue value = JSC::jsUndefined();
|
||||
JSC::JSValue value = toJS(property.value);
|
||||
|
||||
if (property.value) {
|
||||
value = toJS(property.value);
|
||||
if (value.isEmpty()) {
|
||||
value = JSC::jsUndefined();
|
||||
}
|
||||
|
||||
to->putDirect(vm, propertyName, value, getPropertyAttributes(property));
|
||||
@@ -335,7 +494,10 @@ extern "C" napi_status napi_set_property(napi_env env, napi_value target,
|
||||
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
PutPropertySlot slot(object, true);
|
||||
object->put(object, globalObject, keyProp.toPropertyKey(globalObject), toJS(value), slot);
|
||||
Identifier identifier = keyProp.toPropertyKey(globalObject);
|
||||
JSValue jsValue = toJS(value);
|
||||
|
||||
object->put(object, globalObject, identifier, jsValue, slot);
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
scope.clearException();
|
||||
@@ -454,10 +616,12 @@ extern "C" napi_status napi_set_named_property(napi_env env, napi_value object,
|
||||
JSC::EnsureStillAliveScope ensureAlive2(target);
|
||||
|
||||
auto nameStr = WTF::String::fromUTF8(utf8name, strlen(utf8name));
|
||||
auto name = JSC::PropertyName(JSC::Identifier::fromString(vm, WTFMove(nameStr)));
|
||||
auto identifier = JSC::Identifier::fromString(vm, WTFMove(nameStr));
|
||||
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
target->putDirect(globalObject->vm(), name, jsValue, 0);
|
||||
PutPropertySlot slot(target, true);
|
||||
|
||||
target->put(target, globalObject, identifier, jsValue, slot);
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
scope.clearException();
|
||||
return napi_ok;
|
||||
@@ -572,6 +736,9 @@ node_api_create_external_string_latin1(napi_env env,
|
||||
length = length == NAPI_AUTO_LENGTH ? strlen(str) : length;
|
||||
WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create(reinterpret_cast<LChar*>(str), static_cast<unsigned int>(length), finalize_hint, [finalize_callback](void* hint, void* str, unsigned length) {
|
||||
if (finalize_callback) {
|
||||
#if NAPI_VERBOSE
|
||||
printf("[napi] string finalize_callback\n");
|
||||
#endif
|
||||
finalize_callback(reinterpret_cast<napi_env>(Bun__getDefaultGlobal()), nullptr, hint);
|
||||
}
|
||||
});
|
||||
@@ -609,6 +776,10 @@ node_api_create_external_string_utf16(napi_env env,
|
||||
|
||||
length = length == NAPI_AUTO_LENGTH ? std::char_traits<char16_t>::length(str) : length;
|
||||
WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create(reinterpret_cast<UChar*>(str), static_cast<unsigned int>(length), finalize_hint, [finalize_callback](void* hint, void* str, unsigned length) {
|
||||
#if NAPI_VERBOSE
|
||||
printf("[napi] string finalize_callback\n");
|
||||
#endif
|
||||
|
||||
if (finalize_callback) {
|
||||
finalize_callback(reinterpret_cast<napi_env>(Bun__getDefaultGlobal()), nullptr, hint);
|
||||
}
|
||||
@@ -631,6 +802,7 @@ extern "C" void napi_module_register(napi_module* mod)
|
||||
{
|
||||
auto* globalObject = Bun__getDefaultGlobal();
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto keyStr = WTF::String::fromUTF8(mod->nm_modname);
|
||||
globalObject->napiModuleRegisterCallCount++;
|
||||
JSValue pendingNapiModule = globalObject->pendingNapiModule;
|
||||
JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject()
|
||||
@@ -643,10 +815,8 @@ extern "C" void napi_module_register(napi_module* mod)
|
||||
}
|
||||
|
||||
EnsureStillAliveScope ensureAlive(object);
|
||||
auto resultValue = toJS(
|
||||
mod->nm_register_func(toNapi(globalObject), toNapi(object)));
|
||||
JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object)));
|
||||
|
||||
auto keyStr = WTF::String::fromUTF8(mod->nm_modname);
|
||||
EnsureStillAliveScope ensureAlive2(resultValue);
|
||||
if (resultValue.isEmpty()) {
|
||||
JSValue errorInstance = createError(globalObject, makeString("Node-API module \""_s, keyStr, "\" returned an error"_s));
|
||||
@@ -792,14 +962,14 @@ extern "C" napi_status napi_unwrap(napi_env env, napi_value js_object,
|
||||
return NAPI_OBJECT_EXPECTED;
|
||||
}
|
||||
auto* globalObject = toJS(env);
|
||||
auto& vm = globalObject->vm();
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
|
||||
NapiRef* ref = nullptr;
|
||||
if (auto* val = jsDynamicCast<NapiPrototype*>(value)) {
|
||||
ref = val->napiRef;
|
||||
} else if (auto* val = jsDynamicCast<NapiClass*>(value)) {
|
||||
ref = val->napiRef;
|
||||
} else {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
if (ref && result) {
|
||||
@@ -824,28 +994,12 @@ extern "C" napi_status napi_create_function(napi_env env, const char* utf8name,
|
||||
}
|
||||
|
||||
auto method = reinterpret_cast<Zig::FFIFunction>(cb);
|
||||
// if (data) {
|
||||
auto function = Zig::JSFFIFunction::create(vm, globalObject, 1, name, method);
|
||||
function->dataPtr = data;
|
||||
auto* function = NAPIFunction::create(vm, globalObject, length, name, method, data);
|
||||
|
||||
if (result != nullptr) {
|
||||
*result = toNapi(JSC::JSValue(function));
|
||||
}
|
||||
|
||||
// } else {
|
||||
// JSC::JSNativeStdFunction* func = JSC::JSNativeStdFunction::create(
|
||||
// globalObject->vm(), globalObject, 1, String(), [method](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
|
||||
// JSC::MarkedArgumentBuffer values;
|
||||
// values.append(callFrame->thisValue());
|
||||
// for (int i = 0; i < callFrame->argumentCount(); i++) {
|
||||
// values.append(callFrame->argument(i));
|
||||
// }
|
||||
// return method(globalObject, callFrame);
|
||||
// });
|
||||
// *result = toNapi(JSC::JSValue(func));
|
||||
// }
|
||||
|
||||
// std::cout << "napi_create_function: " << utf8name << std::endl;
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -856,14 +1010,20 @@ extern "C" napi_status napi_get_cb_info(
|
||||
// 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) // [out] Receives the data pointer for the callback
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
auto inputArgsCount = argc == nullptr ? 0 : *argc;
|
||||
JSC::CallFrame* callFrame = reinterpret_cast<JSC::CallFrame*>(cbinfo);
|
||||
|
||||
if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) {
|
||||
NAPICallFrame::extract(*frame, argc, argv, this_arg, data);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
auto inputArgsCount = argc == nullptr ? 0 : *argc;
|
||||
|
||||
// napi expects arguments to be copied into the argv array.
|
||||
if (inputArgsCount > 0) {
|
||||
auto outputArgsCount = callFrame->argumentCount();
|
||||
@@ -991,12 +1151,19 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
NapiPrototype* object = jsDynamicCast<NapiPrototype*>(val);
|
||||
JSObject* jsObject = val.getObject();
|
||||
NapiPrototype* object = jsDynamicCast<NapiPrototype*>(jsObject);
|
||||
if (object && object->napiRef) {
|
||||
*result = toNapi(object->napiRef);
|
||||
return napi_ok;
|
||||
}
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
|
||||
NapiClass* object2 = jsDynamicCast<NapiClass*>(jsObject);
|
||||
if (object2 && object2->napiRef) {
|
||||
*result = toNapi(object2->napiRef);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
auto* ref = new NapiRef(globalObject, initial_refcount);
|
||||
if (initial_refcount > 0) {
|
||||
ref->strongRef.set(globalObject->vm(), val);
|
||||
@@ -1012,6 +1179,8 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
|
||||
|
||||
if (object) {
|
||||
object->napiRef = ref;
|
||||
} else if (object2) {
|
||||
object2->napiRef = ref;
|
||||
}
|
||||
|
||||
*result = toNapi(ref);
|
||||
@@ -1047,6 +1216,9 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object,
|
||||
}
|
||||
|
||||
vm.heap.addFinalizer(object, [=](JSCell* cell) -> void {
|
||||
#if NAPI_VERBOSE
|
||||
printf("napi_add_finalizer: %p\n", finalize_hint);
|
||||
#endif
|
||||
finalize_cb(env, native_object, finalize_hint);
|
||||
});
|
||||
|
||||
@@ -1096,7 +1268,7 @@ extern "C" napi_status napi_delete_reference(napi_env env, napi_ref ref)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->~NapiRef();
|
||||
delete napiRef;
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -1430,6 +1602,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);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
JSC::JSValue newTarget = callFrame->newTarget();
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(newTarget));
|
||||
return napi_ok;
|
||||
@@ -1460,7 +1638,6 @@ extern "C" napi_status napi_create_dataview(napi_env env, size_t length,
|
||||
}
|
||||
|
||||
namespace Zig {
|
||||
|
||||
template<typename Visitor>
|
||||
void NapiClass::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
@@ -1502,65 +1679,17 @@ JSC_DEFINE_HOST_FUNCTION(NapiClass_ConstructorFunction,
|
||||
callFrame->setThisValue(subclass);
|
||||
|
||||
size_t count = callFrame->argumentCount();
|
||||
MarkedArgumentBuffer args;
|
||||
MarkedArgumentBufferWithSize<12> args;
|
||||
size_t argc = callFrame->argumentCount() + 1;
|
||||
args.fill(vm, argc, [&](auto* slot) {
|
||||
memcpy(slot, ADDRESS_OF_THIS_VALUE_IN_CALLFRAME(callFrame), sizeof(JSC::JSValue) * argc);
|
||||
});
|
||||
NAPICallFrame frame(JSC::ArgList(args), nullptr);
|
||||
frame.newTarget = newTarget;
|
||||
|
||||
if (count > 6) {
|
||||
for (size_t i = 6; i < count; i++) {
|
||||
args.append(callFrame->uncheckedArgument(i));
|
||||
}
|
||||
}
|
||||
|
||||
napi->constructor()(globalObject, callFrame);
|
||||
auto result = napi->constructor()(globalObject, reinterpret_cast<JSC::CallFrame*>(NAPICallFrame::toNapiCallbackInfo(frame)));
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
JSC::JSValue thisValue = callFrame->thisValue();
|
||||
|
||||
switch (count) {
|
||||
case 0: {
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
JSC::ensureStillAliveHere(callFrame->argument(0));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
JSC::ensureStillAliveHere(callFrame->argument(0));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(1));
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
JSC::ensureStillAliveHere(callFrame->argument(0));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(1));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(2));
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
JSC::ensureStillAliveHere(callFrame->argument(0));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(1));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(2));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(3));
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
JSC::ensureStillAliveHere(callFrame->argument(0));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(1));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(2));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(3));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(4));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
JSC::ensureStillAliveHere(callFrame->argument(0));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(1));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(2));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(3));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(4));
|
||||
JSC::ensureStillAliveHere(callFrame->argument(5));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(thisValue));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(frame.thisValue()));
|
||||
}
|
||||
|
||||
NapiClass* NapiClass::create(VM& vm, Zig::GlobalObject* globalObject, const char* utf8name,
|
||||
@@ -1780,7 +1909,6 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
|
||||
void* finalize_hint,
|
||||
napi_value* result)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
@@ -1790,6 +1918,9 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
auto arrayBuffer = ArrayBuffer::createFromBytes(data, length, createSharedTask<void(void*)>([globalObject, finalize_hint, finalize_cb](void* p) {
|
||||
#if NAPI_VERBOSE
|
||||
printf("[napi] buffer finalize_callback\n");
|
||||
#endif
|
||||
if (finalize_cb != nullptr) {
|
||||
finalize_cb(toNapi(globalObject), p, finalize_hint);
|
||||
}
|
||||
@@ -1815,6 +1946,9 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
auto arrayBuffer = ArrayBuffer::createFromBytes(external_data, byte_length, createSharedTask<void(void*)>([globalObject, finalize_hint, finalize_cb](void* p) {
|
||||
#if NAPI_VERBOSE
|
||||
printf("[napi] arraybuffer finalize_callback\n");
|
||||
#endif
|
||||
if (finalize_cb != nullptr) {
|
||||
finalize_cb(toNapi(globalObject), p, finalize_hint);
|
||||
}
|
||||
@@ -1827,6 +1961,46 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_create_double(napi_env env, double value,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
|
||||
*result = toNapi(jsDoubleNumber(value));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_get_value_double(napi_env env, napi_value value,
|
||||
double* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto* globalObject = toJS(env);
|
||||
JSC::JSValue jsValue = toJS(value);
|
||||
|
||||
if (UNLIKELY(result == nullptr || !globalObject)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
|
||||
if (UNLIKELY(!jsValue || !jsValue.isNumber())) {
|
||||
return napi_number_expected;
|
||||
}
|
||||
|
||||
auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
|
||||
|
||||
*result = jsValue.toNumber(globalObject);
|
||||
|
||||
if (UNLIKELY(scope.exception())) {
|
||||
scope.clearException();
|
||||
return napi_generic_failure;
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_get_value_string_utf8(napi_env env,
|
||||
napi_value napiValue, char* buf,
|
||||
size_t bufsize,
|
||||
@@ -1917,7 +2091,6 @@ extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue,
|
||||
|
||||
extern "C" napi_status napi_create_object(napi_env env, napi_value* result)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (UNLIKELY(result == nullptr || env == nullptr)) {
|
||||
|
||||
@@ -101,6 +101,8 @@ public:
|
||||
~NapiRef()
|
||||
{
|
||||
strongRef.clear();
|
||||
// The weak ref can lead to calling the destructor
|
||||
// so we must first clear the weak ref before we call the finalizer
|
||||
weakValueRef.clear();
|
||||
}
|
||||
|
||||
@@ -240,4 +242,6 @@ static inline NapiRef* toJS(napi_ref val)
|
||||
return reinterpret_cast<NapiRef*>(val);
|
||||
}
|
||||
|
||||
Structure* createNAPIFunctionStructure(VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
}
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForInternalModuleRegistry;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBunInspectorConnection;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNextTickQueue;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNAPIFunction;
|
||||
#include "ZigGeneratedClasses+DOMClientIsoSubspaces.h"
|
||||
/* --- bun --- */
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ public:
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForInternalModuleRegistry;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForBunInspectorConnection;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSNextTickQueue;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNAPIFunction;
|
||||
|
||||
#include "ZigGeneratedClasses+DOMIsoSubspaces.h"
|
||||
/*-- BUN --*/
|
||||
|
||||
|
||||
@@ -3867,6 +3867,7 @@ pub const Expect = struct {
|
||||
return .zero;
|
||||
}
|
||||
|
||||
pub const toHaveReturned = notImplementedJSCFn;
|
||||
pub const toHaveReturnedTimes = notImplementedJSCFn;
|
||||
pub const toHaveReturnedWith = notImplementedJSCFn;
|
||||
pub const toHaveLastReturnedWith = notImplementedJSCFn;
|
||||
|
||||
@@ -289,6 +289,10 @@ export default [
|
||||
fn: "toBe",
|
||||
length: 1,
|
||||
},
|
||||
toBeCalled: {
|
||||
fn: "toHaveBeenCalled",
|
||||
length: 0,
|
||||
},
|
||||
toHaveBeenCalled: {
|
||||
fn: "toHaveBeenCalled",
|
||||
length: 0,
|
||||
@@ -297,19 +301,40 @@ export default [
|
||||
fn: "toHaveBeenCalledTimes",
|
||||
length: 1,
|
||||
},
|
||||
toBeCalledTimes: {
|
||||
fn: "toHaveBeenCalledTimes",
|
||||
length: 1,
|
||||
},
|
||||
toHaveBeenCalledWith: {
|
||||
fn: "toHaveBeenCalledWith",
|
||||
},
|
||||
toBeCalledWith: {
|
||||
fn: "toHaveBeenCalledWith",
|
||||
},
|
||||
toHaveBeenLastCalledWith: {
|
||||
fn: "toHaveBeenLastCalledWith",
|
||||
},
|
||||
lastCalledWith: {
|
||||
fn: "toHaveBeenLastCalledWith",
|
||||
},
|
||||
toHaveBeenNthCalledWith: {
|
||||
fn: "toHaveBeenNthCalledWith",
|
||||
},
|
||||
nthCalledWith: {
|
||||
fn: "toHaveBeenNthCalledWith",
|
||||
},
|
||||
toHaveReturnedTimes: {
|
||||
fn: "toHaveReturnedTimes",
|
||||
length: 1,
|
||||
},
|
||||
toReturn: {
|
||||
fn: "toHaveReturned",
|
||||
length: 1,
|
||||
},
|
||||
toHaveReturned: {
|
||||
fn: "toHaveReturned",
|
||||
length: 1,
|
||||
},
|
||||
toHaveReturnedWith: {
|
||||
fn: "toHaveReturnedWith",
|
||||
length: 1,
|
||||
@@ -318,10 +343,18 @@ export default [
|
||||
fn: "toHaveLastReturnedWith",
|
||||
length: 1,
|
||||
},
|
||||
lastReturnedWith: {
|
||||
fn: "toHaveLastReturnedWith",
|
||||
length: 1,
|
||||
},
|
||||
toHaveNthReturnedWith: {
|
||||
fn: "toHaveNthReturnedWith",
|
||||
length: 1,
|
||||
},
|
||||
nthReturnedWith: {
|
||||
fn: "toHaveNthReturnedWith",
|
||||
length: 1,
|
||||
},
|
||||
toHaveLength: {
|
||||
fn: "toHaveLength",
|
||||
length: 1,
|
||||
@@ -414,6 +447,10 @@ export default [
|
||||
fn: "toThrow",
|
||||
length: 1,
|
||||
},
|
||||
toThrowError: {
|
||||
fn: "toThrow",
|
||||
length: 1,
|
||||
},
|
||||
toThrowErrorMatchingSnapshot: {
|
||||
fn: "toThrowErrorMatchingSnapshot",
|
||||
length: 1,
|
||||
|
||||
@@ -247,11 +247,7 @@ pub export fn napi_create_array_with_length(env: napi_env, length: usize, result
|
||||
result.* = array;
|
||||
return .ok;
|
||||
}
|
||||
pub export fn napi_create_double(_: napi_env, value: f64, result: *napi_value) napi_status {
|
||||
log("napi_create_double", .{});
|
||||
result.* = JSValue.jsNumber(value);
|
||||
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 {
|
||||
log("napi_create_int32", .{});
|
||||
result.* = JSValue.jsNumber(value);
|
||||
@@ -370,14 +366,7 @@ pub extern fn napi_create_error(env: napi_env, code: napi_value, msg: napi_value
|
||||
pub extern fn napi_create_type_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status;
|
||||
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 export fn napi_get_value_double(env: napi_env, value: napi_value, result: *f64) napi_status {
|
||||
log("napi_get_value_double", .{});
|
||||
if (!value.isNumber()) {
|
||||
return .number_expected;
|
||||
}
|
||||
result.* = value.coerceToDouble(env);
|
||||
return .ok;
|
||||
}
|
||||
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 {
|
||||
log("napi_get_value_int32", .{});
|
||||
if (!value.isNumber()) {
|
||||
|
||||
@@ -1,74 +1,94 @@
|
||||
#include "node.h"
|
||||
|
||||
#include <napi.h>
|
||||
#include <iostream>
|
||||
#include <napi.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
napi_value fail(napi_env env, const char *msg)
|
||||
{
|
||||
napi_value result;
|
||||
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &result);
|
||||
return result;
|
||||
napi_value fail(napi_env env, const char *msg) {
|
||||
napi_value result;
|
||||
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
napi_value ok(napi_env env)
|
||||
{
|
||||
napi_value result;
|
||||
napi_get_undefined(env, &result);
|
||||
return result;
|
||||
napi_value ok(napi_env env) {
|
||||
napi_value result;
|
||||
napi_get_undefined(env, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
napi_value test_napi_get_value_string_utf8_with_buffer(const Napi::CallbackInfo &info)
|
||||
{
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
// get how many chars we need to copy
|
||||
uint32_t _len;
|
||||
if (napi_get_value_uint32(env, info[1], &_len) != napi_ok) {
|
||||
return fail(env, "call to napi_get_value_uint32 failed");
|
||||
}
|
||||
size_t len = (size_t)_len;
|
||||
|
||||
if (len == 424242) {
|
||||
len = NAPI_AUTO_LENGTH;
|
||||
} else if (len > 29) {
|
||||
return fail(env, "len > 29");
|
||||
}
|
||||
|
||||
size_t copied;
|
||||
size_t BUF_SIZE = 30;
|
||||
char buf[BUF_SIZE];
|
||||
memset(buf, '*', BUF_SIZE);
|
||||
buf[BUF_SIZE] = '\0';
|
||||
|
||||
if (napi_get_value_string_utf8(env, info[0], buf, len, &copied) != napi_ok) {
|
||||
return fail(env, "call to napi_get_value_string_utf8 failed");
|
||||
}
|
||||
|
||||
std::cout << "Chars to copy: " << len << std::endl;
|
||||
std::cout << "Copied chars: " << copied << std::endl;
|
||||
std::cout << "Buffer: ";
|
||||
for (size_t i = 0; i < BUF_SIZE; i++) {
|
||||
std::cout << (int)buf[i] << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << "Value str: " << buf << std::endl;
|
||||
return ok(env);
|
||||
napi_value test_issue_7685(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env(info.Env());
|
||||
Napi::HandleScope scope(env);
|
||||
#define napi_assert(expr) \
|
||||
{ \
|
||||
if (!expr) { \
|
||||
Napi::Error::New(env, #expr).ThrowAsJavaScriptException(); \
|
||||
} \
|
||||
}
|
||||
napi_assert(info[0].IsNumber());
|
||||
napi_assert(info[1].IsNumber());
|
||||
napi_assert(info[2].IsNumber());
|
||||
napi_assert(info[3].IsNumber());
|
||||
napi_assert(info[4].IsNumber());
|
||||
napi_assert(info[5].IsNumber());
|
||||
napi_assert(info[6].IsNumber());
|
||||
napi_assert(info[7].IsNumber());
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
Napi::Object InitAll(Napi::Env env, Napi::Object exports)
|
||||
{
|
||||
// check that these symbols are defined
|
||||
auto *isolate = v8::Isolate::GetCurrent();
|
||||
node::AddEnvironmentCleanupHook(
|
||||
isolate, [](void *) { }, isolate);
|
||||
node::RemoveEnvironmentCleanupHook(
|
||||
isolate, [](void *) { }, isolate);
|
||||
napi_value
|
||||
test_napi_get_value_string_utf8_with_buffer(const Napi::CallbackInfo &info) {
|
||||
Napi::Env env = info.Env();
|
||||
|
||||
exports.Set(
|
||||
"test_napi_get_value_string_utf8_with_buffer", Napi::Function::New(env, test_napi_get_value_string_utf8_with_buffer));
|
||||
return exports;
|
||||
// get how many chars we need to copy
|
||||
uint32_t _len;
|
||||
if (napi_get_value_uint32(env, info[1], &_len) != napi_ok) {
|
||||
return fail(env, "call to napi_get_value_uint32 failed");
|
||||
}
|
||||
size_t len = (size_t)_len;
|
||||
|
||||
if (len == 424242) {
|
||||
len = NAPI_AUTO_LENGTH;
|
||||
} else if (len > 29) {
|
||||
return fail(env, "len > 29");
|
||||
}
|
||||
|
||||
size_t copied;
|
||||
size_t BUF_SIZE = 30;
|
||||
char buf[BUF_SIZE];
|
||||
memset(buf, '*', BUF_SIZE);
|
||||
buf[BUF_SIZE] = '\0';
|
||||
|
||||
if (napi_get_value_string_utf8(env, info[0], buf, len, &copied) != napi_ok) {
|
||||
return fail(env, "call to napi_get_value_string_utf8 failed");
|
||||
}
|
||||
|
||||
std::cout << "Chars to copy: " << len << std::endl;
|
||||
std::cout << "Copied chars: " << copied << std::endl;
|
||||
std::cout << "Buffer: ";
|
||||
for (size_t i = 0; i < BUF_SIZE; i++) {
|
||||
std::cout << (int)buf[i] << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << "Value str: " << buf << std::endl;
|
||||
return ok(env);
|
||||
}
|
||||
|
||||
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
|
||||
// check that these symbols are defined
|
||||
auto *isolate = v8::Isolate::GetCurrent();
|
||||
node::AddEnvironmentCleanupHook(
|
||||
isolate, [](void *) {}, isolate);
|
||||
node::RemoveEnvironmentCleanupHook(
|
||||
isolate, [](void *) {}, isolate);
|
||||
|
||||
exports.Set("test_issue_7685", Napi::Function::New(env, test_issue_7685));
|
||||
|
||||
exports.Set(
|
||||
"test_napi_get_value_string_utf8_with_buffer",
|
||||
Napi::Function::New(env, test_napi_get_value_string_utf8_with_buffer));
|
||||
return exports;
|
||||
}
|
||||
|
||||
NODE_API_MODULE(napitests, InitAll)
|
||||
|
||||
@@ -31,6 +31,13 @@ describe("napi", () => {
|
||||
}
|
||||
});
|
||||
|
||||
describe("issue_7685", () => {
|
||||
it("works", () => {
|
||||
const args = [...Array(20).keys()];
|
||||
checkSameOutput("test_issue_7685", args);
|
||||
});
|
||||
});
|
||||
|
||||
describe("napi_get_value_string_utf8 with buffer", () => {
|
||||
// see https://github.com/oven-sh/bun/issues/6949
|
||||
it("copies one char", () => {
|
||||
@@ -75,7 +82,9 @@ function runOn(executable: string, test: string, args: any[]) {
|
||||
env: bunEnv,
|
||||
});
|
||||
const errs = exec.stderr.toString();
|
||||
expect(errs).toBe("");
|
||||
if (errs !== "") {
|
||||
throw new Error(errs);
|
||||
}
|
||||
expect(exec.success).toBeTrue();
|
||||
return exec.stdout.toString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user