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:
@@ -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
@@ -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
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user