diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 22e6f6aca..44025b5b9 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -11,6 +11,19 @@ Enum that contains the following elements: - JERRY_INIT_MEM_STATS_SEPARATE - dump memory statistics and reset peak values after parse - JERRY_INIT_DEBUGGER - deprecated, an unused placeholder now +## jerry_type_t + +Enum that contains a set of elements to represent JavaScript type: + + - JERRY_TYPE_NONE - no type information + - JERRY_TYPE_UNDEFINED - undefined value + - JERRY_TYPE_NULL - null value + - JERRY_TYPE_BOOLEAN - boolean value + - JERRY_TYPE_NUMBER - number value + - JERRY_TYPE_STRING - string value + - JERRY_TYPE_OBJECT - object value + - JERRY_TYPE_FUNCTION - function value + ## jerry_error_t Possible types of an error: @@ -1447,6 +1460,44 @@ jerry_value_is_undefined (const jerry_value_t value) - [jerry_release_value](#jerry_release_value) +## jerry_value_get_type + +**Summary** + +Returns the JavaScript type +for a given value as a [jerry_type_t](#jerry_type_t) enum value. + +This is a similar operation as the 'typeof' operator +in the standard with an exception that the 'null' +value has its own enum value. + +**Prototype** + +```c +jerry_type_t +jerry_value_get_type (const jerry_value_t value); +``` + +**Example** + +```c +{ + jerry_value_t number = jerry_create_number (3.3); + + jerry_type_t type_info = jerry_value_get_type (number); + + if (type_info == JERRY_TYPE_NUMBER) + { + ... + } + + jerry_value_release (number); +} +``` + +**See also** +- [jerry_type_t](#jerry_type_t) + ## jerry_is_feature_enabled **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 0d2e29937..f26676067 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -766,6 +766,60 @@ jerry_value_is_undefined (const jerry_value_t value) /**< api value */ return ecma_is_value_undefined (jerry_get_arg_value (value)); } /* jerry_value_is_undefined */ +/** + * Perform the base type of the JavaScript value. + * + * @return jerry_type_t value + */ +jerry_type_t +jerry_value_get_type (const jerry_value_t value) /**< input value to check */ +{ + jerry_assert_api_available (); + + jerry_value_t argument = jerry_get_arg_value (value); + lit_magic_string_id_t lit_id = ecma_get_typeof_lit_id (argument); + + JERRY_ASSERT (lit_id != LIT_MAGIC_STRING__EMPTY); + + switch (lit_id) + { + case LIT_MAGIC_STRING_UNDEFINED: + { + return JERRY_TYPE_UNDEFINED; + } + case LIT_MAGIC_STRING_BOOLEAN: + { + return JERRY_TYPE_BOOLEAN; + } + case LIT_MAGIC_STRING_NUMBER: + { + return JERRY_TYPE_NUMBER; + } + case LIT_MAGIC_STRING_STRING: + { + return JERRY_TYPE_STRING; + } + case LIT_MAGIC_STRING_FUNCTION: + { + return JERRY_TYPE_FUNCTION; + } + case LIT_MAGIC_STRING_OBJECT: + { + /* Based on the ECMA 262 5.1 standard the 'null' value is an object. + * Thus we'll do an extra check for 'null' here. + */ + return ecma_is_value_null (argument) ? JERRY_TYPE_NULL : JERRY_TYPE_OBJECT; + } + default: + { + JERRY_UNREACHABLE (); + break; + } + } + + return JERRY_TYPE_NONE; +} /* jerry_value_get_type */ + /** * Check if the specified feature is enabled. * diff --git a/jerry-core/ecma/base/ecma-helpers-value.c b/jerry-core/ecma/base/ecma-helpers-value.c index 1452a2553..5318333ec 100644 --- a/jerry-core/ecma/base/ecma-helpers-value.c +++ b/jerry-core/ecma/base/ecma-helpers-value.c @@ -21,6 +21,8 @@ #include "jrt-bit-fields.h" #include "vm-defines.h" +#include "ecma-function-object.h" + /** \addtogroup ecma ECMA * @{ * @@ -917,6 +919,55 @@ ecma_free_value_if_not_object (ecma_value_t value) /**< value description */ } } /* ecma_free_value_if_not_object */ +/** + * Get the literal id associated with the given ecma_value type. + * This operation is equivalent to the JavaScript 'typeof' operator. + * + * @returns one of the following value: + * - LIT_MAGIC_STRING_UNDEFINED + * - LIT_MAGIC_STRING_OBJECT + * - LIT_MAGIC_STRING_BOOLEAN + * - LIT_MAGIC_STRING_NUMBER + * - LIT_MAGIC_STRING_STRING + * - LIT_MAGIC_STRING_FUNCTION + */ +lit_magic_string_id_t +ecma_get_typeof_lit_id (ecma_value_t value) /**< input ecma value */ +{ + lit_magic_string_id_t ret_value = LIT_MAGIC_STRING__EMPTY; + + if (ecma_is_value_undefined (value)) + { + ret_value = LIT_MAGIC_STRING_UNDEFINED; + } + else if (ecma_is_value_null (value)) + { + ret_value = LIT_MAGIC_STRING_OBJECT; + } + else if (ecma_is_value_boolean (value)) + { + ret_value = LIT_MAGIC_STRING_BOOLEAN; + } + else if (ecma_is_value_number (value)) + { + ret_value = LIT_MAGIC_STRING_NUMBER; + } + else if (ecma_is_value_string (value)) + { + ret_value = LIT_MAGIC_STRING_STRING; + } + else + { + JERRY_ASSERT (ecma_is_value_object (value)); + + ret_value = ecma_op_is_callable (value) ? LIT_MAGIC_STRING_FUNCTION : LIT_MAGIC_STRING_OBJECT; + } + + JERRY_ASSERT (ret_value != LIT_MAGIC_STRING__EMPTY); + + return ret_value; +} /* ecma_get_typeof_lit_id */ + /** * @} * @} diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 9fdf55070..77b0d0f8f 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -187,6 +187,7 @@ void ecma_value_assign_number (ecma_value_t *value_p, ecma_number_t ecma_number) void ecma_free_value (ecma_value_t value); void ecma_fast_free_value (ecma_value_t value); void ecma_free_value_if_not_object (ecma_value_t value); +lit_magic_string_id_t ecma_get_typeof_lit_id (ecma_value_t value); /* ecma-helpers-string.c */ ecma_string_t *ecma_new_ecma_string_from_utf8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index d4ef229c3..8144816c3 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -286,6 +286,23 @@ bool jerry_value_is_promise (const jerry_value_t value); bool jerry_value_is_string (const jerry_value_t value); bool jerry_value_is_undefined (const jerry_value_t value); +/** + * JerryScript API value type information. + */ +typedef enum +{ + JERRY_TYPE_NONE = 0u, /**< no type information */ + JERRY_TYPE_UNDEFINED, /**< undefined type */ + JERRY_TYPE_NULL, /**< null type */ + JERRY_TYPE_BOOLEAN, /**< boolean type */ + JERRY_TYPE_NUMBER, /**< number type */ + JERRY_TYPE_STRING, /**< string type */ + JERRY_TYPE_OBJECT, /**< object type */ + JERRY_TYPE_FUNCTION, /**< function type */ +} jerry_type_t; + +jerry_type_t jerry_value_get_type (const jerry_value_t value); + /** * Checker function of whether the specified compile feature is enabled. */ diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 97a6534f3..e0596fae7 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -92,47 +92,8 @@ opfunc_logical_not (ecma_value_t left_value) /**< left value */ ecma_value_t opfunc_typeof (ecma_value_t left_value) /**< left value */ { - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - ecma_string_t *type_str_p = NULL; - - if (ecma_is_value_undefined (left_value)) - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_UNDEFINED); - } - else if (ecma_is_value_null (left_value)) - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_OBJECT); - } - else if (ecma_is_value_boolean (left_value)) - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_BOOLEAN); - } - else if (ecma_is_value_number (left_value)) - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_NUMBER); - } - else if (ecma_is_value_string (left_value)) - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_STRING); - } - else - { - JERRY_ASSERT (ecma_is_value_object (left_value)); - - if (ecma_op_is_callable (left_value)) - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_FUNCTION); - } - else - { - type_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_OBJECT); - } - } - - ret_value = ecma_make_string_value (type_str_p); - - return ret_value; + ecma_string_t *type_str_p = ecma_get_magic_string (ecma_get_typeof_lit_id (left_value)); + return ecma_make_string_value (type_str_p); } /* opfunc_typeof */ /** diff --git a/tests/unit-core/test-api-value-type.c b/tests/unit-core/test-api-value-type.c new file mode 100644 index 000000000..01d5b93ef --- /dev/null +++ b/tests/unit-core/test-api-value-type.c @@ -0,0 +1,90 @@ +/* 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. + */ + +#include "jerryscript.h" +#include "jerryscript-port.h" +#include "jerryscript-port-default.h" +#include "test-common.h" + +typedef struct +{ + jerry_type_t type_info; + jerry_value_t value; +} test_entry_t; + +#define ENTRY(TYPE, VALUE) { TYPE, VALUE } + +static jerry_value_t +test_ext_function (const jerry_value_t function_obj, /**< function object */ + const jerry_value_t this_val, /**< function this value */ + const jerry_value_t args_p[], /**< array of arguments */ + const jerry_length_t args_cnt) /**< number of arguments */ +{ + (void) function_obj; + (void) this_val; + (void) args_p; + (void) args_cnt; + return jerry_create_boolean (true); +} /* test_ext_function */ + +int +main (void) +{ + TEST_INIT (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_char_t test_eval_function[] = "function demo(a) { return a + 1; }; demo"; + + test_entry_t entries[] = + { + ENTRY (JERRY_TYPE_NUMBER, jerry_create_number (-33.0)), + ENTRY (JERRY_TYPE_NUMBER, jerry_create_number (3)), + ENTRY (JERRY_TYPE_NUMBER, jerry_create_number_nan ()), + ENTRY (JERRY_TYPE_NUMBER, jerry_create_number_infinity (false)), + ENTRY (JERRY_TYPE_NUMBER, jerry_create_number_infinity (true)), + + ENTRY (JERRY_TYPE_BOOLEAN, jerry_create_boolean (true)), + ENTRY (JERRY_TYPE_BOOLEAN, jerry_create_boolean (false)), + + ENTRY (JERRY_TYPE_UNDEFINED, jerry_create_undefined ()), + + ENTRY (JERRY_TYPE_OBJECT, jerry_create_object ()), + ENTRY (JERRY_TYPE_OBJECT, jerry_create_array (10)), + ENTRY (JERRY_TYPE_OBJECT, jerry_create_error (JERRY_ERROR_TYPE, (const jerry_char_t *) "error")), + + ENTRY (JERRY_TYPE_NULL, jerry_create_null ()), + + ENTRY (JERRY_TYPE_FUNCTION, jerry_eval (test_eval_function, strlen ((char *) test_eval_function), false)), + ENTRY (JERRY_TYPE_FUNCTION, jerry_create_external_function (test_ext_function)), + + ENTRY (JERRY_TYPE_STRING, jerry_create_string (test_eval_function)), + ENTRY (JERRY_TYPE_STRING, jerry_create_string ((jerry_char_t *) "")), + }; + + for (size_t idx = 0; idx < sizeof (entries) / sizeof (entries[0]); idx++) + { + jerry_type_t type_info = jerry_value_get_type (entries[idx].value); + + TEST_ASSERT (type_info != JERRY_TYPE_NONE); + TEST_ASSERT (type_info == entries[idx].type_info); + + jerry_release_value (entries[idx].value); + } + + jerry_cleanup (); + + return 0; +} /* main */