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,318 @@
1
+ mod accounting;
2
+ mod api;
3
+ mod async_runtime;
4
+ mod builtins;
5
+ mod bytecode;
6
+ mod compiler;
7
+ mod conversions;
8
+ mod env;
9
+ mod exceptions;
10
+ mod gc;
11
+ mod properties;
12
+ mod serialization;
13
+ mod shared;
14
+ #[cfg(test)]
15
+ mod snapshot_validation_tests;
16
+ mod state;
17
+ mod validation;
18
+ mod vm;
19
+
20
+ pub use api::{
21
+ ExecutionOptions, ExecutionSnapshot, ExecutionStep, HostError, ResumeOptions, ResumePayload,
22
+ SnapshotInspection, SnapshotPolicy, Suspension, execute, inspect_snapshot, resume,
23
+ resume_with_options, start, start_bytecode,
24
+ };
25
+ pub use bytecode::{BytecodeProgram, FunctionPrototype, Instruction};
26
+ pub use compiler::lower_to_bytecode;
27
+ use compiler::pattern_bindings;
28
+ pub use serialization::{
29
+ canonical_snapshot_auth_bytes, dump_program, dump_snapshot, load_program, load_snapshot,
30
+ };
31
+
32
+ use indexmap::IndexMap;
33
+ use slotmap::SlotMap;
34
+ use std::collections::{HashSet, VecDeque};
35
+
36
+ use self::properties::{
37
+ array_index_from_property_key, format_number_key, ordered_own_property_keys,
38
+ ordered_own_property_keys_filtered, property_name_to_key,
39
+ };
40
+ use self::shared::{
41
+ CallbackCallOptions, is_callable, is_truthy, limit_error, next_snapshot_nonce, pop_many,
42
+ resume_behavior_for_capability, same_value_zero, serialization_error, strict_equal,
43
+ };
44
+ use self::state::*;
45
+ use crate::{
46
+ cancellation::CancellationToken,
47
+ diagnostic::{DiagnosticKind, MustardError, MustardResult, TraceFrame},
48
+ ir::{BinaryOp, Pattern, PropertyName, UnaryOp},
49
+ span::SourceSpan,
50
+ structured::{StructuredNumber, StructuredValue},
51
+ };
52
+
53
+ const INTERNAL_CALLBACK_THROW_MARKER: &str = "\0internal-array-callback-throw";
54
+
55
+ impl Runtime {
56
+ fn new(program: BytecodeProgram, options: ExecutionOptions) -> MustardResult<Self> {
57
+ let ExecutionOptions {
58
+ inputs,
59
+ capabilities,
60
+ limits,
61
+ cancellation_token,
62
+ } = options;
63
+ let mut envs = SlotMap::with_key();
64
+ let globals = envs.insert(Env {
65
+ parent: None,
66
+ bindings: IndexMap::new(),
67
+ accounted_bytes: 0,
68
+ });
69
+ let mut runtime = Self {
70
+ program,
71
+ limits,
72
+ globals,
73
+ envs,
74
+ cells: SlotMap::with_key(),
75
+ objects: SlotMap::with_key(),
76
+ arrays: SlotMap::with_key(),
77
+ maps: SlotMap::with_key(),
78
+ sets: SlotMap::with_key(),
79
+ iterators: SlotMap::with_key(),
80
+ closures: SlotMap::with_key(),
81
+ promises: SlotMap::with_key(),
82
+ frames: Vec::new(),
83
+ root_result: None,
84
+ microtasks: VecDeque::new(),
85
+ pending_host_calls: VecDeque::new(),
86
+ suspended_host_call: None,
87
+ builtin_prototypes: IndexMap::new(),
88
+ builtin_function_objects: IndexMap::new(),
89
+ host_function_objects: IndexMap::new(),
90
+ snapshot_nonce: next_snapshot_nonce(),
91
+ instruction_counter: 0,
92
+ heap_bytes_used: 0,
93
+ allocation_count: 0,
94
+ cancellation_token,
95
+ pending_internal_exception: None,
96
+ snapshot_policy_required: false,
97
+ pending_resume_behavior: ResumeBehavior::Value,
98
+ };
99
+ runtime.account_existing_env(globals)?;
100
+ runtime.install_builtins()?;
101
+ for capability in capabilities {
102
+ runtime.define_global(capability.clone(), Value::HostFunction(capability), false)?;
103
+ }
104
+ for (name, value) in inputs {
105
+ let value = runtime.value_from_structured(value)?;
106
+ runtime.define_global(name, value, true)?;
107
+ }
108
+ Ok(runtime)
109
+ }
110
+
111
+ fn apply_resume_options(&mut self, options: ResumeOptions) -> MustardResult<()> {
112
+ if options.cancellation_token.is_some() {
113
+ self.cancellation_token = options.cancellation_token;
114
+ }
115
+ if let Some(policy) = options.snapshot_policy {
116
+ self.apply_snapshot_policy(policy)?;
117
+ }
118
+ if self.snapshot_policy_required {
119
+ return Err(serialization_error(
120
+ "loaded snapshots require explicit host policy before resume",
121
+ ));
122
+ }
123
+ Ok(())
124
+ }
125
+
126
+ fn apply_snapshot_policy(&mut self, policy: SnapshotPolicy) -> MustardResult<()> {
127
+ validation::validate_snapshot_policy(self, &policy)?;
128
+ self.limits = policy.limits;
129
+ if self.frames.len() > self.limits.call_depth_limit {
130
+ return Err(limit_error("call depth limit exceeded"));
131
+ }
132
+ self.recompute_accounting_after_load()?;
133
+ let outstanding_host_calls =
134
+ self.pending_host_calls.len() + usize::from(self.suspended_host_call.is_some());
135
+ if outstanding_host_calls > self.limits.max_outstanding_host_calls {
136
+ return Err(limit_error("outstanding host-call limit exhausted"));
137
+ }
138
+ if self.instruction_counter > self.limits.instruction_budget {
139
+ return Err(limit_error("instruction budget exhausted"));
140
+ }
141
+ self.snapshot_policy_required = false;
142
+ Ok(())
143
+ }
144
+
145
+ fn check_cancellation(&self) -> MustardResult<()> {
146
+ if self
147
+ .cancellation_token
148
+ .as_ref()
149
+ .is_some_and(CancellationToken::is_cancelled)
150
+ {
151
+ return Err(limit_error("execution cancelled"));
152
+ }
153
+ Ok(())
154
+ }
155
+
156
+ pub(crate) fn with_temporary_roots<T, F>(&mut self, roots: &[Value], f: F) -> MustardResult<T>
157
+ where
158
+ F: FnOnce(&mut Self) -> MustardResult<T>,
159
+ {
160
+ let frame_index = self.frames.len().checked_sub(1).ok_or_else(|| {
161
+ MustardError::runtime("no active frame available for temporary roots")
162
+ })?;
163
+ let original_len = self.frames[frame_index].stack.len();
164
+ self.frames[frame_index].stack.extend(roots.iter().cloned());
165
+ let result = f(self);
166
+ if let Some(frame) = self.frames.get_mut(frame_index) {
167
+ frame.stack.truncate(original_len);
168
+ }
169
+ result
170
+ }
171
+
172
+ pub(crate) fn call_callback(
173
+ &mut self,
174
+ callback: Value,
175
+ this_arg: Value,
176
+ args: &[Value],
177
+ options: CallbackCallOptions<'_>,
178
+ ) -> MustardResult<Value> {
179
+ match callback {
180
+ Value::Closure(closure) => {
181
+ let closure = self
182
+ .closures
183
+ .get(closure)
184
+ .cloned()
185
+ .ok_or_else(|| MustardError::runtime("closure not found"))?;
186
+ let env = self.new_env(Some(closure.env))?;
187
+ let had_async_boundary = self.current_async_boundary_index().is_some();
188
+ let (is_async, is_arrow, function_id) = self
189
+ .program
190
+ .functions
191
+ .get(closure.function_id)
192
+ .map(|function| (function.is_async, function.is_arrow, closure.function_id))
193
+ .ok_or_else(|| MustardError::runtime("function not found"))?;
194
+ let frame_this = if is_arrow {
195
+ closure.this_value.clone()
196
+ } else {
197
+ this_arg
198
+ };
199
+ if is_async {
200
+ let promise = self.insert_promise(PromiseState::Pending)?;
201
+ self.push_frame(function_id, env, args, frame_this, Some(promise))?;
202
+ Ok(Value::Promise(promise))
203
+ } else {
204
+ let promise = self.insert_promise(PromiseState::Pending)?;
205
+ let base_depth = self.frames.len();
206
+ self.push_frame(function_id, env, args, frame_this, Some(promise))?;
207
+ self.run_until_frame_depth(base_depth, options.host_suspension_message)?;
208
+ match self.promise_outcome(promise)? {
209
+ Some(PromiseOutcome::Fulfilled(value)) => Ok(value),
210
+ Some(PromiseOutcome::Rejected(rejection)) => {
211
+ self.pending_internal_exception = Some(rejection);
212
+ Err(MustardError::runtime(INTERNAL_CALLBACK_THROW_MARKER))
213
+ }
214
+ None if options.allow_pending_promise_result && had_async_boundary => {
215
+ Ok(Value::Promise(promise))
216
+ }
217
+ None => Err(MustardError::runtime(options.unsettled_message)),
218
+ }
219
+ }
220
+ }
221
+ Value::BuiltinFunction(function)
222
+ if matches!(
223
+ function,
224
+ BuiltinFunction::FunctionCall
225
+ | BuiltinFunction::FunctionApply
226
+ | BuiltinFunction::FunctionBind
227
+ ) =>
228
+ {
229
+ let base_depth = self.frames.len();
230
+ match self.call_callable(Value::BuiltinFunction(function), this_arg, args)? {
231
+ RunState::Completed(value) => Ok(value),
232
+ RunState::PushedFrame => {
233
+ self.run_until_frame_depth(base_depth, options.host_suspension_message)?;
234
+ self.frames
235
+ .last_mut()
236
+ .and_then(|frame| frame.stack.pop())
237
+ .ok_or_else(|| MustardError::runtime("missing callback result"))
238
+ }
239
+ RunState::StartedAsync(value) => Ok(value),
240
+ RunState::Suspended { .. } => {
241
+ Err(MustardError::runtime(options.host_suspension_message))
242
+ }
243
+ }
244
+ }
245
+ Value::BuiltinFunction(function) => self.call_builtin(function, this_arg, args),
246
+ Value::Object(object)
247
+ if self
248
+ .objects
249
+ .get(object)
250
+ .is_some_and(|object| matches!(object.kind, ObjectKind::BoundFunction(_))) =>
251
+ {
252
+ let base_depth = self.frames.len();
253
+ match self.call_callable(Value::Object(object), this_arg, args)? {
254
+ RunState::Completed(value) => Ok(value),
255
+ RunState::PushedFrame => {
256
+ self.run_until_frame_depth(base_depth, options.host_suspension_message)?;
257
+ self.frames
258
+ .last_mut()
259
+ .and_then(|frame| frame.stack.pop())
260
+ .ok_or_else(|| MustardError::runtime("missing callback result"))
261
+ }
262
+ RunState::StartedAsync(value) => Ok(value),
263
+ RunState::Suspended { .. } => {
264
+ Err(MustardError::runtime(options.host_suspension_message))
265
+ }
266
+ }
267
+ }
268
+ Value::HostFunction(capability) => {
269
+ if !options.allow_host_suspension || self.current_async_boundary_index().is_none() {
270
+ return Err(MustardError::runtime(options.host_suspension_message));
271
+ }
272
+ let outstanding =
273
+ self.pending_host_calls.len() + usize::from(self.suspended_host_call.is_some());
274
+ if outstanding >= self.limits.max_outstanding_host_calls {
275
+ return Err(limit_error("outstanding host-call limit exhausted"));
276
+ }
277
+ let args = args
278
+ .iter()
279
+ .cloned()
280
+ .map(|value| self.value_to_structured(value))
281
+ .collect::<MustardResult<Vec<_>>>()?;
282
+ let promise = self.insert_promise(PromiseState::Pending)?;
283
+ self.pending_host_calls.push_back(PendingHostCall {
284
+ capability,
285
+ args,
286
+ promise: Some(promise),
287
+ resume_behavior: ResumeBehavior::Value,
288
+ traceback: self.traceback_snapshots(),
289
+ });
290
+ Ok(Value::Promise(promise))
291
+ }
292
+ _ => Err(MustardError::runtime(options.non_callable_message)),
293
+ }
294
+ }
295
+
296
+ fn bump_instruction_budget(&mut self) -> MustardResult<()> {
297
+ self.instruction_counter += 1;
298
+ if self.instruction_counter > self.limits.instruction_budget {
299
+ return Err(limit_error("instruction budget exhausted"));
300
+ }
301
+ Ok(())
302
+ }
303
+
304
+ pub(super) fn charge_native_helper_work(&mut self, units: usize) -> MustardResult<()> {
305
+ if units == 0 {
306
+ return Ok(());
307
+ }
308
+ self.check_cancellation()?;
309
+ self.instruction_counter = self
310
+ .instruction_counter
311
+ .checked_add(units)
312
+ .ok_or_else(|| limit_error("instruction budget exhausted"))?;
313
+ if self.instruction_counter > self.limits.instruction_budget {
314
+ return Err(limit_error("instruction budget exhausted"));
315
+ }
316
+ Ok(())
317
+ }
318
+ }