From 8bccbbf08f04efa9c373cf1e4ead243eb3c96106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20B=C3=A1tyai?= Date: Thu, 5 Sep 2019 01:25:19 +0200 Subject: [PATCH] Refactor JSON builtin methods (#3031) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu --- jerry-core/ecma/base/ecma-helpers-string.c | 65 +- jerry-core/ecma/base/ecma-helpers.h | 4 + .../ecma-builtin-helpers-json.c | 144 +- .../builtin-objects/ecma-builtin-helpers.h | 7 +- .../ecma/builtin-objects/ecma-builtin-json.c | 2017 +++++++---------- jerry-core/jmem/jmem-heap.c | 5 +- 6 files changed, 958 insertions(+), 1284 deletions(-) diff --git a/jerry-core/ecma/base/ecma-helpers-string.c b/jerry-core/ecma/base/ecma-helpers-string.c index 4236e3770..1fe9abefb 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.c +++ b/jerry-core/ecma/base/ecma-helpers-string.c @@ -2568,6 +2568,56 @@ ecma_stringbuilder_grow (ecma_stringbuilder_t *builder_p, /**< string builder */ return ((lit_utf8_byte_t *) header_p) + header_p->current_size - required_size; } /* ecma_stringbuilder_grow */ +/** + * Get the current size of the string in a string builder + * + * @return the size of the string data + */ +lit_utf8_size_t +ecma_stringbuilder_get_size (ecma_stringbuilder_t *builder_p) /**< string builder */ +{ + ecma_stringbuilder_header_t *header_p = builder_p->header_p; + JERRY_ASSERT (header_p != NULL); + + return ECMA_STRINGBUILDER_STRING_SIZE (header_p); +} /* ecma_stringbuilder_get_size */ + +/** + * Get pointer to the raw string data in a string builder + * + * @return pointer to the string data + */ +lit_utf8_byte_t * +ecma_stringbuilder_get_data (ecma_stringbuilder_t *builder_p) /**< string builder */ +{ + ecma_stringbuilder_header_t *header_p = builder_p->header_p; + JERRY_ASSERT (header_p != NULL); + + return ECMA_STRINGBUILDER_STRING_PTR (header_p); +} /* ecma_stringbuilder_get_data */ + +/** + * Revert the string builder to a smaller size + */ +void +ecma_stringbuilder_revert (ecma_stringbuilder_t *builder_p, /**< string builder */ + const lit_utf8_size_t size) /**< new size */ +{ + ecma_stringbuilder_header_t *header_p = builder_p->header_p; + JERRY_ASSERT (header_p != NULL); + + const lit_utf8_size_t new_size = size + (lit_utf8_size_t) (sizeof (ecma_ascii_string_t)); + JERRY_ASSERT (new_size <= header_p->current_size); + +#if ENABLED (JERRY_MEM_STATS) + jmem_stats_free_string_bytes (header_p->current_size - new_size); +#endif /* ENABLED (JERRY_MEM_STATS) */ + + header_p = jmem_heap_realloc_block (header_p, header_p->current_size, new_size); + header_p->current_size = new_size; + builder_p->header_p = header_p; +} /* ecma_stringbuilder_revert */ + /** * Append an ecma_string_t to a string builder */ @@ -2623,6 +2673,17 @@ ecma_stringbuilder_append_char (ecma_stringbuilder_t *builder_p, /**< string bui lit_char_to_utf8_bytes (dest_p, c); } /* ecma_stringbuilder_append_char */ +/** + * Append a single byte to a string builder + */ +void +ecma_stringbuilder_append_byte (ecma_stringbuilder_t *builder_p, /**< string builder */ + const lit_utf8_byte_t byte) /**< byte */ +{ + lit_utf8_byte_t *dest_p = ecma_stringbuilder_grow (builder_p, 1); + *dest_p = byte; +} /* ecma_stringbuilder_append_byte */ + /** * Finalize a string builder, returning the created string, and releasing the underlying buffer. * @@ -2675,7 +2736,9 @@ ecma_stringbuilder_finalize (ecma_stringbuilder_t *builder_p) /**< string builde const size_t utf8_string_size = string_size + container_size; header_p = jmem_heap_realloc_block (header_p, header_p->current_size, utf8_string_size); - memmove (((lit_utf8_byte_t *) header_p + container_size), string_begin_p, string_size); + memmove (((lit_utf8_byte_t *) header_p + container_size), + ECMA_STRINGBUILDER_STRING_PTR (header_p), + string_size); #if ENABLED (JERRY_MEM_STATS) jmem_stats_allocate_string_bytes (container_size - sizeof (ecma_ascii_string_t)); diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index e9bd614c4..86b21ef5f 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -319,12 +319,16 @@ ecma_string_t *ecma_string_trim (const ecma_string_t *string_p); ecma_stringbuilder_t ecma_stringbuilder_create (void); ecma_stringbuilder_t ecma_stringbuilder_create_from (ecma_string_t *string_p); +lit_utf8_size_t ecma_stringbuilder_get_size (ecma_stringbuilder_t *builder_p); +lit_utf8_byte_t *ecma_stringbuilder_get_data (ecma_stringbuilder_t *builder_p); +void ecma_stringbuilder_revert (ecma_stringbuilder_t *builder_p, const lit_utf8_size_t size); void ecma_stringbuilder_append (ecma_stringbuilder_t *builder_p, const ecma_string_t *string_p); void ecma_stringbuilder_append_magic (ecma_stringbuilder_t *builder_p, const lit_magic_string_id_t id); void ecma_stringbuilder_append_raw (ecma_stringbuilder_t *builder_p, const lit_utf8_byte_t *data_p, const lit_utf8_size_t data_size); void ecma_stringbuilder_append_char (ecma_stringbuilder_t *builder_p, const ecma_char_t c); +void ecma_stringbuilder_append_byte (ecma_stringbuilder_t *builder_p, const lit_utf8_byte_t); ecma_string_t *ecma_stringbuilder_finalize (ecma_stringbuilder_t *builder_p); void ecma_stringbuilder_destroy (ecma_stringbuilder_t *builder_p); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c index 0c75af768..d3cdcb127 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.c @@ -64,12 +64,8 @@ ecma_json_has_object_in_stack (ecma_json_occurence_stack_item_t *stack_p, /**< s */ bool ecma_has_string_value_in_collection (ecma_collection_header_t *collection_p, /**< collection */ - ecma_value_t string_value) /**< string value */ + ecma_string_t *string_p) /**< string */ { - JERRY_ASSERT (ecma_is_value_string (string_value)); - - ecma_string_t *string_p = ecma_get_string_from_value (string_value); - ecma_value_t *ecma_value_p = ecma_collection_iterator_init (collection_p); while (ecma_value_p != NULL) @@ -85,143 +81,7 @@ ecma_has_string_value_in_collection (ecma_collection_header_t *collection_p, /** } return false; -} /* ecma_has_string_value_in_collection*/ - -/** - * Common function to concatenate key-value pairs into an ecma-string. - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * Used by: - * - ecma_builtin_helper_json_create_formatted_json step 10.b.ii - * - ecma_builtin_helper_json_create_non_formatted_json step 10.a.i - * - * @return pointer to ecma-string - * Returned value must be freed with ecma_deref_ecma_string. - */ -static ecma_string_t * -ecma_builtin_helper_json_create_separated_properties (ecma_collection_header_t *partial_p, /**< key-value pairs*/ - ecma_string_t *separator_p) /**< separator*/ -{ - ecma_string_t *properties_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); - - ecma_value_t *ecma_value_p = ecma_collection_iterator_init (partial_p); - - bool first = true; - - while (ecma_value_p != NULL) - { - ecma_string_t *current_p = ecma_get_string_from_value (*ecma_value_p); - ecma_value_p = ecma_collection_iterator_next (ecma_value_p); - - if (JERRY_LIKELY (!first)) - { - properties_str_p = ecma_concat_ecma_strings (properties_str_p, separator_p); - } - - properties_str_p = ecma_concat_ecma_strings (properties_str_p, current_p); - first = false; - } - - return properties_str_p; -} /* ecma_builtin_helper_json_create_separated_properties */ - -/** - * Common function to create a formatted JSON string. - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * Used by: - * - ecma_builtin_json_object step 10.b - * - ecma_builtin_json_array step 10.b - * - * @return ecma value - * Returned value must be freed with ecma_free_value. - */ -ecma_value_t -ecma_builtin_helper_json_create_formatted_json (lit_utf8_byte_t left_bracket, /**< left bracket character */ - lit_utf8_byte_t right_bracket, /**< right bracket character */ - ecma_string_t *stepback_p, /**< stepback*/ - ecma_collection_header_t *partial_p, /**< key-value pairs*/ - ecma_json_stringify_context_t *context_p) /**< context*/ -{ - JERRY_ASSERT (left_bracket < LIT_UTF8_1_BYTE_CODE_POINT_MAX - && right_bracket < LIT_UTF8_1_BYTE_CODE_POINT_MAX); - - /* 10.b.i */ - lit_utf8_byte_t chars[2] = { LIT_CHAR_COMMA, LIT_CHAR_LF }; - - ecma_string_t *separator_p = ecma_new_ecma_string_from_utf8 (chars, 2); - - separator_p = ecma_concat_ecma_strings (separator_p, context_p->indent_str_p); - - /* 10.b.ii */ - ecma_string_t *properties_str_p = ecma_builtin_helper_json_create_separated_properties (partial_p, separator_p); - ecma_deref_ecma_string (separator_p); - - /* 10.b.iii */ - chars[0] = left_bracket; - - ecma_string_t *final_str_p = ecma_new_ecma_string_from_utf8 (chars, 2); - - final_str_p = ecma_concat_ecma_strings (final_str_p, context_p->indent_str_p); - - final_str_p = ecma_concat_ecma_strings (final_str_p, properties_str_p); - ecma_deref_ecma_string (properties_str_p); - - chars[0] = LIT_CHAR_LF; - final_str_p = ecma_append_chars_to_string (final_str_p, chars, 1, 1); - - final_str_p = ecma_concat_ecma_strings (final_str_p, stepback_p); - - chars[0] = right_bracket; - final_str_p = ecma_append_chars_to_string (final_str_p, chars, 1, 1); - - return ecma_make_string_value (final_str_p); -} /* ecma_builtin_helper_json_create_formatted_json */ - -/** - * Common function to create a non-formatted JSON string. - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * Used by: - * - ecma_builtin_json_object step 10.a - * - ecma_builtin_json_array step 10.a - * - * @return ecma value - * Returned value must be freed with ecma_free_value. - */ -ecma_value_t -ecma_builtin_helper_json_create_non_formatted_json (lit_utf8_byte_t left_bracket, /**< left bracket character */ - lit_utf8_byte_t right_bracket, /**< right bracket character */ - ecma_collection_header_t *partial_p) /**< key-value pairs */ -{ - JERRY_ASSERT (left_bracket < LIT_UTF8_1_BYTE_CODE_POINT_MAX - && right_bracket < LIT_UTF8_1_BYTE_CODE_POINT_MAX); - - /* 10.a */ - ecma_string_t *comma_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); - ecma_string_t *properties_str_p; - - /* 10.a.i */ - properties_str_p = ecma_builtin_helper_json_create_separated_properties (partial_p, comma_str_p); - - /* 10.a.ii */ - ecma_string_t *result_str_p = ecma_new_ecma_string_from_code_unit (left_bracket); - - result_str_p = ecma_concat_ecma_strings (result_str_p, properties_str_p); - ecma_deref_ecma_string (properties_str_p); - - lit_utf8_byte_t chars[1] = { right_bracket }; - - result_str_p = ecma_append_chars_to_string (result_str_p, chars, 1, 1); - - return ecma_make_string_value (result_str_p); -} /* ecma_builtin_helper_json_create_non_formatted_json */ +} /* ecma_has_string_value_in_collection */ #endif /* ENABLED (JERRY_BUILTIN_JSON) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index 6f7ccb221..5d46171c2 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -159,20 +159,23 @@ typedef struct ecma_json_occurence_stack_item_t *occurence_stack_last_p; /** The actual indentation text. */ - ecma_string_t *indent_str_p; + ecma_stringbuilder_t indent_builder; /** The indentation text. */ ecma_string_t *gap_str_p; /** The replacer function. */ ecma_object_t *replacer_function_p; + + /** Result string builder. */ + ecma_stringbuilder_t result_builder; } ecma_json_stringify_context_t; ecma_value_t ecma_builtin_json_parse_buffer (const lit_utf8_byte_t * str_start_p, lit_utf8_size_t string_size); ecma_value_t ecma_builtin_json_string_from_object (const ecma_value_t arg1); bool ecma_json_has_object_in_stack (ecma_json_occurence_stack_item_t *stack_p, ecma_object_t *object_p); -bool ecma_has_string_value_in_collection (ecma_collection_header_t *collection_p, ecma_value_t string_value); +bool ecma_has_string_value_in_collection (ecma_collection_header_t *collection_p, ecma_string_t *string_p); ecma_value_t ecma_builtin_helper_json_create_formatted_json (lit_utf8_byte_t left_bracket, lit_utf8_byte_t right_bracket, diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c index d3d9e719a..b5e47b3d9 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-json.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.c @@ -25,7 +25,6 @@ #include "ecma-builtin-helpers.h" #include "ecma-objects.h" #include "ecma-objects-general.h" -#include "ecma-try-catch-macro.h" #include "jrt.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" @@ -60,18 +59,19 @@ */ typedef enum { - invalid_token, /**< error token */ - end_token, /**< end of stream reached */ - number_token, /**< JSON number */ - string_token, /**< JSON string */ - null_token, /**< JSON null primitive value */ - true_token, /**< JSON true primitive value */ - false_token, /**< JSON false primitive value */ - left_brace_token, /**< JSON left brace */ - right_brace_token, /**< JSON right brace */ - left_square_token, /**< JSON left square bracket */ - comma_token, /**< JSON comma */ - colon_token /**< JSON colon */ + TOKEN_INVALID, /**< error token */ + TOKEN_END, /**< end of stream reached */ + TOKEN_NUMBER, /**< JSON number */ + TOKEN_STRING, /**< JSON string */ + TOKEN_NULL, /**< JSON null primitive value */ + TOKEN_TRUE, /**< JSON true primitive value */ + TOKEN_FALSE, /**< JSON false primitive value */ + TOKEN_LEFT_BRACE, /**< JSON left brace */ + TOKEN_RIGHT_BRACE, /**< JSON right brace */ + TOKEN_LEFT_SQUARE, /**< JSON left square bracket */ + TOKEN_RIGHT_SQUARE, /**< JSON right square bracket */ + TOKEN_COMMA, /**< JSON comma */ + TOKEN_COLON /**< JSON colon */ } ecma_json_token_type_t; /** @@ -93,50 +93,6 @@ typedef struct } u; } ecma_json_token_t; -/** - * Compare the string with an ID. - * - * @return true if the match is successful - */ -static bool -ecma_builtin_json_check_id (const lit_utf8_byte_t *string_p, /**< start position */ - const lit_utf8_byte_t *end_p, /**< input end */ - const char *string_id_p) /**< string identifier */ -{ - /* - * String comparison must not depend on lit_utf8_byte_t definition. - */ - JERRY_ASSERT (*string_p == *string_id_p); - - string_p++; - string_id_p++; - - while (string_p < end_p) - { - if (*string_id_p == LIT_CHAR_NULL) - { - /* JSON lexer accepts input strings such as falsenull and - * returns with multiple tokens (false and null in this case). - * This is different from normal lexers, whose return with a - * single identifier. The falsenull input will still cause an - * error eventually, since a separator must always be present - * between two JSON values regardless of the current expression - * type. */ - return true; - } - - if (*string_p != *string_id_p) - { - return false; - } - - string_p++; - string_id_p++; - } - - return (*string_id_p == LIT_CHAR_NULL); -} /* ecma_builtin_json_check_id */ - /** * Parse and extract string token. */ @@ -145,15 +101,15 @@ ecma_builtin_json_parse_string (ecma_json_token_t *token_p) /**< token argument { const lit_utf8_byte_t *current_p = token_p->current_p; const lit_utf8_byte_t *end_p = token_p->end_p; - bool has_escape_sequence = false; - lit_utf8_size_t buffer_size = 0; - /* First step: syntax checking. */ + ecma_stringbuilder_t result_builder = ecma_stringbuilder_create (); + const lit_utf8_byte_t *unappended_p = current_p; + while (true) { if (current_p >= end_p || *current_p <= 0x1f) { - return; + goto invalid_string; } if (*current_p == LIT_CHAR_DOUBLE_QUOTE) @@ -163,139 +119,99 @@ ecma_builtin_json_parse_string (ecma_json_token_t *token_p) /**< token argument if (*current_p == LIT_CHAR_BACKSLASH) { + ecma_stringbuilder_append_raw (&result_builder, + unappended_p, + (lit_utf8_size_t) (current_p - unappended_p)); + current_p++; - has_escape_sequence = true; /* If there is an escape sequence but there's no escapable character just return */ if (current_p >= end_p) { - return; + goto invalid_string; } - switch (*current_p) + const lit_utf8_byte_t c = *current_p; + switch (c) { case LIT_CHAR_DOUBLE_QUOTE: case LIT_CHAR_SLASH: case LIT_CHAR_BACKSLASH: + { + ecma_stringbuilder_append_byte (&result_builder, c); + current_p++; + break; + } case LIT_CHAR_LOWERCASE_B: + { + ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_BS); + current_p++; + break; + } case LIT_CHAR_LOWERCASE_F: + { + ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_FF); + current_p++; + break; + } case LIT_CHAR_LOWERCASE_N: + { + ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_LF); + current_p++; + break; + } case LIT_CHAR_LOWERCASE_R: + { + ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_CR); + current_p++; + break; + } case LIT_CHAR_LOWERCASE_T: { + ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_TAB); + current_p++; break; } case LIT_CHAR_LOWERCASE_U: { if ((end_p - current_p <= ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH)) { - return; + goto invalid_string; } ecma_char_t code_unit; if (!(lit_read_code_unit_from_hex (current_p + 1, ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH, &code_unit))) { - return; + goto invalid_string; } + ecma_stringbuilder_append_char (&result_builder, code_unit); current_p += ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH + 1; - - lit_utf8_byte_t char_buffer[LIT_UTF8_MAX_BYTES_IN_CODE_UNIT]; - buffer_size += lit_code_unit_to_utf8 (code_unit, char_buffer); - continue; + break; } default: { - return; - } - } - } - - buffer_size++; - current_p++; - } - - token_p->type = string_token; - - if (!has_escape_sequence) - { - token_p->u.string_p = ecma_new_ecma_string_from_utf8 (token_p->current_p, buffer_size); - token_p->current_p = current_p + 1; - return; - } - - JMEM_DEFINE_LOCAL_ARRAY (buffer_p, buffer_size, lit_utf8_byte_t); - - lit_utf8_byte_t *write_p = buffer_p; - current_p = token_p->current_p; - - while (*current_p != LIT_CHAR_DOUBLE_QUOTE) - { - if (*current_p == LIT_CHAR_BACKSLASH) - { - current_p++; - - lit_utf8_byte_t special_character; - - switch (*current_p) - { - case LIT_CHAR_LOWERCASE_B: - { - special_character = LIT_CHAR_BS; - break; - } - case LIT_CHAR_LOWERCASE_F: - { - special_character = LIT_CHAR_FF; - break; - } - case LIT_CHAR_LOWERCASE_N: - { - special_character = LIT_CHAR_LF; - break; - } - case LIT_CHAR_LOWERCASE_R: - { - special_character = LIT_CHAR_CR; - break; - } - case LIT_CHAR_LOWERCASE_T: - { - special_character = LIT_CHAR_TAB; - break; - } - case LIT_CHAR_LOWERCASE_U: - { - ecma_char_t code_unit; - - lit_read_code_unit_from_hex (current_p + 1, ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH, &code_unit); - - current_p += ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH + 1; - write_p += lit_code_unit_to_utf8 (code_unit, write_p); - continue; - } - default: - { - special_character = *current_p; - break; + goto invalid_string; } } - *write_p++ = special_character; - current_p++; + unappended_p = current_p; continue; } - *write_p++ = *current_p++; + current_p++; } - JERRY_ASSERT (write_p == buffer_p + buffer_size); - - token_p->u.string_p = ecma_new_ecma_string_from_utf8 (buffer_p, buffer_size); - - JMEM_FINALIZE_LOCAL_ARRAY (buffer_p); - + ecma_stringbuilder_append_raw (&result_builder, + unappended_p, + (lit_utf8_size_t)(current_p - unappended_p)); + token_p->u.string_p = ecma_stringbuilder_finalize (&result_builder); token_p->current_p = current_p + 1; + token_p->type = TOKEN_STRING; + return; + +invalid_string: + ecma_stringbuilder_destroy (&result_builder); } /* ecma_builtin_json_parse_string */ /** @@ -375,7 +291,7 @@ ecma_builtin_json_parse_number (ecma_json_token_t *token_p) /**< token argument while (current_p < end_p && lit_char_is_decimal_digit (*current_p)); } - token_p->type = number_token; + token_p->type = TOKEN_NUMBER; token_p->u.number = ecma_utf8_string_to_number (start_p, (lit_utf8_size_t) (current_p - start_p)); token_p->current_p = current_p; @@ -393,7 +309,7 @@ ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p, /**< token argum { const lit_utf8_byte_t *current_p = token_p->current_p; const lit_utf8_byte_t *end_p = token_p->end_p; - token_p->type = invalid_token; + token_p->type = TOKEN_INVALID; while (current_p < end_p && (*current_p == LIT_CHAR_SP @@ -406,7 +322,7 @@ ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p, /**< token argum if (current_p == end_p) { - token_p->type = end_token; + token_p->type = TOKEN_END; return; } @@ -414,31 +330,37 @@ ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p, /**< token argum { case LIT_CHAR_LEFT_BRACE: { - token_p->type = left_brace_token; + token_p->type = TOKEN_LEFT_BRACE; token_p->current_p = current_p + 1; return; } case LIT_CHAR_RIGHT_BRACE: { - token_p->type = right_brace_token; + token_p->type = TOKEN_RIGHT_BRACE; token_p->current_p = current_p + 1; return; } case LIT_CHAR_LEFT_SQUARE: { - token_p->type = left_square_token; + token_p->type = TOKEN_LEFT_SQUARE; + token_p->current_p = current_p + 1; + return; + } + case LIT_CHAR_RIGHT_SQUARE: + { + token_p->type = TOKEN_RIGHT_SQUARE; token_p->current_p = current_p + 1; return; } case LIT_CHAR_COMMA: { - token_p->type = comma_token; + token_p->type = TOKEN_COMMA; token_p->current_p = current_p + 1; return; } case LIT_CHAR_COLON: { - token_p->type = colon_token; + token_p->type = TOKEN_COLON; token_p->current_p = current_p + 1; return; } @@ -453,31 +375,49 @@ ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p, /**< token argum } case LIT_CHAR_LOWERCASE_N: { - if (ecma_builtin_json_check_id (current_p, token_p->end_p, "null")) + lit_utf8_size_t size = lit_get_magic_string_size (LIT_MAGIC_STRING_NULL); + if (current_p + size <= end_p) { - token_p->type = null_token; - token_p->current_p = current_p + 4; - return; + if (!memcmp (lit_get_magic_string_utf8 (LIT_MAGIC_STRING_NULL), + current_p, + size)) + { + token_p->type = TOKEN_NULL; + token_p->current_p = current_p + size; + return; + } } break; } case LIT_CHAR_LOWERCASE_T: { - if (ecma_builtin_json_check_id (current_p, token_p->end_p, "true")) + lit_utf8_size_t size = lit_get_magic_string_size (LIT_MAGIC_STRING_TRUE); + if (current_p + size <= end_p) { - token_p->type = true_token; - token_p->current_p = current_p + 4; - return; + if (!memcmp (lit_get_magic_string_utf8 (LIT_MAGIC_STRING_TRUE), + current_p, + size)) + { + token_p->type = TOKEN_TRUE; + token_p->current_p = current_p + size; + return; + } } break; } case LIT_CHAR_LOWERCASE_F: { - if (ecma_builtin_json_check_id (current_p, token_p->end_p, "false")) + lit_utf8_size_t size = lit_get_magic_string_size (LIT_MAGIC_STRING_FALSE); + if (current_p + size <= end_p) { - token_p->type = false_token; - token_p->current_p = current_p + 5; - return; + if (!memcmp (lit_get_magic_string_utf8 (LIT_MAGIC_STRING_FALSE), + current_p, + size)) + { + token_p->type = TOKEN_FALSE; + token_p->current_p = current_p + size; + return; + } } break; } @@ -494,42 +434,6 @@ ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p, /**< token argum } } /* ecma_builtin_json_parse_next_token */ -/** - * Checks whether the next token is right square token. - * - * @return true if it is. - * - * Always skips white spaces regardless the next token. - */ -static bool -ecma_builtin_json_check_right_square_token (ecma_json_token_t *token_p) /**< token argument */ -{ - const lit_utf8_byte_t *current_p = token_p->current_p; - const lit_utf8_byte_t *end_p = token_p->end_p; - - /* - * No need for end check since the string is zero terminated. - */ - while (current_p < end_p - && (*current_p == LIT_CHAR_SP - || *current_p == LIT_CHAR_CR - || *current_p == LIT_CHAR_LF - || *current_p == LIT_CHAR_TAB)) - { - current_p++; - } - - token_p->current_p = current_p; - - if (current_p < end_p && *current_p == LIT_CHAR_RIGHT_SQUARE) - { - token_p->current_p = current_p + 1; - return true; - } - - return false; -} /* ecma_builtin_json_check_right_square_token */ - /** * Utility for defining properties. * @@ -559,55 +463,42 @@ ecma_builtin_json_define_value_property (ecma_object_t *obj_p, /**< this object static ecma_value_t ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument */ { - ecma_builtin_json_parse_next_token (token_p, true); - switch (token_p->type) { - case number_token: + case TOKEN_NUMBER: { return ecma_make_number_value (token_p->u.number); } - case string_token: + case TOKEN_STRING: { return ecma_make_string_value (token_p->u.string_p); } - case null_token: + case TOKEN_NULL: { return ECMA_VALUE_NULL; } - case true_token: + case TOKEN_TRUE: { return ECMA_VALUE_TRUE; } - case false_token: + case TOKEN_FALSE: { return ECMA_VALUE_FALSE; } - case left_brace_token: + case TOKEN_LEFT_BRACE: { - bool parse_comma = false; ecma_object_t *object_p = ecma_op_create_object_object_noarg (); + ecma_builtin_json_parse_next_token (token_p, true); + + if (token_p->type == TOKEN_RIGHT_BRACE) + { + return ecma_make_object_value (object_p); + } + while (true) { - ecma_builtin_json_parse_next_token (token_p, !parse_comma); - - if (token_p->type == right_brace_token) - { - return ecma_make_object_value (object_p); - } - - if (parse_comma) - { - if (token_p->type != comma_token) - { - break; - } - - ecma_builtin_json_parse_next_token (token_p, true); - } - - if (token_p->type != string_token) + if (token_p->type != TOKEN_STRING) { break; } @@ -615,16 +506,16 @@ ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument * ecma_string_t *name_p = token_p->u.string_p; ecma_builtin_json_parse_next_token (token_p, false); - - if (token_p->type != colon_token) + if (token_p->type != TOKEN_COLON) { ecma_deref_ecma_string (name_p); break; } + ecma_builtin_json_parse_next_token (token_p, true); ecma_value_t value = ecma_builtin_json_parse_value (token_p); - if (ecma_is_value_undefined (value)) + if (ecma_is_value_empty (value)) { ecma_deref_ecma_string (name_p); break; @@ -634,18 +525,28 @@ ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument * ecma_deref_ecma_string (name_p); ecma_free_value (value); - parse_comma = true; + ecma_builtin_json_parse_next_token (token_p, false); + if (token_p->type == TOKEN_RIGHT_BRACE) + { + return ecma_make_object_value (object_p); + } + + if (token_p->type != TOKEN_COMMA) + { + break; + } + + ecma_builtin_json_parse_next_token (token_p, true); } /* * Parse error occured. */ ecma_deref_object (object_p); - return ECMA_VALUE_UNDEFINED; + return ECMA_VALUE_EMPTY; } - case left_square_token: + case TOKEN_LEFT_SQUARE: { - bool parse_comma = false; uint32_t length = 0; ecma_value_t array_construction = ecma_op_create_array_object (NULL, 0, false); @@ -653,151 +554,143 @@ ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument * ecma_object_t *array_p = ecma_get_object_from_value (array_construction); + ecma_builtin_json_parse_next_token (token_p, true); + + if (token_p->type == TOKEN_RIGHT_SQUARE) + { + return ecma_make_object_value (array_p); + } + while (true) { - if (ecma_builtin_json_check_right_square_token (token_p)) - { - return ecma_make_object_value (array_p); - } - - if (parse_comma) - { - ecma_builtin_json_parse_next_token (token_p, false); - - if (token_p->type != comma_token) - { - break; - } - } - ecma_value_t value = ecma_builtin_json_parse_value (token_p); - if (ecma_is_value_undefined (value)) + if (ecma_is_value_empty (value)) { + JERRY_ASSERT (token_p->type != TOKEN_STRING); break; } ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (length); - ecma_value_t completion = ecma_builtin_helper_def_prop (array_p, index_str_p, value, ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); - JERRY_ASSERT (ecma_is_value_true (completion)); - ecma_deref_ecma_string (index_str_p); - ecma_free_value (value); + ecma_builtin_json_parse_next_token (token_p, false); + + if (token_p->type == TOKEN_RIGHT_SQUARE) + { + return ecma_make_object_value (array_p); + } + + if (token_p->type != TOKEN_COMMA) + { + JERRY_ASSERT (token_p->type != TOKEN_STRING); + break; + } + + ecma_builtin_json_parse_next_token (token_p, true); length++; - parse_comma = true; } - /* - * Parse error occured. - */ ecma_deref_object (array_p); - return ECMA_VALUE_UNDEFINED; + return ECMA_VALUE_EMPTY; } default: { - return ECMA_VALUE_UNDEFINED; + return ECMA_VALUE_EMPTY; } } } /* ecma_builtin_json_parse_value */ /** - * Abstract operation Walk defined in 15.12.2 - * - * See also: - * ECMA-262 v5, 15.12.2 + * Abstract operation InternalizeJSONProperty defined in 24.3.1.1 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_json_walk (ecma_object_t *reviver_p, /**< reviver function */ - ecma_object_t *holder_p, /**< holder object */ - ecma_string_t *name_p) /**< property name */ +ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver function */ + ecma_object_t *holder_p, /**< holder object */ + ecma_string_t *name_p) /**< property name */ { JERRY_ASSERT (reviver_p); JERRY_ASSERT (holder_p); JERRY_ASSERT (name_p); - ecma_value_t ret_value = ECMA_VALUE_EMPTY; + /* 1. */ + ecma_value_t value = ecma_op_object_get (holder_p, name_p); - ECMA_TRY_CATCH (value_get, - ecma_op_object_get (holder_p, name_p), - ret_value); - - if (ecma_is_value_object (value_get)) + /* 2. */ + if (ECMA_IS_VALUE_ERROR (value)) { - ecma_object_t *object_p = ecma_get_object_from_value (value_get); + return value; + } + + /* 3. */ + if (ecma_is_value_object (value)) + { + ecma_object_t *object_p = ecma_get_object_from_value (value); ecma_collection_header_t *props_p = ecma_op_object_get_property_names (object_p, ECMA_LIST_ENUMERABLE); ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p); - while (ecma_value_p != NULL && ecma_is_value_empty (ret_value)) + /* 3.d.iii */ + while (ecma_value_p != NULL) { ecma_string_t *property_name_p = ecma_get_string_from_value (*ecma_value_p); ecma_value_p = ecma_collection_iterator_next (ecma_value_p); - ECMA_TRY_CATCH (value_walk, - ecma_builtin_json_walk (reviver_p, - object_p, - property_name_p), - ret_value); + /* 3.d.iii.1 */ + ecma_value_t result = ecma_builtin_json_internalize_property (reviver_p, object_p, property_name_p); - /* - * We cannot optimize this function since any members - * can be changed (deleted) by the reviver function. - */ - if (ecma_is_value_undefined (value_walk)) + /* 3.d.iii.2 */ + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_free_values_collection (props_p, 0); + ecma_deref_object (object_p); + + return result; + } + + /* 3.d.iii.3 */ + if (ecma_is_value_undefined (result)) { ecma_value_t delete_val = ecma_op_general_object_delete (object_p, property_name_p, false); JERRY_ASSERT (ecma_is_value_boolean (delete_val)); } + /* 3.d.iii.4 */ else { ecma_builtin_json_define_value_property (object_p, property_name_p, - value_walk); + result); + ecma_free_value (result); } - - ECMA_FINALIZE (value_walk); } ecma_free_values_collection (props_p, 0); } - if (ecma_is_value_empty (ret_value)) - { - ecma_value_t arguments_list[2]; - - arguments_list[0] = ecma_make_string_value (name_p); - arguments_list[1] = value_get; - - /* - * The completion value can be anything including exceptions. - */ - ret_value = ecma_op_function_call (reviver_p, - ecma_make_object_value (holder_p), - arguments_list, - 2); - } - else - { - JERRY_ASSERT (ECMA_IS_VALUE_ERROR (ret_value)); - } - - ECMA_FINALIZE (value_get); + ecma_value_t arguments_list[2]; + arguments_list[0] = ecma_make_string_value (name_p); + arguments_list[1] = value; + /* 4. */ + ecma_value_t ret_value = ecma_op_function_call (reviver_p, + ecma_make_object_value (holder_p), + arguments_list, + 2); + ecma_free_value (value); return ret_value; -} /* ecma_builtin_json_walk */ +} /* ecma_builtin_json_internalize_property */ /** * Function to set a string token from the given arguments, fills its fields and advances the string pointer. @@ -813,19 +706,21 @@ ecma_builtin_json_parse_buffer (const lit_utf8_byte_t * str_start_p, /**< String token.current_p = str_start_p; token.end_p = str_start_p + string_size; - ecma_value_t final_result = ecma_builtin_json_parse_value (&token); + ecma_builtin_json_parse_next_token (&token, true); + ecma_value_t result = ecma_builtin_json_parse_value (&token); - if (!ecma_is_value_undefined (final_result)) + if (!ecma_is_value_empty (result)) { ecma_builtin_json_parse_next_token (&token, false); - - if (token.type != end_token) + if (token.type == TOKEN_END) { - ecma_free_value (final_result); - final_result = ECMA_VALUE_UNDEFINED; + return result; } + + ecma_free_value (result); } - return final_result; + + return ecma_raise_syntax_error (ECMA_ERR_MSG ("Invalid JSON format.")); } /*ecma_builtin_json_parse_buffer*/ /** @@ -843,62 +738,547 @@ ecma_builtin_json_parse (ecma_value_t this_arg, /**< 'this' argument */ ecma_value_t arg2) /**< reviver argument */ { JERRY_UNUSED (this_arg); - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - ECMA_TRY_CATCH (string, - ecma_op_to_string (arg1), - ret_value); + ecma_value_t text_value = ecma_op_to_string (arg1); - const ecma_string_t *string_p = ecma_get_string_from_value (string); - - ECMA_STRING_TO_UTF8_STRING (string_p, str_start_p, string_size); - - ecma_value_t final_result = ecma_builtin_json_parse_buffer (str_start_p, string_size); - - if (ecma_is_value_undefined (final_result)) + if (ECMA_IS_VALUE_ERROR (text_value)) { - ret_value = ecma_raise_syntax_error (ECMA_ERR_MSG ("JSON string parse error.")); + return text_value; } + + ecma_string_t *text_string_p = ecma_get_string_from_value (text_value); + + ECMA_STRING_TO_UTF8_STRING (text_string_p, str_start_p, string_size); + ecma_value_t result = ecma_builtin_json_parse_buffer (str_start_p, string_size); + ECMA_FINALIZE_UTF8_STRING (str_start_p, string_size); + ecma_deref_ecma_string (text_string_p); + + if (!ECMA_IS_VALUE_ERROR (result) && ecma_op_is_callable (arg2)) + { + ecma_object_t *object_p = ecma_op_create_object_object_noarg (); + + ecma_property_value_t *prop_value_p; + prop_value_p = ecma_create_named_data_property (object_p, + ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY), + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, + NULL); + + ecma_named_data_property_assign_value (object_p, prop_value_p, result); + + ecma_free_value (result); + result = ecma_builtin_json_internalize_property (ecma_get_object_from_value (arg2), + object_p, + ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY)); + ecma_deref_object (object_p); + } + + return result; +} /* ecma_builtin_json_parse */ + +/** + * Abstract operation 'QuoteJSONString' defined in 24.3.2.2 + */ +static void +ecma_builtin_json_quote (ecma_stringbuilder_t *builder_p, /**< builder for the result */ + ecma_string_t *string_p) /**< string that should be quoted */ +{ + ECMA_STRING_TO_UTF8_STRING (string_p, string_buff, string_buff_size); + const lit_utf8_byte_t *str_p = string_buff; + const lit_utf8_byte_t *regular_str_start_p = string_buff; + const lit_utf8_byte_t *str_end_p = str_p + string_buff_size; + + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_DOUBLE_QUOTE); + + while (str_p < str_end_p) + { + lit_utf8_byte_t c = *str_p++; + + if (c == LIT_CHAR_BACKSLASH || c == LIT_CHAR_DOUBLE_QUOTE) + { + ecma_stringbuilder_append_raw (builder_p, + regular_str_start_p, + (lit_utf8_size_t) (str_p - regular_str_start_p - 1)); + regular_str_start_p = str_p; + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_BACKSLASH); + ecma_stringbuilder_append_byte (builder_p, c); + } + else if (c < LIT_CHAR_SP) + { + ecma_stringbuilder_append_raw (builder_p, + regular_str_start_p, + (lit_utf8_size_t) (str_p - regular_str_start_p - 1)); + regular_str_start_p = str_p; + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_BACKSLASH); + switch (c) + { + case LIT_CHAR_BS: + { + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_B); + break; + } + case LIT_CHAR_FF: + { + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_F); + break; + } + case LIT_CHAR_LF: + { + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_N); + break; + } + case LIT_CHAR_CR: + { + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_R); + break; + } + case LIT_CHAR_TAB: + { + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_T); + break; + } + default: /* Hexadecimal. */ + { + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_U); + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_0); + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_0); + + /* Max range 0-9, hex digits unnecessary. */ + ecma_stringbuilder_append_byte (builder_p, (lit_utf8_byte_t) (LIT_CHAR_0 + (c >> 4))); + + lit_utf8_byte_t c2 = (c & 0xf); + ecma_stringbuilder_append_byte (builder_p, + (lit_utf8_byte_t) (c2 + ((c2 <= 9) + ? LIT_CHAR_0 + : (LIT_CHAR_LOWERCASE_A - 10)))); + break; + } + } + } + } + + ecma_stringbuilder_append_raw (builder_p, + regular_str_start_p, + (lit_utf8_size_t) (str_end_p - regular_str_start_p)); + ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_DOUBLE_QUOTE); + + ECMA_FINALIZE_UTF8_STRING (string_buff, string_buff_size); +} /* ecma_builtin_json_quote */ + +static ecma_value_t +ecma_builtin_json_serialize_property (ecma_json_stringify_context_t *context_p, + ecma_object_t *holder_p, + ecma_string_t *key_p); + +/** + * Abstract operation 'SerializeJSONObject' defined in 24.3.2.3 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_json_serialize_object (ecma_json_stringify_context_t *context_p, /**< context*/ + ecma_object_t *obj_p) /**< the object*/ +{ + /* 1. */ + if (ecma_json_has_object_in_stack (context_p->occurence_stack_last_p, obj_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("The structure is cyclical.")); + } + + /* 2. */ + ecma_json_occurence_stack_item_t stack_item; + stack_item.next_p = context_p->occurence_stack_last_p; + stack_item.object_p = obj_p; + context_p->occurence_stack_last_p = &stack_item; + + /* 3. - 4.*/ + const lit_utf8_size_t stepback_size = ecma_stringbuilder_get_size (&context_p->indent_builder); + ecma_stringbuilder_append (&context_p->indent_builder, context_p->gap_str_p); + + const bool has_gap = !ecma_compare_ecma_string_to_magic_id (context_p->gap_str_p, LIT_MAGIC_STRING__EMPTY); + const lit_utf8_size_t separator_size = ecma_stringbuilder_get_size (&context_p->indent_builder); + + ecma_collection_header_t *property_keys_p; + /* 5. */ + if (context_p->property_list_p->item_count > 0) + { + property_keys_p = context_p->property_list_p; + } + /* 6. */ else { - if (ecma_op_is_callable (arg2)) + property_keys_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_ENUMERABLE); + } + + /* 8. */ + ecma_value_t *ecma_value_p = ecma_collection_iterator_init (property_keys_p); + + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_LEFT_BRACE); + const lit_utf8_size_t left_brace = ecma_stringbuilder_get_size (&context_p->result_builder); + lit_utf8_size_t last_prop = left_brace; + + ecma_value_t result = ECMA_VALUE_EMPTY; + while (ecma_value_p != NULL) + { + if (has_gap) { - ecma_object_t *object_p = ecma_op_create_object_object_noarg (); + ecma_stringbuilder_append_raw (&context_p->result_builder, + ecma_stringbuilder_get_data (&context_p->indent_builder), + separator_size); + } - ecma_property_value_t *prop_value_p; - prop_value_p = ecma_create_named_data_property (object_p, - ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY), - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, - NULL); + ecma_string_t *key_p = ecma_get_string_from_value (*ecma_value_p); + ecma_builtin_json_quote (&context_p->result_builder, key_p); + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_COLON); - ecma_named_data_property_assign_value (object_p, prop_value_p, final_result); - ecma_free_value (final_result); + /* 8.c.iii */ + if (has_gap) + { + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_SP); + } - ret_value = ecma_builtin_json_walk (ecma_get_object_from_value (arg2), - object_p, - ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY)); - ecma_deref_object (object_p); + result = ecma_builtin_json_serialize_property (context_p, obj_p, key_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto cleanup; + } + + /* 8.b */ + if (!ecma_is_value_undefined (result)) + { + /* ecma_builtin_json_serialize_property already appended the result. */ + JERRY_ASSERT (ecma_is_value_empty (result)); + + last_prop = ecma_stringbuilder_get_size (&context_p->result_builder); + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_COMMA); } else { - ret_value = final_result; + /* The property should not be appended, we must backtrack. */ + ecma_stringbuilder_revert (&context_p->result_builder, last_prop); + } + + ecma_value_p = ecma_collection_iterator_next (ecma_value_p); + } + + /* Remove the last comma. */ + ecma_stringbuilder_revert (&context_p->result_builder, last_prop); + + if (last_prop != left_brace && has_gap) + { + /* We appended at least one element, and have a separator, so must append the stepback. */ + ecma_stringbuilder_append_raw (&context_p->result_builder, + ecma_stringbuilder_get_data (&context_p->indent_builder), + stepback_size); + } + + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_RIGHT_BRACE); + result = ECMA_VALUE_EMPTY; + + /* 11. */ + context_p->occurence_stack_last_p = stack_item.next_p; + + /* 12. */ + ecma_stringbuilder_revert (&context_p->indent_builder, stepback_size); + +cleanup: + if (context_p->property_list_p->item_count == 0) + { + ecma_free_values_collection (property_keys_p, 0); + } + + return result; +} /* ecma_builtin_json_serialize_object */ + +/** + * Abstract operation 'SerializeJSONArray' defined in 24.3.2.4 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_json_serialize_array (ecma_json_stringify_context_t *context_p, /**< context*/ + ecma_object_t *obj_p) /**< the array object*/ +{ + JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY); + + /* 1. */ + if (ecma_json_has_object_in_stack (context_p->occurence_stack_last_p, obj_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("The structure is cyclical.")); + } + + /* 2. */ + ecma_json_occurence_stack_item_t stack_item; + stack_item.next_p = context_p->occurence_stack_last_p; + stack_item.object_p = obj_p; + context_p->occurence_stack_last_p = &stack_item; + + /* 3. - 4.*/ + const lit_utf8_size_t stepback_size = ecma_stringbuilder_get_size (&context_p->indent_builder); + ecma_stringbuilder_append (&context_p->indent_builder, context_p->gap_str_p); + const lit_utf8_size_t separator_size = ecma_stringbuilder_get_size (&context_p->indent_builder); + + const bool has_gap = !ecma_compare_ecma_string_to_magic_id (context_p->gap_str_p, LIT_MAGIC_STRING__EMPTY); + + /* 6. */ + uint32_t array_length = ((ecma_extended_object_t *) obj_p)->u.array.length; + + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_LEFT_SQUARE); + + const lit_utf8_size_t left_square = ecma_stringbuilder_get_size (&context_p->result_builder); + lit_utf8_size_t last_prop = left_square; + + /* 8. - 9. */ + for (uint32_t index = 0; index < array_length; index++) + { + /* 9.a */ + ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index); + + if (has_gap) + { + ecma_stringbuilder_append_raw (&context_p->result_builder, + ecma_stringbuilder_get_data (&context_p->indent_builder), + separator_size); + } + + ecma_value_t result = ecma_builtin_json_serialize_property (context_p, obj_p, index_str_p); + ecma_deref_ecma_string (index_str_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + if (ecma_is_value_undefined (result)) + { + /* 9.c */ + ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_NULL); + } + else + { + JERRY_ASSERT (ecma_is_value_empty (result)); + } + + last_prop = ecma_stringbuilder_get_size (&context_p->result_builder); + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_COMMA); + } + + /* Remove the last comma. */ + ecma_stringbuilder_revert (&context_p->result_builder, last_prop); + + /* 11.b.iii */ + if (last_prop != left_square && has_gap) + { + /* We appended at least one element, and have a separator, so must append the stepback. */ + ecma_stringbuilder_append_raw (&context_p->result_builder, + ecma_stringbuilder_get_data (&context_p->indent_builder), + stepback_size); + } + + ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_RIGHT_SQUARE); + + /* 12. */ + context_p->occurence_stack_last_p = stack_item.next_p; + + /* 13. */ + ecma_stringbuilder_revert (&context_p->indent_builder, stepback_size); + + return ECMA_VALUE_EMPTY; +} /* ecma_builtin_json_serialize_array */ + +/** + * Abstract operation 'SerializeJSONProperty' defined in 24.3.2.1 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_json_serialize_property (ecma_json_stringify_context_t *context_p, /**< context*/ + ecma_object_t *holder_p, /**< the object*/ + ecma_string_t *key_p) /**< property key*/ +{ + /* 1. */ + ecma_value_t value = ecma_op_object_get (holder_p, key_p); + + /* 2. */ + if (ECMA_IS_VALUE_ERROR (value)) + { + return value; + } + + /* 3. */ + if (ecma_is_value_object (value)) + { + ecma_object_t *value_obj_p = ecma_get_object_from_value (value); + + ecma_value_t to_json = ecma_op_object_get_by_magic_id (value_obj_p, LIT_MAGIC_STRING_TO_JSON_UL); + + if (ECMA_IS_VALUE_ERROR (to_json)) + { + ecma_deref_object (value_obj_p); + return to_json; + } + + /* 3.c */ + if (ecma_op_is_callable (to_json)) + { + ecma_value_t key_value = ecma_make_string_value (key_p); + ecma_value_t call_args[] = { key_value }; + ecma_object_t *to_json_obj_p = ecma_get_object_from_value (to_json); + + ecma_value_t result = ecma_op_function_call (to_json_obj_p, value, call_args, 1); + ecma_deref_object (value_obj_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_deref_object (to_json_obj_p); + return result; + } + + value = result; + } + ecma_free_value (to_json); + } + + /* 4. */ + if (context_p->replacer_function_p) + { + ecma_value_t holder_value = ecma_make_object_value (holder_p); + ecma_value_t key_value = ecma_make_string_value (key_p); + ecma_value_t call_args[] = { key_value, value }; + + ecma_value_t result = ecma_op_function_call (context_p->replacer_function_p, holder_value, call_args, 2); + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + value = result; + } + + /* 5. */ + if (ecma_is_value_object (value)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (value); + lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p); + + ecma_value_t result = ECMA_VALUE_EMPTY; + + /* 5.a */ + if (class_name == LIT_MAGIC_STRING_NUMBER_UL) + { + result = ecma_op_to_number (value); + } + /* 5.b */ + else if (class_name == LIT_MAGIC_STRING_STRING_UL) + { + result = ecma_op_to_string (value); + } + /* 5.c */ + else if (class_name == LIT_MAGIC_STRING_BOOLEAN_UL) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; + result = ext_object_p->u.class_prop.u.value; + } + + if (!ecma_is_value_empty (result)) + { + ecma_deref_object (obj_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + value = result; } } - ECMA_FINALIZE_UTF8_STRING (str_start_p, string_size); + /* 6. - 8. */ + if (ecma_is_value_null (value)) + { + ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_NULL); + return ECMA_VALUE_EMPTY; + } - ECMA_FINALIZE (string); - return ret_value; -} /* ecma_builtin_json_parse */ + if (ecma_is_value_true (value)) + { + ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_TRUE); + return ECMA_VALUE_EMPTY; + } -static ecma_value_t -ecma_builtin_json_str (ecma_string_t *key_p, ecma_object_t *holder_p, ecma_json_stringify_context_t *context_p); + if (ecma_is_value_false (value)) + { + ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_FALSE); + return ECMA_VALUE_EMPTY; + } -static ecma_value_t -ecma_builtin_json_object (ecma_object_t *obj_p, ecma_json_stringify_context_t *context_p); + /* 9. */ + if (ecma_is_value_string (value)) + { + ecma_string_t *value_str_p = ecma_get_string_from_value (value); + /* Quote will append the result. */ + ecma_builtin_json_quote (&context_p->result_builder, value_str_p); + ecma_deref_ecma_string (value_str_p); -static ecma_value_t -ecma_builtin_json_array (ecma_object_t *obj_p, ecma_json_stringify_context_t *context_p); + return ECMA_VALUE_EMPTY; + } + + /* 10. */ + if (ecma_is_value_number (value)) + { + ecma_number_t num_value = ecma_get_number_from_value (value); + + /* 10.a */ + if (!ecma_number_is_nan (num_value) && !ecma_number_is_infinity (num_value)) + { + ecma_value_t result_value = ecma_op_to_string (value); + JERRY_ASSERT (ecma_is_value_string (result_value)); + ecma_string_t *result_string_p = ecma_get_string_from_value (result_value); + + ecma_stringbuilder_append (&context_p->result_builder, result_string_p); + ecma_deref_ecma_string (result_string_p); + } + else + { + /* 10.b */ + ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_NULL); + } + + ecma_free_value (value); + return ECMA_VALUE_EMPTY; + } + + /* 11. */ + if (ecma_is_value_object (value) && !ecma_op_is_callable (value)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (value); + lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p); + + ecma_value_t ret_value; + /* 10.a */ + if (class_name == LIT_MAGIC_STRING_ARRAY_UL) + { + ret_value = ecma_builtin_json_serialize_array (context_p, obj_p); + } + /* 10.b */ + else + { + ret_value = ecma_builtin_json_serialize_object (context_p, obj_p); + } + + ecma_deref_object (obj_p); + return ret_value; + } + + /* 12. */ + ecma_free_value (value); + return ECMA_VALUE_UNDEFINED; +} /* ecma_builtin_json_serialize_property */ /** * Helper function to stringify an object in JSON format representing an ecma_value. @@ -907,8 +1287,8 @@ ecma_builtin_json_array (ecma_object_t *obj_p, ecma_json_stringify_context_t *co * Returned value must be freed with ecma_free_value. * */ -static ecma_value_t ecma_builtin_json_str_helper (const ecma_value_t arg1, /**< object argument */ - ecma_json_stringify_context_t context) /**< context argument */ +static ecma_value_t ecma_builtin_json_str_helper (ecma_json_stringify_context_t *context_p, /**< context argument */ + const ecma_value_t arg1) /**< object argument */ { ecma_value_t ret_value = ECMA_VALUE_EMPTY; ecma_object_t *obj_wrapper_p = ecma_op_create_object_object_noarg (); @@ -920,13 +1300,23 @@ static ecma_value_t ecma_builtin_json_str_helper (const ecma_value_t arg1, /**< JERRY_ASSERT (ecma_is_value_true (put_comp_val)); - ret_value = ecma_builtin_json_str (empty_str_p, obj_wrapper_p, &context); + context_p->result_builder = ecma_stringbuilder_create (); - ecma_free_value (put_comp_val); - ecma_deref_ecma_string (empty_str_p); + if (!ecma_compare_ecma_string_to_magic_id (context_p->gap_str_p, LIT_MAGIC_STRING__EMPTY)) + { + ecma_stringbuilder_append_byte (&context_p->indent_builder, LIT_CHAR_LF); + } + + ret_value = ecma_builtin_json_serialize_property (context_p, obj_wrapper_p, empty_str_p); ecma_deref_object (obj_wrapper_p); - return ret_value; + if (ECMA_IS_VALUE_ERROR (ret_value) || ecma_is_value_undefined (ret_value)) + { + ecma_stringbuilder_destroy (&context_p->result_builder); + return ret_value; + } + + return ecma_make_string_value (ecma_stringbuilder_finalize (&context_p->result_builder)); } /* ecma_builtin_json_str_helper */ /** @@ -940,15 +1330,15 @@ ecma_builtin_json_string_from_object (const ecma_value_t arg1) /**< object argum { ecma_json_stringify_context_t context; context.occurence_stack_last_p = NULL; - context.indent_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + context.indent_builder = ecma_stringbuilder_create (); context.property_list_p = ecma_new_values_collection (); context.replacer_function_p = NULL; context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); - ecma_value_t ret_value = ecma_builtin_json_str_helper (arg1, context); + ecma_value_t ret_value = ecma_builtin_json_str_helper (&context, arg1); ecma_deref_ecma_string (context.gap_str_p); - ecma_deref_ecma_string (context.indent_str_p); + ecma_stringbuilder_destroy (&context.indent_builder); ecma_free_values_collection (context.property_list_p, 0); return ret_value; } /*ecma_builtin_json_string_from_object*/ @@ -969,20 +1359,10 @@ ecma_builtin_json_stringify (ecma_value_t this_arg, /**< 'this' argument */ ecma_value_t arg3) /**< space */ { JERRY_UNUSED (this_arg); - ecma_value_t ret_value = ECMA_VALUE_EMPTY; ecma_json_stringify_context_t context; - - /* 1. */ - context.occurence_stack_last_p = NULL; - - /* 2. */ - context.indent_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); - - /* 3. */ - context.property_list_p = ecma_new_values_collection (); - context.replacer_function_p = NULL; + context.property_list_p = ecma_new_values_collection (); /* 4. */ if (ecma_is_value_object (arg2)) @@ -997,818 +1377,185 @@ ecma_builtin_json_stringify (ecma_value_t this_arg, /**< 'this' argument */ /* 4.b */ else if (ecma_object_get_class_name (obj_p) == LIT_MAGIC_STRING_ARRAY_UL) { - ECMA_TRY_CATCH (array_length, - ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_LENGTH), - ret_value); - - ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num, - array_length, - ret_value); - + ecma_extended_object_t *array_object_p = (ecma_extended_object_t *) obj_p; + uint32_t array_length = array_object_p->u.array.length; uint32_t index = 0; - /* 4.b.ii */ - while ((index < ecma_number_to_uint32 (array_length_num)) && ecma_is_value_empty (ret_value)) + /* 4.b.iii.5 */ + while (index < array_length) { ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index); + ecma_value_t value = ecma_op_object_get (obj_p, index_str_p); + ecma_deref_ecma_string (index_str_p); - ECMA_TRY_CATCH (value, - ecma_op_object_get (obj_p, index_str_p), - ret_value); + if (ECMA_IS_VALUE_ERROR (value)) + { + ecma_free_values_collection (context.property_list_p, 0); + return value; + } - /* 4.b.ii.1 */ + /* 4.b.iii.5.c */ ecma_value_t item = ECMA_VALUE_UNDEFINED; - /* 4.b.ii.2 */ + /* 4.b.iii.5.d */ if (ecma_is_value_string (value)) { - item = ecma_copy_value (value); + ecma_ref_ecma_string (ecma_get_string_from_value (value)); + item = value; } - /* 4.b.ii.3 */ + /* 4.b.iii.5.e */ else if (ecma_is_value_number (value)) { - ECMA_TRY_CATCH (str_val, - ecma_op_to_string (value), - ret_value); - - item = ecma_copy_value (str_val); - - ECMA_FINALIZE (str_val); + ecma_value_t number_str_value = ecma_op_to_string (value); + JERRY_ASSERT (ecma_is_value_string (number_str_value)); + item = number_str_value; } - /* 4.b.ii.4 */ - else if (ecma_is_value_object (value)) + /* 4.b.iii.5.f */ + else if (ecma_is_value_object (value) + && (ecma_object_get_class_name (ecma_get_object_from_value (value)) == LIT_MAGIC_STRING_NUMBER_UL + || ecma_object_get_class_name (ecma_get_object_from_value (value)) == LIT_MAGIC_STRING_STRING_UL)) { - ecma_object_t *obj_val_p = ecma_get_object_from_value (value); - lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_val_p); + ecma_value_t str_val = ecma_op_to_string (value); - /* 4.b.ii.4.a */ - if (class_name == LIT_MAGIC_STRING_NUMBER_UL - || class_name == LIT_MAGIC_STRING_STRING_UL) + if (ECMA_IS_VALUE_ERROR (str_val)) { - ECMA_TRY_CATCH (val, - ecma_op_to_string (value), - ret_value); - - item = ecma_copy_value (val); - - ECMA_FINALIZE (val); + ecma_free_values_collection (context.property_list_p, 0); + ecma_free_value (value); + return str_val; } + + item = str_val; } - /* 4.b.ii.5 */ + ecma_free_value (value); + + /* 4.b.iii.5.g */ if (!ecma_is_value_undefined (item)) { - if (!ecma_has_string_value_in_collection (context.property_list_p, item)) + JERRY_ASSERT (ecma_is_value_string (item)); + ecma_string_t *string_p = ecma_get_string_from_value (item); + + if (!ecma_has_string_value_in_collection (context.property_list_p, string_p)) { - ecma_append_to_values_collection (context.property_list_p, item, 0); - ecma_deref_ecma_string (ecma_get_string_from_value (item)); + ecma_append_to_values_collection (context.property_list_p, item, ECMA_COLLECTION_NO_COPY); } else { - ecma_free_value (item); + ecma_deref_ecma_string (string_p); } } - ECMA_FINALIZE (value); - - ecma_deref_ecma_string (index_str_p); - index++; } - - ECMA_OP_TO_NUMBER_FINALIZE (array_length_num); - ECMA_FINALIZE (array_length); } } - if (ecma_is_value_empty (ret_value)) + ecma_value_t space; + + /* 5. */ + if (ecma_is_value_object (arg3)) { - ecma_value_t space = ecma_copy_value (arg3); + ecma_object_t *obj_p = ecma_get_object_from_value (arg3); + lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p); - /* 5. */ - if (ecma_is_value_object (arg3)) + /* 5.a */ + if (class_name == LIT_MAGIC_STRING_NUMBER_UL) { - ecma_object_t *obj_p = ecma_get_object_from_value (arg3); - lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p); + ecma_value_t value = ecma_op_to_number (arg3); - /* 5.a */ - if (class_name == LIT_MAGIC_STRING_NUMBER_UL) + if (ECMA_IS_VALUE_ERROR (value)) { - ECMA_TRY_CATCH (val, - ecma_op_to_number (arg3), - ret_value); - - ecma_free_value (space); - space = ecma_copy_value (val); - - ECMA_FINALIZE (val); + ecma_free_values_collection (context.property_list_p, 0); + return value; } - /* 5.b */ - else if (class_name == LIT_MAGIC_STRING_STRING_UL) - { - ECMA_TRY_CATCH (val, - ecma_op_to_string (arg3), - ret_value); - ecma_free_value (space); - space = ecma_copy_value (val); - - ECMA_FINALIZE (val); - } + space = value; } - - if (ecma_is_value_empty (ret_value)) + /* 5.b */ + else if (class_name == LIT_MAGIC_STRING_STRING_UL) { - /* 6. */ - if (ecma_is_value_number (space)) + ecma_value_t value = ecma_op_to_string (arg3); + + if (ECMA_IS_VALUE_ERROR (value)) { - ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num, - arg3, - ret_value); - - /* 6.a */ - int32_t num_of_spaces = ecma_number_to_int32 (array_length_num); - num_of_spaces = (num_of_spaces > 10) ? 10 : num_of_spaces; - - /* 6.b */ - if (num_of_spaces < 1) - { - context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); - } - else - { - JMEM_DEFINE_LOCAL_ARRAY (space_buff, num_of_spaces, char); - - for (int32_t i = 0; i < num_of_spaces; i++) - { - space_buff[i] = LIT_CHAR_SP; - } - - context.gap_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) space_buff, - (lit_utf8_size_t) num_of_spaces); - - JMEM_FINALIZE_LOCAL_ARRAY (space_buff); - } - - ECMA_OP_TO_NUMBER_FINALIZE (array_length_num); + ecma_free_values_collection (context.property_list_p, 0); + return value; } - /* 7. */ - else if (ecma_is_value_string (space)) - { - ecma_string_t *space_str_p = ecma_get_string_from_value (space); - ecma_length_t num_of_chars = ecma_string_get_length (space_str_p); - if (num_of_chars < 10) - { - ecma_ref_ecma_string (space_str_p); - context.gap_str_p = space_str_p; - } - else - { - context.gap_str_p = ecma_string_substr (space_str_p, 0, 10); - } - } - /* 8. */ - else - { - context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); - } + space = value; } - - ecma_free_value (space); - - if (ecma_is_value_empty (ret_value)) + else { - /* 9. */ - ret_value = ecma_builtin_json_str_helper (arg1, context); + space = ecma_copy_value (arg3); } - - ecma_deref_ecma_string (context.gap_str_p); + } + else + { + space = ecma_copy_value (arg3); } - ecma_deref_ecma_string (context.indent_str_p); + /* 6. */ + if (ecma_is_value_number (space)) + { + ecma_number_t number = ecma_get_number_from_value (space); + /* 6.a */ + int32_t num_of_spaces = ecma_number_to_int32 (number); + num_of_spaces = JERRY_MIN (10, num_of_spaces); + /* 6.b */ + if (num_of_spaces < 1) + { + context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + } + else + { + JMEM_DEFINE_LOCAL_ARRAY (space_buff, num_of_spaces, char); + + memset (space_buff, LIT_CHAR_SP, (size_t) num_of_spaces); + context.gap_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) space_buff, + (lit_utf8_size_t) num_of_spaces); + + JMEM_FINALIZE_LOCAL_ARRAY (space_buff); + } + } + /* 7. */ + else if (ecma_is_value_string (space)) + { + ecma_string_t *space_str_p = ecma_get_string_from_value (space); + ecma_length_t num_of_chars = ecma_string_get_length (space_str_p); + + if (num_of_chars < 10) + { + ecma_ref_ecma_string (space_str_p); + context.gap_str_p = space_str_p; + } + else + { + context.gap_str_p = ecma_string_substr (space_str_p, 0, 10); + } + } + /* 8. */ + else + { + context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + } + + ecma_free_value (space); + + /* 1., 2., 3. */ + context.occurence_stack_last_p = NULL; + context.indent_builder = ecma_stringbuilder_create (); + + /* 9. */ + ecma_value_t ret_value = ecma_builtin_json_str_helper (&context, arg1); + + ecma_deref_ecma_string (context.gap_str_p); + ecma_stringbuilder_destroy (&context.indent_builder); ecma_free_values_collection (context.property_list_p, 0); return ret_value; } /* ecma_builtin_json_stringify */ -/** - * Abstract operation 'Quote' defined in 15.12.3 - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * @return ecma value - * Returned value must be freed with ecma_free_value. - */ -static ecma_value_t -ecma_builtin_json_quote (ecma_string_t *string_p) /**< string that should be quoted*/ -{ - ECMA_STRING_TO_UTF8_STRING (string_p, string_buff, string_buff_size); - const lit_utf8_byte_t *str_p = string_buff; - const lit_utf8_byte_t *str_end_p = str_p + string_buff_size; - size_t n_bytes = 2; /* Start with 2 for surrounding quotes. */ - - while (str_p < str_end_p) - { - lit_utf8_byte_t c = *str_p++; - - if (c == LIT_CHAR_BACKSLASH || c == LIT_CHAR_DOUBLE_QUOTE) - { - n_bytes += 2; - } - else if (c >= LIT_CHAR_SP && c < LIT_UTF8_1_BYTE_CODE_POINT_MAX) - { - n_bytes++; - } - else if (c > LIT_UTF8_1_BYTE_CODE_POINT_MAX) - { - lit_utf8_size_t sz = lit_get_unicode_char_size_by_utf8_first_byte (c); - n_bytes += sz; - str_p += sz - 1; - } - else - { - switch (c) - { - case LIT_CHAR_BS: - case LIT_CHAR_FF: - case LIT_CHAR_LF: - case LIT_CHAR_CR: - case LIT_CHAR_TAB: - { - n_bytes += 2; - break; - } - default: /* Hexadecimal. */ - { - n_bytes += 2 + 4; - break; - } - } - } - } - - lit_utf8_byte_t *buf_begin = jmem_heap_alloc_block (n_bytes); - JERRY_ASSERT (buf_begin != NULL); - lit_utf8_byte_t *buf = buf_begin; - str_p = string_buff; - - *buf++ = LIT_CHAR_DOUBLE_QUOTE; - - while (str_p < str_end_p) - { - lit_utf8_byte_t c = *str_p++; - - if (c == LIT_CHAR_BACKSLASH || c == LIT_CHAR_DOUBLE_QUOTE) - { - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = c; - } - else if (c >= LIT_CHAR_SP && c < LIT_UTF8_1_BYTE_CODE_POINT_MAX) - { - *buf++ = c; - } - else if (c > LIT_UTF8_1_BYTE_CODE_POINT_MAX) - { - str_p--; - ecma_char_t current_char = lit_utf8_read_next (&str_p); - buf += lit_code_unit_to_utf8 (current_char, (lit_utf8_byte_t *) buf); - } - else - { - switch (c) - { - case LIT_CHAR_BS: - { - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = LIT_CHAR_LOWERCASE_B; - break; - } - case LIT_CHAR_FF: - { - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = LIT_CHAR_LOWERCASE_F; - break; - } - case LIT_CHAR_LF: - { - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = LIT_CHAR_LOWERCASE_N; - break; - } - case LIT_CHAR_CR: - { - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = LIT_CHAR_LOWERCASE_R; - break; - } - case LIT_CHAR_TAB: - { - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = LIT_CHAR_LOWERCASE_T; - break; - } - default: /* Hexadecimal. */ - { - JERRY_ASSERT (c < 0x9f); - *buf++ = LIT_CHAR_BACKSLASH; - *buf++ = LIT_CHAR_LOWERCASE_U; - *buf++ = LIT_CHAR_0; - *buf++ = LIT_CHAR_0; - *buf++ = (lit_utf8_byte_t) (LIT_CHAR_0 + (c >> 4)); /* Max range 0-9, hex digits unnecessary. */ - lit_utf8_byte_t c2 = (c & 0xf); - *buf++ = (lit_utf8_byte_t) (c2 + ((c2 <= 9) ? LIT_CHAR_0 : (LIT_CHAR_LOWERCASE_A - 10))); - break; - } - } - } - } - - *buf++ = LIT_CHAR_DOUBLE_QUOTE; - - /* Make sure we didn't run off the end or allocated more than we actually wanted. */ - JERRY_ASSERT ((size_t) (buf - buf_begin) == n_bytes); - - ecma_string_t *product_str_p = ecma_new_ecma_string_from_utf8 ((const lit_utf8_byte_t *) buf_begin, - (lit_utf8_size_t) (buf - buf_begin)); - jmem_heap_free_block (buf_begin, n_bytes); - ECMA_FINALIZE_UTF8_STRING (string_buff, string_buff_size); - - return ecma_make_string_value (product_str_p); -} /* ecma_builtin_json_quote */ - -/** - * Abstract operation 'Str' defined in 15.12.3 - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * @return ecma value - * Returned value must be freed with ecma_free_value. - */ -static ecma_value_t -ecma_builtin_json_str (ecma_string_t *key_p, /**< property key*/ - ecma_object_t *holder_p, /**< the object*/ - ecma_json_stringify_context_t *context_p) /**< context*/ -{ - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - /* 1. */ - ECMA_TRY_CATCH (value, - ecma_op_object_get (holder_p, key_p), - ret_value); - - ecma_value_t my_val = ecma_copy_value (value); - - /* 2. */ - if (ecma_is_value_object (my_val)) - { - ecma_object_t *value_obj_p = ecma_get_object_from_value (my_val); - - /* 2.a */ - ECMA_TRY_CATCH (toJSON, - ecma_op_object_get_by_magic_id (value_obj_p, LIT_MAGIC_STRING_TO_JSON_UL), - ret_value); - - /* 2.b */ - if (ecma_op_is_callable (toJSON)) - { - ecma_value_t key_value = ecma_make_string_value (key_p); - ecma_value_t call_args[] = { key_value }; - ecma_object_t *toJSON_obj_p = ecma_get_object_from_value (toJSON); - - ECMA_TRY_CATCH (func_ret_val, - ecma_op_function_call (toJSON_obj_p, my_val, call_args, 1), - ret_value); - - ecma_free_value (my_val); - my_val = ecma_copy_value (func_ret_val); - - ECMA_FINALIZE (func_ret_val); - } - - ECMA_FINALIZE (toJSON); - } - - /* 3. */ - if (context_p->replacer_function_p && ecma_is_value_empty (ret_value)) - { - ecma_value_t holder_value = ecma_make_object_value (holder_p); - ecma_value_t key_value = ecma_make_string_value (key_p); - ecma_value_t call_args[] = { key_value, my_val }; - - ECMA_TRY_CATCH (func_ret_val, - ecma_op_function_call (context_p->replacer_function_p, holder_value, call_args, 2), - ret_value); - - ecma_free_value (my_val); - my_val = ecma_copy_value (func_ret_val); - - ECMA_FINALIZE (func_ret_val); - } - - /* 4. */ - if (ecma_is_value_object (my_val) && ecma_is_value_empty (ret_value)) - { - ecma_object_t *obj_p = ecma_get_object_from_value (my_val); - lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p); - - /* 4.a */ - if (class_name == LIT_MAGIC_STRING_NUMBER_UL) - { - ECMA_TRY_CATCH (val, - ecma_op_to_number (my_val), - ret_value); - - ecma_free_value (my_val); - my_val = ecma_copy_value (val); - - ECMA_FINALIZE (val); - } - /* 4.b */ - else if (class_name == LIT_MAGIC_STRING_STRING_UL) - { - ECMA_TRY_CATCH (val, - ecma_op_to_string (my_val), - ret_value); - - ecma_free_value (my_val); - my_val = ecma_copy_value (val); - - ECMA_FINALIZE (val); - } - /* 4.c */ - else if (class_name == LIT_MAGIC_STRING_BOOLEAN_UL) - { - ECMA_TRY_CATCH (val, - ecma_op_to_primitive (my_val, ECMA_PREFERRED_TYPE_NO), - ret_value); - - ecma_free_value (my_val); - my_val = ecma_copy_value (val); - - ECMA_FINALIZE (val); - } - } - - if (ecma_is_value_empty (ret_value)) - { - /* 5. - 7. */ - if (ecma_is_value_null (my_val) || ecma_is_value_boolean (my_val)) - { - ret_value = ecma_op_to_string (my_val); - JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (ret_value)); - } - /* 8. */ - else if (ecma_is_value_string (my_val)) - { - ecma_string_t *value_str_p = ecma_get_string_from_value (my_val); - ret_value = ecma_builtin_json_quote (value_str_p); - } - /* 9. */ - else if (ecma_is_value_number (my_val)) - { - ecma_number_t num_value_p = ecma_get_number_from_value (my_val); - - /* 9.a */ - if (!ecma_number_is_nan (num_value_p) && !ecma_number_is_infinity (num_value_p)) - { - ret_value = ecma_op_to_string (my_val); - JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (ret_value)); - } - else - { - /* 9.b */ - ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING_NULL); - } - } - /* 10. */ - else if (ecma_is_value_object (my_val) && !ecma_op_is_callable (my_val)) - { - ecma_object_t *obj_p = ecma_get_object_from_value (my_val); - lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p); - - /* 10.a */ - if (class_name == LIT_MAGIC_STRING_ARRAY_UL) - { - ECMA_TRY_CATCH (val, - ecma_builtin_json_array (obj_p, context_p), - ret_value); - - ret_value = ecma_copy_value (val); - - ECMA_FINALIZE (val); - } - /* 10.b */ - else - { - ECMA_TRY_CATCH (val, - ecma_builtin_json_object (obj_p, context_p), - ret_value); - - ret_value = ecma_copy_value (val); - - ECMA_FINALIZE (val); - } - } - else - { - /* 11. */ - ret_value = ECMA_VALUE_UNDEFINED; - } - } - - ecma_free_value (my_val); - ECMA_FINALIZE (value); - - return ret_value; -} /* ecma_builtin_json_str */ - -/** - * Abstract operation 'JO' defined in 15.12.3 - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * @return ecma value - * Returned value must be freed with ecma_free_value. - */ -static ecma_value_t -ecma_builtin_json_object (ecma_object_t *obj_p, /**< the object*/ - ecma_json_stringify_context_t *context_p) /**< context*/ -{ - /* 1. */ - if (ecma_json_has_object_in_stack (context_p->occurence_stack_last_p, obj_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG ("The structure is cyclical.")); - } - - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - /* 2. */ - ecma_json_occurence_stack_item_t stack_item; - stack_item.next_p = context_p->occurence_stack_last_p; - stack_item.object_p = obj_p; - context_p->occurence_stack_last_p = &stack_item; - - /* 3. */ - ecma_string_t *stepback_p = context_p->indent_str_p; - - /* 4. */ - ecma_ref_ecma_string (stepback_p); - context_p->indent_str_p = ecma_concat_ecma_strings (stepback_p, context_p->gap_str_p); - - ecma_collection_header_t *property_keys_p; - - /* 5. */ - if (context_p->property_list_p->item_count > 0) - { - property_keys_p = context_p->property_list_p; - } - /* 6. */ - else - { - property_keys_p = ecma_new_values_collection (); - - ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_ENUMERABLE); - - ecma_value_t *ecma_value_p = ecma_collection_iterator_init (props_p); - - while (ecma_value_p != NULL) - { - ecma_string_t *property_name_p = ecma_get_string_from_value (*ecma_value_p); - - ecma_property_t property = ecma_op_object_get_own_property (obj_p, - property_name_p, - NULL, - ECMA_PROPERTY_GET_NO_OPTIONS); - - JERRY_ASSERT (ecma_is_property_enumerable (property)); - - if (ECMA_PROPERTY_GET_TYPE (property) != ECMA_PROPERTY_TYPE_SPECIAL) - { - ecma_append_to_values_collection (property_keys_p, *ecma_value_p, 0); - } - - ecma_value_p = ecma_collection_iterator_next (ecma_value_p); - } - - ecma_free_values_collection (props_p, 0); - } - - /* 7. */ - ecma_collection_header_t *partial_p = ecma_new_values_collection (); - - /* 8. */ - ecma_value_t *ecma_value_p = ecma_collection_iterator_init (property_keys_p); - - while (ecma_value_p != NULL && ecma_is_value_empty (ret_value)) - { - ecma_string_t *key_p = ecma_get_string_from_value (*ecma_value_p); - ecma_value_p = ecma_collection_iterator_next (ecma_value_p); - - /* 8.a */ - ECMA_TRY_CATCH (str_val, - ecma_builtin_json_str (key_p, obj_p, context_p), - ret_value); - - /* 8.b */ - if (!ecma_is_value_undefined (str_val)) - { - ecma_string_t *value_str_p = ecma_get_string_from_value (str_val); - - /* 8.b.i */ - ecma_value_t str_comp_val = ecma_builtin_json_quote (key_p); - JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (str_comp_val)); - - ecma_string_t *member_str_p = ecma_get_string_from_value (str_comp_val); - - /* 8.b.ii */ - member_str_p = ecma_append_magic_string_to_string (member_str_p, LIT_MAGIC_STRING_COLON_CHAR); - - /* 8.b.iii */ - if (!ecma_string_is_empty (context_p->gap_str_p)) - { - member_str_p = ecma_append_magic_string_to_string (member_str_p, LIT_MAGIC_STRING_SPACE_CHAR); - } - - /* 8.b.iv */ - member_str_p = ecma_concat_ecma_strings (member_str_p, value_str_p); - - /* 8.b.v */ - ecma_value_t member_value = ecma_make_string_value (member_str_p); - ecma_append_to_values_collection (partial_p, member_value, 0); - ecma_deref_ecma_string (member_str_p); - } - - ECMA_FINALIZE (str_val); - } - - if (context_p->property_list_p->item_count == 0) - { - ecma_free_values_collection (property_keys_p, 0); - } - - if (ecma_is_value_empty (ret_value)) - { - /* 9. */ - if (partial_p->item_count == 0) - { - lit_utf8_byte_t chars[2] = { LIT_CHAR_LEFT_BRACE, LIT_CHAR_RIGHT_BRACE }; - - ecma_string_t *final_str_p = ecma_new_ecma_string_from_utf8 (chars, 2); - ret_value = ecma_make_string_value (final_str_p); - } - /* 10. */ - else - { - /* 10.a */ - if (ecma_string_is_empty (context_p->gap_str_p)) - { - ret_value = ecma_builtin_helper_json_create_non_formatted_json (LIT_CHAR_LEFT_BRACE, - LIT_CHAR_RIGHT_BRACE, - partial_p); - } - /* 10.b */ - else - { - ret_value = ecma_builtin_helper_json_create_formatted_json (LIT_CHAR_LEFT_BRACE, - LIT_CHAR_RIGHT_BRACE, - stepback_p, - partial_p, - context_p); - } - } - } - - ecma_free_values_collection (partial_p, 0); - - /* 11. */ - context_p->occurence_stack_last_p = stack_item.next_p; - - /* 12. */ - ecma_deref_ecma_string (context_p->indent_str_p); - context_p->indent_str_p = stepback_p; - - /* 13. */ - return ret_value; -} /* ecma_builtin_json_object */ - -/** - * Abstract operation 'JA' defined in 15.12.3 - * - * See also: - * ECMA-262 v5, 15.12.3 - * - * @return ecma value - * Returned value must be freed with ecma_free_value. - */ -static ecma_value_t -ecma_builtin_json_array (ecma_object_t *obj_p, /**< the array object*/ - ecma_json_stringify_context_t *context_p) /**< context*/ -{ - JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY); - - /* 1. */ - if (ecma_json_has_object_in_stack (context_p->occurence_stack_last_p, obj_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG ("The structure is cyclical.")); - } - - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - /* 2. */ - ecma_json_occurence_stack_item_t stack_item; - stack_item.next_p = context_p->occurence_stack_last_p; - stack_item.object_p = obj_p; - context_p->occurence_stack_last_p = &stack_item; - - /* 3. */ - ecma_string_t *stepback_p = context_p->indent_str_p; - - /* 4. */ - ecma_ref_ecma_string (stepback_p); - context_p->indent_str_p = ecma_concat_ecma_strings (stepback_p, context_p->gap_str_p); - - /* 5. */ - ecma_collection_header_t *partial_p = ecma_new_values_collection (); - - /* 6. */ - uint32_t array_length = ((ecma_extended_object_t *) obj_p)->u.array.length; - - /* 7. - 8. */ - for (uint32_t index = 0; index < array_length && ecma_is_value_empty (ret_value); index++) - { - - /* 8.a */ - ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index); - - ECMA_TRY_CATCH (str_val, - ecma_builtin_json_str (index_str_p, obj_p, context_p), - ret_value); - - /* 8.b */ - if (ecma_is_value_undefined (str_val)) - { - ecma_append_to_values_collection (partial_p, ecma_make_magic_string_value (LIT_MAGIC_STRING_NULL), 0); - } - /* 8.c */ - else - { - ecma_append_to_values_collection (partial_p, str_val, 0); - } - - ECMA_FINALIZE (str_val); - ecma_deref_ecma_string (index_str_p); - } - - if (ecma_is_value_empty (ret_value)) - { - /* 9. */ - if (partial_p->item_count == 0) - { - lit_utf8_byte_t chars[2] = { LIT_CHAR_LEFT_SQUARE, LIT_CHAR_RIGHT_SQUARE }; - - ecma_string_t *final_str_p = ecma_new_ecma_string_from_utf8 (chars, 2); - ret_value = ecma_make_string_value (final_str_p); - } - /* 10. */ - else - { - /* 10.a */ - if (ecma_string_is_empty (context_p->gap_str_p)) - { - ret_value = ecma_builtin_helper_json_create_non_formatted_json (LIT_CHAR_LEFT_SQUARE, - LIT_CHAR_RIGHT_SQUARE, - partial_p); - } - /* 10.b */ - else - { - ret_value = ecma_builtin_helper_json_create_formatted_json (LIT_CHAR_LEFT_SQUARE, - LIT_CHAR_RIGHT_SQUARE, - stepback_p, - partial_p, - context_p); - } - } - } - - ecma_free_values_collection (partial_p, 0); - - /* 11. */ - context_p->occurence_stack_last_p = stack_item.next_p; - - /* 12. */ - ecma_deref_ecma_string (context_p->indent_str_p); - context_p->indent_str_p = stepback_p; - - /* 13. */ - return ret_value; -} /* ecma_builtin_json_array */ - /** * @} * @} diff --git a/jerry-core/jmem/jmem-heap.c b/jerry-core/jmem/jmem-heap.c index 06b87c3e0..e325a50c8 100644 --- a/jerry-core/jmem/jmem-heap.c +++ b/jerry-core/jmem/jmem-heap.c @@ -633,10 +633,7 @@ jmem_heap_realloc_block (void *ptr, /**< memory region to reallocate */ /* jmem_heap_alloc_block_internal may trigger garbage collection, which can create new free blocks * in the heap structure, so we need to look up the previous block again. */ - if (JERRY_UNLIKELY (JERRY_CONTEXT (ecma_gc_new_objects) == 0)) - { - prev_p = jmem_heap_find_prev (block_p); - } + prev_p = jmem_heap_find_prev (block_p); memcpy (ret_block_p, block_p, old_size); jmem_heap_insert_block (block_p, prev_p, aligned_old_size);