use deno_core::error::AnyError;
use deno_core::error::JsError;
use deno_core::located_script_name;
use deno_core::v8;
use deno_core::CompiledWasmModuleStore;
use deno_core::Extension;
use deno_core::FsModuleLoader;
use deno_core::GetErrorClassFn;
use deno_core::JsRuntime;
use deno_core::ModuleCode;
use deno_core::ModuleId;
use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier;
use deno_core::RuntimeOptions;
use deno_core::SharedArrayBufferStore;
use deno_core::Snapshot;
use deno_http::DefaultHttpPropertyExtractor;
use log::debug;
use std::rc::Rc;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::Arc;
use crate::permissions::{swarmd_permissions, Permissions};
use crate::js::swarmd_isolate_init;
use crate::bootstrap::BootstrapOptions;
use crate::ops;
use crate::shared::runtime;
use tracing::instrument;
pub type FormatJsErrorFn = dyn Fn(&JsError) -> String + Sync + Send;
#[derive(Clone, Default)]
pub struct ExitCode(Arc<AtomicI32>);
impl ExitCode {
pub fn get(&self) -> i32 {
self.0.load(Relaxed)
}
pub fn set(&mut self, code: i32) {
self.0.store(code, Relaxed);
}
}
pub struct SwarmdWorker {
pub js_runtime: JsRuntime,
exit_code: ExitCode,
}
pub struct WorkerOptions {
pub bootstrap: BootstrapOptions,
pub extensions: Vec<Extension>,
pub startup_snapshot: Option<Snapshot>,
pub create_params: Option<v8::CreateParams>,
pub seed: Option<u64>,
pub module_loader: Rc<dyn ModuleLoader>,
pub strace_ops: Option<Vec<String>>,
pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
pub get_error_class_fn: Option<GetErrorClassFn>,
pub shared_array_buffer_store: Option<SharedArrayBufferStore>,
pub compiled_wasm_module_store: Option<CompiledWasmModuleStore>,
}
impl Default for WorkerOptions {
fn default() -> Self {
Self {
module_loader: Rc::new(FsModuleLoader),
seed: None,
strace_ops: Default::default(),
compiled_wasm_module_store: Default::default(),
shared_array_buffer_store: Default::default(),
format_js_error_fn: Default::default(),
get_error_class_fn: Default::default(),
extensions: Default::default(),
startup_snapshot: Default::default(),
create_params: Default::default(),
bootstrap: Default::default(),
}
}
}
impl SwarmdWorker {
#[instrument(skip(main_module, options))]
pub fn bootstrap_from_options(main_module: ModuleSpecifier, options: WorkerOptions) -> Self {
let bootstrap_options = options.bootstrap.clone();
let mut worker = Self::from_options(main_module, options);
worker.bootstrap(bootstrap_options);
worker
}
#[instrument(skip(main_module, options))]
pub fn from_options(main_module: ModuleSpecifier, mut options: WorkerOptions) -> Self {
let exit_code = ExitCode(Arc::new(AtomicI32::new(0)));
let user_agent = "Swarmd/Edge-Local".to_string();
let mut extensions: Vec<Extension> = vec![
swarmd_permissions::init_ops(),
deno_webidl::deno_webidl::init_ops_and_esm(),
deno_console::deno_console::init_ops_and_esm(),
deno_url::deno_url::init_ops_and_esm(),
deno_web::deno_web::init_ops_and_esm::<Permissions>(
Arc::new(deno_web::BlobStore::default()),
None,
),
deno_fetch::deno_fetch::init_ops_and_esm::<Permissions>(deno_fetch::Options {
user_agent: user_agent.clone(),
..Default::default()
}),
deno_websocket::deno_websocket::init_ops_and_esm::<Permissions>(user_agent, None, None),
deno_crypto::deno_crypto::init_ops_and_esm(None),
deno_net::deno_net::init_ops_and_esm::<Permissions>(None, None),
deno_tls::deno_tls::init_ops_and_esm(),
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
ops::runtime::swarmd_runtime::init_ops_and_esm(main_module.clone()),
runtime::init_ops_and_esm(),
ops::bootstrap::swarmd_bootstrap::init_ops_and_esm(),
ops::http::swarmd_http_runtime::init_ops_and_esm(),
];
for extension in &mut extensions {
extension.js_files = std::borrow::Cow::Borrowed(&[]);
extension.esm_files = std::borrow::Cow::Borrowed(&[]);
extension.esm_entry_point = None;
}
extensions.extend(std::mem::take(&mut options.extensions));
let js_runtime = JsRuntime::new(RuntimeOptions {
module_loader: Some(options.module_loader.clone()),
startup_snapshot: options
.startup_snapshot
.or_else(|| Some(swarmd_isolate_init())),
create_params: options.create_params,
source_map_getter: None,
skip_op_registration: false,
get_error_class_fn: options.get_error_class_fn,
shared_array_buffer_store: options.shared_array_buffer_store.clone(),
compiled_wasm_module_store: options.compiled_wasm_module_store.clone(),
extensions,
preserve_snapshotted_modules: None,
inspector: false,
is_main: true,
feature_checker: None,
op_metrics_factory_fn: None,
..Default::default()
});
Self {
js_runtime,
exit_code,
}
}
pub fn bootstrap(&mut self, options: BootstrapOptions) {
self.js_runtime.op_state().borrow_mut().put(options.clone());
let version = env!("GIT_HASH");
let target = env!("TARGET");
let script = format!("globalThis.bootstrapWorker('{}', '{}')", version, target);
self.js_runtime
.execute_script(located_script_name!(), ModuleCode::from(script))
.unwrap();
}
pub fn execute_script(
&mut self,
script_name: &'static str,
source_code: ModuleCode,
) -> Result<v8::Global<v8::Value>, AnyError> {
self.js_runtime.execute_script(script_name, source_code)
}
pub async fn preload_main_module(
&mut self,
module_specifier: &ModuleSpecifier,
) -> Result<ModuleId, AnyError> {
self.js_runtime
.load_main_module(module_specifier, None)
.await
}
pub async fn preload_side_module(
&mut self,
module_specifier: &ModuleSpecifier,
) -> Result<ModuleId, AnyError> {
self.js_runtime
.load_side_module(module_specifier, None)
.await
}
pub async fn evaluate_module(&mut self, id: ModuleId) -> Result<(), AnyError> {
let mut receiver = self.js_runtime.mod_evaluate(id);
tokio::select! {
biased;
maybe_result = &mut receiver => {
debug!("received module evaluate {:#?}", maybe_result);
maybe_result.expect("Module evaluation result not provided.")
}
event_loop_result = self.run_event_loop(false) => {
event_loop_result?;
let maybe_result = receiver.await;
maybe_result.expect("Module evaluation result not provided.")
}
}
}
pub async fn execute_side_module(
&mut self,
module_specifier: &ModuleSpecifier,
) -> Result<(), AnyError> {
let id = self.preload_side_module(module_specifier).await?;
self.evaluate_module(id).await
}
pub async fn execute_main_module(
&mut self,
module_specifier: &ModuleSpecifier,
) -> Result<(), AnyError> {
let id = self.preload_main_module(module_specifier).await?;
self.evaluate_module(id).await
}
pub async fn run_event_loop(&mut self, wait_for_inspector: bool) -> Result<(), AnyError> {
self.js_runtime
.run_event_loop2(deno_core::PollEventLoopOptions {
wait_for_inspector,
..Default::default()
})
.await
}
pub fn exit_code(&self) -> i32 {
self.exit_code.get()
}
}