compiler.rs 10.4 KB
Newer Older
R
Ryan Dahl 已提交
1
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
A
andy finch 已提交
2 3
use core::ops::Deref;
use crate::flags::DenoFlags;
4
use crate::isolate_state::*;
A
andy finch 已提交
5
use crate::js_errors::JSErrorColor;
A
Andy Hayden 已提交
6
use crate::msg;
7
use crate::ops;
A
Andy Hayden 已提交
8 9
use crate::resources;
use crate::resources::ResourceId;
10
use crate::startup_data;
A
Andy Hayden 已提交
11
use crate::workers;
12
use crate::workers::WorkerBehavior;
A
andy finch 已提交
13
use crate::workers::WorkerInit;
14 15 16
use deno::deno_buf;
use deno::Behavior;
use deno::Buf;
A
andy finch 已提交
17
use deno::JSError;
18 19
use deno::Op;
use deno::StartupData;
A
andy finch 已提交
20 21
use futures::future::*;
use futures::sync::oneshot;
R
Ryan Dahl 已提交
22 23
use futures::Future;
use serde_json;
K
Kitson Kelly 已提交
24
use std::str;
25
use std::sync::Arc;
R
Ryan Dahl 已提交
26
use std::sync::Mutex;
A
andy finch 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
use tokio::runtime::Runtime;

/// Used for normalization of types on internal future completions
type CompilerInnerResult = Result<ModuleMetaData, Option<JSError>>;
type WorkerErrReceiver = oneshot::Receiver<CompilerInnerResult>;

/// Shared resources for used to complete compiler operations.
/// rid is the resource id for compiler worker resource used for sending it
/// compile requests
/// worker_err_receiver is a shared future that will compelete when the
/// compiler worker future completes, and send back an error if present
/// or a None if not
#[derive(Clone)]
struct CompilerShared {
  pub rid: ResourceId,
  pub worker_err_receiver: Shared<WorkerErrReceiver>,
}
R
Ryan Dahl 已提交
44 45

lazy_static! {
A
andy finch 已提交
46 47 48 49 50
  // Shared worker resources so we can spawn
  static ref C_SHARED: Mutex<Option<CompilerShared>> = Mutex::new(None);
  // tokio runtime specifically for spawning logic that is dependent on
  // completetion of the compiler worker future
  static ref C_RUNTIME: Mutex<Runtime> = Mutex::new(Runtime::new().unwrap());
R
Ryan Dahl 已提交
51 52
}

53 54 55 56 57
pub struct CompilerBehavior {
  pub state: Arc<IsolateState>,
}

impl CompilerBehavior {
A
andy finch 已提交
58 59 60 61
  pub fn new(flags: DenoFlags, argv_rest: Vec<String>) -> Self {
    Self {
      state: Arc::new(IsolateState::new(flags, argv_rest, None, true)),
    }
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  }
}

impl IsolateStateContainer for CompilerBehavior {
  fn state(&self) -> Arc<IsolateState> {
    self.state.clone()
  }
}

impl IsolateStateContainer for &CompilerBehavior {
  fn state(&self) -> Arc<IsolateState> {
    self.state.clone()
  }
}

impl Behavior for CompilerBehavior {
  fn startup_data(&mut self) -> Option<StartupData> {
    Some(startup_data::compiler_isolate_init())
  }

  fn dispatch(
    &mut self,
    control: &[u8],
    zero_copy: deno_buf,
  ) -> (bool, Box<Op>) {
87
    ops::dispatch_all(self, control, zero_copy, ops::op_selector_compiler)
88 89 90 91 92 93 94 95 96
  }
}

impl WorkerBehavior for CompilerBehavior {
  fn set_internal_channels(&mut self, worker_channels: WorkerChannels) {
    self.state = Arc::new(IsolateState::new(
      self.state.flags.clone(),
      self.state.argv.clone(),
      Some(worker_channels),
A
andy finch 已提交
97
      true,
98 99 100 101
    ));
  }
}

R
Ryan Dahl 已提交
102 103
// This corresponds to JS ModuleMetaData.
// TODO Rename one or the other so they correspond.
A
andy finch 已提交
104
#[derive(Debug, Clone)]
K
Kitson Kelly 已提交
105
pub struct ModuleMetaData {
R
Ryan Dahl 已提交
106
  pub module_name: String,
107
  pub module_redirect_source_name: Option<String>, // source of redirect
R
Ryan Dahl 已提交
108 109
  pub filename: String,
  pub media_type: msg::MediaType,
K
Kitson Kelly 已提交
110
  pub source_code: Vec<u8>,
111
  pub maybe_output_code_filename: Option<String>,
K
Kitson Kelly 已提交
112
  pub maybe_output_code: Option<Vec<u8>>,
113
  pub maybe_source_map_filename: Option<String>,
K
Kitson Kelly 已提交
114
  pub maybe_source_map: Option<Vec<u8>>,
R
Ryan Dahl 已提交
115 116
}

K
Kitson Kelly 已提交
117
impl ModuleMetaData {
118 119 120 121
  pub fn has_output_code_and_source_map(&self) -> bool {
    self.maybe_output_code.is_some() && self.maybe_source_map.is_some()
  }

122
  pub fn js_source(&self) -> String {
R
Ryan Dahl 已提交
123
    if self.media_type == msg::MediaType::Json {
K
Kitson Kelly 已提交
124 125 126 127
      return format!(
        "export default {};",
        str::from_utf8(&self.source_code).unwrap()
      );
R
Ryan Dahl 已提交
128 129
    }
    match self.maybe_output_code {
K
Kitson Kelly 已提交
130 131
      None => str::from_utf8(&self.source_code).unwrap().to_string(),
      Some(ref output_code) => str::from_utf8(output_code).unwrap().to_string(),
R
Ryan Dahl 已提交
132 133 134 135
    }
  }
}

A
andy finch 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149
fn lazy_start(parent_state: Arc<IsolateState>) -> CompilerShared {
  let mut cell = C_SHARED.lock().unwrap();
  cell
    .get_or_insert_with(|| {
      let worker_result = workers::spawn(
        CompilerBehavior::new(
          parent_state.flags.clone(),
          parent_state.argv.clone(),
        ),
        "TS",
        WorkerInit::Script("compilerMain()".to_string()),
      );
      match worker_result {
        Ok(worker) => {
B
Bert Belder 已提交
150
          let rid = worker.resource.rid;
A
andy finch 已提交
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
          // create oneshot channels and use the sender to pass back
          // results from worker future
          let (err_sender, err_receiver) =
            oneshot::channel::<CompilerInnerResult>();
          let mut runtime = C_RUNTIME.lock().unwrap();
          runtime.spawn(lazy(move || {
            let resource = worker.resource.clone();
            worker.then(move |result| -> Result<(), ()> {
              resource.close();
              match result {
                Err(err) => err_sender.send(Err(Some(err))).unwrap(),
                _ => err_sender.send(Err(None)).unwrap(),
              };
              Ok(())
            })
          }));
          CompilerShared {
            rid,
            worker_err_receiver: err_receiver.shared(),
          }
        }
        Err(err) => {
          println!("{}", err.to_string());
          std::process::exit(1);
        }
      }
    }).clone()
R
Ryan Dahl 已提交
178 179
}

A
andy finch 已提交
180 181 182 183 184 185
fn show_compiler_error(err: JSError) -> ModuleMetaData {
  eprintln!("{}", JSErrorColor(&err).to_string());
  std::process::exit(1);
}

fn req(specifier: &str, referrer: &str, is_worker_main: bool) -> Buf {
R
Ryan Dahl 已提交
186 187 188
  json!({
    "specifier": specifier,
    "referrer": referrer,
A
andy finch 已提交
189
    "isWorker": is_worker_main
R
Ryan Dahl 已提交
190 191 192 193 194 195
  }).to_string()
  .into_boxed_str()
  .into_boxed_bytes()
}

pub fn compile_sync(
196
  parent_state: Arc<IsolateState>,
R
Ryan Dahl 已提交
197 198
  specifier: &str,
  referrer: &str,
K
Kitson Kelly 已提交
199 200
  module_meta_data: &ModuleMetaData,
) -> ModuleMetaData {
201 202 203 204 205 206 207 208
  debug!(
    "Running rust part of compile_sync. specifier: {}, referrer: {}",
    &specifier, &referrer
  );

  let req_msg = req(specifier, referrer, parent_state.is_worker);
  let module_meta_data_ = module_meta_data.clone();

A
andy finch 已提交
209
  let shared = lazy_start(parent_state);
210
  let compiler_rid = shared.rid;
A
andy finch 已提交
211 212 213 214

  let (local_sender, local_receiver) =
    oneshot::channel::<Result<ModuleMetaData, Option<JSError>>>();

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
  let mut runtime = C_RUNTIME.lock().unwrap();
  runtime.spawn(lazy(move || {
    resources::post_message_to_worker(compiler_rid, req_msg)
      .then(move |_| {
        debug!("Sent message to worker");
        resources::get_message_from_worker(compiler_rid)
      }).and_then(move |res_msg| {
        debug!("Received message from worker");
        let res_json = std::str::from_utf8(res_msg.as_ref().unwrap()).unwrap();
        let res_data = serde_json::from_str::<serde_json::Value>(res_json)
          .expect("Error decoding compiler response");
        let res_module_meta_data = ModuleMetaData {
          maybe_output_code: res_data["outputCode"]
            .as_str()
            .map(|s| s.as_bytes().to_owned()),
          maybe_source_map: res_data["sourceMap"]
            .as_str()
            .map(|s| s.as_bytes().to_owned()),
          ..module_meta_data_
        };
        Ok(res_module_meta_data)
      }).map_err(|_| None)
      .then(move |result| {
        local_sender.send(result).expect("Oneshot send() failed");
        Ok(())
      })
  }));
A
andy finch 已提交
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293

  let worker_receiver = shared.worker_err_receiver.clone();

  let union =
    futures::future::select_all(vec![worker_receiver, local_receiver.shared()]);

  match union.wait() {
    Ok((result, i, rest)) => {
      // We got a sucessful finish before any recivers where canceled
      let mut rest_mut = rest;
      match ((*result.deref()).clone(), i) {
        // Either receiver was completed with success.
        (Ok(v), _) => v,
        // Either receiver was completed with a valid error
        // this should be fatal for now since it is not intended
        // to be possible to recover from a uncaught error in a isolate
        (Err(Some(err)), _) => show_compiler_error(err),
        // local_receiver finished first with a none error. This is intended
        // to catch when the local logic can't complete because it is unable
        // to send and/or receive messages from the compiler worker.
        // Due to the way that scheduling works it is very likely that the
        // compiler worker future has already or will in the near future
        // complete with a valid JSError or a None.
        (Err(None), 1) => {
          debug!("Compiler local exited with None error!");
          // While technically possible to get stuck here indefinately
          // in theory it is highly unlikely.
          debug!(
            "Waiting on compiler worker result specifier: {} referrer: {}!",
            specifier, referrer
          );
          let worker_result =
            (*rest_mut.remove(0).wait().unwrap().deref()).clone();
          debug!(
            "Finished waiting on worker result specifier: {} referrer: {}!",
            specifier, referrer
          );
          match worker_result {
            Err(Some(err)) => show_compiler_error(err),
            Err(None) => panic!("Compiler exit for an unknown reason!"),
            Ok(v) => v,
          }
        }
        // While possible beccause the compiler worker can exit without error
        // this shouldn't occurr normally and I don't intend to attempt to
        // handle it right now
        (_, i) => panic!("Odd compiler result for future {}!", i),
      }
    }
    // This should always a result of a reciver being cancled
    // in theory but why not give a print out just in case
    Err((err, i, _)) => panic!("compile_sync {} failed: {}", i, err),
K
Kitson Kelly 已提交
294
  }
R
Ryan Dahl 已提交
295 296 297 298 299
}

#[cfg(test)]
mod tests {
  use super::*;
A
andy finch 已提交
300
  use crate::tokio_util;
R
Ryan Dahl 已提交
301 302 303

  #[test]
  fn test_compile_sync() {
A
andy finch 已提交
304 305 306 307 308 309 310 311 312
    tokio_util::init(|| {
      let cwd = std::env::current_dir().unwrap();
      let cwd_string = cwd.to_str().unwrap().to_owned();

      let specifier = "./tests/002_hello.ts";
      let referrer = cwd_string + "/";

      let mut out = ModuleMetaData {
        module_name: "xxx".to_owned(),
313
        module_redirect_source_name: None,
A
andy finch 已提交
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
        filename: "/tests/002_hello.ts".to_owned(),
        media_type: msg::MediaType::TypeScript,
        source_code: include_bytes!("../tests/002_hello.ts").to_vec(),
        maybe_output_code_filename: None,
        maybe_output_code: None,
        maybe_source_map_filename: None,
        maybe_source_map: None,
      };

      out = compile_sync(
        Arc::new(IsolateState::mock()),
        specifier,
        &referrer,
        &out,
      );
      assert!(
        out
          .maybe_output_code
          .unwrap()
          .starts_with("console.log(\"Hello World\");".as_bytes())
      );
    });
R
Ryan Dahl 已提交
336 337
  }
}