Rework module linking (#4632)

The module linking process from jerry_parse is moved out into
a new jerry_module_link function, and jerry_parse is limited to
create unlinked modules.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2021-03-30 15:40:09 +02:00
committed by GitHub
parent 874a6a49d5
commit 6c484f3529
22 changed files with 1574 additions and 789 deletions
+20 -5
View File
@@ -721,6 +721,24 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
switch (ext_object_p->u.class_prop.class_id)
{
#if JERRY_MODULE_SYSTEM
case LIT_MAGIC_STRING_MODULE_UL:
{
ecma_module_node_t *node_p = ((ecma_module_t *) ext_object_p)->imports_p;
while (node_p != NULL)
{
if (ecma_is_value_object (node_p->u.path_or_module))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (node_p->u.path_or_module));
}
node_p = node_p->next_p;
}
break;
}
#endif /* JERRY_MODULE_SYSTEM */
#if JERRY_BUILTIN_PROMISE
case LIT_MAGIC_STRING_PROMISE_UL:
{
@@ -1705,11 +1723,8 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
#if JERRY_MODULE_SYSTEM
case LIT_MAGIC_STRING_MODULE_UL:
{
ecma_module_t *root_module_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_module_t,
ext_object_p->u.class_prop.u.value);
ecma_bytecode_deref (root_module_p->compiled_code_p);
ecma_module_cleanup (root_module_p);
ecma_module_release_module ((ecma_module_t *) ext_object_p);
ext_object_size = sizeof (ecma_module_t);
break;
}
#endif /* JERRY_MODULE_SYSTEM */
+3
View File
@@ -987,6 +987,9 @@ typedef struct
ecma_value_t date; /**< Date object [[DateValue]] internal property */
int32_t tza; /**< TimeZone adjustment for date objects */
uint32_t length; /**< length related property (e.g. length of ArrayBuffer) */
#if JERRY_MODULE_SYSTEM
uint32_t dfs_ancestor_index; /**< module dfs ancestor index (ES2020 15.2.1.16) */
#endif /* JERRY_MODULE_SYSTEM */
ecma_value_t target; /**< [[ProxyTarget]] or [[WeakRefTarget]] internal property */
ecma_value_t head; /**< points to the async generator task queue head item */
ecma_value_t promise; /**< PromiseCapability[[Promise]] internal slot */
+306 -354
View File
@@ -28,208 +28,31 @@
#if JERRY_MODULE_SYSTEM
/**
* Takes a ModuleSpecifier and applies path normalization to it.
* It's not checked if the ModuleSpecifier is a valid path or not.
* Note: See 15.2.1.17
*
* @return pointer to ecma_string_t containing the normalized and zero terminated path
*/
ecma_string_t *
ecma_module_create_normalized_path (const lit_utf8_byte_t *char_p, /**< module path specifier */
lit_utf8_size_t size, /**< size of module specifier */
ecma_string_t *const base_path_p) /**< base path for the module specifier */
{
JERRY_ASSERT (size > 0);
ecma_string_t *ret_p = NULL;
/* The module specifier is cesu8 encoded, we need to convert is to utf8, and zero terminate it,
* so that OS level functions can handle it. */
lit_utf8_byte_t *path_p = (lit_utf8_byte_t *) jmem_heap_alloc_block (size + 1u);
lit_utf8_size_t utf8_size;
utf8_size = lit_convert_cesu8_string_to_utf8_string (char_p,
size,
path_p,
size);
path_p[utf8_size] = LIT_CHAR_NULL;
lit_utf8_byte_t *module_path_p = NULL;
lit_utf8_size_t module_path_size = 0;
if (base_path_p != NULL)
{
module_path_size = ecma_string_get_size (base_path_p);
module_path_p = (lit_utf8_byte_t *) jmem_heap_alloc_block (module_path_size + 1);
lit_utf8_size_t module_utf8_size;
module_utf8_size = ecma_string_copy_to_utf8_buffer (base_path_p,
module_path_p,
module_path_size);
module_path_p[module_utf8_size] = LIT_CHAR_NULL;
}
lit_utf8_byte_t *normalized_out_p = (lit_utf8_byte_t *) jmem_heap_alloc_block (ECMA_MODULE_MAX_PATH);
size_t normalized_size = jerry_port_normalize_path ((const char *) path_p,
(char *) normalized_out_p,
ECMA_MODULE_MAX_PATH,
(char *) module_path_p);
if (normalized_size > 0)
{
/* Convert the normalized path to cesu8. */
ret_p = ecma_new_ecma_string_from_utf8_converted_to_cesu8 (normalized_out_p, (lit_utf8_size_t) (normalized_size));
}
jmem_heap_free_block (path_p, size + 1u);
jmem_heap_free_block (normalized_out_p, ECMA_MODULE_MAX_PATH);
if (module_path_p != NULL)
{
jmem_heap_free_block (module_path_p, module_path_size + 1);
}
return ret_p;
} /* ecma_module_create_normalized_path */
/**
* Push a new module into the module list. New modules are inserted after the head module, this way in the end the
* root module remains the first in the list.
*/
static void
ecma_module_list_push (ecma_module_t *module_p)
{
ecma_module_t *head_p = JERRY_CONTEXT (module_list_p);
module_p->next_p = head_p->next_p;
head_p->next_p = module_p;
} /* ecma_module_list_push */
/**
* Lookup a module with a specific identifier.
*
* @return pointer to ecma_module_t, if found
* NULL, otherwise
*/
static ecma_module_t *
ecma_module_list_lookup (ecma_string_t *const path_p) /**< module identifier */
{
ecma_module_t *current_p = JERRY_CONTEXT (module_list_p);
while (current_p != NULL)
{
if (ecma_compare_ecma_strings (path_p, current_p->path_p))
{
return current_p;
}
current_p = current_p->next_p;
}
return NULL;
} /* ecma_module_list_lookup */
/**
* Create a new module
*
* @return pointer to created module
*/
static ecma_module_t *
ecma_module_create_module (ecma_string_t *const path_p) /**< module identifier */
{
ecma_module_t *module_p = (ecma_module_t *) jmem_heap_alloc_block (sizeof (ecma_module_t));
memset (module_p, 0, sizeof (ecma_module_t));
module_p->path_p = path_p;
ecma_module_list_push (module_p);
return module_p;
} /* ecma_module_create_module */
/**
* Checks if we already have a module request in the module list.
*
* @return pointer to found or newly created module structure
*/
ecma_module_t *
ecma_module_find_module (ecma_string_t *const path_p) /**< module path */
{
ecma_module_t *module_p = ecma_module_list_lookup (path_p);
if (module_p)
{
ecma_deref_ecma_string (path_p);
return module_p;
}
return ecma_module_create_module (path_p);
} /* ecma_module_find_module */
/**
* Create a new native module
*
* @return pointer to created module
*/
ecma_module_t *
ecma_module_find_native_module (ecma_string_t *const path_p)
{
ecma_module_t *module_p = ecma_module_list_lookup (path_p);
if (module_p != NULL)
{
return module_p;
}
ecma_value_t native = jerry_port_get_native_module (ecma_make_string_value (path_p));
if (!ecma_is_value_undefined (native))
{
JERRY_ASSERT (ecma_is_value_object (native));
module_p = ecma_module_create_module (path_p);
module_p->state = ECMA_MODULE_STATE_NATIVE;
module_p->namespace_object_p = ecma_get_object_from_value (native);
return module_p;
}
return NULL;
} /* ecma_module_find_native_module */
/**
* Initialize context variables for the root module.
*/
void
ecma_module_initialize_context (const ecma_parse_options_t *options_p) /**< configuration options */
ecma_module_initialize_context (void)
{
JERRY_ASSERT (JERRY_CONTEXT (module_current_p) == NULL);
JERRY_ASSERT (JERRY_CONTEXT (module_list_p) == NULL);
ecma_string_t *path_p = ecma_get_magic_string (LIT_MAGIC_STRING_RESOURCE_ANON);
ecma_object_t *obj_p = ecma_create_object (NULL, sizeof (ecma_module_t), ECMA_OBJECT_TYPE_CLASS);
if (options_p != NULL
&& (options_p->options & JERRY_PARSE_HAS_RESOURCE)
&& options_p->resource_name_length > 0)
{
const lit_utf8_byte_t *path_str_chars_p = options_p->resource_name_p;
lit_utf8_size_t path_str_size = (lit_utf8_size_t) options_p->resource_name_length;
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p;
ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_MODULE_UL;
ext_object_p->u.class_prop.extra_info = ECMA_MODULE_STATE_UNLINKED;
path_p = ecma_module_create_normalized_path (path_str_chars_p,
path_str_size,
NULL);
if (path_p == NULL)
{
path_p = ecma_new_ecma_string_from_utf8_converted_to_cesu8 (path_str_chars_p, path_str_size);
}
}
ecma_module_t *module_p = (ecma_module_t *) obj_p;
ecma_module_t *module_p = (ecma_module_t *) jmem_heap_alloc_block (sizeof (ecma_module_t));
memset (module_p, 0, sizeof (ecma_module_t));
module_p->path_p = path_p;
/* Root modules are handled differently then the rest of the referenced modules, as the scope and compiled code
* are handled separately. */
module_p->state = ECMA_MODULE_STATE_ROOT;
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;
JERRY_CONTEXT (module_current_p) = module_p;
JERRY_CONTEXT (module_list_p) = module_p;
} /* ecma_module_initialize_context */
/**
@@ -238,13 +61,30 @@ ecma_module_initialize_context (const ecma_parse_options_t *options_p) /**< conf
void
ecma_module_cleanup_context (void)
{
ecma_module_cleanup (JERRY_CONTEXT (module_current_p));
ecma_deref_object ((ecma_object_t *) JERRY_CONTEXT (module_current_p));
#ifndef JERRY_NDEBUG
JERRY_CONTEXT (module_current_p) = NULL;
JERRY_CONTEXT (module_list_p) = NULL;
#endif /* JERRY_NDEBUG */
} /* ecma_module_cleanup_context */
/**
* Gets the internal module pointer of a module
*
* @return module pointer
*/
static inline ecma_module_t * JERRY_ATTR_ALWAYS_INLINE
ecma_module_get_from_object (ecma_value_t module_val) /**< module */
{
JERRY_ASSERT (ecma_is_value_object (module_val));
ecma_object_t *object_p = ecma_get_object_from_value (module_val);
JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS);
JERRY_ASSERT (((ecma_extended_object_t *) object_p)->u.class_prop.class_id == LIT_MAGIC_STRING_MODULE_UL);
return (ecma_module_t *) object_p;
} /* ecma_module_get_from_object */
/**
* Inserts a {module, export_name} record into a resolve set.
* Note: See 15.2.1.16.3 - resolveSet and exportStarSet
@@ -252,7 +92,7 @@ ecma_module_cleanup_context (void)
* @return true - if the set already contains the record
* false - otherwise
*/
bool
static bool
ecma_module_resolve_set_insert (ecma_module_resolve_set_t **set_p, /**< [in, out] resolve set */
ecma_module_t *const module_p, /**< module */
ecma_string_t *const export_name_p) /**< export name */
@@ -286,7 +126,7 @@ ecma_module_resolve_set_insert (ecma_module_resolve_set_t **set_p, /**< [in, out
/**
* Cleans up contents of a resolve set.
*/
void
static void
ecma_module_resolve_set_cleanup (ecma_module_resolve_set_t *set_p) /**< resolve set */
{
while (set_p != NULL)
@@ -302,7 +142,7 @@ ecma_module_resolve_set_cleanup (ecma_module_resolve_set_t *set_p) /**< resolve
* Pushes a new resolve frame on top of a resolve stack and initializes it
* to begin resolving the specified exported name in the base module.
*/
void
static void
ecma_module_resolve_stack_push (ecma_module_resolve_stack_t **stack_p, /**< [in, out] resolve stack */
ecma_module_t * const module_p, /**< base module */
ecma_string_t * const export_name_p) /**< exported name */
@@ -323,7 +163,7 @@ ecma_module_resolve_stack_push (ecma_module_resolve_stack_t **stack_p, /**< [in,
/**
* Pops the topmost frame from a resolve stack.
*/
void
static void
ecma_module_resolve_stack_pop (ecma_module_resolve_stack_t **stack_p) /**< [in, out] resolve stack */
{
JERRY_ASSERT (stack_p != NULL);
@@ -365,7 +205,6 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
ecma_module_resolve_stack_t *current_frame_p = stack_p;
ecma_module_t *current_module_p = current_frame_p->module_p;
JERRY_ASSERT (current_module_p->state >= ECMA_MODULE_STATE_PARSED);
ecma_string_t *current_export_name_p = current_frame_p->export_name_p;
if (!current_frame_p->resolving)
@@ -380,7 +219,7 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
continue;
}
if (current_module_p->state == ECMA_MODULE_STATE_NATIVE)
if (current_module_p->header.u.class_prop.extra_info == ECMA_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),
@@ -407,8 +246,7 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
if (current_module_p->local_exports_p != NULL)
{
/* 15.2.1.16.3 / 4 */
JERRY_ASSERT (current_module_p->local_exports_p->next_p == NULL);
ecma_module_names_t *export_names_p = current_module_p->local_exports_p->module_names_p;
ecma_module_names_t *export_names_p = current_module_p->local_exports_p;
while (export_names_p != NULL)
{
if (ecma_compare_ecma_strings (current_export_name_p, export_names_p->imex_name_p))
@@ -450,7 +288,7 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
{
/* 5.2.1.16.3 / 5.a.iv */
ecma_module_resolve_stack_push (&stack_p,
indirect_export_p->module_request_p,
ecma_module_get_from_object (*indirect_export_p->u.module_object_p),
export_names_p->local_name_p);
break;
}
@@ -502,7 +340,9 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
JERRY_ASSERT (star_export_p->module_names_p == NULL);
/* 15.2.1.16.3 / 10.c */
ecma_module_resolve_stack_push (&stack_p, star_export_p->module_request_p, export_name_p);
ecma_module_resolve_stack_push (&stack_p,
ecma_module_get_from_object (*star_export_p->u.module_object_p),
export_name_p);
star_export_p = star_export_p->next_p;
}
@@ -539,39 +379,44 @@ ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
* @return ECMA_VALUE_ERROR - if an error occured
* ECMA_VALUE_EMPTY - otherwise
*/
static ecma_value_t
ecma_value_t
ecma_module_evaluate (ecma_module_t *module_p) /**< module */
{
JERRY_ASSERT (module_p->state >= ECMA_MODULE_STATE_PARSED);
if (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_ERROR)
{
return ecma_raise_range_error (ECMA_ERR_MSG ("Module is in error state"));
}
if (module_p->state >= ECMA_MODULE_STATE_EVALUATING)
if (module_p->header.u.class_prop.extra_info >= ECMA_MODULE_STATE_EVALUATING)
{
return ECMA_VALUE_EMPTY;
}
JERRY_ASSERT (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKED);
#if JERRY_BUILTIN_REALMS
ecma_object_t *global_object_p = (ecma_object_t *) ecma_op_function_get_realm (module_p->compiled_code_p);
#else /* !JERRY_BUILTIN_REALMS */
ecma_object_t *global_object_p = ecma_builtin_get_global ();
#endif /* JERRY_BUILTIN_REALMS */
module_p->state = ECMA_MODULE_STATE_EVALUATING;
module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_EVALUATING;
module_p->scope_p = ecma_create_decl_lex_env (ecma_get_global_environment (global_object_p));
ecma_value_t ret_value;
ret_value = vm_run_module (module_p);
module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_ERROR;
if (!ECMA_IS_VALUE_ERROR (ret_value))
{
ecma_free_value (ret_value);
module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_EVALUATED;
ret_value = ECMA_VALUE_EMPTY;
}
module_p->state = ECMA_MODULE_STATE_EVALUATED;
ecma_bytecode_deref (module_p->compiled_code_p);
#ifndef JERRY_NDEBUG
module_p->compiled_code_p = NULL;
#endif /* JERRY_NDEBUG */
return ret_value;
} /* ecma_module_evaluate */
@@ -646,7 +491,7 @@ ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */
return result;
}
JERRY_ASSERT (module_p->state == ECMA_MODULE_STATE_EVALUATED);
JERRY_ASSERT (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_EVALUATED);
ecma_module_resolve_set_t *resolve_set_p = NULL;
ecma_module_resolve_stack_t *stack_p = NULL;
@@ -678,30 +523,36 @@ ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */
break;
}
if (current_module_p->local_exports_p != NULL)
ecma_module_names_t *export_names_p = current_module_p->local_exports_p;
if (export_names_p != NULL)
{
/* 15.2.1.16.2 / 5 */
JERRY_ASSERT (current_module_p->local_exports_p->next_p == NULL);
ecma_module_names_t *export_names_p = current_module_p->local_exports_p->module_names_p;
while (export_names_p != NULL && ecma_is_value_empty (result))
do
{
result = ecma_module_namespace_object_add_export_if_needed (module_p,
export_names_p->imex_name_p);
export_names_p = export_names_p->next_p;
}
while (export_names_p != NULL && ecma_is_value_empty (result));
}
/* 15.2.1.16.2 / 6 */
ecma_module_node_t *indirect_export_p = current_module_p->indirect_exports_p;
while (indirect_export_p != NULL && ecma_is_value_empty (result))
{
ecma_module_names_t *export_names_p = indirect_export_p->module_names_p;
while (export_names_p != NULL && ecma_is_value_empty (result))
export_names_p = indirect_export_p->module_names_p;
JERRY_ASSERT (export_names_p != NULL);
do
{
result = ecma_module_namespace_object_add_export_if_needed (module_p,
export_names_p->imex_name_p);
export_names_p = export_names_p->next_p;
}
while (export_names_p != NULL && ecma_is_value_empty (result));
indirect_export_p = indirect_export_p->next_p;
}
@@ -713,7 +564,7 @@ ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */
/* 15.2.1.16.3/10.c */
ecma_module_resolve_stack_push (&stack_p,
star_export_p->module_request_p,
ecma_module_get_from_object (*star_export_p->u.module_object_p),
ecma_get_magic_string (LIT_MAGIC_STRING_ASTERIX_CHAR));
star_export_p = star_export_p->next_p;
@@ -792,13 +643,17 @@ ecma_module_connect_imports (ecma_module_t *module_p)
/* Resolve imports and create local bindings. */
while (import_node_p != NULL)
{
ecma_value_t result = ecma_module_evaluate (import_node_p->module_request_p);
ecma_module_names_t *import_names_p = import_node_p->module_names_p;
ecma_module_t *imported_module_p = ecma_module_get_from_object (import_node_p->u.path_or_module);
/* Module is evaluated even if it is used only in export-from statements. */
ecma_value_t result = ecma_module_evaluate (imported_module_p);
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
ecma_module_names_t *import_names_p = import_node_p->module_names_p;
while (import_names_p != NULL)
{
const bool is_namespace_import = ecma_compare_ecma_string_to_magic_id (import_names_p->imex_name_p,
@@ -808,19 +663,20 @@ ecma_module_connect_imports (ecma_module_t *module_p)
if (is_namespace_import)
{
result = ecma_module_create_namespace_object (import_node_p->module_request_p);
result = ecma_module_create_namespace_object (imported_module_p);
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
ecma_ref_object (import_node_p->module_request_p->namespace_object_p);
prop_value = ecma_make_object_value (import_node_p->module_request_p->namespace_object_p);
ecma_ref_object (imported_module_p->namespace_object_p);
prop_value = ecma_make_object_value (imported_module_p->namespace_object_p);
}
else /* !is_namespace_import */
{
ecma_module_record_t record;
result = ecma_module_resolve_export (import_node_p->module_request_p, import_names_p->imex_name_p, &record);
result = ecma_module_resolve_export (imported_module_p, import_names_p->imex_name_p, &record);
if (ECMA_IS_VALUE_ERROR (result))
{
@@ -832,7 +688,7 @@ ecma_module_connect_imports (ecma_module_t *module_p)
return ecma_raise_syntax_error (ECMA_ERR_MSG ("Ambiguous import request"));
}
if (record.module_p->state == ECMA_MODULE_STATE_NATIVE)
if (record.module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_NATIVE)
{
ecma_object_t *object_p = record.module_p->namespace_object_p;
prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p), object_p, record.name_p);
@@ -901,9 +757,11 @@ ecma_module_check_indirect_exports (ecma_module_t *module_p)
while (name_p != NULL)
{
ecma_module_record_t record;
ecma_value_t result = ecma_module_resolve_export (indirect_export_p->module_request_p,
name_p->local_name_p,
&record);
ecma_value_t result;
result = ecma_module_resolve_export (ecma_module_get_from_object (*indirect_export_p->u.module_object_p),
name_p->local_name_p,
&record);
if (ECMA_IS_VALUE_ERROR (result))
{
@@ -946,116 +804,234 @@ ecma_module_initialize (ecma_module_t *module_p) /**< module */
return ret_value;
} /* ecma_module_initialize */
static ecma_value_t ecma_module_parse (ecma_module_t *module_p);
/**
* Gets the internal module pointer of a module
*
* @return module pointer - if module_val is a valid module,
* NULL - otherwise
*/
ecma_module_t *
ecma_module_get_resolved_module (ecma_value_t module_val) /**< module */
{
if (!ecma_is_value_object (module_val))
{
return NULL;
}
ecma_object_t *object_p = ecma_get_object_from_value (module_val);
if (ecma_get_object_type (object_p) != ECMA_OBJECT_TYPE_CLASS)
{
return NULL;
}
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
if (ext_object_p->u.class_prop.class_id != LIT_MAGIC_STRING_MODULE_UL)
{
return NULL;
}
return (ecma_module_t *) object_p;
} /* ecma_module_get_resolved_module */
/**
* Parses all referenced modules.
* A module stack for depth-first search
*/
typedef struct ecma_module_stack_item_t
{
struct ecma_module_stack_item_t *prev_p; /**< prev in the stack */
struct ecma_module_stack_item_t *parent_p; /**< parent item in the stack */
ecma_module_t *module_p; /**< currently processed module */
ecma_module_node_t *node_p; /**< currently processed node */
uint32_t dfs_index; /**< dfs index (ES2020 15.2.1.16) */
} ecma_module_stack_item_t;
/**
* Link module dependencies
*
* @return ECMA_VALUE_ERROR - if an error occured
* ECMA_VALUE_EMPTY - otherwise
* ECMA_VALUE_UNDEFINED - otherwise
*/
ecma_value_t
ecma_module_parse_referenced_modules (void)
ecma_module_link (ecma_module_t *module_p, /**< root module */
jerry_module_resolve_callback_t callback, /**< resolve module callback */
void *user_p) /**< pointer passed to the resolve callback */
{
ecma_module_t *current_p = JERRY_CONTEXT (module_list_p);
while (current_p != NULL)
if (module_p->header.u.class_prop.extra_info != ECMA_MODULE_STATE_UNLINKED)
{
if (ECMA_IS_VALUE_ERROR (ecma_module_parse (current_p)))
return ecma_raise_type_error (ECMA_ERR_MSG ("Module must be in unlinked state"));
}
module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_LINKING;
uint32_t dfs_index = 0;
ecma_module_stack_item_t *last_p;
ecma_module_node_t *node_p;
last_p = (ecma_module_stack_item_t *) jmem_heap_alloc_block (sizeof (ecma_module_stack_item_t));
last_p->prev_p = NULL;
last_p->parent_p = NULL;
last_p->module_p = module_p;
last_p->node_p = module_p->imports_p;
last_p->dfs_index = dfs_index;
module_p->header.u.class_prop.u.dfs_ancestor_index = dfs_index;
ecma_value_t module_val = ecma_make_object_value (&module_p->header.object);
ecma_module_stack_item_t *current_p = last_p;
restart:
/* Entering into processing new node phase. Resolve dependencies first. */
node_p = current_p->node_p;
JERRY_ASSERT (ecma_module_get_from_object (module_val)->imports_p == node_p);
while (node_p != NULL)
{
if (ecma_is_value_object (node_p->u.path_or_module))
{
return ECMA_VALUE_ERROR;
/* Already linked. */
node_p = node_p->next_p;
continue;
}
current_p = current_p->next_p;
JERRY_ASSERT (ecma_is_value_string (node_p->u.path_or_module));
ecma_value_t resolve_result = callback (node_p->u.path_or_module, module_val, user_p);
if (JERRY_UNLIKELY (ecma_is_value_error_reference (resolve_result)))
{
ecma_raise_error_from_error_reference (resolve_result);
goto error;
}
ecma_module_t *resolved_module_p = ecma_module_get_resolved_module (resolve_result);
if (resolved_module_p == NULL)
{
ecma_free_value (resolve_result);
ecma_raise_type_error (ECMA_ERR_MSG ("Callback result must be a module"));
goto error;
}
ecma_deref_ecma_string (ecma_get_string_from_value (node_p->u.path_or_module));
node_p->u.path_or_module = resolve_result;
ecma_deref_object (ecma_get_object_from_value (resolve_result));
if (resolved_module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_ERROR)
{
ecma_raise_type_error (ECMA_ERR_MSG ("Cannot link to a module which is in error state"));
goto error;
}
node_p = node_p->next_p;
}
return ECMA_VALUE_EMPTY;
} /* ecma_module_parse_referenced_modules */
/**
* Parses an EcmaScript module.
*
* @return ECMA_VALUE_ERROR - if an error occured
* ECMA_VALUE_EMPTY - otherwise
*/
static ecma_value_t
ecma_module_parse (ecma_module_t *module_p) /**< module */
{
if (module_p->state >= ECMA_MODULE_STATE_PARSING)
/* Find next unlinked node, or return to parent */
while (true)
{
return ECMA_VALUE_EMPTY;
ecma_module_t *current_module_p = current_p->module_p;
node_p = current_p->node_p;
while (node_p != NULL)
{
module_p = ecma_module_get_from_object (node_p->u.path_or_module);
if (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_UNLINKED)
{
current_p->node_p = node_p->next_p;
module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_LINKING;
ecma_module_stack_item_t *item_p;
item_p = (ecma_module_stack_item_t *) jmem_heap_alloc_block (sizeof (ecma_module_stack_item_t));
dfs_index++;
item_p->prev_p = last_p;
item_p->parent_p = current_p;
item_p->module_p = module_p;
item_p->node_p = module_p->imports_p;
item_p->dfs_index = dfs_index;
module_p->header.u.class_prop.u.dfs_ancestor_index = dfs_index;
last_p = item_p;
current_p = item_p;
module_val = node_p->u.path_or_module;
goto restart;
}
if (module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKING)
{
uint32_t dfs_ancestor_index = module_p->header.u.class_prop.u.dfs_ancestor_index;
if (dfs_ancestor_index < current_module_p->header.u.class_prop.u.dfs_ancestor_index)
{
current_module_p->header.u.class_prop.u.dfs_ancestor_index = dfs_ancestor_index;
}
}
node_p = node_p->next_p;
}
if (current_module_p->header.u.class_prop.u.dfs_ancestor_index != current_p->dfs_index)
{
current_p = current_p->parent_p;
JERRY_ASSERT (current_p != NULL);
uint32_t dfs_ancestor_index = current_module_p->header.u.class_prop.u.dfs_ancestor_index;
if (dfs_ancestor_index < current_p->module_p->header.u.class_prop.u.dfs_ancestor_index)
{
current_p->module_p->header.u.class_prop.u.dfs_ancestor_index = dfs_ancestor_index;
}
continue;
}
ecma_module_stack_item_t *end_p = current_p->prev_p;
current_p = current_p->parent_p;
do
{
ecma_module_stack_item_t *prev_p = last_p->prev_p;
JERRY_ASSERT (last_p->module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKING);
last_p->module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_LINKED;
jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t));
last_p = prev_p;
}
while (last_p != end_p);
if (current_p == NULL)
{
return ECMA_VALUE_TRUE;
}
}
module_p->state = ECMA_MODULE_STATE_PARSING;
error:
JERRY_ASSERT (last_p != NULL);
lit_utf8_size_t module_path_size = ecma_string_get_size (module_p->path_p);
lit_utf8_byte_t *module_path_p = (lit_utf8_byte_t *) jmem_heap_alloc_block (module_path_size + 1);
lit_utf8_size_t module_path_utf8_size;
module_path_utf8_size = ecma_string_copy_to_utf8_buffer (module_p->path_p,
module_path_p,
module_path_size);
module_path_p[module_path_utf8_size] = LIT_CHAR_NULL;
size_t source_size = 0;
uint8_t *source_p = jerry_port_read_source ((const char *) module_path_p, &source_size);
if (source_p == NULL)
do
{
jmem_heap_free_block (module_path_p, module_path_size + 1);
return ecma_raise_syntax_error (ECMA_ERR_MSG ("File not found"));
ecma_module_stack_item_t *prev_p = last_p->prev_p;
JERRY_ASSERT (last_p->module_p->header.u.class_prop.extra_info == ECMA_MODULE_STATE_LINKING);
last_p->module_p->header.u.class_prop.extra_info = ECMA_MODULE_STATE_UNLINKED;
jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t));
last_p = prev_p;
}
while (last_p != NULL);
ecma_module_t *prev_module_p = JERRY_CONTEXT (module_current_p);
JERRY_CONTEXT (module_current_p) = module_p;
#if JERRY_DEBUGGER && JERRY_PARSER
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
{
jerry_debugger_send_string (JERRY_DEBUGGER_SOURCE_CODE_NAME,
JERRY_DEBUGGER_NO_SUBTYPE,
module_path_p,
module_path_size - 1);
}
#endif /* JERRY_DEBUGGER && JERRY_PARSER */
/* TODO: Improve this code in the future. */
ecma_parse_options_t parse_options;
parse_options.options = ECMA_PARSE_STRICT_MODE | ECMA_PARSE_MODULE | ECMA_PARSE_HAS_RESOURCE;
parse_options.resource_name_p = module_path_p;
parse_options.resource_name_length = module_path_size;
ecma_compiled_code_t *bytecode_p = parser_parse_script (NULL,
0,
(jerry_char_t *) source_p,
source_size,
ECMA_PARSE_STRICT_MODE | ECMA_PARSE_MODULE,
&parse_options);
JERRY_CONTEXT (module_current_p) = prev_module_p;
jmem_heap_free_block (module_path_p, module_path_size + 1);
jerry_port_release_source (source_p);
if (JERRY_UNLIKELY (bytecode_p == NULL))
{
return ECMA_VALUE_ERROR;
}
if (ECMA_IS_VALUE_ERROR (ecma_module_parse_referenced_modules ()))
{
ecma_bytecode_deref (bytecode_p);
return ECMA_VALUE_ERROR;
}
module_p->compiled_code_p = bytecode_p;
module_p->state = ECMA_MODULE_STATE_PARSED;
return ECMA_VALUE_EMPTY;
} /* ecma_module_parse */
return ECMA_VALUE_ERROR;
} /* ecma_module_link */
/**
* Cleans up a list of module names.
*/
static void
void
ecma_module_release_module_names (ecma_module_names_t *module_name_p) /**< first module name */
{
while (module_name_p != NULL)
@@ -1073,16 +1049,22 @@ ecma_module_release_module_names (ecma_module_names_t *module_name_p) /**< first
/**
* Cleans up a list of module nodes.
*/
void
ecma_module_release_module_nodes (ecma_module_node_t *module_node_p) /**< first module node */
static void
ecma_module_release_module_nodes (ecma_module_node_t *module_node_p, /**< first module node */
bool is_import) /**< free path variable */
{
while (module_node_p != NULL)
{
ecma_module_node_t *next_p = module_node_p->next_p;
ecma_module_release_module_names (module_node_p->module_names_p);
jmem_heap_free_block (module_node_p, sizeof (ecma_module_node_t));
if (is_import && ecma_is_value_string (module_node_p->u.path_or_module))
{
ecma_deref_ecma_string (ecma_get_string_from_value (module_node_p->u.path_or_module));
}
jmem_heap_free_block (module_node_p, sizeof (ecma_module_node_t));
module_node_p = next_p;
}
} /* ecma_module_release_module_nodes */
@@ -1090,15 +1072,10 @@ ecma_module_release_module_nodes (ecma_module_node_t *module_node_p) /**< first
/**
* Cleans up and releases a module structure including all referenced modules.
*/
static void
void
ecma_module_release_module (ecma_module_t *module_p) /**< module */
{
ecma_module_state_t state = module_p->state;
ecma_deref_ecma_string (module_p->path_p);
#ifndef JERRY_NDEBUG
module_p->path_p = NULL;
#endif /* JERRY_NDEBUG */
ecma_module_state_t state = (ecma_module_state_t) module_p->header.u.class_prop.extra_info;
if (module_p->namespace_object_p != NULL)
{
@@ -1112,21 +1089,13 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */
if (state == ECMA_MODULE_STATE_NATIVE)
{
goto finished;
return;
}
if (state >= ECMA_MODULE_STATE_PARSING)
{
ecma_module_release_module_nodes (module_p->imports_p);
ecma_module_release_module_nodes (module_p->local_exports_p);
ecma_module_release_module_nodes (module_p->indirect_exports_p);
ecma_module_release_module_nodes (module_p->star_exports_p);
}
if (state == ECMA_MODULE_STATE_ROOT)
{
goto finished;
}
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 (state >= ECMA_MODULE_STATE_EVALUATING)
{
@@ -1135,30 +1104,13 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */
ecma_deref_object (module_p->scope_p);
}
if (state >= ECMA_MODULE_STATE_PARSED
&& state < ECMA_MODULE_STATE_EVALUATED)
if (module_p->compiled_code_p != NULL)
{
ecma_bytecode_deref (module_p->compiled_code_p);
#ifndef JERRY_NDEBUG
module_p->compiled_code_p = NULL;
#endif /* JERRY_NDEBUG */
}
finished:
jmem_heap_free_block (module_p, sizeof (ecma_module_t));
} /* ecma_module_release_module */
/**
* Cleans up and releases a module list.
*/
void
ecma_module_cleanup (ecma_module_t *head_p) /**< module */
{
while (head_p != NULL)
{
ecma_module_t *next_p = head_p->next_p;
ecma_module_release_module (head_p);
head_p = next_p;
}
} /* ecma_module_cleanup */
#endif /* JERRY_MODULE_SYSTEM */
+36 -33
View File
@@ -40,41 +40,55 @@ typedef struct ecma_module_names
*/
typedef enum
{
ECMA_MODULE_STATE_INIT = 0, /**< module is initialized */
ECMA_MODULE_STATE_PARSING = 1, /**< module is currently being parsed */
ECMA_MODULE_STATE_PARSED = 2, /**< module has been parsed */
ECMA_MODULE_STATE_UNLINKED = 0, /**< module is currently unlinked */
ECMA_MODULE_STATE_LINKING = 1, /**< module is currently being linked */
ECMA_MODULE_STATE_LINKED = 2, /**< module has been linked */
ECMA_MODULE_STATE_EVALUATING = 3, /**< module is currently being evaluated */
ECMA_MODULE_STATE_EVALUATED = 4, /**< module has been evaluated */
ECMA_MODULE_STATE_NATIVE = 5, /**< module is native */
ECMA_MODULE_STATE_ROOT = 6, /**< module is a root module */
ECMA_MODULE_STATE_ERROR = 5, /**< error is encountered during module init */
ECMA_MODULE_STATE_NATIVE = 6, /**< module is native */
} ecma_module_state_t;
/**
* Module structure storing an instance of a module
*
* Note:
* The imports_p list follows the order of import-from/export-from statements in the source
* code of a module, even if a given module specifier is only used by export-from statements.
*/
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 */
struct ecma_module *next_p; /**< next module in the list */
struct ecma_module_node *imports_p; /**< import requests of the module */
struct ecma_module_node *local_exports_p; /**< local exports 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_string_t *path_p; /**< path 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 */
ecma_module_state_t state; /**< evaluation state of the module */
} ecma_module_t;
/**
* Module node to store imports / exports.
*
* Note:
* Only one module node is created for each module specifier: the names are
* concatenated if the same specifier is used multiple times in the source code.
* However, multiple nodes are created for modules with multiple alias
* (for example ./a.mjs and ././a.mjs can refer to the same module).
*/
typedef struct ecma_module_node
{
struct ecma_module_node *next_p; /**< next linked list node */
struct ecma_module_node *next_p; /**< next linked list node */
ecma_module_names_t *module_names_p; /**< names of the requested import/export node */
ecma_module_t *module_request_p; /**< module structure of the requested module */
union
{
ecma_value_t path_or_module; /**< imports: module specifier (if string) or module reference (if object) */
ecma_value_t *module_object_p; /**< non-imports: reference to a path_or_module field in the imports */
} u;
} ecma_module_node_t;
/**
@@ -106,31 +120,20 @@ typedef struct ecma_module_resolve_stack
bool resolving; /**< flag storing wether the current frame started resolving */
} ecma_module_resolve_stack_t;
bool ecma_module_resolve_set_insert (ecma_module_resolve_set_t **set_p,
ecma_module_t *const module_p,
ecma_string_t *const export_name_p);
void ecma_module_resolve_set_cleanup (ecma_module_resolve_set_t *set_p);
void ecma_module_resolve_stack_push (ecma_module_resolve_stack_t **stack_p,
ecma_module_t *const module_p,
ecma_string_t *const export_name_p);
void ecma_module_resolve_stack_pop (ecma_module_resolve_stack_t **stack_p);
ecma_string_t *ecma_module_create_normalized_path (const lit_utf8_byte_t *char_p,
lit_utf8_size_t size,
ecma_string_t *const base_path_p);
ecma_module_t *ecma_module_find_module (ecma_string_t *const path_p);
ecma_module_t *ecma_module_find_native_module (ecma_string_t *const path_p);
ecma_value_t ecma_module_parse_referenced_modules (void);
ecma_value_t ecma_module_initialize (ecma_module_t *module_p);
ecma_module_t *ecma_module_get_resolved_module (ecma_value_t module_val);
void ecma_module_initialize_context (const ecma_parse_options_t *options_p);
ecma_value_t ecma_module_link (ecma_module_t *module_p,
jerry_module_resolve_callback_t callback_p,
void *user_p);
ecma_value_t ecma_module_evaluate (ecma_module_t *module_p);
void ecma_module_initialize_context (void);
void ecma_module_cleanup_context (void);
void ecma_module_release_module_nodes (ecma_module_node_t *module_node_p);
void ecma_module_cleanup (ecma_module_t *head_p);
void ecma_module_release_module_names (ecma_module_names_t *module_name_p);
void ecma_module_release_module (ecma_module_t *module_p);
#endif /* JERRY_MODULE_SYSTEM */
#endif /* !ECMA_MODULE_H */