1use crate::JavascriptValue;
4use sim_lib_sequence::{
5 OrderedSet, OrderedSetIter, OrderedTable, OrderedTableIter, SparseSequence,
6};
7use std::collections::BTreeMap;
8
9const MAX_ARRAY_LENGTH: usize = u32::MAX as usize;
10
11#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
13pub struct JavascriptSymbol {
14 id: u64,
15 description: Option<String>,
16}
17impl JavascriptSymbol {
18 pub fn id(&self) -> u64 {
20 self.id
21 }
22 pub fn description(&self) -> Option<&str> {
24 self.description.as_deref()
25 }
26}
27
28#[derive(Clone, Debug, Default)]
30pub struct JavascriptSymbolRegistry {
31 next: u64,
32 globals: BTreeMap<String, JavascriptSymbol>,
33}
34impl JavascriptSymbolRegistry {
35 pub fn symbol(&mut self, description: Option<String>) -> JavascriptSymbol {
37 let symbol = JavascriptSymbol {
38 id: self.next,
39 description,
40 };
41 self.next += 1;
42 symbol
43 }
44 pub fn symbol_for(&mut self, key: impl Into<String>) -> JavascriptSymbol {
46 let key = key.into();
47 if let Some(symbol) = self.globals.get(&key) {
48 return symbol.clone();
49 }
50 let symbol = self.symbol(Some(key.clone()));
51 self.globals.insert(key, symbol.clone());
52 symbol
53 }
54 pub fn key_for(&self, symbol: &JavascriptSymbol) -> Option<&str> {
56 self.globals
57 .iter()
58 .find_map(|(key, value)| (value == symbol).then_some(key.as_str()))
59 }
60}
61
62#[derive(Clone, Debug, Eq, PartialEq)]
64pub enum JavascriptCollectionError {
65 Index,
67 Limit,
69}
70
71#[derive(Clone, Debug, PartialEq)]
73pub struct JavascriptArray {
74 elements: SparseSequence<JavascriptValue>,
75}
76impl Default for JavascriptArray {
77 fn default() -> Self {
78 Self::sparse(0)
79 }
80}
81impl JavascriptArray {
82 pub fn dense(values: Vec<JavascriptValue>) -> Self {
84 let mut elements = SparseSequence::new(MAX_ARRAY_LENGTH);
85 for (index, value) in values.into_iter().enumerate() {
86 elements
87 .set(index, value)
88 .expect("a materialized vector has a valid JavaScript array length");
89 }
90 Self { elements }
91 }
92 pub fn sparse(length: usize) -> Self {
94 assert!(
95 length <= MAX_ARRAY_LENGTH,
96 "invalid JavaScript array length"
97 );
98 let mut elements = SparseSequence::new(MAX_ARRAY_LENGTH);
99 elements.set_len(length).expect("length was checked");
100 Self { elements }
101 }
102 pub fn len(&self) -> usize {
104 self.elements.len()
105 }
106 pub fn is_empty(&self) -> bool {
108 self.elements.is_empty()
109 }
110 pub fn get(&self, index: usize) -> Option<&JavascriptValue> {
112 self.elements.get(index)
113 }
114 pub fn set(&mut self, index: usize, value: JavascriptValue) {
116 self.elements
117 .set(index, value)
118 .expect("invalid JavaScript array index");
119 }
120 pub fn set_len(&mut self, length: usize) -> Result<(), JavascriptCollectionError> {
122 self.elements
123 .set_len(length)
124 .map_err(|_| JavascriptCollectionError::Index)
125 }
126 pub fn push(&mut self, value: JavascriptValue) -> usize {
128 self.set(self.len(), value);
129 self.len()
130 }
131 pub fn pop(&mut self) -> Option<JavascriptValue> {
133 let index = self.len().checked_sub(1)?;
134 let value = self.elements.remove(index);
135 self.elements
136 .set_len(index)
137 .expect("shrinking an array length is valid");
138 value
139 }
140 pub fn values(&self) -> JavascriptIterator {
142 JavascriptIterator::new(
143 (0..self.len())
144 .map(|index| {
145 self.get(index)
146 .cloned()
147 .unwrap_or(JavascriptValue::Undefined)
148 })
149 .collect(),
150 )
151 }
152 pub fn for_each(
154 &self,
155 max_visits: usize,
156 mut f: impl FnMut(&JavascriptValue, usize),
157 ) -> Result<(), JavascriptCollectionError> {
158 for (visit, (index, value)) in self.elements.occupied_in(..).enumerate() {
159 if visit >= max_visits {
160 return Err(JavascriptCollectionError::Limit);
161 }
162 f(value, index);
163 }
164 Ok(())
165 }
166 pub fn map(
168 &self,
169 max_visits: usize,
170 mut f: impl FnMut(&JavascriptValue, usize) -> JavascriptValue,
171 ) -> Result<Self, JavascriptCollectionError> {
172 let visits = self.elements.occupied_len();
173 if visits > max_visits {
174 return Err(JavascriptCollectionError::Limit);
175 }
176 let mut out = Self::sparse(self.len());
177 for (index, value) in self.elements.occupied_in(..) {
178 out.set(index, f(value, index));
179 }
180 Ok(out)
181 }
182 pub fn filter(
184 &self,
185 max_visits: usize,
186 mut f: impl FnMut(&JavascriptValue, usize) -> bool,
187 ) -> Result<Self, JavascriptCollectionError> {
188 let mut out = Self::default();
189 let mut visits = 0;
190 for (i, value) in self.elements.occupied_in(..) {
191 visits += 1;
192 if visits > max_visits {
193 return Err(JavascriptCollectionError::Limit);
194 }
195 if f(value, i) {
196 out.push(value.clone());
197 }
198 }
199 Ok(out)
200 }
201}
202
203#[derive(Debug)]
205pub struct JavascriptMap {
206 entries: OrderedTable<JavascriptValue, JavascriptValue, SameValueZero>,
207}
208impl Default for JavascriptMap {
209 fn default() -> Self {
210 Self {
211 entries: OrderedTable::new(SameValueZero),
212 }
213 }
214}
215impl Clone for JavascriptMap {
216 fn clone(&self) -> Self {
217 let copy = Self::default();
218 for (key, value) in self.entries.iter() {
219 copy.entries.insert(key, value);
220 }
221 copy
222 }
223}
224impl PartialEq for JavascriptMap {
225 fn eq(&self, other: &Self) -> bool {
226 self.entries.iter().collect::<Vec<_>>() == other.entries.iter().collect::<Vec<_>>()
227 }
228}
229impl JavascriptMap {
230 pub fn set(&mut self, key: JavascriptValue, value: JavascriptValue) {
232 self.entries.insert(key, value);
233 }
234 pub fn get(&self, key: &JavascriptValue) -> Option<JavascriptValue> {
236 self.entries.get(key)
237 }
238 pub fn delete(&mut self, key: &JavascriptValue) -> bool {
240 self.entries.remove(key).is_some()
241 }
242 pub fn len(&self) -> usize {
244 self.entries.len()
245 }
246 pub fn is_empty(&self) -> bool {
248 self.entries.is_empty()
249 }
250 pub fn entries(&self) -> OrderedTableIter<JavascriptValue, JavascriptValue> {
252 self.entries.iter()
253 }
254}
255
256#[derive(Debug)]
258pub struct JavascriptSet {
259 values: OrderedSet<JavascriptValue, SameValueZero>,
260}
261impl Default for JavascriptSet {
262 fn default() -> Self {
263 Self {
264 values: OrderedSet::new(SameValueZero),
265 }
266 }
267}
268impl Clone for JavascriptSet {
269 fn clone(&self) -> Self {
270 let copy = Self::default();
271 for value in self.values.iter() {
272 copy.values.insert(value);
273 }
274 copy
275 }
276}
277impl PartialEq for JavascriptSet {
278 fn eq(&self, other: &Self) -> bool {
279 self.values.iter().collect::<Vec<_>>() == other.values.iter().collect::<Vec<_>>()
280 }
281}
282impl JavascriptSet {
283 pub fn add(&mut self, value: JavascriptValue) {
285 self.values.insert(value);
286 }
287 pub fn has(&self, value: &JavascriptValue) -> bool {
289 self.values.contains(value)
290 }
291 pub fn delete(&mut self, value: &JavascriptValue) -> bool {
293 self.values.remove(value)
294 }
295 pub fn len(&self) -> usize {
297 self.values.len()
298 }
299 pub fn is_empty(&self) -> bool {
301 self.values.is_empty()
302 }
303 pub fn values(&self) -> JavascriptIterator {
305 JavascriptIterator::live(self.values.iter())
306 }
307}
308
309#[derive(Clone, Debug, PartialEq)]
311pub struct JavascriptIteratorResult {
312 pub value: Option<JavascriptValue>,
314 pub done: bool,
316}
317#[derive(Clone, Debug)]
319pub struct JavascriptIterator {
320 source: JavascriptIteratorSource,
321}
322#[derive(Clone)]
323enum JavascriptIteratorSource {
324 Snapshot(std::vec::IntoIter<JavascriptValue>),
325 Live(OrderedSetIter<JavascriptValue>),
326}
327impl std::fmt::Debug for JavascriptIteratorSource {
328 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 formatter.write_str(match self {
330 Self::Snapshot(_) => "Snapshot",
331 Self::Live(_) => "Live",
332 })
333 }
334}
335impl JavascriptIterator {
336 pub fn new(values: Vec<JavascriptValue>) -> Self {
338 Self {
339 source: JavascriptIteratorSource::Snapshot(values.into_iter()),
340 }
341 }
342 fn live(values: OrderedSetIter<JavascriptValue>) -> Self {
343 Self {
344 source: JavascriptIteratorSource::Live(values),
345 }
346 }
347 pub fn next_result(&mut self) -> JavascriptIteratorResult {
349 let value = match &mut self.source {
350 JavascriptIteratorSource::Snapshot(values) => values.next(),
351 JavascriptIteratorSource::Live(values) => values.next(),
352 };
353 if let Some(value) = value {
354 JavascriptIteratorResult {
355 value: Some(value),
356 done: false,
357 }
358 } else {
359 JavascriptIteratorResult {
360 value: None,
361 done: true,
362 }
363 }
364 }
365}
366#[derive(Clone, Copy, Debug, Default)]
367struct SameValueZero;
368impl sim_lib_sequence::KeyEquivalence<JavascriptValue> for SameValueZero {
369 fn equivalent(&self, left: &JavascriptValue, right: &JavascriptValue) -> bool {
370 match (left, right) {
371 (JavascriptValue::Number(a), JavascriptValue::Number(b)) => {
372 a == b || (a.is_nan() && b.is_nan())
373 }
374 _ => left == right,
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 #[test]
383 fn arrays_preserve_holes_and_iterators_materialize_undefined() {
384 let mut a = JavascriptArray::sparse(2);
385 a.set(1, JavascriptValue::Number(2.));
386 assert_eq!(a.map(1, |v, _| v.clone()).unwrap().get(0), None);
387 let mut it = a.values();
388 assert_eq!(it.next_result().value, Some(JavascriptValue::Undefined));
389 assert!(!it.next_result().done);
390 assert!(it.next_result().done);
391 }
392 #[test]
393 fn array_callbacks_skip_holes_and_length_truncation_deletes_values() {
394 let mut array = JavascriptArray::sparse(4);
395 array.set(1, JavascriptValue::Number(1.));
396 array.set(3, JavascriptValue::Number(3.));
397 let mut visited = Vec::new();
398 array.for_each(2, |_, index| visited.push(index)).unwrap();
399 assert_eq!(visited, vec![1, 3]);
400 assert_eq!(array.get(0), None);
401
402 array.set_len(2).unwrap();
403 assert_eq!(array.len(), 2);
404 assert_eq!(array.get(1), Some(&JavascriptValue::Number(1.)));
405 assert_eq!(array.get(3), None);
406 array.set_len(4).unwrap();
407 assert_eq!(array.get(3), None);
408 }
409 #[test]
410 fn map_nan_keys_match_themselves() {
411 let mut m = JavascriptMap::default();
412 m.set(
413 JavascriptValue::Number(f64::NAN),
414 JavascriptValue::Number(1.),
415 );
416 m.set(
417 JavascriptValue::Number(f64::NAN),
418 JavascriptValue::Number(2.),
419 );
420 assert_eq!(m.len(), 1);
421 assert_eq!(
422 m.get(&JavascriptValue::Number(f64::NAN)),
423 Some(JavascriptValue::Number(2.))
424 );
425 }
426 #[test]
427 fn set_positive_and_negative_zero_are_the_same_key() {
428 let mut s = JavascriptSet::default();
429 s.add(JavascriptValue::Number(-0.));
430 s.add(JavascriptValue::Number(0.));
431 assert_eq!(s.len(), 1);
432 }
433 #[test]
434 fn map_replacement_keeps_position() {
435 let mut map = JavascriptMap::default();
436 map.set(
437 JavascriptValue::String("first".into()),
438 JavascriptValue::Number(1.),
439 );
440 map.set(
441 JavascriptValue::String("second".into()),
442 JavascriptValue::Number(2.),
443 );
444 map.set(
445 JavascriptValue::String("first".into()),
446 JavascriptValue::Number(3.),
447 );
448
449 assert_eq!(
450 map.entries().collect::<Vec<_>>(),
451 vec![
452 (
453 JavascriptValue::String("first".into()),
454 JavascriptValue::Number(3.)
455 ),
456 (
457 JavascriptValue::String("second".into()),
458 JavascriptValue::Number(2.)
459 ),
460 ]
461 );
462 }
463 #[test]
464 fn set_delete_then_reinsert_moves_to_the_end() {
465 let mut set = JavascriptSet::default();
466 set.add(JavascriptValue::String("first".into()));
467 set.add(JavascriptValue::String("second".into()));
468 assert!(set.delete(&JavascriptValue::String("first".into())));
469 set.add(JavascriptValue::String("first".into()));
470
471 let mut values = set.values();
472 assert_eq!(
473 values.next_result().value,
474 Some(JavascriptValue::String("second".into()))
475 );
476 assert_eq!(
477 values.next_result().value,
478 Some(JavascriptValue::String("first".into()))
479 );
480 }
481 #[test]
482 fn collection_iterators_visit_entries_added_during_iteration() {
483 let mut map = JavascriptMap::default();
484 map.set(
485 JavascriptValue::String("first".into()),
486 JavascriptValue::Number(1.),
487 );
488 let mut entries = map.entries();
489 assert_eq!(
490 entries.next().unwrap().0,
491 JavascriptValue::String("first".into())
492 );
493 map.set(
494 JavascriptValue::String("second".into()),
495 JavascriptValue::Number(2.),
496 );
497 assert_eq!(
498 entries.next().unwrap().0,
499 JavascriptValue::String("second".into())
500 );
501
502 let mut set = JavascriptSet::default();
503 set.add(JavascriptValue::String("first".into()));
504 let mut values = set.values();
505 assert_eq!(
506 values.next_result().value,
507 Some(JavascriptValue::String("first".into()))
508 );
509 set.add(JavascriptValue::String("second".into()));
510 assert_eq!(
511 values.next_result().value,
512 Some(JavascriptValue::String("second".into()))
513 );
514 }
515 #[test]
516 fn symbols_have_identity_and_registry_keys() {
517 let mut r = JavascriptSymbolRegistry::default();
518 assert_ne!(r.symbol(Some("x".into())), r.symbol(Some("x".into())));
519 let s = r.symbol_for("x");
520 assert_eq!(s, r.symbol_for("x"));
521 assert_eq!(r.key_for(&s), Some("x"));
522 }
523}