Files
bun.sh/src/bun.js/bindings/napi.cpp
2024-10-30 18:55:27 -07:00

2705 lines
92 KiB
C++

#include "headers.h"
#include "node_api.h"
#include "root.h"
#include "JavaScriptCore/DateInstance.h"
#include "JavaScriptCore/JSCast.h"
#include "ZigGlobalObject.h"
#include "JavaScriptCore/JSGlobalObject.h"
#include "JavaScriptCore/SourceCode.h"
#include "js_native_api.h"
#include "napi_handle_scope.h"
#include "napi_macros.h"
#include "napi_finalizer.h"
#include "napi_type_tag.h"
#include "helpers.h"
#include <JavaScriptCore/JSObjectInlines.h>
#include <JavaScriptCore/JSCellInlines.h>
#include <wtf/text/ExternalStringImpl.h>
#include <wtf/text/StringCommon.h>
#include <wtf/text/StringImpl.h>
#include <JavaScriptCore/JSMicrotask.h>
#include <JavaScriptCore/ObjectConstructor.h>
#include <JavaScriptCore/JSModuleLoader.h>
#include <wtf/text/StringView.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
#include "BufferEncodingType.h"
#include <JavaScriptCore/AggregateError.h>
#include <JavaScriptCore/BytecodeIndex.h>
#include <JavaScriptCore/CallFrame.h>
#include <JavaScriptCore/CallFrameInlines.h>
#include <JavaScriptCore/ClassInfo.h>
#include <JavaScriptCore/CodeBlock.h>
#include <JavaScriptCore/Completion.h>
#include <JavaScriptCore/Error.h>
#include <JavaScriptCore/ErrorInstance.h>
#include <JavaScriptCore/Exception.h>
#include <JavaScriptCore/ExceptionScope.h>
#include <JavaScriptCore/FunctionConstructor.h>
#include <JavaScriptCore/Heap.h>
#include <JavaScriptCore/Identifier.h>
#include <JavaScriptCore/InitializeThreading.h>
#include <JavaScriptCore/IteratorOperations.h>
#include <JavaScriptCore/JSArray.h>
#include <JavaScriptCore/JSInternalPromise.h>
#include <JavaScriptCore/ObjectConstructor.h>
#include <JavaScriptCore/ArrayBuffer.h>
#include <JavaScriptCore/JSArrayBuffer.h>
#include "JSFFIFunction.h"
#include <JavaScriptCore/JavaScript.h>
#include "napi.h"
#include <JavaScriptCore/GetterSetter.h>
#include <JavaScriptCore/JSSourceCode.h>
#include <JavaScriptCore/JSNativeStdFunction.h>
#include <JavaScriptCore/BigIntObject.h>
#include "ScriptExecutionContext.h"
#include "Strong.h"
#include "../modules/ObjectModule.h"
#include <JavaScriptCore/JSSourceCode.h>
#include "napi_external.h"
#include "wtf/Compiler.h"
#include "wtf/NakedPtr.h"
#include <JavaScriptCore/JSArrayBuffer.h>
#include <JavaScriptCore/FunctionPrototype.h>
#include <JavaScriptCore/JSWeakMap.h>
#include <JavaScriptCore/JSWeakMapInlines.h>
#include "CommonJSModuleRecord.h"
#include "wtf/text/ASCIIFastPath.h"
#include "JavaScriptCore/WeakInlines.h"
// #include <iostream>
using namespace JSC;
using namespace Zig;
// Every NAPI function should use this at the start. It does the following:
// - if NAPI_VERBOSE is 1, log that the function was called
// - if env is nullptr, return napi_invalid_arg
// - if there is a pending exception, return napi_pending_exception
// No do..while is used as this declares a variable that other macros need to use
#define NAPI_PREAMBLE(_env) \
NAPI_LOG_CURRENT_FUNCTION; \
NAPI_CHECK_ARG(_env, _env); \
/* You should not use this throw scope directly -- if you need */ \
/* to throw or clear exceptions, make your own scope */ \
auto napi_preamble_throw_scope__ = DECLARE_THROW_SCOPE(toJS(_env)->vm()); \
NAPI_RETURN_IF_EXCEPTION(_env)
// Only use this for functions that need their own throw or catch scope. Functions that call into
// JS code that might throw should use NAPI_RETURN_IF_EXCEPTION.
#define NAPI_PREAMBLE_NO_THROW_SCOPE(_env) \
do { \
NAPI_LOG_CURRENT_FUNCTION; \
NAPI_CHECK_ARG(_env, _env); \
} while (0)
// Return an error code if arg is null. Only use for input validation.
#define NAPI_CHECK_ARG(_env, arg) \
do { \
if (UNLIKELY((arg) == nullptr)) { \
return napi_set_last_error(_env, napi_invalid_arg); \
} \
} while (0)
// Return the specified code if condition is false. Only use for input validation.
#define NAPI_RETURN_EARLY_IF_FALSE(_env, condition, code) \
do { \
if (!(condition)) { \
return napi_set_last_error(_env, code); \
} \
} while (0)
// Return an error code if an exception was thrown after NAPI_PREAMBLE
#define NAPI_RETURN_IF_EXCEPTION(_env) RETURN_IF_EXCEPTION(napi_preamble_throw_scope__, napi_set_last_error(_env, napi_pending_exception))
// Return indicating that no error occurred in a NAPI function, and an exception is not expected
#define NAPI_RETURN_SUCCESS(_env) \
do { \
napi_preamble_throw_scope__.assertNoException(); \
return napi_set_last_error(_env, napi_ok); \
} while (0)
// Return indicating that no error occurred in a NAPI function, unless an exception was thrown and not caught
#define NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(_env) \
do { \
NAPI_RETURN_IF_EXCEPTION(_env); \
return napi_set_last_error(_env, napi_ok); \
} while (0)
// Usage: `return napi_set_last_error(napi_ok);`
//
// Sets the global extended error info to indicate the passed-in status, and then returns it.
// All NAPI functions should call this in all places where they return, even if there is no error,
// because the extended error info should always reflect the most recent API call. The only
// exception is napi_get_last_error_info, which should return napi_ok without overwriting the
// extended error info.
//
// Usually, you should use the above macros instead of this function.
//
// This is not part of Node-API, it's a convenience function for Bun.
extern "C" napi_status napi_set_last_error(napi_env env, napi_status status)
{
if (env) {
// napi_get_last_error_info will fill in the other fields if they are requested
toJS(env)->m_lastNapiErrorInfo.error_code = status;
}
return status;
}
extern "C" napi_status
napi_get_last_error_info(napi_env env, const napi_extended_error_info** result)
{
// does not use NAPI_PREAMBLE as we don't want to skip the rest of this if there is an exception
NAPI_LOG_CURRENT_FUNCTION;
if (!env) {
return napi_invalid_arg;
}
NAPI_CHECK_ARG(env, result);
constexpr napi_status last_status = napi_would_deadlock;
constexpr const char* error_messages[] = {
nullptr, // napi_ok
"Invalid argument",
"An object was expected",
"A string was expected",
"A string or symbol was expected",
"A function was expected",
"A number was expected",
"A boolean was expected",
"An array was expected",
"Unknown failure",
"An exception is pending",
"The async work item was cancelled",
"napi_escape_handle already called on scope",
"Invalid handle scope usage",
"Invalid callback scope usage",
"Thread-safe function queue is full",
"Thread-safe function handle is closing",
"A bigint was expected",
"A date was expected",
"An arraybuffer was expected",
"A detachable arraybuffer was expected",
"Main thread would deadlock",
};
static_assert(std::size(error_messages) == last_status + 1,
"error_messages array does not cover all status codes");
auto globalObject = toJS(env);
napi_status status = globalObject->m_lastNapiErrorInfo.error_code;
if (status >= 0 && status <= last_status) {
globalObject->m_lastNapiErrorInfo.error_message = error_messages[status];
} else {
globalObject->m_lastNapiErrorInfo.error_message = nullptr;
}
*result = &globalObject->m_lastNapiErrorInfo;
// return without napi_return_status as that would overwrite the error info
return napi_ok;
}
namespace Napi {
JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject)
{
JSC::JSArray* exportKeys = ownPropertyKeys(globalObject, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include);
JSC::Identifier ident = JSC::Identifier::fromString(vm, "__BunTemporaryGlobal"_s);
WTF::StringBuilder sourceCodeBuilder = WTF::StringBuilder();
// TODO: handle symbol collision
sourceCodeBuilder.append("\nvar $$NativeModule = globalThis['__BunTemporaryGlobal']; console.log($$NativeModule); globalThis['__BunTemporaryGlobal'] = null;\n if (!$$NativeModule) { throw new Error('Assertion failure: Native module not found'); }\n\n"_s);
for (unsigned i = 0; i < exportKeys->length(); i++) {
auto key = exportKeys->getIndexQuickly(i);
if (key.isSymbol()) {
continue;
}
auto named = key.toWTFString(globalObject);
sourceCodeBuilder.append(""_s);
// TODO: handle invalid identifiers
sourceCodeBuilder.append("export var "_s);
sourceCodeBuilder.append(named);
sourceCodeBuilder.append(" = $$NativeModule."_s);
sourceCodeBuilder.append(named);
sourceCodeBuilder.append(";\n"_s);
}
globalObject->putDirect(vm, ident, object, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum);
return JSC::makeSource(sourceCodeBuilder.toString(), JSC::SourceOrigin(), JSC::SourceTaintedOrigin::Untainted, keyString, WTF::TextPosition(), JSC::SourceProviderSourceType::Module);
}
}
class NapiRefWeakHandleOwner final : public JSC::WeakHandleOwner {
public:
void finalize(JSC::Handle<JSC::Unknown>, void* context) final
{
auto* weakValue = reinterpret_cast<NapiRef*>(context);
weakValue->finalizer.call(weakValue->globalObject.get(), weakValue->data);
}
};
static NapiRefWeakHandleOwner& weakValueHandleOwner()
{
static NeverDestroyed<NapiRefWeakHandleOwner> jscWeakValueHandleOwner;
return jscWeakValueHandleOwner;
}
void NapiRef::ref()
{
++refCount;
if (refCount == 1 && !weakValueRef.isClear()) {
auto& vm = globalObject.get()->vm();
strongRef.set(vm, weakValueRef.get());
// isSet() will return always true after being set once
// We cannot rely on isSet() to check if the value is set we need to use isClear()
// .setString/.setObject/.setPrimitive will assert fail if called more than once (even after clear())
// We should not clear the weakValueRef here because we need to keep it if we call NapiRef::unref()
// so we can call the finalizer
}
}
void NapiRef::unref()
{
bool clear = refCount == 1;
refCount = refCount > 0 ? refCount - 1 : 0;
if (clear) {
// we still dont clean weakValueRef so we can ref it again using NapiRef::ref() if the GC didn't collect it
// and use it to call the finalizer when GC'd
strongRef.clear();
}
}
void NapiRef::clear()
{
this->finalizer.call(this->globalObject.get(), this->data);
this->globalObject.clear();
this->weakValueRef.clear();
this->strongRef.clear();
}
WTF_MAKE_ISO_ALLOCATED_IMPL(NapiRef);
static uint32_t getPropertyAttributes(napi_property_descriptor prop)
{
uint32_t result = 0;
const uint32_t attributes = static_cast<uint32_t>(prop.attributes);
if (!(attributes & static_cast<napi_property_attributes>(napi_key_configurable))) {
result |= JSC::PropertyAttribute::DontDelete;
}
if (!(attributes & static_cast<napi_property_attributes>(napi_key_enumerable))) {
result |= JSC::PropertyAttribute::DontEnum;
}
if (!(attributes & napi_key_writable || prop.setter != nullptr)) {
result |= JSC::PropertyAttribute::ReadOnly;
}
return result;
}
NapiWeakValue::~NapiWeakValue()
{
clear();
}
void NapiWeakValue::clear()
{
switch (m_tag) {
case WeakTypeTag::Cell: {
m_value.cell.clear();
break;
}
case WeakTypeTag::String: {
m_value.string.clear();
break;
}
default: {
break;
}
}
m_tag = WeakTypeTag::NotSet;
}
bool NapiWeakValue::isClear() const
{
return m_tag == WeakTypeTag::NotSet;
}
void NapiWeakValue::setPrimitive(JSValue value)
{
switch (m_tag) {
case WeakTypeTag::Cell: {
m_value.cell.clear();
break;
}
case WeakTypeTag::String: {
m_value.string.clear();
break;
}
default: {
break;
}
}
m_tag = WeakTypeTag::Primitive;
m_value.primitive = value;
}
void NapiWeakValue::set(JSValue value, WeakHandleOwner& owner, void* context)
{
if (value.isCell()) {
auto* cell = value.asCell();
if (cell->isString()) {
setString(jsCast<JSString*>(cell), owner, context);
} else {
setCell(cell, owner, context);
}
} else {
setPrimitive(value);
}
}
void NapiWeakValue::setCell(JSCell* cell, WeakHandleOwner& owner, void* context)
{
switch (m_tag) {
case WeakTypeTag::Cell: {
m_value.cell.clear();
break;
}
case WeakTypeTag::String: {
m_value.string.clear();
break;
}
default: {
break;
}
}
m_value.cell = JSC::Weak<JSCell>(cell, &owner, context);
m_tag = WeakTypeTag::Cell;
}
void NapiWeakValue::setString(JSString* string, WeakHandleOwner& owner, void* context)
{
switch (m_tag) {
case WeakTypeTag::Cell: {
m_value.cell.clear();
break;
}
default: {
break;
}
}
m_value.string = JSC::Weak<JSString>(string, &owner, context);
m_tag = WeakTypeTag::String;
}
class NAPICallFrame {
public:
NAPICallFrame(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame, void* dataPtr)
: m_callFrame(callFrame)
, m_dataPtr(dataPtr)
{
// Node-API function calls always run in "sloppy mode," even if the JS side is in strict
// mode. So if `this` is null or undefined, we use globalThis instead; otherwise, we convert
// `this` to an object.
// TODO change to global? or find another way to avoid JSGlobalProxy
JSC::JSObject* jscThis = globalObject->globalThis();
if (!m_callFrame->thisValue().isUndefinedOrNull()) {
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
jscThis = m_callFrame->thisValue().toObject(globalObject);
// https://tc39.es/ecma262/#sec-toobject
// toObject only throws for undefined and null, which we checked for
scope.assertNoException();
}
m_callFrame->setThisValue(jscThis);
}
JSValue thisValue() const
{
return m_callFrame->thisValue();
}
napi_callback_info toNapi()
{
return reinterpret_cast<napi_callback_info>(this);
}
ALWAYS_INLINE void* dataPtr() const
{
return m_dataPtr;
}
void extract(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, Zig::GlobalObject* globalObject)
{
if (this_arg != nullptr) {
*this_arg = ::toNapi(m_callFrame->thisValue(), globalObject);
}
if (data != nullptr) {
*data = dataPtr();
}
size_t maxArgc = 0;
if (argc != nullptr) {
maxArgc = *argc;
*argc = m_callFrame->argumentCount();
}
if (argv != nullptr) {
for (size_t i = 0; i < maxArgc; i++) {
// OK if we overflow argumentCount(), because argument() returns JS undefined
// for OOB which is what we want
argv[i] = ::toNapi(m_callFrame->argument(i), globalObject);
}
}
}
JSValue newTarget()
{
JSValue target = m_callFrame->newTarget();
if (target.isUndefined()) {
// napi_get_new_target:
// "This API returns the new.target of the constructor call. If the current callback
// is not a constructor call, the result is NULL."
// they mean a null pointer, not JavaScript null
return JSValue();
} else {
return target;
}
}
private:
JSC::CallFrame* m_callFrame;
void* m_dataPtr;
};
static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* to, napi_property_descriptor property, bool isInstance, JSC::ThrowScope& scope)
{
JSC::VM& vm = globalObject->vm();
void* dataPtr = property.data;
auto getPropertyName = [&]() -> JSC::Identifier {
if (property.utf8name != nullptr) {
size_t len = strlen(property.utf8name);
if (len > 0) {
return JSC::Identifier::fromString(vm, WTF::String::fromUTF8({ property.utf8name, len }).isolatedCopy());
}
}
if (!property.name) {
throwVMError(globalObject, scope, JSC::createTypeError(globalObject, "Property name is required"_s));
return JSC::Identifier();
}
JSValue nameValue = toJS(property.name);
return nameValue.toPropertyKey(globalObject);
};
JSC::Identifier propertyName = getPropertyName();
if (!propertyName.isSymbol() && propertyName.isEmpty()) {
return;
}
if (property.method) {
const char* utf8name = nullptr;
size_t length = 0;
if (!propertyName.isSymbol()) {
// TODO(@heimskr): do this better
utf8name = reinterpret_cast<const char*>(propertyName.string().span8().data());
length = propertyName.string().length();
}
JSValue value = NapiClass::create(vm, globalObject, utf8name, length, property.method, dataPtr, 0, nullptr);
to->putDirect(vm, propertyName, value, getPropertyAttributes(property));
return;
}
if (property.getter != nullptr || property.setter != nullptr) {
JSC::JSObject* getter = nullptr;
JSC::JSObject* setter = nullptr;
if (property.getter) {
auto name = makeString("get "_s, propertyName.isSymbol() ? String() : propertyName.string());
getter = NapiClass::create(vm, globalObject, reinterpret_cast<const char*>(name.span8().data()), name.length(), property.getter, dataPtr, 0, nullptr);
} else {
JSC::JSNativeStdFunction* getterFunction = JSC::JSNativeStdFunction::create(
globalObject->vm(), globalObject, 0, String(), [](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
return JSValue::encode(JSC::jsUndefined());
});
getter = getterFunction;
}
if (property.setter) {
auto name = makeString("set "_s, propertyName.isSymbol() ? String() : propertyName.string());
setter = NapiClass::create(vm, globalObject, reinterpret_cast<const char*>(name.span8().data()), name.length(), property.setter, dataPtr, 0, nullptr);
}
auto getterSetter = JSC::GetterSetter::create(vm, globalObject, getter, setter);
to->putDirectAccessor(globalObject, propertyName, getterSetter, PropertyAttribute::Accessor | getPropertyAttributes(property));
} else {
JSValue value = toJS(property.value);
if (value.isEmpty()) {
value = JSC::jsUndefined();
}
to->putDirect(vm, propertyName, value, getPropertyAttributes(property));
}
}
extern "C" napi_status napi_set_property(napi_env env, napi_value target,
napi_value key, napi_value value)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, target);
NAPI_CHECK_ARG(env, key);
NAPI_CHECK_ARG(env, value);
JSValue targetValue = toJS(target);
NAPI_RETURN_EARLY_IF_FALSE(env, targetValue.isObject(), napi_object_expected);
auto globalObject = toJS(env);
auto* object = targetValue.getObject();
auto keyProp = toJS(key);
PutPropertySlot slot(object, false);
Identifier identifier = keyProp.toPropertyKey(globalObject);
NAPI_RETURN_IF_EXCEPTION(env);
JSValue jsValue = toJS(value);
bool putResult = object->put(object, globalObject, identifier, jsValue, slot);
NAPI_RETURN_IF_EXCEPTION(env);
if (!putResult) return napi_set_last_error(env, napi_generic_failure);
// we should have returned if there is an exception
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_has_property(napi_env env, napi_value object,
napi_value key, bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, key);
auto globalObject = toJS(env);
auto* target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
auto keyProp = toJS(key);
*result = target->hasProperty(globalObject, keyProp.toPropertyKey(globalObject));
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_get_date_value(napi_env env, napi_value value, double* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
JSValue jsValue = toJS(value);
auto* date = jsDynamicCast<JSC::DateInstance*>(jsValue);
NAPI_RETURN_EARLY_IF_FALSE(env, date != nullptr, napi_date_expected);
*result = date->internalNumber();
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_property(napi_env env, napi_value object,
napi_value key, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, key);
NAPI_CHECK_ARG(env, result);
auto globalObject = toJS(env);
auto* target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
JSC::EnsureStillAliveScope ensureAlive(target);
auto keyProp = toJS(key);
JSC::EnsureStillAliveScope ensureAlive2(keyProp);
*result = toNapi(target->get(globalObject, keyProp.toPropertyKey(globalObject)), globalObject);
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_delete_property(napi_env env, napi_value object,
napi_value key, bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, key);
auto globalObject = toJS(env);
auto& vm = globalObject->vm();
auto* target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
auto keyProp = toJS(key);
auto scope = DECLARE_CATCH_SCOPE(vm);
auto deleteResult = target->deleteProperty(globalObject, keyProp.toPropertyKey(globalObject));
NAPI_RETURN_IF_EXCEPTION(env);
if (LIKELY(result)) {
*result = deleteResult;
}
// we checked for an exception above
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_has_own_property(napi_env env, napi_value object,
napi_value key, bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, key);
NAPI_CHECK_ARG(env, result);
auto globalObject = toJS(env);
auto* target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
auto keyProp = toJS(key);
NAPI_RETURN_EARLY_IF_FALSE(env, keyProp.isString() || keyProp.isSymbol(), napi_name_expected);
*result = target->hasOwnProperty(globalObject, JSC::PropertyName(keyProp.toPropertyKey(globalObject)));
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_set_named_property(napi_env env, napi_value object,
const char* utf8name,
napi_value value)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, utf8name);
// TODO find a way to permit empty strings
NAPI_RETURN_EARLY_IF_FALSE(env, *utf8name, napi_invalid_arg);
NAPI_CHECK_ARG(env, value);
auto target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
auto globalObject = toJS(env);
auto& vm = globalObject->vm();
JSValue jsValue = toJS(value);
JSC::EnsureStillAliveScope ensureAlive(jsValue);
JSC::EnsureStillAliveScope ensureAlive2(target);
auto nameStr = WTF::String::fromUTF8({ utf8name, strlen(utf8name) });
auto identifier = JSC::Identifier::fromString(vm, WTFMove(nameStr));
// TODO should maybe be false
PutPropertySlot slot(target, false);
target->put(target, globalObject, identifier, jsValue, slot);
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_create_arraybuffer(napi_env env,
size_t byte_length, void** data,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
auto& vm = globalObject->vm();
// Node probably doesn't create uninitialized array buffers
// but the node-api docs don't specify whether memory is initialized or not.
RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::tryCreateUninitialized(byte_length, 1);
if (!arrayBuffer) {
return napi_set_last_error(env, napi_generic_failure);
}
auto* jsArrayBuffer = JSC::JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(), WTFMove(arrayBuffer));
NAPI_RETURN_IF_EXCEPTION(env);
if (LIKELY(data && jsArrayBuffer->impl())) {
*data = jsArrayBuffer->impl()->data();
}
*result = toNapi(jsArrayBuffer, globalObject);
NAPI_RETURN_SUCCESS(env);
}
// This is more efficient than using WTF::String::FromUTF8
// it doesn't copy the string
// but it's only safe to use if we are not setting a property
// because we can't guarantee the lifetime of it
#define PROPERTY_NAME_FROM_UTF8(identifierName) \
size_t utf8Len = strlen(utf8Name); \
WTF::String nameString = LIKELY(WTF::charactersAreAllASCII(std::span { reinterpret_cast<const LChar*>(utf8Name), utf8Len })) \
? WTF::String(WTF::StringImpl::createWithoutCopying({ utf8Name, utf8Len })) \
: WTF::String::fromUTF8(utf8Name); \
JSC::PropertyName identifierName = JSC::Identifier::fromString(vm, nameString);
extern "C" napi_status napi_has_named_property(napi_env env, napi_value object,
const char* utf8Name,
bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, utf8Name);
NAPI_CHECK_ARG(env, result);
auto globalObject = toJS(env);
auto& vm = globalObject->vm();
JSObject* target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
PROPERTY_NAME_FROM_UTF8(name);
auto scope = DECLARE_CATCH_SCOPE(vm);
PropertySlot slot(target, PropertySlot::InternalMethodType::HasProperty);
*result = target->getPropertySlot(globalObject, name, slot);
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_get_named_property(napi_env env, napi_value object,
const char* utf8Name,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, utf8Name);
NAPI_CHECK_ARG(env, result);
auto globalObject = toJS(env);
auto& vm = globalObject->vm();
JSObject* target = toJS(object).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, target, napi_object_expected);
PROPERTY_NAME_FROM_UTF8(name);
*result = toNapi(target->get(globalObject, name), globalObject);
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status
node_api_create_external_string_latin1(napi_env env,
char* str,
size_t length,
napi_finalize finalize_callback,
void* finalize_hint,
napi_value* result,
bool* copied)
{
// https://nodejs.org/api/n-api.html#node_api_create_external_string_latin1
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, str);
NAPI_CHECK_ARG(env, result);
length = length == NAPI_AUTO_LENGTH ? strlen(str) : length;
Ref<WTF::ExternalStringImpl> impl = WTF::ExternalStringImpl::create({ reinterpret_cast<const LChar*>(str), static_cast<unsigned int>(length) }, finalize_hint, [finalize_callback, env](void* hint, void* str, unsigned length) {
if (finalize_callback) {
NAPI_LOG("finalizer");
finalize_callback(env, str, hint);
}
});
Zig::GlobalObject* globalObject = toJS(env);
JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl.get()));
ensureStillAliveHere(out);
*result = toNapi(out, globalObject);
ensureStillAliveHere(out);
if (copied) {
*copied = false;
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status
node_api_create_external_string_utf16(napi_env env,
char16_t* str,
size_t length,
napi_finalize finalize_callback,
void* finalize_hint,
napi_value* result,
bool* copied)
{
// https://nodejs.org/api/n-api.html#node_api_create_external_string_utf16
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, str);
NAPI_CHECK_ARG(env, result);
length = length == NAPI_AUTO_LENGTH ? std::char_traits<char16_t>::length(str) : length;
Ref<WTF::ExternalStringImpl> impl = WTF::ExternalStringImpl::create({ reinterpret_cast<const UChar*>(str), static_cast<unsigned int>(length) }, finalize_hint, [finalize_callback, env](void* hint, void* str, unsigned length) {
if (finalize_callback) {
NAPI_LOG("finalizer");
finalize_callback(env, str, hint);
}
});
Zig::GlobalObject* globalObject = toJS(env);
JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl.get()));
ensureStillAliveHere(out);
*result = toNapi(out, globalObject);
ensureStillAliveHere(out);
NAPI_RETURN_SUCCESS(env);
}
extern "C" void napi_module_register(napi_module* mod)
{
auto* globalObject = defaultGlobalObject();
JSC::VM& vm = globalObject->vm();
auto keyStr = WTF::String::fromUTF8(mod->nm_modname);
globalObject->napiModuleRegisterCallCount++;
JSValue pendingNapiModule = globalObject->m_pendingNapiModuleAndExports[0].get();
JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject()
: nullptr;
auto scope = DECLARE_THROW_SCOPE(vm);
JSC::Strong<JSC::JSObject> strongExportsObject;
if (!object) {
auto* exportsObject = JSC::constructEmptyObject(globalObject);
RETURN_IF_EXCEPTION(scope, void());
object = Bun::JSCommonJSModule::create(globalObject, keyStr, exportsObject, false, jsUndefined());
strongExportsObject = { vm, exportsObject };
} else {
JSValue exportsObject = object->getIfPropertyExists(globalObject, WebCore::builtinNames(vm).exportsPublicName());
RETURN_IF_EXCEPTION(scope, void());
if (exportsObject && exportsObject.isObject()) {
strongExportsObject = { vm, exportsObject.getObject() };
}
}
JSC::Strong<JSC::JSObject> strongObject = { vm, object };
Bun::NapiHandleScope handleScope(globalObject);
JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object, globalObject)));
RETURN_IF_EXCEPTION(scope, void());
if (resultValue.isEmpty()) {
JSValue errorInstance = createError(globalObject, makeString("Node-API module \""_s, keyStr, "\" returned an error"_s));
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
return;
}
if (!resultValue.isObject()) {
JSValue errorInstance = createError(globalObject, makeString("Expected Node-API module \""_s, keyStr, "\" to return an exports object"_s));
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
return;
}
// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/node_api.cc#L734-L742
// https://github.com/oven-sh/bun/issues/1288
if (!scope.exception() && strongExportsObject && strongExportsObject.get() != resultValue) {
PutPropertySlot slot(strongObject.get(), false);
strongObject->put(strongObject.get(), globalObject, WebCore::builtinNames(vm).exportsPublicName(), resultValue, slot);
}
globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, object);
}
extern "C" napi_status napi_wrap(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
// Typically when wrapping a class instance, a finalize callback should be
// provided that simply deletes the native instance that is received as the
// data argument to the finalize callback.
void* finalize_hint,
napi_ref* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, js_object);
auto* globalObject = toJS(env);
JSValue value = toJS(js_object);
NAPI_RETURN_EARLY_IF_FALSE(env, value.isCell(), napi_object_expected);
JSCell* cell = value.asCell();
if (globalObject->napiWraps()->has(cell)) {
// Calling napi_wrap() a second time on an object will return an error.
// To associate another native instance with the object, use
// napi_remove_wrap() first.
return napi_set_last_error(env, napi_invalid_arg);
}
// create a new weak reference (refcount 0)
auto* ref = new NapiRef(globalObject, 0, Bun::NapiFinalizer { finalize_cb, finalize_hint });
ref->data = native_object;
ref->weakValueRef.set(value, weakValueHandleOwner(), ref);
// wrap the ref in an external so that it can serve as a JSValue
auto* external = Bun::NapiExternal::create(globalObject->vm(),
globalObject->NapiExternalStructure(),
reinterpret_cast<void*>(ref),
nullptr,
nullptr);
globalObject->napiWraps()->set(globalObject->vm(), cell, external);
if (result) {
*result = toNapi(ref);
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_remove_wrap(napi_env env, napi_value js_object,
void** result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, js_object);
JSValue value = toJS(js_object);
NAPI_RETURN_EARLY_IF_FALSE(env, value.isCell(), napi_object_expected);
Zig::GlobalObject* globalObject = toJS(env);
auto* external = jsDynamicCast<Bun::NapiExternal*>(globalObject->napiWraps()->get(value.asCell()));
if (!external) {
// object had not been wrapped, so there is nothing to do
NAPI_RETURN_SUCCESS(env);
}
auto* ref = reinterpret_cast<NapiRef*>(external->m_value);
if (result) {
*result = ref->data;
}
external->m_value = nullptr;
ref->finalizer = Bun::NapiFinalizer {};
globalObject->napiWraps()->remove(value.asCell());
// don't delete the ref: if weak, it'll delete itself when the JS object is deleted;
// if strong, native addon needs to clean it up.
// the external is garbage collected.
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_unwrap(napi_env env, napi_value js_object,
void** result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, js_object);
NAPI_CHECK_ARG(env, result);
JSValue value = toJS(js_object);
NAPI_RETURN_EARLY_IF_FALSE(env, value.isCell(), napi_object_expected);
Zig::GlobalObject* globalObject = toJS(env);
auto* external = jsDynamicCast<Bun::NapiExternal*>(globalObject->napiWraps()->get(value.asCell()));
NAPI_RETURN_EARLY_IF_FALSE(env, external && external->m_value, napi_invalid_arg);
auto* ref = reinterpret_cast<NapiRef*>(external->m_value);
*result = ref->data;
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_function(napi_env env, const char* utf8name,
size_t length, napi_callback cb,
void* data, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, cb);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
auto name = WTF::String();
if (utf8name != nullptr) {
name = WTF::String::fromUTF8({ utf8name, length == NAPI_AUTO_LENGTH ? strlen(utf8name) : length });
}
NapiClass* function {};
if (utf8name != nullptr) {
function = NapiClass::create(vm, globalObject, utf8name, length == NAPI_AUTO_LENGTH ? strlen(utf8name) : length, cb, data, 0, nullptr);
} else {
function = NapiClass::create(vm, globalObject, nullptr, 0, cb, data, 0, nullptr);
}
ASSERT(function->isCallable());
*result = toNapi(JSValue(function), globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_cb_info(
napi_env env, // [in] NAPI environment handle
napi_callback_info cbinfo, // [in] Opaque callback-info handle
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) // [out] Receives the data pointer for the callback
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, cbinfo);
auto* callFrame = reinterpret_cast<NAPICallFrame*>(cbinfo);
Zig::GlobalObject* globalObject = toJS(env);
callFrame->extract(argc, argv, this_arg, data, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status
napi_define_properties(napi_env env, napi_value object, size_t property_count,
const napi_property_descriptor* properties)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_RETURN_EARLY_IF_FALSE(env, properties || property_count == 0, napi_invalid_arg);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSValue objectValue = toJS(object);
JSC::JSObject* objectObject = objectValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, objectObject, napi_object_expected);
auto throwScope = DECLARE_THROW_SCOPE(vm);
for (size_t i = 0; i < property_count; i++) {
defineNapiProperty(globalObject, objectObject, properties[i], true, throwScope);
RETURN_IF_EXCEPTION(throwScope, napi_set_last_error(env, napi_pending_exception));
}
throwScope.release();
NAPI_RETURN_SUCCESS(env);
}
static JSC::ErrorInstance* createErrorWithCode(JSC::JSGlobalObject* globalObject, const WTF::String& code, const WTF::String& message, JSC::ErrorType type)
{
// no napi functions permit a null message, they must check before calling this function and
// return the right error code
ASSERT(!message.isNull());
auto& vm = globalObject->vm();
// we don't call JSC::createError() as it asserts the message is not an empty string ""
auto* error = JSC::ErrorInstance::create(globalObject->vm(), globalObject->errorStructure(type), message, JSValue(), nullptr, RuntimeType::TypeNothing, type);
if (!code.isNull()) {
error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), JSC::jsString(vm, code), 0);
}
return error;
}
// used to implement napi_throw_*_error
static napi_status throwErrorWithCStrings(napi_env env, const char* code_utf8, const char* msg_utf8, JSC::ErrorType type)
{
auto* globalObject = toJS(env);
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (!msg_utf8) {
return napi_set_last_error(env, napi_invalid_arg);
}
WTF::String code = code_utf8 ? WTF::String::fromUTF8(code_utf8) : WTF::String();
WTF::String message = WTF::String::fromUTF8(msg_utf8);
auto* error = createErrorWithCode(globalObject, code, message, type);
scope.throwException(globalObject, error);
return napi_set_last_error(env, napi_ok);
}
// code must be a string or nullptr (no code)
// msg must be a string
// never calls toString, never throws
static napi_status createErrorWithNapiValues(napi_env env, napi_value code, napi_value message, JSC::ErrorType type, napi_value* result)
{
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, message);
JSValue js_code = toJS(code);
JSValue js_message = toJS(message);
NAPI_RETURN_EARLY_IF_FALSE(env,
js_message.isString() && (js_code.isEmpty() || js_code.isString()),
napi_string_expected);
auto* globalObject = toJS(env);
auto wtf_code = js_code.isEmpty() ? WTF::String() : js_code.getString(globalObject);
auto wtf_message = js_message.getString(globalObject);
*result = toNapi(
createErrorWithCode(globalObject, wtf_code, wtf_message, type),
globalObject);
return napi_set_last_error(env, napi_ok);
}
extern "C" napi_status napi_throw_error(napi_env env,
const char* code,
const char* msg)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
return throwErrorWithCStrings(env, code, msg, JSC::ErrorType::Error);
}
extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
uint32_t initial_refcount,
napi_ref* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
JSValue val = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, val.isCell(), napi_object_expected);
Zig::GlobalObject* globalObject = toJS(env);
auto* ref = new NapiRef(globalObject, initial_refcount, Bun::NapiFinalizer {});
if (initial_refcount > 0) {
ref->strongRef.set(globalObject->vm(), val);
}
ref->weakValueRef.set(val, weakValueHandleOwner(), ref);
*result = toNapi(ref);
NAPI_RETURN_SUCCESS(env);
}
extern "C" void napi_set_ref(NapiRef* ref, JSC__JSValue val_)
{
NAPI_LOG_CURRENT_FUNCTION;
JSValue val = JSValue::decode(val_);
if (val) {
ref->strongRef.set(ref->globalObject->vm(), val);
} else {
ref->strongRef.clear();
}
}
extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
void* finalize_hint,
napi_ref* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, js_object);
NAPI_CHECK_ARG(env, finalize_cb);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSValue objectValue = toJS(js_object);
JSC::JSObject* object = objectValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, object, napi_object_expected);
vm.heap.addFinalizer(object, [finalize_cb, env, native_object, finalize_hint](JSCell* cell) -> void {
NAPI_LOG("finalizer %p", finalize_hint);
finalize_cb(env, native_object, finalize_hint);
});
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_reference_unref(napi_env env, napi_ref ref,
uint32_t* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, ref);
NapiRef* napiRef = toJS(ref);
napiRef->unref();
if (LIKELY(result)) {
*result = napiRef->refCount;
}
NAPI_RETURN_SUCCESS(env);
}
// Attempts to get a referenced value. If the reference is weak,
// the value might no longer be available, in that case the call
// is still successful but the result is NULL.
extern "C" napi_status napi_get_reference_value(napi_env env, napi_ref ref,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, ref);
NAPI_CHECK_ARG(env, result);
NapiRef* napiRef = toJS(ref);
*result = toNapi(napiRef->value(), toJS(env));
NAPI_RETURN_SUCCESS(env);
}
extern "C" JSC__JSValue napi_get_reference_value_internal(NapiRef* napiRef)
{
NAPI_LOG_CURRENT_FUNCTION;
return JSValue::encode(napiRef->value());
}
extern "C" napi_status napi_reference_ref(napi_env env, napi_ref ref,
uint32_t* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, ref);
NapiRef* napiRef = toJS(ref);
napiRef->ref();
if (LIKELY(result)) {
*result = napiRef->refCount;
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_delete_reference(napi_env env, napi_ref ref)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, ref);
NapiRef* napiRef = toJS(ref);
delete napiRef;
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_is_detached_arraybuffer(napi_env env,
napi_value arraybuffer,
bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, arraybuffer);
NAPI_CHECK_ARG(env, result);
JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(toJS(arraybuffer));
NAPI_RETURN_EARLY_IF_FALSE(env, jsArrayBuffer, napi_arraybuffer_expected);
auto* arrayBuffer = jsArrayBuffer->impl();
*result = arrayBuffer->isDetached();
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_detach_arraybuffer(napi_env env,
napi_value arraybuffer)
{
NAPI_PREAMBLE(env);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(toJS(arraybuffer));
NAPI_RETURN_EARLY_IF_FALSE(env, jsArrayBuffer, napi_arraybuffer_expected);
auto* arrayBuffer = jsArrayBuffer->impl();
if (!arrayBuffer->isDetached() && arrayBuffer->isDetachable()) {
arrayBuffer->detach(vm);
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_adjust_external_memory(napi_env env,
int64_t change_in_bytes,
int64_t* adjusted_value)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, adjusted_value);
JSC::Heap& heap = toJS(env)->vm().heap;
if (change_in_bytes > 0) {
heap.deprecatedReportExtraMemory(change_in_bytes);
}
*adjusted_value = heap.extraMemorySize();
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_is_exception_pending(napi_env env, bool* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
NAPI_CHECK_ARG(env, result);
auto globalObject = toJS(env);
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
*result = scope.exception() != nullptr;
// skip macros as they assume we made a throw scope in the preamble
return napi_set_last_error(env, napi_ok);
}
extern "C" napi_status napi_get_and_clear_last_exception(napi_env env,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
if (UNLIKELY(!result)) {
return napi_invalid_arg;
}
auto globalObject = toJS(env);
auto scope = DECLARE_CATCH_SCOPE(globalObject->vm());
if (scope.exception()) {
*result = toNapi(JSValue(scope.exception()->value()), globalObject);
} else {
*result = toNapi(JSC::jsUndefined(), globalObject);
}
scope.clearException();
return napi_set_last_error(env, napi_ok);
}
extern "C" napi_status napi_fatal_exception(napi_env env,
napi_value err)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, err);
auto globalObject = toJS(env);
JSValue value = toJS(err);
JSC::JSObject* obj = value.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, obj && obj->isErrorInstance(), napi_invalid_arg);
Bun__reportUnhandledError(globalObject, JSValue::encode(value));
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_throw(napi_env env, napi_value error)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
auto globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
JSValue value = toJS(error);
if (value) {
JSC::throwException(globalObject, throwScope, value);
} else {
JSC::throwException(globalObject, throwScope, JSC::createError(globalObject, "Error (via napi)"_s));
}
return napi_set_last_error(env, napi_ok);
}
extern "C" napi_status node_api_symbol_for(napi_env env,
const char* utf8description,
size_t length, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
auto* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
if (utf8description == nullptr) {
if (length == 0) {
utf8description = "";
} else {
NAPI_CHECK_ARG(env, utf8description);
}
}
auto description = WTF::String::fromUTF8({ utf8description, length == NAPI_AUTO_LENGTH ? strlen(utf8description) : length });
*result = toNapi(JSC::Symbol::create(vm, vm.symbolRegistry().symbolForKey(description)), globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status node_api_create_syntax_error(napi_env env,
napi_value code,
napi_value msg,
napi_value* result)
{
NAPI_PREAMBLE(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::SyntaxError, result);
}
extern "C" napi_status node_api_throw_syntax_error(napi_env env,
const char* code,
const char* msg)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
return throwErrorWithCStrings(env, code, msg, JSC::ErrorType::SyntaxError);
}
extern "C" napi_status napi_throw_type_error(napi_env env, const char* code,
const char* msg)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
return throwErrorWithCStrings(env, code, msg, JSC::ErrorType::TypeError);
}
extern "C" napi_status napi_create_type_error(napi_env env, napi_value code,
napi_value msg,
napi_value* result)
{
NAPI_PREAMBLE(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::TypeError, result);
}
extern "C" napi_status napi_create_error(napi_env env, napi_value code,
napi_value msg,
napi_value* result)
{
NAPI_PREAMBLE(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::Error, result);
}
extern "C" napi_status napi_throw_range_error(napi_env env, const char* code,
const char* msg)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
return throwErrorWithCStrings(env, code, msg, JSC::ErrorType::RangeError);
}
extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object_value);
JSValue value = toJS(object_value);
NAPI_RETURN_EARLY_IF_FALSE(env, value.isObject(), napi_object_expected);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSC::JSObject* object = JSC::jsCast<JSC::JSObject*>(value);
// TODO is this check necessary?
if (!hasIndexedProperties(object->indexingType())) {
object->freeze(vm);
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object_value);
JSValue value = toJS(object_value);
NAPI_RETURN_EARLY_IF_FALSE(env, value.isObject(), napi_object_expected);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSC::JSObject* object = JSC::jsCast<JSC::JSObject*>(value);
// TODO is this check necessary?
if (!hasIndexedProperties(object->indexingType())) {
object->seal(vm);
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_global(napi_env env, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
if (UNLIKELY(!result)) {
return napi_invalid_arg;
}
Zig::GlobalObject* globalObject = toJS(env);
// TODO change to global? or find another way to avoid JSGlobalProxy
*result = toNapi(globalObject->globalThis(), globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_range_error(napi_env env, napi_value code,
napi_value msg,
napi_value* result)
{
NAPI_PREAMBLE(env);
return createErrorWithNapiValues(env, code, msg, JSC::ErrorType::RangeError, result);
}
extern "C" napi_status napi_get_new_target(napi_env env,
napi_callback_info cbinfo,
napi_value* result)
{
NAPI_PREAMBLE(env);
// handle:
// - if they call this function when it was originally a getter/setter call
// - if they call this function without a result
NAPI_CHECK_ARG(env, cbinfo);
NAPI_CHECK_ARG(env, result);
NAPICallFrame* callFrame = reinterpret_cast<NAPICallFrame*>(cbinfo);
*result = toNapi(callFrame->newTarget(), toJS(env));
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_dataview(napi_env env, size_t length,
napi_value arraybuffer,
size_t byte_offset,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
Zig::GlobalObject* globalObject = toJS(env);
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
RETURN_IF_EXCEPTION(scope, napi_set_last_error(env, napi_pending_exception));
NAPI_CHECK_ARG(env, arraybuffer);
NAPI_CHECK_ARG(env, result);
JSValue arraybufferValue = toJS(arraybuffer);
auto arraybufferPtr = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(arraybufferValue);
NAPI_RETURN_EARLY_IF_FALSE(env, arraybufferPtr, napi_arraybuffer_expected);
if (byte_offset + length > arraybufferPtr->impl()->byteLength()) {
JSC::throwRangeError(globalObject, scope, "byteOffset exceeds source ArrayBuffer byteLength"_s);
RETURN_IF_EXCEPTION(scope, napi_set_last_error(env, napi_pending_exception));
}
auto dataView = JSC::DataView::create(arraybufferPtr->impl(), byte_offset, length);
*result = toNapi(dataView->wrap(globalObject, globalObject), globalObject);
RELEASE_AND_RETURN(scope, napi_set_last_error(env, napi_ok));
}
static size_t getTypedArrayElementByteSize(napi_typedarray_type type)
{
switch (type) {
case napi_int8_array:
return 1;
case napi_uint8_array:
return 1;
case napi_uint8_clamped_array:
return 1;
case napi_int16_array:
return 2;
case napi_uint16_array:
return 2;
case napi_int32_array:
return 4;
case napi_uint32_array:
return 4;
case napi_float32_array:
return 4;
case napi_float64_array:
return 8;
case napi_bigint64_array:
return 8;
case napi_biguint64_array:
return 8;
default:
ASSERT_NOT_REACHED_WITH_MESSAGE("Unexpected napi_typedarray_type");
}
}
static JSC::TypedArrayType getTypedArrayTypeFromNAPI(napi_typedarray_type type)
{
switch (type) {
case napi_int8_array:
return JSC::TypedArrayType::TypeInt8;
case napi_uint8_array:
return JSC::TypedArrayType::TypeUint8;
case napi_uint8_clamped_array:
return JSC::TypedArrayType::TypeUint8Clamped;
case napi_int16_array:
return JSC::TypedArrayType::TypeInt16;
case napi_uint16_array:
return JSC::TypedArrayType::TypeUint16;
case napi_int32_array:
return JSC::TypedArrayType::TypeInt32;
case napi_uint32_array:
return JSC::TypedArrayType::TypeUint32;
case napi_float32_array:
return JSC::TypedArrayType::TypeFloat32;
case napi_float64_array:
return JSC::TypedArrayType::TypeFloat64;
case napi_bigint64_array:
return JSC::TypedArrayType::TypeBigInt64;
case napi_biguint64_array:
return JSC::TypedArrayType::TypeBigUint64;
default:
ASSERT_NOT_REACHED_WITH_MESSAGE("Unexpected napi_typedarray_type");
}
}
static JSC::JSArrayBufferView* createArrayBufferView(
Zig::GlobalObject* globalObject,
napi_typedarray_type type,
RefPtr<ArrayBuffer>&& arrayBuffer,
size_t byteOffset,
size_t length)
{
Structure* structure = globalObject->typedArrayStructure(getTypedArrayTypeFromNAPI(type), arrayBuffer->isResizableOrGrowableShared());
switch (type) {
case napi_int8_array:
return JSC::JSInt8Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_uint8_array:
return JSC::JSUint8Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_uint8_clamped_array:
return JSC::JSUint8ClampedArray::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_int16_array:
return JSC::JSInt16Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_uint16_array:
return JSC::JSUint16Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_int32_array:
return JSC::JSInt32Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_uint32_array:
return JSC::JSUint32Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_float32_array:
return JSC::JSFloat32Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_float64_array:
return JSC::JSFloat64Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_bigint64_array:
return JSC::JSBigInt64Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
case napi_biguint64_array:
return JSC::JSBigUint64Array::create(globalObject, structure, std::move(arrayBuffer), byteOffset, length);
default:
ASSERT_NOT_REACHED_WITH_MESSAGE("Unexpected napi_typedarray_type");
}
}
extern "C" napi_status napi_create_typedarray(
napi_env env,
napi_typedarray_type type,
size_t length,
napi_value arraybuffer,
size_t byte_offset,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
Zig::GlobalObject* globalObject = toJS(env);
auto scope = DECLARE_THROW_SCOPE(globalObject->vm());
RETURN_IF_EXCEPTION(scope, napi_set_last_error(env, napi_pending_exception));
NAPI_CHECK_ARG(env, arraybuffer);
NAPI_CHECK_ARG(env, result);
JSValue arraybufferValue = toJS(arraybuffer);
auto arraybufferPtr = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(arraybufferValue);
NAPI_RETURN_EARLY_IF_FALSE(env, arraybufferPtr, napi_arraybuffer_expected);
JSC::JSArrayBufferView* view = createArrayBufferView(globalObject, type, arraybufferPtr->impl(), byte_offset, length);
*result = toNapi(view, globalObject);
RELEASE_AND_RETURN(scope, napi_set_last_error(env, napi_ok));
}
namespace Zig {
template<typename Visitor>
void NapiClass::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
NapiClass* thisObject = jsCast<NapiClass*>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
}
DEFINE_VISIT_CHILDREN(NapiClass);
template<bool ConstructCall>
JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue NapiClass_ConstructorFunction(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)
{
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSObject* constructorTarget = asObject(callFrame->jsCallee());
NapiClass* napi = jsDynamicCast<NapiClass*>(constructorTarget);
while (!napi && constructorTarget) {
constructorTarget = constructorTarget->getPrototypeDirect().getObject();
napi = jsDynamicCast<NapiClass*>(constructorTarget);
}
if (UNLIKELY(!napi)) {
JSC::throwVMError(globalObject, scope, JSC::createTypeError(globalObject, "NapiClass constructor called on an object that is not a NapiClass"_s));
return JSValue::encode(JSC::jsUndefined());
}
if constexpr (ConstructCall) {
NapiPrototype* prototype = JSC::jsDynamicCast<NapiPrototype*>(napi->getIfPropertyExists(globalObject, vm.propertyNames->prototype));
RETURN_IF_EXCEPTION(scope, {});
if (!prototype) {
JSC::throwVMError(globalObject, scope, JSC::createTypeError(globalObject, "NapiClass constructor is missing the prototype"_s));
return JSValue::encode(JSC::jsUndefined());
}
auto* subclass = prototype->subclass(globalObject, asObject(callFrame->newTarget()));
RETURN_IF_EXCEPTION(scope, {});
callFrame->setThisValue(subclass);
}
NAPICallFrame frame(globalObject, callFrame, napi->dataPtr);
Bun::NapiHandleScope handleScope(jsCast<Zig::GlobalObject*>(globalObject));
JSValue ret = toJS(napi->constructor()(toNapi(globalObject), frame.toNapi()));
RETURN_IF_EXCEPTION(scope, {});
if (ret.isEmpty()) {
ret = jsUndefined();
}
if constexpr (ConstructCall) {
RELEASE_AND_RETURN(scope, JSValue::encode(frame.thisValue()));
} else {
RELEASE_AND_RETURN(scope, JSValue::encode(ret));
}
}
NapiClass* NapiClass::create(VM& vm, Zig::GlobalObject* globalObject, const char* utf8name,
size_t length,
napi_callback constructor,
void* data,
size_t property_count,
const napi_property_descriptor* properties)
{
WTF::String name = WTF::String::fromUTF8({ utf8name, length }).isolatedCopy();
NativeExecutable* executable = vm.getHostFunction(
// for normal call
NapiClass_ConstructorFunction<false>,
ImplementationVisibility::Public,
// for constructor call
NapiClass_ConstructorFunction<true>, name);
Structure* structure = globalObject->NapiClassStructure();
NapiClass* napiClass = new (NotNull, allocateCell<NapiClass>(vm)) NapiClass(vm, executable, globalObject, structure, data);
napiClass->finishCreation(vm, executable, length, name, constructor, data, property_count, properties);
return napiClass;
}
void NapiClass::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, napi_callback constructor,
void* data,
size_t property_count,
const napi_property_descriptor* properties)
{
Base::finishCreation(vm, executable, length, name);
ASSERT(inherits(info()));
this->m_constructor = constructor;
auto globalObject = reinterpret_cast<Zig::GlobalObject*>(this->globalObject());
this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | 0);
NapiPrototype* prototype = NapiPrototype::create(vm, globalObject->NapiPrototypeStructure());
auto throwScope = DECLARE_THROW_SCOPE(vm);
for (size_t i = 0; i < property_count; i++) {
const napi_property_descriptor& property = properties[i];
if (property.attributes & napi_static) {
defineNapiProperty(globalObject, this, property, true, throwScope);
} else {
defineNapiProperty(globalObject, prototype, property, false, throwScope);
}
if (throwScope.exception())
break;
}
this->putDirect(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | 0);
prototype->putDirect(vm, vm.propertyNames->constructor, this, JSC::PropertyAttribute::DontEnum | 0);
}
}
const ClassInfo NapiClass::s_info = { "Function"_s, &NapiClass::Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NapiClass) };
const ClassInfo NapiPrototype::s_info = { "Object"_s, &NapiPrototype::Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NapiPrototype) };
extern "C" napi_status napi_get_all_property_names(
napi_env env, napi_value objectNapi, napi_key_collection_mode key_mode,
napi_key_filter key_filter, napi_key_conversion key_conversion,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, objectNapi);
auto objectValue = toJS(objectNapi);
auto* object = objectValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, object, napi_object_expected);
DontEnumPropertiesMode jsc_key_mode = key_mode == napi_key_include_prototypes ? DontEnumPropertiesMode::Include : DontEnumPropertiesMode::Exclude;
PropertyNameMode jsc_property_mode = PropertyNameMode::StringsAndSymbols;
// TODO verify changing == to & is correct
if (key_filter & napi_key_skip_symbols) {
jsc_property_mode = PropertyNameMode::Strings;
} else if (key_filter & napi_key_skip_strings) {
jsc_property_mode = PropertyNameMode::Symbols;
}
auto globalObject = toJS(env);
JSArray* exportKeys = ownPropertyKeys(globalObject, object, jsc_property_mode, jsc_key_mode);
napi_key_filter filter_by_any_descriptor = static_cast<napi_key_filter>(napi_key_enumerable | napi_key_writable | napi_key_configurable);
// avoid expensive iteration if they don't care whether keys are enumerable, writable, or configurable
if (key_filter & filter_by_any_descriptor) {
JSArray* filteredKeys = JSArray::create(globalObject->vm(), globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), 0);
for (unsigned i = 0; i < exportKeys->getArrayLength(); i++) {
JSValue key = exportKeys->get(globalObject, i);
PropertyDescriptor desc;
object->getOwnPropertyDescriptor(globalObject, key.toPropertyKey(globalObject), desc);
bool include = true;
if (key_filter & napi_key_enumerable) {
include = include && desc.enumerable();
}
if (key_filter & napi_key_writable) {
include = include && desc.writable();
}
if (key_filter & napi_key_configurable) {
include = include && desc.configurable();
}
if (include) {
filteredKeys->push(globalObject, key);
}
}
exportKeys = filteredKeys;
}
*result = toNapi(JSValue(exportKeys), globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_define_class(napi_env env,
const char* utf8name,
size_t length,
napi_callback constructor,
void* data,
size_t property_count,
const napi_property_descriptor* properties,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, utf8name);
NAPI_CHECK_ARG(env, constructor);
NAPI_RETURN_EARLY_IF_FALSE(env, properties || property_count == 0, napi_invalid_arg);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
size_t len = length;
if (len == NAPI_AUTO_LENGTH) {
len = strlen(utf8name);
}
NapiClass* napiClass = NapiClass::create(vm, globalObject, utf8name, len, constructor, data, property_count, properties);
JSValue value = JSValue(napiClass);
JSC::EnsureStillAliveScope ensureStillAlive1(value);
if (data != nullptr) {
napiClass->dataPtr = data;
}
*result = toNapi(value, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSValue jsValue = toJS(value);
JSC::EnsureStillAliveScope ensureStillAlive(jsValue);
// .toString() can throw
JSValue resultValue = JSValue(jsValue.toString(globalObject));
NAPI_RETURN_IF_EXCEPTION(env);
JSC::EnsureStillAliveScope ensureStillAlive1(resultValue);
*result = toNapi(resultValue, globalObject);
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_coerce_to_bool(napi_env env, napi_value value, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSValue jsValue = toJS(value);
// might throw
bool nativeBool = jsValue.toBoolean(globalObject);
NAPI_RETURN_IF_EXCEPTION(env);
*result = toNapi(JSC::jsBoolean(nativeBool), globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_coerce_to_number(napi_env env, napi_value value, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSValue jsValue = toJS(value);
// might throw
double nativeNumber = jsValue.toNumber(globalObject);
NAPI_RETURN_IF_EXCEPTION(env);
*result = toNapi(JSC::jsNumber(nativeNumber), globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_coerce_to_object(napi_env env, napi_value value, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSValue jsValue = toJS(value);
// might throw
JSObject* obj = jsValue.toObject(globalObject);
NAPI_RETURN_IF_EXCEPTION(env);
*result = toNapi(obj, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_property_names(napi_env env, napi_value object,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, object);
NAPI_CHECK_ARG(env, result);
JSValue jsValue = toJS(object);
JSObject* jsObject = jsValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, jsObject, napi_object_expected);
Zig::GlobalObject* globalObject = toJS(env);
JSC::EnsureStillAliveScope ensureStillAlive(jsValue);
JSValue value = JSC::allPropertyKeys(globalObject, jsObject, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
NAPI_RETURN_IF_EXCEPTION(env);
JSC::EnsureStillAliveScope ensureStillAlive1(value);
*result = toNapi(value, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
void* data,
napi_finalize finalize_cb,
void* finalize_hint,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(data), length }, createSharedTask<void(void*)>([globalObject, finalize_hint, finalize_cb](void* p) {
if (finalize_cb != nullptr) {
NAPI_LOG("finalizer");
finalize_cb(toNapi(globalObject), p, finalize_hint);
}
}));
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
auto* buffer = JSC::JSUint8Array::create(globalObject, subclassStructure, WTFMove(arrayBuffer), 0, length);
*result = toNapi(buffer, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* external_data, size_t byte_length,
napi_finalize finalize_cb, void* finalize_hint, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(external_data), byte_length }, createSharedTask<void(void*)>([globalObject, finalize_hint, finalize_cb](void* p) {
if (finalize_cb != nullptr) {
NAPI_LOG("finalizer");
finalize_cb(toNapi(globalObject), p, finalize_hint);
}
}));
auto* buffer = JSC::JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(ArrayBufferSharingMode::Default), WTFMove(arrayBuffer));
*result = toNapi(buffer, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_double(napi_env env, double value,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
*result = toNapi(jsDoubleNumber(value), toJS(env));
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_value_double(napi_env env, napi_value value,
double* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isNumber(), napi_number_expected);
*result = jsValue.asNumber();
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_value_int32(napi_env env, napi_value value, int32_t* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isNumber(), napi_number_expected);
*result = jsValue.isInt32() ? jsValue.asInt32() : JSC::toInt32(jsValue.asDouble());
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_value_uint32(napi_env env, napi_value value, uint32_t* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isNumber(), napi_number_expected);
*result = jsValue.isUInt32() ? jsValue.asUInt32() : JSC::toUInt32(jsValue.asNumber());
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_value_int64(napi_env env, napi_value value, int64_t* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isNumber(), napi_number_expected);
double js_number = jsValue.asNumber();
if (isfinite(js_number)) {
// upper is 2^63 exactly, not 2^63-1, as the latter can't be represented exactly
constexpr double lower = std::numeric_limits<int64_t>::min(), upper = 1ull << 63;
if (js_number >= upper) {
*result = std::numeric_limits<int64_t>::max();
} else if (js_number <= lower) {
*result = std::numeric_limits<int64_t>::min();
} else {
// safe
*result = static_cast<int64_t>(js_number);
}
} else {
*result = 0;
}
NAPI_RETURN_SUCCESS(env);
}
static_assert(std::is_same_v<JSBigInt::Digit, uint64_t>, "All NAPI bigint functions assume that bigint words are 64 bits");
#if USE(BIGINT32)
#error All NAPI bigint functions assume that BIGINT32 is disabled
#endif
extern "C" napi_status napi_get_value_bigint_int64(napi_env env, napi_value value, int64_t* result, bool* lossless)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isHeapBigInt(), napi_bigint_expected);
JSBigInt* bigint = jsValue.asHeapBigInt();
if (bigint->isZero()) {
*result = 0;
if (lossless) *lossless = true;
} else {
uint64_t digit = bigint->digit(0);
uint64_t signed_digit = digit;
if (bigint->sign()) {
// negate, while keeping it unsigned because i don't trust signed overflow
signed_digit = ~signed_digit + 1;
}
memcpy(result, &signed_digit, sizeof digit);
if (lossless) {
if (bigint->length() > 1) {
*lossless = false;
} else if (bigint->sign()) {
// negative
// lossless if numeric value is >= -2^63,
// for which digit will be <= 2^63
*lossless = (digit <= (1ull << 63));
} else {
// positive
// lossless if numeric value is <= 2^63 - 1
*lossless = (digit <= static_cast<uint64_t>(INT64_MAX));
}
}
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_value_bigint_uint64(napi_env env, napi_value value, uint64_t* result, bool* lossless)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isHeapBigInt(), napi_bigint_expected);
JSBigInt* bigint = jsValue.asHeapBigInt();
if (bigint->isZero()) {
*result = 0;
if (lossless) *lossless = true;
} else {
*result = bigint->digit(0);
if (lossless) {
// lossless if and only if only one digit and positive
*lossless = (bigint->length() == 1 && bigint->sign() == false);
}
}
NAPI_RETURN_SUCCESS(env);
}
template<typename BufferElement, WebCore::BufferEncodingType EncodeTo>
napi_status napi_get_value_string_any_encoding(napi_env env, napi_value napiValue, BufferElement* buf, size_t bufsize, size_t* writtenPtr)
{
NAPI_CHECK_ARG(env, napiValue);
JSValue jsValue = toJS(napiValue);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isString(), napi_string_expected);
Zig::GlobalObject* globalObject = toJS(env);
String view = jsValue.asCell()->getString(globalObject);
size_t length = view.length();
if (buf == nullptr) {
// they just want to know the length
NAPI_CHECK_ARG(env, writtenPtr);
if (view.is8Bit()) {
*writtenPtr = Bun__encoding__byteLengthLatin1(view.span8().data(), length, static_cast<uint8_t>(EncodeTo));
} else {
*writtenPtr = Bun__encoding__byteLengthUTF16(view.span16().data(), length, static_cast<uint8_t>(EncodeTo));
}
return napi_set_last_error(env, napi_ok);
}
if (UNLIKELY(bufsize == 0)) {
if (writtenPtr) *writtenPtr = 0;
return napi_set_last_error(env, napi_ok);
}
if (UNLIKELY(bufsize == NAPI_AUTO_LENGTH)) {
if (writtenPtr) *writtenPtr = 0;
buf[0] = '\0';
return napi_set_last_error(env, napi_ok);
}
size_t written;
if (view.is8Bit()) {
written = Bun__encoding__writeLatin1(view.span8().data(), view.length(), reinterpret_cast<unsigned char*>(buf), bufsize - 1, static_cast<uint8_t>(EncodeTo));
} else {
written = Bun__encoding__writeUTF16(view.span16().data(), view.length(), reinterpret_cast<unsigned char*>(buf), bufsize - 1, static_cast<uint8_t>(EncodeTo));
}
if (writtenPtr != nullptr) {
*writtenPtr = written;
}
if (written < bufsize) {
buf[written] = '\0';
}
return napi_set_last_error(env, napi_ok);
}
extern "C" napi_status napi_get_value_string_utf8(napi_env env,
napi_value napiValue, char* buf,
size_t bufsize,
size_t* writtenPtr)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
// this function does set_last_error
return napi_get_value_string_any_encoding<char, WebCore::BufferEncodingType::utf8>(env, napiValue, buf, bufsize, writtenPtr);
}
extern "C" napi_status napi_get_value_string_latin1(napi_env env, napi_value napiValue, char* buf, size_t bufsize, size_t* writtenPtr)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
// this function does set_last_error
return napi_get_value_string_any_encoding<char, WebCore::BufferEncodingType::latin1>(env, napiValue, buf, bufsize, writtenPtr);
}
extern "C" napi_status napi_get_value_string_utf16(napi_env env, napi_value napiValue, char16_t* buf, size_t bufsize, size_t* writtenPtr)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
// this function does set_last_error
return napi_get_value_string_any_encoding<char16_t, WebCore::BufferEncodingType::latin1>(env, napiValue, buf, bufsize, writtenPtr);
}
extern "C" napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, result);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isBoolean(), napi_boolean_expected);
*result = jsValue.asBoolean();
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue,
uint32_t index, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, objectValue);
JSValue jsValue = toJS(objectValue);
JSObject* jsObject = jsValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, jsObject, napi_object_expected);
JSValue element = jsObject->getIndex(toJS(env), index);
NAPI_RETURN_IF_EXCEPTION(env);
*result = toNapi(element, toJS(env));
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_delete_element(napi_env env, napi_value objectValue,
uint32_t index, bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, objectValue);
JSValue jsValue = toJS(objectValue);
JSObject* jsObject = jsValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, jsObject, napi_object_expected);
if (LIKELY(result)) {
*result = JSObject::deletePropertyByIndex(jsObject, toJS(env), index);
}
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_create_object(napi_env env, napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSValue value = JSValue(NapiPrototype::create(vm, globalObject->NapiPrototypeStructure()));
*result = toNapi(value, globalObject);
JSC::EnsureStillAliveScope ensureStillAlive(value);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_external(napi_env env, void* data,
napi_finalize finalize_cb,
void* finalize_hint,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
auto* structure = globalObject->NapiExternalStructure();
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, finalize_cb);
JSC::EnsureStillAliveScope ensureStillAlive(value);
*result = toNapi(value, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_typeof(napi_env env, napi_value val,
napi_valuetype* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
JSValue value = toJS(val);
if (value.isEmpty()) {
// This can happen
*result = napi_undefined;
NAPI_RETURN_SUCCESS(env);
}
if (value.isCell()) {
JSCell* cell = value.asCell();
switch (cell->type()) {
case JSC::JSFunctionType:
case JSC::InternalFunctionType:
*result = napi_function;
NAPI_RETURN_SUCCESS(env);
case JSC::ObjectType:
if (JSC::jsDynamicCast<Bun::NapiExternal*>(value)) {
*result = napi_external;
NAPI_RETURN_SUCCESS(env);
}
*result = napi_object;
NAPI_RETURN_SUCCESS(env);
case JSC::HeapBigIntType:
*result = napi_bigint;
NAPI_RETURN_SUCCESS(env);
case JSC::DerivedStringObjectType:
case JSC::StringObjectType:
case JSC::StringType:
*result = napi_string;
NAPI_RETURN_SUCCESS(env);
case JSC::SymbolType:
*result = napi_symbol;
NAPI_RETURN_SUCCESS(env);
case JSC::FinalObjectType:
case JSC::ArrayType:
case JSC::DerivedArrayType:
*result = napi_object;
NAPI_RETURN_SUCCESS(env);
default: {
if (cell->isCallable() || cell->isConstructor()) {
*result = napi_function;
NAPI_RETURN_SUCCESS(env);
}
if (cell->isObject()) {
*result = napi_object;
NAPI_RETURN_SUCCESS(env);
}
break;
}
}
}
if (value.isNumber()) {
*result = napi_number;
NAPI_RETURN_SUCCESS(env);
}
if (value.isUndefined()) {
*result = napi_undefined;
NAPI_RETURN_SUCCESS(env);
}
if (value.isNull()) {
*result = napi_null;
NAPI_RETURN_SUCCESS(env);
}
if (value.isBoolean()) {
*result = napi_boolean;
NAPI_RETURN_SUCCESS(env);
}
// Unexpected type, report an error in debug mode
ASSERT_NOT_REACHED_WITH_MESSAGE("unknown type passed to napi_typeof");
return napi_set_last_error(env, napi_generic_failure);
}
extern "C" napi_status napi_get_value_bigint_words(napi_env env,
napi_value value,
int* sign_bit,
size_t* word_count,
uint64_t* words)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, word_count);
JSValue jsValue = toJS(value);
NAPI_RETURN_EARLY_IF_FALSE(env, jsValue.isHeapBigInt(), napi_bigint_expected);
// If both sign_bit and words are nullptr, we're just querying the word count
// However, if exactly one of them is nullptr, we have an invalid argument
NAPI_RETURN_EARLY_IF_FALSE(env, (sign_bit == nullptr && words == nullptr) || (sign_bit && words), napi_invalid_arg);
JSBigInt* bigInt = jsValue.asHeapBigInt();
size_t available_words = *word_count;
*word_count = bigInt->length();
// Return ok in this case
if (sign_bit == nullptr && words == nullptr) {
NAPI_RETURN_SUCCESS(env);
}
*sign_bit = (int)bigInt->sign();
size_t len = *word_count;
for (size_t i = 0; i < available_words && i < len; i++) {
words[i] = bigInt->digit(i);
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_get_value_external(napi_env env, napi_value value,
void** result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_CHECK_ARG(env, value);
auto* external = jsDynamicCast<Bun::NapiExternal*>(toJS(value));
NAPI_RETURN_EARLY_IF_FALSE(env, external, napi_invalid_arg);
*result = external->value();
NAPI_RETURN_SUCCESS(env);
}
// TODO: make this per addon instead of globally shared for ALL addons
extern "C" napi_status napi_get_instance_data(napi_env env,
void** data)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, data);
Zig::GlobalObject* globalObject = toJS(env);
*data = globalObject->napiInstanceData;
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_run_script(napi_env env, napi_value script,
napi_value* result)
{
NAPI_PREAMBLE_NO_THROW_SCOPE(env);
NAPI_CHECK_ARG(env, script);
NAPI_CHECK_ARG(env, result);
JSValue scriptValue = toJS(script);
NAPI_RETURN_EARLY_IF_FALSE(env, scriptValue.isString(), napi_string_expected);
Zig::GlobalObject* globalObject = toJS(env);
auto& vm = globalObject->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
WTF::String code = scriptValue.getString(globalObject);
JSC::SourceCode sourceCode = makeSource(code, SourceOrigin(), SourceTaintedOrigin::Untainted);
NakedPtr<Exception> returnedException;
JSValue value = JSC::evaluate(globalObject, sourceCode, globalObject->globalThis(), returnedException);
if (returnedException) {
throwScope.throwException(globalObject, returnedException);
return napi_set_last_error(env, napi_pending_exception);
}
ASSERT(!value.isEmpty());
*result = toNapi(value, globalObject);
return napi_set_last_error(env, napi_ok);
}
extern "C" napi_status napi_set_instance_data(napi_env env,
void* data,
napi_finalize finalize_cb,
void* finalize_hint)
{
NAPI_PREAMBLE(env);
Zig::GlobalObject* globalObject = toJS(env);
globalObject->napiInstanceData = data;
globalObject->napiInstanceDataFinalizer = reinterpret_cast<void*>(finalize_cb);
globalObject->napiInstanceDataFinalizerHint = finalize_hint;
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_bigint_words(napi_env env,
int sign_bit,
size_t word_count,
const uint64_t* words,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_RETURN_EARLY_IF_FALSE(env, sign_bit == 0 || sign_bit == 1, napi_invalid_arg);
NAPI_RETURN_EARLY_IF_FALSE(env, word_count == 0 || words, napi_invalid_arg);
// JSBigInt::createWithLength's size argument is unsigned int
NAPI_RETURN_EARLY_IF_FALSE(env, word_count <= UINT_MAX, napi_invalid_arg);
Zig::GlobalObject* globalObject = toJS(env);
// throws RangeError if size is larger than JSC's limit
auto* bigint = JSBigInt::createWithLength(globalObject, word_count);
NAPI_RETURN_IF_EXCEPTION(env);
ASSERT(bigint);
bigint->setSign(sign_bit);
const uint64_t* word = words;
// TODO: add fast path that uses memcpy here instead of setDigit
// we need to add this to JSC. V8 has this optimization
for (size_t i = 0; i < word_count; i++) {
bigint->setDigit(i, *word++);
}
*result = toNapi(bigint, globalObject);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_create_symbol(napi_env env, napi_value description,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSValue descriptionValue = toJS(description);
if (descriptionValue && !descriptionValue.isUndefinedOrNull()) {
NAPI_RETURN_EARLY_IF_FALSE(env, descriptionValue.isString(), napi_string_expected);
WTF::String descriptionString = descriptionValue.getString(globalObject);
if (descriptionString.length() > 0) {
*result = toNapi(JSC::Symbol::createWithDescription(vm, descriptionString),
globalObject);
NAPI_RETURN_SUCCESS(env);
}
// TODO handle empty string?
}
*result = toNapi(JSC::Symbol::create(vm), globalObject);
NAPI_RETURN_SUCCESS(env);
}
// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/js_native_api_v8.cc#L2904-L2930
extern "C" napi_status napi_new_instance(napi_env env, napi_value constructor,
size_t argc, const napi_value* argv,
napi_value* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, result);
NAPI_RETURN_EARLY_IF_FALSE(env, argc == 0 || argv, napi_invalid_arg);
JSValue constructorValue = toJS(constructor);
JSC::JSObject* constructorObject = constructorValue.getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, constructorObject, napi_function_expected);
JSC::CallData constructData = getConstructData(constructorObject);
NAPI_RETURN_EARLY_IF_FALSE(env, constructData.type != JSC::CallData::Type::None, napi_function_expected);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSC::MarkedArgumentBuffer args;
args.fill(vm, argc, [&](JSValue* buffer) {
gcSafeMemcpy<JSValue>(buffer, reinterpret_cast<const JSValue*>(argv), sizeof(JSValue) * argc);
});
auto value = construct(globalObject, constructorObject, constructData, args);
*result = toNapi(value, globalObject);
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi,
napi_value func_napi, size_t argc,
const napi_value* argv,
napi_value* result_ptr)
{
NAPI_PREAMBLE(env);
NAPI_RETURN_EARLY_IF_FALSE(env, argc == 0 || argv, napi_invalid_arg);
JSValue funcValue = toJS(func_napi);
NAPI_RETURN_EARLY_IF_FALSE(env, funcValue.isObject(), napi_function_expected);
JSC::CallData callData = getCallData(funcValue);
NAPI_RETURN_EARLY_IF_FALSE(env, callData.type != JSC::CallData::Type::None, napi_function_expected);
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
JSC::MarkedArgumentBuffer args;
args.fill(vm, argc, [&](JSValue* buffer) {
gcSafeMemcpy<JSValue>(buffer, reinterpret_cast<const JSValue*>(argv), sizeof(JSValue) * argc);
});
JSValue thisValue = toJS(recv_napi);
if (thisValue.isEmpty()) {
thisValue = JSC::jsUndefined();
}
JSValue result = call(globalObject, funcValue, callData, thisValue, args);
if (result_ptr) {
if (result.isEmpty()) {
*result_ptr = toNapi(JSC::jsUndefined(), globalObject);
} else {
*result_ptr = toNapi(result, globalObject);
}
}
NAPI_RETURN_SUCCESS_UNLESS_EXCEPTION(env);
}
extern "C" napi_status napi_type_tag_object(napi_env env, napi_value value, const napi_type_tag* type_tag)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, type_tag);
Zig::GlobalObject* globalObject = toJS(env);
JSObject* js_object = toJS(value).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, js_object, napi_object_expected);
auto* existing_tag = jsDynamicCast<Bun::NapiTypeTag*>(globalObject->napiTypeTags()->get(js_object));
// cannot tag an object that is already tagged
NAPI_RETURN_EARLY_IF_FALSE(env, existing_tag == nullptr, napi_invalid_arg);
// TODO(@190n) maybe use a BigInt?
auto* new_tag = Bun::NapiTypeTag::create(globalObject->vm(), globalObject->NapiTypeTagStructure(), *type_tag);
globalObject->napiTypeTags()->set(globalObject->vm(), js_object, new_tag);
NAPI_RETURN_SUCCESS(env);
}
extern "C" napi_status napi_check_object_type_tag(napi_env env, napi_value value, const napi_type_tag* type_tag, bool* result)
{
NAPI_PREAMBLE(env);
NAPI_CHECK_ARG(env, value);
NAPI_CHECK_ARG(env, type_tag);
Zig::GlobalObject* globalObject = toJS(env);
JSObject* js_object = toJS(value).getObject();
NAPI_RETURN_EARLY_IF_FALSE(env, js_object, napi_object_expected);
bool match = false;
auto* found_tag = jsDynamicCast<Bun::NapiTypeTag*>(globalObject->napiTypeTags()->get(js_object));
if (found_tag && found_tag->matches(*type_tag)) {
match = true;
}
if (LIKELY(result)) {
*result = match;
}
NAPI_RETURN_SUCCESS(env);
}
extern "C" JS_EXPORT napi_status node_api_create_property_key_latin1(napi_env env, const char* str, size_t length, napi_value* result)
{
// EXPERIMENTAL
// TODO(@190n) use jsAtomString or something
NAPI_LOG_CURRENT_FUNCTION;
return napi_create_string_latin1(env, str, length, result);
}
extern "C" JS_EXPORT napi_status node_api_create_property_key_utf16(napi_env env, const char16_t* str, size_t length, napi_value* result)
{
// EXPERIMENTAL
// TODO(@190n) use jsAtomString or something
NAPI_LOG_CURRENT_FUNCTION;
return napi_create_string_utf16(env, str, length, result);
}
extern "C" JS_EXPORT napi_status node_api_create_property_key_utf8(napi_env env, const char* str, size_t length, napi_value* result)
{
// EXPERIMENTAL
// TODO(@190n) use jsAtomString or something
NAPI_LOG_CURRENT_FUNCTION;
return napi_create_string_utf8(env, str, length, result);
}