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:
Jarred Sumner
2023-12-22 00:55:58 +01:00
committed by GitHub
parent cd55401eba
commit a93f467a74
11 changed files with 446 additions and 201 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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);
}

View File

@@ -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 --- */

View File

@@ -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 --*/

View File

@@ -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;

View File

@@ -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,

View File

@@ -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()) {

View File

@@ -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)

View File

@@ -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();
}