• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright JS Foundation and other contributors, http://js.foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <string.h>
17 #include "jerryscript.h"
18 #include "jerryscript-ext/module.h"
19 #if defined(JERRY_FOR_IAR_CONFIG)
20 #include "jerryscript-core.h"
21 #include "jrt.h"
22 #endif
23 
24 static const jerry_char_t *module_name_property_name = (jerry_char_t *) "moduleName";
25 static const jerry_char_t *module_not_found = (jerry_char_t *) "Module not found";
26 static const jerry_char_t *module_name_not_string = (jerry_char_t *) "Module name is not a string";
27 
28 /**
29  * Create an error related to modules
30  *
31  * Creates an error object of the requested type with the additional property "moduleName" the value of which is a
32  * string containing the name of the module that was requested when the error occurred.
33  *
34  * @return the error
35  */
36 static jerry_value_t
jerryx_module_create_error(jerry_error_t error_type,const jerry_char_t * message,const jerry_value_t module_name)37 jerryx_module_create_error (jerry_error_t error_type, /**< the type of error to create */
38                             const jerry_char_t *message, /**< the error message */
39                             const jerry_value_t module_name) /**< the module name */
40 {
41   jerry_value_t ret = jerry_create_error (error_type, message);
42 
43   jerry_value_t error_object = jerry_get_value_from_error (ret, false);
44   jerry_value_t property_name = jerry_create_string (module_name_property_name);
45 
46   jerry_release_value (jerry_set_property (error_object, property_name, module_name));
47 
48   jerry_release_value (property_name);
49   jerry_release_value (error_object);
50   return ret;
51 } /* jerryx_module_create_error */
52 
53 /**
54  * Initialize the module manager extension.
55  */
56 static void
jerryx_module_manager_init(void * user_data_p)57 jerryx_module_manager_init (void *user_data_p)
58 {
59   *((jerry_value_t *) user_data_p) = jerry_create_object ();
60 } /* jerryx_module_manager_init */
61 
62 /**
63  * Deinitialize the module manager extension.
64  */
65 static void
jerryx_module_manager_deinit(void * user_data_p)66 jerryx_module_manager_deinit (void *user_data_p) /**< context pointer to deinitialize */
67 {
68   jerry_release_value (*(jerry_value_t *) user_data_p);
69 } /* jerryx_module_manager_deinit */
70 
71 /**
72  * Declare the context data manager for modules.
73  */
74 static const jerry_context_data_manager_t jerryx_module_manager =
75 {
76   .init_cb = jerryx_module_manager_init,
77   .deinit_cb = jerryx_module_manager_deinit,
78   .bytes_needed = sizeof (jerry_value_t)
79 };
80 
81 /**
82  * Global static entry point to the linked list of available modules.
83  */
84 static jerryx_native_module_t *first_module_p = NULL;
85 
jerryx_native_module_register(jerryx_native_module_t * module_p)86 void jerryx_native_module_register (jerryx_native_module_t *module_p)
87 {
88   module_p->next_p = first_module_p;
89   first_module_p = module_p;
90 } /* jerryx_native_module_register */
91 
jerryx_native_module_unregister(jerryx_native_module_t * module_p)92 void jerryx_native_module_unregister (jerryx_native_module_t *module_p)
93 {
94   jerryx_native_module_t *parent_p = NULL, *iter_p = NULL;
95 
96   for (iter_p = first_module_p; iter_p != NULL; parent_p = iter_p, iter_p = iter_p->next_p)
97   {
98     if (iter_p == module_p)
99     {
100       if (parent_p)
101       {
102         parent_p->next_p = module_p->next_p;
103       }
104       else
105       {
106         first_module_p = module_p->next_p;
107       }
108       module_p->next_p = NULL;
109     }
110   }
111 } /* jerryx_native_module_unregister */
112 
113 /**
114  * Attempt to retrieve a module by name from a cache, and return false if not found.
115  */
116 static bool
jerryx_module_check_cache(jerry_value_t cache,jerry_value_t module_name,jerry_value_t * result)117 jerryx_module_check_cache (jerry_value_t cache, /**< cache from which to attempt to retrieve the module by name */
118                            jerry_value_t module_name, /**< JerryScript string value holding the module name */
119                            jerry_value_t *result) /**< Resulting value */
120 {
121   bool ret = false;
122 
123   /* Check if the cache has the module. */
124   jerry_value_t js_has_property = jerry_has_property (cache, module_name);
125 
126   /* If we succeed in getting an answer, we examine the answer. */
127   if (!jerry_value_is_error (js_has_property))
128   {
129     bool has_property = jerry_get_boolean_value (js_has_property);
130 
131     /* If the module is indeed in the cache, we return it. */
132     if (has_property)
133     {
134       if (result != NULL)
135       {
136         (*result) = jerry_get_property (cache, module_name);
137       }
138       ret = true;
139     }
140   }
141 
142   jerry_release_value (js_has_property);
143 
144   return ret;
145 } /* jerryx_module_check_cache */
146 
147 /**
148  * Attempt to cache a loaded module.
149  *
150  * @return the module on success, otherwise the error encountered when attempting to cache. In the latter case, the
151  * @p module is released.
152  */
153 static jerry_value_t
jerryx_module_add_to_cache(jerry_value_t cache,jerry_value_t module_name,jerry_value_t module)154 jerryx_module_add_to_cache (jerry_value_t cache, /**< cache to which to add the module */
155                             jerry_value_t module_name, /**< key at which to cache the module */
156                             jerry_value_t module) /**< the module to cache */
157 {
158   jerry_value_t ret = jerry_set_property (cache, module_name, module);
159 
160   if (jerry_value_is_error (ret))
161   {
162     jerry_release_value (module);
163   }
164   else
165   {
166     jerry_release_value (ret);
167     ret = module;
168   }
169 
170   return ret;
171 } /* jerryx_module_add_to_cache */
172 
173 static const jerry_char_t *on_resolve_absent = (jerry_char_t *) "Module on_resolve () must not be NULL";
174 
175 /**
176  * Declare and define the default module resolver - one which examines what modules are defined in the above linker
177  * section and loads one that matches the requested name, caching the result for subsequent requests using the context
178  * data mechanism.
179  */
180 static bool
jerryx_resolve_native_module(const jerry_value_t canonical_name,jerry_value_t * result)181 jerryx_resolve_native_module (const jerry_value_t canonical_name, /**< canonical name of the module */
182                               jerry_value_t *result) /**< [out] where to put the resulting module instance */
183 {
184   const jerryx_native_module_t *module_p = NULL;
185 #if defined(JERRY_FOR_IAR_CONFIG)
186   jerry_char_t* name_string;
187 #endif
188 
189   jerry_size_t name_size = jerry_get_utf8_string_size (canonical_name);
190 #if defined(JERRY_FOR_IAR_CONFIG)
191   name_string = (jerry_char_t*) jerry_vla_malloc (sizeof(jerry_char_t) * name_size);
192   if (!name_string)
193   {
194     JERRY_ERROR_MSG("malloc name_string fail\n");
195     return false;
196   }
197 #else
198   JERRY_VLA (jerry_char_t, name_string, name_size);
199 #endif
200   jerry_string_to_utf8_char_buffer (canonical_name, name_string, name_size);
201 
202   /* Look for the module by its name in the list of module definitions. */
203   for (module_p = first_module_p; module_p != NULL; module_p = module_p->next_p)
204   {
205     if (module_p->name_p != NULL
206         && strlen ((char *) module_p->name_p) == name_size
207         && !strncmp ((char *) module_p->name_p, (char *) name_string, name_size))
208     {
209       /* If we find the module by its name we load it and cache it if it has an on_resolve () and complain otherwise. */
210       (*result) = ((module_p->on_resolve_p) ? module_p->on_resolve_p ()
211                                             : jerryx_module_create_error (JERRY_ERROR_TYPE,
212                                                                           on_resolve_absent,
213                                                                           canonical_name));
214 #if defined(JERRY_FOR_IAR_CONFIG)
215       jerry_vla_free ((char*)name_string);
216 #endif
217       return true;
218     }
219   }
220 #if defined(JERRY_FOR_IAR_CONFIG)
221   jerry_vla_free ((char*)name_string);
222 #endif
223 
224   return false;
225 } /* jerryx_resolve_native_module */
226 
227 jerryx_module_resolver_t jerryx_module_native_resolver =
228 {
229   .get_canonical_name_p = NULL,
230   .resolve_p = jerryx_resolve_native_module
231 };
232 
233 static void
jerryx_module_resolve_local(const jerry_value_t name,const jerryx_module_resolver_t ** resolvers_p,size_t resolver_count,jerry_value_t * result)234 jerryx_module_resolve_local (const jerry_value_t name, /**< name of the module to load */
235                              const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */
236                              size_t resolver_count, /**< number of resolvers in @p resolvers */
237                              jerry_value_t *result) /**< location to store the result, or NULL to remove the module */
238 {
239   size_t index;
240   size_t canonical_names_used = 0;
241   jerry_value_t instances;
242 #if defined(JERRY_FOR_IAR_CONFIG)
243   jerry_value_t* canonical_names;
244   canonical_names = (jerry_value_t*) jerry_vla_malloc (sizeof(jerry_value_t) * resolver_count);
245   if (!canonical_names)
246   {
247     JERRY_ERROR_MSG("malloc canonical_names fail\n");
248     return;
249   }
250 #else
251   JERRY_VLA (jerry_value_t, canonical_names, resolver_count);
252 #endif
253   jerry_value_t (*get_canonical_name_p) (const jerry_value_t name);
254   bool (*resolve_p) (const jerry_value_t canonical_name,
255                      jerry_value_t *result);
256 
257   if (!jerry_value_is_string (name))
258   {
259     if (result != NULL)
260     {
261       *result = jerryx_module_create_error (JERRY_ERROR_COMMON, module_name_not_string, name);
262     }
263     goto done;
264   }
265 
266   instances = *(jerry_value_t *) jerry_get_context_data (&jerryx_module_manager);
267 
268   /**
269    * Establish the canonical name for the requested module. Each resolver presents its own canonical name. If one of
270    * the canonical names matches a cached module, it is returned as the result.
271    */
272   for (index = 0; index < resolver_count; index++)
273   {
274     get_canonical_name_p = (resolvers_p[index] == NULL ? NULL : resolvers_p[index]->get_canonical_name_p);
275     canonical_names[index] = ((get_canonical_name_p == NULL) ? jerry_acquire_value (name)
276                                                              : get_canonical_name_p (name));
277     canonical_names_used++;
278     if (jerryx_module_check_cache (instances, canonical_names[index], result))
279     {
280       /* A NULL for result indicates that we are to delete the module from the cache if found. Let's do that here.*/
281       if (result == NULL)
282       {
283         jerry_delete_property (instances, canonical_names[index]);
284       }
285       goto done;
286     }
287   }
288 
289   if (result == NULL)
290   {
291     goto done;
292   }
293 
294   /**
295    * Past this point we assume a module is wanted, and therefore result is not NULL. So, we try each resolver until one
296    * manages to resolve the module.
297    */
298   for (index = 0; index < resolver_count; index++)
299   {
300     resolve_p = (resolvers_p[index] == NULL ? NULL : resolvers_p[index]->resolve_p);
301     if (resolve_p != NULL && resolve_p (canonical_names[index], result))
302     {
303       if (!jerry_value_is_error (*result))
304       {
305         *result = jerryx_module_add_to_cache (instances, canonical_names[index], *result);
306       }
307       goto done;
308     }
309   }
310 
311   /* If none of the resolvers manage to find the module, complain with "Module not found" */
312   *result = jerryx_module_create_error (JERRY_ERROR_COMMON, module_not_found, name);
313 
314 done:
315   /* Release the canonical names as returned by the various resolvers. */
316   for (index = 0; index < canonical_names_used; index++)
317   {
318     jerry_release_value (canonical_names[index]);
319   }
320 #if defined(JERRY_FOR_IAR_CONFIG)
321   jerry_vla_free ((char*)canonical_names);
322 #endif
323 } /* jerryx_module_resolve_local */
324 
325 /**
326  * Resolve a single module using the module resolvers available in the section declared above and load it into the
327  * current context.
328  *
329  * @p name - name of the module to resolve
330  * @p resolvers - list of resolvers to invoke
331  * @p count - number of resolvers in the list
332  *
333  * @return a jerry_value_t containing one of the followings:
334  *   - the result of having loaded the module named @p name, or
335  *   - the result of a previous successful load, or
336  *   - an error indicating that something went wrong during the attempt to load the module.
337  */
338 jerry_value_t
jerryx_module_resolve(const jerry_value_t name,const jerryx_module_resolver_t ** resolvers_p,size_t resolver_count)339 jerryx_module_resolve (const jerry_value_t name, /**< name of the module to load */
340                        const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */
341                        size_t resolver_count) /**< number of resolvers in @p resolvers */
342 {
343   /* Set to zero to circumvent fatal warning. */
344   jerry_value_t ret = 0;
345   jerryx_module_resolve_local (name, resolvers_p, resolver_count, &ret);
346   return ret;
347 } /* jerryx_module_resolve */
348 
349 void
jerryx_module_clear_cache(const jerry_value_t name,const jerryx_module_resolver_t ** resolvers_p,size_t resolver_count)350 jerryx_module_clear_cache (const jerry_value_t name, /**< name of the module to remove, or undefined */
351                            const jerryx_module_resolver_t **resolvers_p, /**< list of resolvers */
352                            size_t resolver_count) /**< number of resolvers in @p resolvers */
353 {
354   void *instances_p = jerry_get_context_data (&jerryx_module_manager);
355 
356   if (jerry_value_is_undefined (name))
357   {
358     /* We were requested to clear the entire cache, so we bounce the context data in the most agnostic way possible. */
359     jerryx_module_manager.deinit_cb (instances_p);
360     jerryx_module_manager.init_cb (instances_p);
361     return;
362   }
363 
364   /* Delete the requested module from the cache if it's there. */
365   jerryx_module_resolve_local (name, resolvers_p, resolver_count, NULL);
366 } /* jerryx_module_clear_cache */
367