diff --git a/examples/spec/destructuring.js b/examples/spec/destructuring.js index e806b8e..23bef1c 100644 --- a/examples/spec/destructuring.js +++ b/examples/spec/destructuring.js @@ -19,6 +19,13 @@ const [head, ...tail] = [1, 2, 3, 4]; test('array rest head', head, 1); testDeep('array rest tail', tail, [2, 3, 4]); +const [astral] = '𠮷𠮶'; +test('array destructure astral string', astral, '𠮷'); + +let astralAssign; +[astralAssign] = '𠮷𠮶'; +test('array destructure assign astral string', astralAssign, '𠮷'); + const { name, age } = { name: 'Alice', age: 30 }; test('object destructure name', name, 'Alice'); test('object destructure age', age, 30); @@ -48,6 +55,11 @@ function arrayParam([x, y]) { } test('array param destructure', arrayParam([3, 4]), 12); +function arrayAstralParam([ch]) { + return ch; +} +test('array param destructure astral string', arrayAstralParam('𠮷𠮶'), '𠮷'); + const swap = ([a, b]) => [b, a]; testDeep('swap via destructure', swap([1, 2]), [2, 1]); diff --git a/include/silver/opcode.h b/include/silver/opcode.h index 43f608c..e10dbf7 100644 --- a/include/silver/opcode.h +++ b/include/silver/opcode.h @@ -187,6 +187,10 @@ OP_DEF( ITER_GET_VALUE, 1, 2, 3, none) /* catch_off obj -> catch_of OP_DEF( ITER_CLOSE, 1, 3, 0, none) /* close iterator */ OP_DEF( ITER_CALL, 2, 4, 5, u8) /* call iterator method */ OP_DEF( AWAIT_ITER_NEXT, 1, 3, 4, none) /* async iterator next */ +OP_DEF( DESTRUCTURE_INIT, 1, 1, 3, none) /* iterable -> iter next tag */ +OP_DEF( DESTRUCTURE_NEXT, 1, 3, 4, none) /* iter next tag -> iter next tag value|undef */ +OP_DEF( DESTRUCTURE_REST, 1, 3, 4, none) /* iter next tag -> iter next tag array */ +OP_DEF( DESTRUCTURE_CLOSE, 1, 3, 0, none) /* close destructuring iterator */ OP_DEF( AWAIT, 1, 1, 1, none) /* promise -> resolved value */ OP_DEF( YIELD, 1, 1, 2, none) /* val -> received */ diff --git a/src/silver/compiler.c b/src/silver/compiler.c index 54ed4a0..783f85d 100644 --- a/src/silver/compiler.c +++ b/src/silver/compiler.c @@ -2460,29 +2460,37 @@ static void compile_destructure_store(sv_compiler_t *c, sv_ast_t *target, compile_lhs_set(c, target, false); } -static void compile_destructure_pattern(sv_compiler_t *c, sv_ast_t *pat, - bool keep, bool consume_source, - sv_destructure_mode_t mode, - sv_var_kind_t kind) { +static void compile_destructure_pattern( + sv_compiler_t *c, sv_ast_t *pat, + bool keep, bool consume_source, + sv_destructure_mode_t mode, + sv_var_kind_t kind +) { if (!pat) return; if (keep) emit_op(c, OP_DUP); if (pat->type == N_ARRAY_PAT || pat->type == N_ARRAY) { + if (!consume_source && !keep) emit_op(c, OP_DUP); + emit_op(c, OP_DESTRUCTURE_INIT); + + int err_local = add_local(c, "", 0, false, c->scope_depth); + int try_jump = emit_jump(c, OP_TRY_PUSH); + c->try_depth++; + for (int i = 0; i < pat->args.count; i++) { sv_ast_t *elem = pat->args.items[i]; - if (!elem || elem->type == N_EMPTY) continue; - + if (!elem || elem->type == N_EMPTY) { + emit_op(c, OP_DESTRUCTURE_NEXT); + emit_op(c, OP_POP); + continue; + } + if (elem->type == N_REST || elem->type == N_SPREAD) { - emit_op(c, OP_DUP); - emit_op(c, OP_DUP); - emit_atom_op(c, OP_GET_FIELD, "slice", 5); - emit_number(c, (double)i); - emit_op(c, OP_CALL_METHOD); - emit_u16(c, 1); + emit_op(c, OP_DESTRUCTURE_REST); compile_destructure_store(c, elem->right, mode, kind); continue; } - + sv_ast_t *target = elem; sv_ast_t *default_val = NULL; if (elem->type == N_ASSIGN_PAT || @@ -2490,11 +2498,9 @@ static void compile_destructure_pattern(sv_compiler_t *c, sv_ast_t *pat, target = elem->left; default_val = elem->right; } - - emit_op(c, OP_DUP); - emit_number(c, (double)i); - emit_op(c, OP_GET_ELEM); - + + emit_op(c, OP_DESTRUCTURE_NEXT); + if (default_val) { emit_op(c, OP_DUP); emit_op(c, OP_IS_UNDEF); @@ -2503,9 +2509,26 @@ static void compile_destructure_pattern(sv_compiler_t *c, sv_ast_t *pat, compile_expr(c, default_val); patch_jump(c, skip); } - + compile_destructure_store(c, target, mode, kind); } + + emit_op(c, OP_TRY_POP); + emit_op(c, OP_DESTRUCTURE_CLOSE); + int end_jump = emit_jump(c, OP_JMP); + + patch_jump(c, try_jump); + int catch_tag = emit_jump(c, OP_CATCH); + + emit_put_local(c, err_local); + emit_op(c, OP_DESTRUCTURE_CLOSE); + emit_get_local(c, err_local); + emit_op(c, OP_THROW); + patch_jump(c, catch_tag); + patch_jump(c, end_jump); + c->try_depth--; + + return; } else if (pat->type == N_OBJECT_PAT || pat->type == N_OBJECT) { for (int i = 0; i < pat->args.count; i++) { sv_ast_t *prop = pat->args.items[i]; diff --git a/src/silver/engine.c b/src/silver/engine.c index 15b1b90..650c0c5 100644 --- a/src/silver/engine.c +++ b/src/silver/engine.c @@ -1200,6 +1200,10 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_ITER_CLOSE: { sv_op_iter_close(vm, js); NEXT(1); } L_ITER_CALL: { VM_CHECK(sv_op_iter_call(vm, js, ip)); NEXT(2); } L_AWAIT_ITER_NEXT: { VM_CHECK(sv_op_await_iter_next(vm, js)); NEXT(1); } + L_DESTRUCTURE_INIT: { VM_CHECK(sv_op_destructure_init(vm, js)); NEXT(1); } + L_DESTRUCTURE_NEXT: { VM_CHECK(sv_op_destructure_next(vm, js)); NEXT(1); } + L_DESTRUCTURE_REST: { VM_CHECK(sv_op_destructure_rest(vm, js)); NEXT(1); } + L_DESTRUCTURE_CLOSE:{ sv_op_destructure_close(vm, js); NEXT(1); } L_AWAIT: { ant_value_t await_val = vm->stack[--vm->sp]; diff --git a/src/silver/ops/iteration.h b/src/silver/ops/iteration.h index 58d8f47..783c109 100644 --- a/src/silver/ops/iteration.h +++ b/src/silver/ops/iteration.h @@ -134,8 +134,9 @@ static inline ant_value_t sv_op_for_await_of(sv_vm_t *vm, ant_t *js) { return tov(0); } -static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { - int hint = (int)sv_get_u8(ip + 1); +static inline ant_value_t sv_iter_advance( + sv_vm_t *vm, ant_t *js, int hint, ant_value_t *out_value, bool *out_done +) { int tag = hint ? hint : (int)js_getnum(vm->stack[vm->sp - 1]); switch (tag) { case SV_ITER_ARRAY: { @@ -143,12 +144,12 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { int idx = (int)js_getnum(vm->stack[vm->sp - 2]); ant_offset_t len = js_arr_len(js, arr); if (idx >= (int)len) { - vm->stack[vm->sp++] = js_mkundef(); - vm->stack[vm->sp++] = js_true; + *out_value = js_mkundef(); + *out_done = true; } else { - vm->stack[vm->sp++] = js_arr_get(js, arr, (ant_offset_t)idx); - vm->stack[vm->sp++] = js_false; - vm->stack[vm->sp - 4] = tov(idx + 1); + *out_value = js_arr_get(js, arr, (ant_offset_t)idx); + *out_done = false; + vm->stack[vm->sp - 2] = tov(idx + 1); } return tov(0); } @@ -157,8 +158,8 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { map_iterator_state_t *st = (map_iterator_state_t *)(uintptr_t)js_getnum(vm->stack[vm->sp - 3]); if (!st->current) { - vm->stack[vm->sp++] = js_mkundef(); - vm->stack[vm->sp++] = js_true; + *out_value = js_mkundef(); + *out_done = true; } else { map_entry_t *entry = st->current; ant_value_t value; @@ -180,8 +181,8 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { value = js_mkundef(); } st->current = entry->hh.next; - vm->stack[vm->sp++] = value; - vm->stack[vm->sp++] = js_false; + *out_value = value; + *out_done = false; } return tov(0); } @@ -190,8 +191,8 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { set_iterator_state_t *st = (set_iterator_state_t *)(uintptr_t)js_getnum(vm->stack[vm->sp - 3]); if (!st->current) { - vm->stack[vm->sp++] = js_mkundef(); - vm->stack[vm->sp++] = js_true; + *out_value = js_mkundef(); + *out_done = true; } else { set_entry_t *entry = st->current; ant_value_t value; @@ -204,8 +205,8 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { value = entry->value; } st->current = entry->hh.next; - vm->stack[vm->sp++] = value; - vm->stack[vm->sp++] = js_false; + *out_value = value; + *out_done = false; } return tov(0); } @@ -215,8 +216,8 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { int idx = (int)js_getnum(vm->stack[vm->sp - 2]); ant_offset_t slen = str_len_fast(js, str); if (idx >= (int)slen) { - vm->stack[vm->sp++] = js_mkundef(); - vm->stack[vm->sp++] = js_true; + *out_value = js_mkundef(); + *out_done = true; } else { ant_offset_t off = vstr(js, str, NULL); utf8proc_int32_t cp; @@ -225,9 +226,9 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { (utf8proc_ssize_t)(slen - idx), &cp ); - vm->stack[vm->sp++] = js_mkstr(js, (const void *)(uintptr_t)(off + idx), cb_len); - vm->stack[vm->sp++] = js_false; - vm->stack[vm->sp - 4] = tov(idx + (int)cb_len); + *out_value = js_mkstr(js, (const void *)(uintptr_t)(off + idx), cb_len); + *out_done = false; + vm->stack[vm->sp - 2] = tov(idx + (int)cb_len); } return tov(0); } @@ -243,13 +244,25 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { if (!is_object_type(result)) return js_mkerr_typed(js, JS_ERR_TYPE, "Iterator result is not an object"); ant_value_t done = js_getprop_fallback(js, result, "done"); - ant_value_t value = js_getprop_fallback(js, result, "value"); - vm->stack[vm->sp++] = value; - vm->stack[vm->sp++] = mkval(T_BOOL, js_truthy(js, done)); + *out_value = js_getprop_fallback(js, result, "value"); + *out_done = js_truthy(js, done); return tov(0); }} } +static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { + int hint = (int)sv_get_u8(ip + 1); + ant_value_t value; + bool done = false; + + ant_value_t status = sv_iter_advance(vm, js, hint, &value, &done); + if (is_err(status)) return status; + vm->stack[vm->sp++] = value; + vm->stack[vm->sp++] = done ? js_true : js_false; + + return tov(0); +} + static inline void sv_op_iter_get_value(sv_vm_t *vm, ant_t *js) { ant_value_t obj = vm->stack[--vm->sp]; ant_value_t done = js_getprop_fallback(js, obj, "done"); @@ -270,6 +283,39 @@ static inline void sv_op_iter_close(sv_vm_t *vm, ant_t *js) { vm->sp -= 3; } +static inline ant_value_t sv_op_destructure_init(sv_vm_t *vm, ant_t *js) { + return sv_op_for_of(vm, js); +} + +static inline void sv_op_destructure_close(sv_vm_t *vm, ant_t *js) { + sv_op_iter_close(vm, js); +} + +static inline ant_value_t sv_op_destructure_next(sv_vm_t *vm, ant_t *js) { + ant_value_t value; + bool done = false; + + ant_value_t status = sv_iter_advance(vm, js, 0, &value, &done); + if (is_err(status)) return status; + vm->stack[vm->sp++] = done ? js_mkundef() : value; + + return tov(0); +} + +static inline ant_value_t sv_op_destructure_rest(sv_vm_t *vm, ant_t *js) { + ant_value_t rest = js_mkarr(js); + for (;;) { + ant_value_t value; + bool done = false; + ant_value_t status = sv_iter_advance(vm, js, 0, &value, &done); + if (is_err(status)) return status; + if (done) break; + js_arr_push(js, rest, value); + } + vm->stack[vm->sp++] = rest; + return tov(0); +} + static inline ant_value_t sv_op_iter_call(sv_vm_t *vm, ant_t *js, uint8_t *ip) { ant_value_t method = vm->stack[vm->sp - 1]; ant_value_t iterator = vm->stack[vm->sp - 4];