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.

add support for resolving coderefs and handling frozen/sealed objects in `js_unary`

+201 -228
+1 -1
meson.build
··· 79 79 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 80 80 81 81 version_conf = configuration_data() 82 - version_conf.set('ANT_VERSION', '0.2.3.12') 82 + version_conf.set('ANT_VERSION', '0.2.3.13') 83 83 version_conf.set('ANT_GIT_HASH', git_hash) 84 84 version_conf.set('ANT_BUILD_DATE', build_date) 85 85
+174 -227
src/ant.c
··· 7664 7664 return res; 7665 7665 } 7666 7666 7667 + static inline jsval_t resolve_coderef(struct js *js, jsval_t v) { 7668 + if (vtype(v) == T_CODEREF) { 7669 + return lookup(js, &js->code[coderefoff(v)], codereflen(v)); 7670 + } 7671 + return v; 7672 + } 7673 + 7674 + static void unlink_prop(struct js *js, jsoff_t obj_off, jsoff_t prop_off, jsoff_t prev_off) { 7675 + jsoff_t deleted_next = loadoff(js, prop_off) & ~(CONSTMASK | ARRMASK | SLOTMASK); 7676 + jsoff_t target = prev_off ? prev_off : obj_off; 7677 + jsoff_t current = loadoff(js, target); 7678 + saveoff(js, target, (deleted_next & ~3U) | (current & (CONSTMASK | ARRMASK | SLOTMASK | 3U))); 7679 + increment_version(js, obj_off); 7680 + } 7681 + 7682 + static jsval_t check_frozen_sealed(struct js *js, jsval_t obj, const char *action) { 7683 + if (js_truthy(js, get_slot(js, obj, SLOT_FROZEN))) { 7684 + if (js->flags & F_STRICT) return js_mkerr(js, "cannot %s property of frozen object", action); 7685 + return js_mkfalse(); 7686 + } 7687 + if (js_truthy(js, get_slot(js, obj, SLOT_SEALED))) { 7688 + if (js->flags & F_STRICT) return js_mkerr(js, "cannot %s property of sealed object", action); 7689 + return js_mkfalse(); 7690 + } 7691 + return js_mkundef(); 7692 + } 7693 + 7667 7694 static jsval_t js_unary(struct js *js) { 7668 - if (next(js) == TOK_NEW) { 7695 + uint8_t tok = next(js); 7696 + 7697 + static const void *dispatch[] = { 7698 + [TOK_NEW] = &&do_new, 7699 + [TOK_DELETE] = &&do_delete, 7700 + [TOK_AWAIT] = &&do_await, 7701 + [TOK_POSTINC] = &&do_prefix_inc, 7702 + [TOK_POSTDEC] = &&do_prefix_inc, 7703 + [TOK_NOT] = &&do_unary_op, 7704 + [TOK_TILDA] = &&do_unary_op, 7705 + [TOK_TYPEOF] = &&do_typeof, 7706 + [TOK_VOID] = &&do_unary_op, 7707 + [TOK_MINUS] = &&do_unary_op, 7708 + [TOK_PLUS] = &&do_unary_op, 7709 + }; 7710 + 7711 + if (tok < sizeof(dispatch)/sizeof(dispatch[0]) && dispatch[tok]) { 7712 + goto *dispatch[tok]; 7713 + } 7714 + return js_postfix(js); 7715 + 7716 + do_new: { 7669 7717 js->consumed = 1; 7670 7718 jsval_t obj = mkobj(js, 0); 7671 7719 jsval_t saved_this = js->this_val; 7672 7720 js->this_val = obj; 7673 - 7674 - // Parse constructor: can be identifier, member expression, or grouped 7675 - // We need to parse the constructor WITHOUT continuing to parse property access 7676 - // after the call, because property access should be on the constructed object. 7677 - jsval_t ctor = js_group(js); // Parse identifier or (expr) 7678 - if (is_err(ctor)) { 7679 - js->this_val = saved_this; 7680 - return ctor; 7681 - } 7682 - 7683 - // Handle member access on the constructor itself: new foo.Bar(), new foo[x]() 7721 + 7722 + jsval_t ctor = js_group(js); 7723 + if (is_err(ctor)) { js->this_val = saved_this; return ctor; } 7724 + 7684 7725 while (next(js) == TOK_DOT || next(js) == TOK_LBRACKET) { 7726 + ctor = resolve_coderef(js, ctor); 7727 + if (is_err(ctor)) { js->this_val = saved_this; return ctor; } 7728 + 7685 7729 if (js->tok == TOK_DOT) { 7686 7730 js->consumed = 1; 7687 - if (vtype(ctor) == T_CODEREF) { 7688 - ctor = lookup(js, &js->code[coderefoff(ctor)], codereflen(ctor)); 7689 - if (is_err(ctor)) { js->this_val = saved_this; return ctor; } 7690 - } 7691 7731 if (next(js) != TOK_IDENTIFIER && !is_keyword_propname(js->tok)) { 7692 7732 js->this_val = saved_this; 7693 7733 return js_mkerr_typed(js, JS_ERR_SYNTAX, "identifier expected"); 7694 7734 } 7695 7735 js->consumed = 1; 7696 - jsval_t prop_name = mkcoderef((jsoff_t)js->toff, (jsoff_t)js->tlen); 7697 - ctor = do_op(js, TOK_DOT, ctor, prop_name); 7736 + ctor = do_op(js, TOK_DOT, ctor, mkcoderef((jsoff_t)js->toff, (jsoff_t)js->tlen)); 7698 7737 } else { 7699 7738 js->consumed = 1; 7700 - if (vtype(ctor) == T_CODEREF) { 7701 - ctor = lookup(js, &js->code[coderefoff(ctor)], codereflen(ctor)); 7702 - if (is_err(ctor)) { js->this_val = saved_this; return ctor; } 7703 - } 7704 7739 jsval_t idx = js_expr(js); 7705 7740 if (is_err(idx)) { js->this_val = saved_this; return idx; } 7706 7741 if (next(js) != TOK_RBRACKET) { js->this_val = saved_this; return js_mkerr_typed(js, JS_ERR_SYNTAX, "] expected"); } ··· 7708 7743 ctor = do_op(js, TOK_BRACKET, ctor, idx); 7709 7744 } 7710 7745 } 7711 - 7712 - // Resolve the constructor if it's still a coderef 7713 - if (vtype(ctor) == T_CODEREF) { 7714 - ctor = lookup(js, &js->code[coderefoff(ctor)], codereflen(ctor)); 7715 - if (is_err(ctor)) { js->this_val = saved_this; return ctor; } 7716 - } 7717 - if (vtype(ctor) == T_PROP || vtype(ctor) == T_PROPREF) { 7718 - ctor = resolveprop(js, ctor); 7719 - } 7720 - 7721 - // Now handle optional call arguments: new Foo() vs new Foo 7746 + 7747 + ctor = resolve_coderef(js, ctor); 7748 + if (is_err(ctor)) { js->this_val = saved_this; return ctor; } 7749 + if (vtype(ctor) == T_PROP || vtype(ctor) == T_PROPREF) ctor = resolveprop(js, ctor); 7750 + 7722 7751 jsval_t result; 7752 + push_this(obj); 7723 7753 if (next(js) == TOK_LPAREN) { 7724 - push_this(obj); 7725 7754 jsval_t params = js_call_params(js); 7726 - if (is_err(params)) { 7727 - pop_this(); 7728 - js->this_val = saved_this; 7729 - return params; 7730 - } 7755 + if (is_err(params)) { pop_this(); js->this_val = saved_this; return params; } 7731 7756 result = do_op(js, TOK_CALL, ctor, params); 7732 - pop_this(); 7733 7757 } else { 7734 - // new Foo without parentheses - call with no args 7735 - push_this(obj); 7736 7758 result = do_op(js, TOK_CALL, ctor, mkcoderef(0, 0)); 7737 - pop_this(); 7738 - // do_call_op set consumed=1, but we didn't consume the peeked token 7739 - // Reset consumed so the peeked token can be seen by caller 7740 7759 js->consumed = 0; 7741 7760 } 7742 - 7761 + pop_this(); 7762 + 7743 7763 jsval_t constructed_obj = js->this_val; 7744 7764 js->this_val = saved_this; 7745 - 7746 - jsval_t new_result; 7747 - if (vtype(result) == T_OBJ || vtype(result) == T_ARR || vtype(result) == T_PROMISE || vtype(result) == T_FUNC) { 7748 - new_result = result; 7749 - } else { 7750 - new_result = constructed_obj; 7751 - } 7752 - 7753 - // Continue parsing property access after 'new X()' - e.g., new A().foo 7754 - // Track the object for method calls (this binding) 7765 + 7766 + uint8_t rtype = vtype(result); 7767 + jsval_t new_result = ( 7768 + rtype == T_OBJ || rtype == T_ARR || 7769 + rtype == T_PROMISE || rtype == T_FUNC 7770 + ) ? result : constructed_obj; 7771 + 7755 7772 jsval_t call_obj = js_mkundef(); 7756 7773 while (next(js) == TOK_DOT || next(js) == TOK_LBRACKET || next(js) == TOK_OPTIONAL_CHAIN || next(js) == TOK_LPAREN) { 7757 - if (js->tok == TOK_DOT || js->tok == TOK_OPTIONAL_CHAIN) { 7758 - uint8_t op = js->tok; 7774 + uint8_t op = js->tok; 7775 + if (op == TOK_DOT || op == TOK_OPTIONAL_CHAIN) { 7759 7776 js->consumed = 1; 7760 - call_obj = new_result; // Save object for potential method call 7777 + call_obj = new_result; 7761 7778 if (op == TOK_OPTIONAL_CHAIN && (vtype(call_obj) == T_NULL || vtype(call_obj) == T_UNDEF)) { 7762 - new_result = js_mkundef(); 7763 - call_obj = js_mkundef(); 7779 + new_result = call_obj = js_mkundef(); 7764 7780 } else { 7765 7781 if (next(js) != TOK_IDENTIFIER && !is_keyword_propname(js->tok)) { 7766 7782 return js_mkerr_typed(js, JS_ERR_SYNTAX, "identifier expected"); 7767 7783 } 7768 7784 js->consumed = 1; 7769 - jsval_t prop_name = mkcoderef((jsoff_t)js->toff, (jsoff_t)js->tlen); 7770 - new_result = do_op(js, op, new_result, prop_name); 7785 + new_result = do_op(js, op, new_result, mkcoderef((jsoff_t)js->toff, (jsoff_t)js->tlen)); 7771 7786 } 7772 - } else if (js->tok == TOK_LBRACKET) { 7787 + } else if (op == TOK_LBRACKET) { 7773 7788 js->consumed = 1; 7774 - call_obj = new_result; // Save object for potential method call 7789 + call_obj = new_result; 7775 7790 jsval_t idx = js_expr(js); 7776 7791 if (is_err(idx)) return idx; 7777 7792 if (next(js) != TOK_RBRACKET) return js_mkerr_typed(js, JS_ERR_SYNTAX, "] expected"); 7778 7793 js->consumed = 1; 7779 7794 new_result = do_op(js, TOK_BRACKET, new_result, idx); 7780 - } else if (js->tok == TOK_LPAREN) { 7781 - // Method call - use saved object as 'this' 7782 - jsval_t func_this = call_obj; 7783 - if (vtype(func_this) == T_UNDEF) { 7784 - // No property access before call, use global this 7785 - func_this = js->this_val; 7786 - } 7795 + } else { 7796 + jsval_t func_this = vtype(call_obj) == T_UNDEF ? js->this_val : call_obj; 7787 7797 push_this(func_this); 7788 7798 jsval_t params = js_call_params(js); 7789 - if (is_err(params)) { 7790 - pop_this(); 7791 - return params; 7792 - } 7799 + if (is_err(params)) { pop_this(); return params; } 7793 7800 new_result = do_op(js, TOK_CALL, new_result, params); 7794 7801 pop_this(); 7795 - call_obj = js_mkundef(); // Reset after call 7802 + call_obj = js_mkundef(); 7796 7803 } 7797 7804 } 7798 7805 return new_result; 7799 - } else if (next(js) == TOK_DELETE) { 7806 + } 7807 + 7808 + do_delete: { 7800 7809 js->consumed = 1; 7801 - 7810 + 7802 7811 if ((js->flags & F_STRICT) && next(js) == TOK_IDENTIFIER) { 7803 7812 jsoff_t id_pos = js->pos; 7804 7813 uint8_t id_tok = js->tok; 7805 - jsoff_t id_toff = js->toff; 7806 - jsoff_t id_tlen = js->tlen; 7814 + jsoff_t id_toff = js->toff, id_tlen = js->tlen; 7807 7815 js->consumed = 1; 7808 7816 uint8_t after = next(js); 7809 7817 if (after != TOK_DOT && after != TOK_LBRACKET && after != TOK_OPTIONAL_CHAIN) { 7810 7818 return js_mkerr_typed(js, JS_ERR_SYNTAX, "cannot delete unqualified identifier in strict mode"); 7811 7819 } 7812 - js->pos = id_pos; 7813 - js->tok = id_tok; 7814 - js->toff = id_toff; 7815 - js->tlen = id_tlen; 7816 - js->consumed = 0; 7820 + js->pos = id_pos; js->tok = id_tok; js->toff = id_toff; js->tlen = id_tlen; js->consumed = 0; 7817 7821 } 7818 - 7819 - jsoff_t save_pos = js->pos; 7820 - uint8_t save_tok = js->tok; 7821 - js_parse_state_t saved_delete; 7822 - JS_SAVE_STATE(js, saved_delete); 7823 - uint8_t saved_delete_flags = js->flags; 7822 + 7823 + js_parse_state_t saved_state; 7824 + JS_SAVE_STATE(js, saved_state); 7825 + uint8_t saved_flags = js->flags; 7824 7826 jsval_t operand = js_postfix(js); 7827 + 7825 7828 if (is_err(operand)) { 7826 - JS_RESTORE_STATE(js, saved_delete); 7827 - js->flags = saved_delete_flags & ~F_THROW; 7828 - js->flags |= F_NOEXEC; 7829 + JS_RESTORE_STATE(js, saved_state); 7830 + js->flags = (saved_flags & ~F_THROW) | F_NOEXEC; 7829 7831 js_postfix(js); 7830 - js->flags = saved_delete_flags; 7832 + js->flags = saved_flags; 7831 7833 return js_mktrue(); 7832 7834 } 7833 7835 if (js->flags & F_NOEXEC) return js_mktrue(); 7834 - 7836 + 7835 7837 if (vtype(operand) == T_PROPREF) { 7836 7838 jsoff_t obj_off = propref_obj(operand); 7837 7839 jsoff_t key_off = propref_key(operand); ··· 7839 7841 jsval_t key = mkval(T_STR, key_off); 7840 7842 jsoff_t len; 7841 7843 const char *key_str = (const char *)&js->mem[vstr(js, key, &len)]; 7842 - 7844 + 7843 7845 if (is_proxy(js, obj)) { 7844 7846 jsval_t result = proxy_delete(js, obj, key_str, len); 7845 - if (is_err(result)) return result; 7846 - return js_truthy(js, result) ? js_mktrue() : js_mkfalse(); 7847 + return is_err(result) ? result : (js_truthy(js, result) ? js_mktrue() : js_mkfalse()); 7847 7848 } 7848 - 7849 - if (js_truthy(js, get_slot(js, obj, SLOT_FROZEN))) { 7850 - if (js->flags & F_STRICT) return js_mkerr(js, "cannot delete property of frozen object"); 7851 - return js_mkfalse(); 7852 - } 7853 - 7854 - if (js_truthy(js, get_slot(js, obj, SLOT_SEALED))) { 7855 - if (js->flags & F_STRICT) return js_mkerr(js, "cannot delete property of sealed object"); 7856 - return js_mkfalse(); 7857 - } 7858 - 7849 + 7850 + jsval_t err = check_frozen_sealed(js, obj, "delete"); 7851 + if (vtype(err) != T_UNDEF) return err; 7852 + 7859 7853 jsoff_t prop_off = lkp(js, obj, key_str, len); 7860 7854 if (prop_off == 0) return js_mktrue(); 7855 + 7861 7856 if (is_const_prop(js, prop_off)) { 7862 7857 if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 7863 7858 return js_mkfalse(); 7864 7859 } 7865 - 7860 + 7866 7861 descriptor_entry_t *desc = lookup_descriptor(obj_off, key_str, len); 7867 7862 if (desc && !desc->configurable) { 7868 7863 if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 7869 7864 return js_mkfalse(); 7870 7865 } 7866 + 7871 7867 jsoff_t first_prop = loadoff(js, obj_off) & ~(3U | CONSTMASK | ARRMASK | SLOTMASK); 7872 7868 if (first_prop == prop_off) { 7873 - jsoff_t deleted_next = loadoff(js, prop_off) & ~(CONSTMASK | ARRMASK | SLOTMASK); 7874 - jsoff_t current = loadoff(js, obj_off); 7875 - saveoff(js, obj_off, (deleted_next & ~3U) | (current & (CONSTMASK | ARRMASK | SLOTMASK | 3U))); 7876 - increment_version(js, obj_off); 7869 + unlink_prop(js, obj_off, prop_off, 0); 7877 7870 return js_mktrue(); 7878 7871 } 7879 - jsoff_t prev = first_prop; 7880 - while (prev != 0) { 7872 + for (jsoff_t prev = first_prop; prev != 0; ) { 7881 7873 jsoff_t next_prop = loadoff(js, prev) & ~(3U | CONSTMASK | ARRMASK | SLOTMASK); 7882 - if (next_prop == prop_off) { 7883 - jsoff_t deleted_next = loadoff(js, prop_off) & ~(CONSTMASK | ARRMASK | SLOTMASK); 7884 - jsoff_t current = loadoff(js, prev); 7885 - saveoff(js, prev, (deleted_next & ~3U) | (current & (CONSTMASK | ARRMASK | SLOTMASK | 3U))); 7886 - increment_version(js, obj_off); 7887 - return js_mktrue(); 7888 - } 7874 + if (next_prop == prop_off) { unlink_prop(js, obj_off, prop_off, prev); return js_mktrue(); } 7889 7875 prev = next_prop; 7890 7876 } 7891 7877 return js_mktrue(); 7892 7878 } 7893 - 7894 - if (vtype(operand) != T_PROP) { 7895 - return js_mktrue(); 7896 - } 7897 - jsoff_t prop_off = (jsoff_t) vdata(operand); 7879 + 7880 + if (vtype(operand) != T_PROP) return js_mktrue(); 7881 + 7882 + jsoff_t prop_off = (jsoff_t)vdata(operand); 7898 7883 if (is_const_prop(js, prop_off)) { 7899 7884 if (js->flags & F_STRICT) return js_mkerr(js, "cannot delete constant property"); 7900 7885 return js_mkfalse(); 7901 7886 } 7902 - 7903 - jsoff_t owner_obj_off = 0; 7904 - jsoff_t prev_prop_off = 0; 7887 + 7888 + jsoff_t owner_obj_off = 0, prev_prop_off = 0; 7905 7889 bool is_first_prop = false; 7906 - 7907 - for (jsoff_t off = 0; off < js->brk;) { 7890 + for (jsoff_t off = 0; off < js->brk; ) { 7908 7891 jsoff_t v = loadoff(js, off); 7909 7892 jsoff_t cleaned = v & ~(CONSTMASK | ARRMASK | SLOTMASK); 7910 7893 jsoff_t n = esize(cleaned); 7911 7894 if ((cleaned & 3) == T_OBJ) { 7912 7895 jsoff_t first_prop = cleaned & ~3U; 7913 - if (first_prop == prop_off) { 7914 - owner_obj_off = off; 7915 - is_first_prop = true; 7916 - break; 7896 + if (first_prop == prop_off) { owner_obj_off = off; is_first_prop = true; break; } 7897 + for (jsoff_t cur = first_prop; cur != 0 && cur < js->brk; ) { 7898 + jsoff_t nx = loadoff(js, cur) & ~(3U | CONSTMASK | ARRMASK | SLOTMASK); 7899 + if (nx == prop_off) { owner_obj_off = off; prev_prop_off = cur; break; } 7900 + cur = nx; 7917 7901 } 7918 - jsoff_t cur_prop = first_prop; 7919 - while (cur_prop != 0 && cur_prop < js->brk) { 7920 - jsoff_t next = loadoff(js, cur_prop) & ~(3U | CONSTMASK | ARRMASK | SLOTMASK); 7921 - if (next == prop_off) { 7922 - owner_obj_off = off; 7923 - prev_prop_off = cur_prop; 7924 - break; 7925 - } 7926 - cur_prop = next; 7927 - } 7928 - if (owner_obj_off != 0) break; 7902 + if (owner_obj_off) break; 7929 7903 } 7930 7904 off += n; 7931 7905 } 7932 - 7933 - if (owner_obj_off != 0) { 7906 + 7907 + if (owner_obj_off) { 7934 7908 jsval_t owner_obj = mkval(T_OBJ, owner_obj_off); 7935 - 7936 - if (js_truthy(js, get_slot(js, owner_obj, SLOT_FROZEN))) { 7937 - if (js->flags & F_STRICT) return js_mkerr(js, "cannot delete property of frozen object"); 7938 - return js_mkfalse(); 7939 - } 7940 - 7941 - if (js_truthy(js, get_slot(js, owner_obj, SLOT_SEALED))) { 7942 - if (js->flags & F_STRICT) return js_mkerr(js, "cannot delete property of sealed object"); 7943 - return js_mkfalse(); 7944 - } 7945 - 7909 + jsval_t err = check_frozen_sealed(js, owner_obj, "delete"); 7910 + if (vtype(err) != T_UNDEF) return err; 7911 + 7946 7912 jsoff_t key_str_off = loadoff(js, (jsoff_t)(prop_off + sizeof(jsoff_t))); 7947 7913 jsoff_t key_len = (loadoff(js, key_str_off) >> 2) - 1; 7948 7914 const char *key_str = (char *)&js->mem[key_str_off + sizeof(jsoff_t)]; ··· 7951 7917 if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 7952 7918 return js_mkfalse(); 7953 7919 } 7954 - 7955 - if (is_first_prop) { 7956 - jsoff_t deleted_next = loadoff(js, prop_off) & ~(CONSTMASK | ARRMASK | SLOTMASK); 7957 - jsoff_t current = loadoff(js, owner_obj_off); 7958 - saveoff(js, owner_obj_off, (deleted_next & ~3U) | (current & (CONSTMASK | ARRMASK | SLOTMASK | 3U))); 7959 - } else { 7960 - jsoff_t deleted_next = loadoff(js, prop_off) & ~(CONSTMASK | ARRMASK | SLOTMASK); 7961 - jsoff_t current = loadoff(js, prev_prop_off); 7962 - saveoff(js, prev_prop_off, (deleted_next & ~3U) | (current & (CONSTMASK | ARRMASK | SLOTMASK | 3U))); 7963 - } 7964 - increment_version(js, owner_obj_off); 7920 + unlink_prop(js, owner_obj_off, prop_off, is_first_prop ? 0 : prev_prop_off); 7965 7921 } 7966 - (void) save_pos; 7967 - (void) save_tok; 7968 7922 return js_mktrue(); 7969 - } else if (next(js) == TOK_AWAIT) { 7923 + } 7924 + 7925 + do_await: { 7970 7926 js->consumed = 1; 7971 7927 jsval_t expr = js_unary(js); 7972 7928 if (is_err(expr)) return expr; 7973 7929 if (js->flags & F_NOEXEC) return expr; 7930 + 7974 7931 jsval_t resolved = resolveprop(js, expr); 7975 - if (vtype(resolved) != T_PROMISE) { 7976 - return resolved; 7977 - } 7978 - 7932 + if (vtype(resolved) != T_PROMISE) return resolved; 7933 + 7979 7934 uint32_t pid = get_promise_id(js, resolved); 7980 7935 promise_data_entry_t *pd = get_promise_data(pid, false); 7981 7936 if (!pd) return js_mkerr(js, "invalid promise state"); 7982 - 7983 - if (pd->state != 0) { 7984 - if (pd->state == 1) { return pd->value; 7985 - } else if (pd->state == 2) return js_throw(js, pd->value); 7986 - } 7987 - 7988 - mco_coro* current_mco = mco_running(); 7937 + 7938 + if (pd->state == 1) return pd->value; 7939 + if (pd->state == 2) return js_throw(js, pd->value); 7940 + 7941 + mco_coro *current_mco = mco_running(); 7989 7942 if (!current_mco) return js_mkerr(js, "await can only be used inside async functions"); 7990 - 7943 + 7991 7944 async_exec_context_t *ctx = (async_exec_context_t *)mco_get_user_data(current_mco); 7992 7945 if (!ctx || !ctx->coro) return js_mkerr(js, "invalid async context"); 7993 - 7946 + 7994 7947 coroutine_t *coro = ctx->coro; 7995 7948 coro->awaited_promise = resolved; 7996 - coro->is_settled = false; 7997 - coro->is_ready = false; 7998 - 7949 + coro->is_settled = coro->is_ready = false; 7950 + 7999 7951 jsval_t resume_obj = mkobj(js, 0); 8000 7952 set_slot(js, resume_obj, SLOT_CFUNC, js_mkfun(resume_coroutine_wrapper)); 8001 7953 set_slot(js, resume_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); 8002 - jsval_t resume_fn = mkval(T_FUNC, vdata(resume_obj)); 8003 - 7954 + 8004 7955 jsval_t reject_obj = mkobj(js, 0); 8005 7956 set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(reject_coroutine_wrapper)); 8006 7957 set_slot(js, reject_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); 8007 - jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 8008 - 8009 - jsval_t then_args[] = { resume_fn, reject_fn }; 7958 + 7959 + jsval_t then_args[] = { mkval(T_FUNC, vdata(resume_obj)), mkval(T_FUNC, vdata(reject_obj)) }; 8010 7960 jsval_t saved_this = js->this_val; 8011 7961 js->this_val = resolved; 8012 7962 (void)builtin_promise_then(js, then_args, 2); 8013 7963 js->this_val = saved_this; 8014 - 7964 + 8015 7965 js_parse_state_t saved; 8016 7966 JS_SAVE_STATE(js, saved); 8017 7967 uint8_t saved_flags = js->flags; 8018 - 7968 + 8019 7969 mco_result mco_res = mco_yield(current_mco); 8020 - 7970 + 8021 7971 JS_RESTORE_STATE(js, saved); 8022 7972 js->flags = saved_flags; 8023 - 8024 - if (mco_res != MCO_SUCCESS) { 8025 - return js_mkerr(js, "failed to yield coroutine"); 8026 - } 8027 - 7973 + 7974 + if (mco_res != MCO_SUCCESS) return js_mkerr(js, "failed to yield coroutine"); 7975 + 8028 7976 jsval_t result = coro->result; 8029 7977 bool is_error = coro->is_error; 8030 - 8031 7978 coro->is_settled = false; 8032 7979 coro->awaited_promise = js_mkundef(); 8033 - 8034 - if (is_error) { 8035 - return js_throw(js, result); 8036 - } 8037 - return result; 8038 - } else if (next(js) == TOK_POSTINC || js->tok == TOK_POSTDEC) { 7980 + 7981 + return is_error ? js_throw(js, result) : result; 7982 + } 7983 + 7984 + do_prefix_inc: { 8039 7985 uint8_t op = js->tok; 8040 7986 js->consumed = 1; 8041 7987 if ((js->flags & F_STRICT) && next(js) == TOK_IDENTIFIER && is_eval_or_arguments(js, js->toff, js->tlen)) { ··· 8051 7997 return js_mkerr_typed(js, JS_ERR_SYNTAX, "Invalid left-hand side in assignment"); 8052 7998 } 8053 7999 return do_op(js, op == TOK_POSTINC ? TOK_PLUS : TOK_MINUS, resolved, tov(1)); 8054 - } else if (next(js) == TOK_NOT || js->tok == TOK_TILDA || js->tok == TOK_TYPEOF || 8055 - js->tok == TOK_VOID || js->tok == TOK_MINUS || js->tok == TOK_PLUS) { 8056 - uint8_t t = js->tok; 8057 - if (t == TOK_MINUS) t = TOK_UMINUS; 8058 - if (t == TOK_PLUS) t = TOK_UPLUS; 8000 + } 8001 + 8002 + do_typeof: { 8059 8003 js->consumed = 1; 8060 8004 jsoff_t saved_pos = js->pos; 8061 - uint8_t saved_tok = js->tok; 8062 - uint8_t saved_consumed = js->consumed; 8063 - uint8_t saved_flags = js->flags; 8005 + uint8_t saved_tok = js->tok, saved_consumed = js->consumed, saved_flags = js->flags; 8064 8006 jsval_t operand = js_unary(js); 8065 - if (t == TOK_TYPEOF && is_err(operand)) { 8066 - js->pos = saved_pos; 8067 - js->tok = saved_tok; 8068 - js->consumed = saved_consumed; 8007 + if (is_err(operand)) { 8008 + js->pos = saved_pos; js->tok = saved_tok; js->consumed = saved_consumed; 8069 8009 js->flags = (saved_flags & ~F_THROW) | F_NOEXEC; 8070 8010 js_unary(js); 8071 8011 js->flags = saved_flags & ~F_THROW; 8072 8012 operand = js_mkundef(); 8073 8013 } 8014 + return do_op(js, TOK_TYPEOF, js_mkundef(), operand); 8015 + } 8016 + 8017 + do_unary_op: { 8018 + uint8_t t = js->tok; 8019 + if (t == TOK_MINUS) t = TOK_UMINUS; 8020 + if (t == TOK_PLUS) t = TOK_UPLUS; 8021 + js->consumed = 1; 8022 + jsval_t operand = js_unary(js); 8074 8023 return do_op(js, t, js_mkundef(), operand); 8075 - } else { 8076 - return js_postfix(js); 8077 8024 } 8078 8025 } 8079 8026
+26
tests/test_all_new.js
··· 1 + function A() { this.foo = 1; this.bar = 42; } 2 + A.prototype.method = function() { return "method"; }; 3 + 4 + console.log("Test 1 - new A().foo:", new A().foo); 5 + console.log("Test 2 - new A().bar:", new A().bar); 6 + console.log("Test 3 - new A().method():", new A().method()); 7 + 8 + var x = new A().foo; 9 + console.log("Test 4 - assignment:", x); 10 + 11 + if (new A().foo) console.log("Test 5 - if condition: PASS"); 12 + if (new A().bar === 42) console.log("Test 6 - comparison: PASS"); 13 + 14 + function B() { this.nested = { deep: "value" }; } 15 + console.log("Test 7 - chained:", new B().nested.deep); 16 + console.log("Test 8 - bracket:", new A()["foo"]); 17 + 18 + function C() { this.x = 99; } 19 + console.log("Test 9 - no parens:", new C); 20 + console.log("Test 10 - (new C):", (new C)); 21 + console.log("Test 11 - (new C).x:", (new C).x); 22 + 23 + // Test new with member access on constructor 24 + var ns = { Ctor: function() { this.v = "ns"; } }; 25 + console.log("Test 12 - new ns.Ctor():", new ns.Ctor()); 26 + console.log("Test 13 - new ns.Ctor().v:", new ns.Ctor().v);