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.

prevent segfault assigning to T_CFUNC

+99 -12
+38 -10
src/ant.c
··· 1510 1510 if (lookup_string_prop_meta(js, as_obj, key, (size_t)klen, &meta) && !meta.enumerable) continue; 1511 1511 } 1512 1512 1513 + if (prop->has_getter || prop->has_setter) { 1514 + if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); 1515 + first = false; 1516 + if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); 1517 + n += strkey_interned(js, key, (size_t)klen, buf + n, REMAIN(n, len)); 1518 + n += cpy(buf + n, REMAIN(n, len), ": ", 2); 1519 + if (prop->has_getter && prop->has_setter) 1520 + n += cpy(buf + n, REMAIN(n, len), "[Getter/Setter]", 15); 1521 + else if (prop->has_getter) 1522 + n += cpy(buf + n, REMAIN(n, len), "[Getter]", 8); 1523 + else 1524 + n += cpy(buf + n, REMAIN(n, len), "[Setter]", 8); 1525 + continue; 1526 + } 1527 + 1513 1528 if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); 1514 1529 first = false; 1515 1530 if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); ··· 3139 3154 return false; 3140 3155 } 3141 3156 3157 + // TODO: decompose into smaller helpers 3142 3158 ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) { 3143 3159 uint8_t ot = vtype(obj); 3144 3160 3145 - if (ot == T_STR || ot == T_NUM || ot == T_BOOL) { 3161 + if (ot == T_STR || ot == T_NUM || ot == T_BOOL || ot == T_CFUNC) { 3146 3162 ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); 3147 3163 const char *key = (char *)(uintptr_t)(koff); 3148 - ant_value_t proto = get_prototype_for_type(js, ot); 3149 - if (is_object_type(proto)) { 3150 - ant_value_t setter = js_mkundef(); 3151 - bool has_setter = false; 3152 - lkp_with_setter(js, proto, key, klen, &setter, &has_setter); 3153 - if (has_setter && (vtype(setter) == T_FUNC || vtype(setter) == T_CFUNC)) { 3154 - call_proto_accessor(js, obj, setter, true, &v, 1, true); 3155 - return v; 3164 + 3165 + if (ot != T_CFUNC) { 3166 + ant_value_t proto = get_prototype_for_type(js, ot); 3167 + if (is_object_type(proto)) { 3168 + ant_value_t setter = js_mkundef(); 3169 + bool has_setter = false; 3170 + lkp_with_setter(js, proto, key, klen, &setter, &has_setter); 3171 + if (has_setter && (vtype(setter) == T_FUNC || vtype(setter) == T_CFUNC)) { 3172 + call_proto_accessor(js, obj, setter, true, &v, 1, true); 3173 + return v; 3174 + } 3156 3175 } 3157 3176 } 3177 + 3158 3178 if (sv_vm_is_strict(js->vm)) 3159 3179 return js_mkerr_typed(js, JS_ERR_TYPE, 3160 3180 "Cannot create property '%.*s' on %s", ··· 5761 5781 ant_value_t obj = args[0]; 5762 5782 ant_value_t prop = args[1]; 5763 5783 ant_value_t descriptor = args[2]; 5784 + uint8_t t = vtype(obj); 5764 5785 5765 - uint8_t t = vtype(obj); 5786 + if (t == T_CFUNC) { 5787 + ant_value_t fn_obj = mkobj(js, 0); 5788 + set_slot(fn_obj, SLOT_CFUNC, obj); 5789 + obj = js_obj_to_func(fn_obj); 5790 + args[0] = obj; 5791 + t = T_FUNC; 5792 + } 5793 + 5766 5794 if (t != T_OBJ && t != T_ARR && t != T_FUNC) { 5767 5795 return js_mkerr(js, "Object.defineProperty called on non-object"); 5768 5796 }
+2 -2
src/silver/ops/objects.h
··· 122 122 123 123 const char *key = NULL; 124 124 size_t key_len = 0; 125 - ant_value_t val = js_mkundef(); 126 125 ant_iter_t iter = js_prop_iter_begin(js, src); 127 126 128 - while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 127 + while (js_prop_iter_next(&iter, &key, &key_len, NULL)) { 128 + ant_value_t val = js_get(js, src, key); 129 129 ant_value_t prop_key = js_mkstr(js, key, key_len); 130 130 js_setprop(js, dst, prop_key, val); 131 131 }
+59
tests/test_spread_accessor.js
··· 1 + // Repro: object spread should invoke getters 2 + 3 + const src = {}; 4 + Object.defineProperty(src, 'x', { 5 + get() { return 42; }, 6 + enumerable: true, 7 + }); 8 + Object.defineProperty(src, 'y', { 9 + get() { return 'hello'; }, 10 + enumerable: true, 11 + }); 12 + src.z = 99; // plain data property for comparison 13 + 14 + const copy = { ...src }; 15 + 16 + let pass = true; 17 + 18 + if (copy.x !== 42) { 19 + console.log("FAIL: copy.x expected 42, got", copy.x); 20 + pass = false; 21 + } 22 + if (copy.y !== 'hello') { 23 + console.log("FAIL: copy.y expected 'hello', got", copy.y); 24 + pass = false; 25 + } 26 + if (copy.z !== 99) { 27 + console.log("FAIL: copy.z expected 99, got", copy.z); 28 + pass = false; 29 + } 30 + 31 + // Also test getter that references `this` 32 + const src2 = { _val: 10 }; 33 + Object.defineProperty(src2, 'doubled', { 34 + get() { return this._val * 2; }, 35 + enumerable: true, 36 + }); 37 + 38 + const copy2 = { ...src2 }; 39 + if (copy2.doubled !== 20) { 40 + console.log("FAIL: copy2.doubled expected 20, got", copy2.doubled); 41 + pass = false; 42 + } 43 + if (copy2._val !== 10) { 44 + console.log("FAIL: copy2._val expected 10, got", copy2._val); 45 + pass = false; 46 + } 47 + 48 + // Getter-only (no setter) should still copy the value, not the accessor 49 + const desc = Object.getOwnPropertyDescriptor(copy, 'x'); 50 + if (desc.get) { 51 + console.log("FAIL: copy.x should be a data property, not an accessor"); 52 + pass = false; 53 + } 54 + if (desc.value !== 42) { 55 + console.log("FAIL: copy.x descriptor value expected 42, got", desc.value); 56 + pass = false; 57 + } 58 + 59 + if (pass) console.log("PASS");