Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788// Object.defineProperty on a dense array index: a store-equivalent descriptor (value +// writable/enumerable/configurable all explicitly true, no accessor) takes a dense fast// path; everything else materializes the dense elements into real properties. Every// expectation here was verified against node first.function assert(c, m) { if (!c) { console.log('FAIL:', m); process.exit(1); } }const D = { writable: true, enumerable: true, configurable: true };
// store-equivalent: value lands, array stays enumerable-ordered, descriptor reads back all-true{ const a = [1, 2, 3]; Object.defineProperty(a, '1', { value: 9, ...D }); assert(a[1] === 9, 'fast path stores the value'); const d = Object.getOwnPropertyDescriptor(a, '1'); assert(d.value === 9 && d.writable && d.enumerable && d.configurable, 'descriptor reads back'); assert(Object.keys(a).join(',') === '0,1,2', 'key order'); assert(JSON.stringify(a) === '[1,9,3]', 'json');}// beyond length extends like a plain store{ const a = [1]; Object.defineProperty(a, '5', { value: 7, ...D }); assert(a.length === 6 && a[5] === 7, 'length extension');}// partial descriptor on an EXISTING element keeps its attributes (all true for elements){ const a = [1, 2, 3]; Object.defineProperty(a, '1', { value: 9 }); const d = Object.getOwnPropertyDescriptor(a, '1'); assert(d.writable && d.enumerable && d.configurable, 'existing element keeps attributes'); assert(a[1] === 9, 'value updated');}// partial descriptor on a NEW index defaults every attribute to false{ const a = [1, 2, 3]; Object.defineProperty(a, '5', { value: 9 }); const d = Object.getOwnPropertyDescriptor(a, '5'); assert(d.writable === false && d.enumerable === false && d.configurable === false, 'new prop defaults false'); assert(a.length === 6, 'length extended'); let threw = false; try { Object.defineProperty(a, '5', { value: 10, writable: true }); } catch (e) { threw = true; } assert(threw, 'non-configurable redefine throws');}// accessors and the dense element they shadow{ const a = [1, 2, 3]; Object.defineProperty(a, '1', { get() { return 42; }, configurable: true, enumerable: true }); assert(a[1] === 42, 'getter shadows dense element'); assert(JSON.stringify(a) === '[1,42,3]', 'json consults getter');}// non-canonical keys are plain string keys, never elements{ const a = [1, 2, 3]; for (const k of ['01', '+1', '1e0', '4294967295']) Object.defineProperty(a, k, { value: 'K' + k, ...D }); assert(a.length === 3 && a[1] === 2, 'array untouched by non-canonical keys'); assert(Object.getOwnPropertyDescriptor(a, '01').value === 'K01', 'string key stored');}// preventExtensions: existing index redefine OK, new index throws{ const a = [1, 2, 3]; Object.preventExtensions(a); Object.defineProperty(a, '1', { value: 9, ...D }); assert(a[1] === 9, 'existing index redefine on non-extensible'); let threw = false; try { Object.defineProperty(a, '5', { value: 9, ...D }); } catch (e) { threw = true; } assert(threw, 'new index on non-extensible throws');}// sealed: configurable:true must throw, value unchanged{ const a = Object.seal([1, 2, 3]); let threw = false; try { Object.defineProperty(a, '1', { value: 9, ...D }); } catch (e) { threw = true; } assert(threw && a[1] === 2, 'sealed redefine with configurable:true throws');}// frozen: define fails, value unchanged{ const a = Object.freeze([1, 2, 3]); let threw = false; try { Object.defineProperty(a, '1', { value: 9, ...D }); } catch (e) { threw = true; } assert(threw && a[1] === 2, 'frozen define throws');}// hole filling{ const a = [1, , 3]; Object.defineProperty(a, '1', { value: 2, ...D }); assert(a[1] === 2 && (1 in a), 'hole filled');}console.log('PASS');