2022-01-07 22:09:52 -05:00
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
2019-03-26 11:56:34 -04:00
2020-01-06 14:07:35 -05:00
use crate ::bindings ;
2020-09-14 12:48:57 -04:00
use crate ::error ::attach_handle_to_error ;
2020-10-14 08:04:09 -04:00
use crate ::error ::generic_error ;
2020-09-14 12:48:57 -04:00
use crate ::error ::ErrWithV8Handle ;
use crate ::error ::JsError ;
2022-03-15 18:43:17 -04:00
use crate ::extensions ::OpDecl ;
2022-03-08 09:40:34 -05:00
use crate ::extensions ::OpEventLoopFn ;
2021-05-26 15:07:12 -04:00
use crate ::inspector ::JsRuntimeInspector ;
2020-09-06 10:50:49 -04:00
use crate ::module_specifier ::ModuleSpecifier ;
use crate ::modules ::ModuleId ;
use crate ::modules ::ModuleLoadId ;
use crate ::modules ::ModuleLoader ;
2021-02-23 09:22:55 -05:00
use crate ::modules ::ModuleMap ;
2020-09-06 10:50:49 -04:00
use crate ::modules ::NoopModuleLoader ;
2022-03-22 11:39:58 -04:00
use crate ::op_void_async ;
use crate ::op_void_sync ;
2019-09-30 14:59:44 -04:00
use crate ::ops ::* ;
2022-04-15 10:08:09 -04:00
use crate ::source_map ::SourceMapGetter ;
2021-04-28 12:41:50 -04:00
use crate ::Extension ;
use crate ::OpMiddlewareFn ;
2021-04-30 10:51:54 -04:00
use crate ::OpResult ;
2020-09-10 09:57:45 -04:00
use crate ::OpState ;
2021-03-31 10:37:38 -04:00
use crate ::PromiseId ;
2021-11-16 09:02:28 -05:00
use anyhow ::Error ;
2021-07-30 07:36:43 -04:00
use futures ::channel ::oneshot ;
2020-10-11 07:20:40 -04:00
use futures ::future ::poll_fn ;
2022-03-14 13:44:15 -04:00
use futures ::future ::Future ;
2021-05-26 15:07:12 -04:00
use futures ::future ::FutureExt ;
2019-08-07 12:55:39 -04:00
use futures ::stream ::FuturesUnordered ;
2019-11-16 19:17:47 -05:00
use futures ::stream ::StreamExt ;
use futures ::task ::AtomicWaker ;
2020-08-11 21:07:14 -04:00
use std ::any ::Any ;
2020-04-21 09:48:44 -04:00
use std ::cell ::RefCell ;
2020-01-06 14:07:35 -05:00
use std ::collections ::HashMap ;
2021-11-25 13:49:09 -05:00
use std ::collections ::HashSet ;
2020-08-11 21:07:14 -04:00
use std ::ffi ::c_void ;
2020-02-24 18:53:29 -05:00
use std ::mem ::forget ;
2020-01-06 14:07:35 -05:00
use std ::option ::Option ;
2020-04-21 09:48:44 -04:00
use std ::rc ::Rc ;
2021-07-06 13:42:52 -04:00
use std ::sync ::Arc ;
use std ::sync ::Mutex ;
2020-05-29 17:41:39 -04:00
use std ::sync ::Once ;
2019-11-16 19:17:47 -05:00
use std ::task ::Context ;
use std ::task ::Poll ;
2020-01-24 15:10:49 -05:00
2021-10-09 16:37:19 -04:00
type PendingOpFuture = OpCall < ( PromiseId , OpId , OpResult ) > ;
2020-04-18 20:05:13 -04:00
2020-04-22 14:24:49 -04:00
pub enum Snapshot {
Static ( & 'static [ u8 ] ) ,
JustCreated ( v8 ::StartupData ) ,
2020-05-09 21:00:40 -04:00
Boxed ( Box < [ u8 ] > ) ,
2020-04-22 14:24:49 -04:00
}
2021-11-16 09:02:28 -05:00
pub type JsErrorCreateFn = dyn Fn ( JsError ) -> Error ;
2019-07-10 18:53:48 -04:00
2021-11-16 09:02:28 -05:00
pub type GetErrorClassFn = & 'static dyn for < ' e > Fn ( & ' e Error ) -> & 'static str ;
2020-08-25 18:22:15 -04:00
2020-08-11 21:07:14 -04:00
/// Objects that need to live as long as the isolate
#[ derive(Default) ]
struct IsolateAllocations {
near_heap_limit_callback_data :
Option < ( Box < RefCell < dyn Any > > , v8 ::NearHeapLimitCallback ) > ,
}
2019-03-12 18:47:54 -04:00
/// A single execution context of JavaScript. Corresponds roughly to the "Web
2020-09-06 15:44:29 -04:00
/// Worker" concept in the DOM. A JsRuntime is a Future that can be used with
/// an event loop (Tokio, async_std).
////
/// The JsRuntime future completes when there is an error or when all
2019-03-12 18:47:54 -04:00
/// pending ops have completed.
///
2021-04-23 11:50:45 -04:00
/// Pending ops are created in JavaScript by calling Deno.core.opAsync(), and in Rust
/// by implementing an async function that takes a serde::Deserialize "control argument"
/// and an optional zero copy buffer, each async Op is tied to a Promise in JavaScript.
2020-09-06 15:44:29 -04:00
pub struct JsRuntime {
2020-05-29 17:41:39 -04:00
// This is an Option<OwnedIsolate> instead of just OwnedIsolate to workaround
2021-06-21 13:37:51 -04:00
// a safety issue with SnapshotCreator. See JsRuntime::drop.
2020-05-29 17:41:39 -04:00
v8_isolate : Option < v8 ::OwnedIsolate > ,
2021-06-21 13:37:51 -04:00
// This is an Option<Box<JsRuntimeInspector> instead of just Box<JsRuntimeInspector>
// to workaround a safety issue. See JsRuntime::drop.
2021-05-26 15:07:12 -04:00
inspector : Option < Box < JsRuntimeInspector > > ,
2020-01-06 14:07:35 -05:00
snapshot_creator : Option < v8 ::SnapshotCreator > ,
has_snapshotted : bool ,
2022-04-17 07:53:08 -04:00
built_from_snapshot : bool ,
2020-08-11 21:07:14 -04:00
allocations : IsolateAllocations ,
2021-04-28 12:41:50 -04:00
extensions : Vec < Extension > ,
2022-03-08 09:40:34 -05:00
event_loop_middlewares : Vec < Box < OpEventLoopFn > > ,
2020-05-29 17:41:39 -04:00
}
2020-10-14 08:04:09 -04:00
struct DynImportModEvaluate {
2021-04-28 12:28:46 -04:00
load_id : ModuleLoadId ,
2020-10-14 08:04:09 -04:00
module_id : ModuleId ,
promise : v8 ::Global < v8 ::Promise > ,
module : v8 ::Global < v8 ::Module > ,
}
struct ModEvaluate {
promise : v8 ::Global < v8 ::Promise > ,
2021-11-16 09:02:28 -05:00
sender : oneshot ::Sender < Result < ( ) , Error > > ,
2020-10-14 08:04:09 -04:00
}
2021-09-29 04:47:24 -04:00
pub struct CrossIsolateStore < T > ( Arc < Mutex < CrossIsolateStoreInner < T > > > ) ;
2021-07-06 13:42:52 -04:00
2021-09-29 04:47:24 -04:00
struct CrossIsolateStoreInner < T > {
map : HashMap < u32 , T > ,
2021-07-06 13:42:52 -04:00
last_id : u32 ,
}
2021-09-29 04:47:24 -04:00
impl < T > CrossIsolateStore < T > {
pub ( crate ) fn insert ( & self , value : T ) -> u32 {
let mut store = self . 0. lock ( ) . unwrap ( ) ;
let last_id = store . last_id ;
store . map . insert ( last_id , value ) ;
store . last_id + = 1 ;
2021-07-06 13:42:52 -04:00
last_id
}
2021-09-29 04:47:24 -04:00
pub ( crate ) fn take ( & self , id : u32 ) -> Option < T > {
let mut store = self . 0. lock ( ) . unwrap ( ) ;
store . map . remove ( & id )
}
}
impl < T > Default for CrossIsolateStore < T > {
fn default ( ) -> Self {
CrossIsolateStore ( Arc ::new ( Mutex ::new ( CrossIsolateStoreInner {
map : Default ::default ( ) ,
last_id : 0 ,
} ) ) )
2021-07-06 13:42:52 -04:00
}
}
2021-09-29 04:47:24 -04:00
impl < T > Clone for CrossIsolateStore < T > {
fn clone ( & self ) -> Self {
Self ( self . 0. clone ( ) )
}
}
pub type SharedArrayBufferStore =
CrossIsolateStore < v8 ::SharedRef < v8 ::BackingStore > > ;
pub type CompiledWasmModuleStore = CrossIsolateStore < v8 ::CompiledWasmModule > ;
2020-09-06 15:44:29 -04:00
/// Internal state for JsRuntime which is stored in one of v8::Isolate's
2020-05-29 17:41:39 -04:00
/// embedder slots.
2020-09-14 23:49:12 -04:00
pub ( crate ) struct JsRuntimeState {
2022-04-17 07:53:08 -04:00
global_realm : Option < JsRealm > ,
2020-07-18 16:32:11 -04:00
pub ( crate ) js_recv_cb : Option < v8 ::Global < v8 ::Function > > ,
2021-11-16 14:23:12 -05:00
pub ( crate ) js_macrotask_cbs : Vec < v8 ::Global < v8 ::Function > > ,
pub ( crate ) js_nexttick_cbs : Vec < v8 ::Global < v8 ::Function > > ,
2021-11-27 18:46:12 -05:00
pub ( crate ) js_promise_reject_cb : Option < v8 ::Global < v8 ::Function > > ,
pub ( crate ) js_uncaught_exception_cb : Option < v8 ::Global < v8 ::Function > > ,
2021-11-16 14:23:12 -05:00
pub ( crate ) has_tick_scheduled : bool ,
2021-07-03 17:33:36 -04:00
pub ( crate ) js_wasm_streaming_cb : Option < v8 ::Global < v8 ::Function > > ,
2020-11-11 17:11:40 -05:00
pub ( crate ) pending_promise_exceptions :
HashMap < v8 ::Global < v8 ::Promise > , v8 ::Global < v8 ::Value > > ,
2021-07-05 12:59:49 -04:00
pending_dyn_mod_evaluate : Vec < DynImportModEvaluate > ,
2020-10-14 08:04:09 -04:00
pending_mod_evaluate : Option < ModEvaluate > ,
2021-07-05 12:59:49 -04:00
/// A counter used to delay our dynamic import deadlock detection by one spin
/// of the event loop.
dyn_module_evaluate_idle_counter : u32 ,
2022-04-15 10:08:09 -04:00
pub ( crate ) source_map_getter : Option < Box < dyn SourceMapGetter > > ,
2020-12-11 12:49:26 -05:00
pub ( crate ) js_error_create_fn : Rc < JsErrorCreateFn > ,
2020-09-05 20:34:02 -04:00
pub ( crate ) pending_ops : FuturesUnordered < PendingOpFuture > ,
2021-11-25 13:49:09 -05:00
pub ( crate ) unrefed_ops : HashSet < i32 > ,
2021-02-23 07:08:50 -05:00
pub ( crate ) have_unpolled_ops : bool ,
2020-09-10 09:57:45 -04:00
pub ( crate ) op_state : Rc < RefCell < OpState > > ,
2022-04-08 04:32:48 -04:00
#[ allow(dead_code) ]
// We don't explicitly re-read this prop but need the slice to live alongside the isolate
pub ( crate ) op_ctxs : Box < [ OpCtx ] > ,
2021-07-06 13:42:52 -04:00
pub ( crate ) shared_array_buffer_store : Option < SharedArrayBufferStore > ,
2021-09-29 04:47:24 -04:00
pub ( crate ) compiled_wasm_module_store : Option < CompiledWasmModuleStore > ,
2022-04-13 05:50:57 -04:00
/// The error that was passed to an explicit `Deno.core.terminate` call.
/// It will be retrieved by `exception_to_err_result` and used as an error
/// instead of any other exceptions.
pub ( crate ) explicit_terminate_exception : Option < v8 ::Global < v8 ::Value > > ,
2019-11-16 19:17:47 -05:00
waker : AtomicWaker ,
2020-05-29 17:41:39 -04:00
}
2020-09-06 15:44:29 -04:00
impl Drop for JsRuntime {
2019-03-11 17:57:36 -04:00
fn drop ( & mut self ) {
2021-05-26 15:07:12 -04:00
// The Isolate object must outlive the Inspector object, but this is
// currently not enforced by the type system.
self . inspector . take ( ) ;
2020-01-06 14:07:35 -05:00
if let Some ( creator ) = self . snapshot_creator . take ( ) {
2020-02-24 18:53:29 -05:00
// TODO(ry): in rusty_v8, `SnapShotCreator::get_owned_isolate()` returns
// a `struct OwnedIsolate` which is not actually owned, hence the need
// here to leak the `OwnedIsolate` in order to avoid a double free and
// the segfault that it causes.
let v8_isolate = self . v8_isolate . take ( ) . unwrap ( ) ;
forget ( v8_isolate ) ;
2020-01-06 10:24:44 -05:00
// TODO(ry) V8 has a strange assert which prevents a SnapshotCreator from
// being deallocated if it hasn't created a snapshot yet.
// https://github.com/v8/v8/blob/73212783fbd534fac76cc4b66aac899c13f71fc8/src/api.cc#L603
// If that assert is removed, this if guard could be removed.
// WARNING: There may be false positive LSAN errors here.
2020-01-06 14:07:35 -05:00
if self . has_snapshotted {
2020-01-06 10:24:44 -05:00
drop ( creator ) ;
}
}
2019-03-11 17:57:36 -04:00
}
}
2021-07-02 03:32:48 -04:00
fn v8_init ( v8_platform : Option < v8 ::SharedRef < v8 ::Platform > > ) {
2021-04-12 06:15:04 -04:00
// Include 10MB ICU data file.
#[ repr(C, align(16)) ]
2022-03-11 09:29:01 -05:00
struct IcuData ( [ u8 ; 10284336 ] ) ;
2021-04-12 06:15:04 -04:00
static ICU_DATA : IcuData = IcuData ( * include_bytes! ( " icudtl.dat " ) ) ;
2022-03-11 09:29:01 -05:00
v8 ::icu ::set_common_data_70 ( & ICU_DATA . 0 ) . unwrap ( ) ;
2021-04-12 06:15:04 -04:00
let v8_platform = v8_platform
2021-07-02 03:32:48 -04:00
. unwrap_or_else ( | | v8 ::new_default_platform ( 0 , false ) . make_shared ( ) ) ;
2021-04-12 06:15:04 -04:00
v8 ::V8 ::initialize_platform ( v8_platform ) ;
2020-01-06 14:07:35 -05:00
v8 ::V8 ::initialize ( ) ;
2021-04-12 06:15:04 -04:00
2021-04-12 06:31:20 -04:00
let flags = concat! (
2021-04-26 11:54:07 -04:00
" --experimental-wasm-threads " ,
2021-07-03 17:33:36 -04:00
" --wasm-test-streaming " ,
2021-04-12 06:31:20 -04:00
" --harmony-import-assertions " ,
" --no-validate-asm " ,
) ;
v8 ::V8 ::set_flags_from_string ( flags ) ;
2020-01-06 14:07:35 -05:00
}
2020-09-11 09:18:49 -04:00
#[ derive(Default) ]
pub struct RuntimeOptions {
2022-04-15 10:08:09 -04:00
/// Source map reference for errors.
pub source_map_getter : Option < Box < dyn SourceMapGetter > > ,
2020-09-14 21:23:48 -04:00
/// Allows a callback to be set whenever a V8 exception is made. This allows
/// the caller to wrap the JsError into an error. By default this callback
/// is set to `JsError::create()`.
2020-12-11 12:49:26 -05:00
pub js_error_create_fn : Option < Rc < JsErrorCreateFn > > ,
2020-09-14 21:23:48 -04:00
2020-11-21 09:56:14 -05:00
/// Allows to map error type to a string "class" used to represent
/// error in JavaScript.
pub get_error_class_fn : Option < GetErrorClassFn > ,
2020-09-11 09:18:49 -04:00
/// Implementation of `ModuleLoader` which will be
/// called when V8 requests to load ES modules.
///
/// If not provided runtime will error if code being
/// executed tries to load modules.
pub module_loader : Option < Rc < dyn ModuleLoader > > ,
2020-09-06 10:50:49 -04:00
2021-04-28 12:41:50 -04:00
/// JsRuntime extensions, not to be confused with ES modules
/// these are sets of ops and other JS code to be initialized.
pub extensions : Vec < Extension > ,
2020-09-11 09:18:49 -04:00
/// V8 snapshot that should be loaded on startup.
///
/// Currently can't be used with `will_snapshot`.
pub startup_snapshot : Option < Snapshot > ,
2020-08-11 21:07:14 -04:00
2020-09-11 09:18:49 -04:00
/// Prepare runtime to take snapshot of loaded code.
///
/// Currently can't be used with `startup_snapshot`.
pub will_snapshot : bool ,
2020-08-11 21:07:14 -04:00
2020-10-17 05:56:15 -04:00
/// Isolate creation parameters.
pub create_params : Option < v8 ::CreateParams > ,
2021-04-12 06:15:04 -04:00
/// V8 platform instance to use. Used when Deno initializes V8
/// (which it only does once), otherwise it's silenty dropped.
2021-07-02 03:32:48 -04:00
pub v8_platform : Option < v8 ::SharedRef < v8 ::Platform > > ,
2021-07-06 13:42:52 -04:00
2021-09-29 04:47:24 -04:00
/// The store to use for transferring SharedArrayBuffers between isolates.
2021-07-06 13:42:52 -04:00
/// If multiple isolates should have the possibility of sharing
2021-09-29 04:47:24 -04:00
/// SharedArrayBuffers, they should use the same [SharedArrayBufferStore]. If
/// no [SharedArrayBufferStore] is specified, SharedArrayBuffer can not be
/// serialized.
2021-07-06 13:42:52 -04:00
pub shared_array_buffer_store : Option < SharedArrayBufferStore > ,
2021-09-29 04:47:24 -04:00
/// The store to use for transferring `WebAssembly.Module` objects between
/// isolates.
/// If multiple isolates should have the possibility of sharing
/// `WebAssembly.Module` objects, they should use the same
/// [CompiledWasmModuleStore]. If no [CompiledWasmModuleStore] is specified,
/// `WebAssembly.Module` objects cannot be serialized.
pub compiled_wasm_module_store : Option < CompiledWasmModuleStore > ,
2020-09-11 09:18:49 -04:00
}
2020-08-11 21:07:14 -04:00
2020-09-11 09:18:49 -04:00
impl JsRuntime {
2020-11-05 20:26:14 -05:00
/// Only constructor, configuration is done through `options`.
2020-10-17 05:56:15 -04:00
pub fn new ( mut options : RuntimeOptions ) -> Self {
2021-04-12 06:15:04 -04:00
let v8_platform = options . v8_platform . take ( ) ;
2020-05-29 17:41:39 -04:00
static DENO_INIT : Once = Once ::new ( ) ;
2021-04-12 06:15:04 -04:00
DENO_INIT . call_once ( move | | v8_init ( v8_platform ) ) ;
2019-03-11 17:57:36 -04:00
2021-01-05 16:10:50 -05:00
let has_startup_snapshot = options . startup_snapshot . is_some ( ) ;
2022-03-14 13:44:15 -04:00
let js_error_create_fn = options
. js_error_create_fn
. unwrap_or_else ( | | Rc ::new ( JsError ::create ) ) ;
// Add builtins extension
options
. extensions
. insert ( 0 , crate ::ops_builtin ::init_builtins ( ) ) ;
let ops = Self ::collect_ops ( & mut options . extensions ) ;
let mut op_state = OpState ::new ( ops . len ( ) ) ;
if let Some ( get_error_class_fn ) = options . get_error_class_fn {
op_state . get_error_class_fn = get_error_class_fn ;
}
let op_state = Rc ::new ( RefCell ::new ( op_state ) ) ;
2022-04-08 04:32:48 -04:00
let op_ctxs = ops
. into_iter ( )
. enumerate ( )
. map ( | ( id , decl ) | OpCtx {
id ,
state : op_state . clone ( ) ,
decl ,
} )
. collect ::< Vec < _ > > ( )
. into_boxed_slice ( ) ;
2022-03-14 13:44:15 -04:00
2020-07-18 16:32:11 -04:00
let global_context ;
2020-08-11 21:07:14 -04:00
let ( mut isolate , maybe_snapshot_creator ) = if options . will_snapshot {
2020-01-06 10:24:44 -05:00
// TODO(ry) Support loading snapshots before snapshotting.
2020-08-11 21:07:14 -04:00
assert! ( options . startup_snapshot . is_none ( ) ) ;
2022-03-15 17:50:17 -04:00
let mut creator =
v8 ::SnapshotCreator ::new ( Some ( & bindings ::EXTERNAL_REFERENCES ) ) ;
2020-01-06 10:24:44 -05:00
let isolate = unsafe { creator . get_owned_isolate ( ) } ;
2020-09-06 15:44:29 -04:00
let mut isolate = JsRuntime ::setup_isolate ( isolate ) ;
2020-06-20 07:18:08 -04:00
{
let scope = & mut v8 ::HandleScope ::new ( & mut isolate ) ;
2022-04-08 04:32:48 -04:00
let context = bindings ::initialize_context ( scope , & op_ctxs , false ) ;
2020-07-18 16:32:11 -04:00
global_context = v8 ::Global ::new ( scope , context ) ;
2020-06-20 07:18:08 -04:00
creator . set_default_context ( context ) ;
}
2020-01-06 10:24:44 -05:00
( isolate , Some ( creator ) )
} else {
2020-10-17 05:56:15 -04:00
let mut params = options
. create_params
. take ( )
. unwrap_or_else ( v8 ::Isolate ::create_params )
2022-03-15 17:50:17 -04:00
. external_references ( & * * bindings ::EXTERNAL_REFERENCES ) ;
2020-08-11 21:07:14 -04:00
let snapshot_loaded = if let Some ( snapshot ) = options . startup_snapshot {
2020-04-22 14:24:49 -04:00
params = match snapshot {
Snapshot ::Static ( data ) = > params . snapshot_blob ( data ) ,
Snapshot ::JustCreated ( data ) = > params . snapshot_blob ( data ) ,
2020-05-09 21:00:40 -04:00
Snapshot ::Boxed ( data ) = > params . snapshot_blob ( data ) ,
2020-04-22 14:24:49 -04:00
} ;
true
} else {
false
} ;
2020-01-06 10:24:44 -05:00
let isolate = v8 ::Isolate ::new ( params ) ;
2020-09-06 15:44:29 -04:00
let mut isolate = JsRuntime ::setup_isolate ( isolate ) ;
2020-06-20 07:18:08 -04:00
{
let scope = & mut v8 ::HandleScope ::new ( & mut isolate ) ;
2022-04-08 04:32:48 -04:00
let context =
bindings ::initialize_context ( scope , & op_ctxs , snapshot_loaded ) ;
2022-03-14 13:44:15 -04:00
2020-07-18 16:32:11 -04:00
global_context = v8 ::Global ::new ( scope , context ) ;
2020-06-20 07:18:08 -04:00
}
2020-01-06 10:24:44 -05:00
( isolate , None )
} ;
2021-06-21 13:37:51 -04:00
let inspector =
JsRuntimeInspector ::new ( & mut isolate , global_context . clone ( ) ) ;
2021-05-26 15:07:12 -04:00
2020-09-11 09:18:49 -04:00
let loader = options
. module_loader
. unwrap_or_else ( | | Rc ::new ( NoopModuleLoader ) ) ;
2020-09-06 15:44:29 -04:00
isolate . set_slot ( Rc ::new ( RefCell ::new ( JsRuntimeState {
2022-04-17 07:53:08 -04:00
global_realm : Some ( JsRealm ( global_context ) ) ,
2020-01-25 08:31:42 -05:00
pending_promise_exceptions : HashMap ::new ( ) ,
2021-07-05 12:59:49 -04:00
pending_dyn_mod_evaluate : vec ! [ ] ,
2020-10-14 08:04:09 -04:00
pending_mod_evaluate : None ,
2021-07-05 12:59:49 -04:00
dyn_module_evaluate_idle_counter : 0 ,
2020-07-18 16:32:11 -04:00
js_recv_cb : None ,
2021-11-16 14:23:12 -05:00
js_macrotask_cbs : vec ! [ ] ,
js_nexttick_cbs : vec ! [ ] ,
2021-11-27 18:46:12 -05:00
js_promise_reject_cb : None ,
js_uncaught_exception_cb : None ,
2021-11-16 14:23:12 -05:00
has_tick_scheduled : false ,
2021-07-03 17:33:36 -04:00
js_wasm_streaming_cb : None ,
2022-04-15 10:08:09 -04:00
source_map_getter : options . source_map_getter ,
2020-09-14 21:23:48 -04:00
js_error_create_fn ,
2019-04-14 20:07:34 -04:00
pending_ops : FuturesUnordered ::new ( ) ,
2021-11-25 13:49:09 -05:00
unrefed_ops : HashSet ::new ( ) ,
2021-07-06 13:42:52 -04:00
shared_array_buffer_store : options . shared_array_buffer_store ,
2021-09-29 04:47:24 -04:00
compiled_wasm_module_store : options . compiled_wasm_module_store ,
2021-05-19 14:53:43 -04:00
op_state : op_state . clone ( ) ,
2022-04-08 04:32:48 -04:00
op_ctxs ,
2021-02-23 07:08:50 -05:00
have_unpolled_ops : false ,
2022-04-13 05:50:57 -04:00
explicit_terminate_exception : None ,
2019-11-16 19:17:47 -05:00
waker : AtomicWaker ::new ( ) ,
2020-05-29 17:41:39 -04:00
} ) ) ) ;
2020-01-06 10:24:44 -05:00
2021-05-19 14:53:43 -04:00
let module_map = ModuleMap ::new ( loader , op_state ) ;
isolate . set_slot ( Rc ::new ( RefCell ::new ( module_map ) ) ) ;
2021-01-05 16:10:50 -05:00
let mut js_runtime = Self {
2020-05-29 17:41:39 -04:00
v8_isolate : Some ( isolate ) ,
2021-06-21 13:37:51 -04:00
inspector : Some ( inspector ) ,
2020-05-29 17:41:39 -04:00
snapshot_creator : maybe_snapshot_creator ,
has_snapshotted : false ,
2022-04-17 07:53:08 -04:00
built_from_snapshot : has_startup_snapshot ,
2020-08-11 21:07:14 -04:00
allocations : IsolateAllocations ::default ( ) ,
2022-03-08 09:40:34 -05:00
event_loop_middlewares : Vec ::with_capacity ( options . extensions . len ( ) ) ,
2021-04-28 12:41:50 -04:00
extensions : options . extensions ,
2021-01-05 16:10:50 -05:00
} ;
2021-04-30 10:38:35 -04:00
// TODO(@AaronO): diff extensions inited in snapshot and those provided
// for now we assume that snapshot and extensions always match
2021-01-05 16:10:50 -05:00
if ! has_startup_snapshot {
2022-04-17 07:53:08 -04:00
let realm = js_runtime . global_realm ( ) ;
js_runtime . init_extension_js ( & realm ) . unwrap ( ) ;
2020-01-06 10:24:44 -05:00
}
2021-04-30 10:38:35 -04:00
// Init extension ops
js_runtime . init_extension_ops ( ) . unwrap ( ) ;
2022-03-14 13:44:15 -04:00
// Init callbacks (opresolve)
2021-10-04 05:45:41 -04:00
js_runtime . init_cbs ( ) ;
2021-01-05 16:10:50 -05:00
js_runtime
2020-01-06 10:24:44 -05:00
}
2020-10-07 09:56:52 -04:00
pub fn global_context ( & mut self ) -> v8 ::Global < v8 ::Context > {
2022-04-17 07:53:08 -04:00
self . global_realm ( ) . 0
2020-09-14 23:49:12 -04:00
}
2020-10-07 09:56:52 -04:00
pub fn v8_isolate ( & mut self ) -> & mut v8 ::OwnedIsolate {
2020-10-05 05:08:19 -04:00
self . v8_isolate . as_mut ( ) . unwrap ( )
}
2021-06-21 13:37:51 -04:00
pub fn inspector ( & mut self ) -> & mut Box < JsRuntimeInspector > {
self . inspector . as_mut ( ) . unwrap ( )
2021-05-26 15:07:12 -04:00
}
2022-04-17 07:53:08 -04:00
pub fn global_realm ( & mut self ) -> JsRealm {
let state = Self ::state ( self . v8_isolate ( ) ) ;
let state = state . borrow ( ) ;
state . global_realm . clone ( ) . unwrap ( )
}
pub fn create_realm ( & mut self ) -> Result < JsRealm , Error > {
let realm = {
// SAFETY: Having the scope tied to self's lifetime makes it impossible to
// reference self.ops while the scope is alive. Here we turn it into an
// unbound lifetime, which is sound because 1. it only lives until the end
// of this block, and 2. the HandleScope only has access to the isolate,
// and nothing else we're accessing from self does.
let scope = & mut v8 ::HandleScope ::new ( unsafe {
& mut * ( self . v8_isolate ( ) as * mut v8 ::OwnedIsolate )
} ) ;
let context = bindings ::initialize_context (
scope ,
& Self ::state ( self . v8_isolate ( ) ) . borrow ( ) . op_ctxs ,
self . built_from_snapshot ,
) ;
JsRealm ::new ( v8 ::Global ::new ( scope , context ) )
} ;
if ! self . built_from_snapshot {
self . init_extension_js ( & realm ) ? ;
}
Ok ( realm )
}
2021-04-28 12:28:46 -04:00
pub fn handle_scope ( & mut self ) -> v8 ::HandleScope {
2022-04-17 07:53:08 -04:00
self . global_realm ( ) . handle_scope ( self )
2021-04-28 12:28:46 -04:00
}
2020-04-23 05:51:07 -04:00
fn setup_isolate ( mut isolate : v8 ::OwnedIsolate ) -> v8 ::OwnedIsolate {
2020-01-06 10:24:44 -05:00
isolate . set_capture_stack_trace_for_uncaught_exceptions ( true , 10 ) ;
isolate . set_promise_reject_callback ( bindings ::promise_reject_callback ) ;
2020-09-06 10:50:49 -04:00
isolate . set_host_initialize_import_meta_object_callback (
bindings ::host_initialize_import_meta_object_callback ,
) ;
isolate . set_host_import_module_dynamically_callback (
bindings ::host_import_module_dynamically_callback ,
) ;
2020-01-06 10:24:44 -05:00
isolate
}
2020-09-14 23:49:12 -04:00
pub ( crate ) fn state ( isolate : & v8 ::Isolate ) -> Rc < RefCell < JsRuntimeState > > {
2020-09-06 15:44:29 -04:00
let s = isolate . get_slot ::< Rc < RefCell < JsRuntimeState > > > ( ) . unwrap ( ) ;
2020-05-29 17:41:39 -04:00
s . clone ( )
2019-03-21 09:48:19 -04:00
}
2021-05-19 14:53:43 -04:00
pub ( crate ) fn module_map ( isolate : & v8 ::Isolate ) -> Rc < RefCell < ModuleMap > > {
let module_map = isolate . get_slot ::< Rc < RefCell < ModuleMap > > > ( ) . unwrap ( ) ;
module_map . clone ( )
}
2022-04-17 07:53:08 -04:00
/// Initializes JS of provided Extensions in the given realm
fn init_extension_js ( & mut self , realm : & JsRealm ) -> Result < ( ) , Error > {
2021-04-28 12:41:50 -04:00
// Take extensions to avoid double-borrow
let mut extensions : Vec < Extension > = std ::mem ::take ( & mut self . extensions ) ;
for m in extensions . iter_mut ( ) {
let js_files = m . init_js ( ) ;
for ( filename , source ) in js_files {
2021-05-29 10:20:52 -04:00
let source = source ( ) ? ;
2021-04-28 12:41:50 -04:00
// TODO(@AaronO): use JsRuntime::execute_static() here to move src off heap
2022-04-17 07:53:08 -04:00
realm . execute_script ( self , filename , & source ) ? ;
2021-04-28 12:41:50 -04:00
}
}
// Restore extensions
self . extensions = extensions ;
Ok ( ( ) )
}
2022-03-14 13:44:15 -04:00
/// Collects ops from extensions & applies middleware
2022-03-15 18:43:17 -04:00
fn collect_ops ( extensions : & mut [ Extension ] ) -> Vec < OpDecl > {
2021-04-28 12:41:50 -04:00
// Middleware
let middleware : Vec < Box < OpMiddlewareFn > > = extensions
. iter_mut ( )
. filter_map ( | e | e . init_middleware ( ) )
. collect ( ) ;
2022-03-14 13:44:15 -04:00
2021-04-28 12:41:50 -04:00
// macroware wraps an opfn in all the middleware
2022-03-15 18:43:17 -04:00
let macroware = move | d | middleware . iter ( ) . fold ( d , | d , m | m ( d ) ) ;
2021-04-28 12:41:50 -04:00
2022-03-22 11:39:58 -04:00
// Flatten ops, apply middlware & override disabled ops
2022-03-14 13:44:15 -04:00
extensions
. iter_mut ( )
. filter_map ( | e | e . init_ops ( ) )
. flatten ( )
2022-03-15 18:43:17 -04:00
. map ( | d | OpDecl {
name : d . name ,
.. macroware ( d )
} )
2022-03-22 11:39:58 -04:00
. map ( | op | match op . enabled {
true = > op ,
false = > OpDecl {
v8_fn_ptr : match op . is_async {
true = > op_void_async ::v8_fn_ptr ( ) ,
false = > op_void_sync ::v8_fn_ptr ( ) ,
} ,
.. op
} ,
} )
2022-03-14 13:44:15 -04:00
. collect ( )
}
/// Initializes ops of provided Extensions
fn init_extension_ops ( & mut self ) -> Result < ( ) , Error > {
let op_state = self . op_state ( ) ;
// Take extensions to avoid double-borrow
let mut extensions : Vec < Extension > = std ::mem ::take ( & mut self . extensions ) ;
// Setup state
2021-04-28 12:41:50 -04:00
for e in extensions . iter_mut ( ) {
2022-03-14 13:44:15 -04:00
// ops are already registered during in bindings::initialize_context();
2021-04-28 12:41:50 -04:00
e . init_state ( & mut op_state . borrow_mut ( ) ) ? ;
2022-03-08 09:40:34 -05:00
2022-03-14 13:44:15 -04:00
// Setup event-loop middleware
2022-03-08 09:40:34 -05:00
if let Some ( middleware ) = e . init_event_loop_middleware ( ) {
self . event_loop_middlewares . push ( middleware ) ;
}
2021-04-28 12:41:50 -04:00
}
2022-03-14 13:44:15 -04:00
2021-04-28 12:41:50 -04:00
// Restore extensions
self . extensions = extensions ;
Ok ( ( ) )
}
2022-03-16 04:04:38 -04:00
/// Grab a Global handle to a v8 value returned by the expression
pub ( crate ) fn grab < ' s , T > (
scope : & mut v8 ::HandleScope < ' s > ,
root : v8 ::Local < ' s , v8 ::Value > ,
path : & str ,
) -> Option < v8 ::Local < ' s , T > >
where
v8 ::Local < ' s , T > : TryFrom < v8 ::Local < ' s , v8 ::Value > , Error = v8 ::DataError > ,
{
path
. split ( '.' )
. fold ( Some ( root ) , | p , k | {
let p = v8 ::Local ::< v8 ::Object > ::try_from ( p ? ) . ok ( ) ? ;
let k = v8 ::String ::new ( scope , k ) ? ;
p . get ( scope , k . into ( ) )
} ) ?
. try_into ( )
. ok ( )
}
pub ( crate ) fn grab_global < ' s , T > (
scope : & mut v8 ::HandleScope < ' s > ,
path : & str ,
) -> Option < v8 ::Local < ' s , T > >
where
v8 ::Local < ' s , T > : TryFrom < v8 ::Local < ' s , v8 ::Value > , Error = v8 ::DataError > ,
{
let context = scope . get_current_context ( ) ;
let global = context . global ( scope ) ;
Self ::grab ( scope , global . into ( ) , path )
2021-10-04 05:45:41 -04:00
}
2021-04-21 20:48:17 -04:00
2022-03-16 04:04:38 -04:00
pub ( crate ) fn ensure_objs < ' s > (
2022-03-15 17:50:17 -04:00
scope : & mut v8 ::HandleScope < ' s > ,
2022-03-16 04:04:38 -04:00
root : v8 ::Local < ' s , v8 ::Object > ,
path : & str ,
) -> Option < v8 ::Local < ' s , v8 ::Object > > {
path . split ( '.' ) . fold ( Some ( root ) , | p , k | {
let k = v8 ::String ::new ( scope , k ) ? . into ( ) ;
match p ? . get ( scope , k ) {
Some ( v ) if ! v . is_null_or_undefined ( ) = > v . try_into ( ) . ok ( ) ,
_ = > {
let o = v8 ::Object ::new ( scope ) ;
p ? . set ( scope , k , o . into ( ) ) ;
Some ( o )
}
}
} )
2022-03-15 17:50:17 -04:00
}
2021-10-04 05:45:41 -04:00
/// Grabs a reference to core.js' opresolve & syncOpsCache()
fn init_cbs ( & mut self ) {
2022-03-16 04:04:38 -04:00
let scope = & mut self . handle_scope ( ) ;
let recv_cb =
Self ::grab_global ::< v8 ::Function > ( scope , " Deno.core.opresolve " ) . unwrap ( ) ;
let recv_cb = v8 ::Global ::new ( scope , recv_cb ) ;
2021-10-04 05:45:41 -04:00
// Put global handles in state
2022-03-16 04:04:38 -04:00
let state_rc = JsRuntime ::state ( scope ) ;
2021-04-21 20:48:17 -04:00
let mut state = state_rc . borrow_mut ( ) ;
2021-10-04 05:45:41 -04:00
state . js_recv_cb . replace ( recv_cb ) ;
2021-04-25 16:00:05 -04:00
}
2020-11-05 20:26:14 -05:00
/// Returns the runtime's op state, which can be used to maintain ops
/// and access resources between op calls.
2020-09-10 09:57:45 -04:00
pub fn op_state ( & mut self ) -> Rc < RefCell < OpState > > {
2020-10-07 09:56:52 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2020-09-10 09:57:45 -04:00
let state = state_rc . borrow ( ) ;
state . op_state . clone ( )
}
2021-06-21 19:45:41 -04:00
/// Executes traditional JavaScript code (traditional = not ES modules).
2020-01-07 06:45:44 -05:00
///
2020-11-05 20:26:14 -05:00
/// The execution takes place on the current global context, so it is possible
/// to maintain local JS state and invoke this method multiple times.
///
2021-06-21 19:45:41 -04:00
/// `name` can be a filepath or any other string, eg.
///
/// - "/some/file/path.js"
/// - "<anon>"
/// - "[native code]"
///
/// The same `name` value can be used for multiple executions.
///
2021-11-16 09:02:28 -05:00
/// `Error` can be downcast to a type that exposes additional information
2020-09-14 12:48:57 -04:00
/// about the V8 exception. By default this type is `JsError`, however it may
2020-09-14 21:23:48 -04:00
/// be a different type if `RuntimeOptions::js_error_create_fn` has been set.
2021-06-21 19:45:41 -04:00
pub fn execute_script (
2020-01-06 10:24:44 -05:00
& mut self ,
2021-06-21 19:45:41 -04:00
name : & str ,
source_code : & str ,
2021-11-16 09:02:28 -05:00
) -> Result < v8 ::Global < v8 ::Value > , Error > {
2022-04-17 07:53:08 -04:00
self . global_realm ( ) . execute_script ( self , name , source_code )
2019-03-11 17:57:36 -04:00
}
2019-07-10 18:53:48 -04:00
/// Takes a snapshot. The isolate should have been created with will_snapshot
/// set to true.
///
2021-11-16 09:02:28 -05:00
/// `Error` can be downcast to a type that exposes additional information
2020-09-14 12:48:57 -04:00
/// about the V8 exception. By default this type is `JsError`, however it may
2020-09-14 21:23:48 -04:00
/// be a different type if `RuntimeOptions::js_error_create_fn` has been set.
2020-04-22 14:24:49 -04:00
pub fn snapshot ( & mut self ) -> v8 ::StartupData {
2020-01-06 14:07:35 -05:00
assert! ( self . snapshot_creator . is_some ( ) ) ;
2022-03-15 17:50:17 -04:00
// Nuke Deno.core.ops.* to avoid ExternalReference snapshotting issues
// TODO(@AaronO): make ops stable across snapshots
{
let scope = & mut self . handle_scope ( ) ;
2022-03-16 04:04:38 -04:00
let o = Self ::grab_global ::< v8 ::Object > ( scope , " Deno.core.ops " ) . unwrap ( ) ;
let names = o . get_own_property_names ( scope ) . unwrap ( ) ;
2022-03-15 17:50:17 -04:00
for i in 0 .. names . length ( ) {
let key = names . get_index ( scope , i ) . unwrap ( ) ;
2022-03-16 04:04:38 -04:00
o . delete ( scope , key ) ;
2022-03-15 17:50:17 -04:00
}
}
2020-10-07 09:56:52 -04:00
let state = Self ::state ( self . v8_isolate ( ) ) ;
2020-01-06 10:24:44 -05:00
2022-04-17 07:53:08 -04:00
state . borrow_mut ( ) . global_realm . take ( ) ;
2020-01-06 10:24:44 -05:00
2021-06-21 13:37:51 -04:00
self . inspector . take ( ) ;
2021-05-19 14:53:43 -04:00
// Overwrite existing ModuleMap to drop v8::Global handles
self
. v8_isolate ( )
. set_slot ( Rc ::new ( RefCell ::new ( ModuleMap ::new (
Rc ::new ( NoopModuleLoader ) ,
state . borrow ( ) . op_state . clone ( ) ,
) ) ) ) ;
// Drop other v8::Global handles before snapshotting
2021-04-25 12:57:48 -04:00
std ::mem ::take ( & mut state . borrow_mut ( ) . js_recv_cb ) ;
2020-09-06 10:50:49 -04:00
2020-01-06 14:07:35 -05:00
let snapshot_creator = self . snapshot_creator . as_mut ( ) . unwrap ( ) ;
2020-01-06 10:24:44 -05:00
let snapshot = snapshot_creator
. create_blob ( v8 ::FunctionCodeHandling ::Keep )
. unwrap ( ) ;
2020-01-06 14:07:35 -05:00
self . has_snapshotted = true ;
2020-02-24 18:53:29 -05:00
2020-02-28 02:28:33 -05:00
snapshot
2019-04-08 10:12:43 -04:00
}
2020-05-29 17:41:39 -04:00
2022-03-22 09:32:32 -04:00
/// Returns the namespace object of a module.
///
/// This is only available after module evaluation has completed.
/// This function panics if module has not been instantiated.
pub fn get_module_namespace (
& mut self ,
module_id : ModuleId ,
) -> Result < v8 ::Global < v8 ::Object > , Error > {
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
let module_handle = module_map_rc
. borrow ( )
. get_handle ( module_id )
. expect ( " ModuleInfo not found " ) ;
let scope = & mut self . handle_scope ( ) ;
let module = module_handle . open ( scope ) ;
if module . get_status ( ) = = v8 ::ModuleStatus ::Errored {
let exception = module . get_exception ( ) ;
let err = exception_to_err_result ( scope , exception , false )
. map_err ( | err | attach_handle_to_error ( scope , err , exception ) ) ;
return err ;
}
assert! ( matches! (
module . get_status ( ) ,
v8 ::ModuleStatus ::Instantiated | v8 ::ModuleStatus ::Evaluated
) ) ;
let module_namespace : v8 ::Local < v8 ::Object > =
v8 ::Local ::try_from ( module . get_module_namespace ( ) )
. map_err ( | err : v8 ::DataError | generic_error ( err . to_string ( ) ) ) ? ;
Ok ( v8 ::Global ::new ( scope , module_namespace ) )
}
2020-08-11 21:07:14 -04:00
/// Registers a callback on the isolate when the memory limits are approached.
/// Use this to prevent V8 from crashing the process when reaching the limit.
///
/// Calls the closure with the current heap limit and the initial heap limit.
/// The return value of the closure is set as the new limit.
pub fn add_near_heap_limit_callback < C > ( & mut self , cb : C )
where
C : FnMut ( usize , usize ) -> usize + 'static ,
{
let boxed_cb = Box ::new ( RefCell ::new ( cb ) ) ;
let data = boxed_cb . as_ptr ( ) as * mut c_void ;
2020-08-12 00:08:50 -04:00
let prev = self
. allocations
. near_heap_limit_callback_data
. replace ( ( boxed_cb , near_heap_limit_callback ::< C > ) ) ;
if let Some ( ( _ , prev_cb ) ) = prev {
self
2020-10-05 05:08:19 -04:00
. v8_isolate ( )
2020-08-12 00:08:50 -04:00
. remove_near_heap_limit_callback ( prev_cb , 0 ) ;
}
2020-08-11 21:07:14 -04:00
self
2020-10-05 05:08:19 -04:00
. v8_isolate ( )
2020-08-11 21:07:14 -04:00
. add_near_heap_limit_callback ( near_heap_limit_callback ::< C > , data ) ;
}
pub fn remove_near_heap_limit_callback ( & mut self , heap_limit : usize ) {
if let Some ( ( _ , cb ) ) = self . allocations . near_heap_limit_callback_data . take ( )
{
self
2020-10-05 05:08:19 -04:00
. v8_isolate ( )
2020-08-11 21:07:14 -04:00
. remove_near_heap_limit_callback ( cb , heap_limit ) ;
}
}
2019-03-11 17:57:36 -04:00
2021-07-02 04:46:37 -04:00
fn pump_v8_message_loop ( & mut self ) {
let scope = & mut self . handle_scope ( ) ;
while v8 ::Platform ::pump_message_loop (
& v8 ::V8 ::get_current_platform ( ) ,
scope ,
false , // don't block if there are no tasks
) {
// do nothing
}
2021-07-03 17:33:36 -04:00
scope . perform_microtask_checkpoint ( ) ;
2021-07-02 04:46:37 -04:00
}
2022-01-24 11:59:41 -05:00
pub fn poll_value (
& mut self ,
global : & v8 ::Global < v8 ::Value > ,
cx : & mut Context ,
) -> Poll < Result < v8 ::Global < v8 ::Value > , Error > > {
let state = self . poll_event_loop ( cx , false ) ;
let mut scope = self . handle_scope ( ) ;
let local = v8 ::Local ::< v8 ::Value > ::new ( & mut scope , global ) ;
if let Ok ( promise ) = v8 ::Local ::< v8 ::Promise > ::try_from ( local ) {
match promise . state ( ) {
v8 ::PromiseState ::Pending = > match state {
Poll ::Ready ( Ok ( _ ) ) = > {
let msg = " Promise resolution is still pending but the event loop has already resolved. " ;
Poll ::Ready ( Err ( generic_error ( msg ) ) )
}
Poll ::Ready ( Err ( e ) ) = > Poll ::Ready ( Err ( e ) ) ,
Poll ::Pending = > Poll ::Pending ,
} ,
v8 ::PromiseState ::Fulfilled = > {
let value = promise . result ( & mut scope ) ;
let value_handle = v8 ::Global ::new ( & mut scope , value ) ;
Poll ::Ready ( Ok ( value_handle ) )
}
v8 ::PromiseState ::Rejected = > {
let exception = promise . result ( & mut scope ) ;
Poll ::Ready ( exception_to_err_result ( & mut scope , exception , false ) )
}
}
} else {
let value_handle = v8 ::Global ::new ( & mut scope , local ) ;
Poll ::Ready ( Ok ( value_handle ) )
}
}
2021-09-04 14:19:26 -04:00
/// Waits for the given value to resolve while polling the event loop.
///
/// This future resolves when either the value is resolved or the event loop runs to
/// completion.
pub async fn resolve_value (
& mut self ,
global : v8 ::Global < v8 ::Value > ,
2021-11-16 09:02:28 -05:00
) -> Result < v8 ::Global < v8 ::Value > , Error > {
2022-01-24 11:59:41 -05:00
poll_fn ( | cx | self . poll_value ( & global , cx ) ) . await
2021-09-04 14:19:26 -04:00
}
2020-10-11 07:20:40 -04:00
/// Runs event loop to completion
///
/// This future resolves when:
/// - there are no more pending dynamic imports
/// - there are no more pending ops
2021-05-26 15:07:12 -04:00
/// - there are no more active inspector sessions (only if `wait_for_inspector` is set to true)
pub async fn run_event_loop (
& mut self ,
wait_for_inspector : bool ,
2021-11-16 09:02:28 -05:00
) -> Result < ( ) , Error > {
2021-05-26 15:07:12 -04:00
poll_fn ( | cx | self . poll_event_loop ( cx , wait_for_inspector ) ) . await
2020-10-11 07:20:40 -04:00
}
2019-06-12 13:53:24 -04:00
2020-10-11 07:20:40 -04:00
/// Runs a single tick of event loop
2021-05-26 15:07:12 -04:00
///
/// If `wait_for_inspector` is set to true event loop
/// will return `Poll::Pending` if there are active inspector sessions.
2020-10-11 07:20:40 -04:00
pub fn poll_event_loop (
& mut self ,
cx : & mut Context ,
2021-05-26 15:07:12 -04:00
wait_for_inspector : bool ,
2021-11-16 09:02:28 -05:00
) -> Poll < Result < ( ) , Error > > {
2021-06-21 13:37:51 -04:00
// We always poll the inspector first
let _ = self . inspector ( ) . poll_unpin ( cx ) ;
2021-05-26 15:07:12 -04:00
2020-10-11 07:20:40 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2020-05-29 17:41:39 -04:00
{
let state = state_rc . borrow ( ) ;
state . waker . register ( cx . waker ( ) ) ;
}
2020-02-24 18:53:29 -05:00
2021-07-03 17:33:36 -04:00
self . pump_v8_message_loop ( ) ;
2020-10-14 08:04:09 -04:00
// Ops
{
2021-10-24 15:41:57 -04:00
self . resolve_async_ops ( cx ) ? ;
2021-11-16 14:23:12 -05:00
self . drain_nexttick ( ) ? ;
2020-10-14 08:04:09 -04:00
self . drain_macrotasks ( ) ? ;
self . check_promise_exceptions ( ) ? ;
}
2020-10-05 05:08:19 -04:00
// Dynamic module loading - ie. modules loaded using "import()"
{
2020-10-11 07:20:40 -04:00
let poll_imports = self . prepare_dyn_imports ( cx ) ? ;
2020-09-06 10:50:49 -04:00
assert! ( poll_imports . is_ready ( ) ) ;
2020-10-11 07:20:40 -04:00
let poll_imports = self . poll_dyn_imports ( cx ) ? ;
2020-09-06 10:50:49 -04:00
assert! ( poll_imports . is_ready ( ) ) ;
2020-05-29 17:41:39 -04:00
2020-11-27 14:47:35 -05:00
self . evaluate_dyn_imports ( ) ;
2019-03-14 19:17:52 -04:00
2020-10-11 07:20:40 -04:00
self . check_promise_exceptions ( ) ? ;
2019-03-11 17:57:36 -04:00
}
2022-03-08 09:40:34 -05:00
// Event loop middlewares
let mut maybe_scheduling = false ;
{
let state = state_rc . borrow ( ) ;
let op_state = state . op_state . clone ( ) ;
for f in & self . event_loop_middlewares {
if f ( & mut op_state . borrow_mut ( ) , cx ) {
maybe_scheduling = true ;
}
}
}
2020-10-14 08:04:09 -04:00
// Top level module
2020-11-27 14:47:35 -05:00
self . evaluate_pending_module ( ) ;
2020-10-14 08:04:09 -04:00
2021-07-05 12:59:49 -04:00
let mut state = state_rc . borrow_mut ( ) ;
2021-05-19 14:53:43 -04:00
let module_map = module_map_rc . borrow ( ) ;
2021-11-25 13:49:09 -05:00
let has_pending_refed_ops =
state . pending_ops . len ( ) > state . unrefed_ops . len ( ) ;
2021-05-19 14:53:43 -04:00
let has_pending_dyn_imports = module_map . has_pending_dynamic_imports ( ) ;
2020-10-14 08:04:09 -04:00
let has_pending_dyn_module_evaluation =
! state . pending_dyn_mod_evaluate . is_empty ( ) ;
let has_pending_module_evaluation = state . pending_mod_evaluate . is_some ( ) ;
2021-07-03 17:33:36 -04:00
let has_pending_background_tasks =
self . v8_isolate ( ) . has_pending_background_tasks ( ) ;
2021-11-19 08:01:30 -05:00
let has_tick_scheduled = state . has_tick_scheduled ;
2021-05-26 15:07:12 -04:00
let inspector_has_active_sessions = self
. inspector
. as_ref ( )
. map ( | i | i . has_active_sessions ( ) )
. unwrap_or ( false ) ;
2020-10-14 08:04:09 -04:00
2021-11-25 13:49:09 -05:00
if ! has_pending_refed_ops
2020-10-14 08:04:09 -04:00
& & ! has_pending_dyn_imports
& & ! has_pending_dyn_module_evaluation
& & ! has_pending_module_evaluation
2021-07-03 17:33:36 -04:00
& & ! has_pending_background_tasks
2021-11-19 08:01:30 -05:00
& & ! has_tick_scheduled
2022-03-08 09:40:34 -05:00
& & ! maybe_scheduling
2020-10-14 08:04:09 -04:00
{
2021-05-26 15:07:12 -04:00
if wait_for_inspector & & inspector_has_active_sessions {
return Poll ::Pending ;
}
2020-10-05 05:08:19 -04:00
return Poll ::Ready ( Ok ( ( ) ) ) ;
2020-05-29 17:41:39 -04:00
}
2019-03-11 17:57:36 -04:00
2020-10-05 05:08:19 -04:00
// Check if more async ops have been dispatched
// during this turn of event loop.
2021-07-03 17:33:36 -04:00
// If there are any pending background tasks, we also wake the runtime to
// make sure we don't miss them.
// TODO(andreubotella) The event loop will spin as long as there are pending
// background tasks. We should look into having V8 notify us when a
// background task is done.
2021-11-29 18:31:12 -05:00
if state . have_unpolled_ops
| | has_pending_background_tasks
| | has_tick_scheduled
2022-03-08 09:40:34 -05:00
| | maybe_scheduling
2021-11-29 18:31:12 -05:00
{
2020-10-05 05:08:19 -04:00
state . waker . wake ( ) ;
2019-03-11 17:57:36 -04:00
}
2020-10-05 05:08:19 -04:00
2020-10-14 08:04:09 -04:00
if has_pending_module_evaluation {
2021-11-25 13:49:09 -05:00
if has_pending_refed_ops
2020-10-14 08:04:09 -04:00
| | has_pending_dyn_imports
| | has_pending_dyn_module_evaluation
2021-07-03 17:33:36 -04:00
| | has_pending_background_tasks
2021-11-29 18:31:12 -05:00
| | has_tick_scheduled
2022-03-08 09:40:34 -05:00
| | maybe_scheduling
2020-10-14 08:04:09 -04:00
{
// pass, will be polled again
} else {
2021-12-01 09:22:11 -05:00
let msg = " Module evaluation is still pending but there are no pending ops or dynamic imports. This situation is often caused by unresolved promises. " ;
2020-10-14 08:04:09 -04:00
return Poll ::Ready ( Err ( generic_error ( msg ) ) ) ;
}
}
if has_pending_dyn_module_evaluation {
2021-11-25 13:49:09 -05:00
if has_pending_refed_ops
2021-07-03 17:33:36 -04:00
| | has_pending_dyn_imports
| | has_pending_background_tasks
2021-11-29 18:31:12 -05:00
| | has_tick_scheduled
2021-07-03 17:33:36 -04:00
{
2020-10-14 08:04:09 -04:00
// pass, will be polled again
2021-07-05 12:59:49 -04:00
} else if state . dyn_module_evaluate_idle_counter > = 1 {
2021-12-01 09:22:11 -05:00
let mut msg = " Dynamically imported module evaluation is still pending but there are no pending ops. This situation is often caused by unresolved promises.
2021-04-28 12:28:46 -04:00
Pending dynamic modules :\ n " .to_string();
for pending_evaluate in & state . pending_dyn_mod_evaluate {
2021-05-19 14:53:43 -04:00
let module_info = module_map
2021-04-28 12:28:46 -04:00
. get_info_by_id ( & pending_evaluate . module_id )
. unwrap ( ) ;
msg . push_str ( & format! ( " - {} " , module_info . name . as_str ( ) ) ) ;
}
2020-10-14 08:04:09 -04:00
return Poll ::Ready ( Err ( generic_error ( msg ) ) ) ;
2021-07-05 12:59:49 -04:00
} else {
// Delay the above error by one spin of the event loop. A dynamic import
// evaluation may complete during this, in which case the counter will
// reset.
state . dyn_module_evaluate_idle_counter + = 1 ;
state . waker . wake ( ) ;
2020-10-14 08:04:09 -04:00
}
}
2020-10-05 05:08:19 -04:00
Poll ::Pending
2019-03-11 17:57:36 -04:00
}
}
2020-10-11 07:20:40 -04:00
extern " C " fn near_heap_limit_callback < F > (
data : * mut c_void ,
current_heap_limit : usize ,
initial_heap_limit : usize ,
) -> usize
where
F : FnMut ( usize , usize ) -> usize ,
{
let callback = unsafe { & mut * ( data as * mut F ) } ;
callback ( current_heap_limit , initial_heap_limit )
}
2020-09-06 15:44:29 -04:00
impl JsRuntimeState {
2021-05-19 14:53:43 -04:00
/// Called by `bindings::host_import_module_dynamically_callback`
/// after initiating new dynamic import load.
pub fn notify_new_dynamic_import ( & mut self ) {
// Notify event loop to poll again soon.
2020-09-06 10:50:49 -04:00
self . waker . wake ( ) ;
}
2020-05-29 17:41:39 -04:00
}
2020-03-02 17:20:16 -05:00
pub ( crate ) fn exception_to_err_result < ' s , T > (
2020-06-20 07:18:08 -04:00
scope : & mut v8 ::HandleScope < ' s > ,
2020-02-24 18:53:29 -05:00
exception : v8 ::Local < v8 ::Value > ,
2020-10-25 23:34:00 -04:00
in_promise : bool ,
2021-11-16 09:02:28 -05:00
) -> Result < T , Error > {
2022-04-13 05:50:57 -04:00
let state_rc = JsRuntime ::state ( scope ) ;
2020-12-28 10:36:44 -05:00
let is_terminating_exception = scope . is_execution_terminating ( ) ;
2020-02-24 18:53:29 -05:00
let mut exception = exception ;
if is_terminating_exception {
2022-04-13 05:50:57 -04:00
// TerminateExecution was called. Cancel isolate termination so that the
2020-02-24 18:53:29 -05:00
// exception can be created..
2020-12-28 10:36:44 -05:00
scope . cancel_terminate_execution ( ) ;
2020-02-24 18:53:29 -05:00
2022-04-13 05:50:57 -04:00
// If the termination is the result of a `Deno.core.terminate` call, we want
// to use the exception that was passed to it rather than the exception that
// was passed to this function.
2022-04-15 10:08:09 -04:00
let mut state = state_rc . borrow_mut ( ) ;
2022-04-13 05:50:57 -04:00
exception = state
. explicit_terminate_exception
. take ( )
. map ( | exception | v8 ::Local ::new ( scope , exception ) )
. unwrap_or_else ( | | {
// Maybe make a new exception object.
if exception . is_null_or_undefined ( ) {
let message = v8 ::String ::new ( scope , " execution terminated " ) . unwrap ( ) ;
v8 ::Exception ::error ( scope , message )
} else {
exception
}
} ) ;
2019-03-21 09:48:19 -04:00
}
2020-02-24 18:53:29 -05:00
2020-10-25 23:34:00 -04:00
let mut js_error = JsError ::from_v8_exception ( scope , exception ) ;
if in_promise {
2022-04-13 10:41:39 -04:00
js_error . exception_message = format! (
2020-10-25 23:34:00 -04:00
" Uncaught (in promise) {} " ,
2022-04-13 10:41:39 -04:00
js_error . exception_message . trim_start_matches ( " Uncaught " )
2020-10-25 23:34:00 -04:00
) ;
}
2022-04-15 10:08:09 -04:00
let js_error = ( state_rc . borrow ( ) . js_error_create_fn ) ( js_error ) ;
2020-02-24 18:53:29 -05:00
if is_terminating_exception {
// Re-enable exception termination.
2020-12-28 10:36:44 -05:00
scope . terminate_execution ( ) ;
2020-02-24 18:53:29 -05:00
}
2020-02-29 19:24:36 -05:00
Err ( js_error )
2020-02-24 18:53:29 -05:00
}
2020-09-06 10:50:49 -04:00
// Related to module loading
2020-09-06 15:44:29 -04:00
impl JsRuntime {
2021-05-19 14:53:43 -04:00
pub ( crate ) fn instantiate_module (
2020-09-06 10:50:49 -04:00
& mut self ,
2021-05-19 14:53:43 -04:00
id : ModuleId ,
2021-11-16 09:02:28 -05:00
) -> Result < ( ) , Error > {
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2021-02-23 09:22:55 -05:00
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
2020-09-06 10:50:49 -04:00
2021-05-19 14:53:43 -04:00
let module = module_map_rc
2021-02-23 09:22:55 -05:00
. borrow ( )
. get_handle ( id )
. map ( | handle | v8 ::Local ::new ( tc_scope , handle ) )
. expect ( " ModuleInfo not found " ) ;
2020-09-06 10:50:49 -04:00
2021-02-23 09:22:55 -05:00
if module . get_status ( ) = = v8 ::ModuleStatus ::Errored {
let exception = module . get_exception ( ) ;
2021-05-19 14:53:43 -04:00
let err = exception_to_err_result ( tc_scope , exception , false )
. map_err ( | err | attach_handle_to_error ( tc_scope , err , exception ) ) ;
return err ;
2020-09-06 10:50:49 -04:00
}
2021-05-19 14:53:43 -04:00
// IMPORTANT: No borrows to `ModuleMap` can be held at this point because
// `module_resolve_callback` will be calling into `ModuleMap` from within
// the isolate.
let instantiate_result =
module . instantiate_module ( tc_scope , bindings ::module_resolve_callback ) ;
if instantiate_result . is_none ( ) {
let exception = tc_scope . exception ( ) . unwrap ( ) ;
let err = exception_to_err_result ( tc_scope , exception , false )
. map_err ( | err | attach_handle_to_error ( tc_scope , err , exception ) ) ;
return err ;
}
Ok ( ( ) )
2020-09-06 10:50:49 -04:00
}
2021-05-19 14:53:43 -04:00
fn dynamic_import_module_evaluate (
2020-10-14 08:04:09 -04:00
& mut self ,
load_id : ModuleLoadId ,
id : ModuleId ,
2021-11-16 09:02:28 -05:00
) -> Result < ( ) , Error > {
2020-10-14 08:04:09 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2020-10-14 08:04:09 -04:00
2021-05-19 14:53:43 -04:00
let module_handle = module_map_rc
2020-10-14 08:04:09 -04:00
. borrow ( )
2020-11-21 10:23:35 -05:00
. get_handle ( id )
. expect ( " ModuleInfo not found " ) ;
2020-10-14 08:04:09 -04:00
let status = {
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2021-10-27 17:26:15 -04:00
let module = module_handle . open ( scope ) ;
2020-10-14 08:04:09 -04:00
module . get_status ( )
} ;
2021-04-04 07:26:00 -04:00
match status {
v8 ::ModuleStatus ::Instantiated | v8 ::ModuleStatus ::Evaluated = > { }
_ = > return Ok ( ( ) ) ,
2021-02-23 09:22:55 -05:00
}
2020-10-14 08:04:09 -04:00
2021-02-23 09:22:55 -05:00
// IMPORTANT: Top-level-await is enabled, which means that return value
// of module evaluation is a promise.
//
// This promise is internal, and not the same one that gets returned to
// the user. We add an empty `.catch()` handler so that it does not result
// in an exception if it rejects. That will instead happen for the other
// promise if not handled by the user.
//
// For more details see:
// https://github.com/denoland/deno/issues/4908
// https://v8.dev/features/top-level-await#module-execution-order
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2021-11-22 07:51:20 -05:00
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
let module = v8 ::Local ::new ( tc_scope , & module_handle ) ;
let maybe_value = module . evaluate ( tc_scope ) ;
2021-02-23 09:22:55 -05:00
// Update status after evaluating.
let status = module . get_status ( ) ;
if let Some ( value ) = maybe_value {
assert! (
status = = v8 ::ModuleStatus ::Evaluated
| | status = = v8 ::ModuleStatus ::Errored
) ;
let promise = v8 ::Local ::< v8 ::Promise > ::try_from ( value )
. expect ( " Expected to get promise as module evaluation result " ) ;
let empty_fn = | _scope : & mut v8 ::HandleScope ,
_args : v8 ::FunctionCallbackArguments ,
_rv : v8 ::ReturnValue | { } ;
2021-11-22 07:51:20 -05:00
let empty_fn = v8 ::FunctionTemplate ::new ( tc_scope , empty_fn ) ;
let empty_fn = empty_fn . get_function ( tc_scope ) . unwrap ( ) ;
promise . catch ( tc_scope , empty_fn ) ;
2021-02-23 09:22:55 -05:00
let mut state = state_rc . borrow_mut ( ) ;
2021-11-22 07:51:20 -05:00
let promise_global = v8 ::Global ::new ( tc_scope , promise ) ;
let module_global = v8 ::Global ::new ( tc_scope , module ) ;
2020-10-14 08:04:09 -04:00
2021-02-23 09:22:55 -05:00
let dyn_import_mod_evaluate = DynImportModEvaluate {
2021-04-28 12:28:46 -04:00
load_id ,
2021-02-23 09:22:55 -05:00
module_id : id ,
promise : promise_global ,
module : module_global ,
} ;
2020-10-14 08:04:09 -04:00
2021-07-05 12:59:49 -04:00
state . pending_dyn_mod_evaluate . push ( dyn_import_mod_evaluate ) ;
2021-11-22 07:51:20 -05:00
} else if tc_scope . has_terminated ( ) | | tc_scope . is_execution_terminating ( ) {
return Err (
generic_error ( " Cannot evaluate dynamically imported module, because JavaScript execution has been terminated. " )
) ;
2021-02-23 09:22:55 -05:00
} else {
assert! ( status = = v8 ::ModuleStatus ::Errored ) ;
2020-10-14 08:04:09 -04:00
}
Ok ( ( ) )
}
2021-03-04 07:19:47 -05:00
// TODO(bartlomieju): make it return `ModuleEvaluationFuture`?
2020-10-14 08:04:09 -04:00
/// Evaluates an already instantiated ES module.
///
2021-03-04 07:19:47 -05:00
/// Returns a receiver handle that resolves when module promise resolves.
/// Implementors must manually call `run_event_loop()` to drive module
/// evaluation future.
///
2021-11-16 09:02:28 -05:00
/// `Error` can be downcast to a type that exposes additional information
2020-10-14 08:04:09 -04:00
/// about the V8 exception. By default this type is `JsError`, however it may
/// be a different type if `RuntimeOptions::js_error_create_fn` has been set.
2021-02-23 09:22:55 -05:00
///
/// This function panics if module has not been instantiated.
2021-03-04 07:19:47 -05:00
pub fn mod_evaluate (
2020-10-14 08:04:09 -04:00
& mut self ,
id : ModuleId ,
2021-11-16 09:02:28 -05:00
) -> oneshot ::Receiver < Result < ( ) , Error > > {
2020-10-07 09:56:52 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2021-11-22 07:51:20 -05:00
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
2020-09-06 10:50:49 -04:00
2021-05-19 14:53:43 -04:00
let module = module_map_rc
2020-09-06 10:50:49 -04:00
. borrow ( )
2020-11-21 10:23:35 -05:00
. get_handle ( id )
2021-11-22 07:51:20 -05:00
. map ( | handle | v8 ::Local ::new ( tc_scope , handle ) )
2020-09-06 10:50:49 -04:00
. expect ( " ModuleInfo not found " ) ;
let mut status = module . get_status ( ) ;
2021-02-23 09:22:55 -05:00
assert_eq! ( status , v8 ::ModuleStatus ::Instantiated ) ;
2020-09-06 10:50:49 -04:00
2021-07-30 07:36:43 -04:00
let ( sender , receiver ) = oneshot ::channel ( ) ;
2020-10-14 08:04:09 -04:00
2021-02-23 09:22:55 -05:00
// IMPORTANT: Top-level-await is enabled, which means that return value
// of module evaluation is a promise.
//
// Because that promise is created internally by V8, when error occurs during
// module evaluation the promise is rejected, and since the promise has no rejection
// handler it will result in call to `bindings::promise_reject_callback` adding
// the promise to pending promise rejection table - meaning JsRuntime will return
// error on next poll().
//
// This situation is not desirable as we want to manually return error at the
// end of this function to handle it further. It means we need to manually
// remove this promise from pending promise rejection table.
//
// For more details see:
// https://github.com/denoland/deno/issues/4908
// https://v8.dev/features/top-level-await#module-execution-order
2021-11-22 07:51:20 -05:00
let maybe_value = module . evaluate ( tc_scope ) ;
2021-02-23 09:22:55 -05:00
// Update status after evaluating.
status = module . get_status ( ) ;
2022-04-13 05:50:57 -04:00
let explicit_terminate_exception =
state_rc . borrow_mut ( ) . explicit_terminate_exception . take ( ) ;
if let Some ( exception ) = explicit_terminate_exception {
let exception = v8 ::Local ::new ( tc_scope , exception ) ;
sender
. send ( exception_to_err_result ( tc_scope , exception , false ) )
. expect ( " Failed to send module evaluation error. " ) ;
} else if let Some ( value ) = maybe_value {
2021-02-23 09:22:55 -05:00
assert! (
status = = v8 ::ModuleStatus ::Evaluated
| | status = = v8 ::ModuleStatus ::Errored
) ;
let promise = v8 ::Local ::< v8 ::Promise > ::try_from ( value )
. expect ( " Expected to get promise as module evaluation result " ) ;
2021-11-22 07:51:20 -05:00
let promise_global = v8 ::Global ::new ( tc_scope , promise ) ;
2021-02-23 09:22:55 -05:00
let mut state = state_rc . borrow_mut ( ) ;
state . pending_promise_exceptions . remove ( & promise_global ) ;
2021-11-22 07:51:20 -05:00
let promise_global = v8 ::Global ::new ( tc_scope , promise ) ;
2021-02-23 09:22:55 -05:00
assert! (
state . pending_mod_evaluate . is_none ( ) ,
" There is already pending top level module evaluation "
) ;
2020-10-14 08:04:09 -04:00
2021-02-23 09:22:55 -05:00
state . pending_mod_evaluate = Some ( ModEvaluate {
promise : promise_global ,
sender ,
} ) ;
2021-11-22 07:51:20 -05:00
tc_scope . perform_microtask_checkpoint ( ) ;
} else if tc_scope . has_terminated ( ) | | tc_scope . is_execution_terminating ( ) {
sender . send ( Err (
generic_error ( " Cannot evaluate module, because JavaScript execution has been terminated. " )
) ) . expect ( " Failed to send module evaluation error. " ) ;
2021-02-23 09:22:55 -05:00
} else {
assert! ( status = = v8 ::ModuleStatus ::Errored ) ;
2020-09-06 10:50:49 -04:00
}
2020-11-27 14:47:35 -05:00
receiver
2020-10-14 08:04:09 -04:00
}
2021-11-16 09:02:28 -05:00
fn dynamic_import_reject ( & mut self , id : ModuleLoadId , err : Error ) {
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2020-09-06 10:50:49 -04:00
2021-05-19 14:53:43 -04:00
let resolver_handle = module_map_rc
2020-09-06 10:50:49 -04:00
. borrow_mut ( )
2021-05-19 14:53:43 -04:00
. dynamic_import_map
2020-09-06 10:50:49 -04:00
. remove ( & id )
2021-05-19 14:53:43 -04:00
. expect ( " Invalid dynamic import id " ) ;
2021-10-27 17:26:15 -04:00
let resolver = resolver_handle . open ( scope ) ;
2020-09-06 10:50:49 -04:00
let exception = err
. downcast_ref ::< ErrWithV8Handle > ( )
. map ( | err | err . get_handle ( scope ) )
. unwrap_or_else ( | | {
let message = err . to_string ( ) ;
let message = v8 ::String ::new ( scope , & message ) . unwrap ( ) ;
v8 ::Exception ::type_error ( scope , message )
} ) ;
2021-05-19 14:53:43 -04:00
// IMPORTANT: No borrows to `ModuleMap` can be held at this point because
// rejecting the promise might initiate another `import()` which will
// in turn call `bindings::host_import_module_dynamically_callback` which
// will reach into `ModuleMap` from within the isolate.
2020-09-06 10:50:49 -04:00
resolver . reject ( scope , exception ) . unwrap ( ) ;
scope . perform_microtask_checkpoint ( ) ;
}
2021-05-19 14:53:43 -04:00
fn dynamic_import_resolve ( & mut self , id : ModuleLoadId , mod_id : ModuleId ) {
2021-07-05 12:59:49 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2020-09-06 10:50:49 -04:00
2021-05-19 14:53:43 -04:00
let resolver_handle = module_map_rc
2020-09-06 10:50:49 -04:00
. borrow_mut ( )
2021-05-19 14:53:43 -04:00
. dynamic_import_map
2020-09-06 10:50:49 -04:00
. remove ( & id )
2021-05-19 14:53:43 -04:00
. expect ( " Invalid dynamic import id " ) ;
2021-10-27 17:26:15 -04:00
let resolver = resolver_handle . open ( scope ) ;
2020-09-06 10:50:49 -04:00
let module = {
2021-05-19 14:53:43 -04:00
module_map_rc
. borrow ( )
2020-11-21 10:23:35 -05:00
. get_handle ( mod_id )
. map ( | handle | v8 ::Local ::new ( scope , handle ) )
2020-09-06 10:50:49 -04:00
. expect ( " Dyn import module info not found " )
} ;
// Resolution success
assert_eq! ( module . get_status ( ) , v8 ::ModuleStatus ::Evaluated ) ;
2021-05-19 14:53:43 -04:00
// IMPORTANT: No borrows to `ModuleMap` can be held at this point because
// resolving the promise might initiate another `import()` which will
// in turn call `bindings::host_import_module_dynamically_callback` which
// will reach into `ModuleMap` from within the isolate.
2020-09-06 10:50:49 -04:00
let module_namespace = module . get_module_namespace ( ) ;
resolver . resolve ( scope , module_namespace ) . unwrap ( ) ;
2021-07-05 12:59:49 -04:00
state_rc . borrow_mut ( ) . dyn_module_evaluate_idle_counter = 0 ;
2020-09-06 10:50:49 -04:00
scope . perform_microtask_checkpoint ( ) ;
}
fn prepare_dyn_imports (
& mut self ,
cx : & mut Context ,
2021-11-16 09:02:28 -05:00
) -> Poll < Result < ( ) , Error > > {
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2020-09-06 10:50:49 -04:00
2021-05-19 14:53:43 -04:00
if module_map_rc . borrow ( ) . preparing_dynamic_imports . is_empty ( ) {
2020-10-05 05:08:19 -04:00
return Poll ::Ready ( Ok ( ( ) ) ) ;
}
2020-09-06 10:50:49 -04:00
loop {
2021-05-19 14:53:43 -04:00
let poll_result = module_map_rc
2021-04-28 12:28:46 -04:00
. borrow_mut ( )
2021-05-19 14:53:43 -04:00
. preparing_dynamic_imports
2021-04-28 12:28:46 -04:00
. poll_next_unpin ( cx ) ;
if let Poll ::Ready ( Some ( prepare_poll ) ) = poll_result {
let dyn_import_id = prepare_poll . 0 ;
let prepare_result = prepare_poll . 1 ;
match prepare_result {
Ok ( load ) = > {
2021-05-19 14:53:43 -04:00
module_map_rc
2021-04-28 12:28:46 -04:00
. borrow_mut ( )
2021-05-19 14:53:43 -04:00
. pending_dynamic_imports
2021-04-28 12:28:46 -04:00
. push ( load . into_future ( ) ) ;
}
Err ( err ) = > {
2021-05-19 14:53:43 -04:00
self . dynamic_import_reject ( dyn_import_id , err ) ;
2020-09-06 10:50:49 -04:00
}
}
2021-04-28 12:28:46 -04:00
// Continue polling for more prepared dynamic imports.
continue ;
2020-09-06 10:50:49 -04:00
}
2021-04-28 12:28:46 -04:00
// There are no active dynamic import loads, or none are ready.
return Poll ::Ready ( Ok ( ( ) ) ) ;
2020-09-06 10:50:49 -04:00
}
}
2021-11-16 09:02:28 -05:00
fn poll_dyn_imports ( & mut self , cx : & mut Context ) -> Poll < Result < ( ) , Error > > {
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2020-10-05 05:08:19 -04:00
2021-05-19 14:53:43 -04:00
if module_map_rc . borrow ( ) . pending_dynamic_imports . is_empty ( ) {
2020-10-05 05:08:19 -04:00
return Poll ::Ready ( Ok ( ( ) ) ) ;
}
2020-09-06 10:50:49 -04:00
loop {
2021-05-19 14:53:43 -04:00
let poll_result = module_map_rc
2021-04-28 12:28:46 -04:00
. borrow_mut ( )
2021-05-19 14:53:43 -04:00
. pending_dynamic_imports
2021-04-28 12:28:46 -04:00
. poll_next_unpin ( cx ) ;
if let Poll ::Ready ( Some ( load_stream_poll ) ) = poll_result {
let maybe_result = load_stream_poll . 0 ;
let mut load = load_stream_poll . 1 ;
let dyn_import_id = load . id ;
if let Some ( load_stream_result ) = maybe_result {
match load_stream_result {
2021-12-15 13:22:36 -05:00
Ok ( ( request , info ) ) = > {
2021-04-28 12:28:46 -04:00
// A module (not necessarily the one dynamically imported) has been
// fetched. Create and register it, and if successful, poll for the
// next recursive-load event related to this dynamic import.
2021-12-15 13:22:36 -05:00
let register_result = load . register_and_recurse (
& mut self . handle_scope ( ) ,
& request ,
& info ,
) ;
2021-05-19 14:53:43 -04:00
match register_result {
2021-04-28 12:28:46 -04:00
Ok ( ( ) ) = > {
// Keep importing until it's fully drained
2021-05-19 14:53:43 -04:00
module_map_rc
2021-04-28 12:28:46 -04:00
. borrow_mut ( )
2021-05-19 14:53:43 -04:00
. pending_dynamic_imports
2021-04-28 12:28:46 -04:00
. push ( load . into_future ( ) ) ;
2020-09-06 10:50:49 -04:00
}
2021-05-19 14:53:43 -04:00
Err ( err ) = > self . dynamic_import_reject ( dyn_import_id , err ) ,
2020-09-06 10:50:49 -04:00
}
}
2021-04-28 12:28:46 -04:00
Err ( err ) = > {
// A non-javascript error occurred; this could be due to a an invalid
// module specifier, or a problem with the source map, or a failure
// to fetch the module source code.
2021-05-19 14:53:43 -04:00
self . dynamic_import_reject ( dyn_import_id , err )
2021-02-23 09:22:55 -05:00
}
2020-10-06 04:18:22 -04:00
}
2021-04-28 12:28:46 -04:00
} else {
// The top-level module from a dynamic import has been instantiated.
// Load is done.
2021-06-28 21:03:02 -04:00
let module_id =
load . root_module_id . expect ( " Root module should be loaded " ) ;
2021-05-19 14:53:43 -04:00
let result = self . instantiate_module ( module_id ) ;
2021-04-28 12:28:46 -04:00
if let Err ( err ) = result {
2021-05-19 14:53:43 -04:00
self . dynamic_import_reject ( dyn_import_id , err ) ;
2021-04-28 12:28:46 -04:00
}
2021-05-19 14:53:43 -04:00
self . dynamic_import_module_evaluate ( dyn_import_id , module_id ) ? ;
2020-10-06 04:18:22 -04:00
}
2021-04-28 12:28:46 -04:00
// Continue polling for more ready dynamic imports.
continue ;
2020-10-06 04:18:22 -04:00
}
2021-04-28 12:28:46 -04:00
// There are no active dynamic import loads, or none are ready.
return Poll ::Ready ( Ok ( ( ) ) ) ;
2020-10-06 04:18:22 -04:00
}
}
2021-06-22 11:47:09 -04:00
/// "deno_core" runs V8 with Top Level Await enabled. It means that each
/// module evaluation returns a promise from V8.
/// Feature docs: https://v8.dev/features/top-level-await
2020-10-14 08:04:09 -04:00
///
/// This promise resolves after all dependent modules have also
/// resolved. Each dependent module may perform calls to "import()" and APIs
/// using async ops will add futures to the runtime's event loop.
/// It means that the promise returned from module evaluation will
/// resolve only after all futures in the event loop are done.
///
/// Thus during turn of event loop we need to check if V8 has
/// resolved or rejected the promise. If the promise is still pending
/// then another turn of event loop must be performed.
2020-11-27 14:47:35 -05:00
fn evaluate_pending_module ( & mut self ) {
2020-10-14 08:04:09 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2021-04-28 12:28:46 -04:00
let maybe_module_evaluation =
state_rc . borrow_mut ( ) . pending_mod_evaluate . take ( ) ;
2020-10-14 08:04:09 -04:00
2021-04-28 12:28:46 -04:00
if maybe_module_evaluation . is_none ( ) {
return ;
}
2020-10-14 08:04:09 -04:00
2021-04-28 12:28:46 -04:00
let module_evaluation = maybe_module_evaluation . unwrap ( ) ;
let scope = & mut self . handle_scope ( ) ;
2021-10-27 17:26:15 -04:00
let promise = module_evaluation . promise . open ( scope ) ;
2021-04-28 12:28:46 -04:00
let promise_state = promise . state ( ) ;
match promise_state {
v8 ::PromiseState ::Pending = > {
// NOTE: `poll_event_loop` will decide if
// runtime would be woken soon
state_rc . borrow_mut ( ) . pending_mod_evaluate = Some ( module_evaluation ) ;
}
v8 ::PromiseState ::Fulfilled = > {
scope . perform_microtask_checkpoint ( ) ;
// Receiver end might have been already dropped, ignore the result
2021-07-30 07:36:43 -04:00
let _ = module_evaluation . sender . send ( Ok ( ( ) ) ) ;
2021-04-28 12:28:46 -04:00
}
v8 ::PromiseState ::Rejected = > {
let exception = promise . result ( scope ) ;
scope . perform_microtask_checkpoint ( ) ;
let err1 = exception_to_err_result ::< ( ) > ( scope , exception , false )
. map_err ( | err | attach_handle_to_error ( scope , err , exception ) )
. unwrap_err ( ) ;
// Receiver end might have been already dropped, ignore the result
2021-07-30 07:36:43 -04:00
let _ = module_evaluation . sender . send ( Err ( err1 ) ) ;
2021-04-28 12:28:46 -04:00
}
}
}
fn evaluate_dyn_imports ( & mut self ) {
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2021-07-05 12:59:49 -04:00
let mut still_pending = vec! [ ] ;
let pending =
std ::mem ::take ( & mut state_rc . borrow_mut ( ) . pending_dyn_mod_evaluate ) ;
for pending_dyn_evaluate in pending {
2021-04-28 12:28:46 -04:00
let maybe_result = {
let scope = & mut self . handle_scope ( ) ;
let module_id = pending_dyn_evaluate . module_id ;
2021-10-27 17:26:15 -04:00
let promise = pending_dyn_evaluate . promise . open ( scope ) ;
let _module = pending_dyn_evaluate . module . open ( scope ) ;
2020-10-14 08:04:09 -04:00
let promise_state = promise . state ( ) ;
match promise_state {
v8 ::PromiseState ::Pending = > {
2021-07-05 12:59:49 -04:00
still_pending . push ( pending_dyn_evaluate ) ;
2021-04-28 12:28:46 -04:00
None
2020-10-14 08:04:09 -04:00
}
v8 ::PromiseState ::Fulfilled = > {
2021-04-28 12:28:46 -04:00
Some ( Ok ( ( pending_dyn_evaluate . load_id , module_id ) ) )
2020-10-14 08:04:09 -04:00
}
v8 ::PromiseState ::Rejected = > {
let exception = promise . result ( scope ) ;
2020-10-25 23:34:00 -04:00
let err1 = exception_to_err_result ::< ( ) > ( scope , exception , false )
2020-10-14 08:04:09 -04:00
. map_err ( | err | attach_handle_to_error ( scope , err , exception ) )
. unwrap_err ( ) ;
2021-04-28 12:28:46 -04:00
Some ( Err ( ( pending_dyn_evaluate . load_id , err1 ) ) )
2020-10-14 08:04:09 -04:00
}
}
} ;
if let Some ( result ) = maybe_result {
match result {
Ok ( ( dyn_import_id , module_id ) ) = > {
2021-05-19 14:53:43 -04:00
self . dynamic_import_resolve ( dyn_import_id , module_id ) ;
2020-10-14 08:04:09 -04:00
}
Err ( ( dyn_import_id , err1 ) ) = > {
2021-05-19 14:53:43 -04:00
self . dynamic_import_reject ( dyn_import_id , err1 ) ;
2020-10-14 08:04:09 -04:00
}
}
}
}
2021-07-05 12:59:49 -04:00
state_rc . borrow_mut ( ) . pending_dyn_mod_evaluate = still_pending ;
2020-10-14 08:04:09 -04:00
}
2021-09-17 21:44:53 -04:00
/// Asynchronously load specified module and all of its dependencies.
///
/// The module will be marked as "main", and because of that
/// "import.meta.main" will return true when checked inside that module.
2020-09-06 10:50:49 -04:00
///
2020-09-06 15:44:29 -04:00
/// User must call `JsRuntime::mod_evaluate` with returned `ModuleId`
2020-09-06 10:50:49 -04:00
/// manually after load is finished.
2021-09-17 21:44:53 -04:00
pub async fn load_main_module (
2020-09-06 10:50:49 -04:00
& mut self ,
specifier : & ModuleSpecifier ,
code : Option < String > ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleId , Error > {
2021-05-19 14:53:43 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
2021-06-28 21:03:02 -04:00
if let Some ( code ) = code {
2021-12-15 13:22:36 -05:00
module_map_rc . borrow_mut ( ) . new_es_module (
2021-06-28 21:03:02 -04:00
& mut self . handle_scope ( ) ,
2021-09-17 21:44:53 -04:00
// main module
2021-06-28 21:03:02 -04:00
true ,
specifier . as_str ( ) ,
& code ,
) ? ;
}
2021-05-19 14:53:43 -04:00
2021-06-28 21:03:02 -04:00
let mut load =
ModuleMap ::load_main ( module_map_rc . clone ( ) , specifier . as_str ( ) ) . await ? ;
2020-09-06 10:50:49 -04:00
2021-12-15 13:22:36 -05:00
while let Some ( load_result ) = load . next ( ) . await {
let ( request , info ) = load_result ? ;
2021-05-19 14:53:43 -04:00
let scope = & mut self . handle_scope ( ) ;
2021-12-15 13:22:36 -05:00
load . register_and_recurse ( scope , & request , & info ) ? ;
2020-09-06 10:50:49 -04:00
}
2021-06-28 21:03:02 -04:00
let root_id = load . root_module_id . expect ( " Root module should be loaded " ) ;
2021-06-19 10:14:43 -04:00
self . instantiate_module ( root_id ) ? ;
Ok ( root_id )
2020-09-06 10:50:49 -04:00
}
2020-10-05 05:08:19 -04:00
2021-09-17 21:44:53 -04:00
/// Asynchronously load specified ES module and all of its dependencies.
///
/// This method is meant to be used when loading some utility code that
/// might be later imported by the main module (ie. an entry point module).
///
/// User must call `JsRuntime::mod_evaluate` with returned `ModuleId`
/// manually after load is finished.
pub async fn load_side_module (
& mut self ,
specifier : & ModuleSpecifier ,
code : Option < String > ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleId , Error > {
2021-09-17 21:44:53 -04:00
let module_map_rc = Self ::module_map ( self . v8_isolate ( ) ) ;
if let Some ( code ) = code {
2021-12-15 13:22:36 -05:00
module_map_rc . borrow_mut ( ) . new_es_module (
2021-09-17 21:44:53 -04:00
& mut self . handle_scope ( ) ,
// not main module
false ,
specifier . as_str ( ) ,
& code ,
) ? ;
}
let mut load =
ModuleMap ::load_side ( module_map_rc . clone ( ) , specifier . as_str ( ) ) . await ? ;
2021-12-15 13:22:36 -05:00
while let Some ( load_result ) = load . next ( ) . await {
let ( request , info ) = load_result ? ;
2021-09-17 21:44:53 -04:00
let scope = & mut self . handle_scope ( ) ;
2021-12-15 13:22:36 -05:00
load . register_and_recurse ( scope , & request , & info ) ? ;
2021-09-17 21:44:53 -04:00
}
let root_id = load . root_module_id . expect ( " Root module should be loaded " ) ;
self . instantiate_module ( root_id ) ? ;
Ok ( root_id )
}
2021-11-16 09:02:28 -05:00
fn check_promise_exceptions ( & mut self ) -> Result < ( ) , Error > {
2020-10-07 09:56:52 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2020-10-05 05:08:19 -04:00
let mut state = state_rc . borrow_mut ( ) ;
if state . pending_promise_exceptions . is_empty ( ) {
return Ok ( ( ) ) ;
}
2020-11-11 17:11:40 -05:00
let key = {
state
. pending_promise_exceptions
. keys ( )
. next ( )
. unwrap ( )
. clone ( )
} ;
2020-10-05 05:08:19 -04:00
let handle = state . pending_promise_exceptions . remove ( & key ) . unwrap ( ) ;
drop ( state ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2020-10-05 05:08:19 -04:00
let exception = v8 ::Local ::new ( scope , handle ) ;
2020-10-25 23:34:00 -04:00
exception_to_err_result ( scope , exception , true )
2020-10-05 05:08:19 -04:00
}
2021-03-31 10:37:38 -04:00
// Send finished responses to JS
2021-11-16 09:02:28 -05:00
fn resolve_async_ops ( & mut self , cx : & mut Context ) -> Result < ( ) , Error > {
2020-10-07 09:56:52 -04:00
let state_rc = Self ::state ( self . v8_isolate ( ) ) ;
2020-10-05 05:08:19 -04:00
2021-04-21 20:48:17 -04:00
let js_recv_cb_handle = state_rc . borrow ( ) . js_recv_cb . clone ( ) . unwrap ( ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2020-10-05 05:08:19 -04:00
2021-03-31 10:37:38 -04:00
// We return async responses to JS in unbounded batches (may change),
// each batch is a flat vector of tuples:
// `[promise_id1, op_result1, promise_id2, op_result2, ...]`
// promise_id is a simple integer, op_result is an ops::OpResult
// which contains a value OR an error, encoded as a tuple.
// This batch is received in JS via the special `arguments` variable
// and then each tuple is used to resolve or reject promises
2021-10-24 15:41:57 -04:00
let mut args : Vec < v8 ::Local < v8 ::Value > > = vec! [ ] ;
// Now handle actual ops.
{
let mut state = state_rc . borrow_mut ( ) ;
state . have_unpolled_ops = false ;
2021-11-25 13:49:09 -05:00
while let Poll ::Ready ( Some ( item ) ) = state . pending_ops . poll_next_unpin ( cx )
{
let ( promise_id , op_id , resp ) = item ;
state . unrefed_ops . remove ( & promise_id ) ;
2022-03-14 13:44:15 -04:00
state . op_state . borrow ( ) . tracker . track_async_completed ( op_id ) ;
2021-10-24 15:41:57 -04:00
args . push ( v8 ::Integer ::new ( scope , promise_id as i32 ) . into ( ) ) ;
args . push ( resp . to_v8 ( scope ) . unwrap ( ) ) ;
}
}
if args . is_empty ( ) {
return Ok ( ( ) ) ;
2021-02-23 07:08:50 -05:00
}
2021-04-03 08:33:01 -04:00
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
2021-10-27 17:26:15 -04:00
let js_recv_cb = js_recv_cb_handle . open ( tc_scope ) ;
2021-04-03 08:33:01 -04:00
let this = v8 ::undefined ( tc_scope ) . into ( ) ;
js_recv_cb . call ( tc_scope , this , args . as_slice ( ) ) ;
2020-10-05 05:08:19 -04:00
match tc_scope . exception ( ) {
2021-03-31 10:37:38 -04:00
None = > Ok ( ( ) ) ,
2020-10-25 23:34:00 -04:00
Some ( exception ) = > exception_to_err_result ( tc_scope , exception , false ) ,
2020-10-05 05:08:19 -04:00
}
}
2021-11-16 09:02:28 -05:00
fn drain_macrotasks ( & mut self ) -> Result < ( ) , Error > {
2021-11-16 14:23:12 -05:00
let state = Self ::state ( self . v8_isolate ( ) ) ;
if state . borrow ( ) . js_macrotask_cbs . is_empty ( ) {
return Ok ( ( ) ) ;
}
2020-10-05 05:08:19 -04:00
2021-11-16 14:23:12 -05:00
let js_macrotask_cb_handles = state . borrow ( ) . js_macrotask_cbs . clone ( ) ;
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2020-10-05 05:08:19 -04:00
2021-11-16 14:23:12 -05:00
for js_macrotask_cb_handle in js_macrotask_cb_handles {
let js_macrotask_cb = js_macrotask_cb_handle . open ( scope ) ;
2020-10-05 05:08:19 -04:00
2021-11-16 14:23:12 -05:00
// Repeatedly invoke macrotask callback until it returns true (done),
// such that ready microtasks would be automatically run before
// next macrotask is processed.
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
let this = v8 ::undefined ( tc_scope ) . into ( ) ;
loop {
let is_done = js_macrotask_cb . call ( tc_scope , this , & [ ] ) ;
if let Some ( exception ) = tc_scope . exception ( ) {
return exception_to_err_result ( tc_scope , exception , false ) ;
}
if tc_scope . has_terminated ( ) | | tc_scope . is_execution_terminating ( ) {
return Ok ( ( ) ) ;
}
2020-10-05 05:08:19 -04:00
2021-11-16 14:23:12 -05:00
let is_done = is_done . unwrap ( ) ;
if is_done . is_true ( ) {
break ;
}
2021-09-30 13:52:58 -04:00
}
2021-11-16 14:23:12 -05:00
}
Ok ( ( ) )
}
2021-09-30 13:52:58 -04:00
2021-11-16 14:23:12 -05:00
fn drain_nexttick ( & mut self ) -> Result < ( ) , Error > {
let state = Self ::state ( self . v8_isolate ( ) ) ;
if state . borrow ( ) . js_nexttick_cbs . is_empty ( ) {
return Ok ( ( ) ) ;
}
if ! state . borrow ( ) . has_tick_scheduled {
let scope = & mut self . handle_scope ( ) ;
scope . perform_microtask_checkpoint ( ) ;
}
// TODO(bartlomieju): Node also checks for absence of "rejection_to_warn"
if ! state . borrow ( ) . has_tick_scheduled {
return Ok ( ( ) ) ;
}
let js_nexttick_cb_handles = state . borrow ( ) . js_nexttick_cbs . clone ( ) ;
let scope = & mut self . handle_scope ( ) ;
for js_nexttick_cb_handle in js_nexttick_cb_handles {
let js_nexttick_cb = js_nexttick_cb_handle . open ( scope ) ;
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
let this = v8 ::undefined ( tc_scope ) . into ( ) ;
js_nexttick_cb . call ( tc_scope , this , & [ ] ) ;
if let Some ( exception ) = tc_scope . exception ( ) {
return exception_to_err_result ( tc_scope , exception , false ) ;
2020-10-05 05:08:19 -04:00
}
}
Ok ( ( ) )
}
2020-09-06 10:50:49 -04:00
}
2022-04-17 07:53:08 -04:00
/// A representation of a JavaScript realm tied to a [`JsRuntime`], that allows
/// execution in the realm's context.
///
/// A [`JsRealm`] instance does not hold ownership of its corresponding realm,
/// so they can be created and dropped as needed. And since every operation on
/// them requires passing a mutable reference to the [`JsRuntime`], multiple
/// [`JsRealm`] instances won't overlap.
///
/// # Panics
///
/// Every method of [`JsRealm`] will panic if you call if with a reference to a
/// [`JsRuntime`] other than the one that corresponds to the current context.
///
/// # Lifetime of the realm
///
/// A [`JsRealm`] instance will keep the underlying V8 context alive even if it
/// would have otherwise been garbage collected.
#[ derive(Clone) ]
pub struct JsRealm ( v8 ::Global < v8 ::Context > ) ;
impl JsRealm {
pub fn new ( context : v8 ::Global < v8 ::Context > ) -> Self {
JsRealm ( context )
}
pub fn context ( & self ) -> & v8 ::Global < v8 ::Context > {
& self . 0
}
pub fn handle_scope < ' s > (
& self ,
runtime : & ' s mut JsRuntime ,
) -> v8 ::HandleScope < ' s > {
v8 ::HandleScope ::with_context ( runtime . v8_isolate ( ) , & self . 0 )
}
pub fn global_object < ' s > (
& self ,
runtime : & ' s mut JsRuntime ,
) -> v8 ::Local < ' s , v8 ::Object > {
let scope = & mut self . handle_scope ( runtime ) ;
self . 0. open ( scope ) . global ( scope )
}
/// Executes traditional JavaScript code (traditional = not ES modules) in the
/// realm's context.
///
/// `name` can be a filepath or any other string, eg.
///
/// - "/some/file/path.js"
/// - "<anon>"
/// - "[native code]"
///
/// The same `name` value can be used for multiple executions.
///
/// `Error` can be downcast to a type that exposes additional information
/// about the V8 exception. By default this type is `JsError`, however it may
/// be a different type if `RuntimeOptions::js_error_create_fn` has been set.
pub fn execute_script (
& self ,
runtime : & mut JsRuntime ,
name : & str ,
source_code : & str ,
) -> Result < v8 ::Global < v8 ::Value > , Error > {
let scope = & mut self . handle_scope ( runtime ) ;
let source = v8 ::String ::new ( scope , source_code ) . unwrap ( ) ;
let name = v8 ::String ::new ( scope , name ) . unwrap ( ) ;
let origin = bindings ::script_origin ( scope , name ) ;
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
let script = match v8 ::Script ::compile ( tc_scope , source , Some ( & origin ) ) {
Some ( script ) = > script ,
None = > {
let exception = tc_scope . exception ( ) . unwrap ( ) ;
return exception_to_err_result ( tc_scope , exception , false ) ;
}
} ;
match script . run ( tc_scope ) {
Some ( value ) = > {
let value_handle = v8 ::Global ::new ( tc_scope , value ) ;
Ok ( value_handle )
}
None = > {
assert! ( tc_scope . has_caught ( ) ) ;
let exception = tc_scope . exception ( ) . unwrap ( ) ;
exception_to_err_result ( tc_scope , exception , false )
}
}
}
// TODO(andreubotella): `mod_evaluate`, `load_main_module`, `load_side_module`
}
2022-03-14 13:44:15 -04:00
#[ inline ]
pub fn queue_async_op (
scope : & v8 ::Isolate ,
op : impl Future < Output = ( PromiseId , OpId , OpResult ) > + 'static ,
) {
let state_rc = JsRuntime ::state ( scope ) ;
let mut state = state_rc . borrow_mut ( ) ;
state . pending_ops . push ( OpCall ::eager ( op ) ) ;
state . have_unpolled_ops = true ;
}
2019-03-11 17:57:36 -04:00
#[ cfg(test) ]
2019-03-26 11:56:34 -04:00
pub mod tests {
2019-03-11 17:57:36 -04:00
use super ::* ;
2021-05-03 11:30:41 -04:00
use crate ::error ::custom_error ;
2022-03-14 13:44:15 -04:00
use crate ::error ::AnyError ;
2021-11-22 07:51:20 -05:00
use crate ::modules ::ModuleSource ;
2020-09-06 10:50:49 -04:00
use crate ::modules ::ModuleSourceFuture ;
2021-12-15 13:22:36 -05:00
use crate ::modules ::ModuleType ;
2021-05-06 13:32:03 -04:00
use crate ::ZeroCopyBuf ;
2022-03-14 13:44:15 -04:00
use deno_ops ::op ;
2019-04-14 21:58:27 -04:00
use futures ::future ::lazy ;
use std ::ops ::FnOnce ;
2021-10-09 16:37:19 -04:00
use std ::pin ::Pin ;
2020-09-05 20:34:02 -04:00
use std ::rc ::Rc ;
2019-03-25 17:43:31 -04:00
use std ::sync ::atomic ::{ AtomicUsize , Ordering } ;
2020-05-29 17:41:39 -04:00
use std ::sync ::Arc ;
2022-03-14 13:44:15 -04:00
// deno_ops macros generate code assuming deno_core in scope.
mod deno_core {
pub use crate ::* ;
}
2019-03-14 19:17:52 -04:00
2019-11-16 19:17:47 -05:00
pub fn run_in_task < F > ( f : F )
2019-04-14 21:58:27 -04:00
where
2019-11-16 19:17:47 -05:00
F : FnOnce ( & mut Context ) + Send + 'static ,
2019-04-14 21:58:27 -04:00
{
2019-12-07 15:04:17 -05:00
futures ::executor ::block_on ( lazy ( move | cx | f ( cx ) ) ) ;
2019-04-14 21:58:27 -04:00
}
2021-12-29 09:21:42 -05:00
#[ derive(Copy, Clone) ]
2020-09-05 20:34:02 -04:00
enum Mode {
2019-12-07 15:04:17 -05:00
Async ,
2021-03-31 10:37:38 -04:00
AsyncZeroCopy ( bool ) ,
2019-03-14 19:17:52 -04:00
}
2020-09-10 09:57:45 -04:00
struct TestState {
2020-09-05 20:34:02 -04:00
mode : Mode ,
dispatch_count : Arc < AtomicUsize > ,
}
2019-10-02 13:05:48 -04:00
2022-03-14 13:44:15 -04:00
#[ op ]
async fn op_test (
rc_op_state : Rc < RefCell < OpState > > ,
control : u8 ,
buf : Option < ZeroCopyBuf > ,
) -> Result < u8 , AnyError > {
let op_state_ = rc_op_state . borrow ( ) ;
2020-09-10 09:57:45 -04:00
let test_state = op_state_ . borrow ::< TestState > ( ) ;
test_state . dispatch_count . fetch_add ( 1 , Ordering ::Relaxed ) ;
match test_state . mode {
Mode ::Async = > {
2021-03-31 10:37:38 -04:00
assert_eq! ( control , 42 ) ;
2022-03-14 13:44:15 -04:00
Ok ( 43 )
2020-09-05 20:34:02 -04:00
}
2021-03-31 10:37:38 -04:00
Mode ::AsyncZeroCopy ( has_buffer ) = > {
assert_eq! ( buf . is_some ( ) , has_buffer ) ;
if let Some ( buf ) = buf {
2020-09-10 09:57:45 -04:00
assert_eq! ( buf . len ( ) , 1 ) ;
2021-03-31 10:37:38 -04:00
}
2022-03-14 13:44:15 -04:00
Ok ( 43 )
2020-04-19 23:54:46 -04:00
}
2020-09-05 20:34:02 -04:00
}
}
2019-10-02 13:05:48 -04:00
2020-09-06 15:44:29 -04:00
fn setup ( mode : Mode ) -> ( JsRuntime , Arc < AtomicUsize > ) {
2020-09-05 20:34:02 -04:00
let dispatch_count = Arc ::new ( AtomicUsize ::new ( 0 ) ) ;
2021-12-29 09:21:42 -05:00
let dispatch_count2 = dispatch_count . clone ( ) ;
let ext = Extension ::builder ( )
2022-03-14 13:44:15 -04:00
. ops ( vec! [ op_test ::decl ( ) ] )
2021-12-29 09:21:42 -05:00
. state ( move | state | {
state . put ( TestState {
mode ,
dispatch_count : dispatch_count2 . clone ( ) ,
} ) ;
Ok ( ( ) )
} )
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ ext ] ,
.. Default ::default ( )
2020-09-05 20:34:02 -04:00
} ) ;
2020-09-10 09:57:45 -04:00
2020-09-22 17:30:03 -04:00
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-09-22 17:30:03 -04:00
" setup.js " ,
r #"
2019-04-23 18:58:00 -04:00
function assert ( cond ) {
if ( ! cond ) {
throw Error ( " assert " ) ;
}
2019-03-14 19:17:52 -04:00
}
2019-04-23 18:58:00 -04:00
" #,
2020-09-22 17:30:03 -04:00
)
. unwrap ( ) ;
2019-04-23 18:58:00 -04:00
assert_eq! ( dispatch_count . load ( Ordering ::Relaxed ) , 0 ) ;
2020-09-06 15:44:29 -04:00
( runtime , dispatch_count )
2019-03-14 19:17:52 -04:00
}
2019-03-11 17:57:36 -04:00
#[ test ]
2019-03-30 14:45:36 -04:00
fn test_dispatch ( ) {
2020-09-06 15:44:29 -04:00
let ( mut runtime , dispatch_count ) = setup ( Mode ::Async ) ;
2020-09-22 17:30:03 -04:00
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-09-22 17:30:03 -04:00
" filename.js " ,
r #"
2021-03-31 10:37:38 -04:00
let control = 42 ;
2021-04-30 21:08:29 -04:00
Deno . core . opAsync ( " op_test " , control ) ;
2019-03-11 17:57:36 -04:00
async function main ( ) {
2021-04-30 21:08:29 -04:00
Deno . core . opAsync ( " op_test " , control ) ;
2019-03-11 17:57:36 -04:00
}
main ( ) ;
" #,
2020-09-22 17:30:03 -04:00
)
. unwrap ( ) ;
2019-04-23 18:58:00 -04:00
assert_eq! ( dispatch_count . load ( Ordering ::Relaxed ) , 2 ) ;
2019-03-11 17:57:36 -04:00
}
2020-06-01 14:20:47 -04:00
2021-11-25 13:49:09 -05:00
#[ test ]
fn test_op_async_promise_id ( ) {
let ( mut runtime , _dispatch_count ) = setup ( Mode ::Async ) ;
runtime
. execute_script (
" filename.js " ,
r #"
const p = Deno . core . opAsync ( " op_test " , 42 ) ;
if ( p [ Symbol . for ( " Deno.core.internalPromiseId " ) ] = = undefined ) {
throw new Error ( " missing id on returned promise " ) ;
}
" #,
)
. unwrap ( ) ;
}
#[ test ]
fn test_ref_unref_ops ( ) {
let ( mut runtime , _dispatch_count ) = setup ( Mode ::Async ) ;
runtime
. execute_script (
" filename.js " ,
r #"
var promiseIdSymbol = Symbol . for ( " Deno.core.internalPromiseId " ) ;
var p1 = Deno . core . opAsync ( " op_test " , 42 ) ;
var p2 = Deno . core . opAsync ( " op_test " , 42 ) ;
" #,
)
. unwrap ( ) ;
{
let isolate = runtime . v8_isolate ( ) ;
let state_rc = JsRuntime ::state ( isolate ) ;
let state = state_rc . borrow ( ) ;
assert_eq! ( state . pending_ops . len ( ) , 2 ) ;
assert_eq! ( state . unrefed_ops . len ( ) , 0 ) ;
}
runtime
. execute_script (
" filename.js " ,
r #"
Deno . core . unrefOp ( p1 [ promiseIdSymbol ] ) ;
Deno . core . unrefOp ( p2 [ promiseIdSymbol ] ) ;
" #,
)
. unwrap ( ) ;
{
let isolate = runtime . v8_isolate ( ) ;
let state_rc = JsRuntime ::state ( isolate ) ;
let state = state_rc . borrow ( ) ;
assert_eq! ( state . pending_ops . len ( ) , 2 ) ;
assert_eq! ( state . unrefed_ops . len ( ) , 2 ) ;
}
runtime
. execute_script (
" filename.js " ,
r #"
Deno . core . refOp ( p1 [ promiseIdSymbol ] ) ;
Deno . core . refOp ( p2 [ promiseIdSymbol ] ) ;
" #,
)
. unwrap ( ) ;
{
let isolate = runtime . v8_isolate ( ) ;
let state_rc = JsRuntime ::state ( isolate ) ;
let state = state_rc . borrow ( ) ;
assert_eq! ( state . pending_ops . len ( ) , 2 ) ;
assert_eq! ( state . unrefed_ops . len ( ) , 0 ) ;
}
}
2020-06-01 14:20:47 -04:00
#[ test ]
fn test_dispatch_no_zero_copy_buf ( ) {
2021-03-31 10:37:38 -04:00
let ( mut runtime , dispatch_count ) = setup ( Mode ::AsyncZeroCopy ( false ) ) ;
2020-09-22 17:30:03 -04:00
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-09-22 17:30:03 -04:00
" filename.js " ,
r #"
2021-04-30 21:08:29 -04:00
Deno . core . opAsync ( " op_test " ) ;
2020-06-01 14:20:47 -04:00
" #,
2020-09-22 17:30:03 -04:00
)
. unwrap ( ) ;
2020-06-01 14:20:47 -04:00
assert_eq! ( dispatch_count . load ( Ordering ::Relaxed ) , 1 ) ;
}
#[ test ]
2020-07-08 11:23:50 -04:00
fn test_dispatch_stack_zero_copy_bufs ( ) {
2021-03-31 10:37:38 -04:00
let ( mut runtime , dispatch_count ) = setup ( Mode ::AsyncZeroCopy ( true ) ) ;
2020-09-22 17:30:03 -04:00
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-09-22 17:30:03 -04:00
" filename.js " ,
r #"
2020-07-08 11:23:50 -04:00
let zero_copy_a = new Uint8Array ( [ 0 ] ) ;
2021-04-30 21:08:29 -04:00
Deno . core . opAsync ( " op_test " , null , zero_copy_a ) ;
2020-06-01 14:20:47 -04:00
" #,
2020-09-22 17:30:03 -04:00
)
. unwrap ( ) ;
2020-06-01 14:20:47 -04:00
assert_eq! ( dispatch_count . load ( Ordering ::Relaxed ) , 1 ) ;
}
2021-07-08 12:56:53 -04:00
#[ test ]
fn test_execute_script_return_value ( ) {
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
let value_global = runtime . execute_script ( " a.js " , " a = 1 + 2 " ) . unwrap ( ) ;
{
let scope = & mut runtime . handle_scope ( ) ;
2021-10-27 17:26:15 -04:00
let value = value_global . open ( scope ) ;
2021-07-08 12:56:53 -04:00
assert_eq! ( value . integer_value ( scope ) . unwrap ( ) , 3 ) ;
}
let value_global = runtime . execute_script ( " b.js " , " b = 'foobar' " ) . unwrap ( ) ;
{
let scope = & mut runtime . handle_scope ( ) ;
2021-10-27 17:26:15 -04:00
let value = value_global . open ( scope ) ;
2021-07-08 12:56:53 -04:00
assert! ( value . is_string ( ) ) ;
assert_eq! (
value . to_string ( scope ) . unwrap ( ) . to_rust_string_lossy ( scope ) ,
" foobar "
) ;
}
}
2022-01-24 11:59:41 -05:00
#[ tokio::test ]
async fn test_poll_value ( ) {
run_in_task ( | cx | {
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
let value_global = runtime
. execute_script ( " a.js " , " Promise.resolve(1 + 2) " )
. unwrap ( ) ;
let v = runtime . poll_value ( & value_global , cx ) ;
{
let scope = & mut runtime . handle_scope ( ) ;
assert! (
matches! ( v , Poll ::Ready ( Ok ( v ) ) if v . open ( scope ) . integer_value ( scope ) . unwrap ( ) = = 3 )
) ;
}
let value_global = runtime
. execute_script (
" a.js " ,
" Promise.resolve(new Promise(resolve => resolve(2 + 2))) " ,
)
. unwrap ( ) ;
let v = runtime . poll_value ( & value_global , cx ) ;
{
let scope = & mut runtime . handle_scope ( ) ;
assert! (
matches! ( v , Poll ::Ready ( Ok ( v ) ) if v . open ( scope ) . integer_value ( scope ) . unwrap ( ) = = 4 )
) ;
}
let value_global = runtime
. execute_script ( " a.js " , " Promise.reject(new Error('fail')) " )
. unwrap ( ) ;
let v = runtime . poll_value ( & value_global , cx ) ;
assert! (
2022-04-13 10:41:39 -04:00
matches! ( v , Poll ::Ready ( Err ( e ) ) if e . downcast_ref ::< JsError > ( ) . unwrap ( ) . exception_message = = " Uncaught Error: fail " )
2022-01-24 11:59:41 -05:00
) ;
let value_global = runtime
. execute_script ( " a.js " , " new Promise(resolve => {}) " )
. unwrap ( ) ;
let v = runtime . poll_value ( & value_global , cx ) ;
matches! ( v , Poll ::Ready ( Err ( e ) ) if e . to_string ( ) = = " Promise resolution is still pending but the event loop has already resolved. " ) ;
} ) ;
}
2021-09-04 14:19:26 -04:00
#[ tokio::test ]
async fn test_resolve_value ( ) {
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
let value_global = runtime
. execute_script ( " a.js " , " Promise.resolve(1 + 2) " )
. unwrap ( ) ;
let result_global = runtime . resolve_value ( value_global ) . await . unwrap ( ) ;
{
let scope = & mut runtime . handle_scope ( ) ;
2021-10-27 17:26:15 -04:00
let value = result_global . open ( scope ) ;
2021-09-04 14:19:26 -04:00
assert_eq! ( value . integer_value ( scope ) . unwrap ( ) , 3 ) ;
}
let value_global = runtime
. execute_script (
" a.js " ,
" Promise.resolve(new Promise(resolve => resolve(2 + 2))) " ,
)
. unwrap ( ) ;
let result_global = runtime . resolve_value ( value_global ) . await . unwrap ( ) ;
{
let scope = & mut runtime . handle_scope ( ) ;
2021-10-27 17:26:15 -04:00
let value = result_global . open ( scope ) ;
2021-09-04 14:19:26 -04:00
assert_eq! ( value . integer_value ( scope ) . unwrap ( ) , 4 ) ;
}
let value_global = runtime
. execute_script ( " a.js " , " Promise.reject(new Error('fail')) " )
. unwrap ( ) ;
let err = runtime . resolve_value ( value_global ) . await . unwrap_err ( ) ;
assert_eq! (
" Uncaught Error: fail " ,
2022-04-13 10:41:39 -04:00
err . downcast ::< JsError > ( ) . unwrap ( ) . exception_message
2021-09-04 14:19:26 -04:00
) ;
let value_global = runtime
. execute_script ( " a.js " , " new Promise(resolve => {}) " )
. unwrap ( ) ;
let error_string = runtime
. resolve_value ( value_global )
. await
. unwrap_err ( )
. to_string ( ) ;
assert_eq! (
" Promise resolution is still pending but the event loop has already resolved. " ,
error_string ,
) ;
}
2019-03-21 09:48:19 -04:00
#[ test ]
fn terminate_execution ( ) {
2019-12-07 15:04:17 -05:00
let ( mut isolate , _dispatch_count ) = setup ( Mode ::Async ) ;
2020-10-05 05:08:19 -04:00
let v8_isolate_handle = isolate . v8_isolate ( ) . thread_safe_handle ( ) ;
2019-03-21 09:48:19 -04:00
2020-02-24 18:53:29 -05:00
let terminator_thread = std ::thread ::spawn ( move | | {
2019-03-21 09:48:19 -04:00
// allow deno to boot and run
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 100 ) ) ;
// terminate execution
2020-02-24 18:53:29 -05:00
let ok = v8_isolate_handle . terminate_execution ( ) ;
2020-02-27 19:27:24 -05:00
assert! ( ok ) ;
2019-03-21 09:48:19 -04:00
} ) ;
2020-02-24 18:53:29 -05:00
// Rn an infinite loop, which should be terminated.
2021-06-21 19:45:41 -04:00
match isolate . execute_script ( " infinite_loop.js " , " for(;;) {} " ) {
2020-02-24 18:53:29 -05:00
Ok ( _ ) = > panic! ( " execution should be terminated " ) ,
Err ( e ) = > {
2020-10-31 14:57:19 -04:00
assert_eq! ( e . to_string ( ) , " Uncaught Error: execution terminated " )
2020-02-24 18:53:29 -05:00
}
} ;
2019-03-21 09:48:19 -04:00
2020-02-24 18:53:29 -05:00
// Cancel the execution-terminating exception in order to allow script
// execution again.
2020-12-28 10:36:44 -05:00
let ok = isolate . v8_isolate ( ) . cancel_terminate_execution ( ) ;
2020-02-24 18:53:29 -05:00
assert! ( ok ) ;
// Verify that the isolate usable again.
isolate
2021-06-21 19:45:41 -04:00
. execute_script ( " simple.js " , " 1 + 1 " )
2020-02-24 18:53:29 -05:00
. expect ( " execution should be possible again " ) ;
2019-03-21 09:48:19 -04:00
2020-02-24 18:53:29 -05:00
terminator_thread . join ( ) . unwrap ( ) ;
2019-03-21 09:48:19 -04:00
}
#[ test ]
fn dangling_shared_isolate ( ) {
2020-02-24 18:53:29 -05:00
let v8_isolate_handle = {
2019-03-21 09:48:19 -04:00
// isolate is dropped at the end of this block
2020-09-06 15:44:29 -04:00
let ( mut runtime , _dispatch_count ) = setup ( Mode ::Async ) ;
2020-10-05 05:08:19 -04:00
runtime . v8_isolate ( ) . thread_safe_handle ( )
2019-03-21 09:48:19 -04:00
} ;
// this should not SEGFAULT
2020-02-24 18:53:29 -05:00
v8_isolate_handle . terminate_execution ( ) ;
2019-03-21 09:48:19 -04:00
}
2020-04-16 06:58:17 -04:00
#[ test ]
fn syntax_error ( ) {
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
2020-04-16 06:58:17 -04:00
let src = " hocuspocus( " ;
2021-06-21 19:45:41 -04:00
let r = runtime . execute_script ( " i.js " , src ) ;
2020-04-16 06:58:17 -04:00
let e = r . unwrap_err ( ) ;
2020-09-06 15:44:29 -04:00
let js_error = e . downcast ::< JsError > ( ) . unwrap ( ) ;
2022-04-15 10:08:09 -04:00
let frame = js_error . frames . first ( ) . unwrap ( ) ;
assert_eq! ( frame . column_number , Some ( 12 ) ) ;
2020-04-16 06:58:17 -04:00
}
2020-03-15 10:31:55 -04:00
#[ test ]
fn test_encode_decode ( ) {
2021-12-04 08:19:06 -05:00
run_in_task ( | cx | {
2020-09-06 15:44:29 -04:00
let ( mut runtime , _dispatch_count ) = setup ( Mode ::Async ) ;
2020-09-22 17:30:03 -04:00
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-09-22 17:30:03 -04:00
" encode_decode_test.js " ,
include_str! ( " encode_decode_test.js " ) ,
)
. unwrap ( ) ;
2021-12-04 08:19:06 -05:00
if let Poll ::Ready ( Err ( _ ) ) = runtime . poll_event_loop ( cx , false ) {
2020-03-15 10:31:55 -04:00
unreachable! ( ) ;
}
} ) ;
}
2021-02-16 08:20:21 -05:00
#[ test ]
fn test_serialize_deserialize ( ) {
2021-12-04 08:19:06 -05:00
run_in_task ( | cx | {
2021-02-16 08:20:21 -05:00
let ( mut runtime , _dispatch_count ) = setup ( Mode ::Async ) ;
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2021-02-16 08:20:21 -05:00
" serialize_deserialize_test.js " ,
include_str! ( " serialize_deserialize_test.js " ) ,
)
. unwrap ( ) ;
2021-12-04 08:19:06 -05:00
if let Poll ::Ready ( Err ( _ ) ) = runtime . poll_event_loop ( cx , false ) {
2021-02-16 08:20:21 -05:00
unreachable! ( ) ;
}
} ) ;
}
2021-05-03 11:30:41 -04:00
#[ test ]
fn test_error_builder ( ) {
2022-03-14 13:44:15 -04:00
#[ op ]
2022-03-15 19:33:46 -04:00
fn op_err ( ) -> Result < ( ) , Error > {
2021-05-03 11:30:41 -04:00
Err ( custom_error ( " DOMExceptionOperationError " , " abc " ) )
}
2021-11-16 09:02:28 -05:00
pub fn get_error_class_name ( _ : & Error ) -> & 'static str {
2021-05-03 11:30:41 -04:00
" DOMExceptionOperationError "
}
2021-12-04 08:19:06 -05:00
run_in_task ( | cx | {
2022-03-14 13:44:15 -04:00
let ext = Extension ::builder ( ) . ops ( vec! [ op_err ::decl ( ) ] ) . build ( ) ;
2021-05-03 11:30:41 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
2021-12-29 09:21:42 -05:00
extensions : vec ! [ ext ] ,
2021-05-03 11:30:41 -04:00
get_error_class_fn : Some ( & get_error_class_name ) ,
.. Default ::default ( )
} ) ;
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2021-05-03 11:30:41 -04:00
" error_builder_test.js " ,
include_str! ( " error_builder_test.js " ) ,
)
. unwrap ( ) ;
2021-12-04 08:19:06 -05:00
if let Poll ::Ready ( Err ( _ ) ) = runtime . poll_event_loop ( cx , false ) {
2021-05-03 11:30:41 -04:00
unreachable! ( ) ;
}
} ) ;
}
2019-04-24 21:43:06 -04:00
#[ test ]
fn will_snapshot ( ) {
let snapshot = {
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
will_snapshot : true ,
.. Default ::default ( )
} ) ;
2021-06-21 19:45:41 -04:00
runtime . execute_script ( " a.js " , " a = 1 + 2 " ) . unwrap ( ) ;
2020-09-06 15:44:29 -04:00
runtime . snapshot ( )
2019-04-24 21:43:06 -04:00
} ;
2020-09-11 09:18:49 -04:00
let snapshot = Snapshot ::JustCreated ( snapshot ) ;
let mut runtime2 = JsRuntime ::new ( RuntimeOptions {
startup_snapshot : Some ( snapshot ) ,
.. Default ::default ( )
} ) ;
2020-09-22 17:30:03 -04:00
runtime2
2021-06-21 19:45:41 -04:00
. execute_script ( " check.js " , " if (a != 3) throw Error('x') " )
2020-09-22 17:30:03 -04:00
. unwrap ( ) ;
2019-04-24 21:43:06 -04:00
}
2020-05-09 21:00:40 -04:00
#[ test ]
fn test_from_boxed_snapshot ( ) {
let snapshot = {
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
will_snapshot : true ,
.. Default ::default ( )
} ) ;
2021-06-21 19:45:41 -04:00
runtime . execute_script ( " a.js " , " a = 1 + 2 " ) . unwrap ( ) ;
2020-09-06 15:44:29 -04:00
let snap : & [ u8 ] = & * runtime . snapshot ( ) ;
2020-05-09 21:00:40 -04:00
Vec ::from ( snap ) . into_boxed_slice ( )
} ;
2020-09-11 09:18:49 -04:00
let snapshot = Snapshot ::Boxed ( snapshot ) ;
let mut runtime2 = JsRuntime ::new ( RuntimeOptions {
startup_snapshot : Some ( snapshot ) ,
.. Default ::default ( )
} ) ;
2020-09-22 17:30:03 -04:00
runtime2
2021-06-21 19:45:41 -04:00
. execute_script ( " check.js " , " if (a != 3) throw Error('x') " )
2020-09-22 17:30:03 -04:00
. unwrap ( ) ;
2020-05-09 21:00:40 -04:00
}
2020-08-11 21:07:14 -04:00
2022-03-22 09:32:32 -04:00
#[ test ]
fn test_get_module_namespace ( ) {
#[ derive(Default) ]
struct ModsLoader ;
impl ModuleLoader for ModsLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
_is_main : bool ,
) -> Result < ModuleSpecifier , Error > {
assert_eq! ( specifier , " file:///main.js " ) ;
assert_eq! ( referrer , " . " ) ;
let s = crate ::resolve_import ( specifier , referrer ) . unwrap ( ) ;
Ok ( s )
}
fn load (
& self ,
_module_specifier : & ModuleSpecifier ,
_maybe_referrer : Option < ModuleSpecifier > ,
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
async { Err ( generic_error ( " Module loading is not supported " ) ) }
. boxed_local ( )
}
}
let loader = std ::rc ::Rc ::new ( ModsLoader ::default ( ) ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
let specifier = crate ::resolve_url ( " file:///main.js " ) . unwrap ( ) ;
let source_code = r #"
export const a = " b " ;
export default 1 + 2 ;
" #
. to_string ( ) ;
let module_id = futures ::executor ::block_on (
runtime . load_main_module ( & specifier , Some ( source_code ) ) ,
)
. unwrap ( ) ;
let _ = runtime . mod_evaluate ( module_id ) ;
let module_namespace = runtime . get_module_namespace ( module_id ) . unwrap ( ) ;
let scope = & mut runtime . handle_scope ( ) ;
let module_namespace =
v8 ::Local ::< v8 ::Object > ::new ( scope , module_namespace ) ;
assert! ( module_namespace . is_module_namespace_object ( ) ) ;
let unknown_export_name = v8 ::String ::new ( scope , " none " ) . unwrap ( ) ;
let binding = module_namespace . get ( scope , unknown_export_name . into ( ) ) ;
assert! ( binding . is_some ( ) ) ;
assert! ( binding . unwrap ( ) . is_undefined ( ) ) ;
let empty_export_name = v8 ::String ::new ( scope , " " ) . unwrap ( ) ;
let binding = module_namespace . get ( scope , empty_export_name . into ( ) ) ;
assert! ( binding . is_some ( ) ) ;
assert! ( binding . unwrap ( ) . is_undefined ( ) ) ;
let a_export_name = v8 ::String ::new ( scope , " a " ) . unwrap ( ) ;
let binding = module_namespace . get ( scope , a_export_name . into ( ) ) ;
assert! ( binding . unwrap ( ) . is_string ( ) ) ;
assert_eq! ( binding . unwrap ( ) , v8 ::String ::new ( scope , " b " ) . unwrap ( ) ) ;
let default_export_name = v8 ::String ::new ( scope , " default " ) . unwrap ( ) ;
let binding = module_namespace . get ( scope , default_export_name . into ( ) ) ;
assert! ( binding . unwrap ( ) . is_number ( ) ) ;
assert_eq! ( binding . unwrap ( ) , v8 ::Number ::new ( scope , 3_ f64 ) ) ;
}
2020-08-11 21:07:14 -04:00
#[ test ]
fn test_heap_limits ( ) {
2021-07-02 06:18:30 -04:00
let create_params =
v8 ::Isolate ::create_params ( ) . heap_limits ( 0 , 3 * 1024 * 1024 ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
2020-10-17 05:56:15 -04:00
create_params : Some ( create_params ) ,
2020-09-11 09:18:49 -04:00
.. Default ::default ( )
} ) ;
2020-10-07 09:56:52 -04:00
let cb_handle = runtime . v8_isolate ( ) . thread_safe_handle ( ) ;
2020-08-11 21:07:14 -04:00
2022-03-14 13:44:15 -04:00
let callback_invoke_count = Rc ::new ( AtomicUsize ::new ( 0 ) ) ;
2020-08-11 21:07:14 -04:00
let inner_invoke_count = Rc ::clone ( & callback_invoke_count ) ;
2020-09-06 15:44:29 -04:00
runtime . add_near_heap_limit_callback (
2020-08-11 21:07:14 -04:00
move | current_limit , _initial_limit | {
inner_invoke_count . fetch_add ( 1 , Ordering ::SeqCst ) ;
cb_handle . terminate_execution ( ) ;
current_limit * 2
} ,
) ;
2020-09-06 15:44:29 -04:00
let err = runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-08-11 21:07:14 -04:00
" script name " ,
r # "let s = ""; while(true) { s += "Hello"; }"# ,
)
. expect_err ( " script should fail " ) ;
assert_eq! (
" Uncaught Error: execution terminated " ,
2022-04-13 10:41:39 -04:00
err . downcast ::< JsError > ( ) . unwrap ( ) . exception_message
2020-08-11 21:07:14 -04:00
) ;
assert! ( callback_invoke_count . load ( Ordering ::SeqCst ) > 0 )
}
#[ test ]
fn test_heap_limit_cb_remove ( ) {
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
2020-08-11 21:07:14 -04:00
2020-09-06 15:44:29 -04:00
runtime . add_near_heap_limit_callback ( | current_limit , _initial_limit | {
2020-08-11 21:07:14 -04:00
current_limit * 2
} ) ;
2021-07-02 06:18:30 -04:00
runtime . remove_near_heap_limit_callback ( 3 * 1024 * 1024 ) ;
2020-09-06 15:44:29 -04:00
assert! ( runtime . allocations . near_heap_limit_callback_data . is_none ( ) ) ;
2020-08-11 21:07:14 -04:00
}
2020-08-12 00:08:50 -04:00
#[ test ]
fn test_heap_limit_cb_multiple ( ) {
2021-07-02 06:18:30 -04:00
let create_params =
v8 ::Isolate ::create_params ( ) . heap_limits ( 0 , 3 * 1024 * 1024 ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
2020-10-17 05:56:15 -04:00
create_params : Some ( create_params ) ,
2020-09-11 09:18:49 -04:00
.. Default ::default ( )
} ) ;
2020-10-07 09:56:52 -04:00
let cb_handle = runtime . v8_isolate ( ) . thread_safe_handle ( ) ;
2020-08-12 00:08:50 -04:00
2022-03-14 13:44:15 -04:00
let callback_invoke_count_first = Rc ::new ( AtomicUsize ::new ( 0 ) ) ;
2020-08-12 00:08:50 -04:00
let inner_invoke_count_first = Rc ::clone ( & callback_invoke_count_first ) ;
2020-09-06 15:44:29 -04:00
runtime . add_near_heap_limit_callback (
2020-08-12 00:08:50 -04:00
move | current_limit , _initial_limit | {
inner_invoke_count_first . fetch_add ( 1 , Ordering ::SeqCst ) ;
current_limit * 2
} ,
) ;
2022-03-14 13:44:15 -04:00
let callback_invoke_count_second = Rc ::new ( AtomicUsize ::new ( 0 ) ) ;
2020-08-12 00:08:50 -04:00
let inner_invoke_count_second = Rc ::clone ( & callback_invoke_count_second ) ;
2020-09-06 15:44:29 -04:00
runtime . add_near_heap_limit_callback (
2020-08-12 00:08:50 -04:00
move | current_limit , _initial_limit | {
inner_invoke_count_second . fetch_add ( 1 , Ordering ::SeqCst ) ;
cb_handle . terminate_execution ( ) ;
current_limit * 2
} ,
) ;
2020-09-06 15:44:29 -04:00
let err = runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-08-12 00:08:50 -04:00
" script name " ,
r # "let s = ""; while(true) { s += "Hello"; }"# ,
)
. expect_err ( " script should fail " ) ;
assert_eq! (
" Uncaught Error: execution terminated " ,
2022-04-13 10:41:39 -04:00
err . downcast ::< JsError > ( ) . unwrap ( ) . exception_message
2020-08-12 00:08:50 -04:00
) ;
assert_eq! ( 0 , callback_invoke_count_first . load ( Ordering ::SeqCst ) ) ;
assert! ( callback_invoke_count_second . load ( Ordering ::SeqCst ) > 0 ) ;
}
2020-09-06 10:50:49 -04:00
#[ test ]
fn es_snapshot ( ) {
#[ derive(Default) ]
struct ModsLoader ;
impl ModuleLoader for ModsLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
_is_main : bool ,
2021-11-16 09:02:28 -05:00
) -> Result < ModuleSpecifier , Error > {
2020-09-06 10:50:49 -04:00
assert_eq! ( specifier , " file:///main.js " ) ;
assert_eq! ( referrer , " . " ) ;
2021-02-17 13:47:18 -05:00
let s = crate ::resolve_import ( specifier , referrer ) . unwrap ( ) ;
2020-09-06 10:50:49 -04:00
Ok ( s )
}
fn load (
& self ,
_module_specifier : & ModuleSpecifier ,
_maybe_referrer : Option < ModuleSpecifier > ,
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
unreachable! ( )
}
}
let loader = std ::rc ::Rc ::new ( ModsLoader ::default ( ) ) ;
2020-09-11 09:18:49 -04:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
will_snapshot : true ,
.. Default ::default ( )
} ) ;
2020-09-06 10:50:49 -04:00
2021-02-17 13:47:18 -05:00
let specifier = crate ::resolve_url ( " file:///main.js " ) . unwrap ( ) ;
2020-09-06 10:50:49 -04:00
let source_code = " Deno.core.print('hello \\ n') " . to_string ( ) ;
let module_id = futures ::executor ::block_on (
2021-09-17 21:44:53 -04:00
runtime . load_main_module ( & specifier , Some ( source_code ) ) ,
2020-09-06 10:50:49 -04:00
)
. unwrap ( ) ;
2021-07-30 07:36:43 -04:00
let _ = runtime . mod_evaluate ( module_id ) ;
2021-05-26 15:07:12 -04:00
futures ::executor ::block_on ( runtime . run_event_loop ( false ) ) . unwrap ( ) ;
2020-09-06 10:50:49 -04:00
2020-09-06 15:44:29 -04:00
let _snapshot = runtime . snapshot ( ) ;
2020-09-06 10:50:49 -04:00
}
2020-09-22 17:30:03 -04:00
#[ test ]
fn test_error_without_stack ( ) {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
// SyntaxError
2021-06-21 19:45:41 -04:00
let result = runtime . execute_script (
2020-09-22 17:30:03 -04:00
" error_without_stack.js " ,
r #"
function main ( ) {
console . log ( " asdf);
}
main ( ) ;
" #,
) ;
let expected_error = r #" Uncaught SyntaxError: Invalid or unexpected token
2022-04-15 10:08:09 -04:00
at error_without_stack . js :3 :15 " #;
2020-09-22 17:30:03 -04:00
assert_eq! ( result . unwrap_err ( ) . to_string ( ) , expected_error ) ;
}
#[ test ]
fn test_error_stack ( ) {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
2021-06-21 19:45:41 -04:00
let result = runtime . execute_script (
2020-09-22 17:30:03 -04:00
" error_stack.js " ,
r #"
function assert ( cond ) {
if ( ! cond ) {
throw Error ( " assert " ) ;
}
}
function main ( ) {
assert ( false ) ;
}
main ( ) ;
" #,
) ;
let expected_error = r #" Error: assert
at assert ( error_stack . js :4 :11 )
at main ( error_stack . js :9 :3 )
2020-10-31 14:57:19 -04:00
at error_stack . js :12 :1 " #;
2020-09-22 17:30:03 -04:00
assert_eq! ( result . unwrap_err ( ) . to_string ( ) , expected_error ) ;
}
#[ test ]
fn test_error_async_stack ( ) {
run_in_task ( | cx | {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-09-22 17:30:03 -04:00
" error_async_stack.js " ,
r #"
( async ( ) = > {
const p = ( async ( ) = > {
await Promise . resolve ( ) . then ( ( ) = > {
throw new Error ( " async " ) ;
} ) ;
} ) ( ) ;
try {
await p ;
} catch ( error ) {
console . log ( error . stack ) ;
throw error ;
}
} ) ( ) ; " #,
)
. unwrap ( ) ;
let expected_error = r #" Error: async
at error_async_stack . js :5 :13
at async error_async_stack . js :4 :5
2020-10-31 14:57:19 -04:00
at async error_async_stack . js :10 :5 " #;
2020-09-22 17:30:03 -04:00
2021-05-26 15:07:12 -04:00
match runtime . poll_event_loop ( cx , false ) {
2020-09-22 17:30:03 -04:00
Poll ::Ready ( Err ( e ) ) = > {
assert_eq! ( e . to_string ( ) , expected_error ) ;
}
_ = > panic! ( ) ,
} ;
} )
}
2020-12-07 18:36:15 -05:00
2021-07-02 04:46:37 -04:00
#[ test ]
fn test_pump_message_loop ( ) {
run_in_task ( | cx | {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
runtime
. execute_script (
" pump_message_loop.js " ,
r #"
function assertEquals ( a , b ) {
if ( a = = = b ) return ;
throw a + " does not equal " + b ;
}
const sab = new SharedArrayBuffer ( 16 ) ;
const i32a = new Int32Array ( sab ) ;
globalThis . resolved = false ;
( function ( ) {
const result = Atomics . waitAsync ( i32a , 0 , 0 ) ;
result . value . then (
( value ) = > { assertEquals ( " ok " , value ) ; globalThis . resolved = true ; } ,
( ) = > { assertUnreachable ( ) ;
} ) ;
} ) ( ) ;
const notify_return_value = Atomics . notify ( i32a , 0 , 1 ) ;
assertEquals ( 1 , notify_return_value ) ;
" #,
)
. unwrap ( ) ;
match runtime . poll_event_loop ( cx , false ) {
Poll ::Ready ( Ok ( ( ) ) ) = > { }
_ = > panic! ( ) ,
} ;
// noop script, will resolve promise from first script
runtime
. execute_script ( " pump_message_loop2.js " , r # "assertEquals(1, 1);"# )
. unwrap ( ) ;
// check that promise from `Atomics.waitAsync` has been resolved
runtime
. execute_script (
" pump_message_loop3.js " ,
r # "assertEquals(globalThis.resolved, true);"# ,
)
. unwrap ( ) ;
} )
}
2020-12-07 18:36:15 -05:00
#[ test ]
fn test_core_js_stack_frame ( ) {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
// Call non-existent op so we get error from `core.js`
let error = runtime
2021-06-21 19:45:41 -04:00
. execute_script (
2020-12-07 18:36:15 -05:00
" core_js_stack_frame.js " ,
2021-04-23 11:50:45 -04:00
" Deno.core.opSync('non_existent'); " ,
2020-12-07 18:36:15 -05:00
)
. unwrap_err ( ) ;
let error_string = error . to_string ( ) ;
// Test that the script specifier is a URL: `deno:<repo-relative path>`.
2021-07-02 06:18:30 -04:00
assert! ( error_string . contains ( " deno:core/01_core.js " ) ) ;
2020-12-07 18:36:15 -05:00
}
2021-04-12 06:15:04 -04:00
#[ test ]
fn test_v8_platform ( ) {
let options = RuntimeOptions {
2021-07-02 03:32:48 -04:00
v8_platform : Some ( v8 ::new_default_platform ( 0 , false ) . make_shared ( ) ) ,
2021-04-12 06:15:04 -04:00
.. Default ::default ( )
} ;
let mut runtime = JsRuntime ::new ( options ) ;
2021-06-21 19:45:41 -04:00
runtime . execute_script ( " <none> " , " " ) . unwrap ( ) ;
2021-04-12 06:15:04 -04:00
}
2021-10-01 14:25:33 -04:00
#[ test ]
fn test_is_proxy ( ) {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
let all_true : v8 ::Global < v8 ::Value > = runtime
. execute_script (
" is_proxy.js " ,
r #"
( function ( ) {
const { isProxy } = Deno . core ;
const o = { a : 1 , b : 2 } ;
const p = new Proxy ( o , { } ) ;
return isProxy ( p ) & & ! isProxy ( o ) & & ! isProxy ( 42 ) ;
} ) ( )
" #,
)
. unwrap ( ) ;
let mut scope = runtime . handle_scope ( ) ;
let all_true = v8 ::Local ::< v8 ::Value > ::new ( & mut scope , & all_true ) ;
assert! ( all_true . is_true ( ) ) ;
}
2021-10-05 16:55:51 -04:00
#[ test ]
fn test_binding_names ( ) {
let mut runtime = JsRuntime ::new ( RuntimeOptions ::default ( ) ) ;
let all_true : v8 ::Global < v8 ::Value > = runtime
. execute_script (
" binding_names.js " ,
" Deno.core.encode.toString() === 'function encode() { [native code] }' " ,
)
. unwrap ( ) ;
let mut scope = runtime . handle_scope ( ) ;
let all_true = v8 ::Local ::< v8 ::Value > ::new ( & mut scope , & all_true ) ;
assert! ( all_true . is_true ( ) ) ;
}
2021-10-24 13:30:55 -04:00
#[ tokio::test ]
async fn test_async_opstate_borrow ( ) {
struct InnerState ( u64 ) ;
2022-03-14 13:44:15 -04:00
#[ op ]
2021-10-24 13:30:55 -04:00
async fn op_async_borrow (
op_state : Rc < RefCell < OpState > > ,
2021-11-16 09:02:28 -05:00
) -> Result < ( ) , Error > {
2021-11-03 09:27:36 -04:00
let n = {
let op_state = op_state . borrow ( ) ;
let inner_state = op_state . borrow ::< InnerState > ( ) ;
inner_state . 0
} ;
2021-10-24 13:30:55 -04:00
// Future must be Poll::Pending on first call
tokio ::time ::sleep ( std ::time ::Duration ::from_millis ( 1 ) ) . await ;
2021-11-03 09:27:36 -04:00
if n ! = 42 {
2021-10-24 13:30:55 -04:00
unreachable! ( ) ;
}
Ok ( ( ) )
}
let extension = Extension ::builder ( )
2022-03-14 13:44:15 -04:00
. ops ( vec! [ op_async_borrow ::decl ( ) ] )
2021-10-24 13:30:55 -04:00
. state ( | state | {
state . put ( InnerState ( 42 ) ) ;
Ok ( ( ) )
} )
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ extension ] ,
.. Default ::default ( )
} ) ;
runtime
. execute_script (
" op_async_borrow.js " ,
" Deno.core.opAsync('op_async_borrow') " ,
)
. unwrap ( ) ;
runtime . run_event_loop ( false ) . await . unwrap ( ) ;
}
2021-11-16 14:23:12 -05:00
#[ tokio::test ]
async fn test_set_macrotask_callback_set_next_tick_callback ( ) {
2022-03-14 13:44:15 -04:00
#[ op ]
2022-03-15 19:33:46 -04:00
async fn op_async_sleep ( ) -> Result < ( ) , Error > {
2021-11-16 14:23:12 -05:00
// Future must be Poll::Pending on first call
tokio ::time ::sleep ( std ::time ::Duration ::from_millis ( 1 ) ) . await ;
Ok ( ( ) )
}
let extension = Extension ::builder ( )
2022-03-14 13:44:15 -04:00
. ops ( vec! [ op_async_sleep ::decl ( ) ] )
2021-11-16 14:23:12 -05:00
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ extension ] ,
.. Default ::default ( )
} ) ;
runtime
. execute_script (
" macrotasks_and_nextticks.js " ,
r #"
( async function ( ) {
const results = [ ] ;
Deno . core . setMacrotaskCallback ( ( ) = > {
results . push ( " macrotask " ) ;
return true ;
} ) ;
Deno . core . setNextTickCallback ( ( ) = > {
results . push ( " nextTick " ) ;
Deno . core . setHasTickScheduled ( false ) ;
} ) ;
Deno . core . setHasTickScheduled ( true ) ;
await Deno . core . opAsync ( ' op_async_sleep ' ) ;
if ( results [ 0 ] ! = " nextTick " ) {
throw new Error ( ` expected nextTick , got : $ { results [ 0 ] } ` ) ;
}
if ( results [ 1 ] ! = " macrotask " ) {
throw new Error ( ` expected macrotask , got : $ { results [ 1 ] } ` ) ;
}
} ) ( ) ;
" #,
)
. unwrap ( ) ;
runtime . run_event_loop ( false ) . await . unwrap ( ) ;
}
#[ tokio::test ]
async fn test_set_macrotask_callback_set_next_tick_callback_multiple ( ) {
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
runtime
. execute_script (
" multiple_macrotasks_and_nextticks.js " ,
r #"
Deno . core . setMacrotaskCallback ( ( ) = > { return true ; } ) ;
Deno . core . setMacrotaskCallback ( ( ) = > { return true ; } ) ;
Deno . core . setNextTickCallback ( ( ) = > { } ) ;
Deno . core . setNextTickCallback ( ( ) = > { } ) ;
" #,
)
. unwrap ( ) ;
let isolate = runtime . v8_isolate ( ) ;
let state_rc = JsRuntime ::state ( isolate ) ;
let state = state_rc . borrow ( ) ;
assert_eq! ( state . js_macrotask_cbs . len ( ) , 2 ) ;
assert_eq! ( state . js_nexttick_cbs . len ( ) , 2 ) ;
}
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
#[ test ]
fn test_has_tick_scheduled ( ) {
use futures ::task ::ArcWake ;
2021-11-19 08:01:30 -05:00
2022-03-14 13:44:15 -04:00
static MACROTASK : AtomicUsize = AtomicUsize ::new ( 0 ) ;
static NEXT_TICK : AtomicUsize = AtomicUsize ::new ( 0 ) ;
2021-11-19 08:01:30 -05:00
2022-03-14 13:44:15 -04:00
#[ op ]
2022-03-15 19:33:46 -04:00
fn op_macrotask ( ) -> Result < ( ) , AnyError > {
2022-03-14 13:44:15 -04:00
MACROTASK . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-11-29 18:31:12 -05:00
Ok ( ( ) )
2022-03-14 13:44:15 -04:00
}
2021-11-19 08:01:30 -05:00
2022-03-14 13:44:15 -04:00
#[ op ]
2022-03-15 19:33:46 -04:00
fn op_next_tick ( ) -> Result < ( ) , AnyError > {
2022-03-14 13:44:15 -04:00
NEXT_TICK . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-11-29 18:31:12 -05:00
Ok ( ( ) )
2022-03-14 13:44:15 -04:00
}
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
let extension = Extension ::builder ( )
2022-03-14 13:44:15 -04:00
. ops ( vec! [ op_macrotask ::decl ( ) , op_next_tick ::decl ( ) ] )
2021-11-29 18:31:12 -05:00
. build ( ) ;
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ extension ] ,
.. Default ::default ( )
} ) ;
runtime
. execute_script (
" has_tick_scheduled.js " ,
r #"
2021-11-19 08:01:30 -05:00
Deno . core . setMacrotaskCallback ( ( ) = > {
Deno . core . opSync ( " op_macrotask " ) ;
return true ; // We're done.
} ) ;
Deno . core . setNextTickCallback ( ( ) = > Deno . core . opSync ( " op_next_tick " ) ) ;
Deno . core . setHasTickScheduled ( true ) ;
" #,
2021-11-29 18:31:12 -05:00
)
. unwrap ( ) ;
struct ArcWakeImpl ( Arc < AtomicUsize > ) ;
impl ArcWake for ArcWakeImpl {
fn wake_by_ref ( arc_self : & Arc < Self > ) {
arc_self . 0. fetch_add ( 1 , Ordering ::Relaxed ) ;
}
}
let awoken_times = Arc ::new ( AtomicUsize ::new ( 0 ) ) ;
let waker =
futures ::task ::waker ( Arc ::new ( ArcWakeImpl ( awoken_times . clone ( ) ) ) ) ;
let cx = & mut Context ::from_waker ( & waker ) ;
assert! ( matches! ( runtime . poll_event_loop ( cx , false ) , Poll ::Pending ) ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( 1 , MACROTASK . load ( Ordering ::Relaxed ) ) ;
assert_eq! ( 1 , NEXT_TICK . load ( Ordering ::Relaxed ) ) ;
2021-11-29 18:31:12 -05:00
assert_eq! ( awoken_times . swap ( 0 , Ordering ::Relaxed ) , 1 ) ;
assert! ( matches! ( runtime . poll_event_loop ( cx , false ) , Poll ::Pending ) ) ;
assert_eq! ( awoken_times . swap ( 0 , Ordering ::Relaxed ) , 1 ) ;
assert! ( matches! ( runtime . poll_event_loop ( cx , false ) , Poll ::Pending ) ) ;
assert_eq! ( awoken_times . swap ( 0 , Ordering ::Relaxed ) , 1 ) ;
assert! ( matches! ( runtime . poll_event_loop ( cx , false ) , Poll ::Pending ) ) ;
assert_eq! ( awoken_times . swap ( 0 , Ordering ::Relaxed ) , 1 ) ;
let state_rc = JsRuntime ::state ( runtime . v8_isolate ( ) ) ;
state_rc . borrow_mut ( ) . has_tick_scheduled = false ;
assert! ( matches! (
runtime . poll_event_loop ( cx , false ) ,
Poll ::Ready ( Ok ( ( ) ) )
) ) ;
assert_eq! ( awoken_times . load ( Ordering ::Relaxed ) , 0 ) ;
assert! ( matches! (
runtime . poll_event_loop ( cx , false ) ,
Poll ::Ready ( Ok ( ( ) ) )
) ) ;
assert_eq! ( awoken_times . load ( Ordering ::Relaxed ) , 0 ) ;
2021-11-19 08:01:30 -05:00
}
2021-11-22 07:51:20 -05:00
#[ test ]
fn terminate_during_module_eval ( ) {
#[ derive(Default) ]
struct ModsLoader ;
impl ModuleLoader for ModsLoader {
fn resolve (
& self ,
specifier : & str ,
referrer : & str ,
_is_main : bool ,
) -> Result < ModuleSpecifier , Error > {
assert_eq! ( specifier , " file:///main.js " ) ;
assert_eq! ( referrer , " . " ) ;
let s = crate ::resolve_import ( specifier , referrer ) . unwrap ( ) ;
Ok ( s )
}
fn load (
& self ,
_module_specifier : & ModuleSpecifier ,
_maybe_referrer : Option < ModuleSpecifier > ,
_is_dyn_import : bool ,
) -> Pin < Box < ModuleSourceFuture > > {
async move {
Ok ( ModuleSource {
code : " console.log('hello world'); " . to_string ( ) ,
module_url_specified : " file:///main.js " . to_string ( ) ,
module_url_found : " file:///main.js " . to_string ( ) ,
2021-12-15 13:22:36 -05:00
module_type : ModuleType ::JavaScript ,
2021-11-22 07:51:20 -05:00
} )
}
. boxed_local ( )
}
}
let loader = std ::rc ::Rc ::new ( ModsLoader ::default ( ) ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
module_loader : Some ( loader ) ,
.. Default ::default ( )
} ) ;
let specifier = crate ::resolve_url ( " file:///main.js " ) . unwrap ( ) ;
let source_code = " Deno.core.print('hello \\ n') " . to_string ( ) ;
let module_id = futures ::executor ::block_on (
runtime . load_main_module ( & specifier , Some ( source_code ) ) ,
)
. unwrap ( ) ;
runtime . v8_isolate ( ) . terminate_execution ( ) ;
let mod_result =
futures ::executor ::block_on ( runtime . mod_evaluate ( module_id ) ) . unwrap ( ) ;
assert! ( mod_result
. unwrap_err ( )
. to_string ( )
. contains ( " JavaScript execution has been terminated " ) ) ;
}
2021-11-27 18:46:12 -05:00
#[ tokio::test ]
async fn test_set_promise_reject_callback ( ) {
2022-03-14 13:44:15 -04:00
static PROMISE_REJECT : AtomicUsize = AtomicUsize ::new ( 0 ) ;
static UNCAUGHT_EXCEPTION : AtomicUsize = AtomicUsize ::new ( 0 ) ;
2021-11-27 18:46:12 -05:00
2022-03-14 13:44:15 -04:00
#[ op ]
2022-03-15 19:33:46 -04:00
fn op_promise_reject ( ) -> Result < ( ) , AnyError > {
2022-03-14 13:44:15 -04:00
PROMISE_REJECT . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-11-27 18:46:12 -05:00
Ok ( ( ) )
2022-03-14 13:44:15 -04:00
}
2021-11-27 18:46:12 -05:00
2022-03-14 13:44:15 -04:00
#[ op ]
2022-03-15 19:33:46 -04:00
fn op_uncaught_exception ( ) -> Result < ( ) , AnyError > {
2022-03-14 13:44:15 -04:00
UNCAUGHT_EXCEPTION . fetch_add ( 1 , Ordering ::Relaxed ) ;
2021-11-27 18:46:12 -05:00
Ok ( ( ) )
2022-03-14 13:44:15 -04:00
}
2021-11-27 18:46:12 -05:00
let extension = Extension ::builder ( )
2022-03-14 13:44:15 -04:00
. ops ( vec! [
op_promise_reject ::decl ( ) ,
op_uncaught_exception ::decl ( ) ,
] )
2021-11-27 18:46:12 -05:00
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ extension ] ,
.. Default ::default ( )
} ) ;
runtime
. execute_script (
" promise_reject_callback.js " ,
r #"
// Note: |promise| is not the promise created below, it's a child.
Deno . core . setPromiseRejectCallback ( ( type , promise , reason ) = > {
if ( type ! = = /* PromiseRejectWithNoHandler */ 0 ) {
throw Error ( " unexpected type: " + type ) ;
}
if ( reason . message ! = = " reject " ) {
throw Error ( " unexpected reason: " + reason ) ;
}
Deno . core . opSync ( " op_promise_reject " ) ;
throw Error ( " promiseReject " ) ; // Triggers uncaughtException handler.
} ) ;
Deno . core . setUncaughtExceptionCallback ( ( err ) = > {
if ( err . message ! = = " promiseReject " ) throw err ;
Deno . core . opSync ( " op_uncaught_exception " ) ;
} ) ;
new Promise ( ( _ , reject ) = > reject ( Error ( " reject " ) ) ) ;
" #,
)
. unwrap ( ) ;
runtime . run_event_loop ( false ) . await . unwrap ( ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( 1 , PROMISE_REJECT . load ( Ordering ::Relaxed ) ) ;
assert_eq! ( 1 , UNCAUGHT_EXCEPTION . load ( Ordering ::Relaxed ) ) ;
2021-11-27 18:46:12 -05:00
runtime
. execute_script (
" promise_reject_callback.js " ,
r #"
{
const prev = Deno . core . setPromiseRejectCallback ( ( .. . args ) = > {
prev ( .. . args ) ;
} ) ;
}
{
const prev = Deno . core . setUncaughtExceptionCallback ( ( .. . args ) = > {
prev ( .. . args ) ;
throw Error ( " fail " ) ;
} ) ;
}
new Promise ( ( _ , reject ) = > reject ( Error ( " reject " ) ) ) ;
" #,
)
. unwrap ( ) ;
// Exception from uncaughtException handler doesn't bubble up but is
// printed to stderr.
runtime . run_event_loop ( false ) . await . unwrap ( ) ;
2022-03-14 13:44:15 -04:00
assert_eq! ( 2 , PROMISE_REJECT . load ( Ordering ::Relaxed ) ) ;
assert_eq! ( 2 , UNCAUGHT_EXCEPTION . load ( Ordering ::Relaxed ) ) ;
2021-11-27 18:46:12 -05:00
}
2022-03-19 10:59:44 -04:00
#[ test ]
fn test_op_return_serde_v8_error ( ) {
#[ op ]
fn op_err ( ) -> Result < std ::collections ::BTreeMap < u64 , u64 > , anyhow ::Error > {
Ok ( [ ( 1 , 2 ) , ( 3 , 4 ) ] . into_iter ( ) . collect ( ) ) // Maps can't have non-string keys in serde_v8
}
let ext = Extension ::builder ( ) . ops ( vec! [ op_err ::decl ( ) ] ) . build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ ext ] ,
.. Default ::default ( )
} ) ;
assert! ( runtime
. execute_script (
" test_op_return_serde_v8_error.js " ,
" Deno.core.opSync('op_err') "
)
. is_err ( ) ) ;
}
2022-03-21 13:04:57 -04:00
#[ test ]
fn test_op_high_arity ( ) {
#[ op ]
fn op_add_4 (
x1 : i64 ,
x2 : i64 ,
x3 : i64 ,
x4 : i64 ,
) -> Result < i64 , anyhow ::Error > {
Ok ( x1 + x2 + x3 + x4 )
}
let ext = Extension ::builder ( ) . ops ( vec! [ op_add_4 ::decl ( ) ] ) . build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ ext ] ,
.. Default ::default ( )
} ) ;
let r = runtime
. execute_script ( " test.js " , " Deno.core.opSync('op_add_4', 1, 2, 3, 4) " )
. unwrap ( ) ;
let scope = & mut runtime . handle_scope ( ) ;
assert_eq! ( r . open ( scope ) . integer_value ( scope ) , Some ( 10 ) ) ;
}
2022-03-22 11:39:58 -04:00
#[ test ]
fn test_op_disabled ( ) {
#[ op ]
fn op_foo ( ) -> Result < i64 , anyhow ::Error > {
Ok ( 42 )
}
let ext = Extension ::builder ( )
. ops ( vec! [ op_foo ::decl ( ) . disable ( ) ] )
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ ext ] ,
.. Default ::default ( )
} ) ;
let r = runtime
. execute_script ( " test.js " , " Deno.core.opSync('op_foo') " )
. unwrap ( ) ;
let scope = & mut runtime . handle_scope ( ) ;
assert! ( r . open ( scope ) . is_undefined ( ) ) ;
}
2022-04-01 18:09:21 -04:00
2022-04-02 07:35:57 -04:00
#[ test ]
fn test_op_detached_buffer ( ) {
use serde_v8 ::DetachedBuffer ;
#[ op ]
fn op_sum_take ( b : DetachedBuffer ) -> Result < u64 , anyhow ::Error > {
Ok ( b . as_ref ( ) . iter ( ) . clone ( ) . map ( | x | * x as u64 ) . sum ( ) )
}
#[ op ]
fn op_boomerang (
b : DetachedBuffer ,
) -> Result < DetachedBuffer , anyhow ::Error > {
Ok ( b )
}
let ext = Extension ::builder ( )
. ops ( vec! [ op_sum_take ::decl ( ) , op_boomerang ::decl ( ) ] )
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ ext ] ,
.. Default ::default ( )
} ) ;
runtime
. execute_script (
" test.js " ,
r #"
const a1 = new Uint8Array ( [ 1 , 2 , 3 ] ) ;
const a1b = a1 . subarray ( 0 , 3 ) ;
const a2 = new Uint8Array ( [ 5 , 10 , 15 ] ) ;
const a2b = a2 . subarray ( 0 , 3 ) ;
2022-04-13 05:50:57 -04:00
2022-04-02 07:35:57 -04:00
if ( ! ( a1 . length > 0 & & a1b . length > 0 ) ) {
throw new Error ( " a1 & a1b should have a length " ) ;
}
let sum = Deno . core . opSync ( ' op_sum_take ' , a1b ) ;
if ( sum ! = = 6 ) {
throw new Error ( ` Bad sum : $ { sum } ` ) ;
}
if ( a1 . length > 0 | | a1b . length > 0 ) {
throw new Error ( " expecting a1 & a1b to be detached " ) ;
}
const a3 = Deno . core . opSync ( ' op_boomerang ' , a2b ) ;
if ( a3 . byteLength ! = 3 ) {
throw new Error ( ` Expected a3 . byteLength = = = 3 , got $ { a3 . byteLength } ` ) ;
}
if ( a3 [ 0 ] ! = = 5 | | a3 [ 1 ] ! = = 10 ) {
throw new Error ( ` Invalid a3 : $ { a3 [ 0 ] } , $ { a3 [ 1 ] } ` ) ;
}
if ( a2 . byteLength > 0 | | a2b . byteLength > 0 ) {
throw new Error ( " expecting a2 & a2b to be detached, a3 re-attached " ) ;
}
2022-04-13 05:50:57 -04:00
2022-04-02 07:35:57 -04:00
const wmem = new WebAssembly . Memory ( { initial : 1 , maximum : 2 } ) ;
const w32 = new Uint32Array ( wmem . buffer ) ;
w32 [ 0 ] = 1 ; w32 [ 1 ] = 2 ; w32 [ 2 ] = 3 ;
const assertWasmThrow = ( ( ) = > {
try {
let sum = Deno . core . opSync ( ' op_sum_take ' , w32 . subarray ( 0 , 2 ) ) ;
return false ;
} catch ( e ) {
return e . message . includes ( ' ExpectedDetachable ' ) ;
}
} ) ;
if ( ! assertWasmThrow ( ) ) {
throw new Error ( " expected wasm mem to not be detachable " ) ;
}
" #,
)
. unwrap ( ) ;
}
2022-04-01 18:09:21 -04:00
#[ test ]
fn test_op_unstable_disabling ( ) {
#[ op ]
fn op_foo ( ) -> Result < i64 , anyhow ::Error > {
Ok ( 42 )
}
#[ op(unstable) ]
fn op_bar ( ) -> Result < i64 , anyhow ::Error > {
Ok ( 42 )
}
let ext = Extension ::builder ( )
. ops ( vec! [ op_foo ::decl ( ) , op_bar ::decl ( ) ] )
. middleware ( | op | if op . is_unstable { op . disable ( ) } else { op } )
. build ( ) ;
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ ext ] ,
.. Default ::default ( )
} ) ;
runtime
. execute_script (
" test.js " ,
r #"
if ( Deno . core . opSync ( ' op_foo ' ) ! = = 42 ) {
throw new Error ( " Exptected op_foo() === 42 " ) ;
}
if ( Deno . core . opSync ( ' op_bar ' ) ! = = undefined ) {
throw new Error ( " Expected op_bar to be disabled " )
}
" #,
)
. unwrap ( ) ;
}
2022-04-17 07:53:08 -04:00
#[ test ]
fn js_realm_simple ( ) {
let mut runtime = JsRuntime ::new ( Default ::default ( ) ) ;
let main_context = runtime . global_context ( ) ;
let main_global = {
let scope = & mut runtime . handle_scope ( ) ;
let local_global = main_context . open ( scope ) . global ( scope ) ;
v8 ::Global ::new ( scope , local_global )
} ;
let realm = runtime . create_realm ( ) . unwrap ( ) ;
assert_ne! ( realm . context ( ) , & main_context ) ;
assert_ne! ( realm . global_object ( & mut runtime ) , main_global ) ;
let main_object = runtime . execute_script ( " " , " Object " ) . unwrap ( ) ;
let realm_object =
realm . execute_script ( & mut runtime , " " , " Object " ) . unwrap ( ) ;
assert_ne! ( main_object , realm_object ) ;
}
#[ test ]
fn js_realm_init ( ) {
#[ op ]
fn op_test ( ) -> Result < String , Error > {
Ok ( String ::from ( " Test " ) )
}
let mut runtime = JsRuntime ::new ( RuntimeOptions {
extensions : vec ! [ Extension ::builder ( ) . ops ( vec! [ op_test ::decl ( ) ] ) . build ( ) ] ,
.. Default ::default ( )
} ) ;
let realm = runtime . create_realm ( ) . unwrap ( ) ;
let ret = realm
. execute_script ( & mut runtime , " " , " Deno.core.opSync('op_test') " )
. unwrap ( ) ;
let scope = & mut realm . handle_scope ( & mut runtime ) ;
assert_eq! ( ret , serde_v8 ::to_v8 ( scope , " Test " ) . unwrap ( ) ) ;
}
#[ test ]
fn js_realm_init_snapshot ( ) {
let snapshot = {
let mut runtime = JsRuntime ::new ( RuntimeOptions {
will_snapshot : true ,
.. Default ::default ( )
} ) ;
let snap : & [ u8 ] = & * runtime . snapshot ( ) ;
Vec ::from ( snap ) . into_boxed_slice ( )
} ;
#[ op ]
fn op_test ( ) -> Result < String , Error > {
Ok ( String ::from ( " Test " ) )
}
let mut runtime = JsRuntime ::new ( RuntimeOptions {
startup_snapshot : Some ( Snapshot ::Boxed ( snapshot ) ) ,
extensions : vec ! [ Extension ::builder ( ) . ops ( vec! [ op_test ::decl ( ) ] ) . build ( ) ] ,
.. Default ::default ( )
} ) ;
let realm = runtime . create_realm ( ) . unwrap ( ) ;
let ret = realm
. execute_script ( & mut runtime , " " , " Deno.core.opSync('op_test') " )
. unwrap ( ) ;
let scope = & mut realm . handle_scope ( & mut runtime ) ;
assert_eq! ( ret , serde_v8 ::to_v8 ( scope , " Test " ) . unwrap ( ) ) ;
}
2019-03-11 17:57:36 -04:00
}