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,400 @@
1
+ use super::*;
2
+
3
+ impl Runtime {
4
+ fn promise_combinator_slot_len(
5
+ promise: &PromiseObject,
6
+ kind: PromiseCombinatorKind,
7
+ ) -> MustardResult<Option<usize>> {
8
+ match (kind, promise.driver.as_ref()) {
9
+ (PromiseCombinatorKind::Race, _) => Ok(None),
10
+ (PromiseCombinatorKind::All, Some(PromiseDriver::All { values, .. })) => {
11
+ Ok(Some(values.len()))
12
+ }
13
+ (
14
+ PromiseCombinatorKind::AllSettled,
15
+ Some(PromiseDriver::AllSettled { results, .. }),
16
+ ) => Ok(Some(results.len())),
17
+ (PromiseCombinatorKind::Any, Some(PromiseDriver::Any { reasons, .. })) => {
18
+ Ok(Some(reasons.len()))
19
+ }
20
+ (_, Some(_)) => Err(MustardError::runtime("promise combinator kind mismatch")),
21
+ (_, None) => Err(MustardError::runtime("promise combinator state missing")),
22
+ }
23
+ }
24
+
25
+ pub(in crate::runtime) fn promise_reaction_target(
26
+ &self,
27
+ reaction: &PromiseReaction,
28
+ ) -> PromiseKey {
29
+ match reaction {
30
+ PromiseReaction::Then { target, .. }
31
+ | PromiseReaction::Finally { target, .. }
32
+ | PromiseReaction::FinallyPassThrough { target, .. }
33
+ | PromiseReaction::Combinator { target, .. } => *target,
34
+ }
35
+ }
36
+
37
+ pub(in crate::runtime) fn invoke_promise_handler(
38
+ &mut self,
39
+ handler: Value,
40
+ args: &[Value],
41
+ target: PromiseKey,
42
+ ) -> MustardResult<()> {
43
+ match handler {
44
+ Value::Closure(closure) => {
45
+ let closure = self
46
+ .closures
47
+ .get(closure)
48
+ .cloned()
49
+ .ok_or_else(|| MustardError::runtime("closure not found"))?;
50
+ let env = self.new_env(Some(closure.env))?;
51
+ let (is_async, function_id) = self
52
+ .program
53
+ .functions
54
+ .get(closure.function_id)
55
+ .map(|function| (function.is_async, closure.function_id))
56
+ .ok_or_else(|| MustardError::runtime("function not found"))?;
57
+ if is_async {
58
+ let bridge = self.insert_promise(PromiseState::Pending)?;
59
+ self.attach_dependent(bridge, target)?;
60
+ self.push_frame(function_id, env, args, Value::Undefined, Some(bridge))?;
61
+ } else {
62
+ self.push_frame(function_id, env, args, Value::Undefined, Some(target))?;
63
+ }
64
+ Ok(())
65
+ }
66
+ Value::BuiltinFunction(function)
67
+ if matches!(
68
+ function,
69
+ BuiltinFunction::FunctionCall
70
+ | BuiltinFunction::FunctionApply
71
+ | BuiltinFunction::FunctionBind
72
+ ) =>
73
+ {
74
+ match self.call_callable(
75
+ Value::BuiltinFunction(function),
76
+ Value::Undefined,
77
+ args,
78
+ )? {
79
+ RunState::Completed(value) => self.resolve_promise(target, value),
80
+ RunState::PushedFrame => Ok(()),
81
+ RunState::StartedAsync(value) => {
82
+ let promise = self.coerce_to_promise(value)?;
83
+ self.attach_dependent(promise, target)
84
+ }
85
+ RunState::Suspended { .. } => Err(MustardError::runtime(
86
+ "promise reactions do not support synchronous host suspensions",
87
+ )),
88
+ }
89
+ }
90
+ Value::BuiltinFunction(function) => {
91
+ let value = self.call_builtin(function, Value::Undefined, args)?;
92
+ self.resolve_promise(target, value)
93
+ }
94
+ Value::Object(object)
95
+ if self
96
+ .objects
97
+ .get(object)
98
+ .is_some_and(|object| matches!(object.kind, ObjectKind::BoundFunction(_))) =>
99
+ {
100
+ match self.call_callable(Value::Object(object), Value::Undefined, args)? {
101
+ RunState::Completed(value) => self.resolve_promise(target, value),
102
+ RunState::PushedFrame => Ok(()),
103
+ RunState::StartedAsync(value) => {
104
+ let promise = self.coerce_to_promise(value)?;
105
+ self.attach_dependent(promise, target)
106
+ }
107
+ RunState::Suspended { .. } => Err(MustardError::runtime(
108
+ "promise reactions do not support synchronous host suspensions",
109
+ )),
110
+ }
111
+ }
112
+ Value::HostFunction(capability) => {
113
+ let outstanding =
114
+ self.pending_host_calls.len() + usize::from(self.suspended_host_call.is_some());
115
+ if outstanding >= self.limits.max_outstanding_host_calls {
116
+ return Err(limit_error("outstanding host-call limit exhausted"));
117
+ }
118
+ let args = args
119
+ .iter()
120
+ .cloned()
121
+ .map(|value| self.value_to_structured(value))
122
+ .collect::<MustardResult<Vec<_>>>()?;
123
+ let promise = self.insert_promise(PromiseState::Pending)?;
124
+ self.attach_dependent(promise, target)?;
125
+ self.pending_host_calls.push_back(PendingHostCall {
126
+ capability,
127
+ args,
128
+ promise: Some(promise),
129
+ resume_behavior: ResumeBehavior::Value,
130
+ traceback: self.traceback_snapshots(),
131
+ });
132
+ Ok(())
133
+ }
134
+ _ => Err(MustardError::runtime("value is not callable")),
135
+ }
136
+ }
137
+
138
+ pub(in crate::runtime) fn make_promise_all_settled_result(
139
+ &mut self,
140
+ result: PromiseSettledResult,
141
+ ) -> MustardResult<Value> {
142
+ let properties = match result {
143
+ PromiseSettledResult::Fulfilled(value) => IndexMap::from([
144
+ ("status".to_string(), Value::String("fulfilled".to_string())),
145
+ ("value".to_string(), value),
146
+ ]),
147
+ PromiseSettledResult::Rejected(reason) => IndexMap::from([
148
+ ("status".to_string(), Value::String("rejected".to_string())),
149
+ ("reason".to_string(), reason),
150
+ ]),
151
+ };
152
+ Ok(Value::Object(
153
+ self.insert_object(properties, ObjectKind::Plain)?,
154
+ ))
155
+ }
156
+
157
+ pub(in crate::runtime) fn make_aggregate_error(
158
+ &mut self,
159
+ reasons: Vec<Value>,
160
+ ) -> MustardResult<Value> {
161
+ let error = self.make_error_object(
162
+ "AggregateError",
163
+ &[Value::String("All promises were rejected".to_string())],
164
+ None,
165
+ None,
166
+ None,
167
+ )?;
168
+ let errors = Value::Array(self.insert_array(reasons, IndexMap::new())?);
169
+ self.set_property(error.clone(), Value::String("errors".to_string()), errors)?;
170
+ Ok(error)
171
+ }
172
+
173
+ pub(in crate::runtime) fn activate_promise_combinator(
174
+ &mut self,
175
+ target: PromiseKey,
176
+ index: usize,
177
+ kind: PromiseCombinatorKind,
178
+ outcome: PromiseOutcome,
179
+ ) -> MustardResult<()> {
180
+ if self.promise_outcome(target)?.is_some() {
181
+ return Ok(());
182
+ }
183
+ let promise = self
184
+ .promises
185
+ .get(target)
186
+ .ok_or_else(|| MustardError::runtime("promise missing"))?;
187
+ if let Some(len) = Self::promise_combinator_slot_len(promise, kind)?
188
+ && index >= len
189
+ {
190
+ return Err(serialization_error(
191
+ "snapshot validation failed: promise combinator index out of range",
192
+ ));
193
+ }
194
+ match kind {
195
+ PromiseCombinatorKind::Race => self.resolve_promise_with_outcome(target, outcome),
196
+ PromiseCombinatorKind::All => {
197
+ let mut resolved_values = None;
198
+ let mut rejection = None;
199
+ {
200
+ let promise = self
201
+ .promises
202
+ .get_mut(target)
203
+ .ok_or_else(|| MustardError::runtime("promise missing"))?;
204
+ let PromiseState::Pending = promise.state else {
205
+ return Ok(());
206
+ };
207
+ let PromiseDriver::All { remaining, values } = promise
208
+ .driver
209
+ .as_mut()
210
+ .ok_or_else(|| MustardError::runtime("promise combinator state missing"))?
211
+ else {
212
+ return Err(MustardError::runtime("promise combinator kind mismatch"));
213
+ };
214
+ match outcome {
215
+ PromiseOutcome::Fulfilled(value) => {
216
+ values[index] = Some(value);
217
+ *remaining = remaining.saturating_sub(1);
218
+ if *remaining == 0 {
219
+ resolved_values = Some(
220
+ values
221
+ .iter()
222
+ .map(|value| value.clone().unwrap_or(Value::Undefined))
223
+ .collect::<Vec<_>>(),
224
+ );
225
+ }
226
+ }
227
+ PromiseOutcome::Rejected(reason) => rejection = Some(reason),
228
+ }
229
+ }
230
+ self.refresh_promise_accounting(target)?;
231
+ if let Some(rejection) = rejection {
232
+ self.reject_promise(target, rejection)?;
233
+ } else if let Some(values) = resolved_values {
234
+ let array = Value::Array(self.insert_array(values, IndexMap::new())?);
235
+ self.resolve_promise(target, array)?;
236
+ }
237
+ Ok(())
238
+ }
239
+ PromiseCombinatorKind::AllSettled => {
240
+ let mut settled_results = None;
241
+ {
242
+ let promise = self
243
+ .promises
244
+ .get_mut(target)
245
+ .ok_or_else(|| MustardError::runtime("promise missing"))?;
246
+ let PromiseState::Pending = promise.state else {
247
+ return Ok(());
248
+ };
249
+ let PromiseDriver::AllSettled { remaining, results } = promise
250
+ .driver
251
+ .as_mut()
252
+ .ok_or_else(|| MustardError::runtime("promise combinator state missing"))?
253
+ else {
254
+ return Err(MustardError::runtime("promise combinator kind mismatch"));
255
+ };
256
+ results[index] = Some(match outcome {
257
+ PromiseOutcome::Fulfilled(value) => PromiseSettledResult::Fulfilled(value),
258
+ PromiseOutcome::Rejected(reason) => {
259
+ PromiseSettledResult::Rejected(reason.value)
260
+ }
261
+ });
262
+ *remaining = remaining.saturating_sub(1);
263
+ if *remaining == 0 {
264
+ settled_results = Some(
265
+ results
266
+ .iter()
267
+ .map(|result| {
268
+ result.clone().unwrap_or(PromiseSettledResult::Fulfilled(
269
+ Value::Undefined,
270
+ ))
271
+ })
272
+ .collect::<Vec<_>>(),
273
+ );
274
+ }
275
+ }
276
+ self.refresh_promise_accounting(target)?;
277
+ if let Some(results) = settled_results {
278
+ let mut values = Vec::with_capacity(results.len());
279
+ for result in results {
280
+ values.push(self.make_promise_all_settled_result(result)?);
281
+ }
282
+ let array = Value::Array(self.insert_array(values, IndexMap::new())?);
283
+ self.resolve_promise(target, array)?;
284
+ }
285
+ Ok(())
286
+ }
287
+ PromiseCombinatorKind::Any => {
288
+ let mut rejection_values = None;
289
+ let mut fulfillment = None;
290
+ {
291
+ let promise = self
292
+ .promises
293
+ .get_mut(target)
294
+ .ok_or_else(|| MustardError::runtime("promise missing"))?;
295
+ let PromiseState::Pending = promise.state else {
296
+ return Ok(());
297
+ };
298
+ let PromiseDriver::Any { remaining, reasons } = promise
299
+ .driver
300
+ .as_mut()
301
+ .ok_or_else(|| MustardError::runtime("promise combinator state missing"))?
302
+ else {
303
+ return Err(MustardError::runtime("promise combinator kind mismatch"));
304
+ };
305
+ match outcome {
306
+ PromiseOutcome::Fulfilled(value) => fulfillment = Some(value),
307
+ PromiseOutcome::Rejected(reason) => {
308
+ reasons[index] = Some(reason.value);
309
+ *remaining = remaining.saturating_sub(1);
310
+ if *remaining == 0 {
311
+ rejection_values = Some(
312
+ reasons
313
+ .iter()
314
+ .map(|value| value.clone().unwrap_or(Value::Undefined))
315
+ .collect::<Vec<_>>(),
316
+ );
317
+ }
318
+ }
319
+ }
320
+ }
321
+ self.refresh_promise_accounting(target)?;
322
+ if let Some(value) = fulfillment {
323
+ self.resolve_promise(target, value)?;
324
+ } else if let Some(reasons) = rejection_values {
325
+ let rejection = PromiseRejection {
326
+ value: self.make_aggregate_error(reasons)?,
327
+ span: None,
328
+ traceback: self.traceback_snapshots(),
329
+ };
330
+ self.reject_promise(target, rejection)?;
331
+ }
332
+ Ok(())
333
+ }
334
+ }
335
+ }
336
+
337
+ pub(in crate::runtime) fn activate_promise_reaction(
338
+ &mut self,
339
+ reaction: PromiseReaction,
340
+ outcome: PromiseOutcome,
341
+ ) -> MustardResult<()> {
342
+ let target = self.promise_reaction_target(&reaction);
343
+ let result = (|| match reaction {
344
+ PromiseReaction::Then {
345
+ target,
346
+ on_fulfilled,
347
+ on_rejected,
348
+ } => match outcome {
349
+ PromiseOutcome::Fulfilled(value) => {
350
+ if let Some(handler) = on_fulfilled {
351
+ self.invoke_promise_handler(handler, &[value], target)
352
+ } else {
353
+ self.resolve_promise(target, value)
354
+ }
355
+ }
356
+ PromiseOutcome::Rejected(rejection) => {
357
+ if let Some(handler) = on_rejected {
358
+ self.invoke_promise_handler(handler, &[rejection.value], target)
359
+ } else {
360
+ self.reject_promise(target, rejection)
361
+ }
362
+ }
363
+ },
364
+ PromiseReaction::Finally { target, callback } => {
365
+ if let Some(callback) = callback {
366
+ let bridge = self.insert_promise(PromiseState::Pending)?;
367
+ self.attach_promise_reaction(
368
+ bridge,
369
+ PromiseReaction::FinallyPassThrough {
370
+ target,
371
+ original_outcome: outcome,
372
+ },
373
+ )?;
374
+ self.invoke_promise_handler(callback, &[], bridge)
375
+ } else {
376
+ self.resolve_promise_with_outcome(target, outcome)
377
+ }
378
+ }
379
+ PromiseReaction::FinallyPassThrough {
380
+ target,
381
+ original_outcome,
382
+ } => match outcome {
383
+ PromiseOutcome::Fulfilled(_) => {
384
+ self.resolve_promise_with_outcome(target, original_outcome)
385
+ }
386
+ PromiseOutcome::Rejected(rejection) => self.reject_promise(target, rejection),
387
+ },
388
+ PromiseReaction::Combinator {
389
+ target,
390
+ index,
391
+ kind,
392
+ } => self.activate_promise_combinator(target, index, kind, outcome),
393
+ })();
394
+
395
+ match result {
396
+ Ok(()) => Ok(()),
397
+ Err(error) => self.reject_promise_from_error(target, error),
398
+ }
399
+ }
400
+ }
@@ -0,0 +1,224 @@
1
+ use super::*;
2
+
3
+ impl Runtime {
4
+ pub(in crate::runtime) fn activate_microtask(
5
+ &mut self,
6
+ job: MicrotaskJob,
7
+ ) -> MustardResult<()> {
8
+ if !self.frames.is_empty() {
9
+ return Err(MustardError::runtime(
10
+ "microtask checkpoint ran while frames were still active",
11
+ ));
12
+ }
13
+ match job {
14
+ MicrotaskJob::ResumeAsync {
15
+ continuation,
16
+ outcome,
17
+ } => {
18
+ self.frames = continuation.frames;
19
+ match outcome {
20
+ PromiseOutcome::Fulfilled(value) => {
21
+ let frame = self.frames.last_mut().ok_or_else(|| {
22
+ MustardError::runtime("async continuation resumed without frames")
23
+ })?;
24
+ frame.stack.push(value);
25
+ }
26
+ PromiseOutcome::Rejected(rejection) => {
27
+ match self.raise_exception_with_origin(
28
+ rejection.value,
29
+ rejection.span,
30
+ Some(rejection.traceback),
31
+ )? {
32
+ StepAction::Continue => {}
33
+ StepAction::Return(_) => {}
34
+ }
35
+ }
36
+ }
37
+ }
38
+ MicrotaskJob::PromiseReaction { reaction, outcome } => {
39
+ self.activate_promise_reaction(reaction, outcome)?;
40
+ }
41
+ }
42
+ Ok(())
43
+ }
44
+
45
+ pub(in crate::runtime) fn has_pending_async_work(&self) -> bool {
46
+ self.suspended_host_call.is_some()
47
+ || !self.pending_host_calls.is_empty()
48
+ || !self.microtasks.is_empty()
49
+ || self.promises.values().any(|promise| {
50
+ matches!(promise.state, PromiseState::Pending)
51
+ && (!promise.awaiters.is_empty()
52
+ || !promise.dependents.is_empty()
53
+ || !promise.reactions.is_empty())
54
+ })
55
+ }
56
+
57
+ pub(in crate::runtime) fn suspend_host_request(
58
+ &mut self,
59
+ request: PendingHostCall,
60
+ ) -> ExecutionStep {
61
+ let capability = request.capability.clone();
62
+ let args = request.args.clone();
63
+ self.suspended_host_call = Some(request);
64
+ self.snapshot_nonce = next_snapshot_nonce();
65
+ ExecutionStep::Suspended(Box::new(Suspension {
66
+ capability,
67
+ args,
68
+ snapshot: ExecutionSnapshot {
69
+ runtime: self.clone(),
70
+ },
71
+ }))
72
+ }
73
+
74
+ pub(in crate::runtime) fn process_idle_state(
75
+ &mut self,
76
+ ) -> MustardResult<Option<ExecutionStep>> {
77
+ if let Some(job) = self.microtasks.pop_front() {
78
+ self.activate_microtask(job)?;
79
+ return Ok(None);
80
+ }
81
+ if let Some(request) = self.pending_host_calls.pop_front() {
82
+ return Ok(Some(self.suspend_host_request(request)));
83
+ }
84
+ if let Some(root_result) = self.root_result.clone() {
85
+ return match root_result {
86
+ Value::Promise(promise) => match self.promise_outcome(promise)? {
87
+ Some(PromiseOutcome::Fulfilled(value)) => Ok(Some(ExecutionStep::Completed(
88
+ self.value_to_structured(value)?,
89
+ ))),
90
+ Some(PromiseOutcome::Rejected(rejection)) => {
91
+ Err(self.root_error_from_rejection(rejection)?)
92
+ }
93
+ None => Err(MustardError::runtime(
94
+ "async root promise could not make progress",
95
+ )),
96
+ },
97
+ value => {
98
+ if self.has_pending_async_work() {
99
+ return Err(MustardError::runtime(
100
+ "async execution became idle with pending work",
101
+ ));
102
+ }
103
+ Ok(Some(ExecutionStep::Completed(
104
+ self.value_to_structured(value)?,
105
+ )))
106
+ }
107
+ };
108
+ }
109
+ if self.has_pending_async_work() {
110
+ return Err(MustardError::runtime(
111
+ "async execution became idle before producing a root result",
112
+ ));
113
+ }
114
+ Err(MustardError::runtime("vm lost all frames"))
115
+ }
116
+
117
+ pub(in crate::runtime) fn resume(
118
+ &mut self,
119
+ payload: ResumePayload,
120
+ ) -> MustardResult<ExecutionStep> {
121
+ if let Err(error) = self.check_cancellation() {
122
+ if let Some(request) = self.suspended_host_call.as_ref() {
123
+ return Err(error.with_traceback(self.compose_traceback(&request.traceback)));
124
+ }
125
+ return Err(self.annotate_runtime_error(error));
126
+ }
127
+ self.collect_garbage()
128
+ .map_err(|error| self.annotate_runtime_error(error))?;
129
+ if let Some(request) = self.suspended_host_call.take() {
130
+ if let Some(promise) = request.promise {
131
+ let outcome = match payload {
132
+ ResumePayload::Value(value) => {
133
+ let value = match request.resume_behavior {
134
+ ResumeBehavior::Value => self
135
+ .value_from_structured(value)
136
+ .map_err(|error| self.annotate_runtime_error(error))?,
137
+ ResumeBehavior::Undefined => Value::Undefined,
138
+ };
139
+ PromiseOutcome::Fulfilled(value)
140
+ }
141
+ ResumePayload::Error(error) => PromiseOutcome::Rejected(PromiseRejection {
142
+ value: self
143
+ .value_from_host_error(error)
144
+ .map_err(|error| self.annotate_runtime_error(error))?,
145
+ span: None,
146
+ traceback: Vec::new(),
147
+ }),
148
+ ResumePayload::Cancelled => {
149
+ return Err(limit_error("execution cancelled")
150
+ .with_traceback(self.compose_traceback(&request.traceback)));
151
+ }
152
+ };
153
+ self.resolve_promise_with_outcome(promise, outcome)
154
+ .map_err(|error| self.annotate_runtime_error(error))?;
155
+ return self.run();
156
+ }
157
+
158
+ return match payload {
159
+ ResumePayload::Value(value) => {
160
+ let value = match request.resume_behavior {
161
+ ResumeBehavior::Value => self
162
+ .value_from_structured(value)
163
+ .map_err(|error| self.annotate_runtime_error(error))?,
164
+ ResumeBehavior::Undefined => Value::Undefined,
165
+ };
166
+ self.pending_resume_behavior = ResumeBehavior::Value;
167
+ let Some(frame) = self.frames.last_mut() else {
168
+ return Err(self.annotate_runtime_error(MustardError::runtime(
169
+ "no suspended frame available",
170
+ )));
171
+ };
172
+ frame.stack.push(value);
173
+ self.run()
174
+ }
175
+ ResumePayload::Error(error) => {
176
+ self.pending_resume_behavior = ResumeBehavior::Value;
177
+ let value = self
178
+ .value_from_host_error(error)
179
+ .map_err(|error| self.annotate_runtime_error(error))?;
180
+ match self.raise_exception(value, None) {
181
+ Ok(StepAction::Continue) => self.run(),
182
+ Ok(StepAction::Return(step)) => Ok(step),
183
+ Err(error) => Err(self.annotate_runtime_error(error)),
184
+ }
185
+ }
186
+ ResumePayload::Cancelled => {
187
+ Err(self.annotate_runtime_error(limit_error("execution cancelled")))
188
+ }
189
+ };
190
+ }
191
+ match payload {
192
+ ResumePayload::Value(value) => {
193
+ let value = match self.pending_resume_behavior {
194
+ ResumeBehavior::Value => self
195
+ .value_from_structured(value)
196
+ .map_err(|error| self.annotate_runtime_error(error))?,
197
+ ResumeBehavior::Undefined => Value::Undefined,
198
+ };
199
+ self.pending_resume_behavior = ResumeBehavior::Value;
200
+ let Some(frame) = self.frames.last_mut() else {
201
+ return Err(self.annotate_runtime_error(MustardError::runtime(
202
+ "no suspended frame available",
203
+ )));
204
+ };
205
+ frame.stack.push(value);
206
+ }
207
+ ResumePayload::Error(error) => {
208
+ self.pending_resume_behavior = ResumeBehavior::Value;
209
+ let value = self
210
+ .value_from_host_error(error)
211
+ .map_err(|error| self.annotate_runtime_error(error))?;
212
+ match self.raise_exception(value, None) {
213
+ Ok(StepAction::Continue) => return self.run(),
214
+ Ok(StepAction::Return(step)) => return Ok(step),
215
+ Err(error) => return Err(self.annotate_runtime_error(error)),
216
+ }
217
+ }
218
+ ResumePayload::Cancelled => {
219
+ return Err(self.annotate_runtime_error(limit_error("execution cancelled")));
220
+ }
221
+ }
222
+ self.run()
223
+ }
224
+ }