diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h index 2f78d376b6..acec402827 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-global.inc.h @@ -202,12 +202,14 @@ OBJECT_VALUE (LIT_MAGIC_STRING_MATH_UL, ECMA_PROPERTY_CONFIGURABLE) #endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_MATH_BUILTIN */ +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN // ECMA-262 v5, 15.1.5.2 -CP_UNIMPLEMENTED_VALUE (LIT_MAGIC_STRING_JSON_U, - ecma_builtin_get (ECMA_BUILTIN_ID_JSON), - ECMA_PROPERTY_WRITABLE, - ECMA_PROPERTY_NOT_ENUMERABLE, - ECMA_PROPERTY_CONFIGURABLE) +OBJECT_VALUE (LIT_MAGIC_STRING_JSON_U, + ecma_builtin_get (ECMA_BUILTIN_ID_JSON), + ECMA_PROPERTY_WRITABLE, + ECMA_PROPERTY_NOT_ENUMERABLE, + ECMA_PROPERTY_CONFIGURABLE) +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN */ #ifdef CONFIG_ECMA_COMPACT_PROFILE OBJECT_VALUE (LIT_MAGIC_STRING_COMPACT_PROFILE_ERROR_UL, diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.cpp new file mode 100644 index 0000000000..f650f360e5 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-json.cpp @@ -0,0 +1,399 @@ +/* Copyright 2014-2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmahelpers Helpers for operations with ECMA data types + * @{ + */ + +#include "ecma-alloc.h" +#include "ecma-helpers.h" +#include "ecma-builtin-helpers.h" + +#define LIST_BLOCK_SIZE 256UL + +/** + * Allocate memory for elements. + * + * @return pointer to current position of the allocated memory. + */ +void ** +realloc_list (list_ctx_t *ctx_p) /**< list context */ +{ + size_t old_block_size = static_cast (ctx_p->block_end_p - ctx_p->block_start_p); + size_t new_block_size = old_block_size + LIST_BLOCK_SIZE; + + size_t current_ptr_offset = static_cast (ctx_p->current_p - ctx_p->block_start_p); + void **new_block_start_p = (void **) mem_heap_alloc_block (new_block_size, MEM_HEAP_ALLOC_SHORT_TERM); + + if (ctx_p->current_p) + { + memcpy (new_block_start_p, ctx_p->block_start_p, static_cast (current_ptr_offset)); + mem_heap_free_block (ctx_p->block_start_p); + } + + ctx_p->block_start_p = new_block_start_p; + ctx_p->block_end_p = new_block_start_p + new_block_size; + ctx_p->current_p = new_block_start_p + current_ptr_offset; + + return ctx_p->current_p; +} /* realloc_list */ + +/** + * Append element to the list. + */ +void +list_append (list_ctx_t *ctx_p, /**< list context */ + void *element_p) /**< element that should be stored */ +{ + void **current_p = ctx_p->current_p; + + if (current_p + 1 > ctx_p->block_end_p) + { + current_p = realloc_list (ctx_p); + } + + *current_p = element_p; + + ctx_p->current_p++; +} /* list_append */ + +/** + * Check the element existance in the list. + * + * @return true if the list already has the value. + */ +bool +list_has_element (list_ctx_t *ctx_p, /**< list context */ + void *element_p) /**< element that should be checked */ +{ + for (void **current_p = ctx_p->block_start_p; + current_p < ctx_p->current_p; + current_p++) + { + if (*current_p == element_p) + { + return true; + } + } + + return false; +} /* list_has_element */ + +/** + * Check the element existance in the list for ecma strings. + * + * @return true if the list already has the value. + */ +bool +list_has_ecma_string_element (list_ctx_t *ctx_p, /**< list context */ + ecma_string_t *string_p) /**< element that should be checked */ +{ + for (void **current_p = ctx_p->block_start_p; + current_p < ctx_p->current_p; + current_p++) + { + if (ecma_compare_ecma_strings ((ecma_string_t *) *current_p, string_p)) + { + return true; + } + } + + return false; +} /* list_has_element */ + +/** + * Check the state of the list allocation. + * + * @return true if the list is already allocated. + */ +static bool +list_is_allocated (list_ctx_t *ctx_p) /**< list context */ +{ + return ctx_p->block_start_p < ctx_p->block_end_p; +} /* list_is_allocated */ + +/** + * Check if the list is empty. + * + * @return true if the list is empty. + */ +bool +list_is_empty (list_ctx_t *ctx_p) +{ + return ctx_p->block_start_p == ctx_p->current_p; +} /* list_is_empty */ + +/** + * Set the current memory position to the previous element. + */ +void +list_remove_last_element (list_ctx_t *ctx_p) /**< list context */ +{ + if (list_is_empty (ctx_p)) + { + return; + } + ctx_p->current_p = NULL; + + ctx_p->current_p--; +} /* list_remove_last_element */ + +/** + * Free the allocated list. + */ +void +free_list (list_ctx_t *ctx_p) /**< list context */ +{ + if (!list_is_allocated (ctx_p)) + { + return; + } + + mem_heap_free_block (ctx_p->block_start_p); + + ctx_p->block_start_p = NULL; + ctx_p->block_end_p = NULL; + ctx_p->current_p = NULL; +} /* free_list */ + +/** + * Free the stored ecma string elements and the list. + */ +void +free_list_with_ecma_string_content (list_ctx_t *ctx_p) /**< list context */ +{ + if (!list_is_allocated (ctx_p)) + { + return; + } + + for (void **current_p = ctx_p->block_start_p; + current_p < ctx_p->current_p; + current_p++) + { + ecma_deref_ecma_string ((ecma_string_t *) *current_p); + } + + free_list (ctx_p); +} /* free_list_with_ecma_string_content */ + +/** + * 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. + */ +ecma_string_t * +ecma_builtin_helper_json_create_separated_properties (list_ctx_t *partial_p, /**< list of key-value pairs*/ + ecma_string_t *separator_p) /**< separator*/ +{ + ecma_string_t *properties_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + ecma_string_t *tmp_str_p; + + uint32_t index = 0; + + for (void **current_p = partial_p->block_start_p; + current_p < partial_p->current_p; + current_p++, index++) + { + if (index == 0) + { + tmp_str_p = ecma_concat_ecma_strings (properties_str_p, (ecma_string_t *) *current_p); + ecma_deref_ecma_string (properties_str_p); + properties_str_p = tmp_str_p; + continue; + } + + tmp_str_p = ecma_concat_ecma_strings (properties_str_p, separator_p); + ecma_deref_ecma_string (properties_str_p); + properties_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (properties_str_p, (ecma_string_t *) *current_p); + ecma_deref_ecma_string (properties_str_p); + properties_str_p = tmp_str_p; + } + + return properties_str_p; +} /* ecma_builtin_json_helper_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 completion value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_builtin_helper_json_create_formatted_json (ecma_string_t *left_bracket_p, /**< left bracket*/ + ecma_string_t *right_bracket_p, /**< right bracket*/ + ecma_string_t *stepback_p, /**< stepback*/ + list_ctx_t *partial_p, /**< list of key-value pairs*/ + stringify_context_t *context_p) /**< context*/ +{ + /* 10.b */ + ecma_string_t *comma_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); + ecma_string_t *line_feed_p = ecma_get_magic_string (LIT_MAGIC_STRING_NEW_LINE_CHAR); + ecma_string_t *properties_str_p; + ecma_string_t *separator_p; + + /* 10.b.i */ + ecma_string_t *tmp_str_p = ecma_concat_ecma_strings (comma_str_p, line_feed_p); + ecma_deref_ecma_string (comma_str_p); + separator_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (separator_p, context_p->indent_str_p); + ecma_deref_ecma_string (separator_p); + separator_p = tmp_str_p; + + /* 10.b.ii */ + properties_str_p = ecma_builtin_helper_json_create_separated_properties (partial_p, separator_p); + ecma_deref_ecma_string (separator_p); + + /* 10.b.iii */ + ecma_string_t *final_str_p; + + tmp_str_p = ecma_concat_ecma_strings (left_bracket_p, line_feed_p); + final_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (final_str_p, context_p->indent_str_p); + ecma_deref_ecma_string (final_str_p); + final_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (final_str_p, properties_str_p); + ecma_deref_ecma_string (final_str_p); + ecma_deref_ecma_string (properties_str_p); + final_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (final_str_p, line_feed_p); + ecma_deref_ecma_string (line_feed_p); + ecma_deref_ecma_string (final_str_p); + final_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (final_str_p, stepback_p); + ecma_deref_ecma_string (final_str_p); + final_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (final_str_p, right_bracket_p); + ecma_deref_ecma_string (final_str_p); + final_str_p = tmp_str_p; + + return ecma_make_normal_completion_value (ecma_make_string_value (final_str_p)); +} /* ecma_builtin_json_helper_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 completion value + * Returned value must be freed with ecma_free_completion_value. + */ +ecma_completion_value_t +ecma_builtin_helper_json_create_non_formatted_json (ecma_string_t *left_bracket_p, /**< left bracket*/ + ecma_string_t *right_bracket_p, /**< right bracket*/ + list_ctx_t *partial_p) /**< list of key-value pairs*/ +{ + /* 10.a */ + ecma_string_t *comma_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_COMMA_CHAR); + ecma_string_t *properties_str_p; + ecma_string_t *tmp_str_p; + + /* 10.a.i */ + properties_str_p = ecma_builtin_helper_json_create_separated_properties (partial_p, comma_str_p); + ecma_deref_ecma_string (comma_str_p); + + /* 10.a.ii */ + tmp_str_p = ecma_concat_ecma_strings (left_bracket_p, properties_str_p); + ecma_deref_ecma_string (properties_str_p); + properties_str_p = tmp_str_p; + + tmp_str_p = ecma_concat_ecma_strings (properties_str_p, right_bracket_p); + ecma_deref_ecma_string (properties_str_p); + properties_str_p = tmp_str_p; + + return ecma_make_normal_completion_value (ecma_make_string_value (properties_str_p)); +} /* ecma_builtin_json_helper_create_non_formatted_json */ + +/** + * Convert decimal value to 4 digit hexadecimal string value. + * + * See also: + * ECMA-262 v5, 15.12.3 + * + * Used by: + * - ecma_builtin_json_quote step 2.c.iii + * + * @return pointer to ecma-string + * Returned value must be freed with ecma_deref_ecma_string. + */ +ecma_string_t * +ecma_builtin_helper_json_create_hex_digit_ecma_string (uint8_t value) /**< value in decimal*/ +{ + /* 2.c.iii */ + ecma_string_t *hex_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + + MEM_DEFINE_LOCAL_ARRAY (hex_buff, 4, lit_utf8_byte_t); + + for (uint32_t i = 0; i < 4; i++) + { + uint8_t remainder = value % 16; + lit_utf8_byte_t ch = ' '; + + if (remainder < 10) + { + ch = (lit_utf8_byte_t) (LIT_CHAR_0 + remainder); + } + else + { + uint8_t a = (uint8_t) (remainder - 10); + ch = (lit_utf8_byte_t) (LIT_CHAR_LOWERCASE_A + a); + } + + hex_buff[3 - i] = ch; + + value = value / 16; + } + + ecma_deref_ecma_string (hex_str_p); + hex_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) hex_buff, 4); + + MEM_FINALIZE_LOCAL_ARRAY (hex_buff); + + JERRY_ASSERT (ecma_string_get_length (hex_str_p)); + + return hex_str_p; +} /* ecma_builtin_helper_json_create_hex_digit_ecma_string */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index ba8423762b..54155e05ad 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -64,6 +64,45 @@ extern ecma_number_t ecma_date_make_date (ecma_number_t day, ecma_number_t time) extern ecma_number_t ecma_date_time_clip (ecma_number_t time); #endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_DATE_BUILTIN */ +typedef struct +{ + void **block_start_p; + void **block_end_p; + void **current_p; +} list_ctx_t; + +typedef struct +{ + list_ctx_t property_list; + list_ctx_t occurence_stack; + + ecma_string_t *indent_str_p; + ecma_string_t *gap_str_p; + ecma_object_t *replacer_function_p; +} stringify_context_t; + +/* ecma-builtin-helper-json.cpp */ +extern void **realloc_list (list_ctx_t *ctx); +extern void list_append (list_ctx_t *ctx, void *element); +extern void free_list (list_ctx_t *ctx); +extern bool list_has_element (list_ctx_t *ctx, void *element); +extern bool list_has_ecma_string_element (list_ctx_t *ctx, ecma_string_t *string_p); +extern bool list_is_empty (list_ctx_t *ctx); +extern void list_remove_last_element (list_ctx_t *ctx); +extern void free_list_with_ecma_string_content (list_ctx_t *ctx); + +extern ecma_string_t *ecma_builtin_helper_json_create_separated_properties (list_ctx_t *partial_p, + ecma_string_t *separator_p); +extern ecma_completion_value_t ecma_builtin_helper_json_create_formatted_json (ecma_string_t *left_bracket_p, + ecma_string_t *right_bracket_p, + ecma_string_t *stepback_p, + list_ctx_t *partial_p, + stringify_context_t *context_p); +extern ecma_completion_value_t ecma_builtin_helper_json_create_non_formatted_json (ecma_string_t *left_bracket_p, + ecma_string_t *right_bracket_p, + list_ctx_t *partial_p); +extern ecma_string_t *ecma_builtin_helper_json_create_hex_digit_ecma_string (uint8_t value); + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-json.cpp new file mode 100644 index 0000000000..d34717d759 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.cpp @@ -0,0 +1,1799 @@ +/* Copyright 2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecma-alloc.h" +#include "ecma-builtins.h" +#include "ecma-conversion.h" +#include "ecma-exceptions.h" +#include "ecma-function-object.h" +#include "ecma-gc.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#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" + +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-json.inc.h" +#define BUILTIN_UNDERSCORED_ID json +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup json ECMA JSON object built-in + * @{ + */ + +/** + * JSON token type + */ +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 */ +} ecma_json_token_type_t; + +/** + * JSON token + */ +typedef struct +{ + ecma_json_token_type_t type; /**< type of the current token */ + lit_utf8_byte_t *current_p; /**< current position of the string processed by the parser */ + lit_utf8_byte_t *end_p; /**< end of the string processed by the parser */ + union + { + struct + { + lit_utf8_byte_t *start_p; /**< when type is string_token, it contains the start of the string */ + lit_utf8_size_t size; /**< when type is string_token, it contains the size of the string */ + } string; + ecma_number_t number; /**< when type is number_token, it contains the value of the number */ + } 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 (lit_utf8_byte_t *string_p, /**< start position */ + const char *id_p) /**< string identifier */ +{ + /* + * String comparison must not depend on lit_utf8_byte_t definition. + */ + JERRY_ASSERT (*string_p == *id_p); + + do + { + string_p++; + id_p++; + if (*id_p == '\0') + { + return !isalpha (*string_p) && !isdigit (*string_p) && *string_p != '$' && *string_p != '_'; + } + } + while (*string_p == *id_p); + + return false; +} /* ecma_builtin_json_check_id */ + +/** + * Parse and extract string token. + */ +static void +ecma_builtin_json_parse_string (ecma_json_token_t *token_p) /**< token argument */ +{ + lit_utf8_byte_t *current_p = token_p->current_p; + lit_utf8_byte_t *write_p = current_p; + + token_p->u.string.start_p = current_p; + + while (*current_p != '"') + { + if (*current_p <= 0x1f) + { + return; + } + if (*current_p == '\\') + { + current_p++; + switch (*current_p) + { + case '"': + case '/': + case '\\': + { + break; + } + case 'f': + { + *current_p = '\f'; + break; + } + case 'n': + { + *current_p = '\n'; + break; + } + case 'r': + { + *current_p = '\r'; + break; + } + case 't': + { + *current_p = '\t'; + break; + } + default: + { + return; + } + } + } + *write_p++ = *current_p++; + } + + token_p->u.string.size = (lit_utf8_size_t) (write_p - token_p->u.string.start_p); + token_p->current_p = current_p + 1; + token_p->type = string_token; +} /* ecma_builtin_json_parse_string */ + +/** + * Parse and extract string token. + */ +static void +ecma_builtin_json_parse_number (ecma_json_token_t *token_p) /**< token argument */ +{ + lit_utf8_byte_t *current_p = token_p->current_p; + lit_utf8_byte_t *start_p = current_p; + + if (*current_p == '-') + { + current_p++; + } + + if (*current_p == '0') + { + current_p++; + if (isdigit (*current_p)) + { + return; + } + } + else if (isdigit (*current_p)) + { + do + { + current_p++; + } + while (isdigit (*current_p)); + } + + if (*current_p == '.') + { + current_p++; + if (!isdigit (*current_p)) + { + return; + } + + do + { + current_p++; + } + while (isdigit (*current_p)); + } + + if (*current_p == 'e' || *current_p == 'E') + { + current_p++; + if (*current_p == '+' || *current_p == '-') + { + current_p++; + } + + if (!isdigit (*current_p)) + { + return; + } + + do + { + current_p++; + } + while (isdigit (*current_p)); + } + token_p->type = number_token; + 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; +} /* ecma_builtin_json_parse_number */ + +/** + * Parse next token. + * + * The function fills the fields of the ecma_json_token_t + * argument and advances the string pointer. + */ +static void +ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p) /**< token argument */ +{ + lit_utf8_byte_t *current_p = token_p->current_p; + token_p->type = invalid_token; + + /* + * No need for end check since the string is zero terminated. + */ + while (*current_p == ' ' || *current_p == '\r' + || *current_p == '\n' || *current_p == '\t') + { + current_p++; + } + + if (current_p == token_p->end_p) + { + token_p->type = end_token; + return; + } + + switch (*current_p) + { + case '{': + { + token_p->type = left_brace_token; + token_p->current_p = current_p + 1; + return; + } + case '}': + { + token_p->type = right_brace_token; + token_p->current_p = current_p + 1; + return; + } + case '[': + { + token_p->type = left_square_token; + token_p->current_p = current_p + 1; + return; + } + case ',': + { + token_p->type = comma_token; + token_p->current_p = current_p + 1; + return; + } + case ':': + { + token_p->type = colon_token; + token_p->current_p = current_p + 1; + return; + } + case '"': + { + token_p->current_p = current_p + 1; + ecma_builtin_json_parse_string (token_p); + return; + } + case 'n': + { + if (ecma_builtin_json_check_id (current_p, "null")) + { + token_p->type = null_token; + token_p->current_p = current_p + 4; + return; + } + break; + } + case 't': + { + if (ecma_builtin_json_check_id (current_p, "true")) + { + token_p->type = true_token; + token_p->current_p = current_p + 4; + return; + } + break; + } + case 'f': + { + if (ecma_builtin_json_check_id (current_p, "false")) + { + token_p->type = false_token; + token_p->current_p = current_p + 5; + return; + } + break; + } + default: + { + if (*current_p == '-' || isdigit (*current_p)) + { + token_p->current_p = current_p; + ecma_builtin_json_parse_number (token_p); + return; + } + break; + } + } +} /* 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 */ +{ + lit_utf8_byte_t *current_p = token_p->current_p; + + /* + * No need for end check since the string is zero terminated. + */ + while (*current_p == ' ' || *current_p == '\r' + || *current_p == '\n' || *current_p == '\t') + { + current_p++; + } + + token_p->current_p = current_p; + + if (*current_p == ']') + { + token_p->current_p = current_p + 1; + return true; + } + return false; +} /* ecma_builtin_json_check_right_square_token */ + +/** + * Utility for defining properties. + * + * It silently ignores all errors. + */ +static void +ecma_builtin_json_define_value_property (ecma_object_t *obj_p, /**< this object */ + ecma_string_t *property_name_p, /**< property name */ + ecma_value_t value) /**< value */ +{ + ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor (); + { + prop_desc.is_value_defined = true; + prop_desc.value = value; + + prop_desc.is_writable_defined = true; + prop_desc.is_writable = true; + + prop_desc.is_enumerable_defined = true; + prop_desc.is_enumerable = true; + + prop_desc.is_configurable_defined = true; + prop_desc.is_configurable = true; + } + + ecma_completion_value_t completion_value = ecma_op_object_define_own_property (obj_p, + property_name_p, + &prop_desc, + false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (completion_value) + || ecma_is_completion_value_normal_false (completion_value)); +} /* ecma_builtin_json_define_value_property */ + +/** + * Parse next value. + * + * The function fills the fields of the ecma_json_token_t + * argument and advances the string pointer. + * + * @return ecma_value with the property value + */ +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); + + switch (token_p->type) + { + case number_token: + { + ecma_number_t *number_p = ecma_alloc_number (); + *number_p = token_p->u.number; + return ecma_make_number_value (number_p); + } + case string_token: + { + ecma_string_t *string_p = ecma_new_ecma_string_from_utf8 (token_p->u.string.start_p, token_p->u.string.size); + return ecma_make_string_value (string_p); + } + case null_token: + { + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_NULL); + } + case true_token: + { + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_TRUE); + } + case false_token: + { + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_FALSE); + } + case left_brace_token: + { + bool parse_comma = false; + ecma_object_t *object_p = ecma_op_create_object_object_noarg (); + + while (true) + { + ecma_builtin_json_parse_next_token (token_p); + + 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); + } + + if (token_p->type != string_token) + { + break; + } + + lit_utf8_byte_t *string_start_p = token_p->u.string.start_p; + lit_utf8_size_t string_size = token_p->u.string.size; + ecma_builtin_json_parse_next_token (token_p); + + if (token_p->type != colon_token) + { + break; + } + + ecma_value_t value = ecma_builtin_json_parse_value (token_p); + + if (ecma_is_value_undefined (value)) + { + break; + } + + ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (string_start_p, string_size); + ecma_builtin_json_define_value_property (object_p, name_p, value); + ecma_deref_ecma_string (name_p); + ecma_free_value (value, true); + parse_comma = true; + } + + /* + * Parse error occured. + */ + ecma_deref_object (object_p); + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + } + case left_square_token: + { + bool parse_comma = false; + uint32_t length = 0; + +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ARRAY_BUILTIN + ecma_object_t *array_prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAY_PROTOTYPE); +#else /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ARRAY_BUILTIN */ + ecma_object_t *array_prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); +#endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ARRAY_BUILTIN */ + + ecma_object_t *array_p = ecma_create_object (array_prototype_p, true, ECMA_OBJECT_TYPE_ARRAY); + ecma_deref_object (array_prototype_p); + + ecma_property_t *class_prop_p = ecma_create_internal_property (array_p, ECMA_INTERNAL_PROPERTY_CLASS); + class_prop_p->u.internal_property.value = LIT_MAGIC_STRING_ARRAY_UL; + + while (true) + { + if (ecma_builtin_json_check_right_square_token (token_p)) + { + ecma_string_t *length_magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH); + ecma_number_t *length_num_p = ecma_alloc_number (); + *length_num_p = ecma_uint32_to_number (length); + + ecma_property_t *length_prop_p = ecma_create_named_data_property (array_p, + length_magic_string_p, + true, + false, + false); + ecma_set_named_data_property_value (length_prop_p, ecma_make_number_value (length_num_p)); + ecma_deref_ecma_string (length_magic_string_p); + + return ecma_make_object_value (array_p); + } + + if (parse_comma) + { + ecma_builtin_json_parse_next_token (token_p); + if (token_p->type != comma_token) + { + break; + } + } + + ecma_value_t value = ecma_builtin_json_parse_value (token_p); + + if (ecma_is_value_undefined (value)) + { + break; + } + + ecma_string_t *name_p = ecma_new_ecma_string_from_uint32 (length); + ecma_property_t *property_p = ecma_create_named_data_property (array_p, name_p, + true, true, true); + ecma_deref_ecma_string (name_p); + + ecma_named_data_property_assign_value (array_p, property_p, value); + ecma_free_value (value, true); + length++; + parse_comma = true; + } + + /* + * Parse error occured. + */ + ecma_deref_object (array_p); + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + } + default: + { + return ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + } + } +} /* ecma_builtin_json_parse_value */ + +/** + * Abstract operation Walk defined in 15.12.2 + * + * See also: + * ECMA-262 v5, 15.12.2 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_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 */ +{ + JERRY_ASSERT (reviver_p); + JERRY_ASSERT (holder_p); + JERRY_ASSERT (name_p); + + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + ECMA_TRY_CATCH (value_get, + ecma_op_object_get (holder_p, name_p), + ret_value); + + if (ecma_is_value_object (value_get)) + { + ecma_object_t *object_p = ecma_get_object_from_value (value_get); + + uint32_t no_properties = 0; + + /* + * The following algorithm works with arrays and objects as well. + */ + for (ecma_property_t *property_p = ecma_get_property_list (object_p); + property_p != NULL; + property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p)) + { + /* + * All properties must be named data or internal properties, since we constructed them. + */ + JERRY_ASSERT (property_p->type == ECMA_PROPERTY_NAMEDDATA + || property_p->type == ECMA_PROPERTY_INTERNAL); + + if (property_p->type == ECMA_PROPERTY_NAMEDDATA + && ecma_is_property_enumerable (property_p)) + { + no_properties++; + } + } + + if (no_properties > 0) + { + MEM_DEFINE_LOCAL_ARRAY (property_names_p, no_properties, ecma_string_t *); + + uint32_t property_index = no_properties; + for (ecma_property_t *property_p = ecma_get_property_list (object_p); + property_p != NULL; + property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p)) + { + if (property_p->type == ECMA_PROPERTY_NAMEDDATA + && ecma_is_property_enumerable (property_p)) + { + ecma_string_t *property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_p->u.named_data_property.name_p); + /* + * The property list must be in creation order. + */ + property_index--; + property_names_p[property_index] = ecma_copy_or_ref_ecma_string (property_name_p); + } + } + + JERRY_ASSERT (property_index == 0); + + for (property_index = 0; + property_index < no_properties && ecma_is_completion_value_empty (ret_value); + property_index++) + { + ECMA_TRY_CATCH (value_walk, + ecma_builtin_json_walk (reviver_p, + object_p, + property_names_p[property_index]), + ret_value); + + /* + * We cannot optimize this function since any members + * can be changed (deleted) by the reviver function. + */ + if (ecma_is_value_undefined (value_walk)) + { + ecma_completion_value_t delete_val = ecma_op_general_object_delete (object_p, + property_names_p[property_index], + false); + JERRY_ASSERT (ecma_is_completion_value_normal_true (delete_val) + || ecma_is_completion_value_normal_false (delete_val)); + } + else + { + ecma_builtin_json_define_value_property (object_p, + property_names_p[property_index], + value_walk); + } + + ECMA_FINALIZE (value_walk); + } + + for (uint32_t i = 0; i < no_properties; i++) + { + ecma_deref_ecma_string (property_names_p[i]); + } + + MEM_FINALIZE_LOCAL_ARRAY (property_names_p); + } + } + + if (ecma_is_completion_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_completion_value_throw (ret_value)); + } + + ECMA_FINALIZE (value_get); + + return ret_value; +} /* ecma_builtin_json_walk */ + +/** + * The JSON object's 'parse' routine + * + * See also: + * ECMA-262 v5, 15.12.2 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_json_parse (ecma_value_t this_arg __attr_unused___, /**< 'this' argument */ + ecma_value_t arg1, /**< string argument */ + ecma_value_t arg2) /**< reviver argument */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + ECMA_TRY_CATCH (string, + ecma_op_to_string (arg1), + ret_value); + + ecma_string_t *string_p = ecma_get_string_from_value (string); + ecma_length_t length = (uint32_t) ecma_string_get_length (string_p); + size_t buffer_size = sizeof (lit_utf8_byte_t) * (length + 1); + + MEM_DEFINE_LOCAL_ARRAY (str_start_p, buffer_size, lit_utf8_byte_t); + + ecma_string_to_utf8_string (string_p, str_start_p, (ssize_t) buffer_size); + str_start_p[length] = LIT_BYTE_NULL; + + ecma_json_token_t token; + token.current_p = str_start_p; + token.end_p = str_start_p + length; + + ecma_value_t final_result = ecma_builtin_json_parse_value (&token); + + if (!ecma_is_value_undefined (final_result)) + { + ecma_builtin_json_parse_next_token (&token); + + if (token.type != end_token) + { + ecma_free_value (final_result, true); + final_result = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + } + } + + if (ecma_is_value_undefined (final_result)) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_SYNTAX)); + } + else + { + if (ecma_op_is_callable (arg2)) + { + ecma_object_t *object_p = ecma_op_create_object_object_noarg (); + ecma_string_t *name_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + ecma_property_t *property_p = ecma_create_named_data_property (object_p, + name_p, + true, + true, + true); + + ecma_named_data_property_assign_value (object_p, property_p, final_result); + ecma_free_value (final_result, true); + + ret_value = ecma_builtin_json_walk (ecma_get_object_from_value (arg2), + object_p, + name_p); + ecma_deref_object (object_p); + ecma_deref_ecma_string (name_p); + } + else + { + ret_value = ecma_make_normal_completion_value (final_result); + } + } + + MEM_FINALIZE_LOCAL_ARRAY (str_start_p); + + ECMA_FINALIZE (string); + return ret_value; +} /* ecma_builtin_json_parse */ + +static ecma_completion_value_t +ecma_builtin_json_str (ecma_string_t *key_p, ecma_object_t *holder_p, stringify_context_t *context_p); + +static ecma_completion_value_t +ecma_builtin_json_object (ecma_object_t *obj_p, stringify_context_t *context_p); + +static ecma_completion_value_t +ecma_builtin_json_array (ecma_object_t *obj_p, stringify_context_t *context_p); + +/** + * The JSON object's 'stringify' routine + * + * See also: + * ECMA-262 v5, 15.12.3 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_json_stringify (ecma_value_t this_arg __attr_unused___, /**< 'this' argument */ + ecma_value_t arg1, /**< value */ + ecma_value_t arg2, /**< replacer */ + ecma_value_t arg3) /**< space */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + stringify_context_t context_p; + + /* 1. */ + context_p.occurence_stack.block_start_p = NULL; + context_p.occurence_stack.block_end_p = NULL; + context_p.occurence_stack.current_p = NULL; + + /* 2. */ + context_p.indent_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + + /* 3. */ + context_p.property_list.block_start_p = NULL; + context_p.property_list.block_end_p = NULL; + context_p.property_list.current_p = NULL; + + context_p.replacer_function_p = NULL; + + /* 4. */ + if (ecma_is_value_object (arg2)) + { + ecma_object_t *obj_p = ecma_get_object_from_value (arg2); + + /* 4.a */ + if (ecma_op_is_callable (arg2)) + { + context_p.replacer_function_p = obj_p; + } + /* 4.b */ + else if (ecma_object_get_class_name (obj_p) == LIT_MAGIC_STRING_ARRAY_UL) + { + ecma_string_t *length_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH); + + ECMA_TRY_CATCH (array_length, + ecma_op_object_get (obj_p, length_str_p), + ret_value); + + ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num, + array_length, + ret_value); + + uint32_t array_length = ecma_number_to_uint32 (array_length_num); + uint32_t index = 0; + + /* 4.b.ii */ + while ((index < array_length) && ecma_is_completion_value_empty (ret_value)) + { + ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index); + + ECMA_TRY_CATCH (value, + ecma_op_object_get (obj_p, index_str_p), + ret_value); + + /* 4.b.ii.1 */ + ecma_value_t item = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED); + + /* 4.b.ii.2 */ + if (ecma_is_value_string (value)) + { + item = ecma_copy_value (value, true); + } + /* 4.b.ii.3 */ + 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, true); + + ECMA_FINALIZE (str_val); + } + /* 4.b.ii.4 */ + else if (ecma_is_value_object (value)) + { + 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); + + /* 4.b.ii.4.a */ + if (class_name == LIT_MAGIC_STRING_NUMBER_UL + || class_name == LIT_MAGIC_STRING_STRING_UL) + { + ECMA_TRY_CATCH (val, + ecma_op_to_string (value), + ret_value); + + item = ecma_copy_value (val, true); + + ECMA_FINALIZE (val); + } + } + + /* 4.b.ii.5 */ + if (!ecma_is_value_undefined (item)) + { + ecma_string_t *item_str_p = ecma_get_string_from_value (item); + + if (!list_has_ecma_string_element (&context_p.property_list, item_str_p)) + { + list_append (&context_p.property_list, item_str_p); + } + else + { + ecma_deref_ecma_string (item_str_p); + } + } + + ECMA_FINALIZE (value); + + ecma_deref_ecma_string (index_str_p); + + index++; + } + + ECMA_OP_TO_NUMBER_FINALIZE (array_length_num); + ECMA_FINALIZE (array_length); + + ecma_deref_ecma_string (length_str_p); + } + } + + ecma_value_t space = ecma_copy_value (arg3, true); + + /* 5. */ + if (ecma_is_value_object (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.a */ + if (class_name == LIT_MAGIC_STRING_NUMBER_UL) + { + ECMA_TRY_CATCH (val, + ecma_op_to_number (arg3), + ret_value); + + ecma_free_value (space, true); + space = ecma_copy_value (val, true); + + ECMA_FINALIZE (val); + } + /* 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, true); + space = ecma_copy_value (val, true); + + ECMA_FINALIZE (val); + } + } + + /* 6. */ + if (ecma_is_value_number (space)) + { + ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num, + arg3, + ret_value); + + /* 6.a */ + uint32_t num_of_spaces = ecma_number_to_uint32 (array_length_num); + uint32_t space = (num_of_spaces > 10) ? 10 : num_of_spaces; + + /* 6.b */ + if (space < 1) + { + context_p.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + } + else + { + MEM_DEFINE_LOCAL_ARRAY (space_buff, space, char); + + for (uint32_t i = 0; i < space; i++) + { + space_buff[i] = ' '; + } + + context_p.gap_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) space_buff, space); + + MEM_FINALIZE_LOCAL_ARRAY (space_buff); + } + + ECMA_OP_TO_NUMBER_FINALIZE (array_length_num); + } + /* 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) + { + context_p.gap_str_p = ecma_copy_or_ref_ecma_string (space_str_p); + } + else + { + ecma_length_t string_len = ecma_string_get_length (space_str_p); + + MEM_DEFINE_LOCAL_ARRAY (zt_string_buff, string_len, lit_utf8_byte_t); + + size_t string_buf_size = (size_t) (string_len) * sizeof (lit_utf8_byte_t); + ssize_t bytes_copied = ecma_string_to_utf8_string (space_str_p, + zt_string_buff, + (ssize_t) string_buf_size); + JERRY_ASSERT (bytes_copied > 0); + + /* Buffer for the first 10 characters. */ + MEM_DEFINE_LOCAL_ARRAY (space_buff, 10, lit_utf8_byte_t); + + for (uint32_t i = 0; i < 10; i++) + { + space_buff[i] = zt_string_buff[i]; + } + + context_p.gap_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) space_buff, 10); + + MEM_FINALIZE_LOCAL_ARRAY (space_buff); + MEM_FINALIZE_LOCAL_ARRAY (zt_string_buff); + } + } + /* 8. */ + else + { + context_p.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + } + + ecma_free_value (space, true); + + /* 9. */ + ecma_object_t *obj_wrapper_p = ecma_create_object (NULL, true, ECMA_OBJECT_TYPE_GENERAL); + ecma_string_t *empty_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY); + + /* 10. */ + ecma_completion_value_t put_comp_val = ecma_op_object_put (obj_wrapper_p, + empty_str_p, + arg1, + false); + + JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp_val)); + ecma_free_completion_value (put_comp_val); + + /* 11. */ + ECMA_TRY_CATCH (str_val, + ecma_builtin_json_str (empty_str_p, obj_wrapper_p, &context_p), + ret_value); + + ret_value = ecma_make_normal_completion_value (ecma_copy_value (str_val, true)); + + ECMA_FINALIZE (str_val); + + ecma_deref_object (obj_wrapper_p); + ecma_deref_ecma_string (empty_str_p); + + ecma_deref_ecma_string (context_p.indent_str_p); + ecma_deref_ecma_string (context_p.gap_str_p); + + free_list_with_ecma_string_content (&context_p.property_list); + free_list (&context_p.occurence_stack); + + return ret_value; +} /* ecma_builtin_json_stringify */ + +/** + * Abstract operation 'Quote' defined in 15.12.3 + * + * See also: + * ECMA-262 v5, 15.12.3 + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_json_quote (ecma_string_t *string_p) /**< string that should be quoted*/ +{ + /* 1. */ + ecma_string_t *quote_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_DOUBLE_QUOTE_CHAR); + ecma_string_t *product_str_p = ecma_copy_or_ref_ecma_string (quote_str_p); + ecma_string_t *tmp_str_p; + + ecma_length_t string_len = ecma_string_get_length (string_p); + + MEM_DEFINE_LOCAL_ARRAY (zt_string_buff, string_len, lit_utf8_byte_t); + + size_t string_buf_size = (size_t) (string_len) * sizeof (lit_utf8_byte_t); + ssize_t bytes_copied = ecma_string_to_utf8_string (string_p, + zt_string_buff, + (ssize_t) string_buf_size); + JERRY_ASSERT (bytes_copied > 0); + + /* 2. */ + for (ecma_length_t i = 0; i < string_len; i++) + { + lit_utf8_byte_t c = zt_string_buff[i]; + + /* 2.a */ + if (c == '\\' || c == '\"') + { + ecma_string_t *backslash_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_BACKSLASH_CHAR); + + /* 2.a.i */ + tmp_str_p = ecma_concat_ecma_strings (product_str_p, backslash_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (backslash_str_p); + product_str_p = tmp_str_p; + + /* 2.a.ii */ + ecma_string_t *c_str_p = ecma_new_ecma_string_from_utf8 (&c, 1); + + tmp_str_p = ecma_concat_ecma_strings (product_str_p, c_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (c_str_p); + product_str_p = tmp_str_p; + } + /* 2.b */ + else if (c == '\b' || c == '\f' || c == '\n' || c == '\r' || c == '\t') + { + ecma_string_t *backslash_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_BACKSLASH_CHAR); + + /* 2.b.i */ + tmp_str_p = ecma_concat_ecma_strings (product_str_p, backslash_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (backslash_str_p); + product_str_p = tmp_str_p; + + /* 2.b.ii */ + lit_utf8_byte_t abbrev = ' '; + + switch (c) + { + case '\b': + { + abbrev = 'b'; + break; + } + case '\f': + { + abbrev = 'f'; + break; + } + case '\n': + { + abbrev = 'n'; + break; + } + case '\r': + { + abbrev = 'r'; + break; + } + case '\t': + { + abbrev = 't'; + break; + } + } + + /* 2.b.iii */ + ecma_string_t *abbrev_str_p = ecma_new_ecma_string_from_utf8 (&abbrev, 1); + + tmp_str_p = ecma_concat_ecma_strings (product_str_p, abbrev_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (abbrev_str_p); + product_str_p = tmp_str_p; + } + /* 2.c */ + else if (c < LIT_CHAR_SP) + { + ecma_string_t *backslash_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_BACKSLASH_CHAR); + + /* 2.c.i */ + tmp_str_p = ecma_concat_ecma_strings (product_str_p, backslash_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (backslash_str_p); + product_str_p = tmp_str_p; + + /* 2.c.ii */ + lit_utf8_byte_t u_ch = 'u'; + ecma_string_t *u_ch_str_p = ecma_new_ecma_string_from_utf8 (&u_ch, 1); + + tmp_str_p = ecma_concat_ecma_strings (product_str_p, u_ch_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (u_ch_str_p); + product_str_p = tmp_str_p; + + /* 2.c.iii */ + ecma_string_t *hex_str_p = ecma_builtin_helper_json_create_hex_digit_ecma_string (c); + + /* 2.c.iv */ + tmp_str_p = ecma_concat_ecma_strings (product_str_p, hex_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (hex_str_p); + product_str_p = tmp_str_p; + } + /* 2.d */ + else + { + ecma_string_t *c_str_p = ecma_new_ecma_string_from_utf8 (&c, 1); + + tmp_str_p = ecma_concat_ecma_strings (product_str_p, c_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (c_str_p); + product_str_p = tmp_str_p; + } + } + + MEM_FINALIZE_LOCAL_ARRAY (zt_string_buff); + + /* 3. */ + tmp_str_p = ecma_concat_ecma_strings (product_str_p, quote_str_p); + ecma_deref_ecma_string (product_str_p); + ecma_deref_ecma_string (quote_str_p); + product_str_p = tmp_str_p; + + /* 4. */ + return ecma_make_normal_completion_value (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 completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_json_str (ecma_string_t *key_p, /**< property key*/ + ecma_object_t *holder_p, /**< the object*/ + stringify_context_t *context_p) /**< context*/ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* 1. */ + ECMA_TRY_CATCH (value, + ecma_op_object_get (holder_p, key_p), + ret_value); + + ecma_value_t my_val = ecma_copy_value (value, true); + + /* 2. */ + if (ecma_is_value_object (my_val)) + { + ecma_object_t *value_obj_p = ecma_get_object_from_value (my_val); + ecma_string_t *to_json_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_TO_JSON_UL); + + /* 2.a */ + ECMA_TRY_CATCH (toJSON, + ecma_op_object_get (value_obj_p, to_json_str_p), + 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, true); + my_val = ecma_copy_value (func_ret_val, true); + + ECMA_FINALIZE (func_ret_val); + } + + ECMA_FINALIZE (toJSON); + + ecma_deref_ecma_string (to_json_str_p); + } + + /* 3. */ + if (context_p->replacer_function_p && ecma_is_completion_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, true); + my_val = ecma_copy_value (func_ret_val, true); + + ECMA_FINALIZE (func_ret_val); + } + + /* 4. */ + if (ecma_is_value_object (my_val) && ecma_is_completion_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, true); + my_val = ecma_copy_value (val, true); + + 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, true); + my_val = ecma_copy_value (val, true); + + 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, true); + my_val = ecma_copy_value (val, true); + + ECMA_FINALIZE (val); + } + } + + if (ecma_is_completion_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_completion_value_normal (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_infinity (num_value_p)) + { + ret_value = ecma_op_to_string (my_val); + JERRY_ASSERT (ecma_is_completion_value_normal (ret_value)); + } + else + { + /* 9.b */ + ecma_string_t *null_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_NULL); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (null_str_p)); + } + } + /* 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_make_normal_completion_value (ecma_copy_value (val, true)); + + ECMA_FINALIZE (val); + } + /* 10.b */ + else + { + ECMA_TRY_CATCH (val, + ecma_builtin_json_object (obj_p, context_p), + ret_value); + + ret_value = ecma_make_normal_completion_value (ecma_copy_value (val, true)); + + ECMA_FINALIZE (val); + } + } + else + { + /* 11. */ + ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_UNDEFINED); + } + } + + ecma_free_value (my_val, true); + 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 completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_json_object (ecma_object_t *obj_p, /**< the object*/ + stringify_context_t *context_p) /**< context*/ +{ + /* 1. */ + if (list_has_element (&context_p->occurence_stack, obj_p)) + { + return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* 2. */ + list_append (&context_p->occurence_stack, obj_p); + + /* 3. */ + ecma_string_t *stepback_p = context_p->indent_str_p; + + /* 4. */ + context_p->indent_str_p = ecma_concat_ecma_strings (context_p->indent_str_p, context_p->gap_str_p); + + list_ctx_t property_keys; + { + property_keys.block_start_p = NULL; + property_keys.block_end_p = NULL; + property_keys.current_p = NULL; + } + + /* 5. */ + if (!list_is_empty (&context_p->property_list)) + { + property_keys = context_p->property_list; + } + /* 6. */ + else + { + for (ecma_property_t *property_p = ecma_get_property_list (obj_p); + property_p != NULL; + property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p)) + { + if (property_p->type == ECMA_PROPERTY_NAMEDDATA && ecma_is_property_enumerable (property_p)) + { + ecma_string_t *prop_key = ECMA_GET_NON_NULL_POINTER (ecma_string_t, + property_p->u.named_data_property.name_p); + + JERRY_ASSERT (prop_key != NULL); + + list_append (&property_keys, ecma_copy_or_ref_ecma_string (prop_key)); + } + } + } + + /* 7. */ + list_ctx_t partial; + { + partial.block_start_p = NULL; + partial.block_end_p = NULL; + partial.current_p = NULL; + } + + /* 8. */ + for (void **key_p = property_keys.block_start_p; + key_p < property_keys.current_p && ecma_is_completion_value_empty (ret_value); + key_p++) + { + /* 8.a */ + ECMA_TRY_CATCH (str_val, + ecma_builtin_json_str ((ecma_string_t *) *key_p, obj_p, context_p), + ret_value); + + /* 8.b */ + if (!ecma_is_value_undefined (str_val)) + { + ecma_string_t *colon_p = ecma_get_magic_string (LIT_MAGIC_STRING_COLON_CHAR); + ecma_string_t *value_str_p = ecma_get_string_from_value (str_val); + ecma_string_t *tmp_str_p; + + /* 8.b.i */ + ecma_completion_value_t str_comp_val = ecma_builtin_json_quote ((ecma_string_t *) *key_p); + JERRY_ASSERT (ecma_is_completion_value_normal (str_comp_val)); + + ecma_string_t *member_str_p = ecma_get_string_from_value (str_comp_val); + + /* 8.b.ii */ + tmp_str_p = ecma_concat_ecma_strings (member_str_p, colon_p); + ecma_free_completion_value (str_comp_val); + ecma_deref_ecma_string (colon_p); + member_str_p = tmp_str_p; + + /* 8.b.iii */ + bool is_gap_empty = (ecma_string_get_length (context_p->gap_str_p) == 0); + + if (!is_gap_empty) + { + ecma_string_t *space_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_SPACE_CHAR); + + tmp_str_p = ecma_concat_ecma_strings (member_str_p, space_str_p); + ecma_deref_ecma_string (member_str_p); + ecma_deref_ecma_string (space_str_p); + member_str_p = tmp_str_p; + } + + /* 8.b.iv */ + tmp_str_p = ecma_concat_ecma_strings (member_str_p, value_str_p); + ecma_deref_ecma_string (member_str_p); + member_str_p = tmp_str_p; + + /* 8.b.v */ + list_append (&partial, member_str_p); + } + + ECMA_FINALIZE (str_val); + } + + if (list_is_empty (&context_p->property_list)) + { + free_list_with_ecma_string_content (&property_keys); + } + + if (!ecma_is_completion_value_empty (ret_value)) + { + free_list_with_ecma_string_content (&partial); + ecma_deref_ecma_string (stepback_p); + return ret_value; + } + + /* 9. */ + if (list_is_empty (&partial)) + { + ecma_string_t *left_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_BRACE_CHAR); + ecma_string_t *right_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR); + + ecma_string_t *final_str_p = ecma_concat_ecma_strings (left_brace_str_p, right_brace_str_p); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (final_str_p)); + + ecma_deref_ecma_string (left_brace_str_p); + ecma_deref_ecma_string (right_brace_str_p); + } + /* 10. */ + else + { + bool is_gap_empty = (ecma_string_get_length (context_p->gap_str_p) == 0); + + /* 10.a */ + if (is_gap_empty) + { + ecma_string_t *left_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_BRACE_CHAR); + ecma_string_t *right_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR); + + ret_value = ecma_builtin_helper_json_create_non_formatted_json (left_brace_str_p, + right_brace_str_p, + &partial); + + ecma_deref_ecma_string (left_brace_str_p); + ecma_deref_ecma_string (right_brace_str_p); + } + /* 10.b */ + else + { + ecma_string_t *left_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_BRACE_CHAR); + ecma_string_t *right_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR); + + ret_value = ecma_builtin_helper_json_create_formatted_json (left_brace_str_p, + right_brace_str_p, + stepback_p, + &partial, + context_p); + + ecma_deref_ecma_string (left_brace_str_p); + ecma_deref_ecma_string (right_brace_str_p); + } + } + + free_list_with_ecma_string_content (&partial); + + /* 11. */ + list_remove_last_element (&context_p->occurence_stack); + + /* 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 completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_json_array (ecma_object_t *obj_p, /**< the array object*/ + stringify_context_t *context_p) /**< context*/ +{ + /* 1. */ + if (list_has_element (&context_p->occurence_stack, obj_p)) + { + return ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE)); + } + + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* 2. */ + list_append (&context_p->occurence_stack, obj_p); + + /* 3. */ + ecma_string_t *stepback_p = context_p->indent_str_p; + + /* 4. */ + context_p->indent_str_p = ecma_concat_ecma_strings (context_p->indent_str_p, context_p->gap_str_p); + + /* 5. */ + list_ctx_t partial; + { + partial.block_start_p = NULL; + partial.block_end_p = NULL; + partial.current_p = NULL; + } + + ecma_string_t *length_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH); + + /* 6. */ + ECMA_TRY_CATCH (array_length, + ecma_op_object_get (obj_p, length_str_p), + ret_value); + + ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num, + array_length, + ret_value); + + uint32_t array_length = ecma_number_to_uint32 (array_length_num); + + /* 7. - 8. */ + for (uint32_t index = 0; + index < array_length && ecma_is_completion_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)) + { + list_append (&partial, ecma_get_magic_string (LIT_MAGIC_STRING_NULL)); + } + /* 8.c */ + else + { + ecma_string_t *str_val_p = ecma_get_string_from_value (str_val); + list_append (&partial, ecma_copy_or_ref_ecma_string (str_val_p)); + } + + ECMA_FINALIZE (str_val); + ecma_deref_ecma_string (index_str_p); + } + + if (ecma_is_completion_value_empty (ret_value)) + { + /* 9. */ + if (list_is_empty (&partial)) + { + ecma_string_t *left_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR); + ecma_string_t *right_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR); + + ecma_string_t *final_str_p = ecma_concat_ecma_strings (left_square_str_p, right_square_str_p); + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (final_str_p)); + + ecma_deref_ecma_string (left_square_str_p); + ecma_deref_ecma_string (right_square_str_p); + } + /* 10. */ + else + { + bool is_gap_empty = (ecma_string_get_length (context_p->gap_str_p) == 0); + /* 10.a */ + if (is_gap_empty) + { + ecma_string_t *left_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR); + ecma_string_t *right_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR); + + ret_value = ecma_builtin_helper_json_create_non_formatted_json (left_square_str_p, + right_square_str_p, + &partial); + + ecma_deref_ecma_string (left_square_str_p); + ecma_deref_ecma_string (right_square_str_p); + } + /* 10.b */ + else + { + ecma_string_t *left_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR); + ecma_string_t *right_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR); + + ret_value = ecma_builtin_helper_json_create_formatted_json (left_square_str_p, + right_square_str_p, + stepback_p, + &partial, + context_p); + + ecma_deref_ecma_string (left_square_str_p); + ecma_deref_ecma_string (right_square_str_p); + } + } + } + + ECMA_OP_TO_NUMBER_FINALIZE (array_length_num); + ECMA_FINALIZE (array_length); + + ecma_deref_ecma_string (length_str_p); + free_list_with_ecma_string_content (&partial); + + /* 11. */ + list_remove_last_element (&context_p->occurence_stack); + + /* 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 */ + +/** + * @} + * @} + * @} + */ + +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h new file mode 100644 index 0000000000..8421fe46d5 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-json.inc.h @@ -0,0 +1,55 @@ +/* Copyright 2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * JSON built-in description + */ + +#ifndef OBJECT_ID +# define OBJECT_ID(builtin_object_id) +#endif /* !OBJECT_ID */ + +#ifndef SIMPLE_VALUE +# define SIMPLE_VALUE(name, simple_value, prop_writable, prop_enumerable, prop_configurable) +#endif /* !SIMPLE_VALUE */ + +#ifndef NUMBER_VALUE +# define NUMBER_VALUE(name, number_value, prop_writable, prop_enumerable, prop_configurable) +#endif /* !NUMBER_VALUE */ + +#ifndef OBJECT_VALUE +# define OBJECT_VALUE(name, obj_getter, prop_writable, prop_enumerable, prop_configurable) +#endif /* !OBJECT_VALUE */ + +#ifndef ROUTINE +# define ROUTINE(name, c_function_name, args_number, length_prop_value) +#endif /* !ROUTINE */ + +/* Object identifier */ +OBJECT_ID (ECMA_BUILTIN_ID_JSON) + +/* Routine properties: + * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ +ROUTINE (LIT_MAGIC_STRING_PARSE, ecma_builtin_json_parse, 2, 2) +ROUTINE (LIT_MAGIC_STRING_STRINGIFY, ecma_builtin_json_stringify, 3, 3) + +#undef OBJECT_ID +#undef SIMPLE_VALUE +#undef NUMBER_VALUE +#undef STRING_VALUE +#undef OBJECT_VALUE +#undef CP_UNIMPLEMENTED_VALUE +#undef ROUTINE diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h index caef8eaa02..6074962f40 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h @@ -130,6 +130,16 @@ BUILTIN (ECMA_BUILTIN_ID_MATH, math) #endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_MATH_BUILTIN */ +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN +/* The JSON object (15.12) */ +BUILTIN (ECMA_BUILTIN_ID_JSON, + ECMA_OBJECT_TYPE_GENERAL, + ECMA_BUILTIN_ID_OBJECT_PROTOTYPE, + true, + true, + json) +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN */ + #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_DATE_BUILTIN /* The Date.prototype object (15.9.4) */ BUILTIN (ECMA_BUILTIN_ID_DATE_PROTOTYPE, diff --git a/jerry-core/ecma/operations/ecma-objects.cpp b/jerry-core/ecma/operations/ecma-objects.cpp index c711b38da3..b691fe15a0 100644 --- a/jerry-core/ecma/operations/ecma-objects.cpp +++ b/jerry-core/ecma/operations/ecma-objects.cpp @@ -683,6 +683,12 @@ ecma_object_get_class_name (ecma_object_t *obj_p) /**< object */ return LIT_MAGIC_STRING_MATH_UL; } #endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_MATH_BUILTIN */ +#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN + case ECMA_BUILTIN_ID_JSON: + { + return LIT_MAGIC_STRING_JSON_U; + } +#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_JSON_BUILTIN */ #ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_ERROR_BUILTINS case ECMA_BUILTIN_ID_ERROR_PROTOTYPE: case ECMA_BUILTIN_ID_EVAL_ERROR_PROTOTYPE: diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 5e2cbefd0a..74d773288b 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -62,6 +62,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TYPE_ERROR_UL, "TypeError") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_URI_ERROR_UL, "URIError") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MATH_UL, "Math") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_JSON_U, "JSON") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STRINGIFY, "stringify") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PARSE, "parse") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PARSE_INT, "parseInt") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PARSE_FLOAT, "parseFloat") @@ -217,12 +218,17 @@ 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") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SLASH_CHAR, "/") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_BACKSLASH_CHAR, "\\") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EMPTY_NON_CAPTURE_GROUP, "(?:)") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR, "[") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR, "]") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LEFT_BRACE_CHAR, "{") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR, "}") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MINUS_CHAR, "-") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COLON_CHAR, ":") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_COMMA_CHAR, ",") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_DOUBLE_QUOTE_CHAR, "\"") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NEW_LINE_CHAR, "\n") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPACE_CHAR, " ") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__EMPTY, "") diff --git a/tests/jerry/json-parse.js b/tests/jerry/json-parse.js new file mode 100644 index 0000000000..0c3252b76c --- /dev/null +++ b/tests/jerry/json-parse.js @@ -0,0 +1,359 @@ +// Copyright 2015 University of Szeged +// Copyright 2015 Samsung Electronics Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Checking primitve types +var str; +var result; +var log; + +function check_parse_error (str) +{ + try { + JSON.parse (str); + // Should throw a parse error. + assert (false); + } catch (e) { + } +} + +str = ' null '; +assert (JSON.parse (str) === null); +str = 'true'; +assert (JSON.parse (str) === true); +str = 'false'; +assert (JSON.parse (str) === false); +str = '-32.5e002'; +assert (JSON.parse (str) == -3250); +str = '"str"'; +assert (JSON.parse (str) == "str"); + +check_parse_error ('undefined'); +check_parse_error ('falses'); +check_parse_error ('+5'); +check_parse_error ('5.'); +check_parse_error ('01'); +check_parse_error ('0x1'); +check_parse_error ('0e-'); +check_parse_error ('3e+a'); +check_parse_error ('55e4,'); +check_parse_error ('5 true'); +check_parse_error ("'str'"); + +// Checking objects +str = ' { "x": 0, "yy": null, "zzz": { "A": 4.0, "BB": { "1": 63e-1 }, "CCC" : false } } '; +result = JSON.parse (str); +assert (typeof result == "object"); +assert (result.x === 0); +assert (result.yy === null); +assert (typeof result.zzz == "object"); +assert (result.zzz.A === 4); +assert (typeof result.zzz.BB == "object"); +assert (result.zzz.BB["1"] === 6.3); +assert (result.zzz.CCC === false); + +check_parse_error ('{'); +check_parse_error ('{{{}'); +check_parse_error ('{x:5}'); +check_parse_error ('{true:4}'); +check_parse_error ('{"x":5 "y":6}'); +check_parse_error ('{"x":5,"y":6,}'); +check_parse_error ('{"x":5,,"y":6}'); + +// Checking arrays +str = '[{"x":[]},[[]],{}, [ null ] ]'; +result = JSON.parse (str); +assert (result.length === 4); +assert (typeof result === "object"); +assert (typeof result[0] === "object"); +assert (typeof result[0].x === "object"); +assert (result[0].x.length === 0); +assert (result[1].length === 1); +assert (result[1][0].length === 0); +assert (typeof result[2] === "object"); +assert (result[3].length === 1); +assert (result[3][0] === null); + +check_parse_error ('['); +check_parse_error ('[[[]'); +check_parse_error ('[ true null ]'); +check_parse_error ('[1,,2]'); +check_parse_error ('[1,2,]'); +check_parse_error ('[1] [2]'); + +// Checking parse with different primitive types + +assert (JSON.parse (null) == null); +assert (JSON.parse (true) == true); +assert (JSON.parse (3) == 3); + +try { + JSON.parse (undefined); + // Should not be reached. + assert (false); +} catch (e) { + assert (e instanceof SyntaxError); +} + +// Checking parse with different object types + +object = { toString: function() { return false; } }; +assert (JSON.parse (object) == false); + +object = {"a": 3, "b": "foo"}; +try { + JSON.parse (object); + // Should not be reached. + assert (false); +} catch (e) { + assert (e instanceof SyntaxError); +} + +array = [3, "foo"]; +try { + JSON.parse (array); + // Should not be reached. + assert (false); +} catch (e) { + assert (e instanceof SyntaxError); +} + +assert (JSON.parse (new Number (3)) == 3); +assert (JSON.parse (new Boolean (true)) == true); + +object = new String ('{"a": 3, "b": "foo"}'); +result = JSON.parse (object); + +assert (result.a == 3); +assert (result.b == "foo"); + +// Checking reviver + +function toStringReviver(k, v) +{ + log += "<" + k + ">:" + (typeof v == "number" ? v : "(obj)") + ", "; + return v; +} + +str = '{ "a":1, "b":2, "c": { "d":4, "e": { "f":6 } } }'; +log = ""; +JSON.parse (str, toStringReviver); +assert (log === ":1, :2, :4, :6, :(obj), :(obj), <>:(obj), "); + +str = '[ 32, 47, 33 ]'; +log = ""; +JSON.parse (str, toStringReviver); +assert (log === "<0>:32, <1>:47, <2>:33, <>:(obj), "); + +// Defining properties multiple times + +str = ' { "a":1, "b":2, "a":3 } '; +log = ""; +JSON.parse (str, toStringReviver); +assert (log === ":3, :2, <>:(obj), "); + +str = ' { "a":1, "b":2, "b":3 } '; +log = ""; +JSON.parse (str, toStringReviver); +assert (log === ":1, :3, <>:(obj), "); + +str = ' { "a":1, "b":{}, "b":[], "a":2, "b":3, "c":4 } '; +log = ""; +JSON.parse (str, toStringReviver); +assert (log === ":2, :3, :4, <>:(obj), "); + +// Changing property value + +str = ' { "a":1, "b":2, "c":3 } '; +result = JSON.parse (str, function (k, v) { + if (k == "a") + { + return 8; + } + if (k == "b") + { + return 9; + } + if (k == "c") + { + return void 0; + } + return v; +}); + +assert (result.a === 8); +assert (result.b === 9); +assert (result.c === void 0); + +// Adding / deleting properties + +str = ' { "a":1, "b":2 } '; +log = ""; +result = JSON.parse (str, function (k, v) { + if (k == "a") + { + // Deleted properties must still be enumerated. + delete this["b"]; + // New properties must not be enumerated. + this.c = 4; + } + if (k != "") + { + log += "<" + k + ">:" + v + " "; + } + return v; +}); + +assert (log === ":1 :undefined "); +assert (result.a === 1); +assert (result.b === void 0); +assert (result.c === 4); + +// Changing properties to accessors + +str = ' { "a":1, "b":2, "c":3 } '; +log = ""; +JSON.parse (str, function (k, v) { + if (k == "a") + { + Object.defineProperty(this, "b", { + enumerable: true, + configurable: true, + get: function() { return 12; } + }); + Object.defineProperty(this, "c", { + enumerable: true, + configurable: true, + set: function(val) { } + }); + } + if (k != "") + { + log += "<" + k + ">:" + v + " "; + } + return v; +}); +assert (log === ":1 :12 :undefined "); + +// Forcing extra walk steps + +str = ' { "a":1, "b":2 } '; +log = ""; +JSON.parse (str, function (k, v) { + if (k == "a") + { + this.b = { x:3, y:4 }; + } + if (k != "") + { + log += "<" + k + ">:" + v + " "; + } + return v; +}); +assert (log === ":1 :3 :4 :[object Object] "); + +// Setting a property to read-only, and change its value. + +str = ' { "a":1, "b":2 } '; +result = JSON.parse (str, function (k, v) { + if (k == "a") + { + Object.defineProperty(this, "b", { + enumerable: true, + // FIXME: Should work with configurable: true. + configurable: false, + writable: false, + value: 2 + }); + return 8; + } + if (k == "b") + { + return 9; + } + return v; +}); + +assert (result.a === 8); +assert (result.b === 2); + +// Throw error in the reviver + +try { + str = ' { "a":1, "b":2 } '; + result = JSON.parse (str, function (k, v) { throw new ReferenceError("error"); } ); + assert(false); +} catch (e) { + assert (e.message === "error"); + assert (e instanceof ReferenceError); +} + +// Throw error in a getter + +try { + str = ' { "a":1, "b":2 } '; + JSON.parse (str, function (k, v) { + if (k == "a") + { + Object.defineProperty(this, "b", { + enumerable: true, + configurable: true, + get: function() { throw new ReferenceError("error"); } + }); + } + return v; + }); + assert(false); +} catch (e) { + assert (e.message === "error"); + assert (e instanceof ReferenceError); +} + +// Checking reviver with different primitive types + +str = ' { "a":1 } '; + +result = JSON.parse (str, 4); +assert (result.a == 1); + +result = JSON.parse (str, null); +assert (result.a == 1); + +result = JSON.parse (str, undefined); +assert (result.a == 1); + +result = JSON.parse (str, true); +assert (result.a == 1); + +result = JSON.parse (str, "foo"); +assert (result.a == 1); + +// Checking reviver with different object types + +str = ' { "a":1 } '; + +result = JSON.parse(str, new Boolean (true)); +assert (result.a == 1); + +result = JSON.parse(str, new String ("foo")); +assert (result.a == 1); + +result = JSON.parse(str, new Number (3)); +assert (result.a == 1); + +result = JSON.parse(str, {"a": 2}); +assert (result.a == 1); + +result = JSON.parse(str, [1, 2, 3]); +assert (result.a == 1); diff --git a/tests/jerry/json-stringify.js b/tests/jerry/json-stringify.js new file mode 100644 index 0000000000..c026593497 --- /dev/null +++ b/tests/jerry/json-stringify.js @@ -0,0 +1,186 @@ +// Copyright 2015 Samsung Electronics Co., Ltd. +// Copyright 2015 University of Szeged. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Checking quoting strings +normal_string = "asdasd"; +assert (JSON.stringify (normal_string) == '"asdasd"'); + +format_characters = "\ba\fs\nd\ra\tsd"; +assert (JSON.stringify (format_characters) == '"\\ba\\fs\\nd\\ra\\tsd"'); + +ctl_string = "asdasd"; +assert (JSON.stringify (ctl_string) == '"asd\\u001fasd"'); + +escpad_string = "\"asda\sd"; +assert (JSON.stringify (escpad_string) == '"\\"asdasd"'); + +// Checking primitive types +assert (JSON.stringify (1) === '1'); +assert (JSON.stringify (true) === 'true'); +assert (JSON.stringify ("foo") === '"foo"'); +assert (JSON.stringify (null) === 'null'); +assert (JSON.stringify (undefined) === undefined); + +assert (JSON.stringify (new Number(1)) === '1'); +assert (JSON.stringify (new Boolean(true)) === 'true'); +assert (JSON.stringify (new String("foo")) === '"foo"'); + +// Checking objects +empty_object = {} +assert (JSON.stringify (empty_object) == '{}'); + +empty_object = {}; +empty_object.a = undefined; + +assert (JSON.stringify (empty_object) == '{}'); + +p_object = { "a": 1, "b": true, "c": "foo", "d": null, "e": undefined }; +assert (JSON.stringify (p_object) == '{"d":null,"c":"foo","b":true,"a":1}'); + +o_object = { "a": new Number(1), "b": new Boolean(true), "c": new String("foo") }; +assert (JSON.stringify (o_object) == '{"c":"foo","b":true,"a":1}'); + +child = { "a": 1, "b": new String("\nfoo"), "c": undefined }; +parent = { "a": true, "b": child, "c": null}; + +assert (JSON.stringify (parent) == '{"c":null,"b":{"b":"\\nfoo","a":1},"a":true}'); + +recursive_object = {}; +recursive_object.a = 2; +recursive_object.b = recursive_object; + +try { + JSON.stringify (recursive_object) + // Should not be reached. + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +// Checking arrays +empty_array = []; +assert (JSON.stringify (empty_array) == '[]'); + +array = [undefined]; +assert (JSON.stringify (array) == '[null]'); + +p_array = [1, true, "foo", null, undefined]; +assert (JSON.stringify (p_array) == '[1,true,"foo",null,null]'); + +o_array = [new Number(1), new Boolean(true), new String("foo")]; +assert (JSON.stringify (o_array) == '[1,true,"foo"]'); + +child = [ 1, new String("\nfoo"), undefined ]; +parent = [ true, child, null ]; + +assert (JSON.stringify (parent) == '[true,[1,"\\nfoo",null],null]'); + +recursive_array = []; +recursive_array[0] = 2; +recursive_array[1] = recursive_array; + +try { + JSON.stringify (recursive_array) + // Should not be reached. + assert (false); +} catch (e) { + assert (e instanceof TypeError); +} + +object = {"a": 1, "b": [1, true, {"a": "foo"}]}; +assert (JSON.stringify (object) == '{"b":[1,true,{"a":"foo"}],"a":1}'); + +array = [1, {"a": 2, "b": true, c: [3]}]; +assert (JSON.stringify (array) == '[1,{"c":[3],"b":true,"a":2}]'); + +// Filtering / replacing +to_json_object = {}; +to_json_object.a = 2; +to_json_object.toJSON = function (key) { return 3; }; + +assert (JSON.stringify (to_json_object) === "3"); + +function replacer_function (key, value) +{ + if (typeof(value) == "string") + return "FOO"; + return value; +} + +object = { "a": "JSON", "b": new String("JSON"), "c": 3 }; +assert (JSON.stringify (object, replacer_function) == '{"c":3,"b":"JSON","a":"FOO"}'); + +filter = ["a", "b"]; +assert (JSON.stringify (object, filter) == '{"a":"JSON","b":"JSON"}'); + +// Throw error in the replacer function +function replacer_thrower (key, value) +{ + throw new ReferenceError("foo"); + return value; +} + +try { + JSON.stringify (object, replacer_thrower) + // Should not be reached. + assert (false); +} catch (e) { + assert (e.message === "foo"); + assert (e instanceof ReferenceError); +} + +// Checking replacer with different primitive types +object = { "a": 2 }; +assert (JSON.stringify (object, 3) == '{"a":2}'); +assert (JSON.stringify (object, true) == '{"a":2}'); +assert (JSON.stringify (object, null) == '{"a":2}'); +assert (JSON.stringify (object, undefined) == '{"a":2}'); +assert (JSON.stringify (object, "foo") == '{"a":2}'); + +// Checking replacer with different primitive types +assert (JSON.stringify (object, new Boolean (true)) == '{"a":2}'); +assert (JSON.stringify (object, new Number (3)) == '{"a":2}'); +assert (JSON.stringify (object, new String ("foo")) == '{"a":2}'); +assert (JSON.stringify (object, { "a": 3 }) == '{"a":2}'); + +// Checking JSON formatting +object = {"a": 2}; +assert (JSON.stringify (object, null, " ") == '{\n "a": 2\n}'); +assert (JSON.stringify (object, null, "asd") == '{\nasd"a": 2\n}'); +assert (JSON.stringify (object, null, "asd0123456789") == '{\nasd0123456"a": 2\n}'); +assert (JSON.stringify (object, null, 100) == '{\n "a": 2\n}'); + +array = [2]; +assert (JSON.stringify (array, null, " ") == '[\n 2\n]'); +assert (JSON.stringify (array, null, "asd") == '[\nasd2\n]'); +assert (JSON.stringify (array, null, "asd0123456789") == '[\nasd01234562\n]'); +assert (JSON.stringify (array, null, 100) == '[\n 2\n]'); + +nested_object = {"a": 2, "b": {"c": 1, "d": true}}; +assert (JSON.stringify (nested_object, null, 2) == '{\n "b": {\n "d": true,\n "c": 1\n },\n "a": 2\n}'); + +nested_array = [2, [1,true]]; +assert (JSON.stringify (nested_array, null, 2) == '[\n 2,\n [\n 1,\n true\n ]\n]'); + +// Checking space (formatting parameter) with different primititve types +object = { "a": 2 }; +assert (JSON.stringify (object, null, true) == '{"a":2}'); +assert (JSON.stringify (object, null, null) == '{"a":2}'); +assert (JSON.stringify (object, null, undefined) == '{"a":2}'); + +// Checking space (formatting parameter) with different object types +assert (JSON.stringify (object, null, new Boolean (true)) == '{"a":2}'); +assert (JSON.stringify (object, null, [1, 2, 3] ) == '{"a":2}'); +assert (JSON.stringify (object, null, { "a": 3 }) == '{"a":2}');