deno_runtime/
worker_bootstrap.rs

1// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2
3use deno_core::v8;
4use deno_core::ModuleSpecifier;
5use deno_telemetry::OtelConfig;
6use serde::Serialize;
7use std::cell::RefCell;
8use std::thread;
9
10use deno_terminal::colors;
11
12/// The execution mode for this worker. Some modes may have implicit behaviour.
13#[derive(Copy, Clone)]
14pub enum WorkerExecutionMode {
15  /// No special behaviour.
16  None,
17
18  /// Running in a worker.
19  Worker,
20  /// `deno run`
21  Run,
22  /// `deno repl`
23  Repl,
24  /// `deno eval`
25  Eval,
26  /// `deno test`
27  Test,
28  /// `deno bench`
29  Bench,
30  /// `deno serve`
31  Serve {
32    is_main: bool,
33    worker_count: Option<usize>,
34  },
35  /// `deno jupyter`
36  Jupyter,
37}
38
39impl WorkerExecutionMode {
40  pub fn discriminant(&self) -> u8 {
41    match self {
42      WorkerExecutionMode::None => 0,
43      WorkerExecutionMode::Worker => 1,
44      WorkerExecutionMode::Run => 2,
45      WorkerExecutionMode::Repl => 3,
46      WorkerExecutionMode::Eval => 4,
47      WorkerExecutionMode::Test => 5,
48      WorkerExecutionMode::Bench => 6,
49      WorkerExecutionMode::Serve { .. } => 7,
50      WorkerExecutionMode::Jupyter => 8,
51    }
52  }
53  pub fn serve_info(&self) -> (Option<bool>, Option<usize>) {
54    match *self {
55      WorkerExecutionMode::Serve {
56        is_main,
57        worker_count,
58      } => (Some(is_main), worker_count),
59      _ => (None, None),
60    }
61  }
62}
63
64/// The log level to use when printing diagnostic log messages, warnings,
65/// or errors in the worker.
66///
67/// Note: This is disconnected with the log crate's log level and the Rust code
68/// in this crate will respect that value instead. To specify that, use
69/// `log::set_max_level`.
70#[derive(Debug, Default, Clone, Copy)]
71pub enum WorkerLogLevel {
72  // WARNING: Ensure this is kept in sync with
73  // the JS values (search for LogLevel).
74  Error = 1,
75  Warn = 2,
76  #[default]
77  Info = 3,
78  Debug = 4,
79}
80
81impl From<log::Level> for WorkerLogLevel {
82  fn from(value: log::Level) -> Self {
83    match value {
84      log::Level::Error => WorkerLogLevel::Error,
85      log::Level::Warn => WorkerLogLevel::Warn,
86      log::Level::Info => WorkerLogLevel::Info,
87      log::Level::Debug => WorkerLogLevel::Debug,
88      log::Level::Trace => WorkerLogLevel::Debug,
89    }
90  }
91}
92
93/// Common bootstrap options for MainWorker & WebWorker
94#[derive(Clone)]
95pub struct BootstrapOptions {
96  pub deno_version: String,
97  /// Sets `Deno.args` in JS runtime.
98  pub args: Vec<String>,
99  pub cpu_count: usize,
100  pub log_level: WorkerLogLevel,
101  pub enable_op_summary_metrics: bool,
102  pub enable_testing_features: bool,
103  pub locale: String,
104  pub location: Option<ModuleSpecifier>,
105  /// Sets `Deno.noColor` in JS runtime.
106  pub no_color: bool,
107  pub is_stdout_tty: bool,
108  pub is_stderr_tty: bool,
109  pub color_level: deno_terminal::colors::ColorLevel,
110  // --unstable-* flags
111  pub unstable_features: Vec<i32>,
112  pub user_agent: String,
113  pub inspect: bool,
114  pub has_node_modules_dir: bool,
115  pub argv0: Option<String>,
116  pub node_debug: Option<String>,
117  pub node_ipc_fd: Option<i64>,
118  pub mode: WorkerExecutionMode,
119  // Used by `deno serve`
120  pub serve_port: Option<u16>,
121  pub serve_host: Option<String>,
122  // OpenTelemetry output options. If `None`, OpenTelemetry is disabled.
123  pub otel_config: Option<OtelConfig>,
124}
125
126impl Default for BootstrapOptions {
127  fn default() -> Self {
128    let cpu_count = thread::available_parallelism()
129      .map(|p| p.get())
130      .unwrap_or(1);
131
132    let runtime_version = env!("CARGO_PKG_VERSION");
133    let user_agent = format!("Deno/{runtime_version}");
134
135    Self {
136      deno_version: runtime_version.to_string(),
137      user_agent,
138      cpu_count,
139      no_color: !colors::use_color(),
140      is_stdout_tty: deno_terminal::is_stdout_tty(),
141      is_stderr_tty: deno_terminal::is_stderr_tty(),
142      color_level: colors::get_color_level(),
143      enable_op_summary_metrics: Default::default(),
144      enable_testing_features: Default::default(),
145      log_level: Default::default(),
146      locale: "en".to_string(),
147      location: Default::default(),
148      unstable_features: Default::default(),
149      inspect: Default::default(),
150      args: Default::default(),
151      has_node_modules_dir: Default::default(),
152      argv0: None,
153      node_debug: None,
154      node_ipc_fd: None,
155      mode: WorkerExecutionMode::None,
156      serve_port: Default::default(),
157      serve_host: Default::default(),
158      otel_config: None,
159    }
160  }
161}
162
163/// This is a struct that we use to serialize the contents of the `BootstrapOptions`
164/// struct above to a V8 form. While `serde_v8` is not as fast as hand-coding this,
165/// it's "fast enough" while serializing a large tuple like this that it doesn't appear
166/// on flamegraphs.
167///
168/// Note that a few fields in here are derived from the process and environment and
169/// are not sourced from the underlying `BootstrapOptions`.
170///
171/// Keep this in sync with `99_main.js`.
172#[derive(Serialize)]
173struct BootstrapV8<'a>(
174  // deno version
175  &'a str,
176  // location
177  Option<&'a str>,
178  // granular unstable flags
179  &'a [i32],
180  // inspect
181  bool,
182  // enable_testing_features
183  bool,
184  // has_node_modules_dir
185  bool,
186  // argv0
187  Option<&'a str>,
188  // node_debug
189  Option<&'a str>,
190  // mode
191  i32,
192  // serve port
193  u16,
194  // serve host
195  Option<&'a str>,
196  // serve is main
197  Option<bool>,
198  // serve worker count
199  Option<usize>,
200  // OTEL config
201  Box<[u8]>,
202);
203
204impl BootstrapOptions {
205  /// Return the v8 equivalent of this structure.
206  pub fn as_v8<'s>(
207    &self,
208    scope: &mut v8::HandleScope<'s>,
209  ) -> v8::Local<'s, v8::Value> {
210    let scope = RefCell::new(scope);
211    let ser = deno_core::serde_v8::Serializer::new(&scope);
212
213    let (serve_is_main, serve_worker_count) = self.mode.serve_info();
214    let bootstrap = BootstrapV8(
215      &self.deno_version,
216      self.location.as_ref().map(|l| l.as_str()),
217      self.unstable_features.as_ref(),
218      self.inspect,
219      self.enable_testing_features,
220      self.has_node_modules_dir,
221      self.argv0.as_deref(),
222      self.node_debug.as_deref(),
223      self.mode.discriminant() as _,
224      self.serve_port.unwrap_or_default(),
225      self.serve_host.as_deref(),
226      serve_is_main,
227      serve_worker_count,
228      if let Some(otel_config) = self.otel_config.as_ref() {
229        Box::new([otel_config.console as u8, otel_config.deterministic as u8])
230      } else {
231        Box::new([])
232      },
233    );
234
235    bootstrap.serialize(ser).unwrap()
236  }
237}