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
+121
View File
@@ -747,6 +747,37 @@ typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error
- [jerry_set_error_object_created_callback](#jerry_set_error_object_created_callback)
## jerry_module_resolve_callback_t
**Summary**
Callback which is called by [jerry_module_link](#jerry_module_link) to get the referenced module.
*Note*:
- If realms are enabled, the returned module should be created in the current realm
(see: [jerry_get_global_object](#jerry_get_global_object))
**Prototype**
```c
typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t specifier,
const jerry_value_t referrer,
void *user_p);
```
- `specifier` - a module specifier string (usually used as a path to the module)
- `referrer` - a module object which contains the `specifier` in its source code
- `user_p` - pointer passed to [jerry_module_link](#jerry_module_link).
- return value
- a module object - if it can be resolved successfully
- an error - otherwise
*New in version [[NEXT_RELEASE]]*.
**See also**
- [jerry_module_link](#jerry_module_link)
- [jerry_get_global_object](#jerry_get_global_object)
## jerry_backtrace_callback_t
**Summary**
@@ -4244,6 +4275,96 @@ jerry_value_as_uint32 (const jerry_value_t value);
}
```
# Functions for module objects
These APIs all depend on module support.
## jerry_module_link
**Summary**
Link modules to their dependencies. The dependencies are resolved by a user callback.
*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_module_link (const jerry_value_t module_val,
jerry_module_resolve_callback_t callback, void *user_p)
```
- `module_val` - module object in unlinked state
- `callback` - user callback which is called to resolve dependencies,
uses `jerry_port_module_resolve` when NULL is passed
- `user_p` - user pointer passed to the callback
- return
- true - if linking is successful
- error - otherwise
*New in version [[NEXT_RELEASE]]*.
**Example**
[doctest]: # (test="compile")
```c
#include <jerryscript.h>
static jerry_value_t
module_resolve_callback (const jerry_value_t specifier,
const jerry_value_t referrer,
void *user_data_p)
{
/* In this case, the specifier contains 'b.mjs', and the referrer is the module
* created in the main() function below. Normally the specifier string should be
* extended to a full file system path, and it should be checked whether a module
* corresponding to this path has been loaded already. For simplicity, this function
* returns with a new module. */
const jerry_char_t script[] = "export var a = 5";
const jerry_char_t file[] = "b.mjs";
jerry_parse_options_t parse_options;
parse_options.options = JERRY_PARSE_MODULE | JERRY_PARSE_HAS_RESOURCE;
parse_options.resource_name_p = file;
parse_options.resource_name_length = sizeof (file) - 1;
return jerry_parse (script, sizeof (script) - 1, &parse_options);
} /* module_resolve_callback */
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
const jerry_char_t script[] = "import a from 'b.mjs'";
const jerry_char_t file[] = "a.mjs";
jerry_parse_options_t parse_options;
parse_options.options = JERRY_PARSE_MODULE | JERRY_PARSE_HAS_RESOURCE;
parse_options.resource_name_p = file;
parse_options.resource_name_length = sizeof (file) - 1;
jerry_value_t ret_value = jerry_parse (script, sizeof (script) - 1, &parse_options);
jerry_module_link (ret_value, module_resolve_callback, NULL);
jerry_release_value (ret_value);
jerry_cleanup ();
return 0;
}
```
**See also**
- [jerry_module_resolve_callback_t](#jerry_module_resolve_callback_t)
# Functions for promise objects
These APIs all depend on the es.next profile (or on some build options).
+32 -36
View File
@@ -85,12 +85,10 @@ void jerry_port_print_char (char c);
### Jerry Module system
The port API provides functions that can be used by the module system to open
and close source files, and normalize file paths.
The `jerry_port_get_native_module` port function can be used to provide native
modules to the engine. This function will be called when an import/export
statement is encountered with an unknown module specifier, which embedders can
use to supply native module objects based on the module name argument.
The port API provides optional functions that can be used by the
user application to resolve modules. If no callback is provided
to `jerry_module_link`, the `jerry_port_module_resolve` function
is used for resolving modules.
```c
/**
@@ -115,40 +113,38 @@ jerry_port_release_source (uint8_t *buffer_p) /**< buffer to free */
} /* jerry_port_release_source */
/**
* Normalize a file path
* Default module resolver.
*
* @return length of the path written to the output buffer
*/
size_t
jerry_port_normalize_path (const char *in_path_p, /**< input file path */
char *out_buf_p, /**< output buffer */
size_t out_buf_size, /**< size of output buffer */
char *base_file_p) /**< base file path */
{
// normalize in_path_p by expanding relative paths etc.
// if base_file_p is not NULL, in_path_p is relative to that file
// write to out_buf_p the normalized path
// return length of written path
} /* jerry_port_normalize_path */
/**
* Get the module object of a native module.
*
* Note:
* This port function is called by jerry-core when JERRY_MODULE_SYSTEM
* is enabled.
*
* @param name String value of the module specifier.
*
* @return Undefined, if 'name' is not a native module
* jerry_value_t containing the module object, otherwise
* @return a module object if resolving is successful, an error otherwise
*/
jerry_value_t
jerry_port_get_native_module (jerry_value_t name) /**< module specifier */
jerry_port_module_resolve (const jerry_value_t specifier, /**< module specifier string */
const jerry_value_t referrer, /**< parent module */
void *user_p) /**< user data */
{
(void) name;
return jerry_create_undefined ();
}
// Resolves a module using the specifier string. If a referrer is a module,
// and specifier is a relative path, the base path should be the directory
// part extracted from the path of the referrer module.
// The callback function of jerry_module_link may call this function
// if it cannot resolve a module. Furthermore if the callback is NULL,
// this function is used for resolving modules.
// The default implementation only resolves ECMAScript modules, and does
// not (currently) use the user data.
} /* jerry_port_module_resolve */
/**
* Release known modules.
*/
void
jerry_port_module_release (const jerry_value_t realm) /**< if this argument is object, release only those modules,
* which realm value is equal to this argument. */
{
// This function releases the known modules, forcing their reload
// when resolved again later. The released modules can be filtered
// by realms. This function is only called by user applications.
} /* jerry_port_module_release */
```
## Date
+61 -31
View File
@@ -160,6 +160,18 @@ static const char * const error_json_not_supported_p = "JSON support is disabled
static const char * const error_symbol_not_supported_p = "Symbol support is disabled";
#endif /* !JERRY_ESNEXT */
#if JERRY_MODULE_SYSTEM
/**
* Error message, if argument is not a module
*/
static const char * const error_not_module_p = "Argument is not a module";
#else /* !JERRY_MODULE_SYSTEM */
/**
* Error message, if Module support is disabled
*/
static const char * const error_module_not_supported_p = "Module support is disabled";
#endif /* JERRY_MODULE_SYSTEM */
#if !JERRY_BUILTIN_PROMISE
/**
* Error message, if Promise support is disabled
@@ -528,9 +540,9 @@ 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 ((const ecma_parse_options_t *) options_p);
ecma_module_initialize_context ();
#else /* !JERRY_MODULE_SYSTEM */
return jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG ("Module system has been disabled")));
return jerry_throw (ecma_raise_syntax_error (ECMA_ERR_MSG (error_module_not_supported_p)));
#endif /* JERRY_MODULE_SYSTEM */
}
@@ -556,27 +568,12 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */
#if JERRY_MODULE_SYSTEM
if (JERRY_UNLIKELY (parse_opts & JERRY_PARSE_MODULE))
{
if (ECMA_IS_VALUE_ERROR (ecma_module_parse_referenced_modules ()))
{
ecma_bytecode_deref (bytecode_data_p);
ecma_module_cleanup_context ();
return ecma_create_error_reference_from_context ();
}
ecma_module_t *root_module_p = JERRY_CONTEXT (module_current_p);
root_module_p->compiled_code_p = bytecode_data_p;
ecma_object_t *obj_p = ecma_create_object (NULL, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS);
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;
ECMA_SET_INTERNAL_VALUE_POINTER (ext_object_p->u.class_prop.u.value, root_module_p);
ecma_module_t *module_p = JERRY_CONTEXT (module_current_p);
module_p->compiled_code_p = bytecode_data_p;
JERRY_CONTEXT (module_current_p) = NULL;
JERRY_CONTEXT (module_list_p) = NULL;
return ecma_make_object_value (obj_p);
return ecma_make_object_value ((ecma_object_t *) module_p);
}
#endif /* JERRY_MODULE_SYSTEM */
@@ -713,20 +710,16 @@ jerry_run (const jerry_value_t func_val) /**< function to run */
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
#if JERRY_MODULE_SYSTEM
if (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_MODULE_UL)
if (JERRY_UNLIKELY (ext_object_p->u.class_prop.class_id == 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_module_t *root_module_p = (ecma_module_t *) ext_object_p;
#if JERRY_BUILTIN_REALMS
ecma_object_t *global_object_p = (ecma_object_t *) ecma_op_function_get_realm (root_module_p->compiled_code_p);
#else /* !JERRY_BUILTIN_REALMS */
ecma_object_t *global_object_p = ecma_builtin_get_global ();
#endif /* JERRY_BUILTIN_REALMS */
if (root_module_p->header.u.class_prop.extra_info != ECMA_MODULE_STATE_LINKED)
{
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Module must be in linked state")));
}
ecma_create_global_lexical_block (global_object_p);
root_module_p->scope_p = ecma_get_global_scope (global_object_p);
return jerry_return (vm_run_module (root_module_p));
return jerry_return (ecma_module_evaluate (root_module_p));
}
#endif /* JERRY_MODULE_SYSTEM */
@@ -770,6 +763,43 @@ jerry_eval (const jerry_char_t *source_p, /**< source code */
parse_opts));
} /* jerry_eval */
/**
* Link modules to their dependencies. The dependencies are resolved by a user callback.
*
* Note:
* returned value must be freed with jerry_release_value, when it is no longer needed.
*
* @return true - if linking is successful, error - otherwise
*/
jerry_value_t
jerry_module_link (const jerry_value_t module_val, /**< root module */
jerry_module_resolve_callback_t callback, /**< resolve module callback, uses
* jerry_port_module_resolve when NULL is passed */
void *user_p) /**< pointer passed to the resolve callback */
{
#if JERRY_MODULE_SYSTEM
if (callback == NULL)
{
callback = jerry_port_module_resolve;
}
ecma_module_t *module_p = ecma_module_get_resolved_module (module_val);
if (module_p == NULL)
{
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_not_module_p)));
}
return jerry_return (ecma_module_link (module_p, callback, user_p));
#else /* !JERRY_MODULE_SYSTEM */
JERRY_UNUSED (module_val);
JERRY_UNUSED (callback);
JERRY_UNUSED (user_p);
return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (error_module_not_supported_p)));
#endif /* JERRY_MODULE_SYSTEM */
} /* jerry_module_link */
/**
* Run enqueued Promise jobs until the first thrown error or until all get executed.
*
+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 */
+7
View File
@@ -261,6 +261,13 @@ jerry_value_t jerry_object_get_property_names (const jerry_value_t obj_val, jerr
jerry_value_t jerry_from_property_descriptor (const jerry_property_descriptor_t *src_prop_desc_p);
jerry_value_t jerry_to_property_descriptor (jerry_value_t obj_value, jerry_property_descriptor_t *out_prop_desc_p);
/**
* Module functions.
*/
jerry_value_t jerry_module_link (const jerry_value_t module_val,
jerry_module_resolve_callback_t callback_p, void *user_p);
/**
* Promise functions.
*/
+16 -22
View File
@@ -217,39 +217,33 @@ uint8_t *jerry_port_read_source (const char *file_name_p, size_t *out_size_p);
void jerry_port_release_source (uint8_t *buffer_p);
/**
* Normalize a file path string.
* Default module resolver.
*
* Note:
* This port function is called by jerry-core when JERRY_MODULE_SYSTEM
* is enabled. The normalized path is used to uniquely identify modules.
* This port function is only used when JERRY_MODULE_SYSTEM is enabled.
*
* @param in_path_p Input path as a zero terminated string.
* @param out_buf_p Pointer to the output buffer where the normalized path should be written.
* @param out_buf_size Size of the output buffer.
* @param base_file_p A file path that 'in_path_p' is relative to, usually the current module file.
* A NULL value represents that 'in_path_p' is relative to the current working directory.
* @param specifier Module specifier string.
* @param referrer Parent module.
* @param user_p An unused pointer.
*
* @return length of the string written to the output buffer
* zero, if the buffer was not sufficient or an error occured
* @return A module object if resolving is successful, an error otherwise.
*/
size_t jerry_port_normalize_path (const char *in_path_p,
char *out_buf_p,
size_t out_buf_size,
char *base_file_p);
jerry_value_t
jerry_port_module_resolve (const jerry_value_t specifier,
const jerry_value_t referrer,
void *user_p);
/**
* Get the module object of a native module.
* Release known modules.
*
* Note:
* This port function is called by jerry-core when JERRY_MODULE_SYSTEM
* is enabled.
* This port function should be called by the user application when
* the module database is no longer needed.
*
* @param name String value of the module specifier.
*
* @return Undefined, if 'name' is not a native module
* jerry_value_t containing the module object, otherwise
* @param realm If this argument is object, release only those modules,
* which realm value is equal to this argument.
*/
jerry_value_t jerry_port_get_native_module (jerry_value_t name);
void jerry_port_module_release (const jerry_value_t realm);
/**
* @}
+8
View File
@@ -288,6 +288,13 @@ typedef void (*jerry_object_native_free_callback_t) (void *native_p);
*/
typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error_object, void *user_p);
/**
* Callback which is called by jerry_module_link to get the referenced module.
*/
typedef jerry_value_t (*jerry_module_resolve_callback_t) (const jerry_value_t specifier,
const jerry_value_t referrer,
void *user_p);
/**
* Callback which tells whether the ECMAScript execution should be stopped.
*
@@ -307,6 +314,7 @@ typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p);
typedef bool (*jerry_object_property_foreach_t) (const jerry_value_t property_name,
const jerry_value_t property_value,
void *user_data_p);
/**
* Function type applied for each object in the engine.
*/
-1
View File
@@ -148,7 +148,6 @@ struct jerry_context_t
#endif /* JERRY_ESNEXT */
#if JERRY_MODULE_SYSTEM
ecma_module_t *module_list_p; /**< current module context */
ecma_module_t *module_current_p; /**< current module context */
#endif /* JERRY_MODULE_SYSTEM */
+3 -5
View File
@@ -551,7 +551,7 @@ typedef struct
parser_stack_iterator_t last_statement; /**< last statement position */
#if JERRY_MODULE_SYSTEM
ecma_module_node_t *module_current_node_p; /**< import / export node that is being processed */
ecma_module_names_t *module_names_p; /**< import / export names that is being processed */
lexer_literal_t *module_identifier_lit_p; /**< the literal for the identifier of the current element */
#endif /* JERRY_MODULE_SYSTEM */
@@ -858,16 +858,14 @@ void parser_free_jumps (parser_stack_iterator_t iterator);
*/
extern const lexer_lit_location_t lexer_default_literal;
void parser_module_finalize_export_node (parser_context_t *context_p);
void parser_module_finalize_import_node (parser_context_t *context_p);
void parser_module_check_request_place (parser_context_t *context_p);
void parser_module_context_init (parser_context_t *context_p);
void parser_module_handle_module_specifier (parser_context_t *context_p);
void parser_module_append_names (parser_context_t *context_p, ecma_module_names_t **module_names_p);
void parser_module_handle_module_specifier (parser_context_t *context_p, ecma_module_node_t **node_list_p);
void parser_module_handle_requests (parser_context_t *context_p);
void parser_module_parse_export_clause (parser_context_t *context_p);
void parser_module_parse_import_clause (parser_context_t *context_p);
void parser_module_set_default (parser_context_t *context_p);
ecma_module_node_t *parser_module_create_module_node (parser_context_t *context_p);
bool parser_module_check_duplicate_import (parser_context_t *context_p, ecma_string_t *local_name_p);
bool parser_module_check_duplicate_export (parser_context_t *context_p, ecma_string_t *export_name_p);
void parser_module_append_export_name (parser_context_t *context_p);
+137 -165
View File
@@ -43,7 +43,8 @@ bool
parser_module_check_duplicate_import (parser_context_t *context_p, /**< parser context */
ecma_string_t *local_name_p) /**< newly imported name */
{
ecma_module_names_t *module_names_p = context_p->module_current_node_p->module_names_p;
ecma_module_names_t *module_names_p = context_p->module_names_p;
while (module_names_p != NULL)
{
if (ecma_compare_ecma_strings (module_names_p->local_name_p, local_name_p))
@@ -55,6 +56,7 @@ parser_module_check_duplicate_import (parser_context_t *context_p, /**< parser c
}
ecma_module_node_t *module_node_p = JERRY_CONTEXT (module_current_p)->imports_p;
while (module_node_p != NULL)
{
module_names_p = module_node_p->module_names_p;
@@ -113,7 +115,8 @@ parser_module_check_duplicate_export (parser_context_t *context_p, /**< parser c
ecma_string_t *export_name_p) /**< exported identifier */
{
/* We have to check in the currently constructed node, as well as all of the already added nodes. */
ecma_module_names_t *current_names_p = context_p->module_current_node_p->module_names_p;
ecma_module_names_t *current_names_p = context_p->module_names_p;
while (current_names_p != NULL)
{
if (ecma_compare_ecma_strings (current_names_p->imex_name_p, export_name_p))
@@ -123,27 +126,23 @@ parser_module_check_duplicate_export (parser_context_t *context_p, /**< parser c
current_names_p = current_names_p->next_p;
}
ecma_module_node_t *export_node_p = JERRY_CONTEXT (module_current_p)->local_exports_p;
if (export_node_p != NULL)
ecma_module_names_t *name_p = JERRY_CONTEXT (module_current_p)->local_exports_p;
while (name_p != NULL)
{
JERRY_ASSERT (export_node_p->next_p == NULL);
ecma_module_names_t *name_p = export_node_p->module_names_p;
while (name_p != NULL)
if (ecma_compare_ecma_strings (name_p->imex_name_p, export_name_p))
{
if (ecma_compare_ecma_strings (name_p->imex_name_p, export_name_p))
{
return true;
}
name_p = name_p->next_p;
return true;
}
name_p = name_p->next_p;
}
export_node_p = JERRY_CONTEXT (module_current_p)->indirect_exports_p;
ecma_module_node_t *export_node_p = JERRY_CONTEXT (module_current_p)->indirect_exports_p;
while (export_node_p != NULL)
{
ecma_module_names_t *name_p = export_node_p->module_names_p;
name_p = export_node_p->module_names_p;
while (name_p != NULL)
{
@@ -162,107 +161,6 @@ parser_module_check_duplicate_export (parser_context_t *context_p, /**< parser c
return false;
} /* parser_module_check_duplicate_export */
/**
* Add export node to parser context.
*/
void
parser_module_finalize_export_node (parser_context_t *context_p) /**< parser context */
{
ecma_module_node_t *module_node_p = context_p->module_current_node_p;
context_p->module_current_node_p = NULL;
ecma_module_node_t **export_list_p;
/* Check which list we should add it to. */
if (module_node_p->module_request_p)
{
/* If the export node has a module request, that means it's either an indirect export, or a star export. */
if (!module_node_p->module_names_p)
{
/* If there are no names in the node, then it's a star export. */
export_list_p = &(JERRY_CONTEXT (module_current_p)->star_exports_p);
}
else
{
export_list_p = &(JERRY_CONTEXT (module_current_p)->indirect_exports_p);
}
}
else
{
/* If there is no module request, then it's a local export. */
export_list_p = &(JERRY_CONTEXT (module_current_p)->local_exports_p);
}
/* Check if we have a node with the same module request, append to it if we do. */
ecma_module_node_t *stored_exports_p = *export_list_p;
while (stored_exports_p != NULL)
{
if (stored_exports_p->module_request_p == module_node_p->module_request_p)
{
ecma_module_names_t *module_names_p = module_node_p->module_names_p;
if (module_names_p != NULL)
{
while (module_names_p->next_p != NULL)
{
module_names_p = module_names_p->next_p;
}
module_names_p->next_p = stored_exports_p->module_names_p;
stored_exports_p->module_names_p = module_node_p->module_names_p;
module_node_p->module_names_p = NULL;
}
jmem_heap_free_block (module_node_p, sizeof (ecma_module_node_t));
return;
}
stored_exports_p = stored_exports_p->next_p;
}
module_node_p->next_p = *export_list_p;
*export_list_p = module_node_p;
} /* parser_module_finalize_export_node */
/**
* Add import node to parser context.
*/
void
parser_module_finalize_import_node (parser_context_t *context_p) /**< parser context */
{
ecma_module_node_t *module_node_p = context_p->module_current_node_p;
context_p->module_current_node_p = NULL;
ecma_module_node_t *stored_imports_p = JERRY_CONTEXT (module_current_p)->imports_p;
/* Check if we have a node with the same module request, append to it if we do. */
while (stored_imports_p != NULL)
{
if (stored_imports_p->module_request_p == module_node_p->module_request_p)
{
ecma_module_names_t *module_names_p = module_node_p->module_names_p;
if (module_names_p != NULL)
{
while (module_names_p->next_p != NULL)
{
module_names_p = module_names_p->next_p;
}
module_names_p->next_p = stored_imports_p->module_names_p;
stored_imports_p->module_names_p = module_node_p->module_names_p;
module_node_p->module_names_p = NULL;
}
jmem_heap_free_block (module_node_p, sizeof (ecma_module_node_t));
return;
}
stored_imports_p = stored_imports_p->next_p;
}
module_node_p->next_p = JERRY_CONTEXT (module_current_p)->imports_p;
JERRY_CONTEXT (module_current_p)->imports_p = module_node_p;
} /* parser_module_finalize_import_node */
/**
* Add module names to current module node.
*/
@@ -271,37 +169,21 @@ parser_module_add_names_to_node (parser_context_t *context_p, /**< parser contex
ecma_string_t *imex_name_p, /**< import/export name */
ecma_string_t *local_name_p) /**< local name */
{
ecma_module_names_t *new_names_p = (ecma_module_names_t *) parser_malloc (context_p,
sizeof (ecma_module_names_t));
memset (new_names_p, 0, sizeof (ecma_module_names_t));
ecma_module_names_t *new_name_p = (ecma_module_names_t *) parser_malloc (context_p,
sizeof (ecma_module_names_t));
ecma_module_node_t *module_node_p = context_p->module_current_node_p;
new_names_p->next_p = module_node_p->module_names_p;
module_node_p->module_names_p = new_names_p;
new_name_p->next_p = context_p->module_names_p;
context_p->module_names_p = new_name_p;
JERRY_ASSERT (imex_name_p != NULL);
ecma_ref_ecma_string (imex_name_p);
new_names_p->imex_name_p = imex_name_p;
new_name_p->imex_name_p = imex_name_p;
JERRY_ASSERT (local_name_p != NULL);
ecma_ref_ecma_string (local_name_p);
new_names_p->local_name_p = local_name_p;
new_name_p->local_name_p = local_name_p;
} /* parser_module_add_names_to_node */
/**
* Create a permanent import/export node from a template node.
* @return - the copy of the template if the second parameter is not NULL.
* - otherwise: an empty node.
*/
ecma_module_node_t *
parser_module_create_module_node (parser_context_t *context_p) /**< parser context */
{
ecma_module_node_t *node_p = (ecma_module_node_t *) parser_malloc (context_p, sizeof (ecma_module_node_t));
memset (node_p, 0, sizeof (ecma_module_node_t));
return node_p;
} /* parser_module_create_module_node */
/**
* Parse an ExportClause.
*/
@@ -529,13 +411,41 @@ parser_module_check_request_place (parser_context_t *context_p) /**< parser cont
}
} /* parser_module_check_request_place */
/**
* Append names to the names list.
*/
void
parser_module_append_names (parser_context_t *context_p, /**< parser context */
ecma_module_names_t **module_names_p) /**< target names */
{
ecma_module_names_t *last_name_p = context_p->module_names_p;
if (last_name_p == NULL)
{
return;
}
if (*module_names_p != NULL)
{
while (last_name_p->next_p != NULL)
{
last_name_p = last_name_p->next_p;
}
last_name_p->next_p = *module_names_p;
}
*module_names_p = context_p->module_names_p;
context_p->module_names_p = NULL;
} /* parser_module_append_names */
/**
* Handle module specifier at the end of the import / export statement.
*/
void
parser_module_handle_module_specifier (parser_context_t *context_p) /**< parser context */
parser_module_handle_module_specifier (parser_context_t *context_p, /**< parser context */
ecma_module_node_t **node_list_p) /**< target node list */
{
ecma_module_node_t *module_node_p = context_p->module_current_node_p;
if (context_p->token.type != LEXER_LITERAL
|| context_p->token.lit_location.type != LEXER_STRING_LITERAL
|| context_p->token.lit_location.length == 0)
@@ -545,32 +455,94 @@ parser_module_handle_module_specifier (parser_context_t *context_p) /**< parser
lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL);
ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->lit_object.literal_p->u.char_p,
context_p->lit_object.literal_p->prop.length);
lexer_literal_t *path_p = context_p->lit_object.literal_p;
ecma_module_t *module_p = ecma_module_find_native_module (name_p);
if (module_p)
{
ecma_deref_ecma_string (name_p);
goto module_found;
}
ecma_deref_ecma_string (name_p);
ecma_string_t *path_p = ecma_module_create_normalized_path (context_p->lit_object.literal_p->u.char_p,
context_p->lit_object.literal_p->prop.length,
JERRY_CONTEXT (module_current_p)->path_p);
if (path_p == NULL)
{
parser_raise_error (context_p, PARSER_ERR_FILE_NOT_FOUND);
}
module_p = ecma_module_find_module (path_p);
module_found:
module_node_p->module_request_p = module_p;
lexer_next_token (context_p);
/* The lexer_next_token may throw an error, so the path is constructed after its call. */
ecma_string_t *path_string_p = ecma_new_ecma_string_from_utf8 (path_p->u.char_p, path_p->prop.length);
ecma_module_node_t *node_p = JERRY_CONTEXT (module_current_p)->imports_p;
ecma_module_node_t *last_node_p = NULL;
/* Check if we have an import node with the same module request. */
while (node_p != NULL)
{
if (ecma_compare_ecma_strings (ecma_get_string_from_value (node_p->u.path_or_module), path_string_p))
{
ecma_deref_ecma_string (path_string_p);
break;
}
last_node_p = node_p;
node_p = node_p->next_p;
}
if (node_p == NULL)
{
node_p = (ecma_module_node_t *) jmem_heap_alloc_block_null_on_error (sizeof (ecma_module_node_t));
if (node_p == NULL)
{
ecma_deref_ecma_string (path_string_p);
parser_raise_error (context_p, PARSER_ERR_OUT_OF_MEMORY);
}
if (last_node_p == NULL)
{
JERRY_CONTEXT (module_current_p)->imports_p = node_p;
}
else
{
last_node_p->next_p = node_p;
}
node_p->next_p = NULL;
node_p->module_names_p = NULL;
node_p->u.path_or_module = ecma_make_string_value (path_string_p);
}
/* Append to imports. */
if (node_list_p == NULL)
{
parser_module_append_names (context_p, &node_p->module_names_p);
return;
}
ecma_value_t *module_object_p = &node_p->u.path_or_module;
node_p = *node_list_p;
last_node_p = NULL;
while (node_p != NULL)
{
if (node_p->u.module_object_p == module_object_p)
{
parser_module_append_names (context_p, &node_p->module_names_p);
return;
}
last_node_p = node_p;
node_p = node_p->next_p;
}
node_p = (ecma_module_node_t *) parser_malloc (context_p, sizeof (ecma_module_node_t));
if (last_node_p == NULL)
{
*node_list_p = node_p;
}
else
{
last_node_p->next_p = node_p;
}
node_p->next_p = NULL;
node_p->module_names_p = context_p->module_names_p;
node_p->u.module_object_p = module_object_p;
context_p->module_names_p = NULL;
} /* parser_module_handle_module_specifier */
#endif /* JERRY_MODULE_SYSTEM */
+9 -15
View File
@@ -2425,11 +2425,9 @@ static void
parser_parse_import_statement (parser_context_t *context_p) /**< parser context */
{
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_IMPORT);
JERRY_ASSERT (context_p->module_names_p == NULL);
parser_module_check_request_place (context_p);
context_p->module_current_node_p = parser_module_create_module_node (context_p);
lexer_next_token (context_p);
/* Check for a ModuleSpecifier*/
@@ -2482,7 +2480,7 @@ parser_parse_import_statement (parser_context_t *context_p) /**< parser context
if (context_p->token.type == LEXER_MULTIPLY)
{
/* NameSpaceImport*/
/* NameSpaceImport */
lexer_next_token (context_p);
if (!lexer_token_is_identifier (context_p, "as", 2))
{
@@ -2527,10 +2525,7 @@ parser_parse_import_statement (parser_context_t *context_p) /**< parser context
lexer_next_token (context_p);
}
parser_module_handle_module_specifier (context_p);
parser_module_finalize_import_node (context_p);
context_p->module_current_node_p = NULL;
parser_module_handle_module_specifier (context_p, NULL);
} /* parser_parse_import_statement */
/**
@@ -2543,11 +2538,10 @@ static bool
parser_parse_export_statement (parser_context_t *context_p) /**< context */
{
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXPORT);
JERRY_ASSERT (context_p->module_names_p == NULL);
parser_module_check_request_place (context_p);
context_p->module_current_node_p = parser_module_create_module_node (context_p);
bool consume_last_statement = false;
lexer_next_token (context_p);
@@ -2628,8 +2622,8 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */
}
lexer_next_token (context_p);
parser_module_handle_module_specifier (context_p);
break;
parser_module_handle_module_specifier (context_p, &(JERRY_CONTEXT (module_current_p)->star_exports_p));
return false;
}
case LEXER_KEYW_VAR:
case LEXER_KEYW_LET:
@@ -2660,7 +2654,8 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */
if (lexer_token_is_identifier (context_p, "from", 4))
{
lexer_next_token (context_p);
parser_module_handle_module_specifier (context_p);
parser_module_handle_module_specifier (context_p, &(JERRY_CONTEXT (module_current_p)->indirect_exports_p));
return false;
}
break;
}
@@ -2672,8 +2667,7 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */
}
context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC | PARSER_MODULE_STORE_IDENT);
parser_module_finalize_export_node (context_p);
context_p->module_current_node_p = NULL;
parser_module_append_names (context_p, &(JERRY_CONTEXT (module_current_p)->local_exports_p));
return consume_last_statement;
} /* parser_parse_export_statement */
+3 -3
View File
@@ -1810,7 +1810,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
context.status_flags |= PARSER_IS_STRICT;
}
context.module_current_node_p = NULL;
context.module_names_p = NULL;
#endif /* JERRY_MODULE_SYSTEM */
if (arg_list_p != NULL)
@@ -2051,9 +2051,9 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
scanner_cleanup (&context);
#if JERRY_MODULE_SYSTEM
if (context.module_current_node_p != NULL)
if (context.module_names_p != NULL)
{
ecma_module_release_module_nodes (context.module_current_node_p);
ecma_module_release_module_names (context.module_names_p);
}
#endif
+38 -8
View File
@@ -78,6 +78,43 @@ restart:
main_source_t *source_file_p = sources_p + source_index;
const char *file_path_p = argv[source_file_p->path_index];
if (source_file_p->type == SOURCE_MODULE)
{
jerry_value_t specifier = jerry_create_string_from_utf8 ((const jerry_char_t *) file_path_p);
jerry_value_t referrer = jerry_create_undefined ();
ret_value = jerry_port_module_resolve (specifier, referrer, NULL);
jerry_release_value (referrer);
jerry_release_value (specifier);
if (!jerry_value_is_error (ret_value))
{
jerry_value_t link_val = jerry_module_link (ret_value, NULL, NULL);
if (jerry_value_is_error (link_val))
{
jerry_release_value (ret_value);
ret_value = link_val;
}
else
{
jerry_release_value (link_val);
jerry_value_t func_val = ret_value;
ret_value = jerry_run (func_val);
jerry_release_value (func_val);
}
}
if (jerry_value_is_error (ret_value))
{
main_print_unhandled_exception (ret_value);
goto exit;
}
jerry_release_value (ret_value);
continue;
}
size_t source_size;
uint8_t *source_p = jerry_port_read_source (file_path_p, &source_size);
@@ -86,8 +123,6 @@ restart:
goto exit;
}
uint32_t parse_opts = JERRY_PARSE_NO_OPTS;
switch (source_file_p->type)
{
case SOURCE_SNAPSHOT:
@@ -100,11 +135,6 @@ restart:
jerry_port_release_source (source_p);
break;
}
case SOURCE_MODULE:
{
parse_opts = JERRY_PARSE_MODULE;
/* FALLTHRU */
}
default:
{
assert (source_file_p->type == SOURCE_SCRIPT
@@ -118,7 +148,7 @@ restart:
}
jerry_parse_options_t parse_options;
parse_options.options = parse_opts | JERRY_PARSE_HAS_RESOURCE;
parse_options.options = JERRY_PARSE_HAS_RESOURCE;
parse_options.resource_name_p = (jerry_char_t *) file_path_p;
parse_options.resource_name_length = (size_t) strlen (file_path_p);
+285 -74
View File
@@ -13,9 +13,6 @@
* limitations under the License.
*/
#if !defined (_WIN32)
#include <libgen.h>
#endif /* !defined (_WIN32) */
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -104,102 +101,316 @@ jerry_port_release_source (uint8_t *buffer_p) /**< buffer to free */
} /* jerry_port_release_source */
/**
* Normalize a file path
* Computes the end of the directory part of a path.
*
* @return length of the path written to the output buffer
* @return end of the directory part of a path.
*/
size_t
jerry_port_normalize_path (const char *in_path_p, /**< input file path */
char *out_buf_p, /**< output buffer */
size_t out_buf_size, /**< size of output buffer */
char *base_file_p) /**< base file path */
static size_t
jerry_port_get_directory_end (const jerry_char_t *path_p) /**< path */
{
size_t ret = 0;
const jerry_char_t *end_p = path_p + strlen ((const char *) path_p);
while (end_p > path_p)
{
#if defined (_WIN32)
size_t base_drive_dir_len;
const size_t in_path_len = strnlen (in_path_p, _MAX_PATH);
if (end_p[-1] == '/' || end_p[-1] == '\\')
{
return (size_t) (end_p - path_p);
}
#else /* !_WIN32 */
if (end_p[-1] == '/')
{
return (size_t) (end_p - path_p);
}
#endif /* _WIN32 */
end_p--;
}
return 0;
} /* jerry_port_get_directory_end */
/**
* Normalize a file path.
*
* @return a newly allocated buffer with the normalized path if the operation is successful,
* NULL otherwise
*/
static jerry_char_t *
jerry_port_normalize_path (const jerry_char_t *in_path_p, /**< path to the referenced module */
size_t in_path_length, /**< length of the path */
const jerry_char_t *base_path_p, /**< base path */
size_t base_path_length) /**< length of the base path */
{
char *path_p;
if (base_file_p != NULL)
if (base_path_length > 0)
{
char drive[_MAX_DRIVE];
char *dir_p = (char *) malloc (_MAX_DIR);
path_p = (char *) malloc (base_path_length + in_path_length + 1);
_splitpath_s (base_file_p, drive, _MAX_DRIVE, dir_p, _MAX_DIR, NULL, 0, NULL, 0);
const size_t drive_len = strnlen (drive, _MAX_DRIVE);
const size_t dir_len = strnlen (dir_p, _MAX_DIR);
base_drive_dir_len = drive_len + dir_len;
path_p = (char *) malloc (base_drive_dir_len + in_path_len + 1);
if (path_p == NULL)
{
return NULL;
}
memcpy (path_p, &drive, drive_len);
memcpy (path_p + drive_len, dir_p, dir_len);
free (dir_p);
memcpy (path_p, base_path_p, base_path_length);
memcpy (path_p + base_path_length, in_path_p, in_path_length);
path_p[base_path_length + in_path_length] = '\0';
}
else
{
base_drive_dir_len = 0;
path_p = (char *) malloc (in_path_len + 1);
}
path_p = (char *) malloc (in_path_length + 1);
memcpy (path_p + base_drive_dir_len, in_path_p, in_path_len + 1);
char *norm_p = _fullpath (out_buf_p, path_p, out_buf_size);
free (path_p);
if (norm_p != NULL)
{
ret = strnlen (norm_p, out_buf_size);
}
#elif defined (__unix__) || defined (__APPLE__)
char *base_dir_p = dirname (base_file_p);
const size_t base_dir_len = strnlen (base_dir_p, PATH_MAX);
const size_t in_path_len = strnlen (in_path_p, PATH_MAX);
char *path_p = (char *) malloc (base_dir_len + 1 + in_path_len + 1);
memcpy (path_p, base_dir_p, base_dir_len);
memcpy (path_p + base_dir_len, "/", 1);
memcpy (path_p + base_dir_len + 1, in_path_p, in_path_len + 1);
char *norm_p = realpath (path_p, NULL);
free (path_p);
if (norm_p != NULL)
{
const size_t norm_len = strnlen (norm_p, out_buf_size);
if (norm_len < out_buf_size)
if (path_p == NULL)
{
memcpy (out_buf_p, norm_p, norm_len + 1);
ret = norm_len;
return NULL;
}
free (norm_p);
memcpy (path_p, in_path_p, in_path_length);
path_p[in_path_length] = '\0';
}
#else
(void) base_file_p; /* unused */
/* Do nothing, just copy the input. */
const size_t in_path_len = strnlen (in_path_p, out_buf_size);
if (in_path_len < out_buf_size)
#if defined (_WIN32)
char full_path[_MAX_PATH];
if (_fullpath (full_path, path_p, _MAX_PATH) != NULL)
{
memcpy (out_buf_p, in_path_p, in_path_len + 1);
ret = in_path_len;
}
#endif
free (path_p);
return ret;
size_t full_path_len = strlen (full_path);
path_p = (char *) malloc (full_path_len + 1);
if (path_p == NULL)
{
return NULL;
}
memcpy (path_p, full_path, full_path_len + 1);
}
#elif defined (__unix__) || defined (__APPLE__)
char *norm_p = realpath (path_p, NULL);
if (norm_p != NULL)
{
free (path_p);
path_p = norm_p;
}
#endif /* _WIN32 */
return (jerry_char_t *) path_p;
} /* jerry_port_normalize_path */
/**
* Get the module object of a native module.
* A module descriptor.
*/
typedef struct jerry_port_module_t
{
struct jerry_port_module_t *next_p; /**< next_module */
jerry_char_t *path_p; /**< path to the module */
size_t base_path_length; /**< base path length for relative difference */
jerry_value_t realm; /**< the realm of the module */
jerry_value_t module; /**< the module itself */
} jerry_port_module_t;
/**
* Native info descriptor for modules.
*/
static const jerry_object_native_info_t jerry_port_module_native_info =
{
.free_cb = NULL,
};
/**
* Default module manager.
*/
typedef struct
{
jerry_port_module_t *module_head_p; /**< first module */
} jerry_port_module_manager_t;
/**
* Release known modules.
*/
static void
jerry_port_module_free (jerry_port_module_manager_t *manager_p, /**< module manager */
const jerry_value_t realm) /**< if this argument is object, release only those modules,
* which realm value is equal to this argument. */
{
jerry_port_module_t *module_p = manager_p->module_head_p;
bool release_all = !jerry_value_is_object (realm);
jerry_port_module_t *prev_p = NULL;
while (module_p != NULL)
{
jerry_port_module_t *next_p = module_p->next_p;
if (release_all || module_p->realm == realm)
{
free (module_p->path_p);
jerry_release_value (module_p->realm);
jerry_release_value (module_p->module);
free (module_p);
if (prev_p == NULL)
{
manager_p->module_head_p = next_p;
}
else
{
prev_p->next_p = next_p;
}
}
else
{
prev_p = module_p;
}
module_p = next_p;
}
} /* jerry_port_module_free */
/**
* Initialize the default module manager.
*/
static void
jerry_port_module_manager_init (void *user_data_p)
{
((jerry_port_module_manager_t *) user_data_p)->module_head_p = NULL;
} /* jerry_port_module_manager_init */
/**
* Deinitialize the default module manager.
*/
static void
jerry_port_module_manager_deinit (void *user_data_p) /**< context pointer to deinitialize */
{
jerry_value_t undef = jerry_create_undefined ();
jerry_port_module_free ((jerry_port_module_manager_t *) user_data_p, undef);
jerry_release_value (undef);
} /* jerry_port_module_manager_deinit */
/**
* Declare the context data manager for modules.
*/
static const jerry_context_data_manager_t jerry_port_module_manager =
{
.init_cb = jerry_port_module_manager_init,
.deinit_cb = jerry_port_module_manager_deinit,
.bytes_needed = sizeof (jerry_port_module_manager_t)
};
/**
* Default module resolver.
*
* @return Undefined, if 'name' is not a native module
* jerry_value_t containing the module object, otherwise
* @return a module object if resolving is successful, an error otherwise
*/
jerry_value_t
jerry_port_get_native_module (jerry_value_t name) /**< module specifier */
jerry_port_module_resolve (const jerry_value_t specifier, /**< module specifier string */
const jerry_value_t referrer, /**< parent module */
void *user_p) /**< user data */
{
(void) name;
return jerry_create_undefined ();
} /* jerry_port_get_native_module */
(void) user_p;
jerry_port_module_t *module_p;
const jerry_char_t *base_path_p = NULL;
size_t base_path_length = 0;
if (jerry_get_object_native_pointer (referrer, (void **) &module_p, &jerry_port_module_native_info))
{
base_path_p = module_p->path_p;
base_path_length = module_p->base_path_length;
}
jerry_size_t in_path_length = jerry_get_utf8_string_size (specifier);
jerry_char_t *in_path_p = (jerry_char_t *) malloc (in_path_length + 1);
jerry_string_to_utf8_char_buffer (specifier, in_path_p, in_path_length);
in_path_p[in_path_length] = '\0';
jerry_char_t *path_p = jerry_port_normalize_path (in_path_p, in_path_length, base_path_p, base_path_length);
if (path_p == NULL)
{
return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Out of memory");
}
jerry_value_t realm = jerry_get_global_object ();
jerry_port_module_manager_t *manager_p;
manager_p = (jerry_port_module_manager_t *) jerry_get_context_data (&jerry_port_module_manager);
module_p = manager_p->module_head_p;
while (module_p != NULL)
{
if (module_p->realm == realm
&& strcmp ((const char *) module_p->path_p, (const char *) path_p) == 0)
{
free (path_p);
free (in_path_p);
jerry_release_value (realm);
return jerry_acquire_value (module_p->module);
}
module_p = module_p->next_p;
}
size_t source_size;
uint8_t *source_p = jerry_port_read_source ((const char *) path_p, &source_size);
if (source_p == NULL)
{
free (path_p);
free (in_path_p);
jerry_release_value (realm);
/* TODO: This is incorrect, but makes test262 module tests pass
* (they should throw SyntaxError, but not because the module cannot be found). */
return jerry_create_error (JERRY_ERROR_SYNTAX, (const jerry_char_t *) "Module file not found");
}
jerry_parse_options_t parse_options;
parse_options.options = JERRY_PARSE_MODULE | JERRY_PARSE_HAS_RESOURCE;
parse_options.resource_name_p = (jerry_char_t *) in_path_p;
parse_options.resource_name_length = (size_t) in_path_length;
jerry_value_t ret_value = jerry_parse (source_p,
source_size,
&parse_options);
jerry_port_release_source (source_p);
free (in_path_p);
if (jerry_value_is_error (ret_value))
{
free (path_p);
jerry_release_value (realm);
return ret_value;
}
module_p = (jerry_port_module_t *) malloc (sizeof (jerry_port_module_t));
module_p->next_p = manager_p->module_head_p;
module_p->path_p = path_p;
module_p->base_path_length = jerry_port_get_directory_end (module_p->path_p);
module_p->realm = realm;
module_p->module = jerry_acquire_value (ret_value);
jerry_set_object_native_pointer (ret_value, module_p, &jerry_port_module_native_info);
manager_p->module_head_p = module_p;
return ret_value;
} /* jerry_port_module_resolve */
/**
* Release known modules.
*/
void
jerry_port_module_release (const jerry_value_t realm) /**< if this argument is object, release only those modules,
* which realm value is equal to this argument. */
{
jerry_port_module_free ((jerry_port_module_manager_t *) jerry_get_context_data (&jerry_port_module_manager),
realm);
} /* jerry_port_module_release */
+1 -1
View File
@@ -37,7 +37,7 @@ LIBS = libjerry-core.a libjerry-ext.a libjerry-math.a
APPNAME = jerry
ASRCS = setjmp.S
CSRCS = jerry_port.c
CSRCS = jerry_port.c jerry_module.c
MAINSRC = jerry_main.c
.PHONY: copylibs
+302
View File
@@ -0,0 +1,302 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "jerryscript.h"
#include "jerryscript-port.h"
/**
* Computes the end of the directory part of a path.
*
* @return end of the directory part of a path.
*/
static size_t
jerry_port_get_directory_end (const jerry_char_t *path_p) /**< path */
{
const jerry_char_t *end_p = path_p + strlen ((const char *) path_p);
while (end_p > path_p)
{
if (end_p[-1] == '/')
{
return (size_t) (end_p - path_p);
}
end_p--;
}
return 0;
} /* jerry_port_get_directory_end */
/**
* Normalize a file path.
*
* @return a newly allocated buffer with the normalized path if the operation is successful,
* NULL otherwise
*/
static jerry_char_t *
jerry_port_normalize_path (const jerry_char_t *in_path_p, /**< path to the referenced module */
size_t in_path_length, /**< length of the path */
const jerry_char_t *base_path_p, /**< base path */
size_t base_path_length) /**< length of the base path */
{
char *path_p;
if (base_path_length > 0)
{
path_p = (char *) malloc (base_path_length + in_path_length + 1);
if (path_p == NULL)
{
return NULL;
}
memcpy (path_p, base_path_p, base_path_length);
memcpy (path_p + base_path_length, in_path_p, in_path_length);
path_p[base_path_length + in_path_length] = '\0';
}
else
{
path_p = (char *) malloc (in_path_length + 1);
if (path_p == NULL)
{
return NULL;
}
memcpy (path_p, in_path_p, in_path_length);
path_p[in_path_length] = '\0';
}
return (jerry_char_t *) path_p;
} /* jerry_port_normalize_path */
/**
* A module descriptor.
*/
typedef struct jerry_port_module_t
{
struct jerry_port_module_t *next_p; /**< next_module */
jerry_char_t *path_p; /**< path to the module */
size_t base_path_length; /**< base path length for relative difference */
jerry_value_t realm; /**< the realm of the module */
jerry_value_t module; /**< the module itself */
} jerry_port_module_t;
/**
* Native info descriptor for modules.
*/
static const jerry_object_native_info_t jerry_port_module_native_info =
{
.free_cb = NULL,
};
/**
* Default module manager.
*/
typedef struct
{
jerry_port_module_t *module_head_p; /**< first module */
} jerry_port_module_manager_t;
/**
* Release known modules.
*/
static void
jerry_port_module_free (jerry_port_module_manager_t *manager_p, /**< module manager */
const jerry_value_t realm) /**< if this argument is object, release only those modules,
* which realm value is equal to this argument. */
{
jerry_port_module_t *module_p = manager_p->module_head_p;
bool release_all = !jerry_value_is_object (realm);
jerry_port_module_t *prev_p = NULL;
while (module_p != NULL)
{
jerry_port_module_t *next_p = module_p->next_p;
if (release_all || module_p->realm == realm)
{
free (module_p->path_p);
jerry_release_value (module_p->realm);
jerry_release_value (module_p->module);
free (module_p);
if (prev_p == NULL)
{
manager_p->module_head_p = next_p;
}
else
{
prev_p->next_p = next_p;
}
}
else
{
prev_p = module_p;
}
module_p = next_p;
}
} /* jerry_port_module_free */
/**
* Initialize the default module manager.
*/
static void
jerry_port_module_manager_init (void *user_data_p)
{
((jerry_port_module_manager_t *) user_data_p)->module_head_p = NULL;
} /* jerry_port_module_manager_init */
/**
* Deinitialize the default module manager.
*/
static void
jerry_port_module_manager_deinit (void *user_data_p) /**< context pointer to deinitialize */
{
jerry_value_t undef = jerry_create_undefined ();
jerry_port_module_free ((jerry_port_module_manager_t *) user_data_p, undef);
jerry_release_value (undef);
} /* jerry_port_module_manager_deinit */
/**
* Declare the context data manager for modules.
*/
static const jerry_context_data_manager_t jerry_port_module_manager =
{
.init_cb = jerry_port_module_manager_init,
.deinit_cb = jerry_port_module_manager_deinit,
.bytes_needed = sizeof (jerry_port_module_manager_t)
};
/**
* Default module resolver.
*
* @return a module object if resolving is successful, an error otherwise
*/
jerry_value_t
jerry_port_module_resolve (const jerry_value_t specifier, /**< module specifier string */
const jerry_value_t referrer, /**< parent module */
void *user_p) /**< user data */
{
(void) user_p;
jerry_port_module_t *module_p;
const jerry_char_t *base_path_p = NULL;
size_t base_path_length = 0;
if (jerry_get_object_native_pointer (referrer, (void **) &module_p, &jerry_port_module_native_info))
{
base_path_p = module_p->path_p;
base_path_length = module_p->base_path_length;
}
jerry_size_t in_path_length = jerry_get_utf8_string_size (specifier);
jerry_char_t *in_path_p = (jerry_char_t *) malloc (in_path_length + 1);
jerry_string_to_utf8_char_buffer (specifier, in_path_p, in_path_length);
in_path_p[in_path_length] = '\0';
jerry_char_t *path_p = jerry_port_normalize_path (in_path_p, in_path_length, base_path_p, base_path_length);
if (path_p == NULL)
{
return jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Out of memory");
}
jerry_value_t realm = jerry_get_global_object ();
jerry_port_module_manager_t *manager_p;
manager_p = (jerry_port_module_manager_t *) jerry_get_context_data (&jerry_port_module_manager);
module_p = manager_p->module_head_p;
while (module_p != NULL)
{
if (module_p->realm == realm
&& strcmp ((const char *) module_p->path_p, (const char *) path_p) == 0)
{
free (path_p);
free (in_path_p);
jerry_release_value (realm);
return jerry_acquire_value (module_p->module);
}
module_p = module_p->next_p;
}
size_t source_size;
uint8_t *source_p = jerry_port_read_source ((const char *) path_p, &source_size);
if (source_p == NULL)
{
free (path_p);
free (in_path_p);
jerry_release_value (realm);
/* TODO: This is incorrect, but makes test262 module tests pass
* (they should throw SyntaxError, but not because the module cannot be found). */
return jerry_create_error (JERRY_ERROR_SYNTAX, (const jerry_char_t *) "Module file not found");
}
jerry_parse_options_t parse_options;
parse_options.options = JERRY_PARSE_MODULE | JERRY_PARSE_HAS_RESOURCE;
parse_options.resource_name_p = (jerry_char_t *) in_path_p;
parse_options.resource_name_length = (size_t) in_path_length;
jerry_value_t ret_value = jerry_parse (source_p,
source_size,
&parse_options);
jerry_port_release_source (source_p);
free (in_path_p);
if (jerry_value_is_error (ret_value))
{
free (path_p);
jerry_release_value (realm);
return ret_value;
}
module_p = (jerry_port_module_t *) malloc (sizeof (jerry_port_module_t));
module_p->next_p = manager_p->module_head_p;
module_p->path_p = path_p;
module_p->base_path_length = jerry_port_get_directory_end (module_p->path_p);
module_p->realm = realm;
module_p->module = jerry_acquire_value (ret_value);
jerry_set_object_native_pointer (ret_value, module_p, &jerry_port_module_native_info);
manager_p->module_head_p = module_p;
return ret_value;
} /* jerry_port_module_resolve */
/**
* Release known modules.
*/
void
jerry_port_module_release (const jerry_value_t realm) /**< if this argument is object, release only those modules,
* which realm value is equal to this argument. */
{
jerry_port_module_free ((jerry_port_module_manager_t *) jerry_get_context_data (&jerry_port_module_manager),
realm);
} /* jerry_port_module_release */
-36
View File
@@ -126,42 +126,6 @@ jerry_port_release_source (uint8_t *buffer_p) /**< buffer to free */
free (buffer_p);
} /* jerry_port_release_source */
/**
* Normalize a file path
*
* @return length of the path written to the output buffer
*/
size_t
jerry_port_normalize_path (const char *in_path_p, /**< input file path */
char *out_buf_p, /**< output buffer */
size_t out_buf_size, /**< size of output buffer */
char *base_file_p) /**< base file path */
{
(void) base_file_p;
size_t len = strlen (in_path_p);
if (len + 1 > out_buf_size)
{
return 0;
}
/* Return the original string. */
strcpy (out_buf_p, in_path_p);
return len;
} /* jerry_port_normalize_path */
/**
* Get the module object of a native module.
*
* @return undefined
*/
jerry_value_t
jerry_port_get_native_module (jerry_value_t name) /**< module specifier */
{
(void) name;
return jerry_create_undefined ();
} /* jerry_port_get_native_module */
/**
* Dummy function to get the time zone adjustment.
*
+1
View File
@@ -58,6 +58,7 @@ set(SOURCE_UNIT_TEST_MAIN_MODULES
test-lit-char-helpers.c
test-literal-storage.c
test-mem-stats.c
test-module.c
test-native-callback-nested.c
test-native-instanceof.c
test-native-pointer.c
+185
View File
@@ -0,0 +1,185 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include "jerryscript.h"
#include "test-common.h"
static void
compare_specifier (jerry_value_t specifier, /* string value */
int id) /* module id */
{
jerry_char_t string[] = "XX_module.mjs";
TEST_ASSERT (id >= 1 && id <= 99 && string[0] == 'X' && string[1] == 'X');
string[0] = (jerry_char_t) ((id / 10) + '0');
string[1] = (jerry_char_t) ((id % 10) + '0');
jerry_size_t length = (jerry_size_t) (sizeof (string) - 1);
jerry_char_t buffer[sizeof (string) - 1];
TEST_ASSERT (jerry_value_is_string (specifier));
TEST_ASSERT (jerry_get_string_size (specifier) == length);
TEST_ASSERT (jerry_string_to_char_buffer (specifier, buffer, length) == length);
TEST_ASSERT (memcmp (buffer, string, length) == 0);
} /* compare_specifier */
static jerry_value_t
create_module (int id) /**< module id */
{
jerry_parse_options_t module_parse_options;
module_parse_options.options = JERRY_PARSE_MODULE;
jerry_value_t result;
if (id == 0)
{
result = jerry_parse ((jerry_char_t *) "", 0, &module_parse_options);
}
else
{
jerry_char_t source[] = "import a from 'XX_module.mjs'";
TEST_ASSERT (id >= 1 && id <= 99 && source[15] == 'X' && source[16] == 'X');
source[15] = (jerry_char_t) ((id / 10) + '0');
source[16] = (jerry_char_t) ((id % 10) + '0');
result = jerry_parse (source, sizeof (source) - 1, &module_parse_options);
}
TEST_ASSERT (!jerry_value_is_error (result));
return result;
} /* create_module */
static int counter = 0;
static jerry_value_t module;
static jerry_value_t
resolve_callback1 (const jerry_value_t specifier, /**< module specifier */
const jerry_value_t referrer, /**< parent module */
void *user_p) /**< user data */
{
TEST_ASSERT (user_p == (void *) &module);
TEST_ASSERT (referrer == module);
compare_specifier (specifier, 1);
counter++;
return counter == 1 ? jerry_create_number (7) : jerry_create_object ();
} /* resolve_callback1 */
static jerry_value_t prev_module;
static bool terminate_with_error;
static jerry_value_t
resolve_callback2 (const jerry_value_t specifier, /**< module specifier */
const jerry_value_t referrer, /**< parent module */
void *user_p) /**< user data */
{
TEST_ASSERT (prev_module == referrer);
TEST_ASSERT (user_p == NULL);
compare_specifier (specifier, ++counter);
if (counter >= 32)
{
if (terminate_with_error)
{
return jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) "Module not found");
}
return create_module (0);
}
prev_module = create_module (counter + 1);
return prev_module;
} /* resolve_callback2 */
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
if (!jerry_is_feature_enabled (JERRY_FEATURE_MODULE))
{
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Module is disabled!\n");
jerry_cleanup ();
return 0;
}
jerry_value_t number = jerry_create_number (5);
jerry_value_t object = jerry_create_object ();
jerry_value_t result = jerry_module_link (number, resolve_callback1, NULL);
TEST_ASSERT (jerry_value_is_error (result));
jerry_release_value (result);
result = jerry_module_link (object, resolve_callback1, NULL);
TEST_ASSERT (jerry_value_is_error (result));
jerry_release_value (result);
module = create_module (1);
/* After an error, module must remain in unlinked mode. */
result = jerry_module_link (module, resolve_callback1, (void *) &module);
TEST_ASSERT (jerry_value_is_error (result));
TEST_ASSERT (counter == 1);
jerry_release_value (result);
result = jerry_module_link (module, resolve_callback1, (void *) &module);
TEST_ASSERT (jerry_value_is_error (result));
TEST_ASSERT (counter == 2);
jerry_release_value (result);
prev_module = module;
counter = 0;
terminate_with_error = true;
result = jerry_module_link (module, resolve_callback2, NULL);
TEST_ASSERT (jerry_value_is_error (result));
TEST_ASSERT (counter == 32);
jerry_release_value (result);
/* The successfully resolved modules is kept around in unlinked state. */
jerry_gc (JERRY_GC_PRESSURE_HIGH);
counter = 31;
terminate_with_error = false;
result = jerry_module_link (module, resolve_callback2, NULL);
TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result));
TEST_ASSERT (counter == 32);
jerry_release_value (result);
jerry_release_value (module);
module = create_module (1);
prev_module = module;
counter = 0;
terminate_with_error = false;
result = jerry_module_link (module, resolve_callback2, NULL);
TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result));
TEST_ASSERT (counter == 32);
jerry_release_value (result);
jerry_release_value (module);
jerry_release_value (object);
jerry_release_value (number);
jerry_cleanup ();
return 0;
} /* main */