-
Notifications
You must be signed in to change notification settings - Fork 682
module extension: add support for canonical name resolution #2013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
module extension: add support for canonical name resolution #2013
Conversation
c9cb700
to
4b53e14
Compare
docs/12.EXT-REFERENCE-MODULE.md
Outdated
jerry_value_t (*get_canonical_name) (const jerry_value_t name); | ||
bool (*resolve) (const jerry_value_t canonical_name, | ||
jerry_value_t *result); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} jerryx_module_resolver_t;
4b53e14
to
9faffed
Compare
@LaszloLango fixed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good patch overall.
docs/12.EXT-REFERENCE-MODULE.md
Outdated
{ | ||
jerry_value_t resulting_path; | ||
|
||
/* Search name for "./" and "../", follow symlinks, etc., then create resulting_path via jerry_create_string () */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Full stop after sentence.
docs/12.EXT-REFERENCE-MODULE.md
Outdated
``` | ||
|
||
We can now load JavaScript files: | ||
```c | ||
static const jerryx_module_resolver_t resolvers[] = | ||
{ | ||
/* Consult the JerryScript module resolver first, in case the requested module is a compiled-in JerryScript module. */ | ||
jerryx_module_native_resolver, | ||
&jerryx_module_native_resolver, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need & ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because jerryx_module_native_resolver
is now a structure stored in a global variable, and this is an array of structure pointers, so I will update the declaration of resolvers
a few lines up.
jerry-ext/module/module.c
Outdated
bool return_value = false; | ||
|
||
jerry_size_t c_module_name_size = jerry_get_utf8_string_size (name); | ||
jerry_char_t c_module_name[c_module_name_size]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this allocation is simple in source code level, I always has doubts about its efficiency. You need to move a lot of data on the stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, should I use jmem_heap_alloc_block_null_on_error()
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am just asking what is its cost. We could introduce convenience stuff for them (I saw you already created some macros for it).
jerry-ext/module/module.c
Outdated
jerry_value_t ret = 0; | ||
const jerryx_module_resolver_t *resolver_p; | ||
jerry_value_t instances; | ||
jerry_value_t canonical_names_static[5]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets use a define for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean "use a define"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#define CANONICAL_NAMES_LOCAL_SIZE 4
or something. I think 4 is enough (probably 2 is the most frequent in practice).
jerry-ext/module/module.c
Outdated
|
||
for (index = 0; index < count; index++) | ||
{ | ||
canonical_names[index] = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0? Not undefined?
jerry-ext/module/module.c
Outdated
resolver_p = resolvers_p[index]; | ||
canonical_names[index] = | ||
resolver_p->get_canonical_name == NULL ? jerry_acquire_value (name) : | ||
resolver_p->get_canonical_name (name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting alignment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I move the else under jerry_acquire_value ()
I get this from check-vera.sh:
./jerry-ext/module/module.c:235: error: Indentation: 45 -> 4.
So, that's why it's like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use round brackets.
9faffed
to
fd0cbe4
Compare
jerry-ext/module/module.c
Outdated
jerryx_module_resolver_t jerryx_module_native_resolver = | ||
{ | ||
NULL, | ||
jerryx_resolve_native_module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.get_canonical_name = NULL,
.resolve = jerryx_resolve_native_module,
{ | ||
jerry_value_t (*get_canonical_name) (const jerry_value_t name); /**< module name to canonicalize */ | ||
bool (*resolve) (const jerry_value_t canonical_name, /**< requested module's canonical name */ | ||
jerry_value_t *result); /**< resulting module */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's your rationale for passing around the name strings as jerry_value_t
s?
I think most module resolvers will convert it right back to a C string.
Is it to avoid having to deal with memory management of the C strings?
(Probably a good reason since we can't assume malloc/free...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, and I was actually thinking that, for path resolution, you might not even convert the whole value to a C string, but you would rather use fixed size chunks to gradually retrieve the string and maybe assemble the canonical name in a similarly gradual fashion. Then, unless you had a directory with a really, really, really long name, you wouldn't really be allocating all that much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call it streamed path resolution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another very important reason: the module cache is stored as a JavaScript object, so I have to convert a C string back to a jerry_value_t
in order to check if that JavaScript object has a module at the module name's key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, yeah that makes sense.
cd7e880
to
ac23faa
Compare
@martijnthe @LaszloLango @zherczeg I have updated the patch to reflect the changes to the moduling system. |
docs/12.EXT-REFERENCE-MODULE.md
Outdated
{ | ||
jerry_value_t resulting_path; | ||
|
||
/* Search name for "./" and "../", follow symlinks, etc., then create resulting_path via jerry_create_string (). */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should emphasize here that the point is if two references points to the same physical file, it should be loaded only once.
|
||
/** | ||
* Load a copy of a module into the current context using the provided module resolvers, or return one that was already | ||
* loaded if it is found. | ||
*/ | ||
jerry_value_t jerryx_module_resolve (const jerry_char_t *name, const jerryx_module_resolver_t *resolvers, size_t count); | ||
jerry_value_t jerryx_module_resolve (const jerry_value_t name, /**< module's name */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
{ | ||
jerry_value_t (*get_canonical_name) (const jerry_value_t name); /**< module name to canonicalize */ | ||
bool (*resolve) (const jerry_value_t canonical_name, /**< requested module's canonical name */ | ||
jerry_value_t *result); /**< resulting module */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not use comments in header files.
{ | ||
const jerryx_native_module_t *module_p = NULL; | ||
|
||
jerry_size_t name_size = jerry_get_utf8_string_size (canonical_name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we call tostring on the name? Or check if it is a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't need to. This function should not be called directly, even though a pointer to it is available. It should only be called by jerryx_module_resolve ()
and it should only ever receive a jerry_value_t
stored in the array of canonical module names created by jerryx_module_resolve ()
. In fact, this function is not even documented. Only the structure jerryx_module_native_resolver
is documented, and only the availability of it as a global data symbol is documented, not its contents.
jerryx_module_resolve (const jerry_char_t *name, /**< name of the module to load */ | ||
const jerryx_module_resolver_t *resolvers_p, /**< list of resolvers */ | ||
jerryx_module_resolve (const jerry_value_t name, /**< name of the module to load */ | ||
const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
**
? A pointer to a list of pointers which points to the resolvers? Why not an array of resolvers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the resolvers are stored in-contiguously, and we do not want to make copies of them, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are they used multiple times? Or why they are scattered?
I think one application use one resolver list, but this is not much of a code size increase, so lleave it be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolvers may be declared global in separate .c files (jerryx_module_native_resolver
certainly is), and they are collected into an array only once - the place that calls jerryx_module_resolve ()
.
jerry-ext/module/module.c
Outdated
resolver_p = resolvers_p[index]; | ||
canonical_names[index] = ((resolver_p && resolver_p->get_canonical_name != NULL) ? | ||
resolver_p->get_canonical_name (name): | ||
jerry_acquire_value (name)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not how we align ?:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's pretty tough not breaking 120 columns with this line. I'm still trying to figure out how to align it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed resolver_p
to res_p
then it fits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move the condition into a bool value rather than shoretning the name.
@@ -61,41 +61,61 @@ const char eval_string5[] = | |||
"}) ();"; | |||
|
|||
static bool | |||
resolve_differently_handled_module (const jerry_char_t *name, | |||
resolve_differently_handled_module (const jerry_value_t name, | |||
jerry_value_t *result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is missing, spaces are wrong.
ac23faa
to
0774aed
Compare
@zherczeg I have addressed your review comments. |
702fdd4
to
45dbd81
Compare
@zherczeg instead of a boolean I went straight for storing the function pointers in variables. |
45dbd81
to
2ff4b5f
Compare
... aaand I forgot to key the cache on the canonical name. Fixed now. |
@zherczeg could you or somebody with Travis access please restart the lint job? It seems to have been killed because it had stalled. |
Thanks! |
struct jerryx_native_module_t *next_p; /**< pointer to next module in the list */ | ||
const jerry_char_t *name_p; | ||
const jerryx_native_module_on_resolve_t on_resolve; | ||
struct jerryx_native_module_t *next_p; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove the comments from here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think there was a misunderstanding here. We do not add comments to declarations, but only to definitions. The type definitions are usually in the header files, so we must put the comments there. Please don't remove these comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I mean only for function arguments, not for types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice improvement
jerry-ext/module/module.c
Outdated
@@ -14,11 +14,13 @@ | |||
*/ | |||
|
|||
#include <string.h> | |||
#include "jmem.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should not use internal headers in jerry-ext
. I don't see what are you using from it. Please remove it if possible.
bool jerryx_module_native_resolver (const jerry_char_t *name, jerry_value_t *result); | ||
typedef struct | ||
{ | ||
jerry_value_t (*get_canonical_name) (const jerry_value_t name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to know for sure. Do we, or do we not add /**<
comments after structure members and function arguments? How do we document function pointer structure members, since the formal parameters of the function signature also needs to be documented?
2ff4b5f
to
d106ad4
Compare
@LaszloLango fixed up the comments and added some |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with some style fix.
*/ | ||
typedef jerry_value_t (*jerryx_module_get_canonical_name_t) (const jerry_value_t name); /**< The name for which to | ||
* compute the canonical | ||
* name */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align the start of the comment lines to the same column.
bool jerryx_module_native_resolver (const jerry_char_t *name, jerry_value_t *result); | ||
typedef bool (*jerryx_module_resolve_t) (const jerry_value_t canonical_name, /**< The module's canonical name */ | ||
jerry_value_t *result); /**< The resulting module, if the function returns | ||
* true */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto. And similar to below.
|
||
return (!strcmp ((char *) name_string, "batman") ? jerry_acquire_value (name): | ||
(!strcmp ((char *) name_string, "bruce-wayne") ? jerry_create_string ((jerry_char_t *) "batman"): | ||
jerry_create_undefined ())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the style of these lines.
jerry_init (JERRY_INIT_EMPTY); | ||
|
||
jerry_value_t batman = jerry_create_string ((jerry_char_t *) "batman"); | ||
jerry_value_t bruce_wayne = jerry_create_string ((jerry_char_t *) "bruce-wayne"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope these are not copyrighted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I'll use Liberator, which is public domain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are still, however, "use at your own risk" characters. - this doesn't sound entirely public
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sigh Alice and Bob it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after the style fixes.
d106ad4
to
8f9dc5e
Compare
@zherczeg @LaszloLango please have another look. I updated the test to use |
Before attempting to load a module, each provided resolver must be given an opportunity to examine the name of the requested module without actually loading it so as to canonicalize it, in case a module can be referred to by multiple names. Then, modules are loaded and cached by their canonical name. JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof [email protected]
8f9dc5e
to
b0fe0bf
Compare
still LGTM |
Still ok. |
…ipt-project#2013) Before attempting to load a module, each provided resolver must be given an opportunity to examine the name of the requested module without actually loading it so as to canonicalize it, in case a module can be referred to by multiple names. Then, modules are loaded and cached by their canonical name. JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof [email protected]
…ipt-project#2013) Before attempting to load a module, each provided resolver must be given an opportunity to examine the name of the requested module without actually loading it so as to canonicalize it, in case a module can be referred to by multiple names. Then, modules are loaded and cached by their canonical name. JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof [email protected]
…ipt-project#2013) Before attempting to load a module, each provided resolver must be given an opportunity to examine the name of the requested module without actually loading it so as to canonicalize it, in case a module can be referred to by multiple names. Then, modules are loaded and cached by their canonical name. JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof [email protected]
Before attempting to load a module, each provided resolver must be given an
opportunity to examine the name of the requested module without actually
loading it so as to canonicalize it, in case a module can be referred to by
multiple names.
Then, modules are loaded and cached by their canonical name.