Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/jsc/any_task_job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub trait AnyTaskJobCtx: Sized {
#[repr(C)]
pub struct AnyTaskJob<C> {
run_from_js_erased: fn(*mut ()) -> JsResult<()>,
release_erased: fn(*mut ()),
vm: bun_ptr::BackRef<VirtualMachine>,
task: WorkPoolTask,
poll: KeepAlive,
Expand All @@ -56,7 +57,29 @@ pub unsafe fn dispatch_erased(ptr: *mut ()) -> JsResult<()> {
entry(ptr)
}

/// Free a queued job at VM shutdown without running its completion. The ctx
/// `Drop` releases what it owns (native resources, JSC handles) — it must run
/// while the VM is still live, which is why
/// `release_queued_tasks_for_shutdown` claims this tag instead of leaving the
/// job parked in the queue (where a C++-side ctx would strand its resources
/// past the leak check).
///
/// # Safety
/// `ptr` must be a live `*mut AnyTaskJob<C>` produced by [`AnyTaskJob::create`]
/// whose task was popped from the event-loop queue (the work-pool phase is
/// done, so the queue held exclusive ownership); the job is freed.
Comment thread
robobun marked this conversation as resolved.
Outdated
pub unsafe fn release_erased(ptr: *mut ()) {
// SAFETY: `AnyTaskJob<C>` is `#[repr(C)]` with `release_erased` second;
// caller contract that `ptr` is such an allocation.
let entry = unsafe { *ptr.cast::<fn(*mut ())>().add(1) };
entry(ptr)
}

const _: () = assert!(core::mem::offset_of!(AnyTaskJob<()>, run_from_js_erased) == 0);
const _: () = assert!(
core::mem::offset_of!(AnyTaskJob<()>, release_erased)
== core::mem::size_of::<fn(*mut ()) -> JsResult<()>>()
);

impl<C> bun_event_loop::Taskable for AnyTaskJob<C> {
const TAG: bun_event_loop::TaskTag = bun_event_loop::task_tag::AnyTaskJob;
Expand All @@ -82,6 +105,7 @@ impl<C: AnyTaskJobCtx> AnyTaskJob<C> {
let vm = bun_ptr::BackRef::new(global.bun_vm());
let job = bun_core::heap::into_raw(Box::new(Self {
run_from_js_erased: |p| Self::run_from_js(p.cast::<Self>()),
release_erased: |p| Self::release(p.cast::<Self>()),
vm,
task: WorkPoolTask {
node: Default::default(),
Expand Down Expand Up @@ -158,4 +182,12 @@ impl<C: AnyTaskJobCtx> AnyTaskJob<C> {
}
this.ctx.then(vm.global())
}

/// [`release_erased`]'s monomorphic body: free the job (running `Drop for
/// C`) without calling [`AnyTaskJobCtx::then`].
Comment thread
robobun marked this conversation as resolved.
Outdated
fn release(this: *mut Self) {
// SAFETY: `this` was produced by `heap::into_raw` in `create`; the
// caller (the popped queue entry) held exclusive ownership.
drop(unsafe { bun_core::heap::take(this) });
}
}
2 changes: 0 additions & 2 deletions src/jsc/bindings/headers-handwritten.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,7 @@ extern "C" size_t Bun__encoding__byteLengthUTF16AsUTF8(const char16_t* ptr, size
extern "C" JSC::EncodedJSValue Bun__encoding__constructFromLatin1(void*, const unsigned char* ptr, size_t len, Encoding encoding);
extern "C" JSC::EncodedJSValue Bun__encoding__constructFromUTF16(void*, const char16_t* ptr, size_t len, Encoding encoding);

extern "C" void Bun__EventLoop__runCallback1(JSC::JSGlobalObject* global, JSC::EncodedJSValue callback, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue arg1);
extern "C" void Bun__EventLoop__runCallback2(JSC::JSGlobalObject* global, JSC::EncodedJSValue callback, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2);
extern "C" void Bun__EventLoop__runCallback3(JSC::JSGlobalObject* global, JSC::EncodedJSValue callback, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3);

/// @note throws a JS exception and returns false if a stack overflow occurs
template<bool isStrict, bool enableAsymmetricMatchers, bool skipPrototype = false>
Expand Down
22 changes: 10 additions & 12 deletions src/jsc/bindings/node/crypto/CryptoDhJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,30 +40,28 @@ void DhJobCtx::runTask(JSGlobalObject* globalObject)
m_result = ByteSource::allocated(dp.release());
}

extern "C" void Bun__DhJobCtx__runFromJS(DhJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__DhJobCtx__takeCallbackArgs(DhJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue* args)
{
ctx->runFromJS(globalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(globalObject, args);
}
void DhJobCtx::runFromJS(JSGlobalObject* lexicalGlobalObject, JSValue callback)
uint32_t DhJobCtx::takeCallbackArgs(JSGlobalObject* lexicalGlobalObject, EncodedJSValue* args)
{
VM& vm = lexicalGlobalObject->vm();
ThrowScope scope = DECLARE_THROW_SCOPE(vm);

if (!m_result) {
// Same message as the synchronous path so callers observe identical errors either way.
JSObject* err = createError(lexicalGlobalObject, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "diffieHellman operation failed"_s);
Bun__EventLoop__runCallback1(lexicalGlobalObject, JSValue::encode(callback), JSValue::encode(jsUndefined()), JSValue::encode(err));
return;
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(err);
return 1;
}

JSValue result = WebCore::createBuffer(lexicalGlobalObject, m_result.span());

Bun__EventLoop__runCallback2(
lexicalGlobalObject,
JSValue::encode(callback),
JSValue::encode(jsUndefined()),
JSValue::encode(jsNull()),
JSValue::encode(result));
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(jsNull());
args[1] = JSValue::encode(result);
return 2;
}

extern "C" DhJob* Bun__DhJob__create(JSGlobalObject* globalObject, DhJobCtx* ctx, EncodedJSValue callback);
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/bindings/node/crypto/CryptoDhJob.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct DhJobCtx {
static std::optional<DhJobCtx> fromJS(JSC::JSGlobalObject*, JSC::ThrowScope&, JSC::JSObject* options);

void runTask(JSC::JSGlobalObject*);
void runFromJS(JSC::JSGlobalObject*, JSC::JSValue callback);
uint32_t takeCallbackArgs(JSC::JSGlobalObject*, JSC::EncodedJSValue* args);
void deinit();

RefPtr<KeyObjectData> m_privateKey;
Expand Down
4 changes: 2 additions & 2 deletions src/jsc/bindings/node/crypto/CryptoGenDhKeyPair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ extern "C" void Bun__DhKeyPairJobCtx__runTask(DhKeyPairJobCtx* ctx, JSGlobalObje
ctx->runTask(globalObject, keyCtx);
}

extern "C" void Bun__DhKeyPairJobCtx__runFromJS(DhKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__DhKeyPairJobCtx__takeCallbackArgs(DhKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue* args)
{
ctx->runFromJS(globalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(globalObject, args);
}

extern "C" DhKeyPairJob* Bun__DhKeyPairJob__create(JSGlobalObject* globalObject, DhKeyPairJobCtx* ctx, EncodedJSValue callback);
Expand Down
4 changes: 2 additions & 2 deletions src/jsc/bindings/node/crypto/CryptoGenDsaKeyPair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ extern "C" void Bun__DsaKeyPairJobCtx__runTask(DsaKeyPairJobCtx* ctx, JSGlobalOb
ctx->runTask(globalObject, keyCtx);
}

extern "C" void Bun__DsaKeyPairJobCtx__runFromJS(DsaKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__DsaKeyPairJobCtx__takeCallbackArgs(DsaKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue* args)
{
ctx->runFromJS(globalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(globalObject, args);
}

extern "C" DsaKeyPairJob* Bun__DsaKeyPairJob__create(JSGlobalObject* globalObject, DsaKeyPairJobCtx* ctx, EncodedJSValue callback);
Expand Down
4 changes: 2 additions & 2 deletions src/jsc/bindings/node/crypto/CryptoGenEcKeyPair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ extern "C" void Bun__EcKeyPairJobCtx__runTask(EcKeyPairJobCtx* ctx, JSGlobalObje
ctx->runTask(globalObject, keyCtx);
}

extern "C" void Bun__EcKeyPairJobCtx__runFromJS(EcKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__EcKeyPairJobCtx__takeCallbackArgs(EcKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue* args)
{
ctx->runFromJS(globalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(globalObject, args);
}

extern "C" EcKeyPairJob* Bun__EcKeyPairJob__create(JSGlobalObject* globalObject, EcKeyPairJobCtx* ctx, EncodedJSValue callback);
Expand Down
36 changes: 16 additions & 20 deletions src/jsc/bindings/node/crypto/CryptoGenKeyPair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,44 +29,40 @@ void KeyPairJobCtx::runTask(JSGlobalObject* globalObject, ncrypto::EVPKeyCtxPoin
m_keyObj = KeyObject::create(CryptoKeyType::Private, WTF::move(key));
}

void KeyPairJobCtx::runFromJS(JSGlobalObject* lexicalGlobalObject, JSValue callback)
uint32_t KeyPairJobCtx::takeCallbackArgs(JSGlobalObject* lexicalGlobalObject, EncodedJSValue* args)
{
VM& vm = lexicalGlobalObject->vm();
ThrowScope scope = DECLARE_THROW_SCOPE(vm);

auto exceptionCallback = [lexicalGlobalObject, callback](JSValue exceptionValue) {
Bun__EventLoop__runCallback1(lexicalGlobalObject, JSValue::encode(callback), JSValue::encode(jsUndefined()), JSValue::encode(exceptionValue));
};

if (!m_keyObj.data()) {
JSValue err = createCryptoError(lexicalGlobalObject, scope, m_opensslError, "key generation failed"_s);
Bun__EventLoop__runCallback1(lexicalGlobalObject, JSValue::encode(callback), JSValue::encode(jsUndefined()), JSValue::encode(err));
return;
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(err);
return 1;
}

JSValue publicKeyValue = m_keyObj.exportPublic(lexicalGlobalObject, scope, m_publicKeyEncoding);
if (scope.exception()) [[unlikely]] {
JSValue exceptionValue = scope.exception();
// The thrown value, not the Exception cell: the callback's err
// argument must be the Error object (node parity).
Comment thread
robobun marked this conversation as resolved.
Outdated
JSValue exceptionValue = scope.exception()->value();
(void)scope.tryClearException();
exceptionCallback(exceptionValue);
return;
args[0] = JSValue::encode(exceptionValue);
return 1;
}

JSValue privateKeyValue = m_keyObj.exportPrivate(lexicalGlobalObject, scope, m_privateKeyEncoding);
if (scope.exception()) [[unlikely]] {
JSValue exceptionValue = scope.exception();
JSValue exceptionValue = scope.exception()->value();
(void)scope.tryClearException();
exceptionCallback(exceptionValue);
return;
args[0] = JSValue::encode(exceptionValue);
return 1;
}

Bun__EventLoop__runCallback3(
lexicalGlobalObject,
JSValue::encode(callback),
JSValue::encode(jsUndefined()),
JSValue::encode(jsNull()),
JSValue::encode(publicKeyValue),
JSValue::encode(privateKeyValue));
args[0] = JSValue::encode(jsNull());
args[1] = JSValue::encode(publicKeyValue);
args[2] = JSValue::encode(privateKeyValue);
return 3;
}

KeyEncodingConfig parseKeyEncodingConfig(JSGlobalObject* globalObject, ThrowScope& scope, JSValue keyTypeValue, JSValue optionsValue)
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/bindings/node/crypto/CryptoGenKeyPair.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct KeyPairJobCtx {
}

void runTask(JSC::JSGlobalObject* globalObject, ncrypto::EVPKeyCtxPointer& ctx);
void runFromJS(JSC::JSGlobalObject* globalObject, JSC::JSValue callback);
uint32_t takeCallbackArgs(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue* args);
void deinit();

int err() const { return m_opensslError; };
Expand Down
4 changes: 2 additions & 2 deletions src/jsc/bindings/node/crypto/CryptoGenNidKeyPair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ extern "C" void Bun__NidKeyPairJobCtx__runTask(NidKeyPairJobCtx* ctx, JSGlobalOb
ctx->runTask(globalObject, keyCtx);
}

extern "C" void Bun__NidKeyPairJobCtx__runFromJS(NidKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__NidKeyPairJobCtx__takeCallbackArgs(NidKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue* args)
{
ctx->runFromJS(globalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(globalObject, args);
}

extern "C" NidKeyPairJob* Bun__NidKeyPairJob__create(JSGlobalObject* globalObject, NidKeyPairJobCtx* ctx, EncodedJSValue callback);
Expand Down
4 changes: 2 additions & 2 deletions src/jsc/bindings/node/crypto/CryptoGenRsaKeyPair.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ extern "C" void Bun__RsaKeyPairJobCtx__runTask(RsaKeyPairJobCtx* ctx, JSGlobalOb
ctx->runTask(globalObject, keyCtx);
}

extern "C" void Bun__RsaKeyPairJobCtx__runFromJS(RsaKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__RsaKeyPairJobCtx__takeCallbackArgs(RsaKeyPairJobCtx* ctx, JSGlobalObject* globalObject, EncodedJSValue* args)
{
ctx->runFromJS(globalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(globalObject, args);
}

extern "C" RsaKeyPairJob* Bun__RsaKeyPairJob__create(JSGlobalObject* globalObject, RsaKeyPairJobCtx* ctx, EncodedJSValue callback);
Expand Down
26 changes: 14 additions & 12 deletions src/jsc/bindings/node/crypto/CryptoHkdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,20 @@ void HkdfJobCtx::runTask(JSGlobalObject* lexicalGlobalObject)
m_result = ByteSource::allocated(dp.release());
}

extern "C" void Bun__HkdfJobCtx__runFromJS(HkdfJobCtx* ctx, JSGlobalObject* lexicalGlobalObject, EncodedJSValue callback)
extern "C" uint32_t Bun__HkdfJobCtx__takeCallbackArgs(HkdfJobCtx* ctx, JSGlobalObject* lexicalGlobalObject, EncodedJSValue* args)
{
ctx->runFromJS(lexicalGlobalObject, JSValue::decode(callback));
return ctx->takeCallbackArgs(lexicalGlobalObject, args);
}
void HkdfJobCtx::runFromJS(JSGlobalObject* lexicalGlobalObject, JSValue callback)
uint32_t HkdfJobCtx::takeCallbackArgs(JSGlobalObject* lexicalGlobalObject, EncodedJSValue* args)
{
auto& vm = lexicalGlobalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);

if (!m_result) {
JSObject* err = createError(lexicalGlobalObject, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "hkdf operation failed"_s);
Bun__EventLoop__runCallback1(lexicalGlobalObject, JSValue::encode(callback), JSValue::encode(jsUndefined()), JSValue::encode(err));
return;
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(err);
return 1;
}

auto& result = m_result.value();
Expand All @@ -88,17 +89,18 @@ void HkdfJobCtx::runFromJS(JSGlobalObject* lexicalGlobalObject, JSValue callback
RefPtr<ArrayBuffer> buf = ArrayBuffer::tryCreateUninitialized(result.size(), 1);
if (!buf) {
JSObject* err = createOutOfMemoryError(lexicalGlobalObject);
Bun__EventLoop__runCallback1(lexicalGlobalObject, JSValue::encode(callback), JSValue::encode(jsUndefined()), JSValue::encode(err));
return;
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(err);
return 1;
}

memcpy(buf->data(), result.data(), result.size());

Bun__EventLoop__runCallback2(lexicalGlobalObject,
JSValue::encode(callback),
JSValue::encode(jsUndefined()),
JSValue::encode(jsNull()),
JSValue::encode(JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(), buf.releaseNonNull())));
JSValue resultBuffer = JSArrayBuffer::create(vm, globalObject->arrayBufferStructure(), buf.releaseNonNull());
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(jsNull());
args[1] = JSValue::encode(resultBuffer);
return 2;
}

extern "C" void Bun__HkdfJobCtx__deinit(HkdfJobCtx* ctx)
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/bindings/node/crypto/CryptoHkdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct HkdfJobCtx {
static std::optional<HkdfJobCtx> fromJS(JSC::JSGlobalObject*, JSC::CallFrame*, JSC::ThrowScope&, Mode);

void runTask(JSC::JSGlobalObject*);
void runFromJS(JSC::JSGlobalObject*, JSC::JSValue callback);
uint32_t takeCallbackArgs(JSC::JSGlobalObject*, JSC::EncodedJSValue* args);
void deinit();

ncrypto::Digest m_digest;
Expand Down
21 changes: 11 additions & 10 deletions src/jsc/bindings/node/crypto/CryptoKeygen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,33 @@ void SecretKeyJobCtx::runTask(JSGlobalObject* lexicalGlobalObject)
m_result = WTF::move(key);
}

extern "C" void Bun__SecretKeyJobCtx__runFromJS(SecretKeyJobCtx* ctx, JSGlobalObject* lexicalGlobalObject, JSC::JSValue callback)
extern "C" uint32_t Bun__SecretKeyJobCtx__takeCallbackArgs(SecretKeyJobCtx* ctx, JSGlobalObject* lexicalGlobalObject, EncodedJSValue* args)
{
ctx->runFromJS(lexicalGlobalObject, callback);
return ctx->takeCallbackArgs(lexicalGlobalObject, args);
}
void SecretKeyJobCtx::runFromJS(JSGlobalObject* lexicalGlobalObject, JSC::JSValue callback)
uint32_t SecretKeyJobCtx::takeCallbackArgs(JSGlobalObject* lexicalGlobalObject, EncodedJSValue* args)
{
VM& vm = lexicalGlobalObject->vm();
ThrowScope scope = DECLARE_THROW_SCOPE(vm);
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);

if (!m_result) {
JSObject* err = createError(lexicalGlobalObject, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "key generation failed"_s);
Bun__EventLoop__runCallback1(lexicalGlobalObject, JSValue::encode(callback), JSValue::encode(jsUndefined()), JSValue::encode(err));
return;
RETURN_IF_EXCEPTION(scope, 0);
args[0] = JSValue::encode(err);
return 1;
}

KeyObject keyObject = KeyObject::create(WTF::move(*m_result));

Structure* structure = globalObject->m_JSSecretKeyObjectClassStructure.get(lexicalGlobalObject);
RETURN_IF_EXCEPTION(scope, 0);
JSSecretKeyObject* secretKey = JSSecretKeyObject::create(vm, structure, lexicalGlobalObject, WTF::move(keyObject));
RETURN_IF_EXCEPTION(scope, 0);

Bun__EventLoop__runCallback2(lexicalGlobalObject,
JSValue::encode(callback),
JSValue::encode(jsUndefined()),
JSValue::encode(jsNull()),
JSValue::encode(secretKey));
args[0] = JSValue::encode(jsNull());
args[1] = JSValue::encode(secretKey);
return 2;
}

extern "C" void Bun__SecretKeyJobCtx__deinit(SecretKeyJobCtx* ctx)
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/bindings/node/crypto/CryptoKeygen.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct SecretKeyJobCtx {
~SecretKeyJobCtx() = default;

void runTask(JSC::JSGlobalObject* lexicalGlobalObject);
void runFromJS(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue callback);
uint32_t takeCallbackArgs(JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue* args);
void deinit();

static std::optional<SecretKeyJobCtx> fromJS(JSC::JSGlobalObject*, JSC::ThrowScope&, JSC::JSValue typeValue, JSC::JSValue optionsValue);
Expand Down
Loading