0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-29 08:58:01 -04:00

chore(core): Refactor runtime and split out tests (#19491)

This is a quick first refactoring to split the tests out of runtime and
move runtime-related code to a top-level runtime module.

There will be a followup to refactor imports a bit, but this is the
major change that will most likely conflict with other work and I want
to merge it early.
This commit is contained in:
Matt Mastracci 2023-06-13 20:03:10 -06:00 committed by GitHub
parent fd9d6baea3
commit ec8e9d4f5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 2616 additions and 2581 deletions

View file

@ -9,8 +9,7 @@ use std::fmt::Formatter;
use anyhow::Error;
use crate::realm::JsRealm;
use crate::runtime::GetErrorClassFn;
use crate::runtime::JsRealm;
use crate::runtime::JsRuntime;
use crate::source_map::apply_source_map;
use crate::source_map::get_source_line;
@ -20,6 +19,9 @@ use crate::url::Url;
// TODO(ry) Deprecate AnyError and encourage deno_core::anyhow::Error instead.
pub type AnyError = anyhow::Error;
pub type JsErrorCreateFn = dyn Fn(JsError) -> Error;
pub type GetErrorClassFn = &'static dyn for<'e> Fn(&'e Error) -> &'static str;
/// Creates a new error with a caller-specified error class name and message.
pub fn custom_error(
class: &'static str,
@ -643,6 +645,56 @@ fn abbrev_file_name(file_name: &str) -> Option<String> {
Some(format!("{}:{},{}......{}", url.scheme(), head, start, end))
}
pub(crate) fn exception_to_err_result<T>(
scope: &mut v8::HandleScope,
exception: v8::Local<v8::Value>,
in_promise: bool,
) -> Result<T, Error> {
let state_rc = JsRuntime::state_from(scope);
let was_terminating_execution = scope.is_execution_terminating();
// Disable running microtasks for a moment. When upgrading to V8 v11.4
// we discovered that canceling termination here will cause the queued
// microtasks to run which breaks some tests.
scope.set_microtasks_policy(v8::MicrotasksPolicy::Explicit);
// If TerminateExecution was called, cancel isolate termination so that the
// exception can be created. Note that `scope.is_execution_terminating()` may
// have returned false if TerminateExecution was indeed called but there was
// no JS to execute after the call.
scope.cancel_terminate_execution();
let mut exception = exception;
{
// If termination is the result of a `op_dispatch_exception` call, we want
// to use the exception that was passed to it rather than the exception that
// was passed to this function.
let state = state_rc.borrow();
exception = if let Some(exception) = &state.dispatched_exception {
v8::Local::new(scope, exception.clone())
} else if was_terminating_execution && exception.is_null_or_undefined() {
let message = v8::String::new(scope, "execution terminated").unwrap();
v8::Exception::error(scope, message)
} else {
exception
};
}
let mut js_error = JsError::from_v8_exception(scope, exception);
if in_promise {
js_error.exception_message = format!(
"Uncaught (in promise) {}",
js_error.exception_message.trim_start_matches("Uncaught ")
);
}
if was_terminating_execution {
// Resume exception termination.
scope.terminate_execution();
}
scope.set_microtasks_policy(v8::MicrotasksPolicy::Auto);
Err(js_error.into())
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,7 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
mod async_cancel;
mod async_cell;
mod bindings;
pub mod error;
mod error_codes;
mod extensions;
@ -18,10 +17,8 @@ mod ops_builtin;
mod ops_builtin_v8;
mod ops_metrics;
mod path;
mod realm;
mod resources;
mod runtime;
pub mod snapshot_util;
mod source_map;
pub mod task;
mod task_queue;
@ -57,6 +54,8 @@ pub use crate::async_cell::AsyncRefCell;
pub use crate::async_cell::AsyncRefFuture;
pub use crate::async_cell::RcLike;
pub use crate::async_cell::RcRef;
pub use crate::error::GetErrorClassFn;
pub use crate::error::JsErrorCreateFn;
pub use crate::extensions::Extension;
pub use crate::extensions::ExtensionBuilder;
pub use crate::extensions::ExtensionFileSource;
@ -103,15 +102,13 @@ pub use crate::ops_builtin::op_void_async;
pub use crate::ops_builtin::op_void_sync;
pub use crate::ops_metrics::OpsTracker;
pub use crate::path::strip_unc_prefix;
pub use crate::realm::JsRealm;
pub use crate::resources::AsyncResult;
pub use crate::resources::Resource;
pub use crate::resources::ResourceId;
pub use crate::resources::ResourceTable;
pub use crate::runtime::CompiledWasmModuleStore;
pub use crate::runtime::CrossIsolateStore;
pub use crate::runtime::GetErrorClassFn;
pub use crate::runtime::JsErrorCreateFn;
pub use crate::runtime::JsRealm;
pub use crate::runtime::JsRuntime;
pub use crate::runtime::JsRuntimeForSnapshot;
pub use crate::runtime::RuntimeOptions;
@ -130,21 +127,30 @@ pub fn v8_version() -> &'static str {
/// An internal module re-exporting functions used by the #[op] (`deno_ops`) macro
#[doc(hidden)]
pub mod _ops {
pub use super::bindings::throw_type_error;
pub use super::error_codes::get_error_code;
pub use super::ops::to_op_result;
pub use super::ops::OpCtx;
pub use super::ops::OpResult;
pub use super::runtime::map_async_op1;
pub use super::runtime::map_async_op2;
pub use super::runtime::map_async_op3;
pub use super::runtime::map_async_op4;
pub use super::runtime::queue_async_op;
pub use super::runtime::queue_fast_async_op;
pub use super::runtime::ops::map_async_op1;
pub use super::runtime::ops::map_async_op2;
pub use super::runtime::ops::map_async_op3;
pub use super::runtime::ops::map_async_op4;
pub use super::runtime::ops::queue_async_op;
pub use super::runtime::ops::queue_fast_async_op;
pub use super::runtime::throw_type_error;
pub use super::runtime::V8_WRAPPER_OBJECT_INDEX;
pub use super::runtime::V8_WRAPPER_TYPE_INDEX;
}
// TODO(mmastrac): Temporary while we move code around
pub mod snapshot_util {
pub use crate::runtime::create_snapshot;
pub use crate::runtime::get_js_files;
pub use crate::runtime::CreateSnapshotOptions;
pub use crate::runtime::CreateSnapshotOutput;
pub use crate::runtime::FilterFn;
}
/// A helper macro that will return a call site in Rust code. Should be
/// used when executing internal one-line scripts for JsRuntime lifecycle.
///

View file

@ -1,6 +1,4 @@
use crate::JsRuntime;
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::bindings;
use crate::error::generic_error;
use crate::fast_string::FastString;
use crate::modules::get_asserted_module_type_from_assertions;
@ -20,7 +18,8 @@ use crate::modules::NoopModuleLoader;
use crate::modules::PrepareLoadFuture;
use crate::modules::RecursiveModuleLoad;
use crate::modules::ResolutionKind;
use crate::snapshot_util::SnapshottedData;
use crate::runtime::JsRuntime;
use crate::runtime::SnapshottedData;
use anyhow::Error;
use futures::future::FutureExt;
use futures::stream::FuturesUnordered;
@ -467,7 +466,7 @@ impl ModuleMap {
let name_str = name.v8(scope);
let source_str = source.v8(scope);
let origin = bindings::module_origin(scope, name_str);
let origin = module_origin(scope, name_str);
let source = v8::script_compiler::Source::new(source_str, Some(&origin));
let tc_scope = &mut v8::TryCatch::new(scope);
@ -820,3 +819,22 @@ fn json_module_evaluation_steps<'a>(
resolver.resolve(tc_scope, undefined.into());
Some(resolver.get_promise(tc_scope).into())
}
pub fn module_origin<'a>(
s: &mut v8::HandleScope<'a>,
resource_name: v8::Local<'a, v8::String>,
) -> v8::ScriptOrigin<'a> {
let source_map_url = v8::String::empty(s);
v8::ScriptOrigin::new(
s,
resource_name.into(),
0,
0,
false,
123,
source_map_url.into(),
true,
false,
true,
)
}

View file

@ -1,8 +1,8 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::ascii_str;
use crate::resolve_import;
use crate::JsRuntime;
use crate::JsRuntimeForSnapshot;
use crate::runtime::JsRuntime;
use crate::runtime::JsRuntimeForSnapshot;
use crate::RuntimeOptions;
use crate::Snapshot;
use deno_ops::op;

View file

@ -1,10 +1,10 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::error::AnyError;
use crate::error::GetErrorClassFn;
use crate::gotham_state::GothamState;
use crate::realm::ContextState;
use crate::resources::ResourceTable;
use crate::runtime::GetErrorClassFn;
use crate::runtime::ContextState;
use crate::runtime::JsRuntimeState;
use crate::OpDecl;
use crate::OpsTracker;

View file

@ -1,5 +1,4 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::bindings::script_origin;
use crate::error::custom_error;
use crate::error::is_instance_of_error;
use crate::error::range_error;
@ -7,6 +6,7 @@ use crate::error::type_error;
use crate::error::JsError;
use crate::ops_builtin::WasmStreamingResource;
use crate::resolve_url;
use crate::runtime::script_origin;
use crate::serde_v8::from_v8;
use crate::source_map::apply_source_map;
use crate::JsRealm;

View file

@ -78,25 +78,6 @@ pub fn script_origin<'a>(
)
}
pub fn module_origin<'a>(
s: &mut v8::HandleScope<'a>,
resource_name: v8::Local<'a, v8::String>,
) -> v8::ScriptOrigin<'a> {
let source_map_url = v8::String::empty(s);
v8::ScriptOrigin::new(
s,
resource_name.into(),
0,
0,
false,
123,
source_map_url.into(),
true,
false,
true,
)
}
fn get<'s, T>(
scope: &mut v8::HandleScope<'s>,
from: v8::Local<v8::Object>,

View file

@ -1,9 +1,8 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::bindings;
use super::bindings;
use crate::error::exception_to_err_result;
use crate::modules::ModuleCode;
use crate::ops::OpCtx;
use crate::runtime::exception_to_err_result;
use crate::runtime::JsRuntimeState;
use crate::task::MaskResultAsSend;
use crate::JsRuntime;

File diff suppressed because it is too large Load diff

35
core/runtime/mod.rs Normal file
View file

@ -0,0 +1,35 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
mod bindings;
mod jsrealm;
mod jsruntime;
#[doc(hidden)]
pub mod ops;
mod snapshot_util;
#[cfg(test)]
mod tests;
pub const V8_WRAPPER_TYPE_INDEX: i32 = 0;
pub const V8_WRAPPER_OBJECT_INDEX: i32 = 1;
pub(crate) use jsrealm::ContextState;
pub use jsrealm::JsRealm;
pub use jsruntime::CompiledWasmModuleStore;
pub use jsruntime::CrossIsolateStore;
pub(crate) use jsruntime::InitMode;
pub use jsruntime::JsRuntime;
pub use jsruntime::JsRuntimeForSnapshot;
pub use jsruntime::JsRuntimeState;
pub use jsruntime::RuntimeOptions;
pub use jsruntime::RuntimeSnapshotOptions;
pub use jsruntime::SharedArrayBufferStore;
pub use jsruntime::Snapshot;
pub use snapshot_util::create_snapshot;
pub use snapshot_util::get_js_files;
pub use snapshot_util::CreateSnapshotOptions;
pub use snapshot_util::CreateSnapshotOutput;
pub use snapshot_util::FilterFn;
pub(crate) use snapshot_util::SnapshottedData;
pub use bindings::script_origin;
pub use bindings::throw_type_error;

156
core/runtime/ops.rs Normal file
View file

@ -0,0 +1,156 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::ops::*;
use crate::OpResult;
use crate::PromiseId;
use anyhow::Error;
use futures::future::Future;
use futures::future::FutureExt;
use futures::future::MaybeDone;
use futures::task::noop_waker;
use std::cell::RefCell;
use std::option::Option;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
#[inline]
pub fn queue_fast_async_op<R: serde::Serialize + 'static>(
ctx: &OpCtx,
promise_id: PromiseId,
op: impl Future<Output = Result<R, Error>> + 'static,
) {
let get_class = {
let state = RefCell::borrow(&ctx.state);
state.tracker.track_async(ctx.id);
state.get_error_class_fn
};
let fut = op
.map(|result| crate::_ops::to_op_result(get_class, result))
.boxed_local();
// SAFETY: this this is guaranteed to be running on a current-thread executor
ctx.context_state.borrow_mut().pending_ops.spawn(unsafe {
crate::task::MaskFutureAsSend::new(OpCall::pending(ctx, promise_id, fut))
});
}
#[inline]
pub fn map_async_op1<R: serde::Serialize + 'static>(
ctx: &OpCtx,
op: impl Future<Output = Result<R, Error>> + 'static,
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
let get_class = {
let state = RefCell::borrow(&ctx.state);
state.tracker.track_async(ctx.id);
state.get_error_class_fn
};
let fut = op
.map(|result| crate::_ops::to_op_result(get_class, result))
.boxed_local();
MaybeDone::Future(fut)
}
#[inline]
pub fn map_async_op2<R: serde::Serialize + 'static>(
ctx: &OpCtx,
op: impl Future<Output = R> + 'static,
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
let state = RefCell::borrow(&ctx.state);
state.tracker.track_async(ctx.id);
let fut = op.map(|result| OpResult::Ok(result.into())).boxed_local();
MaybeDone::Future(fut)
}
#[inline]
pub fn map_async_op3<R: serde::Serialize + 'static>(
ctx: &OpCtx,
op: Result<impl Future<Output = Result<R, Error>> + 'static, Error>,
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
let get_class = {
let state = RefCell::borrow(&ctx.state);
state.tracker.track_async(ctx.id);
state.get_error_class_fn
};
match op {
Err(err) => MaybeDone::Done(OpResult::Err(OpError::new(get_class, err))),
Ok(fut) => MaybeDone::Future(
fut
.map(|result| crate::_ops::to_op_result(get_class, result))
.boxed_local(),
),
}
}
#[inline]
pub fn map_async_op4<R: serde::Serialize + 'static>(
ctx: &OpCtx,
op: Result<impl Future<Output = R> + 'static, Error>,
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
let get_class = {
let state = RefCell::borrow(&ctx.state);
state.tracker.track_async(ctx.id);
state.get_error_class_fn
};
match op {
Err(err) => MaybeDone::Done(OpResult::Err(OpError::new(get_class, err))),
Ok(fut) => MaybeDone::Future(
fut.map(|result| OpResult::Ok(result.into())).boxed_local(),
),
}
}
pub fn queue_async_op<'s>(
ctx: &OpCtx,
scope: &'s mut v8::HandleScope,
deferred: bool,
promise_id: PromiseId,
mut op: MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>>,
) -> Option<v8::Local<'s, v8::Value>> {
// An op's realm (as given by `OpCtx::realm_idx`) must match the realm in
// which it is invoked. Otherwise, we might have cross-realm object exposure.
// deno_core doesn't currently support such exposure, even though embedders
// can cause them, so we panic in debug mode (since the check is expensive).
// TODO(mmastrac): Restore this
// debug_assert_eq!(
// runtime_state.borrow().context(ctx.realm_idx as usize, scope),
// Some(scope.get_current_context())
// );
// All ops are polled immediately
let waker = noop_waker();
let mut cx = Context::from_waker(&waker);
// Note that MaybeDone returns () from the future
let op_call = match op.poll_unpin(&mut cx) {
Poll::Pending => {
let MaybeDone::Future(fut) = op else {
unreachable!()
};
OpCall::pending(ctx, promise_id, fut)
}
Poll::Ready(_) => {
let mut op_result = Pin::new(&mut op).take_output().unwrap();
// If the op is ready and is not marked as deferred we can immediately return
// the result.
if !deferred {
ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
return Some(op_result.to_v8(scope).unwrap());
}
OpCall::ready(ctx, promise_id, op_result)
}
};
// Otherwise we will push it to the `pending_ops` and let it be polled again
// or resolved on the next tick of the event loop.
ctx
.context_state
.borrow_mut()
.pending_ops
// SAFETY: this this is guaranteed to be running on a current-thread executor
.spawn(unsafe { crate::task::MaskFutureAsSend::new(op_call) });
None
}

2306
core/runtime/tests.rs Normal file

File diff suppressed because it is too large Load diff