1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

refactor: use JsRuntime to implement TSC (#7691)

This commits removes "CompilerWorker" in favor of
using "JsRuntime".

"cli/ops/compiler.rs" has been removed in favor of inline
registration of ops in "cli/tsc.rs"
This commit is contained in:
Bartek Iwańczuk 2020-09-26 16:33:25 +02:00 committed by GitHub
parent e0d4696a72
commit ff785bc35a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 81 additions and 190 deletions

View file

@ -216,7 +216,7 @@ impl GlobalState {
if should_compile { if should_compile {
self self
.ts_compiler .ts_compiler
.compile(self, &out, target_lib, permissions, &module_graph, allow_js) .compile(self, &out, target_lib, &module_graph, allow_js)
.await?; .await?;
} }
} }

View file

@ -1,32 +0,0 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use deno_core::serde_json::json;
use std::sync::Arc;
use std::sync::Mutex;
pub fn init(
rt: &mut deno_core::JsRuntime,
response: Arc<Mutex<Option<String>>>,
) {
let custom_assets = std::collections::HashMap::new();
// TODO(ry) use None.
// TODO(bartlomieju): is this op even required?
rt.register_op(
"op_fetch_asset",
crate::op_fetch_asset::op_fetch_asset(custom_assets),
);
super::reg_json_sync(
rt,
"op_compiler_respond",
move |_state, args, _bufs| {
let mut response_slot = response.lock().unwrap();
let replaced_value = response_slot.replace(args.to_string());
assert!(
replaced_value.is_none(),
"op_compiler_respond found unexpected existing compiler output",
);
Ok(json!({}))
},
);
}

View file

@ -3,7 +3,6 @@
mod dispatch_minimal; mod dispatch_minimal;
pub use dispatch_minimal::MinimalOp; pub use dispatch_minimal::MinimalOp;
pub mod compiler;
pub mod errors; pub mod errors;
pub mod fetch; pub mod fetch;
pub mod fs; pub mod fs;

View file

@ -80,12 +80,7 @@ async fn op_transpile(
cli_state.check_unstable("Deno.transpile"); cli_state.check_unstable("Deno.transpile");
let args: TranspileArgs = serde_json::from_value(args)?; let args: TranspileArgs = serde_json::from_value(args)?;
let global_state = cli_state.clone(); let global_state = cli_state.clone();
let permissions = {
let state = state.borrow();
state.borrow::<Permissions>().clone()
};
let result = let result =
runtime_transpile(&global_state, permissions, &args.sources, &args.options) runtime_transpile(global_state, &args.sources, &args.options).await?;
.await?;
Ok(result) Ok(result)
} }

View file

@ -1,3 +1,3 @@
[WILDCARD] [WILDCARD]
error: Uncaught TypeError: Cannot resolve extension for "[WILDCARD]config.json" with mediaType "Json". error: TypeError: Cannot resolve extension for "[WILDCARD]config.json" with mediaType "Json".
[WILDCARD] [WILDCARD]

View file

@ -1,4 +1,4 @@
Check [WILDCARD]compiler_js_error.ts Check [WILDCARD]compiler_js_error.ts
error: Uncaught Error: Error in TS compiler: error: Uncaught Error: Error in TS compiler:
Uncaught AssertionError: Unexpected skip of the emit. AssertionError: Unexpected skip of the emit.
[WILDCARD] [WILDCARD]

View file

@ -8,29 +8,26 @@ use crate::disk_cache::DiskCache;
use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFile;
use crate::file_fetcher::SourceFileFetcher; use crate::file_fetcher::SourceFileFetcher;
use crate::flags::Flags; use crate::flags::Flags;
use crate::fmt_errors::JsError;
use crate::global_state::GlobalState; use crate::global_state::GlobalState;
use crate::js; use crate::js;
use crate::media_type::MediaType; use crate::media_type::MediaType;
use crate::module_graph::ModuleGraph; use crate::module_graph::ModuleGraph;
use crate::module_graph::ModuleGraphLoader; use crate::module_graph::ModuleGraphLoader;
use crate::ops;
use crate::permissions::Permissions; use crate::permissions::Permissions;
use crate::source_maps::SourceMapGetter; use crate::source_maps::SourceMapGetter;
use crate::state::CliModuleLoader;
use crate::tsc_config; use crate::tsc_config;
use crate::version; use crate::version;
use crate::worker::Worker;
use core::task::Context;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::future::Future; use deno_core::error::JsError;
use deno_core::futures::future::FutureExt; use deno_core::json_op_sync;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::serde_json::Value; use deno_core::serde_json::Value;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::JsRuntime;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::RuntimeOptions;
use log::debug; use log::debug;
use log::info; use log::info;
use log::Level; use log::Level;
@ -44,15 +41,12 @@ use std::collections::HashSet;
use std::fs; use std::fs;
use std::io; use std::io;
use std::ops::Deref; use std::ops::Deref;
use std::ops::DerefMut;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::pin::Pin;
use std::str; use std::str;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::task::Poll;
use swc_common::comments::Comment; use swc_common::comments::Comment;
use swc_common::comments::CommentKind; use swc_common::comments::CommentKind;
use swc_ecmascript::dep_graph; use swc_ecmascript::dep_graph;
@ -125,71 +119,6 @@ pub struct CompiledModule {
pub name: String, pub name: String,
} }
pub struct CompilerWorker {
worker: Worker,
response: Arc<Mutex<Option<String>>>,
}
impl CompilerWorker {
pub fn new(
name: String,
permissions: Permissions,
global_state: Arc<GlobalState>,
) -> Self {
let main_module =
ModuleSpecifier::resolve_url_or_path("./$deno$compiler.ts").unwrap();
// TODO(bartlomieju): compiler worker shouldn't require any loader/state
let loader = CliModuleLoader::new(None);
let mut worker = Worker::new(
name,
Some(js::compiler_isolate_init()),
permissions,
main_module,
global_state,
loader,
false,
true,
);
let response = Arc::new(Mutex::new(None));
ops::runtime::init(&mut worker);
ops::errors::init(&mut worker);
ops::timers::init(&mut worker);
ops::compiler::init(&mut worker, response.clone());
Self { worker, response }
}
pub fn get_response(&mut self) -> String {
let mut maybe_response = self.response.lock().unwrap();
assert!(
maybe_response.is_some(),
"Unexpected missing response from TS compiler"
);
maybe_response.take().unwrap()
}
}
impl Deref for CompilerWorker {
type Target = Worker;
fn deref(&self) -> &Self::Target {
&self.worker
}
}
impl DerefMut for CompilerWorker {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.worker
}
}
impl Future for CompilerWorker {
type Output = Result<(), AnyError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let inner = self.get_mut();
inner.worker.poll_unpin(cx)
}
}
lazy_static! { lazy_static! {
/// Matches the `@deno-types` pragma. /// Matches the `@deno-types` pragma.
static ref DENO_TYPES_RE: Regex = static ref DENO_TYPES_RE: Regex =
@ -224,24 +153,6 @@ fn warn_ignored_options(
} }
} }
/// Create a new worker with snapshot of TS compiler and setup compiler's
/// runtime.
fn create_compiler_worker(
global_state: &Arc<GlobalState>,
permissions: Permissions,
) -> CompilerWorker {
// TODO(bartlomieju): this metric is never used anywhere
// Count how many times we start the compiler worker.
global_state.compiler_starts.fetch_add(1, Ordering::SeqCst);
let mut worker =
CompilerWorker::new("TS".to_string(), permissions, global_state.clone());
worker
.execute("globalThis.bootstrapCompilerRuntime()")
.unwrap();
worker
}
#[derive(Clone)] #[derive(Clone)]
pub enum TargetLib { pub enum TargetLib {
Main, Main,
@ -548,7 +459,6 @@ impl TsCompiler {
global_state: &Arc<GlobalState>, global_state: &Arc<GlobalState>,
source_file: &SourceFile, source_file: &SourceFile,
target: TargetLib, target: TargetLib,
permissions: Permissions,
module_graph: &ModuleGraph, module_graph: &ModuleGraph,
allow_js: bool, allow_js: bool,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
@ -634,8 +544,7 @@ impl TsCompiler {
let req_msg = j.to_string(); let req_msg = j.to_string();
let json_str = let json_str = execute_in_tsc(global_state.clone(), req_msg)?;
execute_in_same_thread(global_state, permissions, req_msg).await?;
let compile_response: CompileResponse = serde_json::from_str(&json_str)?; let compile_response: CompileResponse = serde_json::from_str(&json_str)?;
@ -754,8 +663,7 @@ impl TsCompiler {
let req_msg = j.to_string(); let req_msg = j.to_string();
let json_str = let json_str = execute_in_tsc(global_state.clone(), req_msg)?;
execute_in_same_thread(global_state, permissions, req_msg).await?;
let bundle_response: BundleResponse = serde_json::from_str(&json_str)?; let bundle_response: BundleResponse = serde_json::from_str(&json_str)?;
@ -1047,16 +955,62 @@ impl TsCompiler {
} }
} }
async fn execute_in_same_thread( fn execute_in_tsc(
global_state: &Arc<GlobalState>, global_state: Arc<GlobalState>,
permissions: Permissions,
req: String, req: String,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
let mut worker = create_compiler_worker(&global_state, permissions); // TODO(bartlomieju): this is only used in testing, I'm not sure it's
// worth keeping around
// Count how many times we start the compiler worker.
global_state.compiler_starts.fetch_add(1, Ordering::SeqCst);
let mut js_runtime = JsRuntime::new(RuntimeOptions {
startup_snapshot: Some(js::compiler_isolate_init()),
..Default::default()
});
let debug_flag = global_state
.flags
.log_level
.map_or(false, |l| l == log::Level::Debug);
let response = Arc::new(Mutex::new(None));
{
js_runtime.register_op(
"op_fetch_asset",
crate::op_fetch_asset::op_fetch_asset(HashMap::default()),
);
let res = response.clone();
js_runtime.register_op(
"op_compiler_respond",
json_op_sync(move |_state, args, _bufs| {
let mut response_slot = res.lock().unwrap();
let replaced_value = response_slot.replace(args.to_string());
assert!(
replaced_value.is_none(),
"op_compiler_respond found unexpected existing compiler output",
);
Ok(json!({}))
}),
);
}
let bootstrap_script = format!(
"globalThis.bootstrapCompilerRuntime({{ debugFlag: {} }})",
debug_flag
);
js_runtime.execute("<compiler>", &bootstrap_script)?;
let script = format!("globalThis.tsCompilerOnMessage({{ data: {} }});", req); let script = format!("globalThis.tsCompilerOnMessage({{ data: {} }});", req);
worker.execute2("<compiler>", &script)?; js_runtime.execute("<compiler>", &script)?;
(&mut *worker).await?;
Ok(worker.get_response()) let maybe_response = response.lock().unwrap().take();
assert!(
maybe_response.is_some(),
"Unexpected missing response from TS compiler"
);
Ok(maybe_response.unwrap())
} }
async fn create_runtime_module_graph( async fn create_runtime_module_graph(
@ -1099,7 +1053,7 @@ async fn create_runtime_module_graph(
Ok((root_names, module_graph_loader.get_graph())) Ok((root_names, module_graph_loader.get_graph()))
} }
fn js_error_to_errbox(error: AnyError) -> AnyError { fn extract_js_error(error: AnyError) -> AnyError {
match error.downcast::<JsError>() { match error.downcast::<JsError>() {
Ok(js_error) => { Ok(js_error) => {
let msg = format!("Error in TS compiler:\n{}", js_error); let msg = format!("Error in TS compiler:\n{}", js_error);
@ -1195,10 +1149,8 @@ pub async fn runtime_compile(
let compiler = global_state.ts_compiler.clone(); let compiler = global_state.ts_compiler.clone();
let json_str = execute_in_same_thread(global_state, permissions, req_msg) let json_str =
.await execute_in_tsc(global_state.clone(), req_msg).map_err(extract_js_error)?;
.map_err(js_error_to_errbox)?;
let response: RuntimeCompileResponse = serde_json::from_str(&json_str)?; let response: RuntimeCompileResponse = serde_json::from_str(&json_str)?;
if response.diagnostics.0.is_empty() && sources.is_none() { if response.diagnostics.0.is_empty() && sources.is_none() {
@ -1304,9 +1256,8 @@ pub async fn runtime_bundle(
}) })
.to_string(); .to_string();
let json_str = execute_in_same_thread(global_state, permissions, req_msg) let json_str =
.await execute_in_tsc(global_state.clone(), req_msg).map_err(extract_js_error)?;
.map_err(js_error_to_errbox)?;
let _response: RuntimeBundleResponse = serde_json::from_str(&json_str)?; let _response: RuntimeBundleResponse = serde_json::from_str(&json_str)?;
// We're returning `Ok()` instead of `Err()` because it's not runtime // We're returning `Ok()` instead of `Err()` because it's not runtime
// error if there were diagnostics produced; we want to let user handle // error if there were diagnostics produced; we want to let user handle
@ -1316,8 +1267,7 @@ pub async fn runtime_bundle(
/// This function is used by `Deno.transpileOnly()` API. /// This function is used by `Deno.transpileOnly()` API.
pub async fn runtime_transpile( pub async fn runtime_transpile(
global_state: &Arc<GlobalState>, global_state: Arc<GlobalState>,
permissions: Permissions,
sources: &HashMap<String, String>, sources: &HashMap<String, String>,
maybe_options: &Option<String>, maybe_options: &Option<String>,
) -> Result<Value, AnyError> { ) -> Result<Value, AnyError> {
@ -1343,9 +1293,8 @@ pub async fn runtime_transpile(
}) })
.to_string(); .to_string();
let json_str = execute_in_same_thread(global_state, permissions, req_msg) let json_str =
.await execute_in_tsc(global_state, req_msg).map_err(extract_js_error)?;
.map_err(js_error_to_errbox)?;
let v = serde_json::from_str::<Value>(&json_str) let v = serde_json::from_str::<Value>(&json_str)
.expect("Error decoding JSON string."); .expect("Error decoding JSON string.");
Ok(v) Ok(v)
@ -1628,14 +1577,7 @@ mod tests {
.unwrap(); .unwrap();
let result = ts_compiler let result = ts_compiler
.compile( .compile(&mock_state, &out, TargetLib::Main, &module_graph, false)
&mock_state,
&out,
TargetLib::Main,
Permissions::allow_all(),
&module_graph,
false,
)
.await; .await;
assert!(result.is_ok()); assert!(result.is_ok());
let compiled_file = ts_compiler.get_compiled_module(&out.url).unwrap(); let compiled_file = ts_compiler.get_compiled_module(&out.url).unwrap();

View file

@ -106,11 +106,6 @@ delete Object.prototype.__proto__;
}); });
} }
function opNow() {
const res = core.jsonOpSync("op_now");
return res.seconds * 1e3 + res.subsecNanos / 1e6;
}
// We really don't want to depend on JSON dispatch during snapshotting, so // We really don't want to depend on JSON dispatch during snapshotting, so
// this op exchanges strings with Rust as raw byte arrays. // this op exchanges strings with Rust as raw byte arrays.
function getAsset(name) { function getAsset(name) {
@ -728,7 +723,7 @@ delete Object.prototype.__proto__;
function performanceStart() { function performanceStart() {
stats.length = 0; stats.length = 0;
// TODO(kitsonk) replace with performance.mark() when landed // TODO(kitsonk) replace with performance.mark() when landed
statsStart = opNow(); statsStart = new Date();
ts.performance.enable(); ts.performance.enable();
} }
@ -765,7 +760,7 @@ delete Object.prototype.__proto__;
function performanceEnd() { function performanceEnd() {
// TODO(kitsonk) replace with performance.measure() when landed // TODO(kitsonk) replace with performance.measure() when landed
const duration = opNow() - statsStart; const duration = new Date() - statsStart;
stats.push({ key: "Compile time", value: duration }); stats.push({ key: "Compile time", value: duration });
return stats; return stats;
} }
@ -1283,14 +1278,14 @@ delete Object.prototype.__proto__;
); );
result[fileName] = { source, map }; result[fileName] = { source, map };
} }
return Promise.resolve(result); return result;
} }
function opCompilerRespond(msg) { function opCompilerRespond(msg) {
core.jsonOpSync("op_compiler_respond", msg); core.jsonOpSync("op_compiler_respond", msg);
} }
async function tsCompilerOnMessage(msg) { function tsCompilerOnMessage(msg) {
const request = msg.data; const request = msg.data;
switch (request.type) { switch (request.type) {
case CompilerRequestType.Compile: { case CompilerRequestType.Compile: {
@ -1314,7 +1309,7 @@ delete Object.prototype.__proto__;
break; break;
} }
case CompilerRequestType.RuntimeTranspile: { case CompilerRequestType.RuntimeTranspile: {
const result = await runtimeTranspile(request); const result = runtimeTranspile(request);
opCompilerRespond(result); opCompilerRespond(result);
break; break;
} }
@ -1327,24 +1322,16 @@ delete Object.prototype.__proto__;
} }
} }
function runtimeStart(source) {
core.ops();
// First we send an empty `Start` message to let the privileged side know we
// are ready. The response should be a `StartRes` message containing the CLI
// args and other info.
const s = core.jsonOpSync("op_start");
setLogDebug(s.debugFlag, source);
return s;
}
let hasBootstrapped = false; let hasBootstrapped = false;
function bootstrapCompilerRuntime() { function bootstrapCompilerRuntime({ debugFlag }) {
if (hasBootstrapped) { if (hasBootstrapped) {
throw new Error("Worker runtime already bootstrapped"); throw new Error("Worker runtime already bootstrapped");
} }
hasBootstrapped = true; hasBootstrapped = true;
runtimeStart("TS"); delete globalThis.__bootstrap;
core.ops();
setLogDebug(!!debugFlag, "TS");
} }
globalThis.bootstrapCompilerRuntime = bootstrapCompilerRuntime; globalThis.bootstrapCompilerRuntime = bootstrapCompilerRuntime;