diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-jerry.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-jerry.cpp index d4deb010b..f023886b5 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-jerry.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-jerry.cpp @@ -440,6 +440,18 @@ ecma_op_extension_object_get_own_property (ecma_object_t *obj_p, /**< the extens switch (field_p->type) { + case JERRY_API_DATA_TYPE_UNDEFINED: + { + value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + + break; + } + case JERRY_API_DATA_TYPE_NULL: + { + value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL); + + break; + } case JERRY_API_DATA_TYPE_BOOLEAN: { value = ecma_make_simple_value (field_p->v_bool ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE); diff --git a/jerry-core/jerry-api.h b/jerry-core/jerry-api.h index b546505ec..cbdc9ba13 100644 --- a/jerry-core/jerry-api.h +++ b/jerry-core/jerry-api.h @@ -36,6 +36,8 @@ */ typedef enum { + JERRY_API_DATA_TYPE_UNDEFINED, /**< undefined */ + JERRY_API_DATA_TYPE_NULL, /**< null */ JERRY_API_DATA_TYPE_BOOLEAN, /**< bool */ JERRY_API_DATA_TYPE_FLOAT32, /**< 32-bit float */ JERRY_API_DATA_TYPE_FLOAT64, /**< 64-bit float */ @@ -57,9 +59,9 @@ typedef struct ecma_object_t jerry_api_object_t; /** * Description of an extension function's argument */ -typedef struct +typedef struct jerry_api_value_t { - const jerry_api_data_type_t type; /**< argument data type */ + jerry_api_data_type_t type; /**< argument data type */ union { @@ -93,11 +95,10 @@ extern EXTERN_C void jerry_api_release_object (jerry_api_object_t *object_p); extern EXTERN_C -bool jerry_api_call_function (jerry_api_object_t *function_object_p, - jerry_api_value_t *retval_p, - const jerry_api_value_t args_p [], - uint32_t args_count); +void jerry_api_release_value (jerry_api_value_t *value_p); +extern EXTERN_C +jerry_api_string_t* jerry_api_create_string (const char *v); extern EXTERN_C jerry_api_object_t* jerry_api_create_object (void); @@ -118,6 +119,15 @@ bool jerry_api_set_object_field_value (jerry_api_object_t *object_p, const char *field_name_p, const jerry_api_value_t *field_value_p); +extern EXTERN_C +bool jerry_api_call_function (jerry_api_object_t *function_object_p, + jerry_api_value_t *retval_p, + const jerry_api_value_t args_p [], + uint16_t args_count); + +extern EXTERN_C +jerry_api_object_t* jerry_api_get_global (void); + /** * @} */ diff --git a/jerry-core/jerry.cpp b/jerry-core/jerry.cpp index 42ff14d41..125dc43c9 100644 --- a/jerry-core/jerry.cpp +++ b/jerry-core/jerry.cpp @@ -16,10 +16,14 @@ #include #include "deserializer.h" +#include "ecma-alloc.h" +#include "ecma-builtins.h" #include "ecma-extension.h" +#include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-init-finalize.h" +#include "ecma-objects.h" #include "ecma-objects-general.h" #include "jerry.h" #include "jrt.h" @@ -56,6 +60,152 @@ static jerry_flag_t jerry_flags; */ char jerry_extension_characters_buffer [CONFIG_EXTENSION_CHAR_BUFFER_SIZE]; +/** + * Convert ecma-value to Jerry API value representation + * + * Note: + * if the output value contains string / object, it should be freed + * with jerry_api_release_string / jerry_api_release_object, + * just when it becomes unnecessary. + */ +static void +jerry_api_convert_ecma_value_to_api_value (jerry_api_value_t *out_value_p, /**< out: api value */ + const ecma_value_t& value) /**< ecma-value (undefined, + * null, boolean, number, + * string or object */ +{ + JERRY_ASSERT (out_value_p != NULL); + + if (ecma_is_value_undefined (value)) + { + out_value_p->type = JERRY_API_DATA_TYPE_UNDEFINED; + } + if (ecma_is_value_null (value)) + { + out_value_p->type = JERRY_API_DATA_TYPE_NULL; + } + else if (ecma_is_value_boolean (value)) + { + out_value_p->type = JERRY_API_DATA_TYPE_BOOLEAN; + out_value_p->v_bool = ecma_is_value_true (value); + } + else if (ecma_is_value_number (value)) + { + ecma_number_t *num = ecma_get_number_from_value (value); + +#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT32 + out_value_p->type = JERRY_API_DATA_TYPE_FLOAT32; + out_value_p->v_float32 = *num; +#elif CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 + out_value_p->type = JERRY_API_DATA_TYPE_FLOAT64; + out_value_p->v_float64 = *num; +#endif /* CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 */ + } + else if (ecma_is_value_string (value)) + { + ecma_string_t *str = ecma_get_string_from_value (value); + + out_value_p->type = JERRY_API_DATA_TYPE_STRING; + out_value_p->v_string = ecma_copy_or_ref_ecma_string (str); + } + else if (ecma_is_value_object (value)) + { + ecma_object_t *obj = ecma_get_object_from_value (value); + ecma_ref_object (obj); + + out_value_p->type = JERRY_API_DATA_TYPE_OBJECT; + out_value_p->v_object = obj; + } + else + { + /* Impossible type of conversion from ecma_value to api_value */ + JERRY_UNREACHABLE (); + } +} /* jerry_api_convert_ecma_value_to_api_value */ + +/** + * Convert value, represented in Jerry API format, to ecma-value. + * + * Note: + * the output ecma-value should be freed with ecma_free_value when it becomes unnecessary. + */ +static void +jerry_api_convert_api_value_to_ecma_value (ecma_value_t *out_value_p, /**< out: ecma-value */ + const jerry_api_value_t* api_value_p) /**< value in Jerry API format */ +{ + switch (api_value_p->type) + { + case JERRY_API_DATA_TYPE_UNDEFINED: + { + *out_value_p = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + + break; + } + case JERRY_API_DATA_TYPE_NULL: + { + *out_value_p = ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL); + + break; + } + case JERRY_API_DATA_TYPE_BOOLEAN: + { + *out_value_p = ecma_make_simple_value (api_value_p->v_bool ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE); + + break; + } + case JERRY_API_DATA_TYPE_FLOAT32: + { + ecma_number_t *num = ecma_alloc_number (); + *num = static_cast (api_value_p->v_float32); + + *out_value_p = ecma_make_number_value (num); + + break; + } + case JERRY_API_DATA_TYPE_FLOAT64: + { + ecma_number_t *num = ecma_alloc_number (); + *num = static_cast (api_value_p->v_float64); + + *out_value_p = ecma_make_number_value (num); + + break; + } + case JERRY_API_DATA_TYPE_UINT32: + { + ecma_number_t *num = ecma_alloc_number (); + *num = static_cast (api_value_p->v_uint32); + + *out_value_p = ecma_make_number_value (num); + + break; + } + case JERRY_API_DATA_TYPE_STRING: + { + ecma_string_t *str_p = ecma_copy_or_ref_ecma_string (api_value_p->v_string); + + *out_value_p = ecma_make_string_value (str_p); + + break; + } + case JERRY_API_DATA_TYPE_OBJECT: + { + ecma_object_t *obj_p = api_value_p->v_object; + + ecma_ref_object (obj_p); + + *out_value_p = ecma_make_object_value (obj_p); + + break; + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* jerry_api_convert_api_value_to_ecma_value */ + + /** * Extend Global scope with specified extension object * @@ -148,34 +298,34 @@ jerry_api_release_object (jerry_api_object_t *object_p) /**< pointer acquired th } /* jerry_api_release_object */ /** - * Call function specified by a function object + * Release specified Jerry API value + */ +void +jerry_api_release_value (jerry_api_value_t *value_p) /**< API value */ +{ + if (value_p->type == JERRY_API_DATA_TYPE_STRING) + { + jerry_api_release_string (value_p->v_string); + } + else if (value_p->type == JERRY_API_DATA_TYPE_OBJECT) + { + jerry_api_release_object (value_p->v_object); + } +} /* jerry_api_release_value */ + +/** + * Create a string * * Note: - * if call was performed successfully and returned value of type string or object, then caller - * should release the string / object with corresponding jerry_api_release_string / jerry_api_release_object, - * just when the value becomes unnecessary. + * caller should release the string with jerry_api_release_string, just when the value becomes unnecessary. * - * @return true, if call was performed successfully, i.e.: - * - arguments number equals to the function's length property; - * - no unhandled exceptions were thrown; - * - returned value type is corresponding to one of jerry_api_data_type_t (if retval_p is not NULL) - * or is 'undefined' (if retval_p is NULL); - * false - otherwise. + * @return pointer to created string */ -bool -jerry_api_call_function (jerry_api_object_t *function_object_p, /**< function object to call */ - jerry_api_value_t *retval_p, /**< place for function's return value (if it is required) - * or NULL (if it should be 'undefined') */ - const jerry_api_value_t args_p [], /**< function's call arguments - * (NULL if arguments number is zero) */ - uint32_t args_count) /**< number of the arguments */ +jerry_api_string_t* +jerry_api_create_string (const char *v) /**< string value */ { - JERRY_ASSERT (args_count == 0 - || args_p != NULL); - - JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented", - function_object_p, retval_p, args_p, args_count); -} /* jerry_api_call_function */ + return ecma_new_ecma_string ((const ecma_char_t*) v); +} /* jerry_api_create_string */ /** * Create an object @@ -205,8 +355,35 @@ jerry_api_add_object_field (jerry_api_object_t *object_p, /**< object to add fie const jerry_api_value_t *field_value_p, /**< value of the field */ bool is_writable) /**< flag indicating whether the created field should be writable */ { - JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented", - object_p, field_name_p, field_value_p, is_writable); + bool is_successful = false; + + if (ecma_get_object_extensible (object_p)) + { + ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p); + + ecma_property_t *prop_p = ecma_op_object_get_own_property (object_p, field_name_str_p); + + if (prop_p == NULL) + { + is_successful = true; + + ecma_value_t value_to_put; + jerry_api_convert_api_value_to_ecma_value (&value_to_put, field_value_p); + + prop_p = ecma_create_named_data_property (object_p, + field_name_str_p, + is_writable, + true, + true); + ecma_named_data_property_assign_value (object_p, prop_p, value_to_put); + + ecma_free_value (value_to_put, true); + } + + ecma_deref_ecma_string (field_name_str_p); + } + + return is_successful; } /* jerry_api_add_object_field */ /** @@ -220,21 +397,37 @@ bool jerry_api_delete_object_field (jerry_api_object_t *object_p, /**< object to delete field at */ const char *field_name_p) /**< name of the field */ { - JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented", - object_p, field_name_p); + bool is_successful = true; + + ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p); + + ecma_completion_value_t delete_completion = ecma_op_object_delete (object_p, + field_name_str_p, + true); + + if (!ecma_is_completion_value_normal (delete_completion)) + { + JERRY_ASSERT (ecma_is_completion_value_throw (delete_completion)); + + is_successful = false; + } + + ecma_free_completion_value (delete_completion); + + ecma_deref_ecma_string (field_name_str_p); + + return is_successful; } /* jerry_api_delete_object_field */ /** * Get value of field in the specified object * * Note: - * if value was retrieved successfully and it is of type string or object, then caller - * should release the string / object with corresponding jerry_api_release_string / jerry_api_release_object, - * just when the value becomes unnecessary. + * if value was retrieved successfully, it should be freed + * with jerry_api_release_value just when it becomes unnecessary. * * @return true, if field value was retrieved successfully, i.e. upon the call: * - there is field with specified name in the object; - * - field value is not undefined nor null; * false - otherwise. */ bool @@ -242,8 +435,31 @@ jerry_api_get_object_field_value (jerry_api_object_t *object_p, /**< object */ const char *field_name_p, /**< name of the field */ jerry_api_value_t *field_value_p) /**< out: field value, if retrieved successfully */ { - JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented", - object_p, field_name_p, field_value_p); + bool is_successful = true; + + ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p); + + ecma_completion_value_t get_completion = ecma_op_object_get (object_p, + field_name_str_p); + + if (ecma_is_completion_value_normal (get_completion)) + { + ecma_value_t val = ecma_get_completion_value_value (get_completion); + + jerry_api_convert_ecma_value_to_api_value (field_value_p, val); + } + else + { + JERRY_ASSERT (ecma_is_completion_value_throw (get_completion)); + + is_successful = false; + } + + ecma_free_completion_value (get_completion); + + ecma_deref_ecma_string (field_name_str_p); + + return is_successful; } /* jerry_api_get_object_field_value */ /** @@ -259,10 +475,114 @@ jerry_api_set_object_field_value (jerry_api_object_t *object_p, /**< object */ const char *field_name_p, /**< name of the field */ const jerry_api_value_t *field_value_p) /**< field value to set */ { - JERRY_UNIMPLEMENTED_REF_UNUSED_VARS ("API routine is not implemented", - object_p, field_name_p, field_value_p); + bool is_successful = true; + + ecma_string_t* field_name_str_p = ecma_new_ecma_string ((const ecma_char_t*) field_name_p); + + ecma_value_t value_to_put; + jerry_api_convert_api_value_to_ecma_value (&value_to_put, field_value_p); + + ecma_completion_value_t set_completion = ecma_op_object_put (object_p, + field_name_str_p, + value_to_put, + true); + + if (!ecma_is_completion_value_normal (set_completion)) + { + JERRY_ASSERT (ecma_is_completion_value_throw (set_completion)); + + is_successful = false; + } + + ecma_free_completion_value (set_completion); + + ecma_free_value (value_to_put, true); + ecma_deref_ecma_string (field_name_str_p); + + return is_successful; } /* jerry_api_set_object_field_value */ +/** + * Call function specified by a function object + * + * Note: + * if call was performed successfully, returned value should be freed + * with jerry_api_release_value just when the value becomes unnecessary. + * + * @return true, if call was performed successfully, i.e.: + * - no unhandled exceptions were thrown in connection with the call; + * false - otherwise. + */ +bool +jerry_api_call_function (jerry_api_object_t *function_object_p, /**< function object to call */ + jerry_api_value_t *retval_p, /**< place for function's return value (if it is required) + * or NULL (if it should be 'undefined') */ + const jerry_api_value_t args_p [], /**< function's call arguments + * (NULL if arguments number is zero) */ + uint16_t args_count) /**< number of the arguments */ +{ + JERRY_ASSERT (args_count == 0 || args_p != NULL); + JERRY_STATIC_ASSERT (sizeof (args_count) == sizeof (ecma_length_t)); + + bool is_successful = true; + + MEM_DEFINE_LOCAL_ARRAY (arg_values, args_count, ecma_value_t); + + for (uint32_t i = 0; i < args_count; ++i) + { + jerry_api_convert_api_value_to_ecma_value (&arg_values [i], &args_p [i]); + } + + ecma_completion_value_t call_completion; + + call_completion = ecma_op_function_call (function_object_p, + ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED), + arg_values, + args_count); + + if (ecma_is_completion_value_normal (call_completion)) + { + if (retval_p != NULL) + { + jerry_api_convert_ecma_value_to_api_value (retval_p, + ecma_get_completion_value_value (call_completion)); + } + } + else + { + /* unhandled exception during the function call */ + + JERRY_ASSERT (ecma_is_completion_value_throw (call_completion)); + + is_successful = false; + } + + ecma_free_completion_value (call_completion); + + for (uint32_t i = 0; i < args_count; i++) + { + ecma_free_value (arg_values [i], true); + } + + MEM_FINALIZE_LOCAL_ARRAY (arg_values); + + return is_successful; +} /* jerry_api_call_function */ + +/** + * Get global object + * + * Note: + * caller should release the object with jerry_api_release_object, just when the value becomes unnecessary. + * + * @return pointer to the global object + */ +jerry_api_object_t* +jerry_api_get_global (void) +{ + return ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); +} /* jerry_api_get_global */ + /** * Jerry engine initialization */ diff --git a/tests/unit/test_api.cpp b/tests/unit/test_api.cpp new file mode 100644 index 000000000..6eb0ce36a --- /dev/null +++ b/tests/unit/test_api.cpp @@ -0,0 +1,111 @@ +/* Copyright 2015 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include + +#include "jerry.h" +#include "jerry-api.h" + +const char *test_source = "var t = 1;\nfunction f () {\n return t;\n}\nthis.foo = f;\n"; + +/** + * Initialize Jerry API value with specified float64 number + */ +static void +test_api_init_api_value_float64 (jerry_api_value_t *out_value_p, /**< out: API value */ + double v) /**< float64 value to initialize with */ +{ + out_value_p->type = JERRY_API_DATA_TYPE_FLOAT64; + out_value_p->v_float64 = v; +} /* test_api_init_api_value_float64 */ + +/** + * Initialize Jerry API value with specified float64 number + */ +static void +test_api_init_api_value_string (jerry_api_value_t *out_value_p, /**< out: API value */ + const char* v) /**< string value to initialize with */ +{ + out_value_p->type = JERRY_API_DATA_TYPE_STRING; + out_value_p->v_string = jerry_api_create_string (v); +} /* test_api_init_api_value_string */ + +int +main (void) +{ + jerry_init (JERRY_FLAG_EMPTY); + + bool is_ok; + ssize_t sz; + jerry_api_value_t val_t, val_foo; + jerry_api_object_t* global_obj_p; + jerry_api_value_t res, args [2]; + char buffer [16]; + + is_ok = jerry_parse (NULL, test_source, strlen (test_source)); + assert (is_ok); + + is_ok = (jerry_run (NULL) == JERRY_COMPLETION_CODE_OK); + assert (is_ok); + + global_obj_p = jerry_api_get_global (); + + is_ok = jerry_api_get_object_field_value (global_obj_p, "t", &val_t); + assert (is_ok + && val_t.type == JERRY_API_DATA_TYPE_FLOAT64 + && val_t.v_float64 == 1.0); + jerry_api_release_value (&val_t); + + is_ok = jerry_api_get_object_field_value (global_obj_p, "foo", &val_foo); + assert (is_ok + && val_foo.type == JERRY_API_DATA_TYPE_OBJECT); + + test_api_init_api_value_float64 (&args[0], 4); + test_api_init_api_value_float64 (&args[1], 2); + + is_ok = jerry_api_call_function (val_foo.v_object, &res, args, 2); + assert (is_ok + && res.type == JERRY_API_DATA_TYPE_FLOAT64 + && res.v_float64 == 1.0); + jerry_api_release_value (&res); + + test_api_init_api_value_string (&args[0], "abcd"); + is_ok = jerry_api_set_object_field_value (global_obj_p, + "t", + &args[0]); + assert (is_ok); + jerry_api_release_value (&args[0]); + + is_ok = jerry_api_call_function (val_foo.v_object, &res, args, 2); + assert (is_ok + && res.type == JERRY_API_DATA_TYPE_STRING); + sz = jerry_api_string_to_char_buffer (res.v_string, NULL, 0); + assert (sz == -5); + sz = jerry_api_string_to_char_buffer (res.v_string, buffer, -sz); + assert (sz == 5); + jerry_api_release_value (&res); + assert (!strcmp (buffer, "abcd")); + + jerry_api_release_value (&val_foo); + + jerry_api_release_object (global_obj_p); + + jerry_cleanup (); + + return 0; +}