From 87f60da14fae12708677098590cfb8cfedb82f71 Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Mon, 28 Oct 2019 15:46:35 +0100 Subject: [PATCH] Add arithmetic operations support for the API (#3249) This patch extends the jerry_binary_operation_t list with arithmetic operations. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- docs/02.API-REFERENCE.md | 5 + jerry-core/api/jerry.c | 22 ++ jerry-core/include/jerryscript-core.h | 5 + jerry-core/vm/opcodes-ecma-arithmetics.c | 2 +- jerry-core/vm/opcodes.h | 2 +- jerry-core/vm/vm.c | 2 +- .../test-api-binary-operations-arithmetics.c | 292 ++++++++++++++++++ 7 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 tests/unit-core/test-api-binary-operations-arithmetics.c diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 01f04aafb..7f9329926 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -316,6 +316,11 @@ Enum that contains the supported binary operation types - JERRY_BIN_OP_GREATER - greater relation (>) - JERRY_BIN_OP_GREATER_EQUAL - greater or equal relation (>=) - JERRY_BIN_OP_INSTANCEOF - instanceof operation + - JERRY_BIN_OP_ADD - addition operator (+) + - JERRY_BIN_OP_SUB - subtraction operator (-) + - JERRY_BIN_OP_MUL - multiplication operator (*) + - JERRY_BIN_OP_DIV - division operator (/) + - JERRY_BIN_OP_REM - remainder operator (%) *New in version 2.0*. diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index c7e28e39c..8304177e3 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -79,6 +79,17 @@ JERRY_STATIC_ASSERT (((ECMA_PROMISE_STATE_PENDING + 1) == JERRY_PROMISE_STATE_PE promise_internal_state_matches_external); #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */ +/** + * Offset between internal and external arithmetic operator types + */ +#define ECMA_NUMBER_ARITHMETIC_OP_API_OFFSET (JERRY_BIN_OP_SUB - NUMBER_ARITHMETIC_SUBTRACTION) + +JERRY_STATIC_ASSERT (((NUMBER_ARITHMETIC_SUBTRACTION + ECMA_NUMBER_ARITHMETIC_OP_API_OFFSET) == JERRY_BIN_OP_SUB) + && ((NUMBER_ARITHMETIC_MULTIPLICATION + ECMA_NUMBER_ARITHMETIC_OP_API_OFFSET) == JERRY_BIN_OP_MUL) + && ((NUMBER_ARITHMETIC_DIVISION + ECMA_NUMBER_ARITHMETIC_OP_API_OFFSET) == JERRY_BIN_OP_DIV) + && ((NUMBER_ARITHMETIC_REMAINDER + ECMA_NUMBER_ARITHMETIC_OP_API_OFFSET) == JERRY_BIN_OP_REM), + number_arithmetics_operation_type_matches_external); + #if !ENABLED (JERRY_PARSER) && !ENABLED (JERRY_SNAPSHOT_EXEC) #error "JERRY_SNAPSHOT_EXEC must be enabled if JERRY_PARSER is disabled!" #endif /* !ENABLED (JERRY_PARSER) && !ENABLED (JERRY_SNAPSHOT_EXEC) */ @@ -1027,6 +1038,17 @@ jerry_binary_operation (jerry_binary_operation_t op, /**< operation */ ecma_object_t *proto_obj_p = ecma_get_object_from_value (rhs); return jerry_return (ecma_op_object_has_instance (proto_obj_p, lhs)); } + case JERRY_BIN_OP_ADD: + { + return jerry_return (opfunc_addition (lhs, rhs)); + } + case JERRY_BIN_OP_SUB: + case JERRY_BIN_OP_MUL: + case JERRY_BIN_OP_DIV: + case JERRY_BIN_OP_REM: + { + return jerry_return (do_number_arithmetic (op - ECMA_NUMBER_ARITHMETIC_OP_API_OFFSET, lhs, rhs)); + } default: { return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Unsupported binary operation"))); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 218baf284..dcc817d76 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -327,6 +327,11 @@ typedef enum JERRY_BIN_OP_GREATER, /**< greater relation (>) */ JERRY_BIN_OP_GREATER_EQUAL, /**< greater or equal relation (>=)*/ JERRY_BIN_OP_INSTANCEOF, /**< instanceof operation */ + JERRY_BIN_OP_ADD, /**< addition operator (+) */ + JERRY_BIN_OP_SUB, /**< subtraction operator (-) */ + JERRY_BIN_OP_MUL, /**< multiplication operator (*) */ + JERRY_BIN_OP_DIV, /**< division operator (/) */ + JERRY_BIN_OP_REM, /**< remainder operator (%) */ } jerry_binary_operation_t; /** diff --git a/jerry-core/vm/opcodes-ecma-arithmetics.c b/jerry-core/vm/opcodes-ecma-arithmetics.c index 42ee5d779..162bae418 100644 --- a/jerry-core/vm/opcodes-ecma-arithmetics.c +++ b/jerry-core/vm/opcodes-ecma-arithmetics.c @@ -54,7 +54,7 @@ do_number_arithmetic (number_arithmetic_op op, /**< number arithmetic operation switch (op) { - case NUMBER_ARITHMETIC_SUBSTRACTION: + case NUMBER_ARITHMETIC_SUBTRACTION: { result = num_left - num_right; break; diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index faafba7c7..48af585fe 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -31,7 +31,7 @@ */ typedef enum { - NUMBER_ARITHMETIC_SUBSTRACTION, /**< substraction */ + NUMBER_ARITHMETIC_SUBTRACTION, /**< subtraction */ NUMBER_ARITHMETIC_MULTIPLICATION, /**< multiplication */ NUMBER_ARITHMETIC_DIVISION, /**< division */ NUMBER_ARITHMETIC_REMAINDER, /**< remainder calculation */ diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 78595bf2e..355359fea 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -2393,7 +2393,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ continue; } - result = do_number_arithmetic (NUMBER_ARITHMETIC_SUBSTRACTION, + result = do_number_arithmetic (NUMBER_ARITHMETIC_SUBTRACTION, left_value, right_value); diff --git a/tests/unit-core/test-api-binary-operations-arithmetics.c b/tests/unit-core/test-api-binary-operations-arithmetics.c new file mode 100644 index 000000000..a4cde15ef --- /dev/null +++ b/tests/unit-core/test-api-binary-operations-arithmetics.c @@ -0,0 +1,292 @@ +/* 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 "test-common.h" + +#define T(op, lhs, rhs, res) \ + { op, lhs, rhs, res } + +#define T_NAN(op, lhs, rhs) \ + { op, lhs, rhs } + +#define T_ERR(op, lsh, rhs) \ + T_NAN (op, lsh, rhs) + +#define T_ARI(lhs, rhs) \ + T_NAN (JERRY_BIN_OP_SUB, lhs, rhs), \ + T_NAN (JERRY_BIN_OP_MUL, lhs, rhs), \ + T_NAN (JERRY_BIN_OP_DIV, lhs, rhs), \ + T_NAN (JERRY_BIN_OP_REM, lhs, rhs) + +typedef struct +{ + jerry_binary_operation_t op; + jerry_value_t lhs; + jerry_value_t rhs; + jerry_value_t expected; +} test_entry_t; + + +typedef struct +{ + jerry_binary_operation_t op; + jerry_value_t lhs; + jerry_value_t rhs; +} test_nan_entry_t; + +typedef test_nan_entry_t test_error_entry_t; + +int +main (void) +{ + TEST_INIT (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t obj1 = jerry_eval ((const jerry_char_t *) "o={x:1};o", 9, JERRY_PARSE_NO_OPTS); + jerry_value_t obj2 = jerry_eval ((const jerry_char_t *) "o={x:1};o", 9, JERRY_PARSE_NO_OPTS); + jerry_value_t err1 = jerry_create_error (JERRY_ERROR_SYNTAX, (const jerry_char_t *) "error"); + + test_nan_entry_t test_nans[] = + { + /* Testing addition (+) */ + T_NAN (JERRY_BIN_OP_ADD, jerry_create_number (3.1), jerry_create_undefined ()), + T_NAN (JERRY_BIN_OP_ADD, jerry_create_undefined (), jerry_create_undefined ()), + T_NAN (JERRY_BIN_OP_ADD, jerry_create_undefined (), jerry_create_null ()), + + /* Testing subtraction (-), multiplication (*), division (/), remainder (%) */ + T_ARI (jerry_create_number (3.1), jerry_create_undefined ()), + T_ARI (jerry_create_string ((const jerry_char_t *) "foo"), jerry_create_string ((const jerry_char_t *) "bar")), + T_ARI (jerry_create_string ((const jerry_char_t *) "foo"), jerry_create_undefined ()), + T_ARI (jerry_create_string ((const jerry_char_t *) "foo"), jerry_create_null ()), + T_ARI (jerry_create_string ((const jerry_char_t *) "foo"), jerry_create_number (5.0)), + T_ARI (jerry_create_undefined (), jerry_create_string ((const jerry_char_t *) "foo")), + T_ARI (jerry_create_null (), jerry_create_string ((const jerry_char_t *) "foo")), + T_ARI (jerry_create_number (5.0), jerry_create_string ((const jerry_char_t *) "foo")), + T_ARI (jerry_create_undefined (), jerry_create_undefined ()), + T_ARI (jerry_create_undefined (), jerry_create_null ()), + T_ARI (jerry_create_null (), jerry_create_undefined ()), + T_ARI (jerry_acquire_value (obj1), jerry_acquire_value (obj1)), + T_ARI (jerry_acquire_value (obj1), jerry_acquire_value (obj2)), + T_ARI (jerry_acquire_value (obj2), jerry_acquire_value (obj1)), + T_ARI (jerry_acquire_value (obj2), jerry_create_undefined ()), + T_ARI (jerry_acquire_value (obj1), jerry_create_string ((const jerry_char_t *) "foo")), + T_ARI (jerry_acquire_value (obj1), jerry_create_null ()), + T_ARI (jerry_acquire_value (obj1), jerry_create_boolean (true)), + T_ARI (jerry_acquire_value (obj1), jerry_create_boolean (false)), + T_ARI (jerry_acquire_value (obj1), jerry_create_number (5.0)), + + /* Testing division (/) */ + T_NAN (JERRY_BIN_OP_DIV, jerry_create_boolean (false), jerry_create_boolean (false)), + T_NAN (JERRY_BIN_OP_DIV, jerry_create_number (0.0), jerry_create_number (0.0)), + T_NAN (JERRY_BIN_OP_DIV, jerry_create_null (), jerry_create_null ()), + + /* Testing remainder (%) */ + T_NAN (JERRY_BIN_OP_REM, jerry_create_boolean (true), jerry_create_boolean (false)), + T_NAN (JERRY_BIN_OP_REM, jerry_create_boolean (false), jerry_create_boolean (false)), + T_NAN (JERRY_BIN_OP_REM, jerry_create_number (0.0), jerry_create_number (0.0)), + T_NAN (JERRY_BIN_OP_REM, jerry_create_null (), jerry_create_null ()), + }; + + for (uint32_t idx = 0; idx < sizeof (test_nans) / sizeof (test_nan_entry_t); idx++) + { + jerry_value_t result = jerry_binary_operation (test_nans[idx].op, test_nans[idx].lhs, test_nans[idx].rhs); + TEST_ASSERT (jerry_value_is_number (result)); + + double num = jerry_get_number_value (result); + + TEST_ASSERT (num != num); + + jerry_release_value (test_nans[idx].lhs); + jerry_release_value (test_nans[idx].rhs); + jerry_release_value (result); + } + + test_entry_t tests[] = + { + /* Testing addition (+) */ + T (JERRY_BIN_OP_ADD, jerry_create_number (5.0), jerry_create_number (5.0), jerry_create_number (10.0)), + T (JERRY_BIN_OP_ADD, jerry_create_number (3.1), jerry_create_number (10), jerry_create_number (13.1)), + T (JERRY_BIN_OP_ADD, jerry_create_number (3.1), jerry_create_boolean (true), jerry_create_number (4.1)), + T (JERRY_BIN_OP_ADD, + jerry_create_string ((const jerry_char_t *) "foo"), + jerry_create_string ((const jerry_char_t *) "bar"), + jerry_create_string ((const jerry_char_t *) "foobar")), + T (JERRY_BIN_OP_ADD, + jerry_create_string ((const jerry_char_t *) "foo"), + jerry_create_undefined (), + jerry_create_string ((const jerry_char_t *) "fooundefined")), + T (JERRY_BIN_OP_ADD, + jerry_create_string ((const jerry_char_t *) "foo"), + jerry_create_null (), + jerry_create_string ((const jerry_char_t *) "foonull")), + T (JERRY_BIN_OP_ADD, + jerry_create_string ((const jerry_char_t *) "foo"), + jerry_create_number (5.0), + jerry_create_string ((const jerry_char_t *) "foo5")), + + T (JERRY_BIN_OP_ADD, jerry_create_null (), jerry_create_null (), jerry_create_number (0.0)), + T (JERRY_BIN_OP_ADD, jerry_create_boolean (true), jerry_create_boolean (true), jerry_create_number (2.0)), + T (JERRY_BIN_OP_ADD, jerry_create_boolean (true), jerry_create_boolean (false), jerry_create_number (1.0)), + T (JERRY_BIN_OP_ADD, jerry_create_boolean (false), jerry_create_boolean (true), jerry_create_number (1.0)), + T (JERRY_BIN_OP_ADD, jerry_create_boolean (false), jerry_create_boolean (false), jerry_create_number (0.0)), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_acquire_value (obj1), + jerry_create_string ((const jerry_char_t *) "[object Object][object Object]")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_acquire_value (obj2), + jerry_create_string ((const jerry_char_t *) "[object Object][object Object]")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj2), + jerry_acquire_value (obj1), + jerry_create_string ((const jerry_char_t *) "[object Object][object Object]")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_create_null (), + jerry_create_string ((const jerry_char_t *) "[object Object]null")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_create_undefined (), + jerry_create_string ((const jerry_char_t *) "[object Object]undefined")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_create_boolean (true), + jerry_create_string ((const jerry_char_t *) "[object Object]true")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_create_boolean (false), + jerry_create_string ((const jerry_char_t *) "[object Object]false")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_create_number (5.0), + jerry_create_string ((const jerry_char_t *) "[object Object]5")), + T (JERRY_BIN_OP_ADD, + jerry_acquire_value (obj1), + jerry_create_string ((const jerry_char_t *) "foo"), + jerry_create_string ((const jerry_char_t *) "[object Object]foo")), + + /* Testing subtraction (-) */ + T (JERRY_BIN_OP_SUB, jerry_create_number (5.0), jerry_create_number (5.0), jerry_create_number (0.0)), + T (JERRY_BIN_OP_SUB, jerry_create_number (3.1), jerry_create_number (10), jerry_create_number (-6.9)), + T (JERRY_BIN_OP_SUB, jerry_create_number (3.1), jerry_create_boolean (true), jerry_create_number (2.1)), + T (JERRY_BIN_OP_SUB, jerry_create_boolean (true), jerry_create_boolean (true), jerry_create_number (0.0)), + T (JERRY_BIN_OP_SUB, jerry_create_boolean (true), jerry_create_boolean (false), jerry_create_number (1.0)), + T (JERRY_BIN_OP_SUB, jerry_create_boolean (false), jerry_create_boolean (true), jerry_create_number (-1.0)), + T (JERRY_BIN_OP_SUB, jerry_create_boolean (false), jerry_create_boolean (false), jerry_create_number (0.0)), + T (JERRY_BIN_OP_SUB, jerry_create_null (), jerry_create_null (), jerry_create_number (-0.0)), + + + /* Testing multiplication (*) */ + T (JERRY_BIN_OP_MUL, jerry_create_number (5.0), jerry_create_number (5.0), jerry_create_number (25.0)), + T (JERRY_BIN_OP_MUL, jerry_create_number (3.1), jerry_create_number (10), jerry_create_number (31)), + T (JERRY_BIN_OP_MUL, jerry_create_number (3.1), jerry_create_boolean (true), jerry_create_number (3.1)), + T (JERRY_BIN_OP_MUL, jerry_create_boolean (true), jerry_create_boolean (true), jerry_create_number (1.0)), + T (JERRY_BIN_OP_MUL, jerry_create_boolean (true), jerry_create_boolean (false), jerry_create_number (0.0)), + T (JERRY_BIN_OP_MUL, jerry_create_boolean (false), jerry_create_boolean (true), jerry_create_number (0.0)), + T (JERRY_BIN_OP_MUL, jerry_create_boolean (false), jerry_create_boolean (false), jerry_create_number (0.0)), + T (JERRY_BIN_OP_MUL, jerry_create_null (), jerry_create_null (), jerry_create_number (0.0)), + + /* Testing division (/) */ + T (JERRY_BIN_OP_DIV, jerry_create_number (5.0), jerry_create_number (5.0), jerry_create_number (1.0)), + T (JERRY_BIN_OP_DIV, jerry_create_number (3.1), jerry_create_number (10), jerry_create_number (0.31)), + T (JERRY_BIN_OP_DIV, jerry_create_number (3.1), jerry_create_boolean (true), jerry_create_number (3.1)), + T (JERRY_BIN_OP_DIV, jerry_create_boolean (true), jerry_create_boolean (true), jerry_create_number (1.0)), + T (JERRY_BIN_OP_DIV, + jerry_create_boolean (true), + jerry_create_boolean (false), + jerry_create_number_infinity (false)), + T (JERRY_BIN_OP_DIV, jerry_create_boolean (false), jerry_create_boolean (true), jerry_create_number (0.0)), + + /* Testing remainder (%) */ + T (JERRY_BIN_OP_REM, jerry_create_number (5.0), jerry_create_number (5.0), jerry_create_number (0.0)), + T (JERRY_BIN_OP_REM, jerry_create_number (5.0), jerry_create_number (2.0), jerry_create_number (1.0)), + T (JERRY_BIN_OP_REM, jerry_create_number (3.1), jerry_create_number (10), jerry_create_number (3.1)), + T (JERRY_BIN_OP_REM, + jerry_create_number (3.1), + jerry_create_boolean (true), + jerry_create_number (0.10000000000000009)), + T (JERRY_BIN_OP_REM, jerry_create_boolean (true), jerry_create_boolean (true), jerry_create_number (0.0)), + T (JERRY_BIN_OP_REM, jerry_create_boolean (false), jerry_create_boolean (true), jerry_create_number (0.0)), + + }; + + for (uint32_t idx = 0; idx < sizeof (tests) / sizeof (test_entry_t); idx++) + { + jerry_value_t result = jerry_binary_operation (tests[idx].op, tests[idx].lhs, tests[idx].rhs); + TEST_ASSERT (!jerry_value_is_error (result)); + + jerry_value_t equals = jerry_binary_operation (JERRY_BIN_OP_STRICT_EQUAL, result, tests[idx].expected); + TEST_ASSERT (jerry_value_is_boolean (equals) && jerry_get_boolean_value (equals)); + jerry_release_value (equals); + + jerry_release_value (tests[idx].lhs); + jerry_release_value (tests[idx].rhs); + jerry_release_value (tests[idx].expected); + jerry_release_value (result); + } + + jerry_value_t obj3 = jerry_eval ((const jerry_char_t *) "o={valueOf:function(){throw 5}};o", 33, JERRY_PARSE_NO_OPTS); + + test_error_entry_t error_tests[] = + { + /* Testing addition (+) */ + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (err1), jerry_acquire_value (err1)), + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (err1), jerry_create_undefined ()), + T_ERR (JERRY_BIN_OP_ADD, jerry_create_undefined (), jerry_acquire_value (err1)), + + /* Testing subtraction (-), multiplication (*), division (/), remainder (%) */ + T_ARI (jerry_acquire_value (err1), jerry_acquire_value (err1)), + T_ARI (jerry_acquire_value (err1), jerry_create_undefined ()), + T_ARI (jerry_create_undefined (), jerry_acquire_value (err1)), + + /* Testing addition (+) */ + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (obj3), jerry_create_undefined ()), + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (obj3), jerry_create_null ()), + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (obj3), jerry_create_boolean (true)), + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (obj3), jerry_create_boolean (false)), + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (obj3), jerry_acquire_value (obj2)), + T_ERR (JERRY_BIN_OP_ADD, jerry_acquire_value (obj3), jerry_create_string ((const jerry_char_t *) "foo")), + + /* Testing subtraction (-), multiplication (*), division (/), remainder (%) */ + T_ARI (jerry_acquire_value (obj3), jerry_create_undefined ()), + T_ARI (jerry_acquire_value (obj3), jerry_create_null ()), + T_ARI (jerry_acquire_value (obj3), jerry_create_boolean (true)), + T_ARI (jerry_acquire_value (obj3), jerry_create_boolean (false)), + T_ARI (jerry_acquire_value (obj3), jerry_acquire_value (obj2)), + T_ARI (jerry_acquire_value (obj3), jerry_create_string ((const jerry_char_t *) "foo")), + }; + + for (uint32_t idx = 0; idx < sizeof (error_tests) / sizeof (test_error_entry_t); idx++) + { + jerry_value_t result = jerry_binary_operation (tests[idx].op, error_tests[idx].lhs, error_tests[idx].rhs); + TEST_ASSERT (jerry_value_is_error (result)); + jerry_release_value (error_tests[idx].lhs); + jerry_release_value (error_tests[idx].rhs); + jerry_release_value (result); + } + + jerry_release_value (obj1); + jerry_release_value (obj2); + jerry_release_value (obj3); + jerry_release_value (err1); + + jerry_cleanup (); + + return 0; +} /* main */