From 8d916a44f1d0578c0ad3fee8eb5613c2f0ef8f70 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 21 Sep 2017 11:03:23 +0200 Subject: [PATCH] Parse functions directly (#2015) This patch adds direct function source code parsing, which is useful to avoid source code duplications. The patch also improves the Function constructor. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 59 +++- jerry-core/api/jerry-snapshot.c | 8 +- jerry-core/api/jerry.c | 94 ++++++- .../builtin-objects/ecma-builtin-function.c | 198 ++++++------- jerry-core/ecma/operations/ecma-eval.c | 4 +- jerry-core/include/jerryscript-core.h | 5 +- jerry-core/lit/lit-magic-strings.inc.h | 4 +- jerry-core/lit/lit-magic-strings.ini | 2 - jerry-core/parser/js/js-parser.c | 263 +++++++++++------- jerry-core/parser/js/js-parser.h | 5 +- tests/unit-core/test-api.c | 29 ++ 11 files changed, 427 insertions(+), 244 deletions(-) diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 1cfb909c0..9113a02a8 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -667,7 +667,7 @@ jerry_parse (const jerry_char_t *source_p, bool is_strict); ``` -- `source_p` - string, containing source code to parse. It must be a valid utf8 string. +- `source_p` - string, containing source code to parse (must be a valid UTF8 string). - `source_size` - size of the string, in bytes. - `is_strict` - defines strict mode. - return value @@ -706,8 +706,8 @@ main (void) **Summary** Parse script and construct an ECMAScript function. The lexical -environment is set to the global lexical environment. The name -(usually a file name) is also passed to this function which is +environment is set to the global lexical environment. The resource +name (usually a file name) is also passed to this function which is used by the debugger to find the source code. *Note*: The returned value must be freed with [jerry_release_value](#jerry_release_value) when it @@ -717,25 +717,62 @@ is no longer needed. ```c jerry_value_t -jerry_parse_named_resource (const jerry_char_t *name_p, /**< name (usually a file name) */ - size_t name_length, /**< length of name */ +jerry_parse_named_resource (const jerry_char_t *resource_name_p, /**< resource name (usually a file name) */ + size_t resource_name_length, /**< length of resource name */ const jerry_char_t *source_p, /**< script source */ size_t source_size, /**< script source size */ bool is_strict) /**< strict mode */ -{ ``` -- `name_p` - name, usually a file name -- `name_length` - size of the file name, in bytes -- `source_p` - string, containing source code to parse. It must be a valid UTF8 string -- `source_size` - size of the string, in bytes -- `is_strict` - defines strict mode +- `resource_name_p` - resource name, usually a file name (must be a valid UTF8 string). +- `resource_name_length` - size of the resource name, in bytes. +- `source_p` - string, containing source code to parse (must be a valid UTF8 string). +- `source_size` - size of the string, in bytes. +- `is_strict` - defines strict mode. - return value - function object value, if script was parsed successfully, - thrown error, otherwise This function is identical to [jerry_parse](#jerry_parse), except that an additional filename parameter has been added. +## jerry_parse_function + +**Summary** + +Parse function source code and construct an ECMAScript +function. The function arguments and function body are +passed as separated arguments. The lexical environment +is set to the global lexical environment. The resource +name (usually a file name) is also passed to this function +which is used by the debugger to find the source code. + +*Note*: The returned value must be freed with [jerry_release_value](#jerry_release_value) when it +is no longer needed. + +**Prototype** + +```c +jerry_value_t +jerry_parse_function (const jerry_char_t *resource_name_p, /**< resource name (usually a file name) */ + size_t resource_name_length, /**< length of resource name */ + const jerry_char_t *arg_list_p, /**< script source */ + size_t arg_list_size, /**< script source size */ + const jerry_char_t *source_p, /**< script source */ + size_t source_size, /**< script source size */ + bool is_strict) /**< strict mode */ +``` + +- `resource_name_p` - resource name, usually a file name (must be a valid UTF8 string). +- `resource_name_length` - size of the resource name, in bytes. +- `arg_list_p` - argument list of the function (must be a valid UTF8 string). +- `arg_list_size` - size of the argument list, in bytes. +- `source_p` - string, containing source code to parse (must be a valid UTF8 string). +- `source_size` - size of the string, in bytes. +- `is_strict` - defines strict mode. +- return value + - function object value, if script was parsed successfully, + - thrown error, otherwise + ## jerry_run **Summary** diff --git a/jerry-core/api/jerry-snapshot.c b/jerry-core/api/jerry-snapshot.c index fd1d3bdad..9d305b4d7 100644 --- a/jerry-core/api/jerry-snapshot.c +++ b/jerry-core/api/jerry-snapshot.c @@ -479,7 +479,9 @@ jerry_parse_and_save_snapshot (const jerry_char_t *source_p, /**< script source JMEM_ALIGNMENT); globals.snapshot_error_occured = false; - parse_status = parser_parse_script (source_p, + parse_status = parser_parse_script (NULL, + 0, + source_p, source_size, is_strict, &bytecode_data_p); @@ -900,7 +902,9 @@ jerry_parse_and_save_literals (const jerry_char_t *source_p, /**< script source #ifdef JERRY_ENABLE_SNAPSHOT_SAVE ecma_value_t parse_status; ecma_compiled_code_t *bytecode_data_p; - parse_status = parser_parse_script (source_p, + parse_status = parser_parse_script (NULL, + 0, + source_p, source_size, is_strict, &bytecode_data_p); diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 4499ae0a7..1032881ac 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -348,7 +348,9 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */ ecma_compiled_code_t *bytecode_data_p; ecma_value_t parse_status; - parse_status = parser_parse_script (source_p, + parse_status = parser_parse_script (NULL, + 0, + source_p, source_size, is_strict, &bytecode_data_p); @@ -385,28 +387,100 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */ * thrown error - otherwise */ jerry_value_t -jerry_parse_named_resource (const jerry_char_t *name_p, /**< name (usually a file name) */ - size_t name_length, /**< length of name */ +jerry_parse_named_resource (const jerry_char_t *resource_name_p, /**< resource name (usually a file name) */ + size_t resource_name_length, /**< length of resource name */ const jerry_char_t *source_p, /**< script source */ size_t source_size, /**< script source size */ bool is_strict) /**< strict mode */ { -#ifdef JERRY_DEBUGGER +#if defined JERRY_DEBUGGER && defined JERRY_JS_PARSER if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) { jerry_debugger_send_string (JERRY_DEBUGGER_SOURCE_CODE_NAME, JERRY_DEBUGGER_NO_SUBTYPE, - name_p, - name_length); + resource_name_p, + resource_name_length); } -#else /* JERRY_DEBUGGER */ - JERRY_UNUSED (name_p); - JERRY_UNUSED (name_length); -#endif /* JERRY_DEBUGGER */ +#else /* !(JERRY_DEBUGGER && JERRY_JS_PARSER) */ + JERRY_UNUSED (resource_name_p); + JERRY_UNUSED (resource_name_length); +#endif /* JERRY_DEBUGGER && JERRY_JS_PARSER */ return jerry_parse (source_p, source_size, is_strict); } /* jerry_parse_named_resource */ +/** + * Parse function and construct an EcmaScript function. The lexical + * environment is set to the global lexical environment. + * + * @return function object value - if script was parsed successfully, + * thrown error - otherwise + */ +jerry_value_t +jerry_parse_function (const jerry_char_t *resource_name_p, /**< resource name (usually a file name) */ + size_t resource_name_length, /**< length of resource name */ + const jerry_char_t *arg_list_p, /**< script source */ + size_t arg_list_size, /**< script source size */ + const jerry_char_t *source_p, /**< script source */ + size_t source_size, /**< script source size */ + bool is_strict) /**< strict mode */ +{ +#if defined JERRY_DEBUGGER && defined JERRY_JS_PARSER + if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) + { + jerry_debugger_send_string (JERRY_DEBUGGER_SOURCE_CODE_NAME, + JERRY_DEBUGGER_NO_SUBTYPE, + resource_name_p, + resource_name_length); + } +#else /* !(JERRY_DEBUGGER && JERRY_JS_PARSER) */ + JERRY_UNUSED (resource_name_p); + JERRY_UNUSED (resource_name_length); +#endif /* JERRY_DEBUGGER && JERRY_JS_PARSER */ + +#if JERRY_JS_PARSER + jerry_assert_api_available (); + + ecma_compiled_code_t *bytecode_data_p; + ecma_value_t parse_status; + + if (arg_list_p == NULL) + { + /* Must not be a NULL value. */ + arg_list_p = (const jerry_char_t *) ""; + } + + parse_status = parser_parse_script (arg_list_p, + arg_list_size, + source_p, + source_size, + is_strict, + &bytecode_data_p); + + if (ECMA_IS_VALUE_ERROR (parse_status)) + { + return parse_status; + } + + ecma_free_value (parse_status); + + ecma_object_t *lex_env_p = ecma_get_global_environment (); + ecma_object_t *func_obj_p = ecma_op_create_function_object (lex_env_p, + bytecode_data_p); + ecma_bytecode_deref (bytecode_data_p); + + return ecma_make_object_value (func_obj_p); +#else /* !JERRY_JS_PARSER */ + JERRY_UNUSED (arg_list_p); + JERRY_UNUSED (arg_list_size); + JERRY_UNUSED (source_p); + JERRY_UNUSED (source_size); + JERRY_UNUSED (is_strict); + + return ecma_raise_syntax_error (ECMA_ERR_MSG ("The parser has been disabled.")); +#endif /* JERRY_JS_PARSER */ +} /* jerry_parse_function */ + /** * Run an EcmaScript function created by jerry_parse. * diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function.c b/jerry-core/ecma/builtin-objects/ecma-builtin-function.c index e87a8a15f..919d747ef 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function.c @@ -65,116 +65,53 @@ ecma_builtin_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_function_helper_get_function_expression (const ecma_value_t *arguments_list_p, /**< arguments list */ - ecma_length_t arguments_list_len) /**< number of arguments */ +ecma_builtin_function_helper_get_function_arguments (const ecma_value_t *arguments_list_p, /**< arguments list */ + ecma_length_t arguments_list_len) /**< number of arguments */ { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); - ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); - - ecma_string_t *left_parenthesis_str_p, *right_parenthesis_str_p; - ecma_string_t *left_brace_str_p, *right_brace_str_p; - ecma_string_t *comma_str_p; - ecma_string_t *function_kw_str_p, *empty_str_p; - ecma_string_t *expr_str_p, *concated_str_p; - - left_parenthesis_str_p = ecma_new_ecma_string_from_magic_string_id (LIT_MAGIC_STRING_LEFT_PARENTHESIS_CHAR); - right_parenthesis_str_p = ecma_new_ecma_string_from_magic_string_id (LIT_MAGIC_STRING_RIGHT_PARENTHESIS_CHAR); - - left_brace_str_p = ecma_new_ecma_string_from_magic_string_id (LIT_MAGIC_STRING_LEFT_BRACE_CHAR); - right_brace_str_p = ecma_new_ecma_string_from_magic_string_id (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR); - - comma_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); - - function_kw_str_p = ecma_new_ecma_string_from_magic_string_id (LIT_MAGIC_STRING_FUNCTION); - empty_str_p = ecma_new_ecma_string_from_magic_string_id (LIT_MAGIC_STRING__EMPTY); - - /* First, we only process the function arguments skipping the function body */ - ecma_length_t number_of_function_args = (arguments_list_len == 0 ? 0 : arguments_list_len - 1); - - expr_str_p = ecma_concat_ecma_strings (left_parenthesis_str_p, function_kw_str_p); - - concated_str_p = ecma_concat_ecma_strings (expr_str_p, left_parenthesis_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - - for (ecma_length_t idx = 0; - idx < number_of_function_args && ecma_is_value_empty (ret_value); - idx++) + if (arguments_list_len <= 1) { - ECMA_TRY_CATCH (str_arg_value, - ecma_op_to_string (arguments_list_p[idx]), - ret_value); - - ecma_string_t *str_p = ecma_get_string_from_value (str_arg_value); - - concated_str_p = ecma_concat_ecma_strings (expr_str_p, str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - - if (idx < number_of_function_args - 1) - { - concated_str_p = ecma_concat_ecma_strings (expr_str_p, comma_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - } - - ECMA_FINALIZE (str_arg_value); + return ecma_make_string_value (ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY)); } - if (ecma_is_value_empty (ret_value)) + ecma_value_t final_str = ecma_op_to_string (arguments_list_p[0]); + + if (arguments_list_len == 2 || ECMA_IS_VALUE_ERROR (final_str)) { - concated_str_p = ecma_concat_ecma_strings (expr_str_p, right_parenthesis_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - - concated_str_p = ecma_concat_ecma_strings (expr_str_p, left_brace_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - - if (arguments_list_len != 0) - { - ECMA_TRY_CATCH (str_arg_value, - ecma_op_to_string (arguments_list_p[arguments_list_len - 1]), - ret_value); - - ecma_string_t *body_str_p = ecma_get_string_from_value (str_arg_value); - - concated_str_p = ecma_concat_ecma_strings (expr_str_p, body_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - - ECMA_FINALIZE (str_arg_value); - } - - concated_str_p = ecma_concat_ecma_strings (expr_str_p, right_brace_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; - - concated_str_p = ecma_concat_ecma_strings (expr_str_p, right_parenthesis_str_p); - ecma_deref_ecma_string (expr_str_p); - expr_str_p = concated_str_p; + return final_str; + } + + ecma_string_t *comma_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); + + for (ecma_length_t idx = 1; idx < arguments_list_len - 1; idx++) + { + ecma_value_t new_str = ecma_op_to_string (arguments_list_p[idx]); + + if (ECMA_IS_VALUE_ERROR (new_str)) + { + ecma_free_value (final_str); + + /* Return with the error. */ + final_str = new_str; + break; + } + + ecma_string_t *final_str_p = ecma_get_string_from_value (final_str); + ecma_string_t *fragment_str_p = ecma_concat_ecma_strings (final_str_p, comma_str_p); + ecma_deref_ecma_string (final_str_p); + + ecma_string_t *new_str_p = ecma_get_string_from_value (new_str); + final_str_p = ecma_concat_ecma_strings (fragment_str_p, new_str_p); + ecma_deref_ecma_string (new_str_p); + ecma_deref_ecma_string (fragment_str_p); + + final_str = ecma_make_string_value (final_str_p); } - ecma_deref_ecma_string (left_parenthesis_str_p); - ecma_deref_ecma_string (right_parenthesis_str_p); - ecma_deref_ecma_string (left_brace_str_p); - ecma_deref_ecma_string (right_brace_str_p); ecma_deref_ecma_string (comma_str_p); - ecma_deref_ecma_string (function_kw_str_p); - ecma_deref_ecma_string (empty_str_p); - - if (ecma_is_value_empty (ret_value)) - { - ret_value = ecma_make_string_value (expr_str_p); - } - else - { - ecma_deref_ecma_string (expr_str_p); - } - - return ret_value; -} /* ecma_builtin_function_helper_get_function_expression */ + return final_str; +} /* ecma_builtin_function_helper_get_function_arguments */ /** * Handle calling [[Construct]] of built-in Function object @@ -190,18 +127,63 @@ ecma_builtin_function_dispatch_construct (const ecma_value_t *arguments_list_p, { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); - ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); + ecma_value_t arguments_value = ecma_builtin_function_helper_get_function_arguments (arguments_list_p, + arguments_list_len); - ECMA_TRY_CATCH (arguments_value, - ecma_builtin_function_helper_get_function_expression (arguments_list_p, - arguments_list_len), - ret_value); + if (ECMA_IS_VALUE_ERROR (arguments_value)) + { + return arguments_value; + } - ecma_string_t *function_expression_p = ecma_get_string_from_value (arguments_value); + ecma_value_t function_body_value; - ret_value = ecma_op_eval (function_expression_p, false, false); + if (arguments_list_len > 0) + { + function_body_value = ecma_op_to_string (arguments_list_p[arguments_list_len - 1]); - ECMA_FINALIZE (arguments_value); + if (ECMA_IS_VALUE_ERROR (function_body_value)) + { + ecma_free_value (arguments_value); + return function_body_value; + } + } + else + { + /* Very unlikely code path, not optimized. */ + function_body_value = ecma_make_string_value (ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY)); + } + + ecma_string_t *arguments_str_p = ecma_get_string_from_value (arguments_value); + ecma_string_t *function_body_str_p = ecma_get_string_from_value (function_body_value); + + ECMA_STRING_TO_UTF8_STRING (arguments_str_p, arguments_buffer_p, arguments_buffer_size); + ECMA_STRING_TO_UTF8_STRING (function_body_str_p, function_body_buffer_p, function_body_buffer_size); + + ecma_compiled_code_t *bytecode_data_p; + + ecma_value_t ret_value = parser_parse_script (arguments_buffer_p, + arguments_buffer_size, + function_body_buffer_p, + function_body_buffer_size, + false, + &bytecode_data_p); + + if (!ECMA_IS_VALUE_ERROR (ret_value)) + { + JERRY_ASSERT (ecma_is_value_true (ret_value)); + + ecma_object_t *func_obj_p = ecma_op_create_function_object (ecma_get_global_environment (), + bytecode_data_p); + + ecma_bytecode_deref (bytecode_data_p); + ret_value = ecma_make_object_value (func_obj_p); + } + + ECMA_FINALIZE_UTF8_STRING (function_body_buffer_p, function_body_buffer_size); + ECMA_FINALIZE_UTF8_STRING (arguments_buffer_p, arguments_buffer_size); + + ecma_deref_ecma_string (arguments_str_p); + ecma_deref_ecma_string (function_body_str_p); return ret_value; } /* ecma_builtin_function_dispatch_construct */ diff --git a/jerry-core/ecma/operations/ecma-eval.c b/jerry-core/ecma/operations/ecma-eval.c index a742771a3..59e480f07 100644 --- a/jerry-core/ecma/operations/ecma-eval.c +++ b/jerry-core/ecma/operations/ecma-eval.c @@ -88,7 +88,9 @@ ecma_op_eval_chars_buffer (const lit_utf8_byte_t *code_p, /**< code characters b bool is_strict_call = (is_direct && is_called_from_strict_mode_code); - ecma_value_t parse_status = parser_parse_script (code_p, + ecma_value_t parse_status = parser_parse_script (NULL, + 0, + code_p, code_buffer_size, is_strict_call, &bytecode_data_p); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 9c680662c..2b0f6e4f4 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -255,8 +255,11 @@ bool jerry_get_memory_stats (jerry_heap_stats_t *out_stats_p); */ bool jerry_run_simple (const jerry_char_t *script_source_p, size_t script_source_size, jerry_init_flag_t flags); jerry_value_t jerry_parse (const jerry_char_t *source_p, size_t source_size, bool is_strict); -jerry_value_t jerry_parse_named_resource (const jerry_char_t *name_p, size_t name_length, +jerry_value_t jerry_parse_named_resource (const jerry_char_t *resource_name_p, size_t resource_name_length, const jerry_char_t *source_p, size_t source_size, bool is_strict); +jerry_value_t jerry_parse_function (const jerry_char_t *resource_name_p, size_t resource_name_length, + const jerry_char_t *arg_list_p, size_t arg_list_size, + const jerry_char_t *source_p, size_t source_size, bool is_strict); jerry_value_t jerry_run (const jerry_value_t func_val); jerry_value_t jerry_eval (const jerry_char_t *source_p, size_t source_size, bool is_strict); diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 9564e7813..ffaf10b9e 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -23,8 +23,6 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPACE_CHAR, " ") #if !defined (CONFIG_DISABLE_JSON_BUILTIN) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DOUBLE_QUOTE_CHAR, "\"") #endif -LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LEFT_PARENTHESIS_CHAR, "(") -LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RIGHT_PARENTHESIS_CHAR, ")") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMMA_CHAR, ",") #if !defined (CONFIG_DISABLE_NUMBER_BUILTIN) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MINUS_CHAR, "-") @@ -46,8 +44,10 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_G_CHAR, "g") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_I_CHAR, "i") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_M_CHAR, "m") #endif +#if !defined (CONFIG_DISABLE_JSON_BUILTIN) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LEFT_BRACE_CHAR, "{") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR, "}") +#endif #if !defined (CONFIG_DISABLE_MATH_BUILTIN) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PI_U, "PI") #endif diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index 003fe702c..effe26aeb 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -26,8 +26,6 @@ LIT_MAGIC_STRING__EMPTY = "" LIT_MAGIC_STRING_NEW_LINE_CHAR = "\n" LIT_MAGIC_STRING_SPACE_CHAR = " " LIT_MAGIC_STRING_DOUBLE_QUOTE_CHAR = "\"" -LIT_MAGIC_STRING_LEFT_PARENTHESIS_CHAR = "(" -LIT_MAGIC_STRING_RIGHT_PARENTHESIS_CHAR = ")" LIT_MAGIC_STRING_COMMA_CHAR = "," LIT_MAGIC_STRING_MINUS_CHAR = "-" LIT_MAGIC_STRING_SLASH_CHAR = "/" diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 3265ad170..87fd9d8d2 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -40,7 +40,7 @@ parser_copy_identifiers (parser_context_t *context_p) /**< context */ { parser_saved_context_t *parent_p = context_p->last_context_p; - if (parent_p == NULL || parent_p->prev_context_p == NULL) + if (parent_p == NULL || !(parent_p->status_flags & PARSER_IS_FUNCTION)) { /* Return if this function is not a nested function. */ return; @@ -1965,6 +1965,109 @@ parser_free_literals (parser_list_t *literal_pool_p) /**< literals */ parser_list_free (literal_pool_p); } /* parser_free_literals */ +/** + * Parse function arguments + */ +static void +parser_parse_function_arguments (parser_context_t *context_p, /**< context */ + lexer_token_type_t end_type) /**< expected end type */ +{ + if (context_p->token.type == end_type) + { + return; + } + + while (true) + { + uint16_t literal_count = context_p->literal_count; + + if (context_p->token.type != LEXER_LITERAL + || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) + { + parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); + } + + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + LEXER_IDENT_LITERAL); + + if (literal_count == context_p->literal_count + || context_p->token.literal_is_reserved + || context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) + { + context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; + } + + if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS) + { + uint8_t literal_status_flags = context_p->lit_object.literal_p->status_flags; + + literal_status_flags = (uint8_t) (literal_status_flags & ~LEXER_FLAG_NO_REG_STORE); + context_p->lit_object.literal_p->status_flags = literal_status_flags; + + context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; + context_p->status_flags &= ~(PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NEEDED); + } + + if (context_p->literal_count == literal_count) + { + lexer_literal_t *literal_p; + + if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) + { + parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); + } + + literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); + *literal_p = *context_p->lit_object.literal_p; + + literal_p->status_flags &= LEXER_FLAG_SOURCE_PTR; + literal_p->status_flags |= LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; + + context_p->literal_count++; + + /* There cannot be references from the byte code to these literals + * since no byte code has been emitted yet. Therefore there is no + * need to set the index field. */ + context_p->lit_object.literal_p->type = LEXER_UNUSED_LITERAL; + + /* Only the LEXER_FLAG_FUNCTION_ARGUMENT flag is kept. */ + context_p->lit_object.literal_p->status_flags &= LEXER_FLAG_FUNCTION_ARGUMENT; + context_p->lit_object.literal_p->u.char_p = NULL; + } + else + { + uint8_t lexer_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; + context_p->lit_object.literal_p->status_flags |= lexer_flags; + } + + context_p->argument_count++; + if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) + { + parser_raise_error (context_p, PARSER_ERR_REGISTER_LIMIT_REACHED); + } + + lexer_next_token (context_p); + + if (context_p->token.type != LEXER_COMMA) + { + break; + } + + lexer_next_token (context_p); + } + + if (context_p->token.type != end_type) + { + parser_error_t error = ((end_type == LEXER_RIGHT_PAREN) ? PARSER_ERR_RIGHT_PAREN_EXPECTED + : PARSER_ERR_IDENTIFIER_EXPECTED); + + parser_raise_error (context_p, error); + } + + context_p->register_count = context_p->argument_count; +} /* parser_parse_function_arguments */ + /** * Parse and compile EcmaScript source code * @@ -1973,8 +2076,10 @@ parser_free_literals (parser_list_t *literal_pool_p) /**< literals */ * @return compiled code */ static ecma_compiled_code_t * -parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ - size_t size, /**< size of the source code */ +parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ + size_t arg_list_size, /**< size of function argument list */ + const uint8_t *source_p, /**< valid UTF-8 source code */ + size_t source_size, /**< size of the source code */ int strict_mode, /**< strict mode */ parser_error_location_t *error_location_p) /**< error location */ { @@ -1989,7 +2094,19 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ error_location_p->error = PARSER_ERR_NO_ERROR; } - context.status_flags = PARSER_NO_REG_STORE | PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NOT_NEEDED; + if (arg_list_p == NULL) + { + context.status_flags = PARSER_NO_REG_STORE | PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NOT_NEEDED; + context.source_p = source_p; + context.source_end_p = source_p + source_size; + } + else + { + context.status_flags = PARSER_IS_FUNCTION; + context.source_p = arg_list_p; + context.source_end_p = arg_list_p + arg_list_size; + } + context.stack_depth = 0; context.stack_limit = 0; context.last_context_p = NULL; @@ -2000,8 +2117,6 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ context.status_flags |= PARSER_IS_STRICT; } - context.source_p = source_p; - context.source_end_p = source_p + size; context.line = 1; context.column = 1; @@ -2028,7 +2143,9 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ if (context.is_show_opcodes) { - JERRY_DEBUG_MSG ("\n--- Script parsing start ---\n\n"); + JERRY_DEBUG_MSG ("\n--- %s parsing start ---\n\n", + (arg_list_p == NULL) ? "Script" + : "Function"); } #endif /* PARSER_DUMP_BYTE_CODE */ @@ -2046,6 +2163,18 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ * lexer_next_token() must be immediately called. */ lexer_next_token (&context); + if (arg_list_p != NULL) + { + parser_parse_function_arguments (&context, LEXER_EOS); + + context.source_p = source_p; + context.source_end_p = source_p + source_size; + context.line = 1; + context.column = 1; + + lexer_next_token (&context); + } + parser_parse_statements (&context); /* When the parsing is successful, only the @@ -2066,7 +2195,9 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ #ifdef PARSER_DUMP_BYTE_CODE if (context.is_show_opcodes) { - JERRY_DEBUG_MSG ("\nScript parsing successfully completed. Total byte code size: %d bytes\n", + JERRY_DEBUG_MSG ("\n%s parsing successfully completed. Total byte code size: %d bytes\n", + (arg_list_p == NULL) ? "Script" + : "Function", (int) context.total_byte_code_size); } #endif /* PARSER_DUMP_BYTE_CODE */ @@ -2100,7 +2231,9 @@ parser_parse_source (const uint8_t *source_p, /**< valid UTF-8 source code */ #ifdef PARSER_DUMP_BYTE_CODE if (context.is_show_opcodes) { - JERRY_DEBUG_MSG ("\n--- Script parsing end ---\n\n"); + JERRY_DEBUG_MSG ("\n--- %s parsing end ---\n\n", + (arg_list_p == NULL) ? "Script" + : "Function"); } #endif /* PARSER_DUMP_BYTE_CODE */ @@ -2241,99 +2374,9 @@ parser_parse_function (parser_context_t *context_p, /**< context */ lexer_next_token (context_p); - /* Argument parsing. */ - if (context_p->token.type != LEXER_RIGHT_PAREN) - { - while (true) - { - uint16_t literal_count = context_p->literal_count; - - if (context_p->token.type != LEXER_LITERAL - || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) - { - parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); - } - - lexer_construct_literal_object (context_p, - &context_p->token.lit_location, - LEXER_IDENT_LITERAL); - - if (literal_count == context_p->literal_count - || context_p->token.literal_is_reserved - || context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) - { - context_p->status_flags |= PARSER_HAS_NON_STRICT_ARG; - } - - if (context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS) - { - uint8_t literal_status_flags = context_p->lit_object.literal_p->status_flags; - - literal_status_flags = (uint8_t) (literal_status_flags & ~LEXER_FLAG_NO_REG_STORE); - context_p->lit_object.literal_p->status_flags = literal_status_flags; - - context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; - context_p->status_flags &= ~(PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NEEDED); - } - - if (context_p->literal_count == literal_count) - { - lexer_literal_t *literal_p; - - if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS) - { - parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); - } - - literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); - *literal_p = *context_p->lit_object.literal_p; - - literal_p->status_flags &= LEXER_FLAG_SOURCE_PTR; - literal_p->status_flags |= LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; - - context_p->literal_count++; - - /* There cannot be references from the byte code to these literals - * since no byte code has been emitted yet. Therefore there is no - * need to set the index field. */ - context_p->lit_object.literal_p->type = LEXER_UNUSED_LITERAL; - - /* Only the LEXER_FLAG_FUNCTION_ARGUMENT flag is kept. */ - context_p->lit_object.literal_p->status_flags &= LEXER_FLAG_FUNCTION_ARGUMENT; - context_p->lit_object.literal_p->u.char_p = NULL; - } - else - { - uint8_t lexer_flags = LEXER_FLAG_VAR | LEXER_FLAG_INITIALIZED | LEXER_FLAG_FUNCTION_ARGUMENT; - context_p->lit_object.literal_p->status_flags |= lexer_flags; - } - - context_p->argument_count++; - if (context_p->argument_count >= PARSER_MAXIMUM_NUMBER_OF_REGISTERS) - { - parser_raise_error (context_p, PARSER_ERR_REGISTER_LIMIT_REACHED); - } - - lexer_next_token (context_p); - - if (context_p->token.type != LEXER_COMMA) - { - break; - } - - lexer_next_token (context_p); - } - } - - if (context_p->token.type != LEXER_RIGHT_PAREN) - { - parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); - } - + parser_parse_function_arguments (context_p, LEXER_RIGHT_PAREN); lexer_next_token (context_p); - context_p->register_count = context_p->argument_count; - if ((context_p->status_flags & PARSER_IS_PROPERTY_GETTER) && context_p->argument_count != 0) { @@ -2480,14 +2523,17 @@ parser_send_breakpoints (parser_context_t *context_p, /**< context */ * Parse EcamScript source code * * Note: + * if arg_list_p is not NULL, a function body is parsed * returned value must be freed with ecma_free_value * * @return true - if success * syntax error - otherwise */ ecma_value_t -parser_parse_script (const uint8_t *source_p, /**< source code */ - size_t size, /**< size of the source code */ +parser_parse_script (const uint8_t *arg_list_p, /**< function argument list */ + size_t arg_list_size, /**< size of function argument list */ + const uint8_t *source_p, /**< source code */ + size_t source_size, /**< size of the source code */ bool is_strict, /**< strict mode */ ecma_compiled_code_t **bytecode_data_p) /**< [out] JS bytecode */ { @@ -2500,11 +2546,16 @@ parser_parse_script (const uint8_t *source_p, /**< source code */ jerry_debugger_send_string (JERRY_DEBUGGER_SOURCE_CODE, JERRY_DEBUGGER_NO_SUBTYPE, source_p, - size); + source_size); } #endif /* JERRY_DEBUGGER */ - *bytecode_data_p = parser_parse_source (source_p, size, is_strict, &parser_error); + *bytecode_data_p = parser_parse_source (arg_list_p, + arg_list_size, + source_p, + source_size, + is_strict, + &parser_error); if (!*bytecode_data_p) { @@ -2547,8 +2598,10 @@ parser_parse_script (const uint8_t *source_p, /**< source code */ } return ecma_make_simple_value (ECMA_SIMPLE_VALUE_TRUE); #else /* !JERRY_JS_PARSER */ + JERRY_UNUSED (arg_list_p); + JERRY_UNUSED (arg_list_size); JERRY_UNUSED (source_p); - JERRY_UNUSED (size); + JERRY_UNUSED (source_size); JERRY_UNUSED (is_strict); JERRY_UNUSED (bytecode_data_p); diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index a75fb8d2a..f8272cb40 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -128,8 +128,9 @@ typedef struct } parser_error_location_t; /* Note: source must be a valid UTF-8 string */ -ecma_value_t parser_parse_script (const uint8_t *source_p, size_t size, bool is_strict, - ecma_compiled_code_t **bytecode_data_p); +ecma_value_t parser_parse_script (const uint8_t *arg_list_p, size_t arg_list_size, + const uint8_t *source_p, size_t source_size, + bool is_strict, ecma_compiled_code_t **bytecode_data_p); const char *parser_error_to_string (parser_error_t); diff --git a/tests/unit-core/test-api.c b/tests/unit-core/test-api.c index e94a5571a..c7e714ba1 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -1032,6 +1032,35 @@ main (void) TEST_ASSERT (number_val != number_val); jerry_release_value (val_t); + /* Test: create function */ + const char *func_resource = "unknown"; + const char *func_arg_list = "a , b,c"; + const char *func_src = " return 5 + a+\nb+c"; + + jerry_value_t func_val = jerry_parse_function ((const jerry_char_t *) func_resource, + strlen (func_resource), + (const jerry_char_t *) func_arg_list, + strlen (func_arg_list), + (const jerry_char_t *) func_src, + strlen (func_src), + false); + + TEST_ASSERT (!jerry_value_has_error_flag (func_val)); + + jerry_value_t func_args[3] = + { + jerry_create_number (4), + jerry_create_number (6), + jerry_create_number (-2) + }; + + val_t = jerry_call_function (func_val, func_args[0], func_args, 3); + number_val = jerry_get_number_value (val_t); + TEST_ASSERT (number_val == 13.0); + + jerry_release_value (val_t); + jerry_release_value (func_val); + jerry_cleanup (); TEST_ASSERT (test_api_is_free_callback_was_called);