openzoo 0.51.2 → 0.51.3

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.
@@ -0,0 +1,902 @@
1
+ //! The bytecode interpreter of the shared runtime.
2
+ //!
3
+ //! A site's code is a module (see `Module::parse`) produced by
4
+ //! `lib/compile/bytecode.js` from the same IR the Rust backend prints. The
5
+ //! machine is a value stack over `Val`; functions have flat local slots;
6
+ //! callbacks, local closures and try bodies are inline regions of their
7
+ //! parent function that share its locals and get their own `args`.
8
+ use crate::helpers;
9
+ use crate::json;
10
+ use crate::val::{global_call, math, Val};
11
+ use crate::wire::Resp;
12
+ use crate::Ctx;
13
+ use alloc::{string::String, vec::Vec};
14
+
15
+ pub const MAGIC: &[u8; 4] = b"ZOOB";
16
+ pub const VERSION: u8 = 1;
17
+ /// Interpreter recursion limit (callbacks inside callbacks, helper calls).
18
+ const MAX_DEPTH: u32 = 24;
19
+ /// Instruction budget per invoke, so a runaway loop fails with a message
20
+ /// instead of burning the whole compute budget silently.
21
+ const MAX_STEPS: u32 = 400_000;
22
+
23
+ // ---- opcodes (keep in sync with lib/compile/bytecode.js)
24
+ pub const OP_NOP: u8 = 0;
25
+ pub const OP_PUSH_CONST: u8 = 1;
26
+ pub const OP_PUSH_UNDEF: u8 = 2;
27
+ pub const OP_PUSH_NULL: u8 = 3;
28
+ pub const OP_DUP: u8 = 4;
29
+ pub const OP_POP: u8 = 5;
30
+ pub const OP_SWAP: u8 = 6;
31
+ pub const OP_LOAD: u8 = 7;
32
+ pub const OP_STORE: u8 = 8;
33
+ pub const OP_LOAD_ARG: u8 = 9;
34
+ pub const OP_ARGS_FROM: u8 = 10;
35
+ pub const OP_NEW_OBJ: u8 = 11;
36
+ pub const OP_OBJ_SET_K: u8 = 12;
37
+ pub const OP_OBJ_SET: u8 = 13;
38
+ pub const OP_OBJ_SPREAD: u8 = 14;
39
+ pub const OP_NEW_ARR: u8 = 15;
40
+ pub const OP_ARR_PUSH: u8 = 16;
41
+ pub const OP_ARR_SPREAD: u8 = 17;
42
+ pub const OP_GET_K: u8 = 18;
43
+ pub const OP_GET: u8 = 19;
44
+ pub const OP_TEMPLATE: u8 = 20;
45
+ pub const OP_BIN: u8 = 21;
46
+ pub const OP_CMP: u8 = 22;
47
+ pub const OP_UNARY: u8 = 23;
48
+ pub const OP_IN: u8 = 24;
49
+ pub const OP_JMP: u8 = 25;
50
+ pub const OP_JF: u8 = 26;
51
+ pub const OP_JT: u8 = 27;
52
+ pub const OP_JF_KEEP: u8 = 28;
53
+ pub const OP_JT_KEEP: u8 = 29;
54
+ pub const OP_JNN_KEEP: u8 = 30;
55
+ pub const OP_JNULLISH_UNDEF: u8 = 31;
56
+ pub const OP_CALLM: u8 = 32;
57
+ pub const OP_HOST: u8 = 33;
58
+ pub const OP_HELPER: u8 = 34;
59
+ pub const OP_MATH: u8 = 35;
60
+ pub const OP_GLOBAL: u8 = 36;
61
+ pub const OP_JSON_PARSE: u8 = 37;
62
+ pub const OP_JSON_STRINGIFY: u8 = 38;
63
+ pub const OP_KEYS: u8 = 39;
64
+ pub const OP_VALUES: u8 = 40;
65
+ pub const OP_ENTRIES: u8 = 41;
66
+ pub const OP_ISARRAY: u8 = 42;
67
+ pub const OP_NEW_ERROR: u8 = 43;
68
+ pub const OP_LOG: u8 = 44;
69
+ pub const OP_RESP: u8 = 45;
70
+ pub const OP_PARAMS: u8 = 46;
71
+ pub const OP_PARAM: u8 = 47;
72
+ pub const OP_CALL_FN: u8 = 48;
73
+ pub const OP_CALL_INLINE: u8 = 49;
74
+ pub const OP_RET: u8 = 50;
75
+ pub const OP_SEND: u8 = 51;
76
+ pub const OP_THROW: u8 = 52;
77
+ pub const OP_TRY_PUSH: u8 = 53;
78
+ pub const OP_TRY_POP: u8 = 54;
79
+ pub const OP_CAUGHT: u8 = 55;
80
+ pub const OP_ITER_INIT: u8 = 56;
81
+ pub const OP_ITER_NEXT: u8 = 57;
82
+ pub const OP_STORE_PATH: u8 = 58;
83
+ pub const OP_STORE_PATH_DISCARD: u8 = 59;
84
+ pub const OP_DELETE_PATH: u8 = 60;
85
+ pub const OP_TRUTHY: u8 = 61;
86
+ pub const OP_NULLISH: u8 = 62;
87
+ pub const OP_STRICT_EQ_KEEP: u8 = 63;
88
+
89
+ // CALLM flags
90
+ const CALLM_CB: u8 = 1;
91
+ const CALLM_DEFAULT_SORT: u8 = 2;
92
+ const CALLM_MUT: u8 = 4;
93
+ // HOST flags
94
+ const HOST_Q: u8 = 1;
95
+ const HOST_VOID: u8 = 2;
96
+
97
+ pub struct Func<'m> {
98
+ pub nlocals: u16,
99
+ pub code: &'m [u8],
100
+ }
101
+
102
+ pub struct RouteDef {
103
+ pub style: u8, // 0 node, 1 web
104
+ pub params: Vec<(String, bool)>,
105
+ pub node_fn: u16,
106
+ pub methods: Vec<(u8, u16)>,
107
+ }
108
+
109
+ pub struct Module<'m> {
110
+ pub consts: Vec<Val>,
111
+ pub env: Vec<(String, String)>,
112
+ pub funcs: Vec<Func<'m>>,
113
+ pub routes: Vec<RouteDef>,
114
+ }
115
+
116
+ struct R<'m> {
117
+ b: &'m [u8],
118
+ i: usize,
119
+ }
120
+ impl<'m> R<'m> {
121
+ fn u8(&mut self) -> Result<u8, ()> {
122
+ let v = *self.b.get(self.i).ok_or(())?;
123
+ self.i += 1;
124
+ Ok(v)
125
+ }
126
+ fn u16(&mut self) -> Result<u16, ()> {
127
+ let s = self.b.get(self.i..self.i + 2).ok_or(())?;
128
+ self.i += 2;
129
+ Ok(u16::from_le_bytes([s[0], s[1]]))
130
+ }
131
+ fn u32(&mut self) -> Result<u32, ()> {
132
+ let s = self.b.get(self.i..self.i + 4).ok_or(())?;
133
+ self.i += 4;
134
+ Ok(u32::from_le_bytes([s[0], s[1], s[2], s[3]]))
135
+ }
136
+ fn bytes(&mut self, n: usize) -> Result<&'m [u8], ()> {
137
+ let s = self.b.get(self.i..self.i + n).ok_or(())?;
138
+ self.i += n;
139
+ Ok(s)
140
+ }
141
+ }
142
+
143
+ impl<'m> Module<'m> {
144
+ pub fn parse(b: &'m [u8]) -> Result<Module<'m>, ()> {
145
+ let mut r = R { b, i: 0 };
146
+ if r.bytes(4)? != MAGIC || r.u8()? != VERSION {
147
+ return Err(());
148
+ }
149
+ let nc = r.u16()? as usize;
150
+ let mut consts = Vec::with_capacity(nc);
151
+ for _ in 0..nc {
152
+ consts.push(match r.u8()? {
153
+ 0 => Val::Undef,
154
+ 1 => Val::Null,
155
+ 2 => Val::Bool(false),
156
+ 3 => Val::Bool(true),
157
+ 4 => {
158
+ let s = r.bytes(8)?;
159
+ let mut a = [0u8; 8];
160
+ a.copy_from_slice(s);
161
+ Val::Num(f64::from_le_bytes(a))
162
+ }
163
+ 5 => {
164
+ let n = r.u32()? as usize;
165
+ Val::Str(String::from(core::str::from_utf8(r.bytes(n)?).map_err(|_| ())?))
166
+ }
167
+ _ => return Err(()),
168
+ });
169
+ }
170
+ let ne = r.u16()? as usize;
171
+ let mut env = Vec::with_capacity(ne);
172
+ for _ in 0..ne {
173
+ let k = consts.get(r.u16()? as usize).ok_or(())?.to_js_string();
174
+ let v = consts.get(r.u16()? as usize).ok_or(())?.to_js_string();
175
+ env.push((k, v));
176
+ }
177
+ let nf = r.u16()? as usize;
178
+ let mut funcs = Vec::with_capacity(nf);
179
+ for _ in 0..nf {
180
+ let nlocals = r.u16()?;
181
+ let len = r.u32()? as usize;
182
+ funcs.push(Func { nlocals, code: r.bytes(len)? });
183
+ }
184
+ let nr = r.u8()? as usize;
185
+ let mut routes = Vec::with_capacity(nr);
186
+ for _ in 0..nr {
187
+ let style = r.u8()?;
188
+ let np = r.u8()? as usize;
189
+ let mut params = Vec::with_capacity(np);
190
+ for _ in 0..np {
191
+ let name = consts.get(r.u16()? as usize).ok_or(())?.to_js_string();
192
+ let ca = r.u8()? != 0;
193
+ params.push((name, ca));
194
+ }
195
+ let mut node_fn = 0;
196
+ let mut methods = Vec::new();
197
+ if style == 0 {
198
+ node_fn = r.u16()?;
199
+ } else {
200
+ let nm = r.u8()? as usize;
201
+ for _ in 0..nm {
202
+ let m = r.u8()?;
203
+ let f = r.u16()?;
204
+ methods.push((m, f));
205
+ }
206
+ }
207
+ routes.push(RouteDef { style, params, node_fn, methods });
208
+ }
209
+ Ok(Module { consts, env, funcs, routes })
210
+ }
211
+ }
212
+
213
+ struct Frame {
214
+ locals: Vec<Val>,
215
+ tries: Vec<(usize, usize)>, // (handler pc, stack depth)
216
+ }
217
+
218
+ pub struct Vm<'m, 'c, 'a> {
219
+ m: &'m Module<'m>,
220
+ cx: &'c mut Ctx<'a>,
221
+ stack: Vec<Val>,
222
+ route: usize,
223
+ depth: u32,
224
+ steps: u32,
225
+ }
226
+
227
+ fn err(s: &str) -> Val {
228
+ Val::str(s)
229
+ }
230
+
231
+ /// Run one route of a module for the request already in `cx`.
232
+ pub fn run_route(m: &Module, cx: &mut Ctx, route: usize) -> Result<(), Val> {
233
+ let r = m.routes.get(route).ok_or_else(|| err("no such route"))?;
234
+ let mut vm = Vm { m, cx, stack: Vec::with_capacity(32), route, depth: 0, steps: 0 };
235
+ if r.style == 0 {
236
+ vm.call_fn(r.node_fn, Vec::new())?;
237
+ return Ok(());
238
+ }
239
+ let method = vm.cx.req.method; // 0 GET 1 POST 2 PUT 3 DELETE 4 PATCH 5 OPTIONS 6 HEAD
240
+ let mut target = r.methods.iter().find(|(mm, _)| *mm == method).map(|(_, f)| *f);
241
+ if target.is_none() && method == 6 {
242
+ target = r.methods.iter().find(|(mm, _)| *mm == 0).map(|(_, f)| *f);
243
+ }
244
+ match target {
245
+ Some(f) => {
246
+ vm.call_fn(f, Vec::new())?;
247
+ Ok(())
248
+ }
249
+ None => {
250
+ let mut allow = String::new();
251
+ for (i, (mm, _)) in r.methods.iter().enumerate() {
252
+ if i > 0 {
253
+ allow.push_str(", ");
254
+ }
255
+ allow.push_str(crate::wire::METHODS.get(*mm as usize).copied().unwrap_or("GET"));
256
+ }
257
+ if r.methods.iter().any(|(mm, _)| *mm == 0) && !r.methods.iter().any(|(mm, _)| *mm == 6) {
258
+ allow.push_str(", HEAD");
259
+ }
260
+ if !r.methods.iter().any(|(mm, _)| *mm == 5) {
261
+ allow.push_str(", OPTIONS");
262
+ }
263
+ let status = if method == 5 { 204.0 } else { 405.0 };
264
+ vm.cx.res_status(&Val::Num(status));
265
+ vm.cx.res_header(&Val::str("allow"), &Val::Str(allow));
266
+ vm.cx.res_end(&Val::Undef);
267
+ Ok(())
268
+ }
269
+ }
270
+ }
271
+
272
+ impl<'m, 'c, 'a> Vm<'m, 'c, 'a> {
273
+ fn call_fn(&mut self, fi: u16, args: Vec<Val>) -> Result<Val, Val> {
274
+ let f = self.m.funcs.get(fi as usize).ok_or_else(|| err("bad function index"))?;
275
+ if self.depth >= MAX_DEPTH {
276
+ return Err(err("RangeError: Maximum call stack size exceeded"));
277
+ }
278
+ self.depth += 1;
279
+ let mut frame = Frame { locals: alloc::vec![Val::Undef; f.nlocals as usize], tries: Vec::new() };
280
+ let r = self.exec(fi, &mut frame, 0, &args);
281
+ self.depth -= 1;
282
+ r
283
+ }
284
+
285
+ fn pop(&mut self) -> Result<Val, Val> {
286
+ self.stack.pop().ok_or_else(|| err("vm: stack underflow"))
287
+ }
288
+
289
+ fn pop_n(&mut self, n: usize) -> Result<Vec<Val>, Val> {
290
+ if self.stack.len() < n {
291
+ return Err(err("vm: stack underflow"));
292
+ }
293
+ Ok(self.stack.split_off(self.stack.len() - n))
294
+ }
295
+
296
+ fn konst(&self, i: u16) -> Result<Val, Val> {
297
+ self.m.consts.get(i as usize).cloned().ok_or_else(|| err("vm: bad constant"))
298
+ }
299
+
300
+ fn kstr(&self, i: u16) -> Result<String, Val> {
301
+ Ok(self.konst(i)?.to_js_string())
302
+ }
303
+
304
+ /// Execute from `pc` until RET. Errors thrown inside are routed to the
305
+ /// innermost handler pushed within this region; otherwise they propagate.
306
+ fn exec(&mut self, fi: u16, frame: &mut Frame, start: usize, args: &[Val]) -> Result<Val, Val> {
307
+ let code = self.m.funcs[fi as usize].code;
308
+ let try_base = frame.tries.len();
309
+ let mut pc = start;
310
+ loop {
311
+ match self.step(fi, frame, &mut pc, args, code) {
312
+ Ok(Some(v)) => {
313
+ frame.tries.truncate(try_base);
314
+ return Ok(v);
315
+ }
316
+ Ok(None) => {}
317
+ Err(e) => {
318
+ if frame.tries.len() > try_base {
319
+ let (handler, depth) = frame.tries.pop().unwrap();
320
+ self.stack.truncate(depth);
321
+ self.stack.push(e);
322
+ pc = handler;
323
+ } else {
324
+ frame.tries.truncate(try_base);
325
+ return Err(e);
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ #[inline(always)]
333
+ fn rd_u8(code: &[u8], pc: &mut usize) -> Result<u8, Val> {
334
+ let v = *code.get(*pc).ok_or_else(|| err("vm: truncated code"))?;
335
+ *pc += 1;
336
+ Ok(v)
337
+ }
338
+ #[inline(always)]
339
+ fn rd_u16(code: &[u8], pc: &mut usize) -> Result<u16, Val> {
340
+ let s = code.get(*pc..*pc + 2).ok_or_else(|| err("vm: truncated code"))?;
341
+ *pc += 2;
342
+ Ok(u16::from_le_bytes([s[0], s[1]]))
343
+ }
344
+ #[inline(always)]
345
+ fn rd_i32(code: &[u8], pc: &mut usize) -> Result<i32, Val> {
346
+ let s = code.get(*pc..*pc + 4).ok_or_else(|| err("vm: truncated code"))?;
347
+ *pc += 4;
348
+ Ok(i32::from_le_bytes([s[0], s[1], s[2], s[3]]))
349
+ }
350
+ #[inline(always)]
351
+ fn rd_u32(code: &[u8], pc: &mut usize) -> Result<u32, Val> {
352
+ Ok(Self::rd_i32(code, pc)? as u32)
353
+ }
354
+
355
+ fn jump(pc: &mut usize, off: i32) {
356
+ *pc = (*pc as i64 + off as i64) as usize;
357
+ }
358
+
359
+ /// One instruction. `Ok(Some(v))` = the region returned `v`.
360
+ fn step(&mut self, fi: u16, frame: &mut Frame, pc: &mut usize, args: &[Val], code: &[u8]) -> Result<Option<Val>, Val> {
361
+ self.steps += 1;
362
+ if self.steps > MAX_STEPS {
363
+ return Err(err("RangeError: instruction budget exceeded"));
364
+ }
365
+ let op = Self::rd_u8(code, pc)?;
366
+ match op {
367
+ OP_NOP => {}
368
+ OP_PUSH_CONST => {
369
+ let i = Self::rd_u16(code, pc)?;
370
+ let v = self.konst(i)?;
371
+ self.stack.push(v);
372
+ }
373
+ OP_PUSH_UNDEF => self.stack.push(Val::Undef),
374
+ OP_PUSH_NULL => self.stack.push(Val::Null),
375
+ OP_DUP => {
376
+ let v = self.stack.last().cloned().ok_or_else(|| err("vm: stack underflow"))?;
377
+ self.stack.push(v);
378
+ }
379
+ OP_POP => {
380
+ self.pop()?;
381
+ }
382
+ OP_SWAP => {
383
+ let n = self.stack.len();
384
+ if n < 2 {
385
+ return Err(err("vm: stack underflow"));
386
+ }
387
+ self.stack.swap(n - 1, n - 2);
388
+ }
389
+ OP_LOAD => {
390
+ let i = Self::rd_u16(code, pc)? as usize;
391
+ let v = frame.locals.get(i).cloned().ok_or_else(|| err("vm: bad local"))?;
392
+ self.stack.push(v);
393
+ }
394
+ OP_STORE => {
395
+ let i = Self::rd_u16(code, pc)? as usize;
396
+ let v = self.pop()?;
397
+ *frame.locals.get_mut(i).ok_or_else(|| err("vm: bad local"))? = v;
398
+ }
399
+ OP_LOAD_ARG => {
400
+ let i = Self::rd_u8(code, pc)? as usize;
401
+ self.stack.push(args.get(i).cloned().unwrap_or(Val::Undef));
402
+ }
403
+ OP_ARGS_FROM => {
404
+ let i = Self::rd_u8(code, pc)? as usize;
405
+ self.stack.push(Val::Arr(args.iter().skip(i).cloned().collect()));
406
+ }
407
+ OP_NEW_OBJ => self.stack.push(Val::obj()),
408
+ OP_OBJ_SET_K => {
409
+ let k = Self::rd_u16(code, pc)?;
410
+ let v = self.pop()?;
411
+ let key = self.kstr(k)?;
412
+ let o = self.stack.last_mut().ok_or_else(|| err("vm: stack underflow"))?;
413
+ o.set_str(&key, v);
414
+ }
415
+ OP_OBJ_SET => {
416
+ let v = self.pop()?;
417
+ let k = self.pop()?;
418
+ let o = self.stack.last_mut().ok_or_else(|| err("vm: stack underflow"))?;
419
+ o.set(&k, v);
420
+ }
421
+ OP_OBJ_SPREAD => {
422
+ let src = self.pop()?;
423
+ let o = self.stack.last_mut().ok_or_else(|| err("vm: stack underflow"))?;
424
+ if let Val::Obj(kv) = src {
425
+ for (k, v) in kv {
426
+ o.set_str(&k, v);
427
+ }
428
+ }
429
+ }
430
+ OP_NEW_ARR => self.stack.push(Val::arr()),
431
+ OP_ARR_PUSH => {
432
+ let v = self.pop()?;
433
+ let a = self.stack.last_mut().ok_or_else(|| err("vm: stack underflow"))?;
434
+ a.push(v);
435
+ }
436
+ OP_ARR_SPREAD => {
437
+ let src = self.pop()?;
438
+ let a = self.stack.last_mut().ok_or_else(|| err("vm: stack underflow"))?;
439
+ for v in src.iter_values() {
440
+ a.push(v);
441
+ }
442
+ }
443
+ OP_GET_K => {
444
+ let k = Self::rd_u16(code, pc)?;
445
+ let key = self.kstr(k)?;
446
+ let o = self.pop()?;
447
+ self.stack.push(o.get_str(&key));
448
+ }
449
+ OP_GET => {
450
+ let k = self.pop()?;
451
+ let o = self.pop()?;
452
+ self.stack.push(o.get(&k));
453
+ }
454
+ OP_TEMPLATE => {
455
+ let n = Self::rd_u8(code, pc)? as usize;
456
+ let parts = self.pop_n(n)?;
457
+ let mut s = String::new();
458
+ for p in parts.iter() {
459
+ s.push_str(&p.to_js_string());
460
+ }
461
+ self.stack.push(Val::Str(s));
462
+ }
463
+ OP_BIN => {
464
+ let o = Self::rd_u8(code, pc)?;
465
+ let r = self.pop()?;
466
+ let l = self.pop()?;
467
+ self.stack.push(match o {
468
+ 0 => l.add(&r),
469
+ 1 => l.sub(&r),
470
+ 2 => l.mul(&r),
471
+ 3 => l.div(&r),
472
+ 4 => l.rem(&r),
473
+ 5 => l.pow(&r),
474
+ 6 => l.bit_and(&r),
475
+ 7 => l.bit_or(&r),
476
+ 8 => l.bit_xor(&r),
477
+ 9 => l.shl(&r),
478
+ 10 => l.shr(&r),
479
+ 11 => l.ushr(&r),
480
+ _ => return Err(err("vm: bad binary op")),
481
+ });
482
+ }
483
+ OP_CMP => {
484
+ let o = Self::rd_u8(code, pc)?;
485
+ let r = self.pop()?;
486
+ let l = self.pop()?;
487
+ self.stack.push(Val::Bool(cmp(o, &l, &r)?));
488
+ }
489
+ OP_STRICT_EQ_KEEP => {
490
+ // switch: [disc, case] -> [disc, Bool]
491
+ let c = self.pop()?;
492
+ let d = self.stack.last().ok_or_else(|| err("vm: stack underflow"))?;
493
+ let b = d.strict_eq(&c);
494
+ self.stack.push(Val::Bool(b));
495
+ }
496
+ OP_UNARY => {
497
+ let o = Self::rd_u8(code, pc)?;
498
+ let v = self.pop()?;
499
+ self.stack.push(match o {
500
+ 0 => v.neg(),
501
+ 1 => Val::Num(v.to_num()),
502
+ 2 => Val::Bool(!v.truthy()),
503
+ 3 => v.bit_xor(&Val::Num(-1.0)),
504
+ 4 => Val::str(v.type_of()),
505
+ 5 => Val::Undef,
506
+ _ => return Err(err("vm: bad unary op")),
507
+ });
508
+ }
509
+ OP_TRUTHY => {
510
+ let v = self.pop()?;
511
+ self.stack.push(Val::Bool(v.truthy()));
512
+ }
513
+ OP_NULLISH => {
514
+ let v = self.pop()?;
515
+ self.stack.push(Val::Bool(v.is_nullish()));
516
+ }
517
+ OP_IN => {
518
+ let o = self.pop()?;
519
+ let k = self.pop()?;
520
+ self.stack.push(Val::Bool(o.has(&k)));
521
+ }
522
+ OP_JMP => {
523
+ let off = Self::rd_i32(code, pc)?;
524
+ Self::jump(pc, off);
525
+ }
526
+ OP_JF => {
527
+ let off = Self::rd_i32(code, pc)?;
528
+ if !self.pop()?.truthy() {
529
+ Self::jump(pc, off);
530
+ }
531
+ }
532
+ OP_JT => {
533
+ let off = Self::rd_i32(code, pc)?;
534
+ if self.pop()?.truthy() {
535
+ Self::jump(pc, off);
536
+ }
537
+ }
538
+ OP_JF_KEEP => {
539
+ let off = Self::rd_i32(code, pc)?;
540
+ let t = self.stack.last().map(|v| v.truthy()).ok_or_else(|| err("vm: stack underflow"))?;
541
+ if !t {
542
+ Self::jump(pc, off);
543
+ } else {
544
+ self.pop()?;
545
+ }
546
+ }
547
+ OP_JT_KEEP => {
548
+ let off = Self::rd_i32(code, pc)?;
549
+ let t = self.stack.last().map(|v| v.truthy()).ok_or_else(|| err("vm: stack underflow"))?;
550
+ if t {
551
+ Self::jump(pc, off);
552
+ } else {
553
+ self.pop()?;
554
+ }
555
+ }
556
+ OP_JNN_KEEP => {
557
+ let off = Self::rd_i32(code, pc)?;
558
+ let nn = self.stack.last().map(|v| !v.is_nullish()).ok_or_else(|| err("vm: stack underflow"))?;
559
+ if nn {
560
+ Self::jump(pc, off);
561
+ } else {
562
+ self.pop()?;
563
+ }
564
+ }
565
+ OP_JNULLISH_UNDEF => {
566
+ let off = Self::rd_i32(code, pc)?;
567
+ let n = self.stack.last().map(|v| v.is_nullish()).ok_or_else(|| err("vm: stack underflow"))?;
568
+ if n {
569
+ self.pop()?;
570
+ self.stack.push(Val::Undef);
571
+ Self::jump(pc, off);
572
+ }
573
+ }
574
+ OP_CALLM => {
575
+ let name_k = Self::rd_u16(code, pc)?;
576
+ let nargs = Self::rd_u8(code, pc)? as usize;
577
+ let flags = Self::rd_u8(code, pc)?;
578
+ let cb_off = if flags & CALLM_CB != 0 { Some(Self::rd_u32(code, pc)? as usize) } else { None };
579
+ let name = self.kstr(name_k)?;
580
+ let cargs = self.pop_n(nargs)?;
581
+ let mut recv = self.pop()?;
582
+ let mut cb_err: Option<Val> = None;
583
+ let out = if let Some(off) = cb_off {
584
+ let mut f = |a: &[Val]| -> Val {
585
+ if cb_err.is_some() {
586
+ return Val::Undef;
587
+ }
588
+ match self.call_inline(fi, frame, off, a) {
589
+ Ok(v) => v,
590
+ Err(e) => {
591
+ cb_err = Some(e);
592
+ Val::Undef
593
+ }
594
+ }
595
+ };
596
+ recv.call(&name, &cargs, Some(&mut f))
597
+ } else if flags & CALLM_DEFAULT_SORT != 0 {
598
+ let mut f = |a: &[Val]| -> Val {
599
+ let x = a.first().map(|v| v.to_js_string()).unwrap_or_default();
600
+ let y = a.get(1).map(|v| v.to_js_string()).unwrap_or_default();
601
+ Val::Num(if x < y { -1.0 } else if x > y { 1.0 } else { 0.0 })
602
+ };
603
+ recv.call(&name, &cargs, Some(&mut f))
604
+ } else {
605
+ recv.call(&name, &cargs, None)
606
+ };
607
+ if let Some(e) = cb_err {
608
+ return Err(e);
609
+ }
610
+ let out = out?;
611
+ if flags & CALLM_MUT != 0 {
612
+ self.stack.push(out);
613
+ self.stack.push(recv);
614
+ } else {
615
+ self.stack.push(out);
616
+ }
617
+ }
618
+ OP_HOST => {
619
+ let id = Self::rd_u8(code, pc)?;
620
+ let nargs = Self::rd_u8(code, pc)? as usize;
621
+ let flags = Self::rd_u8(code, pc)?;
622
+ let a = self.pop_n(nargs)?;
623
+ let v = self.host(id, &a, flags & HOST_Q != 0)?;
624
+ self.stack.push(if flags & HOST_VOID != 0 { Val::Undef } else { v });
625
+ }
626
+ OP_HELPER => {
627
+ let id = Self::rd_u8(code, pc)?;
628
+ let nargs = Self::rd_u8(code, pc)? as usize;
629
+ let a = self.pop_n(nargs)?;
630
+ let v = helpers::helper(self.cx, id, &a);
631
+ self.stack.push(v);
632
+ }
633
+ OP_MATH => {
634
+ let k = Self::rd_u16(code, pc)?;
635
+ let nargs = Self::rd_u8(code, pc)? as usize;
636
+ let a = self.pop_n(nargs)?;
637
+ let name = self.kstr(k)?;
638
+ self.stack.push(math(&name, &a)?);
639
+ }
640
+ OP_GLOBAL => {
641
+ let k = Self::rd_u16(code, pc)?;
642
+ let nargs = Self::rd_u8(code, pc)? as usize;
643
+ let a = self.pop_n(nargs)?;
644
+ let name = self.kstr(k)?;
645
+ self.stack.push(global_call(&name, &a)?);
646
+ }
647
+ OP_JSON_PARSE => {
648
+ let s = self.pop()?;
649
+ self.stack.push(json::parse(&s.to_js_string())?);
650
+ }
651
+ OP_JSON_STRINGIFY => {
652
+ let v = self.pop()?;
653
+ self.stack.push(Val::Str(json::stringify(&v)));
654
+ }
655
+ OP_KEYS => {
656
+ let v = self.pop()?;
657
+ self.stack.push(v.keys());
658
+ }
659
+ OP_VALUES => {
660
+ let v = self.pop()?;
661
+ self.stack.push(v.values());
662
+ }
663
+ OP_ENTRIES => {
664
+ let v = self.pop()?;
665
+ self.stack.push(v.entries());
666
+ }
667
+ OP_ISARRAY => {
668
+ let v = self.pop()?;
669
+ self.stack.push(Val::Bool(matches!(v, Val::Arr(_))));
670
+ }
671
+ OP_NEW_ERROR => {
672
+ let k = Self::rd_u16(code, pc)?;
673
+ let nargs = Self::rd_u8(code, pc)? as usize;
674
+ let a = self.pop_n(nargs)?;
675
+ let name = self.kstr(k)?;
676
+ let mut e = Ctx::new_error(&a);
677
+ if name != "Error" {
678
+ e.set_str("name", Val::Str(name));
679
+ }
680
+ self.stack.push(e);
681
+ }
682
+ OP_LOG => {
683
+ let nargs = Self::rd_u8(code, pc)? as usize;
684
+ let a = self.pop_n(nargs)?;
685
+ helpers::log(&a);
686
+ self.stack.push(Val::Undef);
687
+ }
688
+ OP_RESP => {
689
+ let k = Self::rd_u16(code, pc)?;
690
+ let init = self.pop()?;
691
+ let body = self.pop()?;
692
+ let kind = self.kstr(k)?;
693
+ self.stack.push(helpers::resp(&kind, body, init));
694
+ }
695
+ OP_PARAMS => {
696
+ let kind = Self::rd_u8(code, pc)?;
697
+ let route = &self.m.routes[self.route];
698
+ let ps: Vec<(&str, bool)> = route.params.iter().map(|(n, c)| (n.as_str(), *c)).collect();
699
+ let v = if kind == 0 { helpers::query(self.cx, &ps) } else { helpers::params(self.cx, &ps) };
700
+ self.stack.push(v);
701
+ }
702
+ OP_PARAM => {
703
+ let k = Self::rd_u16(code, pc)?;
704
+ let ca = Self::rd_u8(code, pc)? != 0;
705
+ let name = self.kstr(k)?;
706
+ let v = helpers::param(self.cx, &name, ca);
707
+ self.stack.push(v);
708
+ }
709
+ OP_CALL_FN => {
710
+ let f = Self::rd_u16(code, pc)?;
711
+ let nargs = Self::rd_u8(code, pc)? as usize;
712
+ let a = self.pop_n(nargs)?;
713
+ let v = self.call_fn(f, a)?;
714
+ self.stack.push(v);
715
+ }
716
+ OP_CALL_INLINE => {
717
+ let off = Self::rd_u32(code, pc)? as usize;
718
+ let nargs = Self::rd_u8(code, pc)? as usize;
719
+ let a = self.pop_n(nargs)?;
720
+ let v = self.call_inline(fi, frame, off, &a)?;
721
+ self.stack.push(v);
722
+ }
723
+ OP_RET => {
724
+ let v = self.pop()?;
725
+ return Ok(Some(v));
726
+ }
727
+ OP_SEND => {
728
+ let v = self.pop()?;
729
+ helpers::send(self.cx, &v)?;
730
+ }
731
+ OP_THROW => {
732
+ let v = self.pop()?;
733
+ return Err(v);
734
+ }
735
+ OP_TRY_PUSH => {
736
+ let off = Self::rd_i32(code, pc)?;
737
+ let handler = (*pc as i64 + off as i64) as usize;
738
+ frame.tries.push((handler, self.stack.len()));
739
+ }
740
+ OP_TRY_POP => {
741
+ frame.tries.pop();
742
+ }
743
+ OP_CAUGHT => {
744
+ let e = self.pop()?;
745
+ self.stack.push(helpers::caught(e));
746
+ }
747
+ OP_ITER_INIT => {
748
+ let arr_l = Self::rd_u16(code, pc)? as usize;
749
+ let idx_l = Self::rd_u16(code, pc)? as usize;
750
+ let it = self.pop()?;
751
+ let snapshot = Val::Arr(it.iter_values());
752
+ *frame.locals.get_mut(arr_l).ok_or_else(|| err("vm: bad local"))? = snapshot;
753
+ *frame.locals.get_mut(idx_l).ok_or_else(|| err("vm: bad local"))? = Val::Num(0.0);
754
+ }
755
+ OP_ITER_NEXT => {
756
+ let arr_l = Self::rd_u16(code, pc)? as usize;
757
+ let idx_l = Self::rd_u16(code, pc)? as usize;
758
+ let out_l = Self::rd_u16(code, pc)? as usize;
759
+ let off = Self::rd_i32(code, pc)?;
760
+ let idx = frame.locals.get(idx_l).map(|v| v.to_num()).unwrap_or(0.0) as usize;
761
+ let item = match frame.locals.get(arr_l) {
762
+ Some(Val::Arr(a)) => a.get(idx).cloned(),
763
+ _ => None,
764
+ };
765
+ match item {
766
+ Some(v) => {
767
+ *frame.locals.get_mut(out_l).ok_or_else(|| err("vm: bad local"))? = v;
768
+ *frame.locals.get_mut(idx_l).ok_or_else(|| err("vm: bad local"))? = Val::Num((idx + 1) as f64);
769
+ }
770
+ None => Self::jump(pc, off),
771
+ }
772
+ }
773
+ OP_STORE_PATH => {
774
+ let root = Self::rd_u16(code, pc)? as usize;
775
+ let nkeys = Self::rd_u8(code, pc)? as usize;
776
+ let keys = self.pop_n(nkeys)?;
777
+ let value = self.pop()?;
778
+ let base = frame.locals.get_mut(root).ok_or_else(|| err("vm: bad local"))?;
779
+ set_path(base, &keys, value);
780
+ }
781
+ OP_STORE_PATH_DISCARD => {
782
+ let nkeys = Self::rd_u8(code, pc)? as usize;
783
+ let _keys = self.pop_n(nkeys)?;
784
+ let _base = self.pop()?;
785
+ let _value = self.pop()?;
786
+ }
787
+ OP_DELETE_PATH => {
788
+ let root = Self::rd_u16(code, pc)? as usize;
789
+ let nkeys = Self::rd_u8(code, pc)? as usize;
790
+ let keys = self.pop_n(nkeys)?;
791
+ let base = frame.locals.get_mut(root).ok_or_else(|| err("vm: bad local"))?;
792
+ delete_path(base, &keys);
793
+ self.stack.push(Val::Bool(true));
794
+ }
795
+ _ => return Err(err("vm: unknown opcode")),
796
+ }
797
+ Ok(None)
798
+ }
799
+
800
+ fn call_inline(&mut self, fi: u16, frame: &mut Frame, off: usize, args: &[Val]) -> Result<Val, Val> {
801
+ if self.depth >= MAX_DEPTH {
802
+ return Err(err("RangeError: Maximum call stack size exceeded"));
803
+ }
804
+ self.depth += 1;
805
+ let r = self.exec(fi, frame, off, args);
806
+ self.depth -= 1;
807
+ r
808
+ }
809
+
810
+ fn host(&mut self, id: u8, a: &[Val], _q: bool) -> Result<Val, Val> {
811
+ let arg = |i: usize| a.get(i).cloned().unwrap_or(Val::Undef);
812
+ let cx = &mut *self.cx;
813
+ Ok(match id {
814
+ 0 => cx.req_method(),
815
+ 1 => cx.req_path(),
816
+ 2 => cx.req_url(),
817
+ 3 => cx.req_full_url(),
818
+ 4 => cx.req_query(),
819
+ 5 => cx.req_query_get(&arg(0)),
820
+ 6 => cx.req_headers(),
821
+ 7 => cx.req_header(&arg(0)),
822
+ 8 => cx.req_text(),
823
+ 9 => cx.req_body(),
824
+ 10 => cx.req_json()?,
825
+ 11 => { cx.res_status(&arg(0)); Val::Undef }
826
+ 12 => { cx.res_header(&arg(0), &arg(1)); Val::Undef }
827
+ 13 => { cx.res_json(&arg(0)); Val::Undef }
828
+ 14 => { cx.res_send(&arg(0)); Val::Undef }
829
+ 15 => { cx.res_end(&arg(0)); Val::Undef }
830
+ 16 => { cx.res_redirect(&arg(0), &arg(1)); Val::Undef }
831
+ 17 => cx.env(&arg(0)),
832
+ 18 => cx.env_obj(),
833
+ 19 => cx.now_ms(),
834
+ 20 => cx.now_iso(),
835
+ 21 => cx.kv_get(&arg(0))?,
836
+ 22 => cx.kv_exists(&arg(0))?,
837
+ 23 => cx.kv_set(&arg(0), &arg(1))?,
838
+ 24 => cx.kv_incrby(&arg(0), &arg(1))?,
839
+ 25 => cx.kv_del(&arg(0))?,
840
+ 26 => cx.slot(),
841
+ 27 => cx.payer_address(),
842
+ 28 => cx.program_address(),
843
+ _ => return Err(err("vm: unknown host call")),
844
+ })
845
+ }
846
+ }
847
+
848
+ fn cmp(op: u8, l: &Val, r: &Val) -> Result<bool, Val> {
849
+ Ok(match op {
850
+ 0 => l.strict_eq(r),
851
+ 1 => !l.strict_eq(r),
852
+ 2 => l.loose_eq(r),
853
+ 3 => !l.loose_eq(r),
854
+ 4 => l.lt(r),
855
+ 5 => l.le(r),
856
+ 6 => l.gt(r),
857
+ 7 => l.ge(r),
858
+ _ => return Err(err("vm: bad compare")),
859
+ })
860
+ }
861
+
862
+ fn set_path(base: &mut Val, keys: &[Val], value: Val) {
863
+ if keys.is_empty() {
864
+ *base = value;
865
+ return;
866
+ }
867
+ let mut cur = base.get(&keys[0]);
868
+ set_path(&mut cur, &keys[1..], value);
869
+ base.set(&keys[0], cur);
870
+ }
871
+
872
+ fn delete_path(base: &mut Val, keys: &[Val]) {
873
+ match keys.len() {
874
+ 0 => {}
875
+ 1 => base.delete(&keys[0]),
876
+ _ => {
877
+ let mut cur = base.get(&keys[0]);
878
+ delete_path(&mut cur, &keys[1..]);
879
+ base.set(&keys[0], cur);
880
+ }
881
+ }
882
+ }
883
+
884
+ /// Entry for the shared runtime: parse the module, set the site namespace and
885
+ /// env, run the route, emit the response (same envelope as `dispatch`).
886
+ pub fn invoke_module(cx: &mut Ctx, code: &[u8], route: usize) -> Result<(), Val> {
887
+ let m = Module::parse(code).map_err(|_| err("vm: malformed module"))?;
888
+ cx.env_dyn = m.env.clone();
889
+ run_route(&m, cx, route)
890
+ }
891
+
892
+ /// Compose the 500 for an uncaught throw (mirrors lib.rs).
893
+ pub fn error_resp(thrown: &Val) -> Resp {
894
+ let msg: String = match thrown {
895
+ Val::Obj(_) => match thrown.get_str("message") {
896
+ Val::Undef => thrown.to_js_string(),
897
+ m => m.to_js_string(),
898
+ },
899
+ _ => thrown.to_js_string(),
900
+ };
901
+ Resp::json_error(500, &msg)
902
+ }