From e3a8cc4766864d2ead09da83b93b1ec3375e2d8c Mon Sep 17 00:00:00 2001 From: Daniel Vince Date: Wed, 23 Jan 2019 16:49:16 +0100 Subject: [PATCH] Part I: Implement ES2015 module system. JerryScript-DCO-1.0-Signed-off-by: Daniel Vince vinced@inf.u-szeged.hu --- .travis.yml | 2 +- docs/05.PORT-API.md | 26 + docs/14.MODULE-SYSTEM.md | 132 +++++ jerry-core/config.h | 1 + jerry-core/ecma/base/ecma-init-finalize.c | 4 + jerry-core/ecma/base/ecma-module.c | 258 +++++++++ jerry-core/ecma/base/ecma-module.h | 27 + jerry-core/ecma/operations/ecma-lex-env.c | 40 ++ jerry-core/ecma/operations/ecma-lex-env.h | 5 + jerry-core/include/jerryscript-port.h | 24 + jerry-core/jcontext/jcontext.h | 18 + jerry-core/parser/js/js-parser-internal.h | 90 ++++ jerry-core/parser/js/js-parser-module.c | 603 ++++++++++++++++++++++ jerry-core/parser/js/js-parser-statm.c | 181 +++++++ jerry-core/parser/js/js-parser-util.c | 40 ++ jerry-core/parser/js/js-parser.c | 31 +- jerry-core/parser/js/js-parser.h | 16 +- jerry-core/profiles/README.md | 4 +- jerry-core/vm/vm.c | 31 ++ jerry-core/vm/vm.h | 4 + jerry-port/default/default-io.c | 69 ++- targets/nuttx-stm32f4/jerry_main.c | 67 +++ tests/jerry/es2015/module-imported-2.js | 31 ++ tests/jerry/es2015/module-imported-3.js | 18 + tests/jerry/es2015/module-imported.js | 31 ++ tests/jerry/es2015/module.js | 35 ++ tests/jerry/fail/module-001.js | 17 + tests/jerry/fail/module-002.js | 16 + tests/jerry/fail/module-003.js | 16 + tests/jerry/fail/module-004.js | 16 + tests/jerry/fail/module-005.js | 16 + tests/jerry/fail/module-006.js | 16 + tests/jerry/fail/module-007.js | 16 + tests/jerry/fail/module-008.js | 16 + tests/jerry/fail/module-009.js | 16 + tests/jerry/fail/module-010.js | 16 + tests/jerry/fail/module-011.js | 16 + tests/jerry/fail/module-012.js | 16 + tests/jerry/fail/module-013.js | 17 + tests/jerry/fail/module-014.js | 18 + tests/jerry/fail/module-015.js | 18 + tests/jerry/fail/module-016.js | 16 + tests/jerry/fail/module-017.js | 17 + tests/jerry/fail/module-018.js | 20 + tests/jerry/fail/module-019.js | 16 + 45 files changed, 2095 insertions(+), 8 deletions(-) create mode 100644 docs/14.MODULE-SYSTEM.md create mode 100644 jerry-core/ecma/base/ecma-module.c create mode 100644 jerry-core/ecma/base/ecma-module.h create mode 100644 jerry-core/parser/js/js-parser-module.c create mode 100644 tests/jerry/es2015/module-imported-2.js create mode 100644 tests/jerry/es2015/module-imported-3.js create mode 100644 tests/jerry/es2015/module-imported.js create mode 100644 tests/jerry/es2015/module.js create mode 100644 tests/jerry/fail/module-001.js create mode 100644 tests/jerry/fail/module-002.js create mode 100644 tests/jerry/fail/module-003.js create mode 100644 tests/jerry/fail/module-004.js create mode 100644 tests/jerry/fail/module-005.js create mode 100644 tests/jerry/fail/module-006.js create mode 100644 tests/jerry/fail/module-007.js create mode 100644 tests/jerry/fail/module-008.js create mode 100644 tests/jerry/fail/module-009.js create mode 100644 tests/jerry/fail/module-010.js create mode 100644 tests/jerry/fail/module-011.js create mode 100644 tests/jerry/fail/module-012.js create mode 100644 tests/jerry/fail/module-013.js create mode 100644 tests/jerry/fail/module-014.js create mode 100644 tests/jerry/fail/module-015.js create mode 100644 tests/jerry/fail/module-016.js create mode 100644 tests/jerry/fail/module-017.js create mode 100644 tests/jerry/fail/module-018.js create mode 100644 tests/jerry/fail/module-019.js diff --git a/.travis.yml b/.travis.yml index 908c31ba6b..1bb9763b6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ dist: trusty # Default job task: run tests as defined in the $OPT environment variable. # Jobs can redefine the 'script' stage in the matrix below. -script: tools/run-tests.py $OPTS +script: travis_wait 20 tools/run-tests.py $OPTS # All the job definitions in the matrix. matrix: diff --git a/docs/05.PORT-API.md b/docs/05.PORT-API.md index efed147434..67f992a3f7 100644 --- a/docs/05.PORT-API.md +++ b/docs/05.PORT-API.md @@ -83,6 +83,32 @@ information. void jerry_port_print_char (char c); ``` +### ES2015 Module system helper functions + +The import statement requires two specific functions for opening and closing files (the modules) port specific. + +```c +/** + * Opens file with the given path and reads its source. + * @return the source of the file + */ +uint8_t * +jerry_port_read_source (const char *file_name_p, /**< file name */ + size_t *out_size_p) /**< [out] read bytes */ +{ + // open file from given path + // return its source +} /* jerry_port_read_source */ + +/** + * Release the previously opened file's content. + */ +void +jerry_port_release_source (uint8_t *buffer_p) /**< buffer to free */ +{ + free (buffer_p); +} /* jerry_port_release_source */ +``` ## Date diff --git a/docs/14.MODULE-SYSTEM.md b/docs/14.MODULE-SYSTEM.md new file mode 100644 index 0000000000..efac2912f8 --- /dev/null +++ b/docs/14.MODULE-SYSTEM.md @@ -0,0 +1,132 @@ +# ES6 module support for JerryScript + +The module system allows users to write import and export statements in scripts. Therefore the logic of the application could be separated in custom modules. +The standard's relevant part can be found [here](https://www.ecma-international.org/ecma-262/6.0/#sec-modules). + +## General + +If the main script contains import statements, then Jerry opens and runs the appropriate scripts before the main script (as the standard says). The script's and the module's extension is `.js`, custom extensions are unnecessary. + +main.js + +```js +import { secret_number } from "./module.js" + +print (secret_number); +``` + +module.js + +```js +var secret_number = 42; + +export secret_number; +``` + +## Supported features + +* import variable or function + * add alias name to the imported variable (function) +* export variable or function + * add alias name to the exported variable (function) + +### Example + +```js +import { + engine, + version as v +} from "./module.js" + +import { getFeatureDetails } from "./module_2.js" + +var version = "v3.1415"; + +print("> main.js"); + +print(">> Engine: " + engine); +print(">> Version: " + v); + +print (">> " + getFeatureDetails()); +print (">> Script version: " + version); +``` + +```js +// module.js +var _engine = "JerryScript"; +export _engine as engine; + +export var version = "1.0 (e92ae0fb)"; +``` + +```js +// module_2.js +var featureName = "EcmaScript 2015 modules"; +var year = 2018; + +export function getFeatureDetails() { + return "Feature name: " + featureName + " | developed in " + year; +} +``` + +## Unsupported features + +* **snapshot** +* errors from the imported scripts +* redirection ( `export { a, b } from 'module.js'` ) +* default import and export + * `import b from 'module.js'` + * `export default b`, +* whole module import statements + * `import * from 'module.js` + * `import { * as module } from 'module.js` +* object freezing ( `Object.freeze (this)` ) + +### Redirection + +An export statement can import variables from a custom module and export it directly from the current script. This statement is called redirection. In this case the `export { b } from 'module2.js'` works as the `b` was imported before then exported as a local variable. + +```js +import { a, b } from 'module.js' + +print (a + b); +``` + +```js +// module.js +export var a = 2; +export { b } from 'module2.js' +``` + +```js +// module2.js +export var b = 40; +``` + +### Default imports and exports + +TODO: This part is going to be written in the next part of the patch. + +### Import the whole module + +The whole module can be imported. In this case the `m` object would contain the exported parts of the module. If the import is not aliased, the `global object` would contain the exports. + +```js +import { * as m } from "./module.js" + +print (m.secret_number); +print (m.getPrettifiedNumber()); +print (m.api.version); +``` + +```js +// module.js +var secret_number = 42; +export secret_number; + +export function getPrettifiedNumber() { + return "*** " + secret_number + " ***"; +} + +export { ble as api } from "./ble.js"; +``` diff --git a/jerry-core/config.h b/jerry-core/config.h index fb67bd74cc..2e11e97814 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -48,6 +48,7 @@ # define CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN # define CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS # define CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN +# define CONFIG_DISABLE_ES2015_MODULE_SYSTEM #endif /* CONFIG_DISABLE_ES2015 */ /** diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index 2dba5920ef..2246f44ed5 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -61,6 +61,10 @@ ecma_finalize (void) { jmem_unregister_free_unused_memory_callback (ecma_free_unused_memory); +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + ecma_module_finalize_lex_envs (); +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + ecma_finalize_global_lex_env (); ecma_finalize_builtins (); ecma_gc_run (JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW); diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c new file mode 100644 index 0000000000..51c8d04bf9 --- /dev/null +++ b/jerry-core/ecma/base/ecma-module.c @@ -0,0 +1,258 @@ +/* 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. + */ + +#include "jerryscript.h" +#include "jcontext.h" + +#include "ecma-helpers.h" +#include "ecma-function-object.h" +#include "ecma-literal-storage.h" +#include "ecma-lex-env.h" +#include "ecma-gc.h" +#include "ecma-module.h" +#include "vm.h" + +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + +/** + * Check if property is exported from the script. + * @returns the export name - if the given property is exported + * NULL - otherwise + */ +static parser_module_names_t * +ecma_parser_module_is_property_exported (ecma_string_t *property_name_p, /**< property name */ + parser_module_node_t *export_node_p) /**< export node */ +{ + parser_module_names_t *current_p = export_node_p->module_names_p; + + if (current_p == NULL) + { + return NULL; + } + + for (uint16_t i = 0; i < export_node_p->module_request_count; i++) + { + parser_module_names_t *next_p = current_p->next_p; + + bool found = ecma_compare_ecma_strings (current_p->local_name_p, property_name_p); + + if (found) + { + return current_p; + } + current_p = next_p; + } + + return NULL; +} /* ecma_parser_module_is_property_exported */ + +/** + * Compare property name with imports. + * @return the export name - if the exported property is imported + * NULL - otherwise + */ +static parser_module_names_t * +ecma_parser_module_compare_property_name_with_import (parser_module_node_t *module_node_p, /**< module node */ + parser_module_names_t *export_names_p) /**< export names */ +{ + parser_module_names_t *current_p = module_node_p->module_names_p; + + if (current_p == NULL) + { + return NULL; + } + + for (uint16_t i = 0; i < module_node_p->module_request_count; i++) + { + parser_module_names_t *next_p = current_p->next_p; + + if (ecma_compare_ecma_strings (current_p->local_name_p, export_names_p->import_name_p)) + { + return current_p; + } + + current_p = next_p; + } + + return NULL; +} /* ecma_parser_module_compare_property_name_with_import */ + +/** + * Connect the imported script's properties to the main script. + */ +static void +ecma_module_connect_properties (ecma_object_t *scope_p) /** scope_p */ +{ + JERRY_ASSERT (ecma_is_lexical_environment (scope_p)); + JERRY_ASSERT (ecma_get_lex_env_type (scope_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE); + + parser_module_context_t *module_context_p = JERRY_CONTEXT (module_top_context_p); + + if (module_context_p == NULL || module_context_p->exports_p == NULL || module_context_p->imports_p == NULL) + { + return; + } + + ecma_object_t *global_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); + ecma_property_header_t *module_properties_p = ecma_get_property_list (scope_p); + + while (module_properties_p != NULL) + { + ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) module_properties_p; + + for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) + { + ecma_property_t *property_p = (ecma_property_t *) (module_properties_p->types + i); + + if (ECMA_PROPERTY_GET_NAME_TYPE (*property_p) == ECMA_DIRECT_STRING_MAGIC + && prop_pair_p->names_cp[i] >= LIT_NON_INTERNAL_MAGIC_STRING__COUNT) + { + continue; + } + + ecma_string_t *prop_name_p = ecma_string_from_property_name (*property_p, prop_pair_p->names_cp[i]); + + parser_module_names_t *exported_name_p = ecma_parser_module_is_property_exported (prop_name_p, + module_context_p->exports_p); + + if (exported_name_p == NULL) + { + ecma_deref_ecma_string (prop_name_p); + continue; + } + + parser_module_names_t *new_name_p = + ecma_parser_module_compare_property_name_with_import (module_context_p->imports_p, + exported_name_p); + + if (new_name_p != NULL) + { + ecma_property_t *new_property_p; + ecma_create_named_data_property (global_obj_p, + new_name_p->import_name_p, + ECMA_PROPERTY_NOT_WRITABLE, + &new_property_p); + + ecma_named_data_property_assign_value (global_obj_p, + ECMA_PROPERTY_VALUE_PTR (new_property_p), + prop_pair_p->values[i].value); + + } + ecma_deref_ecma_string (prop_name_p); + } + + module_properties_p = ECMA_GET_POINTER (ecma_property_header_t, + module_properties_p->next_property_cp); + } + + ecma_module_add_lex_env (scope_p); + parser_module_free_saved_names (module_context_p->exports_p); +} /* ecma_module_connect_properties */ + +/** + * Run an EcmaScript module loaded by ecma_module_load_modules. + */ +static parser_error_t +ecma_parser_module_run (const char *file_path_p, /**< file path */ + size_t path_size, /**< length of the path */ + const char *source_p, /**< module source */ + size_t source_size, /**< length of the source */ + parser_module_node_t *module_node_p) /**< module node */ +{ + parser_module_node_t export_node; + memset (&export_node, 0, sizeof (parser_module_node_t)); + + parser_module_context_t module_context; + module_context.imports_p = module_node_p; + module_context.exports_p = &export_node; + + parser_module_context_t *prev_module_context_p = JERRY_CONTEXT (module_top_context_p); + JERRY_CONTEXT (module_top_context_p) = &module_context; + + jerry_value_t ret_value = jerry_parse ((jerry_char_t *) file_path_p, + path_size, + (jerry_char_t *) source_p, + source_size, + JERRY_PARSE_STRICT_MODE); + + + if (jerry_value_is_error (ret_value)) + { + jerry_release_value (ret_value); + return PARSER_ERR_MODULE_REQUEST_NOT_FOUND; + } + + parser_error_t error = PARSER_ERR_NO_ERROR; + + jerry_value_t func_val = ret_value; + ecma_object_t *func_obj_p = ecma_get_object_from_value (func_val); + + JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION); + + ecma_object_t *scope_p = ecma_create_decl_lex_env (ecma_get_global_environment ()); + ret_value = vm_run_module (ecma_op_function_get_compiled_code ((ecma_extended_object_t *) func_obj_p), scope_p); + + if (jerry_value_is_error (ret_value)) + { + jerry_release_value (ret_value); + error = PARSER_ERR_MODULE_REQUEST_NOT_FOUND; + } + + ecma_module_connect_properties (scope_p); + jerry_release_value (func_val); + + JERRY_CONTEXT (module_top_context_p) = prev_module_context_p; + return error; +} /* ecma_parser_module_run */ + +/** + * Load imported modules. + */ +void +ecma_module_load_modules (parser_context_t *context_p) /**< parser context */ +{ + parser_module_node_t *current_p = context_p->module_context_p->imports_p; + + while (current_p != NULL) + { + uint8_t *script_path_p = current_p->script_path.value_p; + prop_length_t path_length = current_p->script_path.length; + + size_t size = 0; + uint8_t *buffer_p = jerry_port_read_source ((const char *) script_path_p, &size); + + if (buffer_p == NULL) + { + parser_raise_error (context_p, PARSER_ERR_FILE_NOT_FOUND); + } + + parser_error_t error = ecma_parser_module_run ((const char *) script_path_p, + path_length, + (const char *) buffer_p, + size, + current_p); + + jerry_port_release_source (buffer_p); + + if (error != PARSER_ERR_NO_ERROR) + { + parser_raise_error (context_p, error); + } + + current_p = current_p->next_p; + } +} /* ecma_module_load_modules */ + +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ diff --git a/jerry-core/ecma/base/ecma-module.h b/jerry-core/ecma/base/ecma-module.h new file mode 100644 index 0000000000..7d6a217a1c --- /dev/null +++ b/jerry-core/ecma/base/ecma-module.h @@ -0,0 +1,27 @@ +/* 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. + */ + +#ifndef ECMA_MODULE_H +#define ECMA_MODULE_H + +#include "js-parser-internal.h" + +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + +void ecma_module_load_modules (parser_context_t *context_p); + +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + +#endif /* !ECMA_MODULE_H */ diff --git a/jerry-core/ecma/operations/ecma-lex-env.c b/jerry-core/ecma/operations/ecma-lex-env.c index de5bb96c01..3d0a3aed11 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.c +++ b/jerry-core/ecma/operations/ecma-lex-env.c @@ -68,6 +68,46 @@ ecma_get_global_environment (void) return JERRY_CONTEXT (ecma_global_lex_env_p); } /* ecma_get_global_environment */ +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +/** + * Add the lexenv of the newly imported module to the JERRY_CONTEXT. + */ +void +ecma_module_add_lex_env (ecma_object_t *lex_env_p) /**< module lexenv */ +{ + JERRY_ASSERT (lex_env_p != NULL); + JERRY_ASSERT (ecma_is_lexical_environment (lex_env_p)); + + ecma_module_lex_envs_t *new_module_lex_env_p; + new_module_lex_env_p = jmem_heap_alloc_block (sizeof (ecma_module_lex_envs_t)); + new_module_lex_env_p->lex_env_p = lex_env_p; + + new_module_lex_env_p->next_p = JERRY_CONTEXT (ecma_module_lex_envs_p); + JERRY_CONTEXT (ecma_module_lex_envs_p) = new_module_lex_env_p; +} /* ecma_module_add_lex_env */ + +/** + * Finalize the lexenvs of the imported modules and its ECMA components. + */ +void +ecma_module_finalize_lex_envs (void) +{ + ecma_module_lex_envs_t *module_lex_envs = JERRY_CONTEXT (ecma_module_lex_envs_p); + + while (module_lex_envs != NULL) + { + ecma_module_lex_envs_t *next_p = module_lex_envs->next_p; + + ecma_deref_object (module_lex_envs->lex_env_p); + jmem_heap_free_block (module_lex_envs, sizeof (ecma_module_lex_envs_t)); + + module_lex_envs = next_p; + } + + JERRY_CONTEXT (ecma_module_lex_envs_p) = NULL; +} /* ecma_module_finalize_lex_envs */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * @} */ diff --git a/jerry-core/ecma/operations/ecma-lex-env.h b/jerry-core/ecma/operations/ecma-lex-env.h index c0808cbf01..5d9afd9c27 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.h +++ b/jerry-core/ecma/operations/ecma-lex-env.h @@ -34,6 +34,11 @@ void ecma_init_global_lex_env (void); void ecma_finalize_global_lex_env (void); ecma_object_t *ecma_get_global_environment (void); +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +void ecma_module_add_lex_env (ecma_object_t *lex_env_p); +void ecma_module_finalize_lex_envs (void); +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * @} */ diff --git a/jerry-core/include/jerryscript-port.h b/jerry-core/include/jerryscript-port.h index 0a631f4569..0cb615218e 100644 --- a/jerry-core/include/jerryscript-port.h +++ b/jerry-core/include/jerryscript-port.h @@ -191,6 +191,30 @@ void jerry_port_sleep (uint32_t sleep_time); */ void jerry_port_print_char (char c); +/** + * Open a source file and read its contents into a buffer. + * + * Note: + * This port function is called by jerry-core when ES2015_MODULE_SYSTEM + * is enabled. The path is specified in the import statement's 'from "..."' + * section. + * + * @param file_name_p Path that points to the EcmaScript file in the + * filesystem. + * @param out_size_p The opened file's size in bytes. + * + * @return the pointer to the buffer which contains the content of the file. + */ +uint8_t *jerry_port_read_source (const char *file_name_p, size_t *out_size_p); + +/** + * Frees the allocated buffer after the contents of the file are not needed + * anymore. + * + * @param buffer_p The pointer the allocated buffer. + */ +void jerry_port_release_source (uint8_t *buffer_p); + /** * @} */ diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 3f75dbea65..77b525422d 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -28,6 +28,7 @@ #include "vm-defines.h" #include "jerryscript.h" #include "jerryscript-debugger-transport.h" +#include "js-parser-internal.h" /** \addtogroup context Context * @{ @@ -66,6 +67,17 @@ typedef struct jerry_context_data_header */ #define JERRY_CONTEXT_FIRST_MEMBER ecma_builtin_objects +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +/** + * Contains the lexical environments of the loaded modules. + */ +typedef struct ecma_module_lex_envs +{ + ecma_object_t *lex_env_p; /**< pointer to loaded module's lexical environment */ + struct ecma_module_lex_envs *next_p; /**< pointer to the next item in the linked list */ +} ecma_module_lex_envs_t; +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * JerryScript context * @@ -102,6 +114,12 @@ struct jerry_context_t #endif /* !CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN */ ecma_lit_storage_item_t *number_list_first_p; /**< first item of the literal number list */ ecma_object_t *ecma_global_lex_env_p; /**< global lexical environment */ + +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + ecma_module_lex_envs_t *ecma_module_lex_envs_p; /**< list of module's lexical environments */ + parser_module_context_t *module_top_context_p; /**< top (current) module parser context */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + vm_frame_ctx_t *vm_top_context_p; /**< top (current) interpreter context */ jerry_context_data_header_t *context_data_p; /**< linked list of user-provided context-specific pointers */ size_t ecma_gc_objects_number; /**< number of currently allocated objects */ diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 8681da0ee8..122242aa35 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -296,6 +296,59 @@ typedef struct #endif /* JERRY_DEBUGGER */ +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +/** + * String struct for storing the module path. + */ +typedef struct parser_module_utf8_string +{ + uint8_t *value_p; /**< string value stored as uint8_t */ + prop_length_t length; /**< length of the string */ +} parser_module_utf8_string_t; + +/** + * Imported or exported names, for example: + * import { v as x } from "module"; + * exported name: v + * imported name: x + * export x as v; + * exported variable: x + * exported as: v + * Reference: https://www.ecma-international.org/ecma-262/6.0/#table-41 + */ +typedef struct parser_module_names +{ + ecma_string_t *import_name_p; /**< local name of the import - export item */ + ecma_string_t *local_name_p; /**< import name of the import - export item */ + + struct parser_module_names *next_p; /**< next linked list node */ +} parser_module_names_t; + +/** + * Module node to store imports / exports. + */ +typedef struct parser_module_node +{ + parser_module_names_t *module_names_p; /**< names of the requested imports - exports */ + uint16_t module_request_count; /**< count of the requested imports - exports */ + + parser_module_utf8_string_t script_path; /**< path of the requested module*/ + + struct parser_module_node *next_p; /**< next linked list node */ +} parser_module_node_t; + +/** + * Module context in the parser context. It is not in the context if modules + * are not enabled during the build. It's value is NULL if there is no import / + * export in the script, otherwise it contains all the imports and exports. + */ +typedef struct +{ + parser_module_node_t *imports_p; /**< import item of the current context */ + parser_module_node_t *exports_p; /**< export item of the current context */ +} parser_module_context_t; +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * Those members of a context which needs * to be saved when a sub-function is parsed. @@ -342,6 +395,11 @@ typedef struct parser_saved_context_t *last_context_p; /**< last saved context */ parser_stack_iterator_t last_statement; /**< last statement position */ +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + parser_module_context_t *module_context_p; /**< shared module context inside the parser */ + parser_module_node_t *module_current_node_p; /**< import / export node that is being processed */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /* Lexer members. */ lexer_token_t token; /**< current token */ lexer_lit_object_t lit_object; /**< current literal object */ @@ -548,6 +606,38 @@ void parser_scan_until (parser_context_t *context_p, lexer_range_t *range_p, lex void parser_parse_statements (parser_context_t *context_p); void parser_free_jumps (parser_stack_iterator_t iterator); +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +/** + * @} + * + * \addtogroup jsparser_stmt Module statement parser + * @{ + */ + +void parser_module_add_export_node_to_context (parser_context_t *context_p); +void parser_module_add_import_node_to_context (parser_context_t *context_p); +void parser_module_check_request_place (parser_context_t *context_p); +void parser_module_context_cleanup (parser_context_t *context_p); +void parser_module_context_init (parser_context_t *context_p); +void parser_module_free_saved_names (parser_module_node_t *module_node_p); +void parser_module_handle_from_clause (parser_context_t *context_p); +void parser_module_handle_requests (parser_context_t *context_p); +void parser_module_partial_cleanup_on_error (parser_module_node_t *module_node_p); +void parser_module_parse_export_item_list (parser_context_t *context_p); +void parser_module_parse_import_item_list (parser_context_t *context_p); + +parser_module_node_t *parser_module_create_module_node (parser_context_t *context_p, + parser_module_node_t *template_node_p); +parser_module_node_t *parser_module_get_export_node (parser_context_t *context_p); + +void parser_module_add_item_to_node (parser_context_t *context_p, + parser_module_node_t *module_node_p, + lexer_literal_t *import_name_p, + lexer_literal_t *local_name_p, + bool is_import_item); + +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * @} * diff --git a/jerry-core/parser/js/js-parser-module.c b/jerry-core/parser/js/js-parser-module.c new file mode 100644 index 0000000000..4e2c0e8bed --- /dev/null +++ b/jerry-core/parser/js/js-parser-module.c @@ -0,0 +1,603 @@ +/* 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. + */ + +#include "js-parser-internal.h" + +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +#include "jcontext.h" +#include "jerryscript-port.h" + +#include "ecma-exceptions.h" +#include "ecma-function-object.h" +#include "ecma-helpers.h" +#include "ecma-literal-storage.h" +#include "ecma-lex-env.h" +#include "ecma-module.h" + +/** + * Maximum import count limit. + */ +#define MAX_IMPORT_COUNT UINT16_MAX + +/** + * Check duplicates in module node. + * @return true - if the given item is duplicated entry in the current node + * false - otherwise + */ +static bool +parser_module_check_for_duplicates_in_node (parser_module_node_t *module_node_p, /**< module node */ + lexer_literal_t *import_name_p) /**< newly imported name */ +{ + JERRY_ASSERT (import_name_p != NULL); + + parser_module_names_t *import_names_p = module_node_p->module_names_p; + + while (import_names_p != NULL) + { + uint8_t flags = ECMA_STRING_FLAG_IS_ASCII; + lit_utf8_size_t current_length = 0; + const uint8_t *current_p = ecma_string_get_chars (import_names_p->import_name_p, ¤t_length, &flags); + + if (current_p != NULL && current_length == import_name_p->prop.length + && memcmp (current_p, import_name_p->u.char_p, current_length) == 0) + { + return true; + } + + import_names_p = import_names_p->next_p; + } + + return false; +} /* parser_module_check_for_duplicates_in_node */ + +/** + * Check duplicates in the whole module context. + * @return true - if the given item is duplicated entry + * false - otherwise + */ +static bool +parser_module_check_for_duplicates (parser_context_t *context_p, /**< parser context */ + parser_module_node_t *module_node_p, /**< module noda */ + lexer_literal_t *import_name_p) /**< newly imported name */ +{ + if (import_name_p == NULL) + { + return false; + } + + bool hasDuplicate = parser_module_check_for_duplicates_in_node (module_node_p, + import_name_p); + + if (!hasDuplicate) + { + parser_module_node_t *node_p = context_p->module_context_p->imports_p; + while (node_p != NULL && !hasDuplicate) + { + hasDuplicate = parser_module_check_for_duplicates_in_node (node_p, + import_name_p); + node_p = node_p->next_p; + } + } + + return hasDuplicate; +} /* parser_module_check_for_duplicates */ + +/** + * Delete the saved names from the given module node. + */ +void +parser_module_free_saved_names (parser_module_node_t *module_node_p) /**< module node */ +{ + if (module_node_p == NULL || module_node_p->module_names_p == NULL) + { + return; + } + + parser_module_names_t *current_p = module_node_p->module_names_p; + + for (uint16_t i = 0; i < module_node_p->module_request_count; i++) + { + parser_module_names_t *next_p = current_p->next_p; + + if (current_p->import_name_p != NULL) + { + ecma_deref_ecma_string (current_p->import_name_p); + current_p->import_name_p = NULL; + } + + if (current_p->local_name_p != NULL) + { + ecma_deref_ecma_string (current_p->local_name_p); + current_p->local_name_p = NULL; + } + + parser_free (current_p, sizeof (parser_module_names_t)); + current_p = next_p; + } +} /* parser_module_free_saved_names */ + +/** + * Add export node to parser context. + */ +void +parser_module_add_export_node_to_context (parser_context_t *context_p) /**< parser context */ +{ + parser_module_node_t *module_node_p = context_p->module_current_node_p; + parser_module_node_t *exports_p = context_p->module_context_p->exports_p; + + if (exports_p != NULL) + { + parser_module_names_t *module_names_p = module_node_p->module_names_p; + + for (uint16_t i = 0; i < module_node_p->module_request_count - 1 ; i++) + { + module_names_p = module_names_p->next_p; + } + + module_names_p->next_p = exports_p->module_names_p; + exports_p->module_names_p = module_node_p->module_names_p; + + int request_count = exports_p->module_request_count + module_node_p->module_request_count; + + if ((uint16_t) request_count < MAX_IMPORT_COUNT) + { + exports_p->module_request_count = (uint16_t) request_count; + } + else + { + parser_raise_error (context_p, PARSER_ERR_MODULE_REQUEST_LIMIT_REACHED); + } + } + else + { + parser_module_node_t *export_node_p = parser_module_create_module_node (context_p, module_node_p); + context_p->module_context_p->exports_p = export_node_p; + } +} /* parser_module_add_export_node_to_context */ + +/** + * Add import node to parser context. + */ +void +parser_module_add_import_node_to_context (parser_context_t *context_p) /**< parser context */ +{ + parser_module_node_t *module_node_p = context_p->module_current_node_p; + parser_module_node_t *stored_imports = context_p->module_context_p->imports_p; + bool is_stored_module = false; + + while (stored_imports != NULL) + { + if (stored_imports->script_path.length == module_node_p->script_path.length + && memcmp (stored_imports->script_path.value_p, + module_node_p->script_path.value_p, + stored_imports->script_path.length) == 0) + { + parser_free (module_node_p->script_path.value_p, module_node_p->script_path.length * sizeof (uint8_t)); + + parser_module_names_t *module_names_p = module_node_p->module_names_p; + is_stored_module = true; + + for (uint16_t i = 0; i < module_node_p->module_request_count - 1; i++) + { + module_names_p = module_names_p->next_p; + } + + module_names_p->next_p = stored_imports->module_names_p; + stored_imports->module_names_p = module_node_p->module_names_p; + + int request_count = stored_imports->module_request_count + module_node_p->module_request_count; + if ((uint16_t) request_count < MAX_IMPORT_COUNT) + { + stored_imports->module_request_count = (uint16_t) request_count; + } + else + { + parser_raise_error (context_p, PARSER_ERR_MODULE_REQUEST_LIMIT_REACHED); + } + + break; + } + stored_imports = stored_imports->next_p; + } + + if (!is_stored_module) + { + parser_module_node_t *permanent_node_p = parser_module_create_module_node (context_p, module_node_p); + permanent_node_p->next_p = context_p->module_context_p->imports_p; + context_p->module_context_p->imports_p = permanent_node_p; + } + +} /* parser_module_add_import_node_to_context */ + +/** + * Add import or export item to module node. + */ +void +parser_module_add_item_to_node (parser_context_t *context_p, /**< parser context */ + parser_module_node_t *module_node_p, /**< current module node */ + lexer_literal_t *import_name_p, /**< import name */ + lexer_literal_t *local_name_p, /**< local name */ + bool is_import_item) /**< given item is import item */ +{ + if (is_import_item + && parser_module_check_for_duplicates (context_p, module_node_p, import_name_p)) + { + parser_raise_error (context_p, PARSER_ERR_DUPLICATED_LABEL); + } + + parser_module_names_t *new_names_p = (parser_module_names_t *) parser_malloc (context_p, + sizeof (parser_module_names_t)); + + new_names_p->next_p = module_node_p->module_names_p; + module_node_p->module_names_p = new_names_p; + + module_node_p->module_names_p->import_name_p = ecma_new_ecma_string_from_utf8 (import_name_p->u.char_p, + import_name_p->prop.length); + + module_node_p->module_names_p->local_name_p = ecma_new_ecma_string_from_utf8 (local_name_p->u.char_p, + local_name_p->prop.length); + + module_node_p->module_request_count++; +} /* parser_module_add_item_to_node */ + +/** + * Cleanup the whole module context from parser context. + */ +void +parser_module_context_cleanup (parser_context_t *context_p) /**< parser context */ +{ + parser_module_context_t *module_context_p = context_p->module_context_p; + + if (module_context_p == NULL) + { + return; + } + + parser_module_node_t *current_node_p = module_context_p->imports_p; + + while (current_node_p != NULL) + { + parser_free (current_node_p->script_path.value_p, current_node_p->script_path.length * sizeof (uint8_t)); + parser_module_free_saved_names (current_node_p); + + parser_module_node_t *next_node_p = current_node_p->next_p; + + parser_free (current_node_p, sizeof (parser_module_node_t)); + current_node_p = next_node_p; + } + + parser_module_context_t *parent_context_p = JERRY_CONTEXT (module_top_context_p); + if ((parent_context_p == NULL || parent_context_p->exports_p == NULL || parent_context_p->imports_p == NULL) + && module_context_p->exports_p != NULL) + { + parser_module_free_saved_names (module_context_p->exports_p); + parser_free (module_context_p->exports_p, sizeof (parser_module_node_t)); + } + + parser_free (module_context_p, sizeof (parser_module_context_t)); + context_p->module_context_p = NULL; +} /* parser_module_context_cleanup */ + +/** + * Create module context and bind to the parser context. + */ +void +parser_module_context_init (parser_context_t *context_p) /**< parser context */ +{ + if (context_p->module_context_p == NULL) + { + context_p->module_context_p = (parser_module_context_t *) parser_malloc (context_p, + sizeof (parser_module_context_t)); + + memset (context_p->module_context_p, 0, sizeof (parser_module_context_t)); + } +} /* parser_module_context_init */ + +/** + * Create import node. + * @return - the copy of the template if the second parameter is not NULL. + * - otherwise: an empty import node. + */ +parser_module_node_t * +parser_module_create_module_node (parser_context_t *context_p, /**< parser context */ + parser_module_node_t *template_node_p) /**< template node for the new node */ +{ + parser_module_node_t *node = (parser_module_node_t *) parser_malloc (context_p, sizeof (parser_module_node_t)); + + if (template_node_p != NULL) + { + node->module_names_p = template_node_p->module_names_p; + node->module_request_count = template_node_p->module_request_count; + + node->script_path.value_p = template_node_p->script_path.value_p; + node->script_path.length = template_node_p->script_path.length; + node->next_p = NULL; + } + else + { + memset (node, 0, sizeof (parser_module_node_t)); + } + + return node; +} /* parser_module_create_module_node */ + +/** + * Create export node or get the previously created one. + * @return the export node + */ +parser_module_node_t * +parser_module_get_export_node (parser_context_t *context_p) /**< parser context */ +{ + if (context_p->module_context_p->exports_p != NULL) + { + return context_p->module_context_p->exports_p; + } + + return parser_module_create_module_node (context_p, NULL); +} /* parser_module_get_export_node */ + +/** + * Parse export item list. + */ +void +parser_module_parse_export_item_list (parser_context_t *context_p) /**< parser context */ +{ + if (context_p->token.type == LEXER_LITERAL + && lexer_compare_raw_identifier_to_current (context_p, "from", 4)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + if (context_p->token.type == LEXER_KEYW_DEFAULT || context_p->token.type == LEXER_MULTIPLY) + { + /* TODO: This part is going to be implemented in the next part of the patch. */ + parser_raise_error (context_p, PARSER_ERR_NOT_IMPLEMENTED); + } + + bool has_export_name = false; + lexer_literal_t *export_name_p = NULL; + lexer_literal_t *local_name_p = NULL; + parser_module_node_t *module_node_p = context_p->module_current_node_p; + + while (true) + { + if (has_export_name + && context_p->token.type != LEXER_KEYW_DEFAULT + && (context_p->token.type != LEXER_LITERAL + || lexer_compare_raw_identifier_to_current (context_p, "from", 4) + || lexer_compare_raw_identifier_to_current (context_p, "as", 2))) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + if (context_p->token.lit_location.type != LEXER_IDENT_LITERAL + && context_p->token.lit_location.type != LEXER_STRING_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL); + + if (has_export_name) + { + export_name_p = context_p->lit_object.literal_p; + } + else + { + local_name_p = context_p->lit_object.literal_p; + export_name_p = context_p->lit_object.literal_p; + } + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_COMMA) + { + has_export_name = false; + parser_module_add_item_to_node (context_p, module_node_p, export_name_p, local_name_p, false); + } + else if (context_p->token.type == LEXER_LITERAL + && lexer_compare_raw_identifier_to_current (context_p, "as", 2)) + { + if (has_export_name) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + has_export_name = true; + } + else + { + parser_module_add_item_to_node (context_p, module_node_p, export_name_p, local_name_p, false); + break; + } + lexer_next_token (context_p); + } +} /* parser_module_parse_export_item_list */ + +/** + * Parse import item list. + */ +void +parser_module_parse_import_item_list (parser_context_t *context_p) /**< parser context */ +{ + /* Import list is empty, the whole module will be loaded. */ + if (context_p->token.type == LEXER_LITERAL + && lexer_compare_raw_identifier_to_current (context_p, "from", 4)) + { + /* TODO: This part is going to be implemented in the next part of the patch. */ + parser_raise_error (context_p, PARSER_ERR_NOT_IMPLEMENTED); + } + + bool has_import_name = false; + lexer_literal_t *import_name_p = NULL; + lexer_literal_t *local_name_p = NULL; + parser_module_node_t *module_node_p = context_p->module_current_node_p; + + while (true) + { + bool whole_module_needed = context_p->token.type == LEXER_MULTIPLY; + + if (whole_module_needed) + { + parser_raise_error (context_p, PARSER_ERR_NOT_IMPLEMENTED); + } + + if (context_p->token.type != LEXER_LITERAL + || lexer_compare_raw_identifier_to_current (context_p, "from", 4) + || lexer_compare_raw_identifier_to_current (context_p, "as", 2)) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL); + + if (has_import_name) + { + import_name_p = context_p->lit_object.literal_p; + } + else + { + local_name_p = context_p->lit_object.literal_p; + import_name_p = context_p->lit_object.literal_p; + } + + + lexer_next_token (context_p); + + if (context_p->token.type == LEXER_RIGHT_BRACE + || (context_p->token.type == LEXER_LITERAL + && lexer_compare_raw_identifier_to_current (context_p, "from", 4))) + { + parser_module_add_item_to_node (context_p, module_node_p, import_name_p, local_name_p, true); + break; + } + + if (context_p->token.type == LEXER_COMMA) + { + parser_module_add_item_to_node (context_p, module_node_p, import_name_p, local_name_p, true); + has_import_name = false; + } + else if (context_p->token.type == LEXER_LITERAL + && lexer_compare_raw_identifier_to_current (context_p, "as", 2)) + { + if (has_import_name) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } + + has_import_name = true; + } + else + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_COMMA_FROM_EXPECTED); + } + lexer_next_token (context_p); + } +} /* parser_module_parse_import_item_list */ + +/** + * Handle import requests. + * Check if imported variables are exported in the appropriate module. + * Raise parser error if imported item is not exported. + */ +void +parser_module_handle_requests (parser_context_t *context_p) /**< parser context */ +{ + parser_module_context_t *module_context_p = JERRY_CONTEXT (module_top_context_p); + + if (context_p->module_context_p->exports_p == NULL || module_context_p == NULL) + { + return; + } + + bool throw_error = false; + + parser_module_names_t *import_name_p = module_context_p->imports_p->module_names_p; + parser_module_names_t *current_exports_p = context_p->module_context_p->exports_p->module_names_p; + + for (uint16_t i = 0; i < module_context_p->imports_p->module_request_count; ++i) + { + bool request_is_found_in_module = false; + parser_module_names_t *export_iterator_p = current_exports_p; + + for (uint16_t j = 0; j < context_p->module_context_p->exports_p->module_request_count; + ++j, export_iterator_p = export_iterator_p->next_p) + { + if (ecma_compare_ecma_strings (import_name_p->local_name_p, export_iterator_p->import_name_p)) + { + request_is_found_in_module = true; + break; + } + } + + if (!request_is_found_in_module) + { + parser_module_free_saved_names (context_p->module_context_p->exports_p); + throw_error = true; + break; + } + + import_name_p = import_name_p->next_p; + } + + *module_context_p->exports_p = *context_p->module_context_p->exports_p; + parser_free (context_p->module_context_p->exports_p, sizeof (parser_module_node_t)); + + if (throw_error) + { + parser_raise_error (context_p, PARSER_ERR_MODULE_REQUEST_NOT_FOUND); + } +} /* parser_module_handle_requests */ + +/** + * Raises parser error if the import or export statement is not in the global scope. + */ +void +parser_module_check_request_place (parser_context_t *context_p) /**< parser context */ +{ + if (context_p->last_context_p != NULL + || context_p->stack_top_uint8 != 0 + || (JERRY_CONTEXT (status_flags) & ECMA_STATUS_DIRECT_EVAL) != 0) + { + parser_raise_error (context_p, PARSER_ERR_MODULE_UNEXPECTED); + } +} /* parser_module_check_request_place */ + +/** + * Handle from clause at the end of the import / export statement. + */ +void +parser_module_handle_from_clause (parser_context_t *context_p) /**< parser context */ +{ + parser_module_node_t *module_node_p = context_p->module_current_node_p; + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS); + + if (context_p->lit_object.literal_p->prop.length == 0) + { + parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED); + } + + module_node_p->script_path.length = (prop_length_t) (context_p->lit_object.literal_p->prop.length + 1); + module_node_p->script_path.value_p = (uint8_t *) parser_malloc (context_p, + module_node_p->script_path.length * sizeof (uint8_t)); + + memcpy (module_node_p->script_path.value_p, + context_p->lit_object.literal_p->u.char_p, + module_node_p->script_path.length); + module_node_p->script_path.value_p[module_node_p->script_path.length - 1] = '\0'; + + lexer_next_token (context_p); +} /* parser_module_handle_from_clause */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index df5a078ad0..d1f912da00 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -324,6 +324,17 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR; +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + if (context_p->module_context_p != NULL && context_p->module_current_node_p != NULL) + { + parser_module_add_item_to_node (context_p, + context_p->module_current_node_p, + context_p->lit_object.literal_p, + context_p->lit_object.literal_p, + false); + } +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + lexer_next_token (context_p); if (context_p->token.type == LEXER_ASSIGN) @@ -389,6 +400,17 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ name_p = context_p->lit_object.literal_p; +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + if (context_p->module_context_p != NULL && context_p->module_current_node_p != NULL) + { + parser_module_add_item_to_node (context_p, + context_p->module_current_node_p, + name_p, + name_p, + false); + } +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) { @@ -1653,6 +1675,151 @@ parser_parse_continue_statement (parser_context_t *context_p) /**< context */ } } /* parser_parse_continue_statement */ +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +/** + * Parse import statement. + */ +static void +parser_parse_import_statement (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_IMPORT); + + parser_module_check_request_place (context_p); + parser_module_context_init (context_p); + + parser_module_node_t module_node; + memset (&module_node, 0, sizeof (parser_module_node_t)); + context_p->module_current_node_p = &module_node; + + lexer_next_token (context_p); + + switch (context_p->token.type) + { + case LEXER_LEFT_BRACE: + { + lexer_next_token (context_p); + parser_module_parse_import_item_list (context_p); + + if (context_p->token.type != LEXER_RIGHT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + break; + } + + case LEXER_MULTIPLY: + case LEXER_LITERAL: + { + parser_module_parse_import_item_list (context_p); + break; + } + + default: + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_MULTIPLY_LITERAL_EXPECTED); + } + } + + if (context_p->token.type != LEXER_LITERAL ||!lexer_compare_raw_identifier_to_current (context_p, "from", 4)) + { + parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED); + } + + parser_module_handle_from_clause (context_p); + parser_module_add_import_node_to_context (context_p); + + context_p->module_current_node_p = NULL; +} /* parser_parse_import_statement */ + +/** + * Parse export statement. + */ +static void +parser_parse_export_statement (parser_context_t *context_p) /**< context */ +{ + JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXPORT); + + parser_module_check_request_place (context_p); + parser_module_context_init (context_p); + + parser_module_node_t module_node; + memset (&module_node, 0, sizeof (parser_module_node_t)); + context_p->module_current_node_p = &module_node; + + lexer_next_token (context_p); + + switch (context_p->token.type) + { + case LEXER_LEFT_BRACE: + { + lexer_next_token (context_p); + parser_module_parse_export_item_list (context_p); + + if (context_p->token.type != LEXER_RIGHT_BRACE) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); + } + + lexer_next_token (context_p); + break; + } + + case LEXER_KEYW_DEFAULT: + { + /* TODO: This part is going to be implemented in the next part of the patch. */ + parser_raise_error (context_p, PARSER_ERR_NOT_IMPLEMENTED); + break; + } + + case LEXER_MULTIPLY: + case LEXER_LITERAL: + { + parser_module_parse_export_item_list (context_p); + break; + } + + case LEXER_KEYW_FUNCTION: + { + parser_parse_function_statement (context_p); + break; + } + + case LEXER_KEYW_VAR: + { + parser_parse_var_statement (context_p); + break; + } + + case LEXER_KEYW_CLASS: + { + /* TODO: This part is going to be implemented in the next part of the patch. */ + parser_raise_error (context_p, PARSER_ERR_NOT_IMPLEMENTED); + break; + } + + default: + { + parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_MULTIPLY_LITERAL_EXPECTED); + break; + } + } + + if (context_p->token.type == LEXER_LITERAL + && lexer_compare_raw_identifier_to_current (context_p, "from", 4)) + { + /* TODO: Import the requested properties from the given script and export + them from the current to make a redirection. + This part is going to be implemented in the next part of the patch. */ + parser_raise_error (context_p, PARSER_ERR_NOT_IMPLEMENTED); + } + + parser_module_add_export_node_to_context (context_p); + context_p->module_current_node_p = NULL; +} /* parser_parse_export_statement */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * Parse label statement. */ @@ -1898,6 +2065,20 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ } #endif /* !CONFIG_DISABLE_ES2015_CLASS */ +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + case LEXER_KEYW_IMPORT: + { + parser_parse_import_statement (context_p); + break; + } + + case LEXER_KEYW_EXPORT: + { + parser_parse_export_statement (context_p); + break; + } +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + case LEXER_KEYW_FUNCTION: { parser_parse_function_statement (context_p); diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index ca8cc9c385..095eaa6b28 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -1064,6 +1064,46 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Non-strict argument definition."; } + +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + case PARSER_ERR_MODULE_REQUEST_NOT_FOUND: + { + return "Import request not found in the module."; + } + case PARSER_ERR_FILE_NOT_FOUND: + { + return "Requested module not found."; + } + case PARSER_ERR_NOT_IMPLEMENTED: + { + return "Requested feature is not implemented yet."; + } + case PARSER_ERR_LEFT_PAREN_MULTIPLY_LITERAL_EXPECTED: + { + return "Expected '{' or '*' or literal token."; + } + case PARSER_ERR_RIGHT_PAREN_COMMA_FROM_EXPECTED: + { + return "Expected '}' or 'as' or 'from' literal token."; + } + case PARSER_ERR_FROM_EXPECTED: + { + return "Expected 'from' token."; + } + case PARSER_ERR_STRING_EXPECTED: + { + return "Expected a string."; + } + case PARSER_ERR_MODULE_REQUEST_LIMIT_REACHED: + { + return "Maximum module request count reached."; + } + case PARSER_ERR_MODULE_UNEXPECTED: + { + return "Import and export statements must be in the global context."; + } +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + default: { JERRY_ASSERT (error == PARSER_ERR_NO_ERROR); diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 21530c97d8..03e841ed6b 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -19,6 +19,7 @@ #include "ecma-literal-storage.h" #include "jcontext.h" #include "js-parser-internal.h" +#include "ecma-module.h" #ifndef JERRY_DISABLE_JS_PARSER @@ -2360,7 +2361,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ parser_error_location_t *error_location_p) /**< error location */ { parser_context_t context; - ecma_compiled_code_t *compiled_code; + ecma_compiled_code_t *compiled_code_p; context.error = PARSER_ERR_NO_ERROR; context.allocated_buffer_p = NULL; @@ -2397,6 +2398,10 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ context.last_statement.current_p = NULL; context.status_flags |= parse_opts & PARSER_STRICT_MODE_MASK; +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + context.module_context_p = NULL; +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + #ifndef CONFIG_DISABLE_ES2015_CLASS context.status_flags |= PARSER_GET_CLASS_PARSER_OPTS (parse_opts); #endif /* !CONFIG_DISABLE_ES2015_CLASS */ @@ -2474,7 +2479,15 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ JERRY_ASSERT (context.last_cbc_opcode == PARSER_CBC_UNAVAILABLE); JERRY_ASSERT (context.allocated_buffer_p == NULL); - compiled_code = parser_post_processing (&context); +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + if (context.module_context_p != NULL) + { + parser_module_handle_requests (&context); + ecma_module_load_modules (&context); + } +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + + compiled_code_p = parser_post_processing (&context); parser_list_free (&context.literal_pool); #ifdef PARSER_DUMP_BYTE_CODE @@ -2507,7 +2520,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ error_location_p->column = context.token.column; } - compiled_code = NULL; + compiled_code_p = NULL; parser_free_literals (&context.literal_pool); parser_cbc_stream_free (&context.byte_code); } @@ -2522,9 +2535,12 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ } #endif /* PARSER_DUMP_BYTE_CODE */ +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + parser_module_context_cleanup (&context); +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ parser_stack_free (&context); - return compiled_code; + return compiled_code_p; } /* parser_parse_source */ /** @@ -2850,6 +2866,13 @@ void parser_raise_error (parser_context_t *context_p, /**< context */ parser_error_t error) /**< error code */ { +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + if (context_p->module_context_p != NULL) + { + parser_module_free_saved_names (context_p->module_current_node_p); + } +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + parser_saved_context_t *saved_context_p = context_p->last_context_p; while (saved_context_p != NULL) diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index 05496ba052..9a5059a989 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -90,6 +90,7 @@ typedef enum PARSER_ERR_LEFT_BRACE_EXPECTED, /**< left brace expected */ PARSER_ERR_RIGHT_PAREN_EXPECTED, /**< right paren expected */ PARSER_ERR_RIGHT_SQUARE_EXPECTED, /**< right square expected */ + #ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS PARSER_ERR_RIGHT_BRACE_EXPECTED, /**< right brace expected */ #endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ @@ -129,7 +130,20 @@ typedef enum PARSER_ERR_REST_PARAMETER_DEFAULT_INITIALIZER, /**< rest parameter default initializer */ #endif /* !CONFIG_DISABLE_ES2015_FUNCTION_REST_PARAMETER */ PARSER_ERR_OBJECT_PROPERTY_REDEFINED, /**< property of object literal redefined */ - PARSER_ERR_NON_STRICT_ARG_DEFINITION /**< non-strict argument definition */ + +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM + PARSER_ERR_RIGHT_PAREN_COMMA_FROM_EXPECTED, /**< rigth paren or comma or from expected */ + PARSER_ERR_FROM_EXPECTED, /**< from expected */ + PARSER_ERR_STRING_EXPECTED, /**< string literal expected */ + PARSER_ERR_MODULE_REQUEST_NOT_FOUND, /**< not found imported variable in module */ + PARSER_ERR_FILE_NOT_FOUND, /**< not found given file in the import statement */ + PARSER_ERR_NOT_IMPLEMENTED, /**< feature not implemented yet */ + PARSER_ERR_MODULE_REQUEST_LIMIT_REACHED, /**< maximum number of requests reached */ + PARSER_ERR_MODULE_UNEXPECTED, /**< unexpected import or export statement */ + PARSER_ERR_LEFT_PAREN_MULTIPLY_LITERAL_EXPECTED, /**< left paren or multiply or literal expected */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + + PARSER_ERR_NON_STRICT_ARG_DEFINITION /**< non-strict argument definition */ } parser_error_t; /** diff --git a/jerry-core/profiles/README.md b/jerry-core/profiles/README.md index 8130dffee5..0d39ff52b2 100644 --- a/jerry-core/profiles/README.md +++ b/jerry-core/profiles/README.md @@ -36,6 +36,7 @@ CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER CONFIG_DISABLE_ES2015_FUNCTION_REST_PARAMETER CONFIG_DISABLE_ES2015_ITERATOR_BUILTIN CONFIG_DISABLE_ES2015_MAP_BUILTIN +CONFIG_DISABLE_ES2015_MODULE_SYSTEM CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER CONFIG_DISABLE_ES2015_PROMISE_BUILTIN CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN @@ -111,8 +112,9 @@ In JerryScript all of the features are enabled by default, so an empty profile f Disable the [template strings](http://www.ecma-international.org/ecma-262/6.0/#sec-static-semantics-templatestrings). * `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`: Disable the [ArrayBuffer](http://www.ecma-international.org/ecma-262/6.0/#sec-arraybuffer-objects) and [TypedArray](http://www.ecma-international.org/ecma-262/6.0/#sec-typedarray-objects) built-ins. +* `CONFIG_DISABLE_ES2015_MODULE_SYSTEM`: Disable the [module system](http://www.ecma-international.org/ecma-262/6.0/#sec-modules) language element. * `CONFIG_DISABLE_ES2015`: Disable all of the implemented [ECMAScript2015 features](http://www.ecma-international.org/ecma-262/6.0/). (equivalent to : `CONFIG_DISABLE_ES2015_ARROW_FUNCTION`, `CONFIG_DISABLE_ES2015_BUILTIN`, `CONFIG_DISABLE_ES2015_CLASS`, `CONFIG_DISABLE_ES2015_FUNCTION_PARAMETER_INITIALIZER`, `CONFIG_DISABLE_ES2015_FUNCTION_REST_PARAMETER`, - `CONFIG_DISABLE_ES2015_ITERATOR_BUILTIN`,`CONFIG_DISABLE_ES2015_MAP_BUILTIN`, `CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER`, + `CONFIG_DISABLE_ES2015_ITERATOR_BUILTIN`,`CONFIG_DISABLE_ES2015_MAP_BUILTIN`, `CONFIG_DISABLE_ES2015_MODULE_SYSTEM`, `CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER`, `CONFIG_DISABLE_ES2015_SYMBOL_BUILTIN`, `CONFIG_DISABLE_ES2015_PROMISE_BUILTIN`, `CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS`, and `CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN`). diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 40c77adf5f..7746b5a8ba 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -226,6 +226,37 @@ static const uint16_t vm_decode_table[] JERRY_CONST_DATA = #undef CBC_OPCODE +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +/** + * Run ES2015 module code + * + * Note: + * returned value must be freed with ecma_free_value, when it is no longer needed. + * + * @return ecma value + */ +ecma_value_t +vm_run_module (const ecma_compiled_code_t *bytecode_p, /**< pointer to bytecode to run */ + ecma_object_t *lex_env_p) /**< pointer to the specified lexenv to run in */ +{ + ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); + + ecma_value_t ret_value = vm_run (bytecode_p, + ecma_make_object_value (glob_obj_p), + lex_env_p, + false, + NULL, + 0); + + if (ECMA_IS_VALUE_ERROR (ret_value)) + { + ret_value = ecma_create_error_reference_from_context (); + } + + return ret_value; +} /* vm_run_module */ +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + /** * Run global code * diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index c7c98fbeb9..47c9ae5ba3 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -355,6 +355,10 @@ typedef enum ecma_value_t vm_run_global (const ecma_compiled_code_t *bytecode_p); ecma_value_t vm_run_eval (ecma_compiled_code_t *bytecode_data_p, uint32_t parse_opts); +#ifndef CONFIG_DISABLE_ES2015_MODULE_SYSTEM +ecma_value_t vm_run_module (const ecma_compiled_code_t *bytecode_p, ecma_object_t *lex_env_p); +#endif /* !CONFIG_DISABLE_ES2015_MODULE_SYSTEM */ + ecma_value_t vm_run (const ecma_compiled_code_t *bytecode_header_p, ecma_value_t this_binding_value, ecma_object_t *lex_env_p, uint32_t parse_opts, const ecma_value_t *arg_list_p, ecma_length_t arg_list_len); diff --git a/jerry-port/default/default-io.c b/jerry-port/default/default-io.c index 2230a13a76..5719a3455e 100644 --- a/jerry-port/default/default-io.c +++ b/jerry-port/default/default-io.c @@ -14,6 +14,7 @@ */ #include +#include #include "jerryscript-port.h" #include "jerryscript-port-default.h" @@ -99,7 +100,6 @@ jerry_port_log (jerry_log_level_t level, /**< message log level */ } } /* jerry_port_log */ - #ifdef JERRY_DEBUGGER #define DEBUG_BUFFER_SIZE (256) @@ -127,3 +127,70 @@ jerry_port_print_char (char c) /**< the character to print */ } #endif /* JERRY_DEBUGGER */ } /* jerry_port_print_char */ + +/** + * Determines the size of the given file. + * @return size of the file + */ +static size_t +jerry_port_get_file_size (FILE *file_p) /**< opened file */ +{ + fseek (file_p, 0, SEEK_END); + long size = ftell (file_p); + fseek (file_p, 0, SEEK_SET); + + return (size_t) size; +} /* jerry_port_get_file_size */ + +/** + * Opens file with the given path and reads its source. + * @return the source of the file + */ +uint8_t * +jerry_port_read_source (const char *file_name_p, /**< file name */ + size_t *out_size_p) /**< [out] read bytes */ +{ + FILE *file_p = fopen (file_name_p, "rb"); + + if (file_p == NULL) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name_p); + return NULL; + } + + size_t file_size = jerry_port_get_file_size (file_p); + uint8_t *buffer_p = (uint8_t *) malloc (file_size); + + if (buffer_p == NULL) + { + fclose (file_p); + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to allocate memory for module"); + return NULL; + } + + size_t bytes_read = fread (buffer_p, 1u, file_size, file_p); + + if (!bytes_read) + { + fclose (file_p); + free (buffer_p); + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name_p); + return NULL; + } + + fclose (file_p); + *out_size_p = bytes_read; + + return buffer_p; +} /* jerry_port_read_source */ + +/** + * Release the previously opened file's content. + */ +void +jerry_port_release_source (uint8_t *buffer_p) /**< buffer to free */ +{ + free (buffer_p); +} /* jerry_port_release_source */ diff --git a/targets/nuttx-stm32f4/jerry_main.c b/targets/nuttx-stm32f4/jerry_main.c index e32a747279..1b264ae9ac 100644 --- a/targets/nuttx-stm32f4/jerry_main.c +++ b/targets/nuttx-stm32f4/jerry_main.c @@ -491,6 +491,73 @@ jerry_port_log (jerry_log_level_t level, /**< log level */ } } /* jerry_port_log */ +/** + * Determines the size of the given file. + * @return size of the file + */ +static size_t +jerry_port_get_file_size (FILE *file_p) /**< opened file */ +{ + fseek (file_p, 0, SEEK_END); + long size = ftell (file_p); + fseek (file_p, 0, SEEK_SET); + + return (size_t) size; +} /* jerry_port_get_file_size */ + +/** + * Opens file with the given path and reads its source. + * @return the source of the file + */ +uint8_t * +jerry_port_read_source (const char *file_name_p, /**< file name */ + size_t *out_size_p) /**< [out] read bytes */ +{ + FILE *file_p = fopen (file_name_p, "rb"); + + if (file_p == NULL) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to open file: %s\n", file_name_p); + return NULL; + } + + size_t file_size = jerry_port_get_file_size (file_p); + uint8_t *buffer_p = (uint8_t *) malloc (file_size); + + if (buffer_p == NULL) + { + fclose (file_p); + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to allocate memory for module"); + return NULL; + } + + size_t bytes_read = fread (buffer_p, 1u, file_size, file_p); + + if (!bytes_read) + { + fclose (file_p); + free (buffer_p); + + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: failed to read file: %s\n", file_name_p); + return NULL; + } + + fclose (file_p); + *out_size_p = bytes_read; + + return buffer_p; +} /* jerry_port_read_source */ + +/** + * Release the previously opened file's content. + */ +void +jerry_port_release_source (uint8_t *buffer_p) /**< buffer to free */ +{ + free (buffer_p); +} /* jerry_port_release_source */ + /** * Dummy function to get the time zone adjustment. * diff --git a/tests/jerry/es2015/module-imported-2.js b/tests/jerry/es2015/module-imported-2.js new file mode 100644 index 0000000000..6c0258ad3b --- /dev/null +++ b/tests/jerry/es2015/module-imported-2.js @@ -0,0 +1,31 @@ +/* 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 function getString (prefix) { + return prefix + "String"; +} + +function getAreaOfCircle (radius) { + return radius * radius * pi; +} + +export getAreaOfCircle as getArea; + +import { pi } from "tests/jerry/es2015/module-imported-3.js"; +assert (pi === 3.14); + +export pi as b; + +assert (true); diff --git a/tests/jerry/es2015/module-imported-3.js b/tests/jerry/es2015/module-imported-3.js new file mode 100644 index 0000000000..3343e10c89 --- /dev/null +++ b/tests/jerry/es2015/module-imported-3.js @@ -0,0 +1,18 @@ +/* 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 var pi = 3.14; + +assert (true); diff --git a/tests/jerry/es2015/module-imported.js b/tests/jerry/es2015/module-imported.js new file mode 100644 index 0000000000..4720855fb8 --- /dev/null +++ b/tests/jerry/es2015/module-imported.js @@ -0,0 +1,31 @@ +/* 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. + */ + +var a = 1; + +function b () { + return 2; +} + +export var c = 3; + +export function d () { + return 4; +} + +export { a as e, b } +export var f = "str"; + +assert (true); diff --git a/tests/jerry/es2015/module.js b/tests/jerry/es2015/module.js new file mode 100644 index 0000000000..be6e4a5387 --- /dev/null +++ b/tests/jerry/es2015/module.js @@ -0,0 +1,35 @@ +/* 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 { b, c, d, e, f as g } from "tests/jerry/es2015/module-imported.js" + +import +{ + b as pi, + getString, + getArea +} from "tests/jerry/es2015/module-imported-2.js" + +var str = "str"; + +assert (b () === 2); +assert (c === 3); +assert (d () === 4); +assert (e === 1); +assert (g === str); + +assert (pi === 3.14); +assert (getArea (2) == 12.56); +assert (getString (str) === "strString") diff --git a/tests/jerry/fail/module-001.js b/tests/jerry/fail/module-001.js new file mode 100644 index 0000000000..21f09a4fdd --- /dev/null +++ b/tests/jerry/fail/module-001.js @@ -0,0 +1,17 @@ +/* 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. + */ + +// File does not exist. +import b from "tests/jerry/fail/module-exports.js" diff --git a/tests/jerry/fail/module-002.js b/tests/jerry/fail/module-002.js new file mode 100644 index 0000000000..7ff9db72c0 --- /dev/null +++ b/tests/jerry/fail/module-002.js @@ -0,0 +1,16 @@ +/* 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 { , as b } from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-003.js b/tests/jerry/fail/module-003.js new file mode 100644 index 0000000000..843b09cfbb --- /dev/null +++ b/tests/jerry/fail/module-003.js @@ -0,0 +1,16 @@ +/* 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 , as b from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-004.js b/tests/jerry/fail/module-004.js new file mode 100644 index 0000000000..3a6f29b6a0 --- /dev/null +++ b/tests/jerry/fail/module-004.js @@ -0,0 +1,16 @@ +/* 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 { b as , } from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-005.js b/tests/jerry/fail/module-005.js new file mode 100644 index 0000000000..f76f57cfbc --- /dev/null +++ b/tests/jerry/fail/module-005.js @@ -0,0 +1,16 @@ +/* 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 b as , from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-006.js b/tests/jerry/fail/module-006.js new file mode 100644 index 0000000000..e02d2affa7 --- /dev/null +++ b/tests/jerry/fail/module-006.js @@ -0,0 +1,16 @@ +/* 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 { as as as } from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-007.js b/tests/jerry/fail/module-007.js new file mode 100644 index 0000000000..75ee37891f --- /dev/null +++ b/tests/jerry/fail/module-007.js @@ -0,0 +1,16 @@ +/* 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 { from as as } from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-008.js b/tests/jerry/fail/module-008.js new file mode 100644 index 0000000000..c09e83e842 --- /dev/null +++ b/tests/jerry/fail/module-008.js @@ -0,0 +1,16 @@ +/* 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 { b } from diff --git a/tests/jerry/fail/module-009.js b/tests/jerry/fail/module-009.js new file mode 100644 index 0000000000..96fffefabf --- /dev/null +++ b/tests/jerry/fail/module-009.js @@ -0,0 +1,16 @@ +/* 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 { b } from 3 diff --git a/tests/jerry/fail/module-010.js b/tests/jerry/fail/module-010.js new file mode 100644 index 0000000000..ddeb94b953 --- /dev/null +++ b/tests/jerry/fail/module-010.js @@ -0,0 +1,16 @@ +/* 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 { c as a, d as a } from "tests/jerry/es2015/module-imported.js"; diff --git a/tests/jerry/fail/module-011.js b/tests/jerry/fail/module-011.js new file mode 100644 index 0000000000..17e7712683 --- /dev/null +++ b/tests/jerry/fail/module-011.js @@ -0,0 +1,16 @@ +/* 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 diff --git a/tests/jerry/fail/module-012.js b/tests/jerry/fail/module-012.js new file mode 100644 index 0000000000..d9be846df8 --- /dev/null +++ b/tests/jerry/fail/module-012.js @@ -0,0 +1,16 @@ +/* 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 diff --git a/tests/jerry/fail/module-013.js b/tests/jerry/fail/module-013.js new file mode 100644 index 0000000000..50234de44f --- /dev/null +++ b/tests/jerry/fail/module-013.js @@ -0,0 +1,17 @@ +/* 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 var a = 4; +export 3 diff --git a/tests/jerry/fail/module-014.js b/tests/jerry/fail/module-014.js new file mode 100644 index 0000000000..85d2b6d351 --- /dev/null +++ b/tests/jerry/fail/module-014.js @@ -0,0 +1,18 @@ +/* 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. + */ + +if (true) { + import { c } from "tests/jerry/es2015/module-imported.js"; +} diff --git a/tests/jerry/fail/module-015.js b/tests/jerry/fail/module-015.js new file mode 100644 index 0000000000..116161459a --- /dev/null +++ b/tests/jerry/fail/module-015.js @@ -0,0 +1,18 @@ +/* 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. + */ + +function someFunction() { + import { c } from "tests/jerry/es2015/module-imported.js"; +} diff --git a/tests/jerry/fail/module-016.js b/tests/jerry/fail/module-016.js new file mode 100644 index 0000000000..a363fef319 --- /dev/null +++ b/tests/jerry/fail/module-016.js @@ -0,0 +1,16 @@ +/* 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. + */ + +eval ("import { c } from 'tests/jerry/es2015/module-imported.js';"); diff --git a/tests/jerry/fail/module-017.js b/tests/jerry/fail/module-017.js new file mode 100644 index 0000000000..5a5e0f87d4 --- /dev/null +++ b/tests/jerry/fail/module-017.js @@ -0,0 +1,17 @@ +/* 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. + */ + +// File does not exist. +import { a } from "tests/jerry/fail/module-exports.js" diff --git a/tests/jerry/fail/module-018.js b/tests/jerry/fail/module-018.js new file mode 100644 index 0000000000..b41b095807 --- /dev/null +++ b/tests/jerry/fail/module-018.js @@ -0,0 +1,20 @@ +/* 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 function getString () { + return prefix; +} + +assert (getString()); diff --git a/tests/jerry/fail/module-019.js b/tests/jerry/fail/module-019.js new file mode 100644 index 0000000000..f314355755 --- /dev/null +++ b/tests/jerry/fail/module-019.js @@ -0,0 +1,16 @@ +/* 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 { c }