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/**
17 * Unit test for jerry-ext/handler property registration
18 */
19
20#include "jerryscript.h"
21#include "jerryscript-ext/handler.h"
22#include "test-common.h"
23
24#include <string.h>
25
26static jerry_value_t
27method_hello (const jerry_value_t jfunc,  /**< function object */
28              const jerry_value_t jthis,  /**< function this */
29              const jerry_value_t jargv[], /**< arguments */
30              const jerry_length_t jargc) /**< number of arguments */
31{
32  (void) jfunc;
33  (void) jthis;
34  (void) jargv;
35  return jerry_create_number (jargc);
36} /* method_hello */
37
38/**
39 * Helper method to create a non-configurable property on an object
40 */
41static void
42freeze_property (jerry_value_t target_obj, /**< target object */
43                 const char *target_prop) /**< target property name */
44{
45  // "freeze" property
46  jerry_property_descriptor_t prop_desc;
47  jerry_init_property_descriptor_fields (&prop_desc);
48  prop_desc.is_configurable_defined = true;
49  prop_desc.is_configurable = false;
50
51  jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) target_prop);
52  jerry_value_t return_value = jerry_define_own_property (target_obj, prop_name, &prop_desc);
53  TEST_ASSERT (jerry_value_is_boolean (return_value));
54  jerry_release_value (return_value);
55  jerry_release_value (prop_name);
56
57  jerry_free_property_descriptor_fields (&prop_desc);
58} /* freeze_property */
59
60/**
61 * Test registration of various property values.
62 */
63static void
64test_simple_registration (void)
65{
66  jerry_init (JERRY_INIT_EMPTY);
67
68  jerry_value_t target_object = jerry_create_object ();
69
70  // Test simple registration
71  jerryx_property_entry methods[] =
72  {
73    JERRYX_PROPERTY_FUNCTION ("hello", method_hello),
74    JERRYX_PROPERTY_NUMBER ("my_number", 42.5),
75    JERRYX_PROPERTY_STRING ("my_str", "super_str"),
76    JERRYX_PROPERTY_STRING_SZ ("my_str_sz", "super_str", 6),
77    JERRYX_PROPERTY_BOOLEAN ("my_bool", true),
78    JERRYX_PROPERTY_BOOLEAN ("my_bool_false", false),
79    JERRYX_PROPERTY_UNDEFINED ("my_non_value"),
80    JERRYX_PROPERTY_LIST_END (),
81  };
82
83  jerryx_register_result register_result = jerryx_set_properties (target_object, methods);
84
85  TEST_ASSERT (register_result.registered == 7);
86  TEST_ASSERT (jerry_value_is_undefined (register_result.result));
87
88  jerryx_release_property_entry (methods, register_result);
89  jerry_release_value (register_result.result);
90
91  jerry_value_t global_obj = jerry_get_global_object ();
92  jerryx_set_property_str (global_obj, "test", target_object);
93  jerry_release_value (target_object);
94  jerry_release_value (global_obj);
95
96  {
97    const char *test_A = "test.my_number";
98    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
99    TEST_ASSERT (jerry_value_is_number (result));
100    TEST_ASSERT (jerry_get_number_value (result) == 42.5);
101    jerry_release_value (result);
102  }
103
104  {
105    const char *test_A = "test.my_str_sz === 'super_'";
106    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
107    TEST_ASSERT (jerry_value_is_boolean (result));
108    TEST_ASSERT (jerry_get_boolean_value (result) == true);
109    jerry_release_value (result);
110  }
111
112  {
113    const char *test_A = "test.my_str === 'super_str'";
114    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
115    TEST_ASSERT (jerry_value_is_boolean (result));
116    TEST_ASSERT (jerry_get_boolean_value (result) == true);
117    jerry_release_value (result);
118  }
119
120  {
121    const char *test_A = "test.my_bool";
122    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
123    TEST_ASSERT (jerry_value_is_boolean (result));
124    TEST_ASSERT (jerry_get_boolean_value (result) == true);
125    jerry_release_value (result);
126  }
127
128  {
129    const char *test_A = "test.my_bool_false";
130    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
131    TEST_ASSERT (jerry_value_is_boolean (result));
132    TEST_ASSERT (jerry_get_boolean_value (result) == false);
133    jerry_release_value (result);
134  }
135
136  {
137    const char *test_A = "test.my_non_value";
138    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
139    TEST_ASSERT (jerry_value_is_undefined (result));
140    jerry_release_value (result);
141  }
142
143  {
144    const char *test_A = "test.hello(33, 42, 2);";
145    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
146    TEST_ASSERT (jerry_value_is_number (result));
147    TEST_ASSERT ((uint32_t) jerry_get_number_value (result) == 3u);
148    jerry_release_value (result);
149  }
150
151  {
152    const char *test_A = "test.hello();";
153    jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0);
154    TEST_ASSERT (jerry_value_is_number (result));
155    TEST_ASSERT ((uint32_t) jerry_get_number_value (result) == 0u);
156    jerry_release_value (result);
157  }
158
159  jerry_cleanup ();
160} /* test_simple_registration */
161
162/**
163 * Test registration error.
164 *
165 * Trying to register a property which is already a non-configurable property
166 * should result in an error.
167 */
168static void
169test_error_setvalue (void)
170{
171  jerry_init (JERRY_INIT_EMPTY);
172
173  const char *target_prop = "test_err";
174  jerry_value_t global_obj = jerry_get_global_object ();
175  freeze_property (global_obj, target_prop);
176
177  jerry_value_t new_object = jerry_create_object ();
178  jerry_value_t set_result = jerryx_set_property_str (global_obj, target_prop, new_object);
179  TEST_ASSERT (jerry_value_is_error (set_result));
180
181  jerry_release_value (set_result);
182  jerry_release_value (new_object);
183  jerry_release_value (global_obj);
184
185  jerry_cleanup ();
186} /* test_error_setvalue */
187
188/**
189 * Test registration error with jerryx_set_properties.
190 *
191 * Trying to register a property which is already a non-configurable property
192 * should result in an error.
193 */
194static void
195test_error_single_function (void)
196{
197  jerry_init (JERRY_INIT_EMPTY);
198
199  const char *target_prop = "test_err";
200  jerry_value_t target_object = jerry_create_object ();
201  freeze_property (target_object, target_prop);
202
203  jerryx_property_entry methods[] =
204  {
205    JERRYX_PROPERTY_FUNCTION (target_prop, method_hello), // This registration should fail
206    JERRYX_PROPERTY_LIST_END (),
207  };
208
209  jerryx_register_result register_result = jerryx_set_properties (target_object, methods);
210
211  TEST_ASSERT (register_result.registered == 0);
212  TEST_ASSERT (jerry_value_is_error (register_result.result));
213  jerryx_release_property_entry (methods, register_result);
214  jerry_release_value (register_result.result);
215
216  jerry_release_value (target_object);
217
218  jerry_cleanup ();
219} /* test_error_single_function */
220
221/**
222 * Test to see if jerryx_set_properties exits at the first error.
223 */
224static void
225test_error_multiple_functions (void)
226{
227  jerry_init (JERRY_INIT_EMPTY);
228
229  const char *prop_ok = "prop_ok";
230  const char *prop_err = "prop_err";
231  const char *prop_not = "prop_not";
232  jerry_value_t target_object = jerry_create_object ();
233  freeze_property (target_object, prop_err);
234
235  jerryx_property_entry methods[] =
236  {
237    JERRYX_PROPERTY_FUNCTION (prop_ok, method_hello), // This registration is ok
238    JERRYX_PROPERTY_FUNCTION (prop_err, method_hello), // This registration should fail
239    JERRYX_PROPERTY_FUNCTION (prop_not, method_hello), // This registration is not done
240    JERRYX_PROPERTY_LIST_END (),
241  };
242
243  jerryx_register_result register_result = jerryx_set_properties (target_object, methods);
244
245  TEST_ASSERT (register_result.registered == 1);
246  TEST_ASSERT (jerry_value_is_error (register_result.result));
247
248  jerryx_release_property_entry (methods, register_result);
249  jerry_release_value (register_result.result);
250
251  {
252    // Test if property "prop_ok" is correctly registered.
253    jerry_value_t prop_ok_val = jerry_create_string ((const jerry_char_t *) prop_ok);
254    jerry_value_t prop_ok_exists = jerry_has_own_property (target_object, prop_ok_val);
255    TEST_ASSERT (jerry_get_boolean_value (prop_ok_exists) == true);
256    jerry_release_value (prop_ok_exists);
257
258    // Try calling the method
259    jerry_value_t prop_ok_func = jerry_get_property (target_object, prop_ok_val);
260    TEST_ASSERT (jerry_value_is_function (prop_ok_func) == true);
261    jerry_value_t args[2] =
262    {
263      jerry_create_number (22),
264      jerry_create_number (-3),
265    };
266    jerry_size_t args_cnt = sizeof (args) / sizeof (jerry_value_t);
267    jerry_value_t func_result = jerry_call_function (prop_ok_func,
268                                                     jerry_create_undefined (),
269                                                     args,
270                                                     args_cnt);
271    TEST_ASSERT (jerry_value_is_number (func_result) == true);
272    TEST_ASSERT ((uint32_t) jerry_get_number_value (func_result) == 2u);
273    jerry_release_value (func_result);
274    for (jerry_size_t idx = 0; idx < args_cnt; idx++)
275    {
276      jerry_release_value (args[idx]);
277    }
278    jerry_release_value (prop_ok_func);
279    jerry_release_value (prop_ok_val);
280  }
281
282  {
283    // The "prop_err" should exist - as it was "freezed" - but it should not be a function
284    jerry_value_t prop_err_val = jerry_create_string ((const jerry_char_t *) prop_err);
285    jerry_value_t prop_err_exists = jerry_has_own_property (target_object, prop_err_val);
286    TEST_ASSERT (jerry_get_boolean_value (prop_err_exists) == true);
287    jerry_release_value (prop_err_exists);
288
289    jerry_value_t prop_err_func = jerry_value_is_function (prop_err_val);
290    TEST_ASSERT (jerry_value_is_function (prop_err_func) == false);
291    jerry_release_value (prop_err_val);
292  }
293
294  { // The "prop_not" is not available on the target object
295    jerry_value_t prop_not_val = jerry_create_string ((const jerry_char_t *) prop_not);
296    jerry_value_t prop_not_exists = jerry_has_own_property (target_object, prop_not_val);
297    TEST_ASSERT (jerry_get_boolean_value (prop_not_exists) == false);
298    jerry_release_value (prop_not_exists);
299    jerry_release_value (prop_not_val);
300  }
301
302  jerry_release_value (target_object);
303
304  jerry_cleanup ();
305} /* test_error_multiple_functions */
306
307int
308main (void)
309{
310  test_simple_registration ();
311  test_error_setvalue ();
312  test_error_single_function ();
313  test_error_multiple_functions ();
314  return 0;
315} /* main */
316