Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

14
deps/v8/src/date/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>Runtime"
}
buganizer_public: {
component_id: 1456800
}

3
deps/v8/src/date/OWNERS vendored Normal file
View File

@ -0,0 +1,3 @@
ishell@chromium.org
jshin@chromium.org
verwaest@chromium.org

661
deps/v8/src/date/date.cc vendored Normal file
View File

@ -0,0 +1,661 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/date/date.h"
#include <limits>
#include "src/base/overflowing-math.h"
#include "src/date/dateparser-inl.h"
#include "src/numbers/conversions.h"
#include "src/objects/objects-inl.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/intl-objects.h"
#endif
#include "src/strings/string-stream.h"
namespace v8 {
namespace internal {
static const int kDaysIn4Years = 4 * 365 + 1;
static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
static const int kDays1970to2000 = 30 * 365 + 7;
static const int kDaysOffset =
1000 * kDaysIn400Years + 5 * kDaysIn400Years - kDays1970to2000;
static const int kYearsOffset = 400000;
static const char kDaysInMonths[] = {31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
DateCache::DateCache()
: stamp_(kNullAddress),
tz_cache_(
#ifdef V8_INTL_SUPPORT
Intl::CreateTimeZoneCache()
#else
base::OS::CreateTimezoneCache()
#endif
) {
ResetDateCache(base::TimezoneCache::TimeZoneDetection::kSkip);
}
void DateCache::ResetDateCache(
base::TimezoneCache::TimeZoneDetection time_zone_detection) {
if (stamp_.value() >= Smi::kMaxValue) {
stamp_ = Smi::zero();
} else {
stamp_ = Smi::FromInt(stamp_.value() + 1);
}
DCHECK(stamp_ != Smi::FromInt(kInvalidStamp));
for (int i = 0; i < kCacheSize; ++i) {
ClearSegment(&cache_[i]);
}
cache_usage_counter_ = 0;
before_ = &cache_[0];
after_ = &cache_[1];
ymd_valid_ = false;
#ifdef V8_INTL_SUPPORT
if (!v8_flags.icu_timezone_data) {
#endif
local_offset_ms_ = kInvalidLocalOffsetInMs;
#ifdef V8_INTL_SUPPORT
}
#endif
tz_cache_->Clear(time_zone_detection);
tz_name_ = nullptr;
dst_tz_name_ = nullptr;
}
void DateCache::ClearSegment(CacheItem* segment) {
segment->start_ms = 0;
segment->end_ms = -1;
segment->offset_ms = 0;
segment->last_used = 0;
}
void DateCache::YearMonthDayFromDays(int days, int* year, int* month,
int* day) {
if (ymd_valid_) {
// Check conservatively if the given 'days' has
// the same year and month as the cached 'days'.
int new_day = ymd_day_ + (days - ymd_days_);
if (new_day >= 1 && new_day <= 28) {
ymd_day_ = new_day;
ymd_days_ = days;
*year = ymd_year_;
*month = ymd_month_;
*day = new_day;
return;
}
}
int save_days = days;
days += kDaysOffset;
*year = 400 * (days / kDaysIn400Years) - kYearsOffset;
days %= kDaysIn400Years;
DCHECK_EQ(save_days, DaysFromYearMonth(*year, 0) + days);
days--;
int yd1 = days / kDaysIn100Years;
days %= kDaysIn100Years;
*year += 100 * yd1;
days++;
int yd2 = days / kDaysIn4Years;
days %= kDaysIn4Years;
*year += 4 * yd2;
days--;
int yd3 = days / 365;
days %= 365;
*year += yd3;
bool is_leap = (!yd1 || yd2) && !yd3;
DCHECK_GE(days, -1);
DCHECK(is_leap || (days >= 0));
DCHECK((days < 365) || (is_leap && (days < 366)));
DCHECK(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0))));
DCHECK(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days));
DCHECK(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days));
days += is_leap;
// Check if the date is after February.
if (days >= 31 + 28 + (is_leap ? 1 : 0)) {
days -= 31 + 28 + (is_leap ? 1 : 0);
// Find the date starting from March.
for (int i = 2; i < 12; i++) {
if (days < kDaysInMonths[i]) {
*month = i;
*day = days + 1;
break;
}
days -= kDaysInMonths[i];
}
} else {
// Check January and February.
if (days < 31) {
*month = 0;
*day = days + 1;
} else {
*month = 1;
*day = days - 31 + 1;
}
}
DCHECK(DaysFromYearMonth(*year, *month) + *day - 1 == save_days);
ymd_valid_ = true;
ymd_year_ = *year;
ymd_month_ = *month;
ymd_day_ = *day;
ymd_days_ = save_days;
}
int DateCache::DaysFromYearMonth(int year, int month) {
static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
181, 212, 243, 273, 304, 334};
static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
182, 213, 244, 274, 305, 335};
year += month / 12;
month %= 12;
if (month < 0) {
year--;
month += 12;
}
DCHECK_GE(month, 0);
DCHECK_LT(month, 12);
// year_delta is an arbitrary number such that:
// a) year_delta = -1 (mod 400)
// b) year + year_delta > 0 for years in the range defined by
// ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
// Jan 1 1970. This is required so that we don't run into integer
// division of negative numbers.
// c) there shouldn't be an overflow for 32-bit integers in the following
// operations.
static const int year_delta = 399999;
static const int base_day =
365 * (1970 + year_delta) + (1970 + year_delta) / 4 -
(1970 + year_delta) / 100 + (1970 + year_delta) / 400;
int year1 = year + year_delta;
int day_from_year =
365 * year1 + year1 / 4 - year1 / 100 + year1 / 400 - base_day;
if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) {
return day_from_year + day_from_month[month];
}
return day_from_year + day_from_month_leap[month];
}
void DateCache::BreakDownTime(int64_t time_ms, int* year, int* month, int* day,
int* weekday, int* hour, int* min, int* sec,
int* ms) {
int const days = DaysFromTime(time_ms);
int const time_in_day_ms = TimeInDay(time_ms, days);
YearMonthDayFromDays(days, year, month, day);
*weekday = Weekday(days);
*hour = time_in_day_ms / (60 * 60 * 1000);
*min = (time_in_day_ms / (60 * 1000)) % 60;
*sec = (time_in_day_ms / 1000) % 60;
*ms = time_in_day_ms % 1000;
}
// Implements LocalTimeZonedjustment(t, isUTC)
// ECMA 262 - ES#sec-local-time-zone-adjustment
int DateCache::GetLocalOffsetFromOS(int64_t time_ms, bool is_utc) {
double offset;
#ifdef V8_INTL_SUPPORT
if (v8_flags.icu_timezone_data) {
offset = tz_cache_->LocalTimeOffset(static_cast<double>(time_ms), is_utc);
} else {
#endif
// When ICU timezone data is not used, we need to compute the timezone
// offset for a given local time.
//
// The following shows that using DST for (t - LocalTZA - hour) produces
// correct conversion where LocalTZA is the timezone offset in winter (no
// DST) and the timezone offset is assumed to have no historical change.
// Note that it does not work for the past and the future if LocalTZA (no
// DST) is different from the current LocalTZA (no DST). For instance,
// this will break for Europe/Moscow in 2012 ~ 2013 because LocalTZA was
// 4h instead of the current 3h (as of 2018).
//
// Consider transition to DST at local time L1.
// Let L0 = L1 - hour, L2 = L1 + hour,
// U1 = UTC time that corresponds to L1,
// U0 = U1 - hour.
// Transitioning to DST moves local clock one hour forward L1 => L2, so
// U0 = UTC time that corresponds to L0 = L0 - LocalTZA,
// U1 = UTC time that corresponds to L1 = L1 - LocalTZA,
// U1 = UTC time that corresponds to L2 = L2 - LocalTZA - hour.
// Note that DST(U0 - hour) = 0, DST(U0) = 0, DST(U1) = 1.
// U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour),
// U1 = L1 - LocalTZA - DST(L1 - LocalTZA - hour),
// U1 = L2 - LocalTZA - DST(L2 - LocalTZA - hour).
//
// Consider transition from DST at local time L1.
// Let L0 = L1 - hour,
// U1 = UTC time that corresponds to L1,
// U0 = U1 - hour, U2 = U1 + hour.
// Transitioning from DST moves local clock one hour back L1 => L0, so
// U0 = UTC time that corresponds to L0 (before transition)
// = L0 - LocalTZA - hour.
// U1 = UTC time that corresponds to L0 (after transition)
// = L0 - LocalTZA = L1 - LocalTZA - hour
// U2 = UTC time that corresponds to L1 = L1 - LocalTZA.
// Note that DST(U0) = 1, DST(U1) = 0, DST(U2) = 0.
// U0 = L0 - LocalTZA - DST(L0 - LocalTZA - hour) = L0 - LocalTZA - DST(U0).
// U2 = L1 - LocalTZA - DST(L1 - LocalTZA - hour) = L1 - LocalTZA - DST(U1).
// It is impossible to get U1 from local time.
if (local_offset_ms_ == kInvalidLocalOffsetInMs) {
// This gets the constant LocalTZA (arguments are ignored).
local_offset_ms_ =
tz_cache_->LocalTimeOffset(static_cast<double>(time_ms), is_utc);
}
offset = local_offset_ms_;
if (!is_utc) {
const int kMsPerHour = 3600 * 1000;
time_ms -= (offset + kMsPerHour);
}
offset += DaylightSavingsOffsetInMs(time_ms);
#ifdef V8_INTL_SUPPORT
}
#endif
DCHECK_LT(offset, kInvalidLocalOffsetInMs);
return static_cast<int>(offset);
}
void DateCache::ExtendTheAfterSegment(int64_t time_ms, int offset_ms) {
if (!InvalidSegment(after_) && after_->offset_ms == offset_ms &&
after_->start_ms - kDefaultTimeZoneOffsetDeltaInMs <= time_ms &&
time_ms <= after_->end_ms) {
// Extend the after_ segment.
after_->start_ms = time_ms;
} else {
// The after_ segment is either invalid or starts too late.
if (!InvalidSegment(after_)) {
// If the after_ segment is valid, replace it with a new segment.
after_ = LeastRecentlyUsedCacheItem(before_);
}
after_->start_ms = time_ms;
after_->end_ms = time_ms;
after_->offset_ms = offset_ms;
after_->last_used = ++cache_usage_counter_;
}
}
int DateCache::LocalOffsetInMs(int64_t time_ms, bool is_utc) {
if (!is_utc) {
return GetLocalOffsetFromOS(time_ms, is_utc);
}
#ifdef ENABLE_SLOW_DCHECKS
int known_correct_result = 0;
if (v8_flags.enable_slow_asserts) {
// When slow DCHECKs are enabled, we always retrieve the known good result
// (slow) and check that the result produced by the cache matches it.
known_correct_result = GetLocalOffsetFromOS(time_ms, is_utc);
}
#endif // ENABLE_SLOW_DCHECKS
// Invalidate cache if the usage counter is close to overflow.
// Note that cache_usage_counter is incremented less than ten times
// in this function.
if (cache_usage_counter_ >= kMaxInt - 10) {
cache_usage_counter_ = 0;
for (int i = 0; i < kCacheSize; ++i) {
ClearSegment(&cache_[i]);
}
}
// Optimistic fast check.
if (before_->start_ms <= time_ms && time_ms <= before_->end_ms) {
// Cache hit.
before_->last_used = ++cache_usage_counter_;
SLOW_DCHECK(before_->offset_ms == known_correct_result);
return before_->offset_ms;
}
ProbeCache(time_ms);
DCHECK(InvalidSegment(before_) || before_->start_ms <= time_ms);
DCHECK(InvalidSegment(after_) || time_ms < after_->start_ms);
if (InvalidSegment(before_)) {
// Cache miss.
before_->start_ms = time_ms;
before_->end_ms = time_ms;
before_->offset_ms = GetLocalOffsetFromOS(time_ms, is_utc);
before_->last_used = ++cache_usage_counter_;
SLOW_DCHECK(before_->offset_ms == known_correct_result);
return before_->offset_ms;
}
if (time_ms <= before_->end_ms) {
// Cache hit.
before_->last_used = ++cache_usage_counter_;
SLOW_DCHECK(before_->offset_ms == known_correct_result);
return before_->offset_ms;
}
if (time_ms - kDefaultTimeZoneOffsetDeltaInMs > before_->end_ms) {
// If the before_ segment ends too early, then just
// query for the offset of the time_ms
int offset_ms = GetLocalOffsetFromOS(time_ms, is_utc);
ExtendTheAfterSegment(time_ms, offset_ms);
// This swap helps the optimistic fast check in subsequent invocations.
CacheItem* temp = before_;
before_ = after_;
after_ = temp;
SLOW_DCHECK(offset_ms == known_correct_result);
return offset_ms;
}
// Now the time_ms is between
// before_->end_ms and before_->end_ms + default time zone offset delta.
// Update the usage counter of before_ since it is going to be used.
before_->last_used = ++cache_usage_counter_;
// Check if after_ segment is invalid or starts too late.
int64_t new_after_start_ms =
before_->end_ms + kDefaultTimeZoneOffsetDeltaInMs;
if (InvalidSegment(after_) || new_after_start_ms <= after_->start_ms) {
int new_offset_ms = GetLocalOffsetFromOS(new_after_start_ms, is_utc);
ExtendTheAfterSegment(new_after_start_ms, new_offset_ms);
} else {
DCHECK(!InvalidSegment(after_));
// Update the usage counter of after_ since it is going to be used.
after_->last_used = ++cache_usage_counter_;
}
// Now the time_ms is between before_->end_ms and after_->start_ms.
// Only one daylight savings offset change can occur in this interval.
if (before_->offset_ms == after_->offset_ms) {
// Merge two segments if they have the same offset.
before_->end_ms = after_->end_ms;
ClearSegment(after_);
SLOW_DCHECK(before_->offset_ms == known_correct_result);
return before_->offset_ms;
}
// Binary search for time zone offset change point,
// but give up if we don't find it in five iterations.
for (int i = 4; i >= 0; --i) {
int64_t delta = after_->start_ms - before_->end_ms;
int64_t middle_sec = (i == 0) ? time_ms : before_->end_ms + delta / 2;
int offset_ms = GetLocalOffsetFromOS(middle_sec, is_utc);
if (before_->offset_ms == offset_ms) {
before_->end_ms = middle_sec;
if (time_ms <= before_->end_ms) {
SLOW_DCHECK(offset_ms == known_correct_result);
return offset_ms;
}
// If we didn't return, we can't be in the last iteration.
DCHECK_GT(i, 0);
} else {
DCHECK(after_->offset_ms == offset_ms);
after_->start_ms = middle_sec;
if (time_ms >= after_->start_ms) {
// This swap helps the optimistic fast check in subsequent invocations.
CacheItem* temp = before_;
before_ = after_;
after_ = temp;
SLOW_DCHECK(offset_ms == known_correct_result);
return offset_ms;
}
// If we didn't return, we can't be in the last iteration.
DCHECK_GT(i, 0);
}
}
// During the last iteration, we set middle_sec = time_ms and return via one
// of the two return statements above. Thus, we never end up here.
UNREACHABLE();
}
void DateCache::ProbeCache(int64_t time_ms) {
CacheItem* before = nullptr;
CacheItem* after = nullptr;
DCHECK(before_ != after_);
for (int i = 0; i < kCacheSize; ++i) {
if (InvalidSegment(&cache_[i])) {
continue;
}
if (cache_[i].start_ms <= time_ms) {
if (before == nullptr || before->start_ms < cache_[i].start_ms) {
before = &cache_[i];
}
} else if (time_ms < cache_[i].end_ms) {
if (after == nullptr || after->end_ms > cache_[i].end_ms) {
after = &cache_[i];
}
}
}
// If before or after segments were not found,
// then set them to any invalid segment.
if (before == nullptr) {
before =
InvalidSegment(before_) ? before_ : LeastRecentlyUsedCacheItem(after);
}
if (after == nullptr) {
after = InvalidSegment(after_) && before != after_
? after_
: LeastRecentlyUsedCacheItem(before);
}
DCHECK_NOT_NULL(before);
DCHECK_NOT_NULL(after);
DCHECK(before != after);
DCHECK(InvalidSegment(before) || before->start_ms <= time_ms);
DCHECK(InvalidSegment(after) || time_ms < after->start_ms);
DCHECK(InvalidSegment(before) || InvalidSegment(after) ||
before->end_ms < after->start_ms);
before_ = before;
after_ = after;
}
DateCache::CacheItem* DateCache::LeastRecentlyUsedCacheItem(CacheItem* skip) {
CacheItem* result = nullptr;
for (int i = 0; i < kCacheSize; ++i) {
if (&cache_[i] == skip) continue;
if (result == nullptr || result->last_used > cache_[i].last_used) {
result = &cache_[i];
}
}
ClearSegment(result);
return result;
}
namespace {
// ES6 section 20.3.1.1 Time Values and Time Range
const double kMinYear = -1000000.0;
const double kMaxYear = -kMinYear;
const double kMinMonth = -10000000.0;
const double kMaxMonth = -kMinMonth;
const double kMsPerDay = 86400000.0;
const double kMsPerSecond = 1000.0;
const double kMsPerMinute = 60000.0;
const double kMsPerHour = 3600000.0;
} // namespace
double MakeDate(double day, double time) {
if (std::isfinite(day) && std::isfinite(time)) {
return time + day * kMsPerDay;
}
return std::numeric_limits<double>::quiet_NaN();
}
double MakeDay(double year, double month, double date) {
if ((kMinYear <= year && year <= kMaxYear) &&
(kMinMonth <= month && month <= kMaxMonth) && std::isfinite(date)) {
int y = FastD2I(year);
int m = FastD2I(month);
y += m / 12;
m %= 12;
if (m < 0) {
m += 12;
y -= 1;
}
DCHECK_LE(0, m);
DCHECK_LT(m, 12);
// kYearDelta is an arbitrary number such that:
// a) kYearDelta = -1 (mod 400)
// b) year + kYearDelta > 0 for years in the range defined by
// ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
// Jan 1 1970. This is required so that we don't run into integer
// division of negative numbers.
// c) there shouldn't be an overflow for 32-bit integers in the following
// operations.
static const int kYearDelta = 399999;
static const int kBaseDay =
365 * (1970 + kYearDelta) + (1970 + kYearDelta) / 4 -
(1970 + kYearDelta) / 100 + (1970 + kYearDelta) / 400;
int day_from_year = 365 * (y + kYearDelta) + (y + kYearDelta) / 4 -
(y + kYearDelta) / 100 + (y + kYearDelta) / 400 -
kBaseDay;
if ((y % 4 != 0) || (y % 100 == 0 && y % 400 != 0)) {
static const int kDayFromMonth[] = {0, 31, 59, 90, 120, 151,
181, 212, 243, 273, 304, 334};
day_from_year += kDayFromMonth[m];
} else {
static const int kDayFromMonth[] = {0, 31, 60, 91, 121, 152,
182, 213, 244, 274, 305, 335};
day_from_year += kDayFromMonth[m];
}
return static_cast<double>(day_from_year - 1) + DoubleToInteger(date);
}
return std::numeric_limits<double>::quiet_NaN();
}
double MakeTime(double hour, double min, double sec, double ms) {
if (std::isfinite(hour) && std::isfinite(min) && std::isfinite(sec) &&
std::isfinite(ms)) {
double const h = DoubleToInteger(hour);
double const m = DoubleToInteger(min);
double const s = DoubleToInteger(sec);
double const milli = DoubleToInteger(ms);
return h * kMsPerHour + m * kMsPerMinute + s * kMsPerSecond + milli;
}
return std::numeric_limits<double>::quiet_NaN();
}
namespace {
const char* kShortWeekDays[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
const char* kShortMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
template <class... Args>
DateBuffer FormatDate(const char* format, Args... args) {
DateBuffer buffer;
SmallStringOptimizedAllocator<DateBuffer::kInlineSize> allocator(&buffer);
StringStream sstream(&allocator);
sstream.Add(format, args...);
buffer.resize(sstream.length());
return buffer;
}
} // namespace
DateBuffer ToDateString(double time_val, DateCache* date_cache,
ToDateStringMode mode) {
if (std::isnan(time_val)) {
return FormatDate("Invalid Date");
}
int64_t time_ms = static_cast<int64_t>(time_val);
int64_t local_time_ms = (mode == ToDateStringMode::kUTCDateAndTime ||
mode == ToDateStringMode::kISODateAndTime)
? time_ms
: date_cache->ToLocal(time_ms);
int year, month, day, weekday, hour, min, sec, ms;
date_cache->BreakDownTime(local_time_ms, &year, &month, &day, &weekday, &hour,
&min, &sec, &ms);
int timezone_offset = -date_cache->TimezoneOffset(time_ms);
int timezone_hour = std::abs(timezone_offset) / 60;
int timezone_min = std::abs(timezone_offset) % 60;
const char* local_timezone = date_cache->LocalTimezone(time_ms);
switch (mode) {
case ToDateStringMode::kLocalDate:
return FormatDate((year < 0) ? "%s %s %02d %05d" : "%s %s %02d %04d",
kShortWeekDays[weekday], kShortMonths[month], day,
year);
case ToDateStringMode::kLocalTime:
return FormatDate("%02d:%02d:%02d GMT%c%02d%02d (%s)", hour, min, sec,
(timezone_offset < 0) ? '-' : '+', timezone_hour,
timezone_min, local_timezone);
case ToDateStringMode::kLocalDateAndTime:
return FormatDate(
(year < 0) ? "%s %s %02d %05d %02d:%02d:%02d GMT%c%02d%02d (%s)"
: "%s %s %02d %04d %02d:%02d:%02d GMT%c%02d%02d (%s)",
kShortWeekDays[weekday], kShortMonths[month], day, year, hour, min,
sec, (timezone_offset < 0) ? '-' : '+', timezone_hour, timezone_min,
local_timezone);
case ToDateStringMode::kUTCDateAndTime:
return FormatDate((year < 0) ? "%s, %02d %s %05d %02d:%02d:%02d GMT"
: "%s, %02d %s %04d %02d:%02d:%02d GMT",
kShortWeekDays[weekday], day, kShortMonths[month], year,
hour, min, sec);
case ToDateStringMode::kISODateAndTime:
if (year >= 0 && year <= 9999) {
return FormatDate("%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", year,
month + 1, day, hour, min, sec, ms);
} else if (year < 0) {
return FormatDate("-%06d-%02d-%02dT%02d:%02d:%02d.%03dZ", -year,
month + 1, day, hour, min, sec, ms);
} else {
return FormatDate("+%06d-%02d-%02dT%02d:%02d:%02d.%03dZ", year,
month + 1, day, hour, min, sec, ms);
}
}
UNREACHABLE();
}
// ES6 section 20.3.1.16 Date Time String Format
double ParseDateTimeString(Isolate* isolate, DirectHandle<String> str) {
str = String::Flatten(isolate, str);
double out[DateParser::OUTPUT_SIZE];
DisallowGarbageCollection no_gc;
String::FlatContent str_content = str->GetFlatContent(no_gc);
bool result;
if (str_content.IsOneByte()) {
result = DateParser::Parse(isolate, str_content.ToOneByteVector(), out);
} else {
result = DateParser::Parse(isolate, str_content.ToUC16Vector(), out);
}
if (!result) return std::numeric_limits<double>::quiet_NaN();
double const day = MakeDay(out[DateParser::YEAR], out[DateParser::MONTH],
out[DateParser::DAY]);
double const time =
MakeTime(out[DateParser::HOUR], out[DateParser::MINUTE],
out[DateParser::SECOND], out[DateParser::MILLISECOND]);
double date = MakeDate(day, time);
if (std::isnan(out[DateParser::UTC_OFFSET])) {
if (date >= -DateCache::kMaxTimeBeforeUTCInMs &&
date <= DateCache::kMaxTimeBeforeUTCInMs) {
date = isolate->date_cache()->ToUTC(static_cast<int64_t>(date));
} else {
return std::numeric_limits<double>::quiet_NaN();
}
} else {
date -= out[DateParser::UTC_OFFSET] * 1000.0;
}
if (!DateCache::TryTimeClip(&date)) {
return std::numeric_limits<double>::quiet_NaN();
}
return date;
}
} // namespace internal
} // namespace v8

282
deps/v8/src/date/date.h vendored Normal file
View File

@ -0,0 +1,282 @@
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_DATE_DATE_H_
#define V8_DATE_DATE_H_
#include <cmath>
#include "src/base/small-vector.h"
#include "src/base/timezone-cache.h"
#include "src/common/globals.h"
#include "src/objects/smi.h"
namespace v8 {
namespace internal {
class V8_EXPORT_PRIVATE DateCache {
public:
static const int kMsPerMin = 60 * 1000;
static const int kSecPerDay = 24 * 60 * 60;
static const int64_t kMsPerDay = kSecPerDay * 1000;
static const int64_t kMsPerMonth = kMsPerDay * 30;
// The largest time that can be passed to OS date-time library functions.
static const int kMaxEpochTimeInSec = kMaxInt;
static const int64_t kMaxEpochTimeInMs = static_cast<int64_t>(kMaxInt) * 1000;
// The largest time that can be stored in JSDate.
static const int64_t kMaxTimeInMs =
static_cast<int64_t>(864000000) * 10000000;
// Conservative upper bound on time that can be stored in JSDate
// before UTC conversion.
static const int64_t kMaxTimeBeforeUTCInMs = kMaxTimeInMs + kMsPerMonth;
// Sentinel that denotes an invalid local offset.
static const int kInvalidLocalOffsetInMs = kMaxInt;
// Sentinel that denotes an invalid cache stamp.
// It is an invariant of DateCache that cache stamp is non-negative.
static const int kInvalidStamp = -1;
DateCache();
virtual ~DateCache() {
delete tz_cache_;
tz_cache_ = nullptr;
}
// Clears cached timezone information and increments the cache stamp.
void ResetDateCache(
base::TimezoneCache::TimeZoneDetection time_zone_detection);
// Computes floor(time_ms / kMsPerDay).
static int DaysFromTime(int64_t time_ms) {
if (time_ms < 0) time_ms -= (kMsPerDay - 1);
return static_cast<int>(time_ms / kMsPerDay);
}
// Computes modulo(time_ms, kMsPerDay) given that
// days = floor(time_ms / kMsPerDay).
static int TimeInDay(int64_t time_ms, int days) {
return static_cast<int>(time_ms - days * kMsPerDay);
}
// Performs the success path of the ECMA 262 TimeClip operation (when the
// value is within the range, truncates it to an integer). Returns false if
// the value is outside the range, and should be clipped to NaN.
// ECMA 262 - ES#sec-timeclip TimeClip (time)
static bool TryTimeClip(double* time) {
if (-kMaxTimeInMs <= *time && *time <= kMaxTimeInMs) {
// Inline the finite part of DoubleToInteger here, since the range check
// already covers the non-finite checks.
*time = ((*time > 0) ? std::floor(*time) : std::ceil(*time)) + 0.0;
return true;
}
return false;
}
// Given the number of days since the epoch, computes the weekday.
// ECMA 262 - 15.9.1.6.
int Weekday(int days) {
int result = (days + 4) % 7;
return result >= 0 ? result : result + 7;
}
bool IsLeap(int year) {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
// ECMA 262 - ES#sec-local-time-zone-adjustment
int LocalOffsetInMs(int64_t time, bool is_utc);
const char* LocalTimezone(int64_t time_ms) {
if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) {
time_ms = EquivalentTime(time_ms);
}
bool is_dst = DaylightSavingsOffsetInMs(time_ms) != 0;
const char** name = is_dst ? &dst_tz_name_ : &tz_name_;
if (*name == nullptr) {
*name = tz_cache_->LocalTimezone(static_cast<double>(time_ms));
}
return *name;
}
// ECMA 262 - 15.9.5.26
int TimezoneOffset(int64_t time_ms) {
int64_t local_ms = ToLocal(time_ms);
return static_cast<int>((time_ms - local_ms) / kMsPerMin);
}
// ECMA 262 - ES#sec-localtime-t
// LocalTime(t) = t + LocalTZA(t, true)
int64_t ToLocal(int64_t time_ms) {
return time_ms + LocalOffsetInMs(time_ms, true);
}
// ECMA 262 - ES#sec-utc-t
// UTC(t) = t - LocalTZA(t, false)
int64_t ToUTC(int64_t time_ms) {
return time_ms - LocalOffsetInMs(time_ms, false);
}
// Computes a time equivalent to the given time according
// to ECMA 262 - 15.9.1.9.
// The issue here is that some library calls don't work right for dates
// that cannot be represented using a non-negative signed 32 bit integer
// (measured in whole seconds based on the 1970 epoch).
// We solve this by mapping the time to a year with same leap-year-ness
// and same starting day for the year. The ECMAscript specification says
// we must do this, but for compatibility with other browsers, we use
// the actual year if it is in the range 1970..2037
int64_t EquivalentTime(int64_t time_ms) {
int days = DaysFromTime(time_ms);
int time_within_day_ms = static_cast<int>(time_ms - days * kMsPerDay);
int year, month, day;
YearMonthDayFromDays(days, &year, &month, &day);
int new_days = DaysFromYearMonth(EquivalentYear(year), month) + day - 1;
return static_cast<int64_t>(new_days) * kMsPerDay + time_within_day_ms;
}
// Returns an equivalent year in the range [2008-2035] matching
// - leap year,
// - week day of first day.
// ECMA 262 - 15.9.1.9.
int EquivalentYear(int year) {
int week_day = Weekday(DaysFromYearMonth(year, 0));
int recent_year = (IsLeap(year) ? 1956 : 1967) + (week_day * 12) % 28;
// Find the year in the range 2008..2037 that is equivalent mod 28.
// Add 3*28 to give a positive argument to the modulus operator.
return 2008 + (recent_year + 3 * 28 - 2008) % 28;
}
// Given the number of days since the epoch, computes
// the corresponding year, month, and day.
void YearMonthDayFromDays(int days, int* year, int* month, int* day);
// Computes the number of days since the epoch for
// the first day of the given month in the given year.
int DaysFromYearMonth(int year, int month);
// Breaks down the time value.
void BreakDownTime(int64_t time_ms, int* year, int* month, int* day,
int* weekday, int* hour, int* min, int* sec, int* ms);
// Cache stamp is used for invalidating caches in JSDate.
// We increment the stamp each time when the timezone information changes.
// JSDate objects perform stamp check and invalidate their caches if
// their saved stamp is not equal to the current stamp.
Tagged<Smi> stamp() { return stamp_; }
void* stamp_address() { return &stamp_; }
// These functions are virtual so that we can override them when testing.
virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) {
double time_ms = static_cast<double>(time_sec * 1000);
return static_cast<int>(tz_cache_->DaylightSavingsOffset(time_ms));
}
virtual int GetLocalOffsetFromOS(int64_t time_ms, bool is_utc);
private:
// The implementation relies on the fact that no time zones have more than one
// time zone offset change (including DST offset changes) per 19 days. In
// Egypt in 2010 they decided to suspend DST during Ramadan. This led to a
// short interval where DST is in effect from September 10 to September 30.
static const int kDefaultTimeZoneOffsetDeltaInMs = 19 * kSecPerDay * 1000;
static const int kCacheSize = 32;
// Stores a segment of time where time zone offset does not change.
struct CacheItem {
int64_t start_ms;
int64_t end_ms;
int offset_ms;
int last_used;
};
// Computes the daylight savings offset for the given time.
// ECMA 262 - 15.9.1.8
int DaylightSavingsOffsetInMs(int64_t time_ms) {
int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs)
? static_cast<int>(time_ms / 1000)
: static_cast<int>(EquivalentTime(time_ms) / 1000);
return GetDaylightSavingsOffsetFromOS(time_sec);
}
// Sets the before_ and the after_ segments from the timezone offset cache
// such that the before_ segment starts earlier than the given time and the
// after_ segment start later than the given time. Both segments might be
// invalid. The last_used counters of the before_ and after_ are updated.
void ProbeCache(int64_t time_ms);
// Finds the least recently used segment from the timezone offset cache that
// is not equal to the given 'skip' segment.
CacheItem* LeastRecentlyUsedCacheItem(CacheItem* skip);
// Extends the after_ segment with the given point or resets it
// if it starts later than the given time + kDefaultDSTDeltaInMs.
inline void ExtendTheAfterSegment(int64_t time_sec, int offset_ms);
// Makes the given segment invalid.
inline void ClearSegment(CacheItem* segment);
bool InvalidSegment(CacheItem* segment) {
return segment->start_ms > segment->end_ms;
}
Tagged<Smi> stamp_;
// Daylight Saving Time cache.
CacheItem cache_[kCacheSize];
int cache_usage_counter_;
CacheItem* before_;
CacheItem* after_;
int local_offset_ms_;
// Year/Month/Day cache.
bool ymd_valid_;
int ymd_days_;
int ymd_year_;
int ymd_month_;
int ymd_day_;
// Timezone name cache
const char* tz_name_;
const char* dst_tz_name_;
base::TimezoneCache* tz_cache_;
};
// Routines shared between Date and Temporal
// ES6 section 20.3.1.14 MakeDate (day, time)
double MakeDate(double day, double time);
// ES6 section 20.3.1.13 MakeDay (year, month, date)
double MakeDay(double year, double month, double date);
// ES6 section 20.3.1.12 MakeTime (hour, min, sec, ms)
double MakeTime(double hour, double min, double sec, double ms);
using DateBuffer = base::SmallVector<char, 128>;
enum class ToDateStringMode {
kLocalDate,
kLocalTime,
kLocalDateAndTime,
kUTCDateAndTime,
kISODateAndTime
};
// ES6 section 20.3.4.41.1 ToDateString(tv)
DateBuffer ToDateString(double time_val, DateCache* date_cache,
ToDateStringMode mode);
double ParseDateTimeString(Isolate* isolate, DirectHandle<String> str);
} // namespace internal
} // namespace v8
#endif // V8_DATE_DATE_H_

356
deps/v8/src/date/dateparser-inl.h vendored Normal file
View File

@ -0,0 +1,356 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_DATE_DATEPARSER_INL_H_
#define V8_DATE_DATEPARSER_INL_H_
#include "src/date/dateparser.h"
// Include the non-inl header before the rest of the headers.
#include "src/execution/isolate.h"
#include "src/strings/char-predicates-inl.h"
namespace v8 {
namespace internal {
template <typename Char>
bool DateParser::Parse(Isolate* isolate, base::Vector<Char> str, double* out) {
InputReader<Char> in(str);
DateStringTokenizer<Char> scanner(&in);
TimeZoneComposer tz;
TimeComposer time;
DayComposer day;
// Specification:
// Accept ES5 ISO 8601 date-time-strings or legacy dates compatible
// with Safari.
// ES5 ISO 8601 dates:
// [('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]
// where yyyy is in the range 0000..9999 and
// +/-yyyyyy is in the range -999999..+999999 -
// but -000000 is invalid (year zero must be positive),
// MM is in the range 01..12,
// DD is in the range 01..31,
// MM and DD defaults to 01 if missing,,
// HH is generally in the range 00..23, but can be 24 if mm, ss
// and sss are zero (or missing), representing midnight at the
// end of a day,
// mm and ss are in the range 00..59,
// sss is in the range 000..999,
// hh is in the range 00..23,
// mm, ss, and sss default to 00 if missing, and
// timezone defaults to Z if missing
// (following Safari, ISO actually demands local time).
// Extensions:
// We also allow sss to have more or less than three digits (but at
// least one).
// We allow hh:mm to be specified as hhmm.
// Legacy dates:
// Any unrecognized word before the first number is ignored.
// Parenthesized text is ignored.
// An unsigned number followed by ':' is a time value, and is
// added to the TimeComposer. A number followed by '::' adds a second
// zero as well. A number followed by '.' is also a time and must be
// followed by milliseconds.
// Any other number is a date component and is added to DayComposer.
// A month name (or really: any word having the same first three letters
// as a month name) is recorded as a named month in the Day composer.
// A word recognizable as a time-zone is recorded as such, as is
// '(+|-)(hhmm|hh:)'.
// Legacy dates don't allow extra signs ('+' or '-') or umatched ')'
// after a number has been read (before the first number, any garbage
// is allowed).
// Intersection of the two:
// A string that matches both formats (e.g. 1970-01-01) will be
// parsed as an ES5 date-time string - which means it will default
// to UTC time-zone. That's unavoidable if following the ES5
// specification.
// After a valid "T" has been read while scanning an ES5 datetime string,
// the input can no longer be a valid legacy date, since the "T" is a
// garbage string after a number has been read.
// First try getting as far as possible with as ES5 Date Time String.
DateToken next_unhandled_token = ParseES5DateTime(&scanner, &day, &time, &tz);
if (next_unhandled_token.IsInvalid()) return false;
bool has_read_number = !day.IsEmpty();
// If there's anything left, continue with the legacy parser.
bool legacy_parser = false;
for (DateToken token = next_unhandled_token; !token.IsEndOfInput();
token = scanner.Next()) {
if (token.IsNumber()) {
legacy_parser = true;
has_read_number = true;
int n = token.number();
if (scanner.SkipSymbol(':')) {
if (scanner.SkipSymbol(':')) {
// n + "::"
if (!time.IsEmpty()) return false;
time.Add(n);
time.Add(0);
} else {
// n + ":"
if (!time.Add(n)) return false;
if (scanner.Peek().IsSymbol('.')) scanner.Next();
}
} else if (scanner.SkipSymbol('.') && time.IsExpecting(n)) {
time.Add(n);
if (!scanner.Peek().IsNumber()) return false;
int ms = ReadMilliseconds(scanner.Next());
if (ms < 0) return false;
time.AddFinal(ms);
} else if (tz.IsExpecting(n)) {
tz.SetAbsoluteMinute(n);
} else if (time.IsExpecting(n)) {
time.AddFinal(n);
// Require end, white space, "Z", "+" or "-" immediately after
// finalizing time.
DateToken peek = scanner.Peek();
if (!peek.IsEndOfInput() && !peek.IsWhiteSpace() &&
!peek.IsKeywordZ() && !peek.IsAsciiSign())
return false;
} else {
if (!day.Add(n)) return false;
scanner.SkipSymbol('-');
}
} else if (token.IsKeyword()) {
legacy_parser = true;
// Parse a "word" (sequence of chars. >= 'A').
KeywordType type = token.keyword_type();
int value = token.keyword_value();
if (type == AM_PM && !time.IsEmpty()) {
time.SetHourOffset(value);
} else if (type == MONTH_NAME) {
day.SetNamedMonth(value);
scanner.SkipSymbol('-');
} else if (type == TIME_ZONE_NAME && has_read_number) {
tz.Set(value);
} else {
// Garbage words are illegal if a number has been read.
if (has_read_number) return false;
// The first number has to be separated from garbage words by
// whitespace or other separators.
if (scanner.Peek().IsNumber()) return false;
}
} else if (token.IsAsciiSign() && (tz.IsUTC() || !time.IsEmpty())) {
legacy_parser = true;
// Parse UTC offset (only after UTC or time).
tz.SetSign(token.ascii_sign());
// The following number may be empty.
int n = 0;
int length = 0;
if (scanner.Peek().IsNumber()) {
DateToken next_token = scanner.Next();
length = next_token.length();
n = next_token.number();
}
has_read_number = true;
if (scanner.Peek().IsSymbol(':')) {
tz.SetAbsoluteHour(n);
// TODO(littledan): Use minutes as part of timezone?
tz.SetAbsoluteMinute(kNone);
} else if (length == 2 || length == 1) {
// Handle time zones like GMT-8
tz.SetAbsoluteHour(n);
tz.SetAbsoluteMinute(0);
} else if (length == 4 || length == 3) {
// Looks like the hhmm format
tz.SetAbsoluteHour(n / 100);
tz.SetAbsoluteMinute(n % 100);
} else {
// No need to accept time zones like GMT-12345
return false;
}
} else if ((token.IsAsciiSign() || token.IsSymbol(')')) &&
has_read_number) {
// Extra sign or ')' is illegal if a number has been read.
return false;
} else {
// Ignore other characters and whitespace.
}
}
bool success = day.Write(out) && time.Write(out) && tz.Write(out);
if (legacy_parser && success) {
isolate->CountUsage(v8::Isolate::kLegacyDateParser);
}
return success;
}
template <typename CharType>
DateParser::DateToken DateParser::DateStringTokenizer<CharType>::Scan() {
int pre_pos = in_->position();
if (in_->IsEnd()) return DateToken::EndOfInput();
if (in_->IsAsciiDigit()) {
int n = in_->ReadUnsignedNumeral();
int length = in_->position() - pre_pos;
return DateToken::Number(n, length);
}
if (in_->Skip(':')) return DateToken::Symbol(':');
if (in_->Skip('-')) return DateToken::Symbol('-');
if (in_->Skip('+')) return DateToken::Symbol('+');
if (in_->Skip('.')) return DateToken::Symbol('.');
if (in_->Skip(')')) return DateToken::Symbol(')');
if (in_->IsAsciiAlphaOrAbove() && !in_->IsWhiteSpaceChar()) {
DCHECK_EQ(KeywordTable::kPrefixLength, 3);
uint32_t buffer[3] = {0, 0, 0};
int length = in_->ReadWord(buffer, 3);
int index = KeywordTable::Lookup(buffer, length);
return DateToken::Keyword(KeywordTable::GetType(index),
KeywordTable::GetValue(index), length);
}
if (in_->SkipWhiteSpace()) {
return DateToken::WhiteSpace(in_->position() - pre_pos);
}
if (in_->SkipParentheses()) {
return DateToken::Unknown();
}
in_->Next();
return DateToken::Unknown();
}
template <typename Char>
bool DateParser::InputReader<Char>::SkipWhiteSpace() {
if (IsWhiteSpaceOrLineTerminator(ch_)) {
Next();
return true;
}
return false;
}
template <typename Char>
bool DateParser::InputReader<Char>::SkipParentheses() {
if (ch_ != '(') return false;
int balance = 0;
do {
if (ch_ == ')')
--balance;
else if (ch_ == '(')
++balance;
Next();
} while (balance > 0 && ch_);
return true;
}
template <typename Char>
DateParser::DateToken DateParser::ParseES5DateTime(
DateStringTokenizer<Char>* scanner, DayComposer* day, TimeComposer* time,
TimeZoneComposer* tz) {
DCHECK(day->IsEmpty());
DCHECK(time->IsEmpty());
DCHECK(tz->IsEmpty());
// Parse mandatory date string: [('-'|'+')yy]yyyy[':'MM[':'DD]]
if (scanner->Peek().IsAsciiSign()) {
// Keep the sign token, so we can pass it back to the legacy
// parser if we don't use it.
DateToken sign_token = scanner->Next();
if (!scanner->Peek().IsFixedLengthNumber(6)) return sign_token;
int sign = sign_token.ascii_sign();
int year = scanner->Next().number();
if (sign < 0 && year == 0) return sign_token;
day->Add(sign * year);
} else if (scanner->Peek().IsFixedLengthNumber(4)) {
day->Add(scanner->Next().number());
} else {
return scanner->Next();
}
if (scanner->SkipSymbol('-')) {
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!DayComposer::IsMonth(scanner->Peek().number()))
return scanner->Next();
day->Add(scanner->Next().number());
if (scanner->SkipSymbol('-')) {
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!DayComposer::IsDay(scanner->Peek().number()))
return scanner->Next();
day->Add(scanner->Next().number());
}
}
// Check for optional time string: 'T'HH':'mm[':'ss['.'sss]]Z
if (!scanner->Peek().IsKeywordType(TIME_SEPARATOR)) {
if (!scanner->Peek().IsEndOfInput()) return scanner->Next();
} else {
// ES5 Date Time String time part is present.
scanner->Next();
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!Between(scanner->Peek().number(), 0, 24)) {
return DateToken::Invalid();
}
// Allow 24:00[:00[.000]], but no other time starting with 24.
bool hour_is_24 = (scanner->Peek().number() == 24);
time->Add(scanner->Next().number());
if (!scanner->SkipSymbol(':')) return DateToken::Invalid();
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!TimeComposer::IsMinute(scanner->Peek().number()) ||
(hour_is_24 && scanner->Peek().number() > 0)) {
return DateToken::Invalid();
}
time->Add(scanner->Next().number());
if (scanner->SkipSymbol(':')) {
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!TimeComposer::IsSecond(scanner->Peek().number()) ||
(hour_is_24 && scanner->Peek().number() > 0)) {
return DateToken::Invalid();
}
time->Add(scanner->Next().number());
if (scanner->SkipSymbol('.')) {
if (!scanner->Peek().IsNumber() ||
(hour_is_24 && scanner->Peek().number() > 0)) {
return DateToken::Invalid();
}
// Allow more or less than the mandated three digits.
time->Add(ReadMilliseconds(scanner->Next()));
}
}
// Check for optional timezone designation: 'Z' | ('+'|'-')hh':'mm
if (scanner->Peek().IsKeywordZ()) {
scanner->Next();
tz->Set(0);
} else if (scanner->Peek().IsSymbol('+') || scanner->Peek().IsSymbol('-')) {
tz->SetSign(scanner->Next().symbol() == '+' ? 1 : -1);
if (scanner->Peek().IsFixedLengthNumber(4)) {
// hhmm extension syntax.
int hourmin = scanner->Next().number();
int hour = hourmin / 100;
int min = hourmin % 100;
if (!TimeComposer::IsHour(hour) || !TimeComposer::IsMinute(min)) {
return DateToken::Invalid();
}
tz->SetAbsoluteHour(hour);
tz->SetAbsoluteMinute(min);
} else {
// hh:mm standard syntax.
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!TimeComposer::IsHour(scanner->Peek().number())) {
return DateToken::Invalid();
}
tz->SetAbsoluteHour(scanner->Next().number());
if (!scanner->SkipSymbol(':')) return DateToken::Invalid();
if (!scanner->Peek().IsFixedLengthNumber(2) ||
!TimeComposer::IsMinute(scanner->Peek().number())) {
return DateToken::Invalid();
}
tz->SetAbsoluteMinute(scanner->Next().number());
}
}
if (!scanner->Peek().IsEndOfInput()) return DateToken::Invalid();
}
// Successfully parsed ES5 Date Time String.
// ES#sec-date-time-string-format Date Time String Format
// "When the time zone offset is absent, date-only forms are interpreted
// as a UTC time and date-time forms are interpreted as a local time."
if (tz->IsEmpty() && time->IsEmpty()) {
tz->Set(0);
}
day->set_iso_date();
return DateToken::EndOfInput();
}
} // namespace internal
} // namespace v8
#endif // V8_DATE_DATEPARSER_INL_H_

200
deps/v8/src/date/dateparser.cc vendored Normal file
View File

@ -0,0 +1,200 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/date/dateparser.h"
#include "src/objects/objects-inl.h"
#include "src/strings/char-predicates-inl.h"
namespace v8 {
namespace internal {
bool DateParser::DayComposer::Write(double* output) {
if (index_ < 1) return false;
// Day and month defaults to 1.
while (index_ < kSize) {
comp_[index_++] = 1;
}
int year = 0; // Default year is 0 (=> 2000) for KJS compatibility.
int month = kNone;
int day = kNone;
if (named_month_ == kNone) {
if (is_iso_date_ || (index_ == 3 && !IsDay(comp_[0]))) {
// YMD
year = comp_[0];
month = comp_[1];
day = comp_[2];
} else {
// MD(Y)
month = comp_[0];
day = comp_[1];
if (index_ == 3) year = comp_[2];
}
} else {
month = named_month_;
if (index_ == 1) {
// MD or DM
day = comp_[0];
} else if (!IsDay(comp_[0])) {
// YMD, MYD, or YDM
year = comp_[0];
day = comp_[1];
} else {
// DMY, MDY, or DYM
day = comp_[0];
year = comp_[1];
}
}
if (!is_iso_date_) {
if (Between(year, 0, 49))
year += 2000;
else if (Between(year, 50, 99))
year += 1900;
}
if (!Smi::IsValid(year) || !IsMonth(month) || !IsDay(day)) return false;
output[YEAR] = year;
output[MONTH] = month - 1; // 0-based
output[DAY] = day;
return true;
}
bool DateParser::TimeComposer::Write(double* output) {
// All time slots default to 0
while (index_ < kSize) {
comp_[index_++] = 0;
}
int& hour = comp_[0];
int& minute = comp_[1];
int& second = comp_[2];
int& millisecond = comp_[3];
if (hour_offset_ != kNone) {
if (!IsHour12(hour)) return false;
hour %= 12;
hour += hour_offset_;
}
if (!IsHour(hour) || !IsMinute(minute) || !IsSecond(second) ||
!IsMillisecond(millisecond)) {
// A 24th hour is allowed if minutes, seconds, and milliseconds are 0
if (hour != 24 || minute != 0 || second != 0 || millisecond != 0) {
return false;
}
}
output[HOUR] = hour;
output[MINUTE] = minute;
output[SECOND] = second;
output[MILLISECOND] = millisecond;
return true;
}
bool DateParser::TimeZoneComposer::Write(double* output) {
if (sign_ != kNone) {
if (hour_ == kNone) hour_ = 0;
if (minute_ == kNone) minute_ = 0;
// Avoid signed integer overflow (undefined behavior) by doing unsigned
// arithmetic.
unsigned total_seconds_unsigned = hour_ * 3600U + minute_ * 60U;
if (total_seconds_unsigned > Smi::kMaxValue) return false;
int total_seconds = static_cast<int>(total_seconds_unsigned);
if (sign_ < 0) {
total_seconds = -total_seconds;
}
DCHECK(Smi::IsValid(total_seconds));
output[UTC_OFFSET] = total_seconds;
} else {
output[UTC_OFFSET] = std::numeric_limits<double>::quiet_NaN();
}
return true;
}
const int8_t
DateParser::KeywordTable::array[][DateParser::KeywordTable::kEntrySize] = {
{'j', 'a', 'n', DateParser::MONTH_NAME, 1},
{'f', 'e', 'b', DateParser::MONTH_NAME, 2},
{'m', 'a', 'r', DateParser::MONTH_NAME, 3},
{'a', 'p', 'r', DateParser::MONTH_NAME, 4},
{'m', 'a', 'y', DateParser::MONTH_NAME, 5},
{'j', 'u', 'n', DateParser::MONTH_NAME, 6},
{'j', 'u', 'l', DateParser::MONTH_NAME, 7},
{'a', 'u', 'g', DateParser::MONTH_NAME, 8},
{'s', 'e', 'p', DateParser::MONTH_NAME, 9},
{'o', 'c', 't', DateParser::MONTH_NAME, 10},
{'n', 'o', 'v', DateParser::MONTH_NAME, 11},
{'d', 'e', 'c', DateParser::MONTH_NAME, 12},
{'a', 'm', '\0', DateParser::AM_PM, 0},
{'p', 'm', '\0', DateParser::AM_PM, 12},
{'u', 't', '\0', DateParser::TIME_ZONE_NAME, 0},
{'u', 't', 'c', DateParser::TIME_ZONE_NAME, 0},
{'z', '\0', '\0', DateParser::TIME_ZONE_NAME, 0},
{'g', 'm', 't', DateParser::TIME_ZONE_NAME, 0},
{'c', 'd', 't', DateParser::TIME_ZONE_NAME, -5},
{'c', 's', 't', DateParser::TIME_ZONE_NAME, -6},
{'e', 'd', 't', DateParser::TIME_ZONE_NAME, -4},
{'e', 's', 't', DateParser::TIME_ZONE_NAME, -5},
{'m', 'd', 't', DateParser::TIME_ZONE_NAME, -6},
{'m', 's', 't', DateParser::TIME_ZONE_NAME, -7},
{'p', 'd', 't', DateParser::TIME_ZONE_NAME, -7},
{'p', 's', 't', DateParser::TIME_ZONE_NAME, -8},
{'t', '\0', '\0', DateParser::TIME_SEPARATOR, 0},
{'\0', '\0', '\0', DateParser::INVALID, 0},
};
// We could use perfect hashing here, but this is not a bottleneck.
int DateParser::KeywordTable::Lookup(const uint32_t* pre, int len) {
int i;
for (i = 0; array[i][kTypeOffset] != INVALID; i++) {
int j = 0;
while (j < kPrefixLength && pre[j] == static_cast<uint32_t>(array[i][j])) {
j++;
}
// Check if we have a match and the length is legal.
// Word longer than keyword is only allowed for month names.
if (j == kPrefixLength &&
(len <= kPrefixLength || array[i][kTypeOffset] == MONTH_NAME)) {
return i;
}
}
return i;
}
int DateParser::ReadMilliseconds(DateToken token) {
// Read first three significant digits of the original numeral,
// as inferred from the value and the number of digits.
// I.e., use the number of digits to see if there were
// leading zeros.
int number = token.number();
int length = token.length();
if (length < 3) {
// Less than three digits. Multiply to put most significant digit
// in hundreds position.
if (length == 1) {
number *= 100;
} else if (length == 2) {
number *= 10;
}
} else if (length > 3) {
if (length > kMaxSignificantDigits) length = kMaxSignificantDigits;
// More than three digits. Divide by 10^(length - 3) to get three
// most significant digits.
int factor = 1;
do {
DCHECK_LE(factor, 100000000); // factor won't overflow.
factor *= 10;
length--;
} while (length > 3);
number /= factor;
}
return number;
}
} // namespace internal
} // namespace v8

368
deps/v8/src/date/dateparser.h vendored Normal file
View File

@ -0,0 +1,368 @@
// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_DATE_DATEPARSER_H_
#define V8_DATE_DATEPARSER_H_
#include "src/base/vector.h"
#include "src/strings/char-predicates.h"
#include "src/utils/allocation.h"
namespace v8 {
namespace internal {
class DateParser : public AllStatic {
public:
enum {
YEAR,
MONTH,
DAY,
HOUR,
MINUTE,
SECOND,
MILLISECOND,
UTC_OFFSET,
OUTPUT_SIZE
};
// Parse the string as a date. If parsing succeeds, return true after
// filling out the output array as follows (all integers are Smis):
// [0]: year
// [1]: month (0 = Jan, 1 = Feb, ...)
// [2]: day
// [3]: hour
// [4]: minute
// [5]: second
// [6]: millisecond
// [7]: UTC offset in seconds, or null value if no timezone specified
// If parsing fails, return false (content of output array is not defined).
template <typename Char>
static bool Parse(Isolate* isolate, base::Vector<Char> str, double* output);
private:
// Range testing
static inline bool Between(int x, int lo, int hi) {
return static_cast<unsigned>(x - lo) <= static_cast<unsigned>(hi - lo);
}
// Indicates a missing value.
static const int kNone = kMaxInt;
// Maximal number of digits used to build the value of a numeral.
// Remaining digits are ignored.
static const int kMaxSignificantDigits = 9;
// InputReader provides basic string parsing and character classification.
template <typename Char>
class InputReader {
public:
explicit InputReader(base::Vector<Char> s) : index_(0), buffer_(s) {
Next();
}
int position() { return index_; }
// Advance to the next character of the string.
void Next() {
ch_ = (index_ < buffer_.length()) ? buffer_[index_] : 0;
index_++;
}
// Read a string of digits as an unsigned number. Cap value at
// kMaxSignificantDigits, but skip remaining digits if the numeral
// is longer.
int ReadUnsignedNumeral() {
int n = 0;
int i = 0;
// First, skip leading zeros
while (ch_ == '0') Next();
// And then, do the conversion
while (IsAsciiDigit()) {
if (i < kMaxSignificantDigits) n = n * 10 + ch_ - '0';
i++;
Next();
}
return n;
}
// Read a word (sequence of chars. >= 'A'), fill the given buffer with a
// lower-case prefix, and pad any remainder of the buffer with zeroes.
// Return word length.
int ReadWord(uint32_t* prefix, int prefix_size) {
int len;
for (len = 0; IsAsciiAlphaOrAbove() && !IsWhiteSpaceChar();
Next(), len++) {
if (len < prefix_size) prefix[len] = AsciiAlphaToLower(ch_);
}
for (int i = len; i < prefix_size; i++) prefix[i] = 0;
return len;
}
// The skip methods return whether they actually skipped something.
bool Skip(uint32_t c) {
if (ch_ == c) {
Next();
return true;
}
return false;
}
inline bool SkipWhiteSpace();
inline bool SkipParentheses();
// Character testing/classification. Non-ASCII digits are not supported.
bool Is(uint32_t c) const { return ch_ == c; }
bool IsEnd() const { return ch_ == 0; }
bool IsAsciiDigit() const { return IsDecimalDigit(ch_); }
bool IsAsciiAlphaOrAbove() const { return ch_ >= 'A'; }
bool IsWhiteSpaceChar() const { return IsWhiteSpace(ch_); }
bool IsAsciiSign() const { return ch_ == '+' || ch_ == '-'; }
// Return 1 for '+' and -1 for '-'.
int GetAsciiSignValue() const { return 44 - static_cast<int>(ch_); }
private:
int index_;
base::Vector<Char> buffer_;
uint32_t ch_;
};
enum KeywordType {
INVALID,
MONTH_NAME,
TIME_ZONE_NAME,
TIME_SEPARATOR,
AM_PM
};
struct DateToken {
public:
bool IsInvalid() { return tag_ == kInvalidTokenTag; }
bool IsUnknown() { return tag_ == kUnknownTokenTag; }
bool IsNumber() { return tag_ == kNumberTag; }
bool IsSymbol() { return tag_ == kSymbolTag; }
bool IsWhiteSpace() { return tag_ == kWhiteSpaceTag; }
bool IsEndOfInput() { return tag_ == kEndOfInputTag; }
bool IsKeyword() { return tag_ >= kKeywordTagStart; }
int length() { return length_; }
int number() {
DCHECK(IsNumber());
return value_;
}
KeywordType keyword_type() {
DCHECK(IsKeyword());
return static_cast<KeywordType>(tag_);
}
int keyword_value() {
DCHECK(IsKeyword());
return value_;
}
char symbol() {
DCHECK(IsSymbol());
return static_cast<char>(value_);
}
bool IsSymbol(char symbol) {
return IsSymbol() && this->symbol() == symbol;
}
bool IsKeywordType(KeywordType tag) { return tag_ == tag; }
bool IsFixedLengthNumber(int length) {
return IsNumber() && length_ == length;
}
bool IsAsciiSign() {
return tag_ == kSymbolTag && (value_ == '-' || value_ == '+');
}
int ascii_sign() {
DCHECK(IsAsciiSign());
return 44 - value_;
}
bool IsKeywordZ() {
return IsKeywordType(TIME_ZONE_NAME) && length_ == 1 && value_ == 0;
}
bool IsUnknown(int character) { return IsUnknown() && value_ == character; }
// Factory functions.
static DateToken Keyword(KeywordType tag, int value, int length) {
return DateToken(tag, length, value);
}
static DateToken Number(int value, int length) {
return DateToken(kNumberTag, length, value);
}
static DateToken Symbol(char symbol) {
return DateToken(kSymbolTag, 1, symbol);
}
static DateToken EndOfInput() { return DateToken(kEndOfInputTag, 0, -1); }
static DateToken WhiteSpace(int length) {
return DateToken(kWhiteSpaceTag, length, -1);
}
static DateToken Unknown() { return DateToken(kUnknownTokenTag, 1, -1); }
static DateToken Invalid() { return DateToken(kInvalidTokenTag, 0, -1); }
private:
enum TagType {
kInvalidTokenTag = -6,
kUnknownTokenTag = -5,
kWhiteSpaceTag = -4,
kNumberTag = -3,
kSymbolTag = -2,
kEndOfInputTag = -1,
kKeywordTagStart = 0
};
DateToken(int tag, int length, int value)
: tag_(tag), length_(length), value_(value) {}
int tag_;
int length_; // Number of characters.
int value_;
};
template <typename Char>
class DateStringTokenizer {
public:
explicit DateStringTokenizer(InputReader<Char>* in)
: in_(in), next_(Scan()) {}
DateToken Next() {
DateToken result = next_;
next_ = Scan();
return result;
}
DateToken Peek() { return next_; }
bool SkipSymbol(char symbol) {
if (next_.IsSymbol(symbol)) {
next_ = Scan();
return true;
}
return false;
}
private:
DateToken Scan();
InputReader<Char>* in_;
DateToken next_;
};
static int ReadMilliseconds(DateToken number);
// KeywordTable maps names of months, time zones, am/pm to numbers.
class KeywordTable : public AllStatic {
public:
// Look up a word in the keyword table and return an index.
// 'pre' contains a prefix of the word, zero-padded to size kPrefixLength
// and 'len' is the word length.
static int Lookup(const uint32_t* pre, int len);
// Get the type of the keyword at index i.
static KeywordType GetType(int i) {
return static_cast<KeywordType>(array[i][kTypeOffset]);
}
// Get the value of the keyword at index i.
static int GetValue(int i) { return array[i][kValueOffset]; }
static const int kPrefixLength = 3;
static const int kTypeOffset = kPrefixLength;
static const int kValueOffset = kTypeOffset + 1;
static const int kEntrySize = kValueOffset + 1;
static const int8_t array[][kEntrySize];
};
class TimeZoneComposer {
public:
TimeZoneComposer() : sign_(kNone), hour_(kNone), minute_(kNone) {}
void Set(int offset_in_hours) {
sign_ = offset_in_hours < 0 ? -1 : 1;
hour_ = offset_in_hours * sign_;
minute_ = 0;
}
void SetSign(int sign) { sign_ = sign < 0 ? -1 : 1; }
void SetAbsoluteHour(int hour) { hour_ = hour; }
void SetAbsoluteMinute(int minute) { minute_ = minute; }
bool IsExpecting(int n) const {
return hour_ != kNone && minute_ == kNone && TimeComposer::IsMinute(n);
}
bool IsUTC() const { return hour_ == 0 && minute_ == 0; }
bool Write(double* output);
bool IsEmpty() { return hour_ == kNone; }
private:
int sign_;
int hour_;
int minute_;
};
class TimeComposer {
public:
TimeComposer() : index_(0), hour_offset_(kNone) {}
bool IsEmpty() const { return index_ == 0; }
bool IsExpecting(int n) const {
return (index_ == 1 && IsMinute(n)) || (index_ == 2 && IsSecond(n)) ||
(index_ == 3 && IsMillisecond(n));
}
bool Add(int n) {
return index_ < kSize ? (comp_[index_++] = n, true) : false;
}
bool AddFinal(int n) {
if (!Add(n)) return false;
while (index_ < kSize) comp_[index_++] = 0;
return true;
}
void SetHourOffset(int n) { hour_offset_ = n; }
bool Write(double* output);
static bool IsMinute(int x) { return Between(x, 0, 59); }
static bool IsHour(int x) { return Between(x, 0, 23); }
static bool IsSecond(int x) { return Between(x, 0, 59); }
private:
static bool IsHour12(int x) { return Between(x, 0, 12); }
static bool IsMillisecond(int x) { return Between(x, 0, 999); }
static const int kSize = 4;
int comp_[kSize];
int index_;
int hour_offset_;
};
class DayComposer {
public:
DayComposer() : index_(0), named_month_(kNone), is_iso_date_(false) {}
bool IsEmpty() const { return index_ == 0; }
bool Add(int n) {
if (index_ < kSize) {
comp_[index_] = n;
index_++;
return true;
}
return false;
}
void SetNamedMonth(int n) { named_month_ = n; }
bool Write(double* output);
void set_iso_date() { is_iso_date_ = true; }
static bool IsMonth(int x) { return Between(x, 1, 12); }
static bool IsDay(int x) { return Between(x, 1, 31); }
private:
static const int kSize = 3;
int comp_[kSize];
int index_;
int named_month_;
// If set, ensures that data is always parsed in year-month-date order.
bool is_iso_date_;
};
// Tries to parse an ES5 Date Time String. Returns the next token
// to continue with in the legacy date string parser. If parsing is
// complete, returns DateToken::EndOfInput(). If terminally unsuccessful,
// returns DateToken::Invalid(). Otherwise parsing continues in the
// legacy parser.
template <typename Char>
static DateParser::DateToken ParseES5DateTime(
DateStringTokenizer<Char>* scanner, DayComposer* day, TimeComposer* time,
TimeZoneComposer* tz);
};
} // namespace internal
} // namespace v8
#endif // V8_DATE_DATEPARSER_H_