mirror of
https://github.com/denoland/deno.git
synced 2025-09-23 02:42:34 +00:00

This commit updates V8 to version 14.0.0. This update brings changes to Temporal support too, which is close to being stabilized. --------- Co-authored-by: snek <snek@deno.com>
298 lines
6.7 KiB
Rust
298 lines
6.7 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
use std::cell::Cell;
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
use deno_core::GarbageCollected;
|
|
use deno_core::OpState;
|
|
use deno_core::ResourceId;
|
|
use deno_core::error::ResourceError;
|
|
use deno_core::op2;
|
|
use deno_core::v8;
|
|
|
|
pub struct AsyncId(i64);
|
|
|
|
impl Default for AsyncId {
|
|
// `kAsyncIdCounter` should start at `1` because that'll be the id the execution
|
|
// context during bootstrap.
|
|
fn default() -> Self {
|
|
Self(1)
|
|
}
|
|
}
|
|
|
|
impl AsyncId {
|
|
// Increment the internal id counter and return the value.
|
|
fn next(&mut self) -> i64 {
|
|
self.0 += 1;
|
|
self.0
|
|
}
|
|
}
|
|
|
|
fn next_async_id(state: &mut OpState) -> i64 {
|
|
state.borrow_mut::<AsyncId>().next()
|
|
}
|
|
|
|
#[op2(fast)]
|
|
pub fn op_node_new_async_id(state: &mut OpState) -> f64 {
|
|
next_async_id(state) as f64
|
|
}
|
|
|
|
pub struct AsyncWrap {
|
|
provider: i32,
|
|
async_id: i64,
|
|
}
|
|
|
|
// SAFETY: we're sure this can be GCed
|
|
unsafe impl GarbageCollected for AsyncWrap {
|
|
fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
|
|
|
|
fn get_name(&self) -> &'static std::ffi::CStr {
|
|
c"AsyncWrap"
|
|
}
|
|
}
|
|
|
|
impl AsyncWrap {
|
|
pub(crate) fn create(state: &mut OpState, provider: i32) -> Self {
|
|
let async_id = next_async_id(state);
|
|
|
|
Self { provider, async_id }
|
|
}
|
|
}
|
|
|
|
#[op2(base)]
|
|
impl AsyncWrap {
|
|
#[getter]
|
|
fn provider(&self) -> i32 {
|
|
self.provider
|
|
}
|
|
|
|
#[fast]
|
|
fn get_async_id(&self) -> f64 {
|
|
self.async_id as f64
|
|
}
|
|
|
|
#[fast]
|
|
fn get_provider_type(&self) -> i32 {
|
|
self.provider
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Default)]
|
|
enum State {
|
|
#[default]
|
|
Initialized,
|
|
Closing,
|
|
Closed,
|
|
}
|
|
|
|
pub struct HandleWrap {
|
|
handle: Option<ResourceId>,
|
|
state: Rc<Cell<State>>,
|
|
}
|
|
|
|
// SAFETY: we're sure this can be GCed
|
|
unsafe impl GarbageCollected for HandleWrap {
|
|
fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
|
|
|
|
fn get_name(&self) -> &'static std::ffi::CStr {
|
|
c"HandleWrap"
|
|
}
|
|
}
|
|
|
|
impl HandleWrap {
|
|
pub(crate) fn create(handle: Option<ResourceId>) -> Self {
|
|
Self {
|
|
handle,
|
|
state: Rc::new(Cell::new(State::Initialized)),
|
|
}
|
|
}
|
|
|
|
fn is_alive(&self) -> bool {
|
|
self.state.get() != State::Closed
|
|
}
|
|
}
|
|
|
|
static ON_CLOSE_STR: deno_core::FastStaticString =
|
|
deno_core::ascii_str!("_onClose");
|
|
|
|
#[op2(inherit = AsyncWrap)]
|
|
impl HandleWrap {
|
|
#[constructor]
|
|
#[cppgc]
|
|
fn new(
|
|
state: &mut OpState,
|
|
#[smi] provider: i32,
|
|
#[smi] handle: Option<ResourceId>,
|
|
) -> (AsyncWrap, HandleWrap) {
|
|
(
|
|
AsyncWrap::create(state, provider),
|
|
HandleWrap::create(handle),
|
|
)
|
|
}
|
|
|
|
// Ported from Node.js
|
|
//
|
|
// https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L65-L85
|
|
#[reentrant]
|
|
fn close(
|
|
&self,
|
|
op_state: Rc<RefCell<OpState>>,
|
|
#[this] this: v8::Global<v8::Object>,
|
|
scope: &mut v8::HandleScope,
|
|
#[global] cb: Option<v8::Global<v8::Function>>,
|
|
) -> Result<(), ResourceError> {
|
|
if self.state.get() != State::Initialized {
|
|
return Ok(());
|
|
}
|
|
|
|
let state = self.state.clone();
|
|
// This effectively mimicks Node's OnClose callback.
|
|
//
|
|
// https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L135-L157
|
|
let on_close = move |scope: &mut v8::HandleScope| {
|
|
assert!(state.get() == State::Closing);
|
|
state.set(State::Closed);
|
|
|
|
// Workaround for https://github.com/denoland/deno/pull/24656
|
|
//
|
|
// We need to delay 'cb' at least 2 ticks to avoid "close" event happening before "error"
|
|
// event in net.Socket.
|
|
//
|
|
// This is a temporary solution. We should support async close like `uv_close`.
|
|
if let Some(cb) = cb {
|
|
let recv = v8::undefined(scope);
|
|
cb.open(scope).call(scope, recv.into(), &[]);
|
|
}
|
|
};
|
|
|
|
uv_close(scope, op_state, this, on_close);
|
|
self.state.set(State::Closing);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// Ported from Node.js
|
|
//
|
|
// https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L58-L62
|
|
#[fast]
|
|
fn has_ref(&self, state: &mut OpState) -> bool {
|
|
if let Some(handle) = self.handle {
|
|
return state.has_ref(handle);
|
|
}
|
|
|
|
true
|
|
}
|
|
|
|
// Ported from Node.js
|
|
//
|
|
// https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L40-L46
|
|
#[fast]
|
|
#[rename("ref")]
|
|
fn ref_method(&self, state: &mut OpState) {
|
|
if self.is_alive()
|
|
&& let Some(handle) = self.handle
|
|
{
|
|
state.uv_ref(handle);
|
|
}
|
|
}
|
|
|
|
// Ported from Node.js
|
|
//
|
|
// https://github.com/nodejs/node/blob/038d82980ab26cd79abe4409adc2fecad94d7c93/src/handle_wrap.cc#L49-L55
|
|
#[fast]
|
|
fn unref(&self, state: &mut OpState) {
|
|
if self.is_alive()
|
|
&& let Some(handle) = self.handle
|
|
{
|
|
state.uv_unref(handle);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn uv_close<F>(
|
|
scope: &mut v8::HandleScope,
|
|
op_state: Rc<RefCell<OpState>>,
|
|
this: v8::Global<v8::Object>,
|
|
on_close: F,
|
|
) where
|
|
F: FnOnce(&mut v8::HandleScope) + 'static,
|
|
{
|
|
// Call _onClose() on the JS handles. Not needed for Rust handles.
|
|
let this = v8::Local::new(scope, this);
|
|
let on_close_str = ON_CLOSE_STR.v8_string(scope).unwrap();
|
|
let onclose = this.get(scope, on_close_str.into());
|
|
|
|
if let Some(onclose) = onclose {
|
|
let fn_: v8::Local<v8::Function> = onclose.try_into().unwrap();
|
|
fn_.call(scope, this.into(), &[]);
|
|
}
|
|
|
|
op_state
|
|
.borrow()
|
|
.borrow::<deno_core::V8TaskSpawner>()
|
|
.spawn(on_close);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::future::poll_fn;
|
|
use std::task::Poll;
|
|
|
|
use deno_core::JsRuntime;
|
|
use deno_core::RuntimeOptions;
|
|
|
|
async fn js_test(source_code: &'static str) {
|
|
deno_core::extension!(
|
|
test_ext,
|
|
objects = [super::AsyncWrap, super::HandleWrap,],
|
|
state = |state| {
|
|
state.put::<super::AsyncId>(super::AsyncId::default());
|
|
}
|
|
);
|
|
|
|
let mut runtime = JsRuntime::new(RuntimeOptions {
|
|
extensions: vec![test_ext::init()],
|
|
..Default::default()
|
|
});
|
|
|
|
poll_fn(move |cx| {
|
|
runtime
|
|
.execute_script("file://handle_wrap_test.js", source_code)
|
|
.unwrap();
|
|
|
|
let result = runtime.poll_event_loop(cx, Default::default());
|
|
assert!(matches!(result, Poll::Ready(Ok(()))));
|
|
Poll::Ready(())
|
|
})
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_handle_wrap() {
|
|
js_test(
|
|
r#"
|
|
const { HandleWrap } = Deno.core.ops;
|
|
|
|
let called = false;
|
|
class MyHandleWrap extends HandleWrap {
|
|
constructor() {
|
|
super(0, null);
|
|
}
|
|
|
|
_onClose() {
|
|
called = true;
|
|
}
|
|
}
|
|
|
|
const handleWrap = new MyHandleWrap();
|
|
handleWrap.close();
|
|
|
|
if (!called) {
|
|
throw new Error("HandleWrap._onClose was not called");
|
|
}
|
|
"#,
|
|
)
|
|
.await;
|
|
}
|
|
}
|