From 488a0bf7e8ebe9cf62ce270f5132f707595c0bad Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Wed, 17 Feb 2021 16:07:54 +0100 Subject: [PATCH] Improve date internals (#4593) - Optimize year from time calculation - Force arithmetic operations to int32_t/int64_t whenever possible - Optimize number conversion in date parse - Cache local TZA of the date object - Fix a bug in Date.parse timezone parsing JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik robert.fancsik@h-lab.eu --- jerry-core/ecma/base/ecma-gc.c | 7 +- jerry-core/ecma/base/ecma-globals.h | 24 + .../ecma-builtin-date-prototype.c | 187 +++-- .../ecma/builtin-objects/ecma-builtin-date.c | 728 +++++++++--------- .../builtin-objects/ecma-builtin-date.inc.h | 6 +- .../ecma-builtin-helpers-date.c | 566 ++++++-------- .../builtin-objects/ecma-builtin-helpers.h | 36 +- .../builtin-objects/ecma-builtin-intrinsic.c | 14 +- tests/jerry/date-parse.js | 1 + tests/unit-core/test-date-helpers.c | 26 +- 10 files changed, 810 insertions(+), 785 deletions(-) diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index aaa229e50..584321b46 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -1577,9 +1577,12 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ case LIT_MAGIC_STRING_DATE_UL: { - ecma_number_t *num_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, - ext_object_p->u.class_prop.u.value); +#if JERRY_ESNEXT + ext_object_size = sizeof (ecma_date_object_t); +#else /* !JERRY_ESNEXT */ + ecma_number_t *num_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, ext_object_p->u.class_prop.u.date); ecma_dealloc_number (num_p); +#endif /* JERRY_ESNEXT */ break; } case LIT_MAGIC_STRING_REGEXP_UL: diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 68598662b..ed4a0fabe 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -945,6 +945,8 @@ typedef struct union { ecma_value_t value; /**< value of the object (e.g. boolean, number, string, etc.) */ + ecma_value_t date; /**< Date object [[DateValue]] internal property */ + int32_t tza; /**< TimeZone adjustment for date objects */ uint32_t length; /**< length related property (e.g. length of ArrayBuffer) */ ecma_value_t target; /**< [[ProxyTarget]] internal property */ ecma_value_t head; /**< points to the async generator task queue head item */ @@ -2313,6 +2315,28 @@ typedef struct } u; } ecma_mapped_arguments_t; +#if JERRY_ESNEXT + +/** + * Date object descriptor flags + */ +typedef enum +{ + ECMA_DATE_TZA_NONE = 0, + ECMA_DATE_TZA_SET = 1 << 0, +} ecma_date_object_flags_t; + +/** + * Definition of date object + */ +typedef struct +{ + ecma_extended_object_t header; /**< object header */ + ecma_number_t date_value; /**< [[DateValue]] internal property */ +} ecma_date_object_t; + +#endif /* JERRY_ESNEXT */ + /** * @} * @} diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c index 5844cec96..e3e0f006d 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-date-prototype.c @@ -227,84 +227,82 @@ ecma_builtin_date_prototype_to_primitive (ecma_value_t this_arg, /**< this argum static ecma_value_t ecma_builtin_date_prototype_dispatch_get (uint16_t builtin_routine_id, /**< built-in wide routine * identifier */ - ecma_number_t date_num) /**< date converted to number */ + ecma_number_t date_value) /**< date converted to number */ { - if (ecma_number_is_nan (date_num)) + if (ecma_number_is_nan (date_value)) { return ecma_make_nan_value (); } + int32_t result; + switch (builtin_routine_id) { case ECMA_DATE_PROTOTYPE_GET_FULL_YEAR: case ECMA_DATE_PROTOTYPE_GET_UTC_FULL_YEAR: -#if JERRY_BUILTIN_ANNEXB - case ECMA_DATE_PROTOTYPE_GET_YEAR: -#endif /* JERRY_BUILTIN_ANNEXB */ { - date_num = ecma_date_year_from_time (date_num); - -#if JERRY_BUILTIN_ANNEXB - if (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_YEAR) - { - date_num -= 1900; - } -#endif /* JERRY_BUILTIN_ANNEXB */ - + result = ecma_date_year_from_time (date_value); break; } +#if JERRY_BUILTIN_ANNEXB + case ECMA_DATE_PROTOTYPE_GET_YEAR: + { + result = (ecma_date_year_from_time (date_value) - 1900); + break; + } +#endif /* JERRY_BUILTIN_ANNEXB */ case ECMA_DATE_PROTOTYPE_GET_MONTH: case ECMA_DATE_PROTOTYPE_GET_UTC_MONTH: { - date_num = ecma_date_month_from_time (date_num); + result = ecma_date_month_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_DATE: case ECMA_DATE_PROTOTYPE_GET_UTC_DATE: { - date_num = ecma_date_date_from_time (date_num); + result = ecma_date_date_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_DAY: case ECMA_DATE_PROTOTYPE_GET_UTC_DAY: { - date_num = ecma_date_week_day (date_num); + result = ecma_date_week_day (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_HOURS: case ECMA_DATE_PROTOTYPE_GET_UTC_HOURS: { - date_num = ecma_date_hour_from_time (date_num); + result = ecma_date_hour_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_MINUTES: case ECMA_DATE_PROTOTYPE_GET_UTC_MINUTES: { - date_num = ecma_date_min_from_time (date_num); + result = ecma_date_min_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_SECONDS: case ECMA_DATE_PROTOTYPE_GET_UTC_SECONDS: { - date_num = ecma_date_sec_from_time (date_num); + result = ecma_date_sec_from_time (date_value); break; } case ECMA_DATE_PROTOTYPE_GET_MILLISECONDS: case ECMA_DATE_PROTOTYPE_GET_UTC_MILLISECONDS: { - date_num = ecma_date_ms_from_time (date_num); + result = ecma_date_ms_from_time (date_value); break; } default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET); - date_num = ecma_date_timezone_offset (date_num); + result = (int32_t) ((-ecma_date_local_time_zone_adjustment (date_value)) / ECMA_DATE_MS_PER_MINUTE); break; } } - return ecma_make_number_value (date_num); + return ecma_make_int32_value (result); } /* ecma_builtin_date_prototype_dispatch_get */ #if JERRY_BUILTIN_ANNEXB @@ -337,8 +335,7 @@ ecma_builtin_date_prototype_dispatch_get (uint16_t builtin_routine_id, /**< buil static ecma_value_t ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< built-in wide routine * identifier */ - ecma_extended_object_t *ext_object_p, /**< date extended object */ - ecma_number_t date_num, /**< date converted to number */ + ecma_object_t *object_p, /**< date object */ const ecma_value_t arguments_list[], /**< list of arguments * passed to routine */ uint32_t arguments_number) /**< length of arguments' list */ @@ -403,28 +400,55 @@ ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< buil } } +#if JERRY_ESNEXT + ecma_date_object_t *date_object_p = (ecma_date_object_t *) object_p; + ecma_number_t *date_value_p = &date_object_p->date_value; +#else /* !JERRY_ESNEXT */ + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + ecma_number_t *date_value_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, ext_object_p->u.class_prop.u.date); +#endif /* JERRY_ESNEXT */ + + ecma_number_t date_value = *date_value_p; + + if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id)) + { + ecma_number_t local_tza; + +#if JERRY_ESNEXT + if (date_object_p->header.u.class_prop.extra_info & ECMA_DATE_TZA_SET) + { + local_tza = date_object_p->header.u.class_prop.u.tza; + JERRY_ASSERT (local_tza == ecma_date_local_time_zone_adjustment (date_value)); + } + else +#endif /* JERRY_ESNEXT */ + { + local_tza = ecma_date_local_time_zone_adjustment (date_value); + } + + date_value += local_tza; + } + ecma_number_t day_part; ecma_number_t time_part; if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_SET_UTC_DATE) { - if (ecma_number_is_nan (date_num)) + if (ecma_number_is_nan (date_value)) { - if (ECMA_DATE_PROTOTYPE_IS_SET_YEAR_ROUTINE (builtin_routine_id)) + if (!ECMA_DATE_PROTOTYPE_IS_SET_YEAR_ROUTINE (builtin_routine_id)) { - date_num = ECMA_NUMBER_ZERO; - } - else - { - return ecma_make_number_value (date_num); + return ecma_make_number_value (date_value); } + + date_value = ECMA_NUMBER_ZERO; } - time_part = ecma_date_time_within_day (date_num); + time_part = ecma_date_time_in_day_from_time (date_value); - ecma_number_t year = ecma_date_year_from_time (date_num); - ecma_number_t month = ecma_date_month_from_time (date_num); - ecma_number_t day = ecma_date_date_from_time (date_num); + ecma_number_t year = ecma_date_year_from_time (date_value); + ecma_number_t month = ecma_date_month_from_time (date_value); + ecma_number_t day = ecma_date_date_from_time (date_value); switch (builtin_routine_id) { @@ -447,7 +471,7 @@ ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< buil { if (ecma_number_is_nan (converted_number[0])) { - *ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, ext_object_p->u.class_prop.u.value) = converted_number[0]; + *date_value_p = converted_number[0]; return ecma_make_number_value (converted_number[0]); } @@ -494,17 +518,17 @@ ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< buil } else { - if (ecma_number_is_nan (date_num)) + if (ecma_number_is_nan (date_value)) { - return ecma_make_number_value (date_num); + return ecma_make_number_value (date_value); } - day_part = ecma_date_day (date_num); + day_part = ecma_date_day_from_time (date_value) * (ecma_number_t) ECMA_DATE_MS_PER_DAY; - ecma_number_t hour = ecma_date_hour_from_time (date_num); - ecma_number_t min = ecma_date_min_from_time (date_num); - ecma_number_t sec = ecma_date_sec_from_time (date_num); - ecma_number_t ms = ecma_date_ms_from_time (date_num); + ecma_number_t hour = ecma_date_hour_from_time (date_value); + ecma_number_t min = ecma_date_min_from_time (date_value); + ecma_number_t sec = ecma_date_sec_from_time (date_value); + ecma_number_t ms = ecma_date_ms_from_time (date_value); switch (builtin_routine_id) { @@ -574,7 +598,11 @@ ecma_builtin_date_prototype_dispatch_set (uint16_t builtin_routine_id, /**< buil full_date = ecma_date_time_clip (full_date); - *ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, ext_object_p->u.class_prop.u.value) = full_date; + *date_value_p = full_date; + +#if JERRY_ESNEXT + date_object_p->header.u.class_prop.extra_info &= (uint16_t) ~ECMA_DATE_TZA_SET; +#endif /* JERRY_ESNEXT */ return ecma_make_number_value (full_date); } /* ecma_builtin_date_prototype_dispatch_set */ @@ -614,13 +642,22 @@ ecma_builtin_date_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< b return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a Date object")); } - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) ecma_get_object_from_value (this_arg); - ecma_number_t *prim_value_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, - ext_object_p->u.class_prop.u.value); + ecma_object_t *this_obj_p = ecma_get_object_from_value (this_arg); + +#if JERRY_ESNEXT + ecma_date_object_t *date_object_p = (ecma_date_object_t *) this_obj_p; + ecma_number_t *date_value_p = &date_object_p->date_value; +#else + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) this_obj_p; + ecma_number_t *date_value_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, + ext_object_p->u.class_prop.u.date); +#endif + + ecma_number_t date_value = *date_value_p; if (builtin_routine_id == ECMA_DATE_PROTOTYPE_GET_TIME) { - return ecma_make_number_value (*prim_value_p); + return ecma_make_number_value (date_value); } if (builtin_routine_id == ECMA_DATE_PROTOTYPE_SET_TIME) @@ -632,43 +669,59 @@ ecma_builtin_date_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< b return ECMA_VALUE_ERROR; } - *prim_value_p = ecma_date_time_clip (time_num); + *date_value_p = ecma_date_time_clip (time_num); - return ecma_make_number_value (*prim_value_p); + return ecma_make_number_value (*date_value_p); } if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_SET_UTC_MILLISECONDS) { - ecma_number_t this_num = *prim_value_p; - - if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id)) - { - this_num += ecma_date_local_time_zone_adjustment (this_num); - } if (builtin_routine_id <= ECMA_DATE_PROTOTYPE_GET_UTC_TIMEZONE_OFFSET) { - return ecma_builtin_date_prototype_dispatch_get (builtin_routine_id, this_num); + if (!BUILTIN_DATE_FUNCTION_IS_UTC (builtin_routine_id)) + { + ecma_number_t local_tza; +#if JERRY_ESNEXT + if (date_object_p->header.u.class_prop.extra_info & ECMA_DATE_TZA_SET) + { + local_tza = date_object_p->header.u.class_prop.u.tza; + JERRY_ASSERT (local_tza == ecma_date_local_time_zone_adjustment (date_value)); + } + else + { +#endif /* JERRY_ESNEXT */ + local_tza = ecma_date_local_time_zone_adjustment (date_value); +#if JERRY_ESNEXT + JERRY_ASSERT (local_tza <= INT32_MAX && local_tza >= INT32_MIN); + date_object_p->header.u.class_prop.u.tza = (int32_t) local_tza; + date_object_p->header.u.class_prop.extra_info |= ECMA_DATE_TZA_SET; + } +#endif /* JERRY_ESNEXT */ + + date_value += local_tza; + } + + return ecma_builtin_date_prototype_dispatch_get (builtin_routine_id, date_value); } return ecma_builtin_date_prototype_dispatch_set (builtin_routine_id, - ext_object_p, - this_num, + this_obj_p, arguments_list, arguments_number); } if (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_ISO_STRING) { - if (ecma_number_is_nan (*prim_value_p) || ecma_number_is_infinity (*prim_value_p)) + if (ecma_number_is_nan (date_value)) { return ecma_raise_range_error (ECMA_ERR_MSG ("Date must be a finite number")); } - return ecma_date_value_to_iso_string (*prim_value_p); + return ecma_date_value_to_iso_string (date_value); } - if (ecma_number_is_nan (*prim_value_p)) + if (ecma_number_is_nan (date_value)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_INVALID_DATE_UL); } @@ -677,23 +730,23 @@ ecma_builtin_date_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< b { case ECMA_DATE_PROTOTYPE_TO_STRING: { - return ecma_date_value_to_string (*prim_value_p); + return ecma_date_value_to_string (date_value); } case ECMA_DATE_PROTOTYPE_TO_DATE_STRING: { - return ecma_date_value_to_date_string (*prim_value_p); + return ecma_date_value_to_date_string (date_value); } #if !JERRY_ESNEXT case ECMA_DATE_PROTOTYPE_TO_UTC_STRING: { - return ecma_date_value_to_utc_string (*prim_value_p); + return ecma_date_value_to_utc_string (date_value); } #endif /* JERRY_ESNEXT */ default: { JERRY_ASSERT (builtin_routine_id == ECMA_DATE_PROTOTYPE_TO_TIME_STRING); - return ecma_date_value_to_time_string (*prim_value_p); + return ecma_date_value_to_time_string (date_value); } } } /* ecma_builtin_date_prototype_dispatch_routine */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-date.c b/jerry-core/ecma/builtin-objects/ecma-builtin-date.c index d19160570..3271b8b17 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-date.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-date.c @@ -31,6 +31,22 @@ #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" +/** + * This object has a custom dispatch function. + */ +#define BUILTIN_CUSTOM_DISPATCH + +/** + * List of built-in routine identifiers. + */ +enum +{ + ECMA_DATE_ROUTINE_START = 0, + ECMA_DATE_ROUTINE_PARSE, + ECMA_DATE_ROUTINE_UTC, + ECMA_DATE_ROUTINE_NOW, +}; + #define BUILTIN_INC_HEADER_NAME "ecma-builtin-date.inc.h" #define BUILTIN_UNDERSCORED_ID date #include "ecma-builtin-internal-routines-template.inc.h" @@ -45,6 +61,26 @@ * @{ */ +/** + * Encode minimum/maximum limits + * + * See: ecma_date_parse_date_chars + * + * @param min: 8 bits unsigned number + * @param max: 24 bits unsigned number + */ +#define ECMA_DATE_LIMIT(min, max) (min << 24 | max) + +/** + * Decode the minimum value from the encoded limit + */ +#define ECMA_DATE_LIMIT_MIN(limit) (limit >> 24) + +/** + * Decode the maximum value from the encoded limit + */ +#define ECMA_DATE_LIMIT_MAX(limit) (limit & ((1 << 24) - 1)) + /** * Helper function to try to parse a part of a date string * @@ -54,28 +90,37 @@ static ecma_number_t ecma_date_parse_date_chars (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ const lit_utf8_byte_t *str_end_p, /**< pointer to the end of the string */ uint32_t num_of_chars, /**< number of characters to read and convert */ - uint32_t min, /**< minimum valid value */ - uint32_t max) /**< maximum valid value */ + uint32_t limit) /**< minimum/maximum valid value */ { - JERRY_ASSERT (num_of_chars > 0); - const lit_utf8_byte_t *str_start_p = *str_p; + JERRY_ASSERT (num_of_chars > 0 && num_of_chars <= 6); - while (num_of_chars--) - { - if (*str_p >= str_end_p || !lit_char_is_decimal_digit (lit_cesu8_read_next (str_p))) - { - return ecma_number_make_nan (); - } - } - - ecma_number_t parsed_number = ecma_utf8_string_to_number (str_start_p, (lit_utf8_size_t) (*str_p - str_start_p), 0); - - if (parsed_number < min || parsed_number > max) + if (*str_p + num_of_chars > str_end_p) { return ecma_number_make_nan (); } - return parsed_number; + str_end_p = *str_p + num_of_chars; + + uint32_t num = 0; + + while (num_of_chars--) + { + lit_utf8_byte_t c = **str_p; + if (!lit_char_is_decimal_digit (c)) + { + return ecma_number_make_nan (); + } + + num = (num * 10) + (uint32_t) ((c - LIT_CHAR_0)); + (*str_p)++; + } + + if (num >= ECMA_DATE_LIMIT_MIN (limit) && num <= ECMA_DATE_LIMIT_MAX (limit)) + { + return (ecma_number_t) num; + } + + return ecma_number_make_nan (); } /* ecma_date_parse_date_chars */ /** @@ -129,14 +174,10 @@ ecma_date_parse_year (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 s str_start_p++; } - if (str_start_p - *str_p >=4) + if (str_start_p - *str_p >= 4) { *str_p = str_start_p; - if (is_year_sign_negative) - { - return -parsed_year; - } - return parsed_year; + return is_year_sign_negative ? -parsed_year : parsed_year; } return ecma_number_make_nan (); @@ -187,7 +228,7 @@ ecma_date_parse_month_name (const lit_utf8_byte_t **str_p, /**< pointer to the c if (!memcmp (month_names_p[i], *str_p, 3)) { (*str_p) += 3; - return (i+1); + return (i + 1); } } } @@ -198,14 +239,15 @@ ecma_date_parse_month_name (const lit_utf8_byte_t **str_p, /**< pointer to the c * Calculate MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)) for Date constructor and UTC * * See also: - * ECMA-262 v5, 15.9.3.1 - * ECMA-262 v5, 15.9.4.3 + * ECMA-262 v11, 20.4.3.4 * - * @return result of MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)) + * @return false - if the operation fails + * true - otherwise */ -static ecma_value_t +static bool ecma_date_construct_helper (const ecma_value_t *args, /**< arguments passed to the Date constructor */ - uint32_t args_len) /**< number of arguments */ + uint32_t args_len, /**< number of arguments */ + ecma_number_t *tv_p) /**< [out] time value */ { ecma_number_t date_nums[7] = { @@ -227,32 +269,32 @@ ecma_date_construct_helper (const ecma_value_t *args, /**< arguments passed to t if (ECMA_IS_VALUE_ERROR (status)) { - return status; + return false; } } - ecma_number_t prim_value = ecma_number_make_nan (); - + /* 8. */ if (!ecma_number_is_nan (date_nums[0])) { - /* 8. */ - ecma_number_t y = ecma_number_trunc (date_nums[0]); + /* 9.a */ + ecma_number_t yi = ecma_number_trunc (date_nums[0]); - if (y >= 0 && y <= 99) + /* 9.b */ + if (yi >= 0 && yi <= 99) { - date_nums[0] = 1900 + y; + date_nums[0] = 1900 + yi; } } - prim_value = ecma_date_make_date (ecma_date_make_day (date_nums[0], - date_nums[1], - date_nums[2]), - ecma_date_make_time (date_nums[3], - date_nums[4], - date_nums[5], - date_nums[6])); - - return ecma_make_number_value (prim_value); + /* 10. */ + *tv_p = ecma_date_make_date (ecma_date_make_day (date_nums[0], + date_nums[1], + date_nums[2]), + ecma_date_make_time (date_nums[3], + date_nums[4], + date_nums[5], + date_nums[6])); + return true; } /* ecma_date_construct_helper */ /** @@ -265,12 +307,13 @@ ecma_date_construct_helper (const ecma_value_t *args, /**< arguments passed to t * @return the parsed date as ecma_number_t or NaN otherwise */ static ecma_number_t -ecma_builtin_date_parse_basic (const lit_utf8_byte_t *date_str_curr_p, - const lit_utf8_byte_t *date_str_end_p) +ecma_builtin_date_parse_basic (const lit_utf8_byte_t *date_str_curr_p, /**< date string start */ + const lit_utf8_byte_t *date_str_end_p) /**< date string end */ { /* 1. read year */ uint32_t year_digits = 4; + uint32_t year_limit = 9999; bool is_year_sign_negative = false; @@ -278,132 +321,136 @@ ecma_builtin_date_parse_basic (const lit_utf8_byte_t *date_str_curr_p, { is_year_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); year_digits = 6; + year_limit = 999999; } - ecma_number_t year = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, year_digits, - 0, (year_digits == 4) ? 9999 : 999999); + ecma_number_t year = ecma_date_parse_date_chars (&date_str_curr_p, + date_str_end_p, + year_digits, + ECMA_DATE_LIMIT (0, year_limit)); if (is_year_sign_negative) { year = -year; } - if (!ecma_number_is_nan (year)) + if (ecma_number_is_nan (year)) { - ecma_number_t month = ECMA_NUMBER_ONE; - ecma_number_t day = ECMA_NUMBER_ONE; - ecma_number_t time = ECMA_NUMBER_ZERO; + return year; + } - /* 2. read month if any */ - if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_SLASH)) + ecma_number_t month = ECMA_NUMBER_ONE; + ecma_number_t day = ECMA_NUMBER_ONE; + ecma_number_t time = ECMA_NUMBER_ZERO; + + /* 2. read month if any */ + if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_SLASH)) + { + lit_utf8_byte_t separator = *date_str_curr_p++; + month = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (1, 12)); + + /* 3. read day if any */ + if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, separator)) { - lit_utf8_byte_t separator = *date_str_curr_p++; - month = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 1, 12); - - /* 3. read day if any */ - if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, separator)) - { - day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 1, 31); - } - } - - bool is_utc = true; - /* 4. read time if any */ - if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_T, LIT_CHAR_SP)) - { - date_str_curr_p++; - - ecma_number_t hours = ECMA_NUMBER_ZERO; - ecma_number_t minutes = ECMA_NUMBER_ZERO; - ecma_number_t seconds = ECMA_NUMBER_ZERO; - ecma_number_t milliseconds = ECMA_NUMBER_ZERO; - - lit_utf8_size_t remaining_length = lit_utf8_string_length (date_str_curr_p, - (lit_utf8_size_t) (date_str_end_p - date_str_curr_p)); - - if (remaining_length >= 5) - { - /* 4.1 read hours and minutes */ - hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 24); - - if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) - { - minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); - - /* 4.2 read seconds if any */ - if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) - { - seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); - - /* 4.3 read milliseconds if any */ - if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_DOT)) - { - milliseconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 3, 0, 999); - } - } - } - else - { - minutes = ecma_number_make_nan (); - } - - if (hours == 24 && (minutes != 0 || seconds != 0 || milliseconds != 0)) - { - hours = ecma_number_make_nan (); - } - - time = ecma_date_make_time (hours, minutes, seconds, milliseconds); - } - else - { - time = ecma_number_make_nan (); - } - - /* 4.4 read timezone if any */ - if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_Z) - && !ecma_number_is_nan (time)) - { - time = ecma_date_make_time (hours, minutes, seconds, milliseconds); - } - else - { - if (lit_utf8_string_length (date_str_curr_p, (lit_utf8_size_t) (date_str_end_p - date_str_curr_p)) == 6 - && ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_PLUS)) - { - bool is_timezone_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); - /* read hours and minutes */ - hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 24); - - if (hours == 24) - { - hours = ECMA_NUMBER_ZERO; - } - - ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON); - minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); - ecma_number_t timezone_offset = ecma_date_make_time (hours, minutes, ECMA_NUMBER_ZERO, ECMA_NUMBER_ZERO); - time += is_timezone_sign_negative ? timezone_offset : -timezone_offset; - } - else - { - is_utc = false; - } - } - } - - if (date_str_curr_p >= date_str_end_p) - { - ecma_number_t date = ecma_date_make_day (year, month - 1, day); - - ecma_number_t result_date = ecma_date_make_date (date, time); - if (!is_utc) - { - result_date = ecma_date_utc (result_date); - } - - return result_date; + day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (1, 31)); } } - return ecma_number_make_nan (); + + bool is_utc = true; + /* 4. read time if any */ + if (ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_T, LIT_CHAR_SP)) + { + date_str_curr_p++; + + ecma_number_t hours = ECMA_NUMBER_ZERO; + ecma_number_t minutes = ECMA_NUMBER_ZERO; + ecma_number_t seconds = ECMA_NUMBER_ZERO; + ecma_number_t milliseconds = ECMA_NUMBER_ZERO; + + /* 'HH:mm' must present */ + if (date_str_end_p - date_str_curr_p < 5) + { + return ecma_number_make_nan (); + } + + /* 4.1 read hours and minutes */ + hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) + { + return ecma_number_make_nan (); + } + + minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); + + /* 4.2 read seconds if any */ + if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) + { + seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); + + /* 4.3 read milliseconds if any */ + if (ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_DOT)) + { + milliseconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 3, ECMA_DATE_LIMIT (0, 999)); + } + } + + if (hours == 24 && (minutes != 0 || seconds != 0 || milliseconds != 0)) + { + return ecma_number_make_nan (); + } + + time = ecma_date_make_time (hours, minutes, seconds, milliseconds); + + if (ecma_number_is_nan (time)) + { + return time; + } + + /* 4.4 read timezone if any */ + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_Z)) + { + if ((date_str_end_p - date_str_curr_p) == 6 + && (*date_str_curr_p == LIT_CHAR_MINUS || *date_str_curr_p == LIT_CHAR_PLUS)) + { + bool is_timezone_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); + /* read hours and minutes */ + hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); + + if (hours == 24) + { + hours = ECMA_NUMBER_ZERO; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_COLON)) + { + return ecma_number_make_nan (); + } + + minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); + ecma_number_t timezone_offset = ecma_date_make_time (hours, minutes, ECMA_NUMBER_ZERO, ECMA_NUMBER_ZERO); + time += is_timezone_sign_negative ? timezone_offset : -timezone_offset; + } + else + { + is_utc = false; + } + } + } + + if (date_str_curr_p < date_str_end_p) + { + return ecma_number_make_nan (); + } + + ecma_number_t date = ecma_date_make_day (year, month - 1, day); + ecma_number_t result_date = ecma_date_make_date (date, time); + + if (!is_utc) + { + result_date = ecma_date_utc (result_date); + } + + return result_date; } /* ecma_builtin_date_parse_basic */ /** @@ -440,7 +487,8 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p ecma_number_t day = 0; if (is_toUTCString_format) { - day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 31); + day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 31)); + if (ecma_number_is_nan (day)) { return nan; @@ -452,15 +500,17 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p } month = ecma_date_parse_month_name (&date_str_curr_p, date_str_end_p); - if (!(int) month) + + if (month == 0) { - return nan; + return ecma_number_make_nan (); } } else { month = ecma_date_parse_month_name (&date_str_curr_p, date_str_end_p); - if (!(int) month) + + if (month == 0) { return nan; } @@ -470,7 +520,8 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p return nan; } - day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 31); + day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 31)); + if (ecma_number_is_nan (day)) { return nan; @@ -483,6 +534,7 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p } ecma_number_t year = ecma_date_parse_year (&date_str_curr_p, date_str_end_p); + if (ecma_number_is_nan (year)) { return nan; @@ -493,7 +545,8 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p return nan; } - ecma_number_t hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 24); + ecma_number_t hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); + if (ecma_number_is_nan (hours)) { return nan; @@ -504,7 +557,8 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p return nan; } - ecma_number_t minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); + ecma_number_t minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); + if (ecma_number_is_nan (minutes)) { return nan; @@ -515,7 +569,8 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p return nan; } - ecma_number_t seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); + ecma_number_t seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); + if (ecma_number_is_nan (seconds)) { return nan; @@ -526,63 +581,57 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p return nan; } - if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_SP)) + const char gmt_p[] = " GMT"; + if (date_str_end_p - date_str_curr_p < 4 + || memcmp (date_str_curr_p, gmt_p, 4) != 0) { return nan; } - if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_G)) - { - return nan; - } - - if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_M)) - { - return nan; - } - - if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_UPPERCASE_T)) - { - return nan; - } + date_str_curr_p += 4; ecma_number_t time = ecma_date_make_time (hours, minutes, seconds, 0); if (!is_toUTCString_format) { - bool is_timezone_sign_negative = ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS); - if (!is_timezone_sign_negative && !ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, LIT_CHAR_PLUS)) + if (!ecma_date_check_two_chars (date_str_curr_p, date_str_end_p, LIT_CHAR_MINUS, LIT_CHAR_PLUS)) { return nan; } - hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 24); + bool is_timezone_sign_negative = (*date_str_curr_p++ == LIT_CHAR_MINUS); + + hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 24)); + if (ecma_number_is_nan (hours)) { return nan; } + if (hours == 24) { hours = ECMA_NUMBER_ZERO; } - minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); + minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, ECMA_DATE_LIMIT (0, 59)); + if (ecma_number_is_nan (minutes)) { return nan; } ecma_number_t timezone_offset = ecma_date_make_time (hours, minutes, ECMA_NUMBER_ZERO, ECMA_NUMBER_ZERO); + time += is_timezone_sign_negative ? timezone_offset : -timezone_offset; } - if (date_str_curr_p >= date_str_end_p) + if (date_str_curr_p < date_str_end_p) { - ecma_number_t date = ecma_date_make_day (year, month - 1, day); - return ecma_date_make_date (date, time); + return nan; } - return nan; + ecma_number_t date = ecma_date_make_day (year, month - 1, day); + return ecma_date_make_date (date, time); } /* ecma_builtin_date_parse_toString_formats */ /** @@ -594,38 +643,27 @@ ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p * ECMA-262 v9, 20.3.4.41 Date.prototype.toString () * ECMA-262 v9, 20.3.4.43 Date.prototype.toUTCString () * - * @return ecma value - * Returned value must be freed with ecma_free_value. + * @return parsed time */ -static ecma_value_t -ecma_builtin_date_parse (ecma_value_t this_arg, /**< this argument */ - ecma_value_t arg) /**< string */ +static ecma_number_t +ecma_builtin_date_parse (ecma_string_t *string_p) /**< string */ { - JERRY_UNUSED (this_arg); + ECMA_STRING_TO_UTF8_STRING (string_p, str_p, str_size); + const lit_utf8_byte_t *date_str_curr_p = str_p; + const lit_utf8_byte_t *date_str_end_p = str_p + str_size; - /* Date Time String fromat (ECMA-262 v5, 15.9.1.15) */ - ecma_string_t *date_str_p = ecma_op_to_string (arg); - if (JERRY_UNLIKELY (date_str_p == NULL)) + /* try to parse date string as ISO string - ECMA-262 v5, 15.9.1.15 */ + ecma_number_t tv = ecma_builtin_date_parse_basic (date_str_curr_p, date_str_end_p); + + if (ecma_number_is_nan (tv)) { - return ECMA_VALUE_ERROR; + /* try to parse date string in Date.prototype.toString() or toUTCString() format */ + tv = ecma_builtin_date_parse_toString_formats (date_str_curr_p, date_str_end_p); } - ECMA_STRING_TO_UTF8_STRING (date_str_p, date_start_p, date_start_size); - const lit_utf8_byte_t *date_str_curr_p = date_start_p; - const lit_utf8_byte_t *date_str_end_p = date_start_p + date_start_size; + ECMA_FINALIZE_UTF8_STRING (str_p, str_size); - // try to parse date string as ISO string and allow some variants - ECMA-262 v5, 15.9.1.15 - ecma_number_t ret_value = ecma_builtin_date_parse_basic (date_str_curr_p, date_str_end_p); - - if (ecma_number_is_nan (ret_value)) - { - // try to parse date string in Date.prototype.toString() or toUTCString() format - ret_value = ecma_builtin_date_parse_toString_formats (date_str_curr_p, date_str_end_p); - } - - ECMA_FINALIZE_UTF8_STRING (date_start_p, date_start_size); - ecma_deref_ecma_string (date_str_p); - return ecma_make_number_value (ecma_date_time_clip (ret_value)); + return tv; } /* ecma_builtin_date_parse */ /** @@ -638,12 +676,9 @@ ecma_builtin_date_parse (ecma_value_t this_arg, /**< this argument */ * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_date_utc (ecma_value_t this_arg, /**< this argument */ - const ecma_value_t args[], /**< arguments list */ +ecma_builtin_date_utc (const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { - JERRY_UNUSED (this_arg); - #if JERRY_ESNEXT const uint32_t required_args_number = 1; #else /* !JERRY_ESNEXT */ @@ -652,25 +687,17 @@ ecma_builtin_date_utc (ecma_value_t this_arg, /**< this argument */ if (args_number < required_args_number) { - /* Note: - * When the UTC function is called with fewer than two arguments, - * the behaviour is implementation-dependent, so just return NaN. - */ return ecma_make_number_value (ecma_number_make_nan ()); } - ecma_value_t time_value = ecma_date_construct_helper (args, args_number); + ecma_number_t tv; - if (ECMA_IS_VALUE_ERROR (time_value)) + if (!ecma_date_construct_helper (args, args_number, &tv)) { - return time_value; + return ECMA_VALUE_ERROR; } - ecma_number_t time = ecma_get_number_from_value (time_value); - - ecma_free_value (time_value); - - return ecma_make_number_value (ecma_date_time_clip (time)); + return ecma_make_number_value ((ecma_number_t) ecma_date_time_clip (tv)); } /* ecma_builtin_date_utc */ /** @@ -685,20 +712,49 @@ ecma_builtin_date_now_helper (void) } /* ecma_builtin_date_now_helper */ /** - * The Date object's 'now' routine + * Construct a date object with the given [[DateValue]] * - * See also: - * ECMA-262 v5, 15.9.4.4 + * Note: New target must be a valid object * - * @return ecma value - * Returned value must be freed with ecma_free_value. + * @return ECMA_VALUE_ERROR - if the operation fails + * constructed date object - otherwise */ static ecma_value_t -ecma_builtin_date_now (ecma_value_t this_arg) /**< this argument */ +ecma_builtin_date_create (ecma_number_t tv) { - JERRY_UNUSED (this_arg); - return ecma_make_number_value (ecma_builtin_date_now_helper ()); -} /* ecma_builtin_date_now */ +#if JERRY_ESNEXT + JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p) != NULL); + + ecma_object_t *prototype_obj_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), + ECMA_BUILTIN_ID_DATE_PROTOTYPE); + + if (JERRY_UNLIKELY (prototype_obj_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + ecma_object_t *obj_p = ecma_create_object (prototype_obj_p, sizeof (ecma_date_object_t), ECMA_OBJECT_TYPE_CLASS); + ecma_deref_object (prototype_obj_p); + + ecma_date_object_t *date_object_p = (ecma_date_object_t *) obj_p; + date_object_p->header.u.class_prop.class_id = LIT_MAGIC_STRING_DATE_UL; + date_object_p->header.u.class_prop.u.tza = 0; + date_object_p->header.u.class_prop.extra_info = ECMA_DATE_TZA_NONE; + date_object_p->date_value = tv; +#else /* !JERRY_ESNEXT */ + ecma_number_t *date_value_p = ecma_alloc_number (); + *date_value_p = tv; + + ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_DATE_PROTOTYPE); + ecma_object_t *obj_p = ecma_create_object (prototype_obj_p, sizeof (ecma_extended_object_t), ECMA_OBJECT_TYPE_CLASS); + + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; + ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_DATE_UL; + ECMA_SET_INTERNAL_VALUE_POINTER (ext_object_p->u.class_prop.u.date, date_value_p); +#endif /* JERRY_ESNEXT */ + + return ecma_make_object_value (obj_p); +} /* ecma_builtin_date_create */ /** * Handle calling [[Call]] of built-in Date object @@ -715,9 +771,7 @@ ecma_builtin_date_dispatch_call (const ecma_value_t *arguments_list_p, /**< argu JERRY_UNUSED (arguments_list_p); JERRY_UNUSED (arguments_list_len); - ecma_number_t now_val_num = ecma_builtin_date_now_helper (); - - return ecma_date_value_to_string (now_val_num); + return ecma_date_value_to_string (ecma_builtin_date_now_helper ()); } /* ecma_builtin_date_dispatch_call */ /** @@ -733,137 +787,125 @@ ecma_value_t ecma_builtin_date_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< number of arguments */ { -#if JERRY_ESNEXT - JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p)); - - ecma_object_t *prototype_obj_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), - ECMA_BUILTIN_ID_DATE_PROTOTYPE); - if (JERRY_UNLIKELY (prototype_obj_p == NULL)) - { - return ECMA_VALUE_ERROR; - } -#else /* !JERRY_ESNEXT */ - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_DATE_PROTOTYPE); -#endif /* (JERRY_ESNEXT */ - - ecma_object_t *obj_p = ecma_create_object (prototype_obj_p, - sizeof (ecma_extended_object_t), - ECMA_OBJECT_TYPE_CLASS); - -#if JERRY_ESNEXT - ecma_deref_object (prototype_obj_p); -#endif /* JERRY_ESNEXT */ - - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p; - ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_UNDEFINED; - - ecma_number_t prim_value_num = ECMA_NUMBER_ZERO; /* 20.4.2.3 */ if (arguments_list_len == 0) { - prim_value_num = ecma_builtin_date_now_helper (); + return ecma_builtin_date_create (ecma_builtin_date_now_helper ()); } + + ecma_number_t tv; /* 20.4.2.2 */ - else if (arguments_list_len == 1) + if (arguments_list_len == 1) { ecma_value_t argument = arguments_list_p[0]; - ecma_object_t *arg_obj = NULL; /* 4.a */ - if (ecma_is_value_object (argument)) + if (ecma_is_value_object (argument) + && ecma_object_class_is (ecma_get_object_from_value (argument), LIT_MAGIC_STRING_DATE_UL)) { - arg_obj = ecma_get_object_from_value (argument); - } - if (arg_obj && ecma_object_class_is (arg_obj, LIT_MAGIC_STRING_DATE_UL)) - { - ecma_extended_object_t *arg_ext_object_p = (ecma_extended_object_t *) arg_obj; - prim_value_num = *ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, arg_ext_object_p->u.class_prop.u.value); +#if JERRY_ESNEXT + tv = ((ecma_date_object_t *) ecma_get_object_from_value (argument))->date_value; +#else /* !JERRY_ESNEXT */ + ecma_extended_object_t *arg_ext_object_p = (ecma_extended_object_t *) ecma_get_object_from_value (argument); + tv = *ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, arg_ext_object_p->u.class_prop.u.date); +#endif /* JERRY_ESNEXT */ + + return ecma_builtin_date_create (tv); } /* 4.b */ + ecma_value_t primitive = ecma_op_to_primitive (argument, ECMA_PREFERRED_TYPE_NO); + + if (ECMA_IS_VALUE_ERROR (primitive)) + { + return primitive; + } + + if (ecma_is_value_string (primitive)) + { + ecma_string_t *prim_str_p = ecma_get_string_from_value (primitive); + tv = ecma_builtin_date_parse (prim_str_p); + ecma_deref_ecma_string (prim_str_p); + } else { - ecma_value_t prim_comp_value = ecma_op_to_primitive (argument, ECMA_PREFERRED_TYPE_NO); + ecma_value_t prim_value = ecma_op_to_number (primitive, &tv); + ecma_free_value (primitive); - if (ECMA_IS_VALUE_ERROR (prim_comp_value)) + if (ECMA_IS_VALUE_ERROR (prim_value)) { - ecma_deref_object (obj_p); - return prim_comp_value; + return prim_value; } - - if (ecma_is_value_string (prim_comp_value)) - { - ecma_value_t parse_res_value = ecma_builtin_date_parse (ecma_make_object_value (obj_p), prim_comp_value); - - if (ECMA_IS_VALUE_ERROR (parse_res_value)) - { - ecma_deref_object (obj_p); - ecma_free_value (prim_comp_value); - return parse_res_value; - } - - prim_value_num = ecma_get_number_from_value (parse_res_value); - - ecma_free_value (parse_res_value); - } - else - { - ecma_number_t arg; - ecma_value_t prim_value = ecma_op_to_number (prim_comp_value, &arg); - - if (ECMA_IS_VALUE_ERROR (prim_value)) - { - ecma_deref_object (obj_p); - ecma_free_value (prim_comp_value); - return prim_value; - } - - prim_value_num = ecma_date_time_clip (arg); - - } - - ecma_free_value (prim_comp_value); } } /* 20.4.2.1 */ + else if (ecma_date_construct_helper (arguments_list_p, arguments_list_len, &tv)) + { + tv = ecma_date_utc (tv); + } else { - ecma_value_t time_value = ecma_date_construct_helper (arguments_list_p, arguments_list_len); - - if (ECMA_IS_VALUE_ERROR (time_value)) - { - ecma_deref_object (obj_p); - return time_value; - } - - ecma_number_t time = ecma_get_number_from_value (time_value); - prim_value_num = ecma_date_time_clip (ecma_date_utc (time)); - - ecma_free_value (time_value); + return ECMA_VALUE_ERROR; } - if (!ecma_number_is_nan (prim_value_num) && ecma_number_is_infinity (prim_value_num)) - { - prim_value_num = ecma_number_make_nan (); - } - - ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_DATE_UL; - - ecma_number_t *date_num_p = ecma_alloc_number (); - *date_num_p = prim_value_num; - ECMA_SET_INTERNAL_VALUE_POINTER (ext_object_p->u.class_prop.u.value, date_num_p); - - return ecma_make_object_value (obj_p); + return ecma_builtin_date_create (ecma_date_time_clip (tv)); } /* ecma_builtin_date_dispatch_construct */ +/** + * Dispatcher of the built-in's routines + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_builtin_date_dispatch_routine (uint8_t builtin_routine_id, /**< built-in wide routine identifier */ + ecma_value_t this_arg, /**< 'this' argument value */ + const ecma_value_t arguments_list_p[], /**< list of arguments passed to routine */ + uint32_t arguments_number) /**< length of arguments' list */ +{ + JERRY_UNUSED (this_arg); + + switch (builtin_routine_id) + { + case ECMA_DATE_ROUTINE_NOW: + { + return ecma_make_number_value (ecma_builtin_date_now_helper ()); + } + case ECMA_DATE_ROUTINE_UTC: + { + return ecma_builtin_date_utc (arguments_list_p, arguments_number); + } + case ECMA_DATE_ROUTINE_PARSE: + { + if (arguments_number < 1) + { + return ecma_make_number_value (ecma_number_make_nan ()); + } + + ecma_string_t *str_p = ecma_op_to_string (arguments_list_p[0]); + + if (JERRY_UNLIKELY (str_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + ecma_value_t result = ecma_make_number_value (ecma_date_time_clip (ecma_builtin_date_parse (str_p))); + ecma_deref_ecma_string (str_p); + + return result; + } + default: + { + JERRY_UNREACHABLE (); + } + } +} /* ecma_builtin_date_dispatch_routine */ + /** * @} * @} * @} */ -#undef BREAK_IF_FALSE -#undef BREAK_IF_NAN - #endif /* JERRY_BUILTIN_DATE */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-date.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-date.inc.h index 53b1c3e9c..7e675c97e 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-date.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-date.inc.h @@ -30,9 +30,9 @@ NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, 7, ECMA_PROPERTY_FLAG_DEFAULT_LENGTH) -ROUTINE (LIT_MAGIC_STRING_PARSE, ecma_builtin_date_parse, 1, 1) -ROUTINE (LIT_MAGIC_STRING_UTC_U, ecma_builtin_date_utc, NON_FIXED, 7) -ROUTINE (LIT_MAGIC_STRING_NOW, ecma_builtin_date_now, 0, 0) +ROUTINE (LIT_MAGIC_STRING_PARSE, ECMA_DATE_ROUTINE_PARSE, 1, 1) +ROUTINE (LIT_MAGIC_STRING_UTC_U, ECMA_DATE_ROUTINE_UTC, NON_FIXED, 7) +ROUTINE (LIT_MAGIC_STRING_NOW, ECMA_DATE_ROUTINE_NOW, 0, 0) #if JERRY_ESNEXT STRING_VALUE (LIT_MAGIC_STRING_NAME, diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c index 539660852..d2b2fb63d 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c @@ -32,211 +32,168 @@ * @{ */ +/** + * Day names + */ const char day_names_p[7][3] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +/** + * Month names + */ const char month_names_p[12][3] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /** - * Helper function to get day number from time value. + * Calculate the elapsed days since Unix Epoch * - * See also: - * ECMA-262 v5, 15.9.1.2 - * - * @return time value for day number + * @return elapsed days since Unix Epoch */ -extern inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_day (ecma_number_t time) /**< time value */ +extern inline int32_t JERRY_ATTR_ALWAYS_INLINE +ecma_date_day_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - return (ecma_number_t) floor (time / ECMA_DATE_MS_PER_DAY); -} /* ecma_date_day */ - -/** - * Helper function to get time within day from time value. - * - * See also: - * ECMA-262 v5, 15.9.1.2 - * - * @return time value within the day - */ -extern inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_time_within_day (ecma_number_t time) /**< time value */ -{ - JERRY_ASSERT (!ecma_number_is_nan (time)); - - ecma_number_t modulo = fmod (time, ECMA_DATE_MS_PER_DAY); - if (modulo < 0) + if (time < 0) { - modulo += ECMA_DATE_MS_PER_DAY; + time -= ECMA_DATE_MS_PER_DAY - 1; } - return modulo; -} /* ecma_date_time_within_day */ + return (int32_t) (time / ECMA_DATE_MS_PER_DAY); +} /* ecma_date_day_from_time */ /** - * Helper function to get the day number of the first day of a year. + * Abstract operation: DayFromYear * - * See also: - * ECMA-262 v5, 15.9.1.3 + * See also: + * ECMA-262 v11, 20.4.1.3 * - * @return day number of the first day of a year + * @return first of day in the given year */ -static ecma_number_t -ecma_date_day_from_year (ecma_number_t year) /**< year value */ +static int32_t +ecma_date_day_from_year (int32_t year) /**< year value */ { - JERRY_ASSERT (!ecma_number_is_nan (year)); + if (JERRY_LIKELY (year >= 1970)) + { + return (int32_t) (365 * (year - 1970) + + ((year - 1969) / 4) + - ((year - 1901) / 100) + + ((year - 1601) / 400)); + } - return (ecma_number_t) (365 * (year - 1970) - + floor ((year - 1969) / 4) - - floor ((year - 1901) / 100) - + floor ((year - 1601) / 400)); + return (int32_t) (365 * (year - 1970) + + floor ((year - 1969) / 4.0) + - floor ((year - 1901) / 100.0) + + floor ((year - 1601) / 400.0)); } /* ecma_date_day_from_year */ /** - * Helper function to get the time value of the start of a year. + * Abstract operation: DaysInYear * - * See also: - * ECMA-262 v5, 15.9.1.3 + * See also: + * ECMA-262 v11, 20.4.1.3 * - * @return time value of the start of a year + * @return number of days in the given year */ -static inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_time_from_year (ecma_number_t year) /**< year value */ +static int +ecma_date_days_in_year (int32_t year) /**< year */ { - JERRY_ASSERT (!ecma_number_is_nan (year)); + if (year % 4 != 0 + || (year % 100 == 0 && (year % 400 != 0))) + { + return ECMA_DATE_DAYS_IN_YEAR; + } - return ECMA_DATE_MS_PER_DAY * ecma_date_day_from_year (year); -} /* ecma_date_time_from_year */ + return ECMA_DATE_DAYS_IN_LEAP_YEAR; +} /* ecma_date_days_in_year */ /** - * Helper function to determine a year value from the time value. + * Abstract operation: InLeapYear * - * See also: - * ECMA-262 v5, 15.9.1.3 + * See also: + * ECMA-262 v11, 20.4.1.3 * - * @return year value + * @return 1 - if the year is leap + * 0 - otherwise */ -ecma_number_t +static int32_t +ecma_date_in_leap_year (int32_t year) /**< time value */ +{ + return ecma_date_days_in_year (year) - ECMA_DATE_DAYS_IN_YEAR; +} /* ecma_date_in_leap_year */ + +/** + * First days of months in normal and leap years + */ +static const uint16_t first_day_in_month[2][12] = +{ + { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, /* normal year */ + } + , + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 /* leap year */ + } +}; + +/** + * Abstract operation: YearFromTime + * + * See also: + * ECMA-262 v11, 20.4.1.3 + * + * @return year corresponds to the given time + */ +int32_t ecma_date_year_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - /* ECMA-262 v5, 15.9.1.1 define the largest year that is - * representable (285616) forward from 01 January, 1970 UTC. - */ - ecma_number_t year = (ecma_number_t) (1970 + 285616); - ecma_number_t lower_year_boundary = (ecma_number_t) (1970 - 285616); + int32_t approx = (int32_t) (floor (time / ECMA_DATE_MS_PER_DAY / 365.2425) + 1970); + int64_t year_ms = ecma_date_day_from_year (approx) * ((int64_t) ECMA_DATE_MS_PER_DAY); - if (ecma_date_time_from_year (year) < time || ecma_date_time_from_year (lower_year_boundary) > time) + if (year_ms > time) { - return ecma_number_make_nan (); + approx--; } - while (ecma_date_time_from_year (year) > time) + if (year_ms + ecma_date_days_in_year (approx) * ((int64_t) ECMA_DATE_MS_PER_DAY) <= time) { - ecma_number_t year_boundary = (ecma_number_t) floor (lower_year_boundary + (year - lower_year_boundary) / 2); - if (ecma_date_time_from_year (year_boundary) > time) - { - year = year_boundary; - } - else - { - lower_year_boundary = year_boundary; - } - - year--; + approx++; } - return year; + return approx; } /* ecma_date_year_from_time */ /** - * Helper function to decide if time value is in a leap-year. + * Abstract operation: MonthFromTime * - * See also: - * ECMA-262 v5, 15.9.1.3 + * See also: + * ECMA-262 v11, 20.4.1.4 * - * @return 1 if time within a leap year - * 0 otherwise + * @return month corresponds to the given time */ -static int -ecma_date_in_leap_year (ecma_number_t year) /**< time value */ -{ - int mod_400 = (int) fmod (floor (year), 400); - - JERRY_ASSERT (mod_400 >= -399 && mod_400 <= 399); - - if ((mod_400 % 4) != 0) - { - return 0; - } - - if ((mod_400 % 100) != 0) - { - return 1; - } - - if (mod_400 != 0) - { - return 0; - } - - return 1; -} /* ecma_date_in_leap_year */ - -/** - * End day for the first 11 months. - */ -static const int16_t ecma_date_month_end_day[10] = -{ - 58, 89, 119, 150, 180, 211, 242, 272, 303, 333 -}; - -/** - * Helper function to get month from time value. - * - * See also: - * ECMA-262 v5, 15.9.1.4 - * - * @return month number - */ -ecma_number_t +int32_t ecma_date_month_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t year = ecma_date_year_from_time (time); + int32_t year = ecma_date_year_from_time (time); + int32_t day_within_year = ecma_date_day_from_time (time) - ecma_date_day_from_year (year); - if (ecma_number_is_nan (year)) + JERRY_ASSERT (day_within_year >= 0 && day_within_year < ECMA_DATE_DAYS_IN_LEAP_YEAR); + + int32_t in_leap_year = ecma_date_in_leap_year (year); + + for (int i = 1; i < 12; i++) { - return ecma_number_make_nan (); - } - - int day_within_year = (int) (ecma_date_day (time) - ecma_date_day_from_year (year)); - - JERRY_ASSERT (day_within_year >= 0); - - if (day_within_year <= 30) - { - return 0; - } - - day_within_year -= ecma_date_in_leap_year (year); - - JERRY_ASSERT (day_within_year < 365); - - for (int i = 0; i < 10; i++) - { - if (day_within_year <= ecma_date_month_end_day[i]) + if (day_within_year < first_day_in_month[in_leap_year][i]) { - return i + 1; + return i - 1; } } @@ -244,84 +201,64 @@ ecma_date_month_from_time (ecma_number_t time) /**< time value */ } /* ecma_date_month_from_time */ /** - * Helper function to get date number from time value. + * Abstract operation: DateFromTime * - * See also: - * ECMA-262 v5, 15.9.1.5 + * See also: + * ECMA-262 v11, 20.4.1.4 * - * @return date number + * @return date corresponds to the given time */ -ecma_number_t +int32_t ecma_date_date_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t year = ecma_date_year_from_time (time); + int32_t year = ecma_date_year_from_time (time); + int32_t day_within_year = ecma_date_day_from_time (time) - ecma_date_day_from_year (year); - if (ecma_number_is_nan (year)) + JERRY_ASSERT (day_within_year >= 0 && day_within_year < ECMA_DATE_DAYS_IN_LEAP_YEAR); + + int32_t in_leap_year = ecma_date_in_leap_year (year); + + int32_t month = 11; + + for (int i = 1; i < 12; i++) { - return ecma_number_make_nan (); - } - - int day_within_year = (int) (ecma_date_day (time) - ecma_date_day_from_year (year)); - - JERRY_ASSERT (day_within_year >= 0); - - if (day_within_year <= 30) - { - return day_within_year + 1; - } - - int leap_year = ecma_date_in_leap_year (year); - - if (day_within_year <= 58 + leap_year) - { - return day_within_year - 30; - } - - day_within_year -= leap_year; - - JERRY_ASSERT (day_within_year < 365); - - for (int i = 1; i < 10; i++) - { - if (day_within_year <= ecma_date_month_end_day[i]) + if (day_within_year < first_day_in_month[in_leap_year][i]) { - return day_within_year - ecma_date_month_end_day[i - 1]; + month = i - 1; + break; } } - return day_within_year - 333; + return day_within_year + 1 - first_day_in_month[in_leap_year][month]; } /* ecma_date_date_from_time */ /** - * Helper function to get weekday from time value. + * Abstract operation: WeekDay * - * See also: - * ECMA-262 v5, 15.9.1.6 + * See also: + * ECMA-262 v11, 20.4.1.4 * - * Used by: - * - The Date.prototype.getDay routine. (Generated.) - * - The Date.prototype.getUTCDay routine. (Generated.) - * - * @return weekday number + * @return weekday corresponds to the given time */ -ecma_number_t +int32_t ecma_date_week_day (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t week_day = (ecma_number_t) fmod ((ecma_date_day (time) + 4), 7); + int32_t day = ecma_date_day_from_time (time); - return (week_day < 0) ? (7 + week_day) : week_day; + int week_day = (day + 4) % 7; + + return week_day >= 0 ? week_day : week_day + 7; } /* ecma_date_week_day */ /** - * Helper function to get the local time zone offset at a given UTC timestamp. - * You can add this number to the given UTC timestamp to get local time. + * Abstract operation: LocalTZA * - * See also: - * ECMA-262 v5, 15.9.1.9 + * See also: + * ECMA-262 v11, 20.4.1.7 * * @return local time zone adjustment */ @@ -332,10 +269,10 @@ ecma_date_local_time_zone_adjustment (ecma_number_t time) /**< time value */ } /* ecma_date_local_time_zone_adjustment */ /** - * Helper function to get UTC time from local time. + * Abstract operation: UTC * - * See also: - * ECMA-262 v5, 15.9.1.9 + * See also: + * ECMA-262 v11, 20.4.1.9 * * @return UTC time */ @@ -346,83 +283,99 @@ ecma_date_utc (ecma_number_t time) /**< time value */ } /* ecma_date_utc */ /** - * Helper function to get hour from time value. + * Calculate the time component from the given time * - * See also: - * ECMA-262 v5, 15.9.1.10 - * - * @return hour value + * @return time component of the given time */ -ecma_number_t +int32_t +ecma_date_time_in_day_from_time (ecma_number_t time) /**< time value */ +{ + JERRY_ASSERT (!ecma_number_is_nan (time)); + + ecma_number_t day = ecma_date_day_from_time (time); + + return (int32_t) (time - (day * ECMA_DATE_MS_PER_DAY)); +} /* ecma_date_time_in_day_from_time */ + +/** + * Abstract operation: HourFromTime + * + * See also: + * ECMA-262 v11, 20.4.1.10 + * + * @return hours component of the given time + */ +int32_t ecma_date_hour_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t hour = (ecma_number_t) fmod (floor (time / ECMA_DATE_MS_PER_HOUR), - ECMA_DATE_HOURS_PER_DAY); - return (hour < 0) ? ECMA_DATE_HOURS_PER_DAY + hour : hour; + int32_t time_in_day = ecma_date_time_in_day_from_time (time); + + return (int32_t) (time_in_day / ECMA_DATE_MS_PER_HOUR); } /* ecma_date_hour_from_time */ /** - * Helper function to get minute from time value. + * Abstract operation: HourFromTime * - * See also: - * ECMA-262 v5, 15.9.1.10 + * See also: + * ECMA-262 v11, 20.4.1.10 * - * @return minute value + * @return minutes component of the given time */ -ecma_number_t +int32_t ecma_date_min_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t min = (ecma_number_t) fmod (floor (time / ECMA_DATE_MS_PER_MINUTE), - ECMA_DATE_MINUTES_PER_HOUR); - return (min < 0) ? ECMA_DATE_MINUTES_PER_HOUR + min : min; + int32_t time_in_day = ecma_date_time_in_day_from_time (time); + + return ((int32_t) (time_in_day / ECMA_DATE_MS_PER_MINUTE)) % ECMA_DATE_MINUTES_PER_HOUR; } /* ecma_date_min_from_time */ /** - * Helper function to get second from time value. + * Abstract operation: HourFromTime * - * See also: - * ECMA-262 v5, 15.9.1.10 + * See also: + * ECMA-262 v11, 20.4.1.10 * - * @return second value + * @return seconds component of the given time */ -ecma_number_t +int32_t ecma_date_sec_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t sec = (ecma_number_t) fmod (floor (time / ECMA_DATE_MS_PER_SECOND), - ECMA_DATE_SECONDS_PER_MINUTE); - return (sec < 0) ? ECMA_DATE_SECONDS_PER_MINUTE + sec : sec; + int32_t time_in_day = ecma_date_time_in_day_from_time (time); + + return ((int32_t) (time_in_day / ECMA_DATE_MS_PER_SECOND)) % ECMA_DATE_SECONDS_PER_MINUTE; } /* ecma_date_sec_from_time */ /** - * Helper function to get millisecond from time value. + * Abstract operation: HourFromTime * - * See also: - * ECMA-262 v5, 15.9.1.10 + * See also: + * ECMA-262 v11, 20.4.1.10 * - * @return millisecond value + * @return milliseconds component of the given time */ -ecma_number_t +int32_t ecma_date_ms_from_time (ecma_number_t time) /**< time value */ { JERRY_ASSERT (!ecma_number_is_nan (time)); - ecma_number_t milli = (ecma_number_t) fmod (time, ECMA_DATE_MS_PER_SECOND); - return (milli < 0) ? ECMA_DATE_MS_PER_SECOND + milli : milli; + int32_t time_in_day = ecma_date_time_in_day_from_time (time); + + return (int32_t) (time_in_day % ECMA_DATE_MS_PER_SECOND); } /* ecma_date_ms_from_time */ /** - * Helper function to make time value from hour, min, sec and ms. + * Abstract operation: MakeTime * - * See also: - * ECMA-262 v5, 15.9.1.11 + * See also: + * ECMA-262 v11, 20.4.1.11 * - * @return time value + * @return constructed time in milliseconds */ ecma_number_t ecma_date_make_time (ecma_number_t hour, /**< hour value */ @@ -430,37 +383,32 @@ ecma_date_make_time (ecma_number_t hour, /**< hour value */ ecma_number_t sec, /**< second value */ ecma_number_t ms) /**< millisecond value */ { - if (ecma_number_is_nan (hour) - || ecma_number_is_nan (min) - || ecma_number_is_nan (sec) - || ecma_number_is_nan (ms) - || ecma_number_is_infinity (hour) - || ecma_number_is_infinity (min) - || ecma_number_is_infinity (sec) - || ecma_number_is_infinity (ms)) + if (!ecma_number_is_finite (hour) + || !ecma_number_is_finite (min) + || !ecma_number_is_finite (sec) + || !ecma_number_is_finite (ms)) { return ecma_number_make_nan (); } - /* Replaced toInteger to ecma_number_trunc because it does the same thing. */ ecma_number_t h = ecma_number_trunc (hour); ecma_number_t m = ecma_number_trunc (min); ecma_number_t s = ecma_number_trunc (sec); ecma_number_t milli = ecma_number_trunc (ms); - return (h * ECMA_DATE_MS_PER_HOUR - + m * ECMA_DATE_MS_PER_MINUTE - + s * ECMA_DATE_MS_PER_SECOND - + milli); + return (ecma_number_t) ((h * ECMA_DATE_MS_PER_HOUR + + m * ECMA_DATE_MS_PER_MINUTE + + s * ECMA_DATE_MS_PER_SECOND + + milli)); } /* ecma_date_make_time */ /** - * Helper function to make day value from year, month and date. + * Abstract operation: MakeDay * - * See also: - * ECMA-262 v5, 15.9.1.12 + * See also: + * ECMA-262 v11, 20.4.1.12 * - * @return day value + * @return elpased number of days since Unix Epoch */ ecma_number_t ecma_date_make_day (ecma_number_t year, /**< year value */ @@ -468,96 +416,70 @@ ecma_date_make_day (ecma_number_t year, /**< year value */ ecma_number_t date) /**< date value */ { /* 1. */ - if (ecma_number_is_nan (year) - || ecma_number_is_nan (month) - || ecma_number_is_nan (date) - || ecma_number_is_infinity (year) - || ecma_number_is_infinity (month) - || ecma_number_is_infinity (date)) + if (!ecma_number_is_finite (year) + || !ecma_number_is_finite (month) + || !ecma_number_is_finite (date) + || fabs (year) > INT32_MAX) { return ecma_number_make_nan (); } /* 2., 3., 4. */ - ecma_number_t y = ecma_number_trunc (year); + int32_t y = (int32_t) (year); ecma_number_t m = ecma_number_trunc (month); ecma_number_t dt = ecma_number_trunc (date); + /* 5. */ - ecma_number_t ym = y + (ecma_number_t) floor (m / 12); + int32_t ym = y + (int32_t) (floor (m / 12)); + /* 6. */ - ecma_number_t mn = (ecma_number_t) fmod (m, 12); - mn = (mn < 0) ? 12 + mn : mn; + int32_t mn = (int32_t) fmod (m, 12); - /* 7. */ - ecma_number_t time = ecma_date_time_from_year (ym); - - /** - * The algorithm below searches the following date: ym-mn-1 - * To find this time it starts from the beginning of the year (ym) - * then find the first day of the month. - */ - if (!ecma_number_is_nan (time) - && ecma_date_year_from_time (time) == ym) + if (mn < 0) { - /* Get the month */ - time += 31 * mn * ECMA_DATE_MS_PER_DAY; - - /* Get the month's first day */ - time += ((ecma_number_t) 1.0 - ecma_date_date_from_time (time)) * ECMA_DATE_MS_PER_DAY; - - if (!ecma_number_is_nan (time) - && ecma_date_month_from_time (time) == mn - && ecma_date_date_from_time (time) == 1) - { - /* 8. */ - return ecma_date_day (time) + dt - ((ecma_number_t) 1.0); - } + mn += 12; } - return ecma_number_make_nan (); + /* 7. */ + ecma_number_t days = (ecma_date_day_from_year (ym) + + first_day_in_month[ecma_date_in_leap_year (ym)][mn] + + (dt - 1)); + return days * ECMA_DATE_MS_PER_DAY; } /* ecma_date_make_day */ /** - * Helper function to make date value from day and time. + * Abstract operation: MakeTime * - * See also: - * ECMA-262 v5, 15.9.1.13 + * See also: + * ECMA-262 v11, 20.4.1.13 * - * @return date value + * @return elpased number of milliceconds since Unix Epoch */ ecma_number_t ecma_date_make_date (ecma_number_t day, /**< day value */ ecma_number_t time) /**< time value */ { - if (ecma_number_is_nan (day) - || ecma_number_is_nan (time)) + if (!ecma_number_is_finite (day) + || !ecma_number_is_finite (time)) { return ecma_number_make_nan (); } - ecma_number_t result = day * ECMA_DATE_MS_PER_DAY + time; - - if (ecma_number_is_infinity (result)) - { - return ecma_number_make_nan (); - } - - return result; + return day + time; } /* ecma_date_make_date */ /** - * Helper function to calculate number of milliseconds from time value. + * Abstract operation: TimeClip * - * See also: - * ECMA-262 v5, 15.9.1.14 + * See also: + * ECMA-262 v11, 20.4.1.14 * - * @return number of milliseconds + * @return elpased number of milliceconds since Unix Epoch */ ecma_number_t ecma_date_time_clip (ecma_number_t time) /**< time value */ { - if (ecma_number_is_nan (time) - || ecma_number_is_infinity (time) + if (!ecma_number_is_finite (time) || fabs (time) > ECMA_DATE_MAX_VALUE) { return ecma_number_make_nan (); @@ -566,22 +488,6 @@ ecma_date_time_clip (ecma_number_t time) /**< time value */ return ecma_number_trunc (time); } /* ecma_date_time_clip */ -/** - * Helper function to calculate timezone offset. - * - * See also: - * ECMA-262 v5, 15.9.5.26 - * - * @return timezone offset - */ -extern inline ecma_number_t JERRY_ATTR_ALWAYS_INLINE -ecma_date_timezone_offset (ecma_number_t time) /**< time value */ -{ - JERRY_ASSERT (!ecma_number_is_nan (time)); - - return (-ecma_date_local_time_zone_adjustment (time)) / ECMA_DATE_MS_PER_MINUTE; -} /* ecma_date_timezone_offset */ - /** * Common function to convert date to string. * @@ -615,7 +521,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ { case LIT_CHAR_UPPERCASE_Y: /* Year. */ { - number = (int32_t) ecma_date_year_from_time (datetime_number); + number = ecma_date_year_from_time (datetime_number); if (number >= 100000 || number <= -100000) { @@ -633,7 +539,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ } case LIT_CHAR_LOWERCASE_Y: /* ISO Year: -000001, 0000, 0001, 9999, +012345 */ { - number = (int32_t) ecma_date_year_from_time (datetime_number); + number = ecma_date_year_from_time (datetime_number); if (0 <= number && number <= 9999) { number_length = 4; @@ -646,7 +552,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ } case LIT_CHAR_UPPERCASE_M: /* Month. */ { - int32_t month = (int32_t) ecma_date_month_from_time (datetime_number); + int32_t month = ecma_date_month_from_time (datetime_number); JERRY_ASSERT (month >= 0 && month <= 11); @@ -658,19 +564,19 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ /* The 'ecma_date_month_from_time' (ECMA 262 v5, 15.9.1.4) returns a * number from 0 to 11, but we have to print the month from 1 to 12 * for ISO 8601 standard (ECMA 262 v5, 15.9.1.15). */ - number = ((int32_t) ecma_date_month_from_time (datetime_number)) + 1; + number = ecma_date_month_from_time (datetime_number) + 1; number_length = 2; break; } case LIT_CHAR_UPPERCASE_D: /* Day. */ { - number = (int32_t) ecma_date_date_from_time (datetime_number); + number = ecma_date_date_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_UPPERCASE_W: /* Day of week. */ { - int32_t day = (int32_t) ecma_date_week_day (datetime_number); + int32_t day = ecma_date_week_day (datetime_number); JERRY_ASSERT (day >= 0 && day <= 6); @@ -679,25 +585,25 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ } case LIT_CHAR_LOWERCASE_H: /* Hour. */ { - number = (int32_t) ecma_date_hour_from_time (datetime_number); + number = ecma_date_hour_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_LOWERCASE_M: /* Minutes. */ { - number = (int32_t) ecma_date_min_from_time (datetime_number); + number = ecma_date_min_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_LOWERCASE_S: /* Seconds. */ { - number = (int32_t) ecma_date_sec_from_time (datetime_number); + number = ecma_date_sec_from_time (datetime_number); number_length = 2; break; } case LIT_CHAR_LOWERCASE_I: /* Milliseconds. */ { - number = (int32_t) ecma_date_ms_from_time (datetime_number); + number = ecma_date_ms_from_time (datetime_number); number_length = 3; break; } @@ -715,7 +621,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ time_zone = -time_zone; } - number = time_zone / (int32_t) ECMA_DATE_MS_PER_HOUR; + number = time_zone / ECMA_DATE_MS_PER_HOUR; number_length = 2; break; } @@ -730,7 +636,7 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ time_zone = -time_zone; } - number = time_zone % (int32_t) ECMA_DATE_MS_PER_HOUR / (int32_t) ECMA_DATE_MS_PER_MINUTE; + number = (time_zone % ECMA_DATE_MS_PER_HOUR) / ECMA_DATE_MS_PER_MINUTE; number_length = 2; break; } diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index bb9f6e4b6..b2f31c945 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -111,16 +111,16 @@ ecma_builtin_is_regexp_exec (ecma_extended_object_t *obj_p); */ /** Hours in a day. */ -#define ECMA_DATE_HOURS_PER_DAY ((ecma_number_t) 24) +#define ECMA_DATE_HOURS_PER_DAY (24) /** Minutes in an hour. */ -#define ECMA_DATE_MINUTES_PER_HOUR ((ecma_number_t) 60) +#define ECMA_DATE_MINUTES_PER_HOUR (60) /** Seconds in a minute. */ -#define ECMA_DATE_SECONDS_PER_MINUTE ((ecma_number_t) 60) +#define ECMA_DATE_SECONDS_PER_MINUTE (60) /** Milliseconds in a second. */ -#define ECMA_DATE_MS_PER_SECOND ((ecma_number_t) 1000) +#define ECMA_DATE_MS_PER_SECOND (1000) /** ECMA_DATE_MS_PER_MINUTE == 60000 */ #define ECMA_DATE_MS_PER_MINUTE (ECMA_DATE_MS_PER_SECOND * ECMA_DATE_SECONDS_PER_MINUTE) @@ -129,7 +129,11 @@ ecma_builtin_is_regexp_exec (ecma_extended_object_t *obj_p); #define ECMA_DATE_MS_PER_HOUR (ECMA_DATE_MS_PER_MINUTE * ECMA_DATE_MINUTES_PER_HOUR) /** ECMA_DATE_MS_PER_DAY == 86400000 */ -#define ECMA_DATE_MS_PER_DAY (ECMA_DATE_MS_PER_HOUR * ECMA_DATE_HOURS_PER_DAY) +#define ECMA_DATE_MS_PER_DAY ((ECMA_DATE_MS_PER_HOUR * ECMA_DATE_HOURS_PER_DAY)) + +#define ECMA_DATE_DAYS_IN_YEAR (365) + +#define ECMA_DATE_DAYS_IN_LEAP_YEAR (366) /** * This gives a range of 8,640,000,000,000,000 milliseconds @@ -150,23 +154,23 @@ typedef enum extern const char day_names_p[7][3]; extern const char month_names_p[12][3]; -ecma_number_t ecma_date_day (ecma_number_t time); -ecma_number_t ecma_date_time_within_day (ecma_number_t time); -ecma_number_t ecma_date_year_from_time (ecma_number_t time); -ecma_number_t ecma_date_month_from_time (ecma_number_t time); -ecma_number_t ecma_date_date_from_time (ecma_number_t time); -ecma_number_t ecma_date_week_day (ecma_number_t time); +int32_t ecma_date_day_from_time (ecma_number_t time); +int32_t ecma_date_year_from_time (ecma_number_t time); +int32_t ecma_date_month_from_time (ecma_number_t time); +int32_t ecma_date_date_from_time (ecma_number_t time); +int32_t ecma_date_week_day (ecma_number_t time); +int32_t ecma_date_hour_from_time (ecma_number_t time); +int32_t ecma_date_min_from_time (ecma_number_t time); +int32_t ecma_date_sec_from_time (ecma_number_t time); +int32_t ecma_date_ms_from_time (ecma_number_t time); +int32_t ecma_date_time_in_day_from_time (ecma_number_t time); + ecma_number_t ecma_date_local_time_zone_adjustment (ecma_number_t time); ecma_number_t ecma_date_utc (ecma_number_t time); -ecma_number_t ecma_date_hour_from_time (ecma_number_t time); -ecma_number_t ecma_date_min_from_time (ecma_number_t time); -ecma_number_t ecma_date_sec_from_time (ecma_number_t time); -ecma_number_t ecma_date_ms_from_time (ecma_number_t time); ecma_number_t ecma_date_make_time (ecma_number_t hour, ecma_number_t min, ecma_number_t sec, ecma_number_t ms); ecma_number_t ecma_date_make_day (ecma_number_t year, ecma_number_t month, ecma_number_t date); ecma_number_t ecma_date_make_date (ecma_number_t day, ecma_number_t time); ecma_number_t ecma_date_time_clip (ecma_number_t time); -ecma_number_t ecma_date_timezone_offset (ecma_number_t time); ecma_value_t ecma_date_value_to_string (ecma_number_t datetime_number); ecma_value_t ecma_date_value_to_utc_string (ecma_number_t datetime_number); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-intrinsic.c b/jerry-core/ecma/builtin-objects/ecma-builtin-intrinsic.c index 4573bbf28..0ccf6e56d 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-intrinsic.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-intrinsic.c @@ -210,16 +210,20 @@ ecma_builtin_intrinsic_dispatch_routine (uint8_t builtin_routine_id, /**< built- return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a Date object")); } - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) ecma_get_object_from_value (this_arg); - ecma_number_t *prim_value_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, - ext_object_p->u.class_prop.u.value); +#if JERRY_ESNEXT + ecma_number_t *date_value_p = &((ecma_date_object_t *) ecma_get_object_from_value (this_arg))->date_value; +#else /* !JERRY_ESNEXT */ + ecma_extended_object_t *arg_ext_object_p = (ecma_extended_object_t *) ecma_get_object_from_value (argument); + ecma_number_t *date_value_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_number_t, + arg_ext_object_p->u.class_prop.u.date); +#endif /* JERRY_ESNEXT */ - if (ecma_number_is_nan (*prim_value_p)) + if (ecma_number_is_nan (*date_value_p)) { return ecma_make_magic_string_value (LIT_MAGIC_STRING_INVALID_DATE_UL); } - return ecma_date_value_to_utc_string (*prim_value_p); + return ecma_date_value_to_utc_string (*date_value_p); } case ECMA_INTRINSIC_STRING_TRIM_START: case ECMA_INTRINSIC_STRING_TRIM_END: diff --git a/tests/jerry/date-parse.js b/tests/jerry/date-parse.js index 5a0535598..807d5a9dd 100644 --- a/tests/jerry/date-parse.js +++ b/tests/jerry/date-parse.js @@ -61,6 +61,7 @@ var wrongFormats = ["", "+0002015-01-01", "-0002015-01-01", "2015-01T00:00:00.000-03X00", + "2015-01T00:00:00.000-02-02", "2015-01-01T00-03:00", "Fri Jan 01 1 00:00:00 GMT+0000", "Fri Jan 01 11 00:00:00 GMT+0000", diff --git a/tests/unit-core/test-date-helpers.c b/tests/unit-core/test-date-helpers.c index 426dabd7e..bba58edbd 100644 --- a/tests/unit-core/test-date-helpers.c +++ b/tests/unit-core/test-date-helpers.c @@ -33,18 +33,6 @@ int main (void) { - /* int ecma_date_day (time)*/ - - TEST_ASSERT (ecma_date_day (0) == 0); - TEST_ASSERT (ecma_date_day (MS_PER_DAY) == 1); - - /* ecma_number_t ecma_date_time_within_day (time) */ - - TEST_ASSERT (ecma_date_time_within_day (0) == 0); - TEST_ASSERT (ecma_date_time_within_day (42) == 42); - TEST_ASSERT (ecma_date_time_within_day (42.51) == 42.51); - TEST_ASSERT (ecma_date_time_within_day (MS_PER_DAY + 42) == 42); - /* int ecma_date_year_from_time (time) */ TEST_ASSERT (ecma_date_year_from_time (0) == 1970); @@ -119,13 +107,13 @@ main (void) /* ecma_number_t ecma_date_make_day (year, month, date) */ TEST_ASSERT (ecma_date_make_day (1970, 0, 1) == 0); - TEST_ASSERT (ecma_date_make_day (1970, -1, 1) == -31); - TEST_ASSERT (ecma_date_make_day (1970, 0, 2.5) == 1); - TEST_ASSERT (ecma_date_make_day (1970, 1, 35) == 65); - TEST_ASSERT (ecma_date_make_day (1970, 13, 35) == 430); - TEST_ASSERT (ecma_date_make_day (2016, 2, 1) == 16861); - TEST_ASSERT (ecma_date_make_day (2016, 8, 31) == 17075); - TEST_ASSERT (ecma_date_make_day (2016, 9, 1) == 17075); + TEST_ASSERT (ecma_date_make_day (1970, -1, 1) == -2678400000); + TEST_ASSERT (ecma_date_make_day (1970, 0, 2.5) == 86400000); + TEST_ASSERT (ecma_date_make_day (1970, 1, 35) == 5616000000); + TEST_ASSERT (ecma_date_make_day (1970, 13, 35) == 37152000000); + TEST_ASSERT (ecma_date_make_day (2016, 2, 1) == 1456790400000); + TEST_ASSERT (ecma_date_make_day (2016, 8, 31) == 1475280000000); + TEST_ASSERT (ecma_date_make_day (2016, 9, 1) == 1475280000000); /* ecma_number_t ecma_date_make_date (day, time) */