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.

assignment now stores native functions exactly

+284 -14
+103 -13
src/ant.c
··· 3918 3918 3919 3919 ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); 3920 3920 const char *key = (char *)(uintptr_t)(koff); 3921 - 3922 - v = js_expose_cfunc_for_key(js, v, key, (size_t)klen); 3923 - if (is_err(v)) return v; 3924 - 3921 + 3925 3922 if (array_obj_ptr(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') { 3926 3923 ant_arguments_state_t *args_state = js_arguments_state(obj); 3927 3924 if (args_state && !args_state->in_setter && js_arguments_setter(js, obj, key, (size_t)klen, v)) return v; ··· 4058 4055 if (is_err(extensibility_error)) return extensibility_error; 4059 4056 if (extensibility_error == js_false) return v; 4060 4057 } 4061 - 4062 - ant_value_t result = mkprop(js, obj, k, v, 0); 4058 + 4059 + const char *interned_key = intern_string(key, (size_t)klen); 4060 + if (!interned_key) return js_mkerr(js, "oom"); 4061 + ant_value_t result = mkprop_interned_exact(js, obj, interned_key, v, 0); 4063 4062 if (is_err(result)) return result; 4064 4063 array_define_or_set_index(js, obj, key, (size_t)klen); 4065 4064 ··· 4075 4074 4076 4075 ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v) { 4077 4076 obj = js_as_obj(obj); 4078 - v = js_expose_cfunc_for_key(js, v, key, klen); 4079 - if (is_err(v)) return v; 4080 4077 if (is_proxy(obj)) { 4081 4078 ant_value_t result = proxy_set(js, obj, key, klen, v); 4082 4079 if (is_err(result)) return result; ··· 4148 4145 if (extensibility_error == js_false) return v; 4149 4146 } 4150 4147 4151 - ant_value_t k = js_mkstr(js, key, klen); 4152 - if (is_err(k)) return k; 4153 - ant_value_t created = mkprop(js, obj, k, v, 0); 4148 + const char *interned_key = intern_string(key, klen); 4149 + if (!interned_key) return js_mkerr(js, "oom"); 4150 + ant_value_t created = mkprop_interned_exact(js, obj, interned_key, v, 0); 4151 + 4154 4152 if (!is_err(created)) array_define_or_set_index(js, obj, key, klen); 4155 4153 return is_err(created) ? created : v; 4156 4154 } ··· 5986 5984 } 5987 5985 5988 5986 static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mode) { 5987 + if (vtype(obj) == T_CFUNC) { 5988 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 5989 + if (vtype(promoted) != T_FUNC) return mkarr(js); 5990 + obj = promoted; 5991 + } 5989 5992 bool is_arr = (vtype(obj) == T_ARR); 5990 5993 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); 5991 5994 ··· 6196 6199 static ant_value_t builtin_object_keys(ant_t *js, ant_value_t *args, int nargs) { 6197 6200 if (nargs == 0) return mkarr(js); 6198 6201 ant_value_t obj = args[0]; 6202 + if (vtype(obj) == T_CFUNC) { 6203 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 6204 + if (vtype(promoted) != T_FUNC) return mkarr(js); 6205 + obj = promoted; 6206 + } 6199 6207 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); 6200 6208 6201 6209 if (is_proxy(obj)) return proxy_enum(js, obj, OBJ_ENUM_KEYS); ··· 6365 6373 goto done; 6366 6374 } 6367 6375 6376 + if (t == T_CFUNC) { 6377 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 6378 + if (vtype(promoted) != T_FUNC) goto done; 6379 + obj = promoted; 6380 + t = T_FUNC; 6381 + } 6382 + 6368 6383 if (t == T_OBJ) { 6369 6384 ant_value_t prim = get_slot(obj, SLOT_PRIMITIVE); 6370 6385 GC_ROOT_PIN(js, prim); ··· 6386 6401 static ant_value_t builtin_object_values(ant_t *js, ant_value_t *args, int nargs) { 6387 6402 if (nargs == 0) return mkarr(js); 6388 6403 ant_value_t obj = args[0]; 6404 + if (vtype(obj) == T_CFUNC) { 6405 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 6406 + if (vtype(promoted) != T_FUNC) return mkarr(js); 6407 + obj = promoted; 6408 + } 6389 6409 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); 6390 6410 if (is_proxy(obj)) return proxy_enum(js, obj, OBJ_ENUM_VALUES); 6391 6411 return object_enum(js, obj, OBJ_ENUM_VALUES); ··· 6394 6414 static ant_value_t builtin_object_entries(ant_t *js, ant_value_t *args, int nargs) { 6395 6415 if (nargs == 0) return mkarr(js); 6396 6416 ant_value_t obj = args[0]; 6417 + if (vtype(obj) == T_CFUNC) { 6418 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 6419 + if (vtype(promoted) != T_FUNC) return mkarr(js); 6420 + obj = promoted; 6421 + } 6397 6422 if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); 6398 6423 if (is_proxy(obj)) return proxy_enum(js, obj, OBJ_ENUM_ENTRIES); 6399 6424 return object_enum(js, obj, OBJ_ENUM_ENTRIES); ··· 6406 6431 6407 6432 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) 6408 6433 return get_prototype_for_type(js, t); 6409 - if (t == T_CFUNC) return get_prototype_for_type(js, t); 6434 + if (t == T_CFUNC) { 6435 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 6436 + if (vtype(promoted) == T_FUNC) return get_proto(js, promoted); 6437 + return get_prototype_for_type(js, t); 6438 + } 6410 6439 6411 6440 if (is_object_type(obj)) { 6412 6441 if (is_proxy(obj)) return proxy_get_prototype_of(js, obj); ··· 6769 6798 if (vtype(key) != T_STR) { 6770 6799 key = js_tostring_val(js, key); 6771 6800 if (is_err(key)) return key; 6801 + } 6802 + 6803 + if (t == T_CFUNC) { 6804 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 6805 + if (vtype(promoted) == T_FUNC) { 6806 + obj = promoted; 6807 + t = T_FUNC; 6808 + } else { 6809 + ant_offset_t key_len = 0; 6810 + ant_offset_t key_off = vstr(js, key, &key_len); 6811 + ant_value_t value = js_mkundef(); 6812 + return mkval(T_BOOL, js_cfunc_try_get_own(js, obj, (char *)(uintptr_t)(key_off), (size_t)key_len, &value) ? 1 : 0); 6813 + } 6772 6814 } 6773 6815 6774 6816 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); ··· 7417 7459 ant_value_t obj = args[0]; 7418 7460 ant_value_t key = args[1]; 7419 7461 uint8_t t = vtype(obj); 7462 + 7463 + if (t == T_CFUNC) { 7464 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 7465 + if (vtype(promoted) == T_FUNC) { 7466 + obj = promoted; 7467 + t = T_FUNC; 7468 + } 7469 + } 7420 7470 7421 7471 if (t == T_CFUNC) { 7422 7472 bool is_sym = (vtype(key) == T_SYMBOL); ··· 7575 7625 ant_value_t obj = args[0]; 7576 7626 7577 7627 if (vtype(obj) == T_CFUNC) { 7628 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 7629 + if (vtype(promoted) == T_FUNC) obj = promoted; 7630 + } 7631 + 7632 + if (vtype(obj) == T_CFUNC) { 7578 7633 ant_value_t arr = mkarr(js); 7579 7634 ant_offset_t idx = 0; 7580 7635 arr_set(js, arr, idx++, js->length_str); ··· 7628 7683 static ant_value_t builtin_object_getOwnPropertySymbols(ant_t *js, ant_value_t *args, int nargs) { 7629 7684 if (nargs == 0) return mkarr(js); 7630 7685 ant_value_t obj = args[0]; 7686 + 7687 + if (vtype(obj) == T_CFUNC) { 7688 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 7689 + if (vtype(promoted) == T_FUNC) obj = promoted; 7690 + } 7631 7691 7632 7692 uint8_t t = vtype(obj); 7633 7693 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkarr(js); ··· 7739 7799 ant_value_t key = args[0]; 7740 7800 7741 7801 uint8_t t = vtype(obj); 7802 + 7803 + if (t == T_CFUNC) { 7804 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 7805 + if (vtype(promoted) == T_FUNC) { 7806 + obj = promoted; 7807 + t = T_FUNC; 7808 + } else { 7809 + if (vtype(key) == T_SYMBOL) return mkval(T_BOOL, 0); 7810 + if (vtype(key) != T_STR) { 7811 + char buf[64]; 7812 + size_t n = tostr(js, key, buf, sizeof(buf)); 7813 + key = js_mkstr(js, buf, n); 7814 + } 7815 + ant_offset_t key_len = 0; 7816 + ant_offset_t key_off = vstr(js, key, &key_len); 7817 + ant_value_t value = js_mkundef(); 7818 + return mkval(T_BOOL, js_cfunc_try_get_own(js, obj, (char *)(uintptr_t)(key_off), (size_t)key_len, &value) ? 1 : 0); 7819 + } 7820 + } 7742 7821 7743 7822 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); 7744 7823 ant_value_t as_obj = js_as_obj(obj); ··· 14653 14732 if (vtype(sym) != T_SYMBOL) return js_mkundef(); 14654 14733 ant_offset_t sym_off = (ant_offset_t)vdata(sym); 14655 14734 14735 + if (vtype(obj) == T_CFUNC) { 14736 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 14737 + if (vtype(promoted) == T_FUNC) return js_get_sym_with_receiver(js, promoted, sym, receiver); 14738 + } 14739 + 14656 14740 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); 14657 14741 uint8_t ot = vtype(obj); 14658 14742 14659 14743 if (!is_object_type(obj)) { 14660 - if (ot == T_STR || ot == T_NUM || ot == T_BOOL || ot == T_BIGINT || ot == T_SYMBOL) { 14744 + if (ot == T_STR || ot == T_NUM || ot == T_BOOL || ot == T_BIGINT || ot == T_SYMBOL || ot == T_CFUNC) { 14661 14745 ant_value_t proto = get_prototype_for_type(js, ot); 14662 14746 if (!is_object_type(proto)) return js_mkundef(); 14663 14747 obj = js_as_obj(proto); ··· 14710 14794 size_t key_len = strlen(key); 14711 14795 14712 14796 if (vtype(obj) == T_CFUNC) { 14797 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 14798 + if (vtype(promoted) == T_FUNC) return js_try_get(js, promoted, key, out); 14713 14799 if (js_cfunc_try_get_own(js, obj, key, key_len, out)) return true; 14714 14800 ant_offset_t off = lkp_proto(js, obj, key, key_len); 14715 14801 if (off != 0) { ··· 14885 14971 } 14886 14972 14887 14973 ant_value_t js_getprop_proto(ant_t *js, ant_value_t obj, const char *key) { 14974 + if (vtype(obj) == T_CFUNC) { 14975 + ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); 14976 + if (vtype(promoted) == T_FUNC) obj = promoted; 14977 + } 14888 14978 size_t key_len = strlen(key); 14889 14979 ant_offset_t off = lkp_proto(js, obj, key, key_len); 14890 14980 return off == 0 ? js_mkundef() : propref_load(js, off);
+1 -1
src/silver/ops/property.h
··· 740 740 return true; 741 741 } 742 742 743 - return !is_err(mkprop_interned(js, as_obj, interned_key, val, 0)); 743 + return !is_err(mkprop_interned_exact(js, as_obj, interned_key, val, 0)); 744 744 } 745 745 746 746 static inline void sv_op_define_field(
+90
tests/test_native_cfunc_object_semantics_repro.cjs
··· 1 + function snapshotAssignmentAlias() { 2 + const f = fetch; 3 + const obj = {}; 4 + 5 + obj.a = f; 6 + obj.b = f; 7 + 8 + return { 9 + fetchType: typeof fetch, 10 + fetchName: fetch.name, 11 + sameStoredValue: obj.a === obj.b, 12 + sameAsFetchA: obj.a === fetch, 13 + sameAsFetchB: obj.b === fetch, 14 + storedNames: { 15 + a: obj.a && obj.a.name, 16 + b: obj.b && obj.b.name, 17 + }, 18 + }; 19 + } 20 + 21 + function snapshotPromotedState() { 22 + const sym = Symbol('native-fn-repro'); 23 + const originalProto = Object.getPrototypeOf(fetch); 24 + const customProto = { fromCustomProto: 42 }; 25 + 26 + fetch.extra = 'value-from-set'; 27 + Object.defineProperty(fetch, 'defined', { 28 + value: 'value-from-defineProperty', 29 + enumerable: true, 30 + configurable: true, 31 + writable: true, 32 + }); 33 + Object.defineProperty(fetch, sym, { 34 + value: 'symbol-value', 35 + enumerable: true, 36 + configurable: true, 37 + writable: true, 38 + }); 39 + Object.setPrototypeOf(fetch, customProto); 40 + 41 + const names = Object.getOwnPropertyNames(fetch); 42 + const symbols = Object.getOwnPropertySymbols(fetch); 43 + const descExtra = Object.getOwnPropertyDescriptor(fetch, 'extra'); 44 + const descDefined = Object.getOwnPropertyDescriptor(fetch, 'defined'); 45 + const descSym = Object.getOwnPropertyDescriptor(fetch, sym); 46 + 47 + const snapshot = { 48 + extraRead: fetch.extra, 49 + definedRead: fetch.defined, 50 + protoWasUpdated: Object.getPrototypeOf(fetch) === customProto, 51 + inheritedRead: fetch.fromCustomProto, 52 + ownNames: names, 53 + ownSymbols: symbols.map((entry) => String(entry)), 54 + hasExtraName: names.includes('extra'), 55 + hasDefinedName: names.includes('defined'), 56 + hasSymbol: symbols.includes(sym), 57 + extraDescriptor: descExtra && { 58 + value: descExtra.value, 59 + enumerable: descExtra.enumerable, 60 + configurable: descExtra.configurable, 61 + writable: descExtra.writable, 62 + }, 63 + definedDescriptor: descDefined && { 64 + value: descDefined.value, 65 + enumerable: descDefined.enumerable, 66 + configurable: descDefined.configurable, 67 + writable: descDefined.writable, 68 + }, 69 + symbolDescriptor: descSym && { 70 + value: descSym.value, 71 + enumerable: descSym.enumerable, 72 + configurable: descSym.configurable, 73 + writable: descSym.writable, 74 + }, 75 + }; 76 + 77 + delete fetch.extra; 78 + delete fetch.defined; 79 + delete fetch[sym]; 80 + Object.setPrototypeOf(fetch, originalProto); 81 + 82 + return snapshot; 83 + } 84 + 85 + const result = { 86 + assignmentAlias: snapshotAssignmentAlias(), 87 + promotedState: snapshotPromotedState(), 88 + }; 89 + 90 + console.log(JSON.stringify(result, null, 2));
+90
tests/test_native_method_descriptors.cjs
··· 146 146 pass = false; 147 147 } 148 148 149 + const aliasedFetch = fetch; 150 + const aliasTarget = {}; 151 + aliasTarget.a = aliasedFetch; 152 + aliasTarget.b = aliasedFetch; 153 + 154 + if ( 155 + aliasTarget.a !== aliasedFetch || 156 + aliasTarget.b !== aliasedFetch || 157 + aliasTarget.a !== aliasTarget.b 158 + ) { 159 + console.log('FAIL: ordinary assignment should preserve native function identity'); 160 + pass = false; 161 + } 162 + 163 + if (aliasTarget.a.name !== 'fetch' || aliasTarget.b.name !== 'fetch') { 164 + console.log('FAIL: ordinary assignment should not rename native functions'); 165 + pass = false; 166 + } 167 + 168 + const nativeReproSym = Symbol('native-method-descriptors'); 169 + const originalFetchProto = Object.getPrototypeOf(fetch); 170 + const customFetchProto = { fromCustomProto: 42 }; 171 + 172 + fetch.extra = 'value-from-set'; 173 + Object.defineProperty(fetch, 'defined', { 174 + value: 'value-from-defineProperty', 175 + enumerable: true, 176 + configurable: true, 177 + writable: true, 178 + }); 179 + Object.defineProperty(fetch, nativeReproSym, { 180 + value: 'symbol-value', 181 + enumerable: true, 182 + configurable: true, 183 + writable: true, 184 + }); 185 + Object.setPrototypeOf(fetch, customFetchProto); 186 + 187 + if (fetch.extra !== 'value-from-set' || fetch.defined !== 'value-from-defineProperty') { 188 + console.log('FAIL: promoted native functions should preserve own string properties on later reads'); 189 + pass = false; 190 + } 191 + 192 + if (fetch[nativeReproSym] !== 'symbol-value') { 193 + console.log('FAIL: promoted native functions should preserve own symbol properties on later reads'); 194 + pass = false; 195 + } 196 + 197 + if (Object.getPrototypeOf(fetch) !== customFetchProto || fetch.fromCustomProto !== 42) { 198 + console.log('FAIL: promoted native functions should preserve prototype updates'); 199 + pass = false; 200 + } 201 + 202 + const fetchOwnNames = Object.getOwnPropertyNames(fetch); 203 + const fetchOwnSymbols = Object.getOwnPropertySymbols(fetch); 204 + 205 + if (!fetchOwnNames.includes('extra') || !fetchOwnNames.includes('defined')) { 206 + console.log('FAIL: native function own property names should include promoted writes'); 207 + pass = false; 208 + } 209 + 210 + if (!fetchOwnSymbols.includes(nativeReproSym)) { 211 + console.log('FAIL: native function own property symbols should include promoted writes'); 212 + pass = false; 213 + } 214 + 215 + const extraDesc = Object.getOwnPropertyDescriptor(fetch, 'extra'); 216 + const definedDesc = Object.getOwnPropertyDescriptor(fetch, 'defined'); 217 + const symDesc = Object.getOwnPropertyDescriptor(fetch, nativeReproSym); 218 + 219 + if (!extraDesc || extraDesc.value !== 'value-from-set' || !extraDesc.enumerable || !extraDesc.configurable || !extraDesc.writable) { 220 + console.log('FAIL: promoted native string properties should keep their descriptors'); 221 + pass = false; 222 + } 223 + 224 + if (!definedDesc || definedDesc.value !== 'value-from-defineProperty' || !definedDesc.enumerable || !definedDesc.configurable || !definedDesc.writable) { 225 + console.log('FAIL: defineProperty on native functions should be reflected later'); 226 + pass = false; 227 + } 228 + 229 + if (!symDesc || symDesc.value !== 'symbol-value' || !symDesc.enumerable || !symDesc.configurable || !symDesc.writable) { 230 + console.log('FAIL: promoted native symbol properties should keep their descriptors'); 231 + pass = false; 232 + } 233 + 234 + delete fetch.extra; 235 + delete fetch.defined; 236 + delete fetch[nativeReproSym]; 237 + Object.setPrototypeOf(fetch, originalFetchProto); 238 + 149 239 if (pass) console.log('PASS');