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,127 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ use crate::diagnostic::{DiagnosticKind, MustardError, MustardResult};
4
+
5
+ use super::{
6
+ Runtime, api::ExecutionSnapshot, bytecode::BytecodeProgram,
7
+ validation::validate_bytecode_program,
8
+ };
9
+
10
+ const SERIAL_FORMAT_VERSION: u32 = 1;
11
+
12
+ pub fn dump_program(program: &BytecodeProgram) -> MustardResult<Vec<u8>> {
13
+ bincode::serialize(&SerializedProgram {
14
+ version: SERIAL_FORMAT_VERSION,
15
+ program: program.clone(),
16
+ })
17
+ .map_err(|error| MustardError::Message {
18
+ kind: DiagnosticKind::Serialization,
19
+ message: error.to_string(),
20
+ span: None,
21
+ traceback: Vec::new(),
22
+ })
23
+ }
24
+
25
+ pub fn load_program(bytes: &[u8]) -> MustardResult<BytecodeProgram> {
26
+ let decoded: SerializedProgram =
27
+ bincode::deserialize(bytes).map_err(|error| MustardError::Message {
28
+ kind: DiagnosticKind::Serialization,
29
+ message: error.to_string(),
30
+ span: None,
31
+ traceback: Vec::new(),
32
+ })?;
33
+ if decoded.version != SERIAL_FORMAT_VERSION {
34
+ return Err(MustardError::Message {
35
+ kind: DiagnosticKind::Serialization,
36
+ message: format!(
37
+ "serialized program version mismatch: expected {}, got {}",
38
+ SERIAL_FORMAT_VERSION, decoded.version
39
+ ),
40
+ span: None,
41
+ traceback: Vec::new(),
42
+ });
43
+ }
44
+ validate_bytecode_program(&decoded.program)?;
45
+ Ok(decoded.program)
46
+ }
47
+
48
+ pub fn dump_snapshot(snapshot: &ExecutionSnapshot) -> MustardResult<Vec<u8>> {
49
+ bincode::serialize(&SerializedSnapshot {
50
+ version: SERIAL_FORMAT_VERSION,
51
+ runtime: snapshot.runtime.clone(),
52
+ })
53
+ .map_err(|error| MustardError::Message {
54
+ kind: DiagnosticKind::Serialization,
55
+ message: error.to_string(),
56
+ span: None,
57
+ traceback: Vec::new(),
58
+ })
59
+ }
60
+
61
+ pub fn load_snapshot(bytes: &[u8]) -> MustardResult<ExecutionSnapshot> {
62
+ let decoded: SerializedSnapshot =
63
+ bincode::deserialize(bytes).map_err(|error| MustardError::Message {
64
+ kind: DiagnosticKind::Serialization,
65
+ message: error.to_string(),
66
+ span: None,
67
+ traceback: Vec::new(),
68
+ })?;
69
+ if decoded.version != SERIAL_FORMAT_VERSION {
70
+ return Err(MustardError::Message {
71
+ kind: DiagnosticKind::Serialization,
72
+ message: format!(
73
+ "serialized snapshot version mismatch: expected {}, got {}",
74
+ SERIAL_FORMAT_VERSION, decoded.version
75
+ ),
76
+ span: None,
77
+ traceback: Vec::new(),
78
+ });
79
+ }
80
+ ExecutionSnapshot::restore_loaded_runtime(decoded.runtime)
81
+ }
82
+
83
+ pub fn canonical_snapshot_auth_bytes(bytes: &[u8]) -> MustardResult<Vec<u8>> {
84
+ let decoded: SerializedSnapshot =
85
+ bincode::deserialize(bytes).map_err(|error| MustardError::Message {
86
+ kind: DiagnosticKind::Serialization,
87
+ message: error.to_string(),
88
+ span: None,
89
+ traceback: Vec::new(),
90
+ })?;
91
+ if decoded.version != SERIAL_FORMAT_VERSION {
92
+ return Err(MustardError::Message {
93
+ kind: DiagnosticKind::Serialization,
94
+ message: format!(
95
+ "serialized snapshot version mismatch: expected {}, got {}",
96
+ SERIAL_FORMAT_VERSION, decoded.version
97
+ ),
98
+ span: None,
99
+ traceback: Vec::new(),
100
+ });
101
+ }
102
+
103
+ let mut snapshot = ExecutionSnapshot::restore_loaded_runtime(decoded.runtime)?;
104
+ snapshot.runtime.snapshot_nonce = 0;
105
+ bincode::serialize(&SerializedSnapshot {
106
+ version: SERIAL_FORMAT_VERSION,
107
+ runtime: snapshot.runtime,
108
+ })
109
+ .map_err(|error| MustardError::Message {
110
+ kind: DiagnosticKind::Serialization,
111
+ message: error.to_string(),
112
+ span: None,
113
+ traceback: Vec::new(),
114
+ })
115
+ }
116
+
117
+ #[derive(Debug, Clone, Serialize, Deserialize)]
118
+ struct SerializedProgram {
119
+ version: u32,
120
+ program: BytecodeProgram,
121
+ }
122
+
123
+ #[derive(Debug, Clone, Serialize, Deserialize)]
124
+ struct SerializedSnapshot {
125
+ version: u32,
126
+ runtime: Runtime,
127
+ }
@@ -0,0 +1,108 @@
1
+ //! Shared runtime helpers used across sibling runtime modules.
2
+
3
+ use std::sync::atomic::{AtomicU64, Ordering};
4
+
5
+ use super::*;
6
+
7
+ pub(super) struct CallbackCallOptions<'a> {
8
+ pub(super) non_callable_message: &'a str,
9
+ pub(super) host_suspension_message: &'a str,
10
+ pub(super) unsettled_message: &'a str,
11
+ pub(super) allow_host_suspension: bool,
12
+ pub(super) allow_pending_promise_result: bool,
13
+ }
14
+
15
+ pub(super) fn limit_error(message: impl Into<String>) -> MustardError {
16
+ MustardError::Message {
17
+ kind: DiagnosticKind::Limit,
18
+ message: message.into(),
19
+ span: None,
20
+ traceback: Vec::new(),
21
+ }
22
+ }
23
+
24
+ pub(super) fn serialization_error(message: impl Into<String>) -> MustardError {
25
+ MustardError::Message {
26
+ kind: DiagnosticKind::Serialization,
27
+ message: message.into(),
28
+ span: None,
29
+ traceback: Vec::new(),
30
+ }
31
+ }
32
+
33
+ pub(super) fn next_snapshot_nonce() -> u64 {
34
+ static NEXT_SNAPSHOT_NONCE: AtomicU64 = AtomicU64::new(1);
35
+ NEXT_SNAPSHOT_NONCE.fetch_add(1, Ordering::Relaxed)
36
+ }
37
+
38
+ pub(super) fn pop_many(stack: &mut Vec<Value>, count: usize) -> MustardResult<Vec<Value>> {
39
+ if stack.len() < count {
40
+ return Err(MustardError::runtime("stack underflow"));
41
+ }
42
+ let start = stack.len() - count;
43
+ Ok(stack.drain(start..).collect())
44
+ }
45
+
46
+ pub(super) fn resume_behavior_for_capability(capability: &str) -> ResumeBehavior {
47
+ match capability {
48
+ "console.log" | "console.warn" | "console.error" => ResumeBehavior::Undefined,
49
+ _ => ResumeBehavior::Value,
50
+ }
51
+ }
52
+
53
+ pub(super) fn is_truthy(value: &Value) -> bool {
54
+ match value {
55
+ Value::Undefined | Value::Null => false,
56
+ Value::Bool(value) => *value,
57
+ Value::Number(value) => *value != 0.0 && !value.is_nan(),
58
+ Value::BigInt(value) => value != &0.into(),
59
+ Value::String(value) => !value.is_empty(),
60
+ Value::Object(_)
61
+ | Value::Array(_)
62
+ | Value::Map(_)
63
+ | Value::Set(_)
64
+ | Value::Iterator(_)
65
+ | Value::Promise(_)
66
+ | Value::Closure(_)
67
+ | Value::BuiltinFunction(_)
68
+ | Value::HostFunction(_) => true,
69
+ }
70
+ }
71
+
72
+ pub(super) fn is_callable(value: &Value) -> bool {
73
+ matches!(
74
+ value,
75
+ Value::Closure(_) | Value::BuiltinFunction(_) | Value::HostFunction(_)
76
+ )
77
+ }
78
+
79
+ pub(super) fn strict_equal(left: &Value, right: &Value) -> bool {
80
+ match (left, right) {
81
+ (Value::Undefined, Value::Undefined) => true,
82
+ (Value::Null, Value::Null) => true,
83
+ (Value::Bool(left), Value::Bool(right)) => left == right,
84
+ (Value::Number(left), Value::Number(right)) => left == right,
85
+ (Value::BigInt(left), Value::BigInt(right)) => left == right,
86
+ (Value::String(left), Value::String(right)) => left == right,
87
+ (Value::Object(left), Value::Object(right)) => left == right,
88
+ (Value::Array(left), Value::Array(right)) => left == right,
89
+ (Value::Map(left), Value::Map(right)) => left == right,
90
+ (Value::Set(left), Value::Set(right)) => left == right,
91
+ (Value::Iterator(left), Value::Iterator(right)) => left == right,
92
+ (Value::Promise(left), Value::Promise(right)) => left == right,
93
+ (Value::Closure(left), Value::Closure(right)) => left == right,
94
+ (Value::BuiltinFunction(left), Value::BuiltinFunction(right)) => left == right,
95
+ (Value::HostFunction(left), Value::HostFunction(right)) => left == right,
96
+ _ => false,
97
+ }
98
+ }
99
+
100
+ pub(super) fn same_value_zero(left: &Value, right: &Value) -> bool {
101
+ match (left, right) {
102
+ (Value::Number(left), Value::Number(right)) => {
103
+ (left == right) || (left.is_nan() && right.is_nan())
104
+ }
105
+ (Value::BigInt(left), Value::BigInt(right)) => left == right,
106
+ _ => strict_equal(left, right),
107
+ }
108
+ }
@@ -0,0 +1,93 @@
1
+ use crate::{RuntimeLimits, compile};
2
+
3
+ use super::*;
4
+
5
+ fn suspend_async_host_wait(source: &str) -> Suspension {
6
+ let program = compile(source).expect("source should compile");
7
+ match start(
8
+ &program,
9
+ ExecutionOptions {
10
+ capabilities: vec!["fetch_data".to_string()],
11
+ limits: RuntimeLimits::default(),
12
+ cancellation_token: None,
13
+ ..ExecutionOptions::default()
14
+ },
15
+ )
16
+ .expect("execution should suspend")
17
+ {
18
+ ExecutionStep::Suspended(suspension) => *suspension,
19
+ other => panic!("expected suspension, got {other:?}"),
20
+ }
21
+ }
22
+
23
+ #[test]
24
+ fn rejects_invalid_async_continuation_frame_state() {
25
+ let mut suspension = suspend_async_host_wait(
26
+ r#"
27
+ async function main() {
28
+ const value = await fetch_data(1);
29
+ return value + 2;
30
+ }
31
+ main();
32
+ "#,
33
+ );
34
+
35
+ let continuation = suspension
36
+ .snapshot
37
+ .runtime
38
+ .promises
39
+ .values_mut()
40
+ .find_map(|promise| promise.awaiters.first_mut())
41
+ .expect("awaiting async promise continuation should exist");
42
+ continuation.frames[0].ip = 999;
43
+
44
+ let bytes = dump_snapshot(&suspension.snapshot).expect("snapshot should serialize");
45
+ let error =
46
+ load_snapshot(&bytes).expect_err("invalid async continuation should fail validation");
47
+ assert!(
48
+ error
49
+ .to_string()
50
+ .contains("frame instruction pointer 999 is out of range"),
51
+ "unexpected error: {error}"
52
+ );
53
+ }
54
+
55
+ #[test]
56
+ fn rejects_invalid_microtask_frame_state() {
57
+ let mut suspension = suspend_async_host_wait(
58
+ r#"
59
+ async function main() {
60
+ const value = await fetch_data(1);
61
+ return value + 2;
62
+ }
63
+ main();
64
+ "#,
65
+ );
66
+
67
+ let mut continuation = suspension
68
+ .snapshot
69
+ .runtime
70
+ .promises
71
+ .values()
72
+ .find_map(|promise| promise.awaiters.first().cloned())
73
+ .expect("awaiting async promise continuation should exist");
74
+ continuation.frames[0].ip = 999;
75
+ suspension
76
+ .snapshot
77
+ .runtime
78
+ .microtasks
79
+ .push_back(MicrotaskJob::ResumeAsync {
80
+ continuation,
81
+ outcome: PromiseOutcome::Fulfilled(Value::Number(1.0)),
82
+ });
83
+
84
+ let bytes = dump_snapshot(&suspension.snapshot).expect("snapshot should serialize");
85
+ let error =
86
+ load_snapshot(&bytes).expect_err("invalid microtask continuation should fail validation");
87
+ assert!(
88
+ error
89
+ .to_string()
90
+ .contains("frame instruction pointer 999 is out of range"),
91
+ "unexpected error: {error}"
92
+ );
93
+ }