From 919db820ba9c2fa506c793db9b34967d725a040f Mon Sep 17 00:00:00 2001 From: Yanhui Shen Date: Thu, 19 Jan 2017 18:02:40 +0800 Subject: [PATCH] Improve error message JerryScript-DCO-1.0-Signed-off-by: Yanhui Shen shen.elf@gmail.com --- jerry-core/ecma/operations/ecma-exceptions.c | 91 +++++++++++++++++++ jerry-core/ecma/operations/ecma-exceptions.h | 3 + .../ecma/operations/ecma-get-put-value.c | 20 +++- jerry-core/ecma/operations/ecma-reference.c | 10 +- jerry-core/parser/js/js-parser.c | 68 ++++---------- jerry-core/vm/vm.c | 35 ++++--- 6 files changed, 162 insertions(+), 65 deletions(-) diff --git a/jerry-core/ecma/operations/ecma-exceptions.c b/jerry-core/ecma/operations/ecma-exceptions.c index de21d8bd48..303aeacffb 100644 --- a/jerry-core/ecma/operations/ecma-exceptions.c +++ b/jerry-core/ecma/operations/ecma-exceptions.c @@ -13,7 +13,9 @@ * limitations under the License. */ +#include #include "ecma-builtins.h" +#include "ecma-conversion.h" #include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" @@ -156,6 +158,95 @@ ecma_raise_standard_error (ecma_standard_error_t error_type, /**< error type */ return ecma_make_error_obj_value (error_obj_p); } /* ecma_raise_standard_error */ +#ifdef JERRY_ENABLE_ERROR_MESSAGES + +/** + * Raise a standard ecma-error with the given format string and arguments. + * + * @return ecma value + * Returned value must be freed with ecma_free_value + */ +ecma_value_t +ecma_raise_standard_error_with_format (ecma_standard_error_t error_type, /**< error type */ + const char *format, /**< format string */ + ...) /**< ecma-values */ +{ + JERRY_ASSERT (format != NULL); + + ecma_string_t *error_msg_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + ecma_string_t *string1_p; + ecma_string_t *string2_p; + + const char *start_p = format; + const char *end_p = format; + + va_list args; + + va_start (args, format); + + while (*end_p) + { + if (*end_p == '%') + { + /* Concat template string. */ + if (end_p > start_p) + { + string1_p = error_msg_p; + string2_p = ecma_new_ecma_string_from_utf8 ((const lit_utf8_byte_t *) start_p, + (lit_utf8_size_t) (end_p - start_p)); + error_msg_p = ecma_concat_ecma_strings (string1_p, string2_p); + ecma_deref_ecma_string (string1_p); + ecma_deref_ecma_string (string2_p); + } + + /* Convert an argument to string without side effects. */ + ecma_string_t *arg_string_p; + const ecma_value_t arg_val = va_arg (args, ecma_value_t); + if (unlikely (ecma_is_value_object (arg_val))) + { + ecma_object_t *arg_object_p = ecma_get_object_from_value (arg_val); + lit_magic_string_id_t class_name = ecma_object_get_class_name (arg_object_p); + arg_string_p = ecma_get_magic_string (class_name); + } + else + { + jerry_value_t str_val = ecma_op_to_string (arg_val); + arg_string_p = ecma_get_string_from_value (str_val); + } + + /* Concat argument. */ + string1_p = error_msg_p; + string2_p = arg_string_p; + error_msg_p = ecma_concat_ecma_strings (string1_p, string2_p); + ecma_deref_ecma_string (string1_p); + ecma_deref_ecma_string (string2_p); + + start_p = end_p + 1; + } + + end_p++; + } + + va_end (args); + + /* Concat reset of template string. */ + if (start_p < end_p) + { + string1_p = error_msg_p; + string2_p = ecma_new_ecma_string_from_utf8 ((const lit_utf8_byte_t *) start_p, + (lit_utf8_size_t) (end_p - start_p)); + error_msg_p = ecma_concat_ecma_strings (string1_p, string2_p); + ecma_deref_ecma_string (string1_p); + ecma_deref_ecma_string (string2_p); + } + + ecma_object_t *error_obj_p = ecma_new_standard_error_with_message (error_type, error_msg_p); + ecma_deref_ecma_string (error_msg_p); + return ecma_make_error_obj_value (error_obj_p); +} /* ecma_raise_standard_error_with_format */ + +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ + /** * Raise a common error with the given message. * diff --git a/jerry-core/ecma/operations/ecma-exceptions.h b/jerry-core/ecma/operations/ecma-exceptions.h index e57a437282..7decda2320 100644 --- a/jerry-core/ecma/operations/ecma-exceptions.h +++ b/jerry-core/ecma/operations/ecma-exceptions.h @@ -51,6 +51,9 @@ typedef enum ecma_object_t *ecma_new_standard_error (ecma_standard_error_t error_type); ecma_object_t *ecma_new_standard_error_with_message (ecma_standard_error_t error_type, ecma_string_t *message_string_p); ecma_value_t ecma_raise_standard_error (ecma_standard_error_t error_type, const lit_utf8_byte_t *msg_p); +#ifdef JERRY_ENABLE_ERROR_MESSAGES +ecma_value_t ecma_raise_standard_error_with_format (ecma_standard_error_t error_type, const char *msg_p, ...); +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ ecma_value_t ecma_raise_common_error (const char *msg_p); ecma_value_t ecma_raise_eval_error (const char *msg_p); ecma_value_t ecma_raise_range_error (const char *msg_p); diff --git a/jerry-core/ecma/operations/ecma-get-put-value.c b/jerry-core/ecma/operations/ecma-get-put-value.c index debb632342..fbd92af399 100644 --- a/jerry-core/ecma/operations/ecma-get-put-value.c +++ b/jerry-core/ecma/operations/ecma-get-put-value.c @@ -52,7 +52,15 @@ ecma_op_get_value_lex_env_base (ecma_object_t *ref_base_lex_env_p, /**< referenc /* 3. */ if (unlikely (is_unresolvable_reference)) { - return ecma_raise_reference_error (ECMA_ERR_MSG ("Cannot resolve reference.")); +#ifdef JERRY_ENABLE_ERROR_MESSAGES + ecma_value_t var_name_val = ecma_make_string_value (var_name_string_p); + ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_REFERENCE, + "% is not defined", + var_name_val); +#else /* !JERRY_ENABLE_ERROR_MESSAGES */ + ecma_value_t error_value = ecma_raise_reference_error (NULL); +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ + return error_value; } /* 5. */ @@ -149,7 +157,15 @@ ecma_op_put_value_lex_env_base (ecma_object_t *ref_base_lex_env_p, /**< referenc /* 3.a. */ if (is_strict) { - return ecma_raise_reference_error (ECMA_ERR_MSG ("Cannot resolve reference.")); +#ifdef JERRY_ENABLE_ERROR_MESSAGES + ecma_value_t var_name_val = ecma_make_string_value (var_name_string_p); + ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_REFERENCE, + "% is not defined", + var_name_val); +#else /* !JERRY_ENABLE_ERROR_MESSAGES */ + ecma_value_t error_value = ecma_raise_reference_error (NULL); +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ + return error_value; } else { diff --git a/jerry-core/ecma/operations/ecma-reference.c b/jerry-core/ecma/operations/ecma-reference.c index 5f15ea23e2..373f96819b 100644 --- a/jerry-core/ecma/operations/ecma-reference.c +++ b/jerry-core/ecma/operations/ecma-reference.c @@ -123,7 +123,15 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical lex_env_p = ecma_get_lex_env_outer_reference (lex_env_p); } - return ecma_raise_reference_error (ECMA_ERR_MSG ("Cannot resolve reference.")); +#ifdef JERRY_ENABLE_ERROR_MESSAGES + ecma_value_t name_val = ecma_make_string_value (name_p); + ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_REFERENCE, + "% is not defined", + name_val); +#else /* !JERRY_ENABLE_ERROR_MESSAGES */ + ecma_value_t error_value = ecma_raise_reference_error (NULL); +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ + return error_value; } /* ecma_op_resolve_reference_value */ /** diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index be057247fe..2441da0a1f 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2345,13 +2345,6 @@ parser_send_breakpoints (parser_context_t *context_p, /**< context */ #endif /* JERRY_DEBUGGER */ -#define PARSE_ERR_POS_START " [line: " -#define PARSE_ERR_POS_START_SIZE ((uint32_t) sizeof (PARSE_ERR_POS_START) - 1) -#define PARSE_ERR_POS_MIDDLE ", column: " -#define PARSE_ERR_POS_MIDDLE_SIZE ((uint32_t) sizeof (PARSE_ERR_POS_MIDDLE) - 1) -#define PARSE_ERR_POS_END "]" -#define PARSE_ERR_POS_END_SIZE ((uint32_t) sizeof (PARSE_ERR_POS_END)) - #endif /* JERRY_JS_PARSER */ /** @@ -2397,50 +2390,23 @@ parser_parse_script (const uint8_t *source_p, /**< source code */ return ecma_make_error_value (ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL)); } #ifdef JERRY_ENABLE_ERROR_MESSAGES - const char *err_str_p = parser_error_to_string (parser_error.error); - uint32_t err_str_size = lit_zt_utf8_string_size ((const lit_utf8_byte_t *) err_str_p); - - char line_str_p[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; - uint32_t line_len = ecma_uint32_to_utf8_string (parser_error.line, - (lit_utf8_byte_t *) line_str_p, - ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); - - char col_str_p[ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32]; - uint32_t col_len = ecma_uint32_to_utf8_string (parser_error.column, - (lit_utf8_byte_t *) col_str_p, - ECMA_MAX_CHARS_IN_STRINGIFIED_UINT32); - - uint32_t msg_size = (err_str_size - + line_len - + col_len - + PARSE_ERR_POS_START_SIZE - + PARSE_ERR_POS_MIDDLE_SIZE - + PARSE_ERR_POS_END_SIZE); - - ecma_value_t error_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); - - JMEM_DEFINE_LOCAL_ARRAY (error_msg_p, msg_size, char); - char *err_msg_pos_p = error_msg_p; - - strncpy (err_msg_pos_p, err_str_p, err_str_size); - err_msg_pos_p += err_str_size; - - strncpy (err_msg_pos_p, PARSE_ERR_POS_START, PARSE_ERR_POS_START_SIZE); - err_msg_pos_p += PARSE_ERR_POS_START_SIZE; - - strncpy (err_msg_pos_p, line_str_p, line_len); - err_msg_pos_p += line_len; - - strncpy (err_msg_pos_p, PARSE_ERR_POS_MIDDLE, PARSE_ERR_POS_MIDDLE_SIZE); - err_msg_pos_p += PARSE_ERR_POS_MIDDLE_SIZE; - - strncpy (err_msg_pos_p, col_str_p, col_len); - err_msg_pos_p += col_len; - - strncpy (err_msg_pos_p, PARSE_ERR_POS_END, PARSE_ERR_POS_END_SIZE); - - error_value = ecma_raise_syntax_error (error_msg_p); - JMEM_FINALIZE_LOCAL_ARRAY (error_msg_p); + const lit_utf8_byte_t *err_bytes_p = (const lit_utf8_byte_t *) parser_error_to_string (parser_error.error); + lit_utf8_size_t err_bytes_size = lit_zt_utf8_string_size (err_bytes_p); + + ecma_string_t *err_str_p = ecma_new_ecma_string_from_utf8 (err_bytes_p, err_bytes_size); + ecma_value_t err_str_val = ecma_make_string_value (err_str_p); + ecma_value_t line_str_val = ecma_make_uint32_value (parser_error.line); + ecma_value_t col_str_val = ecma_make_uint32_value (parser_error.column); + + ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_SYNTAX, + "% [line: %, column: %]", + err_str_val, + line_str_val, + col_str_val); + + ecma_free_value (col_str_val); + ecma_free_value (line_str_val); + ecma_free_value (err_str_val); return error_value; #else /* !JERRY_ENABLE_ERROR_MESSAGES */ diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index d5a17bf0f8..5eb0e699dd 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -96,7 +96,15 @@ vm_op_get_value (ecma_value_t object, /**< base object */ if (unlikely (ecma_is_value_undefined (object) || ecma_is_value_null (object))) { - return ecma_raise_type_error (ECMA_ERR_MSG ("Base object cannot be null or undefined.")); +#ifdef JERRY_ENABLE_ERROR_MESSAGES + ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_TYPE, + "Cannot read property '%' of %", + property, + object); +#else /* !JERRY_ENABLE_ERROR_MESSAGES */ + ecma_value_t error_value = ecma_raise_type_error (NULL); +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ + return error_value; } ecma_value_t prop_to_string_result = ecma_op_to_string (property); @@ -136,8 +144,20 @@ vm_op_set_value (ecma_value_t object, /**< base object */ if (ECMA_IS_VALUE_ERROR (to_object)) { +#ifdef JERRY_ENABLE_ERROR_MESSAGES + ecma_free_value (to_object); + + ecma_value_t error_value = ecma_raise_standard_error_with_format (ECMA_ERROR_TYPE, + "Cannot set property '%' of %", + property, + object); + ecma_free_value (property); + + return error_value; +#else /* !JERRY_ENABLE_ERROR_MESSAGES */ ecma_free_value (property); return to_object; +#endif /* JERRY_ENABLE_ERROR_MESSAGES */ } object = to_object; @@ -1082,16 +1102,9 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ref_base_lex_env_p = ecma_op_resolve_reference_base (frame_ctx_p->lex_env_p, name_p); - if (ref_base_lex_env_p != NULL) - { - result = ecma_op_get_value_lex_env_base (ref_base_lex_env_p, - name_p, - is_strict); - } - else - { - result = ecma_raise_reference_error (ECMA_ERR_MSG ("Cannot resolve reference.")); - } + result = ecma_op_get_value_lex_env_base (ref_base_lex_env_p, + name_p, + is_strict); if (ECMA_IS_VALUE_ERROR (result)) {