1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-21 23:04:45 -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 {
self
.ts_compiler
.compile(self, &out, target_lib, permissions, &module_graph, allow_js)
.compile(self, &out, target_lib, &module_graph, allow_js)
.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;
pub use dispatch_minimal::MinimalOp;
pub mod compiler;
pub mod errors;
pub mod fetch;
pub mod fs;

View file

@ -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)
}

View file

@ -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]

View file

@ -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]

View file

@ -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();

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
// 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;