From fb6e1fe67dfb0bc942c7b97f0393d48c9ab5e346 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 12 Apr 2021 07:06:26 +0000 Subject: [PATCH] Support native modules JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 219 ++++++++++++++++- jerry-core/api/jerry.c | 221 +++++++++++++++++- jerry-core/ecma/base/ecma-errors.c | 5 + jerry-core/ecma/base/ecma-errors.h | 1 + jerry-core/ecma/base/ecma-globals.h | 3 + jerry-core/ecma/base/ecma-helpers-string.c | 12 +- jerry-core/ecma/base/ecma-module.c | 110 ++++----- jerry-core/ecma/base/ecma-module.h | 22 +- .../builtin-objects/ecma-builtin-global.c | 4 +- jerry-core/include/jerryscript-core.h | 8 + jerry-core/include/jerryscript-types.h | 6 +- jerry-core/lit/lit-strings.c | 69 ++++-- jerry-core/lit/lit-strings.h | 6 +- jerry-core/vm/vm.c | 6 +- tests/unit-core/test-module.c | 178 ++++++++++++++ 15 files changed, 770 insertions(+), 100 deletions(-) diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index b567814e74..e9b2fcf932 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -786,7 +786,6 @@ An enum representing the current status of a module - JERRY_MODULE_STATE_EVALUATING - Module is currently being evaluated - JERRY_MODULE_STATE_EVALUATED - Module has been evaluated (its source code has been executed) - JERRY_MODULE_STATE_ERROR - An error has been encountered before the evaluated state is reached - - JERRY_MODULE_STATE_NATIVE - Module is native module *New in version [[NEXT_RELEASE]]*. @@ -821,6 +820,32 @@ typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t sp - [jerry_module_link](#jerry_module_link) - [jerry_get_global_object](#jerry_get_global_object) +## jerry_native_module_evaluate_callback_t + +**Summary** + +Callback which is called by [jerry_module_evaluate](#jerry_module_evaluate) to evaluate the native module. + +Note: + - Native pointers can be used to assign private data to a native module, + see [jerry_set_object_native_pointer](#jerry_set_object_native_pointer) + +**Prototype** + +```c +typedef jerry_value_t (*jerry_native_module_evaluate_callback_t) (const jerry_value_t native_module); +``` + +- `native_module` - a native module +- return value + - any non-error value - if the module is evaluated successfully + - an error - otherwise + +*New in version [[NEXT_RELEASE]]*. + +**See also** +- [jerry_module_evaluate](#jerry_module_evaluate) + ## jerry_backtrace_callback_t **Summary** @@ -4743,6 +4768,198 @@ main (void) - [jerry_module_link](#jerry_module_link) - [jerry_module_evaluate](#jerry_module_evaluate) +## jerry_native_module_create + +Creates a native module with a list of exports. The initial state of the module is linked. + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. +- Native pointers can be used to assign private data to a native module, + see [jerry_set_object_native_pointer](#jerry_set_object_native_pointer) +- When `callback` is `NULL`, no function is called when the module is evaluated, + only its state is changed to evaluated. +- This API depends on a build option (`JERRY_MODULE_SYSTEM`) and can be checked + in runtime with the `JERRY_FEATURE_MODULE` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). + +**Prototype** + +```c +jerry_value_t +jerry_native_module_create (jerry_native_module_evaluate_callback_t callback, + const jerry_value_t * const exports_p, + size_t number_of_exports); +``` + +- `callback` - a [jerry_native_module_evaluate_callback_t](#jerry_native_module_evaluate_callback_t) callback + which is called by [jerry_module_evaluate](#jerry_module_evaluate) to evaluate the native module. +- `exports_p` - list of the exported bindings of the module, must be valid string identifiers. +- `number_of_exports` - number of exports in the `exports_p` list. +- return + - a native module, if the module is successfully created + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t exports[2] = + { + jerry_create_string ((const jerry_char_t *) "first_export"), + jerry_create_string ((const jerry_char_t *) "second_export") + }; + + jerry_value_t native_module = jerry_native_module_create (NULL, exports, 2); + + jerry_release_value (exports[0]); + jerry_release_value (exports[1]); + jerry_release_value (native_module); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_module_link](#jerry_module_link) +- [jerry_module_evaluate](#jerry_module_evaluate) +- [jerry_native_module_get_export](#jerry_native_module_get_export) +- [jerry_native_module_set_export](#jerry_native_module_set_export) + +## jerry_native_module_get_export + +Gets the value of an export binding which belongs to a native module. + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. +- This API depends on a build option (`JERRY_MODULE_SYSTEM`) and can be checked + in runtime with the `JERRY_FEATURE_MODULE` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). + +**Prototype** + +```c +jerry_value_t +jerry_native_module_get_export (const jerry_value_t native_module_val, + const jerry_value_t export_name_val); +``` + +- `module_val` - a native module object. +- `export_name_val` - string identifier of the export. +- return + - value of the export, if success + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t export = jerry_create_string ((const jerry_char_t *) "an_export"); + + jerry_value_t native_module = jerry_native_module_create (NULL, &export, 1); + + jerry_value_t value = jerry_native_module_get_export (native_module, export); + jerry_release_value (value); + + jerry_release_value (export); + jerry_release_value (native_module); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_native_module_create](#jerry_native_module_create) +- [jerry_native_module_set_export](#jerry_native_module_set_export) + +## jerry_native_module_set_export + +Sets the value of an export binding which belongs to a native module. + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. +- This API depends on a build option (`JERRY_MODULE_SYSTEM`) and can be checked + in runtime with the `JERRY_FEATURE_MODULE` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). + +**Prototype** + +```c +jerry_value_t +jerry_value_t jerry_native_module_set_export (const jerry_value_t native_mmodule_val, + const jerry_value_t export_name_val, + const jerry_value_t value_to_set) +``` + +- `module_val` - a native module object. +- `export_name_val` - string identifier of the export. +- `value_to_set` - new value of the export. +- return + - true, if success + - error, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t export = jerry_create_string ((const jerry_char_t *) "an_export"); + + jerry_value_t native_module = jerry_native_module_create (NULL, &export, 1); + + jerry_value_t number = jerry_create_number (3.5); + jerry_value_t value = jerry_native_module_set_export (native_module, export, number); + jerry_release_value (value); + jerry_release_value (number); + + jerry_release_value (export); + jerry_release_value (native_module); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_native_module_create](#jerry_native_module_create) +- [jerry_native_module_get_export](#jerry_native_module_get_export) + # Functions for promise objects These APIs all depend on the es.next profile (or on some build options). diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 717fc1f60e..6a2e01fa23 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -445,7 +445,7 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */ if ((parse_opts & JERRY_PARSE_MODULE) != 0) { #if JERRY_MODULE_SYSTEM - ecma_module_initialize_context (); + JERRY_CONTEXT (module_current_p) = ecma_module_create (); #else /* !JERRY_MODULE_SYSTEM */ return jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG (ecma_error_module_not_supported_p))); #endif /* JERRY_MODULE_SYSTEM */ @@ -474,7 +474,7 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */ if (JERRY_UNLIKELY (parse_opts & JERRY_PARSE_MODULE)) { ecma_module_t *module_p = JERRY_CONTEXT (module_current_p); - module_p->compiled_code_p = bytecode_data_p; + module_p->u.compiled_code_p = bytecode_data_p; JERRY_CONTEXT (module_current_p) = NULL; @@ -844,7 +844,7 @@ jerry_module_get_request (const jerry_value_t module_val, /**< module */ * returned value must be freed with jerry_release_value, when it is no longer needed. * * @return object - if namespace object is available, - * error - otherwise + * error - otherwise */ jerry_value_t jerry_module_get_namespace (const jerry_value_t module_val) /**< module */ @@ -879,6 +879,221 @@ jerry_module_get_namespace (const jerry_value_t module_val) /**< module */ #endif /* JERRY_MODULE_SYSTEM */ } /* jerry_module_get_namespace */ +/** + * Creates a native module with a list of exports. The initial state of the module is linked. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return native module - if the module is successfully created, + * error - otherwise + */ +jerry_value_t +jerry_native_module_create (jerry_native_module_evaluate_callback_t callback, /**< evaluation callback for + * native modules */ + const jerry_value_t * const exports_p, /**< list of the exported bindings of the module, + * must be valid string identifiers */ + size_t number_of_exports) /**< number of exports in the exports_p list */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_object_t *global_object_p = ecma_builtin_get_global (); + ecma_object_t *scope_p = ecma_create_decl_lex_env (ecma_get_global_environment (global_object_p)); + ecma_module_names_t *local_exports_p = NULL; + + for (size_t i = 0; i < number_of_exports; i++) + { + if (!ecma_is_value_string (exports_p[i])) + { + ecma_deref_object (scope_p); + ecma_module_release_module_names (local_exports_p); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Module exports must be string values"))); + } + + ecma_string_t *name_str_p = ecma_get_string_from_value (exports_p[i]); + + bool valid_identifier = false; + + ECMA_STRING_TO_UTF8_STRING (name_str_p, name_start_p, name_size); + + if (name_size > 0) + { + const lit_utf8_byte_t *name_p = name_start_p; + const lit_utf8_byte_t *name_end_p = name_start_p + name_size; + lit_code_point_t code_point; + + lit_utf8_size_t size = lit_read_code_point_from_cesu8 (name_p, name_end_p, &code_point); + + if (lit_code_point_is_identifier_start (code_point)) + { + name_p += size; + + valid_identifier = true; + + while (name_p < name_end_p) + { + size = lit_read_code_point_from_cesu8 (name_p, name_end_p, &code_point); + + if (!lit_code_point_is_identifier_part (code_point)) + { + valid_identifier = false; + break; + } + + name_p += size; + } + } + } + + ECMA_FINALIZE_UTF8_STRING (name_start_p, name_size); + + if (!valid_identifier) + { + ecma_deref_object (scope_p); + ecma_module_release_module_names (local_exports_p); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Module exports must be valid identifiers"))); + } + + if (ecma_find_named_property (scope_p, name_str_p) != NULL) + { + continue; + } + + ecma_create_named_data_property (scope_p, name_str_p, ECMA_PROPERTY_FLAG_WRITABLE, NULL); + + ecma_module_names_t *new_export_p; + new_export_p = (ecma_module_names_t *) jmem_heap_alloc_block (sizeof (ecma_module_names_t)); + + new_export_p->next_p = local_exports_p; + local_exports_p = new_export_p; + + ecma_ref_ecma_string (name_str_p); + new_export_p->imex_name_p = name_str_p; + + ecma_ref_ecma_string (name_str_p); + new_export_p->local_name_p = name_str_p; + } + + ecma_module_t *module_p = ecma_module_create (); + + module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_LINKED; + module_p->header.u.cls.u2.module_flags |= ECMA_MODULE_IS_NATIVE; + module_p->scope_p = scope_p; + module_p->local_exports_p = local_exports_p; + module_p->u.callback = callback; + + ecma_deref_object (scope_p); + + return ecma_make_object_value (&module_p->header.object); + +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (callback); + JERRY_UNUSED (exports_p); + JERRY_UNUSED (number_of_exports); + + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_module_not_supported_p))); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_native_module_create */ + +/** + * Gets the value of an export which belongs to a native module. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return value of the export - if success + * error - otherwise + */ +jerry_value_t +jerry_native_module_get_export (const jerry_value_t native_module_val, /**< a native module object */ + const jerry_value_t export_name_val) /**< string identifier of the export */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (native_module_val); + + if (module_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_not_module_p))); + } + + if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) + || !ecma_is_value_string (export_name_val)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_wrong_args_msg_p))); + } + + ecma_property_t *property_p = ecma_find_named_property (module_p->scope_p, + ecma_get_string_from_value (export_name_val)); + + if (property_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_unknown_export_p))); + } + + return ecma_copy_value (ECMA_PROPERTY_VALUE_PTR (property_p)->value); +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (native_module_val); + JERRY_UNUSED (export_name_val); + + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_module_not_supported_p))); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_native_module_get_export */ + +/** + * Sets the value of an export which belongs to a native module. + * + * Note: + * returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return true value - if the operation was successful + * error - otherwise + */ +jerry_value_t +jerry_native_module_set_export (const jerry_value_t native_module_val, /**< a native module object */ + const jerry_value_t export_name_val, /**< string identifier of the export */ + const jerry_value_t value_to_set) /**< new value of the export */ +{ + jerry_assert_api_available (); + +#if JERRY_MODULE_SYSTEM + ecma_module_t *module_p = ecma_module_get_resolved_module (native_module_val); + + if (module_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_not_module_p))); + } + + if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) + || !ecma_is_value_string (export_name_val) + || ecma_is_value_error_reference (value_to_set)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_wrong_args_msg_p))); + } + + ecma_property_t *property_p = ecma_find_named_property (module_p->scope_p, + ecma_get_string_from_value (export_name_val)); + + if (property_p == NULL) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_unknown_export_p))); + } + + ecma_named_data_property_assign_value (module_p->scope_p, + ECMA_PROPERTY_VALUE_PTR (property_p), + value_to_set); + return ECMA_VALUE_TRUE; +#else /* !JERRY_MODULE_SYSTEM */ + JERRY_UNUSED (native_module_val); + JERRY_UNUSED (export_name_val); + JERRY_UNUSED (value_to_set); + + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_module_not_supported_p))); +#endif /* JERRY_MODULE_SYSTEM */ +} /* jerry_native_module_set_export */ + /** * Run enqueued Promise jobs until the first thrown error or until all get executed. * diff --git a/jerry-core/ecma/base/ecma-errors.c b/jerry-core/ecma/base/ecma-errors.c index 1426b9b4a8..925979a140 100644 --- a/jerry-core/ecma/base/ecma-errors.c +++ b/jerry-core/ecma/base/ecma-errors.c @@ -81,6 +81,11 @@ const char * const ecma_error_bigint_not_supported_p = "BigInt support is disabl * Error message, if argument is not a module */ const char * const ecma_error_not_module_p = "Argument is not a module"; + +/** + * Error message, if a native module export is not found + */ +const char * const ecma_error_unknown_export_p = "Native module export not found"; #else /* !JERRY_MODULE_SYSTEM */ /** * Error message, if Module support is disabled diff --git a/jerry-core/ecma/base/ecma-errors.h b/jerry-core/ecma/base/ecma-errors.h index ddcfc00470..4301abfe62 100644 --- a/jerry-core/ecma/base/ecma-errors.h +++ b/jerry-core/ecma/base/ecma-errors.h @@ -53,6 +53,7 @@ extern const char * const ecma_error_bigint_not_supported_p; #if JERRY_MODULE_SYSTEM extern const char * const ecma_error_not_module_p; +extern const char * const ecma_error_unknown_export_p; #else /* !JERRY_MODULE_SYSTEM */ extern const char * const ecma_error_module_not_supported_p; #endif /* JERRY_MODULE_SYSTEM */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 625a321cd8..f338b4f280 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1059,6 +1059,9 @@ typedef struct union { uint16_t formal_params_number; /**< for arguments: formal parameters number */ +#if JERRY_MODULE_SYSTEM + uint8_t module_flags; /**< Module flags */ +#endif /* JERRY_MODULE_SYSTEM */ #if JERRY_ESNEXT uint16_t iterator_index; /**< for %Iterator%: [[%Iterator%NextIndex]] property */ uint16_t executable_obj_flags; /**< executable object flags */ diff --git a/jerry-core/ecma/base/ecma-helpers-string.c b/jerry-core/ecma/base/ecma-helpers-string.c index 94cada2942..1d123a2460 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.c +++ b/jerry-core/ecma/base/ecma-helpers-string.c @@ -1313,13 +1313,13 @@ ecma_substring_copy_to_utf8_buffer (const ecma_string_t *string_desc_p, /**< ecm while (start_pos--) { ecma_char_t ch; - lit_utf8_size_t code_unit_size = lit_read_code_unit_from_utf8 (cesu8_str_p, &ch); + lit_utf8_size_t code_unit_size = lit_read_code_unit_from_cesu8 (cesu8_str_p, &ch); cesu8_str_p += code_unit_size; if ((cesu8_str_p != cesu8_end_pos) && lit_is_code_point_utf16_high_surrogate (ch)) { ecma_char_t next_ch; - lit_utf8_size_t next_ch_size = lit_read_code_unit_from_utf8 (cesu8_str_p, &next_ch); + lit_utf8_size_t next_ch_size = lit_read_code_unit_from_cesu8 (cesu8_str_p, &next_ch); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { JERRY_ASSERT (code_unit_size == next_ch_size); @@ -1336,7 +1336,7 @@ ecma_substring_copy_to_utf8_buffer (const ecma_string_t *string_desc_p, /**< ecm while (end_pos--) { ecma_char_t ch; - lit_utf8_size_t code_unit_size = lit_read_code_unit_from_utf8 (cesu8_pos, &ch); + lit_utf8_size_t code_unit_size = lit_read_code_unit_from_cesu8 (cesu8_pos, &ch); if ((size + code_unit_size) > buffer_size) { @@ -1346,7 +1346,7 @@ ecma_substring_copy_to_utf8_buffer (const ecma_string_t *string_desc_p, /**< ecm if (((cesu8_pos + code_unit_size) != cesu8_end_pos) && lit_is_code_point_utf16_high_surrogate (ch)) { ecma_char_t next_ch; - lit_utf8_size_t next_ch_size = lit_read_code_unit_from_utf8 (cesu8_pos + code_unit_size, &next_ch); + lit_utf8_size_t next_ch_size = lit_read_code_unit_from_cesu8 (cesu8_pos + code_unit_size, &next_ch); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { @@ -2482,7 +2482,7 @@ ecma_string_trim_front (const lit_utf8_byte_t *start_p, /**< current string's st while (start_p < end_p) { - lit_utf8_size_t read_size = lit_read_code_unit_from_utf8 (start_p, &ch); + lit_utf8_size_t read_size = lit_read_code_unit_from_cesu8 (start_p, &ch); if (!lit_char_is_white_space (ch)) { @@ -2655,7 +2655,7 @@ ecma_string_pad (ecma_value_t original_string_p, /**< Input ecma string */ while (remaining > 0) { ecma_char_t ch; - lit_utf8_size_t read_size = lit_read_code_unit_from_utf8 (temp_start_p, &ch); + lit_utf8_size_t read_size = lit_read_code_unit_from_cesu8 (temp_start_p, &ch); ecma_stringbuilder_append_char (&builder, ch); temp_start_p += read_size; remaining--; diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index cd4b8dba7e..630c69bf42 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -30,9 +30,11 @@ /** * Initialize context variables for the root module. + * + * @return new module */ -void -ecma_module_initialize_context (void) +ecma_module_t * +ecma_module_create (void) { JERRY_ASSERT (JERRY_CONTEXT (module_current_p) == NULL); @@ -41,19 +43,20 @@ ecma_module_initialize_context (void) ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_MODULE; ext_object_p->u.cls.u1.module_state = JERRY_MODULE_STATE_UNLINKED; + ext_object_p->u.cls.u2.module_flags = 0; ecma_module_t *module_p = (ecma_module_t *) obj_p; + module_p->scope_p = NULL; + module_p->namespace_object_p = NULL; module_p->imports_p = NULL; module_p->local_exports_p = NULL; module_p->indirect_exports_p = NULL; module_p->star_exports_p = NULL; - module_p->compiled_code_p = NULL; - module_p->scope_p = NULL; - module_p->namespace_object_p = NULL; + module_p->u.compiled_code_p = NULL; - JERRY_CONTEXT (module_current_p) = module_p; -} /* ecma_module_initialize_context */ + return module_p; +} /* ecma_module_create */ /** * cleanup context variables for the root module. @@ -219,30 +222,6 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */ continue; } - if (current_module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_NATIVE) - { - ecma_object_t *object_p = current_module_p->namespace_object_p; - ecma_value_t prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p), - object_p, - current_export_name_p); - if (ecma_is_value_found (prop_value)) - { - found = true; - found_record.module_p = current_module_p; - found_record.name_p = current_export_name_p; - ecma_free_value (prop_value); - } - - if (ecma_compare_ecma_string_to_magic_id (current_export_name_p, LIT_MAGIC_STRING_DEFAULT)) - { - ret_value = ecma_raise_syntax_error (ECMA_ERR_MSG ("No default export in native module")); - break; - } - - ecma_module_resolve_stack_pop (&stack_p); - continue; - } - if (current_module_p->local_exports_p != NULL) { /* 15.2.1.16.3 / 4 */ @@ -398,20 +377,40 @@ ecma_module_evaluate (ecma_module_t *module_p) /**< module */ module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATING; ecma_value_t ret_value; - ret_value = vm_run_module (module_p); + + if (module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) + { + ret_value = ECMA_VALUE_UNDEFINED; + + if (module_p->u.callback) + { + ret_value = module_p->u.callback (ecma_make_object_value (&module_p->header.object)); + + if (JERRY_UNLIKELY (ecma_is_value_error_reference (ret_value))) + { + ecma_raise_error_from_error_reference (ret_value); + ret_value = ECMA_VALUE_ERROR; + } + } + } + else + { + ret_value = vm_run_module (module_p); + } module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_ERROR; if (!ECMA_IS_VALUE_ERROR (ret_value)) { - ecma_free_value (ret_value); module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATED; - ret_value = ECMA_VALUE_EMPTY; } - ecma_bytecode_deref (module_p->compiled_code_p); - module_p->compiled_code_p = NULL; + if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)) + { + ecma_bytecode_deref (module_p->u.compiled_code_p); + } + module_p->u.compiled_code_p = NULL; return ret_value; } /* ecma_module_evaluate */ @@ -646,6 +645,8 @@ ecma_module_connect_imports (ecma_module_t *module_p) return result; } + ecma_free_value (result); + while (import_names_p != NULL) { const bool is_namespace_import = ecma_compare_ecma_string_to_magic_id (import_names_p->imex_name_p, @@ -682,29 +683,11 @@ ecma_module_connect_imports (ecma_module_t *module_p) return ecma_raise_syntax_error (ECMA_ERR_MSG ("Ambiguous import request")); } - if (record.module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_NATIVE) - { - ecma_object_t *object_p = record.module_p->namespace_object_p; - ecma_value_t prop_value; - - prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p), object_p, record.name_p); - JERRY_ASSERT (ecma_is_value_found (prop_value)); - - ecma_property_value_t *value_p; - value_p = ecma_create_named_data_property (module_p->scope_p, - import_names_p->local_name_p, - ECMA_PROPERTY_FLAG_WRITABLE, - NULL); - value_p->value = ecma_copy_value_if_not_object (prop_value); - } - else - { - ecma_property_t *property_p = ecma_find_named_property (record.module_p->scope_p, record.name_p); + ecma_property_t *property_p = ecma_find_named_property (record.module_p->scope_p, record.name_p); - ecma_create_named_reference_property (module_p->scope_p, - import_names_p->local_name_p, - property_p); - } + ecma_create_named_reference_property (module_p->scope_p, + import_names_p->local_name_p, + property_p); } import_names_p = import_names_p->next_p; @@ -1074,21 +1057,22 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */ module_p->namespace_object_p = NULL; #endif /* JERRY_NDEBUG */ - if (state == JERRY_MODULE_STATE_NATIVE) + ecma_module_release_module_names (module_p->local_exports_p); + + if (module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) { return; } ecma_module_release_module_nodes (module_p->imports_p, true); - ecma_module_release_module_names (module_p->local_exports_p); ecma_module_release_module_nodes (module_p->indirect_exports_p, false); ecma_module_release_module_nodes (module_p->star_exports_p, false); - if (module_p->compiled_code_p != NULL) + if (module_p->u.compiled_code_p != NULL) { - ecma_bytecode_deref (module_p->compiled_code_p); + ecma_bytecode_deref (module_p->u.compiled_code_p); #ifndef JERRY_NDEBUG - module_p->compiled_code_p = NULL; + module_p->u.compiled_code_p = NULL; #endif /* JERRY_NDEBUG */ } } /* ecma_module_release_module */ diff --git a/jerry-core/ecma/base/ecma-module.h b/jerry-core/ecma/base/ecma-module.h index 0746950023..7b6ff9ff95 100644 --- a/jerry-core/ecma/base/ecma-module.h +++ b/jerry-core/ecma/base/ecma-module.h @@ -23,6 +23,14 @@ #define ECMA_MODULE_MAX_PATH 255u +/** + * Module status flags. + */ +typedef enum +{ + ECMA_MODULE_IS_NATIVE = (1 << 0), /**< native module */ +} ecma_module_flags_t; + /** * Imported or exported names, such as "a as b" * Note: See https://www.ecma-international.org/ecma-262/6.0/#table-39 @@ -47,13 +55,19 @@ typedef struct ecma_module /* Note: state is stored in header.u.class_prop.extra_info */ ecma_extended_object_t header; /**< header part */ /* TODO(dbatyai): These could be compressed pointers */ + ecma_object_t *scope_p; /**< lexical lenvironment of the module */ + ecma_object_t *namespace_object_p; /**< namespace object of the module */ struct ecma_module_node *imports_p; /**< import requests of the module */ ecma_module_names_t *local_exports_p; /**< local exports of the module */ struct ecma_module_node *indirect_exports_p; /**< indirect exports of the module */ struct ecma_module_node *star_exports_p; /**< star exports of the module */ - ecma_compiled_code_t *compiled_code_p; /**< compiled code for the module */ - ecma_object_t *scope_p; /**< lexical lenvironment of the module */ - ecma_object_t *namespace_object_p; /**< namespace object of the module */ + + /* Code used for evaluating a module */ + union + { + ecma_compiled_code_t *compiled_code_p; /**< compiled code for the module */ + jerry_native_module_evaluate_callback_t callback; /**< callback for evaluating native modules */ + } u; } ecma_module_t; /** @@ -114,7 +128,7 @@ ecma_value_t ecma_module_link (ecma_module_t *module_p, void *user_p); ecma_value_t ecma_module_evaluate (ecma_module_t *module_p); -void ecma_module_initialize_context (void); +ecma_module_t *ecma_module_create (void); void ecma_module_cleanup_context (void); ecma_value_t ecma_module_create_namespace_object (ecma_module_t *module_p); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-global.c b/jerry-core/ecma/builtin-objects/ecma-builtin-global.c index d38b934fb5..8e4b05da70 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-global.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-global.c @@ -351,7 +351,7 @@ ecma_builtin_global_object_encode_uri_helper (lit_utf8_byte_t *input_start_p, /* while (input_char_p < input_end_p) { - input_char_p += lit_read_code_unit_from_utf8 (input_char_p, &ch); + input_char_p += lit_read_code_unit_from_cesu8 (input_char_p, &ch); if (lit_is_code_point_utf16_low_surrogate (ch)) { @@ -370,7 +370,7 @@ ecma_builtin_global_object_encode_uri_helper (lit_utf8_byte_t *input_start_p, /* } ecma_char_t next_ch; - lit_utf8_size_t read_size = lit_read_code_unit_from_utf8 (input_char_p, &next_ch); + lit_utf8_size_t read_size = lit_read_code_unit_from_cesu8 (input_char_p, &next_ch); if (lit_is_code_point_utf16_low_surrogate (next_ch)) { diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index e70f12084e..9706024136 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -273,6 +273,14 @@ size_t jerry_module_get_number_of_requests (const jerry_value_t module_val); jerry_value_t jerry_module_get_request (const jerry_value_t module_val, size_t request_index); jerry_value_t jerry_module_get_namespace (const jerry_value_t module_val); +jerry_value_t jerry_native_module_create (jerry_native_module_evaluate_callback_t callback, + const jerry_value_t * const exports_p, size_t number_of_exports); +jerry_value_t jerry_native_module_get_export (const jerry_value_t native_mmodule_val, + const jerry_value_t export_name_val); +jerry_value_t jerry_native_module_set_export (const jerry_value_t native_mmodule_val, + const jerry_value_t export_name_val, + const jerry_value_t value_to_set); + /** * Promise functions. */ diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index 4a12ec3ef2..8af1d72dfe 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -541,7 +541,6 @@ typedef enum JERRY_MODULE_STATE_EVALUATING = 4, /**< module is currently being evaluated */ JERRY_MODULE_STATE_EVALUATED = 5, /**< module has been evaluated (its source code has been executed) */ JERRY_MODULE_STATE_ERROR = 6, /**< an error has been encountered before the evaluated state is reached */ - JERRY_MODULE_STATE_NATIVE = 7, /**< module is native module */ } jerry_module_state_t; /** @@ -551,6 +550,11 @@ typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t sp const jerry_value_t referrer, void *user_p); +/** + * Callback which is called by jerry_module_evaluate to evaluate the native module. + */ +typedef jerry_value_t (*jerry_native_module_evaluate_callback_t) (const jerry_value_t native_module); + /** * Proxy related types. */ diff --git a/jerry-core/lit/lit-strings.c b/jerry-core/lit/lit-strings.c index 30dad4fe05..c2e6d929da 100644 --- a/jerry-core/lit/lit-strings.c +++ b/jerry-core/lit/lit-strings.c @@ -321,7 +321,7 @@ lit_get_utf8_size_of_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, /**< cesu while (offset < cesu8_buf_size) { ecma_char_t ch; - offset += lit_read_code_unit_from_utf8 (cesu8_buf_p + offset, &ch); + offset += lit_read_code_unit_from_cesu8 (cesu8_buf_p + offset, &ch); if (lit_is_code_point_utf16_low_surrogate (ch) && lit_is_code_point_utf16_high_surrogate (prev_ch)) { @@ -352,7 +352,7 @@ lit_get_utf8_length_of_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, /**< ce while (offset < cesu8_buf_size) { ecma_char_t ch; - offset += lit_read_code_unit_from_utf8 (cesu8_buf_p + offset, &ch); + offset += lit_read_code_unit_from_cesu8 (cesu8_buf_p + offset, &ch); if (!lit_is_code_point_utf16_low_surrogate (ch) || !lit_is_code_point_utf16_high_surrogate (prev_ch)) { @@ -423,20 +423,21 @@ lit_read_code_point_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with ch * @return number of bytes occupied by code point in the string */ lit_utf8_size_t -lit_read_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ - ecma_char_t *code_point) /**< [out] code point */ +lit_read_code_unit_from_cesu8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ + ecma_char_t *code_unit) /**< [out] code unit */ { JERRY_ASSERT (buf_p); lit_utf8_byte_t c = buf_p[0]; if ((c & LIT_UTF8_1_BYTE_MASK) == LIT_UTF8_1_BYTE_MARKER) { - *code_point = (ecma_char_t) (c & LIT_UTF8_LAST_7_BITS_MASK); + *code_unit = (ecma_char_t) (c & LIT_UTF8_LAST_7_BITS_MASK); return 1; } lit_code_point_t ret = LIT_UNICODE_CODE_POINT_NULL; lit_utf8_size_t bytes_count; + if ((c & LIT_UTF8_2_BYTE_MASK) == LIT_UTF8_2_BYTE_MARKER) { bytes_count = 2; @@ -456,9 +457,47 @@ lit_read_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer with cha } JERRY_ASSERT (ret <= LIT_UTF16_CODE_UNIT_MAX); - *code_point = (ecma_char_t) ret; + *code_unit = (ecma_char_t) ret; return bytes_count; -} /* lit_read_code_unit_from_utf8 */ +} /* lit_read_code_unit_from_cesu8 */ + +/** + * Decodes a unicode code point from non-empty cesu-8-encoded buffer + * + * @return number of bytes occupied by code point in the string + */ +lit_utf8_size_t +lit_read_code_point_from_cesu8 (const lit_utf8_byte_t *buf_p, /**< buffer with characters */ + const lit_utf8_byte_t *buf_end_p, /**< buffer end */ + lit_code_point_t *code_point) /**< [out] code point */ +{ + ecma_char_t code_unit; + lit_utf8_size_t size = lit_read_code_unit_from_cesu8 (buf_p, &code_unit); + + JERRY_ASSERT (buf_p + size <= buf_end_p); + + if (lit_is_code_point_utf16_high_surrogate (code_unit)) + { + buf_p += size; + + if (buf_p < buf_end_p) + { + ecma_char_t next_code_unit; + lit_utf8_size_t next_size = lit_read_code_unit_from_cesu8 (buf_p, &next_code_unit); + + if (lit_is_code_point_utf16_low_surrogate (next_code_unit)) + { + JERRY_ASSERT (buf_p + next_size <= buf_end_p); + + *code_point = lit_convert_surrogate_pair_to_code_point (code_unit, next_code_unit); + return size + next_size; + } + } + } + + *code_point = code_unit; + return size; +} /* lit_read_code_point_from_cesu8 */ /** * Decodes a unicode code unit from non-empty cesu-8-encoded buffer @@ -472,7 +511,7 @@ lit_read_prev_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, /**< buffer wit JERRY_ASSERT (buf_p); lit_utf8_decr (&buf_p); - return lit_read_code_unit_from_utf8 (buf_p, code_point); + return lit_read_code_unit_from_cesu8 (buf_p, code_point); } /* lit_read_prev_code_unit_from_utf8 */ /** @@ -486,7 +525,7 @@ lit_cesu8_read_next (const lit_utf8_byte_t **buf_p) /**< [in,out] buffer with ch JERRY_ASSERT (*buf_p); ecma_char_t ch; - *buf_p += lit_read_code_unit_from_utf8 (*buf_p, &ch); + *buf_p += lit_read_code_unit_from_cesu8 (*buf_p, &ch); return ch; } /* lit_cesu8_read_next */ @@ -503,7 +542,7 @@ lit_cesu8_read_prev (const lit_utf8_byte_t **buf_p) /**< [in,out] buffer with ch ecma_char_t ch; lit_utf8_decr (buf_p); - lit_read_code_unit_from_utf8 (*buf_p, &ch); + lit_read_code_unit_from_cesu8 (*buf_p, &ch); return ch; } /* lit_cesu8_read_prev */ @@ -519,7 +558,7 @@ lit_cesu8_peek_next (const lit_utf8_byte_t *buf_p) /**< [in,out] buffer with cha JERRY_ASSERT (buf_p != NULL); ecma_char_t ch; - lit_read_code_unit_from_utf8 (buf_p, &ch); + lit_read_code_unit_from_cesu8 (buf_p, &ch); return ch; } /* lit_cesu8_peek_next */ @@ -631,7 +670,7 @@ lit_utf8_string_code_unit_at (const lit_utf8_byte_t *utf8_buf_p, /**< utf-8 stri do { JERRY_ASSERT (current_p < utf8_buf_p + utf8_buf_size); - current_p += lit_read_code_unit_from_utf8 (current_p, &code_unit); + current_p += lit_read_code_unit_from_cesu8 (current_p, &code_unit); } while (code_unit_offset--); @@ -827,7 +866,7 @@ lit_convert_cesu8_string_to_utf8_string (const lit_utf8_byte_t *cesu8_string, /* while (cesu8_pos < cesu8_end_pos) { ecma_char_t ch; - lit_utf8_size_t code_unit_size = lit_read_code_unit_from_utf8 (cesu8_pos, &ch); + lit_utf8_size_t code_unit_size = lit_read_code_unit_from_cesu8 (cesu8_pos, &ch); if (lit_is_code_point_utf16_low_surrogate (ch) && lit_is_code_point_utf16_high_surrogate (prev_ch)) { @@ -900,8 +939,8 @@ bool lit_compare_utf8_strings_relational (const lit_utf8_byte_t *string1_p, /**< while (string1_pos < string1_end_p && string2_pos < string2_end_p) { ecma_char_t ch1, ch2; - string1_pos += lit_read_code_unit_from_utf8 (string1_pos, &ch1); - string2_pos += lit_read_code_unit_from_utf8 (string2_pos, &ch2); + string1_pos += lit_read_code_unit_from_cesu8 (string1_pos, &ch1); + string2_pos += lit_read_code_unit_from_cesu8 (string2_pos, &ch2); if (ch1 < ch2) { diff --git a/jerry-core/lit/lit-strings.h b/jerry-core/lit/lit-strings.h index ecc8ea848a..7ba61a2c06 100644 --- a/jerry-core/lit/lit-strings.h +++ b/jerry-core/lit/lit-strings.h @@ -129,8 +129,10 @@ uint8_t lit_utf16_encode_code_point (lit_code_point_t cp, ecma_char_t *cu_p); lit_utf8_size_t lit_read_code_point_from_utf8 (const lit_utf8_byte_t *buf_p, lit_utf8_size_t buf_size, lit_code_point_t *code_point); -lit_utf8_size_t lit_read_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, - ecma_char_t *code_point); +lit_utf8_size_t lit_read_code_unit_from_cesu8 (const lit_utf8_byte_t *buf_p, + ecma_char_t *code_unit); +lit_utf8_size_t lit_read_code_point_from_cesu8 (const lit_utf8_byte_t *buf_p, const lit_utf8_byte_t *buf_end_p, + lit_code_point_t *code_point); lit_utf8_size_t lit_read_prev_code_unit_from_utf8 (const lit_utf8_byte_t *buf_p, ecma_char_t *code_point); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index a305c1212e..d733496fd0 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -426,7 +426,7 @@ vm_run_module (ecma_module_t *module_p) /**< module to be executed */ } vm_frame_ctx_shared_t shared; - shared.bytecode_header_p = module_p->compiled_code_p; + shared.bytecode_header_p = module_p->u.compiled_code_p; shared.status_flags = 0; return vm_run (&shared, ECMA_VALUE_UNDEFINED, module_p->scope_p); @@ -4933,14 +4933,14 @@ vm_init_module_scope (ecma_module_t *module_p) /**< module without scope */ { ecma_object_t *global_object_p; #if JERRY_BUILTIN_REALMS - global_object_p = (ecma_object_t *) ecma_op_function_get_realm (module_p->compiled_code_p); + global_object_p = (ecma_object_t *) ecma_op_function_get_realm (module_p->u.compiled_code_p); #else /* !JERRY_BUILTIN_REALMS */ global_object_p = ecma_builtin_get_global (); #endif /* JERRY_BUILTIN_REALMS */ ecma_object_t *scope_p = ecma_create_lex_env_class (ecma_get_global_environment (global_object_p), sizeof (ecma_lexical_environment_class_t)); - const ecma_compiled_code_t *compiled_code_p = module_p->compiled_code_p; + const ecma_compiled_code_t *compiled_code_p = module_p->u.compiled_code_p; ecma_value_t *literal_start_p; uint8_t *byte_code_p; uint16_t encoding_limit; diff --git a/tests/unit-core/test-module.c b/tests/unit-core/test-module.c index 3b474f02da..5ce4fcc35a 100644 --- a/tests/unit-core/test-module.c +++ b/tests/unit-core/test-module.c @@ -137,6 +137,93 @@ resolve_callback3 (const jerry_value_t specifier, /**< module specifier */ TEST_ASSERT (false); } /* resolve_callback3 */ +static jerry_value_t +native_module_evaluate (const jerry_value_t native_module) /**< native module */ +{ + ++counter; + + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_EVALUATING); + + jerry_value_t exp_val = jerry_create_string ((const jerry_char_t *) "exp"); + jerry_value_t other_exp_val = jerry_create_string ((const jerry_char_t *) "other_exp"); + /* The native module has no such export. */ + jerry_value_t no_exp_val = jerry_create_string ((const jerry_char_t *) "no_exp"); + + jerry_value_t result = jerry_native_module_get_export (native_module, exp_val); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + result = jerry_native_module_get_export (native_module, other_exp_val); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + result = jerry_native_module_get_export (native_module, no_exp_val); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + jerry_value_t export = jerry_create_number (3.5); + result = jerry_native_module_set_export (native_module, exp_val, export); + TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result)); + jerry_release_value (result); + jerry_release_value (export); + + export = jerry_create_string ((const jerry_char_t *) "str"); + result = jerry_native_module_set_export (native_module, other_exp_val, export); + TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result)); + jerry_release_value (result); + jerry_release_value (export); + + result = jerry_native_module_set_export (native_module, no_exp_val, no_exp_val); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_native_module_get_export (native_module, exp_val); + TEST_ASSERT (jerry_value_is_number (result) && jerry_get_number_value (result) == 3.5); + jerry_release_value (result); + + result = jerry_native_module_get_export (native_module, other_exp_val); + TEST_ASSERT (jerry_value_is_string (result)); + jerry_release_value (result); + + jerry_release_value (exp_val); + jerry_release_value (other_exp_val); + jerry_release_value (no_exp_val); + + if (counter == 4) + { + ++counter; + return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Ooops!"); + } + + return jerry_create_undefined (); +} /* native_module_evaluate */ + +static jerry_value_t +resolve_callback4 (const jerry_value_t specifier, /**< module specifier */ + const jerry_value_t referrer, /**< parent module */ + void *user_p) /**< user data */ +{ + (void) specifier; + (void) referrer; + + ++counter; + + jerry_value_t exports[2] = + { + jerry_create_string ((const jerry_char_t *) "exp"), + jerry_create_string ((const jerry_char_t *) "other_exp") + }; + + jerry_value_t native_module = jerry_native_module_create (native_module_evaluate, exports, 2); + TEST_ASSERT (!jerry_value_is_error (native_module)); + + jerry_release_value (exports[0]); + jerry_release_value (exports[1]); + + *((jerry_value_t *) user_p) = jerry_acquire_value (native_module); + return native_module; +} /* resolve_callback4 */ + int main (void) { @@ -286,6 +373,97 @@ main (void) jerry_release_value (module); + module = jerry_native_module_create (NULL, &object, 1); + TEST_ASSERT (jerry_value_is_error (module)); + jerry_release_value (module); + + module = jerry_native_module_create (NULL, NULL, 0); + TEST_ASSERT (!jerry_value_is_error (module)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_LINKED); + + result = jerry_native_module_get_export (object, number); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + result = jerry_native_module_set_export (module, number, number); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (result); + + jerry_release_value (module); + + /* Valid identifier. */ + jerry_value_t export = jerry_create_string ((const jerry_char_t *) "\xed\xa0\x83\xed\xb2\x80"); + + module = jerry_native_module_create (NULL, &export, 1); + TEST_ASSERT (!jerry_value_is_error (module)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_LINKED); + + result = jerry_module_evaluate (module); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + jerry_release_value (module); + jerry_release_value (export); + + /* Invalid identifiers. */ + export = jerry_create_string ((const jerry_char_t *) "a+"); + module = jerry_native_module_create (NULL, &export, 1); + TEST_ASSERT (jerry_value_is_error (module)); + jerry_release_value (module); + jerry_release_value (export); + + export = jerry_create_string ((const jerry_char_t *) "\xed\xa0\x80"); + module = jerry_native_module_create (NULL, &export, 1); + TEST_ASSERT (jerry_value_is_error (module)); + jerry_release_value (module); + jerry_release_value (export); + + counter = 0; + + for (int i = 0; i < 2; i++) + { + jerry_char_t source3[] = TEST_STRING_LITERAL ( + "import {exp, other_exp as other} from 'native.js'\n" + "import * as namespace from 'native.js'\n" + "if (exp !== 3.5 || other !== 'str') { throw 'Assertion failed!' }\n" + "if (namespace.exp !== 3.5 || namespace.other_exp !== 'str') { throw 'Assertion failed!' }\n" + ); + module = jerry_parse (source3, sizeof (source3) - 1, &module_parse_options); + TEST_ASSERT (!jerry_value_is_error (module)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_UNLINKED); + + jerry_value_t native_module; + + result = jerry_module_link (module, resolve_callback4, (void *) &native_module); + TEST_ASSERT (!jerry_value_is_error (result)); + jerry_release_value (result); + + TEST_ASSERT (counter == i * 2 + 1); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_LINKED); + TEST_ASSERT (jerry_module_get_state (native_module) == JERRY_MODULE_STATE_LINKED); + + result = jerry_module_evaluate (module); + + if (i == 0) + { + TEST_ASSERT (!jerry_value_is_error (result)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_EVALUATED); + TEST_ASSERT (jerry_module_get_state (native_module) == JERRY_MODULE_STATE_EVALUATED); + TEST_ASSERT (counter == 2); + } + else + { + TEST_ASSERT (jerry_value_is_error (result)); + TEST_ASSERT (jerry_module_get_state (module) == JERRY_MODULE_STATE_ERROR); + TEST_ASSERT (jerry_module_get_state (native_module) == JERRY_MODULE_STATE_ERROR); + TEST_ASSERT (counter == 5); + } + + jerry_release_value (result); + jerry_release_value (module); + jerry_release_value (native_module); + } + jerry_release_value (object); jerry_release_value (number);