From 114dbc0dcad52505f677c13641917f0ef6ac1bdc Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 4 Sep 2025 09:32:15 -0700 Subject: [PATCH 1/5] Remove the boa_interop module Move the useful loaders into boa_engine, and the tests into boa_macros_tests. --- Cargo.lock | 14 +- Cargo.toml | 1 - core/engine/Cargo.toml | 2 + core/engine/src/lib.rs | 3 + .../src/module/loader}/embedded.rs | 11 +- .../src/module/{loader.rs => loader/mod.rs} | 76 ++- core/engine/src/module/mod.rs | 160 ++++++ core/interop/ABOUT.md | 37 -- core/interop/Cargo.toml | 28 - core/interop/src/lib.rs | 274 --------- core/interop/src/loaders.rs | 14 - core/interop/src/loaders/cached.rs | 61 -- core/interop/src/loaders/fallback.rs | 46 -- core/interop/src/loaders/filesystem.rs | 63 -- core/interop/src/loaders/functions.rs | 131 ----- core/interop/src/loaders/hashmap.rs | 60 -- core/interop/src/macros.rs | 542 ------------------ core/macros/src/embedded_module_loader.rs | 2 +- core/macros/src/lib.rs | 4 +- core/runtime/Cargo.toml | 1 - core/runtime/src/fetch/headers.rs | 5 +- core/runtime/src/fetch/mod.rs | 3 +- core/runtime/src/fetch/request.rs | 5 +- core/runtime/src/fetch/response.rs | 4 +- core/runtime/src/interval.rs | 6 +- core/runtime/src/microtask/mod.rs | 3 +- core/runtime/src/text/mod.rs | 4 +- core/runtime/src/url.rs | 16 +- tests/macros/Cargo.toml | 1 + .../macros}/tests/assets/fibonacci.js | 0 .../macros}/tests/assets/gcd_callback.js | 0 {core/interop => tests/macros}/tests/class.rs | 5 +- .../macros}/tests/embedded.rs | 4 +- .../macros}/tests/embedded/dir1/file3.js | 0 .../macros}/tests/embedded/dir1/file4.js | 0 .../macros}/tests/embedded/file1.js | 0 .../macros}/tests/embedded/file2.js | 0 .../macros}/tests/fibonacci.rs | 3 +- .../macros}/tests/gcd_callback.rs | 4 +- .../interop => tests/macros}/tests/module.rs | 10 +- tests/wpt/Cargo.toml | 1 - tests/wpt/src/lib.rs | 6 +- 42 files changed, 287 insertions(+), 1323 deletions(-) rename core/{interop/src/loaders => engine/src/module/loader}/embedded.rs (93%) rename core/engine/src/module/{loader.rs => loader/mod.rs} (89%) delete mode 100644 core/interop/ABOUT.md delete mode 100644 core/interop/Cargo.toml delete mode 100644 core/interop/src/lib.rs delete mode 100644 core/interop/src/loaders.rs delete mode 100644 core/interop/src/loaders/cached.rs delete mode 100644 core/interop/src/loaders/fallback.rs delete mode 100644 core/interop/src/loaders/filesystem.rs delete mode 100644 core/interop/src/loaders/functions.rs delete mode 100644 core/interop/src/loaders/hashmap.rs delete mode 100644 core/interop/src/macros.rs rename {core/interop => tests/macros}/tests/assets/fibonacci.js (100%) rename {core/interop => tests/macros}/tests/assets/gcd_callback.js (100%) rename {core/interop => tests/macros}/tests/class.rs (95%) rename {core/interop => tests/macros}/tests/embedded.rs (96%) rename {core/interop => tests/macros}/tests/embedded/dir1/file3.js (100%) rename {core/interop => tests/macros}/tests/embedded/dir1/file4.js (100%) rename {core/interop => tests/macros}/tests/embedded/file1.js (100%) rename {core/interop => tests/macros}/tests/embedded/file2.js (100%) rename {core/interop => tests/macros}/tests/fibonacci.rs (95%) rename {core/interop => tests/macros}/tests/gcd_callback.rs (93%) rename {core/interop => tests/macros}/tests/module.rs (89%) diff --git a/Cargo.lock b/Cargo.lock index 8e016598751..8cf814f8b32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,6 +384,7 @@ dependencies = [ "intrusive-collections", "itertools 0.14.0", "jemallocator", + "lz4_flex", "num-bigint", "num-integer", "num-traits", @@ -481,17 +482,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "boa_interop" -version = "0.20.0" -dependencies = [ - "boa_engine", - "boa_gc", - "boa_macros", - "lz4_flex", - "rustc-hash 2.1.1", -] - [[package]] name = "boa_macros" version = "0.20.0" @@ -511,6 +501,7 @@ name = "boa_macros_tests" version = "0.20.0" dependencies = [ "boa_engine", + "boa_gc", "trybuild", ] @@ -546,7 +537,6 @@ version = "0.20.0" dependencies = [ "boa_engine", "boa_gc", - "boa_interop", "bytemuck", "either", "futures-lite 2.6.1", diff --git a/Cargo.toml b/Cargo.toml index af056e6b48c..076f421149c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ boa_engine = { version = "~0.20.0", path = "core/engine" } boa_gc = { version = "~0.20.0", path = "core/gc" } boa_icu_provider = { version = "~0.20.0", path = "core/icu_provider" } boa_interner = { version = "~0.20.0", path = "core/interner" } -boa_interop = { version = "~0.20.0", path = "core/interop" } boa_macros = { version = "~0.20.0", path = "core/macros" } boa_parser = { version = "~0.20.0", path = "core/parser" } boa_runtime = { version = "~0.20.0", path = "core/runtime" } diff --git a/core/engine/Cargo.toml b/core/engine/Cargo.toml index c6a381397b7..3dc51514e2d 100644 --- a/core/engine/Cargo.toml +++ b/core/engine/Cargo.toml @@ -14,6 +14,7 @@ rust-version.workspace = true [features] default = ["float16", "xsum"] +embedded_lz4 = ["boa_macros/embedded_lz4", "lz4_flex"] # Replace the NaN-boxing implementation of `JsValueInner` with an # enum-based implementation. This implementation is less performant @@ -95,6 +96,7 @@ boa_string.workspace = true cow-utils.workspace = true futures-lite.workspace = true float16 = { version = "0.1", optional = true } +lz4_flex = { workspace = true, optional = true } xsum = { version = "0.1.1", optional = true } serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true diff --git a/core/engine/src/lib.rs b/core/engine/src/lib.rs index 0c1cbee2857..6b0bde436d2 100644 --- a/core/engine/src/lib.rs +++ b/core/engine/src/lib.rs @@ -134,6 +134,9 @@ pub mod prelude { pub use boa_parser::Source; } +#[doc(inline)] +pub use boa_macros::{boa_class, boa_module, embed_module_inner as __embed_module_inner}; + use std::result::Result as StdResult; // Export things to root level diff --git a/core/interop/src/loaders/embedded.rs b/core/engine/src/module/loader/embedded.rs similarity index 93% rename from core/interop/src/loaders/embedded.rs rename to core/engine/src/module/loader/embedded.rs index 56c03573b54..542633b4997 100644 --- a/core/interop/src/loaders/embedded.rs +++ b/core/engine/src/module/loader/embedded.rs @@ -20,8 +20,8 @@ use boa_engine::{Context, JsNativeError, JsResult, JsString, Module, Source}; #[macro_export] macro_rules! embed_module { ($($x: expr),*) => { - $crate::loaders::embedded::EmbeddedModuleLoader::from_iter( - $crate::boa_macros::embed_module_inner!($($x),*), + $crate::module::embedded::EmbeddedModuleLoader::from_iter( + $crate::__embed_module_inner!($($x),*), ) }; } @@ -104,11 +104,12 @@ pub struct EmbeddedModuleLoader { } impl EmbeddedModuleLoader { - /// Gets a module in the `EmbeddedModuleLoader`. + /// Get a module if it has been parsed and created. If the module is not found or + /// was not loaded, this will return `None`. #[must_use] - pub fn get_module(&self, specifier: &JsString) -> Option { + pub fn get_module(&self, name: &JsString) -> Option { self.map - .get(specifier) + .get(name) .and_then(|module| module.borrow().as_module().cloned()) } } diff --git a/core/engine/src/module/loader.rs b/core/engine/src/module/loader/mod.rs similarity index 89% rename from core/engine/src/module/loader.rs rename to core/engine/src/module/loader/mod.rs index c508b8735fb..eda867ad1f8 100644 --- a/core/engine/src/module/loader.rs +++ b/core/engine/src/module/loader/mod.rs @@ -11,12 +11,14 @@ use boa_parser::Source; use crate::script::Script; use crate::{ - Context, JsError, JsNativeError, JsResult, JsString, js_string, object::JsObject, realm::Realm, - vm::ActiveRunnable, + Context, JsError, JsNativeError, JsResult, JsString, js_error, js_string, object::JsObject, + realm::Realm, vm::ActiveRunnable, }; use super::Module; +pub mod embedded; + /// Resolves paths from the referrer and the specifier, normalize the paths and ensure the path /// is within a base. If the base is empty, that last verification will be skipped. /// @@ -266,6 +268,76 @@ impl ModuleLoader for IdleModuleLoader { } } +/// A module loader that uses a map of specifier -> Module to resolve. +/// If the module was not registered, it will not be resolved. +/// +/// A resolution relative to the referrer is performed when loading a +/// module. +#[derive(Default, Debug, Clone)] +pub struct MapModuleLoader { + inner: RefCell>, +} + +impl MapModuleLoader { + /// Creates an empty map module loader. + #[must_use] + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Insert or replace a mapping in the inner map, returning any previous module + /// if there was one. + #[inline] + pub fn insert(&self, specifier: impl AsRef, module: Module) -> Option { + self.inner + .borrow_mut() + .insert(PathBuf::from(specifier.as_ref()), module) + } + + /// Clear the map. + pub fn clear(&self) { + self.inner.borrow_mut().clear(); + } +} + +impl FromIterator<(String, Module)> for MapModuleLoader { + fn from_iter>(iter: T) -> Self { + Self { + inner: RefCell::new( + iter.into_iter() + .map(|(k, v)| (PathBuf::from(k), v)) + .collect(), + ), + } + } +} + +impl ModuleLoader for MapModuleLoader { + fn load_imported_module( + self: Rc, + referrer: Referrer, + specifier: JsString, + context: &RefCell<&mut Context>, + ) -> impl Future> { + let result = (|| { + let path = resolve_module_specifier( + None, + &specifier, + referrer.path(), + &mut context.borrow_mut(), + )?; + if let Some(module) = self.inner.borrow().get(&path) { + Ok(module.clone()) + } else { + Err(js_error!(TypeError: "Module could not be found.")) + } + })(); + + async { result } + } +} + /// A simple module loader that loads modules relative to a root path. /// /// # Note diff --git a/core/engine/src/module/mod.rs b/core/engine/src/module/mod.rs index 386e63d2b7a..61a74299aef 100644 --- a/core/engine/src/module/mod.rs +++ b/core/engine/src/module/mod.rs @@ -685,3 +685,163 @@ impl + Clone> IntoJsModule fo ) } } + +#[test] +#[allow(clippy::missing_panics_doc)] +fn into_js_module() { + use boa_engine::interop::{ContextData, JsRest}; + use boa_engine::{ + Context, IntoJsFunctionCopied, JsValue, Module, Source, UnsafeIntoJsFunction, js_string, + }; + use boa_gc::{Gc, GcRefCell}; + use std::cell::RefCell; + use std::rc::Rc; + + type ResultType = Gc>; + + let loader = Rc::new(MapModuleLoader::default()); + let mut context = Context::builder() + .module_loader(loader.clone()) + .build() + .unwrap(); + + let foo_count = Rc::new(RefCell::new(0)); + let bar_count = Rc::new(RefCell::new(0)); + let dad_count = Rc::new(RefCell::new(0)); + + context.insert_data(Gc::new(GcRefCell::new(JsValue::undefined()))); + + let module = unsafe { + vec![ + ( + js_string!("foo"), + { + let counter = foo_count.clone(); + move || { + *counter.borrow_mut() += 1; + + *counter.borrow() + } + } + .into_js_function_unsafe(&mut context), + ), + ( + js_string!("bar"), + UnsafeIntoJsFunction::into_js_function_unsafe( + { + let counter = bar_count.clone(); + move |i: i32| { + *counter.borrow_mut() += i; + } + }, + &mut context, + ), + ), + ( + js_string!("dad"), + UnsafeIntoJsFunction::into_js_function_unsafe( + { + let counter = dad_count.clone(); + move |args: JsRest<'_>, context: &mut Context| { + *counter.borrow_mut() += args + .into_iter() + .map(|i| i.try_js_into::(context).unwrap()) + .sum::(); + } + }, + &mut context, + ), + ), + ( + js_string!("send"), + (move |value: JsValue, ContextData(result): ContextData| { + *result.borrow_mut() = value; + }) + .into_js_function_copied(&mut context), + ), + ] + } + .into_js_module(&mut context); + + loader.insert("test", module); + + let source = Source::from_bytes( + r" + import * as test from 'test'; + let result = test.foo(); + test.foo(); + for (let i = 1; i <= 5; i++) { + test.bar(i); + } + for (let i = 1; i < 5; i++) { + test.dad(1, 2, 3); + } + + test.send(result); + ", + ); + let root_module = Module::parse(source, None, &mut context).unwrap(); + + let promise_result = root_module.load_link_evaluate(&mut context); + context.run_jobs().unwrap(); + + // Checking if the final promise didn't return an error. + assert!( + promise_result.state().as_fulfilled().is_some(), + "module didn't execute successfully! Promise: {:?}", + promise_result.state() + ); + + let result = context.get_data::().unwrap().borrow().clone(); + + assert_eq!(*foo_count.borrow(), 2); + assert_eq!(*bar_count.borrow(), 15); + assert_eq!(*dad_count.borrow(), 24); + assert_eq!(result.try_js_into(&mut context), Ok(1u32)); +} + +#[test] +fn can_throw_exception() { + use boa_engine::{ + Context, IntoJsFunctionCopied, JsError, JsResult, JsValue, Module, Source, js_string, + }; + use std::rc::Rc; + + let loader = Rc::new(MapModuleLoader::default()); + let mut context = Context::builder() + .module_loader(loader.clone()) + .build() + .unwrap(); + + let module = vec![( + js_string!("doTheThrow"), + IntoJsFunctionCopied::into_js_function_copied( + |message: JsValue| -> JsResult<()> { Err(JsError::from_opaque(message)) }, + &mut context, + ), + )] + .into_js_module(&mut context); + + loader.insert("test", module); + + let source = Source::from_bytes( + r" + import * as test from 'test'; + try { + test.doTheThrow('javascript'); + } catch(e) { + throw 'from ' + e; + } + ", + ); + let root_module = Module::parse(source, None, &mut context).unwrap(); + + let promise_result = root_module.load_link_evaluate(&mut context); + context.run_jobs().unwrap(); + + // Checking if the final promise didn't return an error. + assert_eq!( + promise_result.state().as_rejected(), + Some(&js_string!("from javascript").into()) + ); +} diff --git a/core/interop/ABOUT.md b/core/interop/ABOUT.md deleted file mode 100644 index 71f1fc40831..00000000000 --- a/core/interop/ABOUT.md +++ /dev/null @@ -1,37 +0,0 @@ -# About Boa - -Boa is an open-source, experimental ECMAScript Engine written in Rust for -lexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some -of the [language][boa-conformance]. More information can be viewed at [Boa's -website][boa-web]. - -Try out the most recent release with Boa's live demo -[playground][boa-playground]. - -## Boa Crates - -- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation -- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree. -- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution. -- [**`boa_gc`**][gc] - Boa's garbage collector. -- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. -- [**`boa_interner`**][interner] - Boa's string interner. -- [**`boa_macros`**][macros] - Boa's macros. -- [**`boa_parser`**][parser] - Boa's lexer and parser. -- [**`boa_runtime`**][runtime] - Boa's WebAPI features. -- [**`boa_string`**][string] - Boa's ECMAScript string implementation. - -[boa-conformance]: https://boajs.dev/conformance -[boa-web]: https://boajs.dev/ -[boa-playground]: https://boajs.dev/playground -[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html -[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html -[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html -[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html -[interop]: https://docs.rs/boa_interop/latest/boa_interop/index.html -[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html -[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html -[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html -[string]: https://docs.rs/boa_string/latest/boa_string/index.html -[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html -[cli]: https://crates.io/crates/boa_cli diff --git a/core/interop/Cargo.toml b/core/interop/Cargo.toml deleted file mode 100644 index 492e2c261ea..00000000000 --- a/core/interop/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "boa_interop" -description = "Interop utilities for integrating boa with a Rust host." -keywords = ["javascript", "js", "interop"] -categories = ["api-bindings"] -version.workspace = true -edition.workspace = true -authors.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true -publish = false - -[dependencies] -boa_engine.workspace = true -boa_gc.workspace = true -boa_macros.workspace = true -lz4_flex = { workspace = true, optional = true } -rustc-hash = { workspace = true, features = ["std"] } - -[lints] -workspace = true - -[package.metadata.docs.rs] -all-features = true - -[features] -embedded_lz4 = ["boa_macros/embedded_lz4", "lz4_flex"] diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs deleted file mode 100644 index 07e695b9885..00000000000 --- a/core/interop/src/lib.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! Interop utilities between Boa and its host. - -pub use boa_engine; -pub use boa_macros; - -pub mod loaders; -pub mod macros; - -// Re-export in case some people depend on boa_interop. -#[deprecated(note = "Please use these exports from boa_engine::interop instead.")] -pub use boa_engine::interop::{ContextData, Ignore, JsClass, JsRest}; - -#[deprecated(note = "Please use these exports from boa_engine instead.")] -pub use boa_engine::{IntoJsFunctionCopied, IntoJsModule, UnsafeIntoJsFunction}; - -#[test] -#[allow(clippy::missing_panics_doc)] -fn into_js_module() { - use boa_engine::{Context, JsValue, Module, Source, js_string}; - use boa_gc::{Gc, GcRefCell}; - use std::cell::RefCell; - use std::rc::Rc; - - type ResultType = Gc>; - - let loader = Rc::new(loaders::HashMapModuleLoader::new()); - let mut context = Context::builder() - .module_loader(loader.clone()) - .build() - .unwrap(); - - let foo_count = Rc::new(RefCell::new(0)); - let bar_count = Rc::new(RefCell::new(0)); - let dad_count = Rc::new(RefCell::new(0)); - - context.insert_data(Gc::new(GcRefCell::new(JsValue::undefined()))); - - let module = unsafe { - vec![ - ( - js_string!("foo"), - { - let counter = foo_count.clone(); - move || { - *counter.borrow_mut() += 1; - - *counter.borrow() - } - } - .into_js_function_unsafe(&mut context), - ), - ( - js_string!("bar"), - UnsafeIntoJsFunction::into_js_function_unsafe( - { - let counter = bar_count.clone(); - move |i: i32| { - *counter.borrow_mut() += i; - } - }, - &mut context, - ), - ), - ( - js_string!("dad"), - UnsafeIntoJsFunction::into_js_function_unsafe( - { - let counter = dad_count.clone(); - move |args: JsRest<'_>, context: &mut Context| { - *counter.borrow_mut() += args - .into_iter() - .map(|i| i.try_js_into::(context).unwrap()) - .sum::(); - } - }, - &mut context, - ), - ), - ( - js_string!("send"), - (move |value: JsValue, ContextData(result): ContextData| { - *result.borrow_mut() = value; - }) - .into_js_function_copied(&mut context), - ), - ] - } - .into_js_module(&mut context); - - loader.register(js_string!("test"), module); - - let source = Source::from_bytes( - r" - import * as test from 'test'; - let result = test.foo(); - test.foo(); - for (let i = 1; i <= 5; i++) { - test.bar(i); - } - for (let i = 1; i < 5; i++) { - test.dad(1, 2, 3); - } - - test.send(result); - ", - ); - let root_module = Module::parse(source, None, &mut context).unwrap(); - - let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs().unwrap(); - - // Checking if the final promise didn't return an error. - assert!( - promise_result.state().as_fulfilled().is_some(), - "module didn't execute successfully! Promise: {:?}", - promise_result.state() - ); - - let result = context.get_data::().unwrap().borrow().clone(); - - assert_eq!(*foo_count.borrow(), 2); - assert_eq!(*bar_count.borrow(), 15); - assert_eq!(*dad_count.borrow(), 24); - assert_eq!(result.try_js_into(&mut context), Ok(1u32)); -} - -#[test] -fn can_throw_exception() { - use boa_engine::{Context, JsError, JsResult, JsValue, Module, Source, js_string}; - use std::rc::Rc; - - let loader = Rc::new(loaders::HashMapModuleLoader::new()); - let mut context = Context::builder() - .module_loader(loader.clone()) - .build() - .unwrap(); - - let module = vec![( - js_string!("doTheThrow"), - IntoJsFunctionCopied::into_js_function_copied( - |message: JsValue| -> JsResult<()> { Err(JsError::from_opaque(message)) }, - &mut context, - ), - )] - .into_js_module(&mut context); - - loader.register(js_string!("test"), module); - - let source = Source::from_bytes( - r" - import * as test from 'test'; - try { - test.doTheThrow('javascript'); - } catch(e) { - throw 'from ' + e; - } - ", - ); - let root_module = Module::parse(source, None, &mut context).unwrap(); - - let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs().unwrap(); - - // Checking if the final promise didn't return an error. - assert_eq!( - promise_result.state().as_rejected(), - Some(&js_string!("from javascript").into()) - ); -} - -#[test] -fn class() { - use boa_engine::class::{Class, ClassBuilder}; - use boa_engine::property::Attribute; - use boa_engine::{Context, JsResult, JsValue, Module, Source, js_string}; - use boa_macros::{Finalize, JsData, Trace}; - use std::rc::Rc; - - #[derive(Debug, Trace, Finalize, JsData)] - struct Test { - value: i32, - } - - impl Test { - #[allow(clippy::needless_pass_by_value)] - fn get_value(this: JsClass) -> i32 { - this.borrow().value - } - - #[allow(clippy::needless_pass_by_value)] - fn set_value(this: JsClass, new_value: i32) { - (*this.borrow_mut()).value = new_value; - } - } - - impl Class for Test { - const NAME: &'static str = "Test"; - - fn init(class: &mut ClassBuilder<'_>) -> JsResult<()> { - let get_value = Self::get_value.into_js_function_copied(class.context()); - class.method(js_string!("getValue"), 0, get_value); - let set_value = Self::set_value.into_js_function_copied(class.context()); - class.method(js_string!("setValue"), 1, set_value); - - let get_value_getter = Self::get_value - .into_js_function_copied(class.context()) - .to_js_function(class.context().realm()); - let set_value_setter = Self::set_value - .into_js_function_copied(class.context()) - .to_js_function(class.context().realm()); - class.accessor( - js_string!("value_get"), - Some(get_value_getter), - None, - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ); - class.accessor( - js_string!("value_set"), - None, - Some(set_value_setter), - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ); - - Ok(()) - } - - fn data_constructor( - _new_target: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - Ok(Self { value: 123 }) - } - } - - let loader = Rc::new(loaders::HashMapModuleLoader::new()); - let mut context = Context::builder() - .module_loader(loader.clone()) - .build() - .unwrap(); - - context.register_global_class::().unwrap(); - - let source = Source::from_bytes( - r" - let t = new Test(); - if (t.getValue() != 123) { - throw 'invalid value'; - } - t.setValue(456); - if (t.getValue() != 456) { - throw 'invalid value 456'; - } - if (t.value_get != 456) { - throw 'invalid value 456'; - } - t.value_set = 789; - if (t.getValue() != 789) { - throw 'invalid value 789'; - } - ", - ); - let root_module = Module::parse(source, None, &mut context).unwrap(); - - let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs().unwrap(); - - // Checking if the final promise didn't return an error. - assert!( - promise_result.state().as_fulfilled().is_some(), - "module didn't execute successfully! Promise: {:?}", - promise_result.state() - ); -} diff --git a/core/interop/src/loaders.rs b/core/interop/src/loaders.rs deleted file mode 100644 index 19aa73f5ede..00000000000 --- a/core/interop/src/loaders.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! A collection of JS [`boa_engine::module::ModuleLoader`]s utilities to help in -//! creating custom module loaders. -pub mod cached; -pub mod embedded; -pub mod fallback; -pub mod filesystem; -pub mod functions; -pub mod hashmap; - -pub use cached::CachedModuleLoader; -pub use fallback::FallbackModuleLoader; -pub use filesystem::FsModuleLoader; -pub use functions::FnModuleLoader; -pub use hashmap::HashMapModuleLoader; diff --git a/core/interop/src/loaders/cached.rs b/core/interop/src/loaders/cached.rs deleted file mode 100644 index 9992ee73575..00000000000 --- a/core/interop/src/loaders/cached.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! A module loader that caches modules once they're resolved. -use boa_engine::module::{ModuleLoader, Referrer, resolve_module_specifier}; -use boa_engine::{Context, JsNativeError, JsResult, JsString, Module}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::path::PathBuf; -use std::rc::Rc; - -/// A module loader that caches modules once they're resolved. -#[allow(clippy::module_name_repetitions)] -#[derive(Clone, Debug)] -pub struct CachedModuleLoader { - inner: Rc, - // TODO: Use a specifier instead of a PathBuf. - cache: RefCell>, -} - -impl CachedModuleLoader { - /// Create a new [`CachedModuleLoader`] from an inner module loader and - /// an empty cache. - pub fn new(inner: B) -> Self { - Self { - inner: Rc::new(inner), - cache: RefCell::new(HashMap::new()), - } - } -} - -impl ModuleLoader for CachedModuleLoader -where - B: ModuleLoader, -{ - async fn load_imported_module( - self: Rc, - referrer: Referrer, - specifier: JsString, - context: &RefCell<&mut Context>, - ) -> JsResult { - let path = - resolve_module_specifier(None, &specifier, referrer.path(), &mut context.borrow_mut()) - .map_err(|err| { - JsNativeError::typ() - .with_message("could not resolve module specifier") - .with_cause(err) - })?; - - if let Some(module) = self.cache.borrow().get(&path).cloned() { - return Ok(module); - } - - let module = self - .inner - .clone() - .load_imported_module(referrer, specifier, context) - .await?; - - self.cache.borrow_mut().insert(path, module.clone()); - - Ok(module) - } -} diff --git a/core/interop/src/loaders/fallback.rs b/core/interop/src/loaders/fallback.rs deleted file mode 100644 index d1e436eed0b..00000000000 --- a/core/interop/src/loaders/fallback.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! A module loader that tries to load modules from multiple loaders. -use std::cell::RefCell; -use std::rc::Rc; - -use boa_engine::module::{ModuleLoader, Referrer}; -use boa_engine::{Context, JsResult, JsString, Module}; - -/// A [`ModuleLoader`] that tries to load a module from one loader, and if that fails, -/// falls back to another loader. -#[allow(clippy::module_name_repetitions)] -#[derive(Clone, Debug)] -pub struct FallbackModuleLoader(Rc, Rc); - -impl FallbackModuleLoader { - /// Create a new [`FallbackModuleLoader`] from two loaders. - pub fn new(loader: L, fallback: R) -> Self { - Self(Rc::new(loader), Rc::new(fallback)) - } -} - -impl ModuleLoader for FallbackModuleLoader -where - L: ModuleLoader, - R: ModuleLoader, -{ - async fn load_imported_module( - self: Rc, - referrer: Referrer, - specifier: JsString, - context: &RefCell<&mut Context>, - ) -> JsResult { - if let Ok(module) = self - .0 - .clone() - .load_imported_module(referrer.clone(), specifier.clone(), context) - .await - { - return Ok(module); - } - - self.1 - .clone() - .load_imported_module(referrer, specifier, context) - .await - } -} diff --git a/core/interop/src/loaders/filesystem.rs b/core/interop/src/loaders/filesystem.rs deleted file mode 100644 index a894c9d5c23..00000000000 --- a/core/interop/src/loaders/filesystem.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Filesystem module loader. Loads modules from the filesystem. - -use boa_engine::module::{ModuleLoader, Referrer, resolve_module_specifier}; -use boa_engine::{Context, JsError, JsNativeError, JsResult, JsString, Module, Source, js_string}; -use std::cell::RefCell; -use std::path::{Path, PathBuf}; -use std::rc::Rc; - -/// A module loader that loads modules from the filesystem. -#[derive(Clone, Debug)] -pub struct FsModuleLoader { - root: PathBuf, -} - -impl FsModuleLoader { - /// Create a new [`FsModuleLoader`] from a root path. - /// - /// # Errors - /// An error happens if the root path cannot be canonicalized (e.g. does - /// not exists). - pub fn new(root: impl AsRef) -> JsResult { - let root = root.as_ref(); - let root = root.canonicalize().map_err(|e| { - JsNativeError::typ() - .with_message(format!("could not set module root `{}`", root.display())) - .with_cause(JsError::from_opaque(js_string!(e.to_string()).into())) - })?; - - Ok(Self { root }) - } -} - -impl ModuleLoader for FsModuleLoader { - fn load_imported_module( - self: Rc, - referrer: Referrer, - specifier: JsString, - context: &RefCell<&mut Context>, - ) -> impl Future> { - let result = (|| { - let short_path = specifier.to_std_string_escaped(); - let path = resolve_module_specifier( - Some(&self.root), - &specifier, - referrer.path(), - &mut context.borrow_mut(), - )?; - - let source = Source::from_filepath(&path).map_err(|err| { - JsNativeError::typ() - .with_message(format!("could not open file `{short_path}`")) - .with_cause(JsError::from_opaque(js_string!(err.to_string()).into())) - })?; - let module = Module::parse(source, None, &mut context.borrow_mut()).map_err(|err| { - JsNativeError::syntax() - .with_message(format!("could not parse module `{short_path}`")) - .with_cause(err) - })?; - Ok(module) - })(); - async { result } - } -} diff --git a/core/interop/src/loaders/functions.rs b/core/interop/src/loaders/functions.rs deleted file mode 100644 index 9155ee69096..00000000000 --- a/core/interop/src/loaders/functions.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! This module contains types that help create custom module loaders from functions. -use boa_engine::module::{ModuleLoader, Referrer, resolve_module_specifier}; -use boa_engine::{Context, JsNativeError, JsResult, JsString, Module, Source}; -use std::cell::RefCell; -use std::io::Cursor; -use std::rc::Rc; - -/// Create a [`ModuleLoader`] from a function that -/// takes a referrer and a path, and returns a [Module] if it exists, or an error. -/// -/// This function cannot be `async` and must be blocking. An `async` version of -/// this code will likely exist as a separate function in the future. -/// -/// `F` cannot be a mutable closure as it could recursively call itself. -#[derive(Copy, Clone)] -pub struct FnModuleLoader -where - F: Fn(&Referrer, &JsString) -> JsResult, -{ - factory: F, - name: &'static str, -} - -impl FnModuleLoader -where - F: Fn(&Referrer, &JsString) -> JsResult, -{ - /// Create a new [`FnModuleLoader`] from a function that takes a path and returns - /// a [Module] if it exists. - pub const fn new(factory: F) -> Self { - Self::named(factory, "Unnamed") - } - - /// Create a new [`FnModuleLoader`] from a function that takes a path and returns - /// a [Module] if it exists, with a name. - pub const fn named(factory: F, name: &'static str) -> Self { - Self { factory, name } - } -} - -impl std::fmt::Debug for FnModuleLoader -where - F: Fn(&Referrer, &JsString) -> JsResult, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("FnModuleLoader").field(&self.name).finish() - } -} - -impl ModuleLoader for FnModuleLoader -where - F: Fn(&Referrer, &JsString) -> JsResult + 'static, -{ - fn load_imported_module( - self: Rc, - referrer: Referrer, - specifier: JsString, - _context: &RefCell<&mut Context>, - ) -> impl Future> { - let module = (self.factory)(&referrer, &specifier); - async { module } - } -} - -/// Create a module loader from a function that takes a resolved path -/// and optionally returns the source code. The path is resolved before -/// passing it. If the source cannot be found or would generate an -/// error, the function should return `None`. -/// -/// This function cannot be `async` and must be blocking. An `async` version of -/// this code will likely exist as a separate function in the future. -/// -/// `F` cannot be a mutable closure as it could recursively call itself. -pub struct SourceFnModuleLoader(F, &'static str) -where - F: Fn(&str) -> Option; - -impl std::fmt::Debug for SourceFnModuleLoader -where - F: Fn(&str) -> Option, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("SourceFnModuleLoader") - .field(&self.1) - .finish() - } -} - -impl SourceFnModuleLoader -where - F: Fn(&str) -> Option, -{ - /// Create a new [`SourceFnModuleLoader`] from a function. - pub const fn new(f: F) -> Self { - Self(f, "Unnamed") - } - - /// Create a new [`SourceFnModuleLoader`] from a function, with a name. - /// The name is used in error messages and debug strings. - pub const fn named(f: F, name: &'static str) -> Self { - Self(f, name) - } -} - -impl ModuleLoader for SourceFnModuleLoader -where - F: Fn(&str) -> Option + 'static, -{ - fn load_imported_module( - self: Rc, - referrer: Referrer, - specifier: JsString, - context: &RefCell<&mut Context>, - ) -> impl Future> { - let result = (|| { - let p = resolve_module_specifier( - None, - &specifier, - referrer.path(), - &mut context.borrow_mut(), - )?; - - let m = self.0(&p.to_string_lossy()) - .ok_or_else(|| JsNativeError::error().with_message("Module not found"))?; - let s = Source::from_reader(Cursor::new(m.as_bytes()), Some(&p)); - - Module::parse(s, None, &mut context.borrow_mut()) - })(); - async { result } - } -} diff --git a/core/interop/src/loaders/hashmap.rs b/core/interop/src/loaders/hashmap.rs deleted file mode 100644 index 2ea30d9e687..00000000000 --- a/core/interop/src/loaders/hashmap.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! A `ModuleLoader` that loads modules from a `HashMap` based on the name. -use std::cell::RefCell; -use std::rc::Rc; - -use rustc_hash::FxHashMap; - -use boa_engine::module::{ModuleLoader, Referrer}; -use boa_engine::{Context, JsNativeError, JsResult, JsString, Module}; -use boa_gc::GcRefCell; - -/// A `ModuleLoader` that loads modules from a `HashMap` based on the name. -/// After registering modules, this loader will look for the exact name -/// in its internal map to resolve. -#[derive(Debug, Clone)] -pub struct HashMapModuleLoader(GcRefCell>); - -impl Default for HashMapModuleLoader { - fn default() -> Self { - Self(GcRefCell::new(FxHashMap::default())) - } -} - -impl HashMapModuleLoader { - /// Creates an empty `HashMapModuleLoader`. - #[must_use] - pub fn new() -> Self { - Self::default() - } - - /// Registers a module with a given name. - pub fn register(&self, key: impl Into, value: Module) { - self.0.borrow_mut().insert(key.into(), value); - } -} - -impl FromIterator<(JsString, Module)> for HashMapModuleLoader { - fn from_iter>(iter: T) -> Self { - let map = iter.into_iter().collect(); - Self(GcRefCell::new(map)) - } -} - -impl ModuleLoader for HashMapModuleLoader { - fn load_imported_module( - self: Rc, - _referrer: Referrer, - specifier: JsString, - _context: &RefCell<&mut Context>, - ) -> impl Future> { - let result = self.0.borrow().get(&specifier).cloned().ok_or_else(|| { - JsNativeError::typ() - .with_message(format!( - "could not find module `{}`", - specifier.display_escaped() - )) - .into() - }); - async { result } - } -} diff --git a/core/interop/src/macros.rs b/core/interop/src/macros.rs deleted file mode 100644 index db722eabd91..00000000000 --- a/core/interop/src/macros.rs +++ /dev/null @@ -1,542 +0,0 @@ -//! Module declaring interop macro rules. - -/// Declare a JavaScript class, in a simpler way. -/// -/// This can make declaration of JavaScript classes easier by using a hybrid -/// declarative approach. The class itself follows a closer syntax to JavaScript -/// while the method arguments/results and bodies are written in Rust. -/// -/// This only declares the Boa interop parts of the class. The actual type must -/// be declared separately as a Rust type, along with necessary derives and -/// traits. -/// -/// # Allowed declarations (in order): -/// -/// ## Any number of JS fields -/// ```ignore -/// public () -> { } -/// ``` -/// Declare public fields on the JavaScript prototype at construction. This is optional. -/// Those fields can be overwritten on the object itself. -/// -/// ## Any number of properties -/// ```ignore -/// property [as ""] { -/// get() -> { } -/// set() [-> JsResult<()>] { } -/// } -/// ``` -/// Declare a getter and/or a setter on a JavaScript class property. This is optional. -/// Both get and set are optional, but at least one must be present. The `set` method -/// must either return the unit type or a `JsResult<...>`. The value returned will be -/// ignored, only errors will be used. -/// -/// Using the `as` keyword, you can set the name of the property in JavaScript that -/// would otherwise not be possible in Rust. -/// -/// ## Required JavaScript Constructor -/// ```ignore -/// constructor() { } -/// ``` -/// Declares the JS constructor for the class. This is required, but could throw if creating -/// the object fails. -/// The body MUST return `JsResult`. -/// -/// ## An optional init function -/// ```ignore -/// fn init(class: &mut ClassBuilder) -> JsResult<()> { } -/// ``` -/// Declare a block of code to add at the end of the implementation's init function. -/// -/// ## Any number of methods -/// ```ignore -/// fn [as ]() -> { } -/// ``` -/// Declare methods on the class. This is optional. -/// -/// Using the `as` keyword, you can set the name of the property in JavaScript that -/// would otherwise not be possible in Rust. -/// -/// ---- -/// # Example -/// -/// Here's an example using the animal class declared in [`boa_engine::class`]: -/// ``` -/// # use boa_engine::{JsString, JsData, js_string}; -/// # use boa_gc::{Finalize, Trace}; -/// use boa_engine::interop::{Ignore, JsClass}; -/// use boa_interop::{js_class}; -/// -/// #[derive(Clone, Trace, Finalize, JsData)] -/// pub enum Animal { -/// Cat, -/// Dog, -/// Other, -/// } -/// -/// js_class! { -/// // Implement [`Class`] trait for the `Animal` enum. -/// class Animal { -/// // This sets a field on the JavaScript object. The arguments to -/// // `init` are the arguments passed to the constructor. This -/// // function MUST return the value to be set on the field. If this -/// // returns a `JsResult`, it will be unwrapped and error out during -/// // construction of the object. -/// public age(_name: Ignore, age: i32) -> i32 { -/// age -/// } -/// -/// // This is called when a new instance of the class is created in -/// // JavaScript, e.g. `new Animal("cat")`. -/// // This method is mandatory and MUST return `JsResult`. -/// constructor(name: String) { -/// match name.as_str() { -/// "cat" => Ok(Animal::Cat), -/// "dog" => Ok(Animal::Dog), -/// _ => Ok(Animal::Other), -/// } -/// } -/// -/// // Declare a function on the class itself. -/// // There is a current limitation using `self` in methods, so the -/// // instance must be accessed using an actual argument. -/// fn speak(this: JsClass) -> JsString { -/// match *this.borrow() { -/// Animal::Cat => js_string!("meow"), -/// Animal::Dog => js_string!("woof"), -/// Animal::Other => js_string!(r"¯\_(ツ)_/¯"), -/// } -/// } -/// } -/// } -/// -/// fn main() { -///# use boa_engine::{Context, JsString, Source, js_str}; -/// -/// let mut context = Context::default(); -/// -/// context.register_global_class::().unwrap(); -/// -/// let result = context.eval(Source::from_bytes(r#" -/// let pet = new Animal("dog", 3); -/// -/// `My pet is ${pet.age} years old. Right, buddy? - ${pet.speak()}!` -/// "#)).expect("Could not evaluate script"); -/// -/// assert_eq!( -/// result.as_string().unwrap(), -/// js_str!("My pet is 3 years old. Right, buddy? - woof!") -/// ); -/// } -/// ``` -#[macro_export] -macro_rules! js_class { - ( - class $class_name: ident $(as $class_js_name: literal)? { - $( - $(#[$field_attr: meta])* - public $field_name: ident - ( $( $field_arg: ident: $field_arg_type: ty ),* $(,)? ) -> $field_ty: ty - $field_body: block - )* - - $( - $(#[$field_prop_attr: meta])* - property $field_prop_name: ident $(as $field_prop_js_name: literal)? { - $( - $(#[$field_prop_get_attr: meta])* - $(fn)? get( $( $field_prop_get_arg: ident: $field_prop_get_arg_type: ty ),* $(,)? ) -> $field_prop_get_ty: ty - $field_prop_get_body: block - )? - - $( - $(#[$field_prop_set_attr: meta])* - $(fn)? set( $( $field_prop_set_arg: ident: $field_prop_set_arg_type: ty ),* $(,)? ) - $( -> $field_prop_set_ty: ty )? - $field_prop_set_body: block - )? - } - )* - - - $(#[$constructor_attr: meta])* - constructor( $( $ctor_arg: ident: $ctor_arg_ty: ty ),* $(,)? ) - $constructor_body: block - - $( - $(#[$init_attr: meta])* - init($init_class_builder_name: ident : &mut ClassBuilder $(,)?) -> JsResult<()> - $init_body: block - )? - - $( - $(#[$method_attr: meta])* - fn $method_name: ident $( as $method_js_name: literal )? - ( $( $fn_arg: ident: $fn_arg_type: ty ),* $(,)? ) - $(-> $result_type: ty)? - $method_body: block - )* - } - ) => { - impl $crate::boa_engine::class::Class for $class_name { - - const NAME: &'static str = $crate::__js_class_name!($class_name, $($class_js_name)?); - - const LENGTH: usize = $crate::__count!( $( $ctor_arg )* ); - - #[allow(clippy::items_after_statements)] - fn init(class: &mut $crate::boa_engine::class::ClassBuilder<'_>) -> $crate::boa_engine::JsResult<()> { - // Add properties. - $( - // Declare a function so that the compiler prevents duplicated names. - #[allow(dead_code)] - fn $field_prop_name() {} - - $crate::__get_set_decl!( - class, - $field_prop_name, - $($field_prop_js_name)?, - $( - @get( $( $field_prop_get_arg: $field_prop_get_arg_type ),* ) -> $field_prop_get_ty - $field_prop_get_body, - )? - $( - @set( $( $field_prop_set_arg: $field_prop_set_arg_type ),* ) - $( -> $field_prop_set_ty )? - $field_prop_set_body - )? - ); - )* - - // Add all methods to the class. - $( - fn $method_name ( $($fn_arg: $fn_arg_type),* ) -> $( $result_type )? - $method_body - - let function = $crate::IntoJsFunctionCopied::into_js_function_copied( - $method_name, - class.context(), - ); - - let function_name = $crate::__js_class_name!($method_name, $($method_js_name)?); - - class.method( - $crate::boa_engine::JsString::from(function_name), - $crate::__count!($( $fn_arg )*), - function, - ); - )* - - // Add the init body, if any. - $({ - let $init_class_builder_name = class; - let result: $crate::boa_engine::JsResult<()> = $init_body; - - result?; - })? - - Ok(()) - } - - #[allow(unused_variables)] - fn data_constructor( - new_target: &$crate::boa_engine::JsValue, - args: &[$crate::boa_engine::JsValue], - context: &mut $crate::boa_engine::Context, - ) -> $crate::boa_engine::JsResult<$class_name> { - let rest = args; - $( - let ($ctor_arg, rest) : ($ctor_arg_ty, _) = $crate::boa_engine::interop::TryFromJsArgument::try_from_js_argument(new_target, rest, context)?; - )* - - $constructor_body - } - - fn object_constructor( - instance: &$crate::boa_engine::JsObject, - args: &[$crate::boa_engine::JsValue], - context: &mut $crate::boa_engine::Context - ) -> $crate::boa_engine::JsResult<()> { - // Public JS fields first. - $( - fn $field_name ( $($field_arg: $field_arg_type),* ) -> $field_ty - $field_body - - let function = $crate::IntoJsFunctionCopied::into_js_function_copied( - $field_name, - context, - ); - - instance.set( - $crate::boa_engine::JsString::from(stringify!($field_name)), - function.call(&$crate::boa_engine::JsValue::undefined(), args, context)?, - false, - context - )?; - )* - - Ok(()) - } - - } - } -} - -/// Internal macro to get the JavaScript class name. -#[macro_export] -macro_rules! __js_class_name { - ($class_name: ident, $class_js_name: literal) => { - $class_js_name - }; - ($class_name: ident,) => { - stringify!($class_name) - }; -} - -/// Internal macro to get the JavaScript class length. -#[macro_export] -macro_rules! __count { - () => (0); - ($_: ident $($rest: ident)*) => { - 1 + $crate::__count!($($rest)*) - }; -} - -/// Internal macro to declare a getter/setter name. -#[macro_export] -macro_rules! __get_set_decl { - ( - $class: ident, - $field_name: ident, - $( $js_field_name: literal )?, - @get( $( $get_arg: ident: $get_arg_type: ty ),* ) -> $get_ty: ty - $get_body: block, - ) => { - let function = |$( $get_arg: $get_arg_type ),*| -> $get_ty { $get_body }; - let function_get = - $crate::IntoJsFunctionCopied::into_js_function_copied(function, $class.context()) - .to_js_function($class.context().realm()); - - let field_name = $crate::__js_class_name!($field_name, $($js_field_name)?); - $class.accessor( - $crate::boa_engine::JsString::from(field_name), - Some(function_get), - None, - $crate::boa_engine::property::Attribute::CONFIGURABLE - | $crate::boa_engine::property::Attribute::NON_ENUMERABLE, - ); - }; - ( - $class: ident, - $field_name: ident, - $( $js_field_name: literal )?, - @set( $( $set_arg: ident: $set_arg_type: ty ),* ) - $( -> $field_prop_set_ty: ty )? - $set_body: block - ) => { - let function = |$( $set_arg: $set_arg_type ),*| $(-> $field_prop_set_ty)? { $set_body }; - let function_set = - $crate::IntoJsFunctionCopied::into_js_function_copied(function, $class.context()) - .to_js_function($class.context().realm()); - - let field_name = $crate::__js_class_name!($field_name, $($js_field_name)?); - $class.accessor( - $crate::boa_engine::JsString::from(field_name), - None, - Some(function_set), - $crate::boa_engine::property::Attribute::CONFIGURABLE - | $crate::boa_engine::property::Attribute::NON_ENUMERABLE, - ); - }; - ( - $class: ident, - $field_name: ident, - $( $js_field_name: literal )?, - @get( $( $get_arg: ident: $get_arg_type: ty ),* ) -> $get_ty: ty - $get_body: block, - @set( $( $set_arg: ident: $set_arg_type: ty ),* ) - $( -> $field_prop_set_ty: ty )? - $set_body: block - ) => { - let function_get = - $crate::IntoJsFunctionCopied::into_js_function_copied( - |$( $get_arg: $get_arg_type ),*| -> $get_ty { $get_body }, - $class.context() - ).to_js_function($class.context().realm()); - let function_set = - $crate::IntoJsFunctionCopied::into_js_function_copied( - |$( $set_arg: $set_arg_type ),*| $(-> $field_prop_set_ty)? { $set_body }, - $class.context() - ).to_js_function($class.context().realm()); - - let field_name = $crate::__js_class_name!($field_name, $($js_field_name)?); - $class.accessor( - $crate::boa_engine::JsString::from(field_name), - Some(function_get), - Some(function_set), - $crate::boa_engine::property::Attribute::CONFIGURABLE - | $crate::boa_engine::property::Attribute::NON_ENUMERABLE, - ); - - }; - ( - $class: ident, - $field_name: ident, - $( $js_field_name: literal )?, - ) => { - compile_error!("Property must have at least a getter or a setter"); - }; -} - -// We allow too many lines. This test is straightforward but has a lot of boilerplate -// still. -#[test] -#[allow(clippy::too_many_lines)] -fn js_class_test() { - use crate::IntoJsFunctionCopied; - use crate::{js_class, loaders}; - use boa_engine::interop::JsClass; - use boa_engine::property::Attribute; - use boa_engine::{Context, JsData, JsError, JsResult, Module, Source, js_string}; - use boa_gc::{Finalize, Trace}; - use std::rc::Rc; - - #[derive(Debug, Clone, Default, Trace, Finalize, JsData)] - struct Test { - f1: u32, - f2: u32, - f3: u32, - } - - js_class! { - class Test { - public fp() -> u32 { - 10 - } - - property f1 { - get(this: JsClass) -> u32 { - this.borrow().f1 - } - } - - property f2 { - set(this: JsClass, new_value: u32) { - this.borrow_mut().f1 = new_value; - } - } - - property f3 { - get(this: JsClass) -> u32 { - this.borrow().f3 - } - - set(this: JsClass, new_value: u32) { - this.borrow_mut().f3 = new_value; - } - } - - property f4 { - set() -> JsResult<()> { - Err(JsError::from_opaque(boa_engine::JsString::from("Cannot set f4.").into())) - } - } - - // Just to test the branch with both get, set and return value. - property f5 { - get() -> u8 { 1 } - set() -> () { } - } - - constructor() { - Ok(Test::default()) - } - - init(class: &mut ClassBuilder) -> JsResult<()> { - let get_value_getter = (|this: JsClass| { - this.borrow().f2 - }) - .into_js_function_copied(class.context()) - .to_js_function(class.context().realm()); - - let set_value_setter = (|this: JsClass, new_value: u32| { - this.borrow_mut().f2 = new_value; - }) - .into_js_function_copied(class.context()) - .to_js_function(class.context().realm()); - - class.accessor( - js_string!("value2"), - Some(get_value_getter), - Some(set_value_setter), - Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, - ); - Ok(()) - } - } - } - - let loader = Rc::new(loaders::HashMapModuleLoader::new()); - let mut context = Context::builder() - .module_loader(loader.clone()) - .build() - .unwrap(); - - context.register_global_class::().unwrap(); - - let source = Source::from_bytes( - r" - function assert_eq(name, actual, expected) { - if (expected !== actual) { - throw `Assertion failed: ${name} - expected ${expected}, got ${actual}`; - } - } - - let t = new Test(); - assert_eq('fp', t.fp, 10); - - assert_eq('value2', t.value2, 0); - t.value2 = 123; - assert_eq('value2', t.value2, 123); - - // This should do nothing in Rust as this is a JS property and not a getter/setter. - t.fp = 123; - assert_eq('fp (set)', t.fp, 123); - - // Test separate getter/setter. - assert_eq('f1', t.f1, 0); - t.f2 = 1; - assert_eq('f2', t.f1, 1); - - // Test same getter/setter. - assert_eq('f3', t.f3, 0); - t.f3 = 456; - assert_eq('f3 (set)', t.f3, 456); - - // Test exception on setter. - try { - t.f4 = 123; - throw 'Expected an exception'; - } catch (e) { - if (e !== 'Cannot set f4.') { - throw e; - } - } - ", - ); - let root_module = Module::parse(source, None, &mut context).unwrap(); - - let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs().unwrap(); - - // Checking if the final promise didn't return an error. - assert!( - promise_result.state().as_fulfilled().is_some(), - "module didn't execute successfully! Promise: {:?} ({:?})", - promise_result.state(), - promise_result - .state() - .as_rejected() - .map(|r| r.to_json(&mut context)) - ); -} diff --git a/core/macros/src/embedded_module_loader.rs b/core/macros/src/embedded_module_loader.rs index 0ec925a52d2..8635be78472 100644 --- a/core/macros/src/embedded_module_loader.rs +++ b/core/macros/src/embedded_module_loader.rs @@ -141,7 +141,7 @@ fn find_all_files(dir: &mut fs::ReadDir, root: &PathBuf) -> Vec { } /// Implementation of the `embed_module_inner!` macro. -/// This should not be used directly. Use the `embed_module!` macro from the `boa_interop` +/// This should not be used directly. Use the `embed_module!` macro from the `boa_engine` /// crate instead. pub(crate) fn embed_module_impl(input: TokenStream) -> TokenStream { let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap_or_default()); diff --git a/core/macros/src/lib.rs b/core/macros/src/lib.rs index 5e4e4646bb4..9b6649fb485 100644 --- a/core/macros/src/lib.rs +++ b/core/macros/src/lib.rs @@ -43,7 +43,7 @@ pub fn js_object(input: TokenStream) -> TokenStream { /// /// # Warning /// This should not be used directly as is, and instead should be used through -/// the `embed_module!` macro in `boa_interop` for convenience. +/// the `embed_module!` macro in `boa_engine` for convenience. #[proc_macro] pub fn embed_module_inner(input: TokenStream) -> TokenStream { embedded_module_loader::embed_module_impl(input) @@ -88,7 +88,7 @@ pub fn embed_module_inner(input: TokenStream) -> TokenStream { /// /// # Warning /// This should not be used directly as is, and instead should be used through -/// the `embed_module!` macro in `boa_interop` for convenience. +/// the `embed_module!` macro in `boa_engine` for convenience. #[proc_macro_attribute] pub fn boa_class(attr: TokenStream, item: TokenStream) -> TokenStream { class::class_impl(attr, item) diff --git a/core/runtime/Cargo.toml b/core/runtime/Cargo.toml index db9a3f394d0..60c335001c6 100644 --- a/core/runtime/Cargo.toml +++ b/core/runtime/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true [dependencies] boa_engine.workspace = true boa_gc.workspace = true -boa_interop.workspace = true bytemuck.workspace = true either.workspace = true futures-lite = { workspace = true, optional = true } diff --git a/core/runtime/src/fetch/headers.rs b/core/runtime/src/fetch/headers.rs index ced465795c9..2d7aa9dbab1 100644 --- a/core/runtime/src/fetch/headers.rs +++ b/core/runtime/src/fetch/headers.rs @@ -3,13 +3,12 @@ //! See . #![allow(clippy::needless_pass_by_value)] +use boa_engine::interop::JsClass; use boa_engine::object::builtins::{JsArray, TypedJsFunction}; use boa_engine::value::{Convert, TryFromJs}; use boa_engine::{ - Context, Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, js_error, + Context, Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, boa_class, js_error, }; -use boa_interop::JsClass; -use boa_interop::boa_macros::boa_class; use http::header::HeaderMap as HttpHeaderMap; use http::{HeaderName, HeaderValue}; use std::cell::RefCell; diff --git a/core/runtime/src/fetch/mod.rs b/core/runtime/src/fetch/mod.rs index 6352dd6d55d..9d21d07eedd 100644 --- a/core/runtime/src/fetch/mod.rs +++ b/core/runtime/src/fetch/mod.rs @@ -14,9 +14,8 @@ use boa_engine::class::Class; use boa_engine::realm::Realm; use boa_engine::{ Context, Finalize, JsData, JsError, JsObject, JsResult, JsString, JsValue, NativeObject, Trace, - js_error, + boa_module, js_error, }; -use boa_interop::boa_macros::boa_module; use either::Either; use http::{HeaderName, HeaderValue, Request as HttpRequest, Request}; use std::cell::RefCell; diff --git a/core/runtime/src/fetch/request.rs b/core/runtime/src/fetch/request.rs index 8fd1d14a962..ca0d2f85ac4 100644 --- a/core/runtime/src/fetch/request.rs +++ b/core/runtime/src/fetch/request.rs @@ -5,8 +5,9 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request use super::HttpRequest; use boa_engine::value::{Convert, TryFromJs}; -use boa_engine::{Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, js_error}; -use boa_interop::boa_macros::boa_class; +use boa_engine::{ + Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, boa_class, js_error, +}; use either::Either; use std::collections::BTreeMap; use std::mem; diff --git a/core/runtime/src/fetch/response.rs b/core/runtime/src/fetch/response.rs index 6f97acc3c43..941e950280f 100644 --- a/core/runtime/src/fetch/response.rs +++ b/core/runtime/src/fetch/response.rs @@ -9,10 +9,10 @@ use crate::fetch::headers::JsHeaders; use boa_engine::object::builtins::{JsPromise, JsUint8Array}; use boa_engine::value::{TryFromJs, TryIntoJs}; use boa_engine::{ - Context, JsData, JsNativeError, JsResult, JsString, JsValue, js_error, js_str, js_string, + Context, JsData, JsNativeError, JsResult, JsString, JsValue, boa_class, js_error, js_str, + js_string, }; use boa_gc::{Finalize, Trace}; -use boa_interop::boa_macros::boa_class; use http::StatusCode; use std::rc::Rc; diff --git a/core/runtime/src/interval.rs b/core/runtime/src/interval.rs index 9eb91e40f97..5e7ad6f3eff 100644 --- a/core/runtime/src/interval.rs +++ b/core/runtime/src/interval.rs @@ -1,12 +1,14 @@ //! A module that declares any functions for dealing with intervals or //! timeouts. +use boa_engine::interop::JsRest; use boa_engine::job::{NativeJob, TimeoutJob}; use boa_engine::object::builtins::JsFunction; use boa_engine::value::{IntegerOrInfinity, Nullable}; -use boa_engine::{Context, Finalize, JsData, JsResult, JsValue, Trace, js_error, js_string}; +use boa_engine::{ + Context, Finalize, IntoJsFunctionCopied, JsData, JsResult, JsValue, Trace, js_error, js_string, +}; use boa_gc::{Gc, GcRefCell}; -use boa_interop::{IntoJsFunctionCopied, JsRest}; use std::collections::HashSet; #[cfg(test)] diff --git a/core/runtime/src/microtask/mod.rs b/core/runtime/src/microtask/mod.rs index 4ed56c42f25..a9156a1283c 100644 --- a/core/runtime/src/microtask/mod.rs +++ b/core/runtime/src/microtask/mod.rs @@ -1,7 +1,6 @@ //! Microtask-related functions and types. use boa_engine::realm::Realm; -use boa_engine::{Context, JsResult}; -use boa_interop::boa_macros::boa_module; +use boa_engine::{Context, JsResult, boa_module}; #[cfg(test)] mod tests; diff --git a/core/runtime/src/text/mod.rs b/core/runtime/src/text/mod.rs index f1b538ad45d..e7aea823ac6 100644 --- a/core/runtime/src/text/mod.rs +++ b/core/runtime/src/text/mod.rs @@ -6,9 +6,9 @@ use boa_engine::object::builtins::{JsArrayBuffer, JsTypedArray, JsUint8Array}; use boa_engine::realm::Realm; use boa_engine::value::TryFromJs; use boa_engine::{ - Context, Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, js_error, js_string, + Context, Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, boa_class, boa_module, + js_error, js_string, }; -use boa_interop::boa_macros::{boa_class, boa_module}; #[cfg(test)] mod tests; diff --git a/core/runtime/src/url.rs b/core/runtime/src/url.rs index de0c8e15467..00f121050f7 100644 --- a/core/runtime/src/url.rs +++ b/core/runtime/src/url.rs @@ -17,8 +17,9 @@ mod tests; use boa_engine::class::{Class, ClassBuilder}; use boa_engine::realm::Realm; use boa_engine::value::Convert; -use boa_engine::{Context, Finalize, JsData, JsResult, JsString, JsValue, Trace, js_error}; -use boa_interop::boa_macros::boa_class; +use boa_engine::{ + Context, Finalize, JsData, JsResult, JsString, JsValue, Trace, boa_class, js_error, +}; use std::fmt::Display; /// The `URL` class represents a (properly parsed) Uniform Resource Locator. @@ -33,16 +34,7 @@ impl Url { /// # Errors /// This will error if the context or realm cannot register the class. pub fn register(realm: Option, context: &mut Context) -> JsResult<()> { - if let Some(realm) = realm { - let mut class_builder = ClassBuilder::new::(context); - Url::init(&mut class_builder)?; - let class = class_builder.build(); - realm.register_class::(class); - } else { - context.register_global_class::()?; - } - - Ok(()) + Url::register(realm, context) } } diff --git a/tests/macros/Cargo.toml b/tests/macros/Cargo.toml index 3826548ad56..61f49b21caa 100644 --- a/tests/macros/Cargo.toml +++ b/tests/macros/Cargo.toml @@ -13,6 +13,7 @@ rust-version.workspace = true [dev-dependencies] trybuild.workspace = true boa_engine.workspace = true +boa_gc.workspace = true [lints] workspace = true diff --git a/core/interop/tests/assets/fibonacci.js b/tests/macros/tests/assets/fibonacci.js similarity index 100% rename from core/interop/tests/assets/fibonacci.js rename to tests/macros/tests/assets/fibonacci.js diff --git a/core/interop/tests/assets/gcd_callback.js b/tests/macros/tests/assets/gcd_callback.js similarity index 100% rename from core/interop/tests/assets/gcd_callback.js rename to tests/macros/tests/assets/gcd_callback.js diff --git a/core/interop/tests/class.rs b/tests/macros/tests/class.rs similarity index 95% rename from core/interop/tests/class.rs rename to tests/macros/tests/class.rs index 6991abcb8ca..efefdf95af4 100644 --- a/core/interop/tests/class.rs +++ b/tests/macros/tests/class.rs @@ -1,8 +1,9 @@ //! Test for the class proc-macro. #![allow(unused_crate_dependencies)] -use boa_engine::{Context, JsObject, JsString, Source, js_string}; -use boa_macros::{Finalize, JsData, Trace, boa_class}; +use boa_engine::{ + Context, Finalize, JsData, JsObject, JsString, Source, Trace, boa_class, js_string, +}; #[derive(Clone, Trace, Finalize, JsData)] enum AnimalType { diff --git a/core/interop/tests/embedded.rs b/tests/macros/tests/embedded.rs similarity index 96% rename from core/interop/tests/embedded.rs rename to tests/macros/tests/embedded.rs index 6caef79c2fe..340aed0c864 100644 --- a/core/interop/tests/embedded.rs +++ b/tests/macros/tests/embedded.rs @@ -5,9 +5,9 @@ use std::rc::Rc; use boa_engine::builtins::promise::PromiseState; +use boa_engine::embed_module; +use boa_engine::module::embedded::EmbeddedModuleLoader; use boa_engine::{Context, JsString, JsValue, Module, Source, js_string}; -use boa_interop::embed_module; -use boa_interop::loaders::embedded::EmbeddedModuleLoader; fn load_module_and_test(module_loader: &Rc) { let mut context = Context::builder() diff --git a/core/interop/tests/embedded/dir1/file3.js b/tests/macros/tests/embedded/dir1/file3.js similarity index 100% rename from core/interop/tests/embedded/dir1/file3.js rename to tests/macros/tests/embedded/dir1/file3.js diff --git a/core/interop/tests/embedded/dir1/file4.js b/tests/macros/tests/embedded/dir1/file4.js similarity index 100% rename from core/interop/tests/embedded/dir1/file4.js rename to tests/macros/tests/embedded/dir1/file4.js diff --git a/core/interop/tests/embedded/file1.js b/tests/macros/tests/embedded/file1.js similarity index 100% rename from core/interop/tests/embedded/file1.js rename to tests/macros/tests/embedded/file1.js diff --git a/core/interop/tests/embedded/file2.js b/tests/macros/tests/embedded/file2.js similarity index 100% rename from core/interop/tests/embedded/file2.js rename to tests/macros/tests/embedded/file2.js diff --git a/core/interop/tests/fibonacci.rs b/tests/macros/tests/fibonacci.rs similarity index 95% rename from core/interop/tests/fibonacci.rs rename to tests/macros/tests/fibonacci.rs index 8dd39dafb6e..69d06c4621d 100644 --- a/core/interop/tests/fibonacci.rs +++ b/tests/macros/tests/fibonacci.rs @@ -4,8 +4,7 @@ // You can execute this example with `cargo run --example gcd` use boa_engine::object::builtins::{JsFunction, TypedJsFunction}; -use boa_engine::{Context, JsResult, Module, Source, js_error, js_string}; -use boa_interop::IntoJsFunctionCopied; +use boa_engine::{Context, IntoJsFunctionCopied, JsResult, Module, Source, js_error, js_string}; use std::path::PathBuf; #[allow(clippy::needless_pass_by_value)] diff --git a/core/interop/tests/gcd_callback.rs b/tests/macros/tests/gcd_callback.rs similarity index 93% rename from core/interop/tests/gcd_callback.rs rename to tests/macros/tests/gcd_callback.rs index e5fe6f2fa10..645413a8dd8 100644 --- a/core/interop/tests/gcd_callback.rs +++ b/tests/macros/tests/gcd_callback.rs @@ -1,10 +1,10 @@ #![allow(unused_crate_dependencies)] //! A test that mimics the `boa_engine`'s GCD test with a typed callback. +use boa_engine::interop::ContextData; use boa_engine::object::builtins::JsFunction; -use boa_engine::{Context, Module, Source, js_string}; +use boa_engine::{Context, IntoJsFunctionCopied, Module, Source, js_string}; use boa_gc::Gc; -use boa_interop::{ContextData, IntoJsFunctionCopied}; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/core/interop/tests/module.rs b/tests/macros/tests/module.rs similarity index 89% rename from core/interop/tests/module.rs rename to tests/macros/tests/module.rs index 95abecda7f4..84d471e494b 100644 --- a/core/interop/tests/module.rs +++ b/tests/macros/tests/module.rs @@ -1,8 +1,10 @@ //! Test for the class proc-macro. #![allow(unused_crate_dependencies)] -use boa_engine::{Context, JsString, Module, Source, js_string}; -use boa_macros::{Finalize, JsData, Trace, boa_class, boa_module}; +use boa_engine::module::MapModuleLoader; +use boa_engine::{ + Context, Finalize, JsData, JsString, Module, Source, Trace, boa_class, boa_module, js_string, +}; use std::rc::Rc; #[derive(Clone, Trace, Finalize, JsData)] @@ -72,13 +74,13 @@ const ASSERT_DECL: &str = r" #[test] fn boa_module() { - let module_loader = Rc::new(boa_interop::loaders::HashMapModuleLoader::new()); + let module_loader = Rc::new(MapModuleLoader::new()); let mut context = Context::builder() .module_loader(module_loader.clone()) .build() .expect("Could not create context."); - module_loader.register("/hello.js", hello::boa_module(None, &mut context)); + module_loader.insert("/hello.js", hello::boa_module(None, &mut context)); context .eval(Source::from_bytes(ASSERT_DECL)) diff --git a/tests/wpt/Cargo.toml b/tests/wpt/Cargo.toml index aeb9ed1445a..4fbf07b47c7 100644 --- a/tests/wpt/Cargo.toml +++ b/tests/wpt/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] boa_engine = { path = "../../core/engine" } boa_gc = { path = "../../core/gc" } -boa_interop = { path = "../../core/interop" } boa_runtime = { path = "../../core/runtime", features = ["all"] } rstest = "0.25.0" url = { version = "2.5.4", features = [] } diff --git a/tests/wpt/src/lib.rs b/tests/wpt/src/lib.rs index 75a3243de00..956cbae045b 100644 --- a/tests/wpt/src/lib.rs +++ b/tests/wpt/src/lib.rs @@ -2,14 +2,14 @@ #![allow(unused_crate_dependencies)] use boa_engine::class::Class; +use boa_engine::interop::ContextData; use boa_engine::parser::source::UTF16Input; use boa_engine::property::Attribute; use boa_engine::value::{Nullable, TryFromJs}; use boa_engine::{ - js_error, js_str, js_string, Context, Finalize, JsData, JsResult, JsString, JsValue, Source, - Trace, + js_error, js_str, js_string, Context, Finalize, IntoJsFunctionCopied, JsData, JsResult, + JsString, JsValue, Source, Trace, }; -use boa_interop::{ContextData, IntoJsFunctionCopied}; use boa_runtime::url::Url; use boa_runtime::{DefaultLogger, NullLogger}; use logger::RecordingLogEvent; From 02a8110f3fec8c024faf411664da23c3e3a82cc9 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 4 Sep 2025 10:21:04 -0700 Subject: [PATCH 2/5] Fix merge conflicts --- core/runtime/src/clone/mod.rs | 3 +-- core/runtime/src/url.rs | 12 +++++++++--- core/string/src/common.rs | 1 + tests/macros/tests/embedded.rs | 1 - 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/runtime/src/clone/mod.rs b/core/runtime/src/clone/mod.rs index 128975e7dbe..e34551313fe 100644 --- a/core/runtime/src/clone/mod.rs +++ b/core/runtime/src/clone/mod.rs @@ -5,8 +5,7 @@ use boa_engine::realm::Realm; use boa_engine::value::TryFromJs; -use boa_engine::{Context, JsObject, JsResult}; -use boa_interop::boa_macros::boa_module; +use boa_engine::{Context, JsObject, JsResult, boa_module}; /// Options used by `structuredClone`. This is currently unused. #[derive(Debug, Clone, TryFromJs)] diff --git a/core/runtime/src/url.rs b/core/runtime/src/url.rs index 00f121050f7..e34b555987e 100644 --- a/core/runtime/src/url.rs +++ b/core/runtime/src/url.rs @@ -14,11 +14,11 @@ #[cfg(test)] mod tests; -use boa_engine::class::{Class, ClassBuilder}; +use boa_engine::class::Class; use boa_engine::realm::Realm; use boa_engine::value::Convert; use boa_engine::{ - Context, Finalize, JsData, JsResult, JsString, JsValue, Trace, boa_class, js_error, + Context, Finalize, JsData, JsResult, JsString, JsValue, Trace, boa_class, boa_module, js_error, }; use std::fmt::Display; @@ -34,7 +34,7 @@ impl Url { /// # Errors /// This will error if the context or realm cannot register the class. pub fn register(realm: Option, context: &mut Context) -> JsResult<()> { - Url::register(realm, context) + js_module::boa_register(realm, context) } } @@ -239,3 +239,9 @@ impl Url { Err(js_error!(Error: "URL.revokeObjectURL is not implemented")) } } + +/// JavaScript module containing the Url class. +#[boa_module] +pub mod js_module { + type Url = super::Url; +} diff --git a/core/string/src/common.rs b/core/string/src/common.rs index 967a0da8a89..da110526414 100644 --- a/core/string/src/common.rs +++ b/core/string/src/common.rs @@ -167,6 +167,7 @@ impl StaticJsStrings { (UINT32_ARRAY, "Uint32Array"), (BIG_INT64_ARRAY, "BigInt64Array"), (BIG_UINT64_ARRAY, "BigUint64Array"), + #[cfg(feature = "float16")] (FLOAT16_ARRAY, "Float16Array"), (FLOAT32_ARRAY, "Float32Array"), (FLOAT64_ARRAY, "Float64Array"), diff --git a/tests/macros/tests/embedded.rs b/tests/macros/tests/embedded.rs index 340aed0c864..3fdd447ce72 100644 --- a/tests/macros/tests/embedded.rs +++ b/tests/macros/tests/embedded.rs @@ -71,7 +71,6 @@ fn simple() { load_module_and_test(&module_loader); } -#[cfg(feature = "embedded_lz4")] #[test] fn compressed_lz4() { #[cfg(target_family = "unix")] From 34558ea145202317c83d81b74f1884a9f766057f Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 4 Sep 2025 10:26:20 -0700 Subject: [PATCH 3/5] Add feature needed for tests --- tests/macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/macros/Cargo.toml b/tests/macros/Cargo.toml index 61f49b21caa..6f20d1eb9b8 100644 --- a/tests/macros/Cargo.toml +++ b/tests/macros/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true [dev-dependencies] trybuild.workspace = true -boa_engine.workspace = true +boa_engine = { workspace = true, features = ["embedded_lz4"] } boa_gc.workspace = true [lints] From 580c7911cbf4a01b0ba03e6efbad254a5f352405 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Thu, 4 Sep 2025 10:27:19 -0700 Subject: [PATCH 4/5] Make sure the base test uses no compression --- tests/macros/tests/embedded.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/macros/tests/embedded.rs b/tests/macros/tests/embedded.rs index 3fdd447ce72..1f27bb73022 100644 --- a/tests/macros/tests/embedded.rs +++ b/tests/macros/tests/embedded.rs @@ -64,7 +64,7 @@ fn load_module_and_test(module_loader: &Rc) { #[test] fn simple() { #[cfg(target_family = "unix")] - let module_loader = Rc::new(embed_module!("tests/embedded/")); + let module_loader = Rc::new(embed_module!("tests/embedded/", compress = "none")); #[cfg(target_family = "windows")] let module_loader = Rc::new(embed_module!("tests\\embedded\\")); From 5385a21f783c43e74352fcd18dc85715d9b03cb4 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Fri, 5 Sep 2025 11:28:03 -0700 Subject: [PATCH 5/5] Re-add deprecated APIs --- Cargo.lock | 33 +++++++++++++++++++++++++++++---- core/interop/Cargo.toml | 28 ++++++++++++++++++++++++++++ core/interop/src/lib.rs | 14 ++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 core/interop/Cargo.toml create mode 100644 core/interop/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 75ebeb0a04c..11d8aa1ba19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,7 +366,7 @@ dependencies = [ "float16", "futures-lite 2.6.1", "getrandom 0.3.3", - "hashbrown 0.15.5", + "hashbrown 0.16.0", "iana-time-zone", "icu_calendar", "icu_casemap", @@ -441,7 +441,7 @@ dependencies = [ "boa_macros", "boa_string", "either", - "hashbrown 0.15.5", + "hashbrown 0.16.0", "icu_locale_core", "thin-vec", ] @@ -473,7 +473,7 @@ dependencies = [ "arbitrary", "boa_gc", "boa_macros", - "hashbrown 0.15.5", + "hashbrown 0.16.0", "indexmap", "once_cell", "phf", @@ -482,6 +482,14 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "boa_interop" +version = "0.20.0" +dependencies = [ + "boa_engine", + "boa_macros", +] + [[package]] name = "boa_macros" version = "0.20.0" @@ -1427,6 +1435,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1688,7 +1702,18 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] diff --git a/core/interop/Cargo.toml b/core/interop/Cargo.toml new file mode 100644 index 00000000000..b7db6c1df3a --- /dev/null +++ b/core/interop/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "boa_interop" +description = "Interop utilities for integrating boa with a Rust host." +keywords = ["javascript", "js", "interop"] +categories = ["api-bindings"] +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +publish = false + +[badges] +maintenance = { status = "deprecated" } + +[dependencies] +boa_engine.workspace = true +boa_macros.workspace = true + +[lints] +workspace = true + +[package.metadata.docs.rs] +all-features = true + +[features] +embedded_lz4 = ["boa_engine/embedded_lz4"] diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs new file mode 100644 index 00000000000..a279a3b5879 --- /dev/null +++ b/core/interop/src/lib.rs @@ -0,0 +1,14 @@ +//! Interop utilities between Boa and its host. +#![deprecated(note = "All interop APIs were moved to boa_engine")] + +pub use boa_engine; +pub use boa_macros; + +// Re-export in case some people depend on boa_interop. +#[deprecated(note = "Please use these exports from boa_engine::interop instead.")] +pub use boa_engine::interop::{ContextData, Ignore, JsClass, JsRest}; + +#[deprecated(note = "Please use these exports from boa_engine instead.")] +pub use boa_engine::{ + IntoJsFunctionCopied, IntoJsModule, UnsafeIntoJsFunction, boa_class, boa_module, +};