Support native modules (#4649)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2021-04-13 16:26:38 +02:00 committed by GitHub
parent 05a4e45ece
commit 7b6743403f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 770 additions and 100 deletions

View File

@ -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 <jerryscript.h>
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 <jerryscript.h>
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 <jerryscript.h>
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).

View File

@ -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.
*

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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--;

View File

@ -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;
ecma_property_t *property_p = ecma_find_named_property (record.module_p->scope_p, record.name_p);
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_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 */

View File

@ -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);

View File

@ -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))
{

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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);