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 ;
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 ;
2019-09-30 14:59:44 -04:00
use crate ::ops ::* ;
2021-04-28 12:41:50 -04:00
use crate ::Extension ;
use crate ::OpMiddlewareFn ;
2021-03-31 10:37:38 -04:00
use crate ::OpPayload ;
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 ;
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 ,
2020-08-11 21:07:14 -04:00
allocations : IsolateAllocations ,
2021-04-28 12:41:50 -04:00
extensions : Vec < Extension > ,
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 {
2020-07-18 16:32:11 -04:00
pub global_context : Option < v8 ::Global < v8 ::Context > > ,
pub ( crate ) js_recv_cb : Option < v8 ::Global < v8 ::Function > > ,
2021-10-04 05:45:41 -04:00
pub ( crate ) js_sync_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 ,
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 > > ,
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 > ,
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)) ]
2021-06-25 06:54:53 -04:00
struct IcuData ( [ u8 ; 10144432 ] ) ;
2021-04-12 06:15:04 -04:00
static ICU_DATA : IcuData = IcuData ( * include_bytes! ( " icudtl.dat " ) ) ;
2021-06-25 06:54:53 -04:00
v8 ::icu ::set_common_data_69 ( & 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 {
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 ( ) ;
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 ( ) ) ;
2020-01-06 10:24:44 -05:00
let mut creator =
2020-01-06 14:07:35 -05:00
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 ) ;
let context = bindings ::initialize_context ( scope ) ;
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 )
2020-04-22 14:24:49 -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 ) ;
let context = if snapshot_loaded {
v8 ::Context ::new ( scope )
} else {
// If no snapshot is provided, we initialize the context with empty
// main source code and source maps.
bindings ::initialize_context ( scope )
} ;
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-14 21:23:48 -04:00
let js_error_create_fn = options
. js_error_create_fn
2020-12-11 12:49:26 -05:00
. unwrap_or_else ( | | Rc ::new ( JsError ::create ) ) ;
2021-01-05 16:10:50 -05:00
let mut op_state = OpState ::new ( ) ;
2020-11-21 09:56:14 -05:00
if let Some ( get_error_class_fn ) = options . get_error_class_fn {
op_state . get_error_class_fn = get_error_class_fn ;
}
2020-09-10 09:57:45 -04:00
2021-05-19 14:53:43 -04:00
let op_state = Rc ::new ( RefCell ::new ( op_state ) ) ;
2020-09-06 15:44:29 -04:00
isolate . set_slot ( Rc ::new ( RefCell ::new ( JsRuntimeState {
2020-07-18 16:32:11 -04:00
global_context : Some ( 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-10-04 05:45:41 -04:00
js_sync_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 ,
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 ( ) ,
2021-02-23 07:08:50 -05:00
have_unpolled_ops : false ,
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-04-30 21:08:29 -04:00
// Add builtins extension
options
. extensions
. insert ( 0 , crate ::ops_builtin ::init_builtins ( ) ) ;
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 ,
2020-08-11 21:07:14 -04:00
allocations : IsolateAllocations ::default ( ) ,
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 {
2021-04-30 10:38:35 -04:00
js_runtime . init_extension_js ( ) . 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 ( ) ;
2021-10-04 05:45:41 -04:00
// Init callbacks (opresolve & syncOpsCache)
js_runtime . init_cbs ( ) ;
// Sync ops cache
2021-04-30 10:38:35 -04:00
js_runtime . sync_ops_cache ( ) ;
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 > {
let state = Self ::state ( self . v8_isolate ( ) ) ;
2020-09-14 23:49:12 -04:00
let state = state . borrow ( ) ;
state . global_context . clone ( ) . unwrap ( )
}
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
}
2021-04-28 12:28:46 -04:00
pub fn handle_scope ( & mut self ) -> v8 ::HandleScope {
let context = self . global_context ( ) ;
v8 ::HandleScope ::with_context ( self . v8_isolate ( ) , context )
}
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 ( )
}
2021-04-28 12:41:50 -04:00
/// Initializes JS of provided Extensions
2021-11-16 09:02:28 -05:00
fn init_extension_js ( & mut self ) -> 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
2021-06-21 19:45:41 -04:00
self . execute_script ( filename , & source ) ? ;
2021-04-28 12:41:50 -04:00
}
}
// Restore extensions
self . extensions = extensions ;
Ok ( ( ) )
}
/// Initializes ops of provided Extensions
2021-11-16 09:02:28 -05:00
fn init_extension_ops ( & mut self ) -> Result < ( ) , Error > {
2021-04-28 12:41:50 -04:00
let op_state = self . op_state ( ) ;
// Take extensions to avoid double-borrow
let mut extensions : Vec < Extension > = std ::mem ::take ( & mut self . extensions ) ;
// Middleware
let middleware : Vec < Box < OpMiddlewareFn > > = extensions
. iter_mut ( )
. filter_map ( | e | e . init_middleware ( ) )
. collect ( ) ;
// macroware wraps an opfn in all the middleware
let macroware =
move | name , opfn | middleware . iter ( ) . fold ( opfn , | opfn , m | m ( name , opfn ) ) ;
// Register ops
for e in extensions . iter_mut ( ) {
e . init_state ( & mut op_state . borrow_mut ( ) ) ? ;
// Register each op after middlewaring it
2021-04-28 15:10:44 -04:00
let ops = e . init_ops ( ) . unwrap_or_default ( ) ;
2021-04-28 12:41:50 -04:00
for ( name , opfn ) in ops {
self . register_op ( name , macroware ( name , opfn ) ) ;
}
}
// Restore extensions
self . extensions = extensions ;
Ok ( ( ) )
}
2021-10-04 05:45:41 -04:00
/// Grab a Global handle to a function returned by the given expression
fn grab_fn (
scope : & mut v8 ::HandleScope ,
code : & str ,
) -> v8 ::Global < v8 ::Function > {
let code = v8 ::String ::new ( scope , code ) . unwrap ( ) ;
2021-04-21 20:48:17 -04:00
let script = v8 ::Script ::compile ( scope , code , None ) . unwrap ( ) ;
let v8_value = script . run ( scope ) . unwrap ( ) ;
2021-10-04 05:45:41 -04:00
let cb = v8 ::Local ::< v8 ::Function > ::try_from ( v8_value ) . unwrap ( ) ;
v8 ::Global ::new ( scope , cb )
}
2021-04-21 20:48:17 -04:00
2021-10-04 05:45:41 -04:00
/// Grabs a reference to core.js' opresolve & syncOpsCache()
fn init_cbs ( & mut self ) {
let mut scope = self . handle_scope ( ) ;
let recv_cb = Self ::grab_fn ( & mut scope , " Deno.core.opresolve " ) ;
let sync_cb = Self ::grab_fn ( & mut scope , " Deno.core.syncOpsCache " ) ;
// Put global handles in state
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 ) ;
state . js_sync_cb . replace ( sync_cb ) ;
2019-03-14 19:17:52 -04:00
}
2021-04-25 16:00:05 -04:00
/// Ensures core.js has the latest op-name to op-id mappings
pub fn sync_ops_cache ( & mut self ) {
2021-10-04 05:45:41 -04:00
let scope = & mut self . handle_scope ( ) ;
let state_rc = JsRuntime ::state ( scope ) ;
let js_sync_cb_handle = state_rc . borrow ( ) . js_sync_cb . clone ( ) . unwrap ( ) ;
2021-10-27 17:26:15 -04:00
let js_sync_cb = js_sync_cb_handle . open ( scope ) ;
2021-10-04 05:45:41 -04:00
let this = v8 ::undefined ( scope ) . into ( ) ;
js_sync_cb . call ( scope , this , & [ ] ) ;
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 > {
2021-04-28 12:28:46 -04:00
let scope = & mut self . handle_scope ( ) ;
2020-05-29 17:41:39 -04:00
2021-06-21 19:45:41 -04:00
let source = v8 ::String ::new ( scope , source_code ) . unwrap ( ) ;
let name = v8 ::String ::new ( scope , name ) . unwrap ( ) ;
2020-01-25 08:31:42 -05:00
let origin = bindings ::script_origin ( scope , name ) ;
2020-06-20 07:18:08 -04:00
let tc_scope = & mut v8 ::TryCatch ::new ( scope ) ;
2020-01-25 08:31:42 -05:00
2020-06-20 07:18:08 -04:00
let script = match v8 ::Script ::compile ( tc_scope , source , Some ( & origin ) ) {
Some ( script ) = > script ,
None = > {
let exception = tc_scope . exception ( ) . unwrap ( ) ;
2020-10-25 23:34:00 -04:00
return exception_to_err_result ( tc_scope , exception , false ) ;
2020-06-20 07:18:08 -04:00
}
} ;
2020-04-16 06:58:17 -04:00
2020-06-20 07:18:08 -04:00
match script . run ( tc_scope ) {
2021-07-08 12:56:53 -04:00
Some ( value ) = > {
let value_handle = v8 ::Global ::new ( tc_scope , value ) ;
Ok ( value_handle )
}
2020-01-25 08:31:42 -05:00
None = > {
2020-06-20 07:18:08 -04:00
assert! ( tc_scope . has_caught ( ) ) ;
let exception = tc_scope . exception ( ) . unwrap ( ) ;
2020-10-25 23:34:00 -04:00
exception_to_err_result ( tc_scope , exception , false )
2020-01-25 08:31:42 -05:00
}
2020-01-06 10:24:44 -05:00
}
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 ( ) ) ;
2020-10-07 09:56:52 -04:00
let state = Self ::state ( self . v8_isolate ( ) ) ;
2020-01-06 10:24:44 -05:00
2020-02-28 02:28:33 -05:00
// Note: create_blob() method must not be called from within a HandleScope.
// TODO(piscisaureus): The rusty_v8 type system should enforce this.
2020-07-18 16:32:11 -04:00
state . borrow_mut ( ) . global_context . 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 ) ;
2021-10-04 05:45:41 -04:00
std ::mem ::take ( & mut state . borrow_mut ( ) . js_sync_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
2020-11-05 20:26:14 -05:00
/// Registers an op that can be called from JavaScript.
///
/// The _op_ mechanism allows to expose Rust functions to the JS runtime,
/// which can be called using the provided `name`.
///
/// This function provides byte-level bindings. To pass data via JSON, the
/// following functions can be passed as an argument for `op_fn`:
2021-04-12 15:55:05 -04:00
/// * [op_sync()](fn.op_sync.html)
/// * [op_async()](fn.op_async.html)
2020-09-10 09:57:45 -04:00
pub fn register_op < F > ( & mut self , name : & str , op_fn : F ) -> OpId
where
2021-05-06 13:32:03 -04:00
F : Fn ( Rc < RefCell < OpState > > , OpPayload ) -> Op + 'static ,
2020-09-10 09:57:45 -04:00
{
2020-10-07 09:56:52 -04:00
Self ::state ( self . v8_isolate ( ) )
2020-09-10 09:57:45 -04:00
. borrow_mut ( )
. op_state
. borrow_mut ( )
. op_table
. register_op ( name , op_fn )
}
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
}
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 > {
2021-09-04 14:19:26 -04:00
poll_fn ( | cx | {
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 ) )
}
} )
. await
}
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
}
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
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
{
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
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 > {
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 {
// TerminateExecution was called. Cancel exception termination so that the
// exception can be created..
2020-12-28 10:36:44 -05:00
scope . cancel_terminate_execution ( ) ;
2020-02-24 18:53:29 -05:00
// Maybe make a new exception object.
if exception . is_null_or_undefined ( ) {
2020-03-02 17:20:16 -05:00
let message = v8 ::String ::new ( scope , " execution terminated " ) . unwrap ( ) ;
exception = v8 ::Exception ::error ( scope , message ) ;
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 {
js_error . message = format! (
" Uncaught (in promise) {} " ,
js_error . message . trim_start_matches ( " Uncaught " )
) ;
}
2020-05-29 17:41:39 -04:00
2020-09-06 15:44:29 -04:00
let state_rc = JsRuntime ::state ( scope ) ;
2020-05-29 17:41:39 -04:00
let state = state_rc . borrow ( ) ;
let js_error = ( state . 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 ( ) ;
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 " ) ;
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 ;
let op_state = state . op_state . clone ( ) ;
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 ;
2021-10-24 15:41:57 -04:00
op_state . borrow ( ) . tracker . track_async_completed ( op_id ) ;
2021-11-25 13:49:09 -05:00
state . unrefed_ops . remove ( & promise_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
}
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 ;
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-10-24 13:30:55 -04:00
use crate ::op_async ;
2021-05-03 11:30:41 -04:00
use crate ::op_sync ;
2021-05-06 13:32:03 -04:00
use crate ::ZeroCopyBuf ;
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 ;
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
2021-12-29 09:21:42 -05:00
fn op_test ( rc_op_state : Rc < RefCell < OpState > > , payload : OpPayload ) -> Op {
2021-04-12 17:38:26 -04:00
let rc_op_state2 = rc_op_state . clone ( ) ;
let op_state_ = rc_op_state2 . 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 ) ;
2021-05-06 13:32:03 -04:00
let ( control , buf ) : ( u8 , Option < ZeroCopyBuf > ) =
payload . deserialize ( ) . unwrap ( ) ;
2020-09-10 09:57:45 -04:00
match test_state . mode {
Mode ::Async = > {
2021-03-31 10:37:38 -04:00
assert_eq! ( control , 42 ) ;
2021-10-10 11:20:30 -04:00
let resp = ( 0 , 1 , serialize_op_result ( Ok ( 43 ) , rc_op_state ) ) ;
2021-10-09 16:37:19 -04:00
Op ::Async ( OpCall ::ready ( resp ) )
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
}
2021-10-09 16:37:19 -04:00
let resp = ( 0 , 1 , serialize_op_result ( Ok ( 43 ) , rc_op_state ) ) ;
Op ::Async ( OpCall ::ready ( resp ) )
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 ( )
. ops ( vec! [ ( " op_test " , Box ::new ( op_test ) ) ] )
. 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 "
) ;
}
}
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 " ,
err . downcast ::< JsError > ( ) . unwrap ( ) . message
) ;
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
}
2019-10-22 10:49:58 -04:00
#[ test ]
fn test_pre_dispatch ( ) {
2021-12-04 08:19:06 -05:00
run_in_task ( | cx | {
2021-03-31 10:37:38 -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
" bad_op_id.js " ,
r #"
2019-10-22 10:49:58 -04:00
let thrown ;
try {
2021-10-04 06:34:53 -04:00
Deno . core . opcallSync ( 100 , null , null ) ;
2019-10-22 10:49:58 -04:00
} catch ( e ) {
thrown = e ;
}
2020-01-25 08:31:42 -05:00
assert ( String ( thrown ) = = = " TypeError: Unknown op id: 100 " ) ;
2019-10-22 10:49:58 -04:00
" #,
2020-09-22 17:30:03 -04:00
)
. unwrap ( ) ;
2021-12-04 08:19:06 -05:00
if let Poll ::Ready ( Err ( _ ) ) = runtime . poll_event_loop ( cx , false ) {
2019-11-16 19:17:47 -05:00
unreachable! ( ) ;
}
2019-10-22 10:49:58 -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 ( ) ;
2020-04-16 06:58:17 -04:00
assert_eq! ( js_error . end_column , Some ( 11 ) ) ;
}
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 ( ) {
2021-11-16 09:02:28 -05:00
fn op_err ( _ : & mut OpState , _ : ( ) , _ : ( ) ) -> 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 | {
2021-12-29 09:21:42 -05:00
let ext = Extension ::builder ( )
. ops ( vec! [ ( " op_err " , op_sync ( op_err ) ) ] )
. 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
#[ 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
let callback_invoke_count = Rc ::new ( AtomicUsize ::default ( ) ) ;
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 " ,
2020-09-06 15:44:29 -04:00
err . downcast ::< JsError > ( ) . unwrap ( ) . 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
let callback_invoke_count_first = Rc ::new ( AtomicUsize ::default ( ) ) ;
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
} ,
) ;
let callback_invoke_count_second = Rc ::new ( AtomicUsize ::default ( ) ) ;
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 " ,
2020-09-06 15:44:29 -04:00
err . downcast ::< JsError > ( ) . unwrap ( ) . 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
2020-10-31 14:57:19 -04:00
at error_without_stack . js :3 :14 " #;
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 ) ;
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 ( )
. ops ( vec! [ ( " op_async_borrow " , op_async ( op_async_borrow ) ) ] )
. 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 ( ) {
async fn op_async_sleep (
_op_state : Rc < RefCell < OpState > > ,
_ : ( ) ,
_ : ( ) ,
) -> Result < ( ) , Error > {
// Future must be Poll::Pending on first call
tokio ::time ::sleep ( std ::time ::Duration ::from_millis ( 1 ) ) . await ;
Ok ( ( ) )
}
let extension = Extension ::builder ( )
. ops ( vec! [ ( " op_async_sleep " , op_async ( op_async_sleep ) ) ] )
. 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
2021-11-29 18:31:12 -05:00
let macrotask = Arc ::new ( AtomicUsize ::default ( ) ) ;
let macrotask_ = Arc ::clone ( & macrotask ) ;
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
let next_tick = Arc ::new ( AtomicUsize ::default ( ) ) ;
let next_tick_ = Arc ::clone ( & next_tick ) ;
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
let op_macrotask = move | _ : & mut OpState , _ : ( ) , _ : ( ) | {
macrotask_ . fetch_add ( 1 , Ordering ::Relaxed ) ;
Ok ( ( ) )
} ;
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
let op_next_tick = move | _ : & mut OpState , _ : ( ) , _ : ( ) | {
next_tick_ . fetch_add ( 1 , Ordering ::Relaxed ) ;
Ok ( ( ) )
} ;
2021-11-19 08:01:30 -05:00
2021-11-29 18:31:12 -05:00
let extension = Extension ::builder ( )
. ops ( vec! [ ( " op_macrotask " , op_sync ( op_macrotask ) ) ] )
. ops ( vec! [ ( " op_next_tick " , op_sync ( op_next_tick ) ) ] )
. 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 ) ) ;
assert_eq! ( 1 , macrotask . load ( Ordering ::Relaxed ) ) ;
assert_eq! ( 1 , next_tick . load ( Ordering ::Relaxed ) ) ;
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 ( ) {
let promise_reject = Arc ::new ( AtomicUsize ::default ( ) ) ;
let promise_reject_ = Arc ::clone ( & promise_reject ) ;
let uncaught_exception = Arc ::new ( AtomicUsize ::default ( ) ) ;
let uncaught_exception_ = Arc ::clone ( & uncaught_exception ) ;
let op_promise_reject = move | _ : & mut OpState , _ : ( ) , _ : ( ) | {
promise_reject_ . fetch_add ( 1 , Ordering ::Relaxed ) ;
Ok ( ( ) )
} ;
let op_uncaught_exception = move | _ : & mut OpState , _ : ( ) , _ : ( ) | {
uncaught_exception_ . fetch_add ( 1 , Ordering ::Relaxed ) ;
Ok ( ( ) )
} ;
let extension = Extension ::builder ( )
. ops ( vec! [ ( " op_promise_reject " , op_sync ( op_promise_reject ) ) ] )
. ops ( vec! [ (
" op_uncaught_exception " ,
op_sync ( op_uncaught_exception ) ,
) ] )
. 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 ( ) ;
assert_eq! ( 1 , promise_reject . load ( Ordering ::Relaxed ) ) ;
assert_eq! ( 1 , uncaught_exception . load ( Ordering ::Relaxed ) ) ;
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 ( ) ;
assert_eq! ( 2 , promise_reject . load ( Ordering ::Relaxed ) ) ;
assert_eq! ( 2 , uncaught_exception . load ( Ordering ::Relaxed ) ) ;
}
2019-03-11 17:57:36 -04:00
}