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.

optimize garbage collection during jit

add dynamic buffer handling for string replacements
separate global and emitter event type management

+482 -629
+28
examples/demo/events.js
··· 95 95 temp.dispatchEvent('test', 'second call (not shown)'); 96 96 97 97 console.log(' Handler was removed successfully'); 98 + 99 + console.log('\nExample 8: CustomEvent'); 100 + const catFound = new CustomEvent('animalfound', { 101 + detail: { 102 + name: 'cat', 103 + }, 104 + }); 105 + const dogFound = new CustomEvent('animalfound', { 106 + detail: { 107 + name: 'dog', 108 + }, 109 + }); 110 + 111 + const element = new EventTarget(); 112 + 113 + element.addEventListener('animalfound', e => console.log(' ', e.detail.name, 'found')); 114 + 115 + element.dispatchEvent(catFound); 116 + element.dispatchEvent(dogFound); 117 + 118 + console.log('\nExample 9: CustomEvent with global dispatch'); 119 + addEventListener('build:complete', e => { 120 + console.log(' Build finished:', e.detail.duration + 'ms,', e.detail.files, 'files'); 121 + }); 122 + 123 + dispatchEvent(new CustomEvent('build:complete', { 124 + detail: { duration: 142, files: 37 }, 125 + }));
+27
examples/spec/events.js
··· 166 166 dispatchEvent('global-rem'); 167 167 test('global removeEventListener', globalRemoved, 0); 168 168 169 + console.log('\nCustomEvent Tests\n'); 170 + 171 + const ce1 = new CustomEvent('ping'); 172 + test('CustomEvent type', ce1.type, 'ping'); 173 + test('CustomEvent detail defaults to null', ce1.detail, null); 174 + 175 + const ce2 = new CustomEvent('animalfound', { detail: { name: 'cat' } }); 176 + test('CustomEvent detail', ce2.detail.name, 'cat'); 177 + 178 + let ceReceived = null; 179 + const et5 = new EventTarget(); 180 + et5.addEventListener('animalfound', e => { 181 + ceReceived = e.detail.name; 182 + }); 183 + et5.dispatchEvent(ce2); 184 + test('EventTarget dispatchEvent with CustomEvent', ceReceived, 'cat'); 185 + 186 + let ceGlobal = null; 187 + addEventListener('custom-global', e => { 188 + ceGlobal = e.detail.value; 189 + }); 190 + dispatchEvent(new CustomEvent('custom-global', { detail: { value: 42 } })); 191 + test('global dispatchEvent with CustomEvent', ceGlobal, 42); 192 + 193 + const ce3 = new CustomEvent('tagged'); 194 + test('CustomEvent Symbol.toStringTag', Object.prototype.toString.call(ce3), '[object CustomEvent]'); 195 + 169 196 summary();
+1 -1
include/shapes.h
··· 62 62 63 63 void ant_gc_shapes_begin(void); 64 64 void ant_gc_shapes_mark(ant_shape_t *shape); 65 - void ant_gc_shapes_sweep(void); 65 + bool ant_gc_shapes_sweep(void); 66 66 67 67 size_t ant_shape_total_bytes(void); 68 68 extern uint32_t ant_ic_epoch_counter;
+1 -1
meson/ant.version
··· 1 - 0.8.0 1 + 0.8.1
+8 -329
src/ant.c
··· 379 379 } 380 380 381 381 static ant_object_t *obj_alloc(ant_t *js, uint8_t type_tag, uint8_t inobj_limit) { 382 - size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); 383 - if (threshold < 2048) threshold = 2048; 384 - if (js->obj_arena.live_count >= threshold) gc_run(js); 382 + #ifdef ANT_JIT 383 + if (__builtin_expect(js->jit_active_depth == 0, 1)) 384 + #endif 385 + { 386 + size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); 387 + if (threshold < 2048) threshold = 2048; 388 + if (js->obj_arena.live_count >= threshold) gc_run(js); 389 + } 385 390 386 391 ant_object_t *obj = (ant_object_t *)fixed_arena_alloc(&js->obj_arena); 387 392 if (!obj) return NULL; ··· 8439 8444 return mkval(T_BOOL, memcmp(str_ptr + str_len - search_len, search_ptr, search_len) == 0 ? 1 : 0); 8440 8445 } 8441 8446 8442 - static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int nargs) { 8443 - ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val); 8444 - ant_value_t str = js_tostring_val(js, this_unwrapped); 8445 - if (is_err(str)) return str; 8446 - if (nargs < 1) return str; 8447 - 8448 - if (is_object_type(args[0])) { 8449 - bool called = false; 8450 - ant_value_t replacement_arg = nargs > 1 ? args[1] : js_mkundef(); 8451 - ant_value_t call_args[2] = { str, replacement_arg }; 8452 - ant_value_t dispatched = maybe_call_symbol_method( 8453 - js, args[0], get_replace_sym(), args[0], call_args, 2, &called 8454 - ); 8455 - if (is_err(dispatched)) return dispatched; 8456 - if (called) return dispatched; 8457 - } 8458 - if (nargs < 2) return str; 8459 - 8460 - ant_value_t search = args[0]; 8461 - ant_value_t replacement = args[1]; 8462 - 8463 - ant_offset_t str_len, str_off = vstr(js, str, &str_len); 8464 - const char *str_ptr = (char *)(uintptr_t)(str_off); 8465 - 8466 - bool is_regex = false; 8467 - bool global_flag = false; 8468 - bool is_func_replacement = (vtype(replacement) == T_FUNC); 8469 - char *pattern_buf = NULL; 8470 - ant_offset_t pattern_len = 0; 8471 - 8472 - if (vtype(search) == T_OBJ) { 8473 - ant_offset_t pattern_off = lkp(js, search, "source", 6); 8474 - if (pattern_off == 0) goto not_regex; 8475 - 8476 - ant_value_t pattern_val = propref_load(js, pattern_off); 8477 - if (vtype(pattern_val) != T_STR) goto not_regex; 8478 - 8479 - is_regex = true; 8480 - ant_offset_t plen, poff = vstr(js, pattern_val, &plen); 8481 - pattern_len = plen; 8482 - pattern_buf = (char *)ant_calloc(plen + 1); 8483 - if (!pattern_buf) return js_mkerr(js, "oom"); 8484 - memcpy(pattern_buf, (const void *)(uintptr_t)poff, plen); 8485 - pattern_buf[plen] = '\0'; 8486 - 8487 - ant_offset_t flags_off = lkp(js, search, "flags", 5); 8488 - if (flags_off == 0) { free(pattern_buf); pattern_buf = NULL; goto not_regex; } 8489 - 8490 - ant_value_t flags_val = propref_load(js, flags_off); 8491 - if (vtype(flags_val) != T_STR) { free(pattern_buf); pattern_buf = NULL; goto not_regex; } 8492 - 8493 - ant_offset_t flen, foff = vstr(js, flags_val, &flen); 8494 - const char *flags_str = (char *)(uintptr_t)(foff); 8495 - for (ant_offset_t i = 0; i < flen; i++) { 8496 - if (flags_str[i] == 'g') global_flag = true; 8497 - } 8498 - } 8499 - not_regex: 8500 - 8501 - ant_offset_t repl_len = 0; 8502 - const char *repl_ptr = NULL; 8503 - if (!is_func_replacement) { 8504 - if (vtype(replacement) != T_STR) { if (pattern_buf) free(pattern_buf); return str; } 8505 - ant_offset_t repl_off; 8506 - repl_off = vstr(js, replacement, &repl_len); 8507 - repl_ptr = (char *)(uintptr_t)(repl_off); 8508 - } 8509 - 8510 - size_t result_cap = str_len + repl_len + 256; 8511 - size_t result_len = 0; 8512 - char *result = (char *)ant_calloc(result_cap); 8513 - if (!result) return js_mkerr(js, "oom"); 8514 - 8515 - #define ENSURE_RESULT_CAP(need) do { \ 8516 - if (result_len + (need) >= result_cap) { \ 8517 - result_cap = (result_len + (need) + 1) * 2; \ 8518 - char *nr = (char *)ant_realloc(result, result_cap); \ 8519 - if (!nr) return js_mkerr(js, "oom"); \ 8520 - result = nr; \ 8521 - } \ 8522 - } while(0) 8523 - 8524 - if (is_regex) { 8525 - size_t pcre2_cap = pattern_len * 2 + 16; 8526 - char *pcre2_pattern = (char *)ant_calloc(pcre2_cap); 8527 - if (!pcre2_pattern) return js_mkerr(js, "oom"); 8528 - size_t pcre2_len = js_to_pcre2_pattern(pattern_buf, pattern_len, pcre2_pattern, pcre2_cap); 8529 - 8530 - uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF; 8531 - int errcode; 8532 - PCRE2_SIZE erroffset; 8533 - pcre2_code *re = pcre2_compile((PCRE2_SPTR)pcre2_pattern, pcre2_len, options, &errcode, &erroffset, NULL); 8534 - free(pcre2_pattern); 8535 - if (re == NULL) return js_mkerr(js, "invalid regex pattern"); 8536 - 8537 - pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); 8538 - uint32_t capture_count; 8539 - pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &capture_count); 8540 - 8541 - PCRE2_SIZE pos = 0; 8542 - bool replaced = false; 8543 - 8544 - while (pos <= str_len) { 8545 - int rc = pcre2_match(re, (PCRE2_SPTR)str_ptr, str_len, pos, 0, match_data, NULL); 8546 - if (rc < 0) break; 8547 - 8548 - PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); 8549 - PCRE2_SIZE match_start = ovector[0]; 8550 - PCRE2_SIZE match_end = ovector[1]; 8551 - 8552 - PCRE2_SIZE before_len = match_start - pos; 8553 - ENSURE_RESULT_CAP(before_len); 8554 - memcpy(result + result_len, str_ptr + pos, before_len); 8555 - result_len += before_len; 8556 - 8557 - if (is_func_replacement) { 8558 - int nargs_cb = 1 + capture_count + 2; 8559 - ant_value_t *cb_args = (ant_value_t *)ant_calloc(nargs_cb * sizeof(ant_value_t)); 8560 - if (!cb_args) { 8561 - pcre2_match_data_free(match_data); 8562 - pcre2_code_free(re); 8563 - return js_mkerr(js, "oom"); 8564 - } 8565 - cb_args[0] = js_mkstr(js, str_ptr + match_start, match_end - match_start); 8566 - for (uint32_t i = 1; i <= capture_count; i++) { 8567 - PCRE2_SIZE cap_start = ovector[2*i]; 8568 - PCRE2_SIZE cap_end = ovector[2*i+1]; 8569 - if (cap_start == PCRE2_UNSET) { 8570 - cb_args[i] = js_mkundef(); 8571 - } else { 8572 - cb_args[i] = js_mkstr(js, str_ptr + cap_start, cap_end - cap_start); 8573 - } 8574 - } 8575 - cb_args[1 + capture_count] = tov((double)match_start); 8576 - cb_args[2 + capture_count] = str; 8577 - 8578 - ant_value_t cb_result = sv_vm_call(js->vm, js, replacement, js_mkundef(), cb_args, nargs_cb, NULL, false); 8579 - free(cb_args); 8580 - 8581 - if (vtype(cb_result) == T_ERR) { 8582 - pcre2_match_data_free(match_data); 8583 - pcre2_code_free(re); 8584 - return cb_result; 8585 - } 8586 - 8587 - if (vtype(cb_result) == T_STR) { 8588 - ant_offset_t cb_len, cb_off = vstr(js, cb_result, &cb_len); 8589 - ENSURE_RESULT_CAP(cb_len); 8590 - memcpy(result + result_len, (const void *)(uintptr_t)cb_off, cb_len); 8591 - result_len += cb_len; 8592 - } else { 8593 - char numbuf[32]; 8594 - size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); 8595 - ENSURE_RESULT_CAP(n); 8596 - memcpy(result + result_len, numbuf, n); 8597 - result_len += n; 8598 - } 8599 - } else { 8600 - repl_capture_t caps_buf[16], *caps = (int)capture_count <= 16 ? caps_buf : ant_calloc(sizeof(repl_capture_t) * capture_count); 8601 - for (uint32_t ci = 0; ci < capture_count; ci++) { 8602 - PCRE2_SIZE cs = ovector[2*(ci+1)], ce = ovector[2*(ci+1)+1]; 8603 - if (cs != PCRE2_UNSET) caps[ci] = (repl_capture_t){ str_ptr + cs, ce - cs }; 8604 - else caps[ci] = (repl_capture_t){ NULL, 0 }; 8605 - } 8606 - repl_template(repl_ptr, repl_len, str_ptr + match_start, match_end - match_start, 8607 - str_ptr, str_len, match_start, caps, (int)capture_count, &result, &result_len, &result_cap); 8608 - if (caps != caps_buf) free(caps); 8609 - } 8610 - 8611 - if (match_start == match_end) { 8612 - if (pos < str_len) { 8613 - ENSURE_RESULT_CAP(1); 8614 - result[result_len++] = str_ptr[pos]; 8615 - } 8616 - pos = match_end + 1; 8617 - } else pos = match_end; 8618 - 8619 - replaced = true; 8620 - if (!global_flag) break; 8621 - } 8622 - 8623 - if (pos < str_len) { 8624 - size_t remaining = str_len - pos; 8625 - ENSURE_RESULT_CAP(remaining); 8626 - memcpy(result + result_len, str_ptr + pos, remaining); 8627 - result_len += remaining; 8628 - } 8629 - 8630 - pcre2_match_data_free(match_data); pcre2_code_free(re); 8631 - if (pattern_buf) free(pattern_buf); 8632 - 8633 - ant_value_t ret = replaced ? js_mkstr(js, result, result_len) : str; 8634 - free(result); return ret; 8635 - } else { 8636 - if (vtype(search) != T_STR) { free(result); return str; } 8637 - ant_offset_t search_len, search_off = vstr(js, search, &search_len); 8638 - const char *search_ptr = (char *)(uintptr_t)(search_off); 8639 - 8640 - if (search_len > str_len) { free(result); return str; } 8641 - 8642 - for (ant_offset_t i = 0; i <= str_len - search_len; i++) { 8643 - if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { 8644 - ENSURE_RESULT_CAP(i); 8645 - memcpy(result + result_len, str_ptr, i); 8646 - result_len += i; 8647 - 8648 - if (is_func_replacement) { 8649 - ant_value_t match_str = js_mkstr(js, search_ptr, search_len); 8650 - ant_value_t cb_args[1] = { match_str }; 8651 - ant_value_t cb_result = sv_vm_call(js->vm, js, replacement, js_mkundef(), cb_args, 1, NULL, false); 8652 - 8653 - if (vtype(cb_result) == T_ERR) { free(result); return cb_result; } 8654 - 8655 - if (vtype(cb_result) == T_STR) { 8656 - ant_offset_t cb_len, cb_off = vstr(js, cb_result, &cb_len); 8657 - ENSURE_RESULT_CAP(cb_len); 8658 - memcpy(result + result_len, (const void *)(uintptr_t)cb_off, cb_len); 8659 - result_len += cb_len; 8660 - } else { 8661 - char numbuf[32]; 8662 - size_t n = tostr(js, cb_result, numbuf, sizeof(numbuf)); 8663 - ENSURE_RESULT_CAP(n); 8664 - memcpy(result + result_len, numbuf, n); 8665 - result_len += n; 8666 - } 8667 - } else { 8668 - ENSURE_RESULT_CAP(repl_len); 8669 - memcpy(result + result_len, repl_ptr, repl_len); 8670 - result_len += repl_len; 8671 - } 8672 - 8673 - ant_offset_t after_start = i + search_len; 8674 - ant_offset_t after_len = str_len - after_start; 8675 - if (after_len > 0) { 8676 - ENSURE_RESULT_CAP(after_len); 8677 - memcpy(result + result_len, str_ptr + after_start, after_len); 8678 - result_len += after_len; 8679 - } 8680 - ant_value_t ret = js_mkstr(js, result, result_len); 8681 - free(result); 8682 - return ret; 8683 - } 8684 - } 8685 - free(result); 8686 - return str; 8687 - } 8688 - #undef ENSURE_RESULT_CAP 8689 - } 8690 - 8691 - static ant_value_t builtin_string_replaceAll(ant_t *js, ant_value_t *args, int nargs) { 8692 - ant_value_t str = to_string_val(js, js->this_val); 8693 - if (vtype(str) != T_STR) return js_mkerr(js, "replaceAll called on non-string"); 8694 - if (nargs < 2) return str; 8695 - 8696 - ant_value_t search = args[0]; 8697 - ant_value_t replacement = args[1]; 8698 - 8699 - if (vtype(search) != T_STR) return js_mkerr(js, "replaceAll requires string search pattern"); 8700 - if (vtype(replacement) != T_STR) return js_mkerr(js, "replaceAll requires string replacement"); 8701 - 8702 - ant_offset_t str_len, str_off = vstr(js, str, &str_len); 8703 - const char *str_ptr = (char *)(uintptr_t)(str_off); 8704 - 8705 - ant_offset_t search_len, search_off = vstr(js, search, &search_len); 8706 - const char *search_ptr = (char *)(uintptr_t)(search_off); 8707 - 8708 - ant_offset_t repl_len, repl_off = vstr(js, replacement, &repl_len); 8709 - const char *repl_ptr = (char *)(uintptr_t)(repl_off); 8710 - 8711 - if (search_len == 0) { 8712 - size_t total_len = str_len + (str_len + 1) * repl_len; 8713 - char *result = (char *)ant_calloc(total_len + 1); 8714 - if (!result) return js_mkerr(js, "oom"); 8715 - 8716 - size_t pos = 0; 8717 - memcpy(result + pos, repl_ptr, repl_len); 8718 - pos += repl_len; 8719 - for (ant_offset_t i = 0; i < str_len; i++) { 8720 - result[pos++] = str_ptr[i]; 8721 - memcpy(result + pos, repl_ptr, repl_len); 8722 - pos += repl_len; 8723 - } 8724 - ant_value_t ret = js_mkstr(js, result, pos); 8725 - free(result); 8726 - return ret; 8727 - } 8728 - 8729 - ant_offset_t count = 0; 8730 - for (ant_offset_t i = 0; i <= str_len - search_len; i++) { 8731 - if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { 8732 - count++; 8733 - i += search_len - 1; 8734 - } 8735 - } 8736 - 8737 - if (count == 0) return str; 8738 - 8739 - size_t result_total = str_len - (count * search_len) + (count * repl_len); 8740 - char *result = (char *)ant_calloc(result_total + 1); 8741 - if (!result) return js_mkerr(js, "oom"); 8742 - 8743 - size_t result_pos = 0; 8744 - ant_offset_t str_pos = 0; 8745 - 8746 - while (str_pos <= str_len - search_len) { 8747 - if (memcmp(str_ptr + str_pos, search_ptr, search_len) == 0) { 8748 - memcpy(result + result_pos, repl_ptr, repl_len); 8749 - result_pos += repl_len; 8750 - str_pos += search_len; 8751 - } else { 8752 - result[result_pos++] = str_ptr[str_pos++]; 8753 - } 8754 - } 8755 - 8756 - while (str_pos < str_len) { 8757 - result[result_pos++] = str_ptr[str_pos++]; 8758 - } 8759 - 8760 - ant_value_t ret = js_mkstr(js, result, result_pos); 8761 - free(result); 8762 - return ret; 8763 - } 8764 - 8765 - 8766 8447 static ant_value_t builtin_string_template(ant_t *js, ant_value_t *args, int nargs) { 8767 8448 ant_value_t str = to_string_val(js, js->this_val); 8768 8449 if (vtype(str) != T_STR) return js_mkerr(js, "template called on non-string"); ··· 11948 11629 js_setprop(js, string_proto, js_mkstr(js, "includes", 8), js_mkfun(builtin_string_includes)); 11949 11630 js_setprop(js, string_proto, js_mkstr(js, "startsWith", 10), js_mkfun(builtin_string_startsWith)); 11950 11631 js_setprop(js, string_proto, js_mkstr(js, "endsWith", 8), js_mkfun(builtin_string_endsWith)); 11951 - js_setprop(js, string_proto, js_mkstr(js, "replace", 7), js_mkfun(builtin_string_replace)); 11952 - js_setprop(js, string_proto, js_mkstr(js, "replaceAll", 10), js_mkfun(builtin_string_replaceAll)); 11953 11632 js_setprop(js, string_proto, js_mkstr(js, "template", 8), js_mkfun(builtin_string_template)); 11954 11633 js_setprop(js, string_proto, js_mkstr(js, "charCodeAt", 10), js_mkfun(builtin_string_charCodeAt)); 11955 11634 js_setprop(js, string_proto, js_mkstr(js, "codePointAt", 11), js_mkfun(builtin_string_codePointAt));
+3
src/gc/gc.c
··· 122 122 123 123 void gc_maybe(ant_t *js) { 124 124 if (__builtin_expect(gc_disabled, 0)) return; 125 + #ifdef ANT_JIT 126 + if (__builtin_expect(js->jit_active_depth > 0, 0)) return; 127 + #endif 125 128 if (++gc_tick < GC_MIN_TICK) return; 126 129 size_t live = js->obj_arena.live_count; 127 130
+2 -2
src/gc/objects.c
··· 644 644 gc_mark_roots(js); 645 645 gc_sweep_regex_cache(); 646 646 gc_sweep(js); 647 - ant_gc_shapes_sweep(); 648 - ant_ic_epoch_bump(); 647 + 648 + if (ant_gc_shapes_sweep()) ant_ic_epoch_bump(); 649 649 gc_promote_survivors(js); 650 650 651 651 ant_fixed_arena_t *ca = &js->closure_arena;
+169 -259
src/modules/events.c
··· 68 68 return (EventType **)(uintptr_t)js_getnum(slot); 69 69 } 70 70 71 - static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 72 - EventType **events = get_or_create_emitter_events(js, this_obj); 73 - if (events == NULL) return NULL; 71 + static EventType *make_event_type(const char *event_type) { 72 + size_t etlen = strlen(event_type); 73 + EventType *evt = ant_calloc(sizeof(EventType) + etlen + 1); 74 + if (!evt) return NULL; 74 75 76 + evt->event_type = (char *)(evt + 1); 77 + memcpy(evt->event_type, event_type, etlen + 1); 78 + evt->listener_count = 0; 79 + 80 + return evt; 81 + } 82 + 83 + static EventType *find_or_create_global_event_type(const char *event_type) { 75 84 EventType *evt = NULL; 76 - HASH_FIND_STR(*events, event_type, evt); 85 + HASH_FIND_STR(global_events, event_type, evt); 86 + 87 + if (evt == NULL) { 88 + evt = make_event_type(event_type); 89 + if (!evt) return NULL; 90 + HASH_ADD_KEYPTR(hh, global_events, evt->event_type, strlen(evt->event_type), evt); 91 + } 92 + 77 93 return evt; 78 94 } 79 95 80 - static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 96 + static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 81 97 EventType **events = get_or_create_emitter_events(js, this_obj); 82 98 if (events == NULL) return NULL; 83 99 84 100 EventType *evt = NULL; 85 101 HASH_FIND_STR(*events, event_type, evt); 86 102 87 - if (evt == NULL) { 88 - size_t etlen = strlen(event_type); 89 - evt = ant_calloc(sizeof(EventType) + etlen + 1); 90 - if (!evt) return NULL; 91 - evt->event_type = (char *)(evt + 1); 92 - memcpy(evt->event_type, event_type, etlen + 1); 93 - evt->listener_count = 0; 94 - HASH_ADD_KEYPTR(hh, *events, evt->event_type, etlen, evt); 95 - } 96 - 97 103 return evt; 98 104 } 99 105 100 - static EventType *find_or_create_global_event_type(const char *event_type) { 106 + static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 107 + EventType **events = get_or_create_emitter_events(js, this_obj); 108 + if (events == NULL) return NULL; 109 + 101 110 EventType *evt = NULL; 102 - HASH_FIND_STR(global_events, event_type, evt); 111 + HASH_FIND_STR(*events, event_type, evt); 103 112 104 113 if (evt == NULL) { 105 - size_t etlen = strlen(event_type); 106 - evt = ant_calloc(sizeof(EventType) + etlen + 1); 114 + evt = make_event_type(event_type); 107 115 if (!evt) return NULL; 108 - evt->event_type = (char *)(evt + 1); 109 - memcpy(evt->event_type, event_type, etlen + 1); 110 - evt->listener_count = 0; 111 - HASH_ADD_KEYPTR(hh, global_events, evt->event_type, etlen, evt); 116 + HASH_ADD_KEYPTR(hh, *events, evt->event_type, strlen(evt->event_type), evt); 112 117 } 113 118 114 119 return evt; ··· 120 125 return evt; 121 126 } 122 127 123 - // addEventListener(eventType, listener, options) 124 - static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 125 - ant_value_t this_obj = js_getthis(js); 126 - 127 - if (nargs < 2) { 128 - return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 129 - } 130 - 131 - char *event_type = js_getstr(js, args[0], NULL); 132 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 133 - if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 134 - 135 - EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); 128 + static ant_value_t add_event_listener_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { 136 129 if (evt == NULL) return js_mkerr(js, "failed to create event type"); 137 - 138 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 139 - return js_mkerr(js, "maximum number of listeners for event type '%s' reached", event_type); 140 - } 141 - 130 + if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) 131 + return js_mkerr(js, "maximum number of listeners for event type '%s' reached", evt->event_type); 132 + 142 133 bool once = false; 143 134 if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 144 135 ant_value_t once_val = js_get(js, args[2], "once"); 145 136 if (vtype(once_val) != T_UNDEF) once = js_truthy(js, once_val); 146 137 } 147 - 138 + 148 139 EventListener *listener = &evt->listeners[evt->listener_count++]; 149 140 listener->listener = args[1]; 150 141 listener->once = once; ··· 153 144 } 154 145 155 146 // addEventListener(eventType, listener, options) 147 + static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 148 + if (nargs < 2) return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 149 + char *event_type = js_getstr(js, args[0], NULL); 150 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 151 + if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 152 + return add_event_listener_to(js, args, nargs, find_or_create_emitter_event_type(js, js_getthis(js), event_type)); 153 + } 154 + 155 + // addEventListener(eventType, listener, options) 156 156 static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) { 157 - if (nargs < 2) { 158 - return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 159 - } 160 - 157 + if (nargs < 2) return js_mkerr(js, "addEventListener requires at least 2 arguments (eventType, listener)"); 161 158 char *event_type = js_getstr(js, args[0], NULL); 162 - if (event_type == NULL) { 163 - return js_mkerr(js, "eventType must be a string"); 164 - } 165 - 166 - if (vtype(args[1]) != T_FUNC) { 167 - return js_mkerr(js, "listener must be a function"); 168 - } 169 - 170 - EventType *evt = find_or_create_global_event_type(event_type); 171 - if (evt == NULL) { 172 - return js_mkerr(js, "failed to create event type"); 173 - } 174 - 175 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 176 - return js_mkerr(js, "maximum number of listeners for event type '%s' reached", event_type); 177 - } 159 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 160 + if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 161 + return add_event_listener_to(js, args, nargs, find_or_create_global_event_type(event_type)); 162 + } 163 + 164 + static ant_value_t remove_event_listener_from(ant_t *js, ant_value_t *args, int nargs, EventType *evt) { 165 + if (evt == NULL) goto done; 178 166 179 - bool once = false; 180 - if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 181 - ant_value_t once_val = js_get(js, args[2], "once"); 182 - if (vtype(once_val) != T_UNDEF) once = js_truthy(js, once_val); 167 + for (int i = 0; i < evt->listener_count; i++) { 168 + if (evt->listeners[i].listener != args[1]) continue; 169 + remove_listener_at(evt, i); 170 + goto done; 183 171 } 184 172 185 - EventListener *listener = &evt->listeners[evt->listener_count++]; 186 - listener->listener = args[1]; 187 - listener->once = once; 188 - 189 - return js_mkundef(); 173 + done: return js_mkundef(); 190 174 } 191 175 192 176 // removeEventListener(eventType, listener) 193 177 static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 194 - ant_value_t this_obj = js_getthis(js); 195 - 196 - if (nargs < 2) { 197 - return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 198 - } 199 - 178 + if (nargs < 2) return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 200 179 char *event_type = js_getstr(js, args[0], NULL); 201 180 if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 202 - 203 - EventType *evt = find_emitter_event_type(js, this_obj, event_type); 204 - if (evt == NULL) return js_mkundef(); 205 - 206 - for (int i = 0; i < evt->listener_count; i++) { 207 - if (evt->listeners[i].listener == args[1]) { 208 - remove_listener_at(evt, i); break; 209 - } 210 - } 211 - 212 - return js_mkundef(); 181 + return remove_event_listener_from(js, args, nargs, find_emitter_event_type(js, js_getthis(js), event_type)); 213 182 } 214 183 215 184 // removeEventListener(eventType, listener) 216 185 static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) { 217 - if (nargs < 2) { 218 - return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 219 - } 220 - 186 + if (nargs < 2) return js_mkerr(js, "removeEventListener requires 2 arguments (eventType, listener)"); 221 187 char *event_type = js_getstr(js, args[0], NULL); 222 188 if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 223 - 224 - EventType *evt = find_global_event_type(event_type); 225 - if (evt == NULL) return js_mkundef(); 226 - 227 - for (int i = 0; i < evt->listener_count; i++) { 228 - if (evt->listeners[i].listener == args[1]) { 229 - remove_listener_at(evt, i); break; 230 - } 231 - } 232 - 233 - return js_mkundef(); 189 + return remove_event_listener_from(js, args, nargs, find_global_event_type(event_type)); 234 190 } 235 191 236 - // dispatchEvent(eventType, eventData) 237 - static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { 238 - ant_value_t this_obj = js_getthis(js); 239 - 240 - if (nargs < 1) { 241 - return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 242 - } 243 - 244 - char *event_type = js_getstr(js, args[0], NULL); 245 - if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 246 - 247 - EventType *evt = find_emitter_event_type(js, this_obj, event_type); 192 + static ant_value_t dispatch_event_to(ant_t *js, ant_value_t *args, int nargs, EventType *evt, ant_value_t target) { 248 193 if (evt == NULL || evt->listener_count == 0) return js_true; 249 - 250 - ant_value_t event_obj = js_mkobj(js); 251 - js_set(js, event_obj, "type", args[0]); 252 - js_set(js, event_obj, "target", this_obj); 253 - js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); 254 - 255 - if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { 256 - js_set(js, event_obj, "detail", args[1]); 194 + 195 + ant_value_t event_obj; 196 + const char *event_type; 197 + 198 + if (vtype(args[0]) == T_OBJ) { 199 + ant_value_t type_val = js_get(js, args[0], "type"); 200 + event_type = js_getstr(js, type_val, NULL); 201 + if (event_type == NULL) return js_mkerr(js, "event.type must be a string"); 202 + event_obj = args[0]; 203 + } else { 204 + event_type = js_getstr(js, args[0], NULL); 205 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 206 + event_obj = js_mkobj(js); 207 + js_set(js, event_obj, "type", args[0]); 208 + js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); 209 + if (nargs >= 2 && vtype(args[1]) != T_UNDEF) js_set(js, event_obj, "detail", args[1]); 257 210 } 258 - 211 + 212 + if (is_object_type(target)) js_set(js, event_obj, "target", target); 259 213 ant_value_t listener_args[1] = {event_obj}; 260 - int i = 0; 261 - while (i < evt->listener_count) { 214 + 215 + for (int i = 0; i < evt->listener_count;) { 262 216 EventListener *listener = &evt->listeners[i]; 263 217 ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, 1, NULL, false); 264 - 265 - if (vtype(result) == T_ERR) { 218 + 219 + if (vtype(result) == T_ERR) 266 220 fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); 267 - } 268 - 269 - if (listener->once) { 270 - remove_listener_at(evt, i); 271 - } else i++; 221 + 222 + if (listener->once) remove_listener_at(evt, i); 223 + else i++; 272 224 } 273 - 225 + 274 226 return js_true; 275 227 } 276 228 277 229 // dispatchEvent(eventType, eventData) 230 + static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { 231 + if (nargs < 1) return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 232 + 233 + ant_value_t this_obj = js_getthis(js); 234 + char *event_type = vtype(args[0]) == T_OBJ 235 + ? js_getstr(js, js_get(js, args[0], "type"), NULL) 236 + : js_getstr(js, args[0], NULL); 237 + if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 238 + 239 + return dispatch_event_to(js, args, nargs, find_emitter_event_type(js, this_obj, event_type), this_obj); 240 + } 241 + 242 + // dispatchEvent(eventType, eventData) 278 243 static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) { 279 - if (nargs < 1) { 280 - return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 281 - } 282 - 283 - char *event_type = js_getstr(js, args[0], NULL); 244 + if (nargs < 1) return js_mkerr(js, "dispatchEvent requires at least 1 argument (eventType)"); 245 + 246 + char *event_type = vtype(args[0]) == T_OBJ 247 + ? js_getstr(js, js_get(js, args[0], "type"), NULL) 248 + : js_getstr(js, args[0], NULL); 284 249 if (event_type == NULL) return js_mkerr(js, "eventType must be a string"); 285 - 286 - EventType *evt = find_global_event_type(event_type); 287 - if (evt == NULL || evt->listener_count == 0) { 288 - return js_true; 289 - } 290 - 291 - ant_value_t event_obj = js_mkobj(js); 292 - js_set(js, event_obj, "type", args[0]); 293 - js_set_sym(js, event_obj, get_toStringTag_sym(), js_mkstr(js, "Event", 5)); 294 - 295 - if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { 296 - js_set(js, event_obj, "detail", args[1]); 297 - } 298 - 299 - ant_value_t listener_args[1] = {event_obj}; 300 - int i = 0; 301 - while (i < evt->listener_count) { 302 - EventListener *listener = &evt->listeners[i]; 303 - ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, 1, NULL, false); 304 - 305 - if (vtype(result) == T_ERR) { 306 - fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); 307 - } 308 - 309 - if (listener->once) { 310 - remove_listener_at(evt, i); 311 - } else i++; 312 - } 313 - 314 - return js_true; 250 + 251 + return dispatch_event_to(js, args, nargs, find_global_event_type(event_type), js_mkundef()); 315 252 } 316 253 317 - static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int nargs) { 318 - (void)args; (void)nargs; 319 - 254 + static ant_value_t js_get_event_listeners(ant_t *js, ant_value_t *args, int nargs) { 320 255 EventType *evt, *tmp; 321 256 ant_value_t result = js_mkobj(js); 322 257 ··· 330 265 ant_value_t listener_info = js_mkobj(js); 331 266 js_set(js, listener_info, "listener", evt->listeners[j].listener); 332 267 js_set(js, listener_info, "once", js_bool(evt->listeners[j].once)); 333 - 334 268 js_set(js, listeners_array, key, listener_info); 335 269 } 336 270 ··· 341 275 return result; 342 276 } 343 277 344 - // EventEmitter.prototype.on(event, listener) 345 - static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { 278 + static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once) { 279 + if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); 280 + char *event_type = js_getstr(js, args[0], NULL); 281 + if (event_type == NULL) return js_mkerr(js, "event must be a string"); 282 + if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "listener must be a function"); 283 + 346 284 ant_value_t this_obj = js_getthis(js); 347 - 348 - if (nargs < 2) { 349 - return js_mkerr(js, "on requires 2 arguments (event, listener)"); 350 - } 351 - 352 - char *event_type = js_getstr(js, args[0], NULL); 353 - if (event_type == NULL) { 354 - return js_mkerr(js, "event must be a string"); 355 - } 356 - 357 - if (vtype(args[1]) != T_FUNC) { 358 - return js_mkerr(js, "listener must be a function"); 359 - } 360 - 361 285 EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); 362 - if (evt == NULL) { 363 - return js_mkerr(js, "failed to create event type"); 364 - } 365 - 366 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 286 + if (evt == NULL) return js_mkerr(js, "failed to create event type"); 287 + if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) 367 288 return js_mkerr(js, "maximum number of listeners for event '%s' reached", event_type); 368 - } 369 - 289 + 370 290 EventListener *listener = &evt->listeners[evt->listener_count++]; 371 291 listener->listener = args[1]; 372 - listener->once = false; 373 - 292 + listener->once = once; 374 293 return this_obj; 375 294 } 376 295 296 + // EventEmitter.prototype.on(event, listener) 297 + static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { 298 + return js_eventemitter_add(js, args, nargs, false); 299 + } 300 + 377 301 // EventEmitter.prototype.once(event, listener) 378 302 static ant_value_t js_eventemitter_once(ant_t *js, ant_value_t *args, int nargs) { 379 - ant_value_t this_obj = js_getthis(js); 380 - 381 - if (nargs < 2) { 382 - return js_mkerr(js, "once requires 2 arguments (event, listener)"); 383 - } 384 - 385 - char *event_type = js_getstr(js, args[0], NULL); 386 - if (event_type == NULL) { 387 - return js_mkerr(js, "event must be a string"); 388 - } 389 - 390 - if (vtype(args[1]) != T_FUNC) { 391 - return js_mkerr(js, "listener must be a function"); 392 - } 393 - 394 - EventType *evt = find_or_create_emitter_event_type(js, this_obj, event_type); 395 - if (evt == NULL) { 396 - return js_mkerr(js, "failed to create event type"); 397 - } 398 - 399 - if (evt->listener_count >= MAX_LISTENERS_PER_EVENT) { 400 - return js_mkerr(js, "maximum number of listeners for event '%s' reached", event_type); 401 - } 402 - 403 - EventListener *listener = &evt->listeners[evt->listener_count++]; 404 - listener->listener = args[1]; 405 - listener->once = true; 406 - 407 - return this_obj; 303 + return js_eventemitter_add(js, args, nargs, true); 408 304 } 409 305 410 306 // EventEmitter.prototype.off(event, listener) 411 307 static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { 412 - ant_value_t this_obj = js_getthis(js); 413 - 414 - if (nargs < 2) { 415 - return js_mkerr(js, "off requires 2 arguments (event, listener)"); 416 - } 417 - 308 + if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); 418 309 char *event_type = js_getstr(js, args[0], NULL); 419 - if (event_type == NULL) { 420 - return js_mkerr(js, "event must be a string"); 421 - } 422 - 423 - EventType *evt = find_emitter_event_type(js, this_obj, event_type); 424 - if (evt == NULL) { 425 - return this_obj; 426 - } 427 - 428 - for (int i = 0; i < evt->listener_count; i++) { 429 - if (evt->listeners[i].listener == args[1]) { 430 - remove_listener_at(evt, i); break; 431 - } 432 - } 433 - 310 + if (event_type == NULL) return js_mkerr(js, "event must be a string"); 311 + ant_value_t this_obj = js_getthis(js); 312 + remove_event_listener_from(js, args, nargs, find_emitter_event_type(js, this_obj, event_type)); 434 313 return this_obj; 435 314 } 436 315 ··· 454 333 455 334 int listener_nargs = nargs - 1; 456 335 ant_value_t *listener_args = (listener_nargs > 0) ? &args[1] : NULL; 457 - 458 - int i = 0; 459 - while (i < evt->listener_count) { 336 + 337 + for (int i = 0; i < evt->listener_count;) { 460 338 EventListener *listener = &evt->listeners[i]; 461 339 ant_value_t result = sv_vm_call(js->vm, js, listener->listener, js_mkundef(), listener_args, listener_nargs, NULL, false); 462 - 463 - if (vtype(result) == T_ERR) { 340 + 341 + if (vtype(result) == T_ERR) 464 342 fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); 465 - } 466 - 467 - if (listener->once) { 468 - remove_listener_at(evt, i); 469 - } else i++; 343 + 344 + if (listener->once) remove_listener_at(evt, i); 345 + else i++; 470 346 } 471 347 472 348 return js_true; ··· 536 412 return result; 537 413 } 538 414 415 + // new CustomEvent(type, { detail: ... }) 416 + static ant_value_t js_customevent_ctor(ant_t *js, ant_value_t *args, int nargs) { 417 + if (nargs < 1) return js_mkerr(js, "CustomEvent requires a type argument"); 418 + 419 + char *event_type = js_getstr(js, args[0], NULL); 420 + if (event_type == NULL) return js_mkerr(js, "type must be a string"); 421 + 422 + ant_value_t event_obj = js_getthis(js); 423 + js_set(js, event_obj, "type", args[0]); 424 + js_set(js, event_obj, "detail", js_mknull()); 425 + 426 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 427 + ant_value_t detail = js_get(js, args[1], "detail"); 428 + if (vtype(detail) != T_UNDEF) js_set(js, event_obj, "detail", detail); 429 + } 430 + 431 + return js_mkundef(); 432 + } 433 + 539 434 ant_value_t events_library(ant_t *js) { 540 435 ant_value_t lib = js_mkobj(js); 541 436 ··· 571 466 572 467 ant_value_t eventtarget_ctor = js_mkobj(js); 573 468 ant_value_t eventtarget_proto = js_mkobj(js); 574 - 469 + ant_value_t customevent_ctor = js_mkobj(js); 470 + 575 471 js_set(js, eventtarget_proto, "addEventListener", js_mkfun(js_add_event_listener_method)); 576 472 js_set(js, eventtarget_proto, "removeEventListener", js_mkfun(js_remove_event_listener_method)); 577 473 js_set(js, eventtarget_proto, "dispatchEvent", js_mkfun(js_dispatch_event_method)); ··· 581 477 js_mkprop_fast(js, eventtarget_ctor, "name", 4, ANT_STRING("EventTarget")); 582 478 js_set_descriptor(js, eventtarget_ctor, "name", 4, 0); 583 479 480 + ant_value_t customevent_proto = js_mkobj(js); 481 + js_set_sym(js, customevent_proto, get_toStringTag_sym(), js_mkstr(js, "CustomEvent", 11)); 482 + 483 + js_set_slot(customevent_ctor, SLOT_CFUNC, js_mkfun(js_customevent_ctor)); 484 + js_mkprop_fast(js, customevent_ctor, "prototype", 9, customevent_proto); 485 + js_mkprop_fast(js, customevent_ctor, "name", 4, ANT_STRING("CustomEvent")); 486 + js_set_descriptor(js, customevent_ctor, "name", 4, 0); 487 + 488 + ant_value_t customevent_fn = js_obj_to_func(customevent_ctor); 489 + js_set(js, customevent_proto, "constructor", customevent_fn); 490 + js_set_descriptor(js, customevent_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 491 + 584 492 js_set(js, global, "addEventListener", js_mkfun(js_add_event_listener)); 585 493 js_set(js, global, "removeEventListener", js_mkfun(js_remove_event_listener)); 586 494 js_set(js, global, "dispatchEvent", js_mkfun(js_dispatch_event)); 587 495 js_set(js, global, "getEventListeners", js_mkfun(js_get_event_listeners)); 496 + 497 + js_set(js, global, "CustomEvent", customevent_fn); 588 498 js_set(js, global, "EventTarget", js_obj_to_func_ex(eventtarget_ctor, SV_CALL_IS_DEFAULT_CTOR)); 589 499 } 590 500
+176
src/modules/regex.c
··· 1308 1308 return result_arr; 1309 1309 } 1310 1310 1311 + static bool str_buf_append(char **buf, size_t *len, size_t *cap, const char *data, size_t n) { 1312 + if (n == 0) return true; 1313 + if (*len + n >= *cap) { 1314 + size_t new_cap = (*len + n + 1) * 2; 1315 + char *nb = (char *)ant_realloc(*buf, new_cap); 1316 + if (!nb) return false; 1317 + *buf = nb; 1318 + *cap = new_cap; 1319 + } 1320 + memcpy(*buf + *len, data, n); 1321 + *len += n; 1322 + return true; 1323 + } 1324 + 1325 + static inline ant_value_t emit_str_replacement( 1326 + ant_t *js, ant_value_t replacement, bool is_func, 1327 + const char *repl_ptr, ant_offset_t repl_len, 1328 + const char *str_ptr, ant_value_t str, 1329 + ant_offset_t pos, ant_offset_t match_len, 1330 + char **buf, size_t *buf_len, size_t *buf_cap 1331 + ) { 1332 + if (is_func) { 1333 + ant_value_t cb_args[3] = { js_mkstr(js, str_ptr + pos, match_len), tov((double)pos), str }; 1334 + ant_value_t r = sv_vm_call(js->vm, js, replacement, js_mkundef(), cb_args, 3, NULL, false); 1335 + 1336 + if (vtype(r) == T_ERR) return r; 1337 + ant_value_t r_str = js_tostring_val(js, r); 1338 + 1339 + if (is_err(r_str)) return r_str; 1340 + ant_offset_t rlen, roff = vstr(js, r_str, &rlen); 1341 + 1342 + if (!str_buf_append(buf, buf_len, buf_cap, (const char *)(uintptr_t)roff, rlen)) return js_mkerr(js, "oom"); 1343 + } else if (!str_buf_append(buf, buf_len, buf_cap, repl_ptr, repl_len)) return js_mkerr(js, "oom"); 1344 + 1345 + return js_mkundef(); 1346 + } 1347 + 1348 + static ant_value_t string_replace_impl(ant_t *js, ant_value_t *args, int nargs, bool replace_all) { 1349 + ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val); 1350 + ant_value_t str = js_tostring_val(js, this_unwrapped); 1351 + 1352 + if (is_err(str)) return str; 1353 + if (nargs < 1) return str; 1354 + 1355 + if (is_object_type(args[0])) { 1356 + if (replace_all) { 1357 + ant_value_t global_val = js_getprop_fallback(js, args[0], "global"); 1358 + if (!js_truthy(js, global_val)) return js_mkerr_typed(js, JS_ERR_TYPE, "String.prototype.replaceAll called with a non-global RegExp"); 1359 + } 1360 + 1361 + bool called = false; 1362 + ant_value_t replacement_arg = nargs > 1 ? args[1] : js_mkundef(); 1363 + ant_value_t call_args[2] = { str, replacement_arg }; 1364 + 1365 + ant_value_t result = maybe_call_symbol_method(js, args[0], get_replace_sym(), args[0], call_args, 2, &called); 1366 + if (is_err(result)) return result; 1367 + if (called) return result; 1368 + } 1369 + 1370 + if (nargs < 2) return str; 1371 + ant_value_t search = args[0]; 1372 + ant_value_t replacement = args[1]; 1373 + if (vtype(search) != T_STR) return str; 1374 + 1375 + ant_offset_t str_len, str_off = vstr(js, str, &str_len); 1376 + const char *str_ptr = (char *)(uintptr_t)(str_off); 1377 + ant_offset_t search_len, search_off = vstr(js, search, &search_len); 1378 + const char *search_ptr = (char *)(uintptr_t)(search_off); 1379 + 1380 + bool is_func = (vtype(replacement) == T_FUNC); 1381 + ant_offset_t repl_len = 0; 1382 + const char *repl_ptr = NULL; 1383 + 1384 + if (!is_func) { 1385 + if (vtype(replacement) != T_STR) return str; 1386 + ant_offset_t repl_off = vstr(js, replacement, &repl_len); 1387 + repl_ptr = (char *)(uintptr_t)(repl_off); 1388 + } 1389 + 1390 + if (!replace_all) { 1391 + if (search_len > str_len) return str; 1392 + ant_offset_t match_pos = 0; 1393 + bool found = false; 1394 + 1395 + for (ant_offset_t i = 0; i <= str_len - search_len; i++) 1396 + if (memcmp(str_ptr + i, search_ptr, search_len) == 0) { 1397 + match_pos = i; found = true; break; 1398 + } 1399 + 1400 + if (!found) return str; 1401 + 1402 + size_t cap = str_len + repl_len + 256, len = 0; 1403 + char *buf = (char *)ant_calloc(cap); 1404 + if (!buf) return js_mkerr(js, "oom"); 1405 + 1406 + if (!str_buf_append(&buf, &len, &cap, str_ptr, match_pos)) { 1407 + free(buf); 1408 + return js_mkerr(js, "oom"); 1409 + } 1410 + 1411 + ant_value_t err = emit_str_replacement( 1412 + js, replacement, is_func, repl_ptr, 1413 + repl_len, str_ptr, str, match_pos, 1414 + search_len, &buf, &len, &cap 1415 + ); 1416 + 1417 + if (vtype(err) == T_ERR) { 1418 + free(buf); 1419 + return err; 1420 + } 1421 + 1422 + if (!str_buf_append( 1423 + &buf, &len, &cap, str_ptr + match_pos + search_len, 1424 + str_len - match_pos - search_len) 1425 + ) { 1426 + free(buf); 1427 + return js_mkerr(js, "oom"); 1428 + } 1429 + 1430 + ant_value_t ret = js_mkstr(js, buf, len); 1431 + free(buf); 1432 + 1433 + return ret; 1434 + } else { 1435 + size_t cap = str_len + repl_len + 256, len = 0; 1436 + char *buf = (char *)ant_calloc(cap); 1437 + if (!buf) return js_mkerr(js, "oom"); 1438 + 1439 + ant_offset_t pos = 0; 1440 + bool replaced = false; 1441 + 1442 + while (pos + (ant_offset_t)search_len <= str_len) { 1443 + if (search_len == 0 || memcmp(str_ptr + pos, search_ptr, search_len) == 0) { 1444 + replaced = true; 1445 + ant_value_t err = emit_str_replacement(js, replacement, is_func, repl_ptr, repl_len, str_ptr, str, pos, search_len, &buf, &len, &cap); 1446 + if (vtype(err) == T_ERR) { free(buf); return err; } 1447 + if (search_len == 0) { 1448 + if (pos < str_len && !str_buf_append(&buf, &len, &cap, str_ptr + pos, 1)) { free(buf); return js_mkerr(js, "oom"); } 1449 + pos++; 1450 + } else pos += search_len; 1451 + } else { 1452 + if (!str_buf_append(&buf, &len, &cap, str_ptr + pos, 1)) { free(buf); return js_mkerr(js, "oom"); } 1453 + pos++; 1454 + } 1455 + } 1456 + 1457 + if (!str_buf_append( 1458 + &buf, &len, &cap, str_ptr + pos, 1459 + str_len - pos) 1460 + ) { 1461 + free(buf); 1462 + return js_mkerr(js, "oom"); 1463 + } 1464 + 1465 + if (!replaced) { 1466 + free(buf); 1467 + return str; 1468 + } 1469 + 1470 + ant_value_t ret = js_mkstr(js, buf, len); 1471 + free(buf); 1472 + 1473 + return ret; 1474 + } 1475 + } 1476 + 1477 + static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int nargs) { 1478 + return string_replace_impl(js, args, nargs, false); 1479 + } 1480 + 1481 + static ant_value_t builtin_string_replaceAll(ant_t *js, ant_value_t *args, int nargs) { 1482 + return string_replace_impl(js, args, nargs, true); 1483 + } 1484 + 1311 1485 static ant_value_t builtin_string_search(ant_t *js, ant_value_t *args, int nargs) { 1312 1486 ant_value_t this_unwrapped = unwrap_primitive(js, js->this_val); 1313 1487 ant_value_t str = js_tostring_val(js, this_unwrapped); ··· 1516 1690 ant_value_t string_proto = js_get(js, string_ctor, "prototype"); 1517 1691 js_setprop(js, string_proto, js_mkstr(js, "search", 6), js_mkfun(builtin_string_search)); 1518 1692 js_setprop(js, string_proto, js_mkstr(js, "match", 5), js_mkfun(builtin_string_match)); 1693 + js_setprop(js, string_proto, js_mkstr(js, "replace", 7), js_mkfun(builtin_string_replace)); 1694 + js_setprop(js, string_proto, js_mkstr(js, "replaceAll", 10), js_mkfun(builtin_string_replaceAll)); 1519 1695 } 1520 1696 1521 1697 void gc_sweep_regex_cache(void) {
-1
src/modules/util.c
··· 15 15 #include "modules/symbol.h" 16 16 #include "modules/util.h" 17 17 18 - 19 18 typedef struct { 20 19 char *buf; 21 20 size_t len;
+5 -1
src/pool.c
··· 173 173 size_t pool_threshold = GC_HEAP_GROWTH(js->gc_pool_last_live); 174 174 175 175 if (pool_threshold < (4u * 1024u * 1024u)) pool_threshold = 4u * 1024u * 1024u; 176 - if (js->gc_pool_alloc >= pool_threshold) gc_run(js); 176 + #ifdef ANT_JIT 177 + if (__builtin_expect(js->jit_active_depth > 0, 0)) { /* suppress GC during JIT */ } 178 + else 179 + #endif 180 + { if (js->gc_pool_alloc >= pool_threshold) gc_run(js); } 177 181 178 182 ant_pool_t *pool = pool_for_kind(js, kind); 179 183 if (pool->block_size == 0) pool->block_size = pool_default_block_size(kind);
+18 -13
src/shapes.c
··· 406 406 shape->gc_mark = gc_shape_epoch; 407 407 }} 408 408 409 - static void shape_prune_dead_children(ant_shape_t *shape) { 409 + static bool shape_prune_dead_children(ant_shape_t *shape) { 410 + bool freed_any = false; 410 411 shape_child_entry_t *ce, *ctmp; 412 + 411 413 HASH_ITER(hh, shape->children, ce, ctmp) { 412 - shape_prune_dead_children(ce->child); 413 - 414 - if (ce->child->gc_mark != gc_shape_epoch && !ce->child->children) { 415 - ant_shape_t *child = ce->child; 416 - child->parent = NULL; 417 - HASH_DEL(shape->children, ce); 418 - shape_entry_free(ce); 419 - ant_shape_release(child); 420 - } 421 - } 414 + if (shape_prune_dead_children(ce->child)) freed_any = true; 415 + if (ce->child->gc_mark != gc_shape_epoch && !ce->child->children) { 416 + ant_shape_t *child = ce->child; 417 + child->parent = NULL; 418 + HASH_DEL(shape->children, ce); 419 + shape_entry_free(ce); 420 + ant_shape_release(child); 421 + freed_any = true; 422 + }} 423 + 424 + return freed_any; 422 425 } 423 426 424 - void ant_gc_shapes_sweep(void) { 427 + bool ant_gc_shapes_sweep(void) { 428 + bool freed_any = false; 425 429 for (int i = 0; i <= (int)ANT_INOBJ_MAX_SLOTS; i++) { 426 - if (g_root_shapes[i]) shape_prune_dead_children(g_root_shapes[i]); 430 + if (g_root_shapes[i] && shape_prune_dead_children(g_root_shapes[i])) freed_any = true; 427 431 } 428 432 shape_entry_pool_trim(); 433 + return freed_any; 429 434 } 430 435 431 436 uint8_t ant_shape_get_inobj_limit(const ant_shape_t *shape) {
+44 -22
src/silver/swarm.c
··· 586 586 MIR_new_reg_op(ctx, r_ic_epoch), 587 587 MIR_new_mem_op(ctx, MIR_T_U32, 588 588 (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); 589 - MIR_append_insn(ctx, fn, 590 - MIR_new_insn(ctx, MIR_BNE, 591 - MIR_new_label_op(ctx, slow), 592 - MIR_new_reg_op(ctx, r_ic_epoch), 593 - MIR_new_reg_op(ctx, r_global_epoch))); 589 + { 590 + char ce_name[40]; 591 + snprintf(ce_name, sizeof(ce_name), "gf_ce_%d_%u", bc_off, (unsigned)ic_idx); 592 + MIR_reg_t r_cur_epoch = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, ce_name); 593 + MIR_append_insn(ctx, fn, 594 + MIR_new_insn(ctx, MIR_MOV, 595 + MIR_new_reg_op(ctx, r_cur_epoch), 596 + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_global_epoch, 0, 1))); 597 + MIR_append_insn(ctx, fn, 598 + MIR_new_insn(ctx, MIR_BNE, 599 + MIR_new_label_op(ctx, slow), 600 + MIR_new_reg_op(ctx, r_ic_epoch), 601 + MIR_new_reg_op(ctx, r_cur_epoch))); 602 + } 594 603 595 604 mir_emit_value_to_objptr_or_jmp( 596 605 ctx, fn, obj, r_obj_ptr, r_obj_tag, slow); ··· 2232 2241 2233 2242 MIR_reg_t r_ic_epoch_val = 0; 2234 2243 if (feat.needs_ic_epoch) { 2235 - r_ic_epoch_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_epoch"); 2236 - MIR_reg_t r_ep_tmp = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_ep_ptr"); 2244 + r_ic_epoch_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_ep_ptr"); 2237 2245 MIR_append_insn(ctx, jit_func, 2238 2246 MIR_new_insn(ctx, MIR_MOV, 2239 - MIR_new_reg_op(ctx, r_ep_tmp), 2247 + MIR_new_reg_op(ctx, r_ic_epoch_val), 2240 2248 MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)&ant_ic_epoch_counter))); 2241 - MIR_append_insn(ctx, jit_func, 2242 - MIR_new_insn(ctx, MIR_MOV, 2243 - MIR_new_reg_op(ctx, r_ic_epoch_val), 2244 - MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ep_tmp, 0, 1))); 2245 2249 } 2246 2250 2247 2251 MIR_reg_t r_bailout_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, "bail_val"); ··· 5503 5507 MIR_new_reg_op(ctx, r_ic_epoch), 5504 5508 MIR_new_mem_op(ctx, MIR_T_I32, 5505 5509 (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); 5506 - MIR_append_insn(ctx, jit_func, 5507 - MIR_new_insn(ctx, MIR_BNE, 5508 - MIR_new_label_op(ctx, slow), 5509 - MIR_new_reg_op(ctx, r_ic_epoch), 5510 - MIR_new_reg_op(ctx, r_ic_epoch_val))); 5510 + { 5511 + char ice_cur_name[40]; 5512 + snprintf(ice_cur_name, sizeof(ice_cur_name), "inst_ce_%d_%u", bc_off, (unsigned)ic_idx); 5513 + MIR_reg_t r_cur_ep = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, ice_cur_name); 5514 + MIR_append_insn(ctx, jit_func, 5515 + MIR_new_insn(ctx, MIR_MOV, 5516 + MIR_new_reg_op(ctx, r_cur_ep), 5517 + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ic_epoch_val, 0, 1))); 5518 + MIR_append_insn(ctx, jit_func, 5519 + MIR_new_insn(ctx, MIR_BNE, 5520 + MIR_new_label_op(ctx, slow), 5521 + MIR_new_reg_op(ctx, r_ic_epoch), 5522 + MIR_new_reg_op(ctx, r_cur_ep))); 5523 + } 5511 5524 5512 5525 MIR_append_insn(ctx, jit_func, 5513 5526 MIR_new_insn(ctx, MIR_URSH, ··· 5675 5688 MIR_new_reg_op(ctx, r_ic_epoch), 5676 5689 MIR_new_mem_op(ctx, MIR_T_I32, 5677 5690 (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); 5678 - MIR_append_insn(ctx, jit_func, 5679 - MIR_new_insn(ctx, MIR_BNE, 5680 - MIR_new_label_op(ctx, slow), 5681 - MIR_new_reg_op(ctx, r_ic_epoch), 5682 - MIR_new_reg_op(ctx, r_ic_epoch_val))); 5691 + { 5692 + char cip_ce_name[40]; 5693 + snprintf(cip_ce_name, sizeof(cip_ce_name), "cip_ce_%d_%u", bc_off, (unsigned)ic_idx); 5694 + MIR_reg_t r_cur_ep = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ce_name); 5695 + MIR_append_insn(ctx, jit_func, 5696 + MIR_new_insn(ctx, MIR_MOV, 5697 + MIR_new_reg_op(ctx, r_cur_ep), 5698 + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ic_epoch_val, 0, 1))); 5699 + MIR_append_insn(ctx, jit_func, 5700 + MIR_new_insn(ctx, MIR_BNE, 5701 + MIR_new_label_op(ctx, slow), 5702 + MIR_new_reg_op(ctx, r_ic_epoch), 5703 + MIR_new_reg_op(ctx, r_cur_ep))); 5704 + } 5683 5705 5684 5706 mir_emit_value_to_objptr_or_jmp( 5685 5707 ctx, jit_func, this_obj, r_proto_ptr, r_proto_tag, slow);