mirror of
https://github.com/oven-sh/bun
synced 2026-02-04 16:08:53 +00:00
Compare commits
9 Commits
dylan/pyth
...
jarred/io
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f06208915 | ||
|
|
3223a77158 | ||
|
|
e55a53ee0f | ||
|
|
e1ff68cbfa | ||
|
|
9e916bc2cc | ||
|
|
5535a5ce76 | ||
|
|
88c1e4df72 | ||
|
|
367cccc35c | ||
|
|
ca18ef068e |
@@ -9,6 +9,10 @@
|
||||
#include "JSDOMConvertEnumeration.h"
|
||||
#include <JavaScriptCore/JSArrayBufferView.h>
|
||||
#include "BunClientData.h"
|
||||
#include <JavaScriptCore/FunctionPrototype.h>
|
||||
#include <JavaScriptCore/LazyClassStructure.h>
|
||||
#include <JavaScriptCore/LazyClassStructureInlines.h>
|
||||
#include <JavaScriptCore/VMTrapsInlines.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
@@ -22,6 +26,66 @@ static JSC_DECLARE_CUSTOM_GETTER(jsStringDecoder_lastChar);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsStringDecoder_lastNeed);
|
||||
static JSC_DECLARE_CUSTOM_GETTER(jsStringDecoder_lastTotal);
|
||||
|
||||
class JSStringDecoderPrototype : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static JSStringDecoderPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSStringDecoderPrototype* ptr = new (NotNull, JSC::allocateCell<JSStringDecoderPrototype>(vm)) JSStringDecoderPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSStringDecoderPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
private:
|
||||
JSStringDecoderPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSStringDecoderConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
static JSStringDecoderConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSStringDecoderPrototype* prototype);
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
static constexpr bool needsDestruction = false;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype);
|
||||
|
||||
// Must be defined for each specialization class.
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
private:
|
||||
JSStringDecoderConstructor(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure, call, construct)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype);
|
||||
};
|
||||
|
||||
static inline JSC::EncodedJSValue jsStringDecoderCast(JSGlobalObject* globalObject, JSValue stringDecoderValue)
|
||||
{
|
||||
if (LIKELY(jsDynamicCast<JSStringDecoder*>(stringDecoderValue)))
|
||||
@@ -507,7 +571,7 @@ void JSStringDecoderConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* glo
|
||||
|
||||
JSStringDecoderConstructor* JSStringDecoderConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSStringDecoderPrototype* prototype)
|
||||
{
|
||||
JSStringDecoderConstructor* ptr = new (NotNull, JSC::allocateCell<JSStringDecoderConstructor>(vm)) JSStringDecoderConstructor(vm, structure, construct);
|
||||
JSStringDecoderConstructor* ptr = new (NotNull, JSC::allocateCell<JSStringDecoderConstructor>(vm)) JSStringDecoderConstructor(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject, prototype);
|
||||
return ptr;
|
||||
}
|
||||
@@ -550,6 +614,11 @@ JSC::EncodedJSValue JSStringDecoderConstructor::construct(JSC::JSGlobalObject* l
|
||||
return JSC::JSValue::encode(jsObject);
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue JSStringDecoderConstructor::call(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
|
||||
{
|
||||
return JSC::JSValue::encode(jsUndefined());
|
||||
}
|
||||
|
||||
void JSStringDecoderConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype)
|
||||
{
|
||||
putDirect(vm, vm.propertyNames->length, jsNumber(1), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
|
||||
@@ -561,4 +630,24 @@ void JSStringDecoderConstructor::initializeProperties(VM& vm, JSC::JSGlobalObjec
|
||||
|
||||
const ClassInfo JSStringDecoderConstructor::s_info = { "StringDecoder"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStringDecoderConstructor) };
|
||||
|
||||
void setJSStringDecoderLazyClassStructure(JSC::LazyClassStructure& lazy)
|
||||
{
|
||||
lazy.initLater(
|
||||
[](LazyClassStructure::Initializer& init) {
|
||||
auto* prototype = JSStringDecoderPrototype::create(
|
||||
init.vm, init.global, JSStringDecoderPrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
|
||||
auto* structure = JSStringDecoder::createStructure(init.vm, init.global, prototype);
|
||||
auto* constructor = JSStringDecoderConstructor::create(
|
||||
init.vm, init.global, JSStringDecoderConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
});
|
||||
}
|
||||
|
||||
void JSStringDecoder::destroy(JSCell* cell)
|
||||
{
|
||||
static_cast<JSStringDecoder*>(cell)->JSStringDecoder::~JSStringDecoder();
|
||||
}
|
||||
|
||||
} // namespace Zig
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject);
|
||||
static void destroy(JSCell*) {}
|
||||
static void destroy(JSCell*);
|
||||
|
||||
JSC::JSValue write(JSC::VM&, JSC::JSGlobalObject*, uint8_t*, uint32_t);
|
||||
JSC::JSValue end(JSC::VM&, JSC::JSGlobalObject*, uint8_t*, uint32_t);
|
||||
@@ -63,63 +63,6 @@ private:
|
||||
BufferEncodingType m_encoding;
|
||||
};
|
||||
|
||||
class JSStringDecoderPrototype : public JSC::JSNonFinalObject {
|
||||
public:
|
||||
using Base = JSC::JSNonFinalObject;
|
||||
static JSStringDecoderPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
|
||||
{
|
||||
JSStringDecoderPrototype* ptr = new (NotNull, JSC::allocateCell<JSStringDecoderPrototype>(vm)) JSStringDecoderPrototype(vm, structure);
|
||||
ptr->finishCreation(vm, globalObject);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
DECLARE_INFO;
|
||||
template<typename CellType, JSC::SubspaceAccess>
|
||||
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSStringDecoderPrototype, Base);
|
||||
return &vm.plainObjectSpace();
|
||||
}
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
private:
|
||||
JSStringDecoderPrototype(JSC::VM& vm, JSC::Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
|
||||
};
|
||||
|
||||
class JSStringDecoderConstructor final : public JSC::InternalFunction {
|
||||
public:
|
||||
using Base = JSC::InternalFunction;
|
||||
static JSStringDecoderConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSStringDecoderPrototype* prototype);
|
||||
|
||||
static constexpr unsigned StructureFlags = Base::StructureFlags;
|
||||
static constexpr bool needsDestruction = false;
|
||||
|
||||
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
|
||||
{
|
||||
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info());
|
||||
}
|
||||
|
||||
void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype);
|
||||
|
||||
// Must be defined for each specialization class.
|
||||
static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
private:
|
||||
JSStringDecoderConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction)
|
||||
: Base(vm, structure, nativeFunction, nativeFunction)
|
||||
{
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSStringDecoderPrototype* prototype);
|
||||
};
|
||||
void setJSStringDecoderLazyClassStructure(JSC::LazyClassStructure&);
|
||||
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
#include "BunObject.h"
|
||||
#include "JSNextTickQueue.h"
|
||||
#include "NodeHTTP.h"
|
||||
|
||||
#include "napi_external.h"
|
||||
using namespace Bun;
|
||||
|
||||
extern "C" JSC__JSValue Bun__NodeUtil__jsParseArgs(JSC::JSGlobalObject*, JSC::CallFrame*);
|
||||
@@ -3061,6 +3061,22 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
|
||||
this->initGeneratedLazyClasses();
|
||||
|
||||
m_NapiExternalStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
auto& global = *reinterpret_cast<Zig::GlobalObject*>(init.owner);
|
||||
|
||||
init.set(
|
||||
Bun::NapiExternal::createStructure(init.vm, init.owner, init.owner->objectPrototype()));
|
||||
});
|
||||
|
||||
m_NapiPrototypeStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
auto& global = *reinterpret_cast<Zig::GlobalObject*>(init.owner);
|
||||
|
||||
init.set(
|
||||
Bun::NapiPrototype::createStructure(init.vm, init.owner, init.owner->objectPrototype()));
|
||||
});
|
||||
|
||||
m_cachedGlobalObjectStructure.initLater(
|
||||
[](const JSC::LazyProperty<JSC::JSGlobalObject, Structure>::Initializer& init) {
|
||||
auto& global = *reinterpret_cast<Zig::GlobalObject*>(init.owner);
|
||||
@@ -3305,17 +3321,7 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
init.setStructure(structure);
|
||||
});
|
||||
|
||||
m_JSStringDecoderClassStructure.initLater(
|
||||
[](LazyClassStructure::Initializer& init) {
|
||||
auto* prototype = JSStringDecoderPrototype::create(
|
||||
init.vm, init.global, JSStringDecoderPrototype::createStructure(init.vm, init.global, init.global->objectPrototype()));
|
||||
auto* structure = JSStringDecoder::createStructure(init.vm, init.global, prototype);
|
||||
auto* constructor = JSStringDecoderConstructor::create(
|
||||
init.vm, init.global, JSStringDecoderConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype);
|
||||
init.setPrototype(prototype);
|
||||
init.setStructure(structure);
|
||||
init.setConstructor(constructor);
|
||||
});
|
||||
setJSStringDecoderLazyClassStructure(m_JSStringDecoderClassStructure);
|
||||
|
||||
m_JSReadableStateClassStructure.initLater(
|
||||
[](LazyClassStructure::Initializer& init) {
|
||||
@@ -3931,6 +3937,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
thisObject->m_JSSocketAddressStructure.visit(visitor);
|
||||
thisObject->m_cachedGlobalObjectStructure.visit(visitor);
|
||||
thisObject->m_cachedGlobalProxyStructure.visit(visitor);
|
||||
thisObject->m_NapiExternalStructure.visit(visitor);
|
||||
thisObject->m_NapiPrototypeStructure.visit(visitor);
|
||||
|
||||
thisObject->mockModule.mockFunctionStructure.visit(visitor);
|
||||
thisObject->mockModule.mockResultStructure.visit(visitor);
|
||||
|
||||
@@ -258,6 +258,9 @@ public:
|
||||
|
||||
JSWeakMap* vmModuleContextMap() { return m_vmModuleContextMap.getInitializedOnMainThread(this); }
|
||||
|
||||
Structure* NapiExternalStructure() { return m_NapiExternalStructure.getInitializedOnMainThread(this); }
|
||||
Structure* NapiPrototypeStructure() { return m_NapiPrototypeStructure.getInitializedOnMainThread(this); }
|
||||
|
||||
bool hasProcessObject() const { return m_processObject.isInitialized(); }
|
||||
|
||||
JSC::JSObject* processObject() { return m_processObject.getInitializedOnMainThread(this); }
|
||||
@@ -515,6 +518,8 @@ public:
|
||||
LazyProperty<JSGlobalObject, Structure> m_asyncBoundFunctionStructure;
|
||||
LazyProperty<JSGlobalObject, JSC::JSObject> m_JSDOMFileConstructor;
|
||||
LazyProperty<JSGlobalObject, Structure> m_JSCryptoKey;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiExternalStructure;
|
||||
LazyProperty<JSGlobalObject, Structure> m_NapiPrototypeStructure;
|
||||
|
||||
LazyProperty<JSGlobalObject, JSObject> m_bunObject;
|
||||
LazyProperty<JSGlobalObject, JSObject> m_cryptoObject;
|
||||
|
||||
@@ -59,6 +59,26 @@
|
||||
using namespace JSC;
|
||||
using namespace Zig;
|
||||
|
||||
#define NAPI_VERBOSE 0
|
||||
|
||||
#if !OS(WINDOWS)
|
||||
#if NAPI_VERBOSE
|
||||
#include <stdio.h>
|
||||
|
||||
#define NAPI_PREMABLE \
|
||||
printf("[napi] %s:%d:%s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NAPI_PREMABLE
|
||||
|
||||
#define NAPI_PREMABLE
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace Napi {
|
||||
|
||||
JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject)
|
||||
@@ -161,17 +181,8 @@ void NapiRef::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();
|
||||
|
||||
WTF_MAKE_ISO_ALLOCATED_IMPL(NapiRef);
|
||||
|
||||
@@ -314,6 +325,8 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
|
||||
extern "C" napi_status napi_set_property(napi_env env, napi_value target,
|
||||
napi_value key, napi_value value)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (UNLIKELY(!env || !target || !key)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -338,6 +351,8 @@ extern "C" napi_status napi_set_property(napi_env env, napi_value target,
|
||||
extern "C" napi_status napi_has_property(napi_env env, napi_value object,
|
||||
napi_value key, bool* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (UNLIKELY(!object || !env)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -360,6 +375,8 @@ extern "C" napi_status napi_has_property(napi_env env, napi_value object,
|
||||
extern "C" napi_status napi_get_property(napi_env env, napi_value object,
|
||||
napi_value key, napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
auto& vm = globalObject->vm();
|
||||
|
||||
@@ -382,6 +399,8 @@ extern "C" napi_status napi_get_property(napi_env env, napi_value object,
|
||||
extern "C" napi_status napi_delete_property(napi_env env, napi_value object,
|
||||
napi_value key, bool* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
auto& vm = globalObject->vm();
|
||||
|
||||
@@ -401,6 +420,8 @@ extern "C" napi_status napi_delete_property(napi_env env, napi_value object,
|
||||
extern "C" napi_status napi_has_own_property(napi_env env, napi_value object,
|
||||
napi_value key, bool* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
auto& vm = globalObject->vm();
|
||||
|
||||
@@ -422,6 +443,8 @@ extern "C" napi_status napi_set_named_property(napi_env env, napi_value object,
|
||||
const char* utf8name,
|
||||
napi_value value)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
auto target = toJS(object).getObject();
|
||||
auto& vm = globalObject->vm();
|
||||
@@ -452,6 +475,8 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env,
|
||||
napi_value* result)
|
||||
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSC::JSGlobalObject* globalObject = toJS(env);
|
||||
if (UNLIKELY(!globalObject || !result)) {
|
||||
return napi_invalid_arg;
|
||||
@@ -491,6 +516,7 @@ extern "C" napi_status napi_has_named_property(napi_env env, napi_value object,
|
||||
const char* utf8name,
|
||||
bool* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
auto& vm = globalObject->vm();
|
||||
@@ -513,6 +539,7 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object,
|
||||
const char* utf8name,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto globalObject = toJS(env);
|
||||
auto& vm = globalObject->vm();
|
||||
@@ -671,6 +698,8 @@ extern "C" napi_status napi_wrap(napi_env env,
|
||||
|
||||
napi_ref* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSValue value = toJS(js_object);
|
||||
if (!value || value.isUndefinedOrNull()) {
|
||||
return napi_object_expected;
|
||||
@@ -723,6 +752,8 @@ extern "C" napi_status napi_wrap(napi_env env,
|
||||
extern "C" napi_status napi_remove_wrap(napi_env env, napi_value js_object,
|
||||
void** result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSValue value = toJS(js_object);
|
||||
if (!value || value.isUndefinedOrNull()) {
|
||||
return napi_object_expected;
|
||||
@@ -760,6 +791,8 @@ extern "C" napi_status napi_remove_wrap(napi_env env, napi_value js_object,
|
||||
extern "C" napi_status napi_unwrap(napi_env env, napi_value js_object,
|
||||
void** result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSValue value = toJS(js_object);
|
||||
|
||||
if (!value.isObject()) {
|
||||
@@ -787,6 +820,7 @@ 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_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -831,6 +865,8 @@ extern "C" napi_status napi_get_cb_info(
|
||||
napi_value* this_arg, // [out] Receives the JS 'this' arg for the call
|
||||
void** data)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
auto inputArgsCount = argc == nullptr ? 0 : *argc;
|
||||
JSC::CallFrame* callFrame = reinterpret_cast<JSC::CallFrame*>(cbinfo);
|
||||
@@ -936,6 +972,7 @@ extern "C" napi_status napi_throw_error(napi_env env,
|
||||
const char* code,
|
||||
const char* msg)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -951,7 +988,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
|
||||
uint32_t initial_refcount,
|
||||
napi_ref* result)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
JSC::JSValue val = toJS(value);
|
||||
|
||||
if (!val || !val.isObject()) {
|
||||
@@ -991,7 +1028,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
|
||||
|
||||
extern "C" void napi_set_ref(NapiRef* ref, JSC__JSValue val_)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
JSC::JSValue val = JSC::JSValue::decode(val_);
|
||||
if (val) {
|
||||
ref->strongRef.set(ref->globalObject->vm(), val);
|
||||
@@ -1006,6 +1043,7 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object,
|
||||
void* finalize_hint,
|
||||
napi_ref* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1025,6 +1063,7 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object,
|
||||
extern "C" napi_status napi_reference_unref(napi_env env, napi_ref ref,
|
||||
uint32_t* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->unref();
|
||||
*result = napiRef->refCount;
|
||||
@@ -1037,6 +1076,7 @@ extern "C" napi_status napi_reference_unref(napi_env env, napi_ref ref,
|
||||
extern "C" napi_status napi_get_reference_value(napi_env env, napi_ref ref,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
*result = toNapi(napiRef->value());
|
||||
|
||||
@@ -1045,12 +1085,14 @@ extern "C" napi_status napi_get_reference_value(napi_env env, napi_ref ref,
|
||||
|
||||
extern "C" JSC__JSValue napi_get_reference_value_internal(NapiRef* napiRef)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
return JSC::JSValue::encode(napiRef->value());
|
||||
}
|
||||
|
||||
extern "C" napi_status napi_reference_ref(napi_env env, napi_ref ref,
|
||||
uint32_t* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->ref();
|
||||
*result = napiRef->refCount;
|
||||
@@ -1059,6 +1101,7 @@ extern "C" napi_status napi_reference_ref(napi_env env, napi_ref ref,
|
||||
|
||||
extern "C" napi_status napi_delete_reference(napi_env env, napi_ref ref)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
napiRef->~NapiRef();
|
||||
return napi_ok;
|
||||
@@ -1066,6 +1109,7 @@ extern "C" napi_status napi_delete_reference(napi_env env, napi_ref ref)
|
||||
|
||||
extern "C" void napi_delete_reference_internal(napi_ref ref)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
NapiRef* napiRef = toJS(ref);
|
||||
delete napiRef;
|
||||
}
|
||||
@@ -1074,6 +1118,7 @@ extern "C" napi_status napi_is_detached_arraybuffer(napi_env env,
|
||||
napi_value arraybuffer,
|
||||
bool* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1091,6 +1136,7 @@ extern "C" napi_status napi_is_detached_arraybuffer(napi_env env,
|
||||
extern "C" napi_status napi_detach_arraybuffer(napi_env env,
|
||||
napi_value arraybuffer)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1114,6 +1160,7 @@ extern "C" napi_status napi_adjust_external_memory(napi_env env,
|
||||
int64_t change_in_bytes,
|
||||
int64_t* adjusted_value)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
if (change_in_bytes > 0) {
|
||||
toJS(env)->vm().heap.deprecatedReportExtraMemory(change_in_bytes);
|
||||
}
|
||||
@@ -1123,6 +1170,7 @@ extern "C" napi_status napi_adjust_external_memory(napi_env env,
|
||||
|
||||
extern "C" napi_status napi_is_exception_pending(napi_env env, bool* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
auto globalObject = toJS(env);
|
||||
*result = globalObject->vm().exceptionForInspection() != nullptr;
|
||||
return napi_ok;
|
||||
@@ -1130,6 +1178,7 @@ extern "C" napi_status napi_is_exception_pending(napi_env env, bool* result)
|
||||
extern "C" napi_status napi_get_and_clear_last_exception(napi_env env,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
auto globalObject = toJS(env);
|
||||
*result = toNapi(JSC::JSValue(globalObject->vm().lastException()));
|
||||
globalObject->vm().clearLastException();
|
||||
@@ -1139,6 +1188,7 @@ extern "C" napi_status napi_get_and_clear_last_exception(napi_env env,
|
||||
extern "C" napi_status napi_fatal_exception(napi_env env,
|
||||
napi_value err)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
auto globalObject = toJS(env);
|
||||
JSC::JSValue value = toJS(err);
|
||||
JSC::JSObject* obj = value.getObject();
|
||||
@@ -1153,6 +1203,7 @@ extern "C" napi_status napi_fatal_exception(napi_env env,
|
||||
|
||||
extern "C" napi_status napi_throw(napi_env env, napi_value error)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
auto globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
@@ -1171,6 +1222,7 @@ extern "C" napi_status node_api_symbol_for(napi_env env,
|
||||
const char* utf8description,
|
||||
size_t length, napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
auto* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
if (UNLIKELY(!result || !utf8description)) {
|
||||
@@ -1188,6 +1240,7 @@ extern "C" napi_status node_api_create_syntax_error(napi_env env,
|
||||
napi_value msg,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
if (UNLIKELY(!result)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1209,6 +1262,8 @@ extern "C" napi_status node_api_throw_syntax_error(napi_env env,
|
||||
const char* code,
|
||||
const char* msg)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
auto message = msg ? WTF::String::fromUTF8(msg) : String();
|
||||
auto globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -1225,6 +1280,7 @@ extern "C" napi_status node_api_throw_syntax_error(napi_env env,
|
||||
extern "C" napi_status napi_throw_type_error(napi_env env, const char* code,
|
||||
const char* msg)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -1259,6 +1315,7 @@ extern "C" napi_status napi_create_error(napi_env env, napi_value code,
|
||||
napi_value msg,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1266,6 +1323,10 @@ extern "C" napi_status napi_create_error(napi_env env, napi_value code,
|
||||
JSC::JSValue messageValue = toJS(msg);
|
||||
|
||||
WTF::String message = messageValue.toWTFString(globalObject);
|
||||
if (message.isEmpty()) {
|
||||
message = "Error"_s;
|
||||
}
|
||||
|
||||
auto* error = JSC::createError(globalObject, message);
|
||||
if (codeValue) {
|
||||
error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0);
|
||||
@@ -1277,6 +1338,7 @@ extern "C" napi_status napi_create_error(napi_env env, napi_value code,
|
||||
extern "C" napi_status napi_throw_range_error(napi_env env, const char* code,
|
||||
const char* msg)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -1290,6 +1352,7 @@ extern "C" napi_status napi_throw_range_error(napi_env env, const char* code,
|
||||
|
||||
extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -1310,6 +1373,7 @@ extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value)
|
||||
}
|
||||
extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
@@ -1331,6 +1395,8 @@ extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value)
|
||||
|
||||
extern "C" napi_status napi_get_global(napi_env env, napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
*result = reinterpret_cast<napi_value>(globalObject->globalThis());
|
||||
@@ -1341,6 +1407,7 @@ extern "C" napi_status napi_create_range_error(napi_env env, napi_value code,
|
||||
napi_value msg,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1359,6 +1426,7 @@ extern "C" napi_status napi_get_new_target(napi_env env,
|
||||
napi_callback_info cbinfo,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
// handle:
|
||||
@@ -1379,6 +1447,7 @@ extern "C" napi_status napi_create_dataview(napi_env env, size_t length,
|
||||
size_t byte_offset,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
@@ -1541,7 +1610,7 @@ void NapiClass::finishCreation(VM& vm, NativeExecutable* executable, unsigned le
|
||||
prototypePropertyCount += property.attributes & napi_static ? 0 : 1;
|
||||
}
|
||||
|
||||
NapiPrototype* prototype = NapiPrototype::create(vm, globalObject);
|
||||
NapiPrototype* prototype = NapiPrototype::create(vm, globalObject->NapiPrototypeStructure());
|
||||
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
@@ -1599,6 +1668,7 @@ static napi_extended_error_info last_error_info;
|
||||
extern "C" napi_status
|
||||
napi_get_last_error_info(napi_env env, const napi_extended_error_info** result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
auto globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto lastException = vm.lastException();
|
||||
@@ -1633,6 +1703,8 @@ extern "C" napi_status napi_define_class(napi_env env,
|
||||
const napi_property_descriptor* properties,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (utf8name == nullptr) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1657,6 +1729,7 @@ extern "C" napi_status napi_define_class(napi_env env,
|
||||
extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
if (UNLIKELY(result == nullptr || value == nullptr || env == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1684,7 +1757,7 @@ extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value,
|
||||
extern "C" napi_status napi_get_property_names(napi_env env, napi_value object,
|
||||
napi_value* result)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1714,6 +1787,8 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
|
||||
void* finalize_hint,
|
||||
napi_value* result)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1737,6 +1812,8 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
|
||||
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_PREMABLE
|
||||
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1762,6 +1839,8 @@ extern "C" napi_status napi_get_value_string_utf8(napi_env env,
|
||||
size_t bufsize,
|
||||
size_t* writtenPtr)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSGlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -1823,6 +1902,8 @@ extern "C" napi_status napi_get_value_string_utf8(napi_env env,
|
||||
extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue,
|
||||
uint32_t index, napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
JSValue jsValue = toJS(objectValue);
|
||||
if (UNLIKELY(!env || !jsValue || !jsValue.isObject())) {
|
||||
return napi_invalid_arg;
|
||||
@@ -1844,6 +1925,8 @@ extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue,
|
||||
extern "C" napi_status napi_create_object(napi_env env, napi_value* result)
|
||||
{
|
||||
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (UNLIKELY(result == nullptr || env == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1851,7 +1934,7 @@ extern "C" napi_status napi_create_object(napi_env env, napi_value* result)
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
JSValue value = JSValue(NapiPrototype::create(vm, globalObject));
|
||||
JSValue value = JSValue(NapiPrototype::create(vm, globalObject->NapiPrototypeStructure()));
|
||||
|
||||
*result = toNapi(value);
|
||||
JSC::EnsureStillAliveScope ensureStillAlive(value);
|
||||
@@ -1863,6 +1946,7 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
|
||||
void* finalize_hint,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -1870,9 +1954,9 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
auto* structure = Bun::NapiExternal::createStructure(vm, globalObject, globalObject->objectPrototype());
|
||||
JSValue value = JSValue(Bun::NapiExternal::create(vm, structure, data, finalize_hint, finalize_cb));
|
||||
|
||||
auto* structure = globalObject->NapiExternalStructure();
|
||||
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, reinterpret_cast<void*>(finalize_cb));
|
||||
JSC::EnsureStillAliveScope ensureStillAlive(value);
|
||||
*result = toNapi(value);
|
||||
return napi_ok;
|
||||
}
|
||||
@@ -1880,6 +1964,8 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
|
||||
extern "C" napi_status napi_typeof(napi_env env, napi_value val,
|
||||
napi_valuetype* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (UNLIKELY(result == nullptr))
|
||||
return napi_invalid_arg;
|
||||
|
||||
@@ -1940,6 +2026,8 @@ extern "C" napi_status napi_typeof(napi_env env, napi_value val,
|
||||
*result = napi_object;
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1973,6 +2061,8 @@ extern "C" napi_status napi_get_value_bigint_words(napi_env env,
|
||||
size_t* word_count,
|
||||
uint64_t* words)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
|
||||
JSC::JSValue jsValue = toJS(value);
|
||||
@@ -2012,6 +2102,8 @@ extern "C" napi_status napi_get_value_bigint_words(napi_env env,
|
||||
extern "C" napi_status napi_get_value_external(napi_env env, napi_value value,
|
||||
void** result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
if (UNLIKELY(result == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
}
|
||||
@@ -2029,6 +2121,8 @@ extern "C" napi_status napi_get_value_external(napi_env env, napi_value value,
|
||||
extern "C" napi_status napi_get_instance_data(napi_env env,
|
||||
void** data)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
if (UNLIKELY(data == nullptr)) {
|
||||
return napi_invalid_arg;
|
||||
@@ -2043,6 +2137,8 @@ extern "C" napi_status napi_set_instance_data(napi_env env,
|
||||
napi_finalize finalize_cb,
|
||||
void* finalize_hint)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
if (data)
|
||||
globalObject->napiInstanceData = data;
|
||||
@@ -2059,6 +2155,8 @@ extern "C" napi_status napi_create_bigint_words(napi_env env,
|
||||
const uint64_t* words,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto* bigint = JSC::JSBigInt::tryCreateWithLength(vm, word_count);
|
||||
@@ -2085,6 +2183,8 @@ extern "C" napi_status napi_create_bigint_words(napi_env env,
|
||||
extern "C" napi_status napi_create_symbol(napi_env env, napi_value description,
|
||||
napi_value* result)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -2118,6 +2218,8 @@ extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi,
|
||||
const napi_value* argv,
|
||||
napi_value* result_ptr)
|
||||
{
|
||||
NAPI_PREMABLE
|
||||
|
||||
Zig::GlobalObject* globalObject = toJS(env);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
@@ -2139,14 +2241,21 @@ extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi,
|
||||
}
|
||||
|
||||
JSC::JSValue thisValue = toJS(recv_napi);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
if (thisValue.isEmpty()) {
|
||||
thisValue = JSC::jsUndefined();
|
||||
}
|
||||
JSC::JSValue result = call(globalObject, funcValue, callData, thisValue, args);
|
||||
|
||||
if (result_ptr) {
|
||||
*result_ptr = toNapi(result);
|
||||
if (result.isEmpty()) {
|
||||
*result_ptr = toNapi(JSC::jsUndefined());
|
||||
} else {
|
||||
*result_ptr = toNapi(result);
|
||||
}
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
RETURN_IF_EXCEPTION(scope, napi_generic_failure);
|
||||
|
||||
RELEASE_AND_RETURN(scope, napi_ok);
|
||||
}
|
||||
|
||||
@@ -192,19 +192,13 @@ public:
|
||||
|
||||
DECLARE_INFO;
|
||||
|
||||
static NapiPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
|
||||
static NapiPrototype* create(VM& vm, Structure* structure)
|
||||
{
|
||||
NapiPrototype* footprint = new (NotNull, allocateCell<NapiPrototype>(vm)) NapiPrototype(vm, structure);
|
||||
footprint->finishCreation(vm);
|
||||
return footprint;
|
||||
}
|
||||
|
||||
static NapiPrototype* create(VM& vm, JSGlobalObject* globalObject)
|
||||
{
|
||||
Structure* structure = createStructure(vm, globalObject, globalObject->objectPrototype());
|
||||
return create(vm, globalObject, structure);
|
||||
}
|
||||
|
||||
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
|
||||
{
|
||||
ASSERT(globalObject);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Bun {
|
||||
NapiExternal::~NapiExternal()
|
||||
{
|
||||
if (finalizer) {
|
||||
finalizer(toNapi(globalObject()), m_value, m_finalizerHint);
|
||||
reinterpret_cast<napi_finalize>(finalizer)(toNapi(globalObject()), m_value, m_finalizerHint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "BunBuiltinNames.h"
|
||||
#include "BunClientData.h"
|
||||
#include "node_api.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
@@ -48,14 +47,33 @@ public:
|
||||
JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, napi_finalize finalizer)
|
||||
static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, void* finalizer)
|
||||
{
|
||||
NapiExternal* accessor = new (NotNull, JSC::allocateCell<NapiExternal>(vm)) NapiExternal(vm, structure);
|
||||
|
||||
accessor->finishCreation(vm, value, finalizer_hint, finalizer);
|
||||
|
||||
#if BUN_DEBUG
|
||||
if (auto* callFrame = vm.topCallFrame) {
|
||||
auto origin = callFrame->callerSourceOrigin(vm);
|
||||
accessor->sourceOriginURL = origin.string();
|
||||
|
||||
std::unique_ptr<Vector<StackFrame>> stackTrace = makeUnique<Vector<StackFrame>>();
|
||||
vm.interpreter.getStackTrace(accessor, *stackTrace, 0, 20);
|
||||
if (!stackTrace->isEmpty()) {
|
||||
for (auto& frame : *stackTrace) {
|
||||
if (frame.hasLineAndColumnInfo()) {
|
||||
frame.computeLineAndColumn(accessor->sourceOriginLine, accessor->sourceOriginColumn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return accessor;
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, napi_finalize finalizer)
|
||||
void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, void* finalizer)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
m_value = value;
|
||||
@@ -69,7 +87,13 @@ public:
|
||||
|
||||
void* m_value;
|
||||
void* m_finalizerHint;
|
||||
napi_finalize finalizer;
|
||||
void* finalizer;
|
||||
|
||||
#if BUN_DEBUG
|
||||
String sourceOriginURL = String();
|
||||
unsigned sourceOriginLine = 0;
|
||||
unsigned sourceOriginColumn = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Zig
|
||||
@@ -1545,18 +1545,14 @@ pub const Blob = struct {
|
||||
|
||||
const State = @This();
|
||||
|
||||
/// This is a workaround for some versions of IO uring returning
|
||||
/// EAGAIN when reading a file opened with O_NONBLOCK. Since io_uring waits, we don't need to wait.
|
||||
const non_block_without_io_uring = if (Environment.isLinux) 0 else std.os.O.NONBLOCK;
|
||||
|
||||
const __opener_flags = non_block_without_io_uring | std.os.O.CLOEXEC;
|
||||
const __opener_flags = std.os.O.NONBLOCK | std.os.O.CLOEXEC;
|
||||
|
||||
const open_flags_ = if (@hasDecl(This, "open_flags"))
|
||||
This.open_flags | __opener_flags
|
||||
else
|
||||
std.os.O.RDONLY | __opener_flags;
|
||||
|
||||
pub fn getFdMac(this: *This) bun.FileDescriptor {
|
||||
pub fn getFdImpl(this: *This) bun.FileDescriptor {
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
var path_string = if (@hasField(This, "file_store"))
|
||||
this.file_store.pathlike.path
|
||||
@@ -1602,11 +1598,7 @@ pub const Blob = struct {
|
||||
}
|
||||
}
|
||||
|
||||
if (comptime Environment.isMac) {
|
||||
Callback(this, this.getFdMac());
|
||||
} else {
|
||||
this.getFdLinux(Callback);
|
||||
}
|
||||
Callback(this, this.getFdImpl());
|
||||
}
|
||||
|
||||
const WrappedOpenCallback = *const fn (*State, *http.NetworkThread.Completion, AsyncIO.OpenError!bun.FileDescriptor) void;
|
||||
@@ -1663,57 +1655,18 @@ pub const Blob = struct {
|
||||
}
|
||||
}.onOpen;
|
||||
}
|
||||
|
||||
pub fn getFdLinux(this: *This, comptime callback: OpenCallback) void {
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
var path_string = if (@hasField(This, "file_store"))
|
||||
this.file_store.pathlike.path
|
||||
else
|
||||
this.file_blob.store.?.data.file.pathlike.path;
|
||||
|
||||
var holder = bun.default_allocator.create(State) catch unreachable;
|
||||
holder.* = .{
|
||||
.context = this,
|
||||
};
|
||||
var path_buffer = bun.default_allocator.dupeZ(u8, path_string.slice()) catch unreachable;
|
||||
aio.open(
|
||||
*State,
|
||||
holder,
|
||||
comptime OpenCallbackWrapper(callback),
|
||||
&holder.open_completion,
|
||||
path_buffer,
|
||||
open_flags_,
|
||||
JSC.Node.default_permission,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn FileCloserMixin(comptime This: type) type {
|
||||
return struct {
|
||||
const Closer = @This();
|
||||
close_completion: AsyncIO.Completion = undefined,
|
||||
|
||||
pub fn doClose(this: *This) void {
|
||||
const fd = this.opened_fd;
|
||||
std.debug.assert(fd != null_fd);
|
||||
var aio = &AsyncIO.global;
|
||||
|
||||
var closer = bun.default_allocator.create(Closer) catch unreachable;
|
||||
|
||||
aio.close(
|
||||
*Closer,
|
||||
closer,
|
||||
onClose,
|
||||
&closer.close_completion,
|
||||
fd,
|
||||
);
|
||||
this.opened_fd = null_fd;
|
||||
}
|
||||
|
||||
pub fn onClose(closer: *Closer, _: *http.NetworkThread.Completion, _: AsyncIO.CloseError!void) void {
|
||||
bun.default_allocator.destroy(closer);
|
||||
_ = bun.sys.close(fd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
62
src/bun.zig
62
src/bun.zig
@@ -277,7 +277,7 @@ pub const fmt = struct {
|
||||
var remain = text;
|
||||
var prev_keyword: ?Keyword = null;
|
||||
|
||||
while (remain.len > 0) {
|
||||
outer: while (remain.len > 0) {
|
||||
if (js_lexer.isIdentifierStart(remain[0])) {
|
||||
var i: usize = 1;
|
||||
|
||||
@@ -351,24 +351,56 @@ pub const fmt = struct {
|
||||
prev_keyword = null;
|
||||
|
||||
var i: usize = 1;
|
||||
for (remain[i..]) |c| {
|
||||
if (c == char) {
|
||||
if (i < remain.len) {
|
||||
i += 1;
|
||||
}
|
||||
break;
|
||||
} else if (c == '\\') {
|
||||
i += 1;
|
||||
if (i < remain.len) {
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
if (i < remain.len) {
|
||||
i += 1;
|
||||
while (i < remain.len and remain[i] != char) {
|
||||
if (comptime char == '`') {
|
||||
if (remain[i] == '$' and i + 1 < remain.len and remain[i + 1] == '{') {
|
||||
const curly_start = i;
|
||||
i += 2;
|
||||
|
||||
while (i < remain.len and remain[i] != '}') {
|
||||
if (remain[i] == '\\') {
|
||||
i += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
try writer.print(Output.prettyFmt("<r><green>{s}<r>", true), .{remain[0..curly_start]});
|
||||
try writer.writeAll("${");
|
||||
const curly_remain = QuickAndDirtyJavaScriptSyntaxHighlighter{
|
||||
.text = remain[curly_start + 2 .. i],
|
||||
.enable_colors = this.enable_colors,
|
||||
.limited = false,
|
||||
};
|
||||
|
||||
if (curly_remain.text.len > 0) {
|
||||
try curly_remain.format("", .{}, writer);
|
||||
}
|
||||
|
||||
if (i < remain.len and remain[i] == '}') {
|
||||
i += 1;
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
remain = remain[i..];
|
||||
i = 0;
|
||||
if (remain.len > 0 and remain[0] == char) {
|
||||
try writer.writeAll(Output.prettyFmt("<r><green>`<r>", true));
|
||||
remain = remain[1..];
|
||||
continue :outer;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 1 < remain.len and remain[i] == '\\') {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Include the trailing quote, if any
|
||||
i += @as(usize, @intFromBool(i > 1 and i < remain.len and remain[i] == char));
|
||||
|
||||
try writer.print(Output.prettyFmt("<r><green>{s}<r>", true), .{remain[0..i]});
|
||||
remain = remain[i..];
|
||||
},
|
||||
|
||||
379
src/io/heap.zig
Normal file
379
src/io/heap.zig
Normal file
@@ -0,0 +1,379 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// An intrusive heap implementation backed by a pairing heap[1] implementation.
|
||||
///
|
||||
/// Why? Intrusive data structures require the element type to hold the metadata
|
||||
/// required for the structure, rather than an additional container structure.
|
||||
/// There are numerous pros/cons that are documented well by Boost[2]. For Zig,
|
||||
/// I think the primary benefits are making data structures allocation free
|
||||
/// (rather, shifting allocation up to the consumer which can choose how they
|
||||
/// want the memory to be available). There are various costs to this such as
|
||||
/// the costs of pointer chasing, larger memory overhead, requiring the element
|
||||
/// type to be aware of its container, etc. But for certain use cases an intrusive
|
||||
/// data structure can yield much better performance.
|
||||
///
|
||||
/// Usage notes:
|
||||
/// - The element T is expected to have a field "heap" of type InstrusiveHeapField.
|
||||
/// See the tests for a full example of how to set this.
|
||||
/// - You can easily make this a min or max heap by inverting the result of
|
||||
/// "less" below.
|
||||
///
|
||||
/// [1]: https://en.wikipedia.org/wiki/Pairing_heap
|
||||
/// [2]: https://www.boost.org/doc/libs/1_64_0/doc/html/intrusive/intrusive_vs_nontrusive.html
|
||||
pub fn Intrusive(
|
||||
comptime T: type,
|
||||
comptime Context: type,
|
||||
comptime less: *const fn (ctx: Context, a: *T, b: *T) bool,
|
||||
) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
root: ?*T = null,
|
||||
context: Context,
|
||||
|
||||
/// Insert a new element v into the heap. An element v can only
|
||||
/// be a member of a single heap at any given time. When compiled
|
||||
/// with runtime-safety, assertions will help verify this property.
|
||||
pub fn insert(self: *Self, v: *T) void {
|
||||
self.root = if (self.root) |root| self.meld(v, root) else v;
|
||||
}
|
||||
|
||||
/// Look at the next minimum value but do not remove it.
|
||||
pub fn peek(self: *Self) ?*T {
|
||||
return self.root;
|
||||
}
|
||||
|
||||
/// Delete the minimum value from the heap and return it.
|
||||
pub fn deleteMin(self: *Self) ?*T {
|
||||
const root = self.root orelse return null;
|
||||
self.root = if (root.heap.child) |child|
|
||||
self.combine_siblings(child)
|
||||
else
|
||||
null;
|
||||
|
||||
// Clear pointers with runtime safety so we can verify on
|
||||
// insert that values aren't incorrectly being set multiple times.
|
||||
root.heap = .{};
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/// Remove the value v from the heap.
|
||||
pub fn remove(self: *Self, v: *T) void {
|
||||
// If v doesn't have a previous value, this must be the root
|
||||
// element. If it is NOT the root element, v can't be in this
|
||||
// heap and we trigger an assertion failure.
|
||||
const prev = v.heap.prev orelse {
|
||||
assert(self.root.? == v);
|
||||
_ = self.deleteMin();
|
||||
return;
|
||||
};
|
||||
|
||||
// Detach "v" from the tree and clean up any links so it
|
||||
// is as if this node never nexisted. The previous value
|
||||
// must point to the proper next value and the pointers
|
||||
// must all be cleaned up.
|
||||
if (v.heap.next) |next| next.heap.prev = prev;
|
||||
if (prev.heap.child == v)
|
||||
prev.heap.child = v.heap.next
|
||||
else
|
||||
prev.heap.next = v.heap.next;
|
||||
v.heap.prev = null;
|
||||
v.heap.next = null;
|
||||
|
||||
// If we have children, then we need to merge them back in.
|
||||
const child = v.heap.child orelse return;
|
||||
v.heap.child = null;
|
||||
const x = self.combine_siblings(child);
|
||||
self.root = self.meld(x, self.root.?);
|
||||
}
|
||||
|
||||
/// Meld (union) two heaps together. This isn't a generalized
|
||||
/// union. It assumes that a.heap.next is null so this is only
|
||||
/// meant in specific scenarios in the pairing heap where meld
|
||||
/// is expected.
|
||||
///
|
||||
/// For example, when melding a new value "v" with an existing
|
||||
/// root "root", "v" must always be the first param.
|
||||
fn meld(self: *Self, a: *T, b: *T) *T {
|
||||
assert(a.heap.next == null);
|
||||
|
||||
if (less(self.context, a, b)) {
|
||||
// B points back to A
|
||||
b.heap.prev = a;
|
||||
|
||||
// If B has siblings, then A inherits B's siblings
|
||||
// and B's immediate sibling must point back to A to
|
||||
// maintain the doubly linked list.
|
||||
if (b.heap.next) |b_next| {
|
||||
a.heap.next = b_next;
|
||||
b_next.heap.prev = a;
|
||||
b.heap.next = null;
|
||||
}
|
||||
|
||||
// If A has a child, then B becomes the leftmost sibling
|
||||
// of that child.
|
||||
if (a.heap.child) |a_child| {
|
||||
b.heap.next = a_child;
|
||||
a_child.heap.prev = b;
|
||||
}
|
||||
|
||||
// B becomes the leftmost child of A
|
||||
a.heap.child = b;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// Replace A with B in the tree. Any of B's children
|
||||
// become siblings of A. A becomes the leftmost child of B.
|
||||
// A points back to B
|
||||
b.heap.prev = a.heap.prev;
|
||||
a.heap.prev = b;
|
||||
if (b.heap.child) |b_child| {
|
||||
a.heap.next = b_child;
|
||||
b_child.heap.prev = a;
|
||||
}
|
||||
b.heap.child = a;
|
||||
return b;
|
||||
}
|
||||
|
||||
/// Combine the siblings of the leftmost value "left" into a single
|
||||
/// new rooted with the minimum value.
|
||||
fn combine_siblings(self: *Self, left: *T) *T {
|
||||
left.heap.prev = null;
|
||||
|
||||
// Merge pairs right
|
||||
var root: *T = root: {
|
||||
var a: *T = left;
|
||||
while (true) {
|
||||
var b = a.heap.next orelse break :root a;
|
||||
a.heap.next = null;
|
||||
b = self.meld(a, b);
|
||||
a = b.heap.next orelse break :root b;
|
||||
}
|
||||
};
|
||||
|
||||
// Merge pairs left
|
||||
while (true) {
|
||||
var b = root.heap.prev orelse return root;
|
||||
b.heap.next = null;
|
||||
root = self.meld(b, root);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The state that is required for IntrusiveHeap element types. This
|
||||
/// should be set as the "heap" field in the type T.
|
||||
pub fn IntrusiveField(comptime T: type) type {
|
||||
return struct {
|
||||
child: ?*T = null,
|
||||
prev: ?*T = null,
|
||||
next: ?*T = null,
|
||||
};
|
||||
}
|
||||
|
||||
test "heap" {
|
||||
const Elem = struct {
|
||||
const Self = @This();
|
||||
value: usize = 0,
|
||||
heap: IntrusiveField(Self) = .{},
|
||||
};
|
||||
|
||||
const Heap = Intrusive(Elem, void, (struct {
|
||||
fn less(ctx: void, a: *Elem, b: *Elem) bool {
|
||||
_ = ctx;
|
||||
return a.value < b.value;
|
||||
}
|
||||
}).less);
|
||||
|
||||
var a: Elem = .{ .value = 12 };
|
||||
var b: Elem = .{ .value = 24 };
|
||||
var c: Elem = .{ .value = 7 };
|
||||
var d: Elem = .{ .value = 9 };
|
||||
|
||||
var h: Heap = .{ .context = {} };
|
||||
h.insert(&a);
|
||||
h.insert(&b);
|
||||
h.insert(&c);
|
||||
h.insert(&d);
|
||||
h.remove(&d);
|
||||
|
||||
const testing = std.testing;
|
||||
try testing.expect(h.deleteMin().?.value == 7);
|
||||
try testing.expect(h.deleteMin().?.value == 12);
|
||||
try testing.expect(h.deleteMin().?.value == 24);
|
||||
try testing.expect(h.deleteMin() == null);
|
||||
}
|
||||
|
||||
test "heap remove root" {
|
||||
const Elem = struct {
|
||||
const Self = @This();
|
||||
value: usize = 0,
|
||||
heap: IntrusiveField(Self) = .{},
|
||||
};
|
||||
|
||||
const Heap = Intrusive(Elem, void, (struct {
|
||||
fn less(ctx: void, a: *Elem, b: *Elem) bool {
|
||||
_ = ctx;
|
||||
return a.value < b.value;
|
||||
}
|
||||
}).less);
|
||||
|
||||
var a: Elem = .{ .value = 12 };
|
||||
var b: Elem = .{ .value = 24 };
|
||||
|
||||
var h: Heap = .{ .context = {} };
|
||||
h.insert(&a);
|
||||
h.insert(&b);
|
||||
h.remove(&a);
|
||||
|
||||
const testing = std.testing;
|
||||
try testing.expect(h.deleteMin().?.value == 24);
|
||||
try testing.expect(h.deleteMin() == null);
|
||||
}
|
||||
|
||||
test "heap remove with children" {
|
||||
const Elem = struct {
|
||||
const Self = @This();
|
||||
value: usize = 0,
|
||||
heap: IntrusiveField(Self) = .{},
|
||||
};
|
||||
|
||||
const Heap = Intrusive(Elem, void, (struct {
|
||||
fn less(ctx: void, a: *Elem, b: *Elem) bool {
|
||||
_ = ctx;
|
||||
return a.value < b.value;
|
||||
}
|
||||
}).less);
|
||||
|
||||
var a: Elem = .{ .value = 36 };
|
||||
var b: Elem = .{ .value = 24 };
|
||||
var c: Elem = .{ .value = 12 };
|
||||
|
||||
var h: Heap = .{ .context = {} };
|
||||
h.insert(&a);
|
||||
h.insert(&b);
|
||||
h.insert(&c);
|
||||
h.remove(&b);
|
||||
|
||||
const testing = std.testing;
|
||||
try testing.expect(h.deleteMin().?.value == 12);
|
||||
try testing.expect(h.deleteMin().?.value == 36);
|
||||
try testing.expect(h.deleteMin() == null);
|
||||
}
|
||||
|
||||
test "heap equal values" {
|
||||
const testing = std.testing;
|
||||
|
||||
const Elem = struct {
|
||||
const Self = @This();
|
||||
value: usize = 0,
|
||||
heap: IntrusiveField(Self) = .{},
|
||||
};
|
||||
|
||||
const Heap = Intrusive(Elem, void, (struct {
|
||||
fn less(ctx: void, a: *Elem, b: *Elem) bool {
|
||||
_ = ctx;
|
||||
return a.value < b.value;
|
||||
}
|
||||
}).less);
|
||||
|
||||
var a: Elem = .{ .value = 1 };
|
||||
var b: Elem = .{ .value = 2 };
|
||||
var c: Elem = .{ .value = 3 };
|
||||
var d: Elem = .{ .value = 4 };
|
||||
|
||||
var h: Heap = .{ .context = {} };
|
||||
h.insert(&a);
|
||||
h.insert(&b);
|
||||
h.insert(&c);
|
||||
h.insert(&d);
|
||||
|
||||
try testing.expect(h.deleteMin().?.value == 1);
|
||||
try testing.expect(h.deleteMin().?.value == 2);
|
||||
try testing.expect(h.deleteMin().?.value == 3);
|
||||
try testing.expect(h.deleteMin().?.value == 4);
|
||||
try testing.expect(h.deleteMin() == null);
|
||||
}
|
||||
|
||||
test "heap: million values" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const Elem = struct {
|
||||
const Self = @This();
|
||||
value: usize = 0,
|
||||
heap: IntrusiveField(Self) = .{},
|
||||
};
|
||||
|
||||
const Heap = Intrusive(Elem, void, (struct {
|
||||
fn less(ctx: void, a: *Elem, b: *Elem) bool {
|
||||
_ = ctx;
|
||||
return a.value < b.value;
|
||||
}
|
||||
}).less);
|
||||
|
||||
const NUM_TIMERS: usize = 1000 * 1000;
|
||||
var elems = try alloc.alloc(Elem, NUM_TIMERS);
|
||||
defer alloc.free(elems);
|
||||
|
||||
var i: usize = 0;
|
||||
var value: usize = 0;
|
||||
while (i < NUM_TIMERS) : (i += 1) {
|
||||
if (i % 100 == 0) value += 1;
|
||||
elems[i] = .{ .value = value };
|
||||
}
|
||||
|
||||
var h: Heap = .{ .context = {} };
|
||||
for (elems) |*elem| {
|
||||
h.insert(elem);
|
||||
}
|
||||
|
||||
var count: usize = 0;
|
||||
var last: usize = 0;
|
||||
while (h.deleteMin()) |elem| {
|
||||
count += 1;
|
||||
try testing.expect(elem.value >= last);
|
||||
last = elem.value;
|
||||
}
|
||||
try testing.expect(h.deleteMin() == null);
|
||||
try testing.expect(count == NUM_TIMERS);
|
||||
}
|
||||
|
||||
test "heap: dangling next pointer" {
|
||||
const testing = std.testing;
|
||||
const Elem = struct {
|
||||
const Self = @This();
|
||||
value: usize = 0,
|
||||
heap: IntrusiveField(Self) = .{},
|
||||
};
|
||||
|
||||
const Heap = Intrusive(Elem, void, (struct {
|
||||
fn less(ctx: void, a: *Elem, b: *Elem) bool {
|
||||
_ = ctx;
|
||||
return a.value < b.value;
|
||||
}
|
||||
}).less);
|
||||
|
||||
var a: Elem = .{ .value = 2 };
|
||||
var b: Elem = .{ .value = 4 };
|
||||
var c: Elem = .{ .value = 5 };
|
||||
var d: Elem = .{ .value = 1 };
|
||||
var e: Elem = .{ .value = 3 };
|
||||
|
||||
var h: Heap = .{ .context = {} };
|
||||
h.insert(&a);
|
||||
h.insert(&b);
|
||||
h.insert(&c);
|
||||
h.insert(&d);
|
||||
h.insert(&e);
|
||||
|
||||
try testing.expect(h.deleteMin().?.value == 1);
|
||||
try testing.expect(h.deleteMin().?.value == 2);
|
||||
try testing.expect(h.deleteMin().?.value == 3);
|
||||
try testing.expect(h.deleteMin().?.value == 4);
|
||||
try testing.expect(h.deleteMin().?.value == 5);
|
||||
try testing.expect(h.deleteMin() == null);
|
||||
}
|
||||
708
src/io/io.zig
Normal file
708
src/io/io.zig
Normal file
@@ -0,0 +1,708 @@
|
||||
const bun = @import("root").bun;
|
||||
const std = @import("std");
|
||||
const sys = bun.sys;
|
||||
const linux = std.os.linux;
|
||||
const Environment = bun.Environment;
|
||||
const heap = @import("./heap.zig");
|
||||
const JSC = bun.JSC;
|
||||
|
||||
const log = bun.Output.scoped(.loop, false);
|
||||
|
||||
const TimerHeap = heap.Intrusive(Timer, void, Timer.less);
|
||||
|
||||
const os = std.os;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub const Loop = struct {
|
||||
pending: Request.Queue = .{},
|
||||
waker: bun.Async.Waker,
|
||||
|
||||
timers: TimerHeap = .{ .context = {} },
|
||||
|
||||
cached_now: os.timespec = .{
|
||||
.tv_nsec = 0,
|
||||
.tv_sec = 0,
|
||||
},
|
||||
active: usize = 0,
|
||||
|
||||
var loop: Loop = undefined;
|
||||
|
||||
pub fn schedule(this: *Loop, request: *Request) void {
|
||||
this.pending.push(request);
|
||||
this.waker.wake();
|
||||
}
|
||||
|
||||
pub fn tickEpoll(this: *Loop) void {
|
||||
if (comptime !Environment.isLinux) {
|
||||
@compileError("not implemented");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
|
||||
// Process pending requests
|
||||
{
|
||||
var pending_batch = this.pending.popBatch();
|
||||
var pending = pending_batch.iterator();
|
||||
|
||||
while (pending.next()) |request| {
|
||||
switch (request.callback(request)) {
|
||||
.readable => |readable| {
|
||||
switch (readable.poll.registerWithFd(this, .poll_readable, true, @intCast(readable.fd))) {
|
||||
.err => |err| {
|
||||
readable.onError(request, err);
|
||||
},
|
||||
.result => {
|
||||
this.active += 1;
|
||||
},
|
||||
}
|
||||
},
|
||||
.writable => |writable| {
|
||||
switch (writable.poll.registerWithFd(this, .poll_writable, true, @intCast(writable.fd))) {
|
||||
.err => |err| {
|
||||
writable.onError(request, err);
|
||||
},
|
||||
.result => {
|
||||
this.active += 1;
|
||||
},
|
||||
}
|
||||
},
|
||||
.close => |close| {
|
||||
switch (close.poll.unregisterWithFd(this, @intCast(close.fd))) {
|
||||
.result, .err => {
|
||||
this.active -= 1;
|
||||
close.onDone(request);
|
||||
},
|
||||
}
|
||||
},
|
||||
.timer => |timer| {
|
||||
while (true) {
|
||||
switch (timer.state) {
|
||||
.PENDING => {
|
||||
timer.state = .ACTIVE;
|
||||
if (Timer.less({}, timer, &.{ .next = this.cached_now })) {
|
||||
if (timer.fire() == .rearm) {
|
||||
if (timer.reset) |reset| {
|
||||
timer.next = reset;
|
||||
timer.reset = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
this.timers.insert(timer);
|
||||
},
|
||||
.ACTIVE => {
|
||||
@panic("timer is already active");
|
||||
},
|
||||
.CANCELLED => {
|
||||
timer.deinit();
|
||||
break;
|
||||
},
|
||||
.FIRED => {
|
||||
@panic("timer has already fired");
|
||||
},
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.drainExpiredTimers();
|
||||
|
||||
// Determine our next timeout based on the timers
|
||||
const timeout: i32 = if (this.active == 0) 0 else timeout: {
|
||||
const t = this.timers.peek() orelse break :timeout -1;
|
||||
|
||||
// Determine the time in milliseconds.
|
||||
const ms_now = @as(u64, @intCast(this.cached_now.tv_sec)) * std.time.ms_per_s +
|
||||
@as(u64, @intCast(this.cached_now.tv_nsec)) / std.time.ns_per_ms;
|
||||
const ms_next = @as(u64, @intCast(t.next.tv_sec)) * std.time.ms_per_s +
|
||||
@as(u64, @intCast(t.next.tv_nsec)) / std.time.ns_per_ms;
|
||||
break :timeout @as(i32, @intCast(ms_next -| ms_now));
|
||||
};
|
||||
|
||||
var events: [EventType]256 = undefined;
|
||||
|
||||
const rc = linux.epoll_wait(
|
||||
this.fd(),
|
||||
&events,
|
||||
@intCast(events.len),
|
||||
timeout,
|
||||
);
|
||||
|
||||
switch (std.os.linux.getErrno(rc)) {
|
||||
.INTR => continue,
|
||||
.SUCCESS => {},
|
||||
else => |e| bun.Output.panic("epoll_wait: {s}", .{@tagName(e)}),
|
||||
}
|
||||
|
||||
this.update_now();
|
||||
|
||||
const current_events: []std.os.linux.epoll_event = events[0..rc];
|
||||
for (current_events) |event| {
|
||||
const pollable: Pollable = Pollable.from(event.data.u64);
|
||||
Poll.onUpdateEpoll(pollable.poll(), pollable.tag(), event.events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tickKqueue(this: *Loop) void {
|
||||
if (comptime !Environment.isMac) {
|
||||
@compileError("not implemented");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var stack_fallback = std.heap.stackFallback(@sizeOf([256]EventType), bun.default_allocator);
|
||||
var events_list: std.ArrayList(EventType) = std.ArrayList(EventType).initCapacity(stack_fallback.allocator, 256) catch unreachable;
|
||||
defer events_list.deinit();
|
||||
|
||||
// Process pending requests
|
||||
{
|
||||
var pending_batch = this.pending.popBatch();
|
||||
var pending = pending_batch.iterator();
|
||||
events_list.ensureUnusedCapacity(pending.batch.count) catch bun.outOfMemory();
|
||||
|
||||
while (pending.next()) |request| {
|
||||
switch (request.callback(request)) {
|
||||
.readable => |readable| {
|
||||
const i = events_list.items.len;
|
||||
assert(i + 1 <= events_list.capacity);
|
||||
events_list.items.len += 1;
|
||||
|
||||
Poll.Flags.applyKQueue(
|
||||
.readable,
|
||||
readable.tag,
|
||||
readable.poll,
|
||||
readable.fd,
|
||||
&events_list.items.ptr[i],
|
||||
);
|
||||
},
|
||||
.writable => |writable| {
|
||||
const i = events_list.items.len;
|
||||
assert(i + 1 <= events_list.capacity);
|
||||
events_list.items.len += 1;
|
||||
|
||||
Poll.Flags.applyKQueue(
|
||||
.writable,
|
||||
writable.tag,
|
||||
writable.poll,
|
||||
writable.fd,
|
||||
&events_list.items.ptr[i],
|
||||
);
|
||||
},
|
||||
.close => |close| {
|
||||
const i = events_list.items.len;
|
||||
assert(i + 1 <= events_list.capacity);
|
||||
events_list.items.len += 1;
|
||||
|
||||
Poll.Flags.applyKQueue(
|
||||
.close,
|
||||
close.tag,
|
||||
close.poll,
|
||||
close.fd,
|
||||
&events_list.items.ptr[i],
|
||||
);
|
||||
},
|
||||
.timer => |timer| {
|
||||
while (true) {
|
||||
switch (timer.state) {
|
||||
.PENDING => {
|
||||
timer.state = .ACTIVE;
|
||||
if (Timer.less({}, timer, &.{ .next = this.cached_now })) {
|
||||
if (timer.fire() == .rearm) {
|
||||
if (timer.reset) |reset| {
|
||||
timer.next = reset;
|
||||
timer.reset = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
this.timers.insert(timer);
|
||||
},
|
||||
.ACTIVE => {
|
||||
@panic("timer is already active");
|
||||
},
|
||||
.CANCELLED => {
|
||||
timer.deinit();
|
||||
break;
|
||||
},
|
||||
.FIRED => {
|
||||
@panic("timer has already fired");
|
||||
},
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.drainExpiredTimers();
|
||||
const change_count = events_list.items.len;
|
||||
|
||||
// Determine our next timeout based on the timers
|
||||
const timeout: ?std.os.timespec = timeout: {
|
||||
const t = this.timers.peek() orelse break :timeout null;
|
||||
var out: std.os.timespec = undefined;
|
||||
out.tv_sec = t.next.tv_sec -| this.cached_now.tv_sec;
|
||||
out.tv_nsec = t.next.tv_nsec -| this.cached_now.tv_nsec;
|
||||
|
||||
break :timeout out;
|
||||
};
|
||||
|
||||
const rc = os.system.kevent64(
|
||||
this.fd(),
|
||||
events_list.items.ptr,
|
||||
@intCast(change_count),
|
||||
// The same array may be used for the changelist and eventlist.
|
||||
events_list.items.ptr,
|
||||
// we set 0 here so that if we get an error on
|
||||
// registration, it becomes errno
|
||||
@intCast(events_list.capacity),
|
||||
0,
|
||||
if (timeout) |*t| t else null,
|
||||
);
|
||||
|
||||
switch (std.c.getErrno(rc)) {
|
||||
.INTR => continue,
|
||||
.SUCCESS => {},
|
||||
else => |e| bun.Output.panic("kevent64 failed: {s}", .{@tagName(e)}),
|
||||
}
|
||||
|
||||
this.update_now();
|
||||
|
||||
assert(rc <= events_list.capacity);
|
||||
const current_events: []std.os.darwin.kevent64_s = events_list.items.ptr[0..rc];
|
||||
|
||||
for (current_events) |event| {
|
||||
const pollable: Pollable = Pollable.from(event.udata);
|
||||
Poll.onUpdateKQueue(pollable.poll(), pollable.tag(), event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn drainExpiredTimers(this: *Loop) void {
|
||||
const now = Timer{ .next = this.cached_now };
|
||||
|
||||
// Run our expired timers
|
||||
while (this.timers.peek()) |t| {
|
||||
if (!Timer.less({}, t, &now)) break;
|
||||
|
||||
// Remove the timer
|
||||
assert(this.timers.deleteMin().? == t);
|
||||
|
||||
// Mark completion as done
|
||||
t.state = .FIRED;
|
||||
|
||||
switch (t.fire()) {
|
||||
.disarm => {},
|
||||
.rearm => |new| {
|
||||
t.next = new;
|
||||
t.reset = null;
|
||||
t.state = .ACTIVE;
|
||||
this.timers.insert(t);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_now(this: *Loop) void {
|
||||
if (comptime Environment.isLinux) {
|
||||
const rc = linux.clock_gettime(linux.CLOCK.MONOTONIC, &this.cached_now);
|
||||
assert(rc == 0);
|
||||
} else if (comptime Environment.isMac) {
|
||||
std.os.clock_gettime(std.os.CLOCK.MONOTONIC, &this.cached_now) catch {};
|
||||
} else {
|
||||
@compileError("TODO: implement poll for this platform");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const EventType = if (Environment.isLinux) linux.epoll_event else std.os.system.kevent64_s;
|
||||
|
||||
pub const Request = struct {
|
||||
next: ?*Request = null,
|
||||
callback: *const fn (*Request) Action,
|
||||
|
||||
pub const Queue = bun.UnboundedQueue(Request, .next);
|
||||
};
|
||||
|
||||
pub const Action = union(enum) {
|
||||
readable: FileAction,
|
||||
writable: FileAction,
|
||||
close: CloseAction,
|
||||
timer: *Timer,
|
||||
|
||||
pub const FileAction = struct {
|
||||
fd: bun.FileDescriptor,
|
||||
poll: *Poll,
|
||||
ctx: *anyopaque,
|
||||
tag: Pollable.Tag,
|
||||
onError: *const fn (*anyopaque, sys.Error) void,
|
||||
};
|
||||
|
||||
pub const CloseAction = struct {
|
||||
fd: bun.FileDescriptor,
|
||||
poll: *Poll,
|
||||
ctx: *anyopaque,
|
||||
tag: Pollable.Tag,
|
||||
onDone: *const fn (*anyopaque) void,
|
||||
};
|
||||
};
|
||||
|
||||
const Pollable = struct {
|
||||
value: bun.TaggedPointer,
|
||||
|
||||
const Tag = enum(bun.TaggedPointer.Tag) {};
|
||||
pub fn init(t: Tag, p: *Poll) Pollable {
|
||||
return Pollable{
|
||||
.value = bun.TaggedPointer.init(p, @intFromEnum(t)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn poll(this: Pollable) *Poll {
|
||||
return this.value.get(Poll);
|
||||
}
|
||||
|
||||
pub fn tag(this: Pollable) Tag {
|
||||
return @enumFromInt(this.value.data);
|
||||
}
|
||||
|
||||
pub fn ptr(this: Pollable) *anyopaque {
|
||||
return this.value.to();
|
||||
}
|
||||
};
|
||||
|
||||
pub const Timer = struct {
|
||||
/// The absolute time to fire this timer next.
|
||||
next: os.timespec,
|
||||
|
||||
/// Only used internally. If this is non-null and timer is
|
||||
/// CANCELLED, then the timer is rearmed automatically with this
|
||||
/// as the next time. The callback will not be called on the
|
||||
/// cancellation.
|
||||
reset: ?os.timespec = null,
|
||||
|
||||
/// Internal heap fields.
|
||||
heap: heap.IntrusiveField(Timer) = .{},
|
||||
|
||||
state: State = .PENDING,
|
||||
|
||||
pub const State = enum {
|
||||
/// The timer is waiting to be enabled.
|
||||
PENDING,
|
||||
|
||||
/// The timer is active and will fire at the next time.
|
||||
ACTIVE,
|
||||
|
||||
/// The timer has been cancelled and will not fire.
|
||||
CANCELLED,
|
||||
|
||||
/// The timer has fired and the callback has been called.
|
||||
FIRED,
|
||||
};
|
||||
|
||||
fn less(_: void, a: *const Timer, b: *const Timer) bool {
|
||||
return a.ns() < b.ns();
|
||||
}
|
||||
|
||||
/// Returns the nanoseconds of this timer. Note that maxInt(u64) ns is
|
||||
/// 584 years so if we get any overflows we just use maxInt(u64). If
|
||||
/// any software is running in 584 years waiting on this timer...
|
||||
/// shame on me I guess... but I'll be dead.
|
||||
fn ns(self: *const Timer) u64 {
|
||||
assert(self.next.tv_sec >= 0);
|
||||
assert(self.next.tv_nsec >= 0);
|
||||
|
||||
const max = std.math.maxInt(u64);
|
||||
const s_ns = std.math.mul(
|
||||
u64,
|
||||
@as(u64, @intCast(self.next.tv_sec)),
|
||||
std.time.ns_per_s,
|
||||
) catch return max;
|
||||
return std.math.add(u64, s_ns, @as(u64, @intCast(self.next.tv_nsec))) catch
|
||||
return max;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Poll = struct {
|
||||
flags: Flags,
|
||||
|
||||
pub const Flags = enum {
|
||||
// What are we asking the event loop about?
|
||||
|
||||
/// Poll for readable events
|
||||
poll_readable,
|
||||
|
||||
/// Poll for writable events
|
||||
poll_writable,
|
||||
|
||||
/// Poll for process-related events
|
||||
poll_process,
|
||||
|
||||
/// Poll for machport events
|
||||
poll_machport,
|
||||
|
||||
// What did the event loop tell us?
|
||||
readable,
|
||||
writable,
|
||||
process,
|
||||
eof,
|
||||
hup,
|
||||
machport,
|
||||
|
||||
// What is the type of file descriptor?
|
||||
fifo,
|
||||
tty,
|
||||
|
||||
one_shot,
|
||||
needs_rearm,
|
||||
|
||||
closed,
|
||||
|
||||
nonblocking,
|
||||
|
||||
was_ever_registered,
|
||||
ignore_updates,
|
||||
|
||||
cancelled,
|
||||
|
||||
pub const Set = std.EnumSet(Flags);
|
||||
pub const Struct = std.enums.EnumFieldStruct(Flags, bool, false);
|
||||
|
||||
pub fn fromKQueueEvent(kqueue_event: std.os.system.kevent64_s) Flags.Set {
|
||||
var flags = Flags.Set{};
|
||||
if (kqueue_event.filter == std.os.system.EVFILT_READ) {
|
||||
flags.insert(Flags.readable);
|
||||
log("readable", .{});
|
||||
if (kqueue_event.flags & std.os.system.EV_EOF != 0) {
|
||||
flags.insert(Flags.hup);
|
||||
log("hup", .{});
|
||||
}
|
||||
} else if (kqueue_event.filter == std.os.system.EVFILT_WRITE) {
|
||||
flags.insert(Flags.writable);
|
||||
log("writable", .{});
|
||||
if (kqueue_event.flags & std.os.system.EV_EOF != 0) {
|
||||
flags.insert(Flags.hup);
|
||||
log("hup", .{});
|
||||
}
|
||||
} else if (kqueue_event.filter == std.os.system.EVFILT_PROC) {
|
||||
log("proc", .{});
|
||||
flags.insert(Flags.process);
|
||||
} else if (kqueue_event.filter == std.os.system.EVFILT_MACHPORT) {
|
||||
log("machport", .{});
|
||||
flags.insert(Flags.machport);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
pub fn fromEpollEvent(epoll: std.os.linux.epoll_event) Flags.Set {
|
||||
var flags = Flags.Set{};
|
||||
if (epoll.events & std.os.linux.EPOLL.IN != 0) {
|
||||
flags.insert(Flags.readable);
|
||||
log("readable", .{});
|
||||
}
|
||||
if (epoll.events & std.os.linux.EPOLL.OUT != 0) {
|
||||
flags.insert(Flags.writable);
|
||||
log("writable", .{});
|
||||
}
|
||||
if (epoll.events & std.os.linux.EPOLL.ERR != 0) {
|
||||
flags.insert(Flags.eof);
|
||||
log("eof", .{});
|
||||
}
|
||||
if (epoll.events & std.os.linux.EPOLL.HUP != 0) {
|
||||
flags.insert(Flags.hup);
|
||||
log("hup", .{});
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
pub fn applyKQueue(
|
||||
comptime action: @Type(.EnumLiteral),
|
||||
tag: Pollable.Tag,
|
||||
poll: *Poll,
|
||||
fd: bun.FileDescriptor,
|
||||
kqueue_event: *std.os.system.kevent64_s,
|
||||
) void {
|
||||
defer {
|
||||
switch (comptime action) {
|
||||
.readable => poll.flags.insert(Flags.poll_readable),
|
||||
.writable => poll.flags.insert(Flags.poll_writable),
|
||||
.cancel => {
|
||||
if (poll.flags.contains(Flags.poll_readable)) {
|
||||
poll.flags.remove(Flags.poll_readable);
|
||||
} else if (poll.flags.contains(Flags.poll_writable)) {
|
||||
poll.flags.remove(Flags.poll_writable);
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
if (comptime Environment.allow_assert and action != .cancel) {
|
||||
generation_number += 1;
|
||||
poll.generation_number = generation_number;
|
||||
}
|
||||
}
|
||||
|
||||
kqueue_event.* = switch (comptime action) {
|
||||
.readable => .{
|
||||
.ident = @as(u64, @intCast(fd)),
|
||||
.filter = std.os.system.EVFILT_READ,
|
||||
.data = 0,
|
||||
.fflags = 0,
|
||||
.udata = @intFromPtr(Pollable.init(tag, this).ptr()),
|
||||
.flags = std.c.EV_ADD | one_shot_flag,
|
||||
.ext = .{ generation_number, 0 },
|
||||
},
|
||||
.writable => .{
|
||||
.ident = @as(u64, @intCast(fd)),
|
||||
.filter = std.os.system.EVFILT_WRITE,
|
||||
.data = 0,
|
||||
.fflags = 0,
|
||||
.udata = @intFromPtr(Pollable.init(tag, this).ptr()),
|
||||
.flags = std.c.EV_ADD | one_shot_flag,
|
||||
.ext = .{ generation_number, 0 },
|
||||
},
|
||||
.cancel => if (poll.flags.contains(.poll_readable)) .{
|
||||
.ident = @as(u64, @intCast(fd)),
|
||||
.filter = std.os.system.EVFILT_READ,
|
||||
.data = 0,
|
||||
.fflags = 0,
|
||||
.udata = @intFromPtr(Pollable.init(tag, this).ptr()),
|
||||
.flags = std.c.EV_DELETE,
|
||||
.ext = .{ poll.generation_number, 0 },
|
||||
} else if (poll.flags.contains(.poll_writable)) .{
|
||||
.ident = @as(u64, @intCast(fd)),
|
||||
.filter = std.os.system.EVFILT_WRITE,
|
||||
.data = 0,
|
||||
.fflags = 0,
|
||||
.udata = @intFromPtr(Pollable.init(tag, this).ptr()),
|
||||
.flags = std.c.EV_DELETE,
|
||||
.ext = .{ poll.generation_number, 0 },
|
||||
} else unreachable,
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn unregisterWithFd(this: *Poll, fd: u64) JSC.Maybe(void) {}
|
||||
|
||||
pub fn onUpdateKQueue(
|
||||
this: *Poll,
|
||||
event: std.os.system.kevent64_s,
|
||||
) JSC.Maybe(void) {}
|
||||
|
||||
pub fn onUpdateEpoll(
|
||||
this: *Poll,
|
||||
event: linux.epoll_event,
|
||||
) JSC.Maybe(void) {}
|
||||
|
||||
pub fn registerForEpoll(this: *Poll, loop: *Loop, flag: Flags, one_shot: bool, fd: u64) JSC.Maybe(void) {
|
||||
const watcher_fd = loop.fd;
|
||||
|
||||
log("register: {s} ({d})", .{ @tagName(flag), fd });
|
||||
|
||||
std.debug.assert(fd != bun.invalid_fd);
|
||||
|
||||
if (one_shot) {
|
||||
this.flags.insert(.one_shot);
|
||||
}
|
||||
|
||||
if (comptime Environment.isLinux) {
|
||||
const one_shot_flag: u32 = if (!this.flags.contains(.one_shot)) 0 else linux.EPOLL.ONESHOT;
|
||||
|
||||
const flags: u32 = switch (flag) {
|
||||
.process,
|
||||
.readable,
|
||||
=> linux.EPOLL.IN | linux.EPOLL.HUP | one_shot_flag,
|
||||
.writable => linux.EPOLL.OUT | linux.EPOLL.HUP | linux.EPOLL.ERR | one_shot_flag,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
var event = linux.epoll_event{ .events = flags, .data = .{ .u64 = @intFromPtr(Pollable.init(this).ptr()) } };
|
||||
|
||||
var op: u32 = if (this.isRegistered() or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD;
|
||||
|
||||
const ctl = linux.epoll_ctl(
|
||||
watcher_fd,
|
||||
op,
|
||||
@intCast(fd),
|
||||
&event,
|
||||
);
|
||||
this.flags.insert(.was_ever_registered);
|
||||
if (JSC.Maybe(void).errnoSys(ctl, .epoll_ctl)) |errno| {
|
||||
this.deactivate(loop);
|
||||
return errno;
|
||||
}
|
||||
} else if (comptime Environment.isMac) {
|
||||
var changelist = std.mem.zeroes([2]std.os.system.kevent64_s);
|
||||
|
||||
// output events only include change errors
|
||||
const KEVENT_FLAG_ERROR_EVENTS = 0x000002;
|
||||
|
||||
// The kevent() system call returns the number of events placed in
|
||||
// the eventlist, up to the value given by nevents. If the time
|
||||
// limit expires, then kevent() returns 0.
|
||||
const rc = rc: {
|
||||
while (true) {
|
||||
const rc = std.os.system.kevent64(
|
||||
watcher_fd,
|
||||
&changelist,
|
||||
1,
|
||||
// The same array may be used for the changelist and eventlist.
|
||||
&changelist,
|
||||
// we set 0 here so that if we get an error on
|
||||
// registration, it becomes errno
|
||||
0,
|
||||
KEVENT_FLAG_ERROR_EVENTS,
|
||||
&timeout,
|
||||
);
|
||||
|
||||
if (std.c.getErrno(rc) == .INTR) continue;
|
||||
break :rc rc;
|
||||
}
|
||||
};
|
||||
|
||||
this.flags.insert(.was_ever_registered);
|
||||
|
||||
// If an error occurs while
|
||||
// processing an element of the changelist and there is enough room
|
||||
// in the eventlist, then the event will be placed in the eventlist
|
||||
// with EV_ERROR set in flags and the system error in data.
|
||||
if (changelist[0].flags == std.c.EV_ERROR and changelist[0].data != 0) {
|
||||
return JSC.Maybe(void).errnoSys(changelist[0].data, .kevent).?;
|
||||
// Otherwise, -1 will be returned, and errno will be set to
|
||||
// indicate the error condition.
|
||||
}
|
||||
|
||||
const errno = std.c.getErrno(rc);
|
||||
|
||||
if (errno != .SUCCESS) {
|
||||
this.deactivate(loop);
|
||||
return JSC.Maybe(void){
|
||||
.err = bun.sys.Error.fromCode(errno, .kqueue),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
bun.todo(@src(), {});
|
||||
}
|
||||
this.flags.insert(switch (flag) {
|
||||
.readable => .poll_readable,
|
||||
.process => if (comptime Environment.isLinux) .poll_readable else .poll_process,
|
||||
.writable => .poll_writable,
|
||||
.machport => .poll_machport,
|
||||
else => unreachable,
|
||||
});
|
||||
this.flags.remove(.needs_rearm);
|
||||
|
||||
return JSC.Maybe(void).success;
|
||||
}
|
||||
};
|
||||
1347
src/io/io_darwin.zig
1347
src/io/io_darwin.zig
File diff suppressed because it is too large
Load Diff
1333
src/io/io_linux.zig
1333
src/io/io_linux.zig
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7461,7 +7461,7 @@ var require__ = __commonJS({
|
||||
BlockHash = common.BlockHash,
|
||||
sha1_K = [1518500249, 1859775393, 2400959708, 3395469782];
|
||||
function SHA1() {
|
||||
if (!(this instanceof SHA1)) return new SHA1();
|
||||
if (!new.target) return new SHA1();
|
||||
BlockHash.$call(this),
|
||||
(this.h = [1732584193, 4023233417, 2562383102, 271733878, 3285377520]),
|
||||
(this.W = new Array(80));
|
||||
@@ -7526,7 +7526,7 @@ var require__2 = __commonJS({
|
||||
3329325298,
|
||||
];
|
||||
function SHA256() {
|
||||
if (!(this instanceof SHA256)) return new SHA256();
|
||||
if (!new.target) return new SHA256();
|
||||
BlockHash.$call(this),
|
||||
(this.h = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]),
|
||||
(this.k = sha256_K),
|
||||
@@ -7576,7 +7576,7 @@ var require__3 = __commonJS({
|
||||
var utils = require_utils4(),
|
||||
SHA256 = require__2();
|
||||
function SHA224() {
|
||||
if (!(this instanceof SHA224)) return new SHA224();
|
||||
if (!new.target) return new SHA224();
|
||||
SHA256.$call(this),
|
||||
(this.h = [3238371032, 914150663, 812702999, 4144912697, 4290775857, 1750603025, 1694076839, 3204075428]);
|
||||
}
|
||||
@@ -7632,7 +7632,7 @@ var require__4 = __commonJS({
|
||||
3409855158, 1501505948, 4234509866, 1607167915, 987167468, 1816402316, 1246189591,
|
||||
];
|
||||
function SHA512() {
|
||||
if (!(this instanceof SHA512)) return new SHA512();
|
||||
if (!new.target) return new SHA512();
|
||||
BlockHash.$call(this),
|
||||
(this.h = [
|
||||
1779033703, 4089235720, 3144134277, 2227873595, 1013904242, 4271175723, 2773480762, 1595750129, 1359893119,
|
||||
@@ -7812,7 +7812,7 @@ var require__5 = __commonJS({
|
||||
var utils = require_utils4(),
|
||||
SHA512 = require__4();
|
||||
function SHA384() {
|
||||
if (!(this instanceof SHA384)) return new SHA384();
|
||||
if (!new.target) return new SHA384();
|
||||
SHA512.$call(this),
|
||||
(this.h = [
|
||||
3418070365, 3238371032, 1654270250, 914150663, 2438529370, 812702999, 355462360, 4144912697, 1731405415,
|
||||
@@ -7855,7 +7855,7 @@ var require_ripemd = __commonJS({
|
||||
sum32_4 = utils.sum32_4,
|
||||
BlockHash = common.BlockHash;
|
||||
function RIPEMD160() {
|
||||
if (!(this instanceof RIPEMD160)) return new RIPEMD160();
|
||||
if (!new.target) return new RIPEMD160();
|
||||
BlockHash.$call(this),
|
||||
(this.h = [1732584193, 4023233417, 2562383102, 271733878, 3285377520]),
|
||||
(this.endian = "little");
|
||||
@@ -7954,7 +7954,7 @@ var require_hmac = __commonJS({
|
||||
function Hmac(hash, key, enc) {
|
||||
key = exportIfKeyObject(key);
|
||||
|
||||
if (!(this instanceof Hmac)) return new Hmac(hash, key, enc);
|
||||
if (!new.target) return new Hmac(hash, key, enc);
|
||||
(this.Hash = hash),
|
||||
(this.blockSize = hash.blockSize / 8),
|
||||
(this.outSize = hash.outSize / 8),
|
||||
@@ -8965,7 +8965,7 @@ var require_hmac_drbg = __commonJS({
|
||||
utils = require_utils2(),
|
||||
assert = require_minimalistic_assert();
|
||||
function HmacDRBG(options) {
|
||||
if (!(this instanceof HmacDRBG)) return new HmacDRBG(options);
|
||||
if (!new.target) return new HmacDRBG(options);
|
||||
(this.hash = options.hash),
|
||||
(this.predResist = !!options.predResist),
|
||||
(this.outLen = this.hash.outSize),
|
||||
@@ -9196,7 +9196,7 @@ var require_ec = __commonJS({
|
||||
KeyPair = require_key(),
|
||||
Signature = require_signature();
|
||||
function EC(options) {
|
||||
if (!(this instanceof EC)) return new EC(options);
|
||||
if (!new.target) return new EC(options);
|
||||
typeof options == "string" &&
|
||||
(assert(Object.prototype.hasOwnProperty.$call(curves, options), "Unknown curve " + options),
|
||||
(options = curves[options])),
|
||||
|
||||
@@ -75,6 +75,7 @@ function ERR_INVALID_ARG_VALUE(name, value, reason) {
|
||||
return new Error(`The value '${value}' is invalid for argument '${name}'. Reason: ${reason}`);
|
||||
}
|
||||
|
||||
var isCallingDuplexStreamConstructor = false;
|
||||
// node_modules/readable-stream/lib/ours/primordials.js
|
||||
var require_primordials = __commonJS({
|
||||
"node_modules/readable-stream/lib/ours/primordials.js"(exports, module) {
|
||||
@@ -2000,7 +2001,6 @@ var require_legacy = __commonJS({
|
||||
var { ArrayIsArray, ObjectSetPrototypeOf } = require_primordials();
|
||||
|
||||
function Stream(options) {
|
||||
if (!(this instanceof Stream)) return new Stream(options);
|
||||
EE.$call(this, options);
|
||||
}
|
||||
Stream.prototype = {};
|
||||
@@ -2265,22 +2265,42 @@ var require_readable = __commonJS({
|
||||
|
||||
function Readable(options) {
|
||||
if (!(this instanceof Readable)) return new Readable(options);
|
||||
const isDuplex = this instanceof require_duplex();
|
||||
this._readableState = new ReadableState(options, this, isDuplex);
|
||||
const wasCallingDuplexStreamConstructor = isCallingDuplexStreamConstructor;
|
||||
isCallingDuplexStreamConstructor = false;
|
||||
|
||||
this._events ??= {
|
||||
close: undefined,
|
||||
error: undefined,
|
||||
data: undefined,
|
||||
end: undefined,
|
||||
readable: undefined,
|
||||
// Skip uncommon events...
|
||||
// pause: undefined,
|
||||
// resume: undefined,
|
||||
// pipe: undefined,
|
||||
// unpipe: undefined,
|
||||
// [destroyImpl.kConstruct]: undefined,
|
||||
// [destroyImpl.kDestroy]: undefined,
|
||||
};
|
||||
|
||||
this._readableState = new ReadableState(options, this, wasCallingDuplexStreamConstructor);
|
||||
|
||||
if (options) {
|
||||
const { read, destroy, construct, signal } = options;
|
||||
if (typeof read === "function") this._read = read;
|
||||
if (typeof destroy === "function") this._destroy = destroy;
|
||||
if (typeof construct === "function") this._construct = construct;
|
||||
if (signal && !isDuplex) addAbortSignal(signal, this);
|
||||
if ($isCallable(read)) this._read = read;
|
||||
if ($isCallable(destroy)) this._destroy = destroy;
|
||||
if ($isCallable(construct)) this._construct = construct;
|
||||
if (signal) addAbortSignal(signal, this);
|
||||
}
|
||||
Stream.$call(this, options);
|
||||
|
||||
destroyImpl.construct(this, () => {
|
||||
if (this._readableState.needReadable) {
|
||||
maybeReadMore(this, this._readableState);
|
||||
}
|
||||
});
|
||||
if (!$isUndefinedOrNull(this._construct)) {
|
||||
destroyImpl.construct(this, () => {
|
||||
if (this._readableState.needReadable) {
|
||||
maybeReadMore(this, this._readableState);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Readable.prototype = {};
|
||||
ObjectSetPrototypeOf(Readable.prototype, Stream.prototype);
|
||||
@@ -4387,7 +4407,10 @@ var require_duplex = __commonJS({
|
||||
|
||||
function Duplex(options) {
|
||||
if (!(this instanceof Duplex)) return new Duplex(options);
|
||||
const prevIsCallingDuplexStreamConstructor = isCallingDuplexStreamConstructor;
|
||||
isCallingDuplexStreamConstructor = true;
|
||||
Readable.$call(this, options);
|
||||
isCallingDuplexStreamConstructor = prevIsCallingDuplexStreamConstructor;
|
||||
Writable.$call(this, options);
|
||||
|
||||
if (options) {
|
||||
|
||||
@@ -4123,31 +4123,31 @@ var require_lib = __commonJS({
|
||||
return engine._processChunk(buffer, flushFlag);
|
||||
}
|
||||
function Deflate(opts) {
|
||||
if (!(this instanceof Deflate)) return new Deflate(opts);
|
||||
if (!new.target) return new Deflate(opts);
|
||||
Zlib.$call(this, opts, binding.DEFLATE);
|
||||
}
|
||||
function Inflate(opts) {
|
||||
if (!(this instanceof Inflate)) return new Inflate(opts);
|
||||
if (!new.target) return new Inflate(opts);
|
||||
Zlib.$call(this, opts, binding.INFLATE);
|
||||
}
|
||||
function Gzip(opts) {
|
||||
if (!(this instanceof Gzip)) return new Gzip(opts);
|
||||
if (!new.target) return new Gzip(opts);
|
||||
Zlib.$call(this, opts, binding.GZIP);
|
||||
}
|
||||
function Gunzip(opts) {
|
||||
if (!(this instanceof Gunzip)) return new Gunzip(opts);
|
||||
if (!new.target) return new Gunzip(opts);
|
||||
Zlib.$call(this, opts, binding.GUNZIP);
|
||||
}
|
||||
function DeflateRaw(opts) {
|
||||
if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
|
||||
if (!new.target) return new DeflateRaw(opts);
|
||||
Zlib.$call(this, opts, binding.DEFLATERAW);
|
||||
}
|
||||
function InflateRaw(opts) {
|
||||
if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
|
||||
if (!new.target) return new InflateRaw(opts);
|
||||
Zlib.$call(this, opts, binding.INFLATERAW);
|
||||
}
|
||||
function Unzip(opts) {
|
||||
if (!(this instanceof Unzip)) return new Unzip(opts);
|
||||
if (!new.target) return new Unzip(opts);
|
||||
Zlib.$call(this, opts, binding.UNZIP);
|
||||
}
|
||||
function isValidFlushFlag(flag) {
|
||||
|
||||
@@ -620,7 +620,7 @@ pub export fn napi_new_instance(env: napi_env, constructor: napi_value, argc: us
|
||||
pub export fn napi_instanceof(env: napi_env, object: napi_value, constructor: napi_value, result: *bool) napi_status {
|
||||
log("napi_instanceof", .{});
|
||||
// TODO: does this throw object_expected in node?
|
||||
result.* = object.isCell() and object.isInstanceOf(env, constructor);
|
||||
result.* = object.isObject() and object.isInstanceOf(env, constructor);
|
||||
return .ok;
|
||||
}
|
||||
pub extern fn napi_get_cb_info(env: napi_env, cbinfo: napi_callback_info, argc: [*c]usize, argv: *napi_value, this_arg: *napi_value, data: [*]*anyopaque) napi_status;
|
||||
|
||||
@@ -16,6 +16,8 @@ pub const TaggedPointer = packed struct {
|
||||
_ptr: AddressableSize,
|
||||
data: TagSize,
|
||||
|
||||
pub const Tag = TagSize;
|
||||
|
||||
pub inline fn init(ptr: anytype, data: TagSize) TaggedPointer {
|
||||
const Ptr = @TypeOf(ptr);
|
||||
|
||||
|
||||
@@ -3,5 +3,7 @@ import { readFileSync, writeFileSync } from "fs";
|
||||
// @ts-expect-error
|
||||
const highlighter: (code: string) => string = globalThis[Symbol.for("Bun.lazy")]("unstable_syntaxHighlight");
|
||||
|
||||
// TODO: write tests for syntax highlighting
|
||||
test("highlighter", () => {});
|
||||
test("highlighter", () => {
|
||||
expect(highlighter("`can do ${123} ${'123'} ${`123`}`").length).toBeLessThan(150);
|
||||
expect(highlighter("`can do ${123} ${'123'} ${`123`}`123").length).toBeLessThan(150);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user