mirror of https://github.com/lukechilds/node.git
isaacs
13 years ago
194 changed files with 11152 additions and 4857 deletions
File diff suppressed because it is too large
@ -0,0 +1,384 @@ |
|||
// Copyright 2012 the V8 project authors. All rights reserved.
|
|||
// Redistribution and use in source and binary forms, with or without
|
|||
// modification, are permitted provided that the following conditions are
|
|||
// met:
|
|||
//
|
|||
// * Redistributions of source code must retain the above copyright
|
|||
// notice, this list of conditions and the following disclaimer.
|
|||
// * Redistributions in binary form must reproduce the above
|
|||
// copyright notice, this list of conditions and the following
|
|||
// disclaimer in the documentation and/or other materials provided
|
|||
// with the distribution.
|
|||
// * Neither the name of Google Inc. nor the names of its
|
|||
// contributors may be used to endorse or promote products derived
|
|||
// from this software without specific prior written permission.
|
|||
//
|
|||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
|||
#include "date.h" |
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "objects.h" |
|||
#include "objects-inl.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
|
|||
static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1}; |
|||
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}; |
|||
|
|||
|
|||
void DateCache::ResetDateCache() { |
|||
static const int kMaxStamp = Smi::kMaxValue; |
|||
stamp_ = Smi::FromInt(stamp_->value() + 1); |
|||
if (stamp_->value() > kMaxStamp) { |
|||
stamp_ = Smi::FromInt(0); |
|||
} |
|||
ASSERT(stamp_ != Smi::FromInt(kInvalidStamp)); |
|||
for (int i = 0; i < kDSTSize; ++i) { |
|||
ClearSegment(&dst_[i]); |
|||
} |
|||
dst_usage_counter_ = 0; |
|||
before_ = &dst_[0]; |
|||
after_ = &dst_[1]; |
|||
local_offset_ms_ = kInvalidLocalOffsetInMs; |
|||
ymd_valid_ = false; |
|||
} |
|||
|
|||
|
|||
void DateCache::ClearSegment(DST* segment) { |
|||
segment->start_sec = kMaxEpochTimeInSec; |
|||
segment->end_sec = -kMaxEpochTimeInSec; |
|||
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; |
|||
|
|||
ASSERT(DaysFromYearMonth(*year, 0) + days == save_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; |
|||
|
|||
ASSERT(days >= -1); |
|||
ASSERT(is_leap || (days >= 0)); |
|||
ASSERT((days < 365) || (is_leap && (days < 366))); |
|||
ASSERT(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0)))); |
|||
ASSERT(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days)); |
|||
ASSERT(!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) { |
|||
days -= 31 + 28 + is_leap; |
|||
// 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; |
|||
} |
|||
} |
|||
ASSERT(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; |
|||
} |
|||
|
|||
ASSERT(month >= 0); |
|||
ASSERT(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::ExtendTheAfterSegment(int time_sec, int offset_ms) { |
|||
if (after_->offset_ms == offset_ms && |
|||
after_->start_sec <= time_sec + kDefaultDSTDeltaInSec && |
|||
time_sec <= after_->end_sec) { |
|||
// Extend the after_ segment.
|
|||
after_->start_sec = time_sec; |
|||
} else { |
|||
// The after_ segment is either invalid or starts too late.
|
|||
if (after_->start_sec <= after_->end_sec) { |
|||
// If the after_ segment is valid, replace it with a new segment.
|
|||
after_ = LeastRecentlyUsedDST(before_); |
|||
} |
|||
after_->start_sec = time_sec; |
|||
after_->end_sec = time_sec; |
|||
after_->offset_ms = offset_ms; |
|||
after_->last_used = ++dst_usage_counter_; |
|||
} |
|||
} |
|||
|
|||
|
|||
int DateCache::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); |
|||
|
|||
// Invalidate cache if the usage counter is close to overflow.
|
|||
// Note that dst_usage_counter is incremented less than ten times
|
|||
// in this function.
|
|||
if (dst_usage_counter_ >= kMaxInt - 10) { |
|||
dst_usage_counter_ = 0; |
|||
for (int i = 0; i < kDSTSize; ++i) { |
|||
ClearSegment(&dst_[i]); |
|||
} |
|||
} |
|||
|
|||
// Optimistic fast check.
|
|||
if (before_->start_sec <= time_sec && |
|||
time_sec <= before_->end_sec) { |
|||
// Cache hit.
|
|||
before_->last_used = ++dst_usage_counter_; |
|||
return before_->offset_ms; |
|||
} |
|||
|
|||
ProbeDST(time_sec); |
|||
|
|||
ASSERT(InvalidSegment(before_) || before_->start_sec <= time_sec); |
|||
ASSERT(InvalidSegment(after_) || time_sec < after_->start_sec); |
|||
|
|||
if (InvalidSegment(before_)) { |
|||
// Cache miss.
|
|||
before_->start_sec = time_sec; |
|||
before_->end_sec = time_sec; |
|||
before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec); |
|||
before_->last_used = ++dst_usage_counter_; |
|||
return before_->offset_ms; |
|||
} |
|||
|
|||
if (time_sec <= before_->end_sec) { |
|||
// Cache hit.
|
|||
before_->last_used = ++dst_usage_counter_; |
|||
return before_->offset_ms; |
|||
} |
|||
|
|||
if (time_sec > before_->end_sec + kDefaultDSTDeltaInSec) { |
|||
// If the before_ segment ends too early, then just
|
|||
// query for the offset of the time_sec
|
|||
int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec); |
|||
ExtendTheAfterSegment(time_sec, offset_ms); |
|||
// This swap helps the optimistic fast check in subsequent invocations.
|
|||
DST* temp = before_; |
|||
before_ = after_; |
|||
after_ = temp; |
|||
return offset_ms; |
|||
} |
|||
|
|||
// Now the time_sec is between
|
|||
// before_->end_sec and before_->end_sec + default DST delta.
|
|||
// Update the usage counter of before_ since it is going to be used.
|
|||
before_->last_used = ++dst_usage_counter_; |
|||
|
|||
// Check if after_ segment is invalid or starts too late.
|
|||
// Note that start_sec of invalid segments is kMaxEpochTimeInSec.
|
|||
if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) { |
|||
int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec; |
|||
int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec); |
|||
ExtendTheAfterSegment(new_after_start_sec, new_offset_ms); |
|||
} else { |
|||
ASSERT(!InvalidSegment(after_)); |
|||
// Update the usage counter of after_ since it is going to be used.
|
|||
after_->last_used = ++dst_usage_counter_; |
|||
} |
|||
|
|||
// Now the time_sec is between before_->end_sec and after_->start_sec.
|
|||
// 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_sec = after_->end_sec; |
|||
ClearSegment(after_); |
|||
return before_->offset_ms; |
|||
} |
|||
|
|||
// Binary search for daylight savings offset change point,
|
|||
// but give up if we don't find it in four iterations.
|
|||
for (int i = 4; i >= 0; --i) { |
|||
int delta = after_->start_sec - before_->end_sec; |
|||
int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2; |
|||
int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec); |
|||
if (before_->offset_ms == offset_ms) { |
|||
before_->end_sec = middle_sec; |
|||
if (time_sec <= before_->end_sec) { |
|||
return offset_ms; |
|||
} |
|||
} else { |
|||
ASSERT(after_->offset_ms == offset_ms); |
|||
after_->start_sec = middle_sec; |
|||
if (time_sec >= after_->start_sec) { |
|||
// This swap helps the optimistic fast check in subsequent invocations.
|
|||
DST* temp = before_; |
|||
before_ = after_; |
|||
after_ = temp; |
|||
return offset_ms; |
|||
} |
|||
} |
|||
} |
|||
UNREACHABLE(); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
void DateCache::ProbeDST(int time_sec) { |
|||
DST* before = NULL; |
|||
DST* after = NULL; |
|||
ASSERT(before_ != after_); |
|||
|
|||
for (int i = 0; i < kDSTSize; ++i) { |
|||
if (dst_[i].start_sec <= time_sec) { |
|||
if (before == NULL || before->start_sec < dst_[i].start_sec) { |
|||
before = &dst_[i]; |
|||
} |
|||
} else if (time_sec < dst_[i].end_sec) { |
|||
if (after == NULL || after->end_sec > dst_[i].end_sec) { |
|||
after = &dst_[i]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// If before or after segments were not found,
|
|||
// then set them to any invalid segment.
|
|||
if (before == NULL) { |
|||
before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after); |
|||
} |
|||
if (after == NULL) { |
|||
after = InvalidSegment(after_) && before != after_ |
|||
? after_ : LeastRecentlyUsedDST(before); |
|||
} |
|||
|
|||
ASSERT(before != NULL); |
|||
ASSERT(after != NULL); |
|||
ASSERT(before != after); |
|||
ASSERT(InvalidSegment(before) || before->start_sec <= time_sec); |
|||
ASSERT(InvalidSegment(after) || time_sec < after->start_sec); |
|||
ASSERT(InvalidSegment(before) || InvalidSegment(after) || |
|||
before->end_sec < after->start_sec); |
|||
|
|||
before_ = before; |
|||
after_ = after; |
|||
} |
|||
|
|||
|
|||
DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) { |
|||
DST* result = NULL; |
|||
for (int i = 0; i < kDSTSize; ++i) { |
|||
if (&dst_[i] == skip) continue; |
|||
if (result == NULL || result->last_used > dst_[i].last_used) { |
|||
result = &dst_[i]; |
|||
} |
|||
} |
|||
ClearSegment(result); |
|||
return result; |
|||
} |
|||
|
|||
} } // namespace v8::internal
|
@ -0,0 +1,260 @@ |
|||
// Copyright 2012 the V8 project authors. All rights reserved.
|
|||
// Redistribution and use in source and binary forms, with or without
|
|||
// modification, are permitted provided that the following conditions are
|
|||
// met:
|
|||
//
|
|||
// * Redistributions of source code must retain the above copyright
|
|||
// notice, this list of conditions and the following disclaimer.
|
|||
// * Redistributions in binary form must reproduce the above
|
|||
// copyright notice, this list of conditions and the following
|
|||
// disclaimer in the documentation and/or other materials provided
|
|||
// with the distribution.
|
|||
// * Neither the name of Google Inc. nor the names of its
|
|||
// contributors may be used to endorse or promote products derived
|
|||
// from this software without specific prior written permission.
|
|||
//
|
|||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
|||
#ifndef V8_DATE_H_ |
|||
#define V8_DATE_H_ |
|||
|
|||
#include "allocation.h" |
|||
#include "globals.h" |
|||
#include "platform.h" |
|||
|
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
class DateCache { |
|||
public: |
|||
static const int kMsPerMin = 60 * 1000; |
|||
static const int kSecPerDay = 24 * 60 * 60; |
|||
static const int64_t kMsPerDay = kSecPerDay * 1000; |
|||
|
|||
// 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 + 10 * kMsPerDay; |
|||
|
|||
// 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() : stamp_(0) { |
|||
ResetDateCache(); |
|||
} |
|||
|
|||
virtual ~DateCache() {} |
|||
|
|||
|
|||
// Clears cached timezone information and increments the cache stamp.
|
|||
void ResetDateCache(); |
|||
|
|||
|
|||
// 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); |
|||
} |
|||
|
|||
|
|||
// 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 - 15.9.1.7.
|
|||
int LocalOffsetInMs() { |
|||
if (local_offset_ms_ == kInvalidLocalOffsetInMs) { |
|||
local_offset_ms_ = GetLocalOffsetFromOS(); |
|||
} |
|||
return local_offset_ms_; |
|||
} |
|||
|
|||
|
|||
const char* LocalTimezone(int64_t time_ms) { |
|||
if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) { |
|||
time_ms = EquivalentTime(time_ms); |
|||
} |
|||
return OS::LocalTimezone(static_cast<double>(time_ms)); |
|||
} |
|||
|
|||
// 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 - 15.9.1.9
|
|||
int64_t ToLocal(int64_t time_ms) { |
|||
return time_ms + LocalOffsetInMs() + DaylightSavingsOffsetInMs(time_ms); |
|||
} |
|||
|
|||
// ECMA 262 - 15.9.1.9
|
|||
int64_t ToUTC(int64_t time_ms) { |
|||
time_ms -= LocalOffsetInMs(); |
|||
return time_ms - DaylightSavingsOffsetInMs(time_ms); |
|||
} |
|||
|
|||
|
|||
// 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); |
|||
|
|||
// 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.
|
|||
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>(OS::DaylightSavingsOffset(time_ms)); |
|||
} |
|||
|
|||
virtual int GetLocalOffsetFromOS() { |
|||
double offset = OS::LocalTimeOffset(); |
|||
ASSERT(offset < kInvalidLocalOffsetInMs); |
|||
return static_cast<int>(offset); |
|||
} |
|||
|
|||
private: |
|||
// The implementation relies on the fact that no time zones have
|
|||
// more than one daylight savings offset change 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 kDefaultDSTDeltaInSec = 19 * kSecPerDay; |
|||
|
|||
// Size of the Daylight Savings Time cache.
|
|||
static const int kDSTSize = 32; |
|||
|
|||
// Daylight Savings Time segment stores a segment of time where
|
|||
// daylight savings offset does not change.
|
|||
struct DST { |
|||
int start_sec; |
|||
int end_sec; |
|||
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); |
|||
|
|||
// Sets the before_ and the after_ segments from the DST 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 ProbeDST(int time_sec); |
|||
|
|||
// Finds the least recently used segment from the DST cache that is not
|
|||
// equal to the given 'skip' segment.
|
|||
DST* LeastRecentlyUsedDST(DST* skip); |
|||
|
|||
// Extends the after_ segment with the given point or resets it
|
|||
// if it starts later than the given time + kDefaultDSTDeltaInSec.
|
|||
inline void ExtendTheAfterSegment(int time_sec, int offset_ms); |
|||
|
|||
// Makes the given segment invalid.
|
|||
inline void ClearSegment(DST* segment); |
|||
|
|||
bool InvalidSegment(DST* segment) { |
|||
return segment->start_sec > segment->end_sec; |
|||
} |
|||
|
|||
Smi* stamp_; |
|||
|
|||
// Daylight Saving Time cache.
|
|||
DST dst_[kDSTSize]; |
|||
int dst_usage_counter_; |
|||
DST* before_; |
|||
DST* after_; |
|||
|
|||
int local_offset_ms_; |
|||
|
|||
// Year/Month/Day cache.
|
|||
bool ymd_valid_; |
|||
int ymd_days_; |
|||
int ymd_year_; |
|||
int ymd_month_; |
|||
int ymd_day_; |
|||
}; |
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif |
File diff suppressed because it is too large
@ -0,0 +1,226 @@ |
|||
// Copyright 2012 the V8 project authors. All rights reserved.
|
|||
// Redistribution and use in source and binary forms, with or without
|
|||
// modification, are permitted provided that the following conditions are
|
|||
// met:
|
|||
//
|
|||
// * Redistributions of source code must retain the above copyright
|
|||
// notice, this list of conditions and the following disclaimer.
|
|||
// * Redistributions in binary form must reproduce the above
|
|||
// copyright notice, this list of conditions and the following
|
|||
// disclaimer in the documentation and/or other materials provided
|
|||
// with the distribution.
|
|||
// * Neither the name of Google Inc. nor the names of its
|
|||
// contributors may be used to endorse or promote products derived
|
|||
// from this software without specific prior written permission.
|
|||
//
|
|||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
|||
#include "v8.h" |
|||
|
|||
#include "interface.h" |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
static bool Match(void* key1, void* key2) { |
|||
String* name1 = *static_cast<String**>(key1); |
|||
String* name2 = *static_cast<String**>(key2); |
|||
ASSERT(name1->IsSymbol()); |
|||
ASSERT(name2->IsSymbol()); |
|||
return name1 == name2; |
|||
} |
|||
|
|||
|
|||
Interface* Interface::Lookup(Handle<String> name) { |
|||
ASSERT(IsModule()); |
|||
ZoneHashMap* map = Chase()->exports_; |
|||
if (map == NULL) return NULL; |
|||
ZoneHashMap::Entry* p = map->Lookup(name.location(), name->Hash(), false); |
|||
if (p == NULL) return NULL; |
|||
ASSERT(*static_cast<String**>(p->key) == *name); |
|||
ASSERT(p->value != NULL); |
|||
return static_cast<Interface*>(p->value); |
|||
} |
|||
|
|||
|
|||
#ifdef DEBUG |
|||
// Current nesting depth for debug output.
|
|||
class Nesting { |
|||
public: |
|||
Nesting() { current_ += 2; } |
|||
~Nesting() { current_ -= 2; } |
|||
static int current() { return current_; } |
|||
private: |
|||
static int current_; |
|||
}; |
|||
|
|||
int Nesting::current_ = 0; |
|||
#endif |
|||
|
|||
|
|||
void Interface::DoAdd( |
|||
void* name, uint32_t hash, Interface* interface, bool* ok) { |
|||
MakeModule(ok); |
|||
if (!*ok) return; |
|||
|
|||
#ifdef DEBUG |
|||
if (FLAG_print_interface_details) { |
|||
PrintF("%*s# Adding...\n", Nesting::current(), ""); |
|||
PrintF("%*sthis = ", Nesting::current(), ""); |
|||
this->Print(Nesting::current()); |
|||
PrintF("%*s%s : ", Nesting::current(), "", |
|||
(*reinterpret_cast<String**>(name))->ToAsciiArray()); |
|||
interface->Print(Nesting::current()); |
|||
} |
|||
#endif |
|||
|
|||
ZoneHashMap** map = &Chase()->exports_; |
|||
if (*map == NULL) *map = new ZoneHashMap(Match, 8); |
|||
|
|||
ZoneHashMap::Entry* p = (*map)->Lookup(name, hash, !IsFrozen()); |
|||
if (p == NULL) { |
|||
// This didn't have name but was frozen already, that's an error.
|
|||
*ok = false; |
|||
} else if (p->value == NULL) { |
|||
p->value = interface; |
|||
} else { |
|||
#ifdef DEBUG |
|||
Nesting nested; |
|||
#endif |
|||
reinterpret_cast<Interface*>(p->value)->Unify(interface, ok); |
|||
} |
|||
|
|||
#ifdef DEBUG |
|||
if (FLAG_print_interface_details) { |
|||
PrintF("%*sthis' = ", Nesting::current(), ""); |
|||
this->Print(Nesting::current()); |
|||
PrintF("%*s# Added.\n", Nesting::current(), ""); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
|
|||
void Interface::Unify(Interface* that, bool* ok) { |
|||
if (this->forward_) return this->Chase()->Unify(that, ok); |
|||
if (that->forward_) return this->Unify(that->Chase(), ok); |
|||
ASSERT(this->forward_ == NULL); |
|||
ASSERT(that->forward_ == NULL); |
|||
|
|||
*ok = true; |
|||
if (this == that) return; |
|||
if (this->IsValue()) return that->MakeValue(ok); |
|||
if (that->IsValue()) return this->MakeValue(ok); |
|||
|
|||
#ifdef DEBUG |
|||
if (FLAG_print_interface_details) { |
|||
PrintF("%*s# Unifying...\n", Nesting::current(), ""); |
|||
PrintF("%*sthis = ", Nesting::current(), ""); |
|||
this->Print(Nesting::current()); |
|||
PrintF("%*sthat = ", Nesting::current(), ""); |
|||
that->Print(Nesting::current()); |
|||
} |
|||
#endif |
|||
|
|||
// Merge the smaller interface into the larger, for performance.
|
|||
if (this->exports_ != NULL && (that->exports_ == NULL || |
|||
this->exports_->occupancy() >= that->exports_->occupancy())) { |
|||
this->DoUnify(that, ok); |
|||
} else { |
|||
that->DoUnify(this, ok); |
|||
} |
|||
|
|||
#ifdef DEBUG |
|||
if (FLAG_print_interface_details) { |
|||
PrintF("%*sthis' = ", Nesting::current(), ""); |
|||
this->Print(Nesting::current()); |
|||
PrintF("%*sthat' = ", Nesting::current(), ""); |
|||
that->Print(Nesting::current()); |
|||
PrintF("%*s# Unified.\n", Nesting::current(), ""); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
|
|||
void Interface::DoUnify(Interface* that, bool* ok) { |
|||
ASSERT(this->forward_ == NULL); |
|||
ASSERT(that->forward_ == NULL); |
|||
ASSERT(!this->IsValue()); |
|||
ASSERT(!that->IsValue()); |
|||
ASSERT(*ok); |
|||
|
|||
#ifdef DEBUG |
|||
Nesting nested; |
|||
#endif |
|||
|
|||
// Try to merge all members from that into this.
|
|||
ZoneHashMap* map = that->exports_; |
|||
if (map != NULL) { |
|||
for (ZoneHashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) { |
|||
this->DoAdd(p->key, p->hash, static_cast<Interface*>(p->value), ok); |
|||
if (!*ok) return; |
|||
} |
|||
} |
|||
|
|||
// If the new interface is larger than that's, then there were members in
|
|||
// 'this' which 'that' didn't have. If 'that' was frozen that is an error.
|
|||
int this_size = this->exports_ == NULL ? 0 : this->exports_->occupancy(); |
|||
int that_size = map == NULL ? 0 : map->occupancy(); |
|||
if (that->IsFrozen() && this_size > that_size) { |
|||
*ok = false; |
|||
return; |
|||
} |
|||
|
|||
// Merge interfaces.
|
|||
this->flags_ |= that->flags_; |
|||
that->forward_ = this; |
|||
} |
|||
|
|||
|
|||
#ifdef DEBUG |
|||
void Interface::Print(int n) { |
|||
int n0 = n > 0 ? n : 0; |
|||
|
|||
if (FLAG_print_interface_details) { |
|||
PrintF("%p", static_cast<void*>(this)); |
|||
for (Interface* link = this->forward_; link != NULL; link = link->forward_) |
|||
PrintF("->%p", static_cast<void*>(link)); |
|||
PrintF(" "); |
|||
} |
|||
|
|||
if (IsUnknown()) { |
|||
PrintF("unknown\n"); |
|||
} else if (IsValue()) { |
|||
PrintF("value\n"); |
|||
} else if (IsModule()) { |
|||
PrintF("module %s{", IsFrozen() ? "" : "(unresolved) "); |
|||
ZoneHashMap* map = Chase()->exports_; |
|||
if (map == NULL || map->occupancy() == 0) { |
|||
PrintF("}\n"); |
|||
} else if (n < 0 || n0 >= 2 * FLAG_print_interface_depth) { |
|||
// Avoid infinite recursion on cyclic types.
|
|||
PrintF("...}\n"); |
|||
} else { |
|||
PrintF("\n"); |
|||
for (ZoneHashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) { |
|||
String* name = *static_cast<String**>(p->key); |
|||
Interface* interface = static_cast<Interface*>(p->value); |
|||
PrintF("%*s%s : ", n0 + 2, "", name->ToAsciiArray()); |
|||
interface->Print(n0 + 2); |
|||
} |
|||
PrintF("%*s}\n", n0, ""); |
|||
} |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
} } // namespace v8::internal
|
@ -0,0 +1,156 @@ |
|||
// Copyright 2012 the V8 project authors. All rights reserved.
|
|||
// Redistribution and use in source and binary forms, with or without
|
|||
// modification, are permitted provided that the following conditions are
|
|||
// met:
|
|||
//
|
|||
// * Redistributions of source code must retain the above copyright
|
|||
// notice, this list of conditions and the following disclaimer.
|
|||
// * Redistributions in binary form must reproduce the above
|
|||
// copyright notice, this list of conditions and the following
|
|||
// disclaimer in the documentation and/or other materials provided
|
|||
// with the distribution.
|
|||
// * Neither the name of Google Inc. nor the names of its
|
|||
// contributors may be used to endorse or promote products derived
|
|||
// from this software without specific prior written permission.
|
|||
//
|
|||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
|||
#ifndef V8_INTERFACE_H_ |
|||
#define V8_INTERFACE_H_ |
|||
|
|||
#include "zone-inl.h" // For operator new. |
|||
|
|||
namespace v8 { |
|||
namespace internal { |
|||
|
|||
|
|||
// This class implements the following abstract grammar of interfaces
|
|||
// (i.e. module types):
|
|||
// interface ::= UNDETERMINED | VALUE | MODULE(exports)
|
|||
// exports ::= {name : interface, ...}
|
|||
// A frozen module type is one that is fully determined. Unification does not
|
|||
// allow adding additional exports to frozen interfaces.
|
|||
// Otherwise, unifying modules merges their exports.
|
|||
// Undetermined types are unification variables that can be unified freely.
|
|||
|
|||
class Interface : public ZoneObject { |
|||
public: |
|||
// ---------------------------------------------------------------------------
|
|||
// Factory methods.
|
|||
|
|||
static Interface* NewValue() { |
|||
static Interface value_interface(VALUE + FROZEN); // Cached.
|
|||
return &value_interface; |
|||
} |
|||
|
|||
static Interface* NewUnknown() { |
|||
return new Interface(NONE); |
|||
} |
|||
|
|||
static Interface* NewModule() { |
|||
return new Interface(MODULE); |
|||
} |
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// Mutators.
|
|||
|
|||
// Add a name to the list of exports. If it already exists, unify with
|
|||
// interface, otherwise insert unless this is closed.
|
|||
void Add(Handle<String> name, Interface* interface, bool* ok) { |
|||
DoAdd(name.location(), name->Hash(), interface, ok); |
|||
} |
|||
|
|||
// Unify with another interface. If successful, both interface objects will
|
|||
// represent the same type, and changes to one are reflected in the other.
|
|||
void Unify(Interface* that, bool* ok); |
|||
|
|||
// Determine this interface to be a value interface.
|
|||
void MakeValue(bool* ok) { |
|||
*ok = !IsModule(); |
|||
if (*ok) Chase()->flags_ |= VALUE; |
|||
} |
|||
|
|||
// Determine this interface to be a module interface.
|
|||
void MakeModule(bool* ok) { |
|||
*ok = !IsValue(); |
|||
if (*ok) Chase()->flags_ |= MODULE; |
|||
} |
|||
|
|||
// Do not allow any further refinements, directly or through unification.
|
|||
void Freeze(bool* ok) { |
|||
*ok = IsValue() || IsModule(); |
|||
if (*ok) Chase()->flags_ |= FROZEN; |
|||
} |
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// Accessors.
|
|||
|
|||
// Look up an exported name. Returns NULL if not (yet) defined.
|
|||
Interface* Lookup(Handle<String> name); |
|||
|
|||
// Check whether this is still a fully undetermined type.
|
|||
bool IsUnknown() { return Chase()->flags_ == NONE; } |
|||
|
|||
// Check whether this is a value type.
|
|||
bool IsValue() { return Chase()->flags_ & VALUE; } |
|||
|
|||
// Check whether this is a module type.
|
|||
bool IsModule() { return Chase()->flags_ & MODULE; } |
|||
|
|||
// Check whether this is closed (i.e. fully determined).
|
|||
bool IsFrozen() { return Chase()->flags_ & FROZEN; } |
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// Debugging.
|
|||
#ifdef DEBUG |
|||
void Print(int n = 0); // n = indentation; n < 0 => don't print recursively
|
|||
#endif |
|||
|
|||
// ---------------------------------------------------------------------------
|
|||
// Implementation.
|
|||
private: |
|||
enum Flags { // All flags are monotonic
|
|||
NONE = 0, |
|||
VALUE = 1, // This type describes a value
|
|||
MODULE = 2, // This type describes a module
|
|||
FROZEN = 4 // This type is fully determined
|
|||
}; |
|||
|
|||
int flags_; |
|||
Interface* forward_; // Unification link
|
|||
ZoneHashMap* exports_; // Module exports and their types (allocated lazily)
|
|||
|
|||
explicit Interface(int flags) |
|||
: flags_(flags), |
|||
forward_(NULL), |
|||
exports_(NULL) { |
|||
#ifdef DEBUG |
|||
if (FLAG_print_interface_details) |
|||
PrintF("# Creating %p\n", static_cast<void*>(this)); |
|||
#endif |
|||
} |
|||
|
|||
Interface* Chase() { |
|||
Interface* result = this; |
|||
while (result->forward_ != NULL) result = result->forward_; |
|||
if (result != this) forward_ = result; // On-the-fly path compression.
|
|||
return result; |
|||
} |
|||
|
|||
void DoAdd(void* name, uint32_t hash, Interface* interface, bool* ok); |
|||
void DoUnify(Interface* that, bool* ok); |
|||
}; |
|||
|
|||
} } // namespace v8::internal
|
|||
|
|||
#endif // V8_INTERFACE_H_
|
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue