2 #include "portability.h"
3 #include "lib/string.h"
6 #include <lib/sprintf.h>
9 #include "elk_config.h"
13 #define JS_EXPR_MAX 20
16 #ifndef JS_GC_THRESHOLD
17 #define JS_GC_THRESHOLD 0.75
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] :
"??";
42 static void js_printPos(
struct js *
js) {
44 bool lastStop =
false;
47 if (
js->toff <
js->incSize) {
48 qemu_err(
"[JSE] [Error] Library parsing...");
51 for (
int i =
js->incSize; i < js->clen; i++) {
53 if (i >
js->toff) lastStop =
true;
54 if (buf[i] ==
'\n' && lastStop) {
57 }
else if (buf[i] ==
'\n' && !lastStop) {
62 char* t_dump = calloc(1, (endLine -
js->toff) + 1);
64 memcpy(t_dump, buf +
js->toff, endLine -
js->toff);
68 qemu_err(
"[JSE] [Error] Line %d: %s", line +
js->paramSize, t_dump);
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);
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;
101 static size_t cpy(
char *dst,
size_t dstlen,
const char *src,
size_t srclen) {
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';
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;
113 while (next < js->brk && next != 0) {
116 jsoff_t koff = loadoff(
js, next + (jsoff_t)
sizeof(next));
117 jsval_t val = loadval(
js, next + (jsoff_t) (
sizeof(next) +
sizeof(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) {
124 n += tostr(
js, val, buf + n, len - n);
127 n += tostr(
js, val, buf + n, len - n);
129 next = loadoff(
js, next) & ~3U;
132 return n + cpy(buf + n, len - n,
"}", 1);
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;
140 while (next < js->brk && next != 0) {
143 jsoff_t koff = loadoff(
js, next + (jsoff_t)
sizeof(next));
144 jsval_t val = loadval(
js, next + (jsoff_t) (
sizeof(next) +
sizeof(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) {
154 n += tostr(
js, val, buf + n, len - n);
155 n += cpy(buf + n - 1, len - n,
"\"", 1);
158 n += tostr(
js, val, buf + n, len - n);
159 n += cpy(buf + n, len - n,
"\"", 1);
161 next = loadoff(
js, next) & ~3U;
164 for (
int i = 0; i < n; i++) {
165 if (buf[i] == 0x0) buf[i] =
' ';
168 qemu_log(
"[JSE] STROBJ RETURn %d", len);
169 return n + cpy(buf + n, len - n,
"}", 1);
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);
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));
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);
191 n += cpy(buf + n, len - n, (
char *) &
js->mem[off], slen);
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);
203 jsval_t js_mkerr(
struct js *
js,
const char *xx, ...) {
206 size_t n = cpy(
js->errmsg,
sizeof(
js->errmsg),
"ERROR: ", 7);
208 vsnprintf(
js->errmsg + n,
sizeof(
js->errmsg) - n, xx, ap);
210 js->errmsg[
sizeof(
js->errmsg) - 1] =
'\0';
212 js->pos =
js->clen,
js->tok = TOK_EOF,
js->consumed = 0;
213 return mkval(T_ERR, 0);
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));
232 static int js_cmp(
struct js *
js, jsval_t a, jsval_t b) {
236 const char *a_str = js_str(
js, a);
237 const char *b_str = js_str(
js, b);
239 if (
strcmp(a_str, b_str) < 0) {
241 }
else if (
strcmp(a_str, b_str) > 0) {
249 #ifdef SAYORI_JSE_ARRAY_H
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;
256 if (
js->brk +
sizeof(jsoff_t) >=
js->size)
return array;
258 jsoff_t next = loadoff(
js, (jsoff_t) vdata(value)) & ~3U;
259 while (next < js->brk && next != 0) {
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) {
271 tostr(
js, val, VALUE, available);
272 jse_array_push(
array, KEY, (
JSE_ARRAY_VALUE){.str_value = jse_strdup(VALUE)}, JSE_ARRAY_TYPE_STRING);
276 next = loadoff(
js, next) & ~3U;
283 const char *js_str(
struct js *
js, jsval_t value) {
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);
295 bool js_truthy(
struct js *
js, jsval_t v) {
296 uint8_t t = vtype(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);
303 static jsoff_t js_alloc(
struct js *
js,
size_t size) {
304 jsoff_t ofs =
js->brk;
305 size = align32((jsoff_t) size);
306 if (
js->brk + size >
js->size)
return ~(jsoff_t) 0;
307 js->brk += (jsoff_t) size;
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));
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;
319 return mkval(b & 3, ofs);
322 jsval_t js_mkstr(
struct js *
js,
const void *ptr,
size_t len) {
323 jsoff_t n = (jsoff_t) (len + 1);
325 return mkentity(
js, (jsoff_t) ((n << 2) | T_STR), ptr, n);
328 static jsval_t mkobj(
struct js *
js, jsoff_t parent) {
329 return mkentity(
js, 0 | T_OBJ, &parent,
sizeof(parent));
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);
334 jsoff_t b, head = (jsoff_t) vdata(obj);
335 char buf[
sizeof(koff) +
sizeof(v)];
336 memcpy(&b, &
js->mem[head],
sizeof(b));
337 memcpy(buf, &koff,
sizeof(koff));
338 memcpy(buf +
sizeof(koff), &v,
sizeof(v));
339 jsoff_t brk =
js->brk | T_OBJ;
340 memcpy(&
js->mem[head], &brk,
sizeof(brk));
341 return mkentity(
js, (b & ~3U) | T_PROP, buf,
sizeof(buf));
345 static inline jsoff_t esize(jsoff_t w) {
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;
354 static bool is_mem_entity(uint8_t t) {
355 return t == T_OBJ || t == T_PROP || t == T_STR || t == T_FUNC;
358 #define GCMASK ~(((jsoff_t) ~0) >> 1)
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) {
361 v = loadoff(
js, off);
362 n = esize(v & ~GCMASK);
363 if (v & GCMASK)
continue;
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);
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)));
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;
385 if (
js->code > (
char *)
js->mem &&
js->code - (
char *)
js->mem <
js->size &&
386 js->code - (
char *)
js->mem > start) {
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);
400 js_fixup_offsets(
js, off, n);
401 memmove(&
js->mem[off], &
js->mem[off + n],
js->brk - off - n);
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);
415 static jsoff_t js_unmark_entity(
struct js *
js, jsoff_t off) {
416 jsoff_t v = loadoff(
js, off);
418 saveoff(
js, off, v & ~GCMASK);
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));
423 js_unmark_entity(
js, loadoff(
js, (jsoff_t) (off +
sizeof(off))));
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));
428 return v & ~(GCMASK | 3U);
431 static void js_unmark_used_entities(
struct js *
js) {
432 jsval_t scope =
js->scope;
434 js_unmark_entity(
js, (jsoff_t) vdata(scope));
435 scope = upper(
js, scope);
436 }
while (vdata(scope) != 0);
437 if (
js->nogc) js_unmark_entity(
js,
js->nogc);
442 void js_gc(
struct js *
js) {
445 if (
js->nogc == (jsoff_t) ~0)
return;
446 js_mark_all_entities_for_deletion(
js);
447 js_unmark_used_entities(
js);
448 js_delete_marked_entities(
js);
452 static jsoff_t skiptonext(
const char *code, jsoff_t len, jsoff_t n) {
455 if (is_space(code[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++;
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;
472 static uint8_t parsekeyword(
const char *buf,
size_t len) {
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;
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;
490 return TOK_IDENTIFIER;
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);
501 static uint8_t next(
struct js *
js) {
502 if (
js->consumed == 0)
return js->tok;
505 js->toff =
js->pos = skiptonext(
js->code,
js->clen,
js->pos);
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
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);
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;
541 if (buf[
js->tlen + 1] ==
'x') {
542 if (
js->toff +
js->tlen + 4 >
js->clen)
break;
546 js->tlen += increment;
548 if (buf[0] == buf[
js->tlen])
js->tok = TOK_STRING,
js->tlen++;
550 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
552 js->tval = tov2(jse_p_int(buf, &end));
553 TOK(TOK_NUMBER, (jsoff_t) (end - buf));
555 default:
js->tok = parseident(buf,
js->clen -
js->toff, &
js->tlen);
break;
557 js->pos =
js->toff +
js->tlen;
562 static inline uint8_t lookahead(
struct js *
js) {
563 uint8_t old =
js->tok, tok = 0;
564 jsoff_t pos =
js->pos;
567 js->pos = pos,
js->tok = old;
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);
578 static void delscope(
struct js *
js) {
579 js->scope = upper(
js,
js->scope);
583 static jsval_t js_block(
struct js *
js,
bool create_scope) {
584 jsval_t res = js_mkundef();
585 if (create_scope) mkscope(
js);
588 while (next(
js) != TOK_EOF && next(
js) != TOK_RBRACE && !is_err(res)) {
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");
598 if (create_scope) delscope(
js);
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;
606 while (off < js->brk && off != 0) {
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)];
611 if (streq(buf, len, p, klen))
return off;
612 off = loadoff(
js, off) & ~3U;
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;
625 mkval(T_OBJ, loadoff(
js, (jsoff_t) (vdata(scope) +
sizeof(jsoff_t))));
628 return js_mkerr(
js,
"'%.*s' not found", (
int) len, buf);
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)));
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);
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);
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);
657 if (vtype(res) == T_STR) {
658 jsoff_t n, off = vstr(
js, res, &n);
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);
670 return js_mkerr(
js,
"bad str op");
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");
678 if (vtype(l) == T_STR && streq(ptr, codereflen(r),
"length", 6)) {
680 return tov2(offtolen(loadoff(
js, (jsoff_t) vdata(l))));
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);
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;
692 for (
bool comma =
false; next(
js) != TOK_EOF; comma =
true) {
693 if (!comma && next(
js) == TOK_RPAREN)
break;
695 if (next(
js) == TOK_RPAREN)
break;
696 EXPECT(TOK_COMMA,
js->flags = flags);
698 EXPECT(TOK_RPAREN,
js->flags = flags);
700 return mkcoderef(pos,
js->pos - pos -
js->tlen);
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;
711 static jsval_t call_c(
struct js *
js,
712 jsval_t (*fn)(
struct js *, jsval_t *,
int)) {
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));
722 if (next(
js) == TOK_COMMA)
js->consumed = 1;
724 reverse((jsval_t *) &
js->mem[
js->size], argc);
725 jsval_t res = fn(
js, (jsval_t *) &
js->mem[
js->size], argc);
727 js->size += (jsoff_t)
sizeof(jsval_t) * (jsoff_t) argc;
733 static jsval_t call_js(
struct js *
js,
const char *fn, jsoff_t fnlen) {
740 while (fnpos < fnlen) {
741 fnpos = skiptonext(fn, fnlen, fnpos);
742 if (fnpos < fnlen && fn[fnpos] ==
')')
break;
743 jsoff_t identlen = 0;
744 uint8_t tok = parseident(&fn[fnpos], fnlen - fnpos, &identlen);
745 if (tok != TOK_IDENTIFIER)
break;
749 js->pos = skiptonext(
js->code,
js->clen,
js->pos);
751 jsval_t v =
js->code[
js->pos] ==
')' ? js_mkundef() : js_expr(
js);
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);
757 if (fnpos < fnlen && fn[fnpos] ==
',') fnpos++;
759 if (fnpos < fnlen && fn[fnpos] ==
')') fnpos++;
760 fnpos = skiptonext(fn, fnlen, fnpos);
761 if (fnpos < fnlen && fn[fnpos] ==
'{') fnpos++;
762 size_t n = fnlen - fnpos - 1U;
765 jsval_t res = js_eval(
js, &fn[fnpos], n);
766 if (!is_err(res) && !(
js->flags & F_RETURN)) res = js_mkundef();
773 jsval_t elk_call_js_fnc(
struct js *
js,
const char *fn, jsoff_t fnlen) {
774 return call_js(
js,fn,fnlen);
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;
782 jsoff_t clen =
js->clen, pos =
js->pos;
783 js->code = &
js->code[coderefoff(args)];
784 js->clen = codereflen(args);
785 js->pos = skiptonext(
js->code,
js->clen, 0);
786 uint8_t tok =
js->tok, flags =
js->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);
794 res = call_c(
js, (jsval_t(*)(
struct js *, jsval_t *,
int)) vdata(func));
796 js->code = code,
js->clen = clen,
js->pos = pos;
797 js->flags = flags,
js->tok = tok,
js->nogc = nogc;
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);
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");
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);
816 do_assign_op(
js, TOK_PLUS_ASSIGN, lhs, tov2(1));
821 do_assign_op(
js, TOK_MINUS_ASSIGN, lhs, tov2(1));
824 case TOK_NOT:
if (vtype(r) == T_BOOL)
return mkval(T_BOOL, !vdata(r));
break;
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);
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);
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;
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]) {
871 }
else if (in[n2 + 1] ==
'n') {
873 }
else if (in[n2 + 1] ==
't') {
875 }
else if (in[n2 + 1] ==
'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]));
882 return js_mkerr(
js,
"bad str literal");
886 out[n1++] = ((uint8_t *)
js->code)[
js->toff + n2];
889 return js_mkstr(
js, NULL, n1);
892 static jsval_t js_obj_literal(
struct js *
js) {
893 uint8_t exe = !(
js->flags & F_NOEXEC);
895 jsval_t obj = exe ? mkobj(
js, 0) : js_mkundef();
896 if (is_err(obj))
return obj;
898 while (next(
js) != TOK_RBRACE) {
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);
905 return js_mkerr(
js,
"parse error");
909 jsval_t val = js_expr(
js);
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;
917 if (next(
js) == TOK_RBRACE)
break;
920 EXPECT(TOK_RBRACE, );
924 static jsval_t js_func_literal(
struct js *
js) {
925 uint8_t flags =
js->flags;
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);
935 EXPECT(TOK_RPAREN,
js->flags = flags);
936 EXPECT(TOK_LBRACE,
js->flags = flags);
938 js->flags |= F_NOEXEC;
939 jsval_t res = js_block(
js,
false);
945 jsval_t str = js_mkstr(
js, &
js->code[pos],
js->pos - pos);
948 return mkval(T_FUNC, (
unsigned long) vdata(str));
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; \
956 jsval_t rhs = _f2(js); \
957 if (is_err(rhs)) return rhs; \
958 res = do_op(js, op, res, rhs); \
962 #define LTR_BINOP(_f, _cond) \
963 jsval_t res = _f(js); \
964 while (!is_err(res) && (_cond)) { \
965 uint8_t op = js->tok; \
967 jsval_t rhs = _f(js); \
968 if (is_err(rhs)) return rhs; \
969 res = do_op(js, op, res, rhs); \
973 static jsval_t js_literal(
struct js *
js) {
977 if (
js->maxcss > 0 &&
js->css >
js->maxcss)
return js_mkerr(
js,
"C stack");
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");
994 static jsval_t js_group(
struct js *
js) {
995 if (next(
js) == TOK_LPAREN) {
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");
1003 return js_literal(
js);
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));
1013 while (next(
js) == TOK_LPAREN || next(
js) == TOK_DOT) {
1014 if (
js->tok == TOK_DOT) {
1016 res = do_op(
js, TOK_DOT, res, js_group(
js));
1018 jsval_t params = js_call_params(
js);
1019 if (is_err(params))
return params;
1020 res = do_op(
js, TOK_CALL, res, params);
1026 static jsval_t js_postfix(
struct js *
js) {
1027 jsval_t res = js_call_dot(
js);
1028 if (is_err(res))
return res;
1030 if (
js->tok == TOK_POSTINC ||
js->tok == TOK_POSTDEC) {
1032 res = do_op(
js,
js->tok, res, 0);
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;
1044 return do_op(
js, t, js_mkundef(), js_unary(
js));
1046 return js_postfix(
js);
1050 static jsval_t js_mul_div_rem(
struct js *
js) {
1052 (next(
js) == TOK_MUL ||
js->tok == TOK_DIV ||
js->tok == TOK_REM));
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));
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));
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));
1069 static jsval_t js_equality(
struct js *
js) {
1070 LTR_BINOP(js_comparison, (next(
js) == TOK_EQ || next(
js) == TOK_NE));
1073 static jsval_t js_bitwise_and(
struct js *
js) {
1074 LTR_BINOP(js_equality, (next(
js) == TOK_AND));
1077 static jsval_t js_bitwise_xor(
struct js *
js) {
1078 LTR_BINOP(js_bitwise_and, (next(
js) == TOK_XOR));
1081 static jsval_t js_bitwise_or(
struct js *
js) {
1082 LTR_BINOP(js_bitwise_xor, (next(
js) == TOK_OR));
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) {
1091 res = resolveprop(
js, res);
1092 if (!js_truthy(
js, res))
js->flags |= F_NOEXEC;
1093 if (
js->flags & F_NOEXEC) {
1096 res = js_logical_and(
js);
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) {
1109 res = resolveprop(
js, res);
1110 if (js_truthy(
js, res))
js->flags |= F_NOEXEC;
1111 if (
js->flags & F_NOEXEC) {
1114 res = js_logical_or(
js);
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;
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);
1133 js->flags |= F_NOEXEC;
1135 EXPECT(TOK_COLON,
js->flags = flags);
1137 res = js_ternary(
js);
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));
1153 static jsval_t js_expr(
struct js *
js) {
1154 return js_assignment(
js);
1157 static jsval_t js_let(
struct js *
js) {
1158 uint8_t exe = !(
js->flags & F_NOEXEC);
1161 EXPECT(TOK_IDENTIFIER, );
1163 jsoff_t noff =
js->toff, nlen =
js->tlen;
1164 char *name = (
char *) &
js->code[noff];
1165 jsval_t v = js_mkundef();
1167 if (next(
js) == TOK_ASSIGN) {
1170 if (is_err(v))
return v;
1173 if (lkp(
js,
js->scope, name, nlen) > 0)
1174 return js_mkerr(
js,
"'%.*s' already declared", (
int) nlen, name);
1176 setprop(
js,
js->scope, js_mkstr(
js, name, nlen), resolveprop(
js, v));
1177 if (is_err(x))
return x;
1179 if (next(
js) == TOK_SEMICOLON || next(
js) == TOK_EOF)
break;
1180 EXPECT(TOK_COMMA, );
1182 return js_mkundef();
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));
1192 static jsval_t js_if(
struct js *
js) {
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);
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) {
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;
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");
1225 static inline bool is_err2(jsval_t *v, jsval_t *res) {
1226 bool r = is_err(*v);
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);
1236 if (!expect(
js, TOK_FOR, &res))
goto done;
1237 if (!expect(
js, TOK_LPAREN, &res))
goto done;
1239 if (next(
js) == TOK_SEMICOLON) {
1240 }
else if (next(
js) == TOK_LET) {
1242 if (is_err2(&v, &res))
goto done;
1245 if (is_err2(&v, &res))
goto done;
1247 if (!expect(
js, TOK_SEMICOLON, &res))
goto done;
1248 js->flags |= F_NOEXEC;
1250 if (next(
js) != TOK_SEMICOLON) {
1252 if (is_err2(&v, &res))
goto done;
1254 if (!expect(
js, TOK_SEMICOLON, &res))
goto done;
1256 if (next(
js) != TOK_RPAREN) {
1258 if (is_err2(&v, &res))
goto done;
1260 if (!expect(
js, TOK_RPAREN, &res))
goto done;
1262 v = js_block_or_stmt(
js);
1263 if (is_err2(&v, &res))
goto done;
1265 while (!(flags & F_NOEXEC)) {
1266 js->flags = flags,
js->pos = pos1,
js->consumed = 1;
1267 if (next(
js) != TOK_SEMICOLON) {
1268 v = resolveprop(
js, js_expr(
js));
1269 if (is_err2(&v, &res))
goto done;
1270 if (!js_truthy(
js, v))
break;
1272 js->pos = pos3,
js->consumed = 1,
js->flags |= F_LOOP;
1273 v = js_block_or_stmt(
js);
1274 if (is_err2(&v, &res))
goto done;
1275 if (
js->flags & F_BREAK)
break;
1276 js->flags = flags,
js->pos = pos2,
js->consumed = 1;
1277 if (next(
js) != TOK_RPAREN) {
1279 if (is_err2(&v, &res))
goto done;
1282 js->pos = pos4,
js->tok = TOK_SEMICOLON,
js->consumed = 0;
1284 if (exe) delscope(
js);
1290 static jsval_t js_var(
struct js *
js) {
1291 uint8_t exe = !(
js->flags & F_NOEXEC);
1294 EXPECT(TOK_IDENTIFIER, );
1296 jsoff_t noff =
js->toff, nlen =
js->tlen;
1297 char *name = (
char *) &
js->code[noff];
1298 jsval_t v = js_mkundef();
1300 if (next(
js) == TOK_ASSIGN) {
1303 if (is_err(v))
return v;
1308 if (lkp(
js,
js->scope, name, nlen) > 0) {
1309 return js_mkerr(
js,
"'%.*s' already declared", (
int) nlen, name);
1311 jsval_t x = setprop(
js,
js->scope, js_mkstr(
js, name, nlen), resolveprop(
js, v));
1316 if (next(
js) == TOK_SEMICOLON || next(
js) == TOK_EOF)
break;
1317 EXPECT(TOK_COMMA, );
1319 return js_mkundef();
1324 static jsval_t js_do_while(
struct js *
js) {
1325 uint8_t exe = !(
js->flags & F_NOEXEC);
1326 jsval_t res = js_mkundef();
1329 if (!expect(
js, TOK_DO, &res)) {
1334 res = js_block_or_stmt(
js);
1340 if (!expect(
js, TOK_WHILE, &res)) {
1343 if (!expect(
js, TOK_LPAREN, &res)) {
1351 res = js_block_or_stmt(
js);
1360 }
while (js_truthy(
js, res));
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;
1378 if (is_err2(&v, &res))
goto done;
1380 if (!expect(
js, TOK_RPAREN, &res))
goto done;
1382 v = js_block_or_stmt(
js);
1383 if (is_err2(&v, &res))
goto done;
1385 while (!(flags & F_NOEXEC)) {
1386 js->flags = flags,
js->pos = pos1,
js->consumed = 1;
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;
1397 if (exe) delscope(
js);
1399 js->tok = TOK_SEMICOLON;
1403 static jsval_t js_break(
struct js *
js) {
1404 if (
js->flags & F_NOEXEC) {
1406 if (!(
js->flags & F_LOOP))
return js_mkerr(
js,
"not in loop");
1407 js->flags |= F_BREAK | F_NOEXEC;
1410 return js_mkundef();
1413 static jsval_t js_continue(
struct js *
js) {
1414 if (
js->flags & F_NOEXEC) {
1416 if (!(
js->flags & F_LOOP))
return js_mkerr(
js,
"not in loop");
1417 js->flags |= F_NOEXEC;
1420 return js_mkundef();
1423 static jsval_t js_return(
struct js *
js) {
1424 uint8_t exe = !(
js->flags & F_NOEXEC);
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));
1431 js->flags |= F_RETURN;
1433 return resolveprop(
js, res);
1437 static jsval_t js_switch(
struct js *
js) {
1438 jsval_t res, expr, match;
1439 uint8_t exe = !(
js->flags & F_NOEXEC);
1442 expr = resolveprop(
js, js_expr(
js));
1445 while (next(
js) == TOK_CASE) {
1447 match = resolveprop(
js, js_expr(
js));
1448 if (js_cmp(
js, expr, match) == 0) {
1449 res = js_block(
js, exe);
1455 if (next(
js) == TOK_DEFAULT) {
1457 res = js_block(
js, exe);
1464 static jsval_t js_stmt(
struct js *
js) {
1467 if (
js->brk >
js->gct) js_gc(
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);
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;
1485 case TOK_RETURN: res = js_return(
js);
break;
1486 default: res = resolveprop(
js, js_expr(
js));
break;
1489 if (next(
js) != TOK_SEMICOLON && next(
js) != TOK_EOF && next(
js) != TOK_RBRACE){
1492 return js_mkerr(
js,
"; expected");
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;
1502 js = (
struct js *) buf;
1503 js->mem = (uint8_t *) (
js + 1);
1504 js->size = (jsoff_t) (len -
sizeof(*
js));
1505 js->scope = mkobj(
js, 0);
1506 js->size =
js->size / 8U * 8U;
1509 js->gct =
js->size / 2;
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) {
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) {
1529 int js_getbool(jsval_t value) {
return vdata(value) & 1 ? 1 : 0; }
1531 jsval_t js_glob(
struct js *
js) { (void)
js;
return mkval(T_OBJ, 0); }
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);
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];
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;
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;
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);
1565 bool js_chkargs(jsval_t *args,
int nargs,
const char *spec) {
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');
1572 if (spec[i] !=
'\0' || i != nargs) ok = 0;
1576 jsval_t js_eval(
struct js *
js,
const char *buf,
size_t len) {
1578 jsval_t res = js_mkundef();
1579 if (len == (
size_t) ~0U) len =
strlen(buf);
1583 js->clen = (jsoff_t) len;
1586 while (next(
js) != TOK_EOF && !is_err(res)) {
1592 void js_dump(
struct js *
js) {
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));
Основные определения ядра
size_t strlen(const char *str)
Возращает длину строки
int strcmp(const char *s1, const char *s2)
Сравнение строк
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
void * memcpy(void *restrict destination, const void *restrict source, size_t n)
Копирование непересекающихся массивов используя SSE.
int32_t memcmp(const char *s1, const char *s2, size_t n)
Сравнение массивов
void * memmove(void *dest, void *src, size_t count)
Копирование массивов (в том числе пересекающихся)