mustardscript 0.1.0 → 0.1.2

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 (96) hide show
  1. package/README.md +65 -22
  2. package/SECURITY.md +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/lib/executor.js +16 -1
  5. package/dist/lib/policy.js +301 -22
  6. package/dist/lib/progress.js +499 -113
  7. package/dist/lib/runtime.js +109 -40
  8. package/dist/lib/structured.js +327 -11
  9. package/dist/native-loader.js +11 -12
  10. package/index.d.ts +54 -6
  11. package/mustard.d.ts +23 -1
  12. package/package.json +34 -25
  13. package/Cargo.lock +0 -1579
  14. package/Cargo.toml +0 -40
  15. package/crates/mustard/Cargo.toml +0 -31
  16. package/crates/mustard/src/cancellation.rs +0 -28
  17. package/crates/mustard/src/diagnostic.rs +0 -145
  18. package/crates/mustard/src/ir.rs +0 -435
  19. package/crates/mustard/src/lib.rs +0 -21
  20. package/crates/mustard/src/limits.rs +0 -22
  21. package/crates/mustard/src/parser/expressions.rs +0 -723
  22. package/crates/mustard/src/parser/mod.rs +0 -115
  23. package/crates/mustard/src/parser/operators.rs +0 -105
  24. package/crates/mustard/src/parser/patterns.rs +0 -123
  25. package/crates/mustard/src/parser/scope.rs +0 -107
  26. package/crates/mustard/src/parser/statements.rs +0 -298
  27. package/crates/mustard/src/parser/tests/acceptance.rs +0 -339
  28. package/crates/mustard/src/parser/tests/mod.rs +0 -2
  29. package/crates/mustard/src/parser/tests/rejections.rs +0 -107
  30. package/crates/mustard/src/runtime/accounting.rs +0 -613
  31. package/crates/mustard/src/runtime/api.rs +0 -192
  32. package/crates/mustard/src/runtime/async_runtime/mod.rs +0 -5
  33. package/crates/mustard/src/runtime/async_runtime/promises.rs +0 -246
  34. package/crates/mustard/src/runtime/async_runtime/reactions.rs +0 -400
  35. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +0 -224
  36. package/crates/mustard/src/runtime/builtins/arrays.rs +0 -1205
  37. package/crates/mustard/src/runtime/builtins/collections.rs +0 -573
  38. package/crates/mustard/src/runtime/builtins/install.rs +0 -501
  39. package/crates/mustard/src/runtime/builtins/intl.rs +0 -553
  40. package/crates/mustard/src/runtime/builtins/mod.rs +0 -25
  41. package/crates/mustard/src/runtime/builtins/objects.rs +0 -405
  42. package/crates/mustard/src/runtime/builtins/primitives.rs +0 -859
  43. package/crates/mustard/src/runtime/builtins/promises.rs +0 -335
  44. package/crates/mustard/src/runtime/builtins/regexp.rs +0 -356
  45. package/crates/mustard/src/runtime/builtins/strings.rs +0 -803
  46. package/crates/mustard/src/runtime/builtins/support.rs +0 -561
  47. package/crates/mustard/src/runtime/bytecode.rs +0 -123
  48. package/crates/mustard/src/runtime/compiler/assignments.rs +0 -690
  49. package/crates/mustard/src/runtime/compiler/bindings.rs +0 -92
  50. package/crates/mustard/src/runtime/compiler/context.rs +0 -46
  51. package/crates/mustard/src/runtime/compiler/control.rs +0 -342
  52. package/crates/mustard/src/runtime/compiler/expressions.rs +0 -372
  53. package/crates/mustard/src/runtime/compiler/mod.rs +0 -173
  54. package/crates/mustard/src/runtime/compiler/statements.rs +0 -459
  55. package/crates/mustard/src/runtime/conversions/boundary.rs +0 -293
  56. package/crates/mustard/src/runtime/conversions/coercions.rs +0 -217
  57. package/crates/mustard/src/runtime/conversions/errors.rs +0 -118
  58. package/crates/mustard/src/runtime/conversions/mod.rs +0 -14
  59. package/crates/mustard/src/runtime/conversions/operators.rs +0 -334
  60. package/crates/mustard/src/runtime/env.rs +0 -355
  61. package/crates/mustard/src/runtime/exceptions.rs +0 -377
  62. package/crates/mustard/src/runtime/gc.rs +0 -595
  63. package/crates/mustard/src/runtime/mod.rs +0 -318
  64. package/crates/mustard/src/runtime/properties.rs +0 -1762
  65. package/crates/mustard/src/runtime/serialization.rs +0 -127
  66. package/crates/mustard/src/runtime/shared.rs +0 -108
  67. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +0 -93
  68. package/crates/mustard/src/runtime/state.rs +0 -652
  69. package/crates/mustard/src/runtime/tests/async_host.rs +0 -104
  70. package/crates/mustard/src/runtime/tests/collections.rs +0 -50
  71. package/crates/mustard/src/runtime/tests/diagnostics.rs +0 -36
  72. package/crates/mustard/src/runtime/tests/exceptions.rs +0 -122
  73. package/crates/mustard/src/runtime/tests/execution.rs +0 -553
  74. package/crates/mustard/src/runtime/tests/gc.rs +0 -533
  75. package/crates/mustard/src/runtime/tests/mod.rs +0 -56
  76. package/crates/mustard/src/runtime/tests/serialization.rs +0 -170
  77. package/crates/mustard/src/runtime/validation/bytecode.rs +0 -484
  78. package/crates/mustard/src/runtime/validation/mod.rs +0 -14
  79. package/crates/mustard/src/runtime/validation/policy.rs +0 -94
  80. package/crates/mustard/src/runtime/validation/snapshot.rs +0 -406
  81. package/crates/mustard/src/runtime/validation/walk.rs +0 -206
  82. package/crates/mustard/src/runtime/vm.rs +0 -1016
  83. package/crates/mustard/src/span.rs +0 -22
  84. package/crates/mustard/src/structured.rs +0 -107
  85. package/crates/mustard-bridge/Cargo.toml +0 -17
  86. package/crates/mustard-bridge/src/codec.rs +0 -46
  87. package/crates/mustard-bridge/src/dto.rs +0 -99
  88. package/crates/mustard-bridge/src/lib.rs +0 -12
  89. package/crates/mustard-bridge/src/operations.rs +0 -142
  90. package/crates/mustard-node/Cargo.toml +0 -24
  91. package/crates/mustard-node/build.rs +0 -3
  92. package/crates/mustard-node/src/lib.rs +0 -236
  93. package/crates/mustard-sidecar/Cargo.toml +0 -21
  94. package/crates/mustard-sidecar/src/lib.rs +0 -134
  95. package/crates/mustard-sidecar/src/main.rs +0 -36
  96. package/dist/install.js +0 -117
@@ -1,859 +0,0 @@
1
- use std::{
2
- collections::HashSet,
3
- io::{self, Read, Write},
4
- };
5
-
6
- use oxc_syntax::number::ToJsString;
7
- use rand::random;
8
-
9
- use super::*;
10
-
11
- const JSON_HELPER_IO_CHUNK_BYTES: usize = 256;
12
- const NUMBER_PARSE_HELPER_CHUNK_CHARS: usize = 256;
13
-
14
- #[derive(Default)]
15
- struct JsonStringifyTraversalState {
16
- arrays: HashSet<ArrayKey>,
17
- objects: HashSet<ObjectKey>,
18
- }
19
-
20
- struct BudgetedJsonReader<'runtime, 'source> {
21
- runtime: &'runtime mut Runtime,
22
- source: &'source [u8],
23
- offset: usize,
24
- failure: Option<MustardError>,
25
- }
26
-
27
- impl<'runtime, 'source> BudgetedJsonReader<'runtime, 'source> {
28
- fn new(runtime: &'runtime mut Runtime, source: &'source [u8]) -> Self {
29
- Self {
30
- runtime,
31
- source,
32
- offset: 0,
33
- failure: None,
34
- }
35
- }
36
-
37
- fn into_failure(self) -> Option<MustardError> {
38
- self.failure
39
- }
40
- }
41
-
42
- impl Read for BudgetedJsonReader<'_, '_> {
43
- fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
44
- if self.offset >= self.source.len() {
45
- return Ok(0);
46
- }
47
- let chunk_len = buf
48
- .len()
49
- .min(self.source.len() - self.offset)
50
- .min(JSON_HELPER_IO_CHUNK_BYTES);
51
- if let Err(error) = self.runtime.charge_native_helper_work(1) {
52
- self.failure = Some(error);
53
- return Err(io::Error::other("mustard-json-parse-aborted"));
54
- }
55
- buf[..chunk_len].copy_from_slice(&self.source[self.offset..self.offset + chunk_len]);
56
- self.offset += chunk_len;
57
- Ok(chunk_len)
58
- }
59
- }
60
-
61
- struct JsonOutputWriter<'runtime, 'output> {
62
- runtime: &'runtime mut Runtime,
63
- output: &'output mut String,
64
- failure: Option<MustardError>,
65
- }
66
-
67
- impl<'runtime, 'output> JsonOutputWriter<'runtime, 'output> {
68
- fn new(runtime: &'runtime mut Runtime, output: &'output mut String) -> Self {
69
- Self {
70
- runtime,
71
- output,
72
- failure: None,
73
- }
74
- }
75
-
76
- fn into_failure(self) -> Option<MustardError> {
77
- self.failure
78
- }
79
- }
80
-
81
- impl Write for JsonOutputWriter<'_, '_> {
82
- fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
83
- if buf.is_empty() {
84
- return Ok(0);
85
- }
86
- let chunk_len = buf.len().min(JSON_HELPER_IO_CHUNK_BYTES);
87
- let next_len = self
88
- .output
89
- .len()
90
- .checked_add(chunk_len)
91
- .ok_or_else(|| io::Error::other("json output overflow"))?;
92
- if let Err(error) = self.runtime.ensure_heap_capacity(next_len) {
93
- self.failure = Some(error);
94
- return Err(io::Error::other("mustard-json-stringify-aborted"));
95
- }
96
- if let Err(error) = self.runtime.charge_native_helper_work(1) {
97
- self.failure = Some(error);
98
- return Err(io::Error::other("mustard-json-stringify-aborted"));
99
- }
100
- let chunk = std::str::from_utf8(&buf[..chunk_len]).map_err(io::Error::other)?;
101
- self.output.push_str(chunk);
102
- Ok(chunk_len)
103
- }
104
-
105
- fn flush(&mut self) -> io::Result<()> {
106
- Ok(())
107
- }
108
- }
109
-
110
- impl Runtime {
111
- fn number_receiver(&self, value: Value, method: &str) -> MustardResult<f64> {
112
- match value {
113
- Value::Number(value) => Ok(value),
114
- Value::Object(object) => match &self
115
- .objects
116
- .get(object)
117
- .ok_or_else(|| MustardError::runtime("object missing"))?
118
- .kind
119
- {
120
- ObjectKind::NumberObject(value) => Ok(*value),
121
- _ => Err(MustardError::runtime(format!(
122
- "TypeError: Number.prototype.{method} called on incompatible receiver",
123
- ))),
124
- },
125
- _ => Err(MustardError::runtime(format!(
126
- "TypeError: Number.prototype.{method} called on incompatible receiver",
127
- ))),
128
- }
129
- }
130
-
131
- fn boolean_receiver(&self, value: Value, method: &str) -> MustardResult<bool> {
132
- match value {
133
- Value::Bool(value) => Ok(value),
134
- Value::Object(object) => match &self
135
- .objects
136
- .get(object)
137
- .ok_or_else(|| MustardError::runtime("object missing"))?
138
- .kind
139
- {
140
- ObjectKind::BooleanObject(value) => Ok(*value),
141
- _ => Err(MustardError::runtime(format!(
142
- "TypeError: Boolean.prototype.{method} called on incompatible receiver",
143
- ))),
144
- },
145
- _ => Err(MustardError::runtime(format!(
146
- "TypeError: Boolean.prototype.{method} called on incompatible receiver",
147
- ))),
148
- }
149
- }
150
-
151
- pub(crate) fn construct_date(&mut self, args: &[Value]) -> MustardResult<Value> {
152
- if args.len() > 1 {
153
- return Err(MustardError::runtime(
154
- "TypeError: Date currently supports zero or one constructor argument",
155
- ));
156
- }
157
- let timestamp_ms = match args {
158
- [] => time_clip(current_time_millis()),
159
- [value] => self.date_timestamp_ms_from_value(value.clone())?,
160
- _ => unreachable!(),
161
- };
162
- Ok(Value::Object(self.insert_object(
163
- IndexMap::new(),
164
- ObjectKind::Date(DateObject { timestamp_ms }),
165
- )?))
166
- }
167
-
168
- fn date_receiver(&self, value: Value, method: &str) -> MustardResult<ObjectKey> {
169
- match value {
170
- Value::Object(key) if self.is_date_object(key) => Ok(key),
171
- _ => Err(MustardError::runtime(format!(
172
- "TypeError: Date.prototype.{method} called on incompatible receiver",
173
- ))),
174
- }
175
- }
176
-
177
- pub(crate) fn call_date_get_time(&self, this_value: Value) -> MustardResult<Value> {
178
- let date = self.date_receiver(this_value, "getTime")?;
179
- Ok(Value::Number(self.date_object(date)?.timestamp_ms))
180
- }
181
-
182
- pub(crate) fn call_date_value_of(&self, this_value: Value) -> MustardResult<Value> {
183
- let date = self.date_receiver(this_value, "valueOf")?;
184
- Ok(Value::Number(self.date_object(date)?.timestamp_ms))
185
- }
186
-
187
- pub(crate) fn call_date_to_iso_string(&self, this_value: Value) -> MustardResult<Value> {
188
- let date = self.date_receiver(this_value, "toISOString")?;
189
- let timestamp_ms = self.date_object(date)?.timestamp_ms;
190
- let Some(rendered) = format_iso_datetime(timestamp_ms) else {
191
- return Err(MustardError::runtime("RangeError: Invalid time value"));
192
- };
193
- Ok(Value::String(rendered))
194
- }
195
-
196
- pub(crate) fn call_date_to_json(&self, this_value: Value) -> MustardResult<Value> {
197
- let date = self.date_receiver(this_value, "toJSON")?;
198
- let timestamp_ms = self.date_object(date)?.timestamp_ms;
199
- Ok(match format_iso_datetime(timestamp_ms) {
200
- Some(rendered) => Value::String(rendered),
201
- None => Value::Null,
202
- })
203
- }
204
-
205
- fn date_utc_fields(
206
- &self,
207
- this_value: Value,
208
- method: &str,
209
- ) -> MustardResult<Option<DateTimeFields>> {
210
- let date = self.date_receiver(this_value, method)?;
211
- let timestamp_ms = self.date_object(date)?.timestamp_ms;
212
- Ok(date_time_fields_from_timestamp_ms(timestamp_ms))
213
- }
214
-
215
- pub(crate) fn call_date_get_utc_full_year(&self, this_value: Value) -> MustardResult<Value> {
216
- Ok(Value::Number(
217
- self.date_utc_fields(this_value, "getUTCFullYear")?
218
- .map_or(f64::NAN, |fields| fields.year as f64),
219
- ))
220
- }
221
-
222
- pub(crate) fn call_date_get_utc_month(&self, this_value: Value) -> MustardResult<Value> {
223
- Ok(Value::Number(
224
- self.date_utc_fields(this_value, "getUTCMonth")?
225
- .map_or(f64::NAN, |fields| f64::from(fields.month - 1)),
226
- ))
227
- }
228
-
229
- pub(crate) fn call_date_get_utc_date(&self, this_value: Value) -> MustardResult<Value> {
230
- Ok(Value::Number(
231
- self.date_utc_fields(this_value, "getUTCDate")?
232
- .map_or(f64::NAN, |fields| f64::from(fields.day)),
233
- ))
234
- }
235
-
236
- pub(crate) fn call_date_get_utc_hours(&self, this_value: Value) -> MustardResult<Value> {
237
- Ok(Value::Number(
238
- self.date_utc_fields(this_value, "getUTCHours")?
239
- .map_or(f64::NAN, |fields| f64::from(fields.hour)),
240
- ))
241
- }
242
-
243
- pub(crate) fn call_date_get_utc_minutes(&self, this_value: Value) -> MustardResult<Value> {
244
- Ok(Value::Number(
245
- self.date_utc_fields(this_value, "getUTCMinutes")?
246
- .map_or(f64::NAN, |fields| f64::from(fields.minute)),
247
- ))
248
- }
249
-
250
- pub(crate) fn call_date_get_utc_seconds(&self, this_value: Value) -> MustardResult<Value> {
251
- Ok(Value::Number(
252
- self.date_utc_fields(this_value, "getUTCSeconds")?
253
- .map_or(f64::NAN, |fields| f64::from(fields.second)),
254
- ))
255
- }
256
-
257
- pub(crate) fn date_timestamp_ms_from_value(&self, value: Value) -> MustardResult<f64> {
258
- let timestamp_ms = match value {
259
- Value::Number(value) => value,
260
- Value::String(value) => parse_date_timestamp_ms(&value),
261
- Value::Object(object) if self.is_date_object(object) => {
262
- self.date_object(object)?.timestamp_ms
263
- }
264
- Value::Undefined => f64::NAN,
265
- _ => {
266
- return Err(MustardError::runtime(
267
- "TypeError: Date currently supports only numeric, string, or Date arguments",
268
- ));
269
- }
270
- };
271
- Ok(time_clip(timestamp_ms))
272
- }
273
-
274
- pub(crate) fn call_error_ctor(&mut self, args: &[Value], name: &str) -> MustardResult<Value> {
275
- let options = args.get(1).cloned().unwrap_or(Value::Undefined);
276
- let cause = self.error_options_cause(options)?;
277
- self.make_error_object(name, args, None, None, cause)
278
- }
279
-
280
- pub(crate) fn call_number_ctor(&self, args: &[Value]) -> MustardResult<Value> {
281
- Ok(Value::Number(self.to_number(
282
- args.first().cloned().unwrap_or(Value::Undefined),
283
- )?))
284
- }
285
-
286
- pub(crate) fn call_number_parse_int(&mut self, args: &[Value]) -> MustardResult<Value> {
287
- let input = self.to_string(args.first().cloned().unwrap_or(Value::Undefined))?;
288
- let trimmed = input.trim_start();
289
- let radix_value = args.get(1).cloned().unwrap_or(Value::Undefined);
290
- let radix = if matches!(radix_value, Value::Undefined) {
291
- None
292
- } else {
293
- let parsed = self.to_integer(radix_value)?;
294
- if !(2..=36).contains(&parsed) {
295
- return Ok(Value::Number(f64::NAN));
296
- }
297
- Some(parsed as u32)
298
- };
299
-
300
- let (sign, remainder) = if let Some(stripped) = trimmed.strip_prefix('+') {
301
- (1.0, stripped)
302
- } else if let Some(stripped) = trimmed.strip_prefix('-') {
303
- (-1.0, stripped)
304
- } else {
305
- (1.0, trimmed)
306
- };
307
- let (radix, digits) =
308
- if radix.is_none() && (remainder.starts_with("0x") || remainder.starts_with("0X")) {
309
- (16u32, &remainder[2..])
310
- } else {
311
- (radix.unwrap_or(10), remainder)
312
- };
313
- let mut end = 0usize;
314
- let mut saw_digit = false;
315
- for (index, ch) in digits.char_indices() {
316
- if index % NUMBER_PARSE_HELPER_CHUNK_CHARS == 0 {
317
- self.charge_native_helper_work(1)?;
318
- }
319
- if ch.to_digit(radix).is_none() {
320
- break;
321
- }
322
- saw_digit = true;
323
- end = index + ch.len_utf8();
324
- }
325
- if !saw_digit {
326
- return Ok(Value::Number(f64::NAN));
327
- }
328
- let parsed = i128::from_str_radix(&digits[..end], radix).unwrap_or(0) as f64 * sign;
329
- Ok(Value::Number(parsed))
330
- }
331
-
332
- pub(crate) fn call_number_parse_float(&mut self, args: &[Value]) -> MustardResult<Value> {
333
- let input = self.to_string(args.first().cloned().unwrap_or(Value::Undefined))?;
334
- let trimmed = input.trim_start();
335
- if trimmed.starts_with("Infinity") || trimmed.starts_with("+Infinity") {
336
- return Ok(Value::Number(f64::INFINITY));
337
- }
338
- if trimmed.starts_with("-Infinity") {
339
- return Ok(Value::Number(f64::NEG_INFINITY));
340
- }
341
- let mut end = 0usize;
342
- let mut seen_digit = false;
343
- let mut seen_dot = false;
344
- let mut seen_exp = false;
345
- let mut allow_sign = true;
346
- for (index, ch) in trimmed.char_indices() {
347
- if index % NUMBER_PARSE_HELPER_CHUNK_CHARS == 0 {
348
- self.charge_native_helper_work(1)?;
349
- }
350
- let accepted = if allow_sign && matches!(ch, '+' | '-') {
351
- allow_sign = false;
352
- true
353
- } else if ch.is_ascii_digit() {
354
- seen_digit = true;
355
- allow_sign = false;
356
- true
357
- } else if ch == '.' && !seen_dot && !seen_exp {
358
- seen_dot = true;
359
- allow_sign = false;
360
- true
361
- } else if matches!(ch, 'e' | 'E') && seen_digit && !seen_exp {
362
- seen_exp = true;
363
- allow_sign = true;
364
- true
365
- } else {
366
- false
367
- };
368
- if !accepted {
369
- break;
370
- }
371
- end = index + ch.len_utf8();
372
- }
373
- let parsed = trimmed[..end].parse::<f64>().unwrap_or(f64::NAN);
374
- Ok(Value::Number(parsed))
375
- }
376
-
377
- pub(crate) fn call_number_is_nan(&self, args: &[Value]) -> Value {
378
- Value::Bool(matches!(args.first(), Some(Value::Number(value)) if value.is_nan()))
379
- }
380
-
381
- pub(crate) fn call_number_is_finite(&self, args: &[Value]) -> Value {
382
- Value::Bool(matches!(args.first(), Some(Value::Number(value)) if value.is_finite()))
383
- }
384
-
385
- pub(crate) fn call_number_is_integer(&self, args: &[Value]) -> Value {
386
- Value::Bool(matches!(
387
- args.first(),
388
- Some(Value::Number(value)) if value.is_finite() && value.fract() == 0.0
389
- ))
390
- }
391
-
392
- pub(crate) fn call_number_is_safe_integer(&self, args: &[Value]) -> Value {
393
- Value::Bool(matches!(
394
- args.first(),
395
- Some(Value::Number(value))
396
- if value.is_finite()
397
- && value.fract() == 0.0
398
- && value.abs() <= 9_007_199_254_740_991.0
399
- ))
400
- }
401
-
402
- pub(crate) fn call_string_ctor(&self, args: &[Value]) -> MustardResult<Value> {
403
- Ok(Value::String(self.to_string(
404
- args.first().cloned().unwrap_or(Value::Undefined),
405
- )?))
406
- }
407
-
408
- pub(crate) fn call_boolean_ctor(&self, args: &[Value]) -> MustardResult<Value> {
409
- Ok(Value::Bool(is_truthy(
410
- args.first().unwrap_or(&Value::Undefined),
411
- )))
412
- }
413
-
414
- pub(crate) fn call_number_to_string(&self, this_value: Value) -> MustardResult<Value> {
415
- Ok(Value::String(self.to_string(Value::Number(
416
- self.number_receiver(this_value, "toString")?,
417
- ))?))
418
- }
419
-
420
- pub(crate) fn call_number_value_of(&self, this_value: Value) -> MustardResult<Value> {
421
- Ok(Value::Number(self.number_receiver(this_value, "valueOf")?))
422
- }
423
-
424
- pub(crate) fn call_boolean_to_string(&self, this_value: Value) -> MustardResult<Value> {
425
- Ok(Value::String(self.to_string(Value::Bool(
426
- self.boolean_receiver(this_value, "toString")?,
427
- ))?))
428
- }
429
-
430
- pub(crate) fn call_boolean_value_of(&self, this_value: Value) -> MustardResult<Value> {
431
- Ok(Value::Bool(self.boolean_receiver(this_value, "valueOf")?))
432
- }
433
-
434
- pub(crate) fn construct_number(&mut self, args: &[Value]) -> MustardResult<Value> {
435
- let value = self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?;
436
- Ok(Value::Object(self.insert_object(
437
- IndexMap::new(),
438
- ObjectKind::NumberObject(value),
439
- )?))
440
- }
441
-
442
- pub(crate) fn construct_string(&mut self, args: &[Value]) -> MustardResult<Value> {
443
- let value = self.to_string(args.first().cloned().unwrap_or(Value::Undefined))?;
444
- Ok(Value::Object(self.insert_object(
445
- IndexMap::new(),
446
- ObjectKind::StringObject(value),
447
- )?))
448
- }
449
-
450
- pub(crate) fn construct_boolean(&mut self, args: &[Value]) -> MustardResult<Value> {
451
- let value = is_truthy(args.first().unwrap_or(&Value::Undefined));
452
- Ok(Value::Object(self.insert_object(
453
- IndexMap::new(),
454
- ObjectKind::BooleanObject(value),
455
- )?))
456
- }
457
-
458
- pub(crate) fn call_math_abs(&self, args: &[Value]) -> MustardResult<Value> {
459
- Ok(Value::Number(
460
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
461
- .abs(),
462
- ))
463
- }
464
-
465
- pub(crate) fn call_math_max(&self, args: &[Value]) -> MustardResult<Value> {
466
- let mut value = f64::NEG_INFINITY;
467
- for arg in args {
468
- value = value.max(self.to_number(arg.clone())?);
469
- }
470
- Ok(Value::Number(value))
471
- }
472
-
473
- pub(crate) fn call_math_min(&self, args: &[Value]) -> MustardResult<Value> {
474
- let mut value = f64::INFINITY;
475
- for arg in args {
476
- value = value.min(self.to_number(arg.clone())?);
477
- }
478
- Ok(Value::Number(value))
479
- }
480
-
481
- pub(crate) fn call_math_floor(&self, args: &[Value]) -> MustardResult<Value> {
482
- Ok(Value::Number(
483
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
484
- .floor(),
485
- ))
486
- }
487
-
488
- pub(crate) fn call_math_ceil(&self, args: &[Value]) -> MustardResult<Value> {
489
- Ok(Value::Number(
490
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
491
- .ceil(),
492
- ))
493
- }
494
-
495
- pub(crate) fn call_math_round(&self, args: &[Value]) -> MustardResult<Value> {
496
- Ok(Value::Number(
497
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
498
- .round(),
499
- ))
500
- }
501
-
502
- pub(crate) fn call_math_pow(&self, args: &[Value]) -> MustardResult<Value> {
503
- Ok(Value::Number(
504
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
505
- .powf(self.to_number(args.get(1).cloned().unwrap_or(Value::Undefined))?),
506
- ))
507
- }
508
-
509
- pub(crate) fn call_math_sqrt(&self, args: &[Value]) -> MustardResult<Value> {
510
- Ok(Value::Number(
511
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
512
- .sqrt(),
513
- ))
514
- }
515
-
516
- pub(crate) fn call_math_trunc(&self, args: &[Value]) -> MustardResult<Value> {
517
- Ok(Value::Number(
518
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
519
- .trunc(),
520
- ))
521
- }
522
-
523
- pub(crate) fn call_math_sign(&self, args: &[Value]) -> MustardResult<Value> {
524
- let value = self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?;
525
- Ok(Value::Number(if value.is_nan() {
526
- f64::NAN
527
- } else if value == 0.0 {
528
- value
529
- } else if value.is_sign_positive() {
530
- 1.0
531
- } else {
532
- -1.0
533
- }))
534
- }
535
-
536
- pub(crate) fn call_math_log(&self, args: &[Value]) -> MustardResult<Value> {
537
- Ok(Value::Number(
538
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
539
- .ln(),
540
- ))
541
- }
542
-
543
- pub(crate) fn call_math_exp(&self, args: &[Value]) -> MustardResult<Value> {
544
- Ok(Value::Number(
545
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
546
- .exp(),
547
- ))
548
- }
549
-
550
- pub(crate) fn call_math_log2(&self, args: &[Value]) -> MustardResult<Value> {
551
- Ok(Value::Number(
552
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
553
- .log2(),
554
- ))
555
- }
556
-
557
- pub(crate) fn call_math_log10(&self, args: &[Value]) -> MustardResult<Value> {
558
- Ok(Value::Number(
559
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
560
- .log10(),
561
- ))
562
- }
563
-
564
- pub(crate) fn call_math_sin(&self, args: &[Value]) -> MustardResult<Value> {
565
- Ok(Value::Number(
566
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
567
- .sin(),
568
- ))
569
- }
570
-
571
- pub(crate) fn call_math_cos(&self, args: &[Value]) -> MustardResult<Value> {
572
- Ok(Value::Number(
573
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
574
- .cos(),
575
- ))
576
- }
577
-
578
- pub(crate) fn call_math_atan2(&self, args: &[Value]) -> MustardResult<Value> {
579
- Ok(Value::Number(
580
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
581
- .atan2(self.to_number(args.get(1).cloned().unwrap_or(Value::Undefined))?),
582
- ))
583
- }
584
-
585
- pub(crate) fn call_math_hypot(&self, args: &[Value]) -> MustardResult<Value> {
586
- let mut value: f64 = 0.0;
587
- for arg in args {
588
- value = value.hypot(self.to_number(arg.clone())?);
589
- }
590
- Ok(Value::Number(value))
591
- }
592
-
593
- pub(crate) fn call_math_cbrt(&self, args: &[Value]) -> MustardResult<Value> {
594
- Ok(Value::Number(
595
- self.to_number(args.first().cloned().unwrap_or(Value::Undefined))?
596
- .cbrt(),
597
- ))
598
- }
599
-
600
- pub(crate) fn call_math_random(&self) -> Value {
601
- Value::Number(random::<f64>())
602
- }
603
-
604
- pub(crate) fn call_json_stringify(&mut self, args: &[Value]) -> MustardResult<Value> {
605
- let value = args.first().cloned().unwrap_or(Value::Undefined);
606
- let mut traversal = JsonStringifyTraversalState::default();
607
- let mut output = String::new();
608
- if self.json_stringify_value(&value, &mut traversal, &mut output)? {
609
- Ok(Value::String(output))
610
- } else {
611
- Ok(Value::Undefined)
612
- }
613
- }
614
-
615
- pub(crate) fn call_json_parse(&mut self, args: &[Value]) -> MustardResult<Value> {
616
- let source = self.to_string(args.first().cloned().unwrap_or(Value::Undefined))?;
617
- let mut reader = BudgetedJsonReader::new(self, source.as_bytes());
618
- let parsed: serde_json::Value = match serde_json::from_reader(&mut reader) {
619
- Ok(parsed) => parsed,
620
- Err(error) => {
621
- if let Some(runtime_error) = reader.into_failure() {
622
- return Err(runtime_error);
623
- }
624
- return Err(MustardError::runtime(error.to_string()));
625
- }
626
- };
627
- drop(reader);
628
- self.value_from_json(parsed)
629
- }
630
-
631
- fn json_stringify_value(
632
- &mut self,
633
- value: &Value,
634
- traversal: &mut JsonStringifyTraversalState,
635
- output: &mut String,
636
- ) -> MustardResult<bool> {
637
- self.charge_native_helper_work(1)?;
638
- match value {
639
- Value::Undefined => Ok(false),
640
- Value::Null => {
641
- self.push_json_fragment(output, "null")?;
642
- Ok(true)
643
- }
644
- Value::Bool(value) => {
645
- if *value {
646
- self.push_json_fragment(output, "true")?;
647
- } else {
648
- self.push_json_fragment(output, "false")?;
649
- }
650
- Ok(true)
651
- }
652
- Value::Number(value) => {
653
- self.push_json_fragment(output, &json_number_to_string(*value))?;
654
- Ok(true)
655
- }
656
- Value::BigInt(_) => Err(MustardError::runtime(
657
- "TypeError: Do not know how to serialize a BigInt",
658
- )),
659
- Value::String(value) => {
660
- self.push_json_string(output, value)?;
661
- Ok(true)
662
- }
663
- Value::Closure(_) | Value::BuiltinFunction(_) | Value::HostFunction(_) => Ok(false),
664
- Value::Array(array) => {
665
- if !traversal.arrays.insert(*array) {
666
- return Err(json_stringify_cycle_error());
667
- }
668
- let elements = self
669
- .arrays
670
- .get(*array)
671
- .ok_or_else(|| MustardError::runtime("array missing"))?
672
- .elements
673
- .clone();
674
- let result = (|| {
675
- self.push_json_fragment(output, "[")?;
676
- for (index, value) in elements.iter().enumerate() {
677
- if index > 0 {
678
- self.push_json_fragment(output, ",")?;
679
- }
680
- let value = value.as_ref().unwrap_or(&Value::Undefined);
681
- if !self.json_stringify_value(value, traversal, output)? {
682
- self.push_json_fragment(output, "null")?;
683
- }
684
- }
685
- self.push_json_fragment(output, "]")?;
686
- Ok(true)
687
- })();
688
- traversal.arrays.remove(array);
689
- result
690
- }
691
- Value::Object(object) => self.json_stringify_object(*object, traversal, output),
692
- Value::Map(_) | Value::Set(_) | Value::Iterator(_) | Value::Promise(_) => {
693
- self.push_json_fragment(output, "{}")?;
694
- Ok(true)
695
- }
696
- }
697
- }
698
-
699
- fn json_stringify_object(
700
- &mut self,
701
- object: ObjectKey,
702
- traversal: &mut JsonStringifyTraversalState,
703
- output: &mut String,
704
- ) -> MustardResult<bool> {
705
- let date_timestamp_ms = {
706
- let object_ref = self
707
- .objects
708
- .get(object)
709
- .ok_or_else(|| MustardError::runtime("object missing"))?;
710
- match &object_ref.kind {
711
- ObjectKind::Date(date) => Some(date.timestamp_ms),
712
- _ => None,
713
- }
714
- };
715
- if let Some(timestamp_ms) = date_timestamp_ms {
716
- self.push_json_fragment(output, &self.json_stringify_date(timestamp_ms)?)?;
717
- return Ok(true);
718
- }
719
-
720
- if !traversal.objects.insert(object) {
721
- return Err(json_stringify_cycle_error());
722
- }
723
-
724
- let keys = {
725
- let object_ref = self
726
- .objects
727
- .get(object)
728
- .ok_or_else(|| MustardError::runtime("object missing"))?;
729
- match &object_ref.kind {
730
- ObjectKind::Error(_) => {
731
- ordered_own_property_keys_filtered(&object_ref.properties, |key, _| {
732
- key != "name" && key != "message"
733
- })
734
- }
735
- _ => ordered_own_property_keys(&object_ref.properties),
736
- }
737
- };
738
- let result = (|| {
739
- self.push_json_fragment(output, "{")?;
740
- let mut wrote_any = false;
741
- for key in keys {
742
- let value = self
743
- .objects
744
- .get(object)
745
- .ok_or_else(|| MustardError::runtime("object missing"))?
746
- .properties
747
- .get(&key)
748
- .cloned()
749
- .ok_or_else(|| MustardError::runtime("object property missing"))?;
750
- let rewind = output.len();
751
- if wrote_any {
752
- self.push_json_fragment(output, ",")?;
753
- }
754
- self.push_json_string(output, &key)?;
755
- self.push_json_fragment(output, ":")?;
756
- if !self.json_stringify_value(&value, traversal, output)? {
757
- output.truncate(rewind);
758
- continue;
759
- }
760
- wrote_any = true;
761
- }
762
- self.push_json_fragment(output, "}")?;
763
- Ok(true)
764
- })();
765
-
766
- traversal.objects.remove(&object);
767
- result
768
- }
769
-
770
- fn json_stringify_date(&self, timestamp_ms: f64) -> MustardResult<String> {
771
- if !timestamp_ms.is_finite() {
772
- return Ok("null".to_string());
773
- }
774
-
775
- let timestamp_nanos = timestamp_ms * 1_000_000.0;
776
- if !timestamp_nanos.is_finite()
777
- || timestamp_nanos < i128::MIN as f64
778
- || timestamp_nanos > i128::MAX as f64
779
- {
780
- return Ok("null".to_string());
781
- }
782
-
783
- let Some(rendered) = format_iso_datetime(timestamp_ms) else {
784
- return Ok("null".to_string());
785
- };
786
- serde_json::to_string(&rendered).map_err(|error| MustardError::runtime(error.to_string()))
787
- }
788
-
789
- fn push_json_fragment(&mut self, output: &mut String, fragment: &str) -> MustardResult<()> {
790
- let next_len = output
791
- .len()
792
- .checked_add(fragment.len())
793
- .ok_or_else(|| limit_error("heap limit exceeded"))?;
794
- self.ensure_heap_capacity(next_len)?;
795
- let units = fragment.len().max(1).div_ceil(JSON_HELPER_IO_CHUNK_BYTES);
796
- self.charge_native_helper_work(units)?;
797
- output.push_str(fragment);
798
- Ok(())
799
- }
800
-
801
- fn push_json_string(&mut self, output: &mut String, value: &str) -> MustardResult<()> {
802
- let mut writer = JsonOutputWriter::new(self, output);
803
- let result = serde_json::to_writer(&mut writer, value);
804
- match result {
805
- Ok(()) => {
806
- if let Some(runtime_error) = writer.into_failure() {
807
- Err(runtime_error)
808
- } else {
809
- Ok(())
810
- }
811
- }
812
- Err(error) => {
813
- if let Some(runtime_error) = writer.into_failure() {
814
- Err(runtime_error)
815
- } else {
816
- Err(MustardError::runtime(error.to_string()))
817
- }
818
- }
819
- }
820
- }
821
- }
822
-
823
- impl Runtime {
824
- fn error_options_cause(&self, options: Value) -> MustardResult<Option<Option<Value>>> {
825
- match options {
826
- Value::Undefined | Value::Null => Ok(None),
827
- Value::Object(object) => {
828
- let object = self
829
- .objects
830
- .get(object)
831
- .ok_or_else(|| MustardError::runtime("object missing"))?;
832
- let cause = object.properties.get("cause").cloned();
833
- Ok(Some(cause))
834
- }
835
- Value::Array(array) => {
836
- let array = self
837
- .arrays
838
- .get(array)
839
- .ok_or_else(|| MustardError::runtime("array missing"))?;
840
- Ok(Some(array.properties.get("cause").cloned()))
841
- }
842
- _ => Err(MustardError::runtime(
843
- "TypeError: Error options must be an object in the supported surface",
844
- )),
845
- }
846
- }
847
- }
848
-
849
- fn json_number_to_string(value: f64) -> String {
850
- if !value.is_finite() {
851
- "null".to_string()
852
- } else {
853
- value.to_js_string()
854
- }
855
- }
856
-
857
- fn json_stringify_cycle_error() -> MustardError {
858
- MustardError::runtime("TypeError: Converting circular structure to JSON")
859
- }