mustardscript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/Cargo.lock +1579 -0
  2. package/Cargo.toml +40 -0
  3. package/LICENSE +201 -0
  4. package/README.md +828 -0
  5. package/SECURITY.md +34 -0
  6. package/crates/mustard/Cargo.toml +31 -0
  7. package/crates/mustard/src/cancellation.rs +28 -0
  8. package/crates/mustard/src/diagnostic.rs +145 -0
  9. package/crates/mustard/src/ir.rs +435 -0
  10. package/crates/mustard/src/lib.rs +21 -0
  11. package/crates/mustard/src/limits.rs +22 -0
  12. package/crates/mustard/src/parser/expressions.rs +723 -0
  13. package/crates/mustard/src/parser/mod.rs +115 -0
  14. package/crates/mustard/src/parser/operators.rs +105 -0
  15. package/crates/mustard/src/parser/patterns.rs +123 -0
  16. package/crates/mustard/src/parser/scope.rs +107 -0
  17. package/crates/mustard/src/parser/statements.rs +298 -0
  18. package/crates/mustard/src/parser/tests/acceptance.rs +339 -0
  19. package/crates/mustard/src/parser/tests/mod.rs +2 -0
  20. package/crates/mustard/src/parser/tests/rejections.rs +107 -0
  21. package/crates/mustard/src/runtime/accounting.rs +613 -0
  22. package/crates/mustard/src/runtime/api.rs +192 -0
  23. package/crates/mustard/src/runtime/async_runtime/mod.rs +5 -0
  24. package/crates/mustard/src/runtime/async_runtime/promises.rs +246 -0
  25. package/crates/mustard/src/runtime/async_runtime/reactions.rs +400 -0
  26. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +224 -0
  27. package/crates/mustard/src/runtime/builtins/arrays.rs +1205 -0
  28. package/crates/mustard/src/runtime/builtins/collections.rs +573 -0
  29. package/crates/mustard/src/runtime/builtins/install.rs +501 -0
  30. package/crates/mustard/src/runtime/builtins/intl.rs +553 -0
  31. package/crates/mustard/src/runtime/builtins/mod.rs +25 -0
  32. package/crates/mustard/src/runtime/builtins/objects.rs +405 -0
  33. package/crates/mustard/src/runtime/builtins/primitives.rs +859 -0
  34. package/crates/mustard/src/runtime/builtins/promises.rs +335 -0
  35. package/crates/mustard/src/runtime/builtins/regexp.rs +356 -0
  36. package/crates/mustard/src/runtime/builtins/strings.rs +803 -0
  37. package/crates/mustard/src/runtime/builtins/support.rs +561 -0
  38. package/crates/mustard/src/runtime/bytecode.rs +123 -0
  39. package/crates/mustard/src/runtime/compiler/assignments.rs +690 -0
  40. package/crates/mustard/src/runtime/compiler/bindings.rs +92 -0
  41. package/crates/mustard/src/runtime/compiler/context.rs +46 -0
  42. package/crates/mustard/src/runtime/compiler/control.rs +342 -0
  43. package/crates/mustard/src/runtime/compiler/expressions.rs +372 -0
  44. package/crates/mustard/src/runtime/compiler/mod.rs +173 -0
  45. package/crates/mustard/src/runtime/compiler/statements.rs +459 -0
  46. package/crates/mustard/src/runtime/conversions/boundary.rs +293 -0
  47. package/crates/mustard/src/runtime/conversions/coercions.rs +217 -0
  48. package/crates/mustard/src/runtime/conversions/errors.rs +118 -0
  49. package/crates/mustard/src/runtime/conversions/mod.rs +14 -0
  50. package/crates/mustard/src/runtime/conversions/operators.rs +334 -0
  51. package/crates/mustard/src/runtime/env.rs +355 -0
  52. package/crates/mustard/src/runtime/exceptions.rs +377 -0
  53. package/crates/mustard/src/runtime/gc.rs +595 -0
  54. package/crates/mustard/src/runtime/mod.rs +318 -0
  55. package/crates/mustard/src/runtime/properties.rs +1762 -0
  56. package/crates/mustard/src/runtime/serialization.rs +127 -0
  57. package/crates/mustard/src/runtime/shared.rs +108 -0
  58. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +93 -0
  59. package/crates/mustard/src/runtime/state.rs +652 -0
  60. package/crates/mustard/src/runtime/tests/async_host.rs +104 -0
  61. package/crates/mustard/src/runtime/tests/collections.rs +50 -0
  62. package/crates/mustard/src/runtime/tests/diagnostics.rs +36 -0
  63. package/crates/mustard/src/runtime/tests/exceptions.rs +122 -0
  64. package/crates/mustard/src/runtime/tests/execution.rs +553 -0
  65. package/crates/mustard/src/runtime/tests/gc.rs +533 -0
  66. package/crates/mustard/src/runtime/tests/mod.rs +56 -0
  67. package/crates/mustard/src/runtime/tests/serialization.rs +170 -0
  68. package/crates/mustard/src/runtime/validation/bytecode.rs +484 -0
  69. package/crates/mustard/src/runtime/validation/mod.rs +14 -0
  70. package/crates/mustard/src/runtime/validation/policy.rs +94 -0
  71. package/crates/mustard/src/runtime/validation/snapshot.rs +406 -0
  72. package/crates/mustard/src/runtime/validation/walk.rs +206 -0
  73. package/crates/mustard/src/runtime/vm.rs +1016 -0
  74. package/crates/mustard/src/span.rs +22 -0
  75. package/crates/mustard/src/structured.rs +107 -0
  76. package/crates/mustard-bridge/Cargo.toml +17 -0
  77. package/crates/mustard-bridge/src/codec.rs +46 -0
  78. package/crates/mustard-bridge/src/dto.rs +99 -0
  79. package/crates/mustard-bridge/src/lib.rs +12 -0
  80. package/crates/mustard-bridge/src/operations.rs +142 -0
  81. package/crates/mustard-node/Cargo.toml +24 -0
  82. package/crates/mustard-node/build.rs +3 -0
  83. package/crates/mustard-node/src/lib.rs +236 -0
  84. package/crates/mustard-sidecar/Cargo.toml +21 -0
  85. package/crates/mustard-sidecar/src/lib.rs +134 -0
  86. package/crates/mustard-sidecar/src/main.rs +36 -0
  87. package/dist/index.js +20 -0
  88. package/dist/install.js +117 -0
  89. package/dist/lib/cancellation.js +124 -0
  90. package/dist/lib/errors.js +46 -0
  91. package/dist/lib/executor.js +555 -0
  92. package/dist/lib/policy.js +292 -0
  93. package/dist/lib/progress.js +356 -0
  94. package/dist/lib/runtime.js +109 -0
  95. package/dist/lib/structured.js +286 -0
  96. package/dist/native-loader.js +227 -0
  97. package/index.d.ts +23 -0
  98. package/mustard.d.ts +220 -0
  99. package/package.json +97 -0
@@ -0,0 +1,1205 @@
1
+ use std::cmp::Ordering;
2
+
3
+ use super::*;
4
+
5
+ impl Runtime {
6
+ pub(crate) fn call_array_ctor(&mut self, args: &[Value]) -> MustardResult<Value> {
7
+ if args.len() == 1
8
+ && let Value::Number(length) = args[0]
9
+ {
10
+ if !length.is_finite()
11
+ || length < 0.0
12
+ || length.fract() != 0.0
13
+ || length > u32::MAX as f64
14
+ {
15
+ return Err(MustardError::runtime("RangeError: Invalid array length"));
16
+ }
17
+ let length = length as usize;
18
+ return Ok(Value::Array(
19
+ self.insert_sparse_array(vec![None; length], IndexMap::new())?,
20
+ ));
21
+ }
22
+ self.call_array_of(args)
23
+ }
24
+
25
+ fn array_slots(&self, array: ArrayKey) -> MustardResult<Vec<Option<Value>>> {
26
+ Ok(self
27
+ .arrays
28
+ .get(array)
29
+ .ok_or_else(|| MustardError::runtime("array missing"))?
30
+ .elements
31
+ .clone())
32
+ }
33
+
34
+ fn array_receiver(&self, value: Value, method: &str) -> MustardResult<ArrayKey> {
35
+ match value {
36
+ Value::Array(key) => Ok(key),
37
+ _ => Err(MustardError::runtime(format!(
38
+ "TypeError: Array.prototype.{method} called on incompatible receiver",
39
+ ))),
40
+ }
41
+ }
42
+
43
+ pub(crate) fn set_array_length(&mut self, array: ArrayKey, value: Value) -> MustardResult<()> {
44
+ let requested = self.to_number(value)?;
45
+ if !requested.is_finite()
46
+ || requested < 0.0
47
+ || (requested.fract() != 0.0 && requested != 0.0)
48
+ || requested > u32::MAX as f64
49
+ {
50
+ return Err(MustardError::runtime("RangeError: Invalid array length"));
51
+ }
52
+
53
+ let new_length = if requested == 0.0 {
54
+ 0
55
+ } else {
56
+ requested as usize
57
+ };
58
+
59
+ let array_ref = self
60
+ .arrays
61
+ .get_mut(array)
62
+ .ok_or_else(|| MustardError::runtime("array missing"))?;
63
+ array_ref.elements.resize(new_length, None);
64
+ array_ref.properties.shift_remove("length");
65
+ self.refresh_array_accounting(array)?;
66
+ Ok(())
67
+ }
68
+
69
+ pub(crate) fn call_array_of(&mut self, args: &[Value]) -> MustardResult<Value> {
70
+ Ok(Value::Array(
71
+ self.insert_array(args.to_vec(), IndexMap::new())?,
72
+ ))
73
+ }
74
+
75
+ pub(crate) fn call_array_from(&mut self, args: &[Value]) -> MustardResult<Value> {
76
+ let iterable = args.first().cloned().unwrap_or(Value::Undefined);
77
+ let map_fn = match args.get(1).cloned() {
78
+ Some(Value::Undefined) | None => None,
79
+ Some(value) if is_callable(&value) => Some(value),
80
+ Some(_) => {
81
+ return Err(MustardError::runtime(
82
+ "TypeError: Array.from expects a callable map function",
83
+ ));
84
+ }
85
+ };
86
+ let this_arg = args.get(2).cloned().unwrap_or(Value::Undefined);
87
+ let iterator = self.create_iterator(iterable.clone())?;
88
+ let result = self.insert_array(Vec::new(), IndexMap::new())?;
89
+ let mut roots = vec![iterable, iterator.clone(), Value::Array(result)];
90
+ if let Some(map_fn) = &map_fn {
91
+ roots.push(map_fn.clone());
92
+ roots.push(this_arg.clone());
93
+ }
94
+ self.with_temporary_roots(&roots, |runtime| {
95
+ let mut index = 0usize;
96
+ loop {
97
+ runtime.charge_native_helper_work(1)?;
98
+ let (value, done) = runtime.iterator_next(iterator.clone())?;
99
+ if done {
100
+ break;
101
+ }
102
+ let mapped = if let Some(map_fn) = &map_fn {
103
+ runtime.with_temporary_roots(
104
+ &[
105
+ iterator.clone(),
106
+ Value::Array(result),
107
+ map_fn.clone(),
108
+ this_arg.clone(),
109
+ value.clone(),
110
+ ],
111
+ |runtime| {
112
+ runtime.call_callback(
113
+ map_fn.clone(),
114
+ this_arg.clone(),
115
+ &[value.clone(), Value::Number(index as f64)],
116
+ CallbackCallOptions {
117
+ non_callable_message:
118
+ "TypeError: Array.from expects a callable map function",
119
+ host_suspension_message:
120
+ "TypeError: Array.from mapping does not support host suspensions",
121
+ unsettled_message:
122
+ "synchronous Array.from mapping did not settle",
123
+ allow_host_suspension: false,
124
+ allow_pending_promise_result: true,
125
+ },
126
+ )
127
+ },
128
+ )?
129
+ } else {
130
+ value
131
+ };
132
+ runtime
133
+ .arrays
134
+ .get_mut(result)
135
+ .ok_or_else(|| MustardError::runtime("array missing"))?
136
+ .elements
137
+ .push(Some(mapped));
138
+ runtime.refresh_array_accounting(result)?;
139
+ index += 1;
140
+ }
141
+ Ok(Value::Array(result))
142
+ })
143
+ }
144
+
145
+ pub(crate) fn call_array_push(
146
+ &mut self,
147
+ this_value: Value,
148
+ args: &[Value],
149
+ ) -> MustardResult<Value> {
150
+ let array = self.array_receiver(this_value, "push")?;
151
+ {
152
+ let elements = &mut self
153
+ .arrays
154
+ .get_mut(array)
155
+ .ok_or_else(|| MustardError::runtime("array missing"))?
156
+ .elements;
157
+ elements.extend(args.iter().cloned().map(Some));
158
+ }
159
+ self.refresh_array_accounting(array)?;
160
+ let length = self
161
+ .arrays
162
+ .get(array)
163
+ .ok_or_else(|| MustardError::runtime("array missing"))?
164
+ .elements
165
+ .len();
166
+ Ok(Value::Number(length as f64))
167
+ }
168
+
169
+ pub(crate) fn call_array_pop(&mut self, this_value: Value) -> MustardResult<Value> {
170
+ let array = self.array_receiver(this_value, "pop")?;
171
+ let value = self
172
+ .arrays
173
+ .get_mut(array)
174
+ .ok_or_else(|| MustardError::runtime("array missing"))?
175
+ .elements
176
+ .pop()
177
+ .flatten()
178
+ .unwrap_or(Value::Undefined);
179
+ self.refresh_array_accounting(array)?;
180
+ Ok(value)
181
+ }
182
+
183
+ pub(crate) fn call_array_slice(
184
+ &mut self,
185
+ this_value: Value,
186
+ args: &[Value],
187
+ ) -> MustardResult<Value> {
188
+ let array = self.array_receiver(this_value, "slice")?;
189
+ let elements = self.array_slots(array)?;
190
+ let start = normalize_relative_bound(
191
+ self.to_integer(args.first().cloned().unwrap_or(Value::Number(0.0)))?,
192
+ elements.len(),
193
+ );
194
+ let end = normalize_relative_bound(
195
+ match args.get(1) {
196
+ Some(value) => self.to_integer(value.clone())?,
197
+ None => elements.len() as i64,
198
+ },
199
+ elements.len(),
200
+ );
201
+ let end = end.max(start);
202
+ Ok(Value::Array(self.insert_sparse_array(
203
+ elements[start..end].to_vec(),
204
+ IndexMap::new(),
205
+ )?))
206
+ }
207
+
208
+ pub(crate) fn call_array_splice(
209
+ &mut self,
210
+ this_value: Value,
211
+ args: &[Value],
212
+ ) -> MustardResult<Value> {
213
+ let array = self.array_receiver(this_value, "splice")?;
214
+ if args.is_empty() {
215
+ return Ok(Value::Array(
216
+ self.insert_array(Vec::new(), IndexMap::new())?,
217
+ ));
218
+ }
219
+
220
+ let length = self
221
+ .arrays
222
+ .get(array)
223
+ .ok_or_else(|| MustardError::runtime("array missing"))?
224
+ .elements
225
+ .len();
226
+ let start = normalize_relative_bound(
227
+ self.to_integer(args.first().cloned().unwrap_or(Value::Undefined))?,
228
+ length,
229
+ );
230
+ let delete_count = if args.len() == 1 {
231
+ length - start
232
+ } else {
233
+ clamp_index(self.to_integer(args[1].clone())?, length - start)
234
+ };
235
+ let removed = {
236
+ let array_ref = self
237
+ .arrays
238
+ .get_mut(array)
239
+ .ok_or_else(|| MustardError::runtime("array missing"))?;
240
+ array_ref
241
+ .elements
242
+ .splice(
243
+ start..start + delete_count,
244
+ args[2..].iter().cloned().map(Some),
245
+ )
246
+ .collect::<Vec<Option<Value>>>()
247
+ };
248
+ self.refresh_array_accounting(array)?;
249
+ Ok(Value::Array(
250
+ self.insert_sparse_array(removed, IndexMap::new())?,
251
+ ))
252
+ }
253
+
254
+ pub(crate) fn call_array_concat(
255
+ &mut self,
256
+ this_value: Value,
257
+ args: &[Value],
258
+ ) -> MustardResult<Value> {
259
+ let array = self.array_receiver(this_value, "concat")?;
260
+ let mut elements = self.array_slots(array)?;
261
+ for value in args {
262
+ match value {
263
+ Value::Array(other) => {
264
+ let other = self
265
+ .arrays
266
+ .get(*other)
267
+ .ok_or_else(|| MustardError::runtime("array missing"))?;
268
+ elements.extend(other.elements.iter().cloned());
269
+ }
270
+ other => elements.push(Some(other.clone())),
271
+ }
272
+ }
273
+ Ok(Value::Array(
274
+ self.insert_sparse_array(elements, IndexMap::new())?,
275
+ ))
276
+ }
277
+
278
+ pub(crate) fn call_array_at(&self, this_value: Value, args: &[Value]) -> MustardResult<Value> {
279
+ let array = self.array_receiver(this_value, "at")?;
280
+ let elements = &self
281
+ .arrays
282
+ .get(array)
283
+ .ok_or_else(|| MustardError::runtime("array missing"))?
284
+ .elements;
285
+ let index = self.to_integer(args.first().cloned().unwrap_or(Value::Undefined))?;
286
+ let index = if index < 0 {
287
+ elements.len() as i64 + index
288
+ } else {
289
+ index
290
+ };
291
+ if index < 0 || index >= elements.len() as i64 {
292
+ Ok(Value::Undefined)
293
+ } else {
294
+ Ok(elements[index as usize].clone().unwrap_or(Value::Undefined))
295
+ }
296
+ }
297
+
298
+ fn normalized_flat_depth(&self, args: &[Value]) -> MustardResult<i64> {
299
+ match args.first() {
300
+ None | Some(Value::Undefined) => Ok(1),
301
+ Some(value) => Ok(self.to_integer(value.clone())?.max(0)),
302
+ }
303
+ }
304
+
305
+ fn collect_flattened_value(
306
+ &self,
307
+ flattened: &mut Vec<Value>,
308
+ value: Value,
309
+ depth: i64,
310
+ ) -> MustardResult<()> {
311
+ if depth > 0
312
+ && let Value::Array(array) = value
313
+ {
314
+ let elements = self.array_slots(array)?;
315
+ for element in elements.into_iter().flatten() {
316
+ self.collect_flattened_value(flattened, element, depth - 1)?;
317
+ }
318
+ return Ok(());
319
+ }
320
+ flattened.push(value);
321
+ Ok(())
322
+ }
323
+
324
+ pub(crate) fn call_array_flat(
325
+ &mut self,
326
+ this_value: Value,
327
+ args: &[Value],
328
+ ) -> MustardResult<Value> {
329
+ let array = self.array_receiver(this_value, "flat")?;
330
+ let depth = self.normalized_flat_depth(args)?;
331
+ let elements = self.array_slots(array)?;
332
+ let mut flattened = Vec::new();
333
+ for element in elements.into_iter().flatten() {
334
+ self.collect_flattened_value(&mut flattened, element, depth)?;
335
+ }
336
+ Ok(Value::Array(self.insert_array(flattened, IndexMap::new())?))
337
+ }
338
+
339
+ pub(crate) fn call_array_join(
340
+ &mut self,
341
+ this_value: Value,
342
+ args: &[Value],
343
+ ) -> MustardResult<Value> {
344
+ let array = self.array_receiver(this_value, "join")?;
345
+ let separator = match args.first() {
346
+ Some(value) => self.to_string(value.clone())?,
347
+ None => ",".to_string(),
348
+ };
349
+ let elements = self
350
+ .arrays
351
+ .get(array)
352
+ .ok_or_else(|| MustardError::runtime("array missing"))?
353
+ .elements
354
+ .clone();
355
+ let mut parts = Vec::with_capacity(elements.len());
356
+ for value in &elements {
357
+ self.charge_native_helper_work(1)?;
358
+ parts.push(match value {
359
+ None | Some(Value::Undefined) | Some(Value::Null) => String::new(),
360
+ Some(other) => self.to_string(other.clone())?,
361
+ });
362
+ }
363
+ Ok(Value::String(parts.join(&separator)))
364
+ }
365
+
366
+ pub(crate) fn call_array_includes(
367
+ &self,
368
+ this_value: Value,
369
+ args: &[Value],
370
+ ) -> MustardResult<Value> {
371
+ let array = self.array_receiver(this_value, "includes")?;
372
+ let search = args.first().cloned().unwrap_or(Value::Undefined);
373
+ let elements = &self
374
+ .arrays
375
+ .get(array)
376
+ .ok_or_else(|| MustardError::runtime("array missing"))?
377
+ .elements;
378
+ let start = normalize_search_index(
379
+ self.to_integer(args.get(1).cloned().unwrap_or(Value::Number(0.0)))?,
380
+ elements.len(),
381
+ );
382
+ Ok(Value::Bool(elements.iter().skip(start).any(|value| {
383
+ same_value_zero(&value.clone().unwrap_or(Value::Undefined), &search)
384
+ })))
385
+ }
386
+
387
+ pub(crate) fn call_array_index_of(
388
+ &self,
389
+ this_value: Value,
390
+ args: &[Value],
391
+ ) -> MustardResult<Value> {
392
+ let array = self.array_receiver(this_value, "indexOf")?;
393
+ let search = args.first().cloned().unwrap_or(Value::Undefined);
394
+ let elements = &self
395
+ .arrays
396
+ .get(array)
397
+ .ok_or_else(|| MustardError::runtime("array missing"))?
398
+ .elements;
399
+ let start = normalize_search_index(
400
+ self.to_integer(args.get(1).cloned().unwrap_or(Value::Number(0.0)))?,
401
+ elements.len(),
402
+ );
403
+ let index = elements
404
+ .iter()
405
+ .enumerate()
406
+ .skip(start)
407
+ .find(|(_, value)| {
408
+ value
409
+ .as_ref()
410
+ .is_some_and(|value| strict_equal(value, &search))
411
+ })
412
+ .map(|(index, _)| index as f64)
413
+ .unwrap_or(-1.0);
414
+ Ok(Value::Number(index))
415
+ }
416
+
417
+ pub(crate) fn call_array_last_index_of(
418
+ &self,
419
+ this_value: Value,
420
+ args: &[Value],
421
+ ) -> MustardResult<Value> {
422
+ let array = self.array_receiver(this_value, "lastIndexOf")?;
423
+ let search = args.first().cloned().unwrap_or(Value::Undefined);
424
+ let elements = &self
425
+ .arrays
426
+ .get(array)
427
+ .ok_or_else(|| MustardError::runtime("array missing"))?
428
+ .elements;
429
+ if elements.is_empty() {
430
+ return Ok(Value::Number(-1.0));
431
+ }
432
+ let start = match args.get(1) {
433
+ Some(value) => {
434
+ let index = self.to_integer(value.clone())?;
435
+ if index < 0 {
436
+ let adjusted = elements.len() as i64 + index;
437
+ if adjusted < 0 {
438
+ return Ok(Value::Number(-1.0));
439
+ }
440
+ adjusted as usize
441
+ } else {
442
+ (index as usize).min(elements.len() - 1)
443
+ }
444
+ }
445
+ None => elements.len() - 1,
446
+ };
447
+ let index = elements
448
+ .iter()
449
+ .enumerate()
450
+ .take(start + 1)
451
+ .rev()
452
+ .find(|(_, value)| {
453
+ value
454
+ .as_ref()
455
+ .is_some_and(|value| strict_equal(value, &search))
456
+ })
457
+ .map(|(index, _)| index as f64)
458
+ .unwrap_or(-1.0);
459
+ Ok(Value::Number(index))
460
+ }
461
+
462
+ pub(crate) fn call_array_reverse(&mut self, this_value: Value) -> MustardResult<Value> {
463
+ let array = self.array_receiver(this_value, "reverse")?;
464
+ self.arrays
465
+ .get_mut(array)
466
+ .ok_or_else(|| MustardError::runtime("array missing"))?
467
+ .elements
468
+ .reverse();
469
+ self.refresh_array_accounting(array)?;
470
+ Ok(Value::Array(array))
471
+ }
472
+
473
+ pub(crate) fn call_array_fill(
474
+ &mut self,
475
+ this_value: Value,
476
+ args: &[Value],
477
+ ) -> MustardResult<Value> {
478
+ let array = self.array_receiver(this_value, "fill")?;
479
+ let value = args.first().cloned().unwrap_or(Value::Undefined);
480
+ let length = self
481
+ .arrays
482
+ .get(array)
483
+ .ok_or_else(|| MustardError::runtime("array missing"))?
484
+ .elements
485
+ .len();
486
+ let start = normalize_relative_bound(
487
+ self.to_integer(args.get(1).cloned().unwrap_or(Value::Number(0.0)))?,
488
+ length,
489
+ );
490
+ let end = normalize_relative_bound(
491
+ match args.get(2) {
492
+ Some(value) => self.to_integer(value.clone())?,
493
+ None => length as i64,
494
+ },
495
+ length,
496
+ );
497
+ {
498
+ let elements = &mut self
499
+ .arrays
500
+ .get_mut(array)
501
+ .ok_or_else(|| MustardError::runtime("array missing"))?
502
+ .elements;
503
+ for slot in elements.iter_mut().take(end).skip(start) {
504
+ *slot = Some(value.clone());
505
+ }
506
+ }
507
+ self.refresh_array_accounting(array)?;
508
+ Ok(Value::Array(array))
509
+ }
510
+
511
+ fn sort_compare(
512
+ &mut self,
513
+ comparator: Option<Value>,
514
+ left: Value,
515
+ right: Value,
516
+ ) -> MustardResult<Ordering> {
517
+ self.charge_native_helper_work(1)?;
518
+ match comparator {
519
+ Some(comparator) => {
520
+ let result = self.with_temporary_roots(
521
+ &[comparator.clone(), left.clone(), right.clone()],
522
+ |runtime| {
523
+ runtime.call_callback(
524
+ comparator.clone(),
525
+ Value::Undefined,
526
+ &[left.clone(), right.clone()],
527
+ CallbackCallOptions {
528
+ non_callable_message:
529
+ "TypeError: Array.prototype.sort expects a callable comparator",
530
+ host_suspension_message:
531
+ "TypeError: Array.prototype.sort does not support host suspensions",
532
+ unsettled_message:
533
+ "synchronous Array.prototype.sort comparator did not settle",
534
+ allow_host_suspension: false,
535
+ allow_pending_promise_result: false,
536
+ },
537
+ )
538
+ },
539
+ )?;
540
+ let ordering = self.to_number(result)?;
541
+ Ok(if ordering.is_nan() || ordering == 0.0 {
542
+ Ordering::Equal
543
+ } else if ordering < 0.0 {
544
+ Ordering::Less
545
+ } else {
546
+ Ordering::Greater
547
+ })
548
+ }
549
+ None => Ok(self.to_string(left)?.cmp(&self.to_string(right)?)),
550
+ }
551
+ }
552
+
553
+ pub(crate) fn call_array_sort(
554
+ &mut self,
555
+ this_value: Value,
556
+ args: &[Value],
557
+ ) -> MustardResult<Value> {
558
+ let array = self.array_receiver(this_value, "sort")?;
559
+ let comparator = match args.first().cloned() {
560
+ Some(Value::Undefined) | None => None,
561
+ Some(value) if is_callable(&value) => Some(value),
562
+ Some(_) => {
563
+ return Err(MustardError::runtime(
564
+ "TypeError: Array.prototype.sort expects a callable comparator",
565
+ ));
566
+ }
567
+ };
568
+ let mut roots = vec![Value::Array(array)];
569
+ if let Some(comparator) = &comparator {
570
+ roots.push(comparator.clone());
571
+ }
572
+ self.with_temporary_roots(&roots, |runtime| {
573
+ let elements = runtime.array_slots(array)?;
574
+ let mut present = elements.into_iter().flatten().collect::<Vec<_>>();
575
+ for index in 1..present.len() {
576
+ runtime.charge_native_helper_work(1)?;
577
+ let current = present[index].clone();
578
+ let mut position = index;
579
+ while position > 0
580
+ && runtime.sort_compare(
581
+ comparator.clone(),
582
+ current.clone(),
583
+ present[position - 1].clone(),
584
+ )? == Ordering::Less
585
+ {
586
+ present[position] = present[position - 1].clone();
587
+ position -= 1;
588
+ }
589
+ present[position] = current;
590
+ }
591
+ let holes = runtime.array_length(array)?.saturating_sub(present.len());
592
+ runtime
593
+ .arrays
594
+ .get_mut(array)
595
+ .ok_or_else(|| MustardError::runtime("array missing"))?
596
+ .elements = present
597
+ .into_iter()
598
+ .map(Some)
599
+ .chain(std::iter::repeat_with(|| None).take(holes))
600
+ .collect();
601
+ runtime.refresh_array_accounting(array)?;
602
+ Ok(Value::Array(array))
603
+ })
604
+ }
605
+
606
+ pub(crate) fn call_array_values(&mut self, this_value: Value) -> MustardResult<Value> {
607
+ let array = self.array_receiver(this_value, "values")?;
608
+ Ok(Value::Iterator(self.insert_iterator(
609
+ IteratorState::Array(ArrayIteratorState {
610
+ array,
611
+ next_index: 0,
612
+ }),
613
+ )?))
614
+ }
615
+
616
+ pub(crate) fn call_array_keys(&mut self, this_value: Value) -> MustardResult<Value> {
617
+ let array = self.array_receiver(this_value, "keys")?;
618
+ Ok(Value::Iterator(self.insert_iterator(
619
+ IteratorState::ArrayKeys(ArrayIteratorState {
620
+ array,
621
+ next_index: 0,
622
+ }),
623
+ )?))
624
+ }
625
+
626
+ pub(crate) fn call_array_entries(&mut self, this_value: Value) -> MustardResult<Value> {
627
+ let array = self.array_receiver(this_value, "entries")?;
628
+ Ok(Value::Iterator(self.insert_iterator(
629
+ IteratorState::ArrayEntries(ArrayIteratorState {
630
+ array,
631
+ next_index: 0,
632
+ }),
633
+ )?))
634
+ }
635
+
636
+ fn array_callback(&self, args: &[Value], method: &str) -> MustardResult<(Value, Value)> {
637
+ let callback = args.first().cloned().unwrap_or(Value::Undefined);
638
+ if !is_callable(&callback) {
639
+ return Err(MustardError::runtime(format!(
640
+ "TypeError: Array.prototype.{method} expects a callable callback",
641
+ )));
642
+ }
643
+ let this_arg = args.get(1).cloned().unwrap_or(Value::Undefined);
644
+ Ok((callback, this_arg))
645
+ }
646
+
647
+ fn call_array_callback(
648
+ &mut self,
649
+ callback: Value,
650
+ this_arg: Value,
651
+ args: &[Value],
652
+ ) -> MustardResult<Value> {
653
+ self.call_callback(
654
+ callback,
655
+ this_arg,
656
+ args,
657
+ CallbackCallOptions {
658
+ non_callable_message: "TypeError: array callback is not callable",
659
+ host_suspension_message:
660
+ "TypeError: array callback helpers do not support synchronous host suspensions",
661
+ unsettled_message: "synchronous array callback did not settle",
662
+ allow_host_suspension: true,
663
+ allow_pending_promise_result: true,
664
+ },
665
+ )
666
+ }
667
+
668
+ fn array_callback_value(&self, array: ArrayKey, index: usize) -> MustardResult<Value> {
669
+ self.array_value_at(array, index)
670
+ }
671
+
672
+ fn append_flat_map_value(&mut self, result: ArrayKey, value: Value) -> MustardResult<()> {
673
+ match value {
674
+ Value::Array(array) => {
675
+ let elements = self.array_slots(array)?;
676
+ self.arrays
677
+ .get_mut(result)
678
+ .ok_or_else(|| MustardError::runtime("array missing"))?
679
+ .elements
680
+ .extend(elements.into_iter().flatten().map(Some));
681
+ }
682
+ other => {
683
+ self.arrays
684
+ .get_mut(result)
685
+ .ok_or_else(|| MustardError::runtime("array missing"))?
686
+ .elements
687
+ .push(Some(other));
688
+ }
689
+ }
690
+ self.refresh_array_accounting(result)?;
691
+ Ok(())
692
+ }
693
+
694
+ pub(crate) fn call_array_for_each(
695
+ &mut self,
696
+ this_value: Value,
697
+ args: &[Value],
698
+ ) -> MustardResult<Value> {
699
+ let array = self.array_receiver(this_value, "forEach")?;
700
+ let (callback, this_arg) = self.array_callback(args, "forEach")?;
701
+ let length = self
702
+ .arrays
703
+ .get(array)
704
+ .ok_or_else(|| MustardError::runtime("array missing"))?
705
+ .elements
706
+ .len();
707
+ for index in 0..length {
708
+ self.charge_native_helper_work(1)?;
709
+ if !self.array_has_index(array, index)? {
710
+ continue;
711
+ }
712
+ let value = self.array_callback_value(array, index)?;
713
+ self.with_temporary_roots(
714
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
715
+ |runtime| {
716
+ runtime.call_array_callback(
717
+ callback.clone(),
718
+ this_arg.clone(),
719
+ &[value, Value::Number(index as f64), Value::Array(array)],
720
+ )?;
721
+ Ok(())
722
+ },
723
+ )?;
724
+ }
725
+ Ok(Value::Undefined)
726
+ }
727
+
728
+ pub(crate) fn call_array_map(
729
+ &mut self,
730
+ this_value: Value,
731
+ args: &[Value],
732
+ ) -> MustardResult<Value> {
733
+ let array = self.array_receiver(this_value, "map")?;
734
+ let (callback, this_arg) = self.array_callback(args, "map")?;
735
+ let length = self
736
+ .arrays
737
+ .get(array)
738
+ .ok_or_else(|| MustardError::runtime("array missing"))?
739
+ .elements
740
+ .len();
741
+ let result = self.insert_sparse_array(vec![None; length], IndexMap::new())?;
742
+ self.with_temporary_roots(
743
+ &[
744
+ Value::Array(array),
745
+ callback.clone(),
746
+ this_arg.clone(),
747
+ Value::Array(result),
748
+ ],
749
+ |runtime| {
750
+ for index in 0..length {
751
+ runtime.charge_native_helper_work(1)?;
752
+ if !runtime.array_has_index(array, index)? {
753
+ continue;
754
+ }
755
+ let value = runtime.array_callback_value(array, index)?;
756
+ let mapped = runtime.call_array_callback(
757
+ callback.clone(),
758
+ this_arg.clone(),
759
+ &[value, Value::Number(index as f64), Value::Array(array)],
760
+ )?;
761
+ runtime
762
+ .arrays
763
+ .get_mut(result)
764
+ .ok_or_else(|| MustardError::runtime("array missing"))?
765
+ .elements[index] = Some(mapped);
766
+ runtime.refresh_array_accounting(result)?;
767
+ }
768
+ Ok(Value::Array(result))
769
+ },
770
+ )
771
+ }
772
+
773
+ pub(crate) fn call_array_filter(
774
+ &mut self,
775
+ this_value: Value,
776
+ args: &[Value],
777
+ ) -> MustardResult<Value> {
778
+ let array = self.array_receiver(this_value, "filter")?;
779
+ let (callback, this_arg) = self.array_callback(args, "filter")?;
780
+ let length = self
781
+ .arrays
782
+ .get(array)
783
+ .ok_or_else(|| MustardError::runtime("array missing"))?
784
+ .elements
785
+ .len();
786
+ let result = self.insert_array(Vec::new(), IndexMap::new())?;
787
+ self.with_temporary_roots(
788
+ &[
789
+ Value::Array(array),
790
+ callback.clone(),
791
+ this_arg.clone(),
792
+ Value::Array(result),
793
+ ],
794
+ |runtime| {
795
+ for index in 0..length {
796
+ runtime.charge_native_helper_work(1)?;
797
+ if !runtime.array_has_index(array, index)? {
798
+ continue;
799
+ }
800
+ let value = runtime.array_callback_value(array, index)?;
801
+ let keep = runtime.call_array_callback(
802
+ callback.clone(),
803
+ this_arg.clone(),
804
+ &[
805
+ value.clone(),
806
+ Value::Number(index as f64),
807
+ Value::Array(array),
808
+ ],
809
+ )?;
810
+ if is_truthy(&keep) {
811
+ runtime
812
+ .arrays
813
+ .get_mut(result)
814
+ .ok_or_else(|| MustardError::runtime("array missing"))?
815
+ .elements
816
+ .push(Some(value));
817
+ runtime.refresh_array_accounting(result)?;
818
+ }
819
+ }
820
+ Ok(Value::Array(result))
821
+ },
822
+ )
823
+ }
824
+
825
+ pub(crate) fn call_array_find(
826
+ &mut self,
827
+ this_value: Value,
828
+ args: &[Value],
829
+ ) -> MustardResult<Value> {
830
+ let array = self.array_receiver(this_value, "find")?;
831
+ let (callback, this_arg) = self.array_callback(args, "find")?;
832
+ let length = self
833
+ .arrays
834
+ .get(array)
835
+ .ok_or_else(|| MustardError::runtime("array missing"))?
836
+ .elements
837
+ .len();
838
+ for index in 0..length {
839
+ self.charge_native_helper_work(1)?;
840
+ let value = self.array_callback_value(array, index)?;
841
+ let found = self.with_temporary_roots(
842
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
843
+ |runtime| {
844
+ runtime.call_array_callback(
845
+ callback.clone(),
846
+ this_arg.clone(),
847
+ &[
848
+ value.clone(),
849
+ Value::Number(index as f64),
850
+ Value::Array(array),
851
+ ],
852
+ )
853
+ },
854
+ )?;
855
+ if is_truthy(&found) {
856
+ return Ok(value);
857
+ }
858
+ }
859
+ Ok(Value::Undefined)
860
+ }
861
+
862
+ pub(crate) fn call_array_find_index(
863
+ &mut self,
864
+ this_value: Value,
865
+ args: &[Value],
866
+ ) -> MustardResult<Value> {
867
+ let array = self.array_receiver(this_value, "findIndex")?;
868
+ let (callback, this_arg) = self.array_callback(args, "findIndex")?;
869
+ let length = self
870
+ .arrays
871
+ .get(array)
872
+ .ok_or_else(|| MustardError::runtime("array missing"))?
873
+ .elements
874
+ .len();
875
+ for index in 0..length {
876
+ self.charge_native_helper_work(1)?;
877
+ let value = self.array_callback_value(array, index)?;
878
+ let found = self.with_temporary_roots(
879
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
880
+ |runtime| {
881
+ runtime.call_array_callback(
882
+ callback.clone(),
883
+ this_arg.clone(),
884
+ &[value, Value::Number(index as f64), Value::Array(array)],
885
+ )
886
+ },
887
+ )?;
888
+ if is_truthy(&found) {
889
+ return Ok(Value::Number(index as f64));
890
+ }
891
+ }
892
+ Ok(Value::Number(-1.0))
893
+ }
894
+
895
+ pub(crate) fn call_array_some(
896
+ &mut self,
897
+ this_value: Value,
898
+ args: &[Value],
899
+ ) -> MustardResult<Value> {
900
+ let array = self.array_receiver(this_value, "some")?;
901
+ let (callback, this_arg) = self.array_callback(args, "some")?;
902
+ let length = self
903
+ .arrays
904
+ .get(array)
905
+ .ok_or_else(|| MustardError::runtime("array missing"))?
906
+ .elements
907
+ .len();
908
+ for index in 0..length {
909
+ self.charge_native_helper_work(1)?;
910
+ if !self.array_has_index(array, index)? {
911
+ continue;
912
+ }
913
+ let value = self.array_callback_value(array, index)?;
914
+ let found = self.with_temporary_roots(
915
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
916
+ |runtime| {
917
+ runtime.call_array_callback(
918
+ callback.clone(),
919
+ this_arg.clone(),
920
+ &[value, Value::Number(index as f64), Value::Array(array)],
921
+ )
922
+ },
923
+ )?;
924
+ if is_truthy(&found) {
925
+ return Ok(Value::Bool(true));
926
+ }
927
+ }
928
+ Ok(Value::Bool(false))
929
+ }
930
+
931
+ pub(crate) fn call_array_every(
932
+ &mut self,
933
+ this_value: Value,
934
+ args: &[Value],
935
+ ) -> MustardResult<Value> {
936
+ let array = self.array_receiver(this_value, "every")?;
937
+ let (callback, this_arg) = self.array_callback(args, "every")?;
938
+ let length = self
939
+ .arrays
940
+ .get(array)
941
+ .ok_or_else(|| MustardError::runtime("array missing"))?
942
+ .elements
943
+ .len();
944
+ for index in 0..length {
945
+ self.charge_native_helper_work(1)?;
946
+ if !self.array_has_index(array, index)? {
947
+ continue;
948
+ }
949
+ let value = self.array_callback_value(array, index)?;
950
+ let found = self.with_temporary_roots(
951
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
952
+ |runtime| {
953
+ runtime.call_array_callback(
954
+ callback.clone(),
955
+ this_arg.clone(),
956
+ &[value, Value::Number(index as f64), Value::Array(array)],
957
+ )
958
+ },
959
+ )?;
960
+ if !is_truthy(&found) {
961
+ return Ok(Value::Bool(false));
962
+ }
963
+ }
964
+ Ok(Value::Bool(true))
965
+ }
966
+
967
+ pub(crate) fn call_array_flat_map(
968
+ &mut self,
969
+ this_value: Value,
970
+ args: &[Value],
971
+ ) -> MustardResult<Value> {
972
+ let array = self.array_receiver(this_value, "flatMap")?;
973
+ let (callback, this_arg) = self.array_callback(args, "flatMap")?;
974
+ let length = self
975
+ .arrays
976
+ .get(array)
977
+ .ok_or_else(|| MustardError::runtime("array missing"))?
978
+ .elements
979
+ .len();
980
+ let result = self.insert_array(Vec::new(), IndexMap::new())?;
981
+ self.with_temporary_roots(
982
+ &[
983
+ Value::Array(array),
984
+ callback.clone(),
985
+ this_arg.clone(),
986
+ Value::Array(result),
987
+ ],
988
+ |runtime| {
989
+ for index in 0..length {
990
+ if !runtime.array_has_index(array, index)? {
991
+ continue;
992
+ }
993
+ let value = runtime.array_callback_value(array, index)?;
994
+ let mapped = runtime.call_array_callback(
995
+ callback.clone(),
996
+ this_arg.clone(),
997
+ &[value, Value::Number(index as f64), Value::Array(array)],
998
+ )?;
999
+ runtime.append_flat_map_value(result, mapped)?;
1000
+ }
1001
+ Ok(Value::Array(result))
1002
+ },
1003
+ )
1004
+ }
1005
+
1006
+ pub(crate) fn call_array_reduce(
1007
+ &mut self,
1008
+ this_value: Value,
1009
+ args: &[Value],
1010
+ ) -> MustardResult<Value> {
1011
+ let array = self.array_receiver(this_value, "reduce")?;
1012
+ let callback = args.first().cloned().unwrap_or(Value::Undefined);
1013
+ if !is_callable(&callback) {
1014
+ return Err(MustardError::runtime(
1015
+ "TypeError: Array.prototype.reduce expects a callable callback",
1016
+ ));
1017
+ }
1018
+ let this_arg = Value::Undefined;
1019
+ let length = self
1020
+ .arrays
1021
+ .get(array)
1022
+ .ok_or_else(|| MustardError::runtime("array missing"))?
1023
+ .elements
1024
+ .len();
1025
+ let (mut accumulator, start_index) = match args.get(1).cloned() {
1026
+ Some(initial) => (initial, 0),
1027
+ None => {
1028
+ let Some(index) = (0..length).find(|index| {
1029
+ self.array_has_index(array, *index)
1030
+ .expect("array existence should be stable during reduce")
1031
+ }) else {
1032
+ return Err(MustardError::runtime(
1033
+ "TypeError: Array.prototype.reduce requires an initial value for empty arrays",
1034
+ ));
1035
+ };
1036
+ (self.array_callback_value(array, index)?, index + 1)
1037
+ }
1038
+ };
1039
+ for index in start_index..length {
1040
+ self.charge_native_helper_work(1)?;
1041
+ if !self.array_has_index(array, index)? {
1042
+ continue;
1043
+ }
1044
+ let value = self.array_callback_value(array, index)?;
1045
+ accumulator = self.with_temporary_roots(
1046
+ &[
1047
+ Value::Array(array),
1048
+ callback.clone(),
1049
+ this_arg.clone(),
1050
+ accumulator.clone(),
1051
+ ],
1052
+ |runtime| {
1053
+ runtime.call_array_callback(
1054
+ callback.clone(),
1055
+ this_arg.clone(),
1056
+ &[
1057
+ accumulator.clone(),
1058
+ value,
1059
+ Value::Number(index as f64),
1060
+ Value::Array(array),
1061
+ ],
1062
+ )
1063
+ },
1064
+ )?;
1065
+ }
1066
+ Ok(accumulator)
1067
+ }
1068
+
1069
+ pub(crate) fn call_array_reduce_right(
1070
+ &mut self,
1071
+ this_value: Value,
1072
+ args: &[Value],
1073
+ ) -> MustardResult<Value> {
1074
+ let array = self.array_receiver(this_value, "reduceRight")?;
1075
+ let callback = args.first().cloned().unwrap_or(Value::Undefined);
1076
+ if !is_callable(&callback) {
1077
+ return Err(MustardError::runtime(
1078
+ "TypeError: Array.prototype.reduceRight expects a callable callback",
1079
+ ));
1080
+ }
1081
+ let this_arg = Value::Undefined;
1082
+ let length = self
1083
+ .arrays
1084
+ .get(array)
1085
+ .ok_or_else(|| MustardError::runtime("array missing"))?
1086
+ .elements
1087
+ .len();
1088
+ let (mut accumulator, mut index) = match args.get(1).cloned() {
1089
+ Some(initial) => (initial, length as isize - 1),
1090
+ None => {
1091
+ let Some(found_index) = (0..length).rev().find(|index| {
1092
+ self.array_has_index(array, *index)
1093
+ .expect("array existence should be stable during reduceRight")
1094
+ }) else {
1095
+ return Err(MustardError::runtime(
1096
+ "TypeError: Array.prototype.reduceRight requires an initial value for empty arrays",
1097
+ ));
1098
+ };
1099
+ (
1100
+ self.array_callback_value(array, found_index)?,
1101
+ found_index as isize - 1,
1102
+ )
1103
+ }
1104
+ };
1105
+ while index >= 0 {
1106
+ let current_index = index as usize;
1107
+ self.charge_native_helper_work(1)?;
1108
+ if self.array_has_index(array, current_index)? {
1109
+ let value = self.array_callback_value(array, current_index)?;
1110
+ accumulator = self.with_temporary_roots(
1111
+ &[
1112
+ Value::Array(array),
1113
+ callback.clone(),
1114
+ this_arg.clone(),
1115
+ accumulator.clone(),
1116
+ ],
1117
+ |runtime| {
1118
+ runtime.call_array_callback(
1119
+ callback.clone(),
1120
+ this_arg.clone(),
1121
+ &[
1122
+ accumulator.clone(),
1123
+ value,
1124
+ Value::Number(current_index as f64),
1125
+ Value::Array(array),
1126
+ ],
1127
+ )
1128
+ },
1129
+ )?;
1130
+ }
1131
+ index -= 1;
1132
+ }
1133
+ Ok(accumulator)
1134
+ }
1135
+
1136
+ pub(crate) fn call_array_find_last(
1137
+ &mut self,
1138
+ this_value: Value,
1139
+ args: &[Value],
1140
+ ) -> MustardResult<Value> {
1141
+ let array = self.array_receiver(this_value, "findLast")?;
1142
+ let (callback, this_arg) = self.array_callback(args, "findLast")?;
1143
+ let length = self
1144
+ .arrays
1145
+ .get(array)
1146
+ .ok_or_else(|| MustardError::runtime("array missing"))?
1147
+ .elements
1148
+ .len();
1149
+ for index in (0..length).rev() {
1150
+ self.charge_native_helper_work(1)?;
1151
+ let value = self.array_callback_value(array, index)?;
1152
+ let found = self.with_temporary_roots(
1153
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
1154
+ |runtime| {
1155
+ runtime.call_array_callback(
1156
+ callback.clone(),
1157
+ this_arg.clone(),
1158
+ &[
1159
+ value.clone(),
1160
+ Value::Number(index as f64),
1161
+ Value::Array(array),
1162
+ ],
1163
+ )
1164
+ },
1165
+ )?;
1166
+ if is_truthy(&found) {
1167
+ return Ok(value);
1168
+ }
1169
+ }
1170
+ Ok(Value::Undefined)
1171
+ }
1172
+
1173
+ pub(crate) fn call_array_find_last_index(
1174
+ &mut self,
1175
+ this_value: Value,
1176
+ args: &[Value],
1177
+ ) -> MustardResult<Value> {
1178
+ let array = self.array_receiver(this_value, "findLastIndex")?;
1179
+ let (callback, this_arg) = self.array_callback(args, "findLastIndex")?;
1180
+ let length = self
1181
+ .arrays
1182
+ .get(array)
1183
+ .ok_or_else(|| MustardError::runtime("array missing"))?
1184
+ .elements
1185
+ .len();
1186
+ for index in (0..length).rev() {
1187
+ self.charge_native_helper_work(1)?;
1188
+ let value = self.array_callback_value(array, index)?;
1189
+ let found = self.with_temporary_roots(
1190
+ &[Value::Array(array), callback.clone(), this_arg.clone()],
1191
+ |runtime| {
1192
+ runtime.call_array_callback(
1193
+ callback.clone(),
1194
+ this_arg.clone(),
1195
+ &[value, Value::Number(index as f64), Value::Array(array)],
1196
+ )
1197
+ },
1198
+ )?;
1199
+ if is_truthy(&found) {
1200
+ return Ok(Value::Number(index as f64));
1201
+ }
1202
+ }
1203
+ Ok(Value::Number(-1.0))
1204
+ }
1205
+ }