// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. use crate::isolate_state::*; use crate::msg; use crate::ops; use crate::resources; use crate::resources::Resource; use crate::resources::ResourceId; use crate::startup_data; use crate::workers; use crate::workers::WorkerBehavior; use deno::deno_buf; use deno::Behavior; use deno::Buf; use deno::Op; use deno::StartupData; use futures::Future; use serde_json; use std::str; use std::sync::Arc; use std::sync::Mutex; lazy_static! { static ref C_RID: Mutex> = Mutex::new(None); } pub struct CompilerBehavior { pub state: Arc, } impl CompilerBehavior { pub fn new(state: Arc) -> Self { Self { state } } } impl IsolateStateContainer for CompilerBehavior { fn state(&self) -> Arc { self.state.clone() } } impl IsolateStateContainer for &CompilerBehavior { fn state(&self) -> Arc { self.state.clone() } } impl Behavior for CompilerBehavior { fn startup_data(&mut self) -> Option { Some(startup_data::compiler_isolate_init()) } fn dispatch( &mut self, control: &[u8], zero_copy: deno_buf, ) -> (bool, Box) { ops::dispatch_all(self, control, zero_copy, ops::op_selector_compiler) } } 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), )); } } // This corresponds to JS ModuleMetaData. // TODO Rename one or the other so they correspond. #[derive(Debug)] pub struct ModuleMetaData { pub module_name: String, pub filename: String, pub media_type: msg::MediaType, pub source_code: Vec, pub maybe_output_code_filename: Option, pub maybe_output_code: Option>, pub maybe_source_map_filename: Option, pub maybe_source_map: Option>, } impl ModuleMetaData { pub fn has_output_code_and_source_map(&self) -> bool { self.maybe_output_code.is_some() && self.maybe_source_map.is_some() } pub fn js_source(&self) -> String { if self.media_type == msg::MediaType::Json { return format!( "export default {};", str::from_utf8(&self.source_code).unwrap() ); } match self.maybe_output_code { None => str::from_utf8(&self.source_code).unwrap().to_string(), Some(ref output_code) => str::from_utf8(output_code).unwrap().to_string(), } } } fn lazy_start(parent_state: Arc) -> Resource { let mut cell = C_RID.lock().unwrap(); let rid = cell.get_or_insert_with(|| { let resource = workers::spawn( CompilerBehavior::new(Arc::new(IsolateState::new( parent_state.flags.clone(), parent_state.argv.clone(), None, ))), "compilerMain()".to_string(), ); resource.rid }); Resource { rid: *rid } } fn req(specifier: &str, referrer: &str) -> Buf { json!({ "specifier": specifier, "referrer": referrer, }).to_string() .into_boxed_str() .into_boxed_bytes() } pub fn compile_sync( parent_state: Arc, specifier: &str, referrer: &str, module_meta_data: &ModuleMetaData, ) -> ModuleMetaData { let req_msg = req(specifier, referrer); let compiler = lazy_start(parent_state); let send_future = resources::worker_post_message(compiler.rid, req_msg); send_future.wait().unwrap(); let recv_future = resources::worker_recv_message(compiler.rid); let result = recv_future.wait().unwrap(); assert!(result.is_some()); let res_msg = result.unwrap(); let res_json = std::str::from_utf8(&res_msg).unwrap(); match serde_json::from_str::(res_json) { Ok(serde_json::Value::Object(map)) => ModuleMetaData { module_name: module_meta_data.module_name.clone(), filename: module_meta_data.filename.clone(), media_type: module_meta_data.media_type, source_code: module_meta_data.source_code.clone(), maybe_output_code: match map["outputCode"].as_str() { Some(str) => Some(str.as_bytes().to_owned()), _ => None, }, maybe_output_code_filename: None, maybe_source_map: match map["sourceMap"].as_str() { Some(str) => Some(str.as_bytes().to_owned()), _ => None, }, maybe_source_map_filename: None, }, _ => panic!("error decoding compiler response"), } } #[cfg(test)] mod tests { use super::*; #[test] fn test_compile_sync() { 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(), filename: "/tests/002_hello.ts".to_owned(), media_type: msg::MediaType::TypeScript, source_code: "console.log(\"Hello World\");".as_bytes().to_owned(), 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()) ); } }