1use crate::runtime::host::{abi_arg, abi_result};
10use wasmtime::{FuncType, HeapType, Linker, RefType, ValType};
11
12use crate::runtime::StoreData;
13use crate::runtime::host::{register_host_fn, register_host_fn_async};
14use crate::runtime::intrinsic_types::build_intrinsic_types;
15use crate::runtime::prelude::{MODULE_NAME, closure, declare_method};
16use crate::{MangledName, PackageDeclaration, Param, Type};
17
18fn method_key(method: &str) -> MangledName {
20 crate::mangle::extend(&crate::mangle::prelude("Map"), method)
21}
22
23fn ctor_key(method: &str) -> MangledName {
25 crate::mangle::extend(&crate::mangle::prelude("MapConstructor"), method)
26}
27
28pub fn install(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
29 let engine = linker.engine().clone();
30 let intr = build_intrinsic_types(&engine)?;
31 let obj = ValType::Ref(RefType::new(
32 true,
33 HeapType::ConcreteStruct(intr.object.clone()),
34 ));
35 let boolean = ValType::I32;
36 let ft = |params: Vec<ValType>, results: Vec<ValType>| FuncType::new(&engine, params, results);
37
38 register_host_fn_async(
39 linker,
40 MODULE_NAME,
41 method_key("get"),
42 ft(vec![obj.clone(), obj.clone()], vec![obj.clone()]),
43 true,
44 |caller, params, results| {
45 Box::pin(async move {
46 *abi_result(results, 0)? =
47 super::get(caller, abi_arg(params, 0)?, abi_arg(params, 1)?).await?;
48 Ok(())
49 })
50 },
51 )?;
52 register_host_fn_async(
53 linker,
54 MODULE_NAME,
55 method_key("set"),
56 ft(
57 vec![obj.clone(), obj.clone(), obj.clone()],
58 vec![obj.clone()],
59 ),
60 true,
61 |caller, params, results| {
62 Box::pin(async move {
63 *abi_result(results, 0)? = super::set(
64 caller,
65 abi_arg(params, 0)?,
66 abi_arg(params, 1)?,
67 abi_arg(params, 2)?,
68 )
69 .await?;
70 Ok(())
71 })
72 },
73 )?;
74 register_host_fn_async(
75 linker,
76 MODULE_NAME,
77 method_key("has"),
78 ft(vec![obj.clone(), obj.clone()], vec![boolean.clone()]),
79 true,
80 |caller, params, results| {
81 Box::pin(async move {
82 let r = super::has(caller, abi_arg(params, 0)?, abi_arg(params, 1)?).await?;
83 *abi_result(results, 0)? = wasmtime::Val::I32(i32::from(r));
84 Ok(())
85 })
86 },
87 )?;
88 register_host_fn_async(
89 linker,
90 MODULE_NAME,
91 method_key("delete"),
92 ft(vec![obj.clone(), obj.clone()], vec![boolean.clone()]),
93 true,
94 |caller, params, results| {
95 Box::pin(async move {
96 let r = super::delete(caller, abi_arg(params, 0)?, abi_arg(params, 1)?).await?;
97 *abi_result(results, 0)? = wasmtime::Val::I32(i32::from(r));
98 Ok(())
99 })
100 },
101 )?;
102 register_host_fn(
103 linker,
104 MODULE_NAME,
105 method_key("clear"),
106 ft(vec![obj.clone()], vec![]),
107 true,
108 |caller, params, _results| {
109 super::clear(caller, abi_arg(params, 0)?)?;
110 Ok(())
111 },
112 )?;
113 register_host_fn(
114 linker,
115 MODULE_NAME,
116 method_key("size"),
117 ft(vec![obj.clone()], vec![ValType::F64]),
118 true,
119 |caller, params, results| {
120 *abi_result(results, 0)? = super::size(caller, abi_arg(params, 0)?)?;
121 Ok(())
122 },
123 )?;
124 register_host_fn_async(
125 linker,
126 MODULE_NAME,
127 method_key("forEach"),
128 ft(vec![obj.clone(), obj.clone()], vec![]),
130 true,
131 |caller, params, _results| {
132 Box::pin(async move {
133 let f =
134 closure::read_callback(caller, abi_arg(params, 1)?, "Map#forEach callback")?;
135 super::for_each(caller, abi_arg(params, 0)?, &f).await
136 })
137 },
138 )?;
139 let iter_ret = obj.clone();
140 register_host_fn(
141 linker,
142 MODULE_NAME,
143 method_key("keys"),
144 ft(vec![obj.clone()], vec![iter_ret.clone()]),
145 true,
146 |caller, params, results| {
147 *abi_result(results, 0)? = super::keys(caller, abi_arg(params, 0)?)?;
148 Ok(())
149 },
150 )?;
151 register_host_fn(
152 linker,
153 MODULE_NAME,
154 method_key("values"),
155 ft(vec![obj.clone()], vec![iter_ret.clone()]),
156 true,
157 |caller, params, results| {
158 *abi_result(results, 0)? = super::values(caller, abi_arg(params, 0)?)?;
159 Ok(())
160 },
161 )?;
162 for name in ["entries", "iterator"] {
163 register_host_fn(
164 linker,
165 MODULE_NAME,
166 method_key(name),
167 ft(vec![obj.clone()], vec![iter_ret.clone()]),
168 true,
169 |caller, params, results| {
170 *abi_result(results, 0)? = super::entries(caller, abi_arg(params, 0)?)?;
171 Ok(())
172 },
173 )?;
174 }
175 register_host_fn_async(
178 linker,
179 MODULE_NAME,
180 ctor_key("new"),
181 ft(vec![obj.clone()], vec![obj.clone()]),
182 true,
183 |caller, params, results| {
184 Box::pin(async move {
185 *abi_result(results, 0)? = super::construct(caller, abi_arg(params, 0)?).await?;
186 Ok(())
187 })
188 },
189 )?;
190 Ok(())
191}
192
193pub fn declare(defs: &mut PackageDeclaration) {
194 let k = || Type::TypeVar("K".to_string());
195 let v = || Type::TypeVar("V".to_string());
196 let map_ty = || Type::prelude_interface("Map".to_string(), vec![k(), v()]);
197 let entry = || Type::Tuple(vec![k(), v()].into());
198 let iter = |t: Type| Type::prelude_interface("Iterator".to_string(), vec![t]);
199 let map = || Param::new("map", map_ty());
200 let key = || Param::new("key", k());
201 let m = |defs: &mut PackageDeclaration, name: &str, params: Vec<Param>, ret: Type| {
202 declare_method(defs, name, method_key(name), params, ret);
203 };
204
205 m(defs, "size", vec![map()], Type::Number);
206 m(
207 defs,
208 "get",
209 vec![map(), key()],
210 Type::Union(vec![v(), Type::Undefined]),
211 );
212 m(
213 defs,
214 "set",
215 vec![map(), key(), Param::new("value", v())],
216 map_ty(),
217 );
218 m(defs, "has", vec![map(), key()], Type::Boolean);
219 m(defs, "delete", vec![map(), key()], Type::Boolean);
220 m(defs, "clear", vec![map()], Type::Void);
221
222 let callback = Type::Unknown;
225 m(
226 defs,
227 "forEach",
228 vec![map(), Param::new("callback", callback)],
229 Type::Void,
230 );
231 m(defs, "keys", vec![map()], iter(k()));
232 m(defs, "values", vec![map()], iter(v()));
233 m(defs, "entries", vec![map()], iter(entry()));
234 m(defs, "iterator", vec![map()], iter(entry()));
235
236 declare_method(
239 defs,
240 "new",
241 ctor_key("new"),
242 vec![Param::new(
243 "entries",
244 Type::union(vec![
245 Type::Readonly(Box::new(Type::Array(Box::new(entry())))),
246 Type::prelude_interface("Iterable".to_string(), vec![entry()]),
247 Type::prelude_interface("Iterator".to_string(), vec![entry()]),
248 Type::Null,
249 Type::Undefined,
250 ]),
251 )],
252 map_ty(),
253 );
254}
255
256#[allow(clippy::too_many_lines)]
259pub(crate) fn declare_types(defs: &mut crate::PackageDeclaration) {
260 use crate::runtime::prelude::declaration::doc;
261 use crate::{
262 Dispatch, MethodSig, Param, PropertySig, Span, Type, TypeKind, TypeSymbol, ValueKind,
263 ValueSymbol,
264 };
265 use std::collections::BTreeMap;
266 defs.types.insert(
267 "Map".to_string(),
268 TypeSymbol {
269 name: "Map".to_string(),
270 mangled_name: crate::mangle::prelude("Map"),
271 declaration_span: Span::at(crate::FileId::PRELUDE),
272 kind: TypeKind::Interface { index: None,
273 generics: vec!["K".to_string(), "V".to_string()],
274 methods: BTreeMap::from([
275 (
276 "get".to_string(),
277 MethodSig {
278 optional: false,
279 generics: Vec::new(),
280 params: vec![Param::new("key", Type::TypeVar("K".to_string()))],
281 ret: Type::Union(vec![
282 Type::TypeVar("V".to_string()),
283 Type::Undefined,
284 ]),
285 predicate: None,
286 doc: doc(
287 "/**\n * Returns the value associated with `key`, or `undefined` if the key is not present.\n * @param key The key to look up.\n */",
288 ),
289 },
290 ),
291 (
292 "set".to_string(),
293 MethodSig {
294 optional: false,
295 generics: Vec::new(),
296 params: vec![
297 Param::new("key", Type::TypeVar("K".to_string())),
298 Param::new("value", Type::TypeVar("V".to_string())),
299 ],
300 ret: Type::prelude_interface("Map".to_string(), vec![
301 Type::TypeVar("K".to_string()),
302 Type::TypeVar("V".to_string()),
303 ]),
304 predicate: None,
305 doc: doc(
306 "/**\n * Associates `value` with `key`, overwriting any prior value.\n * @returns The same map (for chaining).\n */",
307 ),
308 },
309 ),
310 (
311 "has".to_string(),
312 MethodSig {
313 optional: false,
314 generics: Vec::new(),
315 params: vec![Param::new("key", Type::TypeVar("K".to_string()))],
316 ret: Type::Boolean,
317 predicate: None,
318 doc: doc(
319 "/**\n * Returns `true` when `key` is present.\n * @param key The key to test for.\n */",
320 ),
321 },
322 ),
323 (
324 "delete".to_string(),
325 MethodSig {
326 optional: false,
327 generics: Vec::new(),
328 params: vec![Param::new("key", Type::TypeVar("K".to_string()))],
329 ret: Type::Boolean,
330 predicate: None,
331 doc: doc(
332 "/**\n * Removes `key` from the map. Returns `true` when something was actually removed.\n * @param key The key to remove.\n */",
333 ),
334 },
335 ),
336 (
337 "keys".to_string(),
338 MethodSig {
339 optional: false,
340 generics: Vec::new(),
341 params: Vec::new(),
342 ret: Type::prelude_interface("Iterator".to_string(), vec![Type::TypeVar("K".to_string())]),
343 predicate: None,
344 doc: doc(
345 "/** Returns a lazy `Iterator<K>` over the keys in insertion order. It walks the map live: keys added later are visited, keys deleted first are skipped. */",
346 ),
347 },
348 ),
349 (
350 "values".to_string(),
351 MethodSig {
352 optional: false,
353 generics: Vec::new(),
354 params: Vec::new(),
355 ret: Type::prelude_interface("Iterator".to_string(), vec![Type::TypeVar("V".to_string())]),
356 predicate: None,
357 doc: doc(
358 "/** Returns a lazy `Iterator<V>` over the values in insertion order. It walks the map live: entries added later are visited, entries deleted first are skipped. */",
359 ),
360 },
361 ),
362 (
363 "entries".to_string(),
364 MethodSig {
365 optional: false,
366 generics: Vec::new(),
367 params: Vec::new(),
368 ret: Type::prelude_interface("Iterator".to_string(), vec![Type::Tuple(vec![
369 Type::TypeVar("K".to_string()),
370 Type::TypeVar("V".to_string()),
371 ].into())]),
372 predicate: None,
373 doc: doc(
374 "/** Returns a lazy `Iterator<[K, V]>` over the entries — the same cursor `for-of` uses. */",
375 ),
376 },
377 ),
378 (
379 "forEach".to_string(),
380 MethodSig {
381 optional: false,
382 generics: Vec::new(),
383 params: vec![Param::new(
384 "callback",
385 Type::Function {
386 optional: 0,
387 params: vec![
388 Type::TypeVar("V".to_string()),
389 Type::TypeVar("K".to_string()),
390 Type::prelude_interface(
391 "Map".to_string(),
392 vec![
393 Type::TypeVar("K".to_string()),
394 Type::TypeVar("V".to_string()),
395 ],
396 ),
397 ],
398 ret: Box::new(Type::Void),
399 predicate: None,
400 has_rest: false,
401 },
402 )],
403 ret: Type::Void,
404 predicate: None,
405 doc: doc(
406 "/**\n * Calls `callback(value, key, map)` once for each entry in insertion order, including entries the callback adds.\n * @param callback Function called once per entry — value first, key second, matching JS.\n */",
407 ),
408 },
409 ),
410 (
411 "iterator".to_string(),
415 MethodSig {
416 optional: false,
417 generics: Vec::new(),
418 params: Vec::new(),
419 ret: Type::prelude_interface("Iterator".to_string(), vec![Type::Tuple(vec![
420 Type::TypeVar("K".to_string()),
421 Type::TypeVar("V".to_string()),
422 ].into())]),
423 predicate: None,
424 doc: doc(
425 "/** Returns a fresh `Iterator<[K, V]>` over the entries in insertion order. It walks the map live: entries added later are visited, entries deleted first are skipped. */",
426 ),
427 },
428 ),
429 (
430 "clear".to_string(),
431 MethodSig {
432 optional: false,
433 generics: Vec::new(),
434 params: Vec::new(),
435 ret: Type::Void,
436 predicate: None,
437 doc: doc("/** Removes every entry. */"),
438 },
439 ),
440 ]),
441 properties: BTreeMap::from([(
442 "size".to_string(),
443 PropertySig {
444 ty: Type::Number,
445 readonly: true,
446 intrinsic: false,
447 optional: false,
448 doc: doc("/** The number of entries currently in the map. */"),
449 },
450 )]),
451 dispatch: Dispatch::Direct,
452 doc: doc(
453 "/** A hash-backed key-value collection. Keys are compared by structural equality through each key's `equals` method; lookup buckets via `hash`. Iteration follows insertion order. */",
454 ),
455 },
456 },
457 );
458
459 defs.types.insert(
460 "MapConstructor".to_string(),
461 TypeSymbol {
462 name: "MapConstructor".to_string(),
463 mangled_name: crate::mangle::prelude("MapConstructor"),
464 declaration_span: Span::at(crate::FileId::PRELUDE),
465 kind: TypeKind::Interface { index: None,
466 generics: Vec::new(),
467 methods: BTreeMap::from([(
468 "new".to_string(),
469 MethodSig {
470 optional: false,
471 generics: vec!["K".to_string(), "V".to_string()],
472 params: vec![Param::with_default(
473 "entries",
474 Type::union(vec![
475 Type::Readonly(Box::new(Type::Array(Box::new(Type::Tuple(vec![
476 Type::TypeVar("K".to_string()),
477 Type::TypeVar("V".to_string()),
478 ].into()))))),
479 Type::prelude_interface(
480 "Iterable".to_string(),
481 vec![Type::Tuple(vec![
482 Type::TypeVar("K".to_string()),
483 Type::TypeVar("V".to_string()),
484 ].into())],
485 ),
486 Type::prelude_interface(
487 "Iterator".to_string(),
488 vec![Type::Tuple(vec![
489 Type::TypeVar("K".to_string()),
490 Type::TypeVar("V".to_string()),
491 ].into())],
492 ),
493 Type::Null,
494 Type::Undefined,
495 ]),
496 crate::DefaultValue::Undefined,
497 )],
498 ret: Type::prelude_interface("Map".to_string(), vec![
499 Type::TypeVar("K".to_string()),
500 Type::TypeVar("V".to_string()),
501 ]),
502 predicate: None,
503 doc: doc(
504 "/** Construct a `Map<K, V>`, optionally from an iterable of `[K, V]` entries: `new Map([[\"a\", 1]])`. */",
505 ),
506 },
507 )]),
508 properties: BTreeMap::new(),
509 dispatch: Dispatch::Static,
510 doc: doc(
511 "/** Constructor object for `Map`. Accessed via the global `Map` binding — call `new Map<K, V>()`. */",
512 ),
513 },
514 },
515 );
516 defs.values.insert(
517 "Map".to_string(),
518 ValueSymbol {
519 name: "Map".to_string(),
520 mangled_name: crate::mangle::prelude("Map"),
521 declaration_span: Span::at(crate::FileId::PRELUDE),
522 kind: ValueKind::Const {
523 ty: Type::prelude_interface("MapConstructor".to_string(), Vec::new()),
524 doc: doc("/** The `Map` constructor — call `new Map<K, V>()`. */"),
525 },
526 },
527 );
528}