mirror of
https://github.com/oven-sh/bun
synced 2026-02-13 12:29:07 +00:00
Upgrade WebKit
This commit is contained in:
@@ -40,15 +40,184 @@
|
||||
#include "JavaScriptCore/ArrayBuffer.h"
|
||||
#include "JavaScriptCore/JSArrayBuffer.h"
|
||||
#include "JSFFIFunction.h"
|
||||
#include "JavaScriptCore/JavaScript.h"
|
||||
#include "JavaScriptCore/JSWeakValue.h"
|
||||
#include "napi.h"
|
||||
#include "JavaScriptCore/GetterSetter.h"
|
||||
|
||||
#include <iostream>
|
||||
using namespace JSC;
|
||||
using namespace Zig;
|
||||
|
||||
class NapiRefWeakHandleOwner final : public JSC::WeakHandleOwner {
|
||||
public:
|
||||
void finalize(JSC::Handle<JSC::Unknown>, void* context) final
|
||||
{
|
||||
auto* weakValue = reinterpret_cast<NapiRef*>(context);
|
||||
weakValue->clear();
|
||||
}
|
||||
};
|
||||
|
||||
static NapiRefWeakHandleOwner& weakValueHandleOwner()
|
||||
{
|
||||
static NeverDestroyed<NapiRefWeakHandleOwner> jscWeakValueHandleOwner;
|
||||
return jscWeakValueHandleOwner;
|
||||
}
|
||||
|
||||
void NapiFinalizer::call(JSC::JSGlobalObject* globalObject, void* data)
|
||||
{
|
||||
if (finalize_cb) {
|
||||
finalize_cb(reinterpret_cast<napi_env>(globalObject), finalize_hint, data);
|
||||
}
|
||||
}
|
||||
|
||||
void NapiRef::ref()
|
||||
{
|
||||
++refCount;
|
||||
if (refCount == 1 && weakValueRef.isSet()) {
|
||||
auto& vm = globalObject.get()->vm();
|
||||
if (weakValueRef.isString()) {
|
||||
strongRef.set(vm, JSC::JSValue(weakValueRef.string()));
|
||||
} else if (weakValueRef.isObject()) {
|
||||
strongRef.set(vm, JSC::JSValue(weakValueRef.object()));
|
||||
} else {
|
||||
strongRef.set(vm, weakValueRef.primitive());
|
||||
}
|
||||
|
||||
weakValueRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NapiRef::unref()
|
||||
{
|
||||
bool clear = refCount == 1;
|
||||
refCount = refCount > 0 ? refCount - 1 : 0;
|
||||
if (clear) {
|
||||
JSC::JSValue val = strongRef.get();
|
||||
if (val.isString()) {
|
||||
weakValueRef.setString(val.toString(globalObject.get()), weakValueHandleOwner(), this);
|
||||
} else if (val.isObject()) {
|
||||
weakValueRef.setObject(val.getObject(), weakValueHandleOwner(), this);
|
||||
} else {
|
||||
weakValueRef.setPrimitive(val);
|
||||
}
|
||||
strongRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NapiRef::clear()
|
||||
{
|
||||
this->finalizer.call(this->globalObject.get(), nullptr);
|
||||
this->globalObject.clear();
|
||||
this->weakValueRef.clear();
|
||||
this->strongRef.clear();
|
||||
}
|
||||
|
||||
// namespace Napi {
|
||||
// class Reference
|
||||
// }
|
||||
#define StackAllocatedCallFramePointerTag 62
|
||||
typedef struct StackAllocatedCallFrame {
|
||||
void* dataPtr;
|
||||
JSC::EncodedJSValue thisValue;
|
||||
// this is "bar" in:
|
||||
// set foo(bar)
|
||||
JSC::EncodedJSValue argument1;
|
||||
} StackAllocatedCallFrame;
|
||||
|
||||
extern "C" Zig::GlobalObject* Bun__getDefaultGlobal();
|
||||
extern "C" Zig::GlobalObject*
|
||||
Bun__getDefaultGlobal();
|
||||
|
||||
static uint32_t getPropertyAttributes(napi_property_attributes attributes)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
if (!(attributes & napi_key_configurable)) {
|
||||
result |= JSC::PropertyAttribute::DontDelete;
|
||||
}
|
||||
|
||||
if (!(attributes & napi_key_enumerable)) {
|
||||
result |= JSC::PropertyAttribute::DontEnum;
|
||||
}
|
||||
|
||||
if (!(attributes & napi_key_writable)) {
|
||||
result |= JSC::PropertyAttribute::ReadOnly;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint32_t getPropertyAttributes(napi_property_descriptor prop)
|
||||
{
|
||||
uint32_t result = getPropertyAttributes(prop.attributes);
|
||||
|
||||
if (!(prop.getter && !prop.setter)) {
|
||||
result |= JSC::PropertyAttribute::ReadOnly;
|
||||
}
|
||||
|
||||
if (prop.method) {
|
||||
result |= JSC::PropertyAttribute::Function;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const ClassInfo NapiClass::s_info = { "Function"_s, &NapiClass::Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NapiClass) };
|
||||
|
||||
static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* to, napi_property_descriptor property)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
void* dataPtr = property.data;
|
||||
auto is_static = !!(property.attributes & napi_static);
|
||||
WTF::String nameStr;
|
||||
if (property.utf8name != nullptr) {
|
||||
nameStr = WTF::String::fromUTF8(property.utf8name);
|
||||
} else if (property.name) {
|
||||
nameStr = toJS(property.name).toWTFString(globalObject);
|
||||
}
|
||||
|
||||
auto propertyName = JSC::PropertyName(JSC::Identifier::fromString(vm, nameStr));
|
||||
|
||||
if (property.method) {
|
||||
auto function = Zig::JSFFIFunction::create(vm, globalObject, 1, nameStr, reinterpret_cast<Zig::FFIFunction>(property.method));
|
||||
function->dataPtr = dataPtr;
|
||||
JSC::JSValue value = JSC::JSValue(function);
|
||||
|
||||
to->putDirect(vm, Identifier::fromString(vm, nameStr), value, getPropertyAttributes(property));
|
||||
return;
|
||||
}
|
||||
|
||||
if (property.getter != nullptr || property.setter != nullptr) {
|
||||
JSC::JSValue getter = {};
|
||||
JSC::JSValue setter = {};
|
||||
|
||||
if (property.getter) {
|
||||
auto callGetter = property.getter;
|
||||
auto function = Zig::JSFFIFunction::create(vm, globalObject, 0, nameStr, reinterpret_cast<Zig::FFIFunction>(property.getter));
|
||||
function->dataPtr = dataPtr;
|
||||
getter = JSC::JSValue(function);
|
||||
}
|
||||
|
||||
if (property.setter) {
|
||||
auto callGetter = property.setter;
|
||||
auto function = Zig::JSFFIFunction::create(vm, globalObject, 1, nameStr, reinterpret_cast<Zig::FFIFunction>(property.setter));
|
||||
function->dataPtr = dataPtr;
|
||||
getter = JSC::JSValue(function);
|
||||
}
|
||||
|
||||
auto getterSetter = JSC::GetterSetter::create(vm, globalObject, getter, setter);
|
||||
to->putDirect(vm, propertyName, getterSetter, getPropertyAttributes(property) | JSC::PropertyAttribute::Accessor);
|
||||
|
||||
} else {
|
||||
// TODO: is dataPtr allowed when given a value?
|
||||
JSC::JSValue value = JSC::jsUndefined();
|
||||
|
||||
if (property.value) {
|
||||
value = toJS(property.value);
|
||||
}
|
||||
|
||||
to->putDirect(vm, propertyName, value, getPropertyAttributes(property));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void napi_module_register(napi_module* mod)
|
||||
{
|
||||
@@ -91,6 +260,55 @@ extern "C" void napi_module_register(napi_module* mod)
|
||||
promise->result(vm);
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_wrap(napi_env env,
|
||||
napi_value js_object,
|
||||
void* native_object,
|
||||
napi_finalize finalize_cb,
|
||||
void* finalize_hint,
|
||||
napi_ref* result)
|
||||
{
|
||||
auto* globalObject = toJS(env);
|
||||
auto* object = toJS(js_object).getObject();
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
|
||||
if (native_object) {
|
||||
uintptr_t ref_ptr = reinterpret_cast<uintptr_t>(native_object);
|
||||
double ref_double = bitwise_cast<double>(ref_ptr);
|
||||
val->putDirect(vm, clientData->builtinNames().passwordPrivateName(), JSC::jsNumber(ref_double), JSC::PropertyAttribute::DontEnum | 0);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
auto* ref = new NapiRef(globalObject, object);
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
if (finalize_cb) {
|
||||
ref->finalizer = { finalize_cb, finalize_hint };
|
||||
}
|
||||
*result = reinterpret_cast<napi_ref>(ref);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_unwrap(napi_env env, napi_value js_object,
|
||||
void** result)
|
||||
{
|
||||
auto* globalObject = toJS(env);
|
||||
auto* object = toJS(js_object).getObject();
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
|
||||
if (native_object) {
|
||||
uintptr_t ref_ptr = reinterpret_cast<uintptr_t>(native_object);
|
||||
double ref_double = bitwise_cast<double>(ref_ptr);
|
||||
val->putDirect(vm, clientData->builtinNames().passwordPrivateName(), JSC::jsNumber(ref_double), JSC::PropertyAttribute::DontEnum | 0);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
auto* ref = new NapiRef(globalObject, object);
|
||||
if (finalize_cb) {
|
||||
ref->finalizer = { finalize_cb, finalize_hint };
|
||||
}
|
||||
*result = reinterpret_cast<napi_ref>(ref);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_create_function(napi_env env, const char* utf8name,
|
||||
size_t length, napi_callback cb,
|
||||
void* data, napi_value* result)
|
||||
@@ -117,16 +335,19 @@ extern "C" napi_status napi_get_cb_info(
|
||||
{
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto inputArgsCount = argc == nullptr ? 0 : *argc;
|
||||
JSC::CallFrame* callFrame = reinterpret_cast<JSC::CallFrame*>(cbinfo);
|
||||
|
||||
auto inputArgsCount = argc == nullptr ? 0 : *argc;
|
||||
|
||||
// napi expects arguments to be copied into the argv array.
|
||||
if (inputArgsCount > 0) {
|
||||
auto outputArgsCount = callFrame->argumentCount();
|
||||
auto argsToCopy = inputArgsCount < outputArgsCount ? inputArgsCount : outputArgsCount;
|
||||
*argc = argsToCopy;
|
||||
|
||||
memcpy(argv, callFrame->addressOfArgumentsStart(), argsToCopy * sizeof(JSC::JSValue));
|
||||
|
||||
// If the user didn't provide expected number of args, we need to fill the rest with undefined.
|
||||
// TODO: can we use memset() here?
|
||||
auto argv_ptr = argv[outputArgsCount];
|
||||
for (size_t i = outputArgsCount; i < inputArgsCount; i++) {
|
||||
argv[i] = reinterpret_cast<napi_value>(JSC::JSValue::encode(JSC::jsUndefined()));
|
||||
@@ -139,8 +360,32 @@ extern "C" napi_status napi_get_cb_info(
|
||||
}
|
||||
|
||||
if (data != nullptr) {
|
||||
Zig::JSFFIFunction* ffiFunction = JSC::jsDynamicCast<Zig::JSFFIFunction*>(vm, JSC::JSValue(callFrame->jsCallee()));
|
||||
*data = reinterpret_cast<void*>(ffiFunction->dataPtr);
|
||||
JSC::JSValue callee = JSC::JSValue(callFrame->jsCallee());
|
||||
if (Zig::JSFFIFunction* ffiFunction = JSC::jsDynamicCast<Zig::JSFFIFunction*>(callee)) {
|
||||
*data = reinterpret_cast<void*>(ffiFunction->dataPtr);
|
||||
} else {
|
||||
*data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status
|
||||
napi_define_properties(napi_env env, napi_value object, size_t property_count,
|
||||
const napi_property_descriptor* properties)
|
||||
{
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
JSC::JSValue objectValue = JSC::JSValue(reinterpret_cast<JSC::EncodedJSValue>(object));
|
||||
JSC::JSObject* objectObject = objectValue.getObject();
|
||||
if (!objectObject) {
|
||||
return napi_object_expected;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < property_count; i++) {
|
||||
defineNapiProperty(globalObject, objectObject, properties[i]);
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
@@ -167,8 +412,73 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
|
||||
{
|
||||
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::Strong<JSC::Unknown> data = { globalObject->vm(), JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(value)) };
|
||||
*reinterpret_cast<JSC::Strong<JSC::Unknown>*>(result) = data;
|
||||
auto* ref = new NapiRef(toJS(env), initial_refcount);
|
||||
JSC::JSValue val = toJS(value);
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
|
||||
if (initial_refcount > 0) {
|
||||
ref->strongRef.set(globalObject->vm(), val);
|
||||
} else {
|
||||
if (val.isString()) {
|
||||
ref->weakValueRef.setString(val.toString(globalObject), weakValueHandleOwner(), ref);
|
||||
} else if (val.isObject()) {
|
||||
ref->weakValueRef.setObject(val.getObject(), weakValueHandleOwner(), ref);
|
||||
} else {
|
||||
ref->weakValueRef.setPrimitive(val);
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t ref_ptr = reinterpret_cast<uintptr_t>(ref);
|
||||
double ref_double = bitwise_cast<double>(ref_ptr);
|
||||
val->putDirect(vm, clientData->builtinNames().passwordPrivateName(), JSC::jsNumber(ref_double), JSC::PropertyAttribute::DontEnum | 0);
|
||||
|
||||
*result = toNapi(ref);
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_reference_unref(napi_env env, napi_ref ref,
|
||||
uint32_t* result)
|
||||
{
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->unref();
|
||||
*result = napiRef->refCount;
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
*result = toNapi(napiRef->value());
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_reference_ref(napi_env env, napi_ref ref,
|
||||
uint32_t* result)
|
||||
{
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->ref();
|
||||
*result = napiRef->refCount;
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_reference_delete(napi_env env, napi_ref ref,
|
||||
uint32_t* result)
|
||||
{
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->ref();
|
||||
*result = napiRef->refCount();
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_delete_reference(napi_env env, napi_ref ref)
|
||||
{
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
~napiRef();
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
@@ -185,7 +495,7 @@ extern "C" napi_status napi_is_detached_arraybuffer(napi_env env,
|
||||
return napi_arraybuffer_expected;
|
||||
}
|
||||
|
||||
JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(vm, value);
|
||||
JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(value);
|
||||
if (!jsArrayBuffer) {
|
||||
return napi_arraybuffer_expected;
|
||||
}
|
||||
@@ -208,7 +518,7 @@ extern "C" napi_status napi_detach_arraybuffer(napi_env env,
|
||||
return napi_arraybuffer_expected;
|
||||
}
|
||||
|
||||
JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(vm, value);
|
||||
JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(value);
|
||||
if (!jsArrayBuffer) {
|
||||
return napi_arraybuffer_expected;
|
||||
}
|
||||
@@ -248,6 +558,25 @@ extern "C" napi_status napi_throw_type_error(napi_env env, const char* code,
|
||||
JSC::throwException(globalObject, throwScope, error);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_create_type_error(napi_env env, napi_value code,
|
||||
napi_value msg,
|
||||
napi_value* result)
|
||||
{
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
JSC::JSValue codeValue = JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(code));
|
||||
JSC::JSValue messageValue = JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(msg));
|
||||
|
||||
auto error = JSC::createTypeError(globalObject, messageValue.toWTFString(globalObject));
|
||||
if (codeValue) {
|
||||
error->putDirect(vm, Identifier::fromString(vm, "code"_s), codeValue, 0);
|
||||
}
|
||||
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(error));
|
||||
return napi_ok;
|
||||
}
|
||||
extern "C" napi_status napi_throw_range_error(napi_env env, const char* code,
|
||||
const char* msg)
|
||||
{
|
||||
@@ -290,7 +619,8 @@ extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value)
|
||||
|
||||
JSC::EncodedJSValue encodedValue = reinterpret_cast<JSC::EncodedJSValue>(object_value);
|
||||
JSC::JSValue value = JSC::JSValue::decode(encodedValue);
|
||||
if (!value.isObject()) {
|
||||
|
||||
if (UNLIKELY(!value.isObject())) {
|
||||
return napi_object_expected;
|
||||
}
|
||||
|
||||
@@ -334,6 +664,20 @@ extern "C" napi_status napi_get_new_target(napi_env env,
|
||||
{
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
// handle:
|
||||
// - if they call this function when it was originally a getter/setter call
|
||||
// - if they call this function without a result
|
||||
if (UNLIKELY(result == nullptr || cbinfo == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
|
||||
if (reinterpret_cast<size_t>(cbinfo) & (static_cast<size_t>(1) << StackAllocatedCallFramePointerTag)) {
|
||||
// This is a stack allocated call frame, so we can't get the new target.
|
||||
// We'll just return undefined.
|
||||
// TODO: verify this is what napi does too
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(JSC::jsUndefined()));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
CallFrame* callFrame = reinterpret_cast<JSC::CallFrame*>(cbinfo);
|
||||
JSC::JSValue newTarget = callFrame->newTarget();
|
||||
@@ -351,11 +695,174 @@ extern "C" napi_status napi_create_dataview(napi_env env, size_t length,
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
JSC::EncodedJSValue encodedArraybuffer = reinterpret_cast<JSC::EncodedJSValue>(arraybuffer);
|
||||
auto arraybufferValue = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(vm, JSC::JSValue::decode(encodedArraybuffer));
|
||||
auto arraybufferValue = JSC::jsDynamicCast<JSC::JSArrayBuffer*>(JSC::JSValue::decode(encodedArraybuffer));
|
||||
if (!arraybufferValue) {
|
||||
return napi_invalid_arg;
|
||||
return napi_arraybuffer_expected;
|
||||
}
|
||||
auto dataView = JSC::DataView::create(arraybufferValue->impl(), byte_offset, length);
|
||||
*result = reinterpret_cast<napi_value>(dataView->wrap(globalObject, globalObject));
|
||||
|
||||
if (result != nullptr) {
|
||||
*result = reinterpret_cast<napi_value>(dataView->wrap(globalObject, globalObject));
|
||||
}
|
||||
|
||||
return 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);
|
||||
|
||||
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);
|
||||
NativeExecutable* executable = vm.getHostFunction(reinterpret_cast<FFIFunction>(constructor), JSC::NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
|
||||
|
||||
Structure* structure = globalObject->NapiClassStructure();
|
||||
NapiClass* napiClass = new (NotNull, allocateCell<NapiClass>(vm)) NapiClass(vm, executable, globalObject, structure);
|
||||
napiClass->finishCreation(vm, executable, length, name, constructor, data, property_count, properties);
|
||||
return napiClass;
|
||||
}
|
||||
|
||||
CallData NapiClass::getConstructData(JSCell* cell)
|
||||
{
|
||||
auto construct = JSC::jsCast<NapiClass*>(cell)->constructor();
|
||||
if (!construct) {
|
||||
return NapiClass::Base::getConstructData(cell);
|
||||
}
|
||||
|
||||
CallData constructData;
|
||||
constructData.type = CallData::Type::Native;
|
||||
constructData.native.function = construct;
|
||||
return constructData;
|
||||
}
|
||||
|
||||
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 = reinterpret_cast<FFIFunction>(constructor);
|
||||
auto globalObject = reinterpret_cast<Zig::GlobalObject*>(this->globalObject());
|
||||
|
||||
// toStringTag + "prototype"
|
||||
// size_t staticPropertyCount = 2;
|
||||
// prototype always has "constructor",
|
||||
size_t prototypePropertyCount = 2;
|
||||
|
||||
this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | 0);
|
||||
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
|
||||
for (size_t i = 0; i < property_count; i++) {
|
||||
const napi_property_descriptor& property = properties[i];
|
||||
// staticPropertyCount += property.attributes & napi_static ? 1 : 0;
|
||||
prototypePropertyCount += property.attributes & napi_static ? 0 : 1;
|
||||
}
|
||||
|
||||
JSC::JSObject* prototype = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), prototypePropertyCount);
|
||||
|
||||
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);
|
||||
} else {
|
||||
defineNapiProperty(globalObject, prototype, property);
|
||||
}
|
||||
}
|
||||
|
||||
this->putDirect(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | 0);
|
||||
prototype->putDirect(vm, vm.propertyNames->constructor, this, JSC::PropertyAttribute::DontEnum | 0);
|
||||
prototype->putDirect(vm, clientData->builtinNames().passwordPrivateName(), JSC::jsNumber(0), JSC::PropertyAttribute::DontEnum | 0);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DontEnumPropertiesMode jsc_key_mode = key_mode == napi_key_include_prototypes ? DontEnumPropertiesMode::Include : DontEnumPropertiesMode::Exclude;
|
||||
PropertyNameMode jsc_property_mode = PropertyNameMode::StringsAndSymbols;
|
||||
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 = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
auto objectValue = JSC::JSValue::decode(reinterpret_cast<EncodedJSValue>(objectNapi));
|
||||
auto* object = objectValue.getObject();
|
||||
if (!object) {
|
||||
return napi_object_expected;
|
||||
}
|
||||
|
||||
JSC::JSArray* exportKeys = ownPropertyKeys(globalObject, object, jsc_property_mode, jsc_key_mode, std::nullopt);
|
||||
// TODO: filter
|
||||
*result = toNapi(JSC::JSValue::encode(exportKeys));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
NapiClass* napiClass = NapiClass::create(vm, globalObject, utf8name, length, constructor, data, property_count, properties);
|
||||
JSC::JSValue value = JSC::JSValue(napiClass);
|
||||
if (data != nullptr) {
|
||||
napiClass->dataPtr = data;
|
||||
}
|
||||
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(value));
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value,
|
||||
napi_value* result)
|
||||
{
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
|
||||
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
JSC::JSValue jsValue = JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(value));
|
||||
|
||||
// .toString() can throw
|
||||
JSC::JSValue resultValue = JSC::JSValue(jsValue.toString(globalObject));
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(resultValue));
|
||||
|
||||
if (UNLIKELY(scope.exception())) {
|
||||
*result = reinterpret_cast<napi_value>(JSC::JSValue::encode(JSC::jsUndefined()));
|
||||
return napi_generic_failure;
|
||||
}
|
||||
scope.clearException();
|
||||
return napi_ok;
|
||||
}
|
||||
Reference in New Issue
Block a user