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.

enable JIT in regex

+135 -26
+2
meson/deps/meson.build
··· 12 12 boringssl_opts.append_compile_args('c', boringssl_warning_flag) 13 13 boringssl_opts.append_compile_args('cpp', boringssl_warning_flag) 14 14 boringssl_opts.set_override_option('warning_level', '0') 15 + boringssl_opts.set_override_option('optimization', 's') 15 16 16 17 boringssl_sub = cmake.subproject('boringssl', options: boringssl_opts) 17 18 ssl_dep = boringssl_sub.dependency('ssl') ··· 137 138 ]).get_variable('ffi_dep') 138 139 139 140 pcre2_dep = subproject('pcre2', default_options: [ 141 + 'optimization=s', 140 142 'warning_level=0', 141 143 'grep=false', 142 144 'test=false'
+3 -3
src/ant.c
··· 13399 13399 return js_eval_bytecode_mode(js, buf, len, SV_COMPILE_REPL, false); 13400 13400 } 13401 13401 13402 - ant_value_t inline sv_call_cfunc(ant_params_t, ant_bind_t) { 13402 + static inline ant_value_t sv_call_cfunc(ant_params_t, ant_bind_t) { 13403 13403 ant_value_t saved_this = js->this_val; 13404 13404 js->this_val = this_val; 13405 13405 ant_value_t res = js_as_cfunc(func)(js, args, nargs); ··· 13407 13407 return res; 13408 13408 } 13409 13409 13410 - ant_value_t inline sv_call_slot_cfunc(ant_params_t, ant_bind_t, ant_value_t cfunc_slot) { 13410 + static inline ant_value_t sv_call_slot_cfunc(ant_params_t, ant_bind_t, ant_value_t cfunc_slot) { 13411 13411 ant_value_t saved_func = js->current_func; 13412 13412 ant_value_t saved_this = js->this_val; 13413 13413 js->current_func = func; ··· 13419 13419 } 13420 13420 13421 13421 13422 - ant_value_t inline sv_call_object_builtin(ant_params_t, ant_value_t this_val) { 13422 + static inline ant_value_t sv_call_object_builtin(ant_params_t, ant_value_t this_val) { 13423 13423 ant_value_t saved_this = js->this_val; 13424 13424 js->this_val = this_val; 13425 13425 ant_value_t res = builtin_Object(js, args, nargs);
+23 -23
src/modules/regex.c
··· 24 24 ant_object_t *obj; 25 25 pcre2_code *code; 26 26 pcre2_match_data *match_data; 27 + bool jit_ready; 27 28 } regex_cache_entry_t; 28 29 29 30 static regex_cache_entry_t *regex_cache = NULL; ··· 529 530 return NULL; 530 531 } 531 532 532 - static regex_cache_entry_t *regex_cache_insert(ant_object_t *obj, pcre2_code *code, pcre2_match_data *match_data) { 533 + static regex_cache_entry_t *regex_cache_insert(ant_object_t *obj, pcre2_code *code, pcre2_match_data *match_data, bool jit_ready) { 533 534 if (regex_cache_count >= regex_cache_cap) { 534 535 size_t new_cap = regex_cache_cap ? regex_cache_cap * 2 : 64; 535 536 regex_cache_entry_t *new_cache = realloc(regex_cache, new_cap * sizeof(regex_cache_entry_t)); ··· 541 542 entry->obj = obj; 542 543 entry->code = code; 543 544 entry->match_data = match_data; 545 + entry->jit_ready = jit_ready; 544 546 return entry; 545 547 } 546 548 547 549 typedef struct { 548 550 pcre2_code *code; 549 551 pcre2_match_data *match_data; 552 + bool jit_ready; 550 553 } compiled_regex_t; 551 554 552 555 static bool regex_get_or_compile(ant_t *js, ant_value_t regexp_obj, compiled_regex_t *out) { ··· 556 559 if (cached) { 557 560 out->code = cached->code; 558 561 out->match_data = cached->match_data; 562 + out->jit_ready = cached->jit_ready; 559 563 return true; 560 564 } 561 565 ··· 567 571 ant_offset_t plen, poff = vstr(js, source_val, &plen); 568 572 const char *pattern_ptr = (char *)(uintptr_t)(poff); 569 573 570 - bool ignore_case = false, multiline = false, dotall = false, sticky = false, unicode = false, v_flag = false; 574 + bool ignore_case = false, multiline = false, dotall = false, v_flag = false; 571 575 ant_offset_t flags_off = lkp(js, regexp_obj, "flags", 5); 572 576 if (flags_off != 0) { 573 577 ant_value_t flags_val = js_propref_load(js, flags_off); 574 578 if (vtype(flags_val) == T_STR) { 575 - ant_offset_t flen, foff = vstr(js, flags_val, &flen); 576 - const char *flags_str = (char *)(uintptr_t)(foff); 577 - for (ant_offset_t i = 0; i < flen; i++) { 578 - if (flags_str[i] == 'i') ignore_case = true; 579 - if (flags_str[i] == 'm') multiline = true; 580 - if (flags_str[i] == 's') dotall = true; 581 - if (flags_str[i] == 'y') sticky = true; 582 - if (flags_str[i] == 'u') unicode = true; 583 - if (flags_str[i] == 'v') v_flag = true; 584 - } 585 - } 579 + ant_offset_t flen, foff = vstr(js, flags_val, &flen); 580 + const char *flags_str = (char *)(uintptr_t)(foff); 581 + for (ant_offset_t i = 0; i < flen; i++) { 582 + if (flags_str[i] == 'i') ignore_case = true; 583 + if (flags_str[i] == 'm') multiline = true; 584 + if (flags_str[i] == 's') dotall = true; 585 + if (flags_str[i] == 'v') v_flag = true; 586 + }} 586 587 } 587 588 588 589 char pcre2_pattern[4096]; ··· 592 593 if (ignore_case) options |= PCRE2_CASELESS; 593 594 if (multiline) options |= PCRE2_MULTILINE; 594 595 if (dotall) options |= PCRE2_DOTALL; 595 - (void)sticky; 596 - (void)unicode; 597 596 598 597 int errcode; 599 598 PCRE2_SIZE erroffset; ··· 601 600 if (re == NULL) return false; 602 601 603 602 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); 604 - regex_cache_insert(obj_ptr, re, match_data); 603 + bool jit_ready = pcre2_jit_compile(re, PCRE2_JIT_COMPLETE) == 0; 604 + regex_cache_insert(obj_ptr, re, match_data, jit_ready); 605 605 606 606 out->code = re; 607 607 out->match_data = match_data; 608 + out->jit_ready = jit_ready; 608 609 return true; 609 610 } 610 611 ··· 723 724 uint32_t match_options = 0; 724 725 if (sticky_flag) match_options |= PCRE2_ANCHORED; 725 726 726 - int rc = pcre2_match(compiled.code, (PCRE2_SPTR)str_ptr, str_len, start_offset, match_options, compiled.match_data, NULL); 727 + int rc; 728 + if (compiled.jit_ready) { 729 + rc = pcre2_jit_match(compiled.code, (PCRE2_SPTR)str_ptr, str_len, start_offset, match_options, compiled.match_data, NULL); 730 + } else rc = pcre2_match(compiled.code, (PCRE2_SPTR)str_ptr, str_len, start_offset, match_options, compiled.match_data, NULL); 727 731 728 732 if (rc < 0) { 729 - if (global_flag || sticky_flag) { 730 - js_setprop(js, regexp, js_mkstr(js, "lastIndex", 9), tov(0)); 731 - } 733 + if (global_flag || sticky_flag) js_setprop(js, regexp, js_mkstr(js, "lastIndex", 9), tov(0)); 732 734 return js_mknull(); 733 735 } 734 736 ··· 770 772 tabptr += nameentrysize; 771 773 } 772 774 js_setprop(js, result_arr, js_mkstr(js, "groups", 6), groups); 773 - } else { 774 - js_setprop(js, result_arr, js_mkstr(js, "groups", 6), js_mkundef()); 775 - } 775 + } else js_setprop(js, result_arr, js_mkstr(js, "groups", 6), js_mkundef()); 776 776 777 777 update_regexp_statics(js, str_ptr, ovector, ovcount); 778 778
+47
tests/bench_crypto.cjs
··· 1 + const now = () => 2 + typeof performance !== 'undefined' && performance && typeof performance.now === 'function' 3 + ? performance.now() 4 + : Date.now(); 5 + 6 + const { createHash, randomBytes } = 7 + typeof require === 'function' ? require('crypto') : Ant.require('crypto'); 8 + 9 + function bench(name, rounds, fn) { 10 + fn(); 11 + const start = now(); 12 + let result; 13 + for (let i = 0; i < rounds; i++) result = fn(); 14 + const elapsed = now() - start; 15 + console.log( 16 + `${name}: ${elapsed.toFixed(2)}ms total, ${(elapsed / rounds).toFixed(4)}ms/round` 17 + ); 18 + return result; 19 + } 20 + 21 + const small = 'The quick brown fox jumps over the lazy dog'; 22 + const medium = '0123456789abcdef'.repeat(4096); 23 + const large = 'abcdef0123456789'.repeat(65536); 24 + 25 + console.log('=== Crypto Benchmark ==='); 26 + console.log(`small bytes: ${small.length}`); 27 + console.log(`medium bytes: ${medium.length}`); 28 + console.log(`large bytes: ${large.length}`); 29 + console.log(''); 30 + 31 + bench('sha256 small x20000', 20000, () => 32 + createHash('sha256').update(small).digest('hex') 33 + ); 34 + 35 + bench('sha256 medium x1500', 1500, () => 36 + createHash('sha256').update(medium).digest('hex') 37 + ); 38 + 39 + bench('sha256 large x120', 120, () => 40 + createHash('sha256').update(large).digest('hex') 41 + ); 42 + 43 + bench('sha512 medium x1000', 1000, () => 44 + createHash('sha512').update(medium).digest('hex') 45 + ); 46 + 47 + bench('randomBytes 4k x4000', 4000, () => randomBytes(4096));
+60
tests/bench_regex.cjs
··· 1 + const now = () => 2 + typeof performance !== 'undefined' && performance && typeof performance.now === 'function' 3 + ? performance.now() 4 + : Date.now(); 5 + 6 + function bench(name, rounds, fn) { 7 + fn(); 8 + const start = now(); 9 + let result; 10 + for (let i = 0; i < rounds; i++) result = fn(); 11 + const elapsed = now() - start; 12 + console.log( 13 + `${name}: ${elapsed.toFixed(2)}ms total, ${(elapsed / rounds).toFixed(4)}ms/round` 14 + ); 15 + return result; 16 + } 17 + 18 + const routeCorpus = new Array(2000) 19 + .fill(0) 20 + .map((_, i) => `/api/v1/users/${i}/posts/${i % 17}?limit=50&offset=${i}`) 21 + .join('\n'); 22 + 23 + const unicodeCorpus = new Array(2500) 24 + .fill('alpha beta gamma delta user_123 route_456 JSON42 token99') 25 + .join(' '); 26 + 27 + const routeSource = 28 + '^/api/v(?<version>[0-9]+)/users/(?<user>[0-9]+)/posts/(?<post>[0-9]+)(?:\\?(?<query>.*))?$'; 29 + 30 + const routeRe = new RegExp(routeSource, 'gm'); 31 + const tokenRe = /\b[a-z_]+[0-9]*\b/g; 32 + const splitRe = /[A-Za-z_][A-Za-z0-9_]{0,31}/g; 33 + 34 + console.log('=== Regex Benchmark ==='); 35 + console.log(`route corpus bytes: ${routeCorpus.length}`); 36 + console.log(`unicode corpus bytes: ${unicodeCorpus.length}`); 37 + console.log(''); 38 + 39 + bench('compile route regexp x5000', 5000, () => new RegExp(routeSource, 'gm')); 40 + 41 + bench('route matches x400', 400, () => { 42 + routeRe.lastIndex = 0; 43 + let count = 0; 44 + while (routeRe.exec(routeCorpus)) count++; 45 + return count; 46 + }); 47 + 48 + bench('token scan x500', 500, () => { 49 + tokenRe.lastIndex = 0; 50 + let count = 0; 51 + while (tokenRe.exec(unicodeCorpus)) count++; 52 + return count; 53 + }); 54 + 55 + bench('identifier split x500', 500, () => { 56 + splitRe.lastIndex = 0; 57 + let count = 0; 58 + while (splitRe.exec(unicodeCorpus)) count++; 59 + return count; 60 + });