MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

object builtins

+494 -56
+12
include/ant.h
··· 13 13 JS_ERR, JS_PRIV, JS_PROMISE, JS_OBJ, JS_FUNC 14 14 }; 15 15 16 + typedef enum { 17 + JS_ERR_TYPE, 18 + JS_ERR_SYNTAX, 19 + JS_ERR_REFERENCE, 20 + JS_ERR_RANGE, 21 + JS_ERR_EVAL, 22 + JS_ERR_URI, 23 + JS_ERR_INTERNAL, 24 + JS_ERR_GENERIC 25 + } js_err_type_t; 26 + 16 27 struct js *js_create(void *buf, size_t len); 17 28 struct js *js_create_dynamic(size_t initial_size, size_t max_size); 18 29 ··· 50 61 void js_arr_push(struct js *, jsval_t arr, jsval_t val); 51 62 jsval_t js_mkstr(struct js *, const void *, size_t); 52 63 jsval_t js_mkerr(struct js *js, const char *fmt, ...); 64 + jsval_t js_mkerr_typed(struct js *js, js_err_type_t err_type, const char *fmt, ...); 53 65 jsval_t js_mkfun(jsval_t (*fn)(struct js *, jsval_t *, int)); 54 66 jsval_t js_call(struct js *js, jsval_t func, jsval_t *args, int nargs); 55 67
+1 -1
meson.build
··· 74 74 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 75 75 76 76 version_conf = configuration_data() 77 - version_conf.set('ANT_VERSION', '0.1.0.34') 77 + version_conf.set('ANT_VERSION', '0.1.0.35') 78 78 version_conf.set('ANT_GIT_HASH', git_hash) 79 79 version_conf.set('ANT_BUILD_DATE', build_date) 80 80
+481 -55
src/ant.c
··· 345 345 static jsoff_t align32(jsoff_t v) { return ((v + 3) >> 2) << 2; } 346 346 347 347 #define CHECKV(_v) do { if (is_err(_v)) { res = (_v); goto done; } } while (0) 348 - #define EXPECT(_tok, _e) do { if (next(js) != _tok) { _e; return js_mkerr(js, "parse error"); }; js->consumed = 1; } while (0) 348 + #define EXPECT(_tok, _e) do { if (next(js) != _tok) { _e; return js_mkerr_typed(js, JS_ERR_SYNTAX, "parse error"); }; js->consumed = 1; } while (0) 349 349 350 350 static bool streq(const char *buf, size_t len, const char *p, size_t n); 351 351 static size_t tostr(struct js *js, jsval_t value, char *buf, size_t len); ··· 1614 1614 js->errmsg[js->errmsg_size - 1] = '\0'; 1615 1615 } 1616 1616 1617 - jsval_t js_mkerr(struct js *js, const char *xx, ...) { 1617 + static const char *get_error_type_name(js_err_type_t err_type) { 1618 + switch (err_type) { 1619 + case JS_ERR_TYPE: return "TypeError"; 1620 + case JS_ERR_SYNTAX: return "SyntaxError"; 1621 + case JS_ERR_REFERENCE: return "ReferenceError"; 1622 + case JS_ERR_RANGE: return "RangeError"; 1623 + case JS_ERR_EVAL: return "EvalError"; 1624 + case JS_ERR_URI: return "URIError"; 1625 + case JS_ERR_INTERNAL: return "InternalError"; 1626 + case JS_ERR_GENERIC: return "Error"; 1627 + default: return "Error"; 1628 + } 1629 + } 1630 + 1631 + jsval_t js_mkerr_typed(struct js *js, js_err_type_t err_type, const char *xx, ...) { 1618 1632 va_list ap; 1619 1633 int line = 0, col = 0; 1620 1634 char error_line[256] = {0}; ··· 1643 1657 1644 1658 size_t remaining = js->errmsg_size - n; 1645 1659 if (remaining > 1) { 1646 - n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31mTypeError\x1b[0m: \x1b[1m%s\x1b[0m", error_msg); 1660 + const char *err_name = get_error_type_name(err_type); 1661 + n += (size_t) snprintf(js->errmsg + n, remaining, "\x1b[31m%s\x1b[0m: \x1b[1m%s\x1b[0m", err_name, error_msg); 1647 1662 } 1648 1663 1649 1664 format_error_stack(js, &n, line, col, true, error_line, error_col); 1650 1665 1651 1666 js->pos = js->clen, js->tok = TOK_EOF, js->consumed = 0; 1652 1667 return mkval(T_ERR, 0); 1668 + } 1669 + 1670 + jsval_t js_mkerr(struct js *js, const char *xx, ...) { 1671 + va_list ap; 1672 + char error_msg[256] = {0}; 1673 + 1674 + va_start(ap, xx); 1675 + vsnprintf(error_msg, sizeof(error_msg), xx, ap); 1676 + va_end(ap); 1677 + 1678 + return js_mkerr_typed(js, JS_ERR_TYPE, "%s", error_msg); 1653 1679 } 1654 1680 1655 1681 static jsval_t js_throw(struct js *js, jsval_t value) { ··· 3112 3138 t == TOK_TRY || t == TOK_CLASS || t == TOK_ASYNC); 3113 3139 bool asi_ok = js->had_newline || js->tok == TOK_SEMICOLON || js->tok == TOK_RBRACE || js->tok == TOK_EOF; 3114 3140 if (!is_err(res) && !is_block_tok && !asi_ok) { 3115 - res = js_mkerr(js, "; expected"); 3141 + res = js_mkerr_typed(js, JS_ERR_SYNTAX, "; expected"); 3116 3142 break; 3117 3143 } 3118 3144 if (js->flags & (F_RETURN | F_THROW)) break; ··· 3334 3360 } 3335 3361 3336 3362 if (js->flags & F_STRICT) { 3337 - return js_mkerr(js, "ReferenceError: '%.*s' is not defined", (int) len, buf); 3363 + return js_mkerr_typed(js, JS_ERR_REFERENCE, "'%.*s' is not defined", (int) len, buf); 3338 3364 } 3339 3365 3340 3366 jsval_t global_scope = js->scope; ··· 3548 3574 const char *ptr = (char *) &js->code[coderefoff(r)]; 3549 3575 size_t plen = codereflen(r); 3550 3576 3551 - if (vtype(r) != T_CODEREF) return js_mkerr(js, "ident expected"); 3577 + if (vtype(r) != T_CODEREF) return js_mkerr_typed(js, JS_ERR_SYNTAX, "ident expected"); 3552 3578 uint8_t t = vtype(l); 3553 3579 3554 3580 if (t == T_STR && streq(ptr, plen, "length", 6)) { ··· 4527 4553 if (brace_count > 0) n++; 4528 4554 } 4529 4555 4530 - if (brace_count != 0) return js_mkerr(js, "unclosed ${"); 4556 + if (brace_count != 0) return js_mkerr_typed(js, JS_ERR_SYNTAX, "unclosed ${"); 4531 4557 4532 4558 const char *saved_code = js->code; 4533 4559 jsoff_t saved_clen = js->clen; ··· 4638 4664 else if (in[n] == '}') brace_count--; 4639 4665 if (brace_count > 0) n++; 4640 4666 } 4641 - if (brace_count != 0) return js_mkerr(js, "unclosed ${"); 4667 + if (brace_count != 0) return js_mkerr_typed(js, JS_ERR_SYNTAX, "unclosed ${"); 4642 4668 4643 4669 const char *saved_code = js->code; 4644 4670 jsoff_t saved_clen = js->clen, saved_pos = js->pos; ··· 4783 4809 } 4784 4810 4785 4811 if (js->pos >= js->clen || js->code[js->pos] != '/') { 4786 - return js_mkerr(js, "unterminated regex"); 4812 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "unterminated regex"); 4787 4813 } 4788 4814 4789 4815 jsoff_t pattern_end = js->pos; ··· 4838 4864 while (next(js) != TOK_RBRACE) { 4839 4865 jsval_t key = 0; 4840 4866 jsoff_t id_off = 0, id_len = 0; 4867 + bool is_computed = false; 4868 + 4869 + if (js->tok == TOK_REST) { 4870 + js->consumed = 1; 4871 + jsval_t spread_expr = js_expr(js); 4872 + if (is_err(spread_expr)) return spread_expr; 4873 + if (!exe) goto spread_next; 4874 + 4875 + jsval_t spread_obj = resolveprop(js, spread_expr); 4876 + uint8_t st = vtype(spread_obj); 4877 + if (st != T_OBJ && st != T_ARR && st != T_FUNC) goto spread_next; 4878 + 4879 + jsval_t src_obj = (st == T_OBJ) ? spread_obj : mkval(T_OBJ, vdata(spread_obj)); 4880 + jsoff_t next_prop = loadoff(js, (jsoff_t) vdata(src_obj)) & ~(3U | CONSTMASK); 4881 + 4882 + while (next_prop < js->brk && next_prop != 0) { 4883 + jsoff_t koff = loadoff(js, next_prop + (jsoff_t) sizeof(next_prop)); 4884 + jsoff_t klen = offtolen(loadoff(js, koff)); 4885 + const char *prop_key = (char *) &js->mem[koff + sizeof(koff)]; 4886 + jsval_t prop_val = loadval(js, next_prop + (jsoff_t) (sizeof(next_prop) + sizeof(koff))); 4887 + 4888 + next_prop = loadoff(js, next_prop) & ~(3U | CONSTMASK); 4889 + 4890 + if (streq(prop_key, klen, "__proto__", 9)) continue; 4891 + if (klen > 7 && memcmp(prop_key, "__desc_", 7) == 0) continue; 4892 + 4893 + jsval_t key_str = js_mkstr(js, prop_key, klen); 4894 + setprop(js, obj, key_str, prop_val); 4895 + } 4896 + 4897 + spread_next: 4898 + if (next(js) == TOK_RBRACE) break; 4899 + EXPECT(TOK_COMMA, ); 4900 + continue; 4901 + } 4841 4902 4842 4903 if (js->tok == TOK_IDENTIFIER) { 4843 4904 id_off = js->toff; ··· 4847 4908 if (exe) key = js_str_literal(js); 4848 4909 } else if (js->tok == TOK_NUMBER) { 4849 4910 if (exe) key = js_mkstr(js, js->code + js->toff, js->tlen); 4911 + } else if (js->tok == TOK_LBRACKET) { 4912 + is_computed = true; 4913 + js->consumed = 1; 4914 + jsval_t key_expr = js_expr(js); 4915 + if (is_err(key_expr)) return key_expr; 4916 + 4917 + if (exe) { 4918 + jsval_t resolved_key = resolveprop(js, key_expr); 4919 + if (vtype(resolved_key) == T_STR) { 4920 + key = resolved_key; 4921 + } else { 4922 + char buf[64]; 4923 + size_t n = tostr(js, resolved_key, buf, sizeof(buf)); 4924 + key = js_mkstr(js, buf, n); 4925 + } 4926 + if (is_err(key)) return key; 4927 + } 4928 + 4929 + if (next(js) != TOK_RBRACKET) { 4930 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "] expected after computed property name"); 4931 + } 4850 4932 } else { 4851 - return js_mkerr(js, "parse error"); 4933 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "parse error"); 4852 4934 } 4853 4935 js->consumed = 1; 4854 4936 4855 - if (id_len > 0 && (next(js) == TOK_COMMA || next(js) == TOK_RBRACE)) { 4937 + if (!is_computed && id_len > 0 && (next(js) == TOK_COMMA || next(js) == TOK_RBRACE)) { 4856 4938 jsval_t val = lookup(js, js->code + id_off, id_len); 4857 4939 if (exe) { 4858 4940 if (is_err(val)) return val; ··· 4860 4942 jsval_t res = setprop(js, obj, key, resolveprop(js, val)); 4861 4943 if (is_err(res)) return res; 4862 4944 } 4863 - } else if (id_len > 0 && next(js) == TOK_LPAREN) { 4945 + } else if (!is_computed && id_len > 0 && next(js) == TOK_LPAREN) { 4864 4946 uint8_t flags = js->flags; 4865 4947 jsoff_t pos = js->pos - 1; 4866 4948 js->consumed = 1; 4867 4949 if (!parse_func_params(js, &flags, NULL)) { 4868 4950 js->flags = flags; 4869 - return js_mkerr(js, "invalid parameters"); 4951 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "invalid parameters"); 4870 4952 } 4871 4953 EXPECT(TOK_RPAREN, js->flags = flags); 4872 4954 EXPECT(TOK_LBRACE, js->flags = flags); ··· 4941 5023 4942 5024 if (next(js) != TOK_IDENTIFIER) { 4943 5025 if (flags) js->flags = *flags; 4944 - js_mkerr(js, "identifier expected"); 5026 + js_mkerr_typed(js, JS_ERR_SYNTAX, "identifier expected"); 4945 5027 return false; 4946 5028 } 4947 5029 ··· 4950 5032 4951 5033 if ((js->flags & F_STRICT) && is_strict_restricted(param_name, param_len)) { 4952 5034 if (flags) js->flags = *flags; 4953 - js_mkerr(js, "cannot use '%.*s' as parameter name in strict mode", (int) param_len, param_name); 5035 + js_mkerr_typed(js, JS_ERR_SYNTAX, "cannot use '%.*s' as parameter name in strict mode", (int) param_len, param_name); 4954 5036 return false; 4955 5037 } 4956 5038 ··· 4958 5040 for (int i = 0; i < param_count; i++) { 4959 5041 if (param_lens[i] == param_len && memcmp(param_names[i], param_name, param_len) == 0) { 4960 5042 if (flags) js->flags = *flags; 4961 - js_mkerr(js, "duplicate parameter name '%.*s' in strict mode", (int) param_len, param_name); 5043 + js_mkerr_typed(js, JS_ERR_SYNTAX, "duplicate parameter name '%.*s' in strict mode", (int) param_len, param_name); 4962 5044 return false; 4963 5045 } 4964 5046 } ··· 4994 5076 4995 5077 if (is_rest && next(js) != TOK_RPAREN) { 4996 5078 if (flags) js->flags = *flags; 4997 - js_mkerr(js, "rest parameter must be last"); 5079 + js_mkerr_typed(js, JS_ERR_SYNTAX, "rest parameter must be last"); 4998 5080 return false; 4999 5081 } 5000 5082 if (next(js) == TOK_RPAREN) break; 5001 5083 5002 5084 if (next(js) != TOK_COMMA) { 5003 5085 if (flags) js->flags = *flags; 5004 - js_mkerr(js, "parse error"); 5086 + js_mkerr_typed(js, JS_ERR_SYNTAX, "parse error"); 5005 5087 return false; 5006 5088 } 5007 5089 js->consumed = 1; ··· 5025 5107 int param_count = 0; 5026 5108 if (!parse_func_params(js, &flags, &param_count)) { 5027 5109 js->flags = flags; 5028 - return js_mkerr(js, "invalid parameters"); 5110 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "invalid parameters"); 5029 5111 } 5030 5112 5031 5113 EXPECT(TOK_RPAREN, js->flags = flags); ··· 5116 5198 case TOK_ERR: 5117 5199 if ((js->flags & F_STRICT) && js->toff < js->clen && js->code[js->toff] == '0' && 5118 5200 js->toff + 1 < js->clen && is_digit(js->code[js->toff + 1])) { 5119 - return js_mkerr(js, "octal literals are not allowed in strict mode"); 5201 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "octal literals are not allowed in strict mode"); 5120 5202 } 5121 - return js_mkerr(js, "parse error"); 5203 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "parse error"); 5122 5204 case TOK_NUMBER: return js->tval; 5123 5205 case TOK_BIGINT: return js_bigint_literal(js); 5124 5206 case TOK_STRING: return js_str_literal(js); ··· 5157 5239 while (next(js) != TOK_RPAREN && next(js) != TOK_EOF) { 5158 5240 js->consumed = 1; 5159 5241 } 5160 - if (next(js) != TOK_RPAREN) return js_mkerr(js, ") expected"); 5242 + if (next(js) != TOK_RPAREN) return js_mkerr_typed(js, JS_ERR_SYNTAX, ") expected"); 5161 5243 js->consumed = 1; 5162 5244 js->flags = saved_flags; 5163 - if (next(js) != TOK_ARROW) return js_mkerr(js, "=> expected"); 5245 + if (next(js) != TOK_ARROW) return js_mkerr_typed(js, JS_ERR_SYNTAX, "=> expected"); 5164 5246 js->consumed = 1; 5165 5247 return js_arrow_func(js, paren_start, paren_end, true); 5166 5248 } 5167 - return js_mkerr(js, "async ( must be arrow function"); 5249 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "async ( must be arrow function"); 5168 5250 } else if (next_tok == TOK_IDENTIFIER) { 5169 5251 jsoff_t id_start = js->toff; 5170 5252 jsoff_t id_len = js->tlen; ··· 5238 5320 } 5239 5321 return mkcoderef((jsoff_t) id_start, (jsoff_t) id_len); 5240 5322 } 5241 - return js_mkerr(js, "unexpected token after async"); 5323 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "unexpected token after async"); 5242 5324 } 5243 5325 5244 5326 case TOK_NULL: return js_mknull(); ··· 5262 5344 case TOK_TRY: 5263 5345 case TOK_FINALLY: return mkcoderef((jsoff_t) js->toff, (jsoff_t) js->tlen); 5264 5346 5265 - default: return js_mkerr(js, "bad expr"); 5347 + default: return js_mkerr_typed(js, JS_ERR_SYNTAX, "bad expr"); 5266 5348 } 5267 5349 } 5268 5350 ··· 5411 5493 while (next(js) != TOK_RPAREN && next(js) != TOK_EOF) { 5412 5494 js->consumed = 1; 5413 5495 } 5414 - if (next(js) != TOK_RPAREN) return js_mkerr(js, ") expected"); 5496 + if (next(js) != TOK_RPAREN) return js_mkerr_typed(js, JS_ERR_SYNTAX, ") expected"); 5415 5497 js->consumed = 1; 5416 5498 js->flags = saved_flags; 5417 5499 5418 - if (next(js) != TOK_ARROW) return js_mkerr(js, "=> expected"); 5500 + if (next(js) != TOK_ARROW) return js_mkerr_typed(js, JS_ERR_SYNTAX, "=> expected"); 5419 5501 js->consumed = 1; 5420 5502 5421 5503 return js_arrow_func(js, paren_start, paren_end, false); ··· 5427 5509 v = js_expr(js); 5428 5510 if (is_err(v)) return v; 5429 5511 } 5430 - if (next(js) != TOK_RPAREN) return js_mkerr(js, ") expected"); 5512 + if (next(js) != TOK_RPAREN) return js_mkerr_typed(js, JS_ERR_SYNTAX, ") expected"); 5431 5513 js->consumed = 1; 5432 5514 return v; 5433 5515 } ··· 5483 5565 } 5484 5566 jsval_t idx = js_expr(js); 5485 5567 if (is_err(idx)) return idx; 5486 - if (next(js) != TOK_RBRACKET) return js_mkerr(js, "] expected"); 5568 + if (next(js) != TOK_RBRACKET) return js_mkerr_typed(js, JS_ERR_SYNTAX, "] expected"); 5487 5569 js->consumed = 1; 5488 5570 res = do_op(js, TOK_BRACKET, res, idx); 5489 5571 } else { ··· 5724 5806 if (vtype(operand) == T_PROP || vtype(operand) == T_PROPREF) { 5725 5807 do_assign_op(js, op == TOK_POSTINC ? TOK_PLUS_ASSIGN : TOK_MINUS_ASSIGN, operand, tov(1)); 5726 5808 } else { 5727 - return js_mkerr(js, "bad expr"); 5809 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "bad expr"); 5728 5810 } 5729 5811 return do_op(js, op == TOK_POSTINC ? TOK_PLUS : TOK_MINUS, resolved, tov(1)); 5730 5812 } else if (next(js) == TOK_NOT || js->tok == TOK_TILDA || js->tok == TOK_TYPEOF || ··· 5983 6065 if (next(js) == TOK_LBRACE) { 5984 6066 js->consumed = 1; 5985 6067 5986 - typedef struct { jsoff_t name_off; jsoff_t name_len; } PropName; 5987 - PropName props[32]; 6068 + typedef struct { 6069 + jsoff_t src_off; 6070 + jsoff_t src_len; 6071 + jsoff_t var_off; 6072 + jsoff_t var_len; 6073 + jsoff_t default_off; 6074 + jsoff_t default_len; 6075 + } DestructProp; 6076 + DestructProp props[32]; 5988 6077 int prop_count = 0; 5989 6078 5990 6079 while (next(js) != TOK_RBRACE && next(js) != TOK_EOF && prop_count < 32) { 5991 6080 EXPECT(TOK_IDENTIFIER, ); 5992 - props[prop_count].name_off = js->toff; 5993 - props[prop_count].name_len = js->tlen; 5994 - prop_count++; 6081 + props[prop_count].src_off = js->toff; 6082 + props[prop_count].src_len = js->tlen; 6083 + props[prop_count].var_off = js->toff; 6084 + props[prop_count].var_len = js->tlen; 6085 + props[prop_count].default_off = 0; 6086 + props[prop_count].default_len = 0; 5995 6087 js->consumed = 1; 5996 6088 5997 6089 if (next(js) == TOK_COLON) { 5998 6090 js->consumed = 1; 5999 6091 EXPECT(TOK_IDENTIFIER, ); 6092 + props[prop_count].var_off = js->toff; 6093 + props[prop_count].var_len = js->tlen; 6000 6094 js->consumed = 1; 6001 6095 } 6002 6096 6097 + if (next(js) == TOK_ASSIGN) { 6098 + js->consumed = 1; 6099 + props[prop_count].default_off = js->pos; 6100 + uint8_t saved_flags = js->flags; 6101 + js->flags |= F_NOEXEC; 6102 + jsval_t default_expr = js_expr(js); 6103 + js->flags = saved_flags; 6104 + if (is_err(default_expr)) return default_expr; 6105 + props[prop_count].default_len = js->pos - props[prop_count].default_off; 6106 + } 6107 + 6108 + prop_count++; 6109 + 6003 6110 if (next(js) == TOK_RBRACE) break; 6004 6111 EXPECT(TOK_COMMA, ); 6005 6112 } ··· 6012 6119 v = js_expr(js); 6013 6120 if (is_err(v)) return v; 6014 6121 } else { 6015 - return js_mkerr(js, "destructuring requires assignment"); 6122 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "destructuring requires assignment"); 6016 6123 } 6017 6124 6018 6125 if (exe) { ··· 6022 6129 } 6023 6130 6024 6131 for (int i = 0; i < prop_count; i++) { 6025 - const char *prop_name = &js->code[props[i].name_off]; 6026 - jsoff_t prop_len = props[i].name_len; 6132 + const char *var_name = &js->code[props[i].var_off]; 6133 + jsoff_t var_len = props[i].var_len; 6134 + 6135 + const char *src_name = &js->code[props[i].src_off]; 6136 + jsoff_t src_len = props[i].src_len; 6027 6137 6028 - if (lkp(js, js->scope, prop_name, prop_len) > 0) { 6029 - return js_mkerr(js, "'%.*s' already declared", (int) prop_len, prop_name); 6138 + if (lkp(js, js->scope, var_name, var_len) > 0) { 6139 + return js_mkerr(js, "'%.*s' already declared", (int) var_len, var_name); 6030 6140 } 6031 6141 6032 - jsoff_t prop_off = lkp(js, obj, prop_name, prop_len); 6142 + jsoff_t prop_off = lkp(js, obj, src_name, src_len); 6033 6143 jsval_t prop_val = js_mkundef(); 6034 6144 6035 6145 if (prop_off > 0) { 6036 6146 prop_val = resolveprop(js, mkval(T_PROP, prop_off)); 6037 6147 } 6038 6148 6039 - jsval_t x = mkprop(js, js->scope, js_mkstr(js, prop_name, prop_len), prop_val, is_const); 6149 + if (vtype(prop_val) == T_UNDEF && props[i].default_len > 0) { 6150 + const char *saved_code = js->code; 6151 + jsoff_t saved_clen = js->clen, saved_pos = js->pos; 6152 + uint8_t saved_tok = js->tok, saved_consumed = js->consumed; 6153 + 6154 + js->code = &js->code[props[i].default_off - (js->code - saved_code)]; 6155 + js->clen = props[i].default_len; 6156 + js->pos = 0; 6157 + js->consumed = 1; 6158 + 6159 + prop_val = js_expr(js); 6160 + 6161 + js->code = saved_code; 6162 + js->clen = saved_clen; 6163 + js->pos = saved_pos; 6164 + js->tok = saved_tok; 6165 + js->consumed = saved_consumed; 6166 + 6167 + if (is_err(prop_val)) return prop_val; 6168 + prop_val = resolveprop(js, prop_val); 6169 + } 6170 + 6171 + jsval_t x = mkprop(js, js->scope, js_mkstr(js, var_name, var_len), prop_val, is_const); 6040 6172 if (is_err(x)) return x; 6041 6173 } 6042 6174 } ··· 6047 6179 char *name = (char *) &js->code[noff]; 6048 6180 6049 6181 if (exe && (js->flags & F_STRICT) && is_strict_restricted(name, nlen)) { 6050 - return js_mkerr(js, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name); 6182 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name); 6051 6183 } 6052 6184 6053 6185 if (exe && (js->flags & F_STRICT) && is_strict_reserved(name, nlen)) { 6054 - return js_mkerr(js, "'%.*s' is reserved in strict mode", (int) nlen, name); 6186 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "'%.*s' is reserved in strict mode", (int) nlen, name); 6055 6187 } 6056 6188 6057 6189 jsval_t v = js_mkundef(); ··· 6109 6241 next(js); 6110 6242 } 6111 6243 6112 - if (next(js) != TOK_IDENTIFIER) return js_mkerr(js, "identifier expected"); 6244 + if (next(js) != TOK_IDENTIFIER) return js_mkerr_typed(js, JS_ERR_SYNTAX, "identifier expected"); 6113 6245 param_count++; 6114 6246 js->consumed = 1; 6115 6247 ··· 6134 6266 } 6135 6267 6136 6268 if (is_rest && next(js) != TOK_RPAREN) { 6137 - return js_mkerr(js, "rest parameter must be last"); 6269 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "rest parameter must be last"); 6138 6270 } 6139 6271 if (next(js) == TOK_RPAREN) break; 6140 6272 EXPECT(TOK_COMMA, ); ··· 6199 6331 EXPECT(TOK_LPAREN, ); 6200 6332 jsoff_t pos = js->pos - 1; 6201 6333 if (!parse_func_params(js, NULL, NULL)) { 6202 - return js_mkerr(js, "invalid parameters"); 6334 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "invalid parameters"); 6203 6335 } 6204 6336 EXPECT(TOK_RPAREN, ); 6205 6337 EXPECT(TOK_LBRACE, ); ··· 6292 6424 6293 6425 static inline bool expect(struct js *js, uint8_t tok, jsval_t *res) { 6294 6426 if (next(js) != tok) { 6295 - *res = js_mkerr(js, "parse error"); 6427 + *res = js_mkerr_typed(js, JS_ERR_SYNTAX, "parse error"); 6296 6428 return false; 6297 6429 } else { 6298 6430 js->consumed = 1; ··· 7296 7428 jsval_t res = js_mkundef(); 7297 7429 7298 7430 if (flags & F_STRICT) { 7299 - return js_mkerr(js, "with statement not allowed in strict mode"); 7431 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "with statement not allowed in strict mode"); 7300 7432 } 7301 7433 7302 7434 js->consumed = 1; ··· 7394 7526 } 7395 7527 if (next(js) != TOK_IDENTIFIER && (next(js) < TOK_ASYNC || next(js) > TOK_STATIC)) { 7396 7528 js->flags = save_flags; 7397 - return js_mkerr(js, "method name expected"); 7529 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "method name expected"); 7398 7530 } 7399 7531 jsoff_t method_name_off = js->toff, method_name_len = js->tlen; 7400 7532 js->consumed = 1; ··· 7799 7931 char *name = (char *) &js->code[noff]; 7800 7932 7801 7933 if (exe && (js->flags & F_STRICT) && is_strict_restricted(name, nlen)) { 7802 - return js_mkerr(js, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name); 7934 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "cannot use '%.*s' as variable name in strict mode", (int) nlen, name); 7803 7935 } 7804 7936 7805 7937 if (exe && (js->flags & F_STRICT) && is_strict_reserved(name, nlen)) { 7806 - return js_mkerr(js, "'%.*s' is reserved in strict mode", (int) nlen, name); 7938 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "'%.*s' is reserved in strict mode", (int) nlen, name); 7807 7939 } 7808 7940 7809 7941 jsval_t v = js_mkundef(); ··· 7849 7981 *res = js_func_decl_async(js); 7850 7982 return; 7851 7983 } 7852 - *res = js_mkerr(js, "async must be followed by function"); 7984 + *res = js_mkerr_typed(js, JS_ERR_SYNTAX, "async must be followed by function"); 7853 7985 } 7854 7986 7855 7987 static jsval_t js_stmt(struct js *js) { ··· 7910 8042 int next_tok = next(js); 7911 8043 bool asi_applies = js->had_newline || next_tok == TOK_EOF || next_tok == TOK_RBRACE; 7912 8044 bool missing_semicolon = next_tok != TOK_SEMICOLON && !asi_applies; 7913 - if (missing_semicolon) return js_mkerr(js, "; expected"); 8045 + if (missing_semicolon) return js_mkerr_typed(js, JS_ERR_SYNTAX, "; expected"); 7914 8046 if (next_tok == TOK_SEMICOLON) js->consumed = 1; 7915 8047 } 7916 8048 ··· 9293 9425 return obj; 9294 9426 } 9295 9427 9428 + static jsval_t builtin_object_assign(struct js *js, jsval_t *args, int nargs) { 9429 + if (nargs == 0) return js_mkerr(js, "Object.assign requires at least 1 argument"); 9430 + 9431 + jsval_t target = args[0]; 9432 + uint8_t t = vtype(target); 9433 + 9434 + if (t == T_NULL || t == T_UNDEF) { 9435 + return js_mkerr(js, "Cannot convert undefined or null to object"); 9436 + } 9437 + 9438 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) { 9439 + target = js_mkobj(js); 9440 + } 9441 + 9442 + jsval_t as_obj = (vtype(target) == T_OBJ) ? target : mkval(T_OBJ, vdata(target)); 9443 + 9444 + for (int i = 1; i < nargs; i++) { 9445 + jsval_t source = args[i]; 9446 + uint8_t st = vtype(source); 9447 + 9448 + if (st == T_NULL || st == T_UNDEF) continue; 9449 + if (st != T_OBJ && st != T_ARR && st != T_FUNC) continue; 9450 + 9451 + jsval_t src_obj = (st == T_OBJ) ? source : mkval(T_OBJ, vdata(source)); 9452 + jsoff_t next = loadoff(js, (jsoff_t) vdata(src_obj)) & ~(3U | CONSTMASK); 9453 + 9454 + while (next < js->brk && next != 0) { 9455 + jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 9456 + jsoff_t klen = offtolen(loadoff(js, koff)); 9457 + const char *key = (char *) &js->mem[koff + sizeof(koff)]; 9458 + jsval_t val = loadval(js, next + (jsoff_t) (sizeof(next) + sizeof(koff))); 9459 + 9460 + next = loadoff(js, next) & ~(3U | CONSTMASK); 9461 + 9462 + if (streq(key, klen, "__proto__", 9)) continue; 9463 + if (klen > 7 && memcmp(key, "__desc_", 7) == 0) continue; 9464 + 9465 + bool should_copy = true; 9466 + char desc_key[128]; 9467 + snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 9468 + jsoff_t desc_off = lkp(js, src_obj, desc_key, strlen(desc_key)); 9469 + 9470 + if (desc_off != 0) { 9471 + jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 9472 + if (vtype(desc_obj) == T_OBJ) { 9473 + jsoff_t enum_off = lkp(js, desc_obj, "enumerable", 10); 9474 + if (enum_off != 0) { 9475 + jsval_t enum_val = resolveprop(js, mkval(T_PROP, enum_off)); 9476 + should_copy = js_truthy(js, enum_val); 9477 + } 9478 + } 9479 + } 9480 + 9481 + if (should_copy) { 9482 + jsval_t key_str = js_mkstr(js, key, klen); 9483 + setprop(js, as_obj, key_str, val); 9484 + } 9485 + } 9486 + } 9487 + 9488 + return target; 9489 + } 9490 + 9491 + static jsval_t builtin_object_freeze(struct js *js, jsval_t *args, int nargs) { 9492 + if (nargs == 0) return js_mkundef(); 9493 + 9494 + jsval_t obj = args[0]; 9495 + uint8_t t = vtype(obj); 9496 + 9497 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj; 9498 + jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 9499 + 9500 + setprop(js, as_obj, js_mkstr(js, "__frozen__", 10), js_mktrue()); 9501 + jsoff_t next = loadoff(js, (jsoff_t) vdata(as_obj)) & ~(3U | CONSTMASK); 9502 + 9503 + while (next < js->brk && next != 0) { 9504 + jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 9505 + jsoff_t klen = offtolen(loadoff(js, koff)); 9506 + const char *key = (char *) &js->mem[koff + sizeof(koff)]; 9507 + 9508 + jsoff_t cur_prop = next; 9509 + next = loadoff(js, next) & ~(3U | CONSTMASK); 9510 + 9511 + if (streq(key, klen, "__proto__", 9)) continue; 9512 + if (streq(key, klen, "__frozen__", 10)) continue; 9513 + if (streq(key, klen, "__sealed__", 10)) continue; 9514 + if (klen > 7 && memcmp(key, "__desc_", 7) == 0) continue; 9515 + 9516 + jsoff_t head = (jsoff_t) vdata(as_obj); 9517 + jsoff_t firstprop = loadoff(js, head); 9518 + if ((firstprop & ~(3U | CONSTMASK)) == cur_prop) { 9519 + saveoff(js, head, firstprop | CONSTMASK); 9520 + } else { 9521 + jsoff_t prev = head; 9522 + jsoff_t scan = firstprop & ~(3U | CONSTMASK); 9523 + while (scan < js->brk && scan != 0) { 9524 + if (scan == cur_prop) { 9525 + jsoff_t prev_val = loadoff(js, prev); 9526 + saveoff(js, prev, prev_val | CONSTMASK); 9527 + break; 9528 + } 9529 + prev = scan; 9530 + scan = loadoff(js, scan) & ~(3U | CONSTMASK); 9531 + } 9532 + } 9533 + 9534 + char desc_key[128]; 9535 + snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 9536 + jsoff_t desc_off = lkp(js, as_obj, desc_key, strlen(desc_key)); 9537 + if (desc_off != 0) { 9538 + jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 9539 + if (vtype(desc_obj) == T_OBJ) { 9540 + setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mkfalse()); 9541 + setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 9542 + } 9543 + } else { 9544 + jsval_t desc_key_str = js_mkstr(js, desc_key, strlen(desc_key)); 9545 + jsval_t desc_obj = js_mkobj(js); 9546 + setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mkfalse()); 9547 + setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mktrue()); 9548 + setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 9549 + setprop(js, as_obj, desc_key_str, desc_obj); 9550 + } 9551 + } 9552 + 9553 + return obj; 9554 + } 9555 + 9556 + static jsval_t builtin_object_isFrozen(struct js *js, jsval_t *args, int nargs) { 9557 + if (nargs == 0) return js_mktrue(); 9558 + 9559 + jsval_t obj = args[0]; 9560 + uint8_t t = vtype(obj); 9561 + 9562 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_mktrue(); 9563 + jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 9564 + 9565 + jsoff_t frozen_off = lkp(js, as_obj, "__frozen__", 10); 9566 + if (frozen_off != 0) { 9567 + jsval_t frozen_val = resolveprop(js, mkval(T_PROP, frozen_off)); 9568 + if (js_truthy(js, frozen_val)) return js_mktrue(); 9569 + } 9570 + 9571 + return js_mkfalse(); 9572 + } 9573 + 9574 + static jsval_t builtin_object_seal(struct js *js, jsval_t *args, int nargs) { 9575 + if (nargs == 0) return js_mkundef(); 9576 + 9577 + jsval_t obj = args[0]; 9578 + uint8_t t = vtype(obj); 9579 + 9580 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj; 9581 + jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 9582 + 9583 + setprop(js, as_obj, js_mkstr(js, "__sealed__", 10), js_mktrue()); 9584 + jsoff_t next = loadoff(js, (jsoff_t) vdata(as_obj)) & ~(3U | CONSTMASK); 9585 + 9586 + while (next < js->brk && next != 0) { 9587 + jsoff_t koff = loadoff(js, next + (jsoff_t) sizeof(next)); 9588 + jsoff_t klen = offtolen(loadoff(js, koff)); 9589 + const char *key = (char *) &js->mem[koff + sizeof(koff)]; 9590 + 9591 + next = loadoff(js, next) & ~(3U | CONSTMASK); 9592 + 9593 + if (streq(key, klen, "__proto__", 9)) continue; 9594 + if (streq(key, klen, "__frozen__", 10)) continue; 9595 + if (streq(key, klen, "__sealed__", 10)) continue; 9596 + if (klen > 7 && memcmp(key, "__desc_", 7) == 0) continue; 9597 + 9598 + char desc_key[128]; 9599 + snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 9600 + jsoff_t desc_off = lkp(js, as_obj, desc_key, strlen(desc_key)); 9601 + if (desc_off != 0) { 9602 + jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 9603 + if (vtype(desc_obj) == T_OBJ) { 9604 + setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 9605 + } 9606 + } else { 9607 + jsval_t desc_key_str = js_mkstr(js, desc_key, strlen(desc_key)); 9608 + jsval_t desc_obj = js_mkobj(js); 9609 + setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mktrue()); 9610 + setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mktrue()); 9611 + setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 9612 + setprop(js, as_obj, desc_key_str, desc_obj); 9613 + } 9614 + } 9615 + 9616 + return obj; 9617 + } 9618 + 9619 + static jsval_t builtin_object_isSealed(struct js *js, jsval_t *args, int nargs) { 9620 + if (nargs == 0) return js_mktrue(); 9621 + 9622 + jsval_t obj = args[0]; 9623 + uint8_t t = vtype(obj); 9624 + 9625 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_mktrue(); 9626 + jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 9627 + 9628 + jsoff_t sealed_off = lkp(js, as_obj, "__sealed__", 10); 9629 + if (sealed_off != 0) { 9630 + jsval_t sealed_val = resolveprop(js, mkval(T_PROP, sealed_off)); 9631 + if (js_truthy(js, sealed_val)) return js_mktrue(); 9632 + } 9633 + 9634 + jsoff_t frozen_off = lkp(js, as_obj, "__frozen__", 10); 9635 + if (frozen_off != 0) { 9636 + jsval_t frozen_val = resolveprop(js, mkval(T_PROP, frozen_off)); 9637 + if (js_truthy(js, frozen_val)) return js_mktrue(); 9638 + } 9639 + 9640 + return js_mkfalse(); 9641 + } 9642 + 9643 + static jsval_t builtin_object_fromEntries(struct js *js, jsval_t *args, int nargs) { 9644 + if (nargs == 0) return js_mkerr(js, "Object.fromEntries requires an iterable argument"); 9645 + 9646 + jsval_t iterable = args[0]; 9647 + uint8_t t = vtype(iterable); 9648 + 9649 + if (t != T_ARR && t != T_OBJ) { 9650 + return js_mkerr(js, "Object.fromEntries requires an iterable"); 9651 + } 9652 + 9653 + jsval_t result = js_mkobj(js); 9654 + jsoff_t len_off = lkp(js, iterable, "length", 6); 9655 + if (len_off == 0) return result; 9656 + 9657 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 9658 + if (vtype(len_val) != T_NUM) return result; 9659 + 9660 + jsoff_t len = (jsoff_t) tod(len_val); 9661 + 9662 + for (jsoff_t i = 0; i < len; i++) { 9663 + char idx_str[16]; 9664 + snprintf(idx_str, sizeof(idx_str), "%u", (unsigned) i); 9665 + 9666 + jsoff_t entry_off = lkp(js, iterable, idx_str, strlen(idx_str)); 9667 + if (entry_off == 0) continue; 9668 + 9669 + jsval_t entry = resolveprop(js, mkval(T_PROP, entry_off)); 9670 + if (vtype(entry) != T_ARR && vtype(entry) != T_OBJ) continue; 9671 + 9672 + jsoff_t key_off = lkp(js, entry, "0", 1); 9673 + if (key_off == 0) continue; 9674 + jsval_t key = resolveprop(js, mkval(T_PROP, key_off)); 9675 + 9676 + jsoff_t val_off = lkp(js, entry, "1", 1); 9677 + jsval_t val = (val_off != 0) ? resolveprop(js, mkval(T_PROP, val_off)) : js_mkundef(); 9678 + 9679 + if (vtype(key) != T_STR) { 9680 + char buf[64]; 9681 + size_t n = tostr(js, key, buf, sizeof(buf)); 9682 + key = js_mkstr(js, buf, n); 9683 + } 9684 + 9685 + setprop(js, result, key, val); 9686 + } 9687 + 9688 + return result; 9689 + } 9690 + 9691 + static jsval_t builtin_object_hasOwnProperty(struct js *js, jsval_t *args, int nargs) { 9692 + if (nargs < 1) return mkval(T_BOOL, 0); 9693 + 9694 + jsval_t obj = js->this_val; 9695 + jsval_t key = args[0]; 9696 + 9697 + obj = resolveprop(js, obj); 9698 + uint8_t t = vtype(obj); 9699 + 9700 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); 9701 + if (vtype(key) != T_STR) { 9702 + char buf[64]; 9703 + size_t n = tostr(js, key, buf, sizeof(buf)); 9704 + key = js_mkstr(js, buf, n); 9705 + } 9706 + 9707 + jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 9708 + jsoff_t key_len, key_off = vstr(js, key, &key_len); 9709 + const char *key_str = (char *) &js->mem[key_off]; 9710 + 9711 + jsoff_t off = lkp(js, as_obj, key_str, key_len); 9712 + return mkval(T_BOOL, off != 0 ? 1 : 0); 9713 + } 9714 + 9296 9715 static jsval_t builtin_object_toString(struct js *js, jsval_t *args, int nargs) { 9297 9716 (void)args; (void)nargs; 9298 9717 jsval_t obj = js->this_val; ··· 13929 14348 jsval_t glob = js->scope; 13930 14349 jsval_t object_proto = js_mkobj(js); 13931 14350 setprop(js, object_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_object_toString)); 14351 + setprop(js, object_proto, js_mkstr(js, "hasOwnProperty", 14), js_mkfun(builtin_object_hasOwnProperty)); 13932 14352 13933 14353 jsval_t function_proto = js_mkobj(js); 13934 14354 set_proto(js, function_proto, object_proto); ··· 14109 14529 setprop(js, obj_func_obj, js_mkstr(js, "create", 6), js_mkfun(builtin_object_create)); 14110 14530 setprop(js, obj_func_obj, js_mkstr(js, "hasOwn", 6), js_mkfun(builtin_object_hasOwn)); 14111 14531 setprop(js, obj_func_obj, js_mkstr(js, "defineProperty", 14), js_mkfun(builtin_object_defineProperty)); 14532 + setprop(js, obj_func_obj, js_mkstr(js, "assign", 6), js_mkfun(builtin_object_assign)); 14533 + setprop(js, obj_func_obj, js_mkstr(js, "freeze", 6), js_mkfun(builtin_object_freeze)); 14534 + setprop(js, obj_func_obj, js_mkstr(js, "isFrozen", 8), js_mkfun(builtin_object_isFrozen)); 14535 + setprop(js, obj_func_obj, js_mkstr(js, "seal", 4), js_mkfun(builtin_object_seal)); 14536 + setprop(js, obj_func_obj, js_mkstr(js, "isSealed", 8), js_mkfun(builtin_object_isSealed)); 14537 + setprop(js, obj_func_obj, js_mkstr(js, "fromEntries", 11), js_mkfun(builtin_object_fromEntries)); 14112 14538 setprop(js, obj_func_obj, js_mkstr(js, "prototype", 9), object_proto); 14113 14539 setprop(js, glob, js_mkstr(js, "Object", 6), mkval(T_FUNC, vdata(obj_func_obj))); 14114 14540