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.

thread more string operations in the jit

+1814 -111
+1 -1
include/gc.h
··· 6 6 #include <stdint.h> 7 7 8 8 #define GC_MIN_TICK 1024 9 + #define GC_NURSERY_THRESHOLD 32768 9 10 #define GC_FORCE_INTERVAL_MS 50 10 - #define GC_NURSERY_THRESHOLD 4096 11 11 #define GC_MAJOR_EVERY_N_MINOR 8 12 12 13 13 #define GC_HEAP_GROWTH(n) ((n) + (n) / 2)
+1
include/modules/symbol.h
··· 37 37 #define WELLKNOWN_SYMBOLS(X) \ 38 38 X(iterator, "Symbol.iterator") \ 39 39 X(asyncIterator, "Symbol.asyncIterator") \ 40 + X(inspect, "Symbol.inspect") \ 40 41 X(toStringTag, "Symbol.toStringTag") \ 41 42 X(hasInstance, "Symbol.hasInstance") \ 42 43 X(match, "Symbol.match") \
+1
include/silver/ast.h
··· 101 101 FN_INVALID_COOKED = 1 << 11, 102 102 FN_PARSE_STRICT = 1 << 12, 103 103 FN_TEMPLATE_SEGMENT = 1 << 13, 104 + FN_USES_NEW_TARGET = 1 << 14, 104 105 }; 105 106 106 107 enum {
+32 -3
include/silver/glue.h
··· 42 42 43 43 ant_value_t jit_helper_delete(sv_vm_t *vm, ant_t *js, ant_value_t obj, ant_value_t key); 44 44 ant_value_t jit_helper_typeof(sv_vm_t *vm, ant_t *js, ant_value_t v); 45 - ant_value_t jit_helper_special_obj(ant_t *js, uint32_t which); 45 + ant_value_t jit_helper_special_obj(sv_vm_t *vm, ant_t *js, uint32_t which); 46 46 47 47 ant_value_t jit_helper_get_global( 48 48 ant_t *js, const char *str, ··· 108 108 ant_value_t val, const char *str, uint32_t len 109 109 ); 110 110 111 + void jit_helper_define_method_comp( 112 + ant_t *js, 113 + ant_value_t obj, ant_value_t key, ant_value_t fn, uint8_t flags 114 + ); 115 + 111 116 void jit_helper_set_name( 112 117 sv_vm_t *vm, ant_t *js, ant_value_t fn, 113 118 const char *str, uint32_t len ··· 138 143 ant_value_t *elements, int count 139 144 ); 140 145 146 + ant_value_t jit_helper_for_of( 147 + sv_vm_t *vm, ant_t *js, 148 + ant_value_t iterable, ant_value_t *iter_buf 149 + ); 150 + 151 + void jit_helper_destructure_close( 152 + sv_vm_t *vm, ant_t *js, 153 + ant_value_t *iter_buf 154 + ); 155 + 156 + ant_value_t jit_helper_destructure_next( 157 + sv_vm_t *vm, ant_t *js, 158 + ant_value_t *iter_buf 159 + ); 160 + 141 161 ant_value_t jit_helper_throw_error( 142 162 sv_vm_t *vm, ant_t *js, 143 163 const char *str, uint32_t len, int err_type ··· 161 181 162 182 ant_value_t jit_helper_str_append_local( 163 183 sv_vm_t *vm, ant_t *js, sv_func_t *func, 164 - ant_value_t *locals, uint16_t local_idx, ant_value_t rhs 184 + ant_value_t *args, int argc, 185 + ant_value_t *locals, uint16_t slot_idx, 186 + ant_value_t rhs 165 187 ); 166 188 167 189 ant_value_t jit_helper_str_append_local_snapshot( 168 190 sv_vm_t *vm, ant_t *js, sv_func_t *func, 169 - ant_value_t *locals, uint16_t local_idx, 191 + ant_value_t *args, int argc, 192 + ant_value_t *locals, uint16_t slot_idx, 170 193 ant_value_t lhs, ant_value_t rhs 194 + ); 195 + 196 + ant_value_t jit_helper_str_flush_local( 197 + sv_vm_t *vm, ant_t *js, sv_func_t *func, 198 + ant_value_t *args, int argc, 199 + ant_value_t *locals, uint16_t slot_idx 171 200 ); 172 201 173 202 #endif
+23
src/silver/ast.c
··· 1287 1287 return false; 1288 1288 } 1289 1289 1290 + static bool ast_references_new_target(const sv_ast_t *node) { 1291 + if (!node) return false; 1292 + if (node->type == N_NEW_TARGET) return true; 1293 + if (node->type == N_FUNC && !(node->flags & FN_ARROW)) return false; 1294 + 1295 + if (ast_references_new_target(node->left)) return true; 1296 + if (ast_references_new_target(node->right)) return true; 1297 + if (ast_references_new_target(node->cond)) return true; 1298 + if (ast_references_new_target(node->body)) return true; 1299 + if (ast_references_new_target(node->catch_body)) return true; 1300 + if (ast_references_new_target(node->finally_body)) return true; 1301 + if (ast_references_new_target(node->catch_param)) return true; 1302 + if (ast_references_new_target(node->init)) return true; 1303 + if (ast_references_new_target(node->update)) return true; 1304 + 1305 + for (int i = 0; i < node->args.count; i++) 1306 + if (ast_references_new_target(node->args.items[i])) return true; 1307 + 1308 + return false; 1309 + } 1310 + 1290 1311 static sv_ast_t *parse_func(P) { 1291 1312 sv_ast_t *fn = mk(N_FUNC); 1292 1313 ··· 1330 1351 fn->src_end = (uint32_t)(TOFF + TLEN); 1331 1352 if (!(fn->flags & FN_ARROW) && ast_references_arguments(fn->body)) 1332 1353 fn->flags |= FN_USES_ARGS; 1354 + if (!(fn->flags & FN_ARROW) && ast_references_new_target(fn->body)) 1355 + fn->flags |= FN_USES_NEW_TARGET; 1333 1356 return fn; 1334 1357 } 1335 1358
+2 -2
src/silver/compiler.c
··· 911 911 int local = resolve_local(c, node->left->str, node->left->len); 912 912 if (local < 0 || c->locals[local].is_const) return false; 913 913 if (c->locals[local].is_tdz) return false; 914 - if (c->locals[local].depth == -1 && has_implicit_arguments_obj(c)) return false; 914 + if (c->locals[local].depth == -1 && c->strict_args_local >= 0) return false; 915 915 916 916 sv_ast_t *rhs = NULL; 917 917 if (node->op == TOK_PLUS_ASSIGN) rhs = node->right; ··· 4447 4447 emit_put_local(&comp, comp.strict_args_local); 4448 4448 } 4449 4449 4450 - if (!comp.is_arrow && comp.enclosing) { 4450 + if (!comp.is_arrow && comp.enclosing && (node->flags & FN_USES_NEW_TARGET)) { 4451 4451 static const char nt_name[] = "\x01new.target"; 4452 4452 comp.new_target_local = add_local(&comp, nt_name, sizeof(nt_name) - 1, false, comp.scope_depth); 4453 4453 emit_op(&comp, OP_SPECIAL_OBJ);
+365 -13
src/silver/glue.c
··· 9 9 10 10 #include "ops/globals.h" 11 11 #include "ops/property.h" 12 + #include "ops/iteration.h" 12 13 #include "ops/upvalues.h" 13 14 #include "ops/comparison.h" 14 15 #include "ops/calls.h" ··· 22 23 return used > js->cstk.limit; 23 24 } 24 25 26 + static bool jit_vm_ensure_stack(sv_vm_t *vm, int need) { 27 + while (vm->sp + need > vm->stack_size) { 28 + int new_size = vm->stack_size * 2; 29 + if (new_size > SV_STACK_HARD_MAX) new_size = SV_STACK_HARD_MAX; 30 + if (new_size <= vm->stack_size) return false; 31 + 32 + ant_value_t *old = vm->stack; 33 + int old_size = vm->stack_size; 34 + 35 + ant_value_t *ns = realloc(vm->stack, (size_t)new_size * sizeof(ant_value_t)); 36 + if (!ns) return false; 37 + 38 + ptrdiff_t delta = ns - old; 39 + vm->stack = ns; 40 + vm->stack_size = new_size; 41 + 42 + if (delta != 0) { 43 + for (int i = 0; i <= vm->fp; i++) { 44 + if (vm->frames[i].bp) vm->frames[i].bp += delta; 45 + if (vm->frames[i].lp) vm->frames[i].lp += delta; 46 + } 47 + for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) if ( 48 + uv->location != &uv->closed && 49 + sv_slot_in_range(old, (size_t)old_size, uv->location) 50 + ) uv->location += delta; 51 + } 52 + } 53 + 54 + return true; 55 + } 56 + 25 57 ant_value_t jit_helper_stack_overflow_error(sv_vm_t *vm, ant_t *js) { 26 58 return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum JIT call stack size exceeded"); 27 59 } ··· 53 85 54 86 ant_value_t jit_helper_str_append_local( 55 87 sv_vm_t *vm, ant_t *js, sv_func_t *func, 56 - ant_value_t *locals, uint16_t local_idx, ant_value_t rhs 88 + ant_value_t *args, int argc, 89 + ant_value_t *locals, uint16_t slot_idx, 90 + ant_value_t rhs 57 91 ) { 58 - if (!func || !locals || local_idx >= (uint16_t)func->max_locals) 92 + if (!func) 59 93 return SV_JIT_BAILOUT; 60 - 94 + 61 95 sv_frame_t frame = { 62 96 .func = func, 97 + .bp = args, 63 98 .lp = locals, 64 - .argc = func->param_count, 99 + .argc = argc, 100 + .arguments_obj = js_mkundef(), 65 101 }; 66 - 67 - uint16_t slot_idx = (uint16_t)(func->param_count + local_idx); 102 + 68 103 return sv_string_builder_append_slot(vm, js, &frame, func, slot_idx, rhs); 69 104 } 70 105 71 106 ant_value_t jit_helper_str_append_local_snapshot( 72 107 sv_vm_t *vm, ant_t *js, sv_func_t *func, 73 - ant_value_t *locals, uint16_t local_idx, 108 + ant_value_t *args, int argc, 109 + ant_value_t *locals, uint16_t slot_idx, 74 110 ant_value_t lhs, ant_value_t rhs 75 111 ) { 76 - if (!func || !locals || local_idx >= (uint16_t)func->max_locals) 112 + if (!func) 77 113 return SV_JIT_BAILOUT; 78 - 114 + 79 115 sv_frame_t frame = { 80 116 .func = func, 117 + .bp = args, 81 118 .lp = locals, 82 - .argc = func->param_count, 119 + .argc = argc, 120 + .arguments_obj = js_mkundef(), 83 121 }; 84 - 85 - uint16_t slot_idx = (uint16_t)(func->param_count + local_idx); 122 + 86 123 return sv_string_builder_append_snapshot_slot(vm, js, &frame, func, slot_idx, lhs, rhs); 124 + } 125 + 126 + ant_value_t jit_helper_str_flush_local( 127 + sv_vm_t *vm, ant_t *js, sv_func_t *func, 128 + ant_value_t *args, int argc, 129 + ant_value_t *locals, uint16_t slot_idx 130 + ) { 131 + if (!func) 132 + return SV_JIT_BAILOUT; 133 + 134 + sv_frame_t frame = { 135 + .func = func, 136 + .bp = args, 137 + .lp = locals, 138 + .argc = argc, 139 + .arguments_obj = js_mkundef(), 140 + }; 141 + 142 + ant_value_t flush = sv_string_builder_flush_slot(vm, js, &frame, slot_idx); 143 + if (is_err(flush)) return flush; 144 + return js_mkundef(); 87 145 } 88 146 89 147 ant_value_t jit_helper_lt(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r) { ··· 137 195 return sv_global_get_interned_ic(js, str, func, ip); 138 196 } 139 197 140 - ant_value_t jit_helper_special_obj(ant_t *js, uint32_t which) { 198 + ant_value_t jit_helper_special_obj(sv_vm_t *vm, ant_t *js, uint32_t which) { 199 + if (which == 1) return sv_vm_get_new_target(vm, js); 200 + if (which == 2) return sv_vm_get_super_val(vm); 141 201 if (which == 3) return js_get_module_import_binding(js); 142 202 return js_mkundef(); 203 + } 204 + 205 + void jit_helper_define_method_comp( 206 + ant_t *js, 207 + ant_value_t obj, ant_value_t key, ant_value_t fn, uint8_t flags 208 + ) { 209 + ant_value_t desc_obj = js_as_obj(obj); 210 + bool is_getter = (flags & 1) != 0; 211 + bool is_setter = (flags & 2) != 0; 212 + if (vtype(key) == T_SYMBOL) { 213 + if (is_getter) { js_set_sym_getter_desc(js, desc_obj, key, fn, JS_DESC_E | JS_DESC_C); return; } 214 + if (is_setter) { js_set_sym_setter_desc(js, desc_obj, key, fn, JS_DESC_E | JS_DESC_C); return; } 215 + js_set_sym(js, obj, key, fn); 216 + return; 217 + } 218 + ant_value_t key_str = sv_key_to_propstr(js, key); 219 + if ((is_getter || is_setter) && vtype(key_str) == T_STR) { 220 + ant_offset_t klen = 0; 221 + ant_offset_t koff = vstr(js, key_str, &klen); 222 + const char *kptr = (const char *)(uintptr_t)(koff); 223 + if (is_getter) js_set_getter_desc(js, desc_obj, kptr, klen, fn, JS_DESC_E | JS_DESC_C); 224 + else js_set_setter_desc(js, desc_obj, kptr, klen, fn, JS_DESC_E | JS_DESC_C); 225 + return; 226 + } 227 + if (vtype(key_str) == T_STR) { 228 + ant_offset_t klen = 0; 229 + ant_offset_t koff = vstr(js, key_str, &klen); 230 + const char *kptr = (const char *)(uintptr_t)(koff); 231 + js_define_own_prop(js, obj, kptr, (size_t)klen, fn); 232 + } else mkprop(js, obj, key_str, fn, 0); 233 + } 234 + 235 + static ant_value_t jit_iter_advance_from_buf( 236 + sv_vm_t *vm, ant_t *js, ant_value_t *iter_buf, int hint, 237 + ant_value_t *out_value, bool *out_done 238 + ) { 239 + GC_ROOT_SAVE(root_mark, js); 240 + 241 + int tag = hint ? hint : (int)js_getnum(iter_buf[2]); 242 + switch (tag) { 243 + case SV_ITER_ARRAY: { 244 + ant_value_t arr = iter_buf[0]; 245 + GC_ROOT_PIN(js, arr); 246 + int idx = (int)js_getnum(iter_buf[1]); 247 + ant_offset_t len = js_arr_len(js, arr); 248 + if (idx >= (int)len) { 249 + *out_value = js_mkundef(); 250 + *out_done = true; 251 + } else { 252 + *out_value = js_arr_get(js, arr, (ant_offset_t)idx); 253 + *out_done = false; 254 + iter_buf[1] = tov(idx + 1); 255 + } 256 + GC_ROOT_RESTORE(js, root_mark); 257 + return tov(0); 258 + } 259 + 260 + case SV_ITER_MAP: { 261 + map_iterator_state_t *st = (map_iterator_state_t *)(uintptr_t)js_getnum(iter_buf[0]); 262 + if (!st->current) { 263 + *out_value = js_mkundef(); 264 + *out_done = true; 265 + } else { 266 + map_entry_t *entry = st->current; 267 + ant_value_t value; 268 + switch (st->type) { 269 + case ITER_TYPE_MAP_VALUES: 270 + value = entry->value; 271 + break; 272 + case ITER_TYPE_MAP_KEYS: 273 + value = entry->key_val; 274 + break; 275 + case ITER_TYPE_MAP_ENTRIES: { 276 + ant_value_t pair = js_mkarr(js); 277 + js_arr_push(js, pair, entry->key_val); 278 + js_arr_push(js, pair, entry->value); 279 + value = pair; 280 + break; 281 + } 282 + default: 283 + value = js_mkundef(); 284 + } 285 + st->current = entry->hh.next; 286 + *out_value = value; 287 + *out_done = false; 288 + } 289 + GC_ROOT_RESTORE(js, root_mark); 290 + return tov(0); 291 + } 292 + 293 + case SV_ITER_SET: { 294 + set_iterator_state_t *st = (set_iterator_state_t *)(uintptr_t)js_getnum(iter_buf[0]); 295 + if (!st->current) { 296 + *out_value = js_mkundef(); 297 + *out_done = true; 298 + } else { 299 + set_entry_t *entry = st->current; 300 + ant_value_t value; 301 + if (st->type == ITER_TYPE_SET_ENTRIES) { 302 + ant_value_t pair = js_mkarr(js); 303 + js_arr_push(js, pair, entry->value); 304 + js_arr_push(js, pair, entry->value); 305 + value = pair; 306 + } else { 307 + value = entry->value; 308 + } 309 + st->current = entry->hh.next; 310 + *out_value = value; 311 + *out_done = false; 312 + } 313 + GC_ROOT_RESTORE(js, root_mark); 314 + return tov(0); 315 + } 316 + 317 + case SV_ITER_STRING: { 318 + ant_value_t str = iter_buf[0]; 319 + GC_ROOT_PIN(js, str); 320 + int idx = (int)js_getnum(iter_buf[1]); 321 + ant_offset_t slen = str_len_fast(js, str); 322 + if (idx >= (int)slen) { 323 + *out_value = js_mkundef(); 324 + *out_done = true; 325 + } else { 326 + ant_offset_t off = vstr(js, str, NULL); 327 + utf8proc_int32_t cp; 328 + ant_offset_t cb_len = (ant_offset_t)utf8_next( 329 + (const utf8proc_uint8_t *)(uintptr_t)(off + idx), 330 + (utf8proc_ssize_t)(slen - idx), 331 + &cp 332 + ); 333 + *out_value = js_mkstr(js, (const void *)(uintptr_t)(off + idx), cb_len); 334 + *out_done = false; 335 + iter_buf[1] = tov(idx + (int)cb_len); 336 + } 337 + GC_ROOT_PIN(js, *out_value); 338 + GC_ROOT_RESTORE(js, root_mark); 339 + return tov(0); 340 + } 341 + 342 + default: { 343 + ant_value_t iterator = iter_buf[0]; 344 + ant_value_t next_method = iter_buf[1]; 345 + GC_ROOT_PIN(js, iterator); 346 + GC_ROOT_PIN(js, next_method); 347 + uint8_t ft = vtype(next_method); 348 + if (ft != T_FUNC && ft != T_CFUNC) { 349 + GC_ROOT_RESTORE(js, root_mark); 350 + return js_mkerr(js, "iterator.next is not a function"); 351 + } 352 + ant_value_t result = sv_vm_call(vm, js, next_method, iterator, NULL, 0, NULL, false); 353 + if (is_err(result)) { 354 + GC_ROOT_RESTORE(js, root_mark); 355 + return result; 356 + } 357 + GC_ROOT_PIN(js, result); 358 + if (!is_object_type(result)) { 359 + GC_ROOT_RESTORE(js, root_mark); 360 + return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator result is not an object"); 361 + } 362 + ant_value_t done = js_mkundef(); 363 + GC_ROOT_PIN(js, done); 364 + sv_iter_result_unpack(js, result, &done, out_value); 365 + GC_ROOT_PIN(js, *out_value); 366 + if (is_err(done)) { 367 + GC_ROOT_RESTORE(js, root_mark); 368 + return done; 369 + } 370 + if (is_err(*out_value)) { 371 + GC_ROOT_RESTORE(js, root_mark); 372 + return *out_value; 373 + } 374 + *out_done = js_truthy(js, done); 375 + GC_ROOT_RESTORE(js, root_mark); 376 + return tov(0); 377 + }} 378 + } 379 + 380 + ant_value_t jit_helper_for_of( 381 + sv_vm_t *vm, ant_t *js, 382 + ant_value_t iterable, ant_value_t *iter_buf 383 + ) { 384 + GC_ROOT_SAVE(root_mark, js); 385 + GC_ROOT_PIN(js, iterable); 386 + 387 + if (vtype(iterable) == T_ARR) { 388 + iter_buf[0] = iterable; 389 + iter_buf[1] = tov(0); 390 + iter_buf[2] = tov(SV_ITER_ARRAY); 391 + GC_ROOT_RESTORE(js, root_mark); 392 + return tov(0); 393 + } 394 + 395 + if (vtype(iterable) == T_STR) { 396 + if (str_is_heap_rope(iterable) || str_is_heap_builder(iterable)) { 397 + iterable = str_materialize(js, iterable); 398 + if (is_err(iterable)) { 399 + GC_ROOT_RESTORE(js, root_mark); 400 + return iterable; 401 + } 402 + GC_ROOT_PIN(js, iterable); 403 + } 404 + iter_buf[0] = iterable; 405 + iter_buf[1] = tov(0); 406 + iter_buf[2] = tov(SV_ITER_STRING); 407 + GC_ROOT_RESTORE(js, root_mark); 408 + return tov(0); 409 + } 410 + 411 + ant_value_t iter_fn = js_get_sym(js, iterable, get_iterator_sym()); 412 + GC_ROOT_PIN(js, iter_fn); 413 + uint8_t ft = vtype(iter_fn); 414 + if (ft != T_FUNC && ft != T_CFUNC) { 415 + GC_ROOT_RESTORE(js, root_mark); 416 + return js_mkerr(js, "not iterable"); 417 + } 418 + ant_value_t iterator = sv_vm_call(vm, js, iter_fn, iterable, NULL, 0, NULL, false); 419 + if (is_err(iterator)) { 420 + GC_ROOT_RESTORE(js, root_mark); 421 + return iterator; 422 + } 423 + GC_ROOT_PIN(js, iterator); 424 + 425 + map_iterator_state_t *map_st; 426 + iter_type_t map_type; 427 + if (sv_is_map_iter(js, iterator, &map_st, &map_type)) { 428 + iter_buf[0] = ANT_PTR(map_st); 429 + iter_buf[1] = tov((double)map_type); 430 + iter_buf[2] = tov(SV_ITER_MAP); 431 + GC_ROOT_RESTORE(js, root_mark); 432 + return tov(0); 433 + } 434 + 435 + set_iterator_state_t *set_st; 436 + iter_type_t set_type; 437 + if (sv_is_set_iter(js, iterator, &set_st, &set_type)) { 438 + iter_buf[0] = ANT_PTR(set_st); 439 + iter_buf[1] = tov((double)set_type); 440 + iter_buf[2] = tov(SV_ITER_SET); 441 + GC_ROOT_RESTORE(js, root_mark); 442 + return tov(0); 443 + } 444 + 445 + ant_value_t next_method = js_getprop_fallback(js, iterator, "next"); 446 + GC_ROOT_PIN(js, next_method); 447 + iter_buf[0] = iterator; 448 + iter_buf[1] = next_method; 449 + iter_buf[2] = tov(SV_ITER_GENERIC); 450 + GC_ROOT_RESTORE(js, root_mark); 451 + return tov(0); 452 + } 453 + 454 + void jit_helper_destructure_close( 455 + sv_vm_t *vm, ant_t *js, 456 + ant_value_t *iter_buf 457 + ) { 458 + int old_sp = vm->sp; 459 + if (!jit_vm_ensure_stack(vm, 3)) 460 + return; 461 + 462 + vm->stack[vm->sp++] = iter_buf[0]; 463 + vm->stack[vm->sp++] = iter_buf[1]; 464 + vm->stack[vm->sp++] = iter_buf[2]; 465 + sv_op_iter_close(vm, js); 466 + vm->sp = old_sp; 467 + } 468 + 469 + ant_value_t jit_helper_destructure_next( 470 + sv_vm_t *vm, ant_t *js, 471 + ant_value_t *iter_buf 472 + ) { 473 + int old_sp = vm->sp; 474 + if (!jit_vm_ensure_stack(vm, 4)) 475 + return js_mkerr(js, "stack overflow"); 476 + 477 + vm->stack[vm->sp++] = iter_buf[0]; 478 + vm->stack[vm->sp++] = iter_buf[1]; 479 + vm->stack[vm->sp++] = iter_buf[2]; 480 + 481 + ant_value_t value = js_mkundef(); 482 + bool done = false; 483 + ant_value_t status = sv_iter_advance(vm, js, 0, &value, &done); 484 + if (is_err(status)) { 485 + vm->sp = old_sp; 486 + return status; 487 + } 488 + 489 + iter_buf[0] = vm->stack[old_sp + 0]; 490 + iter_buf[1] = vm->stack[old_sp + 1]; 491 + iter_buf[2] = vm->stack[old_sp + 2]; 492 + iter_buf[3] = done ? js_mkundef() : value; 493 + vm->sp = old_sp; 494 + return status; 143 495 } 144 496 145 497 ant_value_t jit_helper_seq(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r) {
+475 -92
src/silver/swarm.c
··· 53 53 LOAD_EXT(jit_helper_mod); 54 54 LOAD_EXT(jit_helper_str_append_local); 55 55 LOAD_EXT(jit_helper_str_append_local_snapshot); 56 + LOAD_EXT(jit_helper_str_flush_local); 56 57 LOAD_EXT(jit_helper_lt); 57 58 LOAD_EXT(jit_helper_le); 58 59 LOAD_EXT(jit_helper_gt); ··· 61 62 LOAD_EXT(jit_helper_apply); 62 63 LOAD_EXT(jit_helper_rest); 63 64 LOAD_EXT(jit_helper_special_obj); 65 + LOAD_EXT(jit_helper_for_of); 66 + LOAD_EXT(jit_helper_destructure_close); 67 + LOAD_EXT(jit_helper_destructure_next); 64 68 LOAD_EXT(jit_helper_get_global); 65 69 LOAD_EXT(jit_helper_get_field); 66 70 LOAD_EXT(jit_helper_to_propkey); ··· 72 76 LOAD_EXT(jit_helper_call_is_proto); 73 77 LOAD_EXT(jit_helper_get_length); 74 78 LOAD_EXT(jit_helper_define_field); 79 + LOAD_EXT(jit_helper_define_method_comp); 75 80 LOAD_EXT(jit_helper_seq); 76 81 LOAD_EXT(jit_helper_eq); 77 82 LOAD_EXT(jit_helper_ne); ··· 1050 1055 case OP_GET_FIELD: case OP_GET_FIELD2: case OP_GET_GLOBAL: 1051 1056 break; 1052 1057 case OP_SPECIAL_OBJ: 1053 - if (sv_get_u8(ip + 1) != 1) return false; 1054 - break; 1058 + // TODO: RE_ENABLE once SPECIAL_OBJ semantics match the interpreter in JIT. 1059 + return false; 1055 1060 case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: 1056 1061 break; 1057 1062 default: ··· 1854 1859 bool needs_bailout; 1855 1860 bool needs_inc_local; 1856 1861 bool needs_args_buf; 1862 + bool needs_iter_roots; 1857 1863 bool needs_close_upval; 1858 1864 bool needs_tco_args; 1859 1865 bool needs_ic_epoch; ··· 1889 1895 case OP_TAIL_CALL: case OP_TAIL_CALL_METHOD: 1890 1896 case OP_ARRAY: case OP_NEW: 1891 1897 case OP_APPLY: case OP_NEW_APPLY: 1898 + case OP_FOR_OF: 1899 + case OP_DESTRUCTURE_INIT: case OP_DESTRUCTURE_NEXT: case OP_DESTRUCTURE_CLOSE: 1892 1900 f.needs_args_buf = true; 1901 + f.needs_iter_roots = true; 1893 1902 if (op == OP_TAIL_CALL || op == OP_TAIL_CALL_METHOD) 1894 1903 f.needs_tco_args = true; 1895 1904 break; ··· 1962 1971 case OP_OBJECT: case OP_ARRAY: case OP_SET_PROTO: 1963 1972 case OP_SWAP: case OP_ROT3L: 1964 1973 case OP_IN: case OP_GET_LENGTH: 1965 - case OP_DEFINE_FIELD: case OP_SEQ: case OP_EQ: 1974 + case OP_DEFINE_FIELD: case OP_DEFINE_METHOD_COMP: case OP_SEQ: case OP_EQ: 1975 + case OP_FOR_OF: 1976 + case OP_DESTRUCTURE_INIT: case OP_DESTRUCTURE_NEXT: case OP_DESTRUCTURE_CLOSE: 1966 1977 case OP_INC_LOCAL: case OP_DEC_LOCAL: case OP_ADD_LOCAL: 1967 1978 case OP_STR_APPEND_LOCAL: 1968 1979 case OP_STR_ALC_SNAPSHOT: ··· 1983 1994 break; 1984 1995 } 1985 1996 case OP_SPECIAL_OBJ: 1986 - if (sv_get_u8(ip + 1) != 1 && sv_get_u8(ip + 1) != 3) { 1987 - if (sv_jit_warn_unlikely) 1988 - fprintf(stderr, "jit: ineligible op SPECIAL_OBJ(%d) in %s\n", 1989 - sv_get_u8(ip + 1), 1990 - func->name ? func->name : "<anonymous>"); 1991 - eligible = false; 1992 - } 1997 + // TODO: RE_ENABLE once SPECIAL_OBJ semantics match the interpreter in JIT. 1998 + if (sv_jit_warn_unlikely) 1999 + fprintf(stderr, "jit: ineligible op SPECIAL_OBJ(%d) in %s\n", 2000 + sv_get_u8(ip + 1), 2001 + func->name ? func->name : "<anonymous>"); 2002 + eligible = false; 1993 2003 break; 1994 2004 default: 1995 2005 if (sv_jit_warn_unlikely) ··· 2125 2135 2126 2136 MIR_type_t sal_ret = MIR_JSVAL; 2127 2137 MIR_item_t str_append_local_proto = MIR_new_proto(ctx, "sal_proto", 2128 - 1, &sal_ret, 6, 2138 + 1, &sal_ret, 8, 2129 2139 MIR_T_I64, "vm", 2130 2140 MIR_T_I64, "js", 2131 2141 MIR_T_P, "func", 2142 + MIR_T_P, "args", 2143 + MIR_T_I32, "argc", 2132 2144 MIR_T_P, "locals", 2133 - MIR_T_I32, "local_idx", 2145 + MIR_T_I32, "slot_idx", 2134 2146 MIR_JSVAL, "rhs"); 2135 2147 2136 2148 MIR_type_t sals_ret = MIR_JSVAL; 2137 2149 MIR_item_t str_append_local_snapshot_proto = MIR_new_proto(ctx, "sals_proto", 2138 - 1, &sals_ret, 7, 2150 + 1, &sals_ret, 9, 2139 2151 MIR_T_I64, "vm", 2140 2152 MIR_T_I64, "js", 2141 2153 MIR_T_P, "func", 2154 + MIR_T_P, "args", 2155 + MIR_T_I32, "argc", 2142 2156 MIR_T_P, "locals", 2143 - MIR_T_I32, "local_idx", 2157 + MIR_T_I32, "slot_idx", 2144 2158 MIR_JSVAL, "lhs", 2145 2159 MIR_JSVAL, "rhs"); 2160 + 2161 + MIR_type_t sfl_ret = MIR_JSVAL; 2162 + MIR_item_t str_flush_local_proto = MIR_new_proto(ctx, "sfl_proto", 2163 + 1, &sfl_ret, 7, 2164 + MIR_T_I64, "vm", 2165 + MIR_T_I64, "js", 2166 + MIR_T_P, "func", 2167 + MIR_T_P, "args", 2168 + MIR_T_I32, "argc", 2169 + MIR_T_P, "locals", 2170 + MIR_T_I32, "slot_idx"); 2146 2171 2147 2172 MIR_type_t truthy_ret = MIR_T_I64; 2148 2173 MIR_item_t truthy_proto = MIR_new_proto(ctx, "truthy_proto", ··· 2211 2236 MIR_T_P, "str", 2212 2237 MIR_T_I32, "len"); 2213 2238 2239 + MIR_item_t define_method_comp_proto = MIR_new_proto(ctx, "dmc_proto", 2240 + 0, NULL, 5, 2241 + MIR_T_I64, "js", 2242 + MIR_JSVAL, "obj", 2243 + MIR_JSVAL, "key", 2244 + MIR_JSVAL, "fn", 2245 + MIR_T_I32, "flags"); 2246 + 2214 2247 MIR_type_t pf_ret = MIR_JSVAL; 2215 2248 MIR_item_t put_field_proto = MIR_new_proto(ctx, "pf_proto", 2216 2249 1, &pf_ret, 6, ··· 2275 2308 2276 2309 MIR_type_t special_obj_ret = MIR_JSVAL; 2277 2310 MIR_item_t special_obj_proto = MIR_new_proto(ctx, "soj_proto", 2278 - 1, &special_obj_ret, 2, 2311 + 1, &special_obj_ret, 3, 2312 + MIR_T_I64, "vm", 2279 2313 MIR_T_I64, "js", 2280 2314 MIR_T_I32, "which"); 2281 2315 2316 + MIR_type_t for_of_ret = MIR_JSVAL; 2317 + MIR_item_t for_of_proto = MIR_new_proto(ctx, "fo_proto", 2318 + 1, &for_of_ret, 4, 2319 + MIR_T_I64, "vm", 2320 + MIR_T_I64, "js", 2321 + MIR_JSVAL, "iterable", 2322 + MIR_T_P, "iter_buf"); 2323 + 2324 + MIR_item_t destructure_close_proto = MIR_new_proto(ctx, "dclose_proto", 2325 + 0, NULL, 3, 2326 + MIR_T_I64, "vm", 2327 + MIR_T_I64, "js", 2328 + MIR_T_P, "iter_buf"); 2329 + 2330 + MIR_type_t destructure_next_ret = MIR_JSVAL; 2331 + MIR_item_t destructure_next_proto = MIR_new_proto(ctx, "dnext_proto", 2332 + 1, &destructure_next_ret, 3, 2333 + MIR_T_I64, "vm", 2334 + MIR_T_I64, "js", 2335 + MIR_T_P, "iter_buf"); 2336 + 2282 2337 MIR_item_t imp_add = MIR_new_import(ctx, "jit_helper_add"); 2283 2338 MIR_item_t imp_sub = MIR_new_import(ctx, "jit_helper_sub"); 2284 2339 MIR_item_t imp_mul = MIR_new_import(ctx, "jit_helper_mul"); ··· 2288 2343 MIR_new_import(ctx, "jit_helper_str_append_local"); 2289 2344 MIR_item_t imp_str_append_local_snapshot = 2290 2345 MIR_new_import(ctx, "jit_helper_str_append_local_snapshot"); 2346 + MIR_item_t imp_str_flush_local = 2347 + MIR_new_import(ctx, "jit_helper_str_flush_local"); 2291 2348 MIR_item_t imp_lt = MIR_new_import(ctx, "jit_helper_lt"); 2292 2349 MIR_item_t imp_le = MIR_new_import(ctx, "jit_helper_le"); 2293 2350 MIR_item_t imp_gt = MIR_new_import(ctx, "jit_helper_gt"); ··· 2296 2353 MIR_item_t imp_apply = MIR_new_import(ctx, "jit_helper_apply"); 2297 2354 MIR_item_t imp_rest = MIR_new_import(ctx, "jit_helper_rest"); 2298 2355 MIR_item_t imp_special_obj = MIR_new_import(ctx, "jit_helper_special_obj"); 2356 + MIR_item_t imp_for_of = MIR_new_import(ctx, "jit_helper_for_of"); 2357 + MIR_item_t imp_dnext = MIR_new_import(ctx, "jit_helper_destructure_next"); 2358 + MIR_item_t imp_dclose = MIR_new_import(ctx, "jit_helper_destructure_close"); 2299 2359 MIR_item_t imp_gg = MIR_new_import(ctx, "jit_helper_get_global"); 2300 2360 MIR_item_t imp_get_field = MIR_new_import(ctx, "jit_helper_get_field"); 2301 2361 MIR_item_t imp_to_propkey = MIR_new_import(ctx, "jit_helper_to_propkey"); ··· 2305 2365 MIR_item_t imp_in = MIR_new_import(ctx, "jit_helper_in"); 2306 2366 MIR_item_t imp_get_length = MIR_new_import(ctx, "jit_helper_get_length"); 2307 2367 MIR_item_t imp_define_field = MIR_new_import(ctx, "jit_helper_define_field"); 2368 + MIR_item_t imp_define_method_comp = MIR_new_import(ctx, "jit_helper_define_method_comp"); 2308 2369 MIR_item_t imp_seq = MIR_new_import(ctx, "jit_helper_seq"); 2309 2370 MIR_item_t imp_eq = MIR_new_import(ctx, "jit_helper_eq"); 2310 2371 MIR_item_t imp_ne = MIR_new_import(ctx, "jit_helper_ne"); ··· 2483 2544 MIR_new_uint_op(ctx, (uint64_t)scratch_slots * sizeof(ant_value_t)))); 2484 2545 } else { 2485 2546 mir_load_imm(ctx, jit_func, r_args_buf, 0); 2547 + } 2548 + 2549 + MIR_reg_t r_iter_roots = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "iter_roots"); 2550 + if (feat.needs_iter_roots && vs.max > 0) { 2551 + MIR_append_insn(ctx, jit_func, 2552 + MIR_new_insn(ctx, MIR_ALLOCA, 2553 + MIR_new_reg_op(ctx, r_iter_roots), 2554 + MIR_new_uint_op(ctx, (uint64_t)vs.max * sizeof(ant_value_t)))); 2555 + for (int i = 0; i < vs.max; i++) { 2556 + MIR_append_insn(ctx, jit_func, 2557 + MIR_new_insn(ctx, MIR_MOV, 2558 + MIR_new_mem_op(ctx, MIR_T_I64, 2559 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_iter_roots, 0, 1), 2560 + MIR_new_uint_op(ctx, mkval(T_UNDEF, 0)))); 2561 + } 2562 + } else { 2563 + mir_load_imm(ctx, jit_func, r_iter_roots, 0); 2486 2564 } 2487 2565 2488 2566 MIR_reg_t r_tco_args = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "tco_args"); ··· 4934 5012 } 4935 5013 4936 5014 case OP_SPECIAL_OBJ: { 5015 + // TODO: RE_ENABLE once SPECIAL_OBJ semantics match the interpreter in JIT. 4937 5016 uint8_t which = sv_get_u8(ip + 1); 4938 5017 MIR_reg_t dst = vstack_push(&vs); 4939 - if (which == 3) { 5018 + if (which == 2 || which == 3) { 4940 5019 MIR_append_insn(ctx, jit_func, 4941 - MIR_new_call_insn(ctx, 5, 5020 + MIR_new_call_insn(ctx, 6, 4942 5021 MIR_new_ref_op(ctx, special_obj_proto), 4943 5022 MIR_new_ref_op(ctx, imp_special_obj), 4944 5023 MIR_new_reg_op(ctx, dst), 5024 + MIR_new_reg_op(ctx, r_vm), 4945 5025 MIR_new_reg_op(ctx, r_js), 4946 5026 MIR_new_int_op(ctx, (int64_t)which))); 4947 5027 } else mir_load_imm(ctx, jit_func, dst, mkval(T_UNDEF, 0)); ··· 5322 5402 uint16_t slot_idx = sv_get_u16(ip + 1); 5323 5403 int pre_op_sp = vs.sp; 5324 5404 if ((int)slot_idx < param_count) { 5405 + MIR_label_t arg_in_range = MIR_new_label(ctx); 5406 + MIR_append_insn(ctx, jit_func, 5407 + MIR_new_insn(ctx, MIR_UBGT, 5408 + MIR_new_label_op(ctx, arg_in_range), 5409 + MIR_new_reg_op(ctx, r_argc), 5410 + MIR_new_int_op(ctx, (int64_t)slot_idx))); 5325 5411 mir_load_imm(ctx, jit_func, r_bailout_val, (uint64_t)SV_JIT_BAILOUT); 5326 5412 mir_emit_bailout_check(ctx, jit_func, r_bailout_val, 5327 5413 0, r_bailout_off, bc_off, 5328 5414 r_bailout_sp, pre_op_sp, bailout_tramp, 5329 5415 r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5330 - break; 5331 - } 5416 + MIR_append_insn(ctx, jit_func, arg_in_range); 5332 5417 5333 - uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 5334 - if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 5418 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5419 + MIR_reg_t rhs = vstack_pop(&vs); 5335 5420 5336 - vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5337 - MIR_reg_t rhs = vstack_pop(&vs); 5421 + MIR_append_insn(ctx, jit_func, 5422 + MIR_new_call_insn(ctx, 11, 5423 + MIR_new_ref_op(ctx, str_append_local_proto), 5424 + MIR_new_ref_op(ctx, imp_str_append_local), 5425 + MIR_new_reg_op(ctx, r_err_tmp), 5426 + MIR_new_reg_op(ctx, r_vm), 5427 + MIR_new_reg_op(ctx, r_js), 5428 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5429 + MIR_new_reg_op(ctx, r_args), 5430 + MIR_new_reg_op(ctx, r_argc), 5431 + MIR_new_uint_op(ctx, 0), 5432 + MIR_new_int_op(ctx, (int64_t)slot_idx), 5433 + MIR_new_reg_op(ctx, rhs))); 5434 + } else { 5435 + uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 5436 + if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 5437 + 5438 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5439 + MIR_reg_t rhs = vstack_pop(&vs); 5440 + 5441 + MIR_append_insn(ctx, jit_func, 5442 + MIR_new_insn(ctx, MIR_MOV, 5443 + MIR_new_mem_op(ctx, MIR_T_I64, 5444 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1), 5445 + MIR_new_reg_op(ctx, local_regs[local_idx]))); 5446 + 5447 + MIR_append_insn(ctx, jit_func, 5448 + MIR_new_call_insn(ctx, 11, 5449 + MIR_new_ref_op(ctx, str_append_local_proto), 5450 + MIR_new_ref_op(ctx, imp_str_append_local), 5451 + MIR_new_reg_op(ctx, r_err_tmp), 5452 + MIR_new_reg_op(ctx, r_vm), 5453 + MIR_new_reg_op(ctx, r_js), 5454 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5455 + MIR_new_uint_op(ctx, 0), 5456 + MIR_new_int_op(ctx, (int64_t)param_count), 5457 + MIR_new_reg_op(ctx, r_lbuf), 5458 + MIR_new_int_op(ctx, (int64_t)slot_idx), 5459 + MIR_new_reg_op(ctx, rhs))); 5460 + 5461 + if (has_captures) { 5462 + for (int i = 0; i < n_locals; i++) 5463 + if (captured_locals[i]) 5464 + MIR_append_insn(ctx, jit_func, 5465 + MIR_new_insn(ctx, MIR_MOV, 5466 + MIR_new_reg_op(ctx, local_regs[i]), 5467 + MIR_new_mem_op(ctx, MIR_T_I64, 5468 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5469 + } 5338 5470 5339 - MIR_append_insn(ctx, jit_func, 5340 - MIR_new_call_insn(ctx, 9, 5341 - MIR_new_ref_op(ctx, str_append_local_proto), 5342 - MIR_new_ref_op(ctx, imp_str_append_local), 5343 - MIR_new_reg_op(ctx, r_err_tmp), 5344 - MIR_new_reg_op(ctx, r_vm), 5345 - MIR_new_reg_op(ctx, r_js), 5346 - MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5347 - MIR_new_reg_op(ctx, r_lbuf), 5348 - MIR_new_int_op(ctx, (int64_t)local_idx), 5349 - MIR_new_reg_op(ctx, rhs))); 5471 + MIR_append_insn(ctx, jit_func, 5472 + MIR_new_insn(ctx, MIR_MOV, 5473 + MIR_new_reg_op(ctx, local_regs[local_idx]), 5474 + MIR_new_mem_op(ctx, MIR_T_I64, 5475 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5476 + if (known_func_locals) known_func_locals[local_idx] = NULL; 5477 + if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 5478 + } 5350 5479 5351 5480 mir_emit_bailout_check(ctx, jit_func, r_err_tmp, 5352 5481 0, r_bailout_off, bc_off, 5353 5482 r_bailout_sp, pre_op_sp, bailout_tramp, 5354 5483 r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5355 5484 5356 - if (has_captures) { 5357 - for (int i = 0; i < n_locals; i++) 5358 - if (captured_locals[i]) 5359 - MIR_append_insn(ctx, jit_func, 5360 - MIR_new_insn(ctx, MIR_MOV, 5361 - MIR_new_reg_op(ctx, local_regs[i]), 5362 - MIR_new_mem_op(ctx, MIR_T_I64, 5363 - (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5364 - } 5365 - 5366 - MIR_append_insn(ctx, jit_func, 5367 - MIR_new_insn(ctx, MIR_MOV, 5368 - MIR_new_reg_op(ctx, local_regs[local_idx]), 5369 - MIR_new_mem_op(ctx, MIR_T_I64, 5370 - (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5371 - if (known_func_locals) known_func_locals[local_idx] = NULL; 5372 - if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 5373 - 5374 5485 MIR_label_t no_err = MIR_new_label(ctx); 5375 5486 MIR_append_insn(ctx, jit_func, 5376 5487 MIR_new_insn(ctx, MIR_URSH, ··· 5403 5514 uint16_t slot_idx = sv_get_u16(ip + 1); 5404 5515 int pre_op_sp = vs.sp; 5405 5516 if ((int)slot_idx < param_count) { 5517 + MIR_label_t arg_in_range = MIR_new_label(ctx); 5518 + MIR_append_insn(ctx, jit_func, 5519 + MIR_new_insn(ctx, MIR_UBGT, 5520 + MIR_new_label_op(ctx, arg_in_range), 5521 + MIR_new_reg_op(ctx, r_argc), 5522 + MIR_new_int_op(ctx, (int64_t)slot_idx))); 5406 5523 mir_load_imm(ctx, jit_func, r_bailout_val, (uint64_t)SV_JIT_BAILOUT); 5407 5524 mir_emit_bailout_check(ctx, jit_func, r_bailout_val, 5408 5525 0, r_bailout_off, bc_off, 5409 5526 r_bailout_sp, pre_op_sp, bailout_tramp, 5410 5527 r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5411 - break; 5412 - } 5528 + MIR_append_insn(ctx, jit_func, arg_in_range); 5529 + 5530 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5531 + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); 5532 + MIR_reg_t rhs = vstack_pop(&vs); 5533 + MIR_reg_t lhs = vstack_pop(&vs); 5534 + 5535 + MIR_append_insn(ctx, jit_func, 5536 + MIR_new_call_insn(ctx, 12, 5537 + MIR_new_ref_op(ctx, str_append_local_snapshot_proto), 5538 + MIR_new_ref_op(ctx, imp_str_append_local_snapshot), 5539 + MIR_new_reg_op(ctx, r_err_tmp), 5540 + MIR_new_reg_op(ctx, r_vm), 5541 + MIR_new_reg_op(ctx, r_js), 5542 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5543 + MIR_new_reg_op(ctx, r_args), 5544 + MIR_new_reg_op(ctx, r_argc), 5545 + MIR_new_uint_op(ctx, 0), 5546 + MIR_new_int_op(ctx, (int64_t)slot_idx), 5547 + MIR_new_reg_op(ctx, lhs), 5548 + MIR_new_reg_op(ctx, rhs))); 5549 + } else { 5550 + uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 5551 + if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 5552 + 5553 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5554 + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); 5555 + MIR_reg_t rhs = vstack_pop(&vs); 5556 + MIR_reg_t lhs = vstack_pop(&vs); 5557 + 5558 + MIR_append_insn(ctx, jit_func, 5559 + MIR_new_insn(ctx, MIR_MOV, 5560 + MIR_new_mem_op(ctx, MIR_T_I64, 5561 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1), 5562 + MIR_new_reg_op(ctx, local_regs[local_idx]))); 5413 5563 5414 - uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 5415 - if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 5564 + MIR_append_insn(ctx, jit_func, 5565 + MIR_new_call_insn(ctx, 12, 5566 + MIR_new_ref_op(ctx, str_append_local_snapshot_proto), 5567 + MIR_new_ref_op(ctx, imp_str_append_local_snapshot), 5568 + MIR_new_reg_op(ctx, r_err_tmp), 5569 + MIR_new_reg_op(ctx, r_vm), 5570 + MIR_new_reg_op(ctx, r_js), 5571 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5572 + MIR_new_uint_op(ctx, 0), 5573 + MIR_new_int_op(ctx, (int64_t)param_count), 5574 + MIR_new_reg_op(ctx, r_lbuf), 5575 + MIR_new_int_op(ctx, (int64_t)slot_idx), 5576 + MIR_new_reg_op(ctx, lhs), 5577 + MIR_new_reg_op(ctx, rhs))); 5416 5578 5417 - vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5418 - vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); 5419 - MIR_reg_t rhs = vstack_pop(&vs); 5420 - MIR_reg_t lhs = vstack_pop(&vs); 5579 + if (has_captures) { 5580 + for (int i = 0; i < n_locals; i++) 5581 + if (captured_locals[i]) 5582 + MIR_append_insn(ctx, jit_func, 5583 + MIR_new_insn(ctx, MIR_MOV, 5584 + MIR_new_reg_op(ctx, local_regs[i]), 5585 + MIR_new_mem_op(ctx, MIR_T_I64, 5586 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5587 + } 5421 5588 5422 - MIR_append_insn(ctx, jit_func, 5423 - MIR_new_call_insn(ctx, 10, 5424 - MIR_new_ref_op(ctx, str_append_local_snapshot_proto), 5425 - MIR_new_ref_op(ctx, imp_str_append_local_snapshot), 5426 - MIR_new_reg_op(ctx, r_err_tmp), 5427 - MIR_new_reg_op(ctx, r_vm), 5428 - MIR_new_reg_op(ctx, r_js), 5429 - MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 5430 - MIR_new_reg_op(ctx, r_lbuf), 5431 - MIR_new_int_op(ctx, (int64_t)local_idx), 5432 - MIR_new_reg_op(ctx, lhs), 5433 - MIR_new_reg_op(ctx, rhs))); 5589 + MIR_append_insn(ctx, jit_func, 5590 + MIR_new_insn(ctx, MIR_MOV, 5591 + MIR_new_reg_op(ctx, local_regs[local_idx]), 5592 + MIR_new_mem_op(ctx, MIR_T_I64, 5593 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5594 + if (known_func_locals) known_func_locals[local_idx] = NULL; 5595 + if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 5596 + } 5434 5597 5435 5598 mir_emit_bailout_check(ctx, jit_func, r_err_tmp, 5436 5599 0, r_bailout_off, bc_off, 5437 5600 r_bailout_sp, pre_op_sp, bailout_tramp, 5438 5601 r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 5439 - 5440 - if (has_captures) { 5441 - for (int i = 0; i < n_locals; i++) 5442 - if (captured_locals[i]) 5443 - MIR_append_insn(ctx, jit_func, 5444 - MIR_new_insn(ctx, MIR_MOV, 5445 - MIR_new_reg_op(ctx, local_regs[i]), 5446 - MIR_new_mem_op(ctx, MIR_T_I64, 5447 - (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5448 - } 5449 - 5450 - MIR_append_insn(ctx, jit_func, 5451 - MIR_new_insn(ctx, MIR_MOV, 5452 - MIR_new_reg_op(ctx, local_regs[local_idx]), 5453 - MIR_new_mem_op(ctx, MIR_T_I64, 5454 - (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 5455 - if (known_func_locals) known_func_locals[local_idx] = NULL; 5456 - if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 5457 5602 5458 5603 MIR_label_t no_err = MIR_new_label(ctx); 5459 5604 MIR_append_insn(ctx, jit_func, ··· 5717 5862 break; 5718 5863 } 5719 5864 5865 + case OP_DEFINE_METHOD_COMP: { 5866 + uint8_t flags = sv_get_u8(ip + 1); 5867 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5868 + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); 5869 + vstack_ensure_boxed(&vs, vs.sp - 3, ctx, jit_func, r_d_slot); 5870 + MIR_reg_t fn_val = vstack_pop(&vs); 5871 + MIR_reg_t key = vstack_pop(&vs); 5872 + MIR_reg_t obj = vstack_top(&vs); 5873 + MIR_append_insn(ctx, jit_func, 5874 + MIR_new_call_insn(ctx, 7, 5875 + MIR_new_ref_op(ctx, define_method_comp_proto), 5876 + MIR_new_ref_op(ctx, imp_define_method_comp), 5877 + MIR_new_reg_op(ctx, r_js), 5878 + MIR_new_reg_op(ctx, obj), 5879 + MIR_new_reg_op(ctx, key), 5880 + MIR_new_reg_op(ctx, fn_val), 5881 + MIR_new_int_op(ctx, (int64_t)flags))); 5882 + break; 5883 + } 5884 + 5720 5885 case OP_GET_ELEM: { 5721 5886 vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 5722 5887 vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); ··· 5841 6006 MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_err_tmp))); 5842 6007 } 5843 6008 MIR_append_insn(ctx, jit_func, no_err); 6009 + break; 6010 + } 6011 + 6012 + case OP_FOR_OF: 6013 + case OP_DESTRUCTURE_INIT: { 6014 + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); 6015 + MIR_reg_t iterable = vstack_pop(&vs); 6016 + MIR_append_insn(ctx, jit_func, 6017 + MIR_new_call_insn(ctx, 7, 6018 + MIR_new_ref_op(ctx, for_of_proto), 6019 + MIR_new_ref_op(ctx, imp_for_of), 6020 + MIR_new_reg_op(ctx, r_err_tmp), 6021 + MIR_new_reg_op(ctx, r_vm), 6022 + MIR_new_reg_op(ctx, r_js), 6023 + MIR_new_reg_op(ctx, iterable), 6024 + MIR_new_reg_op(ctx, r_args_buf))); 6025 + MIR_label_t no_err = MIR_new_label(ctx); 6026 + MIR_append_insn(ctx, jit_func, 6027 + MIR_new_insn(ctx, MIR_URSH, 6028 + MIR_new_reg_op(ctx, r_bool), 6029 + MIR_new_reg_op(ctx, r_err_tmp), 6030 + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); 6031 + MIR_append_insn(ctx, jit_func, 6032 + MIR_new_insn(ctx, MIR_BNE, 6033 + MIR_new_label_op(ctx, no_err), 6034 + MIR_new_reg_op(ctx, r_bool), 6035 + MIR_new_uint_op(ctx, JIT_ERR_TAG))); 6036 + if (jit_try_depth > 0) { 6037 + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; 6038 + MIR_append_insn(ctx, jit_func, 6039 + MIR_new_insn(ctx, MIR_MOV, 6040 + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), 6041 + MIR_new_reg_op(ctx, r_err_tmp))); 6042 + MIR_append_insn(ctx, jit_func, 6043 + MIR_new_insn(ctx, MIR_JMP, 6044 + MIR_new_label_op(ctx, h->catch_label))); 6045 + } else { 6046 + MIR_append_insn(ctx, jit_func, 6047 + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_err_tmp))); 6048 + } 6049 + MIR_append_insn(ctx, jit_func, no_err); 6050 + for (int i = 0; i < 3; i++) { 6051 + MIR_reg_t dst = vstack_push(&vs); 6052 + MIR_append_insn(ctx, jit_func, 6053 + MIR_new_insn(ctx, MIR_MOV, 6054 + MIR_new_reg_op(ctx, dst), 6055 + MIR_new_mem_op(ctx, MIR_T_I64, 6056 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_args_buf, 0, 1))); 6057 + MIR_append_insn(ctx, jit_func, 6058 + MIR_new_insn(ctx, MIR_MOV, 6059 + MIR_new_mem_op(ctx, MIR_T_I64, 6060 + (MIR_disp_t)((vs.sp - 1) * (int)sizeof(ant_value_t)), r_iter_roots, 0, 1), 6061 + MIR_new_reg_op(ctx, dst))); 6062 + } 6063 + break; 6064 + } 6065 + 6066 + case OP_DESTRUCTURE_NEXT: { 6067 + int iter_base = vs.sp - 3; 6068 + for (int i = 0; i < 3; i++) { 6069 + MIR_append_insn(ctx, jit_func, 6070 + MIR_new_insn(ctx, MIR_MOV, 6071 + MIR_new_mem_op(ctx, MIR_T_I64, 6072 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_args_buf, 0, 1), 6073 + MIR_new_mem_op(ctx, MIR_T_I64, 6074 + (MIR_disp_t)((iter_base + i) * (int)sizeof(ant_value_t)), r_iter_roots, 0, 1))); 6075 + } 6076 + MIR_append_insn(ctx, jit_func, 6077 + MIR_new_call_insn(ctx, 6, 6078 + MIR_new_ref_op(ctx, destructure_next_proto), 6079 + MIR_new_ref_op(ctx, imp_dnext), 6080 + MIR_new_reg_op(ctx, r_err_tmp), 6081 + MIR_new_reg_op(ctx, r_vm), 6082 + MIR_new_reg_op(ctx, r_js), 6083 + MIR_new_reg_op(ctx, r_args_buf))); 6084 + MIR_label_t no_err = MIR_new_label(ctx); 6085 + MIR_append_insn(ctx, jit_func, 6086 + MIR_new_insn(ctx, MIR_URSH, 6087 + MIR_new_reg_op(ctx, r_bool), 6088 + MIR_new_reg_op(ctx, r_err_tmp), 6089 + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); 6090 + MIR_append_insn(ctx, jit_func, 6091 + MIR_new_insn(ctx, MIR_BNE, 6092 + MIR_new_label_op(ctx, no_err), 6093 + MIR_new_reg_op(ctx, r_bool), 6094 + MIR_new_uint_op(ctx, JIT_ERR_TAG))); 6095 + if (jit_try_depth > 0) { 6096 + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; 6097 + MIR_append_insn(ctx, jit_func, 6098 + MIR_new_insn(ctx, MIR_MOV, 6099 + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), 6100 + MIR_new_reg_op(ctx, r_err_tmp))); 6101 + MIR_append_insn(ctx, jit_func, 6102 + MIR_new_insn(ctx, MIR_JMP, 6103 + MIR_new_label_op(ctx, h->catch_label))); 6104 + } else { 6105 + MIR_append_insn(ctx, jit_func, 6106 + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_err_tmp))); 6107 + } 6108 + MIR_append_insn(ctx, jit_func, no_err); 6109 + vstack_pop(&vs); 6110 + vstack_pop(&vs); 6111 + vstack_pop(&vs); 6112 + for (int i = 0; i < 4; i++) { 6113 + MIR_reg_t dst = vstack_push(&vs); 6114 + MIR_append_insn(ctx, jit_func, 6115 + MIR_new_insn(ctx, MIR_MOV, 6116 + MIR_new_reg_op(ctx, dst), 6117 + MIR_new_mem_op(ctx, MIR_T_I64, 6118 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_args_buf, 0, 1))); 6119 + if (i < 3) { 6120 + MIR_append_insn(ctx, jit_func, 6121 + MIR_new_insn(ctx, MIR_MOV, 6122 + MIR_new_mem_op(ctx, MIR_T_I64, 6123 + (MIR_disp_t)((vs.sp - 1) * (int)sizeof(ant_value_t)), r_iter_roots, 0, 1), 6124 + MIR_new_reg_op(ctx, dst))); 6125 + } 6126 + } 6127 + break; 6128 + } 6129 + 6130 + case OP_DESTRUCTURE_CLOSE: { 6131 + int iter_base = vs.sp - 3; 6132 + for (int i = 0; i < 3; i++) { 6133 + MIR_append_insn(ctx, jit_func, 6134 + MIR_new_insn(ctx, MIR_MOV, 6135 + MIR_new_mem_op(ctx, MIR_T_I64, 6136 + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_args_buf, 0, 1), 6137 + MIR_new_mem_op(ctx, MIR_T_I64, 6138 + (MIR_disp_t)((iter_base + i) * (int)sizeof(ant_value_t)), r_iter_roots, 0, 1))); 6139 + } 6140 + MIR_append_insn(ctx, jit_func, 6141 + MIR_new_call_insn(ctx, 5, 6142 + MIR_new_ref_op(ctx, destructure_close_proto), 6143 + MIR_new_ref_op(ctx, imp_dclose), 6144 + MIR_new_reg_op(ctx, r_vm), 6145 + MIR_new_reg_op(ctx, r_js), 6146 + MIR_new_reg_op(ctx, r_args_buf))); 6147 + vstack_pop(&vs); 6148 + vstack_pop(&vs); 6149 + vstack_pop(&vs); 5844 6150 break; 5845 6151 } 5846 6152 ··· 7331 7637 case OP_COL_NUM: 7332 7638 case OP_LABEL: 7333 7639 break; 7334 - case OP_STR_FLUSH_LOCAL: 7335 - ok = false; 7640 + case OP_STR_FLUSH_LOCAL: { 7641 + uint16_t slot_idx = sv_get_u16(ip + 1); 7642 + int pre_op_sp = vs.sp; 7643 + if ((int)slot_idx < param_count) { 7644 + MIR_append_insn(ctx, jit_func, 7645 + MIR_new_call_insn(ctx, 10, 7646 + MIR_new_ref_op(ctx, str_flush_local_proto), 7647 + MIR_new_ref_op(ctx, imp_str_flush_local), 7648 + MIR_new_reg_op(ctx, r_err_tmp), 7649 + MIR_new_reg_op(ctx, r_vm), 7650 + MIR_new_reg_op(ctx, r_js), 7651 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 7652 + MIR_new_reg_op(ctx, r_args), 7653 + MIR_new_reg_op(ctx, r_argc), 7654 + MIR_new_uint_op(ctx, 0), 7655 + MIR_new_int_op(ctx, (int64_t)slot_idx))); 7656 + } else { 7657 + uint16_t local_idx = (uint16_t)(slot_idx - (uint16_t)param_count); 7658 + if (local_idx >= (uint16_t)n_locals) { ok = false; break; } 7659 + 7660 + MIR_append_insn(ctx, jit_func, 7661 + MIR_new_insn(ctx, MIR_MOV, 7662 + MIR_new_mem_op(ctx, MIR_T_I64, 7663 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1), 7664 + MIR_new_reg_op(ctx, local_regs[local_idx]))); 7665 + 7666 + MIR_append_insn(ctx, jit_func, 7667 + MIR_new_call_insn(ctx, 10, 7668 + MIR_new_ref_op(ctx, str_flush_local_proto), 7669 + MIR_new_ref_op(ctx, imp_str_flush_local), 7670 + MIR_new_reg_op(ctx, r_err_tmp), 7671 + MIR_new_reg_op(ctx, r_vm), 7672 + MIR_new_reg_op(ctx, r_js), 7673 + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), 7674 + MIR_new_uint_op(ctx, 0), 7675 + MIR_new_int_op(ctx, (int64_t)param_count), 7676 + MIR_new_reg_op(ctx, r_lbuf), 7677 + MIR_new_int_op(ctx, (int64_t)slot_idx))); 7678 + 7679 + MIR_append_insn(ctx, jit_func, 7680 + MIR_new_insn(ctx, MIR_MOV, 7681 + MIR_new_reg_op(ctx, local_regs[local_idx]), 7682 + MIR_new_mem_op(ctx, MIR_T_I64, 7683 + (MIR_disp_t)((int)local_idx * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); 7684 + if (known_func_locals) known_func_locals[local_idx] = NULL; 7685 + if (known_type_locals) known_type_locals[local_idx] = SV_TI_UNKNOWN; 7686 + } 7687 + 7688 + mir_emit_bailout_check(ctx, jit_func, r_err_tmp, 7689 + 0, r_bailout_off, bc_off, 7690 + r_bailout_sp, pre_op_sp, bailout_tramp, 7691 + r_args_buf, &vs, local_regs, n_locals, r_lbuf, r_d_slot); 7692 + 7693 + MIR_label_t no_err = MIR_new_label(ctx); 7694 + MIR_append_insn(ctx, jit_func, 7695 + MIR_new_insn(ctx, MIR_URSH, 7696 + MIR_new_reg_op(ctx, r_bool), 7697 + MIR_new_reg_op(ctx, r_err_tmp), 7698 + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); 7699 + MIR_append_insn(ctx, jit_func, 7700 + MIR_new_insn(ctx, MIR_BNE, 7701 + MIR_new_label_op(ctx, no_err), 7702 + MIR_new_reg_op(ctx, r_bool), 7703 + MIR_new_uint_op(ctx, JIT_ERR_TAG))); 7704 + if (jit_try_depth > 0) { 7705 + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; 7706 + MIR_append_insn(ctx, jit_func, 7707 + MIR_new_insn(ctx, MIR_MOV, 7708 + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), 7709 + MIR_new_reg_op(ctx, r_err_tmp))); 7710 + MIR_append_insn(ctx, jit_func, 7711 + MIR_new_insn(ctx, MIR_JMP, 7712 + MIR_new_label_op(ctx, h->catch_label))); 7713 + } else { 7714 + MIR_append_insn(ctx, jit_func, 7715 + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_err_tmp))); 7716 + } 7717 + MIR_append_insn(ctx, jit_func, no_err); 7336 7718 break; 7719 + } 7337 7720 7338 7721 case OP_SET_NAME: { 7339 7722 uint32_t atom_idx = sv_get_u32(ip + 1);
+123
tests/amp/mini_amp_screen_repro.js
··· 1 + function e4(char = " ", style = {}, width = 1, hyperlink) { 2 + return { char, style: { ...style }, width, hyperlink }; 3 + } 4 + 5 + const bw = e4(" ", {}); 6 + 7 + function sameColor(a, b) { 8 + if (a === b) return true; 9 + if (a === void 0 || b === void 0) return false; 10 + if (a.type !== b.type) return false; 11 + if (a.alpha !== b.alpha) return false; 12 + if (a.type === "index") return a.value === b.value; 13 + if (a.type === "rgb") 14 + return a.value.r === b.value.r && a.value.g === b.value.g && a.value.b === b.value.b; 15 + return true; 16 + } 17 + 18 + function sameStyle(a, b) { 19 + return ( 20 + sameColor(a.fg, b.fg) && 21 + sameColor(a.bg, b.bg) && 22 + a.bold === b.bold && 23 + a.italic === b.italic && 24 + a.underline === b.underline && 25 + a.strikethrough === b.strikethrough && 26 + a.reverse === b.reverse && 27 + a.dim === b.dim 28 + ); 29 + } 30 + 31 + function sameCell(a, b) { 32 + return ( 33 + a.char === b.char && 34 + a.width === b.width && 35 + sameStyle(a.style, b.style) && 36 + a.hyperlink === b.hyperlink 37 + ); 38 + } 39 + 40 + class BufferGrid { 41 + constructor(width, height) { 42 + this.width = width; 43 + this.height = height; 44 + this.resize(width, height); 45 + } 46 + resize(width, height) { 47 + this.width = width; 48 + this.height = height; 49 + this.cells = Array(height).fill(null).map(() => Array(width).fill(null).map(() => bw)); 50 + } 51 + setCell(x, y, cell) { 52 + this.cells[y][x] = { ...cell, style: { ...cell.style } }; 53 + } 54 + getCell(x, y) { 55 + return this.cells[y][x]; 56 + } 57 + clear() { 58 + for (let y = 0; y < this.height; y++) { 59 + for (let x = 0; x < this.width; x++) this.cells[y][x] = bw; 60 + } 61 + } 62 + rowString(y) { 63 + let out = ""; 64 + for (let x = 0; x < this.width; x++) out += this.cells[y][x].char; 65 + return out; 66 + } 67 + } 68 + 69 + class Screen { 70 + constructor(width, height) { 71 + this.width = width; 72 + this.height = height; 73 + this.frontBuffer = new BufferGrid(width, height); 74 + this.backBuffer = new BufferGrid(width, height); 75 + } 76 + getBuffer() { 77 + return this.backBuffer; 78 + } 79 + clear() { 80 + this.backBuffer.clear(); 81 + } 82 + setCell(x, y, cell) { 83 + this.backBuffer.setCell(x, y, cell); 84 + } 85 + getDiff() { 86 + const diff = []; 87 + for (let y = 0; y < this.height; y++) { 88 + for (let x = 0; x < this.width; x++) { 89 + const a = this.frontBuffer.getCell(x, y); 90 + const b = this.backBuffer.getCell(x, y); 91 + if (!sameCell(a, b)) diff.push({ x, y, cell: b }); 92 + } 93 + } 94 + return diff; 95 + } 96 + present() { 97 + const old = this.frontBuffer; 98 + this.frontBuffer = this.backBuffer; 99 + this.backBuffer = old; 100 + } 101 + } 102 + 103 + function drawText(screen, x, y, text) { 104 + for (let i = 0; i < text.length; i++) { 105 + screen.setCell(x + i, y, e4(text[i], { fg: { type: "index", value: 7 } })); 106 + } 107 + } 108 + 109 + const screen = new Screen(20, 4); 110 + 111 + for (let frame = 0; frame < 5; frame++) { 112 + screen.clear(); 113 + drawText(screen, 0, 0, "frame:" + frame); 114 + drawText(screen, 0, 1, "ball:" + " ".repeat(frame) + "o"); 115 + const diff = screen.getDiff(); 116 + console.log(JSON.stringify({ 117 + frame, 118 + diffCount: diff.length, 119 + row0: screen.backBuffer.rowString(0), 120 + row1: screen.backBuffer.rowString(1), 121 + })); 122 + screen.present(); 123 + }
+198
tests/amp/mini_amp_state_repro.js
··· 1 + class Widget { 2 + constructor(key) { 3 + this.key = key; 4 + } 5 + canUpdate(other) { 6 + return this.constructor === other.constructor && this.key === other.key; 7 + } 8 + } 9 + 10 + let BUILD_OWNER = null; 11 + 12 + class Element { 13 + constructor(widget) { 14 + this.widget = widget; 15 + this.parent = void 0; 16 + this._children = []; 17 + this._dirty = false; 18 + this._mounted = false; 19 + } 20 + get dirty() { 21 + return this._dirty; 22 + } 23 + addChild(child) { 24 + child.parent = this; 25 + this._children.push(child); 26 + } 27 + removeChild(child) { 28 + const i = this._children.indexOf(child); 29 + if (i >= 0) this._children.splice(i, 1); 30 + child.parent = void 0; 31 + } 32 + markMounted() { 33 + this._mounted = true; 34 + } 35 + unmount() { 36 + this._mounted = false; 37 + this._dirty = false; 38 + } 39 + markNeedsRebuild() { 40 + if (!this._mounted) return; 41 + this._dirty = true; 42 + BUILD_OWNER.scheduleBuildFor(this); 43 + } 44 + markNeedsBuild() { 45 + this.markNeedsRebuild(); 46 + } 47 + update(widget) { 48 + this.widget = widget; 49 + } 50 + } 51 + 52 + class BuildOwner { 53 + constructor() { 54 + this._dirtyElements = new Set(); 55 + } 56 + scheduleBuildFor(el) { 57 + this._dirtyElements.add(el); 58 + } 59 + buildScopes() { 60 + const batch = Array.from(this._dirtyElements); 61 + this._dirtyElements.clear(); 62 + for (const el of batch) { 63 + if (el.dirty) { 64 + el.performRebuild(); 65 + el._dirty = false; 66 + } 67 + } 68 + } 69 + } 70 + 71 + class State { 72 + _mounted = false; 73 + _mount(widget, context) { 74 + this.widget = widget; 75 + this.context = context; 76 + this._mounted = true; 77 + this.initState?.(); 78 + } 79 + _update(widget) { 80 + const oldWidget = this.widget; 81 + this.widget = widget; 82 + this.didUpdateWidget?.(oldWidget); 83 + } 84 + _unmount() { 85 + this._mounted = false; 86 + this.dispose?.(); 87 + } 88 + setState(fn) { 89 + if (!this._mounted) throw new Error("setState() called after dispose()"); 90 + if (fn) fn(); 91 + this.context.element.markNeedsBuild(); 92 + } 93 + } 94 + 95 + class BuildContext { 96 + constructor(element, widget) { 97 + this.element = element; 98 + this.widget = widget; 99 + } 100 + } 101 + 102 + class RenderObjectWidget extends Widget { 103 + createElement() { 104 + return new RenderObjectElement(this); 105 + } 106 + } 107 + 108 + class RenderObjectElement extends Element { 109 + mount() { 110 + this.renderObject = this.widget.createRenderObject(); 111 + this.markMounted(); 112 + } 113 + update(widget) { 114 + super.update(widget); 115 + widget.updateRenderObject(this.renderObject); 116 + } 117 + performRebuild() {} 118 + } 119 + 120 + class StatefulWidget extends Widget { 121 + createElement() { 122 + return new StatefulElement(this); 123 + } 124 + } 125 + 126 + class StatefulElement extends Element { 127 + mount() { 128 + this._context = new BuildContext(this, this.widget); 129 + this._state = this.widget.createState(); 130 + this._state._mount(this.widget, this._context); 131 + this.rebuild(); 132 + this.markMounted(); 133 + } 134 + performRebuild() { 135 + this.rebuild(); 136 + } 137 + rebuild() { 138 + const next = this._state.build(this._context); 139 + if (!this._child) { 140 + this._child = next.createElement(); 141 + this.addChild(this._child); 142 + this._child.mount(); 143 + return; 144 + } 145 + if (this._child.widget.canUpdate(next)) { 146 + this._child.update(next); 147 + } else { 148 + this._child.unmount(); 149 + this.removeChild(this._child); 150 + this._child = next.createElement(); 151 + this.addChild(this._child); 152 + this._child.mount(); 153 + } 154 + } 155 + } 156 + 157 + class Label extends RenderObjectWidget { 158 + constructor(text) { 159 + super(); 160 + this.text = text; 161 + } 162 + createRenderObject() { 163 + return { text: this.text }; 164 + } 165 + updateRenderObject(ro) { 166 + ro.text = this.text; 167 + console.log("renderObject updated ->", JSON.stringify(ro.text)); 168 + } 169 + } 170 + 171 + class CounterApp extends StatefulWidget { 172 + createState() { 173 + return new CounterState(); 174 + } 175 + } 176 + 177 + class CounterState extends State { 178 + initState() { 179 + this.count = 0; 180 + } 181 + build() { 182 + console.log("build ->", this.count); 183 + return new Label("count:" + this.count); 184 + } 185 + } 186 + 187 + BUILD_OWNER = new BuildOwner(); 188 + 189 + const root = new CounterApp().createElement(); 190 + root.mount(); 191 + 192 + for (let i = 1; i <= 5; i++) { 193 + root._state.setState(() => { 194 + root._state.count = i; 195 + }); 196 + BUILD_OWNER.buildScopes(); 197 + console.log("visible now ->", root._child.renderObject.text); 198 + }
+213
tests/amp/mini_amp_tui_repro.js
··· 1 + function e4(char = " ", style = {}, width = 1) { 2 + return { char, style: { ...style }, width }; 3 + } 4 + 5 + const bw = e4(" ", {}); 6 + 7 + function sameColor(a, b) { 8 + if (a === b) return true; 9 + if (a === void 0 || b === void 0) return false; 10 + if (a.type !== b.type) return false; 11 + switch (a.type) { 12 + case "index": 13 + return a.value === b.value; 14 + case "rgb": 15 + return a.value.r === b.value.r && a.value.g === b.value.g && a.value.b === b.value.b; 16 + default: 17 + return true; 18 + } 19 + } 20 + 21 + function sameStyle(a, b) { 22 + return ( 23 + sameColor(a.fg, b.fg) && 24 + sameColor(a.bg, b.bg) && 25 + a.bold === b.bold && 26 + a.italic === b.italic && 27 + a.underline === b.underline && 28 + a.dim === b.dim 29 + ); 30 + } 31 + 32 + function sameCell(a, b) { 33 + return a.char === b.char && a.width === b.width && sameStyle(a.style, b.style); 34 + } 35 + 36 + class BufferGrid { 37 + constructor(width, height) { 38 + this.resize(width, height); 39 + } 40 + resize(width, height) { 41 + this.width = width; 42 + this.height = height; 43 + this.cells = Array(height) 44 + .fill(null) 45 + .map(() => Array(width).fill(null).map(() => bw)); 46 + } 47 + getCell(x, y) { 48 + if (x < 0 || y < 0 || x >= this.width || y >= this.height) return null; 49 + return this.cells[y][x]; 50 + } 51 + setCell(x, y, cell) { 52 + if (x < 0 || y < 0 || x >= this.width || y >= this.height) return; 53 + this.cells[y][x] = { ...cell, style: { ...cell.style } }; 54 + } 55 + clear() { 56 + for (let y = 0; y < this.height; y++) { 57 + for (let x = 0; x < this.width; x++) this.cells[y][x] = bw; 58 + } 59 + } 60 + } 61 + 62 + class Screen { 63 + constructor(width, height) { 64 + this.width = width; 65 + this.height = height; 66 + this.frontBuffer = new BufferGrid(width, height); 67 + this.backBuffer = new BufferGrid(width, height); 68 + } 69 + clear() { 70 + this.backBuffer.clear(); 71 + } 72 + setCell(x, y, cell) { 73 + this.backBuffer.setCell(x, y, cell); 74 + } 75 + setText(x, y, text, style = {}) { 76 + for (let i = 0; i < text.length; i++) this.setCell(x + i, y, e4(text[i], style)); 77 + } 78 + getDiff() { 79 + const out = []; 80 + for (let y = 0; y < this.height; y++) { 81 + for (let x = 0; x < this.width; x++) { 82 + const a = this.frontBuffer.getCell(x, y) ?? bw; 83 + const b = this.backBuffer.getCell(x, y) ?? bw; 84 + if (!sameCell(a, b)) out.push({ x, y, cell: b }); 85 + } 86 + } 87 + return out; 88 + } 89 + present() { 90 + const old = this.frontBuffer; 91 + this.frontBuffer = this.backBuffer; 92 + this.backBuffer = old; 93 + } 94 + } 95 + 96 + class StringBuilder { 97 + constructor() { 98 + this.parts = []; 99 + } 100 + append(...xs) { 101 + this.parts.push(...xs); 102 + } 103 + toString() { 104 + return this.parts.join(""); 105 + } 106 + } 107 + 108 + function moveTo(y, x) { 109 + return `\x1b[${y + 1};${x + 1}H`; 110 + } 111 + 112 + function ansiStyle(style) { 113 + const out = []; 114 + if (style.bold) out.push("1"); 115 + if (style.dim) out.push("2"); 116 + if (style.fg?.type === "index") out.push(String(30 + style.fg.value)); 117 + if (style.bg?.type === "index") out.push(String(40 + style.bg.value)); 118 + return out.length ? `\x1b[${out.join(";")}m` : "\x1b[0m"; 119 + } 120 + 121 + function renderDiff(diff) { 122 + const sb = new StringBuilder(); 123 + let lastStyle = null; 124 + for (const item of diff) { 125 + sb.append(moveTo(item.y, item.x)); 126 + if (!lastStyle || !sameStyle(lastStyle, item.cell.style)) { 127 + sb.append(ansiStyle(item.cell.style)); 128 + lastStyle = item.cell.style; 129 + } 130 + sb.append(item.cell.char); 131 + } 132 + sb.append("\x1b[0m"); 133 + return sb.toString(); 134 + } 135 + 136 + const width = process.stdout.columns || 80; 137 + const height = process.stdout.rows || 24; 138 + const screen = new Screen(width, height); 139 + 140 + let tick = 0; 141 + let text = ""; 142 + let menuOpen = false; 143 + let running = true; 144 + 145 + function paintFrame() { 146 + screen.clear(); 147 + screen.setText(0, 0, "mini amp tui repro", { fg: { type: "index", value: 6 }, bold: true }); 148 + screen.setText(0, 2, "type text, press m to toggle menu, q to quit", { 149 + fg: { type: "index", value: 7 }, 150 + }); 151 + screen.setText(0, 4, "textbox: [" + text + "]", { 152 + fg: { type: "index", value: 2 }, 153 + }); 154 + screen.setText(0, 6, menuOpen ? "menu: [open]" : "menu: [closed]", { 155 + fg: { type: "index", value: menuOpen ? 3 : 1 }, 156 + bold: menuOpen, 157 + }); 158 + const ballX = 8 + (tick % Math.max(1, Math.min(width - 10, 30))); 159 + screen.setText(0, 8, "ball:", { fg: { type: "index", value: 5 } }); 160 + screen.setText(ballX, 8, "o", { fg: { type: "index", value: 4 }, bold: true }); 161 + screen.setText(0, 10, "tick:" + tick, { fg: { type: "index", value: 7 } }); 162 + 163 + const diff = screen.getDiff(); 164 + const out = renderDiff(diff); 165 + process.stdout.write(out); 166 + screen.present(); 167 + } 168 + 169 + function shutdown() { 170 + if (!running) return; 171 + running = false; 172 + clearInterval(timer); 173 + process.stdout.write("\x1b[0m\x1b[2J\x1b[H"); 174 + if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") { 175 + process.stdin.setRawMode(false); 176 + } 177 + process.stdin.pause(); 178 + } 179 + 180 + if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") { 181 + process.stdin.setRawMode(true); 182 + } 183 + process.stdin.resume(); 184 + process.stdin.on("data", (chunk) => { 185 + if (typeof chunk !== "string") chunk = String(chunk); 186 + for (const ch of chunk) { 187 + if (ch === "q" || ch === "\u0003") { 188 + shutdown(); 189 + return; 190 + } 191 + if (ch === "m") { 192 + menuOpen = !menuOpen; 193 + paintFrame(); 194 + continue; 195 + } 196 + if (ch === "\u007f") { 197 + text = text.slice(0, -1); 198 + paintFrame(); 199 + continue; 200 + } 201 + if (ch >= " " && ch <= "~") { 202 + text += ch; 203 + paintFrame(); 204 + } 205 + } 206 + }); 207 + 208 + process.stdout.write("\x1b[2J\x1b[H\x1b[?25l"); 209 + paintFrame(); 210 + const timer = setInterval(() => { 211 + tick++; 212 + paintFrame(); 213 + }, 120);
+380
tests/amp/mini_amp_widget_tui_repro.js
··· 1 + function e4(char = " ", style = {}, width = 1) { 2 + return { char, style: { ...style }, width }; 3 + } 4 + 5 + const bw = e4(" ", {}); 6 + 7 + function sameColor(a, b) { 8 + if (a === b) return true; 9 + if (a === void 0 || b === void 0) return false; 10 + if (a.type !== b.type) return false; 11 + if (a.type === "index") return a.value === b.value; 12 + return true; 13 + } 14 + 15 + function sameStyle(a, b) { 16 + return sameColor(a.fg, b.fg) && sameColor(a.bg, b.bg) && a.bold === b.bold; 17 + } 18 + 19 + function sameCell(a, b) { 20 + return a.char === b.char && a.width === b.width && sameStyle(a.style, b.style); 21 + } 22 + 23 + class BufferGrid { 24 + constructor(width, height) { 25 + this.width = width; 26 + this.height = height; 27 + this.cells = Array(height).fill(null).map(() => Array(width).fill(null).map(() => bw)); 28 + } 29 + clear() { 30 + for (let y = 0; y < this.height; y++) { 31 + for (let x = 0; x < this.width; x++) this.cells[y][x] = bw; 32 + } 33 + } 34 + setCell(x, y, cell) { 35 + if (x < 0 || y < 0 || x >= this.width || y >= this.height) return; 36 + this.cells[y][x] = { ...cell, style: { ...cell.style } }; 37 + } 38 + getCell(x, y) { 39 + return this.cells[y][x]; 40 + } 41 + } 42 + 43 + class Screen { 44 + constructor(width, height) { 45 + this.width = width; 46 + this.height = height; 47 + this.front = new BufferGrid(width, height); 48 + this.back = new BufferGrid(width, height); 49 + } 50 + clear() { 51 + this.back.clear(); 52 + } 53 + setText(x, y, text, style = {}) { 54 + for (let i = 0; i < text.length; i++) this.back.setCell(x + i, y, e4(text[i], style)); 55 + } 56 + getDiff() { 57 + const out = []; 58 + for (let y = 0; y < this.height; y++) { 59 + for (let x = 0; x < this.width; x++) { 60 + const a = this.front.getCell(x, y); 61 + const b = this.back.getCell(x, y); 62 + if (!sameCell(a, b)) out.push({ x, y, cell: b }); 63 + } 64 + } 65 + return out; 66 + } 67 + present() { 68 + const old = this.front; 69 + this.front = this.back; 70 + this.back = old; 71 + } 72 + } 73 + 74 + function moveTo(y, x) { 75 + return `\x1b[${y + 1};${x + 1}H`; 76 + } 77 + 78 + function styleCode(style) { 79 + const out = []; 80 + if (style.bold) out.push("1"); 81 + if (style.fg?.type === "index") out.push(String(30 + style.fg.value)); 82 + return out.length ? `\x1b[${out.join(";")}m` : "\x1b[0m"; 83 + } 84 + 85 + function renderDiff(diff) { 86 + const parts = []; 87 + let lastStyle = null; 88 + for (const item of diff) { 89 + parts.push(moveTo(item.y, item.x)); 90 + if (!lastStyle || !sameStyle(lastStyle, item.cell.style)) { 91 + parts.push(styleCode(item.cell.style)); 92 + lastStyle = item.cell.style; 93 + } 94 + parts.push(item.cell.char); 95 + } 96 + parts.push("\x1b[0m"); 97 + return parts.join(""); 98 + } 99 + 100 + let BUILD_OWNER = null; 101 + let PIPELINE = null; 102 + let ROOT_ELEMENT = null; 103 + const SCREEN = new Screen(process.stdout.columns || 80, process.stdout.rows || 24); 104 + 105 + class Widget { 106 + canUpdate(other) { 107 + return this.constructor === other.constructor; 108 + } 109 + } 110 + 111 + class Element { 112 + constructor(widget) { 113 + this.widget = widget; 114 + this.parent = void 0; 115 + this._dirty = false; 116 + this._mounted = false; 117 + } 118 + markMounted() { 119 + this._mounted = true; 120 + } 121 + markNeedsRebuild() { 122 + if (!this._mounted) return; 123 + this._dirty = true; 124 + BUILD_OWNER.scheduleBuildFor(this); 125 + } 126 + markNeedsBuild() { 127 + this.markNeedsRebuild(); 128 + } 129 + } 130 + 131 + class BuildOwner { 132 + constructor() { 133 + this.dirty = new Set(); 134 + } 135 + scheduleBuildFor(el) { 136 + this.dirty.add(el); 137 + } 138 + buildScopes() { 139 + const batch = Array.from(this.dirty); 140 + this.dirty.clear(); 141 + for (const el of batch) { 142 + if (el._dirty) { 143 + el.performRebuild(); 144 + el._dirty = false; 145 + } 146 + } 147 + } 148 + } 149 + 150 + class BuildContext { 151 + constructor(element, widget) { 152 + this.element = element; 153 + this.widget = widget; 154 + } 155 + } 156 + 157 + class State { 158 + _mount(widget, context) { 159 + this.widget = widget; 160 + this.context = context; 161 + this._mounted = true; 162 + this.initState?.(); 163 + } 164 + _update(widget) { 165 + const oldWidget = this.widget; 166 + this.widget = widget; 167 + this.didUpdateWidget?.(oldWidget); 168 + } 169 + setState(fn) { 170 + if (fn) fn(); 171 + this.context.element.markNeedsBuild(); 172 + } 173 + } 174 + 175 + class RenderObject { 176 + constructor() { 177 + this._attached = false; 178 + this._needsLayout = true; 179 + this._needsPaint = true; 180 + } 181 + attach() { 182 + this._attached = true; 183 + } 184 + markNeedsLayout() { 185 + if (!this._attached) return; 186 + this._needsLayout = true; 187 + } 188 + markNeedsPaint() { 189 + if (!this._attached) return; 190 + this._needsPaint = true; 191 + } 192 + } 193 + 194 + class LabelRenderObject extends RenderObject { 195 + constructor(line, text, style) { 196 + super(); 197 + this.line = line; 198 + this.text = text; 199 + this.style = style; 200 + } 201 + update(line, text, style) { 202 + this.line = line; 203 + this.text = text; 204 + this.style = style; 205 + this.markNeedsLayout(); 206 + this.markNeedsPaint(); 207 + } 208 + paint(screen) { 209 + screen.setText(0, this.line, this.text, this.style); 210 + this._needsPaint = false; 211 + this._needsLayout = false; 212 + } 213 + } 214 + 215 + class RenderObjectWidget extends Widget { 216 + createElement() { 217 + return new RenderObjectElement(this); 218 + } 219 + } 220 + 221 + class RenderObjectElement extends Element { 222 + mount() { 223 + this.renderObject = this.widget.createRenderObject(); 224 + this.renderObject.attach(); 225 + this.markMounted(); 226 + } 227 + update(widget) { 228 + this.widget = widget; 229 + this.widget.updateRenderObject(this.renderObject); 230 + } 231 + performRebuild() {} 232 + } 233 + 234 + class StatefulWidget extends Widget { 235 + createElement() { 236 + return new StatefulElement(this); 237 + } 238 + } 239 + 240 + class StatefulElement extends Element { 241 + mount() { 242 + this.context = new BuildContext(this, this.widget); 243 + this.state = this.widget.createState(); 244 + this.state._mount(this.widget, this.context); 245 + this.rebuild(); 246 + this.markMounted(); 247 + } 248 + rebuild() { 249 + const widgets = this.state.build(this.context); 250 + if (!this.children) { 251 + this.children = widgets.map((w) => { 252 + const el = w.createElement(); 253 + el.parent = this; 254 + el.mount(); 255 + return el; 256 + }); 257 + return; 258 + } 259 + for (let i = 0; i < widgets.length; i++) { 260 + const next = widgets[i]; 261 + const child = this.children[i]; 262 + if (child.widget.canUpdate(next)) child.update(next); 263 + } 264 + } 265 + performRebuild() { 266 + this.rebuild(); 267 + } 268 + } 269 + 270 + class Label extends RenderObjectWidget { 271 + constructor(line, text, style) { 272 + super(); 273 + this.line = line; 274 + this.text = text; 275 + this.style = style; 276 + } 277 + createRenderObject() { 278 + return new LabelRenderObject(this.line, this.text, this.style); 279 + } 280 + updateRenderObject(ro) { 281 + ro.update(this.line, this.text, this.style); 282 + } 283 + } 284 + 285 + class App extends StatefulWidget { 286 + createState() { 287 + return new AppState(); 288 + } 289 + } 290 + 291 + class AppState extends State { 292 + initState() { 293 + this.tick = 0; 294 + this.text = ""; 295 + this.menuOpen = false; 296 + } 297 + build() { 298 + return [ 299 + new Label(0, "widget-tree repro", { fg: { type: "index", value: 6 }, bold: true }), 300 + new Label(2, "textbox: [" + this.text + "]", { fg: { type: "index", value: 2 } }), 301 + new Label(4, this.menuOpen ? "menu: [open]" : "menu: [closed]", { 302 + fg: { type: "index", value: this.menuOpen ? 3 : 1 }, 303 + bold: this.menuOpen, 304 + }), 305 + new Label(6, "ball: " + " ".repeat(this.tick % 20) + "o", { 306 + fg: { type: "index", value: 4 }, 307 + }), 308 + new Label(8, "tick:" + this.tick, { fg: { type: "index", value: 7 } }), 309 + ]; 310 + } 311 + } 312 + 313 + class Pipeline { 314 + paint() { 315 + SCREEN.clear(); 316 + for (const child of ROOT_ELEMENT.children) { 317 + if (child.renderObject._needsLayout || child.renderObject._needsPaint) { 318 + child.renderObject.paint(SCREEN); 319 + } else { 320 + child.renderObject.paint(SCREEN); 321 + } 322 + } 323 + process.stdout.write(renderDiff(SCREEN.getDiff())); 324 + SCREEN.present(); 325 + } 326 + } 327 + 328 + BUILD_OWNER = new BuildOwner(); 329 + PIPELINE = new Pipeline(); 330 + ROOT_ELEMENT = new App().createElement(); 331 + ROOT_ELEMENT.mount(); 332 + 333 + function frame() { 334 + BUILD_OWNER.buildScopes(); 335 + PIPELINE.paint(); 336 + } 337 + 338 + if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") { 339 + process.stdin.setRawMode(true); 340 + } 341 + process.stdin.resume(); 342 + process.stdin.on("data", (chunk) => { 343 + if (typeof chunk !== "string") chunk = String(chunk); 344 + for (const ch of chunk) { 345 + if (ch === "q" || ch === "\u0003") { 346 + clearInterval(timer); 347 + process.stdout.write("\x1b[0m\x1b[2J\x1b[H"); 348 + process.exit(0); 349 + } 350 + if (ch === "m") { 351 + ROOT_ELEMENT.state.setState(() => { 352 + ROOT_ELEMENT.state.menuOpen = !ROOT_ELEMENT.state.menuOpen; 353 + }); 354 + frame(); 355 + continue; 356 + } 357 + if (ch === "\u007f") { 358 + ROOT_ELEMENT.state.setState(() => { 359 + ROOT_ELEMENT.state.text = ROOT_ELEMENT.state.text.slice(0, -1); 360 + }); 361 + frame(); 362 + continue; 363 + } 364 + if (ch >= " " && ch <= "~") { 365 + ROOT_ELEMENT.state.setState(() => { 366 + ROOT_ELEMENT.state.text += ch; 367 + }); 368 + frame(); 369 + } 370 + } 371 + }); 372 + 373 + process.stdout.write("\x1b[2J\x1b[H\x1b[?25l"); 374 + frame(); 375 + const timer = setInterval(() => { 376 + ROOT_ELEMENT.state.setState(() => { 377 + ROOT_ELEMENT.state.tick++; 378 + }); 379 + frame(); 380 + }, 120);