use std::borrow::Cow;
use std::cell::RefCell;
use std::iter::Chain;
use std::rc::Rc;
use crate::_ops::OpMethodDecl;
use crate::ExtensionFileSource;
use crate::FastString;
use crate::ModuleCodeString;
use crate::OpDecl;
use crate::OpMetricsFactoryFn;
use crate::OpState;
use crate::SourceMapData;
use crate::error::CoreError;
use crate::error::CoreErrorKind;
use crate::extensions::Extension;
use crate::extensions::ExtensionSourceType;
use crate::extensions::GlobalObjectMiddlewareFn;
use crate::extensions::GlobalTemplateMiddlewareFn;
use crate::extensions::OpMiddlewareFn;
use crate::modules::ModuleName;
use crate::ops::OpCtxs;
use crate::ops::OpDeclStorage;
use crate::ops::OpId;
use crate::runtime::ExtensionTranspiler;
use crate::runtime::JsRuntimeState;
use crate::runtime::OpDriverImpl;
pub fn setup_op_state(
op_state: &mut OpState,
extensions: &mut [Extension],
) -> Vec<&'static str> {
let mut lazy_extensions = Vec::with_capacity(extensions.len());
for ext in extensions {
if ext.needs_lazy_init {
lazy_extensions.push(ext.name);
}
ext.take_state(op_state);
}
lazy_extensions
}
pub fn init_ops(
deno_core_ops: &'static [OpDecl],
extensions: &mut [Extension],
) -> (Vec<OpDeclStorage>, Vec<OpMethodDecl>) {
#[cfg(debug_assertions)]
check_extensions_dependencies(extensions);
let mut ops = Vec::with_capacity(extensions.len() + 1);
let no_of_methods = extensions
.iter()
.map(|e| e.method_op_count())
.sum::<usize>();
let mut op_methods = Vec::with_capacity(no_of_methods);
let middlewares: Vec<Box<OpMiddlewareFn>> = extensions
.iter_mut()
.filter_map(|e| e.take_middleware())
.collect();
let has_middleware = !middlewares.is_empty();
let macroware = move |d| middlewares.iter().fold(d, |d, m| m(d));
let rewrite = |decls: &[OpDecl]| -> Vec<OpDecl> {
decls
.iter()
.map(|op| OpDecl {
name: op.name,
name_fast: op.name_fast,
..macroware(*op)
})
.collect()
};
if has_middleware {
ops.push(OpDeclStorage::Owned(rewrite(deno_core_ops)));
} else {
ops.push(OpDeclStorage::Static(deno_core_ops));
}
for ext in extensions.iter_mut() {
ext.init_ops();
let ext_ops = ext.take_ops();
if !ext_ops.is_empty() {
ops.push(if has_middleware {
OpDeclStorage::Owned(rewrite(&ext_ops))
} else {
match ext_ops {
Cow::Borrowed(decls) => OpDeclStorage::Static(decls),
Cow::Owned(decls) => OpDeclStorage::Owned(decls),
}
});
}
let ext_method_ops = ext.init_method_ops();
for ext_op in ext_method_ops {
op_methods.push(*ext_op);
}
}
#[cfg(debug_assertions)]
check_no_duplicate_op_names(&ops);
(ops, op_methods)
}
#[cfg(debug_assertions)]
fn check_extensions_dependencies(exts: &[Extension]) {
for (index, ext) in exts.iter().enumerate() {
let previous_exts = &exts[..index];
ext.check_dependencies(previous_exts);
}
}
#[cfg(debug_assertions)]
fn check_no_duplicate_op_names(ops: &[OpDeclStorage]) {
use std::collections::HashMap;
let mut count_by_name = HashMap::new();
for op in ops.iter().flat_map(|s| s.iter()) {
count_by_name.entry(op.name).or_insert(vec![]).push(op.name);
}
let mut duplicate_ops = vec![];
for (op_name, _count) in count_by_name.iter().filter(|(_k, v)| v.len() > 1) {
duplicate_ops.push(op_name.to_string());
}
if !duplicate_ops.is_empty() {
let mut msg = "Found ops with duplicate names:\n".to_string();
for op_name in duplicate_ops {
msg.push_str(&format!(" - {}\n", op_name));
}
msg.push_str("Op names need to be unique.");
panic!("{}", msg);
}
}
#[allow(clippy::too_many_arguments, reason = "all arguments are needed")]
pub fn create_op_ctxs(
op_decls: Vec<OpDeclStorage>,
op_method_decls: &mut [OpMethodDecl],
op_metrics_factory_fn: Option<OpMetricsFactoryFn>,
op_driver: Rc<OpDriverImpl>,
op_state: Rc<RefCell<OpState>>,
runtime_state: Rc<JsRuntimeState>,
enable_stack_trace_in_ops: bool,
) -> (OpCtxs, usize) {
let op_count = op_decls.iter().map(|storage| storage.len()).sum::<usize>()
+ op_method_decls.len();
let mut decls =
Vec::with_capacity(op_method_decls.len() * 3 + op_decls.len());
let mut ids: Vec<OpId> = Vec::with_capacity(op_count);
for (index, decl) in op_method_decls.iter_mut().enumerate() {
if let Some(mut constructor) = decl.constructor {
constructor.name = decl.name.0;
constructor.name_fast = decl.name.1;
decls.push(OpDeclStorage::Owned(vec![constructor]));
ids.push(index as _);
}
for methods in [decl.methods, decl.static_methods] {
if !methods.is_empty() {
decls.push(OpDeclStorage::Static(methods));
ids.extend(std::iter::repeat_n(index as OpId, methods.len()));
}
}
}
let methods_ctx_offset = ids.len();
for storage in op_decls {
let start = ids.len();
ids.extend((start..start + storage.len()).map(|id| id as OpId));
decls.push(storage);
}
let runtime_state_ptr = runtime_state.as_ref() as *const _;
let op_ctxs = OpCtxs::new(
decls,
op_state,
op_driver,
runtime_state_ptr,
enable_stack_trace_in_ops,
|index, decl| {
let id = ids[index];
let metrics_fn = op_metrics_factory_fn
.as_ref()
.and_then(|f| (f)(id, op_count, decl));
(id, metrics_fn)
},
);
(op_ctxs, methods_ctx_offset)
}
pub fn get_middlewares_and_external_refs(
extensions: &mut [Extension],
) -> (
Vec<GlobalTemplateMiddlewareFn>,
Vec<GlobalObjectMiddlewareFn>,
Vec<v8::ExternalReference>,
) {
let mut global_template_middlewares = Vec::with_capacity(16);
let mut global_object_middlewares = Vec::with_capacity(16);
let mut additional_references = Vec::with_capacity(16);
for extension in extensions {
if let Some(middleware) = extension.get_global_template_middleware() {
global_template_middlewares.push(middleware);
}
if let Some(middleware) = extension.get_global_object_middleware() {
global_object_middlewares.push(middleware);
}
additional_references
.extend_from_slice(extension.get_external_references());
}
(
global_template_middlewares,
global_object_middlewares,
additional_references,
)
}
#[derive(Debug)]
pub struct LoadedSource {
pub source_type: ExtensionSourceType,
pub specifier: ModuleName,
pub code: ModuleCodeString,
pub maybe_source_map: Option<SourceMapData>,
}
#[derive(Debug, Default)]
pub struct LoadedSources {
pub js: Vec<LoadedSource>,
pub esm: Vec<LoadedSource>,
pub lazy_esm: Vec<LoadedSource>,
pub lazy_js: Vec<LoadedSource>,
pub synthetic_esm: Vec<(ModuleName, ModuleName)>,
pub esm_entry_points: Vec<FastString>,
}
impl LoadedSources {
pub fn len(&self) -> usize {
self.js.len() + self.esm.len() + self.lazy_esm.len() + self.lazy_js.len()
}
pub fn is_empty(&self) -> bool {
self.js.is_empty()
&& self.esm.is_empty()
&& self.lazy_esm.is_empty()
&& self.lazy_js.is_empty()
}
}
type VecIntoIter<'a> = <&'a Vec<LoadedSource> as IntoIterator>::IntoIter;
type VecIntoIterMut<'a> = <&'a mut Vec<LoadedSource> as IntoIterator>::IntoIter;
impl<'a> IntoIterator for &'a LoadedSources {
type Item = &'a LoadedSource;
type IntoIter =
Chain<Chain<VecIntoIter<'a>, VecIntoIter<'a>>, VecIntoIter<'a>>;
fn into_iter(self) -> Self::IntoIter {
self
.js
.iter()
.chain(self.esm.iter())
.chain(self.lazy_esm.iter())
}
}
impl<'a> IntoIterator for &'a mut LoadedSources {
type Item = &'a mut LoadedSource;
type IntoIter =
Chain<Chain<VecIntoIterMut<'a>, VecIntoIterMut<'a>>, VecIntoIterMut<'a>>;
fn into_iter(self) -> Self::IntoIter {
self
.js
.iter_mut()
.chain(self.esm.iter_mut())
.chain(self.lazy_esm.iter_mut())
}
}
fn load(
transpiler: Option<&ExtensionTranspiler>,
source: &ExtensionFileSource,
) -> Result<(ModuleCodeString, Option<SourceMapData>), CoreError> {
let mut source_code = source.load()?;
let mut source_map = None;
if let Some(transpiler) = transpiler {
(source_code, source_map) =
transpiler(ModuleName::from_static(source.specifier), source_code)
.map_err(CoreErrorKind::ExtensionTranspiler)?;
}
let mut maybe_source_map = None;
if let Some(source_map) = source_map {
maybe_source_map = Some(source_map);
}
Ok((source_code, maybe_source_map))
}
pub fn into_sources_and_source_maps(
transpiler: Option<&ExtensionTranspiler>,
extensions: &[Extension],
) -> Result<LoadedSources, CoreError> {
let mut sources = LoadedSources::default();
for extension in extensions.iter() {
for file in &*extension.lazy_loaded_esm_files {
let (code, maybe_source_map) = load(transpiler, file)?;
sources.lazy_esm.push(LoadedSource {
source_type: ExtensionSourceType::LazyEsm,
specifier: ModuleName::from_static(file.specifier),
code,
maybe_source_map,
});
}
for file in &*extension.lazy_loaded_js_files {
let (code, maybe_source_map) = load(transpiler, file)?;
sources.lazy_js.push(LoadedSource {
source_type: ExtensionSourceType::LazyJs,
specifier: ModuleName::from_static(file.specifier),
code,
maybe_source_map,
});
}
for (module_spec, backing_spec) in &*extension.synthetic_esm_modules {
sources.synthetic_esm.push((
ModuleName::from_static(module_spec),
ModuleName::from_static(backing_spec),
));
}
if let Some(esm_entry_point) = extension.esm_entry_point {
sources
.esm_entry_points
.push(FastString::from_static(esm_entry_point));
}
for file in &*extension.js_files {
let (code, maybe_source_map) =
load(transpiler, file)?;
sources.js.push(LoadedSource {
source_type: ExtensionSourceType::Js,
specifier: ModuleName::from_static(file.specifier),
code,
maybe_source_map,
});
}
for file in &*extension.esm_files {
let (code, maybe_source_map) =
load(transpiler, file)?;
sources.esm.push(LoadedSource {
source_type: ExtensionSourceType::Esm,
specifier: ModuleName::from_static(file.specifier),
code,
maybe_source_map,
});
}
}
Ok(sources)
}