diff --git a/examples/demo/events.js b/examples/demo/events.js index 713da4a..11f37d5 100644 --- a/examples/demo/events.js +++ b/examples/demo/events.js @@ -95,3 +95,31 @@ temp.removeEventListener('test', handler); temp.dispatchEvent('test', 'second call (not shown)'); console.log(' Handler was removed successfully'); + +console.log('\nExample 8: CustomEvent'); +const catFound = new CustomEvent('animalfound', { + detail: { + name: 'cat', + }, +}); +const dogFound = new CustomEvent('animalfound', { + detail: { + name: 'dog', + }, +}); + +const element = new EventTarget(); + +element.addEventListener('animalfound', e => console.log(' ', e.detail.name, 'found')); + +element.dispatchEvent(catFound); +element.dispatchEvent(dogFound); + +console.log('\nExample 9: CustomEvent with global dispatch'); +addEventListener('build:complete', e => { + console.log(' Build finished:', e.detail.duration + 'ms,', e.detail.files, 'files'); +}); + +dispatchEvent(new CustomEvent('build:complete', { + detail: { duration: 142, files: 37 }, +})); diff --git a/examples/spec/events.js b/examples/spec/events.js index 52c77a9..64085cc 100644 --- a/examples/spec/events.js +++ b/examples/spec/events.js @@ -166,4 +166,31 @@ removeEventListener('global-rem', globalHandler); dispatchEvent('global-rem'); test('global removeEventListener', globalRemoved, 0); +console.log('\nCustomEvent Tests\n'); + +const ce1 = new CustomEvent('ping'); +test('CustomEvent type', ce1.type, 'ping'); +test('CustomEvent detail defaults to null', ce1.detail, null); + +const ce2 = new CustomEvent('animalfound', { detail: { name: 'cat' } }); +test('CustomEvent detail', ce2.detail.name, 'cat'); + +let ceReceived = null; +const et5 = new EventTarget(); +et5.addEventListener('animalfound', e => { + ceReceived = e.detail.name; +}); +et5.dispatchEvent(ce2); +test('EventTarget dispatchEvent with CustomEvent', ceReceived, 'cat'); + +let ceGlobal = null; +addEventListener('custom-global', e => { + ceGlobal = e.detail.value; +}); +dispatchEvent(new CustomEvent('custom-global', { detail: { value: 42 } })); +test('global dispatchEvent with CustomEvent', ceGlobal, 42); + +const ce3 = new CustomEvent('tagged'); +test('CustomEvent Symbol.toStringTag', Object.prototype.toString.call(ce3), '[object CustomEvent]'); + summary(); diff --git a/include/shapes.h b/include/shapes.h index 6b5d669..7b6703f 100644 --- a/include/shapes.h +++ b/include/shapes.h @@ -62,7 +62,7 @@ bool ant_shape_clear_accessor_slot(ant_shape_t *shape, uint32_t slot); void ant_gc_shapes_begin(void); void ant_gc_shapes_mark(ant_shape_t *shape); -void ant_gc_shapes_sweep(void); +bool ant_gc_shapes_sweep(void); size_t ant_shape_total_bytes(void); extern uint32_t ant_ic_epoch_counter; diff --git a/meson/ant.version b/meson/ant.version index 8adc70f..c18d72b 100644 --- a/meson/ant.version +++ b/meson/ant.version @@ -1 +1 @@ -0.8.0 \ No newline at end of file +0.8.1 \ No newline at end of file diff --git a/src/ant.c b/src/ant.c index e7c7bcf..7806318 100644 --- a/src/ant.c +++ b/src/ant.c @@ -379,9 +379,14 @@ static ant_exotic_ops_t *obj_ensure_exotic_ops(ant_object_t *obj) { } static ant_object_t *obj_alloc(ant_t *js, uint8_t type_tag, uint8_t inobj_limit) { - size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); - if (threshold < 2048) threshold = 2048; - if (js->obj_arena.live_count >= threshold) gc_run(js); +#ifdef ANT_JIT + if (__builtin_expect(js->jit_active_depth == 0, 1)) +#endif + { + size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); + if (threshold < 2048) threshold = 2048; + if (js->obj_arena.live_count >= threshold) gc_run(js); + } ant_object_t *obj = (ant_object_t *)fixed_arena_alloc(&js->obj_arena); if (!obj) return NULL; @@ -8439,330 +8444,6 @@ static ant_value_t builtin_string_endsWith(ant_t *js, ant_value_t *args, int nar return mkval(T_BOOL, memcmp(str_ptr + str_len - search_len, search_ptr, search_len) == 0 ? 1 : 0); } -static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val); - ant_value_t str = js_tostring_val(js, this_unwrapped); - if (is_err(str)) return str; - if (nargs < 1) return str; - - if (is_object_type(args[0])) { - bool called = false; - ant_value_t replacement_arg = nargs > 1 ? args[1] : js_mkundef(); - ant_value_t call_args[2] = { str, replacement_arg }; - ant_value_t dispatched = maybe_call_symbol_method( - js, args[0], get_replace_sym(), args[0], call_args, 2, &called - ); - if (is_err(dispatched)) return dispatched; - if (called) return dispatched; - } - if (nargs < 2) return str; - - ant_value_t search = args[0]; - ant_value_t replacement = args[1]; - - ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *)(uintptr_t)(str_off); - - bool is_regex = false; - bool global_flag = false; - bool is_func_replacement = (vtype(replacement) == T_FUNC); - char *pattern_buf = NULL; - ant_offset_t pattern_len = 0; - - if (vtype(search) == T_OBJ) { - ant_offset_t pattern_off = lkp(js, search, "source", 6); - if (pattern_off == 0) goto not_regex; - - ant_value_t pattern_val = propref_load(js, pattern_off); - if (vtype(pattern_val) != T_STR) goto not_regex; - - is_regex = true; - ant_offset_t plen, poff = vstr(js, pattern_val, &plen); - pattern_len = plen; - pattern_buf = (char *)ant_calloc(plen + 1); - if (!pattern_buf) return js_mkerr(js, "oom"); - memcpy(pattern_buf, (const void *)(uintptr_t)poff, plen); - pattern_buf[plen] = '\0'; - - ant_offset_t flags_off = lkp(js, search, "flags", 5); - if (flags_off == 0) { free(pattern_buf); pattern_buf = NULL; goto not_regex; } - - ant_value_t flags_val = propref_load(js, flags_off); - if (vtype(flags_val) != T_STR) { free(pattern_buf); pattern_buf = NULL; goto not_regex; } - - ant_offset_t flen, foff = vstr(js, flags_val, &flen); - const char *flags_str = (char *)(uintptr_t)(foff); - for (ant_offset_t i = 0; i < flen; i++) { - if (flags_str[i] == 'g') global_flag = true; - } - } - not_regex: - - ant_offset_t repl_len = 0; - const char *repl_ptr = NULL; - if (!is_func_replacement) { - if (vtype(replacement) != T_STR) { if (pattern_buf) free(pattern_buf); return str; } - ant_offset_t repl_off; - repl_off = vstr(js, replacement, &repl_len); - repl_ptr = (char *)(uintptr_t)(repl_off); - } - - size_t result_cap = str_len + repl_len + 256; - size_t result_len = 0; - char *result = (char *)ant_calloc(result_cap); - if (!result) return js_mkerr(js, "oom"); - -#define ENSURE_RESULT_CAP(need) do { \ - if (result_len + (need) >= result_cap) { \ - result_cap = (result_len + (need) + 1) * 2; \ - char *nr = (char *)ant_realloc(result, result_cap); \ - if (!nr) return js_mkerr(js, "oom"); \ - result = nr; \ - } \ -} while(0) - - if (is_regex) { - size_t pcre2_cap = pattern_len * 2 + 16; - char *pcre2_pattern = (char *)ant_calloc(pcre2_cap); - if (!pcre2_pattern) return js_mkerr(js, "oom"); - size_t pcre2_len = js_to_pcre2_pattern(pattern_buf, pattern_len, pcre2_pattern, pcre2_cap); - - uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF; - int errcode; - PCRE2_SIZE erroffset; - pcre2_code *re = pcre2_compile((PCRE2_SPTR)pcre2_pattern, pcre2_len, options, &errcode, &erroffset, NULL); - free(pcre2_pattern); - if (re == NULL) return js_mkerr(js, "invalid regex pattern"); - - pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); - uint32_t capture_count; - pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &capture_count); - - PCRE2_SIZE pos = 0; - bool replaced = false; - - while (pos <= str_len) { - int rc = pcre2_match(re, (PCRE2_SPTR)str_ptr, str_len, pos, 0, match_data, NULL); - if (rc < 0) break; - - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); - PCRE2_SIZE match_start = ovector[0]; - PCRE2_SIZE match_end = ovector[1]; - - PCRE2_SIZE before_len = match_start - pos; - ENSURE_RESULT_CAP(before_len); - memcpy(result + result_len, str_ptr + pos, before_len); - result_len += before_len; - - if (is_func_replacement) { - int nargs_cb = 1 + capture_count + 2; - ant_value_t *cb_args = (ant_value_t *)ant_calloc(nargs_cb * sizeof(ant_value_t)); - if (!cb_args) { - pcre2_match_data_free(match_data); - pcre2_code_free(re); - return js_mkerr(js, "oom"); - } - cb_args[0] = js_mkstr(js, str_ptr + match_start, match_end - match_start); - for (uint32_t i = 1; i <= capture_count; i++) { - PCRE2_SIZE cap_start = ovector[2*i]; - PCRE2_SIZE cap_end = ovector[2*i+1]; - if (cap_start == PCRE2_UNSET) { - cb_args[i] = js_mkundef(); - } else { - cb_args[i] = js_mkstr(js, str_ptr + cap_start, cap_end - cap_start); - } - } - cb_args[1 + capture_count] = tov((double)match_start); - cb_args[2 + capture_count] = str; - - ant_value_t cb_result = sv_vm_call(js->vm, js, replacement, js_mkundef(), cb_args, nargs_cb, NULL, false); - free(cb_args); - - if (vtype(cb_result) == T_ERR) { - pcre2_match_data_free(match_data); - pcre2_code_free(re); - return cb_result; - } - - if (vtype(cb_result) == T_STR) { - ant_offset_t cb_len, cb_off = vstr(js, cb_result, &cb_len); - ENSURE_RESULT_CAP(cb_len); - memcpy(result + result_len, (const void *)(uintptr_t)cb_off, cb_len); - result_len += cb_len; - } else { - char numbuf[32]; - size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); - ENSURE_RESULT_CAP(n); - memcpy(result + result_len, numbuf, n); - result_len += n; - } - } else { - repl_capture_t caps_buf[16], *caps = (int)capture_count <= 16 ? caps_buf : ant_calloc(sizeof(repl_capture_t) * capture_count); - for (uint32_t ci = 0; ci < capture_count; ci++) { - PCRE2_SIZE cs = ovector[2*(ci+1)], ce = ovector[2*(ci+1)+1]; - if (cs != PCRE2_UNSET) caps[ci] = (repl_capture_t){ str_ptr + cs, ce - cs }; - else caps[ci] = (repl_capture_t){ NULL, 0 }; - } - repl_template(repl_ptr, repl_len, str_ptr + match_start, match_end - match_start, - str_ptr, str_len, match_start, caps, (int)capture_count, &result, &result_len, &result_cap); - if (caps != caps_buf) free(caps); - } - - if (match_start == match_end) { - if (pos < str_len) { - ENSURE_RESULT_CAP(1); - result[result_len++] = str_ptr[pos]; - } - pos = match_end + 1; - } else pos = match_end; - - replaced = true; - if (!global_flag) break; - } - - if (pos < str_len) { - size_t remaining = str_len - pos; - ENSURE_RESULT_CAP(remaining); - memcpy(result + result_len, str_ptr + pos, remaining); - result_len += remaining; - } - - pcre2_match_data_free(match_data); pcre2_code_free(re); - if (pattern_buf) free(pattern_buf); - - ant_value_t ret = replaced ? js_mkstr(js, result, result_len) : str; - free(result); return ret; - } else { - if (vtype(search) != T_STR) { free(result); return str; } - ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *search_ptr = (char *)(uintptr_t)(search_off); - - if (search_len > str_len) { free(result); return str; } - - for (ant_offset_t i = 0; i <= str_len - search_len; i++) { - if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { - ENSURE_RESULT_CAP(i); - memcpy(result + result_len, str_ptr, i); - result_len += i; - - if (is_func_replacement) { - ant_value_t match_str = js_mkstr(js, search_ptr, search_len); - ant_value_t cb_args[1] = { match_str }; - ant_value_t cb_result = sv_vm_call(js->vm, js, replacement, js_mkundef(), cb_args, 1, NULL, false); - - if (vtype(cb_result) == T_ERR) { free(result); return cb_result; } - - if (vtype(cb_result) == T_STR) { - ant_offset_t cb_len, cb_off = vstr(js, cb_result, &cb_len); - ENSURE_RESULT_CAP(cb_len); - memcpy(result + result_len, (const void *)(uintptr_t)cb_off, cb_len); - result_len += cb_len; - } else { - char numbuf[32]; - size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); - ENSURE_RESULT_CAP(n); - memcpy(result + result_len, numbuf, n); - result_len += n; - } - } else { - ENSURE_RESULT_CAP(repl_len); - memcpy(result + result_len, repl_ptr, repl_len); - result_len += repl_len; - } - - ant_offset_t after_start = i + search_len; - ant_offset_t after_len = str_len - after_start; - if (after_len > 0) { - ENSURE_RESULT_CAP(after_len); - memcpy(result + result_len, str_ptr + after_start, after_len); - result_len += after_len; - } - ant_value_t ret = js_mkstr(js, result, result_len); - free(result); - return ret; - } - } - free(result); - return str; - } -#undef ENSURE_RESULT_CAP -} - -static ant_value_t builtin_string_replaceAll(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t str = to_string_val(js, js->this_val); - if (vtype(str) != T_STR) return js_mkerr(js, "replaceAll called on non-string"); - if (nargs < 2) return str; - - ant_value_t search = args[0]; - ant_value_t replacement = args[1]; - - if (vtype(search) != T_STR) return js_mkerr(js, "replaceAll requires string search pattern"); - if (vtype(replacement) != T_STR) return js_mkerr(js, "replaceAll requires string replacement"); - - ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *)(uintptr_t)(str_off); - - ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *search_ptr = (char *)(uintptr_t)(search_off); - - ant_offset_t repl_len, repl_off = vstr(js, replacement, &repl_len); - const char *repl_ptr = (char *)(uintptr_t)(repl_off); - - if (search_len == 0) { - size_t total_len = str_len + (str_len + 1) * repl_len; - char *result = (char *)ant_calloc(total_len + 1); - if (!result) return js_mkerr(js, "oom"); - - size_t pos = 0; - memcpy(result + pos, repl_ptr, repl_len); - pos += repl_len; - for (ant_offset_t i = 0; i < str_len; i++) { - result[pos++] = str_ptr[i]; - memcpy(result + pos, repl_ptr, repl_len); - pos += repl_len; - } - ant_value_t ret = js_mkstr(js, result, pos); - free(result); - return ret; - } - - ant_offset_t count = 0; - for (ant_offset_t i = 0; i <= str_len - search_len; i++) { - if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { - count++; - i += search_len - 1; - } - } - - if (count == 0) return str; - - size_t result_total = str_len - (count * search_len) + (count * repl_len); - char *result = (char *)ant_calloc(result_total + 1); - if (!result) return js_mkerr(js, "oom"); - - size_t result_pos = 0; - ant_offset_t str_pos = 0; - - while (str_pos <= str_len - search_len) { - if (memcmp(str_ptr + str_pos, search_ptr, search_len) == 0) { - memcpy(result + result_pos, repl_ptr, repl_len); - result_pos += repl_len; - str_pos += search_len; - } else { - result[result_pos++] = str_ptr[str_pos++]; - } - } - - while (str_pos < str_len) { - result[result_pos++] = str_ptr[str_pos++]; - } - - ant_value_t ret = js_mkstr(js, result, result_pos); - free(result); - return ret; -} - - static ant_value_t builtin_string_template(ant_t *js, ant_value_t *args, int nargs) { ant_value_t str = to_string_val(js, js->this_val); if (vtype(str) != T_STR) return js_mkerr(js, "template called on non-string"); @@ -11948,8 +11629,6 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, string_proto, js_mkstr(js, "includes", 8), js_mkfun(builtin_string_includes)); js_setprop(js, string_proto, js_mkstr(js, "startsWith", 10), js_mkfun(builtin_string_startsWith)); js_setprop(js, string_proto, js_mkstr(js, "endsWith", 8), js_mkfun(builtin_string_endsWith)); - js_setprop(js, string_proto, js_mkstr(js, "replace", 7), js_mkfun(builtin_string_replace)); - js_setprop(js, string_proto, js_mkstr(js, "replaceAll", 10), js_mkfun(builtin_string_replaceAll)); js_setprop(js, string_proto, js_mkstr(js, "template", 8), js_mkfun(builtin_string_template)); js_setprop(js, string_proto, js_mkstr(js, "charCodeAt", 10), js_mkfun(builtin_string_charCodeAt)); js_setprop(js, string_proto, js_mkstr(js, "codePointAt", 11), js_mkfun(builtin_string_codePointAt)); diff --git a/src/gc/gc.c b/src/gc/gc.c index 05c5a33..dc0673d 100644 --- a/src/gc/gc.c +++ b/src/gc/gc.c @@ -122,6 +122,9 @@ void gc_run_minor(ant_t *js) { void gc_maybe(ant_t *js) { if (__builtin_expect(gc_disabled, 0)) return; +#ifdef ANT_JIT + if (__builtin_expect(js->jit_active_depth > 0, 0)) return; +#endif if (++gc_tick < GC_MIN_TICK) return; size_t live = js->obj_arena.live_count; diff --git a/src/gc/objects.c b/src/gc/objects.c index b5a64fe..9b5d3e1 100644 --- a/src/gc/objects.c +++ b/src/gc/objects.c @@ -644,8 +644,8 @@ void gc_objects_run(ant_t *js, gc_str_mark_fn str_mark) { gc_mark_roots(js); gc_sweep_regex_cache(); gc_sweep(js); - ant_gc_shapes_sweep(); - ant_ic_epoch_bump(); + + if (ant_gc_shapes_sweep()) ant_ic_epoch_bump(); gc_promote_survivors(js); ant_fixed_arena_t *ca = &js->closure_arena; diff --git a/src/modules/events.c b/src/modules/events.c index f2eedfb..8edb191 100644 --- a/src/modules/events.c +++ b/src/modules/events.c @@ -68,47 +68,52 @@ static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) return (EventType **)(uintptr_t)js_getnum(slot); } -static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { - EventType **events = get_or_create_emitter_events(js, this_obj); - if (events == NULL) return NULL; +static EventType *make_event_type(const char *event_type) { + size_t etlen = strlen(event_type); + EventType *evt = ant_calloc(sizeof(EventType) + etlen + 1); + if (!evt) return NULL; + + evt->event_type = (char *)(evt + 1); + memcpy(evt->event_type, event_type, etlen + 1); + evt->listener_count = 0; + return evt; +} + +static EventType *find_or_create_global_event_type(const char *event_type) { EventType *evt = NULL; - HASH_FIND_STR(*events, event_type, evt); + HASH_FIND_STR(global_events, event_type, evt); + + if (evt == NULL) { + evt = make_event_type(event_type); + if (!evt) return NULL; + HASH_ADD_KEYPTR(hh, global_events, evt->event_type, strlen(evt->event_type), evt); + } + return evt; } -static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { +static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { EventType **events = get_or_create_emitter_events(js, this_obj); if (events == NULL) return NULL; EventType *evt = NULL; HASH_FIND_STR(*events, event_type, evt); - if (evt == NULL) { - size_t etlen = strlen(event_type); - evt = ant_calloc(sizeof(EventType) + etlen + 1); - if (!evt) return NULL; - evt->event_type = (char *)(evt + 1); - memcpy(evt->event_type, event_type, etlen + 1); - evt->listener_count = 0; - HASH_ADD_KEYPTR(hh, *events, evt->event_type, etlen, evt); - } - return evt; } -static EventType *find_or_create_global_event_type(const char *event_type) { +static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { + EventType **events = get_or_create_emitter_events(js, this_obj); + if (events == NULL) return NULL; + EventType *evt = NULL; - HASH_FIND_STR(global_events, event_type, evt); + HASH_FIND_STR(*events, event_type, evt); if (evt == NULL) { - size_t etlen = strlen(event_type); - evt = ant_calloc(sizeof(EventType) + etlen + 1); + evt = make_event_type(event_type); if (!evt) return NULL; - evt->event_type = (char *)(evt + 1); - memcpy(evt->event_type, event_type, etlen + 1); - evt->listener_count = 0; - HASH_ADD_KEYPTR(hh, global_events, evt->event_type, etlen, evt); + HASH_ADD_KEYPTR(hh, *events, evt->event_type, strlen(evt->event_type), evt); } return evt; @@ -120,31 +125,17 @@ static inline EventType *find_global_event_type(const char *event_type) { return evt; } -// addEventListener(eventType, listener, options) -static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - - if (nargs < 2) { - return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); - } - - char *event_type = js_getstr(js, args[0], NULL); - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); - if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); - - EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); +static ant_value_t add_event_listener_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { if (evt == NULL) return js_mkerr(js, "failed to create event type"); - - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { - return js_mkerr(js, "maximum number of listeners for event type '%s' reached", event_type); - } - + if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) + return js_mkerr(js, "maximum number of listeners for event type '%s' reached", evt->event_type); + bool once = false; if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { ant_value_t once_val = js_get(js, args[2], "once"); if (vtype(once_val) != T_UNDEF) once = js_truthy(js, once_val); } - + EventListener *listener = &evt->listeners[evt->listener_count++]; listener->listener = args[1]; listener->once = once; @@ -152,171 +143,115 @@ static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, in return js_mkundef(); } +// addEventListener(eventType, listener, options) +static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 2) return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); + char *event_type = js_getstr(js, args[0], NULL); + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); + if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); + return add_event_listener_to(js, args, nargs, find_or_create_emitter_event_type(js, js_getthis(js), event_type)); +} + // addEventListener(eventType, listener, options) static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) { - return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); - } - + if (nargs < 2) return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); char *event_type = js_getstr(js, args[0], NULL); - if (event_type == NULL) { - return js_mkerr(js, "eventType must be a string"); - } - - if (vtype(args[1]) != T_FUNC) { - return js_mkerr(js, "listener must be a function"); - } - - EventType *evt = find_or_create_global_event_type(event_type); - if (evt == NULL) { - return js_mkerr(js, "failed to create event type"); - } - - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { - return js_mkerr(js, "maximum number of listeners for event type '%s' reached", event_type); - } + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); + if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); + return add_event_listener_to(js, args, nargs, find_or_create_global_event_type(event_type)); +} + +static ant_value_t remove_event_listener_from(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { + if (evt == NULL) goto done; - bool once = false; - if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { - ant_value_t once_val = js_get(js, args[2], "once"); - if (vtype(once_val) != T_UNDEF) once = js_truthy(js, once_val); + for (int i = 0; i < evt->listener_count; i++) { + if (evt->listeners[i].listener != args[1]) continue; + remove_listener_at(evt, i); + goto done; } - EventListener *listener = &evt->listeners[evt->listener_count++]; - listener->listener = args[1]; - listener->once = once; - - return js_mkundef(); + done: return js_mkundef(); } // removeEventListener(eventType, listener) static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - - if (nargs < 2) { - return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); - } - + if (nargs < 2) return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); char *event_type = js_getstr(js, args[0], NULL); if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); - - EventType *evt = find_emitter_event_type(js, this_obj, event_type); - if (evt == NULL) return js_mkundef(); - - for (int i = 0; i < evt->listener_count; i++) { - if (evt->listeners[i].listener == args[1]) { - remove_listener_at(evt, i); break; - } - } - - return js_mkundef(); + return remove_event_listener_from(js, args, nargs, find_emitter_event_type(js, js_getthis(js), event_type)); } // removeEventListener(eventType, listener) static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) { - return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); - } - + if (nargs < 2) return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); char *event_type = js_getstr(js, args[0], NULL); if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); - - EventType *evt = find_global_event_type(event_type); - if (evt == NULL) return js_mkundef(); - - for (int i = 0; i < evt->listener_count; i++) { - if (evt->listeners[i].listener == args[1]) { - remove_listener_at(evt, i); break; - } - } - - return js_mkundef(); + return remove_event_listener_from(js, args, nargs, find_global_event_type(event_type)); } -// dispatchEvent(eventType, eventData) -static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - - if (nargs < 1) { - return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); - } - - char *event_type = js_getstr(js, args[0], NULL); - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); - - EventType *evt = find_emitter_event_type(js, this_obj, event_type); +static ant_value_t dispatch_event_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt, ant_value_t target) { if (evt == NULL || evt->listener_count == 0) return js_true; - - ant_value_t event_obj = js_mkobj(js); - js_set(js, event_obj, "type", args[0]); - js_set(js, event_obj, "target", this_obj); - js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); - - if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { - js_set(js, event_obj, "detail", args[1]); + + ant_value_t event_obj; + const char *event_type; + + if (vtype(args[0]) == T_OBJ) { + ant_value_t type_val = js_get(js, args[0], "type"); + event_type = js_getstr(js, type_val, NULL); + if (event_type == NULL) return js_mkerr(js, "event.type must be a string"); + event_obj = args[0]; + } else { + event_type = js_getstr(js, args[0], NULL); + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); + event_obj = js_mkobj(js); + js_set(js, event_obj, "type", args[0]); + js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); + if (nargs >= 2 && vtype(args[1]) != T_UNDEF) js_set(js, event_obj, "detail", args[1]); } - + + if (is_object_type(target)) js_set(js, event_obj, "target", target); ant_value_t listener_args[1] = {event_obj}; - int i = 0; - while (i < evt->listener_count) { + + for (int i = 0; i < evt->listener_count;) { EventListener *listener = &evt->listeners[i]; ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, 1, NULL, false); - - if (vtype(result) == T_ERR) { + + if (vtype(result) == T_ERR) fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); - } - - if (listener->once) { - remove_listener_at(evt, i); - } else i++; + + if (listener->once) remove_listener_at(evt, i); + else i++; } - + return js_true; } +// dispatchEvent(eventType, eventData) +static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); + + ant_value_t this_obj = js_getthis(js); + char *event_type = vtype(args[0]) == T_OBJ + ? js_getstr(js, js_get(js, args[0], "type"), NULL) + : js_getstr(js, args[0], NULL); + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); + + return dispatch_event_to(js, args, nargs, find_emitter_event_type(js, this_obj, event_type), this_obj); +} + // dispatchEvent(eventType, eventData) static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 1) { - return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); - } - - char *event_type = js_getstr(js, args[0], NULL); + if (nargs < 1) return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); + + char *event_type = vtype(args[0]) == T_OBJ + ? js_getstr(js, js_get(js, args[0], "type"), NULL) + : js_getstr(js, args[0], NULL); if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); - - EventType *evt = find_global_event_type(event_type); - if (evt == NULL || evt->listener_count == 0) { - return js_true; - } - - ant_value_t event_obj = js_mkobj(js); - js_set(js, event_obj, "type", args[0]); - js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); - - if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { - js_set(js, event_obj, "detail", args[1]); - } - - ant_value_t listener_args[1] = {event_obj}; - int i = 0; - while (i < evt->listener_count) { - EventListener *listener = &evt->listeners[i]; - ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, 1, NULL, false); - - if (vtype(result) == T_ERR) { - fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); - } - - if (listener->once) { - remove_listener_at(evt, i); - } else i++; - } - - return js_true; + + return dispatch_event_to(js, args, nargs, find_global_event_type(event_type), js_mkundef()); } -static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; - +static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int nargs) { EventType *evt, *tmp; ant_value_t result = js_mkobj(js); @@ -330,7 +265,6 @@ static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int narg ant_value_t listener_info = js_mkobj(js); js_set(js, listener_info, "listener", evt->listeners[j].listener); js_set(js, listener_info, "once", js_bool(evt->listeners[j].once)); - js_set(js, listeners_array, key, listener_info); } @@ -341,96 +275,41 @@ static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int narg return result; } -// EventEmitter.prototype.on(event, listener) -static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - - if (nargs < 2) { - return js_mkerr(js, "on requires 2 arguments (event, listener)"); - } - +static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once) { + if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); char *event_type = js_getstr(js, args[0], NULL); - if (event_type == NULL) { - return js_mkerr(js, "event must be a string"); - } - - if (vtype(args[1]) != T_FUNC) { - return js_mkerr(js, "listener must be a function"); - } - + if (event_type == NULL) return js_mkerr(js, "event must be a string"); + if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); + + ant_value_t this_obj = js_getthis(js); EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); - if (evt == NULL) { - return js_mkerr(js, "failed to create event type"); - } - - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { + if (evt == NULL) return js_mkerr(js, "failed to create event type"); + if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) return js_mkerr(js, "maximum number of listeners for event '%s' reached", event_type); - } - + EventListener *listener = &evt->listeners[evt->listener_count++]; listener->listener = args[1]; - listener->once = false; - + listener->once = once; return this_obj; } +// EventEmitter.prototype.on(event, listener) +static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { + return js_eventemitter_add(js, args, nargs, false); +} + // EventEmitter.prototype.once(event, listener) static ant_value_t js_eventemitter_once(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - - if (nargs < 2) { - return js_mkerr(js, "once requires 2 arguments (event, listener)"); - } - - char *event_type = js_getstr(js, args[0], NULL); - if (event_type == NULL) { - return js_mkerr(js, "event must be a string"); - } - - if (vtype(args[1]) != T_FUNC) { - return js_mkerr(js, "listener must be a function"); - } - - EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); - if (evt == NULL) { - return js_mkerr(js, "failed to create event type"); - } - - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { - return js_mkerr(js, "maximum number of listeners for event '%s' reached", event_type); - } - - EventListener *listener = &evt->listeners[evt->listener_count++]; - listener->listener = args[1]; - listener->once = true; - - return this_obj; + return js_eventemitter_add(js, args, nargs, true); } // EventEmitter.prototype.off(event, listener) static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { - ant_value_t this_obj = js_getthis(js); - - if (nargs < 2) { - return js_mkerr(js, "off requires 2 arguments (event, listener)"); - } - + if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); char *event_type = js_getstr(js, args[0], NULL); - if (event_type == NULL) { - return js_mkerr(js, "event must be a string"); - } - - EventType *evt = find_emitter_event_type(js, this_obj, event_type); - if (evt == NULL) { - return this_obj; - } - - for (int i = 0; i < evt->listener_count; i++) { - if (evt->listeners[i].listener == args[1]) { - remove_listener_at(evt, i); break; - } - } - + if (event_type == NULL) return js_mkerr(js, "event must be a string"); + ant_value_t this_obj = js_getthis(js); + remove_event_listener_from(js, args, nargs, find_emitter_event_type(js, this_obj, event_type)); return this_obj; } @@ -454,19 +333,16 @@ static ant_value_t js_eventemitter_emit(ant_t *js, ant_value_t *args, int nargs) int listener_nargs = nargs - 1; ant_value_t *listener_args = (listener_nargs > 0) ? &args[1] : NULL; - - int i = 0; - while (i < evt->listener_count) { + + for (int i = 0; i < evt->listener_count;) { EventListener *listener = &evt->listeners[i]; ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, listener_nargs, NULL, false); - - if (vtype(result) == T_ERR) { + + if (vtype(result) == T_ERR) fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); - } - - if (listener->once) { - remove_listener_at(evt, i); - } else i++; + + if (listener->once) remove_listener_at(evt, i); + else i++; } return js_true; @@ -536,6 +412,25 @@ static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int return result; } +// new CustomEvent(type, { detail: ... }) +static ant_value_t js_customevent_ctor(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return js_mkerr(js, "CustomEvent requires a type argument"); + + char *event_type = js_getstr(js, args[0], NULL); + if (event_type == NULL) return js_mkerr(js, "type must be a string"); + + ant_value_t event_obj = js_getthis(js); + js_set(js, event_obj, "type", args[0]); + js_set(js, event_obj, "detail", js_mknull()); + + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { + ant_value_t detail = js_get(js, args[1], "detail"); + if (vtype(detail) != T_UNDEF) js_set(js, event_obj, "detail", detail); + } + + return js_mkundef(); +} + ant_value_t events_library(ant_t *js) { ant_value_t lib = js_mkobj(js); @@ -571,7 +466,8 @@ void init_events_module() { ant_value_t eventtarget_ctor = js_mkobj(js); ant_value_t eventtarget_proto = js_mkobj(js); - + ant_value_t customevent_ctor = js_mkobj(js); + js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); js_set(js, eventtarget_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method)); js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); @@ -581,10 +477,24 @@ void init_events_module() { js_mkprop_fast(js, eventtarget_ctor, "name", 4, ANT_STRING("EventTarget")); js_set_descriptor(js, eventtarget_ctor, "name", 4, 0); + ant_value_t customevent_proto = js_mkobj(js); + js_set_sym(js, customevent_proto, get_toStringTag_sym(), js_mkstr(js, "CustomEvent", 11)); + + js_set_slot(customevent_ctor, SLOT_CFUNC, js_mkfun(js_customevent_ctor)); + js_mkprop_fast(js, customevent_ctor, "prototype", 9, customevent_proto); + js_mkprop_fast(js, customevent_ctor, "name", 4, ANT_STRING("CustomEvent")); + js_set_descriptor(js, customevent_ctor, "name", 4, 0); + + ant_value_t customevent_fn = js_obj_to_func(customevent_ctor); + js_set(js, customevent_proto, "constructor", customevent_fn); + js_set_descriptor(js, customevent_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); + js_set(js, global, "addEventListener", js_mkfun(js_add_event_listener)); js_set(js, global, "removeEventListener", js_mkfun(js_remove_event_listener)); js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event)); js_set(js, global, "getEventListeners", js_mkfun(js_get_event_listeners)); + + js_set(js, global, "CustomEvent", customevent_fn); js_set(js, global, "EventTarget", js_obj_to_func_ex(eventtarget_ctor, SV_CALL_IS_DEFAULT_CTOR)); } diff --git a/src/modules/regex.c b/src/modules/regex.c index 2ce9592..17adbe8 100644 --- a/src/modules/regex.c +++ b/src/modules/regex.c @@ -1308,6 +1308,180 @@ ant_value_t do_regex_match_pcre2( return result_arr; } +static bool str_buf_append(char **buf, size_t *len, size_t *cap, const char *data, size_t n) { + if (n == 0) return true; + if (*len + n >= *cap) { + size_t new_cap = (*len + n + 1) * 2; + char *nb = (char *)ant_realloc(*buf, new_cap); + if (!nb) return false; + *buf = nb; + *cap = new_cap; + } + memcpy(*buf + *len, data, n); + *len += n; + return true; +} + +static inline ant_value_t emit_str_replacement( + ant_t *js, ant_value_t replacement, bool is_func, + const char *repl_ptr, ant_offset_t repl_len, + const char *str_ptr, ant_value_t str, + ant_offset_t pos, ant_offset_t match_len, + char **buf, size_t *buf_len, size_t *buf_cap +) { + if (is_func) { + ant_value_t cb_args[3] = { js_mkstr(js, str_ptr + pos, match_len), tov((double)pos), str }; + ant_value_t r = sv_vm_call(js->vm, js, replacement, js_mkundef(), cb_args, 3, NULL, false); + + if (vtype(r) == T_ERR) return r; + ant_value_t r_str = js_tostring_val(js, r); + + if (is_err(r_str)) return r_str; + ant_offset_t rlen, roff = vstr(js, r_str, &rlen); + + if (!str_buf_append(buf, buf_len, buf_cap, (const char *)(uintptr_t)roff, rlen)) return js_mkerr(js, "oom"); + } else if (!str_buf_append(buf, buf_len, buf_cap, repl_ptr, repl_len)) return js_mkerr(js, "oom"); + + return js_mkundef(); +} + +static ant_value_t string_replace_impl(ant_t *js, ant_value_t *args, int nargs, bool replace_all) { + ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val); + ant_value_t str = js_tostring_val(js, this_unwrapped); + + if (is_err(str)) return str; + if (nargs < 1) return str; + + if (is_object_type(args[0])) { + if (replace_all) { + ant_value_t global_val = js_getprop_fallback(js, args[0], "global"); + if (!js_truthy(js, global_val)) return js_mkerr_typed(js, JS_ERR_TYPE, "String.prototype.replaceAll called with a non-global RegExp"); + } + + bool called = false; + ant_value_t replacement_arg = nargs > 1 ? args[1] : js_mkundef(); + ant_value_t call_args[2] = { str, replacement_arg }; + + ant_value_t result = maybe_call_symbol_method(js, args[0], get_replace_sym(), args[0], call_args, 2, &called); + if (is_err(result)) return result; + if (called) return result; + } + + if (nargs < 2) return str; + ant_value_t search = args[0]; + ant_value_t replacement = args[1]; + if (vtype(search) != T_STR) return str; + + ant_offset_t str_len, str_off = vstr(js, str, &str_len); + const char *str_ptr = (char *)(uintptr_t)(str_off); + ant_offset_t search_len, search_off = vstr(js, search, &search_len); + const char *search_ptr = (char *)(uintptr_t)(search_off); + + bool is_func = (vtype(replacement) == T_FUNC); + ant_offset_t repl_len = 0; + const char *repl_ptr = NULL; + + if (!is_func) { + if (vtype(replacement) != T_STR) return str; + ant_offset_t repl_off = vstr(js, replacement, &repl_len); + repl_ptr = (char *)(uintptr_t)(repl_off); + } + + if (!replace_all) { + if (search_len > str_len) return str; + ant_offset_t match_pos = 0; + bool found = false; + + for (ant_offset_t i = 0; i <= str_len - search_len; i++) + if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { + match_pos = i; found = true; break; + } + + if (!found) return str; + + size_t cap = str_len + repl_len + 256, len = 0; + char *buf = (char *)ant_calloc(cap); + if (!buf) return js_mkerr(js, "oom"); + + if (!str_buf_append(&buf, &len, &cap, str_ptr, match_pos)) { + free(buf); + return js_mkerr(js, "oom"); + } + + ant_value_t err = emit_str_replacement( + js, replacement, is_func, repl_ptr, + repl_len, str_ptr, str, match_pos, + search_len, &buf, &len, &cap + ); + + if (vtype(err) == T_ERR) { + free(buf); + return err; + } + + if (!str_buf_append( + &buf, &len, &cap, str_ptr + match_pos + search_len, + str_len - match_pos - search_len) + ) { + free(buf); + return js_mkerr(js, "oom"); + } + + ant_value_t ret = js_mkstr(js, buf, len); + free(buf); + + return ret; + } else { + size_t cap = str_len + repl_len + 256, len = 0; + char *buf = (char *)ant_calloc(cap); + if (!buf) return js_mkerr(js, "oom"); + + ant_offset_t pos = 0; + bool replaced = false; + + while (pos + (ant_offset_t)search_len <= str_len) { + if (search_len == 0 || memcmp(str_ptr + pos, search_ptr, search_len) == 0) { + replaced = true; + ant_value_t err = emit_str_replacement(js, replacement, is_func, repl_ptr, repl_len, str_ptr, str, pos, search_len, &buf, &len, &cap); + if (vtype(err) == T_ERR) { free(buf); return err; } + if (search_len == 0) { + if (pos < str_len && !str_buf_append(&buf, &len, &cap, str_ptr + pos, 1)) { free(buf); return js_mkerr(js, "oom"); } + pos++; + } else pos += search_len; + } else { + if (!str_buf_append(&buf, &len, &cap, str_ptr + pos, 1)) { free(buf); return js_mkerr(js, "oom"); } + pos++; + } + } + + if (!str_buf_append( + &buf, &len, &cap, str_ptr + pos, + str_len - pos) + ) { + free(buf); + return js_mkerr(js, "oom"); + } + + if (!replaced) { + free(buf); + return str; + } + + ant_value_t ret = js_mkstr(js, buf, len); + free(buf); + + return ret; + } +} + +static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int nargs) { + return string_replace_impl(js, args, nargs, false); +} + +static ant_value_t builtin_string_replaceAll(ant_t *js, ant_value_t *args, int nargs) { + return string_replace_impl(js, args, nargs, true); +} + static ant_value_t builtin_string_search(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val); ant_value_t str = js_tostring_val(js, this_unwrapped); @@ -1516,6 +1690,8 @@ void init_regex_module(void) { ant_value_t string_proto = js_get(js, string_ctor, "prototype"); js_setprop(js, string_proto, js_mkstr(js, "search", 6), js_mkfun(builtin_string_search)); js_setprop(js, string_proto, js_mkstr(js, "match", 5), js_mkfun(builtin_string_match)); + js_setprop(js, string_proto, js_mkstr(js, "replace", 7), js_mkfun(builtin_string_replace)); + js_setprop(js, string_proto, js_mkstr(js, "replaceAll", 10), js_mkfun(builtin_string_replaceAll)); } void gc_sweep_regex_cache(void) { diff --git a/src/modules/util.c b/src/modules/util.c index 6d85aaa..64e11ea 100644 --- a/src/modules/util.c +++ b/src/modules/util.c @@ -15,7 +15,6 @@ #include "modules/symbol.h" #include "modules/util.h" - typedef struct { char *buf; size_t len; diff --git a/src/pool.c b/src/pool.c index f2ceb7f..51bf072 100644 --- a/src/pool.c +++ b/src/pool.c @@ -173,7 +173,11 @@ void *js_type_alloc(ant_t *js, ant_alloc_kind_t kind, size_t size, size_t align) size_t pool_threshold = GC_HEAP_GROWTH(js->gc_pool_last_live); if (pool_threshold < (4u * 1024u * 1024u)) pool_threshold = 4u * 1024u * 1024u; - if (js->gc_pool_alloc >= pool_threshold) gc_run(js); +#ifdef ANT_JIT + if (__builtin_expect(js->jit_active_depth > 0, 0)) { /* suppress GC during JIT */ } + else +#endif + { if (js->gc_pool_alloc >= pool_threshold) gc_run(js); } ant_pool_t *pool = pool_for_kind(js, kind); if (pool->block_size == 0) pool->block_size = pool_default_block_size(kind); diff --git a/src/shapes.c b/src/shapes.c index 06a6fac..25c4d94 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -406,26 +406,31 @@ for (; shape; shape = shape->parent) { shape->gc_mark = gc_shape_epoch; }} -static void shape_prune_dead_children(ant_shape_t *shape) { +static bool shape_prune_dead_children(ant_shape_t *shape) { + bool freed_any = false; shape_child_entry_t *ce, *ctmp; + HASH_ITER(hh, shape->children, ce, ctmp) { - shape_prune_dead_children(ce->child); - - if (ce->child->gc_mark != gc_shape_epoch && !ce->child->children) { - ant_shape_t *child = ce->child; - child->parent = NULL; - HASH_DEL(shape->children, ce); - shape_entry_free(ce); - ant_shape_release(child); - } - } + if (shape_prune_dead_children(ce->child)) freed_any = true; + if (ce->child->gc_mark != gc_shape_epoch && !ce->child->children) { + ant_shape_t *child = ce->child; + child->parent = NULL; + HASH_DEL(shape->children, ce); + shape_entry_free(ce); + ant_shape_release(child); + freed_any = true; + }} + + return freed_any; } -void ant_gc_shapes_sweep(void) { +bool ant_gc_shapes_sweep(void) { + bool freed_any = false; for (int i = 0; i <= (int)ANT_INOBJ_MAX_SLOTS; i++) { - if (g_root_shapes[i]) shape_prune_dead_children(g_root_shapes[i]); + if (g_root_shapes[i] && shape_prune_dead_children(g_root_shapes[i])) freed_any = true; } shape_entry_pool_trim(); + return freed_any; } uint8_t ant_shape_get_inobj_limit(const ant_shape_t *shape) { diff --git a/src/silver/swarm.c b/src/silver/swarm.c index c90dc25..62633f8 100644 --- a/src/silver/swarm.c +++ b/src/silver/swarm.c @@ -586,11 +586,20 @@ static bool mir_emit_get_field_ic_fastpath( MIR_new_reg_op(ctx, r_ic_epoch), MIR_new_mem_op(ctx, MIR_T_U32, (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); - MIR_append_insn(ctx, fn, - MIR_new_insn(ctx, MIR_BNE, - MIR_new_label_op(ctx, slow), - MIR_new_reg_op(ctx, r_ic_epoch), - MIR_new_reg_op(ctx, r_global_epoch))); + { + char ce_name[40]; + snprintf(ce_name, sizeof(ce_name), "gf_ce_%d_%u", bc_off, (unsigned)ic_idx); + MIR_reg_t r_cur_epoch = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, ce_name); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_cur_epoch), + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_global_epoch, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_reg_op(ctx, r_cur_epoch))); + } mir_emit_value_to_objptr_or_jmp( ctx, fn, obj, r_obj_ptr, r_obj_tag, slow); @@ -2232,16 +2241,11 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_reg_t r_ic_epoch_val = 0; if (feat.needs_ic_epoch) { - r_ic_epoch_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_epoch"); - MIR_reg_t r_ep_tmp = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_ep_ptr"); - MIR_append_insn(ctx, jit_func, - MIR_new_insn(ctx, MIR_MOV, - MIR_new_reg_op(ctx, r_ep_tmp), - MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)&ant_ic_epoch_counter))); + r_ic_epoch_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_ep_ptr"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_ic_epoch_val), - MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ep_tmp, 0, 1))); + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)&ant_ic_epoch_counter))); } MIR_reg_t r_bailout_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, "bail_val"); @@ -5503,11 +5507,20 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, r_ic_epoch), MIR_new_mem_op(ctx, MIR_T_I32, (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); - MIR_append_insn(ctx, jit_func, - MIR_new_insn(ctx, MIR_BNE, - MIR_new_label_op(ctx, slow), - MIR_new_reg_op(ctx, r_ic_epoch), - MIR_new_reg_op(ctx, r_ic_epoch_val))); + { + char ice_cur_name[40]; + snprintf(ice_cur_name, sizeof(ice_cur_name), "inst_ce_%d_%u", bc_off, (unsigned)ic_idx); + MIR_reg_t r_cur_ep = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, ice_cur_name); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_cur_ep), + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ic_epoch_val, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_reg_op(ctx, r_cur_ep))); + } MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, @@ -5675,11 +5688,20 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, r_ic_epoch), MIR_new_mem_op(ctx, MIR_T_I32, (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); - MIR_append_insn(ctx, jit_func, - MIR_new_insn(ctx, MIR_BNE, - MIR_new_label_op(ctx, slow), - MIR_new_reg_op(ctx, r_ic_epoch), - MIR_new_reg_op(ctx, r_ic_epoch_val))); + { + char cip_ce_name[40]; + snprintf(cip_ce_name, sizeof(cip_ce_name), "cip_ce_%d_%u", bc_off, (unsigned)ic_idx); + MIR_reg_t r_cur_ep = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ce_name); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_cur_ep), + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ic_epoch_val, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_reg_op(ctx, r_cur_ep))); + } mir_emit_value_to_objptr_or_jmp( ctx, jit_func, this_obj, r_proto_ptr, r_proto_tag, slow);