diff --git a/docs/10.EXT-REFERENCE-HANDLER.md b/docs/10.EXT-REFERENCE-HANDLER.md index 193f29e405..7a30b0231a 100644 --- a/docs/10.EXT-REFERENCE-HANDLER.md +++ b/docs/10.EXT-REFERENCE-HANDLER.md @@ -1,3 +1,418 @@ +# Common methods to handle properties + +The `jerryscript-ext/handler.h` header defines a set of convenience methods +which makes the property access a bit straightforward. + +## jerryx_set_property_str + +**Summary** + +Set a property on a target object with a given name. + +*Note*: +- The property name must be a zero terminated UTF-8 string. +- There should be no '\0' (NULL) character in the name excluding the string terminator. +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. + + +**Prototype** + +```c +jerry_value_t +jerryx_set_property_str (const jerry_value_t target_object, + const char *name, + const jerry_value_t value); +``` + +- `target_object` - the object where the property should be set +- `name` - name of the property +- `value` - property value to be set +- return value + - JS true value, if success + - thrown error, if there was a problem setting the property + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" + +int +main (int argc, char **argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t global = jerry_get_global_object (); + + jerry_value_t value = jerry_create_number (3.3); + jerry_value_t result = jerryx_set_property_str (global, "value", value); + if (jerry_value_is_error (result)) + { + /* The error type/reason can be extracted via the `jerry_get_value_from_error` method */ + printf ("Error during property configuration\r\n"); + } + + jerry_release_value (result); + jerry_release_value (value); + jerry_release_value (global); + jerry_cleanup(); + + return 0; +} +``` + +## jerryx_get_property_str + +**Summary** + +Get the value of a property from the specified object with the given name. + +*Notes*: +- The property name must be a zero terminated UTF-8 string. +- There should be no '\0' (NULL) character in the name excluding the string terminator. +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it + is no longer needed. + + +**Prototype** + +``` +jerry_value_t +jerryx_get_property_str (const jerry_value_t target_object, + const char *name); +``` + +- `target_object` - object on which the property name is accessed +- `name` - property name as an UTF-8 `char*` +- return value + - value of property, if success + - thrown error, if there was a problem accessing the property + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" + +int +main (int argc, char **argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t global = jerry_get_global_object (); + + jerry_value_t math_object = jerryx_get_property_str (global, "Math"); + + /* use math_object */ + + jerry_release_value (math_object); + jerry_release_value (global); + jerry_cleanup(); + + return 0; +} +``` + +## jerryx_has_property_str + +**Summary** + +Check if a property exists on an object. + +*Notes*: +- The operation performed is the same as what the `jerry_has_property` method. +- The property name must be a zero terminated UTF-8 string. +- There should be no '\0' (NULL) character in the name excluding the string terminator. + + +**Prototype** + +``` +bool +jerryx_has_property_str (const jerry_value_t target_object, + const char *name); +``` + +- `target_object` - object on which the property name is accessed +- `name` - property name as an UTF-8 `char*` +- return value + - true, if the given property name exsits on the object + - false, if there is no such property name or there was an error accessing the property + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" + +int +main (int argc, char **argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t global = jerry_get_global_object (); + + bool have_math = jerryx_has_property_str (global, "Math"); + + jerry_release_value (global); + jerry_cleanup(); + + return have_math ? 0 : 1; +} +``` + +# Utility to register multiple properties in bulk + +In some cases it is useful to register multiple properties for a given object +for this the following utility structures and methods are provided. + +## jerryx_property_entry + +**Summary** + +Structure to define an array of properties with `name` and `value` fields which +can be registered to a target object. + +The engine must be initialied before specifying the `jerry_value_t` in the struct. + + +**Prototype** + +```c +typedef struct { + const char *name; + jerry_value_t value; +} jerryx_function_list_entry; +``` + +**See also** + +- [jerryx_set_properties](#jerryx_set_properties) + + +## jerryx_register_result + +**Summary** + +Structure returned as the result of the [jerryx_set_properties](#jerryx_set_properties) operation. +The `result` field will either be a JavaScript undefined value or an error object. +In every case the `registered` field is used to indicated the number of +successfully registered methods. + +This must be passed for the [jerryx_release_property_entry](#jerryx_release_property_entry) method +after the property registration. + +If any error occured during the property registration the `result` field of the structure +must be manually released after processing the error value. + +**Prototype** + +```c +typedef struct { + jerry_value_t result; + uint32_t registered; +} jerryx_register_result; +``` + +**See also** + +- [jerryx_set_properties](#jerryx_set_properties) +- [jerryx_release_property_entry](#jerryx_release_property_entry) + + +## jerryx_set_properties + +**Summary** + +Set multiple properties on a target object. + +The properties are an array of (name, jerry_value_t) pairs and +this list must end with a (NULL, 0) entry. + +Important notes: +* Each property value in the input array is released after a successful property registration. +* The method [jerryx_release_property_entry](#jerryx_release_property_entry) must be called if there is any failed registration + to release the values in the entries array. + It is safe to call this cleanup method in every case not just in case of failure. +* If the error value is reported via the result it must be freed manually. + +**Prototype** + +```c +jerryx_register_result +jerryx_set_properties (const jerry_value_t target_object, + const jerryx_property_entry entries[]); +``` + +- `target_object` - object on which the entries will be set. +- `entries` - array of (name, jerry_value_t) pairs. +- return a [jerryx_register_result](#jerryx_register_result). + - if everything is ok, the struct's `result` field is set to a JS undefined value. + - otherwise the `result` field is an error object indicating the problem. + - in every case the `registered` field contains the number of successfully registered properties. + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" + +static jerry_value_t +handler (const jerry_value_t function_obj, + const jerry_value_t this_val, + const jerry_value_t args_p[], + const jerry_length_t args_cnt) +{ + printf ("native handler called!\n"); + + return jerry_create_boolean (true); +} + +int +main (int argc, char **argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerryx_property_entry methods[] = + { + { "demo", jerry_create_external_function (handler) }, + { NULL, 0 }, + }; + + jerry_value_t global = jerry_get_global_object (); + jerryx_register_result reg = jerryx_set_properties (global, methods); + /* if `reg.result` is undefined all methods are registered */ + if (jerry_value_is_error (reg.result)) + { + printf ("Only registered %d properties\r\n", reg.registered); + /* clean up not registered property values */ + jerryx_release_property_entry (methods, reg); + + /* clean up the error */ + jerry_release_value (reg.result); + } + + jerry_release_value (global); + + jerry_cleanup(); + + return 0; +} +``` + +**Convenience macros** + +To make property registration convenient, there are a set of macros to use +when setting a property entry: + +* `JERRYX_PROPERTY_NUMBER(NAME, NUMBER)` - creates a number entry. +* `JERRYX_PROPERTY_STRING(NAME, STR)` - creates an UTF-8 string entry. This string must be zero terminated. +* `JERRYX_PROPERTY_STRING_SZ(NAME, STR, SIZE)` - creates an UTF-8 string entry using only `SIZE` bytes from the string. +* `JERRYX_PROPERTY_BOOLEAN(NAME, VALUE)` - creates a boolean entry. +* `JERRYX_PROPERTY_FUNCTION(NAME, NATIVE)` - creates a native C function entry. +* `JERRYX_PROPERTY_UNDEFINED(NAME)` - creates an undefined property entry. +* `JERRYX_PROPERTY_LIST_END()` - indicates the end of the property list. + +**Example usage of Convenience macros** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" + +static jerry_value_t +handler (const jerry_value_t function_obj, + const jerry_value_t this_val, + const jerry_value_t args_p[], + const jerry_length_t args_cnt) +{ + printf ("native handler called!\n"); + + return jerry_create_boolean (true); +} + +int +main (int argc, char **argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + /** + * Create a array of properties to be registered. + * This must be done after initializing the engine as creating `jerry_value_t` + * elements are invalid before `jerry_init`. + */ + jerryx_property_entry methods[] = + { + JERRYX_PROPERTY_FUNCTION ("demo", handler), + JERRYX_PROPERTY_NUMBER ("test_num", 2.3), + JERRYX_PROPERTY_UNDEFINED ("this_is_undefined"), + JERRYX_PROPERTY_LIST_END(), + }; + + jerry_value_t global = jerry_get_global_object (); + jerryx_register_result reg = jerryx_set_properties (global, methods); + /* if `reg.result` is undefined all methods are registered */ + if (jerry_value_is_error (reg.result)) + { + printf ("Only registered %d properties\r\n", reg.registered); + /* clean up not registered property values */ + jerryx_release_property_entry (methods, reg); + + /* clean up the error */ + jerry_release_value (reg.result); + } + + jerry_release_value (global); + + jerry_cleanup(); + + return 0; +} +``` + + +**See also** + +- [jerryx_property_entry](#jerryx_property_entry) +- [jerryx_release_property_entry](#jerryx_release_property_entry) +- [jerryx_register_result](#jerryx_register_result) + +## jerryx_release_property_entry + +**Summary** + +Release all `jerry_value_t` in a `jerryx_property_entry` array based on a previous [jerryx_set_properties](#jerryx_set_properties) call +and also the error value (if any) in the `jerryx_register_result` structure. +In case of a successful registration it is safe to call this method. + +After the method call the `ęntries` array should not be used as all values are released. + +**Prototype** + +``` +void +jerryx_release_property_entry (const jerryx_property_entry entries[], + const jerryx_register_result register_result); +``` + +- `entires` - array of [jerryx_property_entry](#jerryx_property_entry). +- `register_result` - result of a previous [jerryx_set_properties](#jerryx_set_properties) call. + +**Example** + +For example usage see [jerryx_set_properties](#jerryx_set_properties). + + # Common external function handlers ## jerryx_handler_assert_fatal diff --git a/jerry-ext/handler/handler-register.c b/jerry-ext/handler/handler-register.c index f30d8f0377..ad35d8c33f 100644 --- a/jerry-ext/handler/handler-register.c +++ b/jerry-ext/handler/handler-register.c @@ -40,3 +40,147 @@ jerryx_handler_register_global (const jerry_char_t *name_p, /**< name of the fun return result_val; } /* jerryx_handler_register_global */ + +/** + * Set multiple properties on a target object. + * + * The properties are an array of (name, property value) pairs and + * this list must end with a (NULL, 0) entry. + * + * Notes: + * - Each property value in the input array is released after a successful property registration. + * - The property name must be a zero terminated UTF-8 string. + * - There should be no '\0' (NULL) character in the name excluding the string terminator. + * - The method `jerryx_release_property_entry` must be called if there is any failed registration + * to release the values in the entries array. + * + * @return `jerryx_register_result` struct - if everything is ok with the (undefined, property entry count) values. + * In case of error the (error object, registered property count) pair. + */ +jerryx_register_result +jerryx_set_properties (const jerry_value_t target_object, /**< target object */ + const jerryx_property_entry entries[]) /**< array of method entries */ +{ +#define JERRYX_SET_PROPERTIES_RESULT(VALUE, IDX) ((jerryx_register_result) { VALUE, IDX }) + uint32_t idx = 0; + for (; ((entries + idx) != NULL) && (entries[idx].name != NULL); idx++) + { + const jerryx_property_entry *entry = &entries[idx]; + + jerry_value_t prop_name = jerry_create_string_from_utf8 ((const jerry_char_t *) entry->name); + jerry_value_t result = jerry_set_property (target_object, prop_name, entry->value); + + jerry_release_value (prop_name); + + // By API definition: + // The jerry_set_property returns TRUE if there is no problem + // and error object if there is any problem. + // Thus there is no need to check if the boolean value is false or not. + if (!jerry_value_is_boolean (result)) + { + return JERRYX_SET_PROPERTIES_RESULT (result, idx); + } + + jerry_release_value (entry->value); + jerry_release_value (result); + } + + return JERRYX_SET_PROPERTIES_RESULT (jerry_create_undefined (), idx); +#undef JERRYX_SET_PROPERTIES_RESULT +} /* jerryx_set_properties */ + +/** + * Release all jerry_value_t in a jerryx_property_entry array based on + * a previous jerryx_set_properties call. + * + * In case of a successful registration it is safe to call this method. + */ +void +jerryx_release_property_entry (const jerryx_property_entry entries[], /**< list of property entries */ + const jerryx_register_result register_result) /**< previous result of registration */ +{ + for (uint32_t idx = register_result.registered; + ((entries + idx) != NULL) && (entries[idx].name != NULL); + idx++) + { + jerry_release_value (entries[idx].value); + } +} /* jerryx_release_property_entry */ + +/** + * Set a property to a specified value with a given name. + * + * Notes: + * - The operation performed is the same as what the `jerry_set_property` method. + * - The property name must be a zero terminated UTF-8 string. + * - There should be no '\0' (NULL) character in the name excluding the string terminator. + * - Returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return true value - if the operation was successful + * thrown error - otherwise + */ +jerry_value_t +jerryx_set_property_str (const jerry_value_t target_object, /**< target object */ + const char *name, /**< property name */ + const jerry_value_t value) /**< value to set */ +{ + jerry_value_t property_name_val = jerry_create_string_from_utf8 ((const jerry_char_t *) name); + jerry_value_t result_val = jerry_set_property (target_object, property_name_val, value); + + jerry_release_value (property_name_val); + + return result_val; +} /* jerryx_set_property_str */ + +/** + * Get a property value of a specified object. + * + * Notes: + * - The operation performed is the same as what the `jerry_get_property` method. + * - The property name must be a zero terminated UTF-8 string. + * - There should be no '\0' (NULL) character in the name excluding the string terminator. + * - Returned value must be freed with jerry_release_value, when it is no longer needed. + * + * @return jerry_value_t - the property value + */ +jerry_value_t +jerryx_get_property_str (const jerry_value_t target_object, /**< target object */ + const char *name) /**< property name */ +{ + jerry_value_t prop_name = jerry_create_string_from_utf8 ((const jerry_char_t *) name); + jerry_value_t result_val = jerry_get_property (target_object, prop_name); + jerry_release_value (prop_name); + + return result_val; +} /* jerryx_get_property_str */ + +/** + * Check if a property exists on an object. + * + * Notes: + * - The operation performed is the same as what the `jerry_has_property` method. + * - The property name must be a zero terminated UTF-8 string. + * - There should be no '\0' (NULL) character in the name excluding the string terminator. + * + * @return true - if the property exists on the given object. + * false - if there is no such property or there was an error accessing the property. + */ +bool +jerryx_has_property_str (const jerry_value_t target_object, /**< target object */ + const char *name) /**< property name */ +{ + bool has_property = false; + + jerry_value_t prop_name = jerry_create_string_from_utf8 ((const jerry_char_t *) name); + jerry_value_t has_prop_val = jerry_has_property (target_object, prop_name); + + if (!jerry_value_is_error (has_prop_val)) + { + has_property = jerry_get_boolean_value (has_prop_val); + } + + jerry_release_value (has_prop_val); + jerry_release_value (prop_name); + + return has_property; +} /* jerryx_has_property_str */ diff --git a/jerry-ext/include/jerryscript-ext/handler.h b/jerry-ext/include/jerryscript-ext/handler.h index c9641d5fa2..41bfeeb885 100644 --- a/jerry-ext/include/jerryscript-ext/handler.h +++ b/jerry-ext/include/jerryscript-ext/handler.h @@ -45,6 +45,56 @@ jerry_value_t jerryx_handler_gc (const jerry_value_t func_obj_val, const jerry_v jerry_value_t jerryx_handler_print (const jerry_value_t func_obj_val, const jerry_value_t this_p, const jerry_value_t args_p[], const jerry_length_t args_cnt); +/** + * Struct used by the `jerryx_set_functions` method to + * register multiple methods for a given object. + */ +typedef struct +{ + const char *name; /**< name of the property to add */ + jerry_value_t value; /**< value of the property */ +} jerryx_property_entry; + +#define JERRYX_PROPERTY_NUMBER(NAME, NUMBER) (jerryx_property_entry) { NAME, jerry_create_number (NUMBER) } +#define JERRYX_PROPERTY_STRING(NAME, STR) \ + (jerryx_property_entry) { NAME, jerry_create_string_from_utf8 ((const jerry_char_t *) STR) } +#define JERRYX_PROPERTY_STRING_SZ(NAME, STR, SIZE) \ + (jerryx_property_entry) { NAME, jerry_create_string_sz_from_utf8 ((const jerry_char_t *) STR, SIZE) } +#define JERRYX_PROPERTY_BOOLEAN(NAME, VALUE) (jerryx_property_entry) { NAME, jerry_create_boolean (VALUE) } +#define JERRYX_PROPERTY_FUNCTION(NAME, FUNC) (jerryx_property_entry) { NAME, jerry_create_external_function (FUNC) } +#define JERRYX_PROPERTY_UNDEFINED(NAME) (jerryx_property_entry) { NAME, jerry_create_undefined() } +#define JERRYX_PROPERTY_LIST_END() (jerryx_property_entry) { NULL, 0 } + +/** + * Stores the result of property register operation. + */ +typedef struct +{ + jerry_value_t result; /**< result of property registraion (undefined or error object) */ + uint32_t registered; /**< number of successfully registered methods */ +} jerryx_register_result; + +jerryx_register_result +jerryx_set_properties (const jerry_value_t target_object, + const jerryx_property_entry entries[]); + +void +jerryx_release_property_entry (const jerryx_property_entry entries[], + const jerryx_register_result register_result); + +jerry_value_t +jerryx_set_property_str (const jerry_value_t target_object, + const char *name, + const jerry_value_t value); + +jerry_value_t +jerryx_get_property_str (const jerry_value_t target_object, + const char *name); + +bool +jerryx_has_property_str (const jerry_value_t target_object, + const char *name); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/tests/unit-ext/test-ext-method-register.c b/tests/unit-ext/test-ext-method-register.c new file mode 100644 index 0000000000..30e886f7f6 --- /dev/null +++ b/tests/unit-ext/test-ext-method-register.c @@ -0,0 +1,316 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Unit test for jerry-ext/handler property registration + */ + +#include "jerryscript.h" +#include "jerryscript-ext/handler.h" +#include "test-common.h" + +#include + +static jerry_value_t +method_hello (const jerry_value_t jfunc, /**< function object */ + const jerry_value_t jthis, /**< function this */ + const jerry_value_t jargv[], /**< arguments */ + const jerry_length_t jargc) /**< number of arguments */ +{ + (void) jfunc; + (void) jthis; + (void) jargv; + return jerry_create_number (jargc); +} /* method_hello */ + +/** + * Helper method to create a non-configurable property on an object + */ +static void +freeze_property (jerry_value_t target_obj, /**< target object */ + const char *target_prop) /**< target property name */ +{ + // "freeze" property + jerry_property_descriptor_t prop_desc; + jerry_init_property_descriptor_fields (&prop_desc); + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = false; + + jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) target_prop); + jerry_value_t return_value = jerry_define_own_property (target_obj, prop_name, &prop_desc); + TEST_ASSERT (jerry_value_is_boolean (return_value)); + jerry_release_value (return_value); + jerry_release_value (prop_name); + + jerry_free_property_descriptor_fields (&prop_desc); +} /* freeze_property */ + +/** + * Test registration of various property values. + */ +static void +test_simple_registration (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t target_object = jerry_create_object (); + + // Test simple registration + jerryx_property_entry methods[] = + { + JERRYX_PROPERTY_FUNCTION ("hello", method_hello), + JERRYX_PROPERTY_NUMBER ("my_number", 42.5), + JERRYX_PROPERTY_STRING ("my_str", "super_str"), + JERRYX_PROPERTY_STRING_SZ ("my_str_sz", "super_str", 6), + JERRYX_PROPERTY_BOOLEAN ("my_bool", true), + JERRYX_PROPERTY_BOOLEAN ("my_bool_false", false), + JERRYX_PROPERTY_UNDEFINED ("my_non_value"), + JERRYX_PROPERTY_LIST_END (), + }; + + jerryx_register_result register_result = jerryx_set_properties (target_object, methods); + + TEST_ASSERT (register_result.registered == 7); + TEST_ASSERT (jerry_value_is_undefined (register_result.result)); + + jerryx_release_property_entry (methods, register_result); + jerry_release_value (register_result.result); + + jerry_value_t global_obj = jerry_get_global_object (); + jerryx_set_property_str (global_obj, "test", target_object); + jerry_release_value (target_object); + jerry_release_value (global_obj); + + { + const char *test_A = "test.my_number"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_number (result)); + TEST_ASSERT (jerry_get_number_value (result) == 42.5); + jerry_release_value (result); + } + + { + const char *test_A = "test.my_str_sz === 'super_'"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_boolean (result)); + TEST_ASSERT (jerry_get_boolean_value (result) == true); + jerry_release_value (result); + } + + { + const char *test_A = "test.my_str === 'super_str'"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_boolean (result)); + TEST_ASSERT (jerry_get_boolean_value (result) == true); + jerry_release_value (result); + } + + { + const char *test_A = "test.my_bool"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_boolean (result)); + TEST_ASSERT (jerry_get_boolean_value (result) == true); + jerry_release_value (result); + } + + { + const char *test_A = "test.my_bool_false"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_boolean (result)); + TEST_ASSERT (jerry_get_boolean_value (result) == false); + jerry_release_value (result); + } + + { + const char *test_A = "test.my_non_value"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + } + + { + const char *test_A = "test.hello(33, 42, 2);"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_number (result)); + TEST_ASSERT ((uint32_t) jerry_get_number_value (result) == 3u); + jerry_release_value (result); + } + + { + const char *test_A = "test.hello();"; + jerry_value_t result = jerry_eval ((const jerry_char_t *) test_A, strlen (test_A), 0); + TEST_ASSERT (jerry_value_is_number (result)); + TEST_ASSERT ((uint32_t) jerry_get_number_value (result) == 0u); + jerry_release_value (result); + } + + jerry_cleanup (); +} /* test_simple_registration */ + +/** + * Test registration error. + * + * Trying to register a property which is already a non-configurable property + * should result in an error. + */ +static void +test_error_setvalue (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const char *target_prop = "test_err"; + jerry_value_t global_obj = jerry_get_global_object (); + freeze_property (global_obj, target_prop); + + jerry_value_t new_object = jerry_create_object (); + jerry_value_t set_result = jerryx_set_property_str (global_obj, target_prop, new_object); + TEST_ASSERT (jerry_value_is_error (set_result)); + + jerry_release_value (set_result); + jerry_release_value (new_object); + jerry_release_value (global_obj); + + jerry_cleanup (); +} /* test_error_setvalue */ + + +/** + * Test registration error with jerryx_set_properties. + * + * Trying to register a property which is already a non-configurable property + * should result in an error. + */ +static void +test_error_single_function (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const char *target_prop = "test_err"; + jerry_value_t target_object = jerry_create_object (); + freeze_property (target_object, target_prop); + + jerryx_property_entry methods[] = + { + JERRYX_PROPERTY_FUNCTION (target_prop, method_hello), // This registration should fail + JERRYX_PROPERTY_LIST_END (), + }; + + jerryx_register_result register_result = jerryx_set_properties (target_object, methods); + + TEST_ASSERT (register_result.registered == 0); + TEST_ASSERT (jerry_value_is_error (register_result.result)); + jerryx_release_property_entry (methods, register_result); + jerry_release_value (register_result.result); + + jerry_release_value (target_object); + + jerry_cleanup (); +} /* test_error_single_function */ + +/** + * Test to see if jerryx_set_properties exits at the first error. + */ +static void +test_error_multiple_functions (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + const char *prop_ok = "prop_ok"; + const char *prop_err = "prop_err"; + const char *prop_not = "prop_not"; + jerry_value_t target_object = jerry_create_object (); + freeze_property (target_object, prop_err); + + jerryx_property_entry methods[] = + { + JERRYX_PROPERTY_FUNCTION (prop_ok, method_hello), // This registration is ok + JERRYX_PROPERTY_FUNCTION (prop_err, method_hello), // This registration should fail + JERRYX_PROPERTY_FUNCTION (prop_not, method_hello), // This registration is not done + JERRYX_PROPERTY_LIST_END (), + }; + + jerryx_register_result register_result = jerryx_set_properties (target_object, methods); + + TEST_ASSERT (register_result.registered == 1); + TEST_ASSERT (jerry_value_is_error (register_result.result)); + + jerryx_release_property_entry (methods, register_result); + jerry_release_value (register_result.result); + + { + // Test if property "prop_ok" is correctly registered. + jerry_value_t prop_ok_val = jerry_create_string ((const jerry_char_t *) prop_ok); + jerry_value_t prop_ok_exists = jerry_has_own_property (target_object, prop_ok_val); + TEST_ASSERT (jerry_get_boolean_value (prop_ok_exists) == true); + jerry_release_value (prop_ok_exists); + + // Try calling the method + jerry_value_t prop_ok_func = jerry_get_property (target_object, prop_ok_val); + TEST_ASSERT (jerry_value_is_function (prop_ok_func) == true); + jerry_value_t args[2] = + { + jerry_create_number (22), + jerry_create_number (-3), + }; + jerry_size_t args_cnt = sizeof (args) / sizeof (jerry_value_t); + jerry_value_t func_result = jerry_call_function (prop_ok_func, + jerry_create_undefined (), + args, + args_cnt); + TEST_ASSERT (jerry_value_is_number (func_result) == true); + TEST_ASSERT ((uint32_t) jerry_get_number_value (func_result) == 2u); + jerry_release_value (func_result); + for (jerry_size_t idx = 0; idx < args_cnt; idx++) + { + jerry_release_value (args[idx]); + } + jerry_release_value (prop_ok_func); + jerry_release_value (prop_ok_val); + } + + { + // The "prop_err" should exist - as it was "freezed" - but it should not be a function + jerry_value_t prop_err_val = jerry_create_string ((const jerry_char_t *) prop_err); + jerry_value_t prop_err_exists = jerry_has_own_property (target_object, prop_err_val); + TEST_ASSERT (jerry_get_boolean_value (prop_err_exists) == true); + jerry_release_value (prop_err_exists); + + jerry_value_t prop_err_func = jerry_value_is_function (prop_err_val); + TEST_ASSERT (jerry_value_is_function (prop_err_func) == false); + jerry_release_value (prop_err_val); + } + + { // The "prop_not" is not available on the target object + jerry_value_t prop_not_val = jerry_create_string ((const jerry_char_t *) prop_not); + jerry_value_t prop_not_exists = jerry_has_own_property (target_object, prop_not_val); + TEST_ASSERT (jerry_get_boolean_value (prop_not_exists) == false); + jerry_release_value (prop_not_exists); + jerry_release_value (prop_not_val); + } + + jerry_release_value (target_object); + + jerry_cleanup (); +} /* test_error_multiple_functions */ + +int +main (void) +{ + test_simple_registration (); + test_error_setvalue (); + test_error_single_function (); + test_error_multiple_functions (); + return 0; +} /* main */