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
18#include "jerryscript.h"
19#include "test-common.h"
20#include "jerryscript-ext/module.h"
21
22/* Load a module. */
23const char eval_string1[] = "require ('my_custom_module');";
24
25/* Load a module using a different resolver. */
26const char eval_string2[] = "require ('differently-handled-module');";
27
28/* Load a broken module using the built-in resolver. */
29const char eval_string3[] =
30"(function() {"
31"  var theError;"
32"  try {"
33"    require ('my_broken_module');"
34"  } catch (anError) {"
35"    theError = anError;"
36"  }"
37"  return (((theError.message === 'Module on_resolve () must not be NULL') &&"
38"    (theError.moduleName === 'my_broken_module') &&"
39"    (theError instanceof TypeError)) ? 1 : 0);"
40"}) ();";
41
42/* Load a non-existent module. */
43const char eval_string4[] =
44"(function() {"
45"  var theError;"
46"  try {"
47"    require ('some_missing_module_xyzzy');"
48"  } catch (anError) {"
49"    theError = anError;"
50"  }"
51"  return (((theError.message === 'Module not found') &&"
52"    (theError.moduleName === 'some_missing_module_xyzzy')) ? 1 : 0);"
53"}) ();";
54
55/* Make sure the result of a module load is cached. */
56const char eval_string5[] =
57"(function() {"
58"  var x = require('cache-check');"
59"  var y = require('cache-check');"
60"  return x === y ? 1 : 0;"
61"}) ();";
62
63/* Make sure the result of a module load is removed from the cache. */
64const char eval_string6[] =
65"(function() {"
66"  var x = require('cache-check');"
67"  clear_require_cache('cache-check');"
68"  var y = require('cache-check');"
69"  return x !== y ? 1 : 0;"
70"}) ();";
71
72/* Make sure the entire cache is cleared. */
73const char eval_string7[] =
74"(function() {"
75"  var x = require('cache-check');"
76"  clear_require_cache(undefined);"
77"  var y = require('cache-check');"
78"  return x !== y ? 1 : 0;"
79"}) ();";
80
81/*
82 * Define a resolver for a module named "differently-handled-module" to check that custom resolvers work.
83 */
84static bool
85resolve_differently_handled_module (const jerry_value_t name,
86                                    jerry_value_t *result)
87{
88  jerry_size_t name_size = jerry_get_utf8_string_size (name);
89  JERRY_VLA (jerry_char_t, name_string, name_size);
90  jerry_string_to_utf8_char_buffer (name, name_string, name_size);
91
92  if (!strncmp ((char *) name_string, "differently-handled-module", name_size))
93  {
94    (*result) = jerry_create_number (29);
95    return true;
96  }
97  return false;
98} /* resolve_differently_handled_module */
99
100static jerryx_module_resolver_t differently_handled_module_resolver =
101{
102  NULL,
103  resolve_differently_handled_module
104};
105
106/*
107 * Define module "cache-check" via its own resolver as an empty object. Since objects are accessible only via references
108 * we can strictly compare the object returned on subsequent attempts at loading "cache-check" with the object returned
109 * on the first attempt and establish that the two are in fact the same object - which in turn shows that caching works.
110 */
111static bool
112cache_check (const jerry_value_t name,
113             jerry_value_t *result)
114{
115  jerry_size_t name_size = jerry_get_utf8_string_size (name);
116  JERRY_VLA (jerry_char_t, name_string, name_size);
117  jerry_string_to_utf8_char_buffer (name, name_string, name_size);
118
119  if (!strncmp ((char *) name_string, "cache-check", name_size))
120  {
121    (*result) = jerry_create_object ();
122    return true;
123  }
124  return false;
125} /* cache_check */
126
127static jerryx_module_resolver_t cache_check_resolver =
128{
129  NULL,
130  cache_check
131};
132
133static const jerryx_module_resolver_t *resolvers[3] =
134{
135  &jerryx_module_native_resolver,
136  &differently_handled_module_resolver,
137  &cache_check_resolver
138};
139
140static jerry_value_t
141handle_clear_require_cache (const jerry_value_t js_function,
142                            const jerry_value_t this_val,
143                            const jerry_value_t args_p[],
144                            const jerry_length_t args_count)
145{
146  (void) js_function;
147  (void) this_val;
148  (void) args_count;
149
150  TEST_ASSERT (args_count == 1);
151  jerryx_module_clear_cache (args_p[0], resolvers, 3);
152
153  return 0;
154} /* handle_clear_require_cache */
155
156static jerry_value_t
157handle_require (const jerry_value_t js_function,
158                const jerry_value_t this_val,
159                const jerry_value_t args_p[],
160                const jerry_length_t args_count)
161{
162  (void) js_function;
163  (void) this_val;
164  (void) args_count;
165
166  jerry_value_t return_value = 0;
167
168  TEST_ASSERT (args_count == 1);
169  return_value = jerryx_module_resolve (args_p[0], resolvers, 3);
170
171  return return_value;
172} /* handle_require */
173
174static void
175assert_number (jerry_value_t js_value, double expected_result)
176{
177  TEST_ASSERT (!jerry_value_is_error (js_value));
178  TEST_ASSERT (jerry_get_number_value (js_value) == expected_result);
179} /* assert_number */
180
181static void
182eval_one (const char *the_string, double expected_result)
183{
184  jerry_value_t js_eval_result = jerry_eval ((const jerry_char_t *) the_string,
185                                             strlen (the_string),
186                                             JERRY_PARSE_STRICT_MODE);
187  assert_number (js_eval_result, expected_result);
188  jerry_release_value (js_eval_result);
189} /* eval_one */
190
191#ifndef ENABLE_INIT_FINI
192extern void my_broken_module_register (void);
193extern void my_custom_module_register (void);
194#endif /* !ENABLE_INIT_FINI */
195
196int
197main (int argc, char **argv)
198{
199  (void) argc;
200  (void) argv;
201  jerry_value_t js_global = 0, js_function = 0, js_property_name = 0;
202  jerry_value_t res;
203
204#ifndef ENABLE_INIT_FINI
205  my_broken_module_register ();
206  my_custom_module_register ();
207#endif /* !ENABLE_INIT_FINI */
208
209  jerry_init (JERRY_INIT_EMPTY);
210
211  js_global = jerry_get_global_object ();
212
213  js_function = jerry_create_external_function (handle_require);
214  js_property_name = jerry_create_string ((const jerry_char_t *) "require");
215  res = jerry_set_property (js_global, js_property_name, js_function);
216  TEST_ASSERT (!jerry_value_is_error (res));
217  TEST_ASSERT (jerry_value_is_boolean (res) && jerry_get_boolean_value (res));
218  jerry_release_value (res);
219  jerry_release_value (js_property_name);
220  jerry_release_value (js_function);
221
222  js_function = jerry_create_external_function (handle_clear_require_cache);
223  js_property_name = jerry_create_string ((const jerry_char_t *) "clear_require_cache");
224  res = jerry_set_property (js_global, js_property_name, js_function);
225  TEST_ASSERT (!jerry_value_is_error (res));
226  TEST_ASSERT (jerry_value_is_boolean (res) && jerry_get_boolean_value (res));
227  jerry_release_value (res);
228  jerry_release_value (js_property_name);
229  jerry_release_value (js_function);
230
231  jerry_release_value (js_global);
232
233  eval_one (eval_string1, 42);
234  eval_one (eval_string2, 29);
235  eval_one (eval_string3, 1);
236  eval_one (eval_string4, 1);
237  eval_one (eval_string5, 1);
238  eval_one (eval_string6, 1);
239  eval_one (eval_string7, 1);
240
241  jerry_cleanup ();
242} /* main */
243