From f05a39a57eea41ef17e63b226c8d3803ab8ce703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Fri, 24 Jan 2025 22:44:14 +0100 Subject: [PATCH] Fix exporting destructured variables Fixes: https://github.com/bellard/quickjs/issues/382 --- quickjs.c | 23 ++++++++++++++--------- test262_errors.txt | 1 - tests/destructured-export.js | 8 ++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 tests/destructured-export.js diff --git a/quickjs.c b/quickjs.c index f19905fad..ddce8f423 100644 --- a/quickjs.c +++ b/quickjs.c @@ -23009,7 +23009,7 @@ static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) present at the top level. */ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, int hasval, int has_ellipsis, - bool allow_initializer) + bool allow_initializer, bool export_flag) { int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; int start_addr, assign_addr; @@ -23128,7 +23128,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_u32(s, prop_name); emit_ic(s, prop_name); } - if (js_parse_destructuring_element(s, tok, is_arg, true, -1, true) < 0) + if (js_parse_destructuring_element(s, tok, is_arg, true, -1, true, export_flag) < 0) return -1; if (s->token.val == '}') break; @@ -23254,6 +23254,11 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, if (tok) { if (js_define_var(s, var_name, tok)) goto var_error; + if (export_flag) { + if (!add_export_entry(s, s->cur_func->module, var_name, var_name, + JS_EXPORT_TYPE_LOCAL)) + goto var_error; + } scope = s->cur_func->scope_level; } if (s->token.val == '=') { /* handle optional default value */ @@ -23329,7 +23334,7 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, emit_u8(s, 0); emit_op(s, OP_drop); } - if (js_parse_destructuring_element(s, tok, is_arg, true, skip_bits & SKIP_HAS_ELLIPSIS, true) < 0) + if (js_parse_destructuring_element(s, tok, is_arg, true, skip_bits & SKIP_HAS_ELLIPSIS, true, export_flag) < 0) return -1; } else { var_name = JS_ATOM_NULL; @@ -23620,7 +23625,7 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { int skip_bits; if (js_parse_skip_parens_token(s, &skip_bits, false) == '=') { - if (js_parse_destructuring_element(s, 0, 0, false, skip_bits & SKIP_HAS_ELLIPSIS, true) < 0) + if (js_parse_destructuring_element(s, 0, 0, false, skip_bits & SKIP_HAS_ELLIPSIS, true, false) < 0) return -1; } else { if (s->token.val == '{') { @@ -25179,7 +25184,7 @@ static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, if ((s->token.val == '[' || s->token.val == '{') && js_parse_skip_parens_token(s, &skip_bits, false) == '=') { emit_op(s, OP_undefined); - if (js_parse_destructuring_element(s, tok, 0, true, skip_bits & SKIP_HAS_ELLIPSIS, true) < 0) + if (js_parse_destructuring_element(s, tok, 0, true, skip_bits & SKIP_HAS_ELLIPSIS, true, export_flag) < 0) return -1; } else { return js_parse_error(s, "variable name expected"); @@ -25301,7 +25306,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { - if (js_parse_destructuring_element(s, tok, 0, true, -1, false) < 0) + if (js_parse_destructuring_element(s, tok, 0, true, -1, false, false) < 0) return -1; has_destructuring = true; } else { @@ -25329,7 +25334,7 @@ static __exception int js_parse_for_in_of(JSParseState *s, int label_name, int skip_bits; if ((s->token.val == '[' || s->token.val == '{') && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, false)) == TOK_IN || tok1 == TOK_OF)) { - if (js_parse_destructuring_element(s, 0, 0, true, skip_bits & SKIP_HAS_ELLIPSIS, true) < 0) + if (js_parse_destructuring_element(s, 0, 0, true, skip_bits & SKIP_HAS_ELLIPSIS, true, false) < 0) return -1; } else { int lvalue_label; @@ -26007,7 +26012,7 @@ static __exception int js_parse_statement_or_decl(JSParseState *s, if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { if (s->token.val == '[' || s->token.val == '{') { /* XXX: TOK_LET is not completely correct */ - if (js_parse_destructuring_element(s, TOK_LET, 0, true, -1, true) < 0) + if (js_parse_destructuring_element(s, TOK_LET, 0, true, -1, true, false) < 0) goto fail; } else { js_parse_error(s, "identifier expected"); @@ -32955,7 +32960,7 @@ static __exception int js_parse_function_decl2(JSParseState *s, emit_op(s, OP_get_arg); emit_u16(s, idx); } - has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, true, -1, true); + has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, true, -1, true, false); if (has_initializer < 0) goto fail; if (has_initializer) diff --git a/test262_errors.txt b/test262_errors.txt index 85eb1cf9e..a9fc12828 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -70,7 +70,6 @@ test262/test/language/expressions/in/private-field-invalid-assignment-target.js: test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated. test262/test/language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js:31: Test262Error: Expected SameValue(«"bad"», «"ok"») to be true test262/test/language/expressions/object/computed-property-name-topropertykey-before-value-evaluation.js:31: strict mode: Test262Error: Expected SameValue(«"bad"», «"ok"») to be true -test262/test/language/module-code/top-level-await/async-module-does-not-block-sibling-modules.js:13: SyntaxError: Could not find export 'check' in module 'test262/test/language/module-code/top-level-await/async-module-sync_FIXTURE.js' test262/test/language/module-code/top-level-await/module-graphs-does-not-hang.js:10: TypeError: $DONE() not called test262/test/language/statements/class/elements/syntax/valid/grammar-field-named-get-followed-by-generator-asi.js:40: SyntaxError: invalid property name test262/test/language/statements/class/elements/syntax/valid/grammar-field-named-get-followed-by-generator-asi.js:40: strict mode: SyntaxError: invalid property name diff --git a/tests/destructured-export.js b/tests/destructured-export.js new file mode 100644 index 000000000..a038c6ffd --- /dev/null +++ b/tests/destructured-export.js @@ -0,0 +1,8 @@ +import { assert, assertArrayEquals } from "./assert.js"; +import * as mod from "./destructured-export.js"; + +export const { a, b, c } = { a: 1, b: 2, c: 3 }; +export const d = 4; + +assert(typeof mod === 'object'); +assertArrayEquals(Object.keys(mod), ["a", "b", "c", "d"]);