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.

fix ES2017 async and SharedArrayBuffer compat

- give Object.getOwnPropertyDescriptors result objects a normal Object prototype
- add SharedArrayBuffer @@species and prototype byteLength support
- stop exposing async functions as constructors with own prototype properties
- expose AsyncFunction through Symbol.toStringTag
- reject async/function line terminator parsing
- reject await in async function parameter initializers

+119 -20
+7 -7
examples/results.txt
··· 1219 1219 compat-table/es2017/Atomics.xor.js: OK 1220 1220 compat-table/es2017/Object.entries.js: OK 1221 1221 compat-table/es2017/Object.getOwnPropertyDescriptors.js: OK 1222 - compat-table/es2017/Object.getOwnPropertyDescriptors.no-undefined.js: TypeError: undefined is not a function 1222 + compat-table/es2017/Object.getOwnPropertyDescriptors.no-undefined.js: OK 1223 1223 compat-table/es2017/Object.values.js: OK 1224 - compat-table/es2017/SharedArrayBuffer.Symbol.species.js: failed 1224 + compat-table/es2017/SharedArrayBuffer.Symbol.species.js: OK 1225 1225 compat-table/es2017/SharedArrayBuffer.js: OK 1226 1226 compat-table/es2017/SharedArrayBuffer.prototype.Symbol.toStringTag.js: OK 1227 - compat-table/es2017/SharedArrayBuffer.prototype.byteLength.js: failed 1227 + compat-table/es2017/SharedArrayBuffer.prototype.byteLength.js: OK 1228 1228 compat-table/es2017/SharedArrayBuffer.prototype.slice.js: OK 1229 1229 compat-table/es2017/String.prototype.padEnd.js: OK 1230 1230 compat-table/es2017/String.prototype.padStart.js: OK ··· 1249 1249 compat-table/es2017/annex-b.Proxy.__lookupGetter__.js: OK 1250 1250 compat-table/es2017/annex-b.Proxy.__lookupSetter__.js: OK 1251 1251 compat-table/es2017/annex-b.for-in-assignment-non-strict.js: OK 1252 - compat-table/es2017/async.Symbol.toStringTag.js: failed 1252 + compat-table/es2017/async.Symbol.toStringTag.js: OK 1253 1253 compat-table/es2017/async.arrow-in-class.js: OK 1254 1254 compat-table/es2017/async.arrow.js: OK 1255 1255 compat-table/es2017/async.await-non-promise.js: OK ··· 1259 1259 compat-table/es2017/async.methods-class.js: OK 1260 1260 compat-table/es2017/async.methods-object.js: OK 1261 1261 compat-table/es2017/async.must-await-value.js: OK 1262 - compat-table/es2017/async.no-await-in-params.js: failed 1263 - compat-table/es2017/async.no-line-break.js: failed 1264 - compat-table/es2017/async.no-prototype.js: failed 1262 + compat-table/es2017/async.no-await-in-params.js: OK 1263 + compat-table/es2017/async.no-line-break.js: OK 1264 + compat-table/es2017/async.no-prototype.js: OK 1265 1265 compat-table/es2017/async.prototype-chain.js: OK 1266 1266 compat-table/es2017/async.return.js: OK 1267 1267 compat-table/es2017/async.throw.js: OK
+18
examples/spec/async.js
··· 10 10 basicAsync().then(v => { 11 11 results.basic = v; 12 12 }); 13 + test('async functions have no prototype property', basicAsync.hasOwnProperty('prototype'), false); 14 + test('async function prototype Symbol.toStringTag', Object.getPrototypeOf(async function () {})[Symbol.toStringTag], 'AsyncFunction'); 15 + test('async function no line break before function', (() => { 16 + try { 17 + Function('async\n function a() {await 0}')(); 18 + return false; 19 + } catch (e) { 20 + return true; 21 + } 22 + })(), true); 23 + test('async function cannot await in parameters', (() => { 24 + try { 25 + Function('(async function a(b = await Promise.resolve()) {}())')(); 26 + return false; 27 + } catch (e) { 28 + return true; 29 + } 30 + })(), true); 13 31 14 32 const arrowAsync = async () => 'arrow result'; 15 33 arrowAsync().then(v => {
+4
examples/spec/atomics.js
··· 5 5 const sab = new SharedArrayBuffer(16); 6 6 const int32 = new Int32Array(sab); 7 7 8 + test('SharedArrayBuffer Symbol.species', SharedArrayBuffer[Symbol.species], SharedArrayBuffer); 9 + test('SharedArrayBuffer.prototype.byteLength exists', 'byteLength' in SharedArrayBuffer.prototype, true); 10 + test('SharedArrayBuffer byteLength getter', sab.byteLength, 16); 11 + 8 12 Atomics.store(int32, 0, 42); 9 13 test('Atomics.store/load', Atomics.load(int32, 0), 42); 10 14
+4
examples/spec/objects.js
··· 116 116 const arrayDescriptors = Object.getOwnPropertyDescriptors(['item']); 117 117 test('array descriptor index value', arrayDescriptors[0].value, 'item'); 118 118 test('array descriptor length value', arrayDescriptors.length.value, 1); 119 + const proxyUndefinedDescriptor = new Proxy({ a: 1 }, { 120 + getOwnPropertyDescriptor() {} 121 + }); 122 + test('Object.getOwnPropertyDescriptors skips undefined proxy descriptors', Object.getOwnPropertyDescriptors(proxyUndefinedDescriptor).hasOwnProperty('a'), false); 119 123 testThrows('Object.getOwnPropertyDescriptors throws without argument', () => Object.getOwnPropertyDescriptors()); 120 124 testThrows('Object.getOwnPropertyDescriptors throws on null', () => Object.getOwnPropertyDescriptors(null)); 121 125 test('Object.getOwnPropertyDescriptors boxes number primitive', typeof Object.getOwnPropertyDescriptors(1), 'object');
+14 -5
src/ant.c
··· 5372 5372 ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto); 5373 5373 if (is_object_type(instance_proto)) js_set_proto_init(func_obj, instance_proto); 5374 5374 } 5375 + 5375 5376 set_slot(func_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty)); 5377 + ant_value_t func = js_obj_to_func(func_obj); 5376 5378 5377 - ant_value_t func = js_obj_to_func(func_obj); 5378 - ant_value_t proto_setup = setup_func_prototype(js, func); 5379 - if (is_err(proto_setup)) return proto_setup; 5379 + if (!is_async) { 5380 + ant_value_t proto_setup = setup_func_prototype(js, func); 5381 + if (is_err(proto_setup)) return proto_setup; 5382 + } 5383 + 5380 5384 return func; 5381 5385 } 5382 5386 ··· 5468 5472 } 5469 5473 5470 5474 ant_value_t func = mkval(T_FUNC, (uintptr_t)closure); 5471 - ant_value_t proto_setup = setup_func_prototype(js, func); 5472 - if (is_err(proto_setup)) return proto_setup; 5475 + if (!is_async) { 5476 + ant_value_t proto_setup = setup_func_prototype(js, func); 5477 + if (is_err(proto_setup)) return proto_setup; 5478 + } 5479 + 5473 5480 if (is_generator) { 5474 5481 ant_value_t prototype = js_get(js, func, "prototype"); 5475 5482 if (is_object_type(prototype) && is_object_type(js->sym.generator_proto)) ··· 7777 7784 7778 7785 static ant_value_t builtin_object_getOwnPropertyDescriptors(ant_t *js, ant_value_t *args, int nargs) { 7779 7786 ant_value_t result = js_mkobj(js); 7787 + 7788 + if (is_object_type(js->sym.object_proto)) js_set_proto_init(result, js->sym.object_proto); 7780 7789 if (nargs == 0 || vtype(args[0]) == T_NULL || vtype(args[0]) == T_UNDEF) { 7781 7790 return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert undefined or null to object"); 7782 7791 }
+11
src/modules/buffer.c
··· 483 483 return js_bool(data->is_detached); 484 484 } 485 485 486 + static ant_value_t js_arraybuffer_byteLength_getter(ant_t *js, ant_value_t *args, int nargs) { 487 + (void)args; (void)nargs; 488 + ant_value_t this_val = js_getthis(js); 489 + ArrayBufferData *data = buffer_get_arraybuffer_data(this_val); 490 + if (!data || data->is_detached) return js_mknum(0); 491 + return js_mknum((double)data->length); 492 + } 493 + 486 494 // ArrayBuffer.isView(value) 487 495 static ant_value_t js_arraybuffer_isView(ant_t *js, ant_value_t *args, int nargs) { 488 496 if (nargs < 1) return js_false; ··· 2732 2740 js_set(js, arraybuffer_proto, "transfer", js_mkfun(js_arraybuffer_transfer)); 2733 2741 js_set(js, arraybuffer_proto, "transferToFixedLength", js_mkfun(js_arraybuffer_transferToFixedLength)); 2734 2742 js_set_getter_desc(js, arraybuffer_proto, "detached", 8, js_mkfun(js_arraybuffer_detached_getter), JS_DESC_E); 2743 + js_set_getter_desc(js, arraybuffer_proto, "byteLength", 10, js_mkfun(js_arraybuffer_byteLength_getter), JS_DESC_C); 2735 2744 js_set_sym(js, arraybuffer_proto, get_toStringTag_sym(), js_mkstr(js, "ArrayBuffer", 11)); 2736 2745 2737 2746 js_set_slot(arraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_arraybuffer_constructor)); ··· 2846 2855 js_set_proto_init(sharedarraybuffer_proto, object_proto); 2847 2856 2848 2857 js_set(js, sharedarraybuffer_proto, "slice", js_mkfun(js_arraybuffer_slice)); 2858 + js_set_getter_desc(js, sharedarraybuffer_proto, "byteLength", 10, js_mkfun(js_arraybuffer_byteLength_getter), JS_DESC_C); 2849 2859 js_set_sym(js, sharedarraybuffer_proto, get_toStringTag_sym(), js_mkstr(js, "SharedArrayBuffer", 17)); 2850 2860 2851 2861 js_set_slot(sharedarraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_sharedarraybuffer_constructor)); 2852 2862 js_mkprop_fast(js, sharedarraybuffer_ctor_obj, "prototype", 9, sharedarraybuffer_proto); 2853 2863 js_mkprop_fast(js, sharedarraybuffer_ctor_obj, "name", 4, ANT_STRING("SharedArrayBuffer")); 2854 2864 js_set_descriptor(js, sharedarraybuffer_ctor_obj, "name", 4, 0); 2865 + js_define_species_getter(js, sharedarraybuffer_ctor_obj); 2855 2866 2856 2867 ant_value_t sharedarraybuffer_ctor = js_obj_to_func(sharedarraybuffer_ctor_obj); 2857 2868 js_set(js, sharedarraybuffer_proto, "constructor", sharedarraybuffer_ctor);
+3
src/modules/symbol.c
··· 346 346 ant_value_t promise_proto = js_get(js, promise_ctor, "prototype"); 347 347 js_set_sym(js, promise_proto, g_toStringTag, js_mkstr(js, "Promise", 7)); 348 348 349 + ant_value_t async_func_proto = js_get_slot(js_glob(js), SLOT_ASYNC_PROTO); 350 + js_set_sym(js, async_func_proto, g_toStringTag, js_mkstr(js, "AsyncFunction", 13)); 351 + 349 352 js_define_species_getter(js, promise_ctor); 350 353 js_define_species_getter(js, array_ctor); 351 354 }
+16 -4
src/silver/ast.c
··· 76 76 #define NEXT() sv_lexer_next(&p->lx) 77 77 #define LA() sv_lexer_lookahead(&p->lx) 78 78 79 + static bool lookahead_crosses_line_terminator(P) { 80 + sv_lexer_state_t saved; 81 + sv_lexer_save_state(&p->lx, &saved); 82 + CONSUMED = 1; 83 + (void)NEXT(); 84 + bool had_newline = HAD_NEWLINE; 85 + sv_lexer_restore_state(&p->lx, &saved); 86 + return had_newline; 87 + } 88 + 79 89 static sv_ast_t *parse_stmt(P); 80 90 static sv_ast_t *parse_expr(P); 81 91 static sv_ast_t *parse_paren_expr(P); ··· 556 566 l_async: { 557 567 uint32_t async_off = (uint32_t)TOFF; 558 568 CONSUME(); 559 - if (LA() == TOK_FUNC) { 569 + bool has_line_term = lookahead_crosses_line_terminator(p); 570 + if (!has_line_term && LA() == TOK_FUNC) { 560 571 NEXT(); CONSUME(); 561 572 sv_ast_t *fn = parse_func(p); 562 573 fn->flags |= FN_ASYNC; 563 574 fn->src_off = async_off; 564 575 return fn; 565 576 } 577 + if (has_line_term) return mk_ident_from_tok(p); 566 578 sv_ast_t *arrow = try_parse_async_arrow(p); 567 579 if (arrow) return arrow; 568 580 return mk_ident_from_tok(p); ··· 1664 1676 CONSUME(); 1665 1677 decl->flags |= EX_DEFAULT; 1666 1678 1667 - if (NEXT() == TOK_ASYNC && LA() == TOK_FUNC) { 1679 + if (NEXT() == TOK_ASYNC && LA() == TOK_FUNC && !lookahead_crosses_line_terminator(p)) { 1668 1680 uint32_t async_off = (uint32_t)TOFF; 1669 1681 CONSUME(); 1670 1682 NEXT(); CONSUME(); ··· 1692 1704 return decl; 1693 1705 } 1694 1706 1695 - if (TOK == TOK_ASYNC && LA() == TOK_FUNC) { 1707 + if (TOK == TOK_ASYNC && LA() == TOK_FUNC && !lookahead_crosses_line_terminator(p)) { 1696 1708 decl->flags |= EX_DECL; 1697 1709 uint32_t async_off = (uint32_t)TOFF; 1698 1710 CONSUME(); ··· 2088 2100 l_async: { 2089 2101 uint8_t la = LA(); 2090 2102 uint32_t async_off = (uint32_t)TOFF; 2091 - if (la == TOK_FUNC) { 2103 + if (la == TOK_FUNC && !lookahead_crosses_line_terminator(p)) { 2092 2104 CONSUME(); 2093 2105 NEXT(); CONSUME(); 2094 2106 sv_ast_t *fn = parse_func(p);
+38
src/silver/compiler.c
··· 4247 4247 if (node->str) end_scope(c); 4248 4248 } 4249 4249 4250 + static bool ast_contains_await_expr(const sv_ast_t *node) { 4251 + if (!node) return false; 4252 + 4253 + if (node->type == N_AWAIT) return true; 4254 + if (node->type == N_FUNC) return false; 4255 + 4256 + if (ast_contains_await_expr(node->left)) return true; 4257 + if (ast_contains_await_expr(node->right)) return true; 4258 + if (ast_contains_await_expr(node->cond)) return true; 4259 + if (ast_contains_await_expr(node->body)) return true; 4260 + if (ast_contains_await_expr(node->catch_body)) return true; 4261 + if (ast_contains_await_expr(node->finally_body)) return true; 4262 + if (ast_contains_await_expr(node->catch_param)) return true; 4263 + if (ast_contains_await_expr(node->init)) return true; 4264 + if (ast_contains_await_expr(node->update)) return true; 4265 + 4266 + for (int i = 0; i < node->args.count; i++) { 4267 + if (ast_contains_await_expr(node->args.items[i])) return true; 4268 + } 4269 + 4270 + return false; 4271 + } 4272 + 4273 + static bool func_params_contain_await(const sv_ast_t *node) { 4274 + if (!node) return false; 4275 + for (int i = 0; i < node->args.count; i++) { 4276 + if (ast_contains_await_expr(node->args.items[i])) return true; 4277 + } 4278 + return false; 4279 + } 4280 + 4250 4281 sv_func_t *compile_function_body( 4251 4282 sv_compiler_t *enclosing, 4252 4283 sv_ast_t *node, 4253 4284 sv_compile_mode_t mode 4254 4285 ) { 4286 + if ((node->flags & FN_ASYNC) && func_params_contain_await(node)) { 4287 + js_mkerr_typed( 4288 + enclosing->js, JS_ERR_SYNTAX, 4289 + "await is not allowed in async function parameters"); 4290 + return NULL; 4291 + } 4292 + 4255 4293 sv_compiler_t comp; 4256 4294 sv_compile_ctx_init_child(&comp, enclosing, node, mode); 4257 4295
+2 -2
src/silver/glue.c
··· 624 624 closure->func_obj = func_obj; 625 625 ant_value_t module_ctx = sv_get_current_closure_module_ctx(js, mkval(T_FUNC, (uintptr_t)parent_closure)); 626 626 627 - js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator); 627 + js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator && !child->is_async); 628 628 js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); 629 629 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 630 630 ··· 632 632 js_set_slot_wb(js, func_obj, SLOT_MODULE_CTX, module_ctx); 633 633 634 634 ant_value_t func_val = mkval(T_FUNC, (uintptr_t)closure); 635 - if (!child->is_arrow && !child->is_method) { 635 + if (!child->is_arrow && !child->is_method && !child->is_async) { 636 636 ant_value_t parent_proto = child->is_generator ? js->sym.generator_proto : js->sym.object_proto; 637 637 sv_setup_function_prototype_with_parent(js, func_obj, func_val, parent_proto); 638 638 }
+2 -2
src/silver/ops/upvalues.h
··· 134 134 closure->func_obj = func_obj; 135 135 ant_value_t module_ctx = sv_get_current_closure_module_ctx(js, frame->callee); 136 136 137 - js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator); 137 + js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator && !child->is_async); 138 138 js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); 139 139 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 140 140 141 141 if (is_object_type(module_ctx)) js_set_slot_wb(js, func_obj, SLOT_MODULE_CTX, module_ctx); 142 - if (!child->is_arrow && !child->is_method) { 142 + if (!child->is_arrow && !child->is_method && !child->is_async) { 143 143 ant_value_t parent_proto = child->is_generator ? js->sym.generator_proto : js->sym.object_proto; 144 144 sv_setup_function_prototype_with_parent(js, func_obj, func_val, parent_proto); 145 145 }