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.

use descriptor tables for getters/setters/properties

+479 -691
+12
include/ant.h
··· 25 25 JS_ERR_GENERIC 26 26 } js_err_type_t; 27 27 28 + #define JS_DESC_W (1 << 0) 29 + #define JS_DESC_E (1 << 1) 30 + #define JS_DESC_C (1 << 2) 31 + 28 32 struct js *js_create(void *buf, size_t len); 29 33 struct js *js_create_dynamic(size_t initial_size, size_t max_size); 30 34 ··· 129 133 ant_register_library(lib, name, "ant:" name, "node:" name, NULL) 130 134 131 135 typedef jsval_t (*js_getter_fn)(struct js *js, jsval_t obj, const char *key, size_t key_len); 136 + typedef bool (*js_setter_fn)(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t value); 132 137 133 138 void js_set_getter(struct js *js, jsval_t obj, js_getter_fn getter); 139 + void js_set_setter(struct js *js, jsval_t obj, js_setter_fn setter); 140 + 141 + void js_set_descriptor(struct js *js, jsval_t obj, const char *key, size_t klen, int flags); 142 + void js_set_getter_desc(struct js *js, jsval_t obj, const char *key, size_t klen, jsval_t getter, int flags); 143 + void js_set_setter_desc(struct js *js, jsval_t obj, const char *key, size_t klen, jsval_t setter, int flags); 144 + void js_set_accessor_desc(struct js *js, jsval_t obj, const char *key, size_t klen, jsval_t getter, jsval_t setter, int flags); 145 + 134 146 void js_print_stack_trace(FILE *stream);
+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.2.1.17') 77 + version_conf.set('ANT_VERSION', '0.2.2.0') 78 78 version_conf.set('ANT_GIT_HASH', git_hash) 79 79 version_conf.set('ANT_BUILD_DATE', build_date) 80 80
+347 -653
src/ant.c
··· 255 255 } intern_prop_cache_entry_t; 256 256 static intern_prop_cache_entry_t intern_prop_cache[INTERN_PROP_CACHE_SIZE]; 257 257 258 - #define DESC_CACHE_SIZE 1024 259 - typedef struct { 260 - jsoff_t obj_off; 261 - uint32_t key_hash; 262 - jsoff_t desc_off; 263 - uint8_t valid; 264 - } desc_cache_entry_t; 265 - static desc_cache_entry_t desc_cache[DESC_CACHE_SIZE]; 266 258 267 - static inline void invalidate_desc_cache_for_obj(jsoff_t obj_off) { 268 - desc_cache_entry_t *cache = desc_cache; 269 - desc_cache_entry_t *end = cache + DESC_CACHE_SIZE; 270 - while (cache < end) { if (cache->obj_off == obj_off) cache->valid = 0; cache++; } 271 - } 272 259 273 260 typedef struct map_entry { 274 261 char *key; ··· 289 276 bool revoked; 290 277 UT_hash_handle hh; 291 278 } proxy_data_t; 279 + 280 + typedef struct dynamic_accessors { 281 + jsoff_t obj_offset; 282 + js_getter_fn getter; 283 + js_setter_fn setter; 284 + UT_hash_handle hh; 285 + } dynamic_accessors_t; 286 + 287 + typedef struct descriptor_entry { 288 + uint64_t key; 289 + bool writable; 290 + bool enumerable; 291 + bool configurable; 292 + bool has_getter; 293 + bool has_setter; 294 + jsval_t getter; 295 + jsval_t setter; 296 + UT_hash_handle hh; 297 + } descriptor_entry_t; 292 298 293 299 #define MAX_FUNC_PARAMS 64 294 300 ··· 316 322 } parsed_func_t; 317 323 318 324 static parsed_func_t *func_parse_cache = NULL; 319 - 320 325 static ant_library_t *library_registry = NULL; 321 326 static esm_module_cache_t global_module_cache = {NULL, 0}; 322 327 static proxy_data_t *proxy_registry = NULL; 328 + static dynamic_accessors_t *accessor_registry = NULL; 329 + static descriptor_entry_t *desc_registry = NULL; 323 330 324 331 void js_protect_init_memory(struct js *js) { 325 332 protected_brk = js_getbrk(js); ··· 395 402 jsoff_t max_size; // maximum allowed memory size (for dynamic growth) 396 403 bool had_newline; // true if newline was crossed before current token 397 404 jsval_t thrown_value; // stores the actual thrown value for catch blocks 398 - bool has_descriptors; // true if defineProperty has ever been called 399 405 bool is_hoisting; // true during function declaration hoisting pass 400 406 uint64_t symbol_counter; // counter for generating unique symbol IDs 401 407 }; ··· 684 690 static jsval_t builtin_function_call(struct js *js, jsval_t *args, int nargs); 685 691 static jsval_t builtin_function_apply(struct js *js, jsval_t *args, int nargs); 686 692 static jsval_t builtin_function_bind(struct js *js, jsval_t *args, int nargs); 693 + static bool try_dynamic_setter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t value); 687 694 688 695 static jsval_t call_js(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope); 689 696 static jsval_t call_js_internal(struct js *js, const char *fn, jsoff_t fnlen, jsval_t closure_scope, jsval_t *bound_args, int bound_argc); ··· 711 718 static jsval_t setprop_nonconfigurable(struct js *js, jsval_t obj, const char *key, size_t keylen, jsval_t v); 712 719 static jsoff_t lkp(struct js *js, jsval_t obj, const char *key, size_t len); 713 720 static jsval_t resolveprop(struct js *js, jsval_t v); 721 + static descriptor_entry_t *lookup_descriptor(jsoff_t obj_off, const char *key, size_t klen); 714 722 715 723 static jsval_t unwrap_primitive(struct js *js, jsval_t val) { 716 724 if (vtype(val) != T_OBJ) return val; ··· 996 1004 return i; 997 1005 } 998 1006 999 - static inline size_t build_desc_key(char *buf, size_t bufsize, const char *key, size_t klen) { 1000 - size_t total = 7 + klen + 1; 1001 - if (total > bufsize) return 0; 1002 - memcpy(buf, "__desc_", 7); 1003 - memcpy(buf + 7, key, klen); 1004 - buf[7 + klen] = '\0'; 1005 - return 7 + klen; 1006 - } 1007 - 1008 1007 static inline size_t uint_to_str(char *buf, size_t bufsize, unsigned int val) { 1009 1008 if (bufsize == 0) return 0; 1010 1009 if (val == 0) { ··· 1384 1383 1385 1384 const char *tag_key = get_toStringTag_sym_key(); 1386 1385 size_t tag_key_len = strlen(tag_key); 1387 - if (!streq(pkstr, pklen, "__proto__", 9) && !streq(pkstr, pklen, "constructor", 11) && !streq(pkstr, pklen, tag_key, tag_key_len) && !streq(pkstr, pklen, "__getter", 8)) { 1386 + if (!streq(pkstr, pklen, "__proto__", 9) && !streq(pkstr, pklen, "constructor", 11) && !streq(pkstr, pklen, tag_key, tag_key_len)) { 1388 1387 has_proto_props = true; 1389 1388 break; 1390 1389 } ··· 1410 1409 1411 1410 const char *tag_key2 = get_toStringTag_sym_key(); 1412 1411 size_t tag_key_len2 = strlen(tag_key2); 1413 - if (!streq(pkstr, pklen, "__proto__", 9) && !streq(pkstr, pklen, "constructor", 11) && !streq(pkstr, pklen, tag_key2, tag_key_len2) && !streq(pkstr, pklen, "__getter", 8)) { 1412 + if (!streq(pkstr, pklen, "__proto__", 9) && !streq(pkstr, pklen, "constructor", 11) && !streq(pkstr, pklen, tag_key2, tag_key_len2)) { 1414 1413 if (!proto_first) n += cpy(buf + n, len - n, ",\n", 2); 1415 1414 proto_first = false; 1416 1415 n += add_indent(buf + n, len - n, stringify_indent); ··· 1570 1569 1571 1570 bool is_desc = (klen > 7 && key[0] == '_' && key[1] == '_' && key[2] == 'd' && key[3] == 'e' && key[4] == 's' && key[5] == 'c' && key[6] == '_'); 1572 1571 const char *tag_sym_key = get_toStringTag_sym_key(); 1573 - bool should_hide = streq(key, klen, "__proto__", 9) || streq(key, klen, tag_sym_key, strlen(tag_sym_key)) || streq(key, klen, "__getter", 8) || is_desc; 1572 + bool should_hide = streq(key, klen, "__proto__", 9) || streq(key, klen, tag_sym_key, strlen(tag_sym_key)) || is_desc; 1574 1573 1575 1574 if (!should_hide) { 1576 - char desc_key[128]; 1577 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 1578 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 1579 - if (desc_off == 0) goto check_done; 1580 - 1581 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 1582 - if (vtype(desc_obj) != T_OBJ) goto check_done; 1583 - 1584 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 1585 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 1586 - if (get_off != 0 || set_off != 0) { 1587 - jsval_t get_val = get_off ? resolveprop(js, mkval(T_PROP, get_off)) : js_mkundef(); 1588 - jsval_t set_val = set_off ? resolveprop(js, mkval(T_PROP, set_off)) : js_mkundef(); 1589 - if ((vtype(get_val) == T_FUNC || vtype(get_val) == T_CFUNC) || 1590 - (vtype(set_val) == T_FUNC || vtype(set_val) == T_CFUNC)) { 1575 + jsoff_t obj_off = (jsoff_t)vdata(obj); 1576 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 1577 + if (desc) { 1578 + if (desc->has_getter || desc->has_setter) { 1579 + should_hide = true; 1580 + } else if (!desc->enumerable) { 1591 1581 should_hide = true; 1592 - goto check_done; 1593 1582 } 1594 1583 } 1595 - 1596 - jsoff_t enumerable_off = lkp(js, desc_obj, "enumerable", 10); 1597 - if (enumerable_off == 0) goto check_done; 1598 - 1599 - jsval_t enumerable_val = resolveprop(js, mkval(T_PROP, enumerable_off)); 1600 - if (!js_truthy(js, enumerable_val)) should_hide = true; 1601 - 1602 - check_done:; 1603 1584 } 1604 1585 1605 1586 if (!should_hide) { ··· 1781 1762 1782 1763 static bool is_hidden_func_prop(const char *key, jsoff_t koff, jsoff_t klen) { 1783 1764 if (klen == 4 && memcmp(key, "name", 4) == 0) return true; 1784 - if (klen == 8 && memcmp(key, "__getter", 8) == 0) return true; 1765 + 1785 1766 if (key[0] == '_' && key[1] == '_') return true; 1786 1767 1787 1768 const char *interned = get_koff_intern(koff); ··· 2982 2963 2983 2964 jsval_t res = mkprop(js, proto_obj, constructor_key, func, false); 2984 2965 if (is_err(res)) return res; 2985 - 2986 - jsval_t desc_key_str = js_mkstr(js, "__desc_constructor", 18); 2987 - if (is_err(desc_key_str)) return desc_key_str; 2988 - 2989 - jsval_t desc_obj = js_mkobj(js); 2990 - if (is_err(desc_obj)) return desc_obj; 2991 - 2992 - setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mktrue()); 2993 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mkfalse()); 2994 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mktrue()); 2995 - setprop(js, proto_obj, desc_key_str, desc_obj); 2966 + js_set_descriptor(js, proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C); 2996 2967 2997 2968 jsval_t prototype_key = js_mkstr(js, "prototype", 9); 2998 2969 if (is_err(prototype_key)) return prototype_key; 2999 2970 3000 2971 res = setprop(js, func, prototype_key, proto_obj); 3001 2972 if (is_err(res)) return res; 3002 - 3003 - jsval_t proto_desc_key = js_mkstr(js, "__desc_prototype", 16); 3004 - if (is_err(proto_desc_key)) return proto_desc_key; 3005 - 3006 - jsval_t proto_desc_obj = js_mkobj(js); 3007 - if (is_err(proto_desc_obj)) return proto_desc_obj; 3008 - 3009 - setprop(js, proto_desc_obj, js_mkstr(js, "writable", 8), js_mktrue()); 3010 - setprop(js, proto_desc_obj, js_mkstr(js, "enumerable", 10), js_mkfalse()); 3011 - setprop(js, proto_desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 3012 - setprop(js, func, proto_desc_key, proto_desc_obj); 2973 + js_set_descriptor(js, func, "prototype", 9, JS_DESC_W); 3013 2974 3014 2975 return js_mkundef(); 3015 2976 } ··· 3044 3005 return v; 3045 3006 } 3046 3007 3008 + if (try_dynamic_setter(js, obj, key, klen, v)) { 3009 + return v; 3010 + } 3011 + 3047 3012 jsoff_t existing = lkp(js, obj, key, klen); 3048 3013 3049 - if (js->has_descriptors) { 3014 + { 3050 3015 jsoff_t obj_off = (jsoff_t)vdata(obj); 3051 - uint32_t hash = (uint32_t)(hash_key(key, klen) ^ obj_off); 3052 - uint32_t slot = hash & (DESC_CACHE_SIZE - 1); 3016 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 3053 3017 3054 - jsoff_t desc_off; 3055 - if (desc_cache[slot].valid && 3056 - desc_cache[slot].obj_off == obj_off && 3057 - desc_cache[slot].key_hash == hash) { 3058 - desc_off = desc_cache[slot].desc_off; 3059 - } else { 3060 - char desc_key[128]; 3061 - size_t desc_key_len = build_desc_key(desc_key, sizeof(desc_key), key, klen); 3062 - desc_off = 0; 3063 - if (desc_key_len > 0) { 3064 - desc_off = lkp(js, obj, desc_key, desc_key_len); 3065 - if (desc_off == 0) desc_off = lkp_proto(js, obj, desc_key, desc_key_len); 3018 + if (!desc) goto no_descriptor; 3019 + 3020 + if (desc->has_setter) { 3021 + jsval_t setter = desc->setter; 3022 + uint8_t setter_type = vtype(setter); 3023 + if (setter_type == T_FUNC || setter_type == T_CFUNC) { 3024 + js_parse_state_t saved; 3025 + JS_SAVE_STATE(js, saved); 3026 + uint8_t saved_flags = js->flags; 3027 + jsoff_t saved_toff = js->toff; 3028 + jsoff_t saved_tlen = js->tlen; 3029 + 3030 + jsval_t saved_this = js->this_val; 3031 + js->this_val = obj; 3032 + push_this(obj); 3033 + jsval_t result = call_js_with_args(js, setter, &v, 1); 3034 + pop_this(); 3035 + js->this_val = saved_this; 3036 + 3037 + JS_RESTORE_STATE(js, saved); 3038 + js->flags = saved_flags; 3039 + js->toff = saved_toff; 3040 + js->tlen = saved_tlen; 3041 + 3042 + if (is_err(result)) return result; 3043 + return v; 3066 3044 } 3067 - desc_cache[slot].obj_off = obj_off; 3068 - desc_cache[slot].key_hash = hash; 3069 - desc_cache[slot].desc_off = desc_off; 3070 - desc_cache[slot].valid = 1; 3071 3045 } 3072 3046 3073 - if (desc_off == 0) goto no_descriptor; 3074 - 3075 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 3076 - if (vtype(desc_obj) != T_OBJ) goto no_descriptor; 3077 - 3078 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 3079 - if (set_off == 0) goto check_getter_only; 3080 - 3081 - jsval_t setter = resolveprop(js, mkval(T_PROP, set_off)); 3082 - uint8_t setter_type = vtype(setter); 3083 - if (setter_type != T_FUNC && setter_type != T_CFUNC) goto check_getter_only; 3084 - 3085 - js_parse_state_t saved; 3086 - JS_SAVE_STATE(js, saved); 3087 - uint8_t saved_flags = js->flags; 3088 - jsoff_t saved_toff = js->toff; 3089 - jsoff_t saved_tlen = js->tlen; 3090 - 3091 - jsval_t saved_this = js->this_val; 3092 - js->this_val = obj; 3093 - push_this(obj); 3094 - jsval_t result = call_js_with_args(js, setter, &v, 1); 3095 - pop_this(); 3096 - js->this_val = saved_this; 3097 - 3098 - JS_RESTORE_STATE(js, saved); 3099 - js->flags = saved_flags; 3100 - js->toff = saved_toff; 3101 - js->tlen = saved_tlen; 3102 - 3103 - if (is_err(result)) return result; 3104 - return v; 3105 - 3106 - check_getter_only:; 3107 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 3108 - if (get_off != 0 && set_off == 0) { 3047 + if (desc->has_getter && !desc->has_setter) { 3109 3048 if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property which has only a getter"); 3110 3049 return v; 3111 3050 } 3112 3051 3113 3052 if (existing <= 0) goto no_descriptor; 3114 3053 3115 - jsoff_t writable_off = lkp(js, desc_obj, "writable", 8); 3116 - if (writable_off == 0) goto no_descriptor; 3117 - 3118 - jsval_t writable_val = resolveprop(js, mkval(T_PROP, writable_off)); 3119 - if (js_truthy(js, writable_val)) goto no_descriptor; 3120 - 3121 - if (js->flags & F_STRICT) return js_mkerr(js, "assignment to read-only property"); 3122 - return mkval(T_PROP, existing); 3054 + if (!desc->writable) { 3055 + if (js->flags & F_STRICT) return js_mkerr(js, "assignment to read-only property"); 3056 + return mkval(T_PROP, existing); 3057 + } 3123 3058 } 3124 3059 3125 3060 no_descriptor: ··· 3216 3151 return js_setprop(js, obj, k, v); 3217 3152 } 3218 3153 3154 + static jsval_t setprop_const(struct js *js, jsval_t obj, const char *key, size_t len, jsval_t v) { 3155 + jsval_t k = js_mkstr(js, key, len); 3156 + if (is_err(k)) return k; 3157 + return mkprop(js, obj, k, v, true); 3158 + } 3159 + 3219 3160 static jsval_t setprop_interned(struct js *js, jsval_t obj, const char *key, size_t len, jsval_t v) { 3220 3161 jsval_t k = js_mkstr(js, key, len); 3221 3162 if (is_err(k)) return k; ··· 3228 3169 jsval_t result = setprop(js, obj, k, v); 3229 3170 if (is_err(result)) return result; 3230 3171 3231 - char desc_key[80]; 3232 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)keylen, key); 3233 - jsval_t desc_obj = mkobj(js, 0); 3234 - if (is_err(desc_obj)) return desc_obj; 3235 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 3236 - setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mktrue()); 3237 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mkfalse()); 3238 - setprop(js, obj, js_mkstr(js, desc_key, strlen(desc_key)), desc_obj); 3172 + js_set_descriptor(js, obj, key, keylen, JS_DESC_W); 3239 3173 3240 3174 return result; 3241 3175 } ··· 4382 4316 } 4383 4317 4384 4318 static jsval_t try_dynamic_getter(struct js *js, jsval_t obj, const char *key, size_t key_len) { 4385 - if (streq(key, key_len, "__getter", 8)) return js_mkundef(); 4386 - 4387 - jsoff_t getter_off = lkp(js, obj, "__getter", 8); 4388 - if (getter_off == 0) return js_mkundef(); 4389 - 4390 - jsval_t getter_val = resolveprop(js, mkval(T_PROP, getter_off)); 4391 - if (vtype(getter_val) != T_CFUNC) return js_mkundef(); 4392 - 4393 - js_getter_fn getter = (js_getter_fn)(void *)vdata(getter_val); 4394 - jsval_t result = getter(js, obj, key, key_len); 4395 - 4396 - if (vtype(result) != T_UNDEF) { 4397 - jsval_t key_str = js_mkstr(js, key, key_len); 4398 - setprop(js, obj, key_str, result); 4399 - } 4400 - 4401 - return result; 4319 + jsoff_t obj_off = (jsoff_t)vdata(obj); 4320 + dynamic_accessors_t *entry = NULL; 4321 + HASH_FIND(hh, accessor_registry, &obj_off, sizeof(jsoff_t), entry); 4322 + if (!entry || !entry->getter) return js_mkundef(); 4323 + return entry->getter(js, obj, key, key_len); 4324 + } 4325 + 4326 + static bool try_dynamic_setter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t value) { 4327 + jsoff_t obj_off = (jsoff_t)vdata(obj); 4328 + dynamic_accessors_t *entry = NULL; 4329 + HASH_FIND(hh, accessor_registry, &obj_off, sizeof(jsoff_t), entry); 4330 + if (!entry || !entry->setter) return false; 4331 + return entry->setter(js, obj, key, key_len, value); 4402 4332 } 4403 4333 4404 4334 static jsval_t lookup(struct js *js, const char *buf, size_t len) { ··· 4452 4382 } 4453 4383 4454 4384 static bool try_accessor_getter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t *out) { 4455 - char desc_key[128]; 4456 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)key_len, key); 4457 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 4458 - 4459 - if (desc_off == 0) { 4460 - desc_off = lkp_proto(js, obj, desc_key, strlen(desc_key)); 4461 - } 4462 - 4463 - if (desc_off == 0) return false; 4464 - 4465 - jsval_t desc_obj = loadval(js, desc_off + sizeof(jsoff_t) * 2); 4466 - if (vtype(desc_obj) != T_OBJ) return false; 4385 + jsoff_t obj_off = (jsoff_t)vdata(obj); 4386 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, key_len); 4467 4387 4468 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 4469 - if (get_off == 0) return false; 4388 + if (!desc || !desc->has_getter) return false; 4470 4389 4471 - jsval_t getter = loadval(js, get_off + sizeof(jsoff_t) * 2); 4390 + jsval_t getter = desc->getter; 4472 4391 if (vtype(getter) != T_FUNC && vtype(getter) != T_CFUNC) return false; 4473 4392 4474 4393 js_parse_state_t saved; ··· 4517 4436 jsoff_t prop_off = lkp(js, obj, key_str, len); 4518 4437 if (prop_off != 0) return resolveprop(js, mkval(T_PROP, prop_off)); 4519 4438 4439 + jsval_t dyn_result = try_dynamic_getter(js, obj, key_str, len); 4440 + if (vtype(dyn_result) != T_UNDEF) return dyn_result; 4441 + 4520 4442 jsoff_t proto_off = lkp_proto(js, obj, key_str, len); 4521 4443 if (proto_off != 0) return resolveprop(js, mkval(T_PROP, proto_off)); 4522 4444 ··· 4527 4449 } 4528 4450 4529 4451 static int check_prop_writable(struct js *js, jsval_t owner, const char *key, jsoff_t klen) { 4530 - char desc_key[128]; 4531 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 4532 - 4533 - jsoff_t desc_off = lkp(js, owner, desc_key, strlen(desc_key)); 4534 - if (desc_off == 0) return -1; 4535 - 4536 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 4537 - if (vtype(desc_obj) != T_OBJ) return -1; 4538 - 4539 - jsoff_t writable_off = lkp(js, desc_obj, "writable", 8); 4540 - if (writable_off == 0) return -1; 4541 - 4542 - jsval_t writable_val = resolveprop(js, mkval(T_PROP, writable_off)); 4543 - return js_truthy(js, writable_val) ? 1 : 0; 4544 - } 4545 - 4546 - static int find_owner_and_check_writable(struct js *js, jsoff_t propoff, const char *key, jsoff_t klen, jsval_t *out_owner) { 4547 - for (jsoff_t scan_off = 0; scan_off < propoff; ) { 4548 - jsoff_t header = loadoff(js, scan_off); 4549 - jsoff_t cleaned = header & ~(GCMASK | CONSTMASK | ARRMASK); 4550 - 4551 - if ((cleaned & 3) != T_OBJ) goto next_scan; 4552 - 4553 - for (jsoff_t next = cleaned & ~3U; next < js->brk && next != 0; next = loadoff(js, next) & ~(3U | CONSTMASK | ARRMASK)) { 4554 - if (next != propoff) continue; 4555 - 4556 - jsval_t obj = mkval(T_OBJ, scan_off); 4557 - *out_owner = obj; 4558 - 4559 - char desc_key[128]; 4560 - size_t desc_key_len = build_desc_key(desc_key, sizeof(desc_key), key, klen); 4561 - if (desc_key_len == 0) return 1; 4562 - 4563 - jsoff_t desc_off = lkp(js, obj, desc_key, desc_key_len); 4564 - if (desc_off == 0) return 1; 4565 - 4566 - jsval_t desc_obj = loadval(js, desc_off + sizeof(jsoff_t) * 2); 4567 - if (vtype(desc_obj) != T_OBJ) return 1; 4568 - 4569 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 4570 - if (set_off != 0) { 4571 - jsval_t setter = loadval(js, set_off + sizeof(jsoff_t) * 2); 4572 - uint8_t st = vtype(setter); 4573 - if (st == T_FUNC || st == T_CFUNC) return 2; 4574 - } 4575 - 4576 - jsoff_t writable_off = lkp(js, desc_obj, "writable", 8); 4577 - if (writable_off == 0) return 1; 4578 - 4579 - jsval_t writable_val = resolveprop(js, mkval(T_PROP, writable_off)); 4580 - return js_truthy(js, writable_val) ? 1 : 0; 4581 - } 4582 - 4583 - next_scan: 4584 - scan_off += esize(cleaned); 4585 - } 4586 - return -1; 4452 + jsoff_t obj_off = (jsoff_t)vdata(owner); 4453 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 4454 + if (!desc) return -1; 4455 + return desc->writable ? 1 : 0; 4587 4456 } 4588 4457 4589 4458 static bool try_accessor_setter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t val, jsval_t *out) { 4590 - char desc_key[128]; 4591 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)key_len, key); 4592 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 4593 - 4594 - if (desc_off == 0) { 4595 - desc_off = lkp_proto(js, obj, desc_key, strlen(desc_key)); 4596 - } 4597 - 4598 - if (desc_off == 0) return false; 4599 - 4600 - jsval_t desc_obj = loadval(js, desc_off + sizeof(jsoff_t) * 2); 4601 - if (vtype(desc_obj) != T_OBJ) return false; 4459 + jsoff_t obj_off = (jsoff_t)vdata(obj); 4460 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, key_len); 4602 4461 4603 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 4604 - if (set_off == 0) return false; 4462 + if (!desc || !desc->has_setter) return false; 4605 4463 4606 - jsval_t setter = loadval(js, set_off + sizeof(jsoff_t) * 2); 4464 + jsval_t setter = desc->setter; 4607 4465 if (vtype(setter) != T_FUNC && vtype(setter) != T_CFUNC) return false; 4608 4466 4609 4467 js_parse_state_t saved; ··· 4643 4501 jsoff_t klen = offtolen(loadoff(js, koff)); 4644 4502 const char *key = (char *)&js->mem[koff + sizeof(jsoff_t)]; 4645 4503 4646 - if (klen == 6 && memcmp(key, "length", 6) == 0) { 4647 - jsoff_t obj_off = 0; 4648 - for (jsoff_t scan = 0; scan < propoff; ) { 4649 - jsoff_t header = loadoff(js, scan); 4650 - jsoff_t cleaned = header & ~(GCMASK | CONSTMASK | ARRMASK); 4651 - if ((cleaned & 3U) == T_OBJ || (header & ARRMASK)) { 4652 - jsoff_t first_prop = cleaned & ~3U; 4653 - jsoff_t p = first_prop; 4654 - while (p != 0 && p < js->brk) { 4655 - if (p == propoff) { obj_off = scan; break; } 4656 - p = loadoff(js, p) & ~(3U | GCMASK | CONSTMASK); 4657 - } 4658 - if (obj_off != 0) break; 4659 - } 4660 - jsoff_t sz = esize(cleaned); 4661 - if (sz == (jsoff_t)~0U) break; 4662 - scan += sz; 4663 - } 4664 - if (obj_off != 0 && is_arr_off(js, obj_off)) { 4665 - jsval_t err = validate_array_length(js, val); 4666 - if (is_err(err)) return err; 4667 - } 4668 - } 4669 - 4670 4504 if (is_const_prop(js, propoff)) { 4671 4505 if (js->flags & F_STRICT) return js_mkerr(js, "assignment to constant"); 4672 4506 return mkval(T_PROP, propoff); ··· 4679 4513 return lhs; 4680 4514 } 4681 4515 4682 - if (js->has_descriptors) { 4683 - jsval_t owner = js_mkundef(); 4684 - int check_result = find_owner_and_check_writable(js, propoff, key, klen, &owner); 4685 - 4686 - if (check_result == 2) { 4687 - jsval_t setter_result; 4688 - if (try_accessor_setter(js, owner, key, klen, val, &setter_result)) { 4689 - return setter_result; 4690 - } 4691 - } else if (check_result == 0) { 4692 - if (js->flags & F_STRICT) return js_mkerr(js, "Cannot assign to read only property"); 4693 - return lhs; 4694 - } 4695 - } 4696 - 4697 4516 saveval(js, (jsoff_t) ((vdata(lhs) & ~3U) + sizeof(jsoff_t) * 2), val); 4698 4517 return lhs; 4699 4518 } ··· 4804 4623 const char *keystr; 4805 4624 size_t keylen; 4806 4625 if (vtype(key_val) == T_NUM) { 4807 - keylen = strnum(key_val, keybuf, sizeof(keybuf)); 4626 + double dv = tod(key_val); 4627 + if (dv >= 0 && dv <= 0xFFFFFFFF && dv == (double)(uint32_t)dv) { 4628 + keylen = uint_to_str(keybuf, sizeof(keybuf), (uint32_t)dv); 4629 + } else { 4630 + keylen = strnum(key_val, keybuf, sizeof(keybuf)); 4631 + } 4808 4632 keystr = keybuf; 4809 4633 } else if (vtype(key_val) == T_STR) { 4810 4634 jsoff_t slen; ··· 4862 4686 4863 4687 jsoff_t own_off = lkp(js, obj, keystr, keylen); 4864 4688 if (own_off != 0) { 4865 - char desc_key[128]; 4866 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)keylen, keystr); 4867 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 4868 - if (desc_off != 0) { 4869 - jsval_t desc_obj = loadval(js, desc_off + sizeof(jsoff_t) * 2); 4870 - if (vtype(desc_obj) == T_OBJ) { 4871 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 4872 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 4873 - if (get_off != 0 || set_off != 0) { 4874 - jsval_t key = js_mkstr(js, keystr, keylen); 4875 - return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(key)); 4876 - } 4877 - } 4689 + jsoff_t obj_off = (jsoff_t)vdata(obj); 4690 + descriptor_entry_t *desc = lookup_descriptor(obj_off, keystr, keylen); 4691 + if (desc && (desc->has_getter || desc->has_setter)) { 4692 + jsval_t key = js_mkstr(js, keystr, keylen); 4693 + return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(key)); 4878 4694 } 4879 4695 return mkval(T_PROP, own_off); 4880 4696 } ··· 4886 4702 4887 4703 jsval_t dyn_result = try_dynamic_getter(js, obj, keystr, keylen); 4888 4704 if (vtype(dyn_result) != T_UNDEF) { 4889 - jsoff_t dyn_off = lkp(js, obj, keystr, keylen); 4890 - if (dyn_off != 0) return mkval(T_PROP, dyn_off); 4705 + jsval_t key = js_mkstr(js, keystr, keylen); 4706 + return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(key)); 4891 4707 } 4892 4708 4893 4709 jsoff_t off = lkp_proto(js, obj, keystr, keylen); ··· 4954 4770 } 4955 4771 jsval_t func_obj = mkval(T_OBJ, vdata(l)); 4956 4772 jsoff_t off = lkp_proto(js, l, ptr, plen); 4957 - if (off != 0) return mkval(T_PROP, off); 4773 + if (off != 0) { 4774 + jsoff_t obj_off = (jsoff_t)vdata(l); 4775 + descriptor_entry_t *desc = lookup_descriptor(obj_off, ptr, plen); 4776 + if (desc) { 4777 + jsval_t key = js_mkstr(js, ptr, plen); 4778 + return mkpropref(obj_off, (jsoff_t)vdata(key)); 4779 + } 4780 + return mkval(T_PROP, off); 4781 + } 4958 4782 if (streq(ptr, plen, "name", 4)) return js_mkstr(js, "", 0); 4959 4783 jsval_t key = js_mkstr(js, ptr, plen); 4960 4784 jsval_t prop = setprop(js, func_obj, key, js_mkundef()); ··· 5002 4826 5003 4827 jsoff_t own_off = lkp(js, l, ptr, plen); 5004 4828 if (own_off != 0) { 5005 - char desc_key[128]; 5006 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)plen, ptr); 5007 - jsoff_t desc_off = lkp(js, l, desc_key, strlen(desc_key)); 5008 - if (desc_off != 0) { 5009 - jsval_t desc_obj = loadval(js, desc_off + sizeof(jsoff_t) * 2); 5010 - if (vtype(desc_obj) == T_OBJ) { 5011 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 5012 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 5013 - if (get_off != 0 || set_off != 0) { 5014 - jsval_t key = js_mkstr(js, ptr, plen); 5015 - return mkpropref((jsoff_t)vdata(l), (jsoff_t)vdata(key)); 5016 - } 5017 - } 4829 + jsoff_t obj_off = (jsoff_t)vdata(l); 4830 + descriptor_entry_t *desc = lookup_descriptor(obj_off, ptr, plen); 4831 + if (desc) { 4832 + jsval_t key = js_mkstr(js, ptr, plen); 4833 + return mkpropref((jsoff_t)vdata(l), (jsoff_t)vdata(key)); 5018 4834 } 5019 4835 return mkval(T_PROP, own_off); 5020 - } 5021 - 5022 - char desc_key[128]; 5023 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)plen, ptr); 5024 - jsoff_t proto_desc_off = lkp_proto(js, l, desc_key, strlen(desc_key)); 5025 - if (proto_desc_off != 0) { 5026 - jsval_t desc_obj = loadval(js, proto_desc_off + sizeof(jsoff_t) * 2); 5027 - if (vtype(desc_obj) == T_OBJ) { 5028 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 5029 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 5030 - if (get_off != 0 || set_off != 0) { 5031 - jsval_t key = js_mkstr(js, ptr, plen); 5032 - return mkpropref((jsoff_t)vdata(l), (jsoff_t)vdata(key)); 5033 - } 5034 - } 5035 4836 } 5036 4837 5037 4838 jsval_t result = try_dynamic_getter(js, l, ptr, plen); ··· 7233 7034 jsval_t val = mkval(T_FUNC, (unsigned long) vdata(func_obj)); 7234 7035 7235 7036 if (is_getter || is_setter) { 7236 - js->has_descriptors = true; 7237 - invalidate_desc_cache_for_obj((jsoff_t)vdata(obj)); 7238 7037 jsoff_t key_len; 7239 7038 const char *key_str = NULL; 7240 7039 if (vtype(key) == T_STR) { ··· 7242 7041 key_str = (char *)&js->mem[key_off]; 7243 7042 } 7244 7043 7245 - char desc_key[128]; 7246 7044 if (key_str) { 7247 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)key_len, key_str); 7248 - } else { 7249 - snprintf(desc_key, sizeof(desc_key), "__desc_computed"); 7045 + if (is_getter) { 7046 + js_set_getter_desc(js, obj, key_str, key_len, val, JS_DESC_E | JS_DESC_C); 7047 + } else { 7048 + js_set_setter_desc(js, obj, key_str, key_len, val, JS_DESC_E | JS_DESC_C); 7049 + } 7250 7050 } 7251 - 7252 - jsoff_t existing_desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 7253 - jsval_t desc_obj; 7254 - if (existing_desc_off != 0) { 7255 - desc_obj = resolveprop(js, mkval(T_PROP, existing_desc_off)); 7256 - } else { 7257 - desc_obj = mkobj(js, 0); 7258 - if (is_err(desc_obj)) return desc_obj; 7259 - } 7260 - 7261 - if (is_getter) { 7262 - setprop(js, desc_obj, js_mkstr(js, "get", 3), val); 7263 - } else { 7264 - setprop(js, desc_obj, js_mkstr(js, "set", 3), val); 7265 - } 7266 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mktrue()); 7267 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mktrue()); 7268 - 7269 - jsval_t desc_key_str = js_mkstr(js, desc_key, strlen(desc_key)); 7270 - setprop(js, obj, desc_key_str, desc_obj); 7271 7051 } else { 7272 7052 jsval_t res = setprop(js, obj, key, val); 7273 7053 if (is_err(res)) return res; ··· 7500 7280 if (is_err(len_key)) return len_key; 7501 7281 jsval_t res_len = setprop(js, func_obj, len_key, tov(param_count)); 7502 7282 if (is_err(res_len)) return res_len; 7283 + js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 7503 7284 7504 7285 if (is_async) { 7505 7286 jsval_t async_key = js_mkstr(js, "__async", 7); ··· 8123 7904 return js_mkfalse(); 8124 7905 } 8125 7906 8126 - char desc_key[80]; 8127 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)len, key_str); 8128 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 8129 - if (desc_off != 0) { 8130 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 8131 - if (vtype(desc_obj) == T_OBJ) { 8132 - jsoff_t config_off = lkp(js, desc_obj, "configurable", 12); 8133 - if (config_off != 0) { 8134 - jsval_t config_val = resolveprop(js, mkval(T_PROP, config_off)); 8135 - if (!js_truthy(js, config_val)) { 8136 - if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 8137 - return js_mkfalse(); 8138 - } 8139 - } 8140 - } 7907 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key_str, len); 7908 + if (desc && !desc->configurable) { 7909 + if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 7910 + return js_mkfalse(); 8141 7911 } 8142 7912 jsoff_t first_prop = loadoff(js, obj_off) & ~(3U | CONSTMASK | GCMASK); 8143 7913 if (first_prop == prop_off) { ··· 8231 8001 jsoff_t key_str_off = loadoff(js, (jsoff_t)(prop_off + sizeof(jsoff_t))); 8232 8002 jsoff_t key_len = (loadoff(js, key_str_off) >> 2) - 1; 8233 8003 const char *key_str = (char *)&js->mem[key_str_off + sizeof(jsoff_t)]; 8234 - char desc_key[80]; 8235 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)key_len, key_str); 8236 - jsoff_t desc_off = lkp(js, owner_obj, desc_key, strlen(desc_key)); 8237 - if (desc_off == 0) goto delete_prop; 8238 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 8239 - if (vtype(desc_obj) != T_OBJ) goto delete_prop; 8240 - jsoff_t config_off = lkp(js, desc_obj, "configurable", 12); 8241 - if (config_off == 0) goto delete_prop; 8242 - jsval_t config_val = resolveprop(js, mkval(T_PROP, config_off)); 8243 - if (js_truthy(js, config_val)) goto delete_prop; 8244 - if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 8245 - return js_mkfalse(); 8246 - 8247 - delete_prop: 8004 + descriptor_entry_t *desc = lookup_descriptor(owner_obj_off, key_str, key_len); 8005 + if (desc && !desc->configurable) { 8006 + if (js->flags & F_STRICT) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); 8007 + return js_mkfalse(); 8008 + } 8248 8009 8249 8010 if (is_first_prop) { 8250 8011 jsoff_t deleted_next = loadoff(js, prop_off) & ~(GCMASK | CONSTMASK); ··· 9120 8881 if (is_err(len_key)) return len_key; 9121 8882 jsval_t res_len = setprop(js, func_obj, len_key, tov(param_count)); 9122 8883 if (is_err(res_len)) return res_len; 8884 + js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 9123 8885 jsval_t name_key = js_mkstr(js, "name", 4); 9124 8886 if (is_err(name_key)) return name_key; 9125 8887 jsval_t name_val = js_mkstr(js, name, nlen); ··· 9185 8947 if (is_err(async_key)) return async_key; 9186 8948 jsval_t res_async = setprop(js, func_obj, async_key, js_mktrue()); 9187 8949 if (is_err(res_async)) return res_async; 8950 + jsval_t len_key = js_mkstr(js, "length", 6); 8951 + if (is_err(len_key)) return len_key; 8952 + jsval_t res_len = setprop(js, func_obj, len_key, tov(0)); 8953 + if (is_err(res_len)) return res_len; 8954 + js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 9188 8955 jsval_t name_key = js_mkstr(js, "name", 4); 9189 8956 if (is_err(name_key)) return name_key; 9190 8957 jsval_t name_val = js_mkstr(js, name, nlen); ··· 9306 9073 static bool for_in_should_skip_key(struct js *js, jsval_t obj, const char *key, jsoff_t klen) { 9307 9074 if (streq(key, klen, "__proto__", 9)) return true; 9308 9075 9309 - if ( 9310 - klen > 7 && key[0] == '_' && 9311 - key[1] == '_' && key[2] == 'd' 9312 - && key[3] == 'e' && key[4] == 's' 9313 - && key[5] == 'c' && key[6] == '_' 9314 - ) return true; 9315 - 9316 - char desc_key[128]; 9317 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 9318 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 9319 - if (desc_off == 0) return false; 9320 - 9321 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 9322 - if (vtype(desc_obj) != T_OBJ) return false; 9076 + jsoff_t obj_off = (jsoff_t)vdata(obj); 9077 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 9078 + if (desc && !desc->enumerable) return true; 9323 9079 9324 - jsoff_t enumerable_off = lkp(js, desc_obj, "enumerable", 10); 9325 - if (enumerable_off == 0) return false; 9326 - 9327 - jsval_t enumerable_val = resolveprop(js, mkval(T_PROP, enumerable_off)); 9328 - return !js_truthy(js, enumerable_val); 9080 + return false; 9329 9081 } 9330 9082 9331 9083 static jsval_t for_in_iter_object(struct js *js, for_iter_ctx_t *ctx, jsval_t obj) { ··· 9627 9379 } 9628 9380 9629 9381 if (!should_skip) { 9630 - char desc_key[128]; 9631 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 9632 - jsoff_t desc_off = lkp(js, iter_obj, desc_key, strlen(desc_key)); 9633 - if (desc_off != 0) { 9634 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 9635 - if (vtype(desc_obj) == T_OBJ) { 9636 - jsoff_t enumerable_off = lkp(js, desc_obj, "enumerable", 10); 9637 - if (enumerable_off != 0) { 9638 - jsval_t enumerable_val = resolveprop(js, mkval(T_PROP, enumerable_off)); 9639 - if (!js_truthy(js, enumerable_val)) { 9640 - should_skip = true; 9641 - } 9642 - } 9643 - } 9382 + jsoff_t iter_obj_off = (jsoff_t)vdata(iter_obj); 9383 + descriptor_entry_t *desc = lookup_descriptor(iter_obj_off, key, klen); 9384 + if (desc && !desc->enumerable) { 9385 + should_skip = true; 9644 9386 } 9645 9387 } 9646 9388 ··· 10926 10668 jsval_t method_func = mkval(T_FUNC, (unsigned long) vdata(method_obj)); 10927 10669 10928 10670 if (methods[i].is_getter || methods[i].is_setter) { 10929 - char desc_key[128]; 10930 10671 jsoff_t name_len; 10931 10672 const char *name_str = (const char *)&js->mem[vstr(js, method_name, &name_len)]; 10932 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)name_len, name_str); 10933 - 10934 - jsoff_t desc_off = lkp(js, proto, desc_key, strlen(desc_key)); 10935 - jsval_t desc_obj; 10936 - 10937 - if (desc_off != 0) { 10938 - desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 10939 - } else { 10940 - desc_obj = mkobj(js, 0); 10941 - if (is_err(desc_obj)) return desc_obj; 10942 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mkfalse()); 10943 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mktrue()); 10944 - setprop(js, proto, js_mkstr(js, desc_key, strlen(desc_key)), desc_obj); 10945 - } 10946 10673 10947 10674 if (methods[i].is_getter) { 10948 - setprop(js, desc_obj, js_mkstr(js, "get", 3), method_func); 10675 + js_set_getter_desc(js, proto, name_str, name_len, method_func, JS_DESC_C); 10949 10676 } else { 10950 - setprop(js, desc_obj, js_mkstr(js, "set", 3), method_func); 10677 + js_set_setter_desc(js, proto, name_str, name_len, method_func, JS_DESC_C); 10951 10678 } 10952 10679 } else { 10953 10680 jsval_t set_res = setprop(js, proto, method_name, method_func); ··· 13263 12990 if (is_internal_prop(key, klen)) continue; 13264 12991 13265 12992 bool should_include = true; 13266 - char desc_key[128]; 13267 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 13268 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 12993 + jsoff_t obj_off = (jsoff_t)vdata(obj); 12994 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 13269 12995 13270 - if (desc_off != 0) { 13271 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 13272 - if (vtype(desc_obj) == T_OBJ) { 13273 - jsoff_t get_off = lkp_interned(js, desc_obj, INTERN_GET, 3); 13274 - jsoff_t set_off = lkp_interned(js, desc_obj, INTERN_SET, 3); 13275 - if (get_off != 0 || set_off != 0) { 13276 - jsval_t get_val = get_off ? resolveprop(js, mkval(T_PROP, get_off)) : js_mkundef(); 13277 - jsval_t set_val = set_off ? resolveprop(js, mkval(T_PROP, set_off)) : js_mkundef(); 13278 - if ((vtype(get_val) == T_FUNC || vtype(get_val) == T_CFUNC) || 13279 - (vtype(set_val) == T_FUNC || vtype(set_val) == T_CFUNC)) { 13280 - continue; 13281 - } 13282 - } 13283 - jsoff_t enum_off = lkp(js, desc_obj, "enumerable", 10); 13284 - if (enum_off != 0) { 13285 - jsval_t enum_val = resolveprop(js, mkval(T_PROP, enum_off)); 13286 - should_include = js_truthy(js, enum_val); 13287 - } 12996 + if (desc) { 12997 + if (desc->has_getter || desc->has_setter) { 12998 + continue; 13288 12999 } 13000 + should_include = desc->enumerable; 13289 13001 } 13290 13002 13291 13003 if (should_include) { ··· 13387 13099 if (is_internal_prop(key, klen)) continue; 13388 13100 13389 13101 bool should_include = true; 13390 - char desc_key[128]; 13391 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 13392 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 13393 - 13394 - if (desc_off != 0) { 13395 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 13396 - if (vtype(desc_obj) == T_OBJ) { 13397 - jsoff_t enum_off = lkp(js, desc_obj, "enumerable", 10); 13398 - if (enum_off != 0) { 13399 - jsval_t enum_val = resolveprop(js, mkval(T_PROP, enum_off)); 13400 - should_include = js_truthy(js, enum_val); 13401 - } 13402 - } 13102 + jsoff_t obj_off = (jsoff_t)vdata(obj); 13103 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 13104 + if (desc) { 13105 + should_include = desc->enumerable; 13403 13106 } 13404 13107 13405 13108 if (should_include) { ··· 13452 13155 if (is_internal_prop(key, klen)) continue; 13453 13156 13454 13157 bool should_include = true; 13455 - char desc_key[128]; 13456 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 13457 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 13458 - 13459 - if (desc_off != 0) { 13460 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 13461 - if (vtype(desc_obj) == T_OBJ) { 13462 - jsoff_t enum_off = lkp(js, desc_obj, "enumerable", 10); 13463 - if (enum_off != 0) { 13464 - jsval_t enum_val = resolveprop(js, mkval(T_PROP, enum_off)); 13465 - should_include = js_truthy(js, enum_val); 13466 - } 13467 - } 13158 + jsoff_t obj_off = (jsoff_t)vdata(obj); 13159 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, klen); 13160 + if (desc) { 13161 + should_include = desc->enumerable; 13468 13162 } 13469 13163 13470 13164 if (should_include) { ··· 13604 13298 13605 13299 static jsval_t builtin_object_defineProperty(struct js *js, jsval_t *args, int nargs) { 13606 13300 if (nargs < 3) return js_mkerr(js, "Object.defineProperty requires 3 arguments"); 13607 - js->has_descriptors = true; 13608 13301 13609 13302 jsval_t obj = args[0]; 13610 13303 jsval_t prop = args[1]; ··· 13615 13308 return js_mkerr(js, "Object.defineProperty called on non-object"); 13616 13309 } 13617 13310 13618 - jsval_t as_obj_tmp = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 13619 - invalidate_desc_cache_for_obj((jsoff_t)vdata(as_obj_tmp)); 13620 - 13621 - if (vtype(prop) != T_STR) { 13622 - return js_mkerr(js, "Property key must be a string"); 13311 + if (vtype(prop) == T_SYMBOL) { 13312 + char keybuf[64]; 13313 + snprintf(keybuf, sizeof(keybuf), "__sym_%llu__", (unsigned long long)sym_get_id(prop)); 13314 + prop = js_mkstr(js, keybuf, strlen(keybuf)); 13315 + } else if (vtype(prop) != T_STR) { 13316 + char buf[64]; 13317 + size_t len = tostr(js, prop, buf, sizeof(buf)); 13318 + prop = js_mkstr(js, buf, len); 13623 13319 } 13624 13320 13625 13321 if (vtype(descriptor) != T_OBJ) { ··· 13688 13384 13689 13385 jsoff_t existing_off = lkp(js, as_obj, prop_str, prop_len); 13690 13386 13691 - char desc_key[128]; 13692 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)prop_len, prop_str); 13693 - jsval_t desc_key_str = js_mkstr(js, desc_key, strlen(desc_key)); 13694 - jsval_t desc_obj = js_mkobj(js); 13695 - 13696 13387 if (has_get || has_set) { 13697 - if (has_get) { 13388 + int desc_flags = 13389 + (enumerable ? JS_DESC_E : 0) | 13390 + (configurable ? JS_DESC_C : 0); 13391 + 13392 + if (has_get && has_set) { 13393 + jsval_t getter = resolveprop(js, mkval(T_PROP, get_off)); 13394 + jsval_t setter = resolveprop(js, mkval(T_PROP, set_off)); 13395 + js_set_accessor_desc(js, as_obj, prop_str, prop_len, getter, setter, desc_flags); 13396 + } else if (has_get) { 13698 13397 jsval_t getter = resolveprop(js, mkval(T_PROP, get_off)); 13699 - setprop(js, desc_obj, js_mkstr(js, "get", 3), getter); 13700 - } 13701 - if (has_set) { 13398 + js_set_getter_desc(js, as_obj, prop_str, prop_len, getter, desc_flags); 13399 + } else { 13702 13400 jsval_t setter = resolveprop(js, mkval(T_PROP, set_off)); 13703 - setprop(js, desc_obj, js_mkstr(js, "set", 3), setter); 13401 + js_set_setter_desc(js, as_obj, prop_str, prop_len, setter, desc_flags); 13704 13402 } 13705 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), mkval(T_BOOL, enumerable ? 1 : 0)); 13706 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), mkval(T_BOOL, configurable ? 1 : 0)); 13707 - setprop(js, as_obj, desc_key_str, desc_obj); 13708 13403 13709 13404 if (existing_off == 0) { 13710 13405 jsval_t prop_key = js_mkstr(js, prop_str, prop_len); 13711 13406 mkprop(js, as_obj, prop_key, js_mkundef(), !configurable); 13712 13407 } 13713 13408 } else { 13714 - setprop(js, desc_obj, js_mkstr(js, "writable", 8), mkval(T_BOOL, writable ? 1 : 0)); 13715 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), mkval(T_BOOL, enumerable ? 1 : 0)); 13716 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), mkval(T_BOOL, configurable ? 1 : 0)); 13717 - setprop(js, as_obj, desc_key_str, desc_obj); 13409 + int desc_flags = 13410 + (writable ? JS_DESC_W : 0) | 13411 + (enumerable ? JS_DESC_E : 0) | 13412 + (configurable ? JS_DESC_C : 0); 13413 + 13414 + js_set_descriptor(js, as_obj, prop_str, prop_len, desc_flags); 13718 13415 13719 13416 if (existing_off > 0) { 13720 13417 if (is_const_prop(js, existing_off)) { ··· 13819 13516 if (is_internal_prop(key, klen)) continue; 13820 13517 13821 13518 bool should_copy = true; 13822 - char desc_key[128]; 13823 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 13824 - jsoff_t desc_off = lkp(js, src_obj, desc_key, strlen(desc_key)); 13825 - 13826 - if (desc_off != 0) { 13827 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 13828 - if (vtype(desc_obj) == T_OBJ) { 13829 - jsoff_t enum_off = lkp(js, desc_obj, "enumerable", 10); 13830 - if (enum_off != 0) { 13831 - jsval_t enum_val = resolveprop(js, mkval(T_PROP, enum_off)); 13832 - should_copy = js_truthy(js, enum_val); 13833 - } 13834 - } 13519 + jsoff_t src_obj_off = (jsoff_t)vdata(src_obj); 13520 + descriptor_entry_t *desc = lookup_descriptor(src_obj_off, key, klen); 13521 + if (desc) { 13522 + should_copy = desc->enumerable; 13835 13523 } 13836 13524 13837 13525 if (should_copy) { ··· 13853 13541 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj; 13854 13542 jsval_t as_obj = (t == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); 13855 13543 13856 - js->has_descriptors = true; 13857 - invalidate_desc_cache_for_obj((jsoff_t)vdata(as_obj)); 13858 13544 jsoff_t next = loadoff(js, (jsoff_t) vdata(as_obj)) & ~(3U | CONSTMASK | ARRMASK); 13859 13545 13860 13546 while (next < js->brk && next != 0) { ··· 13885 13571 } 13886 13572 } 13887 13573 13888 - char desc_key[128]; 13889 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 13890 - jsoff_t desc_off = lkp(js, as_obj, desc_key, strlen(desc_key)); 13891 - if (desc_off != 0) { 13892 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 13893 - if (vtype(desc_obj) == T_OBJ) { 13894 - setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mkfalse()); 13895 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 13896 - } 13897 - } else { 13898 - jsval_t desc_key_str = js_mkstr(js, desc_key, strlen(desc_key)); 13899 - jsval_t desc_obj = js_mkobj(js); 13900 - setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mkfalse()); 13901 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mktrue()); 13902 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 13903 - setprop(js, as_obj, desc_key_str, desc_obj); 13904 - } 13574 + js_set_descriptor(js, as_obj, key, klen, JS_DESC_E); 13905 13575 } 13906 13576 13907 13577 setprop(js, as_obj, js_mkstr(js, "__frozen__", 10), js_mktrue()); ··· 13947 13617 13948 13618 if (is_internal_prop(key, klen)) continue; 13949 13619 13950 - char desc_key[128]; 13951 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)klen, key); 13952 - jsoff_t desc_off = lkp(js, as_obj, desc_key, strlen(desc_key)); 13953 - if (desc_off != 0) { 13954 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 13955 - if (vtype(desc_obj) == T_OBJ) { 13956 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 13957 - } 13958 - } else { 13959 - jsval_t desc_key_str = js_mkstr(js, desc_key, strlen(desc_key)); 13960 - jsval_t desc_obj = js_mkobj(js); 13961 - setprop(js, desc_obj, js_mkstr(js, "writable", 8), js_mktrue()); 13962 - setprop(js, desc_obj, js_mkstr(js, "enumerable", 10), js_mktrue()); 13963 - setprop(js, desc_obj, js_mkstr(js, "configurable", 12), js_mkfalse()); 13964 - setprop(js, as_obj, desc_key_str, desc_obj); 13965 - } 13620 + js_set_descriptor(js, as_obj, key, klen, JS_DESC_W | JS_DESC_E); 13966 13621 } 13967 13622 13968 13623 return obj; ··· 14058 13713 jsoff_t key_len, key_off = vstr(js, key, &key_len); 14059 13714 const char *key_str = (char *) &js->mem[key_off]; 14060 13715 14061 - char desc_key[128]; 14062 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)key_len, key_str); 14063 - jsoff_t desc_off = lkp(js, as_obj, desc_key, strlen(desc_key)); 13716 + jsoff_t obj_off = (jsoff_t)vdata(as_obj); 13717 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key_str, key_len); 13718 + 13719 + jsoff_t prop_off = lkp(js, as_obj, key_str, key_len); 13720 + if (prop_off == 0 && !desc) { 13721 + return js_mkundef(); 13722 + } 13723 + 13724 + jsval_t result = js_mkobj(js); 14064 13725 14065 - if (desc_off != 0) { 14066 - jsval_t desc = resolveprop(js, mkval(T_PROP, desc_off)); 14067 - if (vtype(desc) == T_OBJ) { 14068 - jsval_t result = js_mkobj(js); 14069 - 14070 - jsoff_t get_off = lkp_interned(js, desc, INTERN_GET, 3); 14071 - jsoff_t set_off = lkp_interned(js, desc, INTERN_SET, 3); 14072 - bool has_getter = false, has_setter = false; 14073 - 14074 - if (get_off != 0) { 14075 - jsval_t get_val = resolveprop(js, mkval(T_PROP, get_off)); 14076 - if (vtype(get_val) == T_FUNC || vtype(get_val) == T_CFUNC) { 14077 - setprop(js, result, js_mkstr(js, "get", 3), get_val); 14078 - has_getter = true; 14079 - } 14080 - } 14081 - if (set_off != 0) { 14082 - jsval_t set_val = resolveprop(js, mkval(T_PROP, set_off)); 14083 - if (vtype(set_val) == T_FUNC || vtype(set_val) == T_CFUNC) { 14084 - setprop(js, result, js_mkstr(js, "set", 3), set_val); 14085 - has_setter = true; 14086 - } 14087 - } 14088 - 14089 - if (!has_getter && !has_setter) { 14090 - jsoff_t prop_off = lkp(js, as_obj, key_str, key_len); 14091 - if (prop_off != 0) { 14092 - jsval_t prop_val = resolveprop(js, mkval(T_PROP, prop_off)); 14093 - setprop(js, result, js_mkstr(js, "value", 5), prop_val); 14094 - } 14095 - 14096 - jsoff_t writable_off = lkp(js, desc, "writable", 8); 14097 - if (writable_off != 0) { 14098 - setprop(js, result, js_mkstr(js, "writable", 8), resolveprop(js, mkval(T_PROP, writable_off))); 14099 - } 14100 - } 14101 - 14102 - jsoff_t enumerable_off = lkp(js, desc, "enumerable", 10); 14103 - if (enumerable_off != 0) { 14104 - setprop(js, result, js_mkstr(js, "enumerable", 10), resolveprop(js, mkval(T_PROP, enumerable_off))); 14105 - } 14106 - 14107 - jsoff_t configurable_off = lkp(js, desc, "configurable", 12); 14108 - if (configurable_off != 0) { 14109 - setprop(js, result, js_mkstr(js, "configurable", 12), resolveprop(js, mkval(T_PROP, configurable_off))); 14110 - } 14111 - 14112 - return result; 13726 + if (desc && (desc->has_getter || desc->has_setter)) { 13727 + if (desc->has_getter) { 13728 + setprop(js, result, js_mkstr(js, "get", 3), desc->getter); 13729 + } 13730 + if (desc->has_setter) { 13731 + setprop(js, result, js_mkstr(js, "set", 3), desc->setter); 13732 + } 13733 + setprop(js, result, js_mkstr(js, "enumerable", 10), desc->enumerable ? js_mktrue() : js_mkfalse()); 13734 + setprop(js, result, js_mkstr(js, "configurable", 12), desc->configurable ? js_mktrue() : js_mkfalse()); 13735 + } else { 13736 + if (prop_off != 0) { 13737 + jsval_t prop_val = resolveprop(js, mkval(T_PROP, prop_off)); 13738 + setprop(js, result, js_mkstr(js, "value", 5), prop_val); 14113 13739 } 13740 + setprop(js, result, js_mkstr(js, "writable", 8), desc ? (desc->writable ? js_mktrue() : js_mkfalse()) : js_mktrue()); 13741 + setprop(js, result, js_mkstr(js, "enumerable", 10), desc ? (desc->enumerable ? js_mktrue() : js_mkfalse()) : js_mktrue()); 13742 + setprop(js, result, js_mkstr(js, "configurable", 12), desc ? (desc->configurable ? js_mktrue() : js_mkfalse()) : js_mktrue()); 14114 13743 } 14115 13744 14116 - jsoff_t prop_off = lkp(js, as_obj, key_str, key_len); 14117 - if (prop_off != 0) { 14118 - jsval_t result = js_mkobj(js); 14119 - jsval_t prop_val = resolveprop(js, mkval(T_PROP, prop_off)); 14120 - setprop(js, result, js_mkstr(js, "value", 5), prop_val); 14121 - setprop(js, result, js_mkstr(js, "writable", 8), js_mktrue()); 14122 - setprop(js, result, js_mkstr(js, "enumerable", 10), js_mktrue()); 14123 - setprop(js, result, js_mkstr(js, "configurable", 12), js_mktrue()); 14124 - return result; 14125 - } 13745 + return result; 14126 13746 14127 13747 return js_mkundef(); 14128 13748 } ··· 14286 13906 jsoff_t off = lkp(js, as_obj, key_str, key_len); 14287 13907 if (off == 0) return mkval(T_BOOL, 0); 14288 13908 14289 - char desc_key[128]; 14290 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)key_len, key_str); 14291 - jsoff_t desc_off = lkp(js, as_obj, desc_key, strlen(desc_key)); 14292 - if (desc_off == 0) goto enumerable; 13909 + jsoff_t obj_off = (jsoff_t)vdata(as_obj); 13910 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key_str, key_len); 13911 + if (desc) { 13912 + return mkval(T_BOOL, desc->enumerable ? 1 : 0); 13913 + } 14293 13914 14294 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 14295 - if (vtype(desc_obj) != T_OBJ) goto enumerable; 14296 - 14297 - jsoff_t enumerable_off = lkp(js, desc_obj, "enumerable", 10); 14298 - if (enumerable_off == 0) goto enumerable; 14299 - 14300 - jsval_t enumerable_val = resolveprop(js, mkval(T_PROP, enumerable_off)); 14301 - return mkval(T_BOOL, js_truthy(js, enumerable_val) ? 1 : 0); 14302 - 14303 - enumerable: 14304 13915 return mkval(T_BOOL, 1); 14305 13916 } 14306 13917 ··· 20351 19962 js->lwm = js->size; 20352 19963 js->gct = js->size / 2; 20353 19964 js->this_val = js->scope; 20354 - js->has_descriptors = false; 20355 19965 js->errmsg_size = 4096; 20356 19966 js->errmsg = (char *)malloc(js->errmsg_size); 20357 19967 if (js->errmsg) js->errmsg[0] = '\0'; ··· 20619 20229 setprop(js, func_ctor_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_Function)); 20620 20230 setprop_nonconfigurable(js, func_ctor_obj, "prototype", 9, function_proto); 20621 20231 setprop(js, func_ctor_obj, js_mkstr(js, "length", 6), tov(1.0)); 20622 - 20623 - jsval_t func_len_desc = mkobj(js, 0); 20624 - setprop(js, func_len_desc, js_mkstr(js, "enumerable", 10), js_mkfalse()); 20625 - setprop(js, func_len_desc, js_mkstr(js, "configurable", 12), js_mktrue()); 20626 - setprop(js, func_ctor_obj, js_mkstr(js, "__desc_length", 13), func_len_desc); 20232 + js_set_descriptor(js, func_ctor_obj, "length", 6, JS_DESC_C); 20627 20233 setprop(js, glob, js_mkstr(js, "Function", 8), mkval(T_FUNC, vdata(func_ctor_obj))); 20628 20234 20629 20235 jsval_t str_ctor_obj = mkobj(js, 0); ··· 20668 20274 setprop(js, arr_ctor_obj, js_mkstr(js, "isArray", 7), js_mkfun(builtin_Array_isArray)); 20669 20275 setprop(js, arr_ctor_obj, js_mkstr(js, "from", 4), js_mkfun(builtin_Array_from)); 20670 20276 setprop(js, arr_ctor_obj, js_mkstr(js, "of", 2), js_mkfun(builtin_Array_of)); 20671 - setprop(js, arr_ctor_obj, js_mkstr(js, "length", 6), tov(1)); 20277 + setprop(js, arr_ctor_obj, js_mkstr(js, "length", 6), tov(1.0)); 20278 + js_set_descriptor(js, arr_ctor_obj, "length", 6, JS_DESC_C); 20672 20279 setprop(js, glob, js_mkstr(js, "Array", 5), mkval(T_FUNC, vdata(arr_ctor_obj))); 20673 20280 20674 20281 jsval_t map_ctor_obj = mkobj(js, 0); ··· 20965 20572 if (prop_off == 0) return true; 20966 20573 if (is_const_prop(js, prop_off)) return false; 20967 20574 20968 - char desc_key[80]; 20969 - snprintf(desc_key, sizeof(desc_key), "__desc_%.*s", (int)len, key); 20970 - jsoff_t desc_off = lkp(js, obj, desc_key, strlen(desc_key)); 20971 - if (desc_off != 0) { 20972 - jsval_t desc_obj = resolveprop(js, mkval(T_PROP, desc_off)); 20973 - if (vtype(desc_obj) == T_OBJ) { 20974 - jsoff_t config_off = lkp(js, desc_obj, "configurable", 12); 20975 - if (config_off != 0) { 20976 - jsval_t config_val = resolveprop(js, mkval(T_PROP, config_off)); 20977 - if (!js_truthy(js, config_val)) return false; 20978 - } 20979 - } 20980 - } 20575 + descriptor_entry_t *desc = lookup_descriptor(obj_off, key, len); 20576 + if (desc && !desc->configurable) return false; 20981 20577 20982 20578 jsoff_t first_prop = loadoff(js, obj_off) & ~(3U | CONSTMASK | GCMASK); 20983 20579 if (first_prop == prop_off) { ··· 21373 20969 21374 20970 void js_set_getter(struct js *js, jsval_t obj, js_getter_fn getter) { 21375 20971 if (vtype(obj) != T_OBJ) return; 21376 - jsval_t getter_val = mkval(T_CFUNC, (size_t)(void *)getter); 21377 - js_set(js, obj, "__getter", getter_val); 20972 + jsoff_t obj_off = (jsoff_t)vdata(obj); 20973 + dynamic_accessors_t *entry = NULL; 20974 + HASH_FIND(hh, accessor_registry, &obj_off, sizeof(jsoff_t), entry); 20975 + if (!entry) { 20976 + entry = (dynamic_accessors_t *)malloc(sizeof(dynamic_accessors_t)); 20977 + if (!entry) return; 20978 + entry->obj_offset = obj_off; 20979 + entry->getter = NULL; 20980 + entry->setter = NULL; 20981 + HASH_ADD(hh, accessor_registry, obj_offset, sizeof(jsoff_t), entry); 20982 + } 20983 + entry->getter = getter; 20984 + } 20985 + 20986 + void js_set_setter(struct js *js, jsval_t obj, js_setter_fn setter) { 20987 + if (vtype(obj) != T_OBJ) return; 20988 + jsoff_t obj_off = (jsoff_t)vdata(obj); 20989 + dynamic_accessors_t *entry = NULL; 20990 + HASH_FIND(hh, accessor_registry, &obj_off, sizeof(jsoff_t), entry); 20991 + if (!entry) { 20992 + entry = (dynamic_accessors_t *)malloc(sizeof(dynamic_accessors_t)); 20993 + if (!entry) return; 20994 + entry->obj_offset = obj_off; 20995 + entry->getter = NULL; 20996 + entry->setter = NULL; 20997 + HASH_ADD(hh, accessor_registry, obj_offset, sizeof(jsoff_t), entry); 20998 + } 20999 + entry->setter = setter; 21000 + } 21001 + 21002 + static inline uint64_t make_desc_key(jsoff_t obj_off, const char *key, size_t klen) { 21003 + uint32_t key_hash = (uint32_t)hash_key(key, klen); 21004 + return ((uint64_t)obj_off << 32) | key_hash; 21005 + } 21006 + 21007 + static descriptor_entry_t *lookup_descriptor(jsoff_t obj_off, const char *key, size_t klen) { 21008 + uint64_t desc_key = make_desc_key(obj_off, key, klen); 21009 + descriptor_entry_t *entry = NULL; 21010 + HASH_FIND(hh, desc_registry, &desc_key, sizeof(uint64_t), entry); 21011 + return entry; 21012 + } 21013 + 21014 + static descriptor_entry_t *get_or_create_desc(struct js *js, jsval_t obj, const char *key, size_t klen) { 21015 + if (vtype(obj) != T_OBJ && vtype(obj) != T_FUNC) return NULL; 21016 + jsoff_t obj_off = (jsoff_t)vdata(obj); 21017 + uint64_t desc_key = make_desc_key(obj_off, key, klen); 21018 + 21019 + descriptor_entry_t *entry = NULL; 21020 + HASH_FIND(hh, desc_registry, &desc_key, sizeof(uint64_t), entry); 21021 + if (!entry) { 21022 + entry = (descriptor_entry_t *)malloc(sizeof(descriptor_entry_t)); 21023 + if (!entry) return NULL; 21024 + entry->key = desc_key; 21025 + entry->writable = true; 21026 + entry->enumerable = true; 21027 + entry->configurable = true; 21028 + entry->has_getter = false; 21029 + entry->has_setter = false; 21030 + entry->getter = js_mkundef(); 21031 + entry->setter = js_mkundef(); 21032 + HASH_ADD(hh, desc_registry, key, sizeof(uint64_t), entry); 21033 + } 21034 + return entry; 21035 + } 21036 + 21037 + void js_set_descriptor(struct js *js, jsval_t obj, const char *key, size_t klen, int flags) { 21038 + descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); 21039 + if (!entry) return; 21040 + entry->writable = (flags & JS_DESC_W) != 0; 21041 + entry->enumerable = (flags & JS_DESC_E) != 0; 21042 + entry->configurable = (flags & JS_DESC_C) != 0; 21043 + } 21044 + 21045 + void js_set_getter_desc(struct js *js, jsval_t obj, const char *key, size_t klen, jsval_t getter, int flags) { 21046 + descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); 21047 + if (!entry) return; 21048 + entry->enumerable = (flags & JS_DESC_E) != 0; 21049 + entry->configurable = (flags & JS_DESC_C) != 0; 21050 + entry->has_getter = true; 21051 + entry->getter = getter; 21052 + } 21053 + 21054 + void js_set_setter_desc(struct js *js, jsval_t obj, const char *key, size_t klen, jsval_t setter, int flags) { 21055 + descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); 21056 + if (!entry) return; 21057 + entry->enumerable = (flags & JS_DESC_E) != 0; 21058 + entry->configurable = (flags & JS_DESC_C) != 0; 21059 + entry->has_setter = true; 21060 + entry->setter = setter; 21061 + } 21062 + 21063 + void js_set_accessor_desc(struct js *js, jsval_t obj, const char *key, size_t klen, jsval_t getter, jsval_t setter, int flags) { 21064 + descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); 21065 + if (!entry) return; 21066 + entry->enumerable = (flags & JS_DESC_E) != 0; 21067 + entry->configurable = (flags & JS_DESC_C) != 0; 21068 + entry->has_getter = true; 21069 + entry->has_setter = true; 21070 + entry->getter = getter; 21071 + entry->setter = setter; 21378 21072 } 21379 21073 21380 21074 void js_print_stack_trace(FILE *stream) {
+117 -24
src/modules/buffer.c
··· 39 39 static jsval_t js_arraybuffer_slice(struct js *js, jsval_t *args, int nargs); 40 40 static jsval_t js_typedarray_slice(struct js *js, jsval_t *args, int nargs); 41 41 static jsval_t js_typedarray_subarray(struct js *js, jsval_t *args, int nargs); 42 + static jsval_t js_typedarray_fill(struct js *js, jsval_t *args, int nargs); 42 43 static jsval_t js_dataview_getUint8(struct js *js, jsval_t *args, int nargs); 43 44 static jsval_t js_dataview_setUint8(struct js *js, jsval_t *args, int nargs); 44 45 static jsval_t js_dataview_getInt16(struct js *js, jsval_t *args, int nargs); ··· 144 145 } 145 146 146 147 static size_t get_element_size(TypedArrayType type) { 147 - switch (type) { 148 - case TYPED_ARRAY_INT8: 149 - case TYPED_ARRAY_UINT8: 150 - case TYPED_ARRAY_UINT8_CLAMPED: 151 - return 1; 152 - case TYPED_ARRAY_INT16: 153 - case TYPED_ARRAY_UINT16: 154 - return 2; 155 - case TYPED_ARRAY_INT32: 156 - case TYPED_ARRAY_UINT32: 157 - case TYPED_ARRAY_FLOAT32: 158 - return 4; 159 - case TYPED_ARRAY_FLOAT64: 160 - case TYPED_ARRAY_BIGINT64: 161 - case TYPED_ARRAY_BIGUINT64: 162 - return 8; 163 - default: 164 - return 1; 165 - } 148 + static const void *dispatch[] = { 149 + &&L_1, &&L_1, &&L_1, &&L_2, &&L_2, 150 + &&L_4, &&L_4, &&L_4, &&L_8, &&L_8, &&L_8 151 + }; 152 + 153 + if (type > TYPED_ARRAY_BIGUINT64) goto L_1; 154 + goto *dispatch[type]; 155 + 156 + L_1: return 1; 157 + L_2: return 2; 158 + L_4: return 4; 159 + L_8: return 8; 166 160 } 167 161 168 162 // ArrayBuffer constructor ··· 224 218 } 225 219 226 220 static jsval_t typedarray_index_getter(struct js *js, jsval_t obj, const char *key, size_t key_len) { 227 - if (key_len == 0 || key_len > 15) return js_mkundef(); 221 + if (key_len == 0 || key_len > 10) return js_mkundef(); 228 222 229 - char *endptr; 230 - long index = strtol(key, &endptr, 10); 231 - if (endptr != key + key_len || index < 0) return js_mkundef(); 223 + size_t index = 0; 224 + for (size_t i = 0; i < key_len; i++) { 225 + char c = key[i]; 226 + if (c < '0' || c > '9') return js_mkundef(); 227 + index = index * 10 + (c - '0'); 228 + } 232 229 233 230 jsval_t ta_val = js_get(js, obj, "__ta"); 234 231 TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_val); 235 - if (!ta_data || (size_t)index >= ta_data->length) return js_mkundef(); 232 + if (!ta_data || index >= ta_data->length) return js_mkundef(); 236 233 237 234 uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; 238 235 double value; ··· 257 254 L_DONE: return js_mknum(value); 258 255 } 259 256 257 + static bool typedarray_index_setter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t value) { 258 + if (key_len == 0 || key_len > 10) return false; 259 + 260 + size_t index = 0; 261 + for (size_t i = 0; i < key_len; i++) { 262 + char c = key[i]; 263 + if (c < '0' || c > '9') return false; 264 + index = index * 10 + (c - '0'); 265 + } 266 + 267 + jsval_t ta_val = js_get(js, obj, "__ta"); 268 + TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_val); 269 + if (!ta_data || index >= ta_data->length) return false; 270 + 271 + double num_val = js_type(value) == JS_NUM ? js_getnum(value) : 0; 272 + uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; 273 + 274 + switch (ta_data->type) { 275 + case TYPED_ARRAY_INT8: ((int8_t*)data)[index] = (int8_t)num_val; break; 276 + case TYPED_ARRAY_UINT8: 277 + case TYPED_ARRAY_UINT8_CLAMPED: ((uint8_t*)data)[index] = (uint8_t)num_val; break; 278 + case TYPED_ARRAY_INT16: ((int16_t*)data)[index] = (int16_t)num_val; break; 279 + case TYPED_ARRAY_UINT16: ((uint16_t*)data)[index] = (uint16_t)num_val; break; 280 + case TYPED_ARRAY_INT32: ((int32_t*)data)[index] = (int32_t)num_val; break; 281 + case TYPED_ARRAY_UINT32: ((uint32_t*)data)[index] = (uint32_t)num_val; break; 282 + case TYPED_ARRAY_FLOAT32: ((float*)data)[index] = (float)num_val; break; 283 + case TYPED_ARRAY_FLOAT64: ((double*)data)[index] = num_val; break; 284 + default: return false; 285 + } 286 + return true; 287 + } 288 + 260 289 static jsval_t create_typed_array(struct js *js, TypedArrayType type, ArrayBufferData *buffer, size_t byte_offset, size_t length, const char *type_name) { 261 290 TypedArrayData *ta_data = ta_arena_alloc(sizeof(TypedArrayData)); 262 291 if (!ta_data) return js_mkerr(js, "Failed to allocate TypedArray"); ··· 280 309 js_set(js, obj, "BYTES_PER_ELEMENT", js_mknum((double)element_size)); 281 310 js_set(js, obj, "slice", js_mkfun(js_typedarray_slice)); 282 311 js_set(js, obj, "subarray", js_mkfun(js_typedarray_subarray)); 312 + js_set(js, obj, "fill", js_mkfun(js_typedarray_fill)); 283 313 284 314 js_set_getter(js, obj, typedarray_index_getter); 315 + js_set_setter(js, obj, typedarray_index_setter); 285 316 286 317 return obj; 287 318 } ··· 382 413 size_t new_offset = ta_data->byte_offset + begin * element_size; 383 414 384 415 return create_typed_array(js, ta_data->type, ta_data->buffer, new_offset, new_length, "TypedArray"); 416 + } 417 + 418 + // TypedArray.prototype.fill(value, start, end) 419 + static jsval_t js_typedarray_fill(struct js *js, jsval_t *args, int nargs) { 420 + jsval_t this_val = js_getthis(js); 421 + jsval_t ta_data_val = js_get(js, this_val, "__ta"); 422 + 423 + TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); 424 + if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); 425 + 426 + double value = 0; 427 + if (nargs > 0 && js_type(args[0]) == JS_NUM) value = js_getnum(args[0]); 428 + 429 + int start = 0, end = ta_data->length; 430 + if (nargs > 1 && js_type(args[1]) == JS_NUM) start = (int)js_getnum(args[1]); 431 + if (nargs > 2 && js_type(args[2]) == JS_NUM) end = (int)js_getnum(args[2]); 432 + 433 + if (start < 0) start = ta_data->length + start; 434 + if (end < 0) end = ta_data->length + end; 435 + if (start < 0) start = 0; 436 + if (end < 0) end = 0; 437 + if (start > (int)ta_data->length) start = ta_data->length; 438 + if (end > (int)ta_data->length) end = ta_data->length; 439 + if (end < start) end = start; 440 + 441 + uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; 442 + 443 + static const void *dispatch[] = { 444 + &&L_INT8, &&L_UINT8, &&L_UINT8, &&L_INT16, &&L_UINT16, 445 + &&L_INT32, &&L_UINT32, &&L_FLOAT32, &&L_FLOAT64, &&L_DONE, &&L_DONE 446 + }; 447 + 448 + if (ta_data->type > TYPED_ARRAY_BIGUINT64) goto L_DONE; 449 + goto *dispatch[ta_data->type]; 450 + 451 + L_INT8: 452 + for (int i = start; i < end; i++) ((int8_t*)data)[i] = (int8_t)value; 453 + goto L_DONE; 454 + L_UINT8: 455 + for (int i = start; i < end; i++) data[i] = (uint8_t)value; 456 + goto L_DONE; 457 + L_INT16: 458 + for (int i = start; i < end; i++) ((int16_t*)data)[i] = (int16_t)value; 459 + goto L_DONE; 460 + L_UINT16: 461 + for (int i = start; i < end; i++) ((uint16_t*)data)[i] = (uint16_t)value; 462 + goto L_DONE; 463 + L_INT32: 464 + for (int i = start; i < end; i++) ((int32_t*)data)[i] = (int32_t)value; 465 + goto L_DONE; 466 + L_UINT32: 467 + for (int i = start; i < end; i++) ((uint32_t*)data)[i] = (uint32_t)value; 468 + goto L_DONE; 469 + L_FLOAT32: 470 + for (int i = start; i < end; i++) ((float*)data)[i] = (float)value; 471 + goto L_DONE; 472 + L_FLOAT64: 473 + for (int i = start; i < end; i++) ((double*)data)[i] = value; 474 + goto L_DONE; 475 + L_DONE: 476 + return this_val; 385 477 } 386 478 387 479 #define DEFINE_TYPEDARRAY_CONSTRUCTOR(name, type) \ ··· 788 880 jsval_t name##_proto = js_mkobj(js); \ 789 881 js_set(js, name##_proto, "slice", js_mkfun(js_typedarray_slice)); \ 790 882 js_set(js, name##_proto, "subarray", js_mkfun(js_typedarray_subarray)); \ 883 + js_set(js, name##_proto, "fill", js_mkfun(js_typedarray_fill)); \ 791 884 js_setprop(js, name##_ctor_obj, js_mkstr(js, "__native_func", 13), js_mkfun(js_##name##_constructor)); \ 792 885 js_setprop(js, name##_ctor_obj, js_mkstr(js, "prototype", 9), name##_proto); \ 793 886 js_set(js, glob, #name, js_obj_to_func(name##_ctor_obj)); \
+1 -6
src/modules/localstorage.c
··· 309 309 js_set(js, storage_obj, "setFile", js_mkfun(js_localstorage_setFile)); 310 310 311 311 jsval_t length_getter = js_mkfun(js_localstorage_length); 312 - jsval_t desc = js_mkobj(js); 313 - js_set(js, desc, "get", length_getter); 314 - js_set(js, desc, "enumerable", js_mktrue()); 315 - js_set(js, desc, "configurable", js_mkfalse()); 316 - js_set(js, storage_obj, "__desc_length", desc); 317 - js_set(js, storage_obj, "__getter", js_mktrue()); 312 + js_set_getter_desc(js, storage_obj, "length", 6, length_getter, JS_DESC_E); 318 313 319 314 js_set(js, storage_obj, get_toStringTag_sym_key(), js_mkstr(js, "Storage", 7)); 320 315 js_set(js, glob, "localStorage", storage_obj);
-1
src/modules/reflect.c
··· 101 101 102 102 while (js_prop_iter_next(&iter, &key, &key_len, &value) && count < 256) { 103 103 if (key_len >= 9 && memcmp(key, "__proto__", 9) == 0) continue; 104 - if (key_len >= 7 && memcmp(key, "__desc_", 7) == 0) continue; 105 104 if (key_len >= 2 && key[0] == '_' && key[1] == '_') continue; 106 105 107 106 temp_keys[count++] = js_mkstr(js, key, key_len);
+1 -6
src/modules/sessionstorage.c
··· 183 183 js_set(js, storage_obj, "key", js_mkfun(js_sessionstorage_key)); 184 184 185 185 jsval_t length_getter = js_mkfun(js_sessionstorage_length); 186 - jsval_t desc = js_mkobj(js); 187 - js_set(js, desc, "get", length_getter); 188 - js_set(js, desc, "enumerable", js_mktrue()); 189 - js_set(js, desc, "configurable", js_mkfalse()); 190 - js_set(js, storage_obj, "__desc_length", desc); 191 - js_set(js, storage_obj, "__getter", js_mktrue()); 186 + js_set_getter_desc(js, storage_obj, "length", 6, length_getter, JS_DESC_E); 192 187 193 188 js_set(js, storage_obj, get_toStringTag_sym_key(), js_mkstr(js, "Storage", 7)); 194 189 js_set(js, glob, "sessionStorage", storage_obj);