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 }
common.h
Основные определения ядра
strlen
size_t strlen(const char *str)
Возращает длину строки
Definition: string.c:88
strcmp
int strcmp(const char *s1, const char *s2)
Сравнение строк
Definition: string.c:253
memset
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
memcpy
void * memcpy(void *restrict destination, const void *restrict source, size_t n)
Копирование непересекающихся массивов используя SSE.
Definition: string.c:173
memcmp
int32_t memcmp(const char *s1, const char *s2, size_t n)
Сравнение массивов
Definition: string.c:305
memmove
void * memmove(void *dest, void *src, size_t count)
Копирование массивов (в том числе пересекающихся)
Definition: string.c:220
JSE_ARRAY
Структура массива
Definition: jse_array.h:40
array
Definition: minesweeper.c:29
js
Definition: elk.h:48
va_list
Definition: stdarg.h:9
JSE_ARRAY_VALUE
Значение ключа
Definition: jse_array.h:22