1use crate::runtime::host::{abi_arg, abi_result};
19mod install;
20
21pub(crate) use install::declare_types;
22pub use install::{declare, install};
23
24use wasmtime::{
25 ArrayRef, ArrayRefPre, Caller, FieldType, Finality, Mutability, RefType, Rooted, StorageType,
26 StructRef, StructRefPre, StructType, Val, ValType,
27};
28
29use crate::runtime::StoreData;
30use crate::runtime::fuel;
31use crate::runtime::gc_singleton::singleton_struct;
32use crate::runtime::host::{host_map_tombstone, host_object_vtable, write_submilli_array_struct};
33use crate::runtime::intrinsic_types::{IntrinsicTypes, intrinsic_types};
34use crate::runtime::prelude::closure::{self, Closure};
35use crate::runtime::prelude::collection::{
36 both_nan, decode_key, encode_key, is_null_key, probe_capacity, rehash_capacity,
37};
38use crate::runtime::prelude::collection::{is_a, object_field, read_array_vals, unbox_bool};
39use crate::runtime::prelude::iterator::{
40 IterKind, IteratorSource, build_iterator, iter_done, iter_yield, next_closure_type, shared_next,
41};
42use crate::runtime::prelude::keep::KeptValue;
43use crate::runtime::prelude::ledger::{
44 LedgerCursor, LedgerFields, forward_cleared, forward_rehashed,
45};
46use crate::runtime::prelude::map::raw_index_array_type;
47use crate::runtime::prelude::vtable::dispatch_vtable_slot;
48
49const INITIAL_CAPACITY: i32 = 8;
52
53pub(crate) fn set_backing_struct(
63 engine: &wasmtime::Engine,
64 intr: &IntrinsicTypes,
65) -> wasmtime::Result<StructType> {
66 let imm = Mutability::Const;
67 let mutv = Mutability::Var;
68 let raw_index = raw_index_array_type(engine)?;
69 singleton_struct(
70 engine,
71 Finality::NonFinal,
72 Some(intr.object.clone()),
73 vec![
74 FieldType::new(
75 imm,
76 StorageType::ValType(ValType::Ref(RefType::new(
77 false,
78 intr.vtable.clone().into(),
79 ))),
80 ),
81 FieldType::new(
82 mutv,
83 StorageType::ValType(ValType::Ref(RefType::new(
84 false,
85 intr.raw_array.clone().into(),
86 ))),
87 ),
88 FieldType::new(mutv, StorageType::ValType(ValType::I32)),
89 FieldType::new(
90 mutv,
91 StorageType::ValType(ValType::Ref(RefType::new(false, raw_index.clone().into()))),
92 ),
93 FieldType::new(mutv, StorageType::ValType(ValType::I32)),
94 FieldType::new(
95 mutv,
96 StorageType::ValType(ValType::Ref(RefType::new(false, raw_index.clone().into()))),
97 ),
98 FieldType::new(
99 mutv,
100 StorageType::ValType(ValType::Ref(RefType::new(false, raw_index.into()))),
101 ),
102 FieldType::new(mutv, StorageType::ValType(ValType::I64)),
103 ],
104 )
105}
106
107const F_ELEMENTS: usize = 1;
109const F_SIZE: usize = 2;
110const F_ORDER: usize = 3;
111const F_ORDER_LEN: usize = 4;
112const F_HASHES: usize = 5;
113const F_ORDER_POSITIONS: usize = 6;
114
115fn backing(caller: &mut Caller<'_, StoreData>, recv: &Val) -> wasmtime::Result<Rooted<StructRef>> {
117 let Val::AnyRef(Some(any)) = recv else {
118 return Err(wasmtime::Error::msg("Set method: receiver is null"));
119 };
120 any.as_struct(&mut *caller)?
121 .ok_or_else(|| wasmtime::Error::msg("Set method: receiver is not a $SetBacking"))
122}
123
124fn field_array(
126 caller: &mut Caller<'_, StoreData>,
127 b: &Rooted<StructRef>,
128 idx: usize,
129) -> wasmtime::Result<Rooted<ArrayRef>> {
130 match b.field(&mut *caller, idx)? {
131 Val::AnyRef(Some(a)) => a.unwrap_array(&mut *caller),
132 other => Err(wasmtime::Error::msg(format!(
133 "Set backing field {idx} is not an array: {other:?}"
134 ))),
135 }
136}
137
138fn field_i32(
140 caller: &mut Caller<'_, StoreData>,
141 b: &Rooted<StructRef>,
142 idx: usize,
143) -> wasmtime::Result<i32> {
144 match b.field(&mut *caller, idx)? {
145 Val::I32(n) => Ok(n),
146 other => Err(wasmtime::Error::msg(format!(
147 "Set backing field {idx} is not an i32: {other:?}"
148 ))),
149 }
150}
151
152fn is_tombstone(caller: &mut Caller<'_, StoreData>, slot: &Val) -> wasmtime::Result<bool> {
154 let tomb = host_map_tombstone(caller)?;
155 match (slot, &tomb) {
156 (Val::AnyRef(Some(a)), Val::AnyRef(Some(b))) => Ok(Rooted::ref_eq(&caller, a, b)?),
157 _ => Ok(false),
158 }
159}
160
161fn is_null(v: &Val) -> bool {
162 matches!(v, Val::AnyRef(None))
163}
164
165async fn hash(caller: &mut Caller<'_, StoreData>, elem: &Val) -> wasmtime::Result<i32> {
167 if is_null_key(caller, elem)? {
168 return Ok(0);
169 }
170 if super::undefined::is_undefined(caller, elem)? {
171 return Ok(1);
172 }
173 match dispatch_vtable_slot(caller, elem, 3, &[]).await? {
174 Val::I32(h) => Ok(h),
175 other => Err(wasmtime::Error::msg(format!(
176 "Set element hash returned {other:?}, expected i32"
177 ))),
178 }
179}
180
181async fn equals(
183 caller: &mut Caller<'_, StoreData>,
184 elem: &Val,
185 slot: &Val,
186) -> wasmtime::Result<bool> {
187 let left_null = is_null_key(caller, elem)?;
188 let right_null = is_null_key(caller, slot)?;
189 if left_null || right_null {
190 return Ok(left_null && right_null);
191 }
192 let left_undefined = super::undefined::is_undefined(caller, elem)?;
193 let right_undefined = super::undefined::is_undefined(caller, slot)?;
194 if left_undefined || right_undefined {
195 return Ok(left_undefined && right_undefined);
196 }
197 if both_nan(caller, elem, slot)? {
198 return Ok(true);
199 }
200 match dispatch_vtable_slot(caller, elem, 2, &[*slot]).await? {
201 Val::I32(b) => Ok(b != 0),
202 other => Err(wasmtime::Error::msg(format!(
203 "Set element equals returned {other:?}, expected i32"
204 ))),
205 }
206}
207
208fn index_value(
209 caller: &mut Caller<'_, StoreData>,
210 array: &Rooted<ArrayRef>,
211 index: u32,
212) -> wasmtime::Result<i32> {
213 #[cfg(test)]
214 {
215 caller.data_mut().collection_index_reads += 1;
216 }
217 match array.get(&mut *caller, index)? {
218 Val::I32(value) => Ok(value),
219 _ => Err(crate::runtime::host::fatal_host_error(
220 "Invalid collection index/hash array",
221 )),
222 }
223}
224
225fn new_raw_array(caller: &mut Caller<'_, StoreData>, n: i32) -> wasmtime::Result<Rooted<ArrayRef>> {
227 let raw = intrinsic_types(&mut *caller)?.raw_array.clone();
228 let pre = ArrayRefPre::new(&mut *caller, raw);
229 let capacity =
230 u32::try_from(n).map_err(|_| wasmtime::Error::msg("Negative collection capacity"))?;
231 ArrayRef::new(&mut *caller, &pre, &Val::null_any_ref(), capacity)
232}
233
234fn new_index_array(
236 caller: &mut Caller<'_, StoreData>,
237 n: i32,
238) -> wasmtime::Result<Rooted<ArrayRef>> {
239 let ty = raw_index_array_type(caller.engine())?;
240 let pre = ArrayRefPre::new(&mut *caller, ty);
241 let capacity =
242 u32::try_from(n).map_err(|_| wasmtime::Error::msg("Negative collection capacity"))?;
243 ArrayRef::new(&mut *caller, &pre, &Val::I32(0), capacity)
244}
245
246pub(super) async fn add(
254 caller: &mut Caller<'_, StoreData>,
255 recv: &Val,
256 value: &Val,
257) -> wasmtime::Result<Val> {
258 let encoded_key = encode_key(caller, value)?;
259 let value = &encoded_key;
260 let b = backing(caller, recv)?;
261 let key_hash = hash(caller, value).await?;
262
263 let size = field_i32(caller, &b, F_SIZE)?;
264 let cap0 = field_array(caller, &b, F_ELEMENTS)?.len(&mut *caller)?;
265 let order_len = field_i32(caller, &b, F_ORDER_LEN)?;
266 if let Some(capacity) = rehash_capacity(cap0, size, order_len)? {
267 if find_slot_hashed(caller, &b, value, key_hash)
268 .await?
269 .is_some()
270 {
271 return Ok(*recv);
272 }
273 rehash(caller, &b, capacity)?;
274 }
275
276 let elements = field_array(caller, &b, F_ELEMENTS)?;
277 let order = field_array(caller, &b, F_ORDER)?;
278 let order_len = field_i32(caller, &b, F_ORDER_LEN)?;
279 let cap = probe_capacity(elements.len(&mut *caller)?)?;
280
281 let hashes = field_array(caller, &b, F_HASHES)?;
282 let positions = field_array(caller, &b, F_ORDER_POSITIONS)?;
283 let mut i = key_hash & (cap - 1);
284 let mut first_tomb: i32 = -1;
285 for _ in 0..cap {
286 fuel::charge(&mut *caller, fuel::ELEM, 1)?;
287 let slot = elements.get(&mut *caller, i as u32)?;
288 if is_null(&slot) {
289 let ins = if first_tomb == -1 { i } else { first_tomb };
290 elements.set(&mut *caller, ins as u32, *value)?;
291 order.set(&mut *caller, order_len as u32, Val::I32(ins))?;
292 hashes.set(&mut *caller, ins as u32, Val::I32(key_hash))?;
293 positions.set(&mut *caller, ins as u32, Val::I32(order_len))?;
294 b.set_field(&mut *caller, F_ORDER_LEN, Val::I32(order_len + 1))?;
295 b.set_field(&mut *caller, F_SIZE, Val::I32(size + 1))?;
296 return Ok(*recv);
297 }
298 if is_tombstone(caller, &slot)? {
299 if first_tomb == -1 {
300 first_tomb = i;
301 }
302 } else if index_value(caller, &hashes, i as u32)? == key_hash
303 && equals(caller, value, &slot).await?
304 {
305 return Ok(*recv);
306 }
307 i = (i + 1) & (cap - 1);
308 }
309 Err(wasmtime::Error::msg("Set insertion found no empty bucket"))
310}
311
312pub(super) async fn has(
314 caller: &mut Caller<'_, StoreData>,
315 recv: &Val,
316 value: &Val,
317) -> wasmtime::Result<bool> {
318 let encoded_key = encode_key(caller, value)?;
319 let b = backing(caller, recv)?;
320 Ok(find_slot(caller, &b, &encoded_key).await?.is_some())
321}
322
323async fn find_slot(
325 caller: &mut Caller<'_, StoreData>,
326 b: &Rooted<StructRef>,
327 key: &Val,
328) -> wasmtime::Result<Option<u32>> {
329 let key_hash = hash(caller, key).await?;
330 find_slot_hashed(caller, b, key, key_hash).await
331}
332
333async fn find_slot_hashed(
334 caller: &mut Caller<'_, StoreData>,
335 b: &Rooted<StructRef>,
336 key: &Val,
337 key_hash: i32,
338) -> wasmtime::Result<Option<u32>> {
339 let elements = field_array(caller, b, F_ELEMENTS)?;
340 let cap = probe_capacity(elements.len(&mut *caller)?)?;
341 let hashes = field_array(caller, b, F_HASHES)?;
342 let mut i = key_hash & (cap - 1);
343 for _ in 0..cap {
344 fuel::charge(&mut *caller, fuel::ELEM, 1)?;
345 let slot = elements.get(&mut *caller, i as u32)?;
346 if is_null(&slot) {
347 return Ok(None);
348 }
349 if !is_tombstone(caller, &slot)?
350 && index_value(caller, &hashes, i as u32)? == key_hash
351 && equals(caller, key, &slot).await?
352 {
353 return Ok(Some(i as u32));
354 }
355 i = (i + 1) & (cap - 1);
356 }
357 Ok(None)
358}
359
360pub(super) async fn delete(
363 caller: &mut Caller<'_, StoreData>,
364 recv: &Val,
365 value: &Val,
366) -> wasmtime::Result<bool> {
367 let encoded_key = encode_key(caller, value)?;
368 let value = &encoded_key;
369 let b = backing(caller, recv)?;
370 let elements = field_array(caller, &b, F_ELEMENTS)?;
371 let order = field_array(caller, &b, F_ORDER)?;
372 let positions = field_array(caller, &b, F_ORDER_POSITIONS)?;
373 let Some(index) = find_slot(caller, &b, value).await? else {
374 return Ok(false);
375 };
376 let position = index_value(caller, &positions, index)?;
377 let position = u32::try_from(position).map_err(crate::runtime::host::fatal_host_error)?;
378 let size = field_i32(caller, &b, F_SIZE)?;
379 let remaining = size.checked_sub(1).filter(|n| *n >= 0).ok_or_else(|| {
380 crate::runtime::host::fatal_host_error("Invalid collection size on deletion")
381 })?;
382 let tomb = host_map_tombstone(caller)?;
383 elements.set(&mut *caller, index, tomb)?;
384 order.set(&mut *caller, position, Val::I32(-1))?;
385 b.set_field(&mut *caller, F_SIZE, Val::I32(remaining))?;
386 Ok(true)
387}
388
389pub(super) fn clear(caller: &mut Caller<'_, StoreData>, recv: &Val) -> wasmtime::Result<()> {
391 let b = backing(caller, recv)?;
392 let old_elements = field_array(caller, &b, F_ELEMENTS)?;
393 let old_order = field_array(caller, &b, F_ORDER)?;
394 let old_order_len = field_i32(caller, &b, F_ORDER_LEN)?;
395 let elements = new_raw_array(caller, INITIAL_CAPACITY)?;
396 let order = new_index_array(caller, INITIAL_CAPACITY)?;
397 let hashes = new_index_array(caller, INITIAL_CAPACITY)?;
398 let positions = new_index_array(caller, INITIAL_CAPACITY)?;
399 b.set_field(
400 &mut *caller,
401 F_ELEMENTS,
402 Val::AnyRef(Some(elements.to_anyref())),
403 )?;
404 b.set_field(&mut *caller, F_SIZE, Val::I32(0))?;
405 b.set_field(&mut *caller, F_ORDER, Val::AnyRef(Some(order.to_anyref())))?;
406 b.set_field(&mut *caller, F_ORDER_LEN, Val::I32(0))?;
407 b.set_field(
408 &mut *caller,
409 F_HASHES,
410 Val::AnyRef(Some(hashes.to_anyref())),
411 )?;
412 b.set_field(
413 &mut *caller,
414 F_ORDER_POSITIONS,
415 Val::AnyRef(Some(positions.to_anyref())),
416 )?;
417 forward_cleared(
418 caller,
419 &old_elements,
420 &old_order,
421 old_order_len,
422 &elements,
423 &order,
424 )
425}
426
427pub(super) fn size(caller: &mut Caller<'_, StoreData>, recv: &Val) -> wasmtime::Result<Val> {
430 let b = backing(caller, recv)?;
431 let n = field_i32(caller, &b, F_SIZE)?;
432 Ok(Val::F64((n as f64).to_bits()))
433}
434
435fn rehash(
438 caller: &mut Caller<'_, StoreData>,
439 b: &Rooted<StructRef>,
440 new_cap: i32,
441) -> wasmtime::Result<()> {
442 let old_elements = field_array(caller, b, F_ELEMENTS)?;
443 let old_order = field_array(caller, b, F_ORDER)?;
444 let old_hashes = field_array(caller, b, F_HASHES)?;
445 let old_order_len = field_i32(caller, b, F_ORDER_LEN)?;
446 fuel::charge(
448 &mut *caller,
449 fuel::ELEM,
450 (new_cap as u64)
451 .saturating_mul(4)
452 .saturating_add(old_order_len as u64),
453 )?;
454
455 let new_elements = new_raw_array(caller, new_cap)?;
456 let new_order = new_index_array(caller, new_cap)?;
457 let new_hashes = new_index_array(caller, new_cap)?;
458 let new_positions = new_index_array(caller, new_cap)?;
459 let mut new_order_len: i32 = 0;
460
461 for o in 0..old_order_len {
462 let Val::I32(probe_idx) = old_order.get(&mut *caller, o as u32)? else {
463 return Err(wasmtime::Error::msg("Invalid collection ledger entry"));
464 };
465 if probe_idx == -1 {
466 continue;
467 }
468 let elem = old_elements.get(&mut *caller, probe_idx as u32)?;
469 let key_hash = index_value(caller, &old_hashes, probe_idx as u32)?;
470 let mut k = key_hash & (new_cap - 1);
471 let mut inserted = false;
472 for _ in 0..new_cap {
473 fuel::charge(&mut *caller, fuel::ELEM, 1)?;
474 if is_null(&new_elements.get(&mut *caller, k as u32)?) {
475 new_elements.set(&mut *caller, k as u32, elem)?;
476 new_order.set(&mut *caller, new_order_len as u32, Val::I32(k))?;
477 new_hashes.set(&mut *caller, k as u32, Val::I32(key_hash))?;
478 new_positions.set(&mut *caller, k as u32, Val::I32(new_order_len))?;
479 new_order_len += 1;
480 inserted = true;
481 break;
482 }
483 k = (k + 1) & (new_cap - 1);
484 }
485 if !inserted {
486 return Err(wasmtime::Error::msg("Set rehash found no empty bucket"));
487 }
488 }
489
490 b.set_field(
491 &mut *caller,
492 F_ELEMENTS,
493 Val::AnyRef(Some(new_elements.to_anyref())),
494 )?;
495 b.set_field(
496 &mut *caller,
497 F_ORDER,
498 Val::AnyRef(Some(new_order.to_anyref())),
499 )?;
500 b.set_field(&mut *caller, F_ORDER_LEN, Val::I32(new_order_len))?;
501 b.set_field(
502 &mut *caller,
503 F_HASHES,
504 Val::AnyRef(Some(new_hashes.to_anyref())),
505 )?;
506 b.set_field(
507 &mut *caller,
508 F_ORDER_POSITIONS,
509 Val::AnyRef(Some(new_positions.to_anyref())),
510 )?;
511 forward_rehashed(caller, &old_elements, &new_elements, &new_order)
512}
513
514const LEDGER: LedgerFields = LedgerFields {
520 entries: F_ELEMENTS,
521 order: F_ORDER,
522 order_len: F_ORDER_LEN,
523};
524
525pub(super) async fn for_each(
529 caller: &mut Caller<'_, StoreData>,
530 recv: &Val,
531 f: &Closure,
532) -> wasmtime::Result<()> {
533 let b = backing(caller, recv)?;
534 let stored = LedgerCursor::start(caller, b, LEDGER)?.to_val(caller)?;
537 loop {
538 let mut cursor = LedgerCursor::from_val(caller, &stored)?;
539 let Some(bucket) = cursor.next_bucket(caller, LEDGER)? else {
540 return Ok(());
541 };
542 cursor.store(caller, &stored)?;
543 let elem = set_element(caller, &cursor, bucket)?;
544 f.call_dynamic(caller, &[elem, elem, *recv]).await?;
546 }
547}
548
549fn set_element(
551 caller: &mut Caller<'_, StoreData>,
552 cursor: &LedgerCursor,
553 bucket: u32,
554) -> wasmtime::Result<Val> {
555 let elements = cursor.current_array(caller, F_ELEMENTS)?;
556 let elem = elements.get(&mut *caller, bucket)?;
557 decode_key(caller, elem)
558}
559
560fn make_set_iterator(
564 caller: &mut Caller<'_, StoreData>,
565 recv: &Val,
566 kind: IterKind,
567) -> wasmtime::Result<Val> {
568 let b = backing(caller, recv)?;
569 let cursor = LedgerCursor::start(caller, b, LEDGER)?.to_val(caller)?;
570
571 let intr = intrinsic_types(&mut *caller)?;
572 let (next_ty, next_struct) = next_closure_type(caller.engine(), &intr)?;
573 let next = shared_next(
574 caller,
575 IteratorSource::Set,
576 kind,
577 next_ty,
578 move |caller, params, results| set_next_step(caller, params, results, kind),
579 )?;
580 build_iterator(caller, next_struct, next, cursor)
581}
582
583fn set_next_step(
587 caller: &mut Caller<'_, StoreData>,
588 params: &[Val],
589 results: &mut [Val],
590 kind: IterKind,
591) -> wasmtime::Result<()> {
592 let stored = abi_arg(params, 0)?;
593 let mut cursor = LedgerCursor::from_val(caller, stored)?;
594 let bucket = cursor.next_bucket(caller, LEDGER)?;
595 cursor.store(caller, stored)?;
596 let Some(bucket) = bucket else {
597 *abi_result(results, 0)? = iter_done(caller)?;
598 return Ok(());
599 };
600 let elem = set_element(caller, &cursor, bucket)?;
601 let yielded = match kind {
602 IterKind::Keys | IterKind::Values => elem,
603 IterKind::Entries => {
604 let pair = write_submilli_array_struct(caller, &[elem, elem])?;
605 Val::AnyRef(Some(pair.to_anyref()))
606 }
607 };
608 *abi_result(results, 0)? = iter_yield(caller, yielded)?;
609 Ok(())
610}
611
612pub(super) fn values(caller: &mut Caller<'_, StoreData>, recv: &Val) -> wasmtime::Result<Val> {
615 make_set_iterator(caller, recv, IterKind::Values)
616}
617
618pub(super) fn entries(caller: &mut Caller<'_, StoreData>, recv: &Val) -> wasmtime::Result<Val> {
620 make_set_iterator(caller, recv, IterKind::Entries)
621}
622
623enum Keep {
630 All,
631 IfMember { member: Val, want: bool },
632}
633
634async fn add_pass(
637 caller: &mut Caller<'_, StoreData>,
638 source: &Val,
639 keep: Keep,
640 result: &Val,
641) -> wasmtime::Result<()> {
642 let b = backing(caller, source)?;
643 let elements = field_array(caller, &b, F_ELEMENTS)?;
644 let order = field_array(caller, &b, F_ORDER)?;
645 let order_len = field_i32(caller, &b, F_ORDER_LEN)?;
646 for o in 0..order_len {
647 let Val::I32(idx) = order.get(&mut *caller, o as u32)? else {
648 continue;
649 };
650 if idx == -1 {
651 continue;
652 }
653 let elem = elements.get(&mut *caller, idx as u32)?;
654 let elem = decode_key(caller, elem)?;
655 let keep_it = match &keep {
656 Keep::All => true,
657 Keep::IfMember { member, want } => has(caller, member, &elem).await? == *want,
658 };
659 if keep_it {
660 add(caller, result, &elem).await?;
661 }
662 }
663 Ok(())
664}
665
666pub(super) async fn union(
669 caller: &mut Caller<'_, StoreData>,
670 recv: &Val,
671 other: &Val,
672) -> wasmtime::Result<Val> {
673 let result = build_empty(caller)?;
674 add_pass(caller, recv, Keep::All, &result).await?;
675 add_pass(caller, other, Keep::All, &result).await?;
676 Ok(result)
677}
678
679pub(super) async fn intersection(
681 caller: &mut Caller<'_, StoreData>,
682 recv: &Val,
683 other: &Val,
684) -> wasmtime::Result<Val> {
685 let result = build_empty(caller)?;
686 add_pass(
687 caller,
688 recv,
689 Keep::IfMember {
690 member: *other,
691 want: true,
692 },
693 &result,
694 )
695 .await?;
696 Ok(result)
697}
698
699pub(super) async fn difference(
701 caller: &mut Caller<'_, StoreData>,
702 recv: &Val,
703 other: &Val,
704) -> wasmtime::Result<Val> {
705 let result = build_empty(caller)?;
706 add_pass(
707 caller,
708 recv,
709 Keep::IfMember {
710 member: *other,
711 want: false,
712 },
713 &result,
714 )
715 .await?;
716 Ok(result)
717}
718
719pub(super) async fn symmetric_difference(
722 caller: &mut Caller<'_, StoreData>,
723 recv: &Val,
724 other: &Val,
725) -> wasmtime::Result<Val> {
726 let result = build_empty(caller)?;
727 add_pass(
728 caller,
729 recv,
730 Keep::IfMember {
731 member: *other,
732 want: false,
733 },
734 &result,
735 )
736 .await?;
737 add_pass(
738 caller,
739 other,
740 Keep::IfMember {
741 member: *recv,
742 want: false,
743 },
744 &result,
745 )
746 .await?;
747 Ok(result)
748}
749
750async fn relation(
754 caller: &mut Caller<'_, StoreData>,
755 source: &Val,
756 member: &Val,
757 fail_when_found: bool,
758) -> wasmtime::Result<bool> {
759 let b = backing(caller, source)?;
760 let elements = field_array(caller, &b, F_ELEMENTS)?;
761 let order = field_array(caller, &b, F_ORDER)?;
762 let order_len = field_i32(caller, &b, F_ORDER_LEN)?;
763 for o in 0..order_len {
764 let Val::I32(idx) = order.get(&mut *caller, o as u32)? else {
765 continue;
766 };
767 if idx == -1 {
768 continue;
769 }
770 let elem = elements.get(&mut *caller, idx as u32)?;
771 let elem = decode_key(caller, elem)?;
772 if has(caller, member, &elem).await? == fail_when_found {
773 return Ok(false);
774 }
775 }
776 Ok(true)
777}
778
779pub(super) async fn is_subset_of(
781 caller: &mut Caller<'_, StoreData>,
782 recv: &Val,
783 other: &Val,
784) -> wasmtime::Result<bool> {
785 relation(caller, recv, other, false).await
786}
787
788pub(super) async fn is_superset_of(
790 caller: &mut Caller<'_, StoreData>,
791 recv: &Val,
792 other: &Val,
793) -> wasmtime::Result<bool> {
794 relation(caller, other, recv, false).await
795}
796
797pub(super) async fn is_disjoint_from(
799 caller: &mut Caller<'_, StoreData>,
800 recv: &Val,
801 other: &Val,
802) -> wasmtime::Result<bool> {
803 relation(caller, recv, other, true).await
804}
805
806pub(super) async fn construct(
817 caller: &mut Caller<'_, StoreData>,
818 init: &Val,
819) -> wasmtime::Result<Val> {
820 let coll = build_empty(caller)?;
821 if super::undefined::is_nullish(caller, init)? {
822 return Ok(coll);
823 }
824
825 let intr = intrinsic_types(&mut *caller)?;
826 if is_a(caller, init, &intr.array)? {
827 for elem in read_array_vals(caller, init)? {
828 add(caller, &coll, &elem).await?;
829 }
830 return Ok(coll);
831 }
832 if is_a(caller, init, &intr.string)? {
833 for cp in super::collection::string_code_points(caller, init)? {
834 add(caller, &coll, &cp).await?;
835 }
836 return Ok(coll);
837 }
838
839 let kept_iterator = KeptValue::new(caller)?;
841 let kept_result = KeptValue::new(caller)?;
842 let it = if is_a(caller, init, &set_backing_struct(caller.engine(), &intr)?)? {
843 values(caller, init)?
844 } else if let Some(iter_method) = object_field(caller, init, "iterator")? {
845 let c = closure::read(caller, &iter_method, "Set ctor iterable")?;
846 c.call(caller, &[]).await?
847 } else {
848 *init
849 };
850 kept_iterator.set(caller, it)?;
851
852 let next = object_field(caller, &it, "next")?
853 .ok_or_else(|| wasmtime::Error::msg("Set ctor: initializer is not iterable"))?;
854 let next_closure = closure::read(caller, &next, "Set ctor iterator")?;
855 loop {
856 let result = next_closure.call(caller, &[]).await?;
857 kept_result.set(caller, result)?;
858 let done = object_field(caller, &result, "done")?
859 .ok_or_else(|| wasmtime::Error::msg("Set ctor: iterator result missing `done`"))?;
860 if unbox_bool(caller, &done)? {
861 break;
862 }
863 let value = object_field(caller, &result, "value")?
864 .ok_or_else(|| wasmtime::Error::msg("Set ctor: iterator result missing `value`"))?;
865 add(caller, &coll, &value).await?;
866 }
867 Ok(coll)
868}
869
870fn build_empty(caller: &mut Caller<'_, StoreData>) -> wasmtime::Result<Val> {
872 let intr = intrinsic_types(&mut *caller)?;
873 let ty = set_backing_struct(caller.engine(), &intr)?;
874 let vtable = host_object_vtable(caller)?;
875 let elements = new_raw_array(caller, INITIAL_CAPACITY)?;
876 let order = new_index_array(caller, INITIAL_CAPACITY)?;
877 let hashes = new_index_array(caller, INITIAL_CAPACITY)?;
878 let positions = new_index_array(caller, INITIAL_CAPACITY)?;
879 let pre = StructRefPre::new(&mut *caller, ty);
880 let st = StructRef::new(
881 &mut *caller,
882 &pre,
883 &[
884 vtable,
885 Val::AnyRef(Some(elements.to_anyref())),
886 Val::I32(0),
887 Val::AnyRef(Some(order.to_anyref())),
888 Val::I32(0),
889 Val::AnyRef(Some(hashes.to_anyref())),
890 Val::AnyRef(Some(positions.to_anyref())),
891 Val::I64(0),
892 ],
893 )?;
894 Ok(Val::AnyRef(Some(st.to_anyref())))
895}
896
897#[cfg(test)]
898mod tests {
899 use super::*;
900 use crate::codegen::intrinsics::declare_intrinsic_types;
901 use crate::runtime::intrinsic_types::build_intrinsic_types;
902 use wasm_encoder::{
903 ConstExpr, ExportKind, ExportSection, GlobalSection, GlobalType as EncGlobalType,
904 HeapType as EncHeapType, Module, RefType as EncRefType, TypeSection, ValType as EncValType,
905 };
906 use wasmtime::{Config, Engine};
907
908 #[test]
912 fn set_backing_matches_codegen() {
913 let mut config = Config::new();
914 config.wasm_gc(true);
915 config.wasm_function_references(true);
916 let engine = Engine::new(&config).unwrap();
917
918 let intr = build_intrinsic_types(&engine).unwrap();
919 let host = set_backing_struct(&engine, &intr).unwrap();
920
921 let mut module = Module::new();
922 let mut types = TypeSection::new();
923 let idx = declare_intrinsic_types(&mut types);
924 let backing_idx = idx.set;
925 module.section(&types);
926
927 let mut globals = GlobalSection::new();
928 globals.global(
929 EncGlobalType {
930 val_type: EncValType::Ref(EncRefType {
931 nullable: true,
932 heap_type: EncHeapType::Concrete(backing_idx),
933 }),
934 mutable: false,
935 shared: false,
936 },
937 &ConstExpr::ref_null(EncHeapType::Concrete(backing_idx)),
938 );
939 let mut exports = ExportSection::new();
940 exports.export("backing", ExportKind::Global, 0);
941 module.section(&globals);
942 module.section(&exports);
943
944 let module = wasmtime::Module::new(&engine, module.finish()).unwrap();
945 let recovered = module
946 .get_export("backing")
947 .unwrap()
948 .global()
949 .unwrap()
950 .content()
951 .as_ref()
952 .map(|r| r.heap_type().clone())
953 .unwrap()
954 .as_concrete_struct()
955 .unwrap()
956 .clone();
957 assert!(StructType::eq(&host, &recovered));
958 }
959}