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.

add completion support to repl/eval

+130 -20
+2
examples/spec/async_iterators.js
··· 1 1 import { test, testDeep, summary } from './helpers.js'; 2 + import { inspect } from 'node:util'; 2 3 3 4 console.log('Async Iterator Tests\n'); 4 5 ··· 17 18 test('async generator function constructor name', AsyncGeneratorFunction.name, 'AsyncGeneratorFunction'); 18 19 test('async generator function prototype chain', Object.getPrototypeOf(protoShapeAsyncGen), AsyncGeneratorFunction.prototype); 19 20 test('async generator function prototype tag', AsyncGeneratorFunction.prototype[Symbol.toStringTag], 'AsyncGeneratorFunction'); 21 + test('async generator function inspect tag', inspect(protoShapeAsyncGen), '[AsyncGeneratorFunction: protoShapeAsyncGen]'); 20 22 test('async generator function prototype prototype', AsyncGeneratorFunction.prototype.prototype, sharedAsyncGeneratorProto); 21 23 test( 22 24 'new async generator function throws',
+47
examples/spec/completion.js
··· 1 + import { test, testDeep, testThrows, summary } from './helpers.js'; 2 + 3 + console.log('Completion Value Tests\n'); 4 + 5 + test('empty blocks produce undefined', eval('{}{}{};'), undefined); 6 + test('label inside block completion', eval("{foo: 'bar'}{};"), 'bar'); 7 + test('block before labelled completion', eval("{}{foo: 'bar'};"), 'bar'); 8 + test('last non-empty block completion', eval("{a: 'b'}{c: 'd'}{};"), 'd'); 9 + 10 + test('nested label passes expression completion', eval('a: b: c: 1;'), 1); 11 + test('nested label passes comma expression completion', eval('a: b: c: (1, 2, 3);'), 3); 12 + 13 + test('statement-position braces parse as block label', eval('{ a: 1 }'), 1); 14 + test('multiple labels in block preserve last completion', eval('{ a: 1; b: 2 }'), 2); 15 + testDeep('parenthesized braces parse as object literal', eval('({ a: 1, b: 2 })'), { a: 1, b: 2 }); 16 + testThrows('comma cannot separate labelled statements', () => eval('{ a: 1, b: 2 }')); 17 + 18 + test('declaration empty completion preserves previous value', eval("'a'; var x = 5;"), 'a'); 19 + test('if true carries body completion', eval("if (true) 'a';"), 'a'); 20 + test('if false has empty completion', eval("if (false) 'a';"), undefined); 21 + test('if false overwrites previous expression with undefined', eval("'a'; if (false) 'b';"), undefined); 22 + test('if true empty body overwrites previous expression with undefined', eval("'a'; if (true) ;"), undefined); 23 + test('for loop completion is last body value', eval('for (let i=0;i<3;i++) i;'), 2); 24 + test('while false overwrites previous expression with undefined', eval("'a'; while (false) 'b';"), undefined); 25 + test('for false overwrites previous expression with undefined', eval("'a'; for (;false;) 'b';"), undefined); 26 + test('for empty body overwrites previous expression with undefined', eval("'a'; for (let i=0;i<1;i++);"), undefined); 27 + test('do while empty body overwrites previous expression with undefined', eval("'a'; do ; while(false);"), undefined); 28 + test('with empty body overwrites previous expression with undefined', eval("'a'; with ({}) ;"), undefined); 29 + 30 + test('empty finally preserves try completion', eval("try { 'a' } finally { }"), 'a'); 31 + test('non-empty finally overrides try completion', eval("try { 'a' } finally { 'b' }"), 'b'); 32 + test('empty try finally overwrites previous expression with undefined', eval("'a'; try { } finally { }"), undefined); 33 + 34 + test('break label preserves previous non-empty completion', eval("outer: { 'before'; break outer; 'after'; }"), 'before'); 35 + test('nested block completion flows outward', eval("outer: { { 'inner'; } ; }"), 'inner'); 36 + test('empty block after expression preserves value', eval("'a'; { var x; }"), 'a'); 37 + 38 + test('switch fallthrough preserves last non-empty case value', eval("switch (1) { case 0: 'zero'; case 1: 'one'; case 2: ; }"), 'one'); 39 + test('switch with no matching case is empty', eval("switch (9) { case 0: 'zero'; }"), undefined); 40 + test('switch with no matching case overwrites previous expression with undefined', eval("'a'; switch (9) { case 0: 'zero'; }"), undefined); 41 + test('switch default fallthrough can be overwritten', eval("switch (2) { case 1: 'one'; default: 'default'; case 2: 'two'; }"), 'two'); 42 + 43 + test('function block body ignores completion value', eval("(function(){ 'block'; })()"), undefined); 44 + test('arrow block body ignores completion value', eval("(()=>{ 'block'; })()"), undefined); 45 + test('arrow expression body returns expression value', eval("(()=> 'expr')()"), 'expr'); 46 + 47 + summary();
+7
examples/spec/generators.js
··· 1 1 import { test, testDeep, summary } from './helpers.js'; 2 + import { inspect } from 'node:util'; 2 3 3 4 console.log('Generator Tests\n'); 4 5 ··· 262 263 const ownGeneratorProto = Object.getPrototypeOf(protoShapeGen); 263 264 const sharedGeneratorProto = Object.getPrototypeOf(ownGeneratorProto); 264 265 const iteratorProto = Object.getPrototypeOf(sharedGeneratorProto); 266 + const GeneratorFunction = protoShapeFn.constructor; 267 + test('generator function constructor name', GeneratorFunction.name, 'GeneratorFunction'); 268 + test('generator function prototype chain', Object.getPrototypeOf(protoShapeFn), GeneratorFunction.prototype); 269 + test('generator function prototype tag', GeneratorFunction.prototype[Symbol.toStringTag], 'GeneratorFunction'); 270 + test('generator function toStringTag', Object.prototype.toString.call(protoShapeFn), '[object GeneratorFunction]'); 271 + test('generator function inspect tag', inspect(protoShapeFn), '[GeneratorFunction: protoShapeFn]'); 265 272 test('generator instance uses function prototype', ownGeneratorProto, protoShapeFn.prototype); 266 273 test('generator shared prototype has next', sharedGeneratorProto.hasOwnProperty('next'), true); 267 274 test('generator Symbol.iterator inherited from iterator prototype', iteratorProto.hasOwnProperty(Symbol.iterator), true);
+1
include/silver/compiler.h
··· 119 119 bool is_tla; 120 120 int try_depth; 121 121 int with_depth; 122 + int completion_local; 122 123 int strict_args_local; 123 124 int new_target_local; 124 125 int super_local;
+13 -2
src/modules/io.c
··· 210 210 211 211 lbrack: 212 212 switch (p[1]) { 213 - case 'A': if (memcmp(p + 2, "syncFunction", 7) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; 213 + case 'A': 214 + if ( 215 + memcmp(p + 2, "syncFunction", 7) == 0 || 216 + memcmp(p + 2, "syncGeneratorFunction", 21) == 0 217 + ) { EMIT_UNTIL(']', JSON_FUNC) } 218 + break; 214 219 case 'b': if (memcmp(p + 2, "yte", 3) == 0 || memcmp(p + 2, "uffer]", 6) == 0) { EMIT_UNTIL(']', JSON_STRING) } break; 215 220 case 'F': if (memcmp(p + 2, "unction", 7) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; 216 221 case 'n': if (memcmp(p + 2, "ative code", 10) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; 217 222 case 'C': if (memcmp(p + 2, "ircular", 7) == 0) { EMIT_UNTIL(']', JSON_REF) } break; 218 - case 'G': if (memcmp(p + 2, "etter/Setter]", 13) == 0 || memcmp(p + 2, "etter]", 6) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; 223 + case 'G': 224 + if ( 225 + memcmp(p + 2, "etter/Setter]", 13) == 0 || 226 + memcmp(p + 2, "etter]", 6) == 0 || 227 + memcmp(p + 2, "eneratorFunction", 16) == 0 228 + ) { EMIT_UNTIL(']', JSON_FUNC) } 229 + break; 219 230 case 'S': if (memcmp(p + 2, "etter]", 6) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; 220 231 case 'O': if (memcmp(p + 2, "bject: null prototype]", 22) == 0) { EMIT_UNTIL(']', JSON_TAG) } break; 221 232 case 'M': if (memcmp(p + 2, "odule]", 6) == 0) { EMIT_UNTIL(']', JSON_TAG) } break;
+3
src/modules/symbol.c
··· 375 375 ant_value_t async_func_proto = js_get_slot(js_glob(js), SLOT_ASYNC_PROTO); 376 376 js_set_sym(js, async_func_proto, g_toStringTag, js_mkstr(js, "AsyncFunction", 13)); 377 377 378 + ant_value_t generator_func_proto = js_get_slot(js_glob(js), SLOT_GENERATOR_PROTO); 379 + js_set_sym(js, generator_func_proto, g_toStringTag, js_mkstr(js, "GeneratorFunction", 17)); 380 + 378 381 ant_value_t async_generator_func_proto = js_get_slot(js_glob(js), SLOT_ASYNC_GENERATOR_PROTO); 379 382 js_set_sym(js, async_generator_func_proto, g_toStringTag, js_mkstr(js, "AsyncGeneratorFunction", 22)); 380 383
+2
src/silver/compile_ctx.c
··· 38 38 ctx->source_len = source_len; 39 39 ctx->mode = mode; 40 40 ctx->is_strict = is_strict; 41 + ctx->completion_local = -1; 41 42 ctx->strict_args_local = -1; 42 43 ctx->new_target_local = -1; 43 44 ctx->super_local = -1; ··· 63 64 ctx->is_async = node && !!(node->flags & FN_ASYNC); 64 65 ctx->is_strict = enclosing->is_strict; 65 66 ctx->mode = mode; 67 + ctx->completion_local = -1; 66 68 ctx->strict_args_local = -1; 67 69 ctx->new_target_local = -1; 68 70 ctx->super_local = -1;
+55 -18
src/silver/compiler.c
··· 387 387 return c && (c->mode == SV_COMPILE_EVAL || c->mode == SV_COMPILE_REPL); 388 388 } 389 389 390 + static inline bool is_completion_top_level(const sv_compiler_t *c) { 391 + return has_completion_value(c) && c->enclosing && !c->enclosing->enclosing; 392 + } 393 + 394 + static inline bool has_completion_accumulator(const sv_compiler_t *c) { 395 + return c && c->completion_local >= 0; 396 + } 397 + 390 398 static inline bool has_module_import_binding(const sv_compiler_t *c) { 391 399 for (const sv_compiler_t *cur = c; cur; cur = cur->enclosing) { 392 400 if (cur->mode == SV_COMPILE_MODULE) return true; ··· 902 910 903 911 static void emit_put_local(sv_compiler_t *c, int local_idx) { 904 912 emit_put_local_typed(c, local_idx, SV_TI_UNKNOWN); 913 + } 914 + 915 + static void emit_set_completion_from_stack(sv_compiler_t *c) { 916 + if (has_completion_accumulator(c)) emit_put_local(c, c->completion_local); 917 + else emit_op(c, OP_POP); 918 + } 919 + 920 + static void emit_set_completion_undefined(sv_compiler_t *c) { 921 + if (!has_completion_accumulator(c)) return; 922 + emit_op(c, OP_UNDEF); 923 + emit_put_local(c, c->completion_local); 905 924 } 906 925 907 926 static uint8_t infer_expr_type(sv_compiler_t *c, sv_ast_t *node); ··· 3087 3106 break; 3088 3107 3089 3108 case N_ASSIGN: 3090 - if (compile_self_append_stmt(c, node)) break; 3109 + if (!has_completion_accumulator(c) && compile_self_append_stmt(c, node)) break; 3091 3110 compile_expr(c, node); 3092 - emit_op(c, OP_POP); 3111 + emit_set_completion_from_stack(c); 3093 3112 break; 3094 3113 3095 3114 case N_FUNC: 3096 3115 if (node->str && !(node->flags & FN_ARROW)) break; 3097 3116 compile_expr(c, node); 3098 - emit_op(c, OP_POP); 3117 + emit_set_completion_from_stack(c); 3099 3118 break; 3100 3119 3101 3120 case N_CLASS: 3102 3121 compile_class(c, node); 3103 - emit_op(c, OP_POP); 3122 + if (node->flags & FN_PAREN) emit_set_completion_from_stack(c); 3123 + else emit_op(c, OP_POP); 3104 3124 break; 3105 3125 3106 3126 case N_WITH: 3127 + emit_set_completion_undefined(c); 3107 3128 compile_expr(c, node->left); 3108 3129 emit_op(c, OP_ENTER_WITH); 3109 3130 c->with_depth++; ··· 3114 3135 3115 3136 default: 3116 3137 compile_expr(c, node); 3117 - emit_op(c, OP_POP); 3138 + emit_set_completion_from_stack(c); 3118 3139 break; 3119 3140 } 3120 3141 } ··· 3447 3468 } 3448 3469 3449 3470 void compile_if(sv_compiler_t *c, sv_ast_t *node) { 3471 + emit_set_completion_undefined(c); 3472 + 3450 3473 bool folded_truth = false; 3451 3474 if (fold_static_typeof_compare(c, node->cond, &folded_truth)) { 3452 3475 if (folded_truth) compile_stmt(c, node->left); ··· 3468 3491 } 3469 3492 3470 3493 void compile_while(sv_compiler_t *c, sv_ast_t *node) { 3494 + emit_set_completion_undefined(c); 3495 + 3471 3496 int loop_start = c->code_len; 3472 3497 push_loop(c, loop_start, NULL, 0, false); 3473 3498 ··· 3485 3510 } 3486 3511 3487 3512 void compile_do_while(sv_compiler_t *c, sv_ast_t *node) { 3513 + emit_set_completion_undefined(c); 3514 + 3488 3515 int loop_start = c->code_len; 3489 3516 push_loop(c, loop_start, NULL, 0, false); 3490 3517 compile_stmt(c, node->body); ··· 3562 3589 } 3563 3590 3564 3591 void compile_for(sv_compiler_t *c, sv_ast_t *node) { 3592 + emit_set_completion_undefined(c); 3565 3593 begin_scope(c); 3566 3594 3567 3595 int *iter_slots = NULL; ··· 3731 3759 } 3732 3760 3733 3761 static void compile_for_each(sv_compiler_t *c, sv_ast_t *node, bool is_for_of) { 3762 + emit_set_completion_undefined(c); 3734 3763 begin_scope(c); 3735 3764 3736 3765 int *iter_slots = NULL; ··· 4023 4052 } 4024 4053 4025 4054 void compile_try(sv_compiler_t *c, sv_ast_t *node) { 4055 + emit_set_completion_undefined(c); 4056 + 4026 4057 c->try_depth++; 4027 4058 int try_jump = emit_jump(c, OP_TRY_PUSH); 4028 4059 ··· 4081 4112 4082 4113 4083 4114 void compile_switch(sv_compiler_t *c, sv_ast_t *node) { 4115 + emit_set_completion_undefined(c); 4116 + 4084 4117 int case_count = node->args.count; 4085 4118 int default_case = -1; 4086 4119 int *match_to_stub = NULL; ··· 4450 4483 sv_ast_t *m = node->args.items[i]; 4451 4484 if (m->type == N_STATIC_BLOCK) { 4452 4485 begin_scope(c); 4486 + int saved_completion_local = c->completion_local; 4487 + c->completion_local = -1; 4453 4488 compile_stmts(c, &m->args); 4489 + c->completion_local = saved_completion_local; 4454 4490 end_scope(c); 4455 4491 continue; 4456 4492 } ··· 4830 4866 body_using_try_jump = emit_jump(&comp, OP_TRY_PUSH); 4831 4867 } 4832 4868 4869 + bool completion_top = is_completion_top_level(&comp); 4870 + if (completion_top) { 4871 + emit_op(&comp, OP_UNDEF); 4872 + comp.completion_local = add_local(&comp, "", 0, false, comp.scope_depth); 4873 + emit_put_local(&comp, comp.completion_local); 4874 + } 4875 + 4833 4876 if (node->body) { 4834 4877 if (node->body->type == N_BLOCK) { 4835 - int last_expr_idx = -1; 4836 - if (has_completion_value(&comp) && node->body->args.count > 0) { 4837 - sv_ast_t *last = node->body->args.items[node->body->args.count - 1]; 4838 - if (sv_ast_can_be_expression_statement(last)) 4839 - last_expr_idx = node->body->args.count - 1; 4840 - } 4841 - for (int i = 0; i < node->body->args.count; i++) { 4842 - sv_ast_t *stmt = node->body->args.items[i]; 4843 - if (i == last_expr_idx) { 4844 - compile_expr(&comp, stmt); 4845 - emit_return_from_stack(&comp); 4846 - } else compile_stmt(&comp, stmt); 4847 - } 4878 + for (int i = 0; i < node->body->args.count; i++) 4879 + compile_stmt(&comp, node->body->args.items[i]); 4848 4880 } else compile_tail_return_expr(&comp, node->body); 4849 4881 } 4850 4882 ··· 4874 4906 comp.using_stack_async = old_using_async; 4875 4907 } 4876 4908 4909 + if (completion_top) { 4910 + emit_get_local(&comp, comp.completion_local); 4911 + emit_return_from_stack(&comp); 4912 + } 4913 + 4877 4914 emit_using_cleanups_to_depth(&comp, -1); 4878 4915 emit_close_upvals(&comp); 4879 4916 emit_op(&comp, OP_RETURN_UNDEF);