From 7f6a6997002cecac5efdef4055404ffbb00bbe5f Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Fri, 9 Jul 2021 07:23:28 +0200 Subject: [PATCH] Implement namespace exports in modules (#4708) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/ecma/base/ecma-module.c | 17 +++++++++-- jerry-core/parser/js/js-parser-statm.c | 36 +++++++++++++++++++++++- jerry-core/parser/js/js-scanner.c | 14 +++++++++ tests/jerry/es.next/module-export-10.mjs | 33 ++++++++++++++++++++++ tests/jerry/es.next/module-import-07.mjs | 30 ++++++++++++++++++++ tests/jerry/fail/module-024.mjs | 5 ++-- 6 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 tests/jerry/es.next/module-export-10.mjs create mode 100644 tests/jerry/es.next/module-import-07.mjs diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index b18facb78..03e5db429 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -474,8 +474,21 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */ { ecma_module_t *target_module_p = ecma_module_get_from_object (*indirect_export_p->u.module_object_p); - if (!ecma_module_resolve_set_append (resolve_set_p, target_module_p, export_names_p->local_name_p) - && resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND) + if (ecma_compare_ecma_string_to_magic_id (export_names_p->local_name_p, + LIT_MAGIC_STRING_ASTERIX_CHAR)) + { + /* Namespace export. */ + ecma_value_t namespace = ecma_make_object_value (target_module_p->namespace_object_p); + + JERRY_ASSERT (namespace & ECMA_MODULE_NAMESPACE_RESULT_FLAG); + + if (!ecma_module_resolve_update (resolve_result_p, namespace)) + { + goto exit; + } + } + else if (!ecma_module_resolve_set_append (resolve_set_p, target_module_p, export_names_p->local_name_p) + && resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND) { resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR; } diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index dc1e4820a..06c049adf 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -2618,13 +2618,47 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ case LEXER_MULTIPLY: { lexer_next_token (context_p); + + ecma_module_node_t **target_node_list_p = &(JERRY_CONTEXT (module_current_p)->star_exports_p); + + if (lexer_token_is_identifier (context_p, "as", 2)) + { + target_node_list_p = &(JERRY_CONTEXT (module_current_p)->indirect_exports_p); + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_NEW_IDENT_LITERAL); + + lexer_literal_t *literal_p = PARSER_GET_LITERAL (context_p->lit_object.index); + ecma_string_t *export_name_p = ecma_new_ecma_string_from_utf8 (literal_p->u.char_p, + literal_p->prop.length); + + if (parser_module_check_duplicate_export (context_p, export_name_p)) + { + ecma_deref_ecma_string (export_name_p); + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER); + } + + ecma_string_t *local_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_ASTERIX_CHAR); + parser_module_add_names_to_node (context_p, export_name_p, local_name_p); + ecma_deref_ecma_string (export_name_p); + + lexer_next_token (context_p); + } + if (!lexer_token_is_identifier (context_p, "from", 4)) { parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED); } lexer_next_token (context_p); - parser_module_handle_module_specifier (context_p, &(JERRY_CONTEXT (module_current_p)->star_exports_p)); + parser_module_handle_module_specifier (context_p, target_node_list_p); return false; } case LEXER_KEYW_VAR: diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 95193da6f..9350bcfdb 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -1851,6 +1851,20 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); + + if (lexer_token_is_identifier (context_p, "as", 2)) + { + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_LITERAL + && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) + { + scanner_raise_error (context_p); + } + + lexer_next_token (context_p); + } + if (!lexer_token_is_identifier (context_p, "from", 4)) { scanner_raise_error (context_p); diff --git a/tests/jerry/es.next/module-export-10.mjs b/tests/jerry/es.next/module-export-10.mjs new file mode 100644 index 000000000..47a266dab --- /dev/null +++ b/tests/jerry/es.next/module-export-10.mjs @@ -0,0 +1,33 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Export the same namespace using two names */ +export + * as ns1 from + "./module-export-04.mjs" +export * as + ns2 + from "./module-export-04.mjs" +export * + as ns3 from "./module-export-04.mjs" + +/* Local bindings can have the same names as exports. */ +import ns1, {x as ns2} + from "./module-export-04.mjs" +let ns3 = 9.5 + +assert(ns1 === "str") +assert(ns2 === 41) +assert(ns3 === 9.5) diff --git a/tests/jerry/es.next/module-import-07.mjs b/tests/jerry/es.next/module-import-07.mjs new file mode 100644 index 000000000..a24adf3e1 --- /dev/null +++ b/tests/jerry/es.next/module-import-07.mjs @@ -0,0 +1,30 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {ns1 as obj, ns2} from "module-export-10.mjs" +import {ns3} from "module-export-10.mjs" + +assert(typeof obj === "object") +assert(obj === ns2) +assert(obj === ns3) + +assert(obj.x === 41) +try { + obj.x = 42 + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} +assert(obj.x === 41) diff --git a/tests/jerry/fail/module-024.mjs b/tests/jerry/fail/module-024.mjs index 1e9f3bad5..5b2425ee5 100644 --- a/tests/jerry/fail/module-024.mjs +++ b/tests/jerry/fail/module-024.mjs @@ -13,5 +13,6 @@ * limitations under the License. */ -/* Star exports can't have an export name. */ -export * as star from "../es.next/module-export-01.mjs" +export function ns() {} +/* Duplicated export. */ +export * as ns from "../es.next/module-export-fail-test.mjs"