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.

share enumerable property iteration for spread and assign

+149 -51
+10
include/ant.h
··· 151 151 ant_offset_t off; 152 152 } ant_iter_t; 153 153 154 + typedef struct { 155 + uint32_t slot; 156 + bool is_symbol; 157 + const char *str; 158 + size_t key_len; 159 + ant_offset_t sym_off; 160 + } ant_iter_key_t; 161 + 154 162 ant_iter_t js_prop_iter_begin(ant_t *js, ant_value_t obj); 155 163 void js_prop_iter_end(ant_iter_t *iter); 156 164 165 + bool js_prop_iter_next_key(ant_iter_t *iter, ant_iter_key_t *key_out, ant_value_t *value); 157 166 bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, ant_value_t *value); 158 167 bool js_prop_iter_next_val(ant_iter_t *iter, ant_value_t *key_out, ant_value_t *value); 168 + bool js_is_own_enumerable_prop(ant_t *js, ant_value_t source, ant_object_t *source_ptr, const ant_iter_key_t *key); 159 169 160 170 ant_value_t js_obj_to_func(ant_value_t obj); 161 171 ant_value_t js_obj_to_func_ex(ant_value_t obj, uint8_t flags);
+59 -43
src/ant.c
··· 7095 7095 return obj; 7096 7096 } 7097 7097 7098 - static inline bool is_enumerable_prop( 7098 + bool js_is_own_enumerable_prop( 7099 7099 ant_t *js, ant_value_t source, ant_object_t *source_ptr, 7100 - ant_value_t prop_key, uint32_t slot 7100 + const ant_iter_key_t *key 7101 7101 ) { 7102 - if (vtype(prop_key) == T_STR) { 7103 - size_t klen = 0; 7104 - 7105 - const char *kstr = js_getstr(js, prop_key, &klen); 7106 - if (is_internal_prop(kstr, klen)) return false; 7107 - 7102 + if (!key) return false; 7103 + 7104 + if (key->is_symbol) { 7105 + if (!source_ptr || source_ptr->is_exotic) { 7106 + prop_meta_t meta; 7107 + return !lookup_symbol_prop_meta(js_as_obj(source), key->sym_off, &meta) || meta.enumerable; 7108 + } 7109 + return (ant_shape_get_attrs(source_ptr->shape, key->slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; 7110 + } 7111 + 7112 + if (!key->str || is_internal_prop(key->str, key->key_len)) return false; 7113 + 7108 7114 if (!source_ptr || source_ptr->is_exotic) { 7109 - descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), kstr, klen); 7115 + descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), key->str, key->key_len); 7110 7116 return !desc || desc->enumerable; 7111 - }} 7112 - 7113 - return (ant_shape_get_attrs(source_ptr->shape, slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; 7117 + } 7118 + 7119 + return (ant_shape_get_attrs(source_ptr->shape, key->slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; 7114 7120 } 7115 7121 7116 7122 static ant_value_t builtin_object_assign(ant_t *js, ant_value_t *args, int nargs) { ··· 7139 7145 ant_iter_t iter = js_prop_iter_begin(js, source); 7140 7146 ant_object_t *source_ptr = js_obj_ptr(js_as_obj(source)); 7141 7147 7142 - ant_value_t prop_key = js_mkundef(); 7148 + ant_iter_key_t key = {0}; 7143 7149 ant_value_t val = js_mkundef(); 7144 7150 7145 - while (js_prop_iter_next_val(&iter, &prop_key, &val)) if ( 7146 - is_enumerable_prop(js, source, source_ptr, prop_key, (uint32_t)(iter.off - 1)) 7147 - ) js_setprop(js, as_obj, prop_key, val); 7151 + while (js_prop_iter_next_key(&iter, &key, &val)) { 7152 + if (!js_is_own_enumerable_prop(js, source, source_ptr, &key)) continue; 7153 + ant_value_t prop_key = key.is_symbol ? mkval(T_SYMBOL, key.sym_off) : js_mkstr(js, key.str, key.key_len); 7154 + js_setprop(js, as_obj, prop_key, val); 7155 + } 7148 7156 7149 7157 js_prop_iter_end(&iter); 7150 7158 } ··· 14842 14850 } 14843 14851 14844 14852 bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, ant_value_t *value) { 14845 - if (!iter || !iter->ctx) return false; 14846 - prop_iter_ctx_t *ctx = (prop_iter_ctx_t *)iter->ctx; 14853 + ant_iter_key_t meta = {0}; 14847 14854 14848 - ant_object_t *obj = ctx->obj; 14849 - if (!obj || !obj->shape) return false; 14850 - 14851 - uint32_t count = ant_shape_count(obj->shape); 14852 - while (ctx->index < count) { 14853 - uint32_t i = ctx->index++; 14854 - const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); 14855 - if (!prop) continue; 14856 - if (prop->type == ANT_SHAPE_KEY_SYMBOL) continue; 14857 - if (i >= obj->prop_count) continue; 14858 - 14859 - if (key) { 14860 - *key = prop->key.interned; 14861 - if (key_len) *key_len = strlen(prop->key.interned); 14862 - } 14863 - 14864 - if (value) *value = ant_object_prop_get_unchecked(obj, i); 14865 - iter->off = i + 1; 14866 - 14855 + while (js_prop_iter_next_key(iter, &meta, value)) { 14856 + if (meta.is_symbol) continue; 14857 + if (key) *key = meta.str; 14858 + if (key_len) *key_len = meta.key_len; 14867 14859 return true; 14868 14860 } 14869 14861 14870 14862 return false; 14871 14863 } 14872 14864 14873 - bool js_prop_iter_next_val(ant_iter_t *iter, ant_value_t *key_out, ant_value_t *value) { 14865 + bool js_prop_iter_next_key(ant_iter_t *iter, ant_iter_key_t *key_out, ant_value_t *value) { 14874 14866 if (!iter || !iter->ctx) return false; 14875 14867 prop_iter_ctx_t *ctx = (prop_iter_ctx_t *)iter->ctx; 14876 14868 14877 14869 ant_object_t *obj = ctx->obj; 14878 14870 if (!obj || !obj->shape) return false; 14871 + 14879 14872 uint32_t count = ant_shape_count(obj->shape); 14880 - 14881 14873 while (ctx->index < count) { 14882 14874 uint32_t i = ctx->index++; 14883 14875 const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); 14884 - 14885 14876 if (!prop) continue; 14886 14877 if (i >= obj->prop_count) continue; 14887 - 14878 + 14888 14879 if (key_out) { 14889 - if (prop->type == ANT_SHAPE_KEY_SYMBOL) *key_out = mkval(T_SYMBOL, prop->key.sym_off); 14890 - else *key_out = js_mkstr(ctx->js, prop->key.interned, strlen(prop->key.interned)); 14880 + key_out->slot = i; 14881 + key_out->is_symbol = (prop->type == ANT_SHAPE_KEY_SYMBOL); 14882 + if (key_out->is_symbol) { 14883 + key_out->str = NULL; 14884 + key_out->key_len = 0; 14885 + key_out->sym_off = prop->key.sym_off; 14886 + } else { 14887 + key_out->str = prop->key.interned; 14888 + key_out->key_len = strlen(prop->key.interned); 14889 + key_out->sym_off = 0; 14890 + } 14891 14891 } 14892 14892 14893 14893 if (value) *value = ant_object_prop_get_unchecked(obj, i); ··· 14897 14897 } 14898 14898 14899 14899 return false; 14900 + } 14901 + 14902 + bool js_prop_iter_next_val(ant_iter_t *iter, ant_value_t *key_out, ant_value_t *value) { 14903 + ant_iter_key_t meta = {0}; 14904 + ant_t *js = NULL; 14905 + 14906 + if (!iter || !iter->ctx) return false; 14907 + js = ((prop_iter_ctx_t *)iter->ctx)->js; 14908 + if (!js_prop_iter_next_key(iter, &meta, value)) return false; 14909 + 14910 + if (key_out) { 14911 + if (meta.is_symbol) *key_out = mkval(T_SYMBOL, meta.sym_off); 14912 + else *key_out = js_mkstr(js, meta.str, meta.key_len); 14913 + } 14914 + 14915 + return true; 14900 14916 } 14901 14917 14902 14918 void js_prop_iter_end(ant_iter_t *iter) {
+16 -8
src/silver/ops/objects.h
··· 119 119 ant_value_t src = vm->stack[vm->sp - 1]; 120 120 ant_value_t dst = vm->stack[vm->sp - 2]; 121 121 if (!is_object_type(src) || !is_object_type(dst)) return; 122 - 123 - const char *key = NULL; 124 - size_t key_len = 0; 122 + 125 123 ant_iter_t iter = js_prop_iter_begin(js, src); 124 + ant_object_t *source_ptr = js_obj_ptr(js_as_obj(src)); 126 125 127 - while (js_prop_iter_next(&iter, &key, &key_len, NULL)) { 128 - ant_value_t val = js_get(js, src, key); 129 - ant_value_t prop_key = js_mkstr(js, key, key_len); 130 - js_setprop(js, dst, prop_key, val); 126 + ant_iter_key_t key = {0}; 127 + ant_value_t val = js_mkundef(); 128 + 129 + while (js_prop_iter_next_key(&iter, &key, NULL)) { 130 + if (!js_is_own_enumerable_prop(js, src, source_ptr, &key)) continue; 131 + if (key.is_symbol) { 132 + ant_value_t prop_key = mkval(T_SYMBOL, key.sym_off); 133 + val = js_get_sym(js, src, prop_key); 134 + js_setprop(js, dst, prop_key, val); 135 + } else { 136 + val = js_get(js, src, key.str); 137 + js_setprop(js, dst, js_mkstr(js, key.str, key.key_len), val); 138 + } 131 139 } 132 - 140 + 133 141 js_prop_iter_end(&iter); 134 142 } 135 143
+64
tests/test_spread_symbols.cjs
··· 1 + let pass = true; 2 + 3 + const visible = Symbol("visible"); 4 + const hidden = Symbol("hidden"); 5 + const getterKey = Symbol("getter"); 6 + let getterCalls = 0; 7 + 8 + const source = { 9 + plain: 7, 10 + [visible]: 11, 11 + }; 12 + 13 + Object.defineProperty(source, hidden, { 14 + value: 13, 15 + enumerable: false, 16 + }); 17 + 18 + Object.defineProperty(source, getterKey, { 19 + enumerable: true, 20 + get() { 21 + getterCalls++; 22 + return 17; 23 + }, 24 + }); 25 + 26 + const copy = { ...source }; 27 + 28 + if (copy.plain !== 7) { 29 + console.log("FAIL: object spread should keep string-keyed data properties"); 30 + pass = false; 31 + } 32 + 33 + if (copy[visible] !== 11) { 34 + console.log("FAIL: object spread should copy enumerable symbol properties"); 35 + pass = false; 36 + } 37 + 38 + if (Object.getOwnPropertySymbols(copy).includes(hidden)) { 39 + console.log("FAIL: object spread should skip non-enumerable symbol properties"); 40 + pass = false; 41 + } 42 + 43 + if (copy[getterKey] !== 17) { 44 + console.log("FAIL: object spread should read enumerable symbol getters"); 45 + pass = false; 46 + } 47 + 48 + if (getterCalls !== 1) { 49 + console.log("FAIL: symbol getter should be invoked exactly once during spread"); 50 + pass = false; 51 + } 52 + 53 + const getterDesc = Object.getOwnPropertyDescriptor(copy, getterKey); 54 + if (!getterDesc || getterDesc.get) { 55 + console.log("FAIL: copied symbol getter should become a data property"); 56 + pass = false; 57 + } 58 + 59 + if (!getterDesc || getterDesc.value !== 17) { 60 + console.log("FAIL: copied symbol getter value should be materialized"); 61 + pass = false; 62 + } 63 + 64 + if (pass) console.log("PASS");