compiler.rs 6.3 KB
Newer Older
R
Ryan Dahl 已提交
1
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
2
use crate::diagnostics::Diagnostic;
A
Andy Hayden 已提交
3 4
use crate::msg;
use crate::resources;
5
use crate::startup_data;
6
use crate::state::*;
A
andy finch 已提交
7
use crate::tokio_util;
8 9
use crate::worker::Worker;
use deno::js_check;
10
use deno::Buf;
R
Ryan Dahl 已提交
11
use futures::Future;
12
use futures::Stream;
K
Kitson Kelly 已提交
13
use std::str;
14
use std::sync::atomic::Ordering;
R
Ryan Dahl 已提交
15 16 17

// This corresponds to JS ModuleMetaData.
// TODO Rename one or the other so they correspond.
A
andy finch 已提交
18
#[derive(Debug, Clone)]
K
Kitson Kelly 已提交
19
pub struct ModuleMetaData {
R
Ryan Dahl 已提交
20
  pub module_name: String,
21
  pub module_redirect_source_name: Option<String>, // source of redirect
R
Ryan Dahl 已提交
22 23
  pub filename: String,
  pub media_type: msg::MediaType,
K
Kitson Kelly 已提交
24
  pub source_code: Vec<u8>,
25
  pub maybe_output_code_filename: Option<String>,
K
Kitson Kelly 已提交
26
  pub maybe_output_code: Option<Vec<u8>>,
27
  pub maybe_source_map_filename: Option<String>,
K
Kitson Kelly 已提交
28
  pub maybe_source_map: Option<Vec<u8>>,
R
Ryan Dahl 已提交
29 30
}

K
Kitson Kelly 已提交
31
impl ModuleMetaData {
32 33 34 35
  pub fn has_output_code_and_source_map(&self) -> bool {
    self.maybe_output_code.is_some() && self.maybe_source_map.is_some()
  }

36
  pub fn js_source(&self) -> String {
R
Ryan Dahl 已提交
37
    if self.media_type == msg::MediaType::Json {
K
Kitson Kelly 已提交
38 39 40 41
      return format!(
        "export default {};",
        str::from_utf8(&self.source_code).unwrap()
      );
R
Ryan Dahl 已提交
42 43
    }
    match self.maybe_output_code {
K
Kitson Kelly 已提交
44 45
      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 已提交
46 47 48 49
    }
  }
}

R
Ryan Dahl 已提交
50
type CompilerConfig = Option<(String, Vec<u8>)>;
51

R
Ryan Dahl 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65
/// Creates the JSON message send to compiler.ts's onmessage.
fn req(root_names: Vec<String>, compiler_config: CompilerConfig) -> Buf {
  let j = if let Some((config_path, config_data)) = compiler_config {
    json!({
      "rootNames": root_names,
      "configPath": config_path,
      "config": str::from_utf8(&config_data).unwrap(),
    })
  } else {
    json!({
      "rootNames": root_names,
    })
  };
  j.to_string().into_boxed_str().into_boxed_bytes()
R
Ryan Dahl 已提交
66 67
}

68 69 70 71 72 73
/// Returns an optional tuple which represents the state of the compiler
/// configuration where the first is canonical name for the configuration file
/// and a vector of the bytes of the contents of the configuration file.
pub fn get_compiler_config(
  parent_state: &ThreadSafeState,
  _compiler_type: &str,
R
Ryan Dahl 已提交
74
) -> CompilerConfig {
75 76 77 78 79 80 81 82 83 84
  // The compiler type is being passed to make it easier to implement custom
  // compilers in the future.
  match (&parent_state.config_path, &parent_state.config) {
    (Some(config_path), Some(config)) => {
      Some((config_path.to_string(), config.to_vec()))
    }
    _ => None,
  }
}

A
andy finch 已提交
85
pub fn compile_async(
R
Ryan Dahl 已提交
86
  state: ThreadSafeState,
K
Kitson Kelly 已提交
87
  module_meta_data: &ModuleMetaData,
88
) -> impl Future<Item = ModuleMetaData, Error = Diagnostic> {
K
Kitson Kelly 已提交
89 90
  let module_name = module_meta_data.module_name.clone();

91
  debug!(
K
Kitson Kelly 已提交
92 93
    "Running rust part of compile_sync. module_name: {}",
    &module_name
94 95
  );

K
Kitson Kelly 已提交
96
  let root_names = vec![module_name.clone()];
R
Ryan Dahl 已提交
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
  let compiler_config = get_compiler_config(&state, "typescript");
  let req_msg = req(root_names, compiler_config);

  // Count how many times we start the compiler worker.
  state.metrics.compiler_starts.fetch_add(1, Ordering::SeqCst);

  let mut worker = Worker::new(
    "TS".to_string(),
    startup_data::compiler_isolate_init(),
    // TODO(ry) Maybe we should use a separate state for the compiler.
    // as was done previously.
    state.clone(),
  );
  js_check(worker.execute("denoMain()"));
  js_check(worker.execute("workerMain()"));
  js_check(worker.execute("compilerMain()"));
A
andy finch 已提交
113

K
Kitson Kelly 已提交
114
  let compiling_job = state.progress.add(format!("Compiling {}", module_name));
R
Ryan Dahl 已提交
115

R
Ryan Dahl 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
  let resource = worker.state.resource.clone();
  let compiler_rid = resource.rid;
  let first_msg_fut = resources::post_message_to_worker(compiler_rid, req_msg)
    .then(move |_| worker)
    .then(move |result| {
      if let Err(err) = result {
        // TODO(ry) Need to forward the error instead of exiting.
        eprintln!("{}", err.to_string());
        std::process::exit(1);
      }
      debug!("Sent message to worker");
      let stream_future =
        resources::get_message_stream_from_worker(compiler_rid).into_future();
      stream_future.map(|(f, _rest)| f).map_err(|(f, _rest)| f)
    });

  first_msg_fut
    .map_err(|_| panic!("not handled"))
    .and_then(move |maybe_msg: Option<Buf>| {
      debug!("Received message from worker");

137 138 139 140 141 142 143
      if let Some(msg) = maybe_msg {
        let json_str = std::str::from_utf8(&msg).unwrap();
        debug!("Message: {}", json_str);
        if let Some(diagnostics) = Diagnostic::from_emit_result(json_str) {
          return Err(diagnostics);
        }
      }
R
Ryan Dahl 已提交
144

K
Kitson Kelly 已提交
145 146 147
      let r = state
        .dir
        .fetch_module_meta_data(&module_name, ".", true, true);
R
Ryan Dahl 已提交
148
      let module_meta_data_after_compile = r.unwrap();
R
Ryan Dahl 已提交
149

R
Ryan Dahl 已提交
150 151
      // Explicit drop to keep reference alive until future completes.
      drop(compiling_job);
R
Ryan Dahl 已提交
152

R
Ryan Dahl 已提交
153 154 155 156 157
      Ok(module_meta_data_after_compile)
    }).then(move |r| {
      // TODO(ry) do this in worker's destructor.
      // resource.close();
      r
A
andy finch 已提交
158 159 160 161
    })
}

pub fn compile_sync(
R
Ryan Dahl 已提交
162
  state: ThreadSafeState,
A
andy finch 已提交
163
  module_meta_data: &ModuleMetaData,
164
) -> Result<ModuleMetaData, Diagnostic> {
K
Kitson Kelly 已提交
165
  tokio_util::block_on(compile_async(state, module_meta_data))
R
Ryan Dahl 已提交
166 167 168 169 170 171 172 173
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_compile_sync() {
A
andy finch 已提交
174 175
    tokio_util::init(|| {
      let specifier = "./tests/002_hello.ts";
R
Ryan Dahl 已提交
176 177 178 179
      use crate::worker;
      let module_name = worker::root_specifier_to_url(specifier)
        .unwrap()
        .to_string();
A
andy finch 已提交
180 181

      let mut out = ModuleMetaData {
R
Ryan Dahl 已提交
182
        module_name,
A
andy finch 已提交
183 184 185 186 187 188 189 190 191 192
        module_redirect_source_name: None,
        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,
      };

K
Kitson Kelly 已提交
193
      out = compile_sync(ThreadSafeState::mock(), &out).unwrap();
A
andy finch 已提交
194 195 196 197 198 199 200
      assert!(
        out
          .maybe_output_code
          .unwrap()
          .starts_with("console.log(\"Hello World\");".as_bytes())
      );
    })
201 202
  }

203 204 205 206 207 208 209
  #[test]
  fn test_get_compiler_config_no_flag() {
    let compiler_type = "typescript";
    let state = ThreadSafeState::mock();
    let out = get_compiler_config(&state, compiler_type);
    assert_eq!(out, None);
  }
R
Ryan Dahl 已提交
210
}