Skip to content

Commit f1883b9

Browse files
authored
Provide support for native modules with ES6 imports (#3090)
JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai [email protected]
1 parent 2a89eec commit f1883b9

File tree

10 files changed

+415
-201
lines changed

10 files changed

+415
-201
lines changed

docs/05.PORT-API.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,14 @@ information.
8383
void jerry_port_print_char (char c);
8484
```
8585

86-
### ES2015 Module system helper functions
86+
### ES2015 Module system
8787

88-
The module system requires two specific functions for opening and closing files.
89-
It also requires a platform specific way of normalizing file paths.
88+
The port API provides functions that can be used by the module system to open
89+
and close source files, and normalize file paths.
90+
The `jerry_port_get_native_module` port function can be used to provide native
91+
modules to the engine. This function will be called when an import/export
92+
statement is encountered with an unknown module specifier, which embedders can
93+
use to supply native module objects based on the module name argument.
9094

9195
```c
9296
/**
@@ -126,6 +130,25 @@ jerry_port_normalize_path (const char *in_path_p, /**< input file path */
126130
// write to out_buf_p the normalized path
127131
// return length of written path
128132
} /* jerry_port_normalize_path */
133+
134+
/**
135+
* Get the module object of a native module.
136+
*
137+
* Note:
138+
* This port function is called by jerry-core when ES2015_MODULE_SYSTEM
139+
* is enabled.
140+
*
141+
* @param name String value of the module specifier.
142+
*
143+
* @return Undefined, if 'name' is not a native module
144+
* jerry_value_t containing the module object, otherwise
145+
*/
146+
jerry_value_t
147+
jerry_port_get_native_module (jerry_value_t name) /**< module specifier */
148+
{
149+
(void) name;
150+
return jerry_create_undefined ();
151+
}
129152
```
130153
131154
## Date

docs/15.MODULE-SYSTEM.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
The module system allows users to write import and export statements in scripts, which can be used to separate the logic of the application into custom modules.
44
The standard's relevant part can be found [here](https://www.ecma-international.org/ecma-262/6.0/#sec-modules).
5+
Embedders wishing to use native builtin modules with ES6 imports can use the [Port API](05.PORT-API.md#es2015-module-system) to do so.
56

67
## General
78

jerry-core/ecma/base/ecma-module.c

Lines changed: 123 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,13 @@ ecma_module_create_normalized_path (const uint8_t *char_p, /**< module specifier
9797
} /* ecma_module_create_normalized_path */
9898

9999
/**
100-
* Checks if we already have a module request in the module list.
100+
* Find a module with a specific identifier
101101
*
102-
* @return pointer to found or newly created module structure
102+
* @return pointer to ecma_module_t, if found
103+
* NULL, otherwise
103104
*/
104105
ecma_module_t *
105-
ecma_module_find_or_create_module (ecma_string_t * const path_p) /**< module path */
106+
ecma_module_find_module (ecma_string_t *const path_p) /**< module identifier */
106107
{
107108
ecma_module_t *current_p = JERRY_CONTEXT (ecma_modules_p);
108109
while (current_p != NULL)
@@ -114,16 +115,59 @@ ecma_module_find_or_create_module (ecma_string_t * const path_p) /**< module pat
114115
current_p = current_p->next_p;
115116
}
116117

117-
current_p = (ecma_module_t *) jmem_heap_alloc_block (sizeof (ecma_module_t));
118-
memset (current_p, 0, sizeof (ecma_module_t));
119-
120-
ecma_ref_ecma_string (path_p);
121-
current_p->path_p = path_p;
122-
current_p->next_p = JERRY_CONTEXT (ecma_modules_p);
123-
JERRY_CONTEXT (ecma_modules_p) = current_p;
124118
return current_p;
119+
} /* ecma_module_find_module */
120+
121+
/**
122+
* Create a new module
123+
*
124+
* @return pointer to created module
125+
*/
126+
static ecma_module_t *
127+
ecma_module_create_module (ecma_string_t *const path_p) /**< module identifier */
128+
{
129+
ecma_module_t *module_p = (ecma_module_t *) jmem_heap_alloc_block (sizeof (ecma_module_t));
130+
memset (module_p, 0, sizeof (ecma_module_t));
131+
132+
module_p->path_p = path_p;
133+
module_p->next_p = JERRY_CONTEXT (ecma_modules_p);
134+
JERRY_CONTEXT (ecma_modules_p) = module_p;
135+
return module_p;
136+
} /* ecma_module_create_module */
137+
138+
/**
139+
* Checks if we already have a module request in the module list.
140+
*
141+
* @return pointer to found or newly created module structure
142+
*/
143+
ecma_module_t *
144+
ecma_module_find_or_create_module (ecma_string_t *const path_p) /**< module path */
145+
{
146+
ecma_module_t *module_p = ecma_module_find_module (path_p);
147+
if (module_p)
148+
{
149+
ecma_deref_ecma_string (path_p);
150+
return module_p;
151+
}
152+
153+
return ecma_module_create_module (path_p);
125154
} /* ecma_module_find_or_create_module */
126155

156+
/**
157+
* Create a new native module
158+
*
159+
* @return pointer to created module
160+
*/
161+
ecma_module_t *
162+
ecma_module_create_native_module (ecma_string_t *const path_p, /**< module identifier */
163+
ecma_object_t *const namespace_p) /**< module namespace */
164+
{
165+
ecma_module_t *module_p = ecma_module_create_module (path_p);
166+
module_p->state = ECMA_MODULE_STATE_NATIVE;
167+
module_p->namespace_object_p = namespace_p;
168+
return module_p;
169+
} /* ecma_module_create_native_module */
170+
127171
/**
128172
* Creates a module context.
129173
*
@@ -274,6 +318,30 @@ ecma_module_resolve_export (ecma_module_t * const module_p, /**< base module */
274318
continue;
275319
}
276320

321+
if (current_module_p->state == ECMA_MODULE_STATE_NATIVE)
322+
{
323+
ecma_object_t *object_p = current_module_p->namespace_object_p;
324+
ecma_value_t prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p),
325+
object_p,
326+
current_export_name_p);
327+
if (ecma_is_value_found (prop_value))
328+
{
329+
found = true;
330+
found_record.module_p = current_module_p;
331+
found_record.name_p = current_export_name_p;
332+
ecma_free_value (prop_value);
333+
}
334+
335+
if (ecma_compare_ecma_string_to_magic_id (current_export_name_p, LIT_MAGIC_STRING_DEFAULT))
336+
{
337+
ret_value = ecma_raise_syntax_error (ECMA_ERR_MSG ("No default export in native module."));
338+
break;
339+
}
340+
341+
ecma_module_resolve_stack_pop (&stack_p);
342+
continue;
343+
}
344+
277345
if (context_p->local_exports_p != NULL)
278346
{
279347
/* 15.2.1.16.3 / 4 */
@@ -645,25 +713,44 @@ ecma_module_connect_imports (void)
645713
return ecma_raise_syntax_error (ECMA_ERR_MSG ("Ambiguous import request."));
646714
}
647715

648-
result = ecma_module_evaluate (record.module_p);
649-
650-
if (ECMA_IS_VALUE_ERROR (result))
716+
if (record.module_p->state == ECMA_MODULE_STATE_NATIVE)
651717
{
652-
return result;
718+
ecma_object_t *object_p = record.module_p->namespace_object_p;
719+
ecma_value_t prop_value = ecma_op_object_find_own (ecma_make_object_value (object_p),
720+
object_p,
721+
record.name_p);
722+
JERRY_ASSERT (ecma_is_value_found (prop_value));
723+
724+
ecma_op_create_mutable_binding (local_env_p, import_names_p->local_name_p, true /* is_deletable */);
725+
ecma_op_set_mutable_binding (local_env_p,
726+
import_names_p->local_name_p,
727+
prop_value,
728+
false /* is_strict */);
729+
730+
ecma_free_value (prop_value);
653731
}
732+
else
733+
{
734+
result = ecma_module_evaluate (record.module_p);
654735

655-
ecma_object_t *ref_base_lex_env_p;
656-
ecma_value_t prop_value = ecma_op_get_value_lex_env_base (record.module_p->scope_p,
657-
&ref_base_lex_env_p,
658-
record.name_p);
736+
if (ECMA_IS_VALUE_ERROR (result))
737+
{
738+
return result;
739+
}
659740

660-
ecma_op_create_mutable_binding (local_env_p, import_names_p->local_name_p, true /* is_deletable */);
661-
ecma_op_set_mutable_binding (local_env_p,
662-
import_names_p->local_name_p,
663-
prop_value,
664-
false /* is_strict */);
741+
ecma_object_t *ref_base_lex_env_p;
742+
ecma_value_t prop_value = ecma_op_get_value_lex_env_base (record.module_p->scope_p,
743+
&ref_base_lex_env_p,
744+
record.name_p);
745+
746+
ecma_op_create_mutable_binding (local_env_p, import_names_p->local_name_p, true /* is_deletable */);
747+
ecma_op_set_mutable_binding (local_env_p,
748+
import_names_p->local_name_p,
749+
prop_value,
750+
false /* is_strict */);
665751

666-
ecma_free_value (prop_value);
752+
ecma_free_value (prop_value);
753+
}
667754
}
668755

669756
import_names_p = import_names_p->next_p;
@@ -873,6 +960,17 @@ static void
873960
ecma_module_release_module (ecma_module_t *module_p) /**< module */
874961
{
875962
ecma_deref_ecma_string (module_p->path_p);
963+
964+
if (module_p->namespace_object_p != NULL)
965+
{
966+
ecma_deref_object (module_p->namespace_object_p);
967+
}
968+
969+
if (module_p->state == ECMA_MODULE_STATE_NATIVE)
970+
{
971+
goto finished;
972+
}
973+
876974
if (module_p->state >= ECMA_MODULE_STATE_PARSING)
877975
{
878976
ecma_module_release_module_context (module_p->context_p);
@@ -889,11 +987,7 @@ ecma_module_release_module (ecma_module_t *module_p) /**< module */
889987
ecma_bytecode_deref (module_p->compiled_code_p);
890988
}
891989

892-
if (module_p->namespace_object_p != NULL)
893-
{
894-
ecma_deref_object (module_p->namespace_object_p);
895-
}
896-
990+
finished:
897991
jmem_heap_free_block (module_p, sizeof (ecma_module_t));
898992
} /* ecma_module_release_module */
899993

jerry-core/ecma/base/ecma-module.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ typedef enum
7070
ECMA_MODULE_STATE_PARSED = 2, /**< module has been parsed */
7171
ECMA_MODULE_STATE_EVALUATING = 3, /**< module is currently being evaluated */
7272
ECMA_MODULE_STATE_EVALUATED = 4, /**< module has been evaluated */
73+
ECMA_MODULE_STATE_NATIVE = 5, /**< module is native */
7374
} ecma_module_state_t;
7475

7576
/**
@@ -116,18 +117,21 @@ typedef struct ecma_module_resolve_stack
116117
} ecma_module_resolve_stack_t;
117118

118119
bool ecma_module_resolve_set_insert (ecma_module_resolve_set_t **set_p,
119-
ecma_module_t * const module_p,
120-
ecma_string_t * const export_name_p);
120+
ecma_module_t *const module_p,
121+
ecma_string_t *const export_name_p);
121122
void ecma_module_resolve_set_cleanup (ecma_module_resolve_set_t *set_p);
122123

123124
void ecma_module_resolve_stack_push (ecma_module_resolve_stack_t **stack_p,
124-
ecma_module_t * const module_p,
125-
ecma_string_t * const export_name_p);
125+
ecma_module_t *const module_p,
126+
ecma_string_t *const export_name_p);
126127
void ecma_module_resolve_stack_pop (ecma_module_resolve_stack_t **stack_p);
127128

128129
ecma_string_t *ecma_module_create_normalized_path (const uint8_t *char_p,
129130
prop_length_t size);
130-
ecma_module_t *ecma_module_find_or_create_module (ecma_string_t * const path_p);
131+
ecma_module_t *ecma_module_find_module (ecma_string_t *const path_p);
132+
ecma_module_t *ecma_module_create_native_module (ecma_string_t *const path_p,
133+
ecma_object_t *const namespace_p);
134+
ecma_module_t *ecma_module_find_or_create_module (ecma_string_t *const path_p);
131135

132136
ecma_value_t ecma_module_connect_imports (void);
133137
ecma_value_t ecma_module_parse_modules (void);

jerry-core/include/jerryscript-port.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <stdio.h>
2222

2323
#include "jerryscript-compiler.h"
24+
#include "jerryscript-core.h"
2425

2526
#ifdef __cplusplus
2627
extern "C"
@@ -236,6 +237,20 @@ size_t jerry_port_normalize_path (const char *in_path_p,
236237
size_t out_buf_size,
237238
char *base_file_p);
238239

240+
/**
241+
* Get the module object of a native module.
242+
*
243+
* Note:
244+
* This port function is called by jerry-core when ES2015_MODULE_SYSTEM
245+
* is enabled.
246+
*
247+
* @param name String value of the module specifier.
248+
*
249+
* @return Undefined, if 'name' is not a native module
250+
* jerry_value_t containing the module object, otherwise
251+
*/
252+
jerry_value_t jerry_port_get_native_module (jerry_value_t name);
253+
239254
/**
240255
* @}
241256
*/

jerry-core/parser/js/js-parser-module.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,12 @@ parser_module_context_init (void)
284284

285285
if (path_p == NULL)
286286
{
287+
ecma_ref_ecma_string (path_str_p);
287288
path_p = path_str_p;
288289
}
289290

290291
ecma_module_t *module_p = ecma_module_find_or_create_module (path_p);
291292

292-
if (path_p != path_str_p)
293-
{
294-
ecma_deref_ecma_string (path_p);
295-
}
296-
297293
module_p->state = ECMA_MODULE_STATE_EVALUATED;
298294
module_p->scope_p = ecma_get_global_environment ();
299295
ecma_ref_object (module_p->scope_p);
@@ -542,6 +538,28 @@ parser_module_handle_module_specifier (parser_context_t *context_p) /**< parser
542538

543539
lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL);
544540

541+
ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->lit_object.literal_p->u.char_p,
542+
context_p->lit_object.literal_p->prop.length);
543+
544+
ecma_module_t *module_p = ecma_module_find_module (name_p);
545+
if (module_p)
546+
{
547+
ecma_deref_ecma_string (name_p);
548+
goto module_found;
549+
}
550+
551+
ecma_value_t native = jerry_port_get_native_module (ecma_make_string_value (name_p));
552+
553+
if (!ecma_is_value_undefined (native))
554+
{
555+
JERRY_ASSERT (ecma_is_value_object (native));
556+
ecma_object_t *module_object_p = ecma_get_object_from_value (native);
557+
558+
module_p = ecma_module_create_native_module (name_p, module_object_p);
559+
goto module_found;
560+
}
561+
562+
ecma_deref_ecma_string (name_p);
545563
ecma_string_t *path_p = ecma_module_create_normalized_path (context_p->lit_object.literal_p->u.char_p,
546564
context_p->lit_object.literal_p->prop.length);
547565

@@ -550,9 +568,9 @@ parser_module_handle_module_specifier (parser_context_t *context_p) /**< parser
550568
parser_raise_error (context_p, PARSER_ERR_FILE_NOT_FOUND);
551569
}
552570

553-
ecma_module_t *module_p = ecma_module_find_or_create_module (path_p);
554-
ecma_deref_ecma_string (path_p);
571+
module_p = ecma_module_find_or_create_module (path_p);
555572

573+
module_found:
556574
module_node_p->module_request_p = module_p;
557575
lexer_next_token (context_p);
558576
} /* parser_module_handle_module_specifier */

0 commit comments

Comments
 (0)