/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
libs/core/ops_builtin.rs
885 строк
25 KB
Bartek Iwańczuk
perf: use V8 native queueMicrotask instead of op (#35787)
06 июл 2026, 16:47
Не верифицирован
06 июл 2026, 16:47
242ca14
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::cell::RefCell; use std::io::Write; use std::io::stderr; use std::io::stdout; use std::rc::Rc; use bytes::BytesMut; use deno_error::JsErrorBox; use serde_v8::ByteString; use crate::CancelHandle; use crate::CancelTryFuture; use crate::JsBuffer; use crate::ModuleId; use crate::OpDecl; use crate::OpState; use crate::Resource; use crate::error::CoreError; use crate::error::CoreErrorKind; use crate::error::ResourceError; use crate::error::exception_to_err; use crate::error::exception_to_err_result; use crate::io::AdaptiveBufferStrategy; use crate::io::AsyncResult; use crate::io::BufMutView; use crate::io::BufView; use crate::io::ResourceId; use crate::io::WriteOutcome; use crate::modules::ModuleMap; use crate::modules::recursive_load::RecursiveModuleLoad; use crate::op2; use crate::ops_builtin_types; use crate::ops_builtin_v8; use crate::runtime::JsRealm; use crate::runtime::v8_static_strings; macro_rules! builtin_ops { ( $($op:ident $(:: $sub:ident)*),* ) => { pub const BUILTIN_OPS: &'static [OpDecl] = &[ $( $op $(:: $sub) * () ),* ]; } } builtin_ops! { op_close, op_try_close, op_cancel_read, op_print, op_resources, op_wasm_streaming_feed, op_wasm_streaming_set_url, op_void_sync, op_error_async, op_error_async_deferred, op_void_async, op_void_async_deferred, op_add, op_add_async, op_read, op_read_all, op_write, op_read_sync, op_write_sync, op_write_all, op_pipe, op_write_type_error, op_shutdown, op_str_byte_length, op_panic, op_cancel_handle, op_encode_binary_string, op_is_terminal, op_import_sync, op_import_sync_with_source, ops_builtin_types::op_is_any_array_buffer, ops_builtin_types::op_is_arguments_object, ops_builtin_types::op_is_array_buffer, ops_builtin_types::op_is_array_buffer_view, ops_builtin_types::op_is_async_function, ops_builtin_types::op_is_big_int_object, ops_builtin_types::op_is_boolean_object, ops_builtin_types::op_is_boxed_primitive, ops_builtin_types::op_is_data_view, ops_builtin_types::op_is_date, ops_builtin_types::op_is_generator_function, ops_builtin_types::op_is_generator_object, ops_builtin_types::op_is_map, ops_builtin_types::op_is_map_iterator, ops_builtin_types::op_is_module_namespace_object, ops_builtin_types::op_is_native_error, ops_builtin_types::op_is_number_object, ops_builtin_types::op_is_promise, ops_builtin_types::op_is_proxy, ops_builtin_types::op_is_reg_exp, ops_builtin_types::op_is_set, ops_builtin_types::op_is_set_iterator, ops_builtin_types::op_is_shared_array_buffer, ops_builtin_types::op_is_string_object, ops_builtin_types::op_is_symbol_object, ops_builtin_types::op_is_typed_array, ops_builtin_types::op_is_weak_map, ops_builtin_types::op_is_weak_set, ops_builtin_v8::op_add_main_module_handler, ops_builtin_v8::op_set_handled_promise_rejection_handler, ops_builtin_v8::op_timer_schedule, ops_builtin_v8::op_timer_track, ops_builtin_v8::op_timer_untrack, ops_builtin_v8::op_timer_now, ops_builtin_v8::op_ref_op, ops_builtin_v8::op_unref_op, ops_builtin_v8::op_lazy_load_esm, ops_builtin_v8::op_load_ext_script, ops_builtin_v8::op_set_captured_bootstrap, ops_builtin_v8::op_run_microtasks, ops_builtin_v8::op_drain_pending_rejections, ops_builtin_v8::op_compile_function, ops_builtin_v8::op_eval_context, ops_builtin_v8::op_encode, ops_builtin_v8::op_decode, ops_builtin_v8::op_serialize, ops_builtin_v8::op_deserialize, ops_builtin_v8::op_structured_clone, ops_builtin_v8::op_set_promise_hooks, ops_builtin_v8::op_get_promise_details, ops_builtin_v8::op_get_proxy_details, ops_builtin_v8::op_get_non_index_property_names, ops_builtin_v8::op_get_constructor_name, ops_builtin_v8::op_get_extras_binding_object, ops_builtin_v8::op_memory_usage, ops_builtin_v8::op_set_wasm_streaming_callback, ops_builtin_v8::op_abort_wasm_streaming, ops_builtin_v8::op_destructure_error, ops_builtin_v8::op_dispatch_exception, ops_builtin_v8::op_op_names, ops_builtin_v8::op_current_user_call_site, ops_builtin_v8::op_set_format_exception_callback, ops_builtin_v8::op_event_loop_has_more_work, ops_builtin_v8::op_immediate_check, ops_builtin_v8::op_leak_tracing_enable, ops_builtin_v8::op_leak_tracing_submit, ops_builtin_v8::op_leak_tracing_get_all, ops_builtin_v8::op_leak_tracing_get, ops_builtin_v8::op_get_ext_import_meta_proto } #[op2(fast)] pub fn op_panic(#[string] message: String) { #[allow(clippy::print_stderr, reason = "intentional panic output")] { eprintln!("JS PANIC: {}", message); } panic!("JS PANIC: {}", message); } /// Return map of resources with id as key /// and string representation as value. #[op2] pub fn op_resources(state: &mut OpState) -> Vec<(ResourceId, String)> { state .resource_table .names() .map(|(rid, name)| (rid, name.to_string())) .collect() } #[op2(fast)] fn op_add(a: i32, b: i32) -> i32 { a + b } #[allow(clippy::unused_async, reason = "intentially async")] #[op2] pub async fn op_add_async(a: i32, b: i32) -> i32 { a + b } #[op2(fast)] pub fn op_void_sync() {} #[allow(clippy::unused_async, reason = "intentially async")] #[op2] pub async fn op_void_async() {} #[allow(clippy::unused_async, reason = "intentially async")] #[op2] pub async fn op_error_async() -> Result<(), JsErrorBox> { Err(JsErrorBox::generic("error")) } #[allow(clippy::unused_async, reason = "intentially async")] #[op2(async(deferred), fast)] pub async fn op_error_async_deferred() -> Result<(), JsErrorBox> { Err(JsErrorBox::generic("error")) } #[allow(clippy::unused_async, reason = "intentially async")] #[op2(async(deferred), fast)] pub async fn op_void_async_deferred() {} /// Remove a resource from the resource table. #[op2(fast)] pub fn op_close( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, ) -> Result<(), ResourceError> { let resource = state.borrow_mut().resource_table.take_any(rid)?; resource.close(); Ok(()) } /// Try to remove a resource from the resource table. If there is no resource /// with the specified `rid`, this is a no-op. #[op2(fast)] pub fn op_try_close(state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId) { if let Ok(resource) = state.borrow_mut().resource_table.take_any(rid) { resource.close(); } } /// Cancel pending read operations for a resource without removing it from the /// resource table. #[op2(fast)] pub fn op_cancel_read(state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId) { if let Ok(resource) = state.borrow().resource_table.get_any(rid) { resource.cancel_read_ops(); } } /// Builtin utility to print to stdout/stderr #[op2(fast)] pub fn op_print( #[string] msg: &str, is_err: bool, ) -> Result<(), std::io::Error> { let mut out: Box<dyn Write> = if is_err { Box::new(stderr()) } else { Box::new(stdout()) }; // Use a manual write loop instead of write_all because the fd may be // in non-blocking mode (e.g. when Node's process.stdout sets // O_NONBLOCK via uv_pipe_open/uv_tty_init). write_all does not // retry on WouldBlock. let mut buf = msg.as_bytes(); while !buf.is_empty() { match out.write(buf) { Ok(n) => buf = &buf[n..], Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { std::thread::yield_now(); continue; } Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue, Err(e) => return Err(e), } } out.flush().unwrap(); Ok(()) } pub struct WasmStreamingResource(pub(crate) RefCell<v8::WasmStreaming<false>>); impl Resource for WasmStreamingResource { // Implementing the write side makes the resource a valid sink for `op_pipe`, // which is how `WebAssembly.instantiateStreaming` pumps a response body // straight into V8's streaming compiler without a JS round-trip per chunk // (see `handleWasmStreaming` in ext/fetch/26_fetch.js). `on_bytes_received` // is synchronous, infallible, and always consumes the whole chunk. fn write(self: Rc<Self>, buf: BufView) -> AsyncResult<WriteOutcome> { let nwritten = buf.len(); self.0.borrow_mut().on_bytes_received(&buf); Box::pin(std::future::ready(Ok(WriteOutcome::Full { nwritten }))) } fn write_all(self: Rc<Self>, view: BufView) -> AsyncResult<()> { self.0.borrow_mut().on_bytes_received(&view); Box::pin(std::future::ready(Ok(()))) } fn close(self: Rc<Self>) { // At this point there are no clones of Rc<WasmStreamingResource> on the // resource table, and no one should own a reference outside of the stack. // Therefore, we can be sure `self` is the only reference. match Rc::try_unwrap(self) { Ok(wsr) => { wsr.0.into_inner().finish(); } _ => { panic!("Couldn't consume WasmStreamingResource."); } } } } /// Feed bytes to WasmStreamingResource. #[op2(fast)] pub fn op_wasm_streaming_feed( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, #[buffer] bytes: &[u8], ) -> Result<(), ResourceError> { let wasm_streaming = state .borrow_mut() .resource_table .get::<WasmStreamingResource>(rid)?; wasm_streaming.0.borrow_mut().on_bytes_received(bytes); Ok(()) } #[op2(fast)] pub fn op_wasm_streaming_set_url( state: &mut OpState, #[smi] rid: ResourceId, #[string] url: &str, ) -> Result<(), ResourceError> { let wasm_streaming = state.resource_table.get::<WasmStreamingResource>(rid)?; wasm_streaming.0.borrow_mut().set_url(url); Ok(()) } // Get a resource from the resource table and // handle unrefing the current task. fn get_resource( state: Rc<RefCell<OpState>>, rid: ResourceId, promise_id: i32, ) -> Result<Rc<dyn Resource>, JsErrorBox> { let op_state = state.borrow(); let resource = op_state .resource_table .get_any(rid) .map_err(JsErrorBox::from_err)?; if op_state.unrefed_resources.contains(&rid) { op_state.unrefed_ops.borrow_mut().insert(promise_id); } Ok(resource) } #[op2(promise_id)] async fn op_read( state: Rc<RefCell<OpState>>, #[smi] promise_id: i32, #[smi] rid: ResourceId, #[buffer] buf: JsBuffer, ) -> Result<u32, JsErrorBox> { let resource = get_resource(state, rid, promise_id)?; let view = BufMutView::from(buf); resource.read_byob(view).await.map(|(n, _)| n as u32) } #[op2(promise_id)] #[buffer] async fn op_read_all( state: Rc<RefCell<OpState>>, #[smi] promise_id: i32, #[smi] rid: ResourceId, ) -> Result<BytesMut, JsErrorBox> { let resource = get_resource(state, rid, promise_id)?; let (min, maybe_max) = resource.size_hint(); let mut buffer_strategy = AdaptiveBufferStrategy::new_from_hint_u64(min, maybe_max); let mut buf = BufMutView::new(buffer_strategy.buffer_size()); loop { #[allow(deprecated, reason = "needed for compatibility")] buf.maybe_grow(buffer_strategy.buffer_size()).unwrap(); let (n, new_buf) = resource.clone().read_byob(buf).await?; buf = new_buf; buf.advance_cursor(n); if n == 0 { break; } buffer_strategy.notify_read(n); } let nread = buf.reset_cursor(); // If the buffer is larger than the amount of data read, shrink it to the // amount of data read. buf.truncate(nread); Ok(buf.maybe_unwrap_bytes().unwrap()) } #[op2(promise_id)] async fn op_write( state: Rc<RefCell<OpState>>, #[smi] promise_id: i32, #[smi] rid: ResourceId, #[buffer] buf: JsBuffer, ) -> Result<u32, JsErrorBox> { let resource = get_resource(state, rid, promise_id)?; let view = BufView::from(buf); let resp = resource.write(view).await?; Ok(resp.nwritten() as u32) } #[op2(fast)] fn op_read_sync( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, #[buffer] data: &mut [u8], ) -> Result<u32, JsErrorBox> { let resource = state .borrow_mut() .resource_table .get_any(rid) .map_err(JsErrorBox::from_err)?; resource.read_byob_sync(data).map(|n| n as u32) } #[op2(fast)] fn op_write_sync( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, #[buffer] data: &[u8], ) -> Result<u32, JsErrorBox> { let resource = state .borrow_mut() .resource_table .get_any(rid) .map_err(JsErrorBox::from_err)?; let nwritten = resource.write_sync(data)?; Ok(nwritten as u32) } #[op2] async fn op_write_all( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, #[buffer] buf: JsBuffer, ) -> Result<(), JsErrorBox> { let resource = state .borrow() .resource_table .get_any(rid) .map_err(JsErrorBox::from_err)?; let view = BufView::from(buf); resource.write_all(view).await?; Ok(()) } /// Pipe all data from a source resource to a sink resource, entirely in Rust. /// /// This is the fast path for `ReadableStream#pipeTo` when both the source and /// the sink are backed by a `deno_core::Resource` (see /// `getReadableStreamResourceBacking` / `getWritableStreamResourceBacking` in /// `ext/web/06_streams.js`). It avoids copying every chunk into and back out of /// JavaScript. Close / abort / cancel semantics are handled by the JS caller. /// /// Chunks are read with `read()`, which hands back the resource's own buffer /// (zero-copy for sources that buffer internally, like fetch response bodies /// and wrapped `ReadableStream`s), and passed to the sink as-is; `write_all` /// takes the same `BufView`, so the pump itself adds no copy. Sources that /// only buffer externally (files, sockets) allocate per chunk in their `read()` /// impl, exactly as a `read_byob` into a fresh buffer would. /// /// When `cancel_rid` names a [`CancelHandle`] resource, each read and write is /// raced against it, so cancelling the handle (the JS caller closes it from an /// `AbortSignal` handler) aborts the pump promptly. The handle is consumed /// before returning, mirroring `op_net_connect_tcp`. #[op2(promise_id)] async fn op_pipe( state: Rc<RefCell<OpState>>, #[smi] promise_id: i32, #[smi] src_rid: ResourceId, #[smi] dst_rid: ResourceId, #[smi] cancel_rid: Option<ResourceId>, ) -> Result<(), JsErrorBox> { // Unref bookkeeping is keyed off the source, since that is what we await on. let src = get_resource(state.clone(), src_rid, promise_id)?; let dst = state .borrow() .resource_table .get_any(dst_rid) .map_err(JsErrorBox::from_err)?; let cancel_handle = cancel_rid.and_then(|rid| { state.borrow().resource_table.get::<CancelHandle>(rid).ok() }); // Reuse the same adaptive sizing strategy as `op_read_all`. let (min, maybe_max) = src.size_hint(); let mut buffer_strategy = AdaptiveBufferStrategy::new_from_hint_u64(min, maybe_max); let result = async { loop { let read = src.clone().read(buffer_strategy.buffer_size()); let buf = match &cancel_handle { Some(handle) => read.try_or_cancel(handle).await?, None => read.await?, }; if buf.is_empty() { break; // source reached EOF } buffer_strategy.notify_read(buf.len()); let write = dst.clone().write_all(buf); match &cancel_handle { Some(handle) => write.try_or_cancel(handle).await?, None => write.await?, } } Ok::<(), JsErrorBox>(()) } .await; // Consume the cancel handle regardless of outcome so it doesn't linger in the // resource table (the JS caller may also close it from its abort handler; both // paths are idempotent). if let Some(rid) = cancel_rid && let Ok(handle) = state.borrow_mut().resource_table.take_any(rid) { handle.close(); } result } #[op2] async fn op_write_type_error( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, #[string] error: String, ) -> Result<(), JsErrorBox> { let resource = state .borrow() .resource_table .get_any(rid) .map_err(JsErrorBox::from_err)?; resource.write_error(&JsErrorBox::type_error(error)).await?; Ok(()) } #[op2] async fn op_shutdown( state: Rc<RefCell<OpState>>, #[smi] rid: ResourceId, ) -> Result<(), JsErrorBox> { let resource = state .borrow() .resource_table .get_any(rid) .map_err(JsErrorBox::from_err)?; resource.shutdown().await } #[op2(fast)] fn op_str_byte_length<'s, 'i>( scope: &mut v8::PinScope<'s, 'i>, value: v8::Local<'s, v8::Value>, ) -> u32 { if let Ok(string) = v8::Local::<v8::String>::try_from(value) { string.utf8_length(scope) as u32 } else { 0 } } /// Creates a [`CancelHandle`] resource that can be used to cancel invocations of certain ops. #[op2(fast)] #[smi] pub fn op_cancel_handle(state: &mut OpState) -> u32 { state.resource_table.add(CancelHandle::new()) } #[op2] #[serde] fn op_encode_binary_string(#[buffer] s: &[u8]) -> ByteString { ByteString::from(s) } #[op2(fast)] fn op_is_terminal(state: &mut OpState, #[smi] rid: ResourceId) -> bool { match state.resource_table.get_handle(rid) { Ok(handle) => handle.is_terminal(), _ => false, } } async fn do_load_job<'s, 'i>( scope: &mut v8::PinScope<'s, 'i>, module_map_rc: Rc<ModuleMap>, specifier: &str, code: Option<String>, ) -> Result<ModuleId, CoreError> { let root_id = RecursiveModuleLoad::side( specifier.to_string(), module_map_rc.clone(), crate::modules::SideModuleKind::Sync, code, ) .await? .run_to_completion(|load, step| match step { crate::modules::recursive_load::RegisterStep::Register { request, source, } => load .register_and_recurse(scope, request, source) .map_err(|e| e.into_error(scope, false, false)), }) .await?; let module = module_map_rc .get_module(scope, root_id) .expect("Module must exist"); match module.get_status() { v8::ModuleStatus::Uninstantiated => { module_map_rc .instantiate_module(scope, root_id) .map_err(|e| { let exception = v8::Local::new(scope, e); exception_to_err(scope, exception, false, false) })?; } v8::ModuleStatus::Instantiated => { // Already instantiated — caller (op_import_sync) will evaluate. } v8::ModuleStatus::Instantiating | v8::ModuleStatus::Evaluating => { return Err( JsErrorBox::generic(format!( "Cannot require() ES Module {specifier} in a cycle." )) .into(), ); } v8::ModuleStatus::Evaluated => { // OK } v8::ModuleStatus::Errored => { return Err( CoreErrorKind::Js(exception_to_err( scope, module.get_exception(), false, false, )) .into_box(), ); } } Ok(root_id) } /// Wrap module with another module that also exports `__esModule=true` in order /// to maintain compat with node, which does this to maintain compat with babel. fn wrap_module<'s, 'i>( scope: &mut v8::PinScope<'s, 'i>, module: v8::Local<'s, v8::Module>, ) -> Option<v8::Local<'s, v8::Module>> { const SOURCE: &str = " export * from 'original'; export {default} from 'original'; export const __esModule = true;"; let source = v8::String::new(scope, SOURCE)?; let origin = v8::ScriptOrigin::new( scope, source.into(), 0, 0, false, 0, None, true, false, true, None, ); let mut source = v8::script_compiler::Source::new(source, Some(&origin)); let wrapper_module = v8::script_compiler::compile_module(scope, &mut source)?; let global_module = v8::Global::new(scope, module); scope.set_slot(global_module); #[allow( clippy::unnecessary_wraps, reason = "required by v8 callback signature" )] fn resolve_callback<'s>( context: v8::Local<'s, v8::Context>, specifier: v8::Local<'s, v8::String>, _: v8::Local<'s, v8::FixedArray>, _: v8::Local<'s, v8::Module>, ) -> Option<v8::Local<'s, v8::Module>> { // SAFETY: It is safe to open a CallbackScope from a context in this callback. v8::callback_scope!(unsafe scope, context); debug_assert_eq!(specifier.to_rust_string_lossy(scope), "original"); let module = scope.remove_slot::<v8::Global<v8::Module>>().unwrap(); Some(v8::Local::new(scope, module)) } wrapper_module.instantiate_module(scope, resolve_callback)?; let result = wrapper_module.evaluate(scope)?; if let Ok(promise) = result.try_cast::<v8::Promise>() { promise.mark_as_handled(); if promise.state() == v8::PromiseState::Rejected { let exception_state = JsRealm::exception_state_from_scope(scope); // TODO: remove after crrev.com/c/7595271 exception_state.track_promise_rejection( scope, promise, v8::PromiseRejectEvent::PromiseHandlerAddedAfterReject, None, ); } } Some(wrapper_module) } #[op2(reentrant)] fn op_import_sync<'s, 'i>( scope: &mut v8::PinScope<'s, 'i>, #[string] specifier: &str, #[string] code: Option<String>, ) -> Result<v8::Local<'s, v8::Value>, CoreError> { let module_map_rc = JsRealm::module_map_from(scope); // no js execution within block_on let module_id = futures::executor::block_on(do_load_job( scope, module_map_rc.clone(), specifier, code, ))?; // Check for re-entrant require() cycle: if this module is already on // the op_import_sync call stack, it means CJS code required an ESM // that (transitively) requires the same ESM back. if module_map_rc .import_sync_eval_stack .borrow() .contains(&module_id) { return Err( JsErrorBox::generic(format!( "Cannot require() ES Module {specifier} in a cycle." )) .into(), ); } let module = module_map_rc .get_module(scope, module_id) .expect("Module must exist"); // A module that was evaluated asynchronously (e.g. via `await import()`) // and contains top-level await cannot be loaded via `require()`. Detect // this regardless of current status so retries after async load still // throw ERR_REQUIRE_ASYNC_MODULE. if module.is_graph_async() { return Err(CoreErrorKind::TLA.into_box()); } match module.get_status() { v8::ModuleStatus::Uninstantiated | v8::ModuleStatus::Instantiating | v8::ModuleStatus::Evaluating => { return Err( JsErrorBox::generic(format!( "Cannot require() ES Module {specifier} in a cycle." )) .into(), ); } v8::ModuleStatus::Instantiated => { module_map_rc .import_sync_eval_stack .borrow_mut() .push(module_id); let result = module_map_rc.mod_evaluate_sync(scope, module_id); module_map_rc.import_sync_eval_stack.borrow_mut().pop(); result?; } v8::ModuleStatus::Evaluated => { // OK } v8::ModuleStatus::Errored => { return Err( CoreErrorKind::Js(exception_to_err( scope, module.get_exception(), false, false, )) .into_box(), ); } } let namespace = module.get_module_namespace().cast::<v8::Object>(); v8::tc_scope!(let scope, scope); let default = v8_static_strings::DEFAULT.v8_string(scope).unwrap(); let es_module = v8_static_strings::ESMODULE.v8_string(scope).unwrap(); // If the module has a default export and no __esModule export, wrap it. if namespace.has_own_property(scope, default.into()) == Some(true) && namespace.has_own_property(scope, es_module.into()) == Some(false) { let Some(module) = wrap_module(scope, module) else { let exception = scope.exception().unwrap(); return exception_to_err_result(scope, exception, false, false) .map_err(|e| CoreErrorKind::Js(e).into_box()); }; Ok(v8::Local::new(scope, module.get_module_namespace())) } else { Ok(v8::Local::new(scope, namespace).into()) } } /// Like `op_import_sync`, but when `code` is provided, compiles the source /// directly under the given specifier URL instead of going through the module /// loader. This ensures hook-provided source is used even if the module is /// already cached in the module map from a previous disk load. #[op2(reentrant)] fn op_import_sync_with_source<'s, 'i>( scope: &mut v8::PinScope<'s, 'i>, #[string] specifier: &str, #[string] code: String, ) -> Result<v8::Local<'s, v8::Value>, CoreError> { let module_map_rc = JsRealm::module_map_from(scope); let module_id = module_map_rc .new_module_from_js_source( scope, false, crate::modules::ModuleType::JavaScript, crate::modules::ModuleName::from(specifier.to_string()), crate::modules::ModuleCodeString::from(code), false, None, ) .map_err(|e| e.into_error(scope, false, false))?; module_map_rc .instantiate_module(scope, module_id) .map_err(|e| { let exception = v8::Local::new(scope, e); CoreErrorKind::Js(exception_to_err(scope, exception, false, false)) .into_box() })?; module_map_rc.mod_evaluate_sync(scope, module_id)?; let module = module_map_rc .get_module(scope, module_id) .expect("Module must exist"); let namespace = module.get_module_namespace().cast::<v8::Object>(); v8::tc_scope!(let scope, scope); let default = v8_static_strings::DEFAULT.v8_string(scope).unwrap(); let es_module = v8_static_strings::ESMODULE.v8_string(scope).unwrap(); if namespace.has_own_property(scope, default.into()) == Some(true) && namespace.has_own_property(scope, es_module.into()) == Some(false) { let Some(module) = wrap_module(scope, module) else { let exception = scope.exception().unwrap(); return exception_to_err_result(scope, exception, false, false) .map_err(|e| CoreErrorKind::Js(e).into_box()); }; Ok(v8::Local::new(scope, module.get_module_namespace())) } else { Ok(v8::Local::new(scope, namespace).into()) } }