SayoriOS  0.3.3
elk.c
1 #include "common.h"
2 #include "portability.h"
3 #include "lib/string.h"
4 #include <io/ports.h>
5 #include <lib/stdio.h>
6 #include <lib/sprintf.h>
7 #include <stdarg.h>
8 
9 #include "elk_config.h"
10 #include "elk.h"
11 
12 #ifndef JS_EXPR_MAX
13 #define JS_EXPR_MAX 20
14 #endif
15 
16 #ifndef JS_GC_THRESHOLD
17 #define JS_GC_THRESHOLD 0.75
18 #endif
19 
20 
21 
22 static const char *typestr(uint8_t t) {
23  const char *names[] = { "object", "prop", "string", "undefined", "null",
24  "number", "boolean", "function", "coderef",
25  "cfunc", "err", "nan" };
26  return (t < sizeof(names) / sizeof(names[0])) ? names[t] : "??";
27 }
28 
29 // Pack JS values into uin64_t, double nan, per IEEE 754
30 // 64bit "double": 1 bit sign, 11 bits exponent, 52 bits mantissa
31 //
32 // seeeeeee|eeeemmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm|mmmmmmmm
33 // 11111111|11110000|00000000|00000000|00000000|00000000|00000000|00000000 inf
34 // 11111111|11111000|00000000|00000000|00000000|00000000|00000000|00000000 qnan
35 //
36 // 11111111|1111tttt|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv
37 // NaN marker |type| 48-bit placeholder for values: pointers, strings
38 //
39 // On 64-bit platforms, pointers are really 48 bit only, so they can fit,
40 // provided they are sign extended
41 
42 static void js_printPos(struct js *js) {
43  size_t line = 1;
44  bool lastStop = false;
45  size_t endLine = 0;
46  char* buf = js->code;
47  if (js->toff < js->incSize) {
48  qemu_err("[JSE] [Error] Library parsing...");
49  return;
50  }
51  for (int i = js->incSize; i < js->clen; i++) {
52  //qemu_log("[%d > %d] Line:%d | `%c`",i, js->toff, line, buf[i]);
53  if (i > js->toff) lastStop = true;
54  if (buf[i] == '\n' && lastStop) {
55  endLine = i;
56  break;
57  } else if (buf[i] == '\n' && !lastStop) {
58  line++;
59  }
60  }
61 
62  char* t_dump = calloc(1, (endLine - js->toff) + 1);
63 
64  memcpy(t_dump, buf + js->toff, endLine - js->toff);
65 
66  jse_trim(t_dump);
67 
68  qemu_err("[JSE] [Error] Line %d: %s", line + js->paramSize, t_dump);
69  kfree(t_dump);
70 }
71 
72 static void elk_dump(struct js *js) {
73  qemu_note("[JSE] Engine dump:");
74  qemu_note(" |--- Maximum view RAM | %d",js->css);
75  qemu_note(" |--- Low WaterMark | %d",js->lwm);
76  qemu_note(" |--- Is fatal | %d",js->isFatal);
77  qemu_note(" |--- Last token | %d",js->tok);
78  qemu_note(" |--- Consumed | %d",js->consumed);
79  qemu_note(" |--- Flags | %x",js->flags);
80  qemu_note(" |--- Length code | %d",js->clen);
81  qemu_note(" |--- Position code | %d",js->pos);
82  qemu_note(" |--- Last position token (offset) | %d",js->toff);
83  qemu_note(" |--- Last position token (len) | %d",js->tlen);
84  qemu_note(" |--- Scope | %d",js->scope);
85  qemu_note(" |--- Size | %d",js->size);
86  qemu_note(" |--- brk | %d",js->brk);
87  qemu_note(" |--- gct | %d",js->gct);
88  qemu_note(" |--- Max RAM JS | %d",js->maxcss);
89 }
90 
91 static void setlwm(struct js *js) {
92  jsoff_t n = 0, css = 0;
93  if (js->brk < js->size) n = js->size - js->brk;
94  if (js->lwm > n) js->lwm = n;
95  if ((char *) js->cstk > (char *) &n)
96  css = (jsoff_t) ((char *) js->cstk - (char *) &n);
97  if (css > js->css) js->css = css;
98 }
99 
100 // Copy src to dst, make no overflows, 0-terminate. Return bytes copied
101 static size_t cpy(char *dst, size_t dstlen, const char *src, size_t srclen) {
102  size_t i = 0;
103  for (i = 0; i < dstlen && i < srclen && src[i] != 0; i++) dst[i] = src[i];
104  if (dstlen > 0) dst[i < dstlen ? i : dstlen - 1] = '\0';
105  return i;
106 }
107 
108 // Stringify JS object
109 static size_t js_getObjLite(struct js *js, jsval_t obj, char *buf, size_t len) {
110  qemu_log("[JSE] js_getObjLite Len:%d", len);
111  size_t n = cpy(buf, len, "{", 1);
112  jsoff_t next = loadoff(js, (jsoff_t) vdata(obj)) & ~3U; // First prop offset
113  while (next < js->brk && next != 0) { // Iterate over props
114 
115  //qemu_log("[JSE] STROBJ %d < %d && %d != 0", next , js->brk , next);
116  jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next));
117  jsval_t val = loadval(js, next + (jsoff_t) (sizeof(next) + sizeof(koff)));
118  // printf("PROP %u, koff %u\n", next & ~3, koff);
119  n += cpy(buf + n, len - n, ",", n == 1 ? 0 : 1);
120  n += tostr(js, mkval(T_STR, koff), buf + n, len - n);
121  n += cpy(buf + n, len - n, ":", 1);
122  if (js_type(val) == JS_NUM) {
123  //qemu_note("is number");
124  n += tostr(js, val, buf + n, len - n);
125  } else {
126  //qemu_note("is NO numer");
127  n += tostr(js, val, buf + n, len - n);
128  }
129  next = loadoff(js, next) & ~3U; // Load next prop offset
130  //qemu_log(" |-- STROBJ NEXT (n:%d)(%d) %s", n, strlen(buf), buf);
131  }
132  return n + cpy(buf + n, len - n, "}", 1);
133 }
134 
135 // Stringify JS object
136 static size_t strobj(struct js *js, jsval_t obj, char *buf, size_t len) {
137  qemu_log("[JSE] STROBJ Len:%d", len);
138  size_t n = cpy(buf, len, "{", 1);
139  jsoff_t next = loadoff(js, (jsoff_t) vdata(obj)) & ~3U; // First prop offset
140  while (next < js->brk && next != 0) { // Iterate over props
141 
142  //qemu_log("[JSE] STROBJ %d < %d && %d != 0", next , js->brk , next);
143  jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next));
144  jsval_t val = loadval(js, next + (jsoff_t) (sizeof(next) + sizeof(koff)));
145  // printf("PROP %u, koff %u\n", next & ~3, koff);
146  n += cpy(buf + n, len - n, ",", n == 1 ? 0 : 1);
147  n += cpy(buf + n, len - n, "\"", 1);
148  n += tostr(js, mkval(T_STR, koff), buf + n, len - n);
149  n += cpy(buf + n, len - n, "\"", 1);
150  n += cpy(buf + n, len - n, ":", 1);
151  n += cpy(buf + n, len - n, "\"", 1);
152  if (js_type(val) == JS_NUM) {
153  //qemu_note("is number");
154  n += tostr(js, val, buf + n, len - n);
155  n += cpy(buf + n - 1, len - n, "\"", 1);
156  } else {
157  //qemu_note("is NO numer");
158  n += tostr(js, val, buf + n, len - n);
159  n += cpy(buf + n, len - n, "\"", 1);
160  }
161  next = loadoff(js, next) & ~3U; // Load next prop offset
162  //qemu_log(" |-- STROBJ NEXT (n:%d)(%d) %s", n, strlen(buf), buf);
163  }
164  for (int i = 0; i < n; i++) {
165  if (buf[i] == 0x0) buf[i] = ' ';
166  //qemu_log("[%d] %x | %c",i, buf[i], buf[i]);
167  }
168  qemu_log("[JSE] STROBJ RETURn %d", len);
169  return n + cpy(buf + n, len - n, "}", 1);
170 }
171 
172 // Stringify numeric JS value
173 // Во время тестов вызывался только когда требовался result
174 static size_t strnum(jsval_t value, char *buf, size_t len) {
175  double_t dv = tod2(value), iv;
176  return (size_t) snprintf(buf, len, "%d", dv);
177 }
178 
179 // Return mem offset and length of the JS string
180 static jsoff_t vstr(struct js *js, jsval_t value, jsoff_t *len) {
181  jsoff_t off = (jsoff_t) vdata(value);
182  if (len) *len = offtolen(loadoff(js, off));
183  return (jsoff_t) (off + sizeof(off));
184 }
185 
186 // Stringify string JS value
187 static size_t strstring(struct js *js, jsval_t value, char *buf, size_t len) {
188  jsoff_t slen, off = vstr(js, value, &slen);
189  size_t n = 0;
190  //n += cpy(buf + n, len - n, "\"", 1);
191  n += cpy(buf + n, len - n, (char *) &js->mem[off], slen);
192  //n += cpy(buf + n, len - n, "\"", 1);
193  return n;
194 }
195 
196 // Stringify JS function
197 static size_t strfunc(struct js *js, jsval_t value, char *buf, size_t len) {
198  jsoff_t sn, off = vstr(js, value, &sn);
199  size_t n = cpy(buf, len, "function", 8);
200  return n + cpy(buf + n, len - n, (char *) &js->mem[off], sn);
201 }
202 
203 jsval_t js_mkerr(struct js *js, const char *xx, ...) {
204  js_printPos(js);
205  va_list ap;
206  size_t n = cpy(js->errmsg, sizeof(js->errmsg), "ERROR: ", 7);
207  va_start(ap, xx);
208  vsnprintf(js->errmsg + n, sizeof(js->errmsg) - n, xx, ap);
209  va_end(ap);
210  js->errmsg[sizeof(js->errmsg) - 1] = '\0';
211  js->isFatal = 1;
212  js->pos = js->clen, js->tok = TOK_EOF, js->consumed = 0; // Jump to the end
213  return mkval(T_ERR, 0);
214 }
215 
216 // Stringify JS value into the given buffer
217 static size_t tostr(struct js *js, jsval_t value, char *buf, size_t len) {
218  switch (vtype(value)) {
219  case T_UNDEF: return cpy(buf, len, "undefined", 9);
220  case T_NULL: return cpy(buf, len, "null", 4);
221  case T_BOOL: return cpy(buf, len, vdata(value) & 1 ? "true" : "false", vdata(value) & 1 ? 4 : 5);
222  case T_OBJ: return strobj(js, value, buf, len);
223  case T_STR: return strstring(js, value, buf, len);
224  case T_NUM: return strnum(value, buf, len);
225  case T_FUNC: return strfunc(js, value, buf, len);
226  case T_CFUNC: return (size_t) snprintf(buf, len, "\"c_func_0x%lx\"", (unsigned long) vdata(value));
227  case T_PROP: return (size_t) snprintf(buf, len, "PROP@%lu", (unsigned long) vdata(value));
228  default: return (size_t) snprintf(buf, len, "VTYPE%d", vtype(value));
229  }
230 }
231 
232 static int js_cmp(struct js *js, jsval_t a, jsval_t b) {
233  if (a == b) {
234  return 0; // Значения a и b равны
235  } else {
236  const char *a_str = js_str(js, a);
237  const char *b_str = js_str(js, b);
238 
239  if (strcmp(a_str, b_str) < 0) {
240  return -1; // Значение a меньше b
241  } else if (strcmp(a_str, b_str) > 0) {
242  return 1; // Значение a больше b
243  } else {
244  return 0; // Значения a и b равны
245  }
246  }
247  return 1;
248 }
249 #ifdef SAYORI_JSE_ARRAY_H
250 // Конвектировать объект в массив
251 JSE_ARRAY* js_getObjectToArray(struct js *js, jsval_t value) {
252  char *buf = (char *) &js->mem[js->brk + sizeof(jsoff_t)];
253  size_t len, available = js->size - js->brk - sizeof(jsoff_t);
254  if (is_err(value)) return js->errmsg;
255  JSE_ARRAY* array = jse_array_link_create();
256  if (js->brk + sizeof(jsoff_t) >= js->size) return array;
257 
258  jsoff_t next = loadoff(js, (jsoff_t) vdata(value)) & ~3U; // First prop offset
259  while (next < js->brk && next != 0) { // Iterate over props
260  jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next));
261  jsval_t val = loadval(js, next + (jsoff_t) (sizeof(next) + sizeof(koff)));
262  char* KEY = calloc(1, available + 1);
263  tostr(js, mkval(T_STR, koff), KEY, available);
264  char* VALUE = calloc(1, available + 1);
265  if (js_type(val) == JS_NUM) {
266  //qemu_note("is number");
267  //tostr(js, val, VALUE, available);
268  jse_array_push(array, KEY, (JSE_ARRAY_VALUE){.int_value = js_getnum(val)}, JSE_ARRAY_TYPE_INT);
269  } else {
270  //qemu_note("is NO numer");
271  tostr(js, val, VALUE, available);
272  jse_array_push(array, KEY, (JSE_ARRAY_VALUE){.str_value = jse_strdup(VALUE)}, JSE_ARRAY_TYPE_STRING);
273  }
274  free(KEY);
275  free(VALUE);
276  next = loadoff(js, next) & ~3U; // Load next prop offset
277  }
278  return array;
279 }
280 #endif //SAYORI_JSE_ARRAY_H
281 
282 // Stringify JS value into a free JS memory block
283 const char *js_str(struct js *js, jsval_t value) {
284  // Leave jsoff_t placeholder between js->brk and a stringify buffer,
285  // in case if next step is convert it into a JS variable
286  char *buf = (char *) &js->mem[js->brk + sizeof(jsoff_t)];
287  size_t len, available = js->size - js->brk - sizeof(jsoff_t);
288  if (is_err(value)) return js->errmsg;
289  if (js->brk + sizeof(jsoff_t) >= js->size) return "";
290  len = tostr(js, value, buf, available);
291  js_mkstr(js, NULL, len);
292  return buf;
293 }
294 
295 bool js_truthy(struct js *js, jsval_t v) {
296  uint8_t t = vtype(v);
297 
298  // qemu_warn("[js_truthy] v: %d | %f", tod2(v), tod2(v));
299  return (t == T_BOOL && vdata(v) != 0) || (t == T_NUM && tod2(v) != 0.0) ||
300  (t == T_OBJ || t == T_FUNC) || (t == T_STR && vstrlen(js, v) > 0);
301 }
302 
303 static jsoff_t js_alloc(struct js *js, size_t size) {
304  jsoff_t ofs = js->brk;
305  size = align32((jsoff_t) size); // 4-byte align, (n + k - 1) / k * k
306  if (js->brk + size > js->size) return ~(jsoff_t) 0;
307  js->brk += (jsoff_t) size;
308  return ofs;
309 }
310 
311 static jsval_t mkentity(struct js *js, jsoff_t b, const void *buf, size_t len) {
312  jsoff_t ofs = js_alloc(js, len + sizeof(b));
313  if (ofs == (jsoff_t) ~0) return js_mkerr(js, "oom");
314  memcpy(&js->mem[ofs], &b, sizeof(b));
315  // Using memmove - in case we're stringifying data from the free JS mem
316  if (buf != NULL) memmove(&js->mem[ofs + sizeof(b)], buf, len);
317  if ((b & 3) == T_STR) js->mem[ofs + sizeof(b) + len - 1] = 0; // 0-terminate
318  // printf("MKE: %u @ %u type %d\n", js->brk - ofs, ofs, b & 3);
319  return mkval(b & 3, ofs);
320 }
321 
322 jsval_t js_mkstr(struct js *js, const void *ptr, size_t len) {
323  jsoff_t n = (jsoff_t) (len + 1);
324  // printf("MKSTR %u %u\n", n, js->brk);
325  return mkentity(js, (jsoff_t) ((n << 2) | T_STR), ptr, n);
326 }
327 
328 static jsval_t mkobj(struct js *js, jsoff_t parent) {
329  return mkentity(js, 0 | T_OBJ, &parent, sizeof(parent));
330 }
331 
332 static jsval_t setprop(struct js *js, jsval_t obj, jsval_t k, jsval_t v) {
333  jsoff_t koff = (jsoff_t) vdata(k); // Key offset
334  jsoff_t b, head = (jsoff_t) vdata(obj); // Property list head
335  char buf[sizeof(koff) + sizeof(v)]; // Property memory layout
336  memcpy(&b, &js->mem[head], sizeof(b)); // Load current 1st prop offset
337  memcpy(buf, &koff, sizeof(koff)); // Initialize prop data: copy key
338  memcpy(buf + sizeof(koff), &v, sizeof(v)); // Copy value
339  jsoff_t brk = js->brk | T_OBJ; // New prop offset
340  memcpy(&js->mem[head], &brk, sizeof(brk)); // Repoint head to the new prop
341  return mkentity(js, (b & ~3U) | T_PROP, buf, sizeof(buf)); // Create new prop
342 }
343 
344 // Return T_OBJ/T_PROP/T_STR entity size based on the first word in memory
345 static inline jsoff_t esize(jsoff_t w) {
346  switch (w & 3U) {
347  case T_OBJ: return (jsoff_t) (sizeof(jsoff_t) + sizeof(jsoff_t));
348  case T_PROP: return (jsoff_t) (sizeof(jsoff_t) + sizeof(jsoff_t) + sizeof(jsval_t));
349  case T_STR: return (jsoff_t) (sizeof(jsoff_t) + align32(w >> 2U));
350  default: return (jsoff_t) ~0U;
351  }
352 }
353 
354 static bool is_mem_entity(uint8_t t) {
355  return t == T_OBJ || t == T_PROP || t == T_STR || t == T_FUNC;
356 }
357 
358 #define GCMASK ~(((jsoff_t) ~0) >> 1) // Entity deletion marker
359 static void js_fixup_offsets(struct js *js, jsoff_t start, jsoff_t size) {
360  for (jsoff_t n, v, off = 0; off < js->brk; off += n) { // start from 0!
361  v = loadoff(js, off);
362  n = esize(v & ~GCMASK);
363  if (v & GCMASK) continue; // To be deleted, don't bother
364  if ((v & 3) != T_OBJ && (v & 3) != T_PROP) continue;
365  if (v > start) saveoff(js, off, v - size);
366  if ((v & 3) == T_OBJ) {
367  jsoff_t u = loadoff(js, (jsoff_t) (off + sizeof(jsoff_t)));
368  if (u > start) saveoff(js, (jsoff_t) (off + sizeof(jsoff_t)), u - size);
369  }
370  if ((v & 3) == T_PROP) {
371  jsoff_t koff = loadoff(js, (jsoff_t) (off + sizeof(off)));
372  if (koff > start) saveoff(js, (jsoff_t) (off + sizeof(off)), koff - size);
373  jsval_t val = loadval(js, (jsoff_t) (off + sizeof(off) + sizeof(off)));
374  if (is_mem_entity(vtype(val)) && vdata(val) > start) {
375  saveval(js, (jsoff_t) (off + sizeof(off) + sizeof(off)),
376  mkval(vtype(val), (unsigned long) (vdata(val) - size)));
377  }
378  }
379  }
380  // Fixup js->scope
381  jsoff_t off = (jsoff_t) vdata(js->scope);
382  if (off > start) js->scope = mkval(T_OBJ, off - size);
383  if (js->nogc >= start) js->nogc -= size;
384  // Fixup code that we're executing now, if required
385  if (js->code > (char *) js->mem && js->code - (char *) js->mem < js->size &&
386  js->code - (char *) js->mem > start) {
387  js->code -= size;
388  // printf("GC-ing code under us!! %ld\n", js->code - (char *) js->mem);
389  }
390  // printf("FIXEDOFF %u %u\n", start, size);
391 }
392 
393 static void js_delete_marked_entities(struct js *js) {
394  for (jsoff_t n, v, off = 0; off < js->brk; off += n) {
395  v = loadoff(js, off);
396  n = esize(v & ~GCMASK);
397  if (v & GCMASK) { // This entity is marked for deletion, remove it
398  // printf("DEL: %4u %d %x\n", off, v & 3, n);
399  // assert(off + n <= js->brk);
400  js_fixup_offsets(js, off, n);
401  memmove(&js->mem[off], &js->mem[off + n], js->brk - off - n);
402  js->brk -= n; // Shrink brk boundary by the size of deleted entity
403  n = 0; // We shifted data, next iteration stay on this offset
404  }
405  }
406 }
407 
408 static void js_mark_all_entities_for_deletion(struct js *js) {
409  for (jsoff_t v, off = 0; off < js->brk; off += esize(v)) {
410  v = loadoff(js, off);
411  saveoff(js, off, v | GCMASK);
412  }
413 }
414 
415 static jsoff_t js_unmark_entity(struct js *js, jsoff_t off) {
416  jsoff_t v = loadoff(js, off);
417  if (v & GCMASK) {
418  saveoff(js, off, v & ~GCMASK);
419  // printf("UNMARK %5u %d\n", off, v & 3);
420  if ((v & 3) == T_OBJ) js_unmark_entity(js, v & ~(GCMASK | 3));
421  if ((v & 3) == T_PROP) {
422  js_unmark_entity(js, v & ~(GCMASK | 3)); // Unmark next prop
423  js_unmark_entity(js, loadoff(js, (jsoff_t) (off + sizeof(off)))); // key
424  jsval_t val = loadval(js, (jsoff_t) (off + sizeof(off) + sizeof(off)));
425  if (is_mem_entity(vtype(val))) js_unmark_entity(js, (jsoff_t) vdata(val));
426  }
427  }
428  return v & ~(GCMASK | 3U);
429 }
430 
431 static void js_unmark_used_entities(struct js *js) {
432  jsval_t scope = js->scope;
433  do {
434  js_unmark_entity(js, (jsoff_t) vdata(scope));
435  scope = upper(js, scope);
436  } while (vdata(scope) != 0); // When global scope is GC-ed, stop
437  if (js->nogc) js_unmark_entity(js, js->nogc);
438  // printf("UNMARK: nogc %u\n", js->nogc);
439  // js_dump(js);
440 }
441 
442 void js_gc(struct js *js) {
443  // printf("================== GC %u\n", js->nogc);
444  setlwm(js);
445  if (js->nogc == (jsoff_t) ~0) return; // ~0 is a special case: GC Is disabled
446  js_mark_all_entities_for_deletion(js);
447  js_unmark_used_entities(js);
448  js_delete_marked_entities(js);
449 }
450 
451 // Skip whitespaces and comments
452 static jsoff_t skiptonext(const char *code, jsoff_t len, jsoff_t n) {
453  // printf("SKIP: [%.*s]\n", len - n, &code[n]);
454  while (n < len) {
455  if (is_space(code[n])) {
456  n++;
457  } else if (n + 1 < len && code[n] == '/' && code[n + 1] == '/') {
458  for (n += 2; n < len && code[n] != '\n';) n++;
459  } else if (n + 3 < len && code[n] == '/' && code[n + 1] == '*') {
460  for (n += 4; n < len && (code[n - 2] != '*' || code[n - 1] != '/');) n++;
461  } else {
462  break;
463  }
464  }
465  return n;
466 }
467 
468 static bool streq(const char *buf, size_t len, const char *p, size_t n) {
469  return n == len && memcmp(buf, p, len) == 0;
470 }
471 
472 static uint8_t parsekeyword(const char *buf, size_t len) {
473  switch (buf[0]) {
474  case 'b': if (streq("break", 5, buf, len)) return TOK_BREAK; break;
475  case 'c': if (streq("class", 5, buf, len)) return TOK_CLASS; if (streq("case", 4, buf, len)) return TOK_CASE; if (streq("catch", 5, buf, len)) return TOK_CATCH; if (streq("const", 5, buf, len)) return TOK_CONST; if (streq("continue", 8, buf, len)) return TOK_CONTINUE; break;
476  case 'd': if (streq("do", 2, buf, len)) return TOK_DO; if (streq("default", 7, buf, len)) return TOK_DEFAULT; break; // if (streq("delete", 6, buf, len)) return TOK_DELETE; break;
477  case 'e': if (streq("else", 4, buf, len)) return TOK_ELSE; break;
478  case 'f': if (streq("for", 3, buf, len)) return TOK_FOR; if (streq("function", 8, buf, len)) return TOK_FUNC; if (streq("finally", 7, buf, len)) return TOK_FINALLY; if (streq("false", 5, buf, len)) return TOK_FALSE; break;
479  case 'i': if (streq("if", 2, buf, len)) return TOK_IF; if (streq("in", 2, buf, len)) return TOK_IN; if (streq("instanceof", 10, buf, len)) return TOK_INSTANCEOF; break;
480  case 'l': if (streq("let", 3, buf, len)) return TOK_LET; break;
481  case 'n': if (streq("new", 3, buf, len)) return TOK_NEW; if (streq("null", 4, buf, len)) return TOK_NULL; break;
482  case 'r': if (streq("return", 6, buf, len)) return TOK_RETURN; break;
483  case 's': if (streq("switch", 6, buf, len)) return TOK_SWITCH; break;
484  case 't': if (streq("try", 3, buf, len)) return TOK_TRY; if (streq("this", 4, buf, len)) return TOK_THIS; if (streq("throw", 5, buf, len)) return TOK_THROW; if (streq("true", 4, buf, len)) return TOK_TRUE; if (streq("typeof", 6, buf, len)) return TOK_TYPEOF; break;
485  case 'u': if (streq("undefined", 9, buf, len)) return TOK_UNDEF; break;
486  case 'v': if (streq("var", 3, buf, len)) return TOK_VAR; if (streq("void", 4, buf, len)) return TOK_VOID; break;
487  case 'w': if (streq("while", 5, buf, len)) return TOK_WHILE; if (streq("with", 4, buf, len)) return TOK_WITH; break;
488  case 'y': if (streq("yield", 5, buf, len)) return TOK_YIELD; break;
489  }
490  return TOK_IDENTIFIER;
491 }
492 
493 static uint8_t parseident(const char *buf, jsoff_t len, jsoff_t *tlen) {
494  if (is_ident_begin(buf[0])) {
495  while (*tlen < len && is_ident_continue(buf[*tlen])) (*tlen)++;
496  return parsekeyword(buf, *tlen);
497  }
498  return TOK_ERR;
499 }
500 
501 static uint8_t next(struct js *js) {
502  if (js->consumed == 0) return js->tok;
503  js->consumed = 0;
504  js->tok = TOK_ERR;
505  js->toff = js->pos = skiptonext(js->code, js->clen, js->pos);
506  js->tlen = 0;
507  const char *buf = js->code + js->toff;
508  if (js->toff >= js->clen) { js->tok = TOK_EOF; return js->tok; }
509 #define TOK(T, LEN) { js->tok = T; js->tlen = (LEN); break; }
510 #define LOOK(OFS, CH) js->toff + OFS < js->clen && buf[OFS] == CH
511  switch (buf[0]) {
512  case '?': TOK(TOK_Q, 1);
513  case ':': TOK(TOK_COLON, 1);
514  case '(': TOK(TOK_LPAREN, 1);
515  case ')': TOK(TOK_RPAREN, 1);
516  case '{': TOK(TOK_LBRACE, 1);
517  case '}': TOK(TOK_RBRACE, 1);
518  case ';': TOK(TOK_SEMICOLON, 1);
519  case ',': TOK(TOK_COMMA, 1);
520  case '!': if (LOOK(1, '=') && LOOK(2, '=')) TOK(TOK_NE, 3); TOK(TOK_NOT, 1);
521  case '.': TOK(TOK_DOT, 1);
522  case '~': TOK(TOK_TILDA, 1);
523  case '-': if (LOOK(1, '-')) TOK(TOK_POSTDEC, 2); if (LOOK(1, '=')) TOK(TOK_MINUS_ASSIGN, 2); TOK(TOK_MINUS, 1);
524  case '+': if (LOOK(1, '+')) TOK(TOK_POSTINC, 2); if (LOOK(1, '=')) TOK(TOK_PLUS_ASSIGN, 2); TOK(TOK_PLUS, 1);
525  case '*': if (LOOK(1, '*')) TOK(TOK_EXP, 2); if (LOOK(1, '=')) TOK(TOK_MUL_ASSIGN, 2); TOK(TOK_MUL, 1);
526  case '/': if (LOOK(1, '=')) TOK(TOK_DIV_ASSIGN, 2); TOK(TOK_DIV, 1);
527  case '%': if (LOOK(1, '=')) TOK(TOK_REM_ASSIGN, 2); TOK(TOK_REM, 1);
528  case '&': if (LOOK(1, '&')) TOK(TOK_LAND, 2); if (LOOK(1, '=')) TOK(TOK_AND_ASSIGN, 2); TOK(TOK_AND, 1);
529  case '|': if (LOOK(1, '|')) TOK(TOK_LOR, 2); if (LOOK(1, '=')) TOK(TOK_OR_ASSIGN, 2); TOK(TOK_OR, 1);
530  case '=': if (LOOK(1, '=') && LOOK(2, '=')) TOK(TOK_EQ, 3); TOK(TOK_ASSIGN, 1);
531  case '<': if (LOOK(1, '<') && LOOK(2, '=')) TOK(TOK_SHL_ASSIGN, 3); if (LOOK(1, '<')) TOK(TOK_SHL, 2); if (LOOK(1, '=')) TOK(TOK_LE, 2); TOK(TOK_LT, 1);
532  case '>': if (LOOK(1, '>') && LOOK(2, '=')) TOK(TOK_SHR_ASSIGN, 3); if (LOOK(1, '>')) TOK(TOK_SHR, 2); if (LOOK(1, '=')) TOK(TOK_GE, 2); TOK(TOK_GT, 1);
533  case '^': if (LOOK(1, '=')) TOK(TOK_XOR_ASSIGN, 2); TOK(TOK_XOR, 1);
534  case '"': case '\'':
535  js->tlen++;
536  while (js->toff + js->tlen < js->clen && buf[js->tlen] != buf[0]) {
537  uint8_t increment = 1;
538  if (buf[js->tlen] == '\\') {
539  if (js->toff + js->tlen + 2 > js->clen) break;
540  increment = 2;
541  if (buf[js->tlen + 1] == 'x') {
542  if (js->toff + js->tlen + 4 > js->clen) break;
543  increment = 4;
544  }
545  }
546  js->tlen += increment;
547  }
548  if (buf[0] == buf[js->tlen]) js->tok = TOK_STRING, js->tlen++;
549  break;
550  case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
551  char *end;
552  js->tval = tov2(jse_p_int(buf, &end)); // TODO(lsm): protect against OOB access
553  TOK(TOK_NUMBER, (jsoff_t) (end - buf));
554  }
555  default: js->tok = parseident(buf, js->clen - js->toff, &js->tlen); break;
556  }
557  js->pos = js->toff + js->tlen;
558  //printf("NEXT: %d %d [%s]\n", js->tok, js->pos, (int) js->tlen, buf);
559  return js->tok;
560 }
561 
562 static inline uint8_t lookahead(struct js *js) {
563  uint8_t old = js->tok, tok = 0;
564  jsoff_t pos = js->pos;
565  js->consumed = 1;
566  tok = next(js);
567  js->pos = pos, js->tok = old;
568  return tok;
569 }
570 
571 static void mkscope(struct js *js) {
572  passert((js->flags & F_NOEXEC) == 0);
573  jsoff_t prev = (jsoff_t) vdata(js->scope);
574  js->scope = mkobj(js, prev);
575  // printf("ENTER SCOPE %u, prev %u\n", (jsoff_t) vdata(js->scope), prev);
576 }
577 
578 static void delscope(struct js *js) {
579  js->scope = upper(js, js->scope);
580  // printf("EXIT SCOPE %u\n", (jsoff_t) vdata(js->scope));
581 }
582 
583 static jsval_t js_block(struct js *js, bool create_scope) {
584  jsval_t res = js_mkundef();
585  if (create_scope) mkscope(js); // Enter new scope
586  js->consumed = 1;
587  // jsoff_t pos = js->pos;
588  while (next(js) != TOK_EOF && next(js) != TOK_RBRACE && !is_err(res)) {
589  uint8_t t = js->tok;
590  res = js_stmt(js);
591  if (!is_err(res) && t != TOK_LBRACE && t != TOK_IF && t != TOK_WHILE &&
592  js->tok != TOK_SEMICOLON) {
593  res = js_mkerr(js, "; expected");
594  break;
595  }
596  }
597  // printf("BLOCKEND %s\n", js_str(js, res));
598  if (create_scope) delscope(js); // Exit scope
599  return res;
600 }
601 
602 // Seach for property in a single object
603 static jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len) {
604  jsoff_t off = loadoff(js, (jsoff_t) vdata(obj)) & ~3U; // Load first prop off
605  // printf("LKP: %lu %u [%.*s]\n", vdata(obj), off, (int) len, buf);
606  while (off < js->brk && off != 0) { // Iterate over props
607  jsoff_t koff = loadoff(js, (jsoff_t) (off + sizeof(off)));
608  jsoff_t klen = (loadoff(js, koff) >> 2) - 1;
609  const char *p = (char *) &js->mem[koff + sizeof(koff)];
610  // printf(" %u %u[%.*s]\n", off, (int) klen, (int) klen, p);
611  if (streq(buf, len, p, klen)) return off; // Found !
612  off = loadoff(js, off) & ~3U; // Load next prop offset
613  }
614  return 0; // Not found
615 }
616 
617 // Lookup variable in the scope chain
618 static jsval_t lookup(struct js *js, const char *buf, size_t len) {
619  if (js->flags & F_NOEXEC) return 0;
620  for (jsval_t scope = js->scope;;) {
621  jsoff_t off = lkp(js, scope, buf, len);
622  if (off != 0) return mkval(T_PROP, off);
623  if (vdata(scope) == 0) break;
624  scope =
625  mkval(T_OBJ, loadoff(js, (jsoff_t) (vdata(scope) + sizeof(jsoff_t))));
626  }
627  //js_printPos(js);
628  return js_mkerr(js, "'%.*s' not found", (int) len, buf);
629 }
630 
631 static jsval_t resolveprop(struct js *js, jsval_t v) {
632  if (vtype(v) != T_PROP) return v;
633  return resolveprop(js,
634  loadval(js, (jsoff_t) (vdata(v) + sizeof(jsoff_t) * 2)));
635 }
636 
637 static jsval_t assign(struct js *js, jsval_t lhs, jsval_t val) {
638  saveval(js, (jsoff_t) ((vdata(lhs) & ~3U) + sizeof(jsoff_t) * 2), val);
639  return lhs;
640 }
641 
642 static jsval_t do_assign_op(struct js *js, uint8_t op, jsval_t l, jsval_t r) {
643  uint8_t m[] = {TOK_PLUS, TOK_MINUS, TOK_MUL, TOK_DIV, TOK_REM, TOK_SHL,
644  TOK_SHR, TOK_ZSHR, TOK_AND, TOK_XOR, TOK_OR};
645  jsval_t res = do_op(js, m[op - TOK_PLUS_ASSIGN], resolveprop(js, l), r);
646  return assign(js, l, res);
647 }
648 
649 static jsval_t do_string_op(struct js *js, uint8_t op, jsval_t l, jsval_t r) {
650  jsoff_t n1, off1 = vstr(js, l, &n1);
651  jsoff_t n2, off2 = vstr(js, r, &n2);
652  if (op == TOK_PLUS) {
653  jsval_t res = js_mkstr(js, NULL, n1 + n2);
654  // printf("STRPLUS %u %u %u %u [%.*s] [%.*s]\n", n1, off1, n2, off2, (int)
655  // n1,
656  // &js->mem[off1], (int) n2, &js->mem[off2]);
657  if (vtype(res) == T_STR) {
658  jsoff_t n, off = vstr(js, res, &n);
659  memmove(&js->mem[off], &js->mem[off1], n1);
660  memmove(&js->mem[off + n1], &js->mem[off2], n2);
661  }
662  return res;
663  } else if (op == TOK_EQ) {
664  bool eq = n1 == n2 && memcmp(&js->mem[off1], &js->mem[off2], n1) == 0;
665  return mkval(T_BOOL, eq ? 1 : 0);
666  } else if (op == TOK_NE) {
667  bool eq = n1 == n2 && memcmp(&js->mem[off1], &js->mem[off2], n1) == 0;
668  return mkval(T_BOOL, eq ? 0 : 1);
669  } else {
670  return js_mkerr(js, "bad str op");
671  }
672 }
673 
674 static jsval_t do_dot_op(struct js *js, jsval_t l, jsval_t r) {
675  const char *ptr = (char *) &js->code[coderefoff(r)];
676  if (vtype(r) != T_CODEREF) return js_mkerr(js, "ident expected");
677  // Handle stringvalue.length
678  if (vtype(l) == T_STR && streq(ptr, codereflen(r), "length", 6)) {
679  // qemu_warn("[do_dot_op] [TOV] %d | %f", offtolen(loadoff(js, (jsoff_t) vdata(l))), offtolen(loadoff(js, (jsoff_t) vdata(l))));
680  return tov2(offtolen(loadoff(js, (jsoff_t) vdata(l))));
681  }
682  if (vtype(l) != T_OBJ) return js_mkerr(js, "lookup in non-obj");
683  jsoff_t off = lkp(js, l, ptr, codereflen(r));
684  return off == 0 ? js_mkundef() : mkval(T_PROP, off);
685 }
686 
687 static jsval_t js_call_params(struct js *js) {
688  jsoff_t pos = js->pos;
689  uint8_t flags = js->flags;
690  js->flags |= F_NOEXEC;
691  js->consumed = 1;
692  for (bool comma = false; next(js) != TOK_EOF; comma = true) {
693  if (!comma && next(js) == TOK_RPAREN) break;
694  js_expr(js);
695  if (next(js) == TOK_RPAREN) break;
696  EXPECT(TOK_COMMA, js->flags = flags);
697  }
698  EXPECT(TOK_RPAREN, js->flags = flags);
699  js->flags = flags;
700  return mkcoderef(pos, js->pos - pos - js->tlen);
701 }
702 
703 static void reverse(jsval_t *args, int nargs) {
704  for (int i = 0; i < nargs / 2; i++) {
705  jsval_t tmp = args[i];
706  args[i] = args[nargs - i - 1], args[nargs - i - 1] = tmp;
707  }
708 }
709 
710 // Call native C function
711 static jsval_t call_c(struct js *js,
712  jsval_t (*fn)(struct js *, jsval_t *, int)) {
713  int argc = 0;
714  while (js->pos < js->clen) {
715  if (next(js) == TOK_RPAREN) break;
716  jsval_t arg = resolveprop(js, js_expr(js));
717  if (js->brk + sizeof(arg) > js->size) return js_mkerr(js, "call oom");
718  js->size -= (jsoff_t) sizeof(arg);
719  memcpy(&js->mem[js->size], &arg, sizeof(arg));
720  argc++;
721  // printf(" arg %d -> %s\n", argc, js_str(js, arg));
722  if (next(js) == TOK_COMMA) js->consumed = 1;
723  }
724  reverse((jsval_t *) &js->mem[js->size], argc);
725  jsval_t res = fn(js, (jsval_t *) &js->mem[js->size], argc);
726  setlwm(js);
727  js->size += (jsoff_t) sizeof(jsval_t) * (jsoff_t) argc; // Restore stack
728  return res;
729 }
730 
731 
732 // Call JS function. 'fn' looks like this: "(a,b) { return a + b; }"
733 static jsval_t call_js(struct js *js, const char *fn, jsoff_t fnlen) {
734  jsoff_t fnpos = 1;
735  // printf("JSCALL [%.*s] -> %.*s\n", (int) js->clen, js->code, (int) fnlen,
736  // fn);
737  // printf("JSCALL, nogc %u [%.*s]\n", js->nogc, (int) fnlen, fn);
738  mkscope(js); // Create function call scope
739  // Loop over arguments list "(a, b)" and set scope variables
740  while (fnpos < fnlen) {
741  fnpos = skiptonext(fn, fnlen, fnpos); // Skip to the identifier
742  if (fnpos < fnlen && fn[fnpos] == ')') break; // Closing paren? break!
743  jsoff_t identlen = 0; // Identifier length
744  uint8_t tok = parseident(&fn[fnpos], fnlen - fnpos, &identlen);
745  if (tok != TOK_IDENTIFIER) break;
746  // Here we have argument name. Calculate arg value
747  // printf(" [%.*s] -> %u [%.*s] -> ", (int) identlen, &fn[fnpos], js->pos,
748  // (int) js->clen, js->code);
749  js->pos = skiptonext(js->code, js->clen, js->pos);
750  js->consumed = 1;
751  jsval_t v = js->code[js->pos] == ')' ? js_mkundef() : js_expr(js);
752  // Set argument in the function scope
753  setprop(js, js->scope, js_mkstr(js, &fn[fnpos], identlen), v);
754  js->pos = skiptonext(js->code, js->clen, js->pos);
755  if (js->pos < js->clen && js->code[js->pos] == ',') js->pos++;
756  fnpos = skiptonext(fn, fnlen, fnpos + identlen); // Skip past identifier
757  if (fnpos < fnlen && fn[fnpos] == ',') fnpos++; // And skip comma
758  }
759  if (fnpos < fnlen && fn[fnpos] == ')') fnpos++; // Skip to the function body
760  fnpos = skiptonext(fn, fnlen, fnpos); // Up to the opening brace
761  if (fnpos < fnlen && fn[fnpos] == '{') fnpos++; // And skip the brace
762  size_t n = fnlen - fnpos - 1U; // Function code with stripped braces
763  // printf("flags: %d, body: %zu [%.*s]\n", js->flags, n, (int) n, &fn[fnpos]);
764  js->flags = F_CALL; // Mark we're in the function call
765  jsval_t res = js_eval(js, &fn[fnpos], n); // Call function, no GC
766  if (!is_err(res) && !(js->flags & F_RETURN)) res = js_mkundef(); // No return
767  delscope(js); // Delete call scope
768  // printf(" -> %d [%s], tok %d\n", js->flags, js_str(js, res), js->tok);
769  return res;
770 }
771 
772 
773 jsval_t elk_call_js_fnc(struct js *js, const char *fn, jsoff_t fnlen) {
774  return call_js(js,fn,fnlen);
775 }
776 
777 static jsval_t do_call_op(struct js *js, jsval_t func, jsval_t args) {
778  if (vtype(args) != T_CODEREF) return js_mkerr(js, "bad call");
779  if (vtype(func) != T_FUNC && vtype(func) != T_CFUNC)
780  return js_mkerr(js, "calling non-function");
781  const char *code = js->code; // Save current parser state
782  jsoff_t clen = js->clen, pos = js->pos; // code, position and code length
783  js->code = &js->code[coderefoff(args)]; // Point parser to args
784  js->clen = codereflen(args); // Set args length
785  js->pos = skiptonext(js->code, js->clen, 0); // Skip to 1st arg
786  uint8_t tok = js->tok, flags = js->flags; // Save flags
787  jsoff_t nogc = js->nogc;
788  jsval_t res = js_mkundef();
789  if (vtype(func) == T_FUNC) {
790  jsoff_t fnlen, fnoff = vstr(js, func, &fnlen);
791  js->nogc = (jsoff_t) (fnoff - sizeof(jsoff_t));
792  res = call_js(js, (const char *) (&js->mem[fnoff]), fnlen);
793  } else {
794  res = call_c(js, (jsval_t(*)(struct js *, jsval_t *, int)) vdata(func));
795  }
796  js->code = code, js->clen = clen, js->pos = pos; // Restore parser
797  js->flags = flags, js->tok = tok, js->nogc = nogc;
798  js->consumed = 1;
799  return res;
800 }
801 
802 static jsval_t do_op(struct js *js, uint8_t op, jsval_t lhs, jsval_t rhs) {
803  if (js->flags & F_NOEXEC) return 0;
804  jsval_t l = resolveprop(js, lhs), r = resolveprop(js, rhs);
805  // printf("OP %d %d %d\n", op, vtype(lhs), vtype(r));
806  setlwm(js);
807  if (is_err(l)) return l;
808  if (is_err(r)) return r;
809  if (is_assign(op) && vtype(lhs) != T_PROP) return js_mkerr(js, "bad lhs");
810  switch (op) {
811  case TOK_TYPEOF: return js_mkstr(js, typestr(vtype(r)), strlen(typestr(vtype(r))));
812  case TOK_CALL: return do_call_op(js, l, r);
813  case TOK_ASSIGN: return assign(js, lhs, r);
814  case TOK_POSTINC: {
815  //if (vtype(lhs) != T_OBJ && vtype(lhs) != T_CODEREF) return js_mkerr(js, "bad lhs pc");
816  do_assign_op(js, TOK_PLUS_ASSIGN, lhs, tov2(1));
817  return l;
818  }
819  case TOK_POSTDEC: {
820  //if (vtype(lhs) != T_OBJ && vtype(lhs) != T_CODEREF) return js_mkerr(js, "bad lhs dc");
821  do_assign_op(js, TOK_MINUS_ASSIGN, lhs, tov2(1));
822  return l;
823  }
824  case TOK_NOT: if (vtype(r) == T_BOOL) return mkval(T_BOOL, !vdata(r)); break;
825  }
826  if (is_assign(op)) return do_assign_op(js, op, lhs, r);
827  if (vtype(l) == T_STR && vtype(r) == T_STR) return do_string_op(js, op, l, r);
828  if (is_unary(op) && vtype(r) != T_NUM) return js_mkerr(js, "type mismatch");
829  if (!is_unary(op) && op != TOK_DOT && (vtype(l) != T_NUM || vtype(r) != T_NUM)) return js_mkerr(js, "type mismatch");
830  double_t a = tod2(l), b = tod2(r);
831  // qemu_warn("[do_op] a: %d | %f", a, a);
832  // qemu_warn("[do_op] b: %d | %f", b, b);
833  switch (op) {
834  //case TOK_EXP: return tov(pow(a, b));
835  case TOK_DIV: return tod2(r) == 0 ? js_mkerr(js, "div by zero") : tov2(a / b);
836  case TOK_REM: return tov2(a - b * ((double_t) (long) (a / b)));
837  case TOK_MUL: return tov2(a * b);
838  case TOK_PLUS: return tov2(a + b);
839  case TOK_MINUS: return tov2(a - b);
840  case TOK_XOR: return tov2((double_t)((long) a ^ (long) b));
841  case TOK_AND: return tov2((double_t)((long) a & (long) b));
842  case TOK_OR: return tov2((double_t)((long) a | (long) b));
843  case TOK_UMINUS: return tov2(-b);
844  case TOK_UPLUS: return r;
845  case TOK_TILDA: return tov2((double_t)(~(long) b));
846  case TOK_NOT: return mkval(T_BOOL, b == 0);
847  case TOK_SHL: return tov2((double_t)((long) a << (long) b));
848  case TOK_SHR: return tov2((double_t)((long) a >> (long) b));
849  case TOK_DOT: return do_dot_op(js, l, r);
850  case TOK_EQ: return mkval(T_BOOL, (long) a == (long) b);
851  case TOK_NE: return mkval(T_BOOL, (long) a != (long) b);
852  case TOK_LT: return mkval(T_BOOL, a < b);
853  case TOK_LE: return mkval(T_BOOL, a <= b);
854  case TOK_GT: return mkval(T_BOOL, a > b);
855  case TOK_GE: return mkval(T_BOOL, a >= b);
856  default: return js_mkerr(js, "unknown op %d", (int) op); // LCOV_EXCL_LINE
857  }
858 }
859 
860 static jsval_t js_str_literal(struct js *js) {
861  uint8_t *in = (uint8_t *) &js->code[js->toff];
862  uint8_t *out = &js->mem[js->brk + sizeof(jsoff_t)];
863  size_t n1 = 0, n2 = 0;
864  // printf("STR %u %lu %lu\n", js->brk, js->tlen, js->clen);
865  if (js->brk + sizeof(jsoff_t) + js->tlen > js->size)
866  return js_mkerr(js, "oom");
867  while (n2++ + 2 < js->tlen) {
868  if (in[n2] == '\\') {
869  if (in[n2 + 1] == in[0]) {
870  out[n1++] = in[0];
871  } else if (in[n2 + 1] == 'n') {
872  out[n1++] = '\n';
873  } else if (in[n2 + 1] == 't') {
874  out[n1++] = '\t';
875  } else if (in[n2 + 1] == 'r') {
876  out[n1++] = '\r';
877  } else if (in[n2 + 1] == 'x' && is_xdigit(in[n2 + 2]) &&
878  is_xdigit(in[n2 + 3])) {
879  out[n1++] = (uint8_t) ((unhex(in[n2 + 2]) << 4U) | unhex(in[n2 + 3]));
880  n2 += 2;
881  } else {
882  return js_mkerr(js, "bad str literal");
883  }
884  n2++;
885  } else {
886  out[n1++] = ((uint8_t *) js->code)[js->toff + n2];
887  }
888  }
889  return js_mkstr(js, NULL, n1);
890 }
891 
892 static jsval_t js_obj_literal(struct js *js) {
893  uint8_t exe = !(js->flags & F_NOEXEC);
894  // printf("OLIT1\n");
895  jsval_t obj = exe ? mkobj(js, 0) : js_mkundef();
896  if (is_err(obj)) return obj;
897  js->consumed = 1;
898  while (next(js) != TOK_RBRACE) {
899  jsval_t key = 0;
900  if (js->tok == TOK_IDENTIFIER) {
901  if (exe) key = js_mkstr(js, js->code + js->toff, js->tlen);
902  } else if (js->tok == TOK_STRING) {
903  if (exe) key = js_str_literal(js);
904  } else {
905  return js_mkerr(js, "parse error");
906  }
907  js->consumed = 1;
908  EXPECT(TOK_COLON, );
909  jsval_t val = js_expr(js);
910  if (exe) {
911  // printf("XXXX [%s] scope: %lu\n", js_str(js, val), vdata(js->scope));
912  if (is_err(val)) return val;
913  if (is_err(key)) return key;
914  jsval_t res = setprop(js, obj, key, resolveprop(js, val));
915  if (is_err(res)) return res;
916  }
917  if (next(js) == TOK_RBRACE) break;
918  EXPECT(TOK_COMMA, );
919  }
920  EXPECT(TOK_RBRACE, );
921  return obj;
922 }
923 
924 static jsval_t js_func_literal(struct js *js) {
925  uint8_t flags = js->flags; // Save current flags
926  js->consumed = 1;
927  EXPECT(TOK_LPAREN, js->flags = flags);
928  jsoff_t pos = js->pos - 1;
929  for (bool comma = false; next(js) != TOK_EOF; comma = true) {
930  if (!comma && next(js) == TOK_RPAREN) break;
931  EXPECT(TOK_IDENTIFIER, js->flags = flags);
932  if (next(js) == TOK_RPAREN) break;
933  EXPECT(TOK_COMMA, js->flags = flags);
934  }
935  EXPECT(TOK_RPAREN, js->flags = flags);
936  EXPECT(TOK_LBRACE, js->flags = flags);
937  js->consumed = 0;
938  js->flags |= F_NOEXEC; // Set no-execution flag to parse the
939  jsval_t res = js_block(js, false); // Skip function body - no exec
940  if (is_err(res)) { // But fail short on parse error
941  js->flags = flags;
942  return res;
943  }
944  js->flags = flags; // Restore flags
945  jsval_t str = js_mkstr(js, &js->code[pos], js->pos - pos);
946  js->consumed = 1;
947  // printf("FUNC: %u [%.*s]\n", pos, js->pos - pos, &js->code[pos]);
948  return mkval(T_FUNC, (unsigned long) vdata(str));
949 }
950 
951 #define RTL_BINOP(_f1, _f2, _cond) \
952  jsval_t res = _f1(js); \
953  while (!is_err(res) && (_cond)) { \
954  uint8_t op = js->tok; \
955  js->consumed = 1; \
956  jsval_t rhs = _f2(js); \
957  if (is_err(rhs)) return rhs; \
958  res = do_op(js, op, res, rhs); \
959  } \
960  return res;
961 
962 #define LTR_BINOP(_f, _cond) \
963  jsval_t res = _f(js); \
964  while (!is_err(res) && (_cond)) { \
965  uint8_t op = js->tok; \
966  js->consumed = 1; \
967  jsval_t rhs = _f(js); \
968  if (is_err(rhs)) return rhs; \
969  res = do_op(js, op, res, rhs); \
970  } \
971  return res;
972 
973 static jsval_t js_literal(struct js *js) {
974  next(js);
975  setlwm(js);
976  // printf("css : %u\n", js->css);
977  if (js->maxcss > 0 && js->css > js->maxcss) return js_mkerr(js, "C stack");
978  js->consumed = 1;
979  switch (js->tok) {
980  case TOK_ERR: return js_mkerr(js, "parse error");
981  case TOK_NUMBER: return js->tval;
982  case TOK_STRING: return js_str_literal(js);
983  case TOK_LBRACE: return js_obj_literal(js);
984  case TOK_FUNC: return js_func_literal(js);
985  case TOK_NULL: return js_mknull();
986  case TOK_UNDEF: return js_mkundef();
987  case TOK_TRUE: return js_mktrue();
988  case TOK_FALSE: return js_mkfalse();
989  case TOK_IDENTIFIER: return mkcoderef((jsoff_t) js->toff, (jsoff_t) js->tlen);
990  default: return js_mkerr(js, "bad expr");
991  }
992 }
993 
994 static jsval_t js_group(struct js *js) {
995  if (next(js) == TOK_LPAREN) {
996  js->consumed = 1;
997  jsval_t v = js_expr(js);
998  if (is_err(v)) return v;
999  if (next(js) != TOK_RPAREN) return js_mkerr(js, ") expected");
1000  js->consumed = 1;
1001  return v;
1002  } else {
1003  return js_literal(js);
1004  }
1005 }
1006 
1007 static jsval_t js_call_dot(struct js *js) {
1008  jsval_t res = js_group(js);
1009  if (is_err(res)) return res;
1010  if (vtype(res) == T_CODEREF) {
1011  res = lookup(js, &js->code[coderefoff(res)], codereflen(res));
1012  }
1013  while (next(js) == TOK_LPAREN || next(js) == TOK_DOT) {
1014  if (js->tok == TOK_DOT) {
1015  js->consumed = 1;
1016  res = do_op(js, TOK_DOT, res, js_group(js));
1017  } else {
1018  jsval_t params = js_call_params(js);
1019  if (is_err(params)) return params;
1020  res = do_op(js, TOK_CALL, res, params);
1021  }
1022  }
1023  return res;
1024 }
1025 
1026 static jsval_t js_postfix(struct js *js) {
1027  jsval_t res = js_call_dot(js);
1028  if (is_err(res)) return res;
1029  next(js);
1030  if (js->tok == TOK_POSTINC || js->tok == TOK_POSTDEC) {
1031  js->consumed = 1;
1032  res = do_op(js, js->tok, res, 0);
1033  }
1034  return res;
1035 }
1036 
1037 static jsval_t js_unary(struct js *js) {
1038  if (next(js) == TOK_NOT || js->tok == TOK_TILDA || js->tok == TOK_TYPEOF ||
1039  js->tok == TOK_MINUS || js->tok == TOK_PLUS) {
1040  uint8_t t = js->tok;
1041  if (t == TOK_MINUS) t = TOK_UMINUS;
1042  if (t == TOK_PLUS) t = TOK_UPLUS;
1043  js->consumed = 1;
1044  return do_op(js, t, js_mkundef(), js_unary(js));
1045  } else {
1046  return js_postfix(js);
1047  }
1048 }
1049 
1050 static jsval_t js_mul_div_rem(struct js *js) {
1051  LTR_BINOP(js_unary,
1052  (next(js) == TOK_MUL || js->tok == TOK_DIV || js->tok == TOK_REM));
1053 }
1054 
1055 static jsval_t js_plus_minus(struct js *js) {
1056  LTR_BINOP(js_mul_div_rem, (next(js) == TOK_PLUS || js->tok == TOK_MINUS));
1057 }
1058 
1059 static jsval_t js_shifts(struct js *js) {
1060  LTR_BINOP(js_plus_minus, (next(js) == TOK_SHR || next(js) == TOK_SHL ||
1061  next(js) == TOK_ZSHR));
1062 }
1063 
1064 static jsval_t js_comparison(struct js *js) {
1065  LTR_BINOP(js_shifts, (next(js) == TOK_LT || next(js) == TOK_LE ||
1066  next(js) == TOK_GT || next(js) == TOK_GE));
1067 }
1068 
1069 static jsval_t js_equality(struct js *js) {
1070  LTR_BINOP(js_comparison, (next(js) == TOK_EQ || next(js) == TOK_NE));
1071 }
1072 
1073 static jsval_t js_bitwise_and(struct js *js) {
1074  LTR_BINOP(js_equality, (next(js) == TOK_AND));
1075 }
1076 
1077 static jsval_t js_bitwise_xor(struct js *js) {
1078  LTR_BINOP(js_bitwise_and, (next(js) == TOK_XOR));
1079 }
1080 
1081 static jsval_t js_bitwise_or(struct js *js) {
1082  LTR_BINOP(js_bitwise_xor, (next(js) == TOK_OR));
1083 }
1084 
1085 static jsval_t js_logical_and(struct js *js) {
1086  jsval_t res = js_bitwise_or(js);
1087  if (is_err(res)) return res;
1088  uint8_t flags = js->flags;
1089  while (next(js) == TOK_LAND) {
1090  js->consumed = 1;
1091  res = resolveprop(js, res);
1092  if (!js_truthy(js, res)) js->flags |= F_NOEXEC; // false && ... shortcut
1093  if (js->flags & F_NOEXEC) {
1094  js_logical_and(js);
1095  } else {
1096  res = js_logical_and(js);
1097  }
1098  }
1099  js->flags = flags;
1100  return res;
1101 }
1102 
1103 static jsval_t js_logical_or(struct js *js) {
1104  jsval_t res = js_logical_and(js);
1105  if (is_err(res)) return res;
1106  uint8_t flags = js->flags;
1107  while (next(js) == TOK_LOR) {
1108  js->consumed = 1;
1109  res = resolveprop(js, res);
1110  if (js_truthy(js, res)) js->flags |= F_NOEXEC; // true || ... shortcut
1111  if (js->flags & F_NOEXEC) {
1112  js_logical_or(js);
1113  } else {
1114  res = js_logical_or(js);
1115  }
1116  }
1117  js->flags = flags;
1118  return res;
1119 }
1120 
1121 static jsval_t js_ternary(struct js *js) {
1122  jsval_t res = js_logical_or(js);
1123  if (next(js) == TOK_Q) {
1124  uint8_t flags = js->flags;
1125  js->consumed = 1;
1126  if (js_truthy(js, resolveprop(js, res))) {
1127  res = js_ternary(js);
1128  js->flags |= F_NOEXEC;
1129  EXPECT(TOK_COLON, js->flags = flags);
1130  js_ternary(js);
1131  js->flags = flags;
1132  } else {
1133  js->flags |= F_NOEXEC;
1134  js_ternary(js);
1135  EXPECT(TOK_COLON, js->flags = flags);
1136  js->flags = flags;
1137  res = js_ternary(js);
1138  }
1139  }
1140  return res;
1141 }
1142 
1143 static jsval_t js_assignment(struct js *js) {
1144  RTL_BINOP(js_ternary, js_assignment,
1145  (next(js) == TOK_ASSIGN || js->tok == TOK_PLUS_ASSIGN ||
1146  js->tok == TOK_MINUS_ASSIGN || js->tok == TOK_MUL_ASSIGN ||
1147  js->tok == TOK_DIV_ASSIGN || js->tok == TOK_REM_ASSIGN ||
1148  js->tok == TOK_SHL_ASSIGN || js->tok == TOK_SHR_ASSIGN ||
1149  js->tok == TOK_ZSHR_ASSIGN || js->tok == TOK_AND_ASSIGN ||
1150  js->tok == TOK_XOR_ASSIGN || js->tok == TOK_OR_ASSIGN));
1151 }
1152 
1153 static jsval_t js_expr(struct js *js) {
1154  return js_assignment(js);
1155 }
1156 
1157 static jsval_t js_let(struct js *js) {
1158  uint8_t exe = !(js->flags & F_NOEXEC);
1159  js->consumed = 1;
1160  for (;;) {
1161  EXPECT(TOK_IDENTIFIER, );
1162  js->consumed = 0;
1163  jsoff_t noff = js->toff, nlen = js->tlen;
1164  char *name = (char *) &js->code[noff];
1165  jsval_t v = js_mkundef();
1166  js->consumed = 1;
1167  if (next(js) == TOK_ASSIGN) {
1168  js->consumed = 1;
1169  v = js_expr(js);
1170  if (is_err(v)) return v; // Propagate error if any
1171  }
1172  if (exe) {
1173  if (lkp(js, js->scope, name, nlen) > 0)
1174  return js_mkerr(js, "'%.*s' already declared", (int) nlen, name);
1175  jsval_t x =
1176  setprop(js, js->scope, js_mkstr(js, name, nlen), resolveprop(js, v));
1177  if (is_err(x)) return x;
1178  }
1179  if (next(js) == TOK_SEMICOLON || next(js) == TOK_EOF) break; // Stop
1180  EXPECT(TOK_COMMA, );
1181  }
1182  return js_mkundef();
1183 }
1184 
1185 static jsval_t js_block_or_stmt(struct js *js) {
1186  if (next(js) == TOK_LBRACE) return js_block(js, !(js->flags & F_NOEXEC));
1187  jsval_t res = resolveprop(js, js_stmt(js));
1188  js->consumed = 0; //
1189  return res;
1190 }
1191 
1192 static jsval_t js_if(struct js *js) {
1193  js->consumed = 1;
1194  EXPECT(TOK_LPAREN, );
1195  jsval_t res = js_mkundef(), cond = resolveprop(js, js_expr(js));
1196  EXPECT(TOK_RPAREN, );
1197  bool cond_true = js_truthy(js, cond), exe = !(js->flags & F_NOEXEC);
1198  // printf("IF COND: %s, true? %d\n", js_str(js, cond), cond_true);
1199  if (!cond_true) js->flags |= F_NOEXEC;
1200  jsval_t blk = js_block_or_stmt(js);
1201  if (cond_true) res = blk;
1202  if (exe && !cond_true) js->flags &= (uint8_t) ~F_NOEXEC;
1203  if (lookahead(js) == TOK_ELSE) {
1204  js->consumed = 1;
1205  next(js);
1206  js->consumed = 1;
1207  if (cond_true) js->flags |= F_NOEXEC;
1208  blk = js_block_or_stmt(js);
1209  if (!cond_true) res = blk;
1210  if (cond_true && exe) js->flags &= (uint8_t) ~F_NOEXEC;
1211  }
1212  return res;
1213 }
1214 
1215 static inline bool expect(struct js *js, uint8_t tok, jsval_t *res) {
1216  if (next(js) != tok) {
1217  *res = js_mkerr(js, "parse error");
1218  return false;
1219  } else {
1220  js->consumed = 1;
1221  return true;
1222  }
1223 }
1224 
1225 static inline bool is_err2(jsval_t *v, jsval_t *res) {
1226 bool r = is_err(*v);
1227 if (r) *res = *v;
1228 return r;
1229 }
1230 
1231 static jsval_t js_for(struct js *js) {
1232  uint8_t flags = js->flags, exe = !(flags & F_NOEXEC);
1233  jsval_t v, res = js_mkundef();
1234  jsoff_t pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
1235  if (exe) mkscope(js); // Enter new scope
1236  if (!expect(js, TOK_FOR, &res)) goto done;
1237  if (!expect(js, TOK_LPAREN, &res)) goto done;
1238 
1239  if (next(js) == TOK_SEMICOLON) { // initialisation
1240  } else if (next(js) == TOK_LET) {
1241  v = js_let(js);
1242  if (is_err2(&v, &res)) goto done;
1243  } else {
1244  v = js_expr(js);
1245  if (is_err2(&v, &res)) goto done;
1246  }
1247  if (!expect(js, TOK_SEMICOLON, &res)) goto done;
1248  js->flags |= F_NOEXEC;
1249  pos1 = js->pos; // condition
1250  if (next(js) != TOK_SEMICOLON) {
1251  v = js_expr(js);
1252  if (is_err2(&v, &res)) goto done;
1253  }
1254  if (!expect(js, TOK_SEMICOLON, &res)) goto done;
1255  pos2 = js->pos; // final expr
1256  if (next(js) != TOK_RPAREN) {
1257  v = js_expr(js);
1258  if (is_err2(&v, &res)) goto done;
1259  }
1260  if (!expect(js, TOK_RPAREN, &res)) goto done;
1261  pos3 = js->pos; // body
1262  v = js_block_or_stmt(js);
1263  if (is_err2(&v, &res)) goto done;
1264  pos4 = js->pos; // end of body
1265  while (!(flags & F_NOEXEC)) {
1266  js->flags = flags, js->pos = pos1, js->consumed = 1;
1267  if (next(js) != TOK_SEMICOLON) { // Is condition specified?
1268  v = resolveprop(js, js_expr(js)); // Yes. check condition
1269  if (is_err2(&v, &res)) goto done; // Fail short on error
1270  if (!js_truthy(js, v)) break; // Exit the loop if condition is false
1271  }
1272  js->pos = pos3, js->consumed = 1, js->flags |= F_LOOP; // Execute the
1273  v = js_block_or_stmt(js); // loop body
1274  if (is_err2(&v, &res)) goto done; // Fail on error
1275  if (js->flags & F_BREAK) break; // break was executed - exit the loop!
1276  js->flags = flags, js->pos = pos2, js->consumed = 1; // Jump to final expr
1277  if (next(js) != TOK_RPAREN) { // Is it specified?
1278  v = js_expr(js); // Yes. Execute it
1279  if (is_err2(&v, &res)) goto done; // On error, fail short
1280  }
1281  }
1282  js->pos = pos4, js->tok = TOK_SEMICOLON, js->consumed = 0;
1283  done:
1284  if (exe) delscope(js); // Exit scope
1285  js->flags = flags; // Restore flags
1286  return res;
1287 }
1288 
1289 // Пример обработчика для ключевого слова var
1290 static jsval_t js_var(struct js *js) {
1291  uint8_t exe = !(js->flags & F_NOEXEC);
1292  js->consumed = 1;
1293  for (;;) {
1294  EXPECT(TOK_IDENTIFIER, );
1295  js->consumed = 0;
1296  jsoff_t noff = js->toff, nlen = js->tlen;
1297  char *name = (char *) &js->code[noff];
1298  jsval_t v = js_mkundef();
1299  js->consumed = 1;
1300  if (next(js) == TOK_ASSIGN) {
1301  js->consumed = 1;
1302  v = js_expr(js);
1303  if (is_err(v)) return v; // Propagate error if any
1304  }
1305  if (exe) {
1306  // Логика обработки ключевого слова var
1307  // Например, регистрация переменной в текущей области видимости или в глобальной области видимости
1308  if (lkp(js, js->scope, name, nlen) > 0) {
1309  return js_mkerr(js, "'%.*s' already declared", (int) nlen, name);
1310  }
1311  jsval_t x = setprop(js, js->scope, js_mkstr(js, name, nlen), resolveprop(js, v));
1312  if (is_err(x)) {
1313  return x;
1314  }
1315  }
1316  if (next(js) == TOK_SEMICOLON || next(js) == TOK_EOF) break; // Stop
1317  EXPECT(TOK_COMMA, );
1318  }
1319  return js_mkundef();
1320 }
1321 
1322 // Пример обработчика для ключевого слова do
1324 static jsval_t js_do_while(struct js *js) {
1325  uint8_t exe = !(js->flags & F_NOEXEC);
1326  jsval_t res = js_mkundef();
1327  jsoff_t pos1, pos2;
1328 
1329  if (!expect(js, TOK_DO, &res)) {
1330  return res;
1331  }
1332  pos1 = js->pos; // Начало блока
1333  js->consumed = 0; // Сброс флага consumed перед блоком
1334  res = js_block_or_stmt(js);
1335 
1336  if (is_err(res)) {
1337  return res;
1338  }
1339 
1340  if (!expect(js, TOK_WHILE, &res)) {
1341  return res;
1342  }
1343  if (!expect(js, TOK_LPAREN, &res)) {
1344  return res;
1345  }
1346 
1347  pos2 = js->pos; // Запоминаем позицию для дальнейшего возврата
1348  js->pos = pos1; // Возвращаемся к началу блока
1349  if (exe) {
1350  do {
1351  res = js_block_or_stmt(js);
1352  if (is_err(res)) {
1353  return res;
1354  }
1355  js->pos = pos2;
1356  res = js_expr(js);
1357  if (is_err(res)) {
1358  return res;
1359  }
1360  } while (js_truthy(js, res));
1361  } else {
1362  js->pos = pos2; // Возвращение к позиции после do
1363  }
1364  return res;
1365 }
1366 
1367 
1368 static jsval_t js_while(struct js *js) {
1369  uint8_t flags = js->flags, exe = !(flags & F_NOEXEC);
1370  jsval_t v, res = js_mkundef();
1371  jsoff_t pos1 = 0, pos2 = 0;
1372  if (exe) mkscope(js); // Входим в новую область видимости
1373  if (!expect(js, TOK_WHILE, &res)) goto done;
1374  if (!expect(js, TOK_LPAREN, &res)) goto done;
1375 
1376  pos1 = js->pos; // Начало условия
1377  v = js_expr(js); // Вычисляем условие выражения
1378  if (is_err2(&v, &res)) goto done;
1379 
1380  if (!expect(js, TOK_RPAREN, &res)) goto done;
1381  pos2 = js->pos; // Конец условия
1382  v = js_block_or_stmt(js); // Выполняем тело цикла
1383  if (is_err2(&v, &res)) goto done;
1384 
1385  while (!(flags & F_NOEXEC)) {
1386  js->flags = flags, js->pos = pos1, js->consumed = 1;
1387  // Проверяем условие цикла
1388  v = resolveprop(js, js_expr(js));
1389  if (is_err2(&v, &res)) goto done;
1390  if (!js_truthy(js, v)) break; // Если условие ложно, выходим из цикла
1391  js->pos = pos2, js->consumed = 1; // Переходим к концу условия
1392  v = js_block_or_stmt(js); // Выполняем тело цикла
1393  if (is_err2(&v, &res)) goto done;
1394  }
1395 
1396  done:
1397  if (exe) delscope(js); // Выходим из области видимости
1398  js->flags = flags; // Восстанавливаем флаги
1399  js->tok = TOK_SEMICOLON;
1400  return res; // Возвращаем результат
1401 }
1402 
1403 static jsval_t js_break(struct js *js) {
1404  if (js->flags & F_NOEXEC) {
1405  } else {
1406  if (!(js->flags & F_LOOP)) return js_mkerr(js, "not in loop");
1407  js->flags |= F_BREAK | F_NOEXEC;
1408  }
1409  js->consumed = 1;
1410  return js_mkundef();
1411 }
1412 
1413 static jsval_t js_continue(struct js *js) {
1414  if (js->flags & F_NOEXEC) {
1415  } else {
1416  if (!(js->flags & F_LOOP)) return js_mkerr(js, "not in loop");
1417  js->flags |= F_NOEXEC;
1418  }
1419  js->consumed = 1;
1420  return js_mkundef();
1421 }
1422 
1423 static jsval_t js_return(struct js *js) {
1424  uint8_t exe = !(js->flags & F_NOEXEC);
1425  js->consumed = 1;
1426  if (exe && !(js->flags & F_CALL)) return js_mkerr(js, "not in func");
1427  if (next(js) == TOK_SEMICOLON) return js_mkundef();
1428  jsval_t res = resolveprop(js, js_expr(js));
1429  if (exe) {
1430  js->pos = js->clen; // Shift to the end - exit the code snippet
1431  js->flags |= F_RETURN; // Tell caller we've executed
1432  }
1433  return resolveprop(js, res);
1434 }
1435 
1436 
1437 static jsval_t js_switch(struct js *js) {
1438  jsval_t res, expr, match;
1439  uint8_t exe = !(js->flags & F_NOEXEC);
1440 
1441  js->consumed = 1;
1442  expr = resolveprop(js, js_expr(js)); // Вычисление выражения оператора switch
1443 
1444  // Цикл поиска совпадения case
1445  while (next(js) == TOK_CASE) {
1446  js->consumed = 1;
1447  match = resolveprop(js, js_expr(js)); // Вычисление выражения case
1448  if (js_cmp(js, expr, match) == 0) { // Поиск совпадения
1449  res = js_block(js, exe); // Выполнение блока кода для совпавшего case
1450  break;
1451  }
1452  }
1453 
1454  // Поиск и выполнение блока кода default
1455  if (next(js) == TOK_DEFAULT) {
1456  js->consumed = 1;
1457  res = js_block(js, exe); // Выполнение блока кода для default, если совпадений нет
1458  }
1459 
1460  //js->tok = TOK_SEMICOLON;
1461  return res;
1462 }
1463 
1464 static jsval_t js_stmt(struct js *js) {
1465  jsval_t res;
1466  // jsoff_t pos = js->pos - js->tlen;
1467  if (js->brk > js->gct) js_gc(js);
1468  switch (next(js)) {
1469  case TOK_CASE: case TOK_CATCH: case TOK_CLASS: case TOK_CONST:
1470  case TOK_DEFAULT: case TOK_DELETE: case TOK_DO: case TOK_FINALLY:
1471  case TOK_IN: case TOK_INSTANCEOF: case TOK_NEW:
1472  case TOK_THIS: case TOK_THROW: case TOK_TRY: case TOK_VOID:
1473  case TOK_WITH: case TOK_YIELD:
1474  res = js_mkerr(js, "'%.*s' not implemented", (int) js->tlen, js->code + js->toff);
1475  break;
1476  case TOK_SWITCH: res = js_switch(js); break;
1477  case TOK_WHILE: res = js_while(js); break;
1478  case TOK_CONTINUE: res = js_continue(js); break;
1479  case TOK_BREAK: res = js_break(js); break;
1480  case TOK_LET: res = js_let(js); break;
1481  case TOK_VAR: res = js_var(js); break;
1482  case TOK_IF: res = js_if(js); break;
1483  case TOK_LBRACE: res = js_block(js, !(js->flags & F_NOEXEC)); break;
1484  case TOK_FOR: res = js_for(js); break; // 25222 -> 27660
1485  case TOK_RETURN: res = js_return(js); break;
1486  default: res = resolveprop(js, js_expr(js)); break;
1487  }
1488  //printf("STMT [%.*s] -> %s, tok %d, flags %d\n", (int) (js->pos - pos), &js->code[pos], js_str(js, res), next(js), js->flags);
1489  if (next(js) != TOK_SEMICOLON && next(js) != TOK_EOF && next(js) != TOK_RBRACE){
1490  //qemu_err("[JSE] [Fatal] the character ';' is expected, but %d is received", next(js));
1491 
1492  return js_mkerr(js, "; expected");
1493  }
1494  js->consumed = 1;
1495  return res;
1496 }
1497 
1498 struct js *js_create(void *buf, size_t len) {
1499  struct js *js = NULL;
1500  if (len < sizeof(*js) + esize(T_OBJ)) return js;
1501  memset(buf, 0, len); // Important!
1502  js = (struct js *) buf; // struct js lives at the beginning
1503  js->mem = (uint8_t *) (js + 1); // Then goes memory for JS data
1504  js->size = (jsoff_t) (len - sizeof(*js)); // JS memory size
1505  js->scope = mkobj(js, 0); // Create global scope
1506  js->size = js->size / 8U * 8U; // Align js->size by 8 byte
1507  js->lwm = js->size; // Initial LWM: 100% free
1508  js->isFatal = 0; // Сброс состояния фатальности
1509  js->gct = js->size / 2;
1510  return js;
1511 }
1512 
1513 void js_setgct(struct js *js, size_t gct) { js->gct = (jsoff_t) gct; }
1514 void js_setmaxcss(struct js *js, size_t max) { js->maxcss = (jsoff_t) max; }
1515 jsval_t js_mktrue(void) { return mkval(T_BOOL, 1); }
1516 jsval_t js_mkfalse(void) { return mkval(T_BOOL, 0); }
1517 jsval_t js_mkundef(void) { return mkval(T_UNDEF, 0); }
1518 jsval_t js_mknull(void) { return mkval(T_NULL, 0); }
1519 jsval_t js_mknum(double_t value) {
1520  // warn("[js_mknum] [TOV] %d | %f", value, value);
1521  return tov2(value);
1522 }
1523 jsval_t js_mkobj(struct js *js) { return mkobj(js, 0); }
1524 jsval_t js_mkfun(jsval_t (*fn)(struct js *, jsval_t *, int)) { return mkval(T_CFUNC, (size_t) (void *) fn); }
1525 double_t js_getnum(jsval_t value) {
1526  // qemu_note("[js_getnum] [TOD] %d | %f", tod2(value),tod2(value));
1527  return tod2(value);
1528 }
1529 int js_getbool(jsval_t value) { return vdata(value) & 1 ? 1 : 0; }
1530 
1531 jsval_t js_glob(struct js *js) { (void) js; return mkval(T_OBJ, 0); }
1532 
1533 void js_set(struct js *js, jsval_t obj, const char *key, jsval_t val) {
1534  if (vtype(obj) == T_OBJ) setprop(js, obj, js_mkstr(js, key, strlen(key)), val);
1535 }
1536 
1537 char *js_getstr(struct js *js, jsval_t value, size_t *len) {
1538  if (vtype(value) != T_STR) return NULL;
1539  jsoff_t n, off = vstr(js, value, &n);
1540  if (len != NULL) *len = n;
1541  return (char *) &js->mem[off];
1542 }
1543 
1544 int js_type(jsval_t val) {
1545  switch (vtype(val)) {
1546  case T_UNDEF: return JS_UNDEF;
1547  case T_NULL: return JS_NULL;
1548  case T_BOOL: return vdata(val) == 0 ? JS_FALSE: JS_TRUE;
1549  case T_STR: return JS_STR;
1550  case T_NUM: return JS_NUM;
1551  case T_ERR: return JS_ERR;
1552  default: return JS_PRIV;
1553  }
1554 }
1555 void js_stats(struct js *js, size_t *total, size_t *lwm, size_t *css) {
1556  if (total) *total = js->size;
1557  if (lwm) *lwm = js->lwm;
1558  if (css) *css = js->css;
1559 }
1560 
1561 void js_statsInfo(struct js *js) {
1562  qemu_ok("\n[JSE] Information: \n\ Total RAM: %d b. \n Low free ram: %d b. \n Maximum stack: %d b.",js->size,js->lwm, js->css);
1563 }
1564 
1565 bool js_chkargs(jsval_t *args, int nargs, const char *spec) {
1566  int i = 0, ok = 1;
1567  for (; ok && i < nargs && spec[i]; i++) {
1568  uint8_t t = vtype(args[i]), c = (uint8_t) spec[i];
1569  ok = (c == 'b' && t == T_BOOL) || (c == 'd' && t == T_NUM) ||
1570  (c == 's' && t == T_STR) || (c == 'j');
1571  }
1572  if (spec[i] != '\0' || i != nargs) ok = 0;
1573  return ok;
1574 }
1575 
1576 jsval_t js_eval(struct js *js, const char *buf, size_t len) {
1577  // printf("EVAL: [%.*s]\n", (int) len, buf);
1578  jsval_t res = js_mkundef();
1579  if (len == (size_t) ~0U) len = strlen(buf);
1580  js->consumed = 1;
1581  js->tok = TOK_ERR;
1582  js->code = buf;
1583  js->clen = (jsoff_t) len;
1584  js->pos = 0;
1585  js->cstk = &res;
1586  while (next(js) != TOK_EOF && !is_err(res)) {
1587  res = js_stmt(js);
1588  }
1589  return res;
1590 }
1591 
1592 void js_dump(struct js *js) {
1593  jsoff_t off = 0, v;
1594  printf("JS size %u, brk %u, lwm %u, css %u, nogc %u\n", js->size, js->brk,
1595  js->lwm, (unsigned) js->css, js->nogc);
1596  while (off < js->brk) {
1597  memcpy(&v, &js->mem[off], sizeof(v));
1598  printf(" %5u: ", off);
1599  if ((v & 3U) == T_OBJ) {
1600  printf("OBJ %u %u\n", v & ~3U,
1601  loadoff(js, (jsoff_t) (off + sizeof(off))));
1602  } else if ((v & 3U) == T_PROP) {
1603  jsoff_t koff = loadoff(js, (jsoff_t) (off + sizeof(v)));
1604  jsval_t val = loadval(js, (jsoff_t) (off + sizeof(v) + sizeof(v)));
1605  printf("PROP next %u, koff %u vtype %d vdata %lu\n", v & ~3U, koff,
1606  vtype(val), (unsigned long) vdata(val));
1607  } else if ((v & 3) == T_STR) {
1608  jsoff_t len = offtolen(v);
1609  printf("STR %u [%.*s]\n", len, (int) len, js->mem + off + sizeof(v));
1610  } else {
1611  printf("???\n");
1612  break;
1613  }
1614  off += esize(v);
1615  }
1616 }
Основные определения ядра
size_t strlen(const char *str)
Возращает длину строки
Definition: string.c:88
int strcmp(const char *s1, const char *s2)
Сравнение строк
Definition: string.c:253
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
void * memcpy(void *restrict destination, const void *restrict source, size_t n)
Копирование непересекающихся массивов используя SSE.
Definition: string.c:173
int32_t memcmp(const char *s1, const char *s2, size_t n)
Сравнение массивов
Definition: string.c:305
void * memmove(void *dest, void *src, size_t count)
Копирование массивов (в том числе пересекающихся)
Definition: string.c:220
Структура массива
Definition: jse_array.h:40
Definition: elk.h:48
Definition: stdarg.h:9
Значение ключа
Definition: jse_array.h:22