2023-01-02 16:00:42 -05:00
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2023-03-21 18:33:12 -04:00
use crate ::modules ::ModuleCode ;
2021-11-16 09:02:28 -05:00
use crate ::OpState ;
2023-02-20 15:45:34 -05:00
use anyhow ::Context as _ ;
2021-11-16 09:02:28 -05:00
use anyhow ::Error ;
2023-01-14 23:18:58 -05:00
use std ::cell ::RefCell ;
2023-02-20 15:45:34 -05:00
use std ::path ::PathBuf ;
2023-01-14 23:18:58 -05:00
use std ::rc ::Rc ;
use std ::task ::Context ;
2022-08-21 08:07:53 -04:00
use v8 ::fast_api ::FastFunction ;
2021-04-28 12:41:50 -04:00
2023-02-19 19:11:56 -05:00
#[ derive(Clone, Debug) ]
pub enum ExtensionFileSourceCode {
/// Source code is included in the binary produced. Either by being defined
/// inline, or included using `include_str!()`. If you are snapshotting, this
/// will result in two copies of the source code being included - one in the
/// snapshot, the other the static string in the `Extension`.
IncludedInBinary ( & 'static str ) ,
2023-02-20 15:45:34 -05:00
// Source code is loaded from a file on disk. It's meant to be used if the
// embedder is creating snapshots. Files will be loaded from the filesystem
// during the build time and they will only be present in the V8 snapshot.
LoadedFromFsDuringSnapshot ( PathBuf ) ,
}
2023-03-23 18:00:46 -04:00
#[ derive(Clone, Debug) ]
pub struct ExtensionFileSource {
pub specifier : & 'static str ,
pub code : ExtensionFileSourceCode ,
}
impl ExtensionFileSource {
fn find_non_ascii ( s : & str ) -> String {
s . chars ( ) . filter ( | c | ! c . is_ascii ( ) ) . collect ::< String > ( )
}
2023-03-21 18:33:12 -04:00
pub fn load ( & self ) -> Result < ModuleCode , Error > {
2023-03-23 18:00:46 -04:00
match & self . code {
ExtensionFileSourceCode ::IncludedInBinary ( code ) = > {
debug_assert! (
code . is_ascii ( ) ,
" Extension code must be 7-bit ASCII: {} (found {}) " ,
self . specifier ,
Self ::find_non_ascii ( code )
) ;
2023-04-04 08:46:31 -04:00
Ok ( ModuleCode ::from_static ( code ) )
2023-03-23 18:00:46 -04:00
}
2023-02-20 15:45:34 -05:00
ExtensionFileSourceCode ::LoadedFromFsDuringSnapshot ( path ) = > {
2023-03-21 18:33:12 -04:00
let msg = | | format! ( " Failed to read \" {} \" " , path . display ( ) ) ;
2023-03-23 18:00:46 -04:00
let s = std ::fs ::read_to_string ( path ) . with_context ( msg ) ? ;
debug_assert! (
s . is_ascii ( ) ,
" Extension code must be 7-bit ASCII: {} (found {}) " ,
self . specifier ,
Self ::find_non_ascii ( & s )
) ;
Ok ( s . into ( ) )
2023-02-20 15:45:34 -05:00
}
}
}
2023-02-19 19:11:56 -05:00
}
2022-03-14 13:44:15 -04:00
pub type OpFnRef = v8 ::FunctionCallback ;
2022-03-15 18:43:17 -04:00
pub type OpMiddlewareFn = dyn Fn ( OpDecl ) -> OpDecl ;
2023-03-17 18:15:27 -04:00
pub type OpStateFn = dyn FnOnce ( & mut OpState ) ;
2022-06-28 05:23:36 -04:00
pub type OpEventLoopFn = dyn Fn ( Rc < RefCell < OpState > > , & mut Context ) -> bool ;
2021-04-28 12:41:50 -04:00
2022-03-15 18:43:17 -04:00
pub struct OpDecl {
pub name : & 'static str ,
pub v8_fn_ptr : OpFnRef ,
2022-03-22 11:39:58 -04:00
pub enabled : bool ,
2022-08-21 08:07:53 -04:00
pub is_async : bool ,
2022-04-01 18:09:21 -04:00
pub is_unstable : bool ,
2022-05-12 13:06:42 -04:00
pub is_v8 : bool ,
2023-03-18 18:30:04 -04:00
pub force_registration : bool ,
2023-04-30 04:50:24 -04:00
pub arg_count : u8 ,
2023-03-31 08:42:14 -04:00
pub fast_fn : Option < FastFunction > ,
2022-03-22 11:39:58 -04:00
}
impl OpDecl {
pub fn enabled ( self , enabled : bool ) -> Self {
Self { enabled , .. self }
}
pub fn disable ( self ) -> Self {
self . enabled ( false )
}
2022-03-15 18:43:17 -04:00
}
2023-03-17 14:22:15 -04:00
/// Declares a block of Deno `#[op]`s. The first parameter determines the name of the
/// op declaration block, and is usually `deno_ops`. This block generates a function that
/// returns a [`Vec<OpDecl>`].
///
/// This can be either a compact form like:
///
/// ```no_compile
/// # use deno_core::*;
/// #[op]
/// fn op_xyz() {}
///
/// deno_core::ops!(deno_ops, [
/// op_xyz
/// ]);
///
/// // Use the ops:
/// deno_ops()
/// ```
///
/// ... or a parameterized form like so that allows passing a number of type parameters
/// to each `#[op]`:
///
/// ```no_compile
/// # use deno_core::*;
/// #[op]
/// fn op_xyz<P>() where P: Clone {}
///
/// deno_core::ops!(deno_ops,
/// parameters = [P: Clone],
/// ops = [
/// op_xyz<P>
/// ]
/// );
///
/// // Use the ops, with `String` as the parameter `P`:
/// deno_ops::<String>()
/// ```
#[ macro_export ]
macro_rules ! ops {
( $name :ident , parameters = [ $( $param :ident : $type :ident ) , + ] , ops = [ $( $( #[ $m:meta ] ) * $( $op :ident ) ::+ $( < $op_param :ident > ) ? ) , + $(, ) ? ] ) = > {
pub ( crate ) fn $name < $( $param : $type + 'static ) , + > ( ) -> Vec < $crate ::OpDecl > {
vec! [
$(
$( #[ $m ] ) *
$( $op ) ::+ :: decl $( :: < $op_param > ) ? ( ) ,
) +
]
}
} ;
( $name :ident , [ $( $( #[ $m:meta ] ) * $( $op :ident ) ::+ ) , + $(, ) ? ] ) = > {
pub ( crate ) fn $name ( ) -> Vec < $crate ::OpDecl > {
vec! [
$( $( #[ $m ] ) * $( $op ) ::+ :: decl ( ) , ) +
]
}
}
}
/// Defines a Deno extension. The first parameter is the name of the extension symbol namespace to create. This is the symbol you
/// will use to refer to the extension.
///
/// Most extensions will define a combination of ops and ESM files, like so:
///
/// ```no_compile
/// #[op]
/// fn op_xyz() {
/// }
///
/// deno_core::extension!(
/// my_extension,
/// ops = [ op_xyz ],
/// esm = [ "my_script.js" ],
/// );
/// ```
///
/// The following options are available for the [`extension`] macro:
///
/// * deps: a comma-separated list of module dependencies, eg: `deps = [ my_other_extension ]`
/// * parameters: a comma-separated list of parameters and base traits, eg: `parameters = [ P: MyTrait ]`
2023-04-17 10:10:59 -04:00
/// * bounds: a comma-separated list of additional type bounds, eg: `bounds = [ P::MyAssociatedType: MyTrait ]`
2023-03-17 14:22:15 -04:00
/// * ops: a comma-separated list of [`OpDecl`]s to provide, eg: `ops = [ op_foo, op_bar ]`
/// * esm: a comma-separated list of ESM module filenames (see [`include_js_files`]), eg: `esm = [ dir "dir", "my_file.js" ]`
/// * esm_setup_script: see [`ExtensionBuilder::esm_setup_script`]
/// * js: a comma-separated list of JS filenames (see [`include_js_files`]), eg: `js = [ dir "dir", "my_file.js" ]`
/// * config: a structure-like definition for configuration parameters which will be required when initializing this extension, eg: `config = { my_param: Option<usize> }`
/// * middleware: an [`OpDecl`] middleware function with the signature `fn (OpDecl) -> OpDecl`
/// * state: a state initialization function, with the signature `fn (&mut OpState, ...) -> ()`, where `...` are parameters matching the fields of the config struct
/// * event_loop_middleware: an event-loop middleware function (see [`ExtensionBuilder::event_loop_middleware`])
#[ macro_export ]
macro_rules ! extension {
(
$name :ident
$(, deps = [ $( $dep :ident ) , * ] ) ?
$(, parameters = [ $( $param :ident : $type :ident ) , + ] ) ?
2023-04-17 10:10:59 -04:00
$(, bounds = [ $( $bound :path : $bound_type :ident ) , + ] ) ?
2023-03-17 14:22:15 -04:00
$(, ops_fn = $ops_symbol :ident $( < $ops_param :ident > ) ? ) ?
2023-04-12 09:13:32 -04:00
$(, ops = [ $( $( #[ $m:meta ] ) * $( $op :ident ) ::+ $( < $( $op_param :ident ) , * > ) ? ) , + $(, ) ? ] ) ?
2023-03-17 14:22:15 -04:00
$(, esm_entry_point = $esm_entry_point :literal ) ?
$(, esm = [ $( dir $dir_esm :literal , ) ? $( $esm :literal ) , * $(, ) ? ] ) ?
$(, esm_setup_script = $esm_setup_script :expr ) ?
$(, js = [ $( dir $dir_js :literal , ) ? $( $js :literal ) , * $(, ) ? ] ) ?
2023-03-17 18:15:27 -04:00
$(, options = { $( $options_id :ident : $options_type :ty ) , * $(, ) ? } ) ?
2023-03-17 14:22:15 -04:00
$(, middleware = $middleware_fn :expr ) ?
$(, state = $state_fn :expr ) ?
$(, event_loop_middleware = $event_loop_middleware_fn :ident ) ?
$(, customizer = $customizer_fn :expr ) ?
$(, ) ?
) = > {
/// Extension struct for
#[ doc = stringify!($name) ]
/// .
#[ allow(non_camel_case_types) ]
pub struct $name {
}
impl $name {
#[ inline(always) ]
fn ext ( ) -> $crate ::ExtensionBuilder {
$crate ::Extension ::builder_with_deps ( stringify! ( $name ) , & [ $( $( stringify! ( $dep ) ) , * ) ? ] )
}
/// If ESM or JS was specified, add those files to the extension.
#[ inline(always) ]
#[ allow(unused_variables) ]
fn with_js ( ext : & mut $crate ::ExtensionBuilder ) {
$( ext . esm (
2023-03-18 16:09:13 -04:00
$crate ::include_js_files! ( $name $( dir $dir_esm , ) ? $( $esm , ) * )
2023-03-17 14:22:15 -04:00
) ; ) ?
$(
ext . esm ( vec! [ ExtensionFileSource {
2023-03-18 16:09:13 -04:00
specifier : " ext:setup " ,
2023-03-17 14:22:15 -04:00
code : ExtensionFileSourceCode ::IncludedInBinary ( $esm_setup_script ) ,
} ] ) ;
) ?
$(
ext . esm_entry_point ( $esm_entry_point ) ;
) ?
$( ext . js (
2023-03-18 16:09:13 -04:00
$crate ::include_js_files! ( $name $( dir $dir_js , ) ? $( $js , ) * )
2023-03-17 14:22:15 -04:00
) ; ) ?
}
// If ops were specified, add those ops to the extension.
#[ inline(always) ]
#[ allow(unused_variables) ]
2023-04-17 10:10:59 -04:00
fn with_ops $( < $( $param : $type + 'static ) , + > ) ? ( ext : & mut $crate ::ExtensionBuilder )
$( where $( $bound : $bound_type ) , + ) ?
{
2023-03-17 14:22:15 -04:00
// If individual ops are specified, roll them up into a vector and apply them
$(
2023-03-18 07:32:11 -04:00
ext . ops ( vec! [
$(
$( #[ $m ] ) *
2023-04-12 09:13:32 -04:00
$( $op ) ::+ :: decl $( :: < $( $op_param ) , * > ) ? ( )
2023-03-18 07:32:11 -04:00
) , +
] ) ;
2023-03-17 14:22:15 -04:00
) ?
// Otherwise use the ops_fn, if provided
$crate ::extension! ( ! __ops__ ext $( $ops_symbol $( < $ops_param > ) ? ) ? __eot__ ) ;
}
// Includes the state and middleware functions, if defined.
#[ inline(always) ]
#[ allow(unused_variables) ]
2023-04-17 10:10:59 -04:00
fn with_state_and_middleware $( < $( $param : $type + 'static ) , + > ) ? ( ext : & mut $crate ::ExtensionBuilder , $( $( $options_id : $options_type ) , * ) ? )
$( where $( $bound : $bound_type ) , + ) ?
{
2023-03-18 07:32:11 -04:00
$crate ::extension! ( ! __config__ ext $( parameters = [ $( $param : $type ) , * ] ) ? $( config = { $( $options_id : $options_type ) , * } ) ? $( state_fn = $state_fn ) ? ) ;
2023-03-17 14:22:15 -04:00
$(
ext . event_loop_middleware ( $event_loop_middleware_fn ) ;
) ?
$(
ext . middleware ( $middleware_fn ) ;
) ?
}
#[ inline(always) ]
#[ allow(unused_variables) ]
2023-03-18 18:30:04 -04:00
#[ allow(clippy::redundant_closure_call) ]
2023-03-17 14:22:15 -04:00
fn with_customizer ( ext : & mut $crate ::ExtensionBuilder ) {
$( ( $customizer_fn ) ( ext ) ; ) ?
}
#[ allow(dead_code) ]
2023-04-17 10:10:59 -04:00
pub fn init_js_only $( < $( $param : $type + 'static ) , * > ) ? ( ) -> $crate ::Extension
$( where $( $bound : $bound_type ) , + ) ?
{
2023-03-17 14:22:15 -04:00
let mut ext = Self ::ext ( ) ;
// If esm or JS was specified, add JS files
Self ::with_js ( & mut ext ) ;
2023-04-12 09:13:32 -04:00
Self ::with_ops $( ::< $( $param ) , + > ) ? ( & mut ext ) ;
2023-03-17 14:22:15 -04:00
Self ::with_customizer ( & mut ext ) ;
2023-03-17 18:15:27 -04:00
ext . take ( )
2023-03-17 14:22:15 -04:00
}
#[ allow(dead_code) ]
2023-04-17 10:10:59 -04:00
pub fn init_ops_and_esm $( < $( $param : $type + 'static ) , + > ) ? ( $( $( $options_id : $options_type ) , * ) ? ) -> $crate ::Extension
$( where $( $bound : $bound_type ) , + ) ?
{
2023-03-17 14:22:15 -04:00
let mut ext = Self ::ext ( ) ;
// If esm or JS was specified, add JS files
Self ::with_js ( & mut ext ) ;
2023-04-12 09:13:32 -04:00
Self ::with_ops $( ::< $( $param ) , + > ) ? ( & mut ext ) ;
Self ::with_state_and_middleware $( ::< $( $param ) , + > ) ? ( & mut ext , $( $( $options_id , ) * ) ? ) ;
2023-03-17 14:22:15 -04:00
Self ::with_customizer ( & mut ext ) ;
2023-03-17 18:15:27 -04:00
ext . take ( )
2023-03-17 14:22:15 -04:00
}
#[ allow(dead_code) ]
2023-04-17 10:10:59 -04:00
pub fn init_ops $( < $( $param : $type + 'static ) , + > ) ? ( $( $( $options_id : $options_type ) , * ) ? ) -> $crate ::Extension
$( where $( $bound : $bound_type ) , + ) ?
{
2023-03-17 14:22:15 -04:00
let mut ext = Self ::ext ( ) ;
2023-04-12 09:13:32 -04:00
Self ::with_ops $( ::< $( $param ) , + > ) ? ( & mut ext ) ;
Self ::with_state_and_middleware $( ::< $( $param ) , + > ) ? ( & mut ext , $( $( $options_id , ) * ) ? ) ;
2023-03-17 14:22:15 -04:00
Self ::with_customizer ( & mut ext ) ;
2023-03-17 18:15:27 -04:00
ext . take ( )
2023-03-17 14:22:15 -04:00
}
}
} ;
2023-03-17 18:15:27 -04:00
// This branch of the macro generates a config object that calls the state function with itself.
2023-03-18 07:32:11 -04:00
( ! __config__ $ext :ident $( parameters = [ $( $param :ident : $type :ident ) , + ] ) ? config = { $( $options_id :ident : $options_type :ty ) , * } $( state_fn = $state_fn :expr ) ? ) = > {
2023-03-17 14:22:15 -04:00
{
#[ doc(hidden) ]
2023-03-17 20:58:00 -04:00
struct Config $( < $( $param : $type + 'static ) , + > ) ? {
2023-03-17 18:15:27 -04:00
$( pub $options_id : $options_type , ) *
2023-03-17 14:22:15 -04:00
$( __phantom_data : ::std ::marker ::PhantomData < ( $( $param ) , + ) > , ) ?
}
2023-03-18 07:32:11 -04:00
let config = Config {
2023-03-17 18:15:27 -04:00
$( $options_id , ) *
2023-03-17 14:22:15 -04:00
$( __phantom_data : ::std ::marker ::PhantomData ::< ( $( $param ) , + ) > ::default ( ) ) ?
2023-03-18 07:32:11 -04:00
} ;
let state_fn : fn ( & mut $crate ::OpState , Config $( < $( $param ) , + > ) ? ) = $( $state_fn ) ? ;
$ext . state ( move | state : & mut $crate ::OpState | {
state_fn ( state , config ) ;
} ) ;
2023-03-17 14:22:15 -04:00
}
} ;
2023-03-18 07:32:11 -04:00
( ! __config__ $ext :ident $( parameters = [ $( $param :ident : $type :ident ) , + ] ) ? $( state_fn = $state_fn :expr ) ? ) = > {
$( $ext . state ( $state_fn ) ; ) ?
2023-03-17 18:15:27 -04:00
} ;
2023-03-17 14:22:15 -04:00
( ! __ops__ $ext :ident __eot__ ) = > {
} ;
( ! __ops__ $ext :ident $ops_symbol :ident __eot__ ) = > {
$ext . ops ( $ops_symbol ( ) )
} ;
( ! __ops__ $ext :ident $ops_symbol :ident < $ops_param :ident > __eot__ ) = > {
$ext . ops ( $ops_symbol ::< $ops_param > ( ) )
} ;
}
2021-04-28 12:41:50 -04:00
#[ derive(Default) ]
pub struct Extension {
2023-02-07 18:21:43 -05:00
js_files : Option < Vec < ExtensionFileSource > > ,
esm_files : Option < Vec < ExtensionFileSource > > ,
2023-02-13 18:43:53 -05:00
esm_entry_point : Option < & 'static str > ,
2022-07-22 09:36:32 -04:00
ops : Option < Vec < OpDecl > > ,
2021-04-28 12:41:50 -04:00
opstate_fn : Option < Box < OpStateFn > > ,
middleware_fn : Option < Box < OpMiddlewareFn > > ,
2022-03-08 09:40:34 -05:00
event_loop_middleware : Option < Box < OpEventLoopFn > > ,
2021-04-28 12:41:50 -04:00
initialized : bool ,
2022-03-22 11:39:58 -04:00
enabled : bool ,
2023-01-08 17:48:46 -05:00
name : & 'static str ,
2023-03-09 07:10:54 -05:00
deps : Option < & 'static [ & 'static str ] > ,
2023-03-18 18:30:04 -04:00
force_op_registration : bool ,
2023-04-13 20:41:32 -04:00
pub ( crate ) is_core : bool ,
2021-04-28 12:41:50 -04:00
}
// Note: this used to be a trait, but we "downgraded" it to a single concrete type
// for the initial iteration, it will likely become a trait in the future
impl Extension {
2023-01-08 17:48:46 -05:00
pub fn builder ( name : & 'static str ) -> ExtensionBuilder {
ExtensionBuilder {
name ,
.. Default ::default ( )
}
}
2023-03-09 07:10:54 -05:00
pub fn builder_with_deps (
name : & 'static str ,
deps : & 'static [ & 'static str ] ,
) -> ExtensionBuilder {
ExtensionBuilder {
name ,
deps ,
.. Default ::default ( )
}
}
2023-01-08 17:48:46 -05:00
/// Check if dependencies have been loaded, and errors if either:
/// - The extension is depending on itself or an extension with the same name.
/// - A dependency hasn't been loaded yet.
2023-03-09 19:22:27 -05:00
pub fn check_dependencies ( & self , previous_exts : & [ Extension ] ) {
2023-03-09 07:10:54 -05:00
if let Some ( deps ) = self . deps {
2023-01-08 17:48:46 -05:00
' dep_loop : for dep in deps {
if dep = = & self . name {
panic! ( " Extension ' {} ' is either depending on itself or there is another extension with the same name " , self . name ) ;
}
for ext in previous_exts {
if dep = = & ext . name {
continue 'dep_loop ;
}
}
panic! ( " Extension ' {} ' is missing dependency ' {dep} ' " , self . name ) ;
}
}
2021-04-28 18:16:45 -04:00
}
2021-04-28 12:41:50 -04:00
/// returns JS source code to be loaded into the isolate (either at snapshotting,
/// or at startup). as a vector of a tuple of the file name, and the source code.
2023-03-09 19:22:27 -05:00
pub fn get_js_sources ( & self ) -> Option < & Vec < ExtensionFileSource > > {
self . js_files . as_ref ( )
2021-04-28 12:41:50 -04:00
}
2023-03-09 19:22:27 -05:00
pub fn get_esm_sources ( & self ) -> Option < & Vec < ExtensionFileSource > > {
self . esm_files . as_ref ( )
2023-02-07 14:22:46 -05:00
}
2023-02-13 18:43:53 -05:00
pub fn get_esm_entry_point ( & self ) -> Option < & 'static str > {
self . esm_entry_point
}
2021-04-28 12:41:50 -04:00
/// Called at JsRuntime startup to initialize ops in the isolate.
2022-03-15 18:43:17 -04:00
pub fn init_ops ( & mut self ) -> Option < Vec < OpDecl > > {
2021-04-28 12:41:50 -04:00
// TODO(@AaronO): maybe make op registration idempotent
if self . initialized {
panic! ( " init_ops called twice: not idempotent or correct " ) ;
}
self . initialized = true ;
2022-03-22 11:39:58 -04:00
let mut ops = self . ops . take ( ) ? ;
for op in ops . iter_mut ( ) {
op . enabled = self . enabled & & op . enabled ;
2023-03-18 18:30:04 -04:00
op . force_registration = self . force_op_registration ;
2022-03-22 11:39:58 -04:00
}
Some ( ops )
2021-04-28 12:41:50 -04:00
}
/// Allows setting up the initial op-state of an isolate at startup.
2023-03-17 18:15:27 -04:00
pub fn init_state ( & mut self , state : & mut OpState ) {
if let Some ( op_fn ) = self . opstate_fn . take ( ) {
2023-03-07 16:37:37 -05:00
op_fn ( state ) ;
2021-04-28 12:41:50 -04:00
}
}
/// init_middleware lets us middleware op registrations, it's called before init_ops
2021-05-07 09:45:07 -04:00
pub fn init_middleware ( & mut self ) -> Option < Box < OpMiddlewareFn > > {
2021-04-28 12:41:50 -04:00
self . middleware_fn . take ( )
}
2022-03-08 09:40:34 -05:00
pub fn init_event_loop_middleware ( & mut self ) -> Option < Box < OpEventLoopFn > > {
self . event_loop_middleware . take ( )
}
pub fn run_event_loop_middleware (
& self ,
2022-06-28 05:23:36 -04:00
op_state_rc : Rc < RefCell < OpState > > ,
2022-03-08 09:40:34 -05:00
cx : & mut Context ,
) -> bool {
self
. event_loop_middleware
. as_ref ( )
2022-06-28 05:23:36 -04:00
. map ( | f | f ( op_state_rc , cx ) )
2022-03-08 09:40:34 -05:00
. unwrap_or ( false )
}
2022-03-22 11:39:58 -04:00
pub fn enabled ( self , enabled : bool ) -> Self {
Self { enabled , .. self }
}
pub fn disable ( self ) -> Self {
self . enabled ( false )
}
2023-05-03 20:44:59 -04:00
pub ( crate ) fn find_esm (
& self ,
specifier : & str ,
) -> Option < & ExtensionFileSource > {
self
. get_esm_sources ( ) ?
. iter ( )
. find ( | s | s . specifier = = specifier )
}
2021-04-28 12:41:50 -04:00
}
2021-04-28 18:16:45 -04:00
// Provides a convenient builder pattern to declare Extensions
#[ derive(Default) ]
pub struct ExtensionBuilder {
2023-02-07 18:21:43 -05:00
js : Vec < ExtensionFileSource > ,
esm : Vec < ExtensionFileSource > ,
2023-02-13 18:43:53 -05:00
esm_entry_point : Option < & 'static str > ,
2022-03-15 18:43:17 -04:00
ops : Vec < OpDecl > ,
2021-04-28 18:16:45 -04:00
state : Option < Box < OpStateFn > > ,
middleware : Option < Box < OpMiddlewareFn > > ,
2022-03-08 09:40:34 -05:00
event_loop_middleware : Option < Box < OpEventLoopFn > > ,
2023-01-08 17:48:46 -05:00
name : & 'static str ,
2023-03-09 07:10:54 -05:00
deps : & 'static [ & 'static str ] ,
2023-03-18 18:30:04 -04:00
force_op_registration : bool ,
2023-04-13 20:41:32 -04:00
is_core : bool ,
2021-04-28 18:16:45 -04:00
}
impl ExtensionBuilder {
2023-02-07 18:21:43 -05:00
pub fn js ( & mut self , js_files : Vec < ExtensionFileSource > ) -> & mut Self {
2021-04-28 18:16:45 -04:00
self . js . extend ( js_files ) ;
self
}
2023-02-07 18:21:43 -05:00
pub fn esm ( & mut self , esm_files : Vec < ExtensionFileSource > ) -> & mut Self {
2023-02-07 16:09:50 -05:00
self . esm . extend ( esm_files ) ;
2023-02-07 14:22:46 -05:00
self
}
2023-02-13 18:43:53 -05:00
pub fn esm_entry_point ( & mut self , entry_point : & 'static str ) -> & mut Self {
self . esm_entry_point = Some ( entry_point ) ;
self
}
2022-03-15 18:43:17 -04:00
pub fn ops ( & mut self , ops : Vec < OpDecl > ) -> & mut Self {
2021-04-28 18:16:45 -04:00
self . ops . extend ( ops ) ;
self
}
pub fn state < F > ( & mut self , opstate_fn : F ) -> & mut Self
where
2023-03-17 18:15:27 -04:00
F : FnOnce ( & mut OpState ) + 'static ,
2021-04-28 18:16:45 -04:00
{
self . state = Some ( Box ::new ( opstate_fn ) ) ;
self
}
pub fn middleware < F > ( & mut self , middleware_fn : F ) -> & mut Self
where
2022-03-15 18:43:17 -04:00
F : Fn ( OpDecl ) -> OpDecl + 'static ,
2021-04-28 18:16:45 -04:00
{
self . middleware = Some ( Box ::new ( middleware_fn ) ) ;
self
}
2022-03-08 09:40:34 -05:00
pub fn event_loop_middleware < F > ( & mut self , middleware_fn : F ) -> & mut Self
where
2022-06-28 05:23:36 -04:00
F : Fn ( Rc < RefCell < OpState > > , & mut Context ) -> bool + 'static ,
2022-03-08 09:40:34 -05:00
{
self . event_loop_middleware = Some ( Box ::new ( middleware_fn ) ) ;
self
}
2023-03-18 18:30:04 -04:00
/// Mark that ops from this extension should be added to `Deno.core.ops`
/// unconditionally. This is useful is some ops are not available
/// during snapshotting, as ops are not registered by default when a
/// `JsRuntime` is created with an existing snapshot.
pub fn force_op_registration ( & mut self ) -> & mut Self {
self . force_op_registration = true ;
self
}
2023-03-17 18:15:27 -04:00
/// Consume the [`ExtensionBuilder`] and return an [`Extension`].
pub fn take ( self ) -> Extension {
let js_files = Some ( self . js ) ;
let esm_files = Some ( self . esm ) ;
let ops = Some ( self . ops ) ;
let deps = Some ( self . deps ) ;
Extension {
js_files ,
esm_files ,
esm_entry_point : self . esm_entry_point ,
ops ,
opstate_fn : self . state ,
middleware_fn : self . middleware ,
event_loop_middleware : self . event_loop_middleware ,
initialized : false ,
enabled : true ,
name : self . name ,
2023-03-18 18:30:04 -04:00
force_op_registration : self . force_op_registration ,
2023-03-17 18:15:27 -04:00
deps ,
2023-04-13 20:41:32 -04:00
is_core : self . is_core ,
2023-03-17 18:15:27 -04:00
}
}
2021-04-28 18:16:45 -04:00
pub fn build ( & mut self ) -> Extension {
let js_files = Some ( std ::mem ::take ( & mut self . js ) ) ;
2023-02-07 14:22:46 -05:00
let esm_files = Some ( std ::mem ::take ( & mut self . esm ) ) ;
2021-04-28 18:16:45 -04:00
let ops = Some ( std ::mem ::take ( & mut self . ops ) ) ;
2023-01-08 17:48:46 -05:00
let deps = Some ( std ::mem ::take ( & mut self . deps ) ) ;
2021-04-28 18:16:45 -04:00
Extension {
js_files ,
2023-02-07 14:22:46 -05:00
esm_files ,
2023-02-13 18:43:53 -05:00
esm_entry_point : self . esm_entry_point . take ( ) ,
2021-04-28 18:16:45 -04:00
ops ,
opstate_fn : self . state . take ( ) ,
middleware_fn : self . middleware . take ( ) ,
2022-03-08 09:40:34 -05:00
event_loop_middleware : self . event_loop_middleware . take ( ) ,
2021-04-28 18:16:45 -04:00
initialized : false ,
2022-03-22 11:39:58 -04:00
enabled : true ,
2023-01-08 17:48:46 -05:00
name : self . name ,
deps ,
2023-03-18 18:30:04 -04:00
force_op_registration : self . force_op_registration ,
2023-04-13 20:41:32 -04:00
is_core : self . is_core ,
2021-04-28 18:16:45 -04:00
}
}
2023-04-13 20:41:32 -04:00
#[ doc(hidden) ]
pub ( crate ) fn deno_core ( & mut self ) -> & mut Self {
self . is_core = true ;
self
}
2021-04-28 18:16:45 -04:00
}
2023-02-08 16:40:18 -05:00
/// Helps embed JS files in an extension. Returns a vector of
2023-02-19 19:11:56 -05:00
/// `ExtensionFileSource`, that represent the filename and source code. All
2023-03-08 06:44:54 -05:00
/// specified files are rewritten into "ext:<extension_name>/<file_name>".
2021-04-28 12:41:50 -04:00
///
2023-02-19 19:11:56 -05:00
/// An optional "dir" option can be specified to prefix all files with a
/// directory name.
///
/// Example (for "my_extension"):
2021-04-28 12:41:50 -04:00
/// ```ignore
/// include_js_files!(
/// "01_hello.js",
/// "02_goodbye.js",
/// )
2023-02-19 19:11:56 -05:00
/// // Produces following specifiers:
2023-03-08 06:44:54 -05:00
/// - "ext:my_extension/01_hello.js"
/// - "ext:my_extension/02_goodbye.js"
2023-02-08 16:40:18 -05:00
///
2023-02-19 19:11:56 -05:00
/// /// Example with "dir" option (for "my_extension"):
2023-02-08 16:40:18 -05:00
/// ```ignore
2023-02-19 19:11:56 -05:00
/// include_js_files!(
/// dir "js",
2023-02-08 16:40:18 -05:00
/// "01_hello.js",
/// "02_goodbye.js",
/// )
2023-02-19 19:11:56 -05:00
/// // Produces following specifiers:
2023-03-08 06:44:54 -05:00
/// - "ext:my_extension/js/01_hello.js"
/// - "ext:my_extension/js/02_goodbye.js"
2023-02-08 16:40:18 -05:00
/// ```
2023-02-20 15:45:34 -05:00
#[ cfg(not(feature = " include_js_files_for_snapshotting " )) ]
2023-02-08 16:40:18 -05:00
#[ macro_export ]
2023-02-19 19:11:56 -05:00
macro_rules ! include_js_files {
2023-03-18 16:09:13 -04:00
( $name :ident dir $dir :literal , $( $file :literal , ) + ) = > {
2023-02-08 16:40:18 -05:00
vec! [
$( $crate ::ExtensionFileSource {
2023-03-18 16:09:13 -04:00
specifier : concat ! ( " ext: " , stringify! ( $name ) , " / " , $file ) ,
2023-02-19 19:11:56 -05:00
code : $crate ::ExtensionFileSourceCode ::IncludedInBinary (
include_str! ( concat! ( $dir , " / " , $file )
) ) ,
} , ) +
]
} ;
2023-03-18 16:09:13 -04:00
( $name :ident $( $file :literal , ) + ) = > {
2023-02-19 19:11:56 -05:00
vec! [
$( $crate ::ExtensionFileSource {
2023-03-18 16:09:13 -04:00
specifier : concat ! ( " ext: " , stringify! ( $name ) , " / " , $file ) ,
2023-02-19 19:11:56 -05:00
code : $crate ::ExtensionFileSourceCode ::IncludedInBinary (
include_str! ( $file )
) ,
2023-02-08 16:40:18 -05:00
} , ) +
]
} ;
}
2023-02-20 15:45:34 -05:00
#[ cfg(feature = " include_js_files_for_snapshotting " ) ]
#[ macro_export ]
macro_rules ! include_js_files {
2023-03-18 16:09:13 -04:00
( $name :ident dir $dir :literal , $( $file :literal , ) + ) = > {
2023-02-20 15:45:34 -05:00
vec! [
$( $crate ::ExtensionFileSource {
2023-03-18 16:09:13 -04:00
specifier : concat ! ( " ext: " , stringify! ( $name ) , " / " , $file ) ,
2023-02-20 15:45:34 -05:00
code : $crate ::ExtensionFileSourceCode ::LoadedFromFsDuringSnapshot (
std ::path ::PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( $dir ) . join ( $file )
) ,
} , ) +
]
} ;
2023-03-18 16:09:13 -04:00
( $name :ident $( $file :literal , ) + ) = > {
2023-02-20 15:45:34 -05:00
vec! [
$( $crate ::ExtensionFileSource {
2023-03-18 16:09:13 -04:00
specifier : concat ! ( " ext: " , stringify! ( $name ) , " / " , $file ) ,
2023-02-20 15:45:34 -05:00
code : $crate ::ExtensionFileSourceCode ::LoadedFromFsDuringSnapshot (
std ::path ::PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( $file )
) ,
} , ) +
]
} ;
}