mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
1478 lines
46 KiB
C
1478 lines
46 KiB
C
/* 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 "jcontext.h"
|
|
|
|
#include "ecma-exceptions.h"
|
|
#include "ecma-function-object.h"
|
|
#include "ecma-gc.h"
|
|
#include "ecma-globals.h"
|
|
#include "ecma-helpers.h"
|
|
#include "ecma-lex-env.h"
|
|
#include "ecma-module.h"
|
|
#include "ecma-objects.h"
|
|
#include "lit-char-helpers.h"
|
|
#include "vm.h"
|
|
|
|
#if JERRY_MODULE_SYSTEM
|
|
|
|
/**
|
|
* Type of the result returned by ecma_module_resolve_export.
|
|
*/
|
|
typedef enum
|
|
{
|
|
ECMA_MODULE_RESOLVE_NOT_FOUND, /**< reference not found */
|
|
ECMA_MODULE_RESOLVE_CIRCULAR, /**< only circular references are found */
|
|
ECMA_MODULE_RESOLVE_ERROR, /**< module in error state is encountered */
|
|
ECMA_MODULE_RESOLVE_AMBIGUOUS, /**< reference is ambiguous */
|
|
ECMA_MODULE_RESOLVE_FOUND, /**< reference found */
|
|
} ecma_module_resolve_result_type_t;
|
|
|
|
/**
|
|
* A record that stores the result of ecma_module_resolve_export.
|
|
*/
|
|
typedef struct
|
|
{
|
|
ecma_module_resolve_result_type_t result_type; /**< result type */
|
|
ecma_value_t result; /**< result value */
|
|
} ecma_module_resolve_result_t;
|
|
|
|
/**
|
|
* This flag is set in the result if the value is a namespace object.
|
|
*/
|
|
#define ECMA_MODULE_NAMESPACE_RESULT_FLAG 0x2
|
|
|
|
/**
|
|
* Initialize context variables for the root module.
|
|
*
|
|
* @return new module
|
|
*/
|
|
ecma_module_t *
|
|
ecma_module_create (void)
|
|
{
|
|
JERRY_ASSERT (JERRY_CONTEXT (module_current_p) == NULL);
|
|
|
|
ecma_object_t *obj_p = ecma_create_object (NULL, sizeof (ecma_module_t), ECMA_OBJECT_TYPE_CLASS);
|
|
|
|
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p;
|
|
ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_MODULE;
|
|
ext_object_p->u.cls.u1.module_state = JERRY_MODULE_STATE_UNLINKED;
|
|
ext_object_p->u.cls.u2.module_flags = 0;
|
|
|
|
ecma_module_t *module_p = (ecma_module_t *) obj_p;
|
|
|
|
module_p->scope_p = NULL;
|
|
module_p->namespace_object_p = NULL;
|
|
module_p->imports_p = NULL;
|
|
module_p->local_exports_p = NULL;
|
|
module_p->indirect_exports_p = NULL;
|
|
module_p->star_exports_p = NULL;
|
|
module_p->u.compiled_code_p = NULL;
|
|
|
|
return module_p;
|
|
} /* ecma_module_create */
|
|
|
|
/**
|
|
* Cleanup context variables for the root module.
|
|
*/
|
|
void
|
|
ecma_module_cleanup_context (void)
|
|
{
|
|
ecma_deref_object ((ecma_object_t *) JERRY_CONTEXT (module_current_p));
|
|
#ifndef JERRY_NDEBUG
|
|
JERRY_CONTEXT (module_current_p) = NULL;
|
|
#endif /* JERRY_NDEBUG */
|
|
} /* ecma_module_cleanup_context */
|
|
|
|
/**
|
|
* Sets module state to error.
|
|
*/
|
|
static void
|
|
ecma_module_set_error_state (ecma_module_t *module_p) /**< module */
|
|
{
|
|
module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_ERROR;
|
|
|
|
if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL
|
|
&& !jcontext_has_pending_abort ())
|
|
{
|
|
jerry_value_t exception = jcontext_take_exception ();
|
|
|
|
JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_ERROR,
|
|
ecma_make_object_value (&module_p->header.object),
|
|
exception,
|
|
JERRY_CONTEXT (module_state_changed_callback_user_p));
|
|
jcontext_raise_exception (exception);
|
|
}
|
|
} /* ecma_module_set_error_state */
|
|
|
|
/**
|
|
* 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_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE));
|
|
|
|
return (ecma_module_t *) object_p;
|
|
} /* ecma_module_get_from_object */
|
|
|
|
/**
|
|
* Cleans up a list of module names.
|
|
*/
|
|
void
|
|
ecma_module_release_module_names (ecma_module_names_t *module_name_p) /**< first module name */
|
|
{
|
|
while (module_name_p != NULL)
|
|
{
|
|
ecma_module_names_t *next_p = module_name_p->next_p;
|
|
|
|
ecma_deref_ecma_string (module_name_p->imex_name_p);
|
|
ecma_deref_ecma_string (module_name_p->local_name_p);
|
|
jmem_heap_free_block (module_name_p, sizeof (ecma_module_names_t));
|
|
|
|
module_name_p = next_p;
|
|
}
|
|
} /* ecma_module_release_module_names */
|
|
|
|
/**
|
|
* Cleans up a list of module nodes.
|
|
*/
|
|
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);
|
|
|
|
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 */
|
|
|
|
/**
|
|
* Creates a new resolve set item from a {module, export_name} pair.
|
|
*
|
|
* @return new resolve set item
|
|
*/
|
|
static ecma_module_resolve_set_t *
|
|
ecma_module_resolve_set_create (ecma_module_t *const module_p, /**< module */
|
|
ecma_string_t *const export_name_p) /**< export name */
|
|
{
|
|
ecma_module_resolve_set_t *new_p;
|
|
new_p = (ecma_module_resolve_set_t *) jmem_heap_alloc_block (sizeof (ecma_module_resolve_set_t));
|
|
|
|
new_p->next_p = NULL;
|
|
new_p->module_p = module_p;
|
|
new_p->name_p = export_name_p;
|
|
|
|
return new_p;
|
|
} /* ecma_module_resolve_set_create */
|
|
|
|
/**
|
|
* Appends a {module, export_name} record into a resolve set.
|
|
*
|
|
* @return true - if the record is appended successfully
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
ecma_module_resolve_set_append (ecma_module_resolve_set_t *set_p, /**< resolve set */
|
|
ecma_module_t *const module_p, /**< module */
|
|
ecma_string_t *const export_name_p) /**< export name */
|
|
{
|
|
JERRY_ASSERT (set_p != NULL);
|
|
ecma_module_resolve_set_t *current_p = set_p;
|
|
|
|
while (true)
|
|
{
|
|
if (current_p->module_p == module_p
|
|
&& ecma_compare_ecma_strings (current_p->name_p, export_name_p))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ecma_module_resolve_set_t *next_p = current_p->next_p;
|
|
|
|
if (next_p == NULL)
|
|
{
|
|
current_p->next_p = ecma_module_resolve_set_create (module_p, export_name_p);
|
|
return true;
|
|
}
|
|
|
|
current_p = next_p;
|
|
}
|
|
} /* ecma_module_resolve_set_append */
|
|
|
|
/**
|
|
* Cleans up contents of a resolve set.
|
|
*/
|
|
static void
|
|
ecma_module_resolve_set_cleanup (ecma_module_resolve_set_t *set_p) /**< resolve set */
|
|
{
|
|
while (set_p != NULL)
|
|
{
|
|
ecma_module_resolve_set_t *next_p = set_p->next_p;
|
|
jmem_heap_free_block (set_p, sizeof (ecma_module_resolve_set_t));
|
|
set_p = next_p;
|
|
}
|
|
} /* ecma_module_resolve_set_cleanup */
|
|
|
|
/**
|
|
* Throws the appropriate error based on the resolve result
|
|
*
|
|
* @return error value
|
|
*/
|
|
static ecma_value_t
|
|
ecma_module_resolve_throw (ecma_module_resolve_result_t *resolve_result_p, /**< resolve result */
|
|
ecma_string_t *name_p) /**< referenced value */
|
|
{
|
|
#if JERRY_ERROR_MESSAGES
|
|
ecma_value_t name_val = ecma_make_string_value (name_p);
|
|
const char *msg_p;
|
|
|
|
switch (resolve_result_p->result_type)
|
|
{
|
|
case ECMA_MODULE_RESOLVE_CIRCULAR:
|
|
{
|
|
msg_p = "Detected cycle while resolving name '%' (module)";
|
|
break;
|
|
}
|
|
case ECMA_MODULE_RESOLVE_AMBIGUOUS:
|
|
{
|
|
msg_p = "Name '%' is ambiguous (module)";
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_ASSERT (resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND
|
|
|| resolve_result_p->result_type == ECMA_MODULE_RESOLVE_ERROR);
|
|
|
|
msg_p = "Name '%' is not found (module)";
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ecma_raise_standard_error_with_format (JERRY_ERROR_SYNTAX, msg_p, name_val);
|
|
#else /* JERRY_ERROR_MESSAGES */
|
|
JERRY_UNUSED (resolve_result_p);
|
|
JERRY_UNUSED (name_p);
|
|
|
|
return ecma_raise_syntax_error (NULL);
|
|
#endif /* !JERRY_ERROR_MESSAGES */
|
|
} /* ecma_module_resolve_throw */
|
|
|
|
/**
|
|
* Updates the resolve record with the passed type/value pair
|
|
*
|
|
* @return true - if the record is updated successfully
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
ecma_module_resolve_update (ecma_module_resolve_result_t *resolve_result_p, /**< [in,out] resolve result */
|
|
ecma_value_t result) /**< result value */
|
|
{
|
|
JERRY_ASSERT (resolve_result_p->result_type != ECMA_MODULE_RESOLVE_AMBIGUOUS
|
|
&& resolve_result_p->result_type != ECMA_MODULE_RESOLVE_ERROR);
|
|
|
|
if (resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND
|
|
|| resolve_result_p->result_type == ECMA_MODULE_RESOLVE_CIRCULAR)
|
|
{
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_FOUND;
|
|
resolve_result_p->result = result;
|
|
return true;
|
|
}
|
|
|
|
JERRY_ASSERT (resolve_result_p->result_type == ECMA_MODULE_RESOLVE_FOUND);
|
|
|
|
if (resolve_result_p->result == result)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_AMBIGUOUS;
|
|
return false;
|
|
} /* ecma_module_resolve_update */
|
|
|
|
/**
|
|
* Finds the reference in the imported bindings.
|
|
*
|
|
* Note:
|
|
* This function is needed because the namespace object is created before the imports are connected
|
|
*
|
|
* @return true - if the record is updated successfully
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
ecma_module_resolve_import (ecma_module_resolve_result_t *resolve_result_p, /**< [in,out] resolve result */
|
|
ecma_module_resolve_set_t *resolve_set_p, /**< resolve set */
|
|
ecma_module_t *module_p, /**< base module */
|
|
ecma_string_t *local_name_p) /**< local name */
|
|
{
|
|
ecma_module_node_t *import_node_p = module_p->imports_p;
|
|
|
|
while (true)
|
|
{
|
|
JERRY_ASSERT (import_node_p != NULL);
|
|
|
|
for (ecma_module_names_t *import_names_p = import_node_p->module_names_p;
|
|
import_names_p != NULL;
|
|
import_names_p = import_names_p->next_p)
|
|
{
|
|
if (ecma_compare_ecma_strings (local_name_p, import_names_p->local_name_p))
|
|
{
|
|
ecma_module_t *imported_module_p = ecma_module_get_from_object (import_node_p->u.path_or_module);
|
|
|
|
if (ecma_compare_ecma_string_to_magic_id (import_names_p->imex_name_p,
|
|
LIT_MAGIC_STRING_ASTERIX_CHAR))
|
|
{
|
|
/* Namespace import. */
|
|
ecma_value_t namespace = ecma_make_object_value (imported_module_p->namespace_object_p);
|
|
|
|
JERRY_ASSERT (namespace & ECMA_MODULE_NAMESPACE_RESULT_FLAG);
|
|
|
|
return ecma_module_resolve_update (resolve_result_p, namespace);
|
|
}
|
|
|
|
if (!ecma_module_resolve_set_append (resolve_set_p, imported_module_p, import_names_p->imex_name_p)
|
|
&& resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND)
|
|
{
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
import_node_p = import_node_p->next_p;
|
|
}
|
|
} /* ecma_module_resolve_import */
|
|
|
|
/**
|
|
* Resolves which module satisfies an export based from a specific module in the import tree.
|
|
*
|
|
* Note: See ES11 15.2.1.17.3
|
|
*/
|
|
static void
|
|
ecma_module_resolve_export (ecma_module_t *const module_p, /**< base module */
|
|
ecma_string_t *const export_name_p, /**< export name */
|
|
ecma_module_resolve_result_t *resolve_result_p) /**< [out] resolve result */
|
|
{
|
|
ecma_module_resolve_set_t *resolve_set_p = ecma_module_resolve_set_create (module_p, export_name_p);
|
|
ecma_module_resolve_set_t *current_set_p = resolve_set_p;
|
|
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_NOT_FOUND;
|
|
resolve_result_p->result = ECMA_VALUE_UNDEFINED;
|
|
|
|
do
|
|
{
|
|
ecma_module_t *current_module_p = current_set_p->module_p;
|
|
ecma_string_t *current_export_name_p = current_set_p->name_p;
|
|
|
|
if (current_module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_ERROR)
|
|
{
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if (current_module_p->header.u.cls.u2.module_flags & ECMA_MODULE_HAS_NAMESPACE)
|
|
{
|
|
ecma_property_t *property_p = ecma_find_named_property (current_module_p->namespace_object_p,
|
|
current_export_name_p);
|
|
|
|
if (property_p != NULL)
|
|
{
|
|
ecma_property_value_t *property_value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
|
|
|
|
JERRY_ASSERT ((!(*property_p & ECMA_PROPERTY_FLAG_DATA)
|
|
&& !(property_value_p->value & ECMA_MODULE_NAMESPACE_RESULT_FLAG))
|
|
|| ((*property_p & ECMA_PROPERTY_FLAG_DATA)
|
|
&& ecma_is_value_object (property_value_p->value)
|
|
&& ecma_object_class_is (ecma_get_object_from_value (property_value_p->value),
|
|
ECMA_OBJECT_CLASS_MODULE_NAMESPACE)));
|
|
|
|
if (!ecma_module_resolve_update (resolve_result_p, property_value_p->value))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
goto next_iteration;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* 6. */
|
|
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))
|
|
{
|
|
ecma_property_t *property_p = ecma_find_named_property (current_module_p->scope_p,
|
|
export_names_p->local_name_p);
|
|
|
|
if (property_p != NULL)
|
|
{
|
|
ecma_value_t reference = ecma_property_to_reference (property_p);
|
|
|
|
JERRY_ASSERT (!(reference & ECMA_MODULE_NAMESPACE_RESULT_FLAG));
|
|
|
|
if (!ecma_module_resolve_update (resolve_result_p, reference))
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
else if (!ecma_module_resolve_import (resolve_result_p,
|
|
resolve_set_p,
|
|
current_module_p,
|
|
export_names_p->local_name_p))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
goto next_iteration;
|
|
}
|
|
|
|
export_names_p = export_names_p->next_p;
|
|
}
|
|
|
|
/* 7. */
|
|
ecma_module_node_t *indirect_export_p = current_module_p->indirect_exports_p;
|
|
|
|
while (indirect_export_p != NULL)
|
|
{
|
|
export_names_p = indirect_export_p->module_names_p;
|
|
|
|
while (export_names_p != NULL)
|
|
{
|
|
if (ecma_compare_ecma_strings (current_export_name_p, export_names_p->imex_name_p))
|
|
{
|
|
ecma_module_t *target_module_p = ecma_module_get_from_object (*indirect_export_p->u.module_object_p);
|
|
|
|
if (ecma_compare_ecma_string_to_magic_id (export_names_p->local_name_p,
|
|
LIT_MAGIC_STRING_ASTERIX_CHAR))
|
|
{
|
|
/* Namespace export. */
|
|
ecma_value_t namespace = ecma_make_object_value (target_module_p->namespace_object_p);
|
|
|
|
JERRY_ASSERT (namespace & ECMA_MODULE_NAMESPACE_RESULT_FLAG);
|
|
|
|
if (!ecma_module_resolve_update (resolve_result_p, namespace))
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
else if (!ecma_module_resolve_set_append (resolve_set_p, target_module_p, export_names_p->local_name_p)
|
|
&& resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND)
|
|
{
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR;
|
|
}
|
|
|
|
goto next_iteration;
|
|
}
|
|
|
|
export_names_p = export_names_p->next_p;
|
|
}
|
|
|
|
indirect_export_p = indirect_export_p->next_p;
|
|
}
|
|
}
|
|
|
|
/* 8. */
|
|
if (ecma_compare_ecma_string_to_magic_id (current_export_name_p, LIT_MAGIC_STRING_DEFAULT))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
/* 10. */
|
|
ecma_module_node_t *star_export_p = current_module_p->star_exports_p;
|
|
while (star_export_p != NULL)
|
|
{
|
|
JERRY_ASSERT (star_export_p->module_names_p == NULL);
|
|
|
|
ecma_module_t *target_module_p = ecma_module_get_from_object (*star_export_p->u.module_object_p);
|
|
|
|
if (!ecma_module_resolve_set_append (resolve_set_p, target_module_p, current_export_name_p)
|
|
&& resolve_result_p->result_type == ECMA_MODULE_RESOLVE_NOT_FOUND)
|
|
{
|
|
resolve_result_p->result_type = ECMA_MODULE_RESOLVE_CIRCULAR;
|
|
}
|
|
|
|
star_export_p = star_export_p->next_p;
|
|
}
|
|
|
|
next_iteration:
|
|
current_set_p = current_set_p->next_p;
|
|
}
|
|
while (current_set_p != NULL);
|
|
|
|
exit:
|
|
ecma_module_resolve_set_cleanup (resolve_set_p);
|
|
} /* ecma_module_resolve_export */
|
|
|
|
/**
|
|
* Evaluates an EcmaScript module.
|
|
*
|
|
* @return ECMA_VALUE_ERROR - if an error occured
|
|
* ECMA_VALUE_EMPTY - otherwise
|
|
*/
|
|
ecma_value_t
|
|
ecma_module_evaluate (ecma_module_t *module_p) /**< module */
|
|
{
|
|
if (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_ERROR)
|
|
{
|
|
return ecma_raise_range_error (ECMA_ERR_MSG ("Module is in error state"));
|
|
}
|
|
|
|
if (module_p->header.u.cls.u1.module_state >= JERRY_MODULE_STATE_EVALUATING)
|
|
{
|
|
return ECMA_VALUE_EMPTY;
|
|
}
|
|
|
|
JERRY_ASSERT (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKED);
|
|
JERRY_ASSERT (module_p->scope_p != NULL);
|
|
|
|
module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATING;
|
|
|
|
ecma_value_t ret_value;
|
|
|
|
if (module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)
|
|
{
|
|
ret_value = ECMA_VALUE_UNDEFINED;
|
|
|
|
if (module_p->u.callback)
|
|
{
|
|
ret_value = module_p->u.callback (ecma_make_object_value (&module_p->header.object));
|
|
|
|
if (JERRY_UNLIKELY (ecma_is_value_error_reference (ret_value)))
|
|
{
|
|
ecma_raise_error_from_error_reference (ret_value);
|
|
ret_value = ECMA_VALUE_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret_value = vm_run_module (module_p);
|
|
}
|
|
|
|
if (JERRY_LIKELY (!ECMA_IS_VALUE_ERROR (ret_value)))
|
|
{
|
|
module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_EVALUATED;
|
|
|
|
if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL)
|
|
{
|
|
JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_EVALUATED,
|
|
ecma_make_object_value (&module_p->header.object),
|
|
ret_value,
|
|
JERRY_CONTEXT (module_state_changed_callback_user_p));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ecma_module_set_error_state (module_p);
|
|
}
|
|
|
|
if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE))
|
|
{
|
|
ecma_bytecode_deref (module_p->u.compiled_code_p);
|
|
}
|
|
|
|
module_p->u.compiled_code_p = NULL;
|
|
return ret_value;
|
|
} /* ecma_module_evaluate */
|
|
|
|
/**
|
|
* Resolves an export and adds it to the modules namespace object, if the export name is not yet handled.
|
|
* Note: See 15.2.1.16.2 and 15.2.1.18
|
|
*
|
|
* @return ECMA_VALUE_ERROR - if an error occured
|
|
* ECMA_VALUE_EMPTY - otherwise
|
|
*/
|
|
static ecma_value_t
|
|
ecma_module_namespace_object_add_export_if_needed (ecma_collection_t *properties_p, /**< collection of properties */
|
|
ecma_module_t *module_p, /**< module */
|
|
ecma_string_t *export_name_p, /**< export name */
|
|
bool allow_default) /**< allow default export */
|
|
{
|
|
if (!allow_default)
|
|
{
|
|
if (ecma_compare_ecma_string_to_magic_id (export_name_p, LIT_MAGIC_STRING_DEFAULT))
|
|
{
|
|
return ECMA_VALUE_EMPTY;
|
|
}
|
|
|
|
/* No need to check duplications before star exports are processed. */
|
|
ecma_value_t *buffer_p = properties_p->buffer_p;
|
|
ecma_value_t *buffer_end_p = properties_p->buffer_p + properties_p->item_count;
|
|
|
|
while (buffer_p < buffer_end_p)
|
|
{
|
|
if (ecma_compare_ecma_strings (ecma_get_string_from_value (*buffer_p), export_name_p))
|
|
{
|
|
return ECMA_VALUE_EMPTY;
|
|
}
|
|
|
|
buffer_p += 2;
|
|
}
|
|
}
|
|
|
|
ecma_module_resolve_result_t resolve_result;
|
|
ecma_module_resolve_export (module_p, export_name_p, &resolve_result);
|
|
|
|
if (resolve_result.result_type == ECMA_MODULE_RESOLVE_AMBIGUOUS)
|
|
{
|
|
return ECMA_VALUE_EMPTY;
|
|
}
|
|
|
|
if (resolve_result.result_type != ECMA_MODULE_RESOLVE_FOUND)
|
|
{
|
|
return ecma_module_resolve_throw (&resolve_result, export_name_p);
|
|
}
|
|
|
|
ecma_collection_push_back (properties_p, ecma_make_string_value (export_name_p));
|
|
ecma_collection_push_back (properties_p, resolve_result.result);
|
|
return ECMA_VALUE_EMPTY;
|
|
} /* ecma_module_namespace_object_add_export_if_needed */
|
|
|
|
/**
|
|
* Helper routine for heapsort algorithm.
|
|
*/
|
|
static void
|
|
ecma_module_heap_sort_shift_down (ecma_value_t *buffer_p, /**< array of items */
|
|
uint32_t item_count, /**< number of items */
|
|
uint32_t item_index) /**< index of updated item */
|
|
{
|
|
while (true)
|
|
{
|
|
uint32_t highest_index = item_index;
|
|
uint32_t current_index = (item_index << 1) + 2;
|
|
|
|
if (current_index >= item_count)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ecma_compare_ecma_strings_relational (ecma_get_string_from_value (buffer_p[highest_index]),
|
|
ecma_get_string_from_value (buffer_p[current_index])))
|
|
{
|
|
highest_index = current_index;
|
|
}
|
|
|
|
current_index += 2;
|
|
|
|
if (current_index < item_count
|
|
&& ecma_compare_ecma_strings_relational (ecma_get_string_from_value (buffer_p[highest_index]),
|
|
ecma_get_string_from_value (buffer_p[current_index])))
|
|
{
|
|
highest_index = current_index;
|
|
}
|
|
|
|
if (highest_index == item_index)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ecma_value_t tmp = buffer_p[highest_index];
|
|
buffer_p[highest_index] = buffer_p[item_index];
|
|
buffer_p[item_index] = tmp;
|
|
|
|
tmp = buffer_p[highest_index + 1];
|
|
buffer_p[highest_index + 1] = buffer_p[item_index + 1];
|
|
buffer_p[item_index + 1] = tmp;
|
|
|
|
item_index = highest_index;
|
|
}
|
|
} /* ecma_module_heap_sort_shift_down */
|
|
|
|
/**
|
|
* Creates a namespace object for a module.
|
|
* Note: See 15.2.1.18
|
|
*
|
|
* @return ECMA_VALUE_ERROR - if an error occured
|
|
* ECMA_VALUE_EMPTY - otherwise
|
|
*/
|
|
static ecma_value_t
|
|
ecma_module_create_namespace_object (ecma_module_t *module_p) /**< module */
|
|
{
|
|
JERRY_ASSERT (module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING);
|
|
JERRY_ASSERT (module_p->namespace_object_p != NULL);
|
|
JERRY_ASSERT (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_HAS_NAMESPACE));
|
|
|
|
ecma_module_resolve_set_t *resolve_set_p;
|
|
resolve_set_p = ecma_module_resolve_set_create (module_p, ecma_get_magic_string (LIT_MAGIC_STRING_ASTERIX_CHAR));
|
|
|
|
/* The properties collection stores name / result item pairs. Name is always
|
|
* a string, and result can be a property reference or namespace object. */
|
|
ecma_module_resolve_set_t *current_set_p = resolve_set_p;
|
|
ecma_collection_t *properties_p = ecma_new_collection ();
|
|
ecma_value_t result = ECMA_VALUE_EMPTY;
|
|
bool allow_default = true;
|
|
|
|
do
|
|
{
|
|
ecma_module_t *current_module_p = current_set_p->module_p;
|
|
|
|
if (current_module_p->header.u.cls.u2.module_flags & ECMA_MODULE_HAS_NAMESPACE)
|
|
{
|
|
JERRY_ASSERT (!allow_default);
|
|
|
|
jmem_cpointer_t prop_iter_cp = current_module_p->namespace_object_p->u1.property_list_cp;
|
|
|
|
#if JERRY_PROPERTY_HASHMAP
|
|
if (prop_iter_cp != JMEM_CP_NULL)
|
|
{
|
|
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t,
|
|
prop_iter_cp);
|
|
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
|
|
{
|
|
prop_iter_cp = prop_iter_p->next_property_cp;
|
|
}
|
|
}
|
|
#endif /* JERRY_PROPERTY_HASHMAP */
|
|
|
|
while (prop_iter_cp != JMEM_CP_NULL)
|
|
{
|
|
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
|
|
|
|
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
|
|
|
|
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
|
|
|
|
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
|
|
{
|
|
if (!ECMA_PROPERTY_IS_RAW (prop_iter_p->types[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ecma_string_t *name_p = ecma_string_from_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i]);
|
|
result = ecma_module_namespace_object_add_export_if_needed (properties_p, module_p, name_p, false);
|
|
ecma_deref_ecma_string (name_p);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (result))
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
prop_iter_cp = prop_iter_p->next_property_cp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ecma_module_names_t *export_names_p = current_module_p->local_exports_p;
|
|
|
|
if (export_names_p != NULL)
|
|
{
|
|
do
|
|
{
|
|
result = ecma_module_namespace_object_add_export_if_needed (properties_p,
|
|
module_p,
|
|
export_names_p->imex_name_p,
|
|
allow_default);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (result))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
export_names_p = export_names_p->next_p;
|
|
}
|
|
while (export_names_p != NULL);
|
|
}
|
|
|
|
ecma_module_node_t *indirect_export_p = current_module_p->indirect_exports_p;
|
|
|
|
while (indirect_export_p != NULL)
|
|
{
|
|
export_names_p = indirect_export_p->module_names_p;
|
|
|
|
while (export_names_p != NULL)
|
|
{
|
|
result = ecma_module_namespace_object_add_export_if_needed (properties_p,
|
|
module_p,
|
|
export_names_p->imex_name_p,
|
|
allow_default);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (result))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
export_names_p = export_names_p->next_p;
|
|
}
|
|
|
|
indirect_export_p = indirect_export_p->next_p;
|
|
}
|
|
}
|
|
|
|
allow_default = false;
|
|
|
|
ecma_module_node_t *star_export_p = current_module_p->star_exports_p;
|
|
|
|
while (star_export_p != NULL)
|
|
{
|
|
JERRY_ASSERT (star_export_p->module_names_p == NULL);
|
|
|
|
/* Circular imports are ignored */
|
|
ecma_module_resolve_set_append (resolve_set_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;
|
|
}
|
|
|
|
current_set_p = current_set_p->next_p;
|
|
}
|
|
while (current_set_p != NULL);
|
|
|
|
ecma_value_t *buffer_p = properties_p->buffer_p;
|
|
uint32_t item_count = properties_p->item_count;
|
|
|
|
if (item_count >= 4)
|
|
{
|
|
/* Sort items with heapsort if at least two items are stored in the buffer. */
|
|
uint32_t end = (item_count >> 1) & ~(uint32_t) 0x1;
|
|
|
|
do
|
|
{
|
|
end -= 2;
|
|
ecma_module_heap_sort_shift_down (buffer_p, item_count, end);
|
|
}
|
|
while (end > 0);
|
|
|
|
end = item_count - 2;
|
|
|
|
do
|
|
{
|
|
ecma_value_t tmp = buffer_p[end];
|
|
buffer_p[end] = buffer_p[0];
|
|
buffer_p[0] = tmp;
|
|
|
|
tmp = buffer_p[end + 1];
|
|
buffer_p[end + 1] = buffer_p[1];
|
|
buffer_p[1] = tmp;
|
|
|
|
ecma_module_heap_sort_shift_down (buffer_p, end, 0);
|
|
end -= 2;
|
|
}
|
|
while (end > 0);
|
|
}
|
|
|
|
ecma_value_t *buffer_end_p = properties_p->buffer_p + item_count;
|
|
|
|
while (buffer_p < buffer_end_p)
|
|
{
|
|
if (buffer_p[1] & ECMA_MODULE_NAMESPACE_RESULT_FLAG)
|
|
{
|
|
ecma_property_value_t *property_value_p;
|
|
property_value_p = ecma_create_named_data_property (module_p->namespace_object_p,
|
|
ecma_get_string_from_value (buffer_p[0]),
|
|
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE,
|
|
NULL);
|
|
property_value_p->value = buffer_p[1];
|
|
}
|
|
else
|
|
{
|
|
ecma_create_named_reference_property (module_p->namespace_object_p,
|
|
ecma_get_string_from_value (buffer_p[0]),
|
|
buffer_p[1]);
|
|
}
|
|
|
|
buffer_p += 2;
|
|
}
|
|
|
|
module_p->header.u.cls.u2.module_flags |= ECMA_MODULE_HAS_NAMESPACE;
|
|
|
|
ecma_module_release_module_names (module_p->local_exports_p);
|
|
module_p->local_exports_p = NULL;
|
|
|
|
ecma_module_release_module_nodes (module_p->indirect_exports_p, false);
|
|
module_p->indirect_exports_p = NULL;
|
|
|
|
exit:
|
|
/* Clean up. */
|
|
ecma_module_resolve_set_cleanup (resolve_set_p);
|
|
ecma_collection_destroy (properties_p);
|
|
return result;
|
|
} /* ecma_module_create_namespace_object */
|
|
|
|
/**
|
|
* Connects imported values to the current module scope.
|
|
*
|
|
* @return ECMA_VALUE_ERROR - if an error occured
|
|
* ECMA_VALUE_EMPTY - otherwise
|
|
*/
|
|
static ecma_value_t
|
|
ecma_module_connect_imports (ecma_module_t *module_p)
|
|
{
|
|
ecma_object_t *local_env_p = module_p->scope_p;
|
|
JERRY_ASSERT (ecma_is_lexical_environment (local_env_p));
|
|
|
|
ecma_module_node_t *import_node_p = module_p->imports_p;
|
|
|
|
/* Check that the imported bindings don't exist yet. */
|
|
while (import_node_p != NULL)
|
|
{
|
|
ecma_module_names_t *import_names_p = import_node_p->module_names_p;
|
|
|
|
while (import_names_p != NULL)
|
|
{
|
|
ecma_object_t *lex_env_p = local_env_p;
|
|
ecma_property_t *binding_p = NULL;
|
|
|
|
if (lex_env_p->type_flags_refs & ECMA_OBJECT_FLAG_BLOCK)
|
|
{
|
|
binding_p = ecma_find_named_property (lex_env_p, import_names_p->local_name_p);
|
|
|
|
JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
|
|
lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
|
|
}
|
|
|
|
if (binding_p != NULL)
|
|
{
|
|
return ecma_raise_syntax_error (ECMA_ERR_MSG ("Imported binding shadows local variable"));
|
|
}
|
|
|
|
ecma_value_t status = ecma_op_has_binding (lex_env_p, import_names_p->local_name_p);
|
|
|
|
#if JERRY_BUILTIN_PROXY
|
|
if (ECMA_IS_VALUE_ERROR (status))
|
|
{
|
|
return status;
|
|
}
|
|
#endif /* JERRY_BUILTIN_PROXY */
|
|
|
|
if (ecma_is_value_true (status))
|
|
{
|
|
return ecma_raise_syntax_error (ECMA_ERR_MSG ("Imported binding shadows local variable"));
|
|
}
|
|
|
|
import_names_p = import_names_p->next_p;
|
|
}
|
|
|
|
import_node_p = import_node_p->next_p;
|
|
}
|
|
|
|
import_node_p = module_p->imports_p;
|
|
|
|
/* Resolve imports and create local bindings. */
|
|
while (import_node_p != NULL)
|
|
{
|
|
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);
|
|
|
|
while (import_names_p != NULL)
|
|
{
|
|
if (ecma_compare_ecma_string_to_magic_id (import_names_p->imex_name_p,
|
|
LIT_MAGIC_STRING_ASTERIX_CHAR))
|
|
{
|
|
/* Namespace import. */
|
|
ecma_property_value_t *value_p;
|
|
value_p = ecma_create_named_data_property (module_p->scope_p,
|
|
import_names_p->local_name_p,
|
|
ECMA_PROPERTY_FIXED,
|
|
NULL);
|
|
value_p->value = ecma_make_object_value (imported_module_p->namespace_object_p);
|
|
}
|
|
else
|
|
{
|
|
ecma_module_resolve_result_t resolve_result;
|
|
ecma_module_resolve_export (imported_module_p, import_names_p->imex_name_p, &resolve_result);
|
|
|
|
if (resolve_result.result_type != ECMA_MODULE_RESOLVE_FOUND)
|
|
{
|
|
return ecma_module_resolve_throw (&resolve_result, import_names_p->imex_name_p);
|
|
}
|
|
|
|
if (resolve_result.result & ECMA_MODULE_NAMESPACE_RESULT_FLAG)
|
|
{
|
|
ecma_property_value_t *property_value_p;
|
|
property_value_p = ecma_create_named_data_property (module_p->scope_p,
|
|
import_names_p->local_name_p,
|
|
ECMA_PROPERTY_FIXED,
|
|
NULL);
|
|
property_value_p->value = resolve_result.result;
|
|
}
|
|
else
|
|
{
|
|
ecma_create_named_reference_property (module_p->scope_p,
|
|
import_names_p->local_name_p,
|
|
resolve_result.result);
|
|
}
|
|
}
|
|
|
|
import_names_p = import_names_p->next_p;
|
|
}
|
|
|
|
ecma_module_release_module_names (import_node_p->module_names_p);
|
|
import_node_p->module_names_p = NULL;
|
|
|
|
import_node_p = import_node_p->next_p;
|
|
}
|
|
|
|
return ECMA_VALUE_EMPTY;
|
|
} /* ecma_module_connect_imports */
|
|
|
|
/**
|
|
* Initialize the current module by creating the local binding for the imported variables
|
|
* and verifying indirect exports.
|
|
*
|
|
* @return ECMA_VALUE_ERROR - if an error occured
|
|
* ECMA_VALUE_EMPTY - otherwise
|
|
*/
|
|
ecma_value_t
|
|
ecma_module_initialize (ecma_module_t *module_p) /**< module */
|
|
{
|
|
ecma_module_node_t *import_node_p = module_p->imports_p;
|
|
|
|
while (import_node_p != NULL)
|
|
{
|
|
/* Module is evaluated even if it is used only in export-from statements. */
|
|
ecma_value_t result = ecma_module_evaluate (ecma_module_get_from_object (import_node_p->u.path_or_module));
|
|
|
|
if (ECMA_IS_VALUE_ERROR (result))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
ecma_free_value (result);
|
|
|
|
import_node_p = import_node_p->next_p;
|
|
}
|
|
|
|
return ECMA_VALUE_EMPTY;
|
|
} /* ecma_module_initialize */
|
|
|
|
/**
|
|
* 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_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (ecma_module_t *) object_p;
|
|
} /* ecma_module_get_resolved_module */
|
|
|
|
/**
|
|
* 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_UNDEFINED - otherwise
|
|
*/
|
|
ecma_value_t
|
|
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 */
|
|
{
|
|
if (module_p->header.u.cls.u1.module_state != JERRY_MODULE_STATE_UNLINKED)
|
|
{
|
|
return ecma_raise_type_error (ECMA_ERR_MSG ("Module must be in unlinked state"));
|
|
}
|
|
|
|
module_p->header.u.cls.u1.module_state = JERRY_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.cls.u3.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)
|
|
{
|
|
ecma_module_t *resolved_module_p;
|
|
|
|
if (!ecma_is_value_object (node_p->u.path_or_module))
|
|
{
|
|
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;
|
|
}
|
|
|
|
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));
|
|
}
|
|
else
|
|
{
|
|
resolved_module_p = ecma_module_get_from_object (node_p->u.path_or_module);
|
|
}
|
|
|
|
if (resolved_module_p->header.u.cls.u1.module_state == JERRY_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;
|
|
}
|
|
|
|
/* Find next unlinked node, or return to parent */
|
|
while (true)
|
|
{
|
|
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.cls.u1.module_state == JERRY_MODULE_STATE_UNLINKED)
|
|
{
|
|
current_p->node_p = node_p->next_p;
|
|
module_p->header.u.cls.u1.module_state = JERRY_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.cls.u3.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.cls.u1.module_state == JERRY_MODULE_STATE_LINKING)
|
|
{
|
|
uint32_t dfs_ancestor_index = module_p->header.u.cls.u3.dfs_ancestor_index;
|
|
|
|
if (dfs_ancestor_index < current_module_p->header.u.cls.u3.dfs_ancestor_index)
|
|
{
|
|
current_module_p->header.u.cls.u3.dfs_ancestor_index = dfs_ancestor_index;
|
|
}
|
|
}
|
|
|
|
node_p = node_p->next_p;
|
|
}
|
|
|
|
if (current_module_p->scope_p == NULL)
|
|
{
|
|
JERRY_ASSERT (!(current_module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE));
|
|
|
|
/* Initialize scope for handling circular references. */
|
|
ecma_value_t result = vm_init_module_scope (current_module_p);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (result))
|
|
{
|
|
ecma_module_set_error_state (current_module_p);
|
|
goto error;
|
|
}
|
|
|
|
JERRY_ASSERT (result == ECMA_VALUE_EMPTY);
|
|
}
|
|
|
|
if (current_module_p->namespace_object_p == NULL)
|
|
{
|
|
ecma_object_t *namespace_object_p = ecma_create_object (NULL,
|
|
sizeof (ecma_extended_object_t),
|
|
ECMA_OBJECT_TYPE_CLASS);
|
|
|
|
namespace_object_p->type_flags_refs &= (ecma_object_descriptor_t) ~ECMA_OBJECT_FLAG_EXTENSIBLE;
|
|
|
|
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) namespace_object_p;
|
|
ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_MODULE_NAMESPACE;
|
|
ECMA_SET_INTERNAL_VALUE_POINTER (ext_object_p->u.cls.u3.value, module_p);
|
|
|
|
current_module_p->namespace_object_p = namespace_object_p;
|
|
ecma_deref_object (namespace_object_p);
|
|
}
|
|
|
|
if (current_module_p->header.u.cls.u3.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.cls.u3.dfs_ancestor_index;
|
|
|
|
if (dfs_ancestor_index < current_p->module_p->header.u.cls.u3.dfs_ancestor_index)
|
|
{
|
|
current_p->module_p->header.u.cls.u3.dfs_ancestor_index = dfs_ancestor_index;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
ecma_module_stack_item_t *end_p = current_p->prev_p;
|
|
current_p = current_p->parent_p;
|
|
|
|
ecma_module_stack_item_t *iterator_p = last_p;
|
|
|
|
do
|
|
{
|
|
JERRY_ASSERT (iterator_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (ecma_module_create_namespace_object (iterator_p->module_p)))
|
|
{
|
|
ecma_module_set_error_state (iterator_p->module_p);
|
|
goto error;
|
|
}
|
|
|
|
iterator_p = iterator_p->prev_p;
|
|
}
|
|
while (iterator_p != end_p);
|
|
|
|
iterator_p = last_p;
|
|
|
|
do
|
|
{
|
|
JERRY_ASSERT (iterator_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING);
|
|
|
|
if (ECMA_IS_VALUE_ERROR (ecma_module_connect_imports (iterator_p->module_p)))
|
|
{
|
|
ecma_module_set_error_state (iterator_p->module_p);
|
|
goto error;
|
|
}
|
|
|
|
iterator_p = iterator_p->prev_p;
|
|
}
|
|
while (iterator_p != end_p);
|
|
|
|
do
|
|
{
|
|
ecma_module_stack_item_t *prev_p = last_p->prev_p;
|
|
|
|
JERRY_ASSERT (last_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING);
|
|
last_p->module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_LINKED;
|
|
|
|
if (JERRY_CONTEXT (module_state_changed_callback_p) != NULL)
|
|
{
|
|
JERRY_CONTEXT (module_state_changed_callback_p) (JERRY_MODULE_STATE_LINKED,
|
|
ecma_make_object_value (&last_p->module_p->header.object),
|
|
ECMA_VALUE_UNDEFINED,
|
|
JERRY_CONTEXT (module_state_changed_callback_user_p));
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
error:
|
|
JERRY_ASSERT (last_p != NULL);
|
|
|
|
do
|
|
{
|
|
ecma_module_stack_item_t *prev_p = last_p->prev_p;
|
|
|
|
if (last_p->module_p->header.u.cls.u1.module_state != JERRY_MODULE_STATE_ERROR)
|
|
{
|
|
JERRY_ASSERT (last_p->module_p->header.u.cls.u1.module_state == JERRY_MODULE_STATE_LINKING);
|
|
last_p->module_p->header.u.cls.u1.module_state = JERRY_MODULE_STATE_UNLINKED;
|
|
}
|
|
|
|
jmem_heap_free_block (last_p, sizeof (ecma_module_stack_item_t));
|
|
last_p = prev_p;
|
|
}
|
|
while (last_p != NULL);
|
|
|
|
return ECMA_VALUE_ERROR;
|
|
} /* ecma_module_link */
|
|
|
|
/**
|
|
* Compute the result of 'import()' calls
|
|
*
|
|
* @return promise object representing the result of the operation
|
|
*/
|
|
ecma_value_t
|
|
ecma_module_import (ecma_value_t specifier, /**< module specifier */
|
|
ecma_value_t user_value) /**< user value assigned to the script */
|
|
{
|
|
ecma_string_t *specifier_p = ecma_op_to_string (specifier);
|
|
|
|
if (JERRY_UNLIKELY (specifier_p == NULL))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if (JERRY_CONTEXT (module_import_callback_p) == NULL)
|
|
{
|
|
ecma_deref_ecma_string (specifier_p);
|
|
goto error_module_instantiate;
|
|
}
|
|
|
|
jerry_value_t result;
|
|
result = JERRY_CONTEXT (module_import_callback_p) (ecma_make_string_value (specifier_p),
|
|
user_value,
|
|
JERRY_CONTEXT (module_import_callback_user_p));
|
|
ecma_deref_ecma_string (specifier_p);
|
|
|
|
if (JERRY_UNLIKELY (ecma_is_value_error_reference (result)))
|
|
{
|
|
ecma_raise_error_from_error_reference (result);
|
|
goto error;
|
|
}
|
|
|
|
if (ecma_is_value_object (result)
|
|
&& ecma_is_promise (ecma_get_object_from_value (result)))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
ecma_module_t *module_p = ecma_module_get_resolved_module (result);
|
|
|
|
if (module_p == NULL)
|
|
{
|
|
ecma_free_value (result);
|
|
goto error_module_instantiate;
|
|
}
|
|
|
|
if (module_p->header.u.cls.u1.module_state != JERRY_MODULE_STATE_EVALUATED)
|
|
{
|
|
ecma_deref_object (&module_p->header.object);
|
|
goto error_module_instantiate;
|
|
}
|
|
|
|
result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL);
|
|
ecma_fulfill_promise (result, ecma_make_object_value (module_p->namespace_object_p));
|
|
ecma_deref_object (&module_p->header.object);
|
|
return result;
|
|
|
|
error_module_instantiate:
|
|
ecma_raise_range_error (ECMA_ERR_MSG ("Module cannot be instantiated"));
|
|
|
|
error:
|
|
if (jcontext_has_pending_abort ())
|
|
{
|
|
return ECMA_VALUE_ERROR;
|
|
}
|
|
|
|
ecma_value_t exception = jcontext_take_exception ();
|
|
|
|
ecma_value_t promise = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_VALUE_UNDEFINED, NULL);
|
|
ecma_reject_promise (promise, exception);
|
|
ecma_free_value (exception);
|
|
return promise;
|
|
} /* ecma_module_import */
|
|
|
|
/**
|
|
* Cleans up and releases a module structure including all referenced modules.
|
|
*/
|
|
void
|
|
ecma_module_release_module (ecma_module_t *module_p) /**< module */
|
|
{
|
|
jerry_module_state_t state = (jerry_module_state_t) module_p->header.u.cls.u1.module_state;
|
|
|
|
JERRY_ASSERT (state != JERRY_MODULE_STATE_INVALID);
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
module_p->scope_p = NULL;
|
|
module_p->namespace_object_p = NULL;
|
|
#endif /* JERRY_NDEBUG */
|
|
|
|
ecma_module_release_module_names (module_p->local_exports_p);
|
|
|
|
if (module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ecma_module_release_module_nodes (module_p->imports_p, true);
|
|
ecma_module_release_module_nodes (module_p->indirect_exports_p, false);
|
|
ecma_module_release_module_nodes (module_p->star_exports_p, false);
|
|
|
|
if (module_p->u.compiled_code_p != NULL)
|
|
{
|
|
ecma_bytecode_deref (module_p->u.compiled_code_p);
|
|
#ifndef JERRY_NDEBUG
|
|
module_p->u.compiled_code_p = NULL;
|
|
#endif /* JERRY_NDEBUG */
|
|
}
|
|
} /* ecma_module_release_module */
|
|
|
|
#endif /* JERRY_MODULE_SYSTEM */
|