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:
parent
e0d4696a72
commit
ff785bc35a
8 changed files with 81 additions and 190 deletions
|
@ -216,7 +216,7 @@ impl GlobalState {
|
|||
if should_compile {
|
||||
self
|
||||
.ts_compiler
|
||||
.compile(self, &out, target_lib, permissions, &module_graph, allow_js)
|
||||
.compile(self, &out, target_lib, &module_graph, allow_js)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!({}))
|
||||
},
|
||||
);
|
||||
}
|
|
@ -3,7 +3,6 @@
|
|||
mod dispatch_minimal;
|
||||
pub use dispatch_minimal::MinimalOp;
|
||||
|
||||
pub mod compiler;
|
||||
pub mod errors;
|
||||
pub mod fetch;
|
||||
pub mod fs;
|
||||
|
|
|
@ -80,12 +80,7 @@ async fn op_transpile(
|
|||
cli_state.check_unstable("Deno.transpile");
|
||||
let args: TranspileArgs = serde_json::from_value(args)?;
|
||||
let global_state = cli_state.clone();
|
||||
let permissions = {
|
||||
let state = state.borrow();
|
||||
state.borrow::<Permissions>().clone()
|
||||
};
|
||||
let result =
|
||||
runtime_transpile(&global_state, permissions, &args.sources, &args.options)
|
||||
.await?;
|
||||
runtime_transpile(global_state, &args.sources, &args.options).await?;
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[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]
|
|
@ -1,4 +1,4 @@
|
|||
Check [WILDCARD]compiler_js_error.ts
|
||||
error: Uncaught Error: Error in TS compiler:
|
||||
Uncaught AssertionError: Unexpected skip of the emit.
|
||||
AssertionError: Unexpected skip of the emit.
|
||||
[WILDCARD]
|
||||
|
|
194
cli/tsc.rs
194
cli/tsc.rs
|
@ -8,29 +8,26 @@ use crate::disk_cache::DiskCache;
|
|||
use crate::file_fetcher::SourceFile;
|
||||
use crate::file_fetcher::SourceFileFetcher;
|
||||
use crate::flags::Flags;
|
||||
use crate::fmt_errors::JsError;
|
||||
use crate::global_state::GlobalState;
|
||||
use crate::js;
|
||||
use crate::media_type::MediaType;
|
||||
use crate::module_graph::ModuleGraph;
|
||||
use crate::module_graph::ModuleGraphLoader;
|
||||
use crate::ops;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::source_maps::SourceMapGetter;
|
||||
use crate::state::CliModuleLoader;
|
||||
use crate::tsc_config;
|
||||
use crate::version;
|
||||
use crate::worker::Worker;
|
||||
use core::task::Context;
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::Future;
|
||||
use deno_core::futures::future::FutureExt;
|
||||
use deno_core::error::JsError;
|
||||
use deno_core::json_op_sync;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::JsRuntime;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::RuntimeOptions;
|
||||
use log::debug;
|
||||
use log::info;
|
||||
use log::Level;
|
||||
|
@ -44,15 +41,12 @@ use std::collections::HashSet;
|
|||
use std::fs;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::str;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::task::Poll;
|
||||
use swc_common::comments::Comment;
|
||||
use swc_common::comments::CommentKind;
|
||||
use swc_ecmascript::dep_graph;
|
||||
|
@ -125,71 +119,6 @@ pub struct CompiledModule {
|
|||
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! {
|
||||
/// Matches the `@deno-types` pragma.
|
||||
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)]
|
||||
pub enum TargetLib {
|
||||
Main,
|
||||
|
@ -548,7 +459,6 @@ impl TsCompiler {
|
|||
global_state: &Arc<GlobalState>,
|
||||
source_file: &SourceFile,
|
||||
target: TargetLib,
|
||||
permissions: Permissions,
|
||||
module_graph: &ModuleGraph,
|
||||
allow_js: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
|
@ -634,8 +544,7 @@ impl TsCompiler {
|
|||
|
||||
let req_msg = j.to_string();
|
||||
|
||||
let json_str =
|
||||
execute_in_same_thread(global_state, permissions, req_msg).await?;
|
||||
let json_str = execute_in_tsc(global_state.clone(), req_msg)?;
|
||||
|
||||
let compile_response: CompileResponse = serde_json::from_str(&json_str)?;
|
||||
|
||||
|
@ -754,8 +663,7 @@ impl TsCompiler {
|
|||
|
||||
let req_msg = j.to_string();
|
||||
|
||||
let json_str =
|
||||
execute_in_same_thread(global_state, permissions, req_msg).await?;
|
||||
let json_str = execute_in_tsc(global_state.clone(), req_msg)?;
|
||||
|
||||
let bundle_response: BundleResponse = serde_json::from_str(&json_str)?;
|
||||
|
||||
|
@ -1047,16 +955,62 @@ impl TsCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
async fn execute_in_same_thread(
|
||||
global_state: &Arc<GlobalState>,
|
||||
permissions: Permissions,
|
||||
fn execute_in_tsc(
|
||||
global_state: Arc<GlobalState>,
|
||||
req: String,
|
||||
) -> 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);
|
||||
worker.execute2("<compiler>", &script)?;
|
||||
(&mut *worker).await?;
|
||||
Ok(worker.get_response())
|
||||
js_runtime.execute("<compiler>", &script)?;
|
||||
|
||||
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(
|
||||
|
@ -1099,7 +1053,7 @@ async fn create_runtime_module_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>() {
|
||||
Ok(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 json_str = execute_in_same_thread(global_state, permissions, req_msg)
|
||||
.await
|
||||
.map_err(js_error_to_errbox)?;
|
||||
|
||||
let json_str =
|
||||
execute_in_tsc(global_state.clone(), req_msg).map_err(extract_js_error)?;
|
||||
let response: RuntimeCompileResponse = serde_json::from_str(&json_str)?;
|
||||
|
||||
if response.diagnostics.0.is_empty() && sources.is_none() {
|
||||
|
@ -1304,9 +1256,8 @@ pub async fn runtime_bundle(
|
|||
})
|
||||
.to_string();
|
||||
|
||||
let json_str = execute_in_same_thread(global_state, permissions, req_msg)
|
||||
.await
|
||||
.map_err(js_error_to_errbox)?;
|
||||
let json_str =
|
||||
execute_in_tsc(global_state.clone(), req_msg).map_err(extract_js_error)?;
|
||||
let _response: RuntimeBundleResponse = serde_json::from_str(&json_str)?;
|
||||
// We're returning `Ok()` instead of `Err()` because it's not runtime
|
||||
// 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.
|
||||
pub async fn runtime_transpile(
|
||||
global_state: &Arc<GlobalState>,
|
||||
permissions: Permissions,
|
||||
global_state: Arc<GlobalState>,
|
||||
sources: &HashMap<String, String>,
|
||||
maybe_options: &Option<String>,
|
||||
) -> Result<Value, AnyError> {
|
||||
|
@ -1343,9 +1293,8 @@ pub async fn runtime_transpile(
|
|||
})
|
||||
.to_string();
|
||||
|
||||
let json_str = execute_in_same_thread(global_state, permissions, req_msg)
|
||||
.await
|
||||
.map_err(js_error_to_errbox)?;
|
||||
let json_str =
|
||||
execute_in_tsc(global_state, req_msg).map_err(extract_js_error)?;
|
||||
let v = serde_json::from_str::<Value>(&json_str)
|
||||
.expect("Error decoding JSON string.");
|
||||
Ok(v)
|
||||
|
@ -1628,14 +1577,7 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
let result = ts_compiler
|
||||
.compile(
|
||||
&mock_state,
|
||||
&out,
|
||||
TargetLib::Main,
|
||||
Permissions::allow_all(),
|
||||
&module_graph,
|
||||
false,
|
||||
)
|
||||
.compile(&mock_state, &out, TargetLib::Main, &module_graph, false)
|
||||
.await;
|
||||
assert!(result.is_ok());
|
||||
let compiled_file = ts_compiler.get_compiled_module(&out.url).unwrap();
|
||||
|
|
|
@ -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
|
||||
// this op exchanges strings with Rust as raw byte arrays.
|
||||
function getAsset(name) {
|
||||
|
@ -728,7 +723,7 @@ delete Object.prototype.__proto__;
|
|||
function performanceStart() {
|
||||
stats.length = 0;
|
||||
// TODO(kitsonk) replace with performance.mark() when landed
|
||||
statsStart = opNow();
|
||||
statsStart = new Date();
|
||||
ts.performance.enable();
|
||||
}
|
||||
|
||||
|
@ -765,7 +760,7 @@ delete Object.prototype.__proto__;
|
|||
|
||||
function performanceEnd() {
|
||||
// TODO(kitsonk) replace with performance.measure() when landed
|
||||
const duration = opNow() - statsStart;
|
||||
const duration = new Date() - statsStart;
|
||||
stats.push({ key: "Compile time", value: duration });
|
||||
return stats;
|
||||
}
|
||||
|
@ -1283,14 +1278,14 @@ delete Object.prototype.__proto__;
|
|||
);
|
||||
result[fileName] = { source, map };
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function opCompilerRespond(msg) {
|
||||
core.jsonOpSync("op_compiler_respond", msg);
|
||||
}
|
||||
|
||||
async function tsCompilerOnMessage(msg) {
|
||||
function tsCompilerOnMessage(msg) {
|
||||
const request = msg.data;
|
||||
switch (request.type) {
|
||||
case CompilerRequestType.Compile: {
|
||||
|
@ -1314,7 +1309,7 @@ delete Object.prototype.__proto__;
|
|||
break;
|
||||
}
|
||||
case CompilerRequestType.RuntimeTranspile: {
|
||||
const result = await runtimeTranspile(request);
|
||||
const result = runtimeTranspile(request);
|
||||
opCompilerRespond(result);
|
||||
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;
|
||||
|
||||
function bootstrapCompilerRuntime() {
|
||||
function bootstrapCompilerRuntime({ debugFlag }) {
|
||||
if (hasBootstrapped) {
|
||||
throw new Error("Worker runtime already bootstrapped");
|
||||
}
|
||||
hasBootstrapped = true;
|
||||
runtimeStart("TS");
|
||||
delete globalThis.__bootstrap;
|
||||
core.ops();
|
||||
setLogDebug(!!debugFlag, "TS");
|
||||
}
|
||||
|
||||
globalThis.bootstrapCompilerRuntime = bootstrapCompilerRuntime;
|
||||
|
|
Loading…
Reference in a new issue