diff --git a/crates/moonbit/src/async/ev.mbt b/crates/moonbit/src/async/ev.mbt index ea41df22e..47424487c 100644 --- a/crates/moonbit/src/async/ev.mbt +++ b/crates/moonbit/src/async/ev.mbt @@ -17,7 +17,7 @@ /// source; `owned_coroutines` contains only runtime bridge coroutines. priv struct ComponentTaskState { lane : TaskLane - subscribers : Map[Int, Subscriber] + subscribers : @sorted_map.SortedMap[Int, Subscriber] task : Coroutine owned_coroutines : @set.Set[Coroutine] mut resolution : TaskResolution @@ -52,7 +52,7 @@ fn current_waitableset() -> WaitableSet { ///| fn component_task_state(waitable_set : WaitableSet) -> ComponentTaskState? { - component_tasks.get(waitable_set) + component_tasks.get(waitable_set.0) } ///| @@ -66,7 +66,7 @@ fn finish_waitableset( } drop_task_wakeup(task_state) guard task_state.subscribers.is_empty() else { panic() } - component_tasks.remove(waitable_set) + component_tasks.remove(waitable_set.0) waitable_set.drop() tls_set(0) } @@ -154,13 +154,13 @@ pub fn with_waitableset(f : async () -> Unit) -> Int { let task = spawn_owned(f, lane, None) let task_state = { lane, - subscribers: Map([]), + subscribers: SortedMap([]), task, owned_coroutines: Set([]), resolution: Pending, cancellation_pending: false, } - component_tasks.set(waitable_set, task_state) + component_tasks.set(waitable_set.0, task_state) reschedule(lane) next_callback(waitable_set, task_state) } @@ -215,7 +215,9 @@ pub fn cb(event : Int, waitable_id : Int, code : Int) -> Int { } ///| -let component_tasks : Map[WaitableSet, ComponentTaskState] = Map([]) +// Host handles need not be dense. An ordered map bounds lookup cost without +// hashing or allocating storage up to the largest handle. +let component_tasks : @sorted_map.SortedMap[Int, ComponentTaskState] = SortedMap([]) ///| /// Spawn a coroutine owned by the current component-model async task. diff --git a/crates/moonbit/src/async/moon.pkg b/crates/moonbit/src/async/moon.pkg index 5443921be..23919fa48 100644 --- a/crates/moonbit/src/async/moon.pkg +++ b/crates/moonbit/src/async/moon.pkg @@ -2,6 +2,7 @@ import { "moonbitlang/core/deque" @deque, "moonbitlang/core/ref" @ref, "moonbitlang/core/set" @set, + "moonbitlang/core/sorted_map" @sorted_map, } warnings = "-44" diff --git a/crates/moonbit/src/async_support.rs b/crates/moonbit/src/async_support.rs index 11a1bc572..3772e8935 100644 --- a/crates/moonbit/src/async_support.rs +++ b/crates/moonbit/src/async_support.rs @@ -1256,6 +1256,11 @@ impl<'a> InterfaceGenerator<'a> { .world_gen .pkg_resolver .qualify_package(&helper_package, ASYNC_CORE_DIR); + let sorted_map = endpoint_use.lower.then(|| { + self.world_gen + .pkg_resolver + .qualify_external_package(&helper_package, "moonbitlang/core/sorted_map") + }); let elem_size = result_type .map(|ty| self.world_gen.sizes.size(ty).size_wasm32()) @@ -1754,7 +1759,7 @@ fn wasm{symbol_name}FutureLowerCommitted(future : {ffi}Future[{result}]) -> Int ) }) .unwrap_or_default(); - let lower_bridge = if endpoint_use.lower { + let lower_bridge = if let Some(sorted_map) = &sorted_map { format!( r#" ///| @@ -1764,7 +1769,7 @@ priv struct Wasm{symbol_name}FutureProducer {{ }} ///| -let wasm{symbol_name}FutureProducers : Map[Int, Wasm{symbol_name}FutureProducer] = Map([]) +let wasm{symbol_name}FutureProducers : {sorted_map}SortedMap[Int, Wasm{symbol_name}FutureProducer] = {sorted_map}SortedMap([]) {reject_prepared_func} @@ -2094,7 +2099,7 @@ fn wasm{symbol_name}StreamLowerCommitted(stream : {ffi}Stream[{result}]) -> Int ) }) .unwrap_or_default(); - let lower_bridge = if endpoint_use.lower { + let lower_bridge = if let Some(sorted_map) = &sorted_map { format!( r#" ///| @@ -2104,7 +2109,7 @@ priv struct Wasm{symbol_name}StreamProducer {{ }} ///| -let wasm{symbol_name}StreamProducers : Map[Int, Wasm{symbol_name}StreamProducer] = Map([]) +let wasm{symbol_name}StreamProducers : {sorted_map}SortedMap[Int, Wasm{symbol_name}StreamProducer] = {sorted_map}SortedMap([]) {reject_prepared_func} diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 9c3142b89..d1b56e483 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -163,6 +163,12 @@ impl MoonBit { .packages .iter() .map(|(k, v)| format!("\"{}/{}\" @{},", self.project_name, k.replace(".", "/"), v)) + .chain( + imports + .external_packages + .iter() + .map(|(path, alias)| format!("\"{path}\" @{alias},")), + ) .collect::>(); deps.sort(); uwriteln!(moon_pkg, "{}", deps.join("\n")); @@ -3458,6 +3464,49 @@ mod tests { ); } + #[test] + fn producer_tables_import_ordered_maps_without_changing_wit_maps() { + let files = generate( + r#" + package a:b; + + interface outgoing { + send: func(ready: future, values: stream, labels: map); + } + + interface incoming { + receive: func() -> tuple, stream>; + } + + world runner { + import outgoing; + import incoming; + export incoming; + } + "#, + "runner", + ); + + for package in ["interface/a/b/outgoing", "gen/interface/a/b/incoming"] { + let manifest = file(&files, &format!("{package}/moon.pkg")); + assert!( + manifest.contains("\"moonbitlang/core/sorted_map\" @sorted_map,"), + "{manifest}" + ); + let ffi = file(&files, &format!("{package}/ffi.mbt")); + for kind in ["Future", "Stream"] { + assert!( + ffi.contains(&format!("{kind}Producers : @sorted_map.SortedMap[Int,")), + "{ffi}" + ); + } + } + let incoming = file(&files, "interface/a/b/incoming/moon.pkg"); + assert!(!incoming.contains("sorted_map"), "{incoming}"); + let outgoing = file(&files, "interface/a/b/outgoing/top.mbt"); + assert!(outgoing.contains("Map[String, UInt]"), "{outgoing}"); + } + #[test] fn fixed_primitive_streams_use_four_kibibyte_buffer_budget() { for (wit_type, expected_window) in [ diff --git a/crates/moonbit/src/pkg.rs b/crates/moonbit/src/pkg.rs index 2f01db0e8..d387d4bfb 100644 --- a/crates/moonbit/src/pkg.rs +++ b/crates/moonbit/src/pkg.rs @@ -14,6 +14,7 @@ pub(crate) const ASYNC_CORE_DIR: &str = "async-core"; #[derive(Default)] pub(crate) struct Imports { pub packages: HashMap, + pub external_packages: HashMap, ns: Ns, } @@ -33,6 +34,15 @@ pub(crate) struct PkgResolver { } impl PkgResolver { + pub(crate) fn qualify_external_package(&mut self, this: &str, path: &str) -> String { + let imports = self.package_import.entry(this.to_string()).or_default(); + let alias = imports + .external_packages + .entry(path.to_string()) + .or_insert_with(|| imports.ns.tmp(path.rsplit('/').next().unwrap())); + format!("@{alias}.") + } + pub(crate) fn qualify_package(&mut self, this: &str, name: &str) -> String { if name != this { let imports = self.package_import.entry(this.to_string()).or_default(); @@ -388,6 +398,22 @@ impl PkgResolver { mod tests { use super::*; + #[test] + fn external_imports_share_the_generated_package_alias_namespace() { + let mut packages = PkgResolver::default(); + let generated = packages.qualify_package("gen", "interface.a.b.sorted_map"); + let external = packages.qualify_external_package("gen", "moonbitlang/core/sorted_map"); + assert_ne!(generated, external); + assert_eq!( + packages.qualify_external_package("gen", "moonbitlang/core/sorted_map"), + external + ); + assert_eq!( + packages.qualify_package("gen", "interface.a.b.sorted_map"), + generated + ); + } + #[test] fn package_names_and_aliases_preserve_kebab_case() { let mut resolve = Resolve::default();