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.

call stack optimzations + hash changes

+357 -3
+4 -1
include/ant.h
··· 3 3 #include <stdbool.h> 4 4 #include <stddef.h> 5 5 #include <stdint.h> 6 + #include <stdio.h> 6 7 7 8 struct js; 8 9 typedef uint64_t jsval_t; ··· 83 84 void ant_register_library(const char *name, ant_library_init_fn init_fn); 84 85 85 86 typedef jsval_t (*js_getter_fn)(struct js *js, jsval_t obj, const char *key, size_t key_len); 86 - void js_set_getter(struct js *js, jsval_t obj, js_getter_fn getter); 87 + 88 + void js_set_getter(struct js *js, jsval_t obj, js_getter_fn getter); 89 + void js_print_stack_trace(FILE *stream);
+127 -2
src/ant.c
··· 137 137 UT_hash_handle hh; 138 138 } ant_library_t; 139 139 140 + typedef struct { 141 + char key[64]; 142 + jsoff_t offset; 143 + uint32_t hash; 144 + UT_hash_handle hh; 145 + } prop_cache_entry_t; 146 + 147 + typedef struct { 148 + jsoff_t obj_offset; 149 + prop_cache_entry_t *cache; 150 + uint32_t hit_count; 151 + UT_hash_handle hh; 152 + } obj_prop_cache_t; 153 + 140 154 static ant_library_t *library_registry = NULL; 141 155 static esm_module_cache_t global_module_cache = {NULL, 0}; 156 + static obj_prop_cache_t *global_property_cache = NULL; 142 157 143 158 void js_protect_init_memory(struct js *js) { 144 159 protected_brk = js_getbrk(js); ··· 1114 1129 return false; 1115 1130 } 1116 1131 1132 + static inline uint32_t hash_key(const char *key, size_t len) { 1133 + uint32_t hash = 2166136261u; 1134 + for (size_t i = 0; i < len && i < 64; i++) { 1135 + hash ^= (uint8_t)key[i]; 1136 + hash *= 16777619u; 1137 + } 1138 + return hash; 1139 + } 1140 + 1141 + static obj_prop_cache_t* get_obj_cache(jsoff_t obj_offset) { 1142 + obj_prop_cache_t *cache = NULL; 1143 + HASH_FIND(hh, global_property_cache, &obj_offset, sizeof(jsoff_t), cache); 1144 + if (!cache) { 1145 + cache = (obj_prop_cache_t *)malloc(sizeof(obj_prop_cache_t)); 1146 + if (!cache) return NULL; 1147 + cache->obj_offset = obj_offset; 1148 + cache->cache = NULL; 1149 + cache->hit_count = 0; 1150 + HASH_ADD(hh, global_property_cache, obj_offset, sizeof(jsoff_t), cache); 1151 + } 1152 + return cache; 1153 + } 1154 + 1155 + static void cache_property(jsoff_t obj_offset, const char *key, size_t key_len, jsoff_t prop_offset) { 1156 + if (key_len > 63) return; 1157 + 1158 + obj_prop_cache_t *obj_cache = get_obj_cache(obj_offset); 1159 + if (!obj_cache) return; 1160 + 1161 + prop_cache_entry_t *entry = NULL; 1162 + HASH_FIND_STR(obj_cache->cache, key, entry); 1163 + 1164 + if (!entry) { 1165 + entry = (prop_cache_entry_t *)malloc(sizeof(prop_cache_entry_t)); 1166 + if (!entry) return; 1167 + memcpy(entry->key, key, key_len); 1168 + entry->key[key_len] = '\0'; 1169 + entry->hash = hash_key(key, key_len); 1170 + HASH_ADD_STR(obj_cache->cache, key, entry); 1171 + } 1172 + entry->offset = prop_offset; 1173 + } 1174 + 1175 + static jsoff_t cache_lookup(jsoff_t obj_offset, const char *key, size_t key_len) { 1176 + if (key_len > 63) return 0; 1177 + 1178 + obj_prop_cache_t *obj_cache = NULL; 1179 + HASH_FIND(hh, global_property_cache, &obj_offset, sizeof(jsoff_t), obj_cache); 1180 + if (!obj_cache) return 0; 1181 + 1182 + prop_cache_entry_t *entry = NULL; 1183 + HASH_FIND_STR(obj_cache->cache, key, entry); 1184 + if (entry) { 1185 + obj_cache->hit_count++; 1186 + return entry->offset; 1187 + } 1188 + return 0; 1189 + } 1190 + 1191 + static void invalidate_obj_cache(jsoff_t obj_offset) { 1192 + obj_prop_cache_t *obj_cache = NULL; 1193 + HASH_FIND(hh, global_property_cache, &obj_offset, sizeof(jsoff_t), obj_cache); 1194 + if (!obj_cache) return; 1195 + 1196 + prop_cache_entry_t *entry, *tmp; 1197 + HASH_ITER(hh, obj_cache->cache, entry, tmp) { 1198 + HASH_DEL(obj_cache->cache, entry); 1199 + free(entry); 1200 + } 1201 + HASH_DEL(global_property_cache, obj_cache); 1202 + free(obj_cache); 1203 + } 1204 + 1117 1205 static jsval_t mkprop(struct js *js, jsval_t obj, jsval_t k, jsval_t v, bool is_const) { 1118 1206 jsoff_t koff = (jsoff_t) vdata(k); 1119 1207 jsoff_t b, head = (jsoff_t) vdata(obj); ··· 1124 1212 jsoff_t brk = js->brk | T_OBJ; 1125 1213 if (is_const) brk |= CONSTMASK; 1126 1214 memcpy(&js->mem[head], &brk, sizeof(brk)); 1215 + 1216 + // Invalidate property cache for this object when a new property is added 1217 + invalidate_obj_cache(head); 1218 + 1127 1219 return mkentity(js, (b & ~(3U | CONSTMASK)) | T_PROP, buf, sizeof(buf)); 1128 1220 } 1129 1221 ··· 1628 1720 return res; 1629 1721 } 1630 1722 1631 - static jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len) { 1723 + static inline jsoff_t lkp_inline(struct js *js, jsval_t obj, const char *buf, size_t len) { 1724 + if (len <= 63) { 1725 + jsoff_t cached = cache_lookup((jsoff_t)vdata(obj), buf, len); 1726 + if (cached != 0) return cached; 1727 + } 1728 + 1632 1729 jsoff_t off = loadoff(js, (jsoff_t) vdata(obj)) & ~(3U | CONSTMASK); 1633 1730 while (off < js->brk && off != 0) { 1634 1731 jsoff_t koff = loadoff(js, (jsoff_t) (off + sizeof(off))); 1635 1732 jsoff_t klen = (loadoff(js, koff) >> 2) - 1; 1636 1733 const char *p = (char *) &js->mem[koff + sizeof(koff)]; 1637 - if (streq(buf, len, p, klen)) return off; 1734 + if (streq(buf, len, p, klen)) { 1735 + if (len <= 63) cache_property((jsoff_t)vdata(obj), buf, len, off); 1736 + return off; 1737 + } 1638 1738 off = loadoff(js, off) & ~(3U | CONSTMASK); 1639 1739 } 1640 1740 return 0; 1741 + } 1742 + 1743 + static jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len) { 1744 + return lkp_inline(js, obj, buf, len); 1641 1745 } 1642 1746 1643 1747 static jsval_t try_dynamic_getter(struct js *js, jsval_t obj, const char *key, size_t key_len) { ··· 8373 8477 jsval_t getter_val = mkval(T_CFUNC, (size_t)(void *)getter); 8374 8478 js_set(js, obj, "__getter", getter_val); 8375 8479 } 8480 + 8481 + void js_print_stack_trace(FILE *stream) { 8482 + if (global_call_stack.depth > 0) { 8483 + for (int i = global_call_stack.depth - 1; i >= 0; i--) { 8484 + call_frame_t *frame = &global_call_stack.frames[i]; 8485 + fprintf(stream, " at "); 8486 + 8487 + if (frame->function_name) { 8488 + fprintf(stream, "%s", frame->function_name); 8489 + } else fprintf(stream, "<anonymous>"); 8490 + 8491 + fprintf(stream, " (\x1b[90m"); 8492 + 8493 + if (frame->filename) { 8494 + fprintf(stream, "%s:%d:%d", frame->filename, frame->line, frame->col); 8495 + } else fprintf(stream, "<unknown>"); 8496 + 8497 + fprintf(stream, "\x1b[0m)\n"); 8498 + } 8499 + } 8500 + }
+43
src/modules/io.c
··· 147 147 return js_mkundef(); 148 148 } 149 149 150 + static jsval_t js_console_assert(struct js *js, jsval_t *args, int nargs) { 151 + if (nargs < 1) return js_mkundef(); 152 + 153 + bool is_truthy = js_truthy(js, args[0]); 154 + if (is_truthy) return js_mkundef(); 155 + 156 + fprintf(stderr, "%sAssertion failed", ANSI_RED); 157 + if (nargs > 1) { 158 + fprintf(stderr, ": "); 159 + for (int i = 1; i < nargs; i++) { 160 + const char *space = i == 1 ? "" : " "; 161 + fprintf(stderr, "%s", space); 162 + 163 + if (js_type(args[i]) == JS_STR) { 164 + char *str = js_getstr(js, args[i], NULL); 165 + fprintf(stderr, "%s", str); 166 + } else { 167 + const char *str = js_str(js, args[i]); 168 + fprintf(stderr, "%s", str); 169 + } 170 + } 171 + } 172 + fprintf(stderr, "%s\n", ANSI_RESET); 173 + 174 + return js_mkundef(); 175 + } 176 + 177 + static jsval_t js_console_trace(struct js *js, jsval_t *args, int nargs) { 178 + fprintf(stderr, "Console Trace"); 179 + if (nargs > 0 && js_type(args[0]) == JS_STR) { 180 + fprintf(stderr, ": "); 181 + char *str = js_getstr(js, args[0], NULL); 182 + fprintf(stderr, "%s", str); 183 + } 184 + fprintf(stderr, "\n"); 185 + 186 + js_print_stack_trace(stderr); 187 + 188 + return js_mkundef(); 189 + } 190 + 150 191 void init_console_module() { 151 192 struct js *js = rt->js; 152 193 jsval_t console_obj = js_mkobj(js); ··· 155 196 js_set(js, console_obj, "log", js_mkfun(js_console_log)); 156 197 js_set(js, console_obj, "error", js_mkfun(js_console_error)); 157 198 js_set(js, console_obj, "warn", js_mkfun(js_console_warn)); 199 + js_set(js, console_obj, "assert", js_mkfun(js_console_assert)); 200 + js_set(js, console_obj, "trace", js_mkfun(js_console_trace)); 158 201 }
+58
tests/test_console_methods.cjs
··· 1 + // Test console methods (log, error, warn, assert, trace) 2 + console.log('=== Testing Console Methods ==='); 3 + 4 + // Test 1: console.log 5 + console.log('Test 1: console.log'); 6 + console.log(' - Basic message'); 7 + console.log(' - Multiple', 'arguments', 'work'); 8 + console.log(' - Numbers:', 42, 3.14); 9 + console.log(' - Objects:', { key: 'value' }); 10 + 11 + // Test 2: console.error 12 + console.error('Test 2: console.error - This should appear in red'); 13 + 14 + // Test 3: console.warn 15 + console.warn('Test 3: console.warn - This should appear in yellow'); 16 + 17 + // Test 4: console.assert with true condition 18 + console.assert(true, 'This should not print'); 19 + console.assert(1 === 1, 'Math check'); 20 + console.log('Test 4: console.assert (true conditions) - passed'); 21 + 22 + // Test 5: console.assert with false condition 23 + console.assert(false, 'Expected assertion failure'); 24 + console.assert(1 === 2, 'This assertion fails'); 25 + console.log('Test 5: console.assert (false conditions) - checked'); 26 + 27 + // Test 6: console.trace 28 + console.trace('Test 6: console.trace with message'); 29 + 30 + // Test 7: console.trace with nested functions 31 + function level3() { 32 + console.trace('Trace from level 3'); 33 + } 34 + 35 + function level2() { 36 + level3(); 37 + } 38 + 39 + function level1() { 40 + level2(); 41 + } 42 + 43 + console.log('Starting nested function trace:'); 44 + level1(); 45 + 46 + // Test 8: Complex objects 47 + const complexObj = { 48 + name: 'test', 49 + nested: { x: 1, y: 2 }, 50 + array: [1, 2, 3], 51 + bool: true 52 + }; 53 + console.log('Test 8: Complex object:', complexObj); 54 + 55 + // Test 9: Empty console.trace 56 + console.trace(); 57 + 58 + console.log('\n=== All console method tests completed ===');
+125
tests/test_property_lookup_cache.cjs
··· 1 + // Test property lookup caching - validates that the optimization doesn't break property access 2 + console.log('Testing property lookup cache optimization...'); 3 + 4 + // Test 1: Basic property access 5 + const obj1 = { name: 'test', value: 42, nested: { x: 1, y: 2 } }; 6 + console.assert(obj1.name === 'test', 'Basic property access failed'); 7 + console.assert(obj1.value === 42, 'Number property access failed'); 8 + console.assert(obj1.nested.x === 1, 'Nested property access failed'); 9 + console.log('โœ“ Test 1: Basic property access'); 10 + 11 + // Test 2: Multiple accesses (should hit cache on second access) 12 + const obj2 = { prop: 'cached' }; 13 + const access1 = obj2.prop; 14 + const access2 = obj2.prop; 15 + const access3 = obj2.prop; 16 + console.assert(access1 === 'cached', 'First access failed'); 17 + console.assert(access2 === 'cached', 'Cached access failed'); 18 + console.assert(access3 === 'cached', 'Second cached access failed'); 19 + console.log('โœ“ Test 2: Multiple property accesses (cache hits)'); 20 + 21 + // Test 3: Property modification (cache invalidation) 22 + const obj3 = { x: 1 }; 23 + console.assert(obj3.x === 1, 'Initial property value wrong'); 24 + obj3.x = 2; 25 + console.assert(obj3.x === 2, 'Property modification failed'); 26 + obj3.x = 3; 27 + console.assert(obj3.x === 3, 'Second modification failed'); 28 + console.log('โœ“ Test 3: Property modification with cache invalidation'); 29 + 30 + // Test 4: Dynamic property creation 31 + const obj4 = {}; 32 + obj4.dynamic = 'new'; 33 + console.assert(obj4.dynamic === 'new', 'Dynamic property creation failed'); 34 + obj4.another = 'property'; 35 + console.assert(obj4.another === 'property', 'Second dynamic property failed'); 36 + console.assert(obj4.dynamic === 'new', 'First dynamic property corrupted'); 37 + console.log('โœ“ Test 4: Dynamic property creation'); 38 + 39 + // Test 5: Method access (direct property access) 40 + const MyFunc = {}; 41 + MyFunc.getData = function() { return 'test data'; }; 42 + console.assert(MyFunc.getData() === 'test data', 'Method access failed'); 43 + console.log('โœ“ Test 5: Method access'); 44 + 45 + // Test 6: Properties with special names 46 + const obj6 = { __code: 'test', name: 'obj6' }; 47 + console.assert(obj6.name === 'obj6', 'Short key property failed'); 48 + console.log('โœ“ Test 6: Properties with special names'); 49 + 50 + // Test 7: Many properties in object (stress test for cache) 51 + const obj7 = {}; 52 + for (let i = 0; i < 50; i++) { 53 + obj7['prop' + i] = i; 54 + } 55 + console.assert(obj7.prop0 === 0, 'First property in large object failed'); 56 + console.assert(obj7.prop25 === 25, 'Middle property in large object failed'); 57 + console.assert(obj7.prop49 === 49, 'Last property in large object failed'); 58 + // Access again - should hit cache 59 + console.assert(obj7.prop25 === 25, 'Cached access in large object failed'); 60 + console.log('โœ“ Test 7: Many properties in object'); 61 + 62 + // Test 8: Property lookup in scope chain 63 + function outer() { 64 + const outerVar = 'outer'; 65 + function inner() { 66 + const innerVar = 'inner'; 67 + return outerVar + innerVar; 68 + } 69 + return inner(); 70 + } 71 + console.assert(outer() === 'outerinner', 'Scope chain lookup failed'); 72 + console.log('โœ“ Test 8: Property lookup in scope chain'); 73 + 74 + // Test 9: Long property names (shouldn't be cached but should work) 75 + const longName = 'verylongpropertyname' + 'thatshouldnot' + 'becached' + 'duetolength'; 76 + const obj9 = {}; 77 + obj9[longName] = 'value'; 78 + console.assert(obj9[longName] === 'value', 'Long property name access failed'); 79 + console.log('โœ“ Test 9: Long property names'); 80 + 81 + // Test 10: Computed properties 82 + const obj10 = {}; 83 + obj10.value = 10; 84 + obj10.double = function() { return this.value * 2; }; 85 + console.assert(obj10.double() === 20, 'Computed property failed'); 86 + console.log('โœ“ Test 10: Computed properties'); 87 + 88 + // Test 11: Property existence checks 89 + const obj11 = { exists: true }; 90 + if (obj11.exists) { 91 + console.log('โœ“ Test 11: Property existence check'); 92 + } else { 93 + throw new Error('Property existence check failed'); 94 + } 95 + 96 + // Test 12: Repeated property pattern (simulates real-world code) 97 + const objs = []; 98 + for (let i = 0; i < 10; i++) { 99 + const o = { id: i, name: 'obj' + i, value: i * 10 }; 100 + objs.push(o); 101 + } 102 + let sum = 0; 103 + for (let i = 0; i < objs.length; i++) { 104 + sum += objs[i].value; 105 + } 106 + console.assert(sum === 450, 'Repeated property pattern failed'); 107 + console.log('โœ“ Test 12: Repeated property access pattern'); 108 + 109 + // Test 13: console.assert with true condition (should not print anything) 110 + console.assert(true, 'This should not appear'); 111 + console.log('โœ“ Test 13: console.assert with true condition'); 112 + 113 + // Test 14: console.assert with false condition (should print message) 114 + console.assert(false, 'This is an expected assertion failure'); 115 + console.log('โœ“ Test 14: console.assert with false condition'); 116 + 117 + // Test 15: Accessing same property repeatedly across iterations 118 + const testObj = { counter: 0 }; 119 + for (let i = 0; i < 100; i++) { 120 + testObj.counter = i; 121 + } 122 + console.assert(testObj.counter === 99, 'Repeated property writes failed'); 123 + console.log('โœ“ Test 15: Repeated property writes'); 124 + 125 + console.log('\nโœ… All property lookup cache tests passed!');